How to check which image is set in my QLabel? - c++

I have a Qt application where I need to show a blinking LED and for that I need to use some png image of off and on led.I created a Qlabel and used setstylesheet to display the image. I created a timer and connected the signal to a slot. Now the problem is how do I know if the current displayed image is OFF led or ON led.
I have many led in GUI so is there any better way to check this?

Don't bother trying to compare the image, just store a variable of the state of the LED. When the timer triggers you change the state of the variable and set the QImage accordingly.
// assuming a boolean variable
var = !var;
if(var)
label->setImage(":/images/imageOn");
else
label->setImage(":/images/imageOff");
This assumes the images imageOn and imageOff have been added to a Qt resource file and are under an 'images' prefix.
It is good practise to separate logic from its visual representation.

You can leverage the property mechanism to store the index of the next image to be used. A QLabel is-a QObject. Objects can have arbitrary properties assigned to them.
You also don't need to use style sheets to set image on a label. It's a premature pessimization because the stylesheet needs to be parsed every time you set it. If you're not using stylesheets for other purposes, to set an image on a label simply use setPixmap.
For example (Qt 5, C++11):
#include <QApplication>
#include <QTimer>
#include <QLabel>
#include <QImage>
#include <QPainter>
void blink(QLabel * label, const QList<QImage> & images)
{
const char * const prop = "imageIndex";
Q_ASSERT(!images.isEmpty());
if (label->property(prop).isNull()) {
// We're setting the image for the first time
label->setProperty(prop, images.size());
}
int i = (label->property(prop).toInt() + 1) % images.size();
label->setPixmap(QPixmap::fromImage(images[i]));
label->setProperty(prop, i);
}
QImage textImage(const QString & text, int size = 64)
{
QImage image(size, size, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
p.setFont(QFont("helvetica", 20));
QTextOption opt;
opt.setAlignment(Qt::AlignCenter);
p.drawText(image.rect(), text, opt);
return image;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QList<QImage> images;
QLabel label;
QTimer timer;
images << textImage("0") << textImage("1") << textImage("2") << textImage("3");
blink(&label, images);
timer.start(250);
QObject::connect(&timer, &QTimer::timeout, [&]{ blink(&label, images); });
label.show();
return a.exec();
}

Related

Zooming in / out on images - QT c++

I am currently using QT to load images into a graphicsview scene. Essentially, I am looking for a way to be able to zoom in and out on the loaded image using a slider. The image loads successfully into the pane, but whatever I try to implement to zoom in / out, the image would disappear.
This is how it looks:
This is my implemented function, but the image disappears:
void MainWindow::on_horizontalSlider_valueChanged(int value)
{
float pool ;
if(value==0 )
pool=0.1;
else
pool = value*0.01;
scene->update();
ui->graphicsView->transform();
ui->graphicsView->scale(pool,pool);
}
This is how I'm loading the images:
void MainWindow::on_BrowseImages_clicked()
{
QString imagePath = QFileDialog::getOpenFileName(
this,
tr("Open File"),
"",
tr("Images (*.jpg *.jpeg *.png)" )
);
imageObject = new QImage();
imageObject->load(imagePath);
image = QPixmap::fromImage(*imageObject);
scene = new QGraphicsScene(this);
scene->addPixmap(image);
scene->setSceneRect(image.rect());
ui->graphicsView->setScene(scene);
//ui->graphicsView->fitInView(scene->sceneRect(),Qt::KeepAspectRatio);
}
When scaling is applied to transformation it always scales around origin.
Scaling with a certain center (that is not the origin) means that the center has been translated to the origin before scaling.
This could be
v' = translateO→C(scale(translateC→O(v)))
or with matrix operations
v' = MtranslateO→C • Mscale • MtranslateC→O • v
However, the Grahics View Framework provides something where combining transformations is actually built-in by default:
Every item provides its own local transformation.
The transformation of a group item is applied to the child items as well forming something which can be imagined as a local coordinate system.
This in mind, I came up with the following MCVE where
- translation to center is applied to the pixmap item
- scaling is applied to a group item which becomes parent of the pixmap item.
testQGraphicsViewScaleItem.cc:
// Qt header:
#include <QtWidgets>
// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup data
QGraphicsScene qGScene;
QGraphicsItemGroup qGItemGrp;
QImage qImgCat("cat.jpg");
QGraphicsPixmapItem qGItemImg(QPixmap::fromImage(qImgCat));
qGItemImg.setTransform(
QTransform().translate(-0.5 * qImgCat.width(), -0.5 * qImgCat.height()));
qGItemGrp.addToGroup(&qGItemImg);
qGScene.addItem(&qGItemGrp);
// setup GUI
QWidget qWinMain;
qWinMain.setWindowTitle("QGraphicsView - Scale Image");
QVBoxLayout qVBox;
QGraphicsView qGView;
qGView.setScene(&qGScene);
qVBox.addWidget(&qGView, 1);
QSlider qSlider(Qt::Horizontal);
qSlider.setRange(-100, 100);
qVBox.addWidget(&qSlider);
qWinMain.setLayout(&qVBox);
qWinMain.show();
// install signal handlers
auto scaleImg = [&](int value) {
const double exp = value * 0.01;
const double scl = pow(10.0, exp);
qGItemGrp.setTransform(QTransform().scale(scl, scl));
};
QObject::connect(&qSlider, &QSlider::valueChanged,
scaleImg);
// runtime loop
return app.exec();
}
and a qmake project file testQGraphicsViewScaleItem.pro:
SOURCES = testQGraphicsViewScaleItem.cc
QT += widgets
Output:

