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_H
MainWindow.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_H
MyClassA.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_H
MyClassB.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
-
@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.
-
@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!
-
@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.
-
Just remove the
QPoint
between 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! 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
MyClassB
because 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
a
andb
and 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"
-
@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() -
@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_H
MainWindow.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_H
MyClassA.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_H
MyClassB.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>
-