PDA

View Full Version : [SOLVED] How can I tell qtcreator to pass parent object to promoted class?



stevebakh
14th March 2010, 19:02
[edit]
Please ignore, this has been "solved". The problem wasn't qtcreator, it was my stupidity! Although the parent isn't explicitly set at the point of instantiation, it is set when calling addWidget, which eventually makes its way to the addChildWidget method in http://qt.gitorious.org/qt/qt/blobs/4.7/src/gui/kernel/qlayout.cpp

Maybe I was casting incorrectly, which may explain why it wasn't working before, but it seems fine now. Sorry for wasting your time.

Hi,

I'm using QtCreator to build the UI for my project. I've got a QStackedWidget object in my mainwindow and each "layer", or "page", of the stacked widget has been promoted to a custom class, derived from QWidget. Note, I've also done something similar for a wizard in my project, where each wizard page has been promoted to a custom class.

The problem is, although the classes are derived from QWidget (and qtcreator knows this), the generated code isn't passing a parent into the constructor of the promoted class on instantiation. It's not even using the setParent method after instantiation.

If I understand correctly (I'm a C++ newbie, feel free to correct me), it means that I will have a memory leak because I'm not deleting these objects and they don't have a parent and thus won't be deleted automatically either. It also means I can't get a reference to the parent object.

I know I could find a pointer or reference to my objects and manually call setParent, but is there any automated way of telling qtcreator to pass a parent object on instantiation (or at least call the setParent method after instantiation on objects that are derived from QObject)?

Thanks!

toutarrive
14th March 2010, 19:18
Could you show us your ui file?

stevebakh
14th March 2010, 19:28
Hi,

The XML content of my MainWindow ui file: http://pastebin.com/EnYmh40z

and the specific bit of code in the generated file:



stackedWidget = new QStackedWidget(centralWidget);
stackedWidget->setObjectName(QString::fromUtf8("stackedWidget"));
appointmentListLayer = new AppointmentListScreen();


I'm starting to think this may be a missing feature. QtCreator knows whether your promoted classes are derived from QObject (or QWidget in this case), so it should call setParent on the instantiated object, in my opinion. Currently, it means pointers are created in the generated file and never deleted, because they have no parent. Memory leaks. :(

toutarrive
14th March 2010, 19:57
Strange, i've promoted two Qwidgets using Qt Designer (not Qt creator) and the parenting mechanism works fine.
Try to build your UI using Designer only and see if you encounter the same problem.

toutarrive
14th March 2010, 20:12
I have built the same test case using Qt creator this time and i've got no problem regarding the parenting of my promoted widgets.

norobro
14th March 2010, 20:39
Could we see one of your custom class header files?

stevebakh
14th March 2010, 20:45
Thanks for testing this toutarrive.

Did you promote the widgets to custom classes?

I'll run another test now. The following are the exact steps I've taken to reproduce this problem:

* Open qt-creator v1.3.1 (linux, 64bit, Qt 4.6.2)
* Create new project: select Qt4 Gui Application, add a mainwindow default class + ui file
* Open the mainwindow.ui file in creator and:
* Add a QStackedWidget widget to the centralWidget.
* Right click on the first page of the stacked widget and click "Promote to ..."
* Make sure QWidget is selected as the BaseClass name and add classname TestPage

-- at this stage, the code will fail to compile. we need to create the class --

* For the project, select "Add New" and select Qt Designer Form Class
* Select Widget from the form template options
* Name it "TestPage", to match the name of the promoted widget above.

-- code now compiles, check the generated code --

After this process, when I check my generated code (ui_mainwindow.h) I see that neither the promoted class, nor the standard QWidget class have been given a parent. The code:


void setupUi(QMainWindow *MainWindow)
{
if (MainWindow->objectName().isEmpty())
MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
MainWindow->resize(600, 400);
centralWidget = new QWidget(MainWindow);
centralWidget->setObjectName(QString::fromUtf8("centralWidget"));
stackedWidget = new QStackedWidget(centralWidget);
stackedWidget->setObjectName(QString::fromUtf8("stackedWidget"));
stackedWidget->setGeometry(QRect(50, 30, 421, 251));

// ***** SEE HERE *****
page = new TestPage();
page->setObjectName(QString::fromUtf8("page"));
stackedWidget->addWidget(page);

// ***** AND HERE *****
page_2 = new QWidget();
page_2->setObjectName(QString::fromUtf8("page_2"));
stackedWidget->addWidget(page_2);


MainWindow->setCentralWidget(centralWidget);
menuBar = new QMenuBar(MainWindow);
menuBar->setObjectName(QString::fromUtf8("menuBar"));
menuBar->setGeometry(QRect(0, 0, 600, 19));
MainWindow->setMenuBar(menuBar);
mainToolBar = new QToolBar(MainWindow);
mainToolBar->setObjectName(QString::fromUtf8("mainToolBar"));
MainWindow->addToolBar(Qt::TopToolBarArea, mainToolBar);
statusBar = new QStatusBar(MainWindow);
statusBar->setObjectName(QString::fromUtf8("statusBar"));
MainWindow->setStatusBar(statusBar);

retranslateUi(MainWindow);

QMetaObject::connectSlotsByName(MainWindow);
} // setupUi

Did you follow this procedure toutarrive, or have I done something wrong?

Thanks again!

stevebakh
14th March 2010, 20:46
Could we see one of your custom class header files?

Hi norobro,

I have just ran through a test case, shown in my previous post. I created the custom classes using QtCreator's wizard. The header looks like this:



#ifndef TESTPAGE_H
#define TESTPAGE_H

#include <QWidget>

namespace Ui {
class TestPage;
}

class TestPage : public QWidget {
Q_OBJECT
public:
TestPage(QWidget *parent = 0);
~TestPage();

protected:
void changeEvent(QEvent *e);

private:
Ui::TestPage *ui;
};

#endif // TESTPAGE_H


Thanks :)

