Allow Text meter widgets to be of same size and well aligned (#3506)

The y scale is computed from the font instead of only from the current content, to avoid having different scales between different Text widgets of the same size. For example, "kph" has a high k and low p, resulting in a text widget with bigger bounding box, and thus smaller scale than a text widget with "watts". Options are added for alignment and for text width, to help align the different entries. The formatting of metrics with the integer part as Text and fraction as AltText is more systematic and it is possible to add an AltTextSuffix, to specify the units when they are not provided in VideoWindow. The visibility of the BoundingRect and Background car be controlled.
This commit is contained in:
Michel Dagenais
2020-06-24 10:58:08 -04:00
committed by GitHub
parent 028e822aa4
commit a27065673c
5 changed files with 146 additions and 61 deletions

View File

@@ -54,6 +54,7 @@ MeterWidget::MeterWidget(QString Name, QWidget *parent, QString Source) : QWidge
m_SubRange = 10;
m_Zoom = 16;
boundingRectVisibility = false;
backgroundVisibility = false;
forceSquareRatio = true;
}
@@ -117,19 +118,29 @@ QSize MeterWidget::minimumSize() const
void MeterWidget::paintEvent(QPaintEvent* paintevent)
{
Q_UNUSED(paintevent);
if(!boundingRectVisibility && !backgroundVisibility) return;
QPainter painter(this);
painter.setClipRegion(videoContainerRegion);
painter.setRenderHint(QPainter::Antialiasing);
int radius = qMin(m_Width, m_Height) * 0.1;
if(backgroundVisibility) painter.setBrush(QBrush(m_BackgroundColor));
else painter.setBrush(Qt::NoBrush);
if (boundingRectVisibility)
{
m_OutlinePen = QPen(boundingRectColor);
m_OutlinePen = QPen(m_BoundingRectColor);
m_OutlinePen.setWidth(2);
m_OutlinePen.setStyle(Qt::SolidLine);
//painter
QPainter painter(this);
painter.setClipRegion(videoContainerRegion);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(m_OutlinePen);
painter.drawRect (1, 1, m_Width-2, m_Height-2);
painter.drawRoundedRect (1, 1, m_Width-2, m_Height-2, radius, radius);
}
else
{
painter.setPen(Qt::NoPen);
painter.drawRoundedRect (0, 0, m_Width, m_Height, radius, radius);
}
}
@@ -141,7 +152,7 @@ void MeterWidget::setColor(QColor mainColor)
void MeterWidget::setBoundingRectVisibility(bool show, QColor boundingRectColor)
{
this->boundingRectVisibility=show;
this->boundingRectColor = boundingRectColor;
this->m_BoundingRectColor = boundingRectColor;
}
TextMeterWidget::TextMeterWidget(QString Name, QWidget *parent, QString Source) : MeterWidget(Name, parent, Source)
@@ -154,7 +165,6 @@ void TextMeterWidget::paintEvent(QPaintEvent* paintevent)
MeterWidget::paintEvent(paintevent);
m_MainBrush = QBrush(m_MainColor);
m_BackgroundBrush = QBrush(m_BackgroundColor);
m_OutlinePen = QPen(m_OutlineColor);
m_OutlinePen.setWidth(1);
m_OutlinePen.setStyle(Qt::SolidLine);
@@ -164,21 +174,32 @@ void TextMeterWidget::paintEvent(QPaintEvent* paintevent)
painter.setClipRegion(videoContainerRegion);
painter.setRenderHint(QPainter::Antialiasing);
//draw background
painter.setPen(Qt::NoPen);
painter.setBrush(m_BackgroundBrush);
if (Text!=QString(""))
painter.drawRect (0, 0, m_Width, m_Height);
QPainterPath my_painterPath;
my_painterPath.addText(QPointF(0,0),m_MainFont,Text);
my_painterPath.addText(QPointF(QFontMetrics(m_MainFont).width(Text), 0),m_AltFont,AltText);
QRectF ValueBoundingRct = my_painterPath.boundingRect();
// define scale
// We use leading whitespace for alignment which boundingRect() does not count
ValueBoundingRct.setLeft(qMin(0.0, ValueBoundingRct.left()));
// The scale should not change with the string content, we use Font ascent and descent
ValueBoundingRct.setTop(qMin(ValueBoundingRct.top(), qreal(qMin(-QFontMetrics(m_MainFont).ascent(),
-QFontMetrics(m_AltFont).ascent()))));
ValueBoundingRct.setBottom(qMax(ValueBoundingRct.bottom(), qreal(qMax(QFontMetrics(m_MainFont).descent(),
QFontMetrics(m_AltFont).descent()))));
// scale to fit the available space
float fontscale = qMin(m_Width / ValueBoundingRct.width(), m_Height / ValueBoundingRct.height());
painter.scale(fontscale, fontscale);
painter.translate(-ValueBoundingRct.x()+(m_Width/fontscale - ValueBoundingRct.width())/2, -ValueBoundingRct.y()+(m_Height/fontscale - ValueBoundingRct.height())/2);
float translationX = -ValueBoundingRct.x(); // AlignLeft
if(alignment == Qt::AlignHCenter)
translationX += (m_Width/fontscale - ValueBoundingRct.width())/2;
else if(alignment == Qt::AlignRight)
translationX += (m_Width/fontscale - ValueBoundingRct.width());
painter.translate(translationX, -ValueBoundingRct.y()+(m_Height/fontscale - ValueBoundingRct.height())/2);
// Write Value
painter.setPen(m_OutlinePen);