QIcon null after creating it from .SVG file

I am working on a project, based on Embedded Linux and Qt 5.7. On filesystem, I have SVG image file and I want to transform it into PNG Image file. I've searched over internet and found several solutions for achieving such task here. However, I do not have SVG module installed and I was forced to use QIcon approach:
void ApplicationFlowDataManager::slotCreateQrCode(const QString& qrCodeContents)
{
qDebug() << Q_FUNC_INFO
<< QImageWriter::supportedImageFormats();
const QString GENERATED_QR_CODE_SVG=QString("/home/root/qrCodeGenerated.svg");
const QString GENERATED_QR_CODE_PNG=QString("/home/root/qrCodeGenerated.png");
qrcodegen::QrCode qr0=qrcodegen::QrCode::encodeText(qrCodeContents.toLocal8Bit().data(),
qrcodegen::QrCode::Ecc::MEDIUM);
QString createdQrCode=QString::fromStdString(qr0.toSvgString(4));
QFile qrCodeSVG(GENERATED_QR_CODE_SVG,
this);
qrCodeSVG.open(QIODevice::WriteOnly|QIODevice::Text);
qrCodeSVG.write(createdQrCode.toUtf8());
qrCodeSVG.close();
QImage imageQrCodePNG=QIcon(GENERATED_QR_CODE_SVG).pixmap(QSize(256,
256)).toImage(); // <---- here is problem, QIcon is NULL and QImage has size QSize(0, 0) and therfore its save method returns false and no .PNG is created
imageQrCodePNG.save(GENERATED_QR_CODE_PNG);
} // slotCreateQrCode
The problem is created QIcon is NULL, like I described in problematic line of code. SVG file exists, I've 100x checked it. I've have also checked for low free space on partition, it is ok, I've checked /home/root/ permissions, they are not the problem since SVG file is created. The directory /home/root/ is not getting locked in any way, what is the problem?
P.S.: For QR Code handling, I am using Nayuki QR Code Generator Library.
Explanation:
QIcon according to the type of file extension uses a QIconEngine that is provided by a plugin, in case the file has an .svg extension, try the plugin that handles that format that is provided by the Qt SVG module, so in your If you don't have that plugin since you don't have the module then the QIcon will be null.
Solution:
You must use the information from the getModule method of QrCode to do the painting manually:
#include <QtWidgets>
#include <QrCode.hpp>
static QImage slotCreateQrCode(const QString& qrCodeContents)
{
qrcodegen::QrCode qr0=qrcodegen::QrCode::encodeText(qrCodeContents.toLocal8Bit().constData(),
qrcodegen::QrCode::Ecc::MEDIUM);
const int s = qr0.getSize();
const int margin = 1;
QImage imageQrCodePNG((s + 2 * margin) * QSize(1, 1), QImage::Format_Mono);
{
QPainter painter(&imageQrCodePNG);
painter.fillRect(imageQrCodePNG.rect(), Qt::white);
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::black);
for (int i = 0; i < s ; ++i)
{
for (int j = 0; j < s; ++j)
{
if(qr0.getModule(i, j))
painter.drawRect(i + margin, j + margin, 1, 1);
}
}
}
return imageQrCodePNG;
} // slotCreateQrCode
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QImage image = slotCreateQrCode("Stack Overflow");
const QSize size(512, 512);
QLabel label;
label.setPixmap(QPixmap::fromImage(image.scaled(size)));
label.setFixedSize(size);
label.show();
return a.exec();
}

