Qt5 QMainWindow components deletion - c++

One of our teacher asked us to create a Qt application without any UI file for the main window (a QMainWindow). Usually I always create one, leave it empty and let the uic deal with it.
I know that if a parental relation is defined between a widget (child) and its parent, then there is no need to delete the widget (deleted when the parent is deleted). So, when the UI is deleted, all the children are destroyed.
If we do not use an UI file (not generated), do we have to manually delete all the widget added to the GUI?
A little sample:
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent){
layout = new QHBoxLayout(this);
aButton = new QButton(this);
layout->addWidget(aButton);
...
}
MainWindow::~MainWindow(){
delete ui; // No need to delete more if parental relation.
// However, what do we do if no ui has been generated?
// Do we have to delete aButton?
}
The value of parent is 0. It is the main entry of the application.
Thanks

Please refer to this article
QWidget, the fundamental class of the Qt Widgets module, extends the parent-child relationship. A child normally also becomes a child widget, i.e. it is displayed in its parent's coordinate system and is graphically clipped by its parent's boundaries. For example, when the application deletes a message box after it has been closed, the message box's buttons and label are also deleted, just as we'd want, because the buttons and label are children of the message box.
So, there is no difference do you use ui or not. When you delete window, all its children will be deleted too.

Related

How do I set the parent of a widget after the widget has already been initlized (ex. if the widget is a parameter to a function call)?