View File

@@ -51,7 +51,9 @@ class MeterWidget : public QWidget
void setBoundingRectVisibility(bool show, QColor boundingRectColor = QColor(255,0,0,255));
float Value, ValueMin, ValueMax;
QString Text, AltText;
QString Text, AltText, AltTextSuffix;
Qt::Alignment alignment;
int textWidth;
protected:
QString m_Name;
@@ -72,6 +74,7 @@ class MeterWidget : public QWidget
QColor m_ScaleColor;
QColor m_OutlineColor;
QColor m_BackgroundColor;
QColor m_BoundingRectColor;
QFont m_MainFont;
QFont m_AltFont;
@@ -81,8 +84,8 @@ class MeterWidget : public QWidget
QPen m_OutlinePen;
QPen m_ScalePen;
bool backgroundVisibility;
bool boundingRectVisibility;
QColor boundingRectColor;
friend class VideoLayoutParser;
};

View File

@@ -37,23 +37,23 @@ VideoLayoutParser::VideoLayoutParser (QList<MeterWidget*>* metersWidget, QList<Q
void VideoLayoutParser::SetDefaultValues()
{
if (meterWidget)
{
meterWidget->m_RelativeWidth = 20.0;
meterWidget->m_RelativeHeight = 20.0;
meterWidget->m_RelativePosX = 50.0;
meterWidget->m_RelativePosY = 80.0;
meterWidget->m_RangeMin = 0.0;
meterWidget->m_RangeMax = 80.0;
meterWidget->m_MainColor = QColor(255,0,0,200);
meterWidget->m_ScaleColor = QColor(255,255,255,200);
meterWidget->m_OutlineColor = QColor(100,100,100,200);
meterWidget->m_BackgroundColor = QColor(100,100,100,0);
meterWidget->m_MainFont = QFont(meterWidget->font().family(), 64);
meterWidget->m_AltFont = QFont(meterWidget->font().family(), 48);
meterWidget->m_VideoContainer = VideoContainer;
}
meterWidget->m_RelativeWidth = 20.0;
meterWidget->m_RelativeHeight = 20.0;
meterWidget->m_RelativePosX = 50.0;
meterWidget->m_RelativePosY = 80.0;
meterWidget->m_RangeMin = 0.0;
meterWidget->m_RangeMax = 80.0;
meterWidget->m_MainColor = QColor(255,0,0,200);
meterWidget->m_ScaleColor = QColor(255,255,255,200);
meterWidget->m_OutlineColor = QColor(100,100,100,200);
meterWidget->m_BackgroundColor = QColor(100,100,100,0);
meterWidget->m_BoundingRectColor = QColor(150,150,150,100);
meterWidget->m_MainFont = QFont(meterWidget->font().family(), 64);
meterWidget->m_AltFont = QFont(meterWidget->font().family(), 48);
meterWidget->m_VideoContainer = VideoContainer;
meterWidget->Text = "";
meterWidget->AltText = "";
meterWidget->AltTextSuffix = "";
}
QColor GetColorFromFields(const QXmlAttributes& qAttributes)
@@ -77,6 +77,15 @@ bool VideoLayoutParser::startElement( const QString&, const QString&,
const QString& qName,
const QXmlAttributes& qAttributes)
{
QString source;
QString meterName;
QString meterType;
QString container; // will be "Video" when not defined otherwise another meter name (allows positioning of one meter inside another one)
int textWidth;
Qt::Alignment alignment;
bool boundingRectVisibility;
bool backgroundVisibility;
if(skipLayout) return true;
buffer.clear();
@@ -114,6 +123,32 @@ bool VideoLayoutParser::startElement( const QString&, const QString&,
else
meterType = QString("Text");
i = qAttributes.index("alignment");
if(i >= 0)
if(qAttributes.value(i) == QString("AlignLeft")) alignment = Qt::AlignLeft;
else if(qAttributes.value(i) == QString("AlignRight")) alignment = Qt::AlignRight;
else alignment = Qt::AlignHCenter;
else
alignment = Qt::AlignHCenter;
i = qAttributes.index("textWidth");
if(i >= 0)
textWidth = qAttributes.value(i).toInt();
else
textWidth = 0;
i = qAttributes.index("BoundingRect");
if(i >= 0 && qAttributes.value(i) == QString("true"))
boundingRectVisibility = true;
else
boundingRectVisibility = false;
i = qAttributes.index("Background");
if(i >= 0 && qAttributes.value(i) == QString("true"))
backgroundVisibility = true;
else
backgroundVisibility = false;
//TODO: allow creation of meter when container will be created later
QWidget* containerWidget = NULL;
if (container == QString("Video"))
@@ -159,7 +194,14 @@ bool VideoLayoutParser::startElement( const QString&, const QString&,
qDebug() << QObject::tr("Error creating meter");
}
SetDefaultValues();
if (meterWidget)
{
SetDefaultValues();
meterWidget->textWidth = textWidth;
meterWidget->alignment = alignment;
meterWidget->boundingRectVisibility = boundingRectVisibility;
meterWidget->backgroundVisibility = backgroundVisibility;
}
}
else if ((qName == "MainColor") && meterWidget)
meterWidget->m_MainColor = GetColorFromFields(qAttributes);
@@ -169,6 +211,8 @@ bool VideoLayoutParser::startElement( const QString&, const QString&,
meterWidget->m_ScaleColor = GetColorFromFields(qAttributes);
else if ((qName == "BackgroundColor") && meterWidget)
meterWidget->m_BackgroundColor = GetColorFromFields(qAttributes);
else if ((qName == "BoundingRectColor") && meterWidget)
meterWidget->m_BoundingRectColor = GetColorFromFields(qAttributes);
else if ((qName == "RelativeSize") && meterWidget)
{
@@ -204,6 +248,8 @@ bool VideoLayoutParser::startElement( const QString&, const QString&,
if ((i = qAttributes.index("Size")) >= 0)
FontSize = qAttributes.value(i).toInt();
meterWidget->m_MainFont = QFont(FontName, FontSize);
// Set fixed width, otherwise the meter digits keep moving sideways from a thin "1" to a fat "5"
meterWidget->m_MainFont.setFixedPitch(true);
}
else if ((qName == "AltFont") && meterWidget)
{
@@ -215,6 +261,11 @@ bool VideoLayoutParser::startElement( const QString&, const QString&,
if ((i = qAttributes.index("Size")) >= 0)
FontSize = qAttributes.value(i).toInt();
meterWidget->m_AltFont = QFont(FontName, FontSize);
meterWidget->m_AltFont.setFixedPitch(true);
}
else if(qName != "layouts" && qName != "Text" && qName != "AltText" && qName != "Angle" && qName != "SubRange")
{
qDebug() << QObject::tr("Unknown start element ") << qName;
}
return true;
@@ -241,6 +292,8 @@ bool VideoLayoutParser::endElement( const QString&, const QString&, const QStrin
meterWidget->m_Zoom = buffer.toInt();
else if (qName == "Text")
meterWidget->Text = QString(buffer);
else if (qName == "AltText")
meterWidget->AltTextSuffix = QString(buffer);
else if (qName == "meter")
{

View File

@@ -46,18 +46,11 @@ private:
QList<QString>* layoutNames;
QWidget* VideoContainer;
MeterWidget* meterWidget;
QString buffer;
int nonameindex;
int layoutPosition;
bool skipLayout;
QString source;
QString meterName;
QString meterType;
QString container; // will be "Video" when not defined otherwise another meter name (allows positioning of one meter inside another one)
MeterWidget* meterWidget;
};
#endif // _VideoLayoutParser_h

View File

@@ -326,13 +326,13 @@ void VideoWindow::telemetryUpdate(RealtimeData rtd)
std::string smyStr1 = myQstr1.toStdString();
if (p_meterWidget->Source() == QString("None"))
{
//Nothing
p_meterWidget->AltText = p_meterWidget->AltTextSuffix;
}
else if (p_meterWidget->Source() == QString("Speed"))
{
p_meterWidget->Value = rtd.getSpeed() * (metric ? 1.0 : MILES_PER_KM);
p_meterWidget->Text = QString::number((int)p_meterWidget->Value);
p_meterWidget->AltText = QString(".") +QString::number((int)(p_meterWidget->Value * 10.0) - (((int) p_meterWidget->Value) * 10)) + (metric ? tr(" kph") : tr(" mph"));
p_meterWidget->Text = QString::number((int)p_meterWidget->Value).rightJustified(p_meterWidget->textWidth);
p_meterWidget->AltText = QString(".") +QString::number((int)(p_meterWidget->Value * 10.0) - (((int) p_meterWidget->Value) * 10)) + (metric ? tr(" kph") : tr(" mph")) + p_meterWidget->AltTextSuffix;
}
else if (p_meterWidget->Source() == QString("Elevation"))
{
@@ -368,58 +368,73 @@ void VideoWindow::telemetryUpdate(RealtimeData rtd)
else if (p_meterWidget->Source() == QString("Cadence"))
{
p_meterWidget->Value = rtd.getCadence();
p_meterWidget->Text = QString::number((int)p_meterWidget->Value);
p_meterWidget->Text = QString::number((int)p_meterWidget->Value).rightJustified(p_meterWidget->textWidth);
p_meterWidget->AltText = p_meterWidget->AltTextSuffix;
}
else if (p_meterWidget->Source() == QString("Watt"))
{
p_meterWidget->Value = rtd.getWatts();
p_meterWidget->Text = QString::number((int)p_meterWidget->Value);
p_meterWidget->Text = QString::number((int)p_meterWidget->Value).rightJustified(p_meterWidget->textWidth);
p_meterWidget->AltText = p_meterWidget->AltTextSuffix;
}
else if (p_meterWidget->Source() == QString("Altitude"))
{
p_meterWidget->Value = rtd.getAltitude() * (metric ? 1.0 : FEET_PER_METER);
p_meterWidget->Text = QString::number((int)p_meterWidget->Value).rightJustified(p_meterWidget->textWidth);
p_meterWidget->AltText = (metric ? tr(" m") : tr(" feet")) + p_meterWidget->AltTextSuffix;
}
else if (p_meterWidget->Source() == QString("HRM"))
{
p_meterWidget->Value = rtd.getHr();
p_meterWidget->Text = QString::number((int)p_meterWidget->Value);
p_meterWidget->Text = QString::number((int)p_meterWidget->Value).rightJustified(p_meterWidget->textWidth);
p_meterWidget->AltText = p_meterWidget->AltTextSuffix;
}
else if (p_meterWidget->Source() == QString("Load"))
{
if (rtd.mode == ERG || rtd.mode == MRC) {
p_meterWidget->Value = rtd.getLoad();
p_meterWidget->Text = QString("%1").arg(round(p_meterWidget->Value));
p_meterWidget->AltText = tr("w");
p_meterWidget->Text = QString("%1").arg(round(p_meterWidget->Value)).rightJustified(p_meterWidget->textWidth);
p_meterWidget->AltText = tr("w") + p_meterWidget->AltTextSuffix;
} else {
p_meterWidget->Value = rtd.getSlope();
p_meterWidget->Text = QString("%1").arg(p_meterWidget->Value, 0, 'f', 1);
p_meterWidget->AltText = tr("%");
p_meterWidget->Text = QString::number((int)p_meterWidget->Value).rightJustified(p_meterWidget->textWidth);
p_meterWidget->AltText = QString(".") + QString::number(abs((int)(p_meterWidget->Value * 10.0) - (((int) p_meterWidget->Value) * 10))) + tr("%") + p_meterWidget->AltTextSuffix;
}
}
else if (p_meterWidget->Source() == QString("Distance"))
{
p_meterWidget->Value = rtd.getDistance() * (metric ? 1.0 : MILES_PER_KM);
p_meterWidget->Text = QString::number((int) p_meterWidget->Value);
p_meterWidget->AltText = QString(".") +QString::number((int)(p_meterWidget->Value * 10.0) - (((int) p_meterWidget->Value) * 10)) + (metric ? tr(" km") : tr(" mi"));
p_meterWidget->Text = QString::number((int) p_meterWidget->Value).rightJustified(p_meterWidget->textWidth);
p_meterWidget->AltText = QString(".") +QString::number((int)(p_meterWidget->Value * 10.0) - (((int) p_meterWidget->Value) * 10)) + (metric ? tr(" km") : tr(" mi")) + p_meterWidget->AltTextSuffix;
}
else if (p_meterWidget->Source() == QString("Time"))
{
p_meterWidget->Value = round(rtd.value(RealtimeData::Time)/100.0)/10.0;
p_meterWidget->Text = time_to_string(p_meterWidget->Value);
p_meterWidget->Text = time_to_string(trunc(p_meterWidget->Value)).rightJustified(p_meterWidget->textWidth);
p_meterWidget->AltText = QString(".") + QString::number((int)(p_meterWidget->Value * 10.0) - (((int) p_meterWidget->Value) * 10)) + p_meterWidget->AltTextSuffix;
}
else if (p_meterWidget->Source() == QString("LapTime"))
{
p_meterWidget->Value = round(rtd.value(RealtimeData::LapTime)/100.0)/10.0;
p_meterWidget->Text = time_to_string(p_meterWidget->Value);
p_meterWidget->Text = time_to_string(trunc(p_meterWidget->Value)).rightJustified(p_meterWidget->textWidth);
p_meterWidget->AltText = QString(".") + QString::number((int)(p_meterWidget->Value * 10.0) - (((int) p_meterWidget->Value) * 10)) + p_meterWidget->AltTextSuffix;
}
else if (p_meterWidget->Source() == QString("LapTimeRemaining"))
{
p_meterWidget->Value = round(rtd.value(RealtimeData::LapTimeRemaining)/100.0)/10.0;
p_meterWidget->Text = time_to_string(p_meterWidget->Value);
p_meterWidget->Text = time_to_string(trunc(p_meterWidget->Value)).rightJustified(p_meterWidget->textWidth);
p_meterWidget->AltText = QString(".") + QString::number((int)(p_meterWidget->Value * 10.0) - (((int) p_meterWidget->Value) * 10)) + p_meterWidget->AltTextSuffix;
}
else if (p_meterWidget->Source() == QString("ErgTimeRemaining"))
{
p_meterWidget->Value = round(rtd.value(RealtimeData::ErgTimeRemaining)/100.0)/10.0;
p_meterWidget->Text = time_to_string(p_meterWidget->Value);
p_meterWidget->Text = time_to_string(trunc(p_meterWidget->Value)).rightJustified(p_meterWidget->textWidth);
p_meterWidget->AltText = QString(".") + QString::number((int)(p_meterWidget->Value * 10.0) - (((int) p_meterWidget->Value) * 10)) + p_meterWidget->AltTextSuffix;
}
else if (p_meterWidget->Source() == QString("TrainerStatus"))
{
p_meterWidget->AltText = p_meterWidget->AltTextSuffix;
if (!rtd.getTrainerStatusAvailable())
{ // we don't have status from trainer thus we cannot indicate anything on screen
p_meterWidget->Text = tr("");