How to make only one point label visible for QLineSeries/QXYSeries

I am using the qtcharts module of qt.
I am using c++ but it does not matter if the solution comes for another language (I will translate it afterwards).
Problem: I plot a bunch of QLineSeries in a QChart and I want to display the point labels only when hovering them.
I planned to use the signal QXYSeries::hovered() to detect when the mouse moves over a point (the same when the mouse moves away the point).
I know that there exists a member function QXYSeries::setPointLabelsVisible() but it makes visible all the points of the series.
I want to be able to display only one point at a time because the series are relatively large and displaying all the labels would degrade the readability.
Question: Is it possible to display only one point label for a QLineSeries ? If yes, how ?
I could not find such a feature anywhere in the Qt documentation.
Here is a baseline code sample to start with (for convenience):
Declaration:
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
};
Definition:
#include <QApplication>
#include <QLineSeries>
#include <QDateTimeAxis>
#include <QValueAxis>
#include <QChartView>
#include <QDateTime>
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
MainWindow w;
w.show();
return app.exec();
}
MainWindow::MainWindow()
{
setWindowTitle("QtCharts baseline");
resize(800, 500);
QtCharts::QChart * chart = new QtCharts::QChart;
chart->setTitle("Baseline sample");
chart->legend()->setAlignment(Qt::AlignRight);
QtCharts::QDateTimeAxis * time_axis = new QtCharts::QDateTimeAxis;
time_axis->setFormat("hh:mm:ss");
time_axis->setTitleText("Time");
time_axis->setTickCount(5);
QtCharts::QValueAxis * value_axis = new QtCharts::QValueAxis;
value_axis->setTitleText("Value (unit)");
value_axis->setTickCount(6);
chart->addAxis(time_axis, Qt::AlignBottom);
chart->addAxis(value_axis, Qt::AlignLeft);
QtCharts::QLineSeries * ls = new QtCharts::QLineSeries;
ls->setName("Test series");
ls->setPointsVisible(true);
//ls->setPointLabelsVisible(true);
QDateTime dt = QDateTime::currentDateTime();
ls->append(dt.toMSecsSinceEpoch(), -10);
ls->append(dt.addSecs(1).toMSecsSinceEpoch(), 8);
ls->append(dt.addSecs(2).toMSecsSinceEpoch(), 27);
ls->append(dt.addSecs(3).toMSecsSinceEpoch(), 12);
ls->append(dt.addSecs(4).toMSecsSinceEpoch(), 42);
chart->addSeries(ls);
ls->attachAxis(time_axis);
ls->attachAxis(value_axis);
QtCharts::QChartView * view = new QtCharts::QChartView;
view->setChart(chart);
this->setCentralWidget(view);
}
One technique you could try is making a "shadow" copy of the line series with just the one or few points you need, sitting on top of the actual data line. Draw with a transparent pen so the line doesn't show up but set the labels to be visible. You can add/remove/change points to the shadow copy of the line series, and only the labels you want drawn can be added to the series. The link shows one
example of the technique.

Setting minimum width of QPushButton while keeping default minimumSizeHint behavior

