I'm studying WidgetMarqueeLabel class:
#include "WidgetMarqueeLabel.h"
#include <QPainter>
#include <QWidget>
WidgetMarqueeLabel::WidgetMarqueeLabel(QWidget *parent)//*parent)
{
px = 0;
py = 10;
speed = 1;
direction = RightToLeft;
connect(&timer3, SIGNAL(timeout()), this, SLOT(refreshLabel()));
timer3.start(10);
}
void WidgetMarqueeLabel::refreshLabel()
{
repaint();
}
WidgetMarqueeLabel::~WidgetMarqueeLabel()
{}
void WidgetMarqueeLabel::show()
{
QLabel::show();
}
void WidgetMarqueeLabel::setAlignment(Qt::Alignment al)
{
m_align = al;
updateCoordinates();
QLabel::setAlignment(al);
}
void WidgetMarqueeLabel::paintEvent(QPaintEvent *evt)
{
QPainter p(this);
if(direction==RightToLeft)
{
px -= speed;
if(px <= (-textLength))
px = width();
}
else
{
px += speed;
if(px >= width())
px = - textLength;
}
p.drawText(px, py+fontPointSize, text());
p.translate(px,0);
}
void WidgetMarqueeLabel::resizeEvent(QResizeEvent *evt)
{
updateCoordinates();
QLabel::resizeEvent(evt);
}
void WidgetMarqueeLabel::updateCoordinates()
{
switch(m_align)
{
case Qt::AlignTop:
py = 10;
break;
case Qt::AlignBottom:
py = height()-10;
break;
case Qt::AlignVCenter:
py = height()/2;
break;
}
fontPointSize = font().pointSize()/2;
textLength = fontMetrics().width(text());
}
void WidgetMarqueeLabel::setSpeed(int s)
{
speed = s;
}
int WidgetMarqueeLabel::getSpeed()
{
return speed;
}
void WidgetMarqueeLabel::setDirection(int d)
{
direction = d;
if (direction==RightToLeft)
px = width() - textLength;
else
px = 0;
refreshLabel();
}
void WidgetMarqueeLabel::close()
{
QLabel::close();
}
I was wondering if it was possible to make the text reappear before the text that reaches the end of the last letter on the right. I want something like this: for example (white space are 25):
WidgetMarqueeLabel
tMarqueeLabel Widge
eLabel WidgetMarque
el WidgetMarqueeLa
WidgetMarqueeLabel
WidgetMarqueeLabel
WidgetMarqueeLabel
WidgetMarqueeLabel
Is this possible?
For this purpose, I once wrote a class.
Example screenshot showing the text "This is an example text. It will be scrolled horizontally.". Note the alpha blending at both sides.
The code:
scrolltext.h:
#ifndef SCROLLTEXT_H
#define SCROLLTEXT_H
#include <QWidget>
#include <QStaticText>
#include <QTimer>
class ScrollText : public QWidget
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText)
Q_PROPERTY(QString separator READ separator WRITE setSeparator)
public:
explicit ScrollText(QWidget *parent = 0);
public slots:
QString text() const;
void setText(QString text);
QString separator() const;
void setSeparator(QString separator);
protected:
virtual void paintEvent(QPaintEvent *);
virtual void resizeEvent(QResizeEvent *);
private:
void updateText();
QString _text;
QString _separator;
QStaticText staticText;
int singleTextWidth;
QSize wholeTextSize;
int leftMargin;
bool scrollEnabled;
int scrollPos;
QImage alphaChannel;
QImage buffer;
QTimer timer;
private slots:
virtual void timer_timeout();
};
#endif // SCROLLTEXT_H
scrolltext.cpp:
#include "scrolltext.h"
#include <QPainter>
ScrollText::ScrollText(QWidget *parent) :
QWidget(parent), scrollPos(0)
{
staticText.setTextFormat(Qt::PlainText);
setFixedHeight(fontMetrics().height());
leftMargin = height() / 3;
setSeparator(" --- ");
connect(&timer, SIGNAL(timeout()), this, SLOT(timer_timeout()));
timer.setInterval(50);
}
QString ScrollText::text() const
{
return _text;
}
void ScrollText::setText(QString text)
{
_text = text;
updateText();
update();
}
QString ScrollText::separator() const
{
return _separator;
}
void ScrollText::setSeparator(QString separator)
{
_separator = separator;
updateText();
update();
}
void ScrollText::updateText()
{
timer.stop();
singleTextWidth = fontMetrics().width(_text);
scrollEnabled = (singleTextWidth > width() - leftMargin);
if(scrollEnabled)
{
scrollPos = -64;
staticText.setText(_text + _separator);
timer.start();
}
else
staticText.setText(_text);
staticText.prepare(QTransform(), font());
wholeTextSize = QSize(fontMetrics().width(staticText.text()), fontMetrics().height());
}
void ScrollText::paintEvent(QPaintEvent*)
{
QPainter p(this);
if(scrollEnabled)
{
buffer.fill(qRgba(0, 0, 0, 0));
QPainter pb(&buffer);
pb.setPen(p.pen());
pb.setFont(p.font());
int x = qMin(-scrollPos, 0) + leftMargin;
while(x < width())
{
pb.drawStaticText(QPointF(x, (height() - wholeTextSize.height()) / 2) + QPoint(2, 2), staticText);
x += wholeTextSize.width();
}
//Apply Alpha Channel
pb.setCompositionMode(QPainter::CompositionMode_DestinationIn);
pb.setClipRect(width() - 15, 0, 15, height());
pb.drawImage(0, 0, alphaChannel);
pb.setClipRect(0, 0, 15, height());
//initial situation: don't apply alpha channel in the left half of the image at all; apply it more and more until scrollPos gets positive
if(scrollPos < 0)
pb.setOpacity((qreal)(qMax(-8, scrollPos) + 8) / 8.0);
pb.drawImage(0, 0, alphaChannel);
//pb.end();
p.drawImage(0, 0, buffer);
}
else
{
p.drawStaticText(QPointF(leftMargin, (height() - wholeTextSize.height()) / 2), staticText);
}
}
void ScrollText::resizeEvent(QResizeEvent*)
{
//When the widget is resized, we need to update the alpha channel.
alphaChannel = QImage(size(), QImage::Format_ARGB32_Premultiplied);
buffer = QImage(size(), QImage::Format_ARGB32_Premultiplied);
//Create Alpha Channel:
if(width() > 64)
{
//create first scanline
QRgb* scanline1 = (QRgb*)alphaChannel.scanLine(0);
for(int x = 1; x < 16; ++x)
scanline1[x - 1] = scanline1[width() - x] = qRgba(0, 0, 0, x << 4);
for(int x = 15; x < width() - 15; ++x)
scanline1[x] = qRgb(0, 0, 0);
//copy scanline to the other ones
for(int y = 1; y < height(); ++y)
memcpy(alphaChannel.scanLine(y), (uchar*)scanline1, width() * 4);
}
else
alphaChannel.fill(qRgb(0, 0, 0));
//Update scrolling state
bool newScrollEnabled = (singleTextWidth > width() - leftMargin);
if(newScrollEnabled != scrollEnabled)
updateText();
}
void ScrollText::timer_timeout()
{
scrollPos = (scrollPos + 2)
% wholeTextSize.width();
update();
}
Really easy. Simply repaint the text displaced by the width of the control:
void WidgetMarqueeLabel::paintEvent(QPaintEvent *evt)
{
QPainter p(this);
if(direction==RightToLeft)
{
px -= speed;
if(px <= (-textLength))
px = width();
}
else
{
px += speed;
if(px >= width())
px = - textLength;
}
p.drawText(px, py+fontPointSize, text());
__p.drawText(px-width(), py+fontPointSize, text());
p.drawText(px+width(), py+fontPointSize, text());
p.translate(px,0);
}
Something like the following should work. The padding is hard-coded at 25, which is what is sounded like you wanted. If you wanted the label to always be a certain size, you could use something like QString::leftJustified.
class MarqueeLabel : public QLabel {
public:
explicit MarqueeLabel(const QString &text) : QLabel(text), pos_(0) {
QString pad(25, ' ');
actual_text_ = text + pad;
startTimer(100);
}
protected:
void timerEvent(QTimerEvent *) {
pos_ = ++pos_ % actual_text_.length();
setText(actual_text_.mid(pos_).append(actual_text_.left(pos_)));
}
private:
QString actual_text_;
int pos_;
};
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MarqueeLabel lbl("WidgetMarqueeLabel");
lbl.show();
return a.exec();
}
Related
New Qt user here (also not an avid C++ programmer, so excuse me if my terminology isn't spot on). I am learning how to develop my own digital instrument cluster. I have managed to create my own graphical objects (horizontal bar graph) using QQuickPaintedItem. I have also managed to connect said objects to my own custom designed ECU over a network.
I want to add an "onClicked" event to the horizontal bar graphs, so that a new window (QWidget?) is displayed when the user clicks/presses an object. This new window would show the configuration parameters for the respective device that the horizontal bar graph is tied to. My intent is to design a generic QML window specific to pressure, temperature, etc. devices. For example, pressing the "Oil Temp", "Intake Air Temp", or "Coolant Temp" objects would take you to the generic QML window for temperature devices, and the parameters for whatever device was pressed would be displayed.
A couple questions that I have are as follows:
How do I add an "onClicked" event to my horizontal bar graph? I am assuming that I need to use MouseEvents, however, exact implementation of mouse events in my case is escaping me. I am assuming that once a click event is registered, that I can just send a signal to open my new window. Examples would be extremely helpful.
Should I be using a QWidget to open the new window, or is there a better alternative? I want the new window to completely replace the main window. When the new window is open, I also do not want the main window sending/receiving network requests in the background. Any examples you could point me to?
horizontalbargraph.cpp
#include "horizontalBarGraph.h"
HorizontalBarGraph::HorizontalBarGraph(QQuickItem *parent)
:QQuickPaintedItem(parent),
_horizontalBarGraphWidth(266),
_minValue(0),
_maxValue(100),
_actualValue(50),
_sensorName("Sensor Name"),
_units("Units"),
_llEnabled(true),
_llValue(20),
_lEnabled(true),
_lValue(40),
_hhEnabled(true),
_hhValue(80),
_hEnabled(true),
_hValue(60)
{
}
void HorizontalBarGraph::paint(QPainter *painter)
{
QRectF rect = this->boundingRect();
painter->setRenderHint(QPainter::Antialiasing);
QPen pen = painter->pen();
pen.setCapStyle(Qt::FlatCap);
QFont bottomFont("Arial", 14, QFont::Bold);
QFont topFont("Arial", 16, QFont::Bold);
QColor yellow(245, 225, 27);
QColor red(226, 32, 40);
QColor blue(71, 92, 167);
QColor gray192(192, 192, 192);
QColor gray212(212, 212, 212);
QColor gray224(224, 224, 224);
//minValue, actualValue, maxValue
painter->save();
painter->setFont(bottomFont);
pen.setColor(gray224);
painter->setPen(pen);
painter->drawText(rect.adjusted(0, _horizontalBarGraphHeight - _yBarChart2, _widthValue - _horizontalBarGraphWidth, 0), Qt::AlignHCenter | Qt::AlignTop, QString::number((_minValue), 'f', 0)); //Draws minValue
painter->drawText(rect.adjusted(_horizontalBarGraphWidth - _widthValue, _horizontalBarGraphHeight - _yBarChart2, 0, 0), Qt::AlignHCenter | Qt::AlignTop, QString::number((_maxValue), 'f', 0)); //Draws maxValue
painter->drawText(rect.adjusted(0, _horizontalBarGraphHeight - _yBarChart2, 0, 0), Qt::AlignHCenter | Qt::AlignTop, QString::number((_actualValue), 'f', 1)); //Draws actualValue
painter->restore();
//Bar chart background
painter->save();
painter->fillRect(rect.adjusted(_xBarChart, _yBarChart1, -_xBarChart, -_yBarChart2), gray224);
painter->restore();
//Lo
painter->save();
if(_lEnabled)
{
_scaleOutput = scale(_lValue, _minValue, _maxValue, _xBarChart, _horizontalBarGraphWidth - _xBarChart);
if(_actualValue <= _lValue)
{
painter->fillRect(rect.adjusted(_xBarChart, _yBarChart1, _scaleOutput - _horizontalBarGraphWidth, -_yBarChart2), yellow);
} else
{
painter->fillRect(rect.adjusted(_xBarChart, _yBarChart1, _scaleOutput - _horizontalBarGraphWidth, -_yBarChart2), gray212);
}
}
painter->restore();
//LoLo
painter->save();
if(_llEnabled)
{
_scaleOutput = scale(_llValue, _minValue, _maxValue, _xBarChart, _horizontalBarGraphWidth - _xBarChart);
if(_actualValue <= _llValue)
{
painter->fillRect(rect.adjusted(_xBarChart, _yBarChart1, _scaleOutput - _horizontalBarGraphWidth, -_yBarChart2), red);
} else
{
painter->fillRect(rect.adjusted(_xBarChart, _yBarChart1, _scaleOutput - _horizontalBarGraphWidth, -_yBarChart2), gray192);
}
}
painter->restore();
//Hi
painter->save();
if(_hEnabled)
{
_scaleOutput = scale(_hValue, _minValue, _maxValue, _xBarChart, _horizontalBarGraphWidth - _xBarChart);
if(_actualValue >= _hValue)
{
painter->fillRect(rect.adjusted(_scaleOutput, _yBarChart1, -_xBarChart, -_yBarChart2), yellow);
} else
{
painter->fillRect(rect.adjusted(_scaleOutput, _yBarChart1, -_xBarChart, -_yBarChart2), gray212);}
}
painter->restore();
//HiHi
painter->save();
if(_hhEnabled)
{
_scaleOutput = scale(_hhValue, _minValue, _maxValue, _xBarChart, _horizontalBarGraphWidth - _xBarChart);
if(_actualValue >= _hhValue)
{
painter->fillRect(rect.adjusted(_scaleOutput, _yBarChart1, -_xBarChart, -_yBarChart2), red);
} else
{
painter->fillRect(rect.adjusted(_scaleOutput, _yBarChart1, -_xBarChart, -_yBarChart2), gray192);}
}
painter->restore();
//Sensor name, Units
painter->save();
painter->setFont(topFont);
pen.setColor(gray224);
painter->setPen(pen);
painter->drawText(rect.adjusted(_xBarChart, 0, 0, -_horizontalBarGraphHeight + _yBarChart1 - _heightBarChart / 2), Qt::AlignLeft | Qt::AlignBottom, _sensorName); //Draws sensor name
painter->drawText(rect.adjusted(0, 0, -_xBarChart, -_horizontalBarGraphHeight + _yBarChart1 - _heightBarChart / 2), Qt::AlignRight | Qt::AlignBottom, _units); //Draws units
painter->restore();
//Arrow
painter->save();
QPainterPath path;
_scaleOutput = scale(_actualValue, _minValue, _maxValue, _xBarChart, _horizontalBarGraphWidth - _xBarChart);
path.moveTo(_scaleOutput - _heightBarChart / 2, _yBarChart1 - _heightBarChart / 2);
path.lineTo(_scaleOutput + _heightBarChart / 2, _yBarChart1 - _heightBarChart / 2);
path.lineTo(_scaleOutput, _yBarChart1 + _heightBarChart / 2);
path.closeSubpath();
painter->fillPath(path, blue);
painter->restore();
//Bounding box
painter->save();
_drawBoundingBox = false;
if(_llEnabled && _actualValue <= _llValue)
{
_drawBoundingBox = true;
_boundingBoxColor = red;
} else if (_lEnabled && _actualValue <= _lValue)
{
_drawBoundingBox = true;
_boundingBoxColor = yellow;
} else if (_hhEnabled && _actualValue >= _hhValue)
{
_drawBoundingBox = true;
_boundingBoxColor = red;
} else if (_hEnabled && _actualValue >= _hValue)
{
_drawBoundingBox = true;
_boundingBoxColor = yellow;
}
if(_drawBoundingBox && _boundingBoxFlash)
{
pen.setColor(_boundingBoxColor);
pen.setWidthF(_boundingBoxStrokeWidth);
painter->setPen(pen);
painter->drawRect(rect.adjusted(0, 0, 0, 0));
}
painter->restore();
}
qreal HorizontalBarGraph::getHorizontalBarGraphWidth()
{
return _horizontalBarGraphWidth;
}
qreal HorizontalBarGraph::getHorizontalBarGraphHeight()
{
return _horizontalBarGraphHeight;
}
qreal HorizontalBarGraph::getMinValue()
{
return _minValue;
}
qreal HorizontalBarGraph::getMaxValue()
{
return _maxValue;
}
qreal HorizontalBarGraph::getActualValue()
{
return _actualValue;
}
QString HorizontalBarGraph::getSensorName()
{
return _sensorName;
}
QString HorizontalBarGraph::getUnits()
{
return _units;
}
bool HorizontalBarGraph::isLlEnabled()
{
return _llEnabled;
}
qreal HorizontalBarGraph::getLlValue()
{
return _llValue;
}
bool HorizontalBarGraph::isLEnabled()
{
return _lEnabled;
}
qreal HorizontalBarGraph::getLValue()
{
return _lValue;
}
bool HorizontalBarGraph::isHEnabled()
{
return _hEnabled;
}
qreal HorizontalBarGraph::getHValue()
{
return _hValue;
}
bool HorizontalBarGraph::isHhEnabled()
{
return _hhEnabled;
}
qreal HorizontalBarGraph::getHhValue()
{
return _hhValue;
}
bool HorizontalBarGraph::isBoundingBoxFlashing()
{
return _boundingBoxFlash;
}
void HorizontalBarGraph::setHorizontalBarGraphWidth(qreal width)
{
if(_horizontalBarGraphWidth == width)
return;
_horizontalBarGraphWidth = width;
update();
emit widthChanged();
}
void HorizontalBarGraph::setHorizontalBarGraphHeight(qreal height)
{
if(_horizontalBarGraphHeight == height)
return;
//This object was designed to have a constant height. This method is technically not needed,
//but qt appears to require it.
//_horizontalBarGraphHeight = height;
//update();
//emit heightChanged();
}
void HorizontalBarGraph::setMinValue(qreal minValue)
{
if(_minValue == minValue)
return;
_minValue = minValue;
update();
emit minValueChanged();
}
void HorizontalBarGraph::setMaxValue(qreal maxValue)
{
if(_maxValue == maxValue)
return;
_maxValue = maxValue;
update();
emit maxValueChanged();
}
void HorizontalBarGraph::setActualValue(qreal actualValue)
{
if(_actualValue == actualValue)
return;
_actualValue = actualValue;
update();
emit actualValueChanged();
}
void HorizontalBarGraph::setSensorName(QString sensorName)
{
if(_sensorName == sensorName)
return;
_sensorName = sensorName;
update();
emit sensorNameChanged();
}
void HorizontalBarGraph::setUnits(QString units)
{
if(_units == units)
return;
_units = units;
update();
emit unitsChanged();
}
void HorizontalBarGraph::enableLl(bool llEnabled)
{
if(_llEnabled == llEnabled)
return;
_llEnabled = llEnabled;
update();
emit enableLlChanged();
}
void HorizontalBarGraph::setLlValue(qreal llValue)
{
if(_llValue == llValue)
return;
_llValue = llValue;
update();
emit llValueChanged();
}
void HorizontalBarGraph::enableL(bool lEnabled)
{
if(_lEnabled == lEnabled)
return;
_lEnabled = lEnabled;
update();
emit enableLChanged();
}
void HorizontalBarGraph::setLValue(qreal lValue)
{
if(_lValue == lValue)
return;
_lValue = lValue;
update();
emit lValueChanged();
}
void HorizontalBarGraph::enableH(bool hEnabled)
{
if(_hEnabled == hEnabled)
return;
_hEnabled = hEnabled;
update();
emit enableHChanged();
}
void HorizontalBarGraph::setHValue(qreal hValue)
{
if(_hValue == hValue)
return;
_hValue = hValue;
update();
emit hValueChanged();
}
void HorizontalBarGraph::enableHh(bool hhEnabled)
{
if(_hhEnabled == hhEnabled)
return;
_hhEnabled = hhEnabled;
update();
emit enableHhChanged();
}
void HorizontalBarGraph::setHhValue(qreal hhValue)
{
if(_hhValue == hhValue)
return;
_hhValue = hhValue;
update();
emit hhValueChanged();
}
void HorizontalBarGraph::setBoundingBoxFlash(bool flash)
{
if(_boundingBoxFlash == flash)
return;
_boundingBoxFlash = flash;
update();
emit boundingBoxFlashChanged();
}
qreal HorizontalBarGraph::scale(qreal input, qreal inputMin, qreal inputMax, qreal outputMin, qreal outputMax)
{
qreal output = (outputMax - outputMin) * (input - inputMin) / (inputMax - inputMin) + outputMin;
if(output > outputMax)
output = outputMax;
if(output < outputMin)
output = outputMin;
return output;
}
horizontalbargraph.h
#ifndef HORIZONTALBARGRAPH_H
#define HORIZONTALBARGRAPH_H
#include <QObject>
#include <QQuickPaintedItem>
#include <QPainter>
class HorizontalBarGraph : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(qreal horizontalBarGraphWidth READ getHorizontalBarGraphWidth WRITE setHorizontalBarGraphWidth NOTIFY horizontalBarGraphWidthChanged)
Q_PROPERTY(qreal horizontalBarGraphHeight READ getHorizontalBarGraphHeight WRITE setHorizontalBarGraphHeight NOTIFY horizontalBarGraphHeightChanged)
Q_PROPERTY(qreal minValue READ getMinValue WRITE setMinValue NOTIFY minValueChanged)
Q_PROPERTY(qreal maxValue READ getMaxValue WRITE setMaxValue NOTIFY maxValueChanged)
Q_PROPERTY(qreal actualValue READ getActualValue WRITE setActualValue NOTIFY actualValueChanged)
Q_PROPERTY(QString sensorName READ getSensorName WRITE setSensorName NOTIFY sensorNameChanged)
Q_PROPERTY(QString units READ getUnits WRITE setUnits NOTIFY unitsChanged)
Q_PROPERTY(bool llEnable READ isLlEnabled WRITE enableLl NOTIFY enableLlChanged)
Q_PROPERTY(qreal llValue READ getLlValue WRITE setLlValue NOTIFY llValueChanged)
Q_PROPERTY(bool lEnable READ isLEnabled WRITE enableL NOTIFY enableLChanged)
Q_PROPERTY(qreal lValue READ getLValue WRITE setLValue NOTIFY lValueChanged)
Q_PROPERTY(bool hhEnable READ isHhEnabled WRITE enableHh NOTIFY enableHhChanged)
Q_PROPERTY(qreal hhValue READ getHhValue WRITE setHhValue NOTIFY hhValueChanged)
Q_PROPERTY(bool hEnable READ isHEnabled WRITE enableH NOTIFY enableHChanged)
Q_PROPERTY(qreal hValue READ getHValue WRITE setHValue NOTIFY hValueChanged)
Q_PROPERTY(bool boundingBoxFlash READ isBoundingBoxFlashing WRITE setBoundingBoxFlash NOTIFY boundingBoxFlashChanged)
public:
HorizontalBarGraph(QQuickItem *parent = nullptr);
virtual void paint(QPainter *painter);
qreal getHorizontalBarGraphWidth();
qreal getHorizontalBarGraphHeight();
qreal getMinValue();
qreal getMaxValue();
qreal getActualValue();
QString getSensorName();
QString getUnits();
bool isLlEnabled();
qreal getLlValue();
bool isLEnabled();
qreal getLValue();
bool isHhEnabled();
qreal getHhValue();
bool isHEnabled();
qreal getHValue();
bool isBoundingBoxFlashing();
void setHorizontalBarGraphWidth(qreal width);
void setHorizontalBarGraphHeight(qreal height);
void setMinValue(qreal minValue);
void setMaxValue(qreal maxValue);
void setActualValue(qreal actualValue);
void setSensorName(QString sensorName);
void setUnits(QString units);
void enableLl(bool llEnabled);
void setLlValue(qreal llValue);
void enableL(bool lEnabled);
void setLValue(qreal lValue);
void enableHh(bool hhEnabled);
void setHhValue(qreal hhValue);
void enableH(bool hEnabled);
void setHValue(qreal hValue);
void setBoundingBoxFlash(bool flash);
signals:
void horizontalBarGraphWidthChanged();
void horizontalBarGraphHeightChanged();
void minValueChanged();
void maxValueChanged();
void actualValueChanged();
void sensorNameChanged();
void unitsChanged();
void enableLlChanged();
void llValueChanged();
void enableLChanged();
void lValueChanged();
void enableHhChanged();
void hhValueChanged();
void enableHChanged();
void hValueChanged();
void boundingBoxFlashChanged();
private:
qreal _horizontalBarGraphWidth;
qreal _horizontalBarGraphHeight = 75;
qreal _minValue;
qreal _maxValue;
qreal _actualValue;
QString _sensorName;
QString _units;
bool _llEnabled;
qreal _llValue;
bool _lEnabled;
qreal _lValue;
bool _hhEnabled;
qreal _hhValue;
bool _hEnabled;
qreal _hValue;
qreal _xBarChart = 25;
qreal _yBarChart1 = 35;
qreal _yBarChart2 = 25;
qreal _heightBarChart = _horizontalBarGraphHeight - _yBarChart1 - _yBarChart2;
qreal _widthValue = _xBarChart * 2;
qreal _scaleOutput;
qreal _boundingBoxStrokeWidth = 7.5;
bool _drawBoundingBox;
QColor _boundingBoxColor;
bool _boundingBoxFlash;
qreal scale(qreal input, qreal inputMin, qreal inputMax, qreal outputMin, qreal outputMax);
};
#endif // HORIZONTALBARGRAPH_H
there is many virtual functions that you can rempliment for get mouse event and best function for mouse click is mouseRelease
if I understand correctly, you want to block all mouse signal in main window while the new windows is open. you can create a QDialog instead of using pure QWidget to ban all mouse interaction from main window .
I have a simple vertex and fragment shader. I use them in a class derived from QtOpenGLWidget.the program is supposed to draw a triangle, but it does not show anything. The glClearColor works fine and I am able to change the background color. When I run the program from command line, the program outputs one single line, but draws nothing:
QOpenGLShaderProgram::attributeLocation( position ): shader program is not linked
When I insert getGLError after gl commands, none of them report an error.
here is the class definition:
class GLCanvas:public QOpenGLWidget , protected QOpenGLFunctions
{
QMatrix4x4 matrix;
QOpenGLShaderProgram* shader;
GLuint posLocation;
GLuint colLocation;
GLuint matLocation;
void cleanup();
public:
GLCanvas(QWidget* parent);
void initializeGL() override;
void paintGL() override;
void resizeGL(int w, int h) override;
QStringList getGLinfo();
};
static const char *vertexShaderSource =
R"(
#version 330
in vec3 position;
void main()
{
gl_Position = vec4(position, 1);
})";
static const char *fragmentShaderSource =
R"(
#version 330
void main()
{
gl_FragColor = vec4(0.4,0.4,0.8,1.0);
})";
GLCanvas::GLCanvas(QWidget* parent): QOpenGLWidget(parent)
{
setFormat(QSurfaceFormat::defaultFormat());
matrix.setToIdentity();
posLocation = -1;
colLocation = -1;
matLocation = -1;
}
void GLCanvas::initializeGL()
{
makeCurrent();
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &GLCanvas::cleanup);
constexpr int glNoError = 0;
GLenum error = -1;
initializeOpenGLFunctions();
glClearColor(.1f, .1f, .1f, 1.0f);
error = glGetError();
if (error != glNoError)
{
qDebug() << "glclearcolor failed";
}
shader = new QOpenGLShaderProgram(this);
shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
error = glGetError();
if (error != glNoError)
{
qDebug() << "failed after adding vertex shader.";
}
shader->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
error = glGetError();
if (error != glNoError)
{
qDebug() << "failed after adding fragment shader.";
}
Q_ASSERT(shader->link());
error = glGetError();
if (error != glNoError)
{
qDebug() << "gl error at link.";
}
//colLocation = shader->attributeLocation("color");
//matLocation = shader->uniformLocation("matrix");
//shader->release();
posLocation = shader->attributeLocation("position");
error = glGetError();
if (error != glNoError)
{
qDebug() << "gl error at link.";
}
glEnable(GL_DEPTH_TEST);
//colLocation = shader->attributeLocation("color");
//matLocation = shader->uniformLocation("matrix");
//shader->release();
//glEnable(GL_CULL_FACE);
}
//###################################################################
void GLCanvas::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GLfloat vertices[] = { 0.0f, 0.707f, .0f, 0.5f, -0.5f, .0f ,-0.5f, -0.5f, .0f};
Q_ASSERT(shader->bind());
shader->setAttributeArray(posLocation, vertices, 3, 0);
glEnableVertexAttribArray(posLocation);
glDrawArrays(GL_LINES, 0, 3);
/*
GLfloat lvertices[] = { 0.907f, 0.907f, 0.5f, -0.957f, -0.957f, 0.5f };
shader->setAttributeArray(posLocation, lvertices, 0, 3);
glDrawArrays(GL_LINE_STRIP, 0, 2);
glDisableVertexAttribArray(posLocation);
*/
//glDisableVertexAttribArray(colLocation);
shader->release();
// qDebug() << getGLinfo();
}
void GLCanvas::resizeGL(int w, int h)
{
Q_UNUSED(w);
Q_UNUSED(h);
//matrix.setToIdentity();
//matrix.perspective(45.0f, w / float(h), 0.01f, 1000.0f);
glViewport(0, 0, w, h);
}
/*
void GLCanvas::mousePressEvent(QMouseEvent *event)
{
Q_UNUSED(event);
}
void GLCanvas::mouseMoveEvent(QMouseEvent *event)
{
Q_UNUSED(event);
}
*/
void GLCanvas::cleanup()
{
if (shader == nullptr)
return;
makeCurrent();
delete shader;
//shader = nullptr;
doneCurrent();
}
I did everything carefully from the beginning and things worked. probably a stupid mistake was the cause of it all. Thank you all.
I have Graphic scene in that i have to alight right,left,top or bottom based on first selected item(Reference Item). i searched i got some code but in this its aligning to scene right position. I have to align items based on first selected item. how can i do this?
void GraphicScene::ItemsRightAlign()
{
if (selectedItems().isEmpty())
return;
QRectF refRect = selectedItems().first()->boundingRect();
QList<QGraphicsItem*> sel =selectedItems(); // for example
foreach(QGraphicsItem* selItem, sel)
{
qreal dx = 0, dy = 0;
QRectF itemRect = selItem->mapToScene(selItem->boundingRect()).boundingRect();
//if(align_right)
dx = refRect.right() - itemRect.right();
qDebug() << "item pos "<< dx << dy << selItem->mapToScene(selItem->boundingRect()).boundingRect() ;
selItem->moveBy(dx, dy);
}
}
For more details
Output should be like this output
The resolution method is to map the point that determines the right, left, up, down to the scene of the first item and the other items obtaining the difference that must be compensated.
graphicsscene.h
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QGraphicsItem>
class GraphicsScene: public QGraphicsScene{
Q_OBJECT
public:
GraphicsScene(QObject *parent=nullptr);
void moveSelecteds(Qt::Alignment aligment);
private slots:
void onSelectionChanged();
private:
void move(QGraphicsItem *ref, QList<QGraphicsItem *> others, Qt::Alignment aligment);
QGraphicsItem *mRef;
};
#endif // GRAPHICSSCENE_H
graphicsscene.cpp
#include "graphicsscene.h"
GraphicsScene::GraphicsScene(QObject *parent):
QGraphicsScene(parent),
mRef(nullptr)
{
connect(this, &GraphicsScene::selectionChanged, this, &GraphicsScene::onSelectionChanged);
}
void GraphicsScene::moveSelecteds(Qt::Alignment aligment){
QList<QGraphicsItem *> its= selectedItems();
if(its.size() < 2)
return;
if(!its.removeOne(mRef))
return;
move(mRef, its, aligment);
}
void GraphicsScene::onSelectionChanged(){
if(selectedItems().isEmpty()){
mRef = nullptr;
}
else if(selectedItems().size() == 1){
mRef = selectedItems().first();
}
}
void GraphicsScene::move(QGraphicsItem *ref, QList<QGraphicsItem *> others, Qt::Alignment aligment){
QPointF p;
switch (aligment) {
case Qt::AlignLeft:
p = QPointF(ref->mapToScene(ref->boundingRect().topLeft()).x(), 0);
break;
case Qt::AlignRight:
p = QPointF(ref->mapToScene(ref->boundingRect().topRight()).x(), 0);
break;
case Qt::AlignTop:
p = QPointF(0, ref->mapToScene(ref->boundingRect().topLeft()).y());
break;
case Qt::AlignBottom:
p = QPointF(0, ref->mapToScene(ref->boundingRect().bottomLeft()).y());
break;
}
for(QGraphicsItem *o: others){
QPointF delta;
switch (aligment) {
case Qt::AlignLeft:{
delta = p - QPointF(o->mapToScene(o->boundingRect().topLeft()).x(), 0);
break;
}
case Qt::AlignRight:{
delta = p - QPointF(o->mapToScene(o->boundingRect().topRight()).x(), 0);
break;
}
case Qt::AlignTop:{
delta = p - QPointF(0, o->mapToScene(o->boundingRect().topLeft()).y());
break;
}
case Qt::AlignBottom:{
delta = p - QPointF(0, o->mapToScene(o->boundingRect().bottomLeft()).y());
break;
}
}
o->moveBy(delta.x(), delta.y());
}
}
In this example you can use the up, down, left, right keys to move the items.
main.cpp
#include "graphicsscene.h"
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QShortcut>
#include <random>
static void create_items(QGraphicsScene & scene){
std::default_random_engine generator;
std::uniform_int_distribution<int> dist_size(30, 40);
std::uniform_int_distribution<int> dist_pos(-50, 50);
for(const QString & colorname : {"red", "green", "blue", "gray", "orange"}){
QRectF r(QPointF(dist_pos(generator), dist_pos(generator)),
QSizeF(dist_size(generator), dist_size(generator)));
auto item = new QGraphicsRectItem(r);
item->setPos(QPointF(dist_pos(generator), dist_pos(generator)));
item->setBrush(QColor(colorname));
item->setFlag(QGraphicsItem::ItemIsSelectable);
scene.addItem(item);
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GraphicsScene scene;
create_items(scene);
QGraphicsView view(&scene);
const QList<QPair<Qt::Key, Qt::Alignment>> k_a {
{Qt::Key_Up, Qt::AlignTop},
{Qt::Key_Down, Qt::AlignBottom},
{Qt::Key_Left, Qt::AlignLeft},
{Qt::Key_Right, Qt::AlignRight}
};
for(const QPair<Qt::Key, Qt::Alignment> & p : k_a){
QShortcut *shorcut = new QShortcut(p.first, &view);
QObject::connect(shorcut, &QShortcut::activated, std::bind(&GraphicsScene::moveSelecteds, &scene, p.second));
}
view.resize(640, 480);
view.show();
return a.exec();
}
The complete example can be found in the following link.
Replace topLeft.x -- for Right align with topRight.x ,For Top Align replace topLeft.y and dx with dy,For Bottom Align replace bottomLeft.y and dx with dy
void GraphicScene::ItemsLeftAlign
{
if (selectedItems().isEmpty())
return;
QGraphicsItem *FirstSelItem = selectedItems().first();
QList<QGraphicsItem*> sel =selectedItems(); // for example
foreach(QGraphicsItem* selItem, sel)
{
qreal dx = 0, dy = 0;
dx = (FirstSelItem->mapToScene(FirstSelItem->boundingRect().topLeft()).x()) -
(selItem->mapToScene(selItem->boundingRect().topLeft()).x());
selItem->moveBy(dx, dy);
}
}
I have a QDoubleSpinBox in range 0-7000, but want the value always displayed as 4 digits
(0-> 0000, 1 -> 0001 , 30 -> 0030, 3333 -> 3333).
I understand I can add a prefix, but a prefix is always added.
What are my options?
If you use integers, then QSpinBox will be enough.
You can simply inherit from QSpinBox and re-implement the textFromValue function:
class MySpinBox: public QSpinBox
{
Q_OBJECT
public:
MySpinBox( QWidget * parent = 0) :
QSpinBox(parent)
{
}
virtual QString textFromValue ( int value ) const
{
/* 4 - number of digits, 10 - base of number, '0' - pad character*/
return QString("%1").arg(value, 4 , 10, QChar('0'));
}
};
Filling QString this way does the trick.
Since prefix is not an option solution if you consider negative values, in my opinion the best and most elegant solution is defining your own custom spin box by deriving QAbstractSpinBox. Here is a small example:
Note that it is far from perfection and it serves just as an example on what could be done:
q4digitspinbox.h:
#ifndef Q4DIGITSPINBOX_H
#define Q4DIGITSPINBOX_H
#include <QAbstractSpinBox>
#include <QLineEdit>
class Q4DigitSpinBox : public QAbstractSpinBox
{
Q_OBJECT
public:
explicit Q4DigitSpinBox(QWidget *parent = 0);
StepEnabled stepEnabled() const;
double maximum() const;
double minimum() const;
void setMaximum(double max);
void setMinimum(double min);
void setRange(double minimum, double maximum);
double value() const;
public slots:
virtual void stepBy(int steps);
void setValue(double val);
signals:
void valueChanged(double i);
void valueChanged(const QString & text);
private:
double m_value;
double m_minimum;
double m_maximum;
QLineEdit m_lineEdit;
};
#endif // Q4DIGITSPINBOX_H
q4digitspinbox.h:
#include "q4digitspinbox.h"
Q4DigitSpinBox::Q4DigitSpinBox(QWidget *parent) :
QAbstractSpinBox(parent),
m_value(0),
m_minimum(-99),
m_maximum(99)
{
setLineEdit(&m_lineEdit);
setValue(0.0);
}
QAbstractSpinBox::StepEnabled Q4DigitSpinBox::stepEnabled() const
{
return StepUpEnabled | StepDownEnabled;
}
double Q4DigitSpinBox::maximum() const
{
return m_maximum;
}
double Q4DigitSpinBox::minimum() const
{
return m_minimum;
}
void Q4DigitSpinBox::setMaximum(double max)
{
m_maximum = max;
}
void Q4DigitSpinBox::setMinimum(double min)
{
m_minimum = min;
}
void Q4DigitSpinBox::setRange(double minimum, double maximum)
{
m_minimum = minimum;
m_maximum = maximum;
}
double Q4DigitSpinBox::value() const
{
return m_value;
}
void Q4DigitSpinBox::stepBy(int steps)
{
m_value += (double)steps / 10;
if (fabs(m_value - 0) < 0.00001)
{
m_value = 0;
}
if(m_value < m_minimum || m_value > m_maximum)
{
return;
}
int prefixNumberOfDigits = 4;
QString valueAsString = QString("%1").arg((int)m_value);
int numberOfDigits = valueAsString.length();
QString prefix;
prefixNumberOfDigits -= numberOfDigits;
if(prefixNumberOfDigits > 0)
{
while(prefixNumberOfDigits--)
{
prefix += "0";
}
}
QString value;
if(m_value < 0)
{
value = QString("-%1%2").arg(prefix).arg(-m_value);
}
else
{
value = QString("%1%2").arg(prefix).arg(m_value);
}
m_lineEdit.setText(value);
emit valueChanged(m_value);
emit valueChanged(value);
}
void Q4DigitSpinBox::setValue(double val)
{
if(val < m_minimum || val > m_maximum)
{
return;
}
int prefixNumberOfDigits = 4;
QString valueAsString = QString("%1").arg((int)val);
int numberOfDigits = valueAsString.length();
QString prefix;
prefixNumberOfDigits -= numberOfDigits;
if(prefixNumberOfDigits > 0)
{
while(prefixNumberOfDigits--)
{
prefix += "0";
}
}
QString value;
if(val < 0)
{
value = QString("-%1%2").arg(prefix).arg(-val);
}
else
{
value = QString("%1%2").arg(prefix).arg(val);
}
m_lineEdit.setText(value);
emit valueChanged(val);
emit valueChanged(value);
}
I didn't provide any commentary since I considered it pretty straight forward, but if needed I can add a few more explanations.
I hope this helps.
When I want to create a new Qt Quick Item in C++ in I extend QQuickItem. It happens that I want my new item to have the same basic properties of a Rectangle. Is there any class I can extend to have a Rectangle directly?
Rectangle is actually QQuickRectangle, but it is not exported to be used in C++. The header file is QtQuick\private\qquickrectangle_p.h.
Check my version of an AdvancedRectangle that can have a different radius for each corner. I had to re-implement Rectangle's behavior.
advancedrectangle.h
#ifndef ADVANCEDRECTANGLE_H
#define ADVANCEDRECTANGLE_H
#include <QQuickPaintedItem>
#include <QPainter>
#include <QPen>
class AdvancedRectangle : public QQuickPaintedItem
{
Q_OBJECT
public:
explicit AdvancedRectangle(QQuickItem *parent = 0);
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
QColor color() const;
void setColor(QColor color);
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
qreal radius() const;
void setRadius(qreal r);
Q_PROPERTY(qreal radiusTopLeft READ radiusTopLeft WRITE setRadiusTopLeft NOTIFY radiusTopLeftChanged)
qreal radiusTopLeft() const;
void setRadiusTopLeft(qreal r);
Q_PROPERTY(qreal radiusTopRight READ radiusTopRight WRITE setRadiusTopRight NOTIFY radiusTopRightChanged)
qreal radiusTopRight() const;
void setRadiusTopRight(qreal r);
Q_PROPERTY(qreal radiusBottomRight READ radiusBottomRight WRITE setRadiusBottomRight NOTIFY radiusBottomRightChanged)
qreal radiusBottomRight() const;
void setRadiusBottomRight(qreal r);
Q_PROPERTY(qreal radiusBottomLeft READ radiusBottomLeft WRITE setRadiusBottomLeft NOTIFY radiusBottomLeftChanged)
qreal radiusBottomLeft() const;
void setRadiusBottomLeft(qreal r);
void paint(QPainter *painter);
enum START_ANGELS {
START_E = 0,
START_N = 90,
START_W = 180,
START_S = 270
};
signals:
void colorChanged();
void radiusChanged();
void radiusTopLeftChanged();
void radiusTopRightChanged();
void radiusBottomRightChanged();
void radiusBottomLeftChanged();
public slots:
private:
QColor color_ = QColor(255, 255, 255);
qreal radius_ = 0.0;
qreal radiusTopLeft_ = -1;
qreal radiusTopRight_ = -1;
qreal radiusBottomRight_ = -1;
qreal radiusBottomLeft_ = -1;
private slots:
void onAppearanceChanged();
};
#endif // ADVANCEDRECTANGLE_H
advancedrectangle.cpp
#include "advancedrectangle.h"
AdvancedRectangle::AdvancedRectangle(QQuickItem *parent) :
QQuickPaintedItem(parent)
{
connect(this, SIGNAL(widthChanged()), SLOT(onAppearanceChanged()));
connect(this, SIGNAL(heightChanged()), SLOT(onAppearanceChanged()));
connect(this, SIGNAL(colorChanged()), SLOT(onAppearanceChanged()));
}
QColor AdvancedRectangle::color() const
{
return color_;
}
void AdvancedRectangle::setColor(QColor color)
{
if (color_ == color) return;
color_ = color;
emit colorChanged();
}
qreal AdvancedRectangle::radius() const
{
return radius_;
}
void AdvancedRectangle::setRadius(qreal r)
{
if (r == radius_) return;
radius_ = r;
emit radiusChanged();
}
qreal AdvancedRectangle::radiusTopLeft() const
{
if (radiusTopLeft_ >= 0) return radiusTopLeft_;
else return radius_;
}
void AdvancedRectangle::setRadiusTopLeft(qreal r)
{
if (r == radiusTopLeft_) return;
radiusTopLeft_ = r;
emit radiusTopLeftChanged();
}
qreal AdvancedRectangle::radiusTopRight() const
{
if (radiusTopRight_ >= 0) return radiusTopRight_;
else return radius_;
}
void AdvancedRectangle::setRadiusTopRight(qreal r)
{
if (r == radiusTopRight_) return;
radiusTopRight_ = r;
emit radiusTopRightChanged();
}
qreal AdvancedRectangle::radiusBottomRight() const
{
if (radiusBottomRight_ >= 0) return radiusBottomRight_;
else return radius_;
}
void AdvancedRectangle::setRadiusBottomRight(qreal r)
{
if (r == radiusBottomRight_) return;
radiusBottomRight_ = r;
emit radiusBottomRightChanged();
}
qreal AdvancedRectangle::radiusBottomLeft() const
{
if (radiusBottomLeft_ >= 0) return radiusBottomLeft_;
else return radius_;
}
void AdvancedRectangle::setRadiusBottomLeft(qreal r)
{
if (r == radiusBottomLeft_) return;
radiusBottomLeft_ = r;
emit radiusBottomLeftChanged();
}
void AdvancedRectangle::paint(QPainter *painter)
{
QPen pen(Qt::NoPen);
painter->setPen(pen);
QBrush brush(color_);
painter->setBrush(brush);
painter->setRenderHints(QPainter::Antialiasing, true);
int _width = width();
int _height = height();
QRectF rectangle;
// top-left
rectangle = QRectF(0, 0, 2*radiusTopLeft(), 2*radiusTopLeft());
painter->drawPie(rectangle, START_W * 16, 90 * -1 * 16);
// top-right
rectangle = QRectF(_width-2*radiusTopRight(), 0, 2*radiusTopRight(), 2*radiusTopRight());
painter->drawPie(rectangle, START_N * 16, 90 * -1 * 16);
// bottom-right
rectangle = QRectF(_width-2*radiusBottomRight(), _height-2*radiusBottomRight(), 2*radiusBottomRight(), 2*radiusBottomRight());
painter->drawPie(rectangle, START_E * 16, 90 * -1 * 16);
// bottom-left
rectangle = QRectF(0, _height-2*radiusBottomLeft(), 2*radiusBottomLeft(), 2*radiusBottomLeft());
painter->drawPie(rectangle, START_S * 16, 90 * -1 * 16);
QPointF points[12] = {
QPointF(radiusTopLeft(), 0),
QPointF(radiusTopLeft(), radiusTopLeft()),
QPointF(0, radiusTopLeft()),
QPointF(0, _height-radiusBottomLeft()),
QPointF(radiusBottomLeft(), _height-radiusBottomLeft()),
QPointF(radiusBottomLeft(), _height),
QPointF(_width-radiusBottomRight(), _height),
QPointF(_width-radiusBottomRight(), _height-radiusBottomRight()),
QPointF(_width, _height-radiusBottomRight()),
QPointF(_width, radiusTopRight()),
QPointF(_width-radiusTopRight(), radiusTopRight()),
QPointF(_width-radiusTopRight(), 0)
};
painter->drawPolygon(points, 12);
}
void AdvancedRectangle::onAppearanceChanged()
{
int new_width = width();
int new_height = height();
update(QRect(0, 0, new_width, new_height));
}
Let me know if you need that MIT licensed.