stevebakh
14th March 2010, 21:15
Hmmm, bizarre - the test case seems to work. I've just traced the code in SVN and it appears that the parent is being set. I followed it back to this file http://qt.gitorious.org/qt/qt/blobs/4.7/src/gui/kernel/qlayout.cpp and the addChildWidget method.

It seems that with my test case, I can access the parent. I'm going to test again with my main project.

[edit]
D'oh! I don't know what's changed, but it seems to be working fine now. The parent isn't explicitly set during instantiation, but it's still set in the code mentioned in the link above. Sorry for wasting your time.

norobro
14th March 2010, 21:30
Glad you solved your problem. I think toutarrive would agree that you didn't waste our time. The reason I am here is to learn and to try to help solve interesting problems like this.

Norm

toutarrive
14th March 2010, 21:36
You won't get any memory leak in your code even yours widgets are instatiated with no parents in their constructors.
The deletion of the child widget of your form will be automatically handled by the Qt .

When you use a layout, widgets (instatiated without a parent) are automatically reparented to the form 'owning' the layout (and though their deletion is automatic).


stackedWidget = new QStackedWidget(centralWidget);

appointmentListLayer = new AppointmentListScreen();
stackedWidget->addWidget(appointmentListLayer);

appointmentInfoLayer = new AppointmentInfoScreen();
stackedWidget->addWidget(appointmentInfoLayer);

patientListLayer = new PatientListScreen();
stackedWidget->addWidget(patientListLayer);

patientInfoLayer = new PatientInfoScreen();
stackedWidget->addWidget(patientInfoLayer);

gridLayout->addWidget(stackedWidget, 0, 0, 1, 1); // automatic reparenting of the widgets


Even though you want to delete your widget manually you can do so and it will not cause an error.

As far as i know, as your promoted widgets are part of QStackWidget (which is a class built on top of QStackedLayout) they don't need to be instatiated with a parent.
The same applies to QTabWidget. (someone corrects me if i'm wrong).

toutarrive
14th March 2010, 23:00
Glad you solved your problem. I think toutarrive would agree that you didn't waste our time. The reason I am here is to learn and to try to help solve interesting problems like this.


Could not agree more :)