In my application, I need all QPushButtons to have a width of at least 150px, but bigger if needed.
To do so, I am using a global stylesheet (which contains many other properties) with this simple constraint :
QPushButton {
min-width: 150px;
}
The thing is, I also want buttons with a text that doesn't fit inside 150px to be unable to shrink to a width below which the whole text wouldn't be displayed.
This is supposed to be the normal behavior for a QPushButton, but the problem is that, as explained in the documentation for minimumSizeHint() :
QLayout will never resize a widget to a size smaller than the minimum size hint unless minimumSize() is set or the size policy is set to QSizePolicy::Ignore. If minimumSize() is set, the minimum size hint will be ignored.
This leads to some cases where buttons with long texts are displayed at the right size at startup, but when shrinking the window, the button gets too small to display all the text.
I have prepared a simple example that shows this behavior :
#include <QWidget>
#include <QApplication>
#include <QHBoxLayout>
#include <QPushButton>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget *w = new QWidget();
QLayout* layout = new QHBoxLayout(w);
QPushButton* buttonA = new QPushButton("A", w);
QPushButton* buttonB = new QPushButton("B", w);
buttonB->setStyleSheet("min-width: 150px");
QPushButton* buttonC = new QPushButton("Long long text that doesn't fit in 150px", w);
QPushButton* buttonD = new QPushButton("Very very very long text that doesn't fit in 150px", w);
buttonD->setStyleSheet("min-width: 150px");
layout->addWidget(buttonA);
layout->addWidget(buttonB);
layout->addWidget(buttonC);
layout->addWidget(buttonD);
w->show();
return a.exec();
}
I thought about using dynamic properties to set the minimum width constraint only to buttons that have a base width < 150px, but this doesn't seem to be doable.
Is there a way to do what I want with stylesheets, or do I have to subclass QPushButton and override the minimumSizeHint() method (which I'd like to avoid as I would have to replace a lot of buttons in my app)?
It would be better if you sub-class QPushButton. But there are 2 work around for this problem:
You can strip the long text to specific numbers of characters and use setToolTip(QString) function to show full text when mouse will enter the button.
You can override parent widget resizeEvent and can check the widths and go for approach number 1 if the size is getting really small.
Use Elide Text example to get idea about elide for QPushButton. Not sure if this will work.
I haven't been able to find a way to do this using only stylesheets, but here is the solution I came up with by subclassing QPushButton
MyPushButton.h
#include <QPushButton>
class MyPushButton
: public QPushButton
{
Q_OBJECT
Q_PROPERTY(int minWidth READ minWidth WRITE setMinWidth)
public:
MyPushButton(QWidget* parent = nullptr);
virtual ~MyPushButton();
int minWidth() const;
void setMinWidth(int width);
virtual QSize minimumSizeHint() const override;
private:
int _minWidth;
};
MyPushButton.cpp
#include "MyPushButton.h"
MyPushButton::MyPushButton(QWidget* parent)
: QPushButton(parent)
, _minWidth(0)
{
}
MyPushButton::~MyPushButton()
{
}
int MyPushButton::minWidth() const
{
return _minWidth;
}
void MyPushButton::setMinWidth(int width)
{
_minWidth = width;
}
QSize MyPushButton::minimumSizeHint() const
{
// if the minimum width is less than minWidth, set it to minWidth
// else return the default minimumSizeHint
return QSize(qMax(QPushButton::minimumSizeHint().width(), _minWidth),
QPushButton::minimumSizeHint().height());
}
stylesheet.qss
MyPushButton {
qproperty-minWidth: 150;
}
Buttons with a minimumSizeHint width lower than 150px will be forced to that size, bigger ones will keep the default behavior of QPushButton.

QT C++ : Moving several labels at the same time

I have been searching about Threads in QT and ways to process multiple events simultaneously according to their docs, only the main GUI thread can manage GUI related events. So my question is: Is it possible to move multiple labels or objects at the same time during execution? Im trying to create sort of a simulation proyect for school.
What I have right now is: it creates a label everytime I run the function so I need that label to move to a certain spot. Problem is since I need the function executed several times, when it executes again, the previous label stops and moves the new one. After it has completed it goes back to the previous.
Any help is appreciated thanks.
New to asking here and QT in general.
Edit:
Here is what I have of my function:
QLabel *cliente = new QLabel(this);
QPixmap pix("image.jpg");
cliente->setGeometry(10,50,128,128);
cliente->setPixmap(pix);
cliente->show();
int speed = 100;
while (cliente->x()<300){
QTime dieTime = QTime::currentTime().addMSecs(speed);
while (QTime::currentTime() < dieTime){
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
cliente->move(cliente->x()+10,cliente->y());
}
To handle the movement of widgets it is most advisable to use the QPropertyAnimation class, but if you want to handle parallel group animations it is opportune to use QParallelAnimationGroup as shown in the following example:
#include <QApplication>
#include <QLabel>
#include <QParallelAnimationGroup>
#include <QPropertyAnimation>
#include <QWidget>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
w.resize(500, 550);
QParallelAnimationGroup group;
for(int i = 0; i < 10; i++){
QLabel *label = new QLabel(QString("label %1").arg(i), &w);
QPropertyAnimation *animation = new QPropertyAnimation(label, "pos");
animation->setDuration(1000);
animation->setStartValue(QPoint(50*i, 0));
animation->setEndValue(QPoint(50*i, 50*(i+1)));
group.addAnimation(animation);
}
group.start();
w.show();
return a.exec();
}
Output: