Skip to content

Commit 037e5a4

Browse files
committed
add SLiMgui color scales window to Help menu
1 parent f655ff8 commit 037e5a4

8 files changed

Lines changed: 261 additions & 63 deletions

QtSLiM/QtSLiMAppDelegate.cpp

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -850,40 +850,46 @@ void QtSLiMAppDelegate::addActionsForGlobalMenuItems(QWidget *window)
850850
}
851851
{
852852
QAction *actionShowCycle_WF = new QAction("Show WF Tick Cycle", this);
853-
//actionAbout->setShortcut(flagsAndKey(Qt::ControlModifier, Qt::Key_Comma));
853+
//actionShowCycle_WF->setShortcut(flagsAndKey(Qt::ControlModifier, Qt::Key_Comma));
854854
connect(actionShowCycle_WF, &QAction::triggered, qtSLiMAppDelegate, &QtSLiMAppDelegate::dispatch_showCycle_WF);
855855
window->addAction(actionShowCycle_WF);
856856
}
857857
{
858858
QAction *actionShowCycle_nonWF = new QAction("Show nonWF Tick Cycle", this);
859-
//actionAbout->setShortcut(flagsAndKey(Qt::ControlModifier, Qt::Key_Comma));
859+
//actionShowCycle_nonWF->setShortcut(flagsAndKey(Qt::ControlModifier, Qt::Key_Comma));
860860
connect(actionShowCycle_nonWF, &QAction::triggered, qtSLiMAppDelegate, &QtSLiMAppDelegate::dispatch_showCycle_nonWF);
861861
window->addAction(actionShowCycle_nonWF);
862862
}
863863
{
864864
QAction *actionShowCycle_WF_MS = new QAction("Show WF Tick Cycle (Multispecies)", this);
865-
//actionAbout->setShortcut(flagsAndKey(Qt::ControlModifier, Qt::Key_Comma));
865+
//actionShowCycle_WF_MS->setShortcut(flagsAndKey(Qt::ControlModifier, Qt::Key_Comma));
866866
connect(actionShowCycle_WF_MS, &QAction::triggered, qtSLiMAppDelegate, &QtSLiMAppDelegate::dispatch_showCycle_WF_MS);
867867
window->addAction(actionShowCycle_WF_MS);
868868
}
869869
{
870870
QAction *actionShowCycle_nonWF_MS = new QAction("Show nonWF Tick Cycle (Multispecies)", this);
871-
//actionAbout->setShortcut(flagsAndKey(Qt::ControlModifier, Qt::Key_Comma));
871+
//actionShowCycle_nonWF_MS->setShortcut(flagsAndKey(Qt::ControlModifier, Qt::Key_Comma));
872872
connect(actionShowCycle_nonWF_MS, &QAction::triggered, qtSLiMAppDelegate, &QtSLiMAppDelegate::dispatch_showCycle_nonWF_MS);
873873
window->addAction(actionShowCycle_nonWF_MS);
874874
}
875875
{
876876
QAction *actionShowColorChart = new QAction("Show Color Chart", this);
877-
//actionAbout->setShortcut(flagsAndKey(Qt::ControlModifier, Qt::Key_Comma));
877+
//actionShowColorChart->setShortcut(flagsAndKey(Qt::ControlModifier, Qt::Key_Comma));
878878
connect(actionShowColorChart, &QAction::triggered, qtSLiMAppDelegate, &QtSLiMAppDelegate::dispatch_showColorChart);
879879
window->addAction(actionShowColorChart);
880880
}
881881
{
882882
QAction *actionShowPlotSymbols = new QAction("Show Plot Symbols", this);
883-
//actionAbout->setShortcut(flagsAndKey(Qt::ControlModifier, Qt::Key_Comma));
883+
//actionShowPlotSymbols->setShortcut(flagsAndKey(Qt::ControlModifier, Qt::Key_Comma));
884884
connect(actionShowPlotSymbols, &QAction::triggered, qtSLiMAppDelegate, &QtSLiMAppDelegate::dispatch_showPlotSymbols);
885885
window->addAction(actionShowPlotSymbols);
886886
}
887+
{
888+
QAction *actionShowColorScales = new QAction("Show SLiMgui Color Scales", this);
889+
//actionShowColorScales->setShortcut(flagsAndKey(Qt::ControlModifier, Qt::Key_Comma));
890+
connect(actionShowColorScales, &QAction::triggered, qtSLiMAppDelegate, &QtSLiMAppDelegate::dispatch_showColorScales);
891+
window->addAction(actionShowColorScales);
892+
}
887893
{
888894
QAction *actionHelp = new QAction("Help", this);
889895
//actionHelp->setShortcut(flagsAndKey(Qt::ControlModifier, Qt::Key_Comma));
@@ -1311,6 +1317,43 @@ void QtSLiMAppDelegate::dispatch_showPlotSymbols(void)
13111317
QtSLiMMakeWindowVisibleAndExposed(imageWindow);
13121318
}
13131319

