What I simply want to do is connect a signal inside a thread to a slot in the main thread to handle UI changes.
This is basically the current state of my thread, nothing fancy but it's just for testing purposes atm:
// synchronizer.h
class Synchronizer : public QObject
{
Q_OBJECT
public:
Synchronizer();
signals:
void newConnection(std::wstring id);
private:
QTimer timer;
private slots:
void synchronize();
}
// synchronizer.cpp
Synchronizer::Synchronizer()
{
connect(&timer, SIGNAL(timeout()), this, SLOT(synchronize()));
timer.start();
}
void Synchronizer::synchronize()
{
emit newConnection(L"test");
}
And here's how my MainWindow looks:
// mainwindow.h
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
Synchronizer synchronizer;
private slots:
void addConnection(std::wstring id);
}
// mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(&synchronizer, SIGNAL(newConnection(std::wstring)),
this, SLOT(addConnection(std::wstring)));
QThread *thread = new QThread;
// The problems starts here?
synchronizer.moveToThread(thread);
thread->start();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::addConnection(std::wstring id)
{
// Add a new connection to QListWidget
ui->connectionList(QString::fromStdWString(id));
}
If I remove there lines:
synchronizer.moveToThread(thread);
thread->start();
everything seems to work as expected, that is a new item is added every second to a QListWidget but as soon as I move the synchronizer object to thread it simply stops working. I'd presume it has something to do with the connect context but I'm not really sure how something like this should be achieved as I'm quite new to Qt.
It seems that the in this case was simply the fact that I am using std::wstring as an argument in the signal without registering the type first and after adding the following line qRegisterMetaType<std::wstring>("std::wstring"); to the code, everything worked as expected.
If I would have read the output console more carefully I would have solved the problem without too much hassle as it was clearly stated that:
QObject::connect: Cannot queue arguments of type 'std::wstring'
So simply speaking, read the compiler output and don't be stupid like me :)
Related
I am making a simple game and want to send a signal from my Game class to my MainWindow. My signal and slot share the same parameter but I can't connect them. I have tried sending very simple signals with a dummy variable but failed to connect. The code is as follows.
game.h
class Game : public QObject
{
Q_OBJECT
public:
Game();
signals:
void test(int l);
MainWindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
public slots:
void testSlot(int l);
game.cpp
void someFunction(){
emit test(2);
}
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow),
g{new Game()}
{
ui->setupUi(this);
g->gameLoop();
connect(g,&Game::test,this,&MainWindow::testSlot);
}
How can I get the signals and slots to connect properly? Thank you in advance.
I think the problem may be in the fact that you have g->gameLoop(); BEFORE the connect. If your someFunction is called from the gameLoop, then the connect is performed only after the game has finished and after the execution returns from the gameLoop(). But of course it's just guessing. I wouldn't expect to see 'gameLoop' called from the Window's constructor so.. it looks odd as well. Other than that, it looks fine, so if my guess is not correct, then probably the problem lies elsewhere in the code we don't see.
I have an infinity loop whithin the run-method of my QThread subclass. This thread should exist till the application is closed. The Code looks something like this.
loopThread.h:
class loopThread : public QThread
{
Q_OBJECT
public:
loopThread();
~loopThread();
protected:
void run() override;
signals:
void resultReady();
};
The implementation of the subclass is as follows:
loopThread.cpp:
void loopThread::run()
{ while(!this->isInterruptionRequested()){
std::cout << "loop active" << std::endl; //checking if it executes the loop or not
}
}
the run()-method executes a infinity loop until requesInterruption() is called in the mainWindow class.
MainWindow.h:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void closeEvent(QCloseEvent *event);
private:
Ui::MainWindow *ui;
loopThread *loopThread = nullptr;
public slots:
void handleResult();
};
The Implementation for this class says
MainWindow.cpp:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
loopThread = new loopThread();
connect(loopThread, &loopThread::noButtonClicked, this, &loopThread::NoButton);
connect(loopThread, &loopThread::finished, loopThread, &loopThread::deleteLater);
loopThread->start();
}
MainWindow::~MainWindow()
{
loopThread->quit();
loopThread->wait();
delete ui;
}
void MainWindow::closeEvent(QCloseEvent *event)
{
loopThread->requestInterruption();
}
So I would suggest that when I close the application by clicking in the "X" in the window the loop should end and the program should stop. The problem is even after closing the window the Console gives me the message "loop active" so it the loop still goes on. Where's my mistake. Is there a way to let a loop end when the program is closed?
The solution with moving a QObject to a Thread produces the same problem.
Thanks for the help guys!
I managed to solve this problem. AS said the infinite loop in the run()-method just send the message to the console. I have avoided this by just let the loop run without any task.
in my case i needed to check the serialcommunication so with the signal readyRead() i can now read this serial message. The loop is therfore executed until i stop the app. hopefulle that doesnt confuse too much. I will post my code when i have time.
I'm trying to connect two frames with a custom signal but I'm not really getting it.
This code is just an example of what im trying to do in my program, my objective is to transfer data between frames.
Files:
(sender)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
signals:
void send();
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
private slots:
void on_pushButton_clicked();
};
#endif // MAINWINDOW_H
On "mainwindow.cpp" I've got the void on_pushButton_clicked() that emits the signal and shows the new frame:
private slot void:
void MainWindow::on_pushButton_clicked()
{
emit send();
Dialog sw;
sw.setModal(true);
sw.exec();
}
(receiver):
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QDebug>
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = nullptr);
~Dialog();
private slots:
void receive();
private:
Ui::Dialog *ui;
int a;
};
#endif // DIALOG_H
and the .cpp:
#include "dialog.h"
#include "ui_dialog.h"
#include "mainwindow.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
a=0;
MainWindow w;
connect(&w, SIGNAL(send()), this, SLOT(receive()));
qDebug() << a;
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::receive(){
qDebug() << "ola";
a++;
}
Conclusion:
So basicly the function Dialog doesn't print the qDebug(), and 'a' is still 0, so I conclude that the connection isn't set/executed.
Thanks all,
Best regards,
Dylan Lopes.
edit: Wrote a conclusion on the end of the post.
Consider the code in your Dialog constructor...
MainWindow w;
connect(&w, SIGNAL(send()), this, SLOT(receive()));
This creates a locally scoped MainWindow on the stack and connects its send() signal to the Dialog's receive() slot. But the MainWindow -- and, hence, the connection -- will be destroyed as soon as the Dialog constructor has completed.
In addition, looking at MainWindow::on_pushButton_clicked...
void MainWindow::on_pushButton_clicked()
{
emit send();
Dialog sw;
sw.setModal(true);
sw.exec();
}
You emit the send() signal before constructing the Dialog.
I don't really know enough about what you're trying to achieve to provide a definitive answer, but in the interests of getting some kind of signal/slot interaction you might want to do the following: change the Dialog constructor to...
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::Dialog)
{
ui->setupUi(this);
a=0;
qDebug() << a;
}
And change MainWindow::on_pushButton_clicked to...
void MainWindow::on_pushButton_clicked()
{
Dialog sw;
connect(this, &MainWindow::send, &sw, &Dialog::receive);
emit send();
sw.setModal(true);
sw.exec();
}
That should at least result in Dialog::receive being invoked and you can work from there.
Connection between a signal and a slot doesn't mean that the signal function will be triggered.
You still need to emit your signal so that a gets updated.
Creating an empty slot isn't working either, as slots are the receiving point of a signal. In this case, on_pushButton_clicked() gets triggered whent he push button is clicked. This doesn't trigger send unless you call EMIT(send()) (IIRC, you emit a signal with EMIT, is that still the case?).
Playing with QMetaObject::invokeMethod method :
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::setText( int value)
{
QString s = QString::number(value);
ui->textEdit->setText(s);
}
void MainWindow::on_pushButton_clicked()
{
QGenericArgument genericArg = Q_ARG(int, 321);
bool inv = QMetaObject::invokeMethod( this,"setText",Qt::BlockingQueuedConnection, genericArg);
qDebug("inv = %d\n", inv);
}
QMetaObject::invokeMethod returns false.
I'm not sure regarding slot "setText". I took it from function name and I suppose it might be related. Where I can find list of slots at all? Should I create special slot for "setText"?
Maybe it is related to fact I run it from the same thread?
UPD:
I have added public slot instead of public method:
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
//void setText( int value);
private slots:
void on_pushButton_clicked();
public slots:
void setText(int value);
private:
Ui::MainWindow *ui;
};
And this helped, but why I'm getting 0 in setText value ?
Your button-event is handled by the event loop that owns the MainWindow object and this same object also contains the method you wish to invoke (setText()). That means that caller and callee (or signal and slot) in your case live on the same thread, and you must not use Qt::BlockingQueuedConnection! To quote the manual: Using this connection type to communicate between objects in the same thread will lead to deadlocks.
If you intent to do processing in your on_pushButton_clicked() after the setText() method has finished, use a Qt::DirectConnection instead, then your setText() will be called as if it was a simple function, and control returns to your clicked() function after setText() finished.
If you intent to finish processing all code in your on_pushButton_clicked() before processing of the setText() function starts, use a Qt::QueuedConnection.
If you wish to execute setText() in parallel to on_pushButton_clicked(), then move your setText() method to another object (which is owned by another thread). Only in this scenario a Qt::BlockingQueuedConnection makes sense.
I'm connecting a signal/slot but the slot is called multiple times (1, 2, 3...) every time I trigger the option, here are my classes:
mainwindow.h:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
Dialog *dialog;
signals:
void s1(QString s);
private slots:
void on_actionTooo_triggered();
};
#endif // MAINWINDOW_H
mainwindow.cpp:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_actionTooo_triggered()
{
dialog = new Dialog(this);
QString s = "hello";
qDebug() << "CONNECT: " << connect(this, SIGNAL(s1(QString)),
dialog, SLOT(s1(QString)), Qt::UniqueConnection);
emit s1(s);
dialog->show();
}
dialog.h:
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
public slots:
void s1(QString str);
private:
Ui::Dialog *ui;
};
dialog.cpp:
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::s1(QString str)
{
qDebug() << str << endl;
}
Every time the option is triggered in the main window, I connect the signal (just once), to the new dialog, but when I run, for example two times, it prints two "hello". If I put a disconnect(this, 0, 0, 0); before I connect the signals it works.
It seems odd to me that it maintains the connect even when the object is destroyed. It binds the connect to the same object created before. Is this the expected behavior?
You are creating a new Dialog everytime you call on_actionTooo_triggered(). This Dialog isn't deleted at the end of your function. Therefore with the next call the signal MainWindow::s1(QString) is emitted to the two different Dialogs which results in multiple ouptuts in qDebug.
void MainWindow::on_actionTooo_triggered()
{
dialog = new Dialog(this);
QString s = "hello";
qDebug() << "CONNECT: " << connect(this, SIGNAL(s1(QString)),
dialog, SLOT(s1(QString)), Qt::UniqueConnection);
emit s1(s);
dialog->show();
}
This code creates a new dialog each time it's triggered and sets up a connection between the newly created dialog and the MainWindow. While the connection is specified as "Unique", a new dialog is created each time, so the connection is not unique as the dialog instance is different.
Your code does not show that the dialog is being deleted and even if you close its window, the instance still remains in memory, so multiple objects are receiving the same signal.
Your Dialog is never destroyed in this code. It's not even leaked as you pass this as a parent in the Dialog's constructor, which makes the MainWindow responsible for this Dialog memory.
Thus you permanently create additional Dialog which slot will be triggered, and thus explaining why you see an incrementally increasing repetition of the output.