Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Accessing MainWindow object from another class



  • Hey,

    If I want to create a Qt widget application, I have my MainWindow.ui, MainWindow.cpp and MainWindow.cpp. Assuming I have another class called TestClass in which I want to call a slot that is implemented the MainWindow class, how can I access the MainWindow Object, which is normally created in main.cpp? If

    For Example:

    void TestClass::startDeviceDiscovery(){
        discoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
        discoveryAgent->setLowEnergyDiscoveryTimeout(5000);
        connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, &MainWindowObject, &MainWindow::deviceDiscovered); //deviceDiscovered is my Slot implemented in MainWindow.cpp
        discoveryAgent->start();
    }
    
    

    So what would be my &MainWindowObject? My main:

    #include "MainWindow.h"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
        return a.exec();
    
    }
    

    So if I create the MainWindow Object outside the main function I cannot use it in my TestClass because I cannot include main there. What is the best way to solve my problem?


  • Qt Champions 2019

    Pass the TestClass a pointer to your mainwindow.



  • @Christian-Ehrlicher Thank you for the fast reply. I am a beginner to C++ and OOP, so the question is how? About the constructor? But I thought when I pass a TestClass Pointer to my MainWindow then I would still not be able to pass the MainWindow Object as a receiver in my connect() function because my TestClass still does not know anything about MainWindow. therefore I assumed that I have to make the MainWindow Object from my main.cpp globally..


  • Qt Champions 2019

    It's up to you how you pass it to your class - either per ctor or as separate function call.


  • Qt Champions 2019

    @TUStudi For testing I suggest you take a look at: https://doc.qt.io/qt-5/qtest-overview.html
    For testing you do not have to create your main window inside main.



  • Hi, not sure if i got your question right. But you can follow the example below. In TestClass.h you have to first declare the signal testClassSignal(). so whenever the signal is invoked in testClass.cpp, the slot in MainWindow.cpp called testClassSlot will be invoked in MainWindow.cpp. I hope it helps.

    MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui( new Ui:MainWindow)
    {
     TestClass * testClass = new TestClass();
    initConnections();
    }
    
    void MainWindow::initConnections()
    {
    QObject::connect(testClass, SIGNAL(testClassSignal()), this, SLOT(testClassSlot()),Qt::AutoConnection);
    }
    
    void MainWindow::testClassSlot()
    {
    
    }
    
    void TestClass::TestClass()
    {
    
    }
    
    


  • @TUStudi
    Is TestClass just for testing? Otherwise the more usual pattern is not to access MainWindow from other classes, but rather the other way round. Have MainWindow do the connect of the TestClass signal to its own slot.



  • @jsulm said in Accessing MainWindow object from another class:

    @TUStudi For testing I suggest you take a look at: https://doc.qt.io/qt-5/qtest-overview.html
    For testing you do not have to create your main window inside main.

    Thank you, but it is not a class for testing, it is just a andom class name ;)

    @qt-1234 said in Accessing MainWindow object from another class:

    Hi, not sure if i got your question right. But you can follow the example below. In TestClass.h you have to first declare the signal testClassSignal(). so whenever the signal is invoked in testClass.cpp, the slot in MainWindow.cpp called testClassSlot will be invoked in MainWindow.cpp. I hope it helps.

    MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui( new Ui:MainWindow)
    {
     TestClass * testClass = new TestClass();
    initConnections();
    }
    
    void MainWindow::initConnections()
    {
    QObject::connect(testClass, SIGNAL(testClassSignal()), this, SLOT(testClassSlot()),Qt::AutoConnection);
    }
    
    void MainWindow::testClassSlot()
    {
    
    }
    
    void TestClass::TestClass()
    {
    
    }
    
    

    Thank you tu @qt-1234. In my case, the Signal is not emitted by the Testclass but by an object of &QBluetoothDeviceDiscoveryAgent and the Signal is the &QBluetoothDeviceDiscoveryAgent::deviceDiscovered it is emitted every time a new Bluetooth device is discovered. So that won't work, will it?

    connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, MainWindowObject, MainWindowSlot);
    

  • Qt Champions 2019

    @TUStudi said in Accessing MainWindow object from another class:

    So that won't work, will it?

    It should. Did you try?



  • @TUStudi
    Maybe it's just me, but I still wouldn't connect to a MainWindow slot from any other class. EDIT To be clear, I would never #include mainwindow.h into TestClass, so there is no way the latter could access &MainWindow::deviceDiscovered.

    If you only ever create one instance of discoveryAgent once, make that exportable from TestClass to MainWindow so that the latter can do the connect on that one instance. If you create instances dynamically, invent a signal from TestClass passing the instance of QBluetoothDeviceDiscoveryAgent as an argument upon creation, and have MainWindow pick that up and put its slot on the instance's deviceDiscovered.

    That's how I would do it. If a Qt expert says I'm wrong, please let me know!

    @jsulm I'm not sure, but I think the OP is saying he can't do that connect from MainWindow because the instance is newed inside TestClass::startDeviceDiscovery()?



  • @JonB said in Accessing MainWindow object from another class:

    @TUStudi
    Maybe it's just me, but I still wouldn't connect to a MainWindow slot from any other class. EDIT To be clear, I would never #include mainwindow.h into TestClass, so there is no way the latter could access &MainWindow::deviceDiscovered.

    If you only ever create one instance of discoveryAgent once, make that exportable from TestClass to MainWindow so that the latter can do the connect on that one instance. If you create instances dynamically, invent a signal from TestClass passing the instance of QBluetoothDeviceDiscoveryAgent as an argument upon creation, and have MainWindow pick that up and put its slot on the instance's deviceDiscovered.

    That's how I would do it. If a Qt expert says I'm wrong, please let me know!

    @jsulm I'm not sure, but I think the OP is saying he can't do that connect from MainWindow because the instance is newed inside TestClass::startDeviceDiscovery()?

    Hello JonB,
    as I wrote in an other Thread, the idea of ​​mine is that I separate the actual logic from the UI. The communication should take place via my controller (my MainWindow.cpp). I.e. the user clicks on the "Scan for Bluetooth devices" button in the UI, this triggers a "button clicked" slot in my MainWindow.cpp. The startDeviceDiscovery method of the "TestClass" (which should contain the logic) is then called in this slot. If devices are then found, my MainWindow is notified by a signal (deviceDiscovered) and this MainWIndow interacts with the UI by entering the devices in the combo box.

    553828a0-c21f-406e-92d2-7b07d312184e-image.png

    But I just notice that a user in my other thread replied, which I unfortunately didn't see.

    Your MainWindow should have a member variable of the TestClass class.
    Your TestClass class should provide an API that your MainWindow can connect to and also that provides whatever data is needed.

    So, do you know how to do that? I thought my problem can be solved If I just make my MainWIndow Object, which is created in my main class, globally.
    But it seems I haven't understood the principle of OOP.



  • @TUStudi said in Accessing MainWindow object from another class:

    I thought my problem can be solved If I just make my MainWIndow Object, which is created in my main class, globally.

    I just would not do this. Make MainWindow know about the other classes, and have it do the connect()s, rather than having the other classes know about MainWindow and they do the connect(). Or, just possibly, put your "controller" class elsewhere than in MainWindow.



  • @TUStudi Hi. Then you will have to first initialize the discoveryAgent class first just like how i did for the testClass before doing the connection of signal and slot.

    DiscoveryAgent * discoveryAgent = new DiscoveryAgent();
    


  • @JonB said in Accessing MainWindow object from another class:

    I just would not do this. Make MainWindow know about the other classes, and have it do the connect()s, rather than having the other classes know about MainWindow and they do the connect(). Or, just possibly, put your "controller" class elsewhere than in MainWindow.

    Thank you, here is my approach and it seems to work. I created a new Slot in my TestClass that is called, when a device is discovered. Then, in this Slot I emitted a Signal with the devicename that is then received by my mainwindow and mainwindow then does the work. Thus, my TestClass does not know anything about my MainWindow.

    void TestClass::startDeviceDiscovery(){
        discoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
        discoveryAgent->setLowEnergyDiscoveryTimeout(5000);
        connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &TestClass::deviceDiscoveredSlot);
        discoveryAgent->start();
    }
    
    void TestClass::deviceDiscoveredSlot(const QBluetoothDeviceInfo &device)
    {
        emit deviceDiscoveredSignal(device);
    }
    
    

    And in my MainWindow Class:

    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        connect(bluetoothModel, &BluetoothModel::deviceDiscoveredSignal, this, &BluetoothController::deviceDiscovered);
    }
    
    
    void MainWindow::deviceDiscovered(const QBluetoothDeviceInfo &device)
    {
        ui->devicesList->addItem(device.name());
    }
    
    

    The only annoying thing is that I have more code with it because I can't call a slot from MainWindow directly. But it works.



  • @TUStudi
    I do believe this is the neater way to go. Your MainWindow and your TestClass are now quite independent of each other. If you had done "call a slot from MainWindow directly" TestClass would not be (re-)usable without MainWindow.


Log in to reply