1320+
void QtSLiMAppDelegate::dispatch_showColorScales(void)
1321+
{
1322+
// This shows a global window that displays SLiMgui's color scales. Unlike the above global image
1323+
// windows shown by globalImageWindowWithPath(), this window is drawn dynamically by a custom widget.
1324+
// This code is based on the code in globalImageWindowWithPath(), with that custom widget.
1325+
int window_width = round(301);
1326+
int window_height = round(197);
1327+
1328+
QWidget *image_window = new QWidget(nullptr, Qt::Window | Qt::Tool); // a parentless standalone window
1329+
1330+
image_window->setWindowTitle("SLiMgui Color Scales");
1331+
image_window->setFixedSize(window_width, window_height);
1332+
1333+
// Make the custom widget
1334+
QtSLiMColorScaleWidget *colorScaleView = new QtSLiMColorScaleWidget(image_window);
1335+
1336+
// Install imageView in the window
1337+
QVBoxLayout *topLayout = new QVBoxLayout;
1338+
1339+
image_window->setLayout(topLayout);
1340+
topLayout->setContentsMargins(0, 0, 0, 0);
1341+
topLayout->setSpacing(0);
1342+
topLayout->addWidget(colorScaleView);
1343+
1344+
// Position the window nicely
1345+
//positionNewSubsidiaryWindow(image_window);
1346+
1347+
// make window actions for all global menu items
1348+
// this does not seem to be necessary on macOS, but maybe it is on Linux; will need testing FIXME
1349+
//qtSLiMAppDelegate->addActionsForGlobalMenuItems(this);
1350+
1351+
image_window->setAttribute(Qt::WA_DeleteOnClose, true);
1352+
1353+
if (image_window)
1354+
QtSLiMMakeWindowVisibleAndExposed(image_window);
1355+
}
1356+
13141357
void QtSLiMAppDelegate::dispatch_help(void)
13151358
{
13161359
QtSLiMHelpWindow &helpWindow = QtSLiMHelpWindow::instance();

QtSLiM/QtSLiMAppDelegate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ public slots:
114114
void dispatch_showCycle_nonWF_MS(void);
115115
void dispatch_showColorChart(void);
116116
void dispatch_showPlotSymbols(void);
117+
void dispatch_showColorScales(void);
117118
void dispatch_help(void);
118119
void dispatch_quit(void);
119120

QtSLiM/QtSLiMExtras.cpp

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,138 @@ void RGBForSelectionCoeff(double value, float *colorRed, float *colorGreen, floa
277277
}
278278
}
279279

