Unsolved App crashes when using findChild<> in Qt
-
@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 aQMainWindow
, 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 anythingMainWindow
should be doing this work. InsteadMainWindow
might pass the required widget toForm
, orForm
might emit a signal whichMainWindow
has a slot onto to do its work. Redesign your code to take a different approach. -
@JonB
I have a QToolButton in mymainwindow.ui
file, all of it's dependencies are defined inmainwindow.cpp
file. I want to make copy of this QToolButton inform.cpp
which is not directly related tomainwindow.cpp
.So is there a solution for this problem.
-
@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 inmainwindow.cpp
file" implies that theQToolButton
will not function standalone inForm
without having theMainWindow
in existence. In theory we likeForm
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 theMainWindow
create the copy of theQToolbar
and pass it toForm
(might be via the constructor, might be via aset()
ter method exported fromForm
). Or pass whatever parameters are necessary to create theQToolbar
inForm
. Either way makeMainForm
export something toForm
instead of havingForm
try to accessMainWindow
. -
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 calledfitSliceToBackground()
which is linked with other function inmainwindow.cpp
so it becomes difficult for me to creat aQToolButton
inForm.cpp
from scratch, hence I just want to export this button toForm.cpp
. -
@Meera-Hadid
But that's precisely the wrong thing to do. If you really have aQToolbar
which can only function if it calls something inMainWindow
then you cannot/should not attempt to use it inForm
.a function called
fitSliceToBackground()
which is linked with other function inmainwindow.cpp
Glancing I cannot see what in
void qMRMLSliceControllerWidget::fitSliceToBackground()
is linked to anything inmainwindow.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
orQToolbutton
, nor can you "re-use" an existing one outside of their parent. -
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 mainwindowThen 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 clickedvoid 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. AMainWindow
is aMainWindow
and aQApplication
is aQApplication
. Casting between the two will never work. In Qt you have aQApplication
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 aMainWindow
) as parent to yourForm
's constructor. If theForm
constructor correctly calls is superclassQObject
(or whatever Qt class it's derived from), you can use (withinForm
)qobject_cast<MainWindow*>(this->parent())
. This is not a nice solution, though (still it works). One way to make this better is to change yourForm
constructor to take aMainWindow*
as parent instead of anyQObject*
/QWidget*
. This would make sure that the cast will not fail. Even better would be that you then also directly store theMainWindow*
as a member variable and use this instead ofparent()
. The general scheme of this approach is: Use the constructor ofForm
to hand down theMainWindow
directly. Let theForm
know about itsMainWindow
. This is the easiest way to access yourMainWindow
. This eliminates the use ofMainWindow::Get()
altogether. When you create yourForm
object you will always have the correctMainWindow
at hand to pass it down.As others have mentioned, this is not the best approach as now
Form
andMainWindow
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 signalvoid Form::fit_to_window_button_pressed()
and connect the tool buttonstriggered()
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 aForm
inMainWindow
you can connect the new signal fromForm
with whatever you like. Since you already haveactionFit_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 aboutMainWindow
andMainWindow
doesn't care that you are using aQToolButton
insideForm
. 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);