How to use qt to notify the mainwindow interface of changes in the subform?

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.