PDA

View Full Version : form.children() doesnt give all widgets for my dynamic form



Charvi
21st May 2012, 08:13
Hi,

I am using QUiLoader. Following is my code:




QUiLoader loader;
QFile file(filename);

QWidget *form = loader.load(&file, this); //"this" here refers to the mainwindow class

int i;
QObjectList list;

list = form->children();
for(i = 0; i < list.size(); i++)
{
QString name = list.at(i)->objectName();
qDebug() <<"object"<< i <<" = "<< name;
}

file.close();



However the output only shows:

object 0 = "_layout"
object 1 = "qt_rubberband"
object 2 = ""
object 3 = "centralwidget"

There are two custom widgets on this form which are being loaded (their constructor called) but their object names are not being listed. Is there something I am missing.


-Charvi

Charvi
22nd May 2012, 07:48
Somewhere I read "Qt Center is where all the questions related to QT get answered". But no answer yet ......

ChrisW67
22nd May 2012, 08:29
You are expecting an immediate response from a volunteer service: you do not get points for being impatient.

Those four objects are the direct children of the widget you loaded. I'll guess the widgets you think are missing are children of "centralwidget". Perhaps you wanted QObject::findChildren().

Charvi
22nd May 2012, 09:48
But QObject::findChildren() requires the object names of the widgets for creating references. This is not desirable for my dynamic loading.

Now I have one more question. QUiLoader class must be creating the references to the widgets internally for creating them then y aren't they exposed for us to use. It must be using the DOM model for loading the form. The object names of the widgets are already present in the .ui file then why do I have to give them explicitly in findChildren() and findChild() methods. It should be read from the XML.

ChrisW67
23rd May 2012, 01:05
QObject::findChildren() can find all children of a given type, it does not require a name as explained and demonstrated in the docs:

Omitting the name argument causes all object names to be matched. The search is performed recursively.
and


This example returns all QPushButtons that are children of parentWidget:


QList<QPushButton *> allPButtons = parentWidget.findChildren<QPushButton *>();


In your case you want all QWidgets.

Charvi
23rd May 2012, 05:45
I have gone through this but this is not desirable in my case. I want to be able to reference the individual widgets by their object names so that it will be specific and want to follow the particular order in which they are on the form when I use getter functions to get the user input. But my other contradicting requirement is that my loader function has to be generic so that it can load any form which may be created well after the loader project is compiled and deployed. :(

Also my other question

Now I have one more question. QUiLoader class must be creating the references to the widgets internally for creating them then y aren't they exposed for us to use. It must be using the DOM model for loading the form. The object names of the widgets are already present in the .ui file then why do I have to give them explicitly in findChildren() and findChild() methods. It should be read from the XML.

I understand the find() and findChildren() methods are for finding and creating references to widgets with the given object name from the form or for that matter all the widgets of a particular base class. But then the form.children() method should be able to display all the widgets on the form which is loaded through loader.load() (as in my code above) but that is not happening.

ChrisW67
23rd May 2012, 10:08
I have gone through this but this is not desirable in my case. I want to be able to reference the individual widgets by their object names
Make up your mind. One moment you list all children, then actually want all descendants, then complain that findChildren() requires an object name, and when told it does not complain that you want to extract a widget by object name. If you know the object name then:


QWidget *widget = form->findChildren<QWidget*>("someobjectname");

If you don't know the object names ahead of time then they should follow some useful pattern to allow you to pick them from the list of all descendent objects: then you know their names.

so that it will be specific
If an object of the name "someobjectname" name exists and it can be cast to a QWidget* (or QLineEdit* or whatever) then this pointer will specifically identify it.

and want to follow the particular order in which they are on the form when I use getter functions to get the user input.
That is what the form tab order, set in the Designer, is for. The order the focus moves "naturally" around the form is how the user might enter the data. It has no bearing on the order they actually enter the data or the order in which you can access the widget content.

But my other contradicting requirement is that my loader function has to be generic so that it can load any form which may be created well after the loader project is compiled and deployed.
There's no contradiction here. You are expecting a QMindReadingFormHandler class so you do not have to do the work. There is no such beast. The forms will have to follow some rules you define and your code will access them using those rules. For example, fields to be extracted might be named "fld_{seq}" and limited to text edits and check boxes: any other widget is ignored.


I understand the find() and findChildren() methods are for finding and creating references to widgets with the given object name from the form or for that matter all the widgets of a particular base class. But then the form.children() method should be able to display all the widgets on the form which is loaded through loader.load() (as in my code above) but that is not happening.
These two sentences are not connected. The first is (mostly) correct. The second is confused. Locating a single object using the generic findChildren() has nothing to do with displaying the dynamically loaded form. Your code above does nothing like displaying the form. If you want to do that you follow the simple pattern in the documentation for QUiLoader. Load the form and insert the resulting widget into the layout of your program.

Here is a complete example. It will dump the content of any line edit on your form when you click Dump.


#include <QtGui>
#include <QUiLoader>
#include <QDebug>

class MainWindow: public QMainWindow {
Q_OBJECT
public:
MainWindow(const QString &fileName, QWidget *p = 0): QMainWindow(p), form(0) {
QWidget *central = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout(central);
QPushButton *button = new QPushButton("Dump", central);
layout->addWidget(button);
central->setLayout(layout);
setCentralWidget(central);

connect(button, SIGNAL(clicked()), SLOT(dump()));

QUiLoader loader;
QFile file(fileName);
if (file.open(QIODevice::ReadOnly)) {
form = loader.load(&file, this);
layout->insertWidget(0, form);
file.close();
}
}
private slots:
void dump() {
Q_ASSERT(form);
// In "natural" order
QList<QLineEdit*> edits = form->findChildren<QLineEdit*>();
foreach(QLineEdit* edit, edits)
qDebug() << edit->objectName() << edit->text();

// Alphabetical order of object name
QMap<QString,QLineEdit*> map;
foreach(QLineEdit* edit, edits)
map.insert(edit->objectName(), edit);
QMapIterator<QString,QLineEdit*> i(map);
while (i.hasNext()) {
i.next();
qDebug() << i.key() << i.value()->text();
}
}

private:
QWidget *form;
};

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

MainWindow m("test.ui");
m.show();
return app.exec();
}
#include "main.moc"

