Access to UI elements from another class qt-c++



  • Hello,
    I have a problem with accessing ui elements from another class(with instance). I have a second QMainWindow in my application, I can access in mainWindow.cxx, write.cxx and secondWindow.cxx class all ui elements but not in read.cxx class. When I click on "pushButton2" on the "secondWindow", nothing happens, no reaction. My code looks like following. Where is my mistake? Thank you for any help.

    Main Window
    @
    -----------------------------mainWindow.h----------------------------------
    #ifndef __mainWindow_h
    #define __mainWindow_h

    #include "ui_mainWindow.h"
    #include "app.h"

    class mainWindow : public QMainWindow
    {
    friend class app;
    friend class write;
    igstkStandardClassBasicTraitsMacro(mainWindow, QMainWindow);
    Q_OBJECT

    public:
    igstkStateMachineMacro();

    mainWindow();
    virtual ~mainWindow();
    void createSignalAndSlots();

    public slots:
    void openSecondWindow();
    void mainWindowTest();

    private:
    Ui::mainWindow m_mainWindowUI;
    };
    #endif

    -----------------------------mainWindow.cxx----------------------------------
    #include "mainWindow.moc"
    #include "mainWindow.h"
    #include "secondWindow.h"
    #include "read.h"
    #include "write.h"

    mainWindow::mainWindow() : m_StateMachine(this)
    {
    m_mainWindowUI.setupUi(this);
    createSignalAndSlots();
    }

    mainWindow::~mainWindow(){}

    void mainWindow::createSignalAndSlots()
    {
    connect(m_mainWindowUI.menuOpenAction, SIGNAL(triggered()), this, SLOT(openSecondWindow()));
    connect(m_mainWindowUI.pushButton1, SIGNAL(clicked()), this, SLOT((mainWindowTest()));
    connect(m_mainWindowUI.pushButton2, SIGNAL(clicked()), write::instance(), SLOT((writeTest()));
    }

    void mainWindow::openSecondWindow()
    {
    secondWindow *secondWin = new secondWindow();
    secondWin->show();
    }

    void mainWindow::mainWindowTest()
    {
    m_mainWindowUI.pushButton1->setEnabled(true); //OK, it works
    }

    -----------------------------write.h----------------------------------
    #pragma once

    #include "mainWindow.h"

    class write : public QObject
    {
    Q_OBJECT

    public:
    static write *instance();
    write();
    virtual ~write() {}

    public slots:
    void writeTest();

    protected:
    app *m_writeUI;
    static write *m_write;

    private:
    };

    -----------------------------write.cxx----------------------------------
    #include <write.moc>
    #include "mainWindow.h"
    #include "write.h"

    write *write::m_write= NULL;

    write::write()
    {
    m_writeUI = dynamic_cast<app*>(QApplication::instance());
    }

    write *write::instance()
    {
    if(m_write == NULL)
    m_write = new write();

    return m_write;
    

    }

    void write::writeTest()
    {
    m_writeUI->m_mainWindowUI.mainQLabelTest->setText("main label test"); //OK, it works
    }
    @

    Second Window
    @
    -----------------------------secondWindow.h----------------------------------
    #ifndef __secondWindow_h
    #define __secondWindow_h

    #include "ui_secondWindow.h"
    #include "app.h"

    class secondWindow : public QMainWindow
    {
    friend class read;
    friend class app;
    igstkStandardClassBasicTraitsMacro(secondWindow, QMainWindow);
    Q_OBJECT

    public:
    igstkStateMachineMacro();

    secondWindow(QWidget *parent= 0);
    virtual ~secondWindow();
    void createSignalAndSlots();
    

    public slots:
    void secondWindowTest();

    private:
    Ui::secondWindow m_secondWindowUI;
    };
    #endif

    -----------------------------secondWindow.cxx----------------------------------
    #include "secondWindow.moc"
    #include "secondWindow.h"
    #include "read.h"

    secondWindow::secondWindow(QWidget *parent) :m_StateMachine(this)
    {
    m_secondWindowUI.setupUi(this);
    createSignalAndSlots();
    }

    secondWindow::~secondWindow(){}

    void secondWindow::createSignalAndSlots()
    {
    connect(m_secondWindowUI.pushButton1, SIGNAL(clicked()),this, SLOT(secondWindowTest()));
    connect(m_secondWindowUI.pushButton2, SIGNAL(clicked()), read::instance(), SLOT(readTest()));
    }

    void secondWindow::secondWindowTest()
    {
    m_secondWindowUI.pushButton1->setEnabled(true); //OK, it works
    }

    -----------------------------read.h----------------------------------
    #pragma once

    #include "secondWindow.h"

    class read : public QObject
    {
    Q_OBJECT

    public:
    static read *instance();
    read();
    virtual ~read() {}

    public slots:
    void readTest();

    protected:
    app *m_readUI;
    static read *m_read;

    private:
    };

    -----------------------------read.cxx----------------------------------
    #include <read.moc>
    #include "secondWindow.h"
    #include "read.h"

    read *read::m_read= NULL;

    read::read()
    {
    m_readUI = dynamic_cast<app*>(QApplication::instance());
    }

    read *read::instance()
    {
    if(m_read == NULL)
    m_read = new read();

    return m_read;
    

    }

    void read::readTest()
    {
    m_readUI->m_secondWindowUI.secondQLabelTest->setText("second label test"); //ERROR, it works but no reaction
    }
    @

    Main and GUI Initializations

    @
    -----------------------------app.h----------------------------------
    #pragma once

    class mainWindow;
    class secondWindow;

    class app : public QApplication
    {
    Q_OBJECT
    igstkStandardClassBasicTraitsMacro(app, QApplication);

    friend class read;
    friend class write;
    friend class mainWindow;
    friend class secondWindow;

    public:
    app(int& argc, char **argv);
    virtual ~app() {}

    protected:
    mainWindow *m_mainWin;
    secondWindow *m_secondWin;

    void setMainWindow(mainWindow *mainWindow)
    {
    m_mainWin = mainWindow;
    }

    void setSecondWindow(secondWindow *secondWindow)
    {
    m_secondWin = secondWindow;
    }
    private:
    };

    -----------------------------app.cxx----------------------------------
    #include "app.moc"
    #include "app.h"

    app::app(int& argc, char **argv): QApplication(argc, argv), m_StateMachine(this){
    }

    -----------------------------main.cxx----------------------------------
    #include "mainWindow.h"
    #include "secondWindow.h"
    #include "app.h"

    int main(int argc, char** argv)
    {
    app app(argc, argv);

    mainWindow main;
    secondWindow second;

    app.setMainWindow(&main);
    app.setSecondWindow(&second);

    main.showMaximized();

    return app.exec();
    }
    @


  • Lifetime Qt Champion

    Hi,

    Please, take a look at the examples in the Qt documentation. They explain very well how to use signals & slots to update ui elements from one class based on changes in another one.

    Could you explain what you are trying to achieve ? I don't really understand why the complexity of your code to update the text from a QLabel.

    You are mixing several complex concepts here and that doesn't look clean. For example, you rarely need to inherit QApplication, and in your code, I don't see the use case. Also why do you need two singleton classes and all the "friending" ?

    One last thing, does your code compile ?

    Hope my questions will help



  • Also, in general, posting huge walls of text is not a very good way to get attention. Especially for such a trivial problem that can be summed up in a few lines of example code. It is hardly necessary to read through all your code for such a trivial answer.

    Accessing a UI element of another class is just like accessing any other accessible member of any other class. There is no rocket science to it.



  • [quote author="SGaist" date="1366734672"]Hi,

    Please, take a look at the examples in the Qt documentation. They explain very well how to use signals & slots to update ui elements from one class based on changes in another one.

    Could you explain what you are trying to achieve ? I don't really understand why the complexity of your code to update the text from a QLabel.

    You are mixing several complex concepts here and that doesn't look clean. For example, you rarely need to inherit QApplication, and in your code, I don't see the use case. Also why do you need two singleton classes and all the "friending" ?

    One last thing, does your code compile ?

    Hope my questions will help

    [/quote]

    Hi,

    Examples unfortunately does not help: ( and it is actually the shortest code from whole application. Main goal was accessing ui elements of second QMainWindow in class read.cxx. I can access ui-elements from mainWindow (in this case mainQLabelTest) in class "write.cxx" but I can't access ui-elements (in case secondQLabelTest) from secondWindow in class read.cxx. If I press the button "pushButton2" on secondwindow, it happens nothings and and I can't find out why? and yes it is compilable and executable.

    Thank you.



  • [quote author="utcenter" date="1366735411"]Also, in general, posting huge walls of text is not a very good way to get attention. Especially for such a trivial problem that can be summed up in a few lines of example code. It is hardly necessary to read through all your code for such a trivial answer.

    Accessing a UI element of another class is just like accessing any other accessible member of any other class. There is no rocket science to it.[/quote]

    Hi,

    Yes I know that it is tedious, but had no other choice in order to express precisely the problem.

    Thank you.


  • Lifetime Qt Champion

    Well, your code, in this state does not compile (i.e. not any declaration of m_StateMachine nowhere).

    If your complete software is as convoluted as what you posted, then I would advise to refactor it from the ground up.

    Is your goal only to change the label from secondWindow based on a click of button on mainwindow ?



  • No that was not my goal qlabel is here just an example. Actually I just want to distribute my whole code. When I click on pushButton2 on the secondWindow, the processing of SLOT "readtest()" and accessing the ui's from secondWindow should take place in read.cxx, not in secondWindow.cxx. The secondWindow has many ui-elements that I want to use in read.cxx and may be in other classes too. Sorry you can remove all the m_StateMachine(), igstkStandardClassBasicTraitsMacro(bla, bla) and igstkStateMachineMacro() then it changes nothing.


  • Lifetime Qt Champion

    Then, if I may, you're "doing it wrong".

    Your read class should not know anything about your GUI classes. It adds a tight coupling that will blow up in your hand once you start making modifications to one class or the other.

    Define an interface for your read class, and externally connect it to your GUI.

    i.e. :
    @
    class MyReadClass : public QObject
    {
    Q_OBJECT
    public:
    void MyReadClass(QObject *parent = 0):
    QObject(parent)
    {}

    signals:
    void messageChanged(const QString& message);

    public slots:
    void startSomeWork()
    {
    doSomeWorkImpl();
    }

    private:
    void doSomeWorkImpl()
    {
    // do your stuff
    emit messageChanged(tr("Done something"));
    }
    };

    class MyCoolWindow : public QWidget
    {
    Q_OBJECT
    public:
    MyCoolWindow(QWidget *parent = 0) :
    QWidget(parent),
    _myLabel(new QLabel(tr("empty"))
    {
    QHBoxLayout *layout = new QHBoxLayout(this);
    layout->addWidget(_myLabel);
    }

    public slots:
    void updateLabel(const QString& text)
    {
    _myLabel->setText(text);
    }

    private:
    QLabel *_myLabel;
    };

    int main(int argc, char *argv[])
    {
    QApplication app(argc, argv);
    MyReadClass myReadClass;
    MyCoolWindow myCoolWindow;
    QObject::connect(&myReadClass, SIGNAL(messageChanged(QString)), &myCoolWindow, SLOT(updateLabel(QString)));
    QTimer::singleShot(1000, &myReadClass, SLOT(startSomeWork()));
    return app.exec();
    }
    @

    Again, please, read the examples and demos from Qt's sources, they cover a very large ground. If you are splitting your code: draw some class diagrams before, do some architecture tests. Without that you will end up with some weird beast.

    Hope it helps



  • There are 3 ways for accessing ui elements from another class:
    1- Use friendly class (Do not recommend)
    2- Use Signal and Slots (Delay problems and making several threads in your app, but good way)
    3- Use trick! (This method is my favorite method) : You can pass ui to another class. You should make a public method in your second class like this:

    SecondClass:
    void PassingUi(Ui::MainWindow *ui);

    Ui::MainWindow *rui;
    void SecondClass::PassUi(Ui::MainWindow *ui)
    {
    rui = ui;
    }

    void SecondClass::Example(){
    rui->myLabel->setText("Data Passed Successfully!");
    }

    Now you can access all ui element from you second class to you MainWindows class remotely, without forwarding classes and make any Signal and Slot.

    You can also pass your ui from Class a to Class b by constructor instead of creating a method. Up to you!

    Enjoy Qt C++.


  • Moderators

    @Hasan-Vaez Your suggestion is bad design: you make your SecondClass know internal details of the first one.

    void SecondClass::Example(){
        rui->myLabel->setText("Data Passed Successfully!");
    }
    

    So, SecondClass knows that the other one has a label called myLabel and manipulates it directly. It is better than two classes know as few details about each other as possible. In the example above: if you rename myLabel in something else you will need to change SecondClass as well.
    Signals/Slots is clearly the better approach - one class simply emits a signal with needed parameters and does not care and does not know about what other classes do with this signal and parameters and how (loose coupling https://en.wikipedia.org/wiki/Loose_coupling).
    "2- Use Signal and Slots (Delay problems and making several threads in your app, but good way)" - what delay problems do you mean? And UI classes may only be manipulated in the GUI thread! It doesn't matter whether second or third approach - NEVER manipulate UI classes outside of the GUI thread! This is another reason to use signals/threads - they can be used across threads.



  • if you rename myLabel in something else you will need to change SecondClass as well.
    [Where is the problem? If you want to make consistency, you can send just the control you want to change it, not all ui controls. In this case you can rename the control in first class. Furthermore in Signal/Slots you need also to change your codes if some controls rename]

    Anyway I said Signal/Slots are good way, maybe the best way. But it is my favorite way to pass Ui to my class. Personally I do not use many Connections to my classes.




  • Moderators

    @Hasan-Vaez Last comment from the link you posted:
    "You shouldn't really be trying to access the MainWindow UI from another class, a better solution is to create a signal in your class that you want to make the change from and a slot in MainWindow that executes the change."
    The advantage with signals/slots: neither the sender nor receiver need to know internal implementation details of the other side.
    And one more note: why should UI B change something in UI A directly? Each UI in your application is responsible for its representation and behaviour. Only A should change itself, else you will have hard time to understand where and how it is actually changed (at least in applications of a reasonable size).
    With your approach you're creating tight coupling which makes the code harder to understand and to maintain.
    In object oriented programming it is called "information hiding".



  • @jsulm Right and agree. All objects should be encapsulated and codes should be readable specially when you have a team working.

    Just tell me in a simple way: We have some data in a "Create New User Form" (We named it Class B) and we need to add New User Data to the database. So The Class B is recalled from Users Form with showing all users records visually (We named it Class A).

    We need to visually and immediately update the User Records in Class A (without reloading all records to show the changes).
    The records have some long data (for example Id, Name, Images, and so on....).
    How you do it with Signal/Slot.

    Harder thing: sometimes you need to change the color of a record from Class B in a QTableViewWidget located in Class A. For example need to change a record's background to red.

    How you do it in a simple way with Signal/Slots?

    There are so many questions on the Net for accessing the Ui elements from a separate class and this discussion will be a good reference for others.

    At the end I say you again I agree with your idea for Signal/Slot and I said in the beginning of this discussion (It is the good idea, maybe the best).


  • Moderators

    @Hasan-Vaez Besides signals/slots you can simply use public API. So, class A provides an interface that can be used by other classes to pass data to it and/or to tell it what to do.
    Visual representation should be decoupled from the actual data storage and program logic. See MVC (Model View Controller) pattern. See http://doc.qt.io/qt-5/model-view-programming.html



  • @jsulm
    This topic is still open since 5 years ago with more than 4K views and I suggest a practical example and solve the programer problems which have this question in minds. If you check the net there are many unsolved question in this regards. Encapsulation is one of the important matters in Object Oriented programming and we do not what to explain it in this topic and it is better to discuss in the related topic.

    Let us close this topic and finalize this discussion as solved topic. Please make a practical solution for the guys who really want to make some changes in the Ui controls. There are some huge programs which needs to change the Ui like huge database programs like MIS, ERP and CRM projects. Most of C programmers know what is MVC and what is Signal/slots in QT.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.