1. Signals and slots
The subform can notify the main form (MainWindow
) of interface changes through the signal and slot mechanism. Here is a simple example that demonstrates how to define a signal in a subform and then wire up this signal in the main form to respond when changes occur.
// ChildWindow.h #ifndef CHILDWINDOW_H #define CHILDWINDOW_H #include <QWidget> class QPushButton; class ChildWindow : public QWidget { Q_OBJECT public: explicit ChildWindow(QWidget *parent = nullptr); signals: void dataChanged(const QString & amp;newData); private slots: void emitDataChangedSignal(); private: QPushButton *changeDataButton; }; #endif // CHILDWINDOW_H
// ChildWindow.cpp #include "ChildWindow.h" #include <QPushButton> ChildWindow::ChildWindow(QWidget *parent) : QWidget(parent) { changeDataButton = new QPushButton("Change Data", this); connect(changeDataButton, & amp;QPushButton::clicked, this, & amp;ChildWindow::emitDataChangedSignal); } void ChildWindow::emitDataChangedSignal() { //Emit a signal to notify the data of changes emit dataChanged("New data from ChildWindow"); }
Then, connect this signal in MainWindow
so that the corresponding slot function is executed when the data changes:
// MainWindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> class QLabel; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); public slots: void handleDataChanged(const QString & amp;newData); private: QLabel *statusLabel; }; #endif // MAINWINDOW_H
// MainWindow.cpp #include "MainWindow.h" #include "ChildWindow.h" #include <QLabel> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { statusLabel = new QLabel("No data changes", this); setCentralWidget(statusLabel); //Create subform ChildWindow *childWindow = new ChildWindow(this); setCentralWidget(childWindow); // Connect the signal of the subform to the slot function of the main form connect(childWindow, &ChildWindow::dataChanged, this, &MainWindow::handleDataChanged); } void MainWindow::handleDataChanged(const QString & amp;newData) { //Update the interface of the main form when the data changes statusLabel->setText("Data changed: " + newData); }
2. Save the main form pointer
One possible approach is to use a public method that calls the main form directly. Here is a simple example
// MainWindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> class QLabel; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); public: void updateStatus(const QString & amp;newStatus); private: QLabel *statusLabel; }; #endif // MAINWINDOW_H
// MainWindow.cpp #include "MainWindow.h" #include "ChildWindow.h" #include <QLabel> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { statusLabel = new QLabel("No data changes", this); setCentralWidget(statusLabel); //Create subform ChildWindow *childWindow = new ChildWindow(this); setCentralWidget(childWindow); //Set the main form pointer to the subform so that the subform can directly call the main form's methods childWindow->setMainWindow(this); } void MainWindow::updateStatus(const QString & amp;newStatus) { //Update the interface status of the main form statusLabel->setText(newStatus); }
// ChildWindow.h #ifndef CHILDWINDOW_H #define CHILDWINDOW_H #include <QWidget> class QPushButton; class MainWindow; class ChildWindow : public QWidget { Q_OBJECT public: explicit ChildWindow(MainWindow *mainWindow, QWidget *parent = nullptr); private slots: void changeData(); private: QPushButton *changeDataButton; MainWindow *mainWindow; //Save the main form pointer }; #endif // CHILDWINDOW_H
// ChildWindow.cpp #include "ChildWindow.h" #include "MainWindow.h" #include <QPushButton> ChildWindow::ChildWindow(MainWindow *mainWindow, QWidget *parent) : QWidget(parent), mainWindow(mainWindow) { changeDataButton = new QPushButton("Change Data", this); connect(changeDataButton, & amp;QPushButton::clicked, this, & amp;ChildWindow::changeData); } void ChildWindow::changeData() { // Directly call the method of the main form in the subform to notify the data of changes mainWindow->updateStatus("Data changed from ChildWindow"); }
3. Singleton or global variable
4. Event filter
Using event filters is a way to implement subform notifications to the main form in Qt. In this approach, the subform can intercept specific types of events and then perform appropriate actions. Here is a simple example of how to use event filters to achieve this:
// MainWindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> class QLabel; class QEvent; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); public slots: void updateStatus(const QString & amp;newStatus); protected: bool eventFilter(QObject *watched, QEvent *event) override; private: QLabel *statusLabel; }; #endif // MAINWINDOW_H
// MainWindow.cpp #include "MainWindow.h" #include <QLabel> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { statusLabel = new QLabel("No data changes", this); setCentralWidget(statusLabel); //Install event filter installEventFilter(this); } void MainWindow::updateStatus(const QString & amp;newStatus) { //Update the interface status of the main form statusLabel->setText(newStatus); } bool MainWindow::eventFilter(QObject *watched, QEvent *event) { // Check whether it is a specific event type. Here we use the mouse button release event as an example if (event->type() == QEvent::MouseButtonRelease) { // Handle events, such as calling the slot function of the main form updateStatus("Data changed from event filter"); return true; // Indicates that the event has been processed } // For other events, call the event filter of the base class return QMainWindow::eventFilter(watched, event); }
In this example, the main window inherits QMainWindow
and implements the eventFilter
method. In the constructor, we installed the event filter by calling installEventFilter(this)
.
In the event filter, we check whether the event type is QEvent::MouseButtonRelease
. If so, perform the corresponding operation, such as calling the main form’s slot function updateStatus
. This is just an example, you can choose different event types and corresponding processing logic according to actual needs.
Then, when the subform needs to notify the main form of a change, it can trigger these events at the appropriate time, thereby being captured by the main form’s event filter.
5. Use polymorphism to achieve
Polymorphism is a feature of object-oriented programming that allows objects of different types to be called by the same interface, thereby achieving more flexible code organization and expansion. In this context, if you want to use polymorphism to implement subform notifications to the main form, you can do so by defining a common base class and then overriding the virtual functions of that base class in the subform. The main form can call this virtual function through the base class pointer.
Here is a simple example implemented using polymorphism:
// MainWindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> class QLabel; class BaseNotifier : public QObject { Q_OBJECT public: virtual void notify(const QString & amp;message) = 0; }; class MainWindow : public QMainWindow, public BaseNotifier { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); public slots: void notify(const QString & amp;message) override; private: QLabel *statusLabel; }; #endif // MAINWINDOW_H
// MainWindow.cpp #include "MainWindow.h" #include <QLabel> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { statusLabel = new QLabel("No data changes", this); setCentralWidget(statusLabel); } void MainWindow::notify(const QString & amp;message) { //Update the interface status of the main form statusLabel->setText(message); }
// ChildWindow.h #ifndef CHILDWINDOW_H #define CHILDWINDOW_H #include <QWidget> #include "MainWindow.h" class QPushButton; class ChildWindow : public QWidget, public BaseNotifier { Q_OBJECT public: ChildWindow(MainWindow *mainWindow, QWidget *parent = nullptr); // Implement the virtual function of the base class void notify(const QString & amp;message) override; private slots: void changeData(); private: QPushButton *changeDataButton; MainWindow *mainWindow; //Save the main form pointer }; #endif // CHILDWINDOW_H
// ChildWindow.cpp #include "ChildWindow.h" #include <QPushButton> ChildWindow::ChildWindow(MainWindow *mainWindow, QWidget *parent) : QWidget(parent), mainWindow(mainWindow) { changeDataButton = new QPushButton("Change Data", this); connect(changeDataButton, & amp;QPushButton::clicked, this, & amp;ChildWindow::changeData); } void ChildWindow::changeData() { //Call the virtual function of the main form in the subform to notify the data of changes mainWindow->notify("Data changed from ChildWindow"); } void ChildWindow::notify(const QString & amp;message) { //Implement the virtual function of the base class in the subform, where other processing can be performed // For example, if the subform itself also needs to handle notifications qDebug() << "ChildWindow received notification:" << message; }
In this example, BaseNotifier
is a pure virtual base class that defines a pure virtual function notify
. Both MainWindow
and ChildWindow
inherit from BaseNotifier
and implement the notify
function. Subforms can notify the main form of changes by calling virtual functions of the main form. This approach allows for some flexibility because you can do specific processing in the subform rather than simply notifying the main form.
6. How to use message queue?
The way in which message queues are used to notify the main form of subforms requires the introduction of some custom signals and slots mechanisms in Qt. This method creates a message queue and delivers the message to the main form through a custom message class. Here’s a simple example:
// Message.h #ifndef MESSAGE_H #define MESSAGE_H #include <QString> class Message { public: enum MessageType { DataChanged }; Message(MessageType type, const QString & amp;data = QString()) : type(type), data(data) {} MessageType type; QString data; }; #endif // MESSAGE_H
// MainWindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> class QLabel; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); public slots: void handleMessage(const Message & amp;message); private: QLabel *statusLabel; }; #endif // MAINWINDOW_H
// MainWindow.cpp #include "MainWindow.h" #include "Message.h" #include <QLabel> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { statusLabel = new QLabel("No data changes", this); setCentralWidget(statusLabel); } void MainWindow::handleMessage(const Message & amp;message) { //Perform corresponding operations based on message type if (message.type == Message::DataChanged) { //Update the interface status of the main form statusLabel->setText(message.data); } }
// ChildWindow.h #ifndef CHILDWINDOW_H #define CHILDWINDOW_H #include <QWidget> #include <QQueue> #include "Message.h" class QPushButton; class ChildWindow : public QWidget { Q_OBJECT public: ChildWindow(QWidget *parent = nullptr); signals: void messageReady(const Message & amp;message); private slots: void changeData(); private: QPushButton *changeDataButton; QQueue<Message> messageQueue; }; #endif // CHILDWINDOW_H
// ChildWindow.cpp #include "ChildWindow.h" #include "Message.h" #include <QPushButton> ChildWindow::ChildWindow(QWidget *parent) : QWidget(parent) { changeDataButton = new QPushButton("Change Data", this); connect(changeDataButton, & amp;QPushButton::clicked, this, & amp;ChildWindow::changeData); } void ChildWindow::changeData() { //Create a message and put it into the message queue Message message(Message::DataChanged, "Data changed from ChildWindow"); messageQueue.enqueue(message); //Send a signal to notify that the message is ready emit messageReady(message); }
// main.cpp #include <QApplication> #include "MainWindow.h" #include "ChildWindow.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow mainWindow; ChildWindow childWindow; // Connect the message signal of the subform to the slot function of the main form QObject::connect( & amp;childWindow, & amp;ChildWindow::messageReady, & amp;mainWindow, & amp;MainWindow::handleMessage); mainWindow.show(); childWindow.show(); return a.exec(); }
In this example, the Message
class defines a message structure that contains the message type and data. The handleMessage
slot function in MainWindow
is responsible for processing the received message and performing corresponding operations according to the message type.
The changeData
slot function in ChildWindow
creates a message and puts it into the message queue, then emits the message via the messageReady
signal. In main.cpp
, use QObject::connect
to connect the subform’s messageReady
signal to the main form’s handleMessage
Slot function.
This approach requires ensuring that the message queue is processed in the main loop so that messages are processed correctly in the UI thread.