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

App crashes when using findChild<> in Qt



  • Nothing is there in my .ui file. In mainwindow.cpp I'm just adding one widget Test inside which I'm adding one label TLabel
    mainwindow.cpp:

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include "QDebug"
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        Test = new QWidget(this);
        TLayout = new QVBoxLayout(Test);
        TLayout->setContentsMargins(0,0,0,0);
        TLayout->setSpacing(2);
        TLabel = new QLabel(Test);
        TLabel->setObjectName("MyLabel");
        TLabel->setText("Done Task!!");
        TLayout->addWidget(TLabel);
        Test->setLayout(TLayout);
    
        MainWindow* app = qobject_cast<MainWindow*>(QApplication::instance());
        QWidget* list1 = app->Test->findChild<QWidget*>("MyLabel");
        qDebug()<<list1;
    
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    

    When I'm trying to access TLabel using findChild<> method, it shows:

    Desktop_Qt_5_15_2_MSVC2019_64bit-Debug\debug\untitled.exe ...
    11:56:32: The program has unexpectedly finished.
    11:56:32: The process was ended forcefully.
    11:56:32: C:\Users\Meera\OneDrive\Desktop\build-untitled-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug\debug\untitled.exe crashed.
    


  • @Meera-Hadid said in App crashes when using findChild<> in Qt:

    Nothing is there in my .ui file. In mainwindow.cpp I'm just adding one widget Test inside which I'm adding one label TLabel
    mainwindow.cpp:

    MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    {

    ...

    MainWindow* app = qobject_cast<MainWindow*>(QApplication::instance());
    QWidget* list1 = app->Test->findChild<QWidget*>("MyLabel");
    

    ...

    MainWindow appears to be derived from QMainWindow. QApplication::instance() returns a QApplication *. Therefore, qobject_cast<MainWindow*>(QApplication::instance()) will return nullptr.
    (nullptr)->Test is an invalid dereference, leading to:

    Desktop_Qt_5_15_2_MSVC2019_64bit-Debug\debug\untitled.exe ...
    11:56:32: The program has unexpectedly finished.
    11:56:32: The process was ended forcefully.
    11:56:32: C:\Users\Meera\OneDrive\Desktop\build-untitled-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug\debug\untitled.exe crashed.
    

    Perhaps this->Test->findChild(), or Test->findChild() is what you're looking for.



  • My main aim is I want to access the widgets of the mainwindow.ui file in other class(form.cxx)

    mainwindow.cxx:

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include "QDebug"
    #include "form.h"
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        Test = new QWidget(this);
        TLayout = new QVBoxLayout(Test);
        TLayout->setContentsMargins(0,0,0,0);
        TLayout->setSpacing(2);
        TLabel = new QLabel(Test);
        TLabel->setObjectName("MyLabel");
        TLabel->setText("Done Task!!");
        TLayout->addWidget(TLabel);
        Test->setLayout(TLayout);
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    MainWindow* MainWindow::Get() {
        MainWindow* app = qobject_cast<MainWindow*>(QApplication::instance());
        return app;
    }
    
    void MainWindow::on_pushButton_4_clicked()
    {
        Form* temp = new Form(this);
        temp->show();
    }
    

    form.cxx:

    #include "form.h"
    #include "ui_form.h"
    #include "mainwindow.h"
    #include "QDebug"
    Form::Form(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::Form)
    {
        ui->setupUi(this);
        QWidget* myList = MainWindow::Get()->Test->findChild<QWidget*>();
        qDebug() <<"myList: "<<myList;
    }
    
    Form::~Form()
    {
        delete ui;
    }
    

    when I click on the on_pushButton_4 button the window of the form.ui file doesn't open
    it shows error:

    13:47:20: The program has unexpectedly finished.
    13:47:20: The process was ended forcefully.
    13:47:20: C:\Users\Meera\OneDrive\Desktop\build-untitled-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug\debug\untitled.exe crashed.
    

    the error is because of this QWidget* myList = MainWindow::Get()->Test->findChild<QWidget*>(); line and I don't know any other alternative solution for this problem.



  • @Meera-Hadid said in App crashes when using findChild<> in Qt:

    My main aim is I want to access the widgets of the mainwindow.ui file in other class(form.cxx)

    mainwindow.cxx:

    MainWindow* MainWindow::Get() {
        MainWindow* app = qobject_cast<MainWindow*>(QApplication::instance());
        return app;
    }
    
    

    Moving this code from the constructor to a member function doesn't change the fact that the QApplication instance is not a MainWindow. Using qobject_cast to attempt to turn it into one will always return a null pointer.

    when I click on the on_pushButton_4 button the window of the form.ui file doesn't open
    it shows error:

    13:47:20: The program has unexpectedly finished.
    13:47:20: The process was ended forcefully.
    13:47:20: C:\Users\Meera\OneDrive\Desktop\build-untitled-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug\debug\untitled.exe crashed.
    

    the error is because of this QWidget* myList = MainWindow::Get()->Test->findChild<QWidget*>(); line and I don't know any other alternative way for this problem.

    If there is only one MainWindow, track that instance in a variable and use it directly. If there can be multiple instances, iterating through QApplication::topLevelWidgets() is an option.



  • Hey, I'm new to Qt can you please demonstrate the code or give any references for this problem.



  • @Meera-Hadid
    Having some other widget (Form) attempting to access a QMainWindow, or attempting to access anything on that main window, is indicative of a bad approach. Descendant widgets should not know, or need to now, about ancestor widgets. QWidget* myList = MainWindow::Get()->Test->findChild<QWidget*>(); is not a good approach, and should not be necessary.

    Why does Form need to do this, what is it trying to achieve and why? If anything MainWindow should be doing this work. Instead MainWindow might pass the required widget to Form, or Form might emit a signal which MainWindow has a slot onto to do its work. Redesign your code to take a different approach.



  • @JonB
    I have a QToolButton in my mainwindow.ui file, all of it's dependencies are defined in mainwindow.cpp file. I want to make copy of this QToolButton in form.cpp which is not directly related to mainwindow.cpp.

    So is there a solution for this problem.


  • Lifetime Qt Champion

    @Meera-Hadid
    Hi

    • I want to make copy of this QToolButton in form.cpp

    you cannot copy widgets. if you assigned the button from mainwindow to "form", it will be torn out of the
    mainwindow and moved into form.

    • it's dependencies are defined in mainwindow.cpp

    What would those be ?

    Did you set it up in Designer and want to reuse this setup in a new widget ?

    Or do you really want the button in Form to call functions in mainwindow ?



  • @Meera-Hadid
    Sounds a bit fishy, if whatever you mean by "all of it's dependencies are defined in mainwindow.cpp file" implies that the QToolButton will not function standalone in Form without having the MainWindow in existence. In theory we like Form to be a standalone whatever-it-is, so it could be used even if you didn't have the main window. That would call for some sort of reorganization like making it its own widget or putting whatever code it needs into a shared file.

    Having said that, from where you are now still get rid of MainWindow* MainWindow::Get() method/approach. If really necessary have the MainWindow create the copy of the QToolbar and pass it to Form (might be via the constructor, might be via a set()ter method exported from Form). Or pass whatever parameters are necessary to create the QToolbar in Form. Either way make MainForm export something to Form instead of having Form try to access MainWindow.



  • @mrjj @JonB

    code for QToolButton in mainwindow.cpp file:

    this->FitToWindowToolButton = new QToolButton(q);
      this->FitToWindowToolButton->setObjectName("FitToWindowToolButton");
      this->FitToWindowToolButton->setAutoRaise(true);
      this->FitToWindowToolButton->setDefaultAction(this->actionFit_to_window);
      this->FitToWindowToolButton->setFixedSize(15, 15);
    
    QObject::connect(this->actionFit_to_window, SIGNAL(triggered()),
                       q, SLOT(fitSliceToBackground()));
    
    void qMRMLSliceControllerWidget::fitSliceToBackground()
    {
      Q_D(qMRMLSliceControllerWidget);
      d->SliceLogic->StartSliceNodeInteraction(vtkMRMLSliceNode::ResetFieldOfViewFlag);
      d->SliceLogic->FitSliceToAll();
      d->MRMLSliceNode->UpdateMatrices();
      d->SliceLogic->EndSliceNodeInteraction();
    }
    

    As you can see the action of this QToolButton is linked with a function called fitSliceToBackground() which is linked with other function in mainwindow.cpp so it becomes difficult for me to creat a QToolButton in Form.cpp from scratch, hence I just want to export this button to Form.cpp.



  • @Meera-Hadid
    But that's precisely the wrong thing to do. If you really have a QToolbar which can only function if it calls something in MainWindow then you cannot/should not attempt to use it in Form.

    a function called fitSliceToBackground() which is linked with other function in mainwindow.cpp

    Glancing I cannot see what in void qMRMLSliceControllerWidget::fitSliceToBackground() is linked to anything in mainwindow.cpp.

    In any case it's up to you refactor as necessary. You cannot/must not do what you are seeming to want to do.

    On top of everything else, as @mrjj observed you can neither directly copy a QToolbar or QToolbutton, nor can you "re-use" an existing one outside of their parent.


  • Lifetime Qt Champion

    Hi
    If you really insist on calling to MainWindow then
    you could define a new public signal in New Form.

    then connect
    this new signal to q fitSliceToBackground slot in mainwindow

    Then your NEW button can trigger the same as FitToWindowToolButton from the other form.

    void MainWindow::on_pushButton_4_clicked()
    {
        Form* temp = new Form(this);   
        QObject::connect(temp, SIGNAL(MySignal()),q, SLOT(fitSliceToBackground()));
        temp->show();
    }
    

    then in Form .h

    signals:
    void MySignal();

    in form.cpp
    for the new toolbutton clicked

    
    void Form::on_newToolButton_clicked()
    {
        emit MySignal(); // send signal to main
    }
    
    


  • @Meera-Hadid said in App crashes when using findChild<> in Qt:

    MainWindow* MainWindow::Get() {
    MainWindow* app = qobject_cast<MainWindow*>(QApplication::instance());
    return app;
    }

    void MainWindow::on_pushButton_4_clicked()
    {
    Form* temp = new Form(this);
    temp->show();
    }

    You should listen to the others, but I will still answer to the approach you are trying so far. First of all: Drop MainWindow::Get()! There is no way around this. A MainWindow is a MainWindow and a QApplication is a QApplication. Casting between the two will never work. In Qt you have a QApplication object and usually a main window. You could also have no main window or several. So, it is up to you to make main window known to others.

    The worst solution to this is to use the Singleton pattern. Currently, there is a trend to discourage the Singleton pattern in any form whatsoever in C++. Though this would certainly solve your problem as long as you only have a single main window. (The reason why the use of Singletons is discouraged is because of testing.)

    A slightly better solution is that you already pass this (which is a MainWindow) as parent to your Form's constructor. If the Form constructor correctly calls is superclass QObject (or whatever Qt class it's derived from), you can use (within Form) qobject_cast<MainWindow*>(this->parent()). This is not a nice solution, though (still it works). One way to make this better is to change your Form constructor to take a MainWindow* as parent instead of any QObject*/QWidget*. This would make sure that the cast will not fail. Even better would be that you then also directly store the MainWindow* as a member variable and use this instead of parent(). The general scheme of this approach is: Use the constructor of Form to hand down the MainWindow directly. Let the Form know about its MainWindow. This is the easiest way to access your MainWindow. This eliminates the use of MainWindow::Get() altogether. When you create your Form object you will always have the correct MainWindow at hand to pass it down.

    As others have mentioned, this is not the best approach as now Form and MainWindow are highly coupled. Using signals and slots we can easily make everything work together perfectly.
    Step 1: (I assume that in the end the tool button should be visually located within the Form.) Create the tool button inside the Form. Have a signal void Form::fit_to_window_button_pressed() and connect the tool buttons triggered() signal with this new signal. Whenever you click on the tool button, Form will emit a signal. (You can connect signals to signals.)
    Step 2: When you create a Form in MainWindow you can connect the new signal from Form with whatever you like. Since you already have actionFit_to_window (and it might be triggered in other ways as well) I suggest that to connect to its trigger slot:

    Form* temp = new Form(this);
    QObject::connect(temp, SIGNAL(fit_to_window_button_pressed()),
                     this->actionFit_to_window, SLOT(trigger()));
    ...
    // and just like you have it already
    QObject::connect(this->actionFit_to_window, SIGNAL(triggered()),
                     q, SLOT(fitSliceToBackground()));
    

    The order of the connect statements does not matter. Neither does it matter that all objects exist before you can do any of the connects. Only the objects which are used as sender and receiver in this particular connect statement need to be created by that time. Chaining signals and slots is a good way to decouple several classes in Qt. Nobody has to know much about the other. (Form does not know anything about MainWindow and MainWindow doesn't care that you are using a QToolButton inside Form. You could easily change the tool button to something different later.)

    BTW, you should switch to the new connect syntax which will fail at compile time instead of at runtime:

    QObject::connect(temp, &Form::fit_to_window_button_pressed,
                     this->actionFit_to_window, &QAction::trigger);
    //and
    QObject::connect(this->actionFit_to_window, &QAction::triggered,
                     q, &qMRMLSliceControllerWidget::fitSliceToBackground);

Log in to reply