slot parameter for QMetaObject::invokeMethod - c++

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.

Related

QT signal and slot connection not working

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.

Can't conect two frames with custom signals

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?).

QTimer won't start when passing a variable

I have a QTimer object that kicks off a function every second. The timer runs correctly if I do the following
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
// Set a Qtimer to update the OSD display every 1 second
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(print()));
timer->start(1000);
}
void MainWindow::print()
{
printf("hello world\n");
}
But I want to pass a variable to print(). But when I do this, I never see my print statement.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
// Set a Qtimer to update the OSD display every 1 second
QTimer *timer = new QTimer(this);
int val = 42;
// Now pass val to print()
connect(timer, SIGNAL(timeout()), this, SLOT(print(val)));
timer->start(1000);
}
void MainWindow::print(int val)
{
// I never see my print statement
printf("hello world, val=%d\n", val);
}
header.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void print(int val);
Why does this not work? What can I do to pass a variable into print() using QTimer?
connect(timer, SIGNAL(timeout()), this, SLOT(print(val)));
Qt signal/slot connections don't work that way. The text within the SIGNAL() and SLOT() macros has to be the signature of the signal/slot method, verbatim; you can't put variables or other non-method-signature text there.
If you look at your program's stdout while it runs, you'll see an error message printed by connect() telling you that it can't find any slot-method named print(val).
If you want to provide a separate value for your slot, you could either make val a member-variable of your class, and have print() look at the member variable instead of an argument, or you could use an intermediary slot, like this:
public slots:
void print(int val);
void print42() {print(val);}
... and then connect your signal to SLOT(print42()) instead.

Trouble connecting loadFinished SIGNAL to custom SLOT

I'm new to Qt, C++ and signals and slots. I'm trying to load in a webpage. Then set a label_3's text to the title of the webpage. To do this I figured I had to connect the loadFinished signal to my custom function. But I'm having trouble doing just that.
I've read up on the manual, different examples and other questions, but I'm stuck.
This is a excerpt from code I have so far.
How do I properly connect the signal loadFinished() to my function labelSetText()?
main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
void MainWindow::on_pushButton_clicked()
{
QString webAdress = ui->lineEdit->text();
QWebView *view = ui->webView;
view->load(QUrl(webAdress));
QString taxt = view->title();
connect(&view, SIGNAL(loadFinished(bool)),
this, SLOT(labelSetText(taxt)));
QWebPage * webPage = view->page();
}
void MainWindow::labelSetText(QString titleStr)
{
ui->label_3->setText(titleStr);
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QWidget>
namespace Ui {
class MainWindow;
}
class MainWindow : public QWidget
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
void labelSetText(QString titleStr);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
EDIT:
This is the error I get
E:\_Programming\C++\playAround\mainwindow.cpp:37: error: no matching function for call to 'MainWindow::connect(QWebView**, const char*, MainWindow* const, const char*)'
this, SLOT(labelSetText(taxt)));
^
That's not how connections work. A signal-slot connection can only pass data from the signal into the slot. It can't pass arbitrary variables like you do. The only way you could write your connect statement is as follows (the this argument is unnecessary):
connect(view, SIGNAL(loadFinished(bool)), SLOT(labelSetText(QString)));
This of course doesn't work, because the signal and slot are incompatible. You of course don't need the intermediate slot, since a label already has the slot you want, but it doesn't help:
connect(view, SIGNAL(loadFinished(bool)), ui->label_3, SLOT(setText(QString)));
Note that you should not have connect(&view, ... since view is already a pointer-to-QObject.
To do it, you need to leverage C++11:
connect(view, &QWebView::loadFinished, [=,this](){
this->ui->label_3->setText(taxt);
});
The lambda syntax translates into a functor class instance with copies of taxt and this as members. The compiler essentially creates the following, on the fly:
class Functor_1 {
MainWindow * _this;
QString taxt;
public:
MyFunctor_1(MainWindow * a1, const QString & a2) : _this(a1), taxt(a2) {}
void operator() {
_this->ui->label_3->setText(taxt);
}
}
...
connect(view, &QWebView::loadFinished, Functor_1(this, taxt));
Of course this means that if you want to use Qt 4 signals and slots, you need to add the taxt member to your MainWindow class, and create a slot to do what the functor does. So, for Qt 4:
class MainWindow : public QMainWindow {
Q_OBJECT
QString m_taxt;
Q_SLOT void loadFinished() {
ui->label_3->setText(m_taxt);
}
...
Q_SLOT void on_pushButton_clicked() {
QString webAdress = ui->lineEdit->text();
QWebView *view = ui->webView;
view->load(QUrl(webAdress));
m_taxt = view->title();
connect(view, SIGNAL(loadFinished(bool)), SLOT(loadFinished());
...
}
};
Note that you shouldn't be connecting repeatedly. For Qt 4 style connection, move the connect to MainWindow's constructor. For Qt 5 style connection, you need to break the connection once it fires.

How to connect a signal from a thread to a slot?

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 :)