280+
QtSLiMColorScaleWidget::QtSLiMColorScaleWidget(QWidget *p_parent) : QWidget(p_parent),
281+
fitnessTicks({"0.0", "0.5", "1.0", "1.5", "2.0"}),
282+
effectTicks({"-1.0", "-0.5", "0.0", "0.5", "1.0"})
283+
{
284+
setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
285+
}
286+
287+
void QtSLiMColorScaleWidget::paintEvent(QPaintEvent * /*p_paintEvent*/)
288+
{
289+
// we're designed to fit in a fixed size of 301 x 197; see dispatch_showColorScales()
290+
//QRect overallRect = contentsRect();
291+
QPainter painter(this);
292+
QRect stripe1 = QRect(15, 33, 271, 20); // odd width so we have a central pixel of exactly yellow
293+
QRect stripe2 = QRect(15, 105, 271, 20); // odd width so we have a central pixel of exactly yellow
294+
295+
static QFont *labelFont = nullptr;
296+
297+
if (!labelFont)
298+
{
299+
labelFont = new QFont();
300+
#ifdef __linux__
301+
labelFont->setPointSize(10);
302+
#else
303+
labelFont->setPointSize(12);
304+
#endif
305+
labelFont->setBold(true);
306+
}
307+
painter.setFont(*labelFont);
308+
309+
const double labelYOffset = -8;
310+
311+
// Fitness color scale
312+
painter.drawText(stripe1.x(), stripe1.y() + labelYOffset, "Individual fitness scale:");
313+
314+
for (int x = stripe1.left() + 1; x <= (stripe1.left() + 1) + (stripe1.width() - 3); ++x)
315+
{
316+
const double scalingFactor = 0.8; // this is constant in QtSLiM; there used to be a slider
317+
QRect sliver(x, stripe1.top() + 1, 1, stripe1.height() - 2);
318+
double sliverFraction = (x - (stripe1.left() + 1)) / (stripe1.width() - 3.0);
319+
double fitness = sliverFraction * 2.0; // cover fitness values of 0.0 to 2.0
320+
float r, g, b;
321+
RGBForFitness(fitness, &r, &g, &b, scalingFactor);
322+
painter.fillRect(sliver, QColor(round(r * 255), round(g * 255), round(b * 255)));
323+
}
324+
325+
QtSLiMFrameRect(stripe1, Qt::black, painter);
326+
327+
// Mutation effect color scale
328+
painter.drawText(stripe2.x(), stripe2.y() + labelYOffset, "Mutation effect scale:");
329+
330+
for (int x = stripe2.left() + 1; x <= (stripe2.left() + 1) + (stripe2.width() - 3); ++x)
331+
{
332+
const double scalingFactor = 0.8; // this is constant in QtSLiM; there used to be a slider
333+
QRect sliver(x, stripe2.top() + 1, 1, stripe2.height() - 2);
334+
double sliverFraction = (x - (stripe2.left() + 1)) / (stripe2.width() - 3.0);
335+
double fitness = sliverFraction * 2.0 - 1; // cover mutation effect values of -1.0 to 1.0
336+
float r, g, b;
337+
RGBForSelectionCoeff(fitness, &r, &g, &b, scalingFactor);
338+
painter.fillRect(sliver, QColor(round(r * 255), round(g * 255), round(b * 255)));
339+
340+
//qDebug() << "x =" << x << " << sliverFraction =" << sliverFraction << " fitness =" << fitness;
341+
}
342+
343+
QtSLiMFrameRect(stripe2, Qt::black, painter);
344+
345+
// Draw axis scales
346+
static QFont *tickFont = nullptr;
347+
348+
if (!tickFont)
349+
{
350+
tickFont = new QFont();
351+
#ifdef __linux__
352+
tickFont->setPointSize(8);
353+
#else
354+
tickFont->setPointSize(10);
355+
#endif
356+
}
357+
painter.setFont(*tickFont);
358+
359+
QFontMetricsF fontMetrics(*tickFont);
360+
361+
for (int tickIndex = 0; tickIndex < 5; tickIndex++)
362+
{
363+
bool longTick = (tickIndex % 2 == 0);
364+
int tickX = round(stripe1.left() + 1 + (tickIndex / 4.0) * (stripe1.width() - 3.0));
365+
QString tickLabel;
366+
double tickLabelWidth;
367+
368+
// label stripe 1
369+
tickLabel = fitnessTicks[tickIndex];
370+
371+
#if (QT_VERSION < QT_VERSION_CHECK(5, 11, 0))
372+
tickLabelWidth = fontMetrics.width(tickLabel); // deprecated in 5.11
373+
#else
374+
tickLabelWidth = fontMetrics.horizontalAdvance(tickLabel); // added in Qt 5.11
375+
#endif
376+
377+
painter.fillRect(tickX, stripe1.bottom() + 1, 1, longTick ? 4 : 2, Qt::black);
378+
painter.drawText(QPointF(tickX - tickLabelWidth / 2.0 + 1, stripe1.bottom() + 16), tickLabel);
379+
380+
// label stripe 2
381+
tickLabel = effectTicks[tickIndex];
382+
383+
#if (QT_VERSION < QT_VERSION_CHECK(5, 11, 0))
384+
tickLabelWidth = fontMetrics.width(tickLabel); // deprecated in 5.11
385+
#else
386+
tickLabelWidth = fontMetrics.horizontalAdvance(tickLabel); // added in Qt 5.11
387+
#endif
388+
389+
painter.fillRect(tickX, stripe2.bottom() + 1, 1, longTick ? 4 : 2, Qt::black);
390+
painter.drawText(QPointF(tickX - tickLabelWidth / 2.0 + 1, stripe2.bottom() + 16), tickLabel);
391+
}
392+
393+
// add final notes in italic
394+
static QFont *noteFont = nullptr;
395+
396+
if (!noteFont)
397+
{
398+
noteFont = new QFont();
399+
#ifdef __linux__
400+
noteFont->setPointSize(9);
401+
#else
402+
noteFont->setPointSize(11);
403+
#endif
404+
noteFont->setItalic(true);
405+
}
406+
painter.setFont(*noteFont);
407+
408+
painter.drawText(stripe1.x(), stripe2.bottom() + 44, "Yellow indicates neutrality on both color scales.");
409+
painter.drawText(stripe1.x(), stripe2.bottom() + 58, "Both scales fade out to white for large values.");
410+
}
411+
280412
// A subclass of QLineEdit that selects all its text when it receives keyboard focus
281413
// thanks to https://stackoverflow.com/a/51807268/2752221
282414
QtSLiMGenerationLineEdit::QtSLiMGenerationLineEdit(const QString &contents, QWidget *p_parent) : QLineEdit(contents, p_parent)

QtSLiM/QtSLiMExtras.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <QStatusBar>
3737
#include <QPlainTextEdit>
3838
#include <QLabel>
39+
#include <QStaticText>
3940

4041
#include <cmath>
4142
#include <algorithm>
@@ -68,6 +69,16 @@ QColor QtSLiMColorWithHSV(double p_hue, double p_saturation, double p_value, dou
6869
void RGBForFitness(double fitness, float *colorRed, float *colorGreen, float *colorBlue, double scalingFactor);
6970
void RGBForSelectionCoeff(double selectionCoeff, float *colorRed, float *colorGreen, float *colorBlue, double scalingFactor);
7071

72+
// A color scale widget that shows the color scales for fitness and selection coefficients
73+
class QtSLiMColorScaleWidget : public QWidget
74+
{
75+
public:
76+
QtSLiMColorScaleWidget(QWidget *p_parent);
77+
protected:
78+
virtual void paintEvent(QPaintEvent *p_paintEvent) override;
79+
private:
80+
std::vector<QString> fitnessTicks, effectTicks;
81+
};
7182

7283
// Whether we're in "dark mode" for user interface rendering
7384
bool QtSLiMInDarkMode(void);

QtSLiM/QtSLiMScriptTextEdit.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3154,7 +3154,7 @@ void QtSLiMScriptTextEdit::highlightCurrentLine()
31543154
// check when/how the message is sent
31553155
}
31563156

3157-
qDebug() << "highlightCurrentLine() with position" << selection.cursor.selectionStart();
3157+
//qDebug() << "highlightCurrentLine() with position" << selection.cursor.selectionStart();
31583158

31593159
extra_selections.append(selection);
31603160
}

0 commit comments

Comments
 (0)