@@ -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
282414QtSLiMGenerationLineEdit::QtSLiMGenerationLineEdit (const QString &contents, QWidget *p_parent) : QLineEdit(contents, p_parent)
0 commit comments