I know that usually parents are set by being given in the constructor argument, for example if you wanted to have a push button within a frame you could do:
QFrame frame = new QFrame();
QPushButton* button = new QPushButton(frame);
But I already have the push button that I want to add as a child to a frame (it was received as an argument in the call to the function I'm working on).
Is there any way to add an already existing widget as a child to another widget? Or add a new child to an existing or nonexistent parent?
(Disclaimer: Answer blatantly stolen from G.M.'s comment (with permission - see above ;))
Simply use QWidget's setParent(QWidget *parent) method.
Note: The new parent takes ownership of the QWidget (i.e. deallocating it when the parent is destroyed etc.) and you will need to call show() on the widget again to make it visible, as changing parents turns QWidgets invisible.

Qt5 Designer and Encapsulation

I've created a main window in Qt Desginer which has the following structure
Ui_MainWindow
VerticalLayout
QTabWidget
Widget (*A)
QChart
QChart
The uic generates a header file "ui_mainwindow.h". This header files contains references on all the sub (subsub, subsubsub, ...) widgets. In my point of view this isn't good encapsulation.
I found a way to set custom classes for the different widgets in the Qt Designer.
Since I have to write these classes manually I have no access/references to the child widgets. I only can control the widget itself. Still all widgets are referenced in the MainWindow UI.
Is there a way to generate separate classes for each (or some selected) widget? I'd like to intercept *A and access the child QCharts.
Thank you,
Even though you can already "intercept A" in the current form. You can certainly separate A in a different section then add a QWidget in your main window, right click on it and promote it to being A

Reusing a QMenu within multiple Qmenu

Hi all,
I have some code generating a dynamically filled QMenu depending on some data (I will call it thisMenu). The QMenu thisMenu is created, taking some "main" QWidget as parent and is added to a QMenuBar within this QWidget (menuBar.addMenu(&thisMenu). Latter on, I want the user to be able of accessing thisMenu from a context menu (the user right click on some portion of the QWidget, which pops a QMenu (called contextMenu) with some actions, and the previous QMenu as a sub-menu).
If I reuse the QMenu that I first created with contextMenu.addMenu(&thisMenu) I find out that, even if contextMenu pops at the right global position, thisMenu is always translated to some other position and appearing sometimes above, sometimes under contextMenu.
I can test that this is linked to the parenting chain : thisMenu is not a child of contextMenu, if I create it a child of contextMenu, everything is fine. Is there a way of cleanly handling this case without recreating a QMenu similar to thisMenu, or changing the parent of thisMenu; i.e. reusing thisMenu in both QMenuBar and in some context menu/QMenu? In other what is the proper way of handling parenting chain for QMenu and sharing QMenu?
Thank you,
In other what is the proper way of handling parenting chain for QMenu and sharing QMenu?
You cannot share a QMenu across multiple places -- each QMenu can only exist in one place at a time. You should create separate QMenus: One for your menu bar and one for your context menu.
A simple way is to put your menu-generating code in a for-loop, to create multiple identical menus.
May I ask why you want to reuse your QMenu?
I can test that this is linked to the parenting chain : thisMenu is not a child of contextMenu
Yes, that is described in the documentation. When you add one QMenu to another, the parent doesn't change: http://qt-project.org/doc/qt-5/QMenu.html#addMenu
if I create it a child of contextMenu, everything is fine.
The position of a widget is always painted in a position relative to its parent. (Remember: A QMenu is a QWidget)
Following JKSH's answer, I decided to use a function to duplicate QMenu, without duplicating the QAction in it (they are not inheriting QWidget), hence conserving all established connections :
void duplicateMenu(QMenu* dst, QMenu& origin)
{
QMenu* sub = dst->addMenu(origin.title());
QList<QAction*> actions=origin.actions();
for(QList<QAction*>::iterator it=actions.begin(); it!=actions.end(); it++)
{
QMenu* itMenu = (*it)->menu();
if(itMenu!=NULL)
duplicateMenu(sub, *itMenu);
else
sub->addAction(*it);
}
}

Unable to display Widget when passing this as the parent when initiating

Hi Im learning QT and I have come across something I don't understand. why is it when I create a widget like so
widget = new Widget(this);
widget->show();doesn't display the widget, but if I leave the 'this' out i.e
widget = new widget();
It does display? whats more I need to be able to call one of the parent's methods from the child and unless I set the patient of the child by passing this upon its creation I am unable to do this.
The widget does show up. But it shows up as part of the main window, not in its own window. Of course if the widget you're creating doesn't have any content in it, then you won't see much. You can test this with something like:
widget = new QTextEdit(this);
widget->show();
If you want to make the widget always show up in its own window, then you should have your widget inherit from QDialog instead of from QWidget. Or, if you don't want (or can't) do this, then you should set the Qt::Window window flag when creating the widget:
widget = new Widget(this, Qt::Window);

How to change/swap the layout of a widget?

I am working on an application in which I need to change the layout of my current tab when a radio button is pressed.
I am using the following code to swap the layout:
changed_layout = new QVBoxLayout;
label1 = new QLabel(string);
delete main_layout;
changed_layout->addWidget(label1);
setLayout(changed_layout);
But when I click the button the software exits (crashes).
You shouldn't try to directly delete QObjects with delete, call ojbect->deleteLater() and set your pointer to null. The Qt framework will safely delete the object after any slots have been exited and control has returned to the event loop.
In your code, change the line delete main_layout; to:
main_layout->deleteLater();
main_layout = NULL;
Update:
The comment from Steve S is correct. To quote Qt docs for setLayout()..
If there already is a layout manager
installed on this widget, QWidget
won't let you install another. You
must first delete the existing layout
manager (returned by layout()) before
you can call setLayout() with the new
layout.layout.
Since setLayout() is a special case of reparenting widgets, we must delete the old layout first and then set the new layout. We would have to be very sure of the signal/slot call stack we are in to call delete().
An alternative would be to setup the new layout as much as possible and then connect a slot to the old layouts destroyed() signal and then call deleteLater() on it. In the slot for the destroyed() signal you should be able to call setLayout() with the new layout.
If this sounds complicated, it's because it is. Changing and destroying layouts at runtime is unusual. If I found that I had to do something like this, I would probably create something like a gridLayout at the top of the hierarcy with the different layouts I would need set inside it and then move the child widgets from layout to layout without having to create and destroy them at runtime.