Qt Multi-Thread Queued Connection Signal / Slot Issue (SLOT does not trigger) - qt

Struggling emitting a signal from main/QML thread to another thread with a QList< QStringList > parameter. Variations I'v tried:
Q_DECLARATIVE_METATYPE in and out
Using EventExport vs. const EventExport& in signal and slot profiles
Sending empty EventExport in prepareExport() so emit has no/low data amount
Checking connect statement (always returns true)
Having qDebug() in prepareExport() and signal always appears to be emitted
Calling emit right after connect as a test (Works! Think you're going to tell me the main thread or cryoUtility objects don't exist but they do!)
Tried qRegisterMetaType with () and ("EventExport")...some say use text for typedef types
Any thoughts much appreciated!
sqlquery_model.h (not certain I need Q_DECLARATIVE_METATYPE but tried with and without...no change)
typedef QList<QStringList> EventExport;
Q_DECLARE_METATYPE(EventExport);
Q_INVOKABLE void prepareExport();
signals:
void updateEventListDataSig(const EventExport&);
sqlquery_model.cpp (this is connected to a qml page using TableView model...this emit does not seem to work)
void SqlQueryModel::prepareExport() {
if (this->rowCount() > 0) {
EventExport eventsList;
for(int i=0; i<this->rowCount(); ++i) {
QStringList eventInfo;
eventInfo.append(this->record().value(0).toString());
eventInfo.append(this->record().value(1).toString());
eventInfo.append(this->record().value(2).toString());
eventInfo.append(this->record().value(3).toString());
eventInfo.append(this->record().value(4).toString());
eventsList.append(eventInfo);
}
emit updateEventListDataSig(eventsList);
qDebug() << "Emit updatedEventListData" << eventsList.count();
}
}
main.cpp (includes sqlquery_model.h, need this as cryoUtility is a separate thread using Qt::QueuedConnection)
// Use string if using typedef method
qRegisterMetaType<EventExport>("EventExport");
mediator.h
void updateEventListDataSig(const EventExport&);
mediator.cpp (connects mainly live here, this test event works)
bool ret = connect(this, SIGNAL(updateEventListDataSig(const EventExport&)), cryoUtility, SLOT(updateEventListData(const EventExport&)), Qt::QueuedConnection);
EventExport ed;
emit updateEventListDataSig(ed);
qDebug() << "Event list CONN: " << ret;
utilities.h
void updateEventListData(const EventExport&);
utilities.cpp (this is the slot, trigger once on test call)
void Utilities::updateEventListData(const EventExport& el) {
qDebug() << "Load event list: ";// << el.count();
//eventList = el;
}

So, after more study, sqlmodelquery connection would have to occur in its constructor as it isn't active yet until its QML page loads.

Related

Can't write to Qt5 textEdit box with ui->control->setText(message)

I am trying to put the data passed to a mainwindow.cpp function on the screen with the typical ui->control->setText(message) without success. The same line works if it is in a timer loop or a button function but not from display_that_data function
"MainWindow::display_that_data()" is called from myudp.cpp with the following lines
MainWindow show_tlm;
show_tlm.display_that_data(data_source, buf_copy);
mainwindow.cpp (updated to include emitting a signal for a SIGNAL/SLOT connection)
The SIGNAL/SLOT connection is set up in the constructor with the following line
connect (this, SIGNAL (showdata_signal()), this, SLOT(showdata_slot()));
void MainWindow::display_that_data(QByteArray data_source, QByteArray tlmBuf){
QString msg ;
msg = " in display_that_data";
qDebug() << msg ;
ui->tlm_vals->setText(msg);
//generate a signal which will trigger showdata_slot
emit showdata_signal();
msg = " in display_that_data after emit showdata_signal()";
qDebug() << msg ;
}
void MainWindow::showdata_slot() {
QString msg = "showdata_slot called";
qDebug() << msg ;
ui->tlm_vals->setText(msg);
}
Runtime debug messages show that code is making it to the showdata_slot but it is still not writing to the ui->tlm_vals
" in display_that_data"
"showdata_slot called"
" in display_that_data after emit showdata_signal()"
but.... neither one of the ui->tlm_vals->setText(msg) lines are putting text on the ui
"MainWindow::realtimeDataSlot()" is called by at timer timout signal as follows:
void MainWindow::setupRealtimeDataDemo(QCustomPlot *customPlot) {
// setup a timer that repeatedly calls MainWindow::realtimeDataSlot
connect(&dataTimer, SIGNAL(timeout()), this, SLOT(realtimeDataSlot()));
dataTimer.start(1000);
}
void MainWindow::realtimeDataSlot(){
QString temp = QString("%1").arg(epochTime, 10, 10, QChar('0'));
ui->tlm_vals->setText(temp);
}
And this works perfectly (of course I have to disable it to see if showdata_slot is writing to the ui)
I thought the problem was a needed SIGNAL and SLOT connection to trigger the write to the ui but generating a SIGNAL/SLOT connection (which debug shows as working) still does not write to the ui from the slot function.
For completeness mainwindow.h contains the following
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void setupRealtimeDataDemo(QCustomPlot *customPlot);
void display_that_data (QByteArray data_source, QByteArray tlmBuf);
private slots:
void realtimeDataSlot();
void showdata_slot();
private:
Ui::MainWindow *ui;
QTimer dataTimer;
What am I missing/doing wrong?
Somewhere deeply embedded in Qt there is a difference between gui mouse click generated event and code generated event and this somehow caues Qt behavior to vary with the same lines of code. I will repost a more more "root level" question on this topic.

Slots interrupting each other with just one Thread?

I have a small example using Slots called by mousemovement and mousewheel.
Now i have the problem that when i zoom and move at the same time, first the onZoom-slot is called and before it is finished it is calling the onMouseMoved-slot. That causes the first slot to lock the mutex (in my original program used by another thread) and the second one to wait for it.
How can I prevent the slots to interrupt each other (and why are they doing it in first place since they are in same thread?).
I read something about using Qt::QueuedConnection but that causes an access violation exception.
main.cpp
#include "ppi.h"
#include <QtGui/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
PPI w;
w.show();
return a.exec();
}
ppi.h
#ifndef PPI_H
#define PPI_H
#include <QtGui/QMainWindow>
#include <QGraphicsView>
#include <QDebug>
#include <QWheelEvent>
#include <QgraphicsEllipseItem>
#include <QMouseEvent>
#include <QMutex>
#include <QThread>
#include <QGraphicsSceneMouseEvent>
//#include "ui_ppi.h"
class PPIView : public QGraphicsView
{
Q_OBJECT
public:
PPIView(QWidget * parent = 0)
: QGraphicsView(parent)
{};
~PPIView(){};
private slots:
void wheelEvent(QWheelEvent *event)
{emit zoom(event);};
signals:
void zoom(QWheelEvent *event);
};
class PPIScene : public QGraphicsScene
{
Q_OBJECT
public:
PPIScene(QObject *parent)
: QGraphicsScene(parent)
{};
~PPIScene(){};
private:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{emit mouseMoved(event);};
signals:
void mouseMoved(QGraphicsSceneMouseEvent *event);
};
class PPI : public QMainWindow
{
Q_OBJECT
public:
PPI(QWidget *parent = 0, Qt::WFlags flags = 0)
: QMainWindow(parent, flags)
{
//ui.setupUi(this);
//ppiScene is inherited from QGraphicsScene, overriding mouseMoveEvent so it emits mouseMoved();
ppiScene = new PPIScene(this);
gVPPI = new PPIView(this);
gVPPI->setMinimumSize(1024,1024);
gVPPI->show();
test = new QGraphicsEllipseItem(-10, -10, 20, 20);
ppiScene->addItem(test);
gVPPI->adjustSize();
connect(ppiScene, SIGNAL(mouseMoved(QGraphicsSceneMouseEvent*)), this, SLOT(onMouseMoved(QGraphicsSceneMouseEvent*)));
connect(gVPPI, SIGNAL(zoom(QWheelEvent*)), this, SLOT(onZoom(QWheelEvent*)));
//ui.gVPPI is inherited from QGraphicsView, overriding wheelEvent, so it emits zoom()
gVPPI->setScene(ppiScene);
gVPPI->setMouseTracking(true);
};
~PPI(){};
QMutex mutex;
private:
//Ui::ppiClass ui;
PPIScene* ppiScene;
PPIView *gVPPI;
QGraphicsEllipseItem *test;
protected slots:
void onZoom(QWheelEvent *event)
{
qDebug() << "Zoom lock" << QThread::currentThreadId();
mutex.lock();
qDebug() << "Zoom locked";
if(event->delta() > 0)
gVPPI->scale(1.01, 1.01);
else
gVPPI->scale(1/1.01, 1/1.01);
qDebug() << "Zoom unlock";
mutex.unlock();
qDebug() << "Zoom unlocked";
};
void onMouseMoved(QGraphicsSceneMouseEvent *event)
{
qDebug() << "Move lock" << QThread::currentThreadId();
mutex.lock();
qDebug() << "move locked";
test->setPos(test->pos()+event->scenePos()-event->lastScenePos());
qDebug() << "Move unlock";
mutex.unlock();
qDebug() << "Move unlocked";
};
};
#endif // PPI_H
Output qDebug():
Move lock 0x1514
move locked
Move unlock
Move unlocked
Move lock 0x1514
move locked
Move unlock
Move unlocked
Zoom lock 0x1514
Zoom locked
Move lock 0x1514
You can use QObject::blockSignals to block signals for specific object,
but I think you have wrong idea about how your code works.
There is one GUI thread, and if signals + slots in the same GUI thread,
as in your case, then they called in sequence order. And if PPI::onZoom will called in another thread context then you have problem here, because of usage of things like ui.gVPPI->scale in non GUI thread is not allowed and may cause crash on assert, or just crash, or randomly works, UB as it is.
The situation when you see that slot 1 was called, then before finish of slot 1 slot 2 is called is likely because of you call some function inside slot 1 that call another function etc and some function deep inside call signal 2, which emit call of slot 2, just set breakpoints in your debugger and findout what is going on.
It doesn't make sense to use any form of locking in a single GUI thread that doesn't cooperate with other threads. Everything in the GUI thread is already serialized for you. You're fighting an imaginary problem. As long as your code is executing, nothing else will execute behind your back in the same thread.
onZoom-slot is called and before it is finished it is calling the onMouseMoved-slot
This is false. Nothing of the sort happens in the code you show. Perhaps ui.gVPPI->scale() is reentering the event loop. You really need to show a self-contained example: your problem is in the code you don't show.
How can I prevent the slots to interrupt each other
It's already prevented for you. You need to do nothing.
in my original program used by another thread
Generally speaking, objects living in the GUI thread cannot have methods directly invoked from other threads.
Most of the time, throwing a second thread at a problem makes you end up with two problems. Properly designed non-blocking code will work correctly, if perhaps a bit slow, even when run in a single thread. That'd be your point of departure - you then move objects to other threads simply to spread the work around. Ideally, though, async processing should be done by running work asynchronously through QtConcurrent::run.
I managed to set up my debugger and the problem seems QGraphicsView::scale() is calling QgraphicsScene::mouseMoveEvent() deep inside directly. So I need to introduce a variable which tells the mouseMoveEvent, whether it was called from QGraphicsView::scale() or from physical mouse movement.
protected slots:
void onZoom(QWheelEvent *event)
{
qDebug() << "Zoom lock" << QThread::currentThreadId();
mutex.lock();
qDebug() << "Zoom locked";
scale = true;
if(event->delta() > 0)
gVPPI->scale(1.01, 1.01);
else
gVPPI->scale(1/1.01, 1/1.01);
scale = false;
qDebug() << "Zoom unlock";
mutex.unlock();
qDebug() << "Zoom unlocked";
};
void onMouseMoved(QGraphicsSceneMouseEvent *event)
{
if(scale == false)
{
qDebug() << "Move lock" << QThread::currentThreadId();
mutex.lock();
qDebug() << "move locked";
}
test->setPos(test->pos()+event->scenePos()-event->lastScenePos());
if(scale == false)
{
qDebug() << "Move unlock";
mutex.unlock();
qDebug() << "Move unlocked";
}
};

Read output of multiple write in QProcess one by one

Can anyone help me read the output of qprocess after write and loop until all task is done?
I have this code
wifi->write("scan\n");
wifi->closeWriteChannel();
wifi->waitForBytesWritten(100);
wifi->waitForReadyRead(100);
wifi->waitForFinished(100);
qDebug() << "read output" << wifi->readAllStandardOutput();
wifi->write("scan\n");
wifi->closeWriteChannel();
wifi->waitForBytesWritten(100);
wifi->waitForReadyRead(100);
wifi->waitForFinished(100);
qDebug() << "read output" << wifi->readAllStandardOutput();
the expected output must be
"OK"
"scan results"
but the ouput is
"OK"
""
thanks.
Your multiple waits are not useful for anything. All you care about is when the process finishes, so have a single waitForFinished call with a much longer timeout (those scans don't happen in ~100ms, a few seconds is a good minimum).
You should not be using the blocking waitForXxx methods. They trip up everyone and are a source of unending grief. Forget that they exist. Use process's signals to react to events as they happen.
Qt 5 + C++11
This is the way forward. This is why you should insist on using a modern development environment, if you can. It's less typing and easier to understand.
void MyObject::startWifi() {
auto process = new QProcess(this);
process->start("program", QStringList() << "argument");
connect(process, &QProcess::started, [process]{
process->write("scan\n");
process->closeWriteChannel();
});
connect(process, &QProcess::finished, [process]{
qDebug() << process->readAllStandardOutput();
process->deleteLater();
});
}
Qt 4
class MyObject : public QObject {
Q_OBJECT
QProcess m_wifi;
Q_SLOT void onStarted() {
m_wifi.write("scan\n");
m_wifi.closeWriteChannel();
}
Q_SLOT void onFinished() {
qDebug() << m_wifi.readAllStandardOutput();
}
public:
MyObject(QObject * parent = 0) : QObject(parent) {
connect(&m_wifi, SIGNAL(started()), SLOT(onStarted()));
connect(&m_wifi, SIGNAL(finished(int,QProcess::ExitStatus)),
SLOT(onFinished()));
}
Q_SLOT void start() {
m_wifi.start("program", QStringList() << "argument");
}
};
Then invoke the start method/slot on an instance of this object. That's all.

Qt: Program crashes when it emits signals

I am using VTK and Qt, and I need to know, when which mouse-button is pressed or released.
That for I created a new class, which inherits QObject and vtkInteractorStyleTrackballCamera:
class CModelMouseInteractorStyle :
public vtkInteractorStyleTrackballCamera,
public QObject
{
public:
CModelMouseInteractorStyle();
virtual void OnLeftButtonDown()
{
std::cout << "Pressed left mouse button." << std::endl;
// Forward events
vtkInteractorStyleTrackballCamera::OnLeftButtonDown();
// emit signal
emit leftButtonDown();
}
virtual void OnLeftButtonUp()
{
std::cout << "Released left mouse button." << std::endl;
// Forward events
vtkInteractorStyleTrackballCamera::OnLeftButtonUp();
// emit signal
emit leftButtonUp();
}
// I have similar functions for right and middle mouse-button
...
signals:
void leftButtonDown();
void leftButtonUp();
// and similar signals, too
...
};
I get the right couts whenever a button is pressed/released and I can interact with my scene as I want to, but only as long as I don't emit any signals.
As soon as I emit a signal in on of the functions, I get a symbol lookup error and the whole program crashes.
Am I doing something wrong? Do I have forgotten any thing?
I am using Qt4.7.4 and Vtk5.8.

itemSelectionChanged() signal of QListWidget when clear() was called

Sorry for the bulky title.
I have a class containing a QListWidget.
I connected it's itemSelectionChanged() signal to a custom slot.
When I call QListWidget::clear(), the slot gets called (as expected) but a call to QListWidget::count() in this slot returns the number of items the QListWidget had before.
A call to count() right after the call to clear() (when the signal was processed as described) returns the correct number 0.
I prepared a complete demo project. Most important is this source file:
#include "ListWidgetTest.hpp"
#include "ui_ListWidgetTest.h"
#include <QDebug>
ListWidgetTest::ListWidgetTest(QWidget* parent)
: QWidget(parent), ui(new Ui::ListWidgetTest)
{
ui->setupUi(this);
for (int i = 0; i < 5; ++i) {
QListWidgetItem* item = new QListWidgetItem(QString("Item %1").arg(i));
ui->listWidget->addItem(item);
}
QObject::connect(ui->pushButton, SIGNAL(clicked()),
this, SLOT(clearList()));
QObject::connect(ui->listWidget, SIGNAL(itemSelectionChanged()),
this, SLOT(selectionChanged()));
}
ListWidgetTest::~ListWidgetTest()
{
delete ui;
}
void ListWidgetTest::clearList()
{
qDebug() << "void ListWidgetTest::clearList()";
ui->listWidget->clear();
qDebug() << "clearList: ui->listWidget->count() is " << ui->listWidget->count();
}
void ListWidgetTest::selectionChanged()
{
qDebug() << "void ListWidgetTest::selectionChanged()";
qDebug() << "selectionChanged: ui->listWidget->count() is " << ui->listWidget->count();
}
Output
void ListWidgetTest::clearList()
void ListWidgetTest::selectionChanged()
selectionChanged: ui->listWidget->count() is 5
clearList: ui->listWidget->count() is 0
What happens
The list gets populated.
Every click on an item calls selectionChanged()
A click on the button calls clearList()
The call to QListWidget::clear() also emits the signal and the slot gets called
The number of items has not changed yet
You can add "Qt::QueuedConnection" to both of QObject::connection. Like:
connect(ui->pushButton, SIGNAL(clicked()),
this, SLOT(clearList()), Qt::QueuedConnection);
connect(ui->listWidget, SIGNAL(itemSelectionChanged()),
this, SLOT(selectionChanged()), Qt::QueuedConnection);
It is works. But sorry I don't know why. Maybe queued connection method can solve multi-signal order problem.
First of all QListWidget::clear() is a SLOT and not a SIGNAL. So obviously it is not emitting/triggering the signal itemSelectionChanged().
You might be accidentally triggering this itemSelectionChanged() just before calling clear(). Check whether you are triggering itemchanged() or selectionchanged() or any other events that trigger itemSelectionChanged() before calling clear().
One possible solution is to declare a custom signal and emit this signal just
after calling clear(). And connect it to the custom slot you have defined.You
will get the expected value in your SLOT

Resources