Solved No update of my QGraphicsPixmapItem
-
I changed the minimal example to this using two signals and two slots.
Please note " QThread::sleep(1);" is used only here - for simulating the measurement routine and the device - which is a black box routine eating some time ...The problem is still that with out
QApplication::processEvents();
inMainWindow::slotPaintColor(int i)
the color does not change.How can I achieve a loop which first changes the color and than - when this color is shown on screen (not before) - the measurement takes place ... and again until the colorlist is at the end ... all without using
QApplication::processEvents();
#include "mainwindow.h" #include "ui_mainwindow.h" using namespace std; struct rgb { int R; int G; int B; char cn [10]; } colors[5]; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); connect(this, &MainWindow::signalPaintColor, this, &MainWindow::slotPaintColor); connect(this, &MainWindow::signalMeasureColor, this, &MainWindow::slotMeasureColor); scene = new QGraphicsScene(this); ui->graphicsView->setScene(scene); scene->addItem(&pixmap); colors[0] = { 255, 255, 255, "white" }; colors[1] = { 255, 0, 0, "red" }; colors[2] = { 0, 255, 0, "green" }; colors[3] = { 0, 0, 255, "blue" }; colors[4] = { 0, 0, 0, "black" }; } MainWindow::~MainWindow() { delete ui; } void MainWindow::slotPaintColor(int i){ QPixmap pix(ui->graphicsView->size()); pix.fill(QColor(colors[i].R, colors[i].G, colors[i].B)); pixmap.setPixmap(pix); ui->statusbar->showMessage("Color: " + QString(colors[i].cn), 0); QApplication::processEvents(); emit signalMeasureColor(); } void MainWindow::slotMeasureColor() { //Sleep is for simulation of external task here ... QThread::sleep(1); } void MainWindow::on_startButton_released() { ui->statusbar->showMessage("Start loop ...", 0); for( int i = 0; i < 5; i++ ) { emit signalPaintColor(i); } }
-
Well: do not block the event loop.
Since you are doing measures "on demand", use the worker object approach. Encapsulate the measurement part in its own class. Move the instance of that class to a dedicated QThread. And then use signals and slots to trigger the measure and pass back the data. Doing so you will decouple the controlling part and the UI.
-
@SGaist thx for the fast response.
Is there a "Qt standard Example" using this technique ?
That I can have a look how his works.Is this what you are talking about? https://wiki.qt.io/QThreads_general_usage
Am I right: This examples uses still old signal/slots syntax?
-
The wiki page is a bit outdated.
Check the QThread documentation. It has vastly improved and shows both technique with modern syntax.
-
@SGaist thx so much - I try this out
-
Hi there,
now i have modified my minimal app to give threads a try.
I would need help with the new syntax for signal/slots across classes.I try to connect the signal from the thread with the slot in the main UI app like this:
connect(mThread, &MyThread::signalPaintColor, this, &MainWindow::slotPaintColor);mainwindow.cpp:20:5: error: no matching member function for call to 'connect'
qobject.h:242:43: note: candidate function [with Func1 = void (MyThread::)(int), Func2 = void (MainWindow::)(int)] not viable: cannot convert from base class pointer 'QThread ' to derived class pointer 'const typename QtPrivate::FunctionPointer<void (MyThread::)(int)>::Object ' (aka 'const MyThread ') for 1st argument
qobject.h:222:36: note: candidate function not viable: no known conversion from 'void (MyThread::)(int)' to 'const char ' for 2nd argument
qobject.h:225:36: note: candidate function not viable: no known conversion from 'void (MyThread::)(int)' to 'const QMetaMethod' for 2nd argument
qobject.h:481:41: note: candidate function not viable: no known conversion from 'void (MyThread::)(int)' to 'const char ' for 2nd argument
qobject.h:283:13: note: candidate template ignored: requirement '!QtPrivate::FunctionPointer<void (MainWindow::)(int)>::IsPointerToMemberFunction' was not satisfied [with Func1 = void (MyThread::)(int), Func2 = void (MainWindow::)(int)]
qobject.h:322:13: note: candidate template ignored: requirement 'QtPrivate::FunctionPointer<void (MainWindow::)(int)>::ArgumentCount == -1' was not satisfied [with Func1 = void (MyThread::)(int), Func2 = void (MainWindow::*)(int)]
qobject.h:274:13: note: candidate function template not viable: requires 3 arguments, but 4 were provided
qobject.h:314:13: note: candidate function template not viable: requires 3 arguments, but 4 were providedFrom mythread.h signals: void signalPaintColor(int i); public slots: void slotMeasureColor(int i);
From mainwindow.h signals: private slots: void on_startButton_released(); void slotPaintColor(int i);
-
@ademmler
Your error message is not on any of these. It states:mainwindow.cpp:20:5: error: no matching member function for call to 'connect'
qobject.h:242:43: note: candidate function [with Func1 = void (MyThread::)(int), Func2 = void (MainWindow::)(int)] not viable: cannot convert from base class pointer 'QThread ' to derived class pointer 'const typename QtPrivate::FunctionPointer<void (MyThread::)(int)>::Object ' (aka 'const MyThread ') for 1st argumentSo show your corresponding code. It even gives you the line number of the call.
-
The code to connect at line 20:
connect(mThread, &MyThread::signalPaintColor, this, &MainWindow::slotPaintColor);Pls be aware - this is a "minimal for testing and learning - not a rock solid solution.
mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" using namespace std; struct rgb { int R; int G; int B; char cn [10]; } colors[6]; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); connect(mThread, &MyThread::signalPaintColor, this, &MainWindow::slotPaintColor); colors[0] = { 255, 255, 255, "white" }; colors[1] = { 255, 0, 0, "red" }; colors[2] = { 0, 255, 0, "green" }; colors[3] = { 0, 0, 255, "blue" }; colors[4] = { 0, 0, 0, "black" }; colors[5] = { 128, 128, 128, "grey" }; scene = new QGraphicsScene(this); ui->graphicsView->setScene(scene); scene->addItem(&pixmap); QPixmap pix(this->width(), this->height()); pix.fill(QColor(colors[5].R, colors[5].G, colors[5].B)); pixmap.setPixmap(pix); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_startButton_released() { mThread = new MyThread; mThread->start(); } void MainWindow::slotPaintColor(int i){ ui->statusbar->showMessage("Color: " + QString(colors[i].cn), 0); QPixmap pix(ui->graphicsView->size()); pix.fill(QColor(colors[i].R, colors[i].G, colors[i].B)); pixmap.setPixmap(pix); }
mythread.cpp
#include "mythread.h" MyThread::MyThread(QObject *parent): QThread(parent) { } void MyThread::run() { QMutex mutex; //Prepare measurment device int err = 0; m_meas = new Measure(meas_i1d3, &err); if (!m_meas){ qDebug() << "No connection to measurment device"; return; } for( int i = 0; i < 5; i++ ) { emit signalPaintColor(i); slotMeasureColor(i); } mutex.unlock(); } void MyThread::slotMeasureColor(int i) { if (int err = m_meas->measure()) { qDebug() << "Could not get correct measurement!"; } char buffer[128]; sprintf(buffer, "%i, %4.1f %4.1f %4.1f", i, m_meas->meas.xyz.X, m_meas->meas.xyz.Y, m_meas->meas.xyz.Z); qDebug() << "Measurment: " << buffer; }
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QGraphicsScene> #include <QGraphicsPixmapItem> #include <QMessageBox> #include <QThread> #include <QDebug> #include "color/measure.h" #include "mythread.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); signals: private slots: void on_startButton_released(); void slotPaintColor(int i); private: Ui::MainWindow *ui; QGraphicsScene *scene; QGraphicsPixmapItem pixmap; Measure *m_meas; QThread *mThread; }; #endif // MAINWINDOW_H
mythread.h
#ifndef MYTHREAD_H #define MYTHREAD_H #include <QtCore> #include <QDebug> #include "color/measure.h" class MyThread :public QThread { Q_OBJECT public: explicit MyThread(QObject *parent = 0); void run() override; QString name; bool Stop; Measure *m_meas; signals: void signalPaintColor(int i); public slots: void slotMeasureColor(int i); }; #endif // MYTHREAD_H
-
I can't see what's wrong, but then I don't use threads :) While you await an expert who will spot it in 1 second, 10% chance that deleting all files in build output directory and rebuild will work, e.g. if you have added
Q_OBJECT
s recently. -
You are declaring a QThread and not a MyThread in your MainWindow class.
Another issue you will have is that your are doing your connection on an
initializeduninitialized pointer so you are in fact not connecting anything valid.You should just start your thread in on_startButton_released. The way it's working now is that it will create a new instance of your MyThread class each time you click on the button which is likely not what you want.
[edit: Fixed typo]
-
@SGaist said in No update of my QGraphicsPixmapItem:
You are declaring a QThread and not a MyThread in your MainWindow class.
Dang!
-
@SGaist said in No update of my QGraphicsPixmapItem:
MyThread
Perfect HELP - thank You! The issue with the connect is gone!
Another issue you will have is that your are doing your connection on an initialized pointer so you are in fact not connecting anything valid.
What should I do instead. if I change
MyThread *mThread;
toMyThread mThread;
I get again:
/Users/ademmler/QtTutorials/QtMinimalMeasure/mainwindow.cpp:20: error: no matching member function for call to 'connect'When I run the code - the measurement works, but the pixmap does not get updated at all - again.
-
@ademmler There was a (now fixed) typo in the sentence: it was "uninitialised" pointer. Which one is the no matching member function ?
-
@ademmler said in No update of my QGraphicsPixmapItem:
What should I do instead. if I change MyThread *mThread; to MyThread mThread; I get again:
/Users/ademmler/QtTutorials/QtMinimalMeasure/mainwindow.cpp:20: error: no matching member function for call to 'connect'If you change from
MyThread *mThread
toMyThread mThread
you must changeconnect(mThread, ...)
toconnect(&mThread, ...)
.You should do this
connect()
before you invokeQThread::start()
. So if you stick withon_startButton_released()
doing amThread = new MyThread
theconnect()
should come on the next line. -
Thank you very much to @SGaist and @JonB
I can't set an answer to be the correct one ... missing user rights for that!In the mean time i did what you and @SGaist has told me to do.
I have put all this to the to after
ui->setupUi(this);
and now it works as expected :-DmThread = new MyThread; connect(mThread, &MyThread::signalPaintColor, this, &MainWindow::slotPaintColor);