Problem understanding Signals/Slots
-
I'm using Qt C++ 6.6.1, Qt Creator 12.0.2, MinGW 64-bit, on Windows 11 for creating a desktop application. This is my first attempt to use Signals/Slots and I need some help. I have read Qt's "Signals and Slots" documentation and looked at a few examples.
Further below is the full source code, but here is the snippet from "MainWindow.cpp" that the compiler does not like:
QObject::connect(&b, &MyClassB::mySignal(QPoint), &a, &MyClassA::setPoint(QPoint));The compiler indicates "Call to non-static member function without an object argument" and "'QPoint' does not refer to a value".
The simplified example is supposed to emit a Signal when the mouse is clicked in the MyClassB which passes the mouse position to a Slot in MyClassA.
I have struggled with this for 3 days and can't seem to resolve it without some help. Here is the code:
MainWindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "myclassa.h" #include "myclassb.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: private: Ui::MainWindow *ui; MyClassA a; MyClassB b; }; #endif // MAINWINDOW_HMainWindow.cpp
#include <QPoint> #include "mainwindow.h" #include "ui_mainwindow.h" #include "myclassa.h" #include "myclassb.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); QObject::connect(&b, &MyClassB::mySignal(QPoint), &a, &MyClassA::setPoint(QPoint)); } MainWindow::~MainWindow() { delete ui; }myClassA.h
#ifndef MYCLASSA_H #define MYCLASSA_H #include <QGraphicsView> #include <QObject> #include <QPoint> #include <QWidget> class MyClassA : public QGraphicsView { Q_OBJECT public: MyClassA(QWidget *parent = nullptr); ~MyClassA(); public slots: void setPoint (QPoint point); private: QPoint p; }; #endif // MYCLASSA_HMyClassA.cpp
#include <QChart> #include <QDebug> #include <QPoint> #include <QGraphicsView> #include "myclassA.h" MyClassA::MyClassA(QWidget *parent) : QGraphicsView(parent) { } MyClassA::~MyClassA() { } void MyClassA::setPoint (QPoint point) { qDebug("MyClassA::setPoint"); p = point; }MyClassB.h
#ifndef MYCLASSB_H #define MYCLASSB_H #include <QChartView> #include <QMouseEvent> #include <QObject> #include <QPoint> #include <QWidget> class MyClassB : public QChartView { Q_OBJECT public: MyClassB(QWidget *parent = nullptr); ~MyClassB(); signals: void mySignal(QPoint p); protected: void mousePressEvent(QMouseEvent *event) override; }; #endif // MYCLASSB_HMyClassB.cpp
#include <QChartView> #include <QMouseEvent> #include <QObject> #include <QWidget> #include "myclassB.h" MyClassB::MyClassB(QWidget *parent) : QChartView(parent) { } MyClassB::~MyClassB() { } void MyClassB::mousePressEvent(QMouseEvent* event) { qDebug("MyClassB::mousePressEvent"); QPoint pos = event->pos(); emit mySignal(pos); QChartView::mousePressEvent(event); /* pass-on the event to the base class */ }MainWindow.ui
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>600</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="QWidget" name="centralwidget"> <widget class="MyClassB" name="myView"> <property name="geometry"> <rect> <x>160</x> <y>90</y> <width>321</width> <height>281</height> </rect> </property> </widget> </widget> <widget class="QMenuBar" name="menubar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>26</height> </rect> </property> </widget> <widget class="QStatusBar" name="statusbar"/> </widget> <customwidgets> <customwidget> <class>MyClassB</class> <extends>QGraphicsView</extends> <header location="global">myclassb.h</header> </customwidget> </customwidgets> <resources/> <connections/> </ui> -
you cannot use signal and slot with your custom class until you register it to the meta object compiler moc
-
I'm using Qt C++ 6.6.1, Qt Creator 12.0.2, MinGW 64-bit, on Windows 11 for creating a desktop application. This is my first attempt to use Signals/Slots and I need some help. I have read Qt's "Signals and Slots" documentation and looked at a few examples.
Further below is the full source code, but here is the snippet from "MainWindow.cpp" that the compiler does not like:
QObject::connect(&b, &MyClassB::mySignal(QPoint), &a, &MyClassA::setPoint(QPoint));The compiler indicates "Call to non-static member function without an object argument" and "'QPoint' does not refer to a value".
The simplified example is supposed to emit a Signal when the mouse is clicked in the MyClassB which passes the mouse position to a Slot in MyClassA.
I have struggled with this for 3 days and can't seem to resolve it without some help. Here is the code:
MainWindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "myclassa.h" #include "myclassb.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: private: Ui::MainWindow *ui; MyClassA a; MyClassB b; }; #endif // MAINWINDOW_HMainWindow.cpp
#include <QPoint> #include "mainwindow.h" #include "ui_mainwindow.h" #include "myclassa.h" #include "myclassb.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); QObject::connect(&b, &MyClassB::mySignal(QPoint), &a, &MyClassA::setPoint(QPoint)); } MainWindow::~MainWindow() { delete ui; }myClassA.h
#ifndef MYCLASSA_H #define MYCLASSA_H #include <QGraphicsView> #include <QObject> #include <QPoint> #include <QWidget> class MyClassA : public QGraphicsView { Q_OBJECT public: MyClassA(QWidget *parent = nullptr); ~MyClassA(); public slots: void setPoint (QPoint point); private: QPoint p; }; #endif // MYCLASSA_HMyClassA.cpp
#include <QChart> #include <QDebug> #include <QPoint> #include <QGraphicsView> #include "myclassA.h" MyClassA::MyClassA(QWidget *parent) : QGraphicsView(parent) { } MyClassA::~MyClassA() { } void MyClassA::setPoint (QPoint point) { qDebug("MyClassA::setPoint"); p = point; }MyClassB.h
#ifndef MYCLASSB_H #define MYCLASSB_H #include <QChartView> #include <QMouseEvent> #include <QObject> #include <QPoint> #include <QWidget> class MyClassB : public QChartView { Q_OBJECT public: MyClassB(QWidget *parent = nullptr); ~MyClassB(); signals: void mySignal(QPoint p); protected: void mousePressEvent(QMouseEvent *event) override; }; #endif // MYCLASSB_HMyClassB.cpp
#include <QChartView> #include <QMouseEvent> #include <QObject> #include <QWidget> #include "myclassB.h" MyClassB::MyClassB(QWidget *parent) : QChartView(parent) { } MyClassB::~MyClassB() { } void MyClassB::mousePressEvent(QMouseEvent* event) { qDebug("MyClassB::mousePressEvent"); QPoint pos = event->pos(); emit mySignal(pos); QChartView::mousePressEvent(event); /* pass-on the event to the base class */ }MainWindow.ui
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>600</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="QWidget" name="centralwidget"> <widget class="MyClassB" name="myView"> <property name="geometry"> <rect> <x>160</x> <y>90</y> <width>321</width> <height>281</height> </rect> </property> </widget> </widget> <widget class="QMenuBar" name="menubar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>26</height> </rect> </property> </widget> <widget class="QStatusBar" name="statusbar"/> </widget> <customwidgets> <customwidget> <class>MyClassB</class> <extends>QGraphicsView</extends> <header location="global">myclassb.h</header> </customwidget> </customwidgets> <resources/> <connections/> </ui>@EricR said in Problem understanding Signals/Slots:
QObject::connect(&b, &MyClassB::mySignal(QPoint),
&a, &MyClassA::setPoint(QPoint));This is not a valid syntax. Please see the documentation or the wiki.
-
you cannot use signal and slot with your custom class until you register it to the meta object compiler moc
@Ronel_qtmaster Thanks for the quick reply. This is my first attempt to use signals/slots and I don't know what you mean by "register it to the meta object compiler". Do you have time to show how to do that, or provide a link that explains it. Thanks!
-
@EricR said in Problem understanding Signals/Slots:
QObject::connect(&b, &MyClassB::mySignal(QPoint),
&a, &MyClassA::setPoint(QPoint));This is not a valid syntax. Please see the documentation or the wiki.
@Christian-Ehrlicher Thanks for the reply. I have read the documentation that you provided, but I don't yet understand what is wrong with the syntax.
-
@Ronel_qtmaster Thanks for the quick reply. This is my first attempt to use signals/slots and I don't know what you mean by "register it to the meta object compiler". Do you have time to show how to do that, or provide a link that explains it. Thanks!
Just remove the
QPointbetween the brackets, like @Christian-Ehrlicher pointed out and you're good to go.QObject::connect(&b, &MyClassB::mySignal(QPoint), &a, &MyClassA::setPoint(QPoint)); // ^ here@Ronel_qtmaster was referring to this:
This is only needed when you want to send your custom class with a signal, but since it's a
QPoint, you don't have to worry.Edit:
Actually it's this:
(remove function "call" completely, since you are passing a function pointer toconnect)
The arguments are identified from looking at the signal and slot.. if they don't match, you would get a warning / compile error anyway.QObject::connect(&b, &MyClassB::mySignal, &a, &MyClassA::setPoint); -
@Ronel_qtmaster Thanks for the quick reply. This is my first attempt to use signals/slots and I don't know what you mean by "register it to the meta object compiler". Do you have time to show how to do that, or provide a link that explains it. Thanks!
@EricR this is the syntax you want to use now compare with what you wrote
QObject::connect(&a, &Counter::valueChanged,
&b, &Counter::setValue); -
@EricR this is the syntax you want to use now compare with what you wrote
QObject::connect(&a, &Counter::valueChanged,
&b, &Counter::setValue);@Ronel_qtmaster Thanks! I changed the line of code in MainWindow.cpp to the following and now it compiles:
QObject::connect(&b, &MyClassB::mySignal, &a, &MyClassA::setPoint);When I run it and I mouse-click in the MyClassB gui object, MyClassB does properly print the qDebug "MyClassB::mousePressEvent" (which is good), which implies the following line of code was reached/executed:
emit mySignal(pos);but MyClassA does not print the qDebug "MyClassA::setPoint", which implies the Signal/Slot mechanism did not work. What am I missing (or not understanding)?
-
@Ronel_qtmaster Thanks! I changed the line of code in MainWindow.cpp to the following and now it compiles:
QObject::connect(&b, &MyClassB::mySignal, &a, &MyClassA::setPoint);When I run it and I mouse-click in the MyClassB gui object, MyClassB does properly print the qDebug "MyClassB::mousePressEvent" (which is good), which implies the following line of code was reached/executed:
emit mySignal(pos);but MyClassA does not print the qDebug "MyClassA::setPoint", which implies the Signal/Slot mechanism did not work. What am I missing (or not understanding)?
@EricR said in Problem understanding Signals/Slots:
but MyClassA does not print the qDebug "MyClassA::setPoint", which implies the Signal/Slot mechanism did not work. What am I missing (or not understanding)?
You used widget promotion, right?
So these classes (objects) you are connecting:
@EricR said in Problem understanding Signals/Slots:
private: Ui::MainWindow *ui; MyClassA a; MyClassB b; };are not the ones you are clicking on...
You see the debug output in
MyClassBbecause the promoted widget also has themouseClickEvent, well, because it's the same code, but theemit mySignal()goes nowhere...You can safely remove your private members
aandband connect to your actual objects you promoted in QtDesigner like this:connect(ui->ObjectnameOfB, &MyClassB::mySignal, ui->ObjectnameOfA, &MyClassA::setPoint);BTW:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); // <- this function creates and inits the object of your promoted class for you // Then it's available under "ui->objectname" -
@Ronel_qtmaster Thanks! I changed the line of code in MainWindow.cpp to the following and now it compiles:
QObject::connect(&b, &MyClassB::mySignal, &a, &MyClassA::setPoint);When I run it and I mouse-click in the MyClassB gui object, MyClassB does properly print the qDebug "MyClassB::mousePressEvent" (which is good), which implies the following line of code was reached/executed:
emit mySignal(pos);but MyClassA does not print the qDebug "MyClassA::setPoint", which implies the Signal/Slot mechanism did not work. What am I missing (or not understanding)?
@EricR just create a signal function with a QPoint as argument, and a slot function with a QPoint as argument.Now when the mouse this pressed, get the current position of the mouse with
QPoint QMouseEvent::globalPos()const
or in the function set Point, just print the point passed as argument using point.x() and point.y() -
@EricR said in Problem understanding Signals/Slots:
but MyClassA does not print the qDebug "MyClassA::setPoint", which implies the Signal/Slot mechanism did not work. What am I missing (or not understanding)?
You used widget promotion, right?
So these classes (objects) you are connecting:
@EricR said in Problem understanding Signals/Slots:
private: Ui::MainWindow *ui; MyClassA a; MyClassB b; };are not the ones you are clicking on...
You see the debug output in
MyClassBbecause the promoted widget also has themouseClickEvent, well, because it's the same code, but theemit mySignal()goes nowhere...You can safely remove your private members
aandband connect to your actual objects you promoted in QtDesigner like this:connect(ui->ObjectnameOfB, &MyClassB::mySignal, ui->ObjectnameOfA, &MyClassA::setPoint);BTW:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); // <- this function creates and inits the object of your promoted class for you // Then it's available under "ui->objectname"@Pl45m4 Problem solved. Thanks to everyone who contributed. I am going to post below the final version of the source code that contains all of the changes that occurred as a result of this topic-thread. Perhaps this example will be useful to another first time user of Qt Signals/Slots.
main.cpp
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }MainWindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_HMainWindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include "myclassa.h" #include "myclassb.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); connect(ui->myView, &MyClassB::mySignal, ui->myLabel, &MyClassA::setPoint); } MainWindow::~MainWindow() { delete ui; }myClassA.h
#ifndef MYCLASSA_H #define MYCLASSA_H #include <QLabel> #include <QObject> #include <QPoint> #include <QWidget> class MyClassA : public QLabel { Q_OBJECT public: MyClassA(QWidget *parent = nullptr); ~MyClassA(); public slots: void setPoint (QPoint point); private: QPoint p; }; #endif // MYCLASSA_HMyClassA.cpp
#include <QDebug> #include <QLabel> #include <QPoint> #include "myclassA.h" MyClassA::MyClassA(QWidget *parent) : QLabel(parent) { } MyClassA::~MyClassA() { } void MyClassA::setPoint (QPoint point) { qDebug("MyClassA::setPoint"); p = point; }MyClassB.h
#ifndef MYCLASSB_H #define MYCLASSB_H #include <QChartView> #include <QMouseEvent> #include <QObject> #include <QPoint> #include <QWidget> class MyClassB : public QChartView { Q_OBJECT public: MyClassB(QWidget *parent = nullptr); ~MyClassB(); signals: void mySignal(QPoint p); protected: void mousePressEvent(QMouseEvent *event) override; }; #endif // MYCLASSB_HMyClassB.cpp
#include <QChartView> #include <QMouseEvent> #include <QWidget> #include "myclassB.h" MyClassB::MyClassB(QWidget *parent) : QChartView(parent) { } MyClassB::~MyClassB() { } void MyClassB::mousePressEvent(QMouseEvent* event) { qDebug("MyClassB::mousePressEvent"); QPoint pos = event->pos(); emit mySignal(pos); QChartView::mousePressEvent(event); /* pass-on the event to the base class */ }MainWindow.ui
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>600</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="QWidget" name="centralwidget"> <widget class="MyClassB" name="myView"> <property name="geometry"> <rect> <x>160</x> <y>90</y> <width>321</width> <height>281</height> </rect> </property> </widget> <widget class="MyClassA" name="myLabel"> <property name="geometry"> <rect> <x>570</x> <y>190</y> <width>121</width> <height>20</height> </rect> </property> <property name="text"> <string>TextLabel</string> </property> </widget> </widget> <widget class="QMenuBar" name="menubar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>26</height> </rect> </property> </widget> <widget class="QStatusBar" name="statusbar"/> </widget> <customwidgets> <customwidget> <class>MyClassB</class> <extends>QGraphicsView</extends> <header>myclassb.h</header> </customwidget> <customwidget> <class>MyClassA</class> <extends>QLabel</extends> <header location="global">myclassa.h</header> </customwidget> </customwidgets> <resources/> <connections/> </ui> -
E EricR has marked this topic as solved on