PDA

View Full Version : Why does a QSpliter reverse the order of QFrames?



Nyte
6th November 2019, 14:22
Hey all, i have a question regarding the use of a QSplitter.

I have a form with 2 frames in it right above each other. Now the content of these frames are a bit dynamic and for testing I use a Python call to read the children which gives me something like:


<QWidget>
<Frame_A>
<Frame_B>
</QWidget>

Now for usability reasons, I had to make these frames scalable as the content in the top frame can be bigger than the bottom. So i added a QSplitter between these frames.

https://i.imgur.com/vTeZUFN.png

But for some reason, when the splitter is added, the order of the frames is suddenly reversed and gives me:


<QWidget>
<Frame_B>
<Frame_A>
</QWidget>

The .ui file seems to be correct and indeed gives me A on top and B below it.

If i turn the frames around (so Frame_A is on the bottom), my result is correct again (but reversed as Frame_B is on the top now). I tried to replicate it with a very barebones form (no other logic assigned to it) and i get the same results. Is this a Qt bug or am i missing something?


Other things i tried and did not work:

- Changing the Object names of the frames (alphabetically)
- Removing the frames and readding them in the order i wanted
- Removing the frames and readding them in the reversed order i wanted
- Editting the XML file manually and changing the order

d_stranz
6th November 2019, 17:45
Is this a Qt bug or am i missing something?

No, and probably.

A verbal description of the problem isn't very useful in helping us understand what is wrong. You need to post some code that demonstrates how you are creating the splitter and adding the child frames to it, as well as what you are doing to cause the order of the frames to become reversed.

Your widget hierarchy -should- look like this:



Widget
Splitter
Frame A
Frame B


From your description, it isn't clear how you are creating the splitter or even if it is in the hierarchy defined by the parent widget at all.

Nyte
7th November 2019, 07:15
I do everything in the Qt Designer (Qt Creator 4.1.0, Based on Qt 5.6.2). For testing this, i made a very simple form with just the 3 elements (2 Qframes with a QSplitter) and everything looks fine in here (the XML/.ui file).

https://i.imgur.com/HM966oo.png

I made a bit of code to read out all the topLevelWidgets in of my form and dump them into a txt file to see the result. This is similar to how I use it in my application.
That code is:



static void dumpWidgetRecursion(QTextStream &str, const QWidget *w, int depth = 0)
{
if (depth)
str << QString(depth * 2, QLatin1Char(' '));
const QRect geom = w->geometry();
str << '"' << w->metaObject()->className() << "\"/\"" << w->objectName() << "\" "
<< geom
<< (w->isVisible() ? "[visible] " : "[hidden] ");
if (w->testAttribute(Qt::WA_Mapped))
str << "[mapped] ";
str << '\n';
foreach (QObject *c, w->children()) {
if (c->isWidgetType())
dumpWidgetRecursion(str, (const QWidget *)(c), depth + 1);
}
}

static void dumpAllWidgets()
{
QString d;
QTextStream str(&d);
std::ofstream myfile("dumpallwidgets.txt");

str << "### QWidgets:\n";
foreach (const QWidget *w, QApplication::topLevelWidgets())
dumpWidgetRecursion(str, w);
myfile << d.toStdString();
}


which gives me a result of:



"QSplitter"/"splitter" 571x241+140+70[visible]
"QFrame"/"frame_B" 571x118+0+123[visible]
"QFrame"/"frame_A" 571x118+0+0[visible]
"QSplitterHandle"/"qt_splithandle_" 100x30+0+0[hidden]
"QSplitterHandle"/"qt_splithandle_" 571x5+0+118[visible]


Notice how Frame_B is above Frame_A. This is causing me issues that i don't have if the splitter is not present.

d_stranz
7th November 2019, 18:10
From the docs for QObject::children():



const QObjectList &QObject::children() const

Returns a list of child objects. The QObjectList class is defined in the <QObject> header file as the following:

typedef QList<QObject*> QObjectList;

The first child added is the first object in the list and the last child added is the last object in the list, i.e. new children are appended at the end.

Note that the list order changes when QWidget children are raised or lowered. A widget that is raised becomes the last object in the list, and a widget that is lowered becomes the first object in the list.


See the last sentence. The order is not guaranteed to be static; it will change based on UI activity. Even if you don't do anything, simply calling show() on this widget may result in frame_b being activated and raised. You could experiment with this by clicking first in frame A and dumping, then in frame B and dumping to see if you get the same order.

In addition, you cannot be guaranteed that the Qt runtime will create and add the widgets in the same order as the hierarchy defined in the UI file. The hierarchy on-scren will be correct, but the ordering of the children may not be the same.

If you actually populated your frames with different child widgets, you would see that the on-screen order of the frames within the splitter is constant (unless you programmatically change them), it is only the order in the list returned by the children() call that might change.

If you are relying on the order of the frames for some program need, then I would suggest you either create them programmatically (i.e., not using Qt Designer) and store their pointers in a separate vector / list in the order you want them, or use Qt Designer, but retrieve the frame pointers at runtime by object name (QObject::findChild()) in the order you want ("Frame_A", "Frame_B") and store those i a vector.