mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 08:38:45 +00:00
Enable Meter Widgets overlaid on Video Window for Linux Meter Widgets, with a transparent background over the video window, use little screen estate while providing all the needed information. They were added for WIN32 first but actually work fine on Linux with minor flag adjustments.
409 lines
14 KiB
C++
409 lines
14 KiB
C++
/*
|
|
* Copyright (c) 2015 Vianney Boyer (vlcvboyer@gmail.com)
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
|
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <QtGui>
|
|
#include <QGraphicsPathItem>
|
|
#include "MeterWidget.h"
|
|
#include "ErgFile.h"
|
|
#include "Context.h"
|
|
|
|
MeterWidget::MeterWidget(QString Name, QWidget *parent, QString Source) : QWidget(parent), m_Name(Name), m_container(parent), m_Source(Source)
|
|
{
|
|
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
|
|
|
#ifdef Q_OS_LINUX
|
|
setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint);
|
|
setAttribute(Qt::WA_TranslucentBackground);
|
|
#else
|
|
setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
|
|
setAttribute(Qt::WA_NoSystemBackground);
|
|
setAttribute(Qt::WA_TranslucentBackground);
|
|
setAttribute(Qt::WA_PaintOnScreen);
|
|
#endif
|
|
|
|
setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
|
|
//default settings
|
|
m_MainColor = QColor(255,0,0,180);
|
|
m_ScaleColor = QColor(200,200,200,200);
|
|
m_OutlineColor = QColor(128,128,128,180);
|
|
m_MainFont = QFont(this->font().family(), 64);
|
|
m_AltFont = QFont(this->font().family(), 48);
|
|
m_BackgroundColor = QColor(96, 96, 96, 0);
|
|
m_RangeMin = 0;
|
|
m_RangeMax = 100;
|
|
m_Angle = 180.0;
|
|
m_SubRange = 10;
|
|
boundingRectVisibility = false;
|
|
forceSquareRatio = true;
|
|
}
|
|
|
|
void MeterWidget::SetRelativeSize(float RelativeWidth, float RelativeHeight)
|
|
{
|
|
m_RelativeWidth = RelativeWidth / 100.0;
|
|
m_RelativeHeight = RelativeHeight / 100.0;
|
|
AdjustSizePos();
|
|
}
|
|
|
|
void MeterWidget::SetRelativePos(float RelativePosX, float RelativePosY)
|
|
{
|
|
m_RelativePosX = RelativePosX / 100.0;
|
|
m_RelativePosY = RelativePosY / 100.0;
|
|
AdjustSizePos();
|
|
}
|
|
|
|
void MeterWidget::AdjustSizePos()
|
|
{
|
|
QPoint p;
|
|
if (m_container->windowFlags() & Qt::Window)
|
|
p = m_container->pos();
|
|
else
|
|
p = m_container->mapToGlobal(m_container->pos());
|
|
ComputeSize();
|
|
m_PosX = p.x() + m_container->width() * m_RelativePosX - m_Width/2;
|
|
m_PosY = p.y() + m_container->height() * m_RelativePosY - m_Height/2;
|
|
move(m_PosX, m_PosY);
|
|
adjustSize();
|
|
}
|
|
|
|
void MeterWidget::ComputeSize()
|
|
{
|
|
if (forceSquareRatio)
|
|
{
|
|
m_Width = m_Height = (m_container->width() * m_RelativeWidth + m_container->height() * m_RelativeHeight) / 2;
|
|
}
|
|
else
|
|
{
|
|
m_Width = m_container->width() * m_RelativeWidth;
|
|
m_Height = m_container->height() * m_RelativeHeight;
|
|
}
|
|
}
|
|
|
|
QSize MeterWidget::sizeHint() const
|
|
{
|
|
return QSize(m_Width, m_Height);
|
|
}
|
|
|
|
QSize MeterWidget::minimumSize() const
|
|
{
|
|
return QSize(m_Width, m_Height);
|
|
}
|
|
|
|
void MeterWidget::paintEvent(QPaintEvent* paintevent)
|
|
{
|
|
Q_UNUSED(paintevent);
|
|
if (boundingRectVisibility)
|
|
{
|
|
m_OutlinePen = QPen(boundingRectColor);
|
|
m_OutlinePen.setWidth(2);
|
|
m_OutlinePen.setStyle(Qt::SolidLine);
|
|
|
|
//painter
|
|
QPainter painter(this);
|
|
painter.setRenderHint(QPainter::Antialiasing);
|
|
|
|
painter.setPen(m_OutlinePen);
|
|
painter.drawRect (1, 1, m_Width-2, m_Height-2);
|
|
}
|
|
}
|
|
|
|
void MeterWidget::setColor(QColor mainColor)
|
|
{
|
|
m_MainColor = mainColor;
|
|
}
|
|
|
|
void MeterWidget::setBoundingRectVisibility(bool show, QColor boundingRectColor)
|
|
{
|
|
this->boundingRectVisibility=show;
|
|
this->boundingRectColor = boundingRectColor;
|
|
}
|
|
|
|
TextMeterWidget::TextMeterWidget(QString Name, QWidget *parent, QString Source) : MeterWidget(Name, parent, Source)
|
|
{
|
|
forceSquareRatio = false;
|
|
}
|
|
|
|
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);
|
|
|
|
//painter
|
|
QPainter painter(this);
|
|
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
|
|
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);
|
|
|
|
// Write Value
|
|
painter.setPen(m_OutlinePen);
|
|
painter.setBrush(m_MainBrush);
|
|
painter.drawPath(my_painterPath);
|
|
}
|
|
|
|
CircularIndicatorMeterWidget::CircularIndicatorMeterWidget(QString Name, QWidget *parent, QString Source) : MeterWidget(Name, parent, Source)
|
|
{
|
|
//defaut settings
|
|
IndicatorGradient = QConicalGradient(0, 0, 150);
|
|
IndicatorGradient.setColorAt(0.0, QColor(255,0,0,180));
|
|
IndicatorGradient.setColorAt(0.3, QColor(255,255,0,180));
|
|
IndicatorGradient.setColorAt(0.7, QColor(0,255,0,180));
|
|
IndicatorGradient.setColorAt(1.0, QColor(0,255,0,180));
|
|
}
|
|
|
|
void CircularIndicatorMeterWidget::paintEvent(QPaintEvent* paintevent)
|
|
{
|
|
MeterWidget::paintEvent(paintevent);
|
|
|
|
m_MainBrush = QBrush(m_MainColor);
|
|
m_OutlinePen = QPen(m_OutlineColor);
|
|
m_OutlinePen.setWidth(1);
|
|
m_OutlinePen.setStyle(Qt::SolidLine);
|
|
|
|
//painter
|
|
QPainter painter(this);
|
|
painter.setRenderHint(QPainter::Antialiasing);
|
|
// define scale and location
|
|
painter.translate(m_Width / 2, m_Height / 2);
|
|
painter.scale(m_Width / 200.0, m_Height / 200.0);
|
|
|
|
// Draw circular indicator
|
|
// Define coordinates:
|
|
static const QPoint CP_pt1(0, -70);
|
|
static const QPoint CP_pt2(0, -90);
|
|
static const QRectF CP_extRect(-90,-90,180,180);
|
|
static const QRectF CP_intRect(-70,-70,140,140);
|
|
// rotate painter
|
|
painter.save();
|
|
painter.rotate(-m_Angle/2);
|
|
|
|
double CPAngle = qBound((float) -1.0, (Value-m_RangeMin) / (m_RangeMax-m_RangeMin), (float) 1.0) * m_Angle;
|
|
QPainterPath CPEmptyPath;
|
|
CPEmptyPath.moveTo(CP_pt1);
|
|
CPEmptyPath.arcMoveTo(CP_intRect, 90 - CPAngle);
|
|
QPainterPath CPIndicatorPath;
|
|
CPIndicatorPath.moveTo(CP_pt1);
|
|
CPIndicatorPath.lineTo(CP_pt2);
|
|
CPIndicatorPath.arcTo(CP_extRect, 90, -1 * CPAngle);
|
|
CPIndicatorPath.lineTo(CPEmptyPath.currentPosition());
|
|
CPIndicatorPath.arcTo(CP_intRect, 90 - CPAngle, CPAngle);
|
|
painter.setBrush(IndicatorGradient);
|
|
painter.setPen(Qt::NoPen);
|
|
painter.drawPath(CPIndicatorPath);
|
|
painter.restore();
|
|
}
|
|
|
|
CircularBargraphMeterWidget::CircularBargraphMeterWidget(QString Name, QWidget *parent, QString Source) : MeterWidget(Name, parent, Source)
|
|
{
|
|
}
|
|
|
|
void CircularBargraphMeterWidget::paintEvent(QPaintEvent* paintevent)
|
|
{
|
|
MeterWidget::paintEvent(paintevent);
|
|
|
|
m_MainBrush = QBrush(m_MainColor);
|
|
m_OutlinePen = QPen(m_MainColor);
|
|
m_OutlinePen.setWidth(3);
|
|
m_OutlinePen.setStyle(Qt::SolidLine);
|
|
|
|
//painter
|
|
QPainter painter(this);
|
|
painter.setRenderHint(QPainter::Antialiasing);
|
|
|
|
//draw bargraph
|
|
painter.setPen(m_OutlinePen);
|
|
painter.setBrush(m_MainBrush);
|
|
painter.save();
|
|
painter.translate(m_Width / 2, m_Height / 2);
|
|
painter.rotate((360.0-m_Angle)/2.0);
|
|
for (int i=0; i<=(int)(m_SubRange*qMin(1.0,(double)(Value-m_RangeMin)/(m_RangeMax-m_RangeMin))); i++)
|
|
{
|
|
painter.drawLine (0, m_Height*2/10, 0, m_Height*4/10);
|
|
painter.rotate(m_Angle/m_SubRange);
|
|
}
|
|
painter.restore();
|
|
}
|
|
|
|
NeedleMeterWidget::NeedleMeterWidget(QString Name, QWidget *parent, QString Source) : MeterWidget(Name, parent, Source)
|
|
{
|
|
}
|
|
|
|
void NeedleMeterWidget::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);
|
|
m_ScalePen = QPen(m_ScaleColor);
|
|
m_ScalePen.setWidth(2);
|
|
m_ScalePen.setStyle(Qt::SolidLine);
|
|
|
|
//painter
|
|
QPainter painter(this);
|
|
painter.setRenderHint(QPainter::Antialiasing);
|
|
|
|
//draw background
|
|
painter.setPen(m_OutlinePen);
|
|
painter.setBrush(m_BackgroundBrush);
|
|
painter.drawEllipse (0, 0, m_Width, m_Height);
|
|
|
|
//draw scale
|
|
painter.setPen(m_ScalePen);
|
|
painter.setBrush(Qt::NoBrush);
|
|
painter.save();
|
|
painter.translate(m_Width / 2, m_Height / 2);
|
|
painter.rotate(m_Angle/2.0-90.0);
|
|
painter.drawArc (-m_Width*4/10, -m_Height*4/10, m_Width*8/10, m_Height*8/10, 0, (int) (16.0*m_Angle));
|
|
painter.restore();
|
|
painter.save();
|
|
painter.translate(m_Width / 2, m_Height / 2);
|
|
painter.rotate((360.0-m_Angle)/2.0);
|
|
for (int i=0; i<=m_SubRange; i++)
|
|
{
|
|
painter.drawLine (0, m_Height*3/10, 0, m_Height*4/10);
|
|
painter.rotate(m_Angle/m_SubRange);
|
|
}
|
|
painter.restore();
|
|
|
|
//draw needle
|
|
painter.setPen(m_OutlinePen);
|
|
painter.setBrush(m_MainBrush);
|
|
QPainterPath my_painterPath;
|
|
painter.save();
|
|
painter.translate(m_Width / 2, m_Height / 2);
|
|
painter.rotate(-m_Angle/2+(qBound((float) -1.0, (Value-m_RangeMin)/(m_RangeMax-m_RangeMin), (float) 1.0)*m_Angle));
|
|
my_painterPath.moveTo(-2, 0);
|
|
my_painterPath.lineTo(0, -m_Height*4/10);
|
|
my_painterPath.lineTo(+2, 0);
|
|
my_painterPath.lineTo(-2, 0);
|
|
painter.drawPath(my_painterPath);
|
|
painter.restore();
|
|
}
|
|
|
|
ElevationMeterWidget::ElevationMeterWidget(QString Name, QWidget *parent, QString Source, Context *context) : MeterWidget(Name, parent, Source), context(context)
|
|
{
|
|
forceSquareRatio = false;
|
|
gradientValue = 0.0;
|
|
}
|
|
|
|
void ElevationMeterWidget::paintEvent(QPaintEvent* paintevent)
|
|
{
|
|
// TODO : show Power when not in slope simulation mode
|
|
if (!context || !context->currentErgFile() || context->currentErgFile()->Points.size()<=1)
|
|
return;
|
|
|
|
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);
|
|
|
|
//painter
|
|
QPainter painter(this);
|
|
painter.setRenderHint(QPainter::Antialiasing);
|
|
|
|
// Find min/max
|
|
double minX, minY, maxX, maxY, cyclistX=0.0;
|
|
// (based on ErgFilePlot.cpp)
|
|
minX=maxX=context->currentErgFile()->Points[0].x; // meters
|
|
minY=maxY=context->currentErgFile()->Points[0].y; // meters or altitude???
|
|
foreach(ErgFilePoint x, context->currentErgFile()->Points)
|
|
{
|
|
if (x.y > maxY) maxY = x.y;
|
|
if (x.x > maxX) maxX = x.x;
|
|
if (x.y < minY) minY = x.y;
|
|
if (x.x < minX) minX = x.x;
|
|
}
|
|
// check if slope shown will not be too inconsistent (based on widget's width/height ratio)
|
|
// we accept 20 times i.e. 5% gradient will be shown as 45°
|
|
if ( m_Width!=0 && (maxY-minY) / 0.05 < (double)m_Height * 0.80 * (maxX-minX) / (double)m_Width)
|
|
maxY = minY + (double)m_Height * 0.80 * (maxX-minX) / (double)m_Width * 0.05;
|
|
double bubbleSize = (double)m_Height*0.010f;
|
|
minY -= (maxY-minY) * 0.20f; // add 20% as bottom headroom (slope gradient will be shown there in a bubble)
|
|
|
|
// this->Value should hold the current distance in meters.
|
|
cyclistX = (this->Value * 1000.0 - minX) * (double)m_Width / (maxX-minX);
|
|
|
|
//Get point to create the polygon
|
|
QPolygon polygon;
|
|
polygon << QPoint(0.0, (double)m_Height);
|
|
double x, y, pt=0;
|
|
double nextX = 1;
|
|
for( pt=0; pt < context->currentErgFile()->Points.size(); pt++)
|
|
{
|
|
for ( ; x < nextX && pt < context->currentErgFile()->Points.size(); pt++)
|
|
{
|
|
x = (context->currentErgFile()->Points[pt].x - minX) * (double)m_Width / (maxX-minX);
|
|
y = (context->currentErgFile()->Points[pt].y - minY) * (double)m_Height / (maxY-minY);
|
|
}
|
|
// Add points to polygon only once every time the x coordinate integer part changes.
|
|
polygon << QPoint(x, (double)m_Height - y);
|
|
nextX = floor(x) + 1.0;
|
|
}
|
|
polygon << QPoint((double) m_Width, (double)m_Height);
|
|
polygon << QPoint(fmin((double) m_Width,cyclistX+bubbleSize), (double)m_Height);
|
|
polygon << QPoint(cyclistX, (double)m_Height-bubbleSize);
|
|
polygon << QPoint(fmax(0.0, cyclistX-bubbleSize), (double)m_Height);
|
|
|
|
painter.setPen(m_OutlinePen);
|
|
painter.setBrush(m_BackgroundBrush);
|
|
painter.drawPolygon(polygon);
|
|
|
|
m_OutlinePen = QPen(m_MainColor);
|
|
m_OutlinePen.setWidth(1);
|
|
m_OutlinePen.setStyle(Qt::SolidLine);
|
|
painter.setPen(m_OutlinePen);
|
|
painter.drawLine(cyclistX, 0.0, cyclistX, (double)m_Height-bubbleSize);
|
|
|
|
//Cosmetic enhancment: Display grade as #.#%
|
|
std::string sGrad;
|
|
QString s_grad ="";
|
|
s_grad = ((-1.0 < this->gradientValue && this->gradientValue < 0.0)?QString("-"):QString("")) + QString::number((int) this->gradientValue);
|
|
s_grad += QString(".") + QString::number(abs((int)(this->gradientValue * 10.0) % 10)) + QString("%");
|
|
|
|
// Display gradient text to the right of the line until the middle, then display to the left of the line
|
|
if (cyclistX < m_Width*0.5) {
|
|
painter.drawText((double)cyclistX+5, ((double)m_Height * 0.95), s_grad);
|
|
} else {
|
|
painter.drawText((double)cyclistX-45, ((double)m_Height * 0.95), s_grad);
|
|
}
|
|
}
|