Charvi
23rd May 2012, 12:00
Make up your mind. One moment you list all children, then actually want all descendants, then complain that findChildren() requires an object name, and when told it does not complain that you want to extract a widget by object name.


Locating a single object using the generic findChildren() has nothing to do with displaying the dynamically loaded form. Your code above does nothing like displaying the form. If you want to do that you follow the simple pattern in the documentation for QUiLoader. Load the form and insert the resulting widget into the layout of your program.

Ok. May be this is because I did not explain my requirement.

First I wanted to create a generic loader program which will load any form. Till this I was done. I used the basic example in the QUiLoader example and did it.
Now I want to take user input from this dynamically loaded form. (So if user types something into the line edit then take that input, etc.) But the QUiLoader class does not give direct access to the pointers of the widgets it creates while loading the form (these are private variables plus it doesnt store them.) We can create instances in our program by using object names which my function, as it is generic, wont have it. So first I get the object names, then the base classes of all widgets to create the references of to widgets.
So I tried to get the object names as follows as u suggested:



QList<QWidget *> list = form->findChildren<QWidget *>()

for(i = 0; i < list.size(); i++)
{
QString name = list.at(i)->objectName();
qDebug() <<"object"<< i <<" = "<< qPrintable(name);
}



It was good for many widgets. But for some widgets (I think the containers) it gives even the widgets inside them like follows:



object 96 = textEdit
object 97 = qt_scrollarea_viewport
object 98 = qt_scrollarea_hcontainer
object 99 =
object 100 = qt_scrollarea_vcontainer
object 101 =
object 102 = plainTextEdit
object 103 = qt_scrollarea_viewport
object 104 = qt_scrollarea_hcontainer
object 105 =
object 106 = qt_scrollarea_vcontainer
object 107 =
object 108 = spinBox
object 109 = qt_spinbox_lineedit
object 110 = doubleSpinBox
object 111 = qt_spinbox_lineedit
object 112 = timeEdit
object 113 = qt_spinbox_lineedit


This is a problem. :(
Once I find a way out of this I can use widget.inherits("") to find the base class and then create the reference.



That is what the form tab order, set in the Designer, is for. The order the focus moves "naturally" around the form is how the user might enter the data. It has no bearing on the order they actually enter the data or the order in which you can access the widget content.


Also after taking the user input I want to send this to some other machine in form of packet. This is the sole reason I am worried about the order of widgets in the form because I will send the values in that order only. So no comples protocol needs to be established.


You are expecting a QMindReadingFormHandler class so you do not have to do the work. There is no such beast.
Well this is bad. ;) Sorry if I annoyed you.



Here is a complete example.
Thank you very much for the code. It is useful.