It’s reliably reproducible with dev on macOS. It seems to be related to the 
introduction of the “compound widget” notion in Qt 5.10 (so this should in 
principle not be a new bug, but we had a few follow-up patches after the 
introduction of that concept).

A widget that has a child as its focusProxy is considered a compound widget. 
When the compound widget’s position in the tab chain is changed, then the 
entire “compound” changes position in the tab order. By that definition, a 
QTabWidget is always a compound widget, as it sets the tab bar as their proxy 
(and the tab bar’s focus policy is NoFocus on macOS, unless I enable 
keypadNavigation, which impacts the behavior in the reproducer).

<side-track>
The conflated meaning of focusProxy is making this rather messy. The original 
idea of focus proxy was to enable building of widgets out of several widgets, 
where the widget inside is just an implementation detail. Ie the lineedit in a 
spinbox or editable combobox should not be visible to the application wrt 
focus. The combobox has focus. And so the outer widget is the focus proxy for 
the inner widget, and forwards key events to the inner widget (e.g. 
QAbstractSpinBox::keyPressEvent passes most events on to the editor).

But focusProxy also gets used for compound widgets, where the outer widget is 
just a container with no focus policy, but with (several, perhaps) focusable 
widgets inside. QTabWidget is one example - it’s just a container, but the tab 
bar has keyboard navigation, and pages inside have keyboard navigation. That’s 
the use case that the QWidget::setFocusProxy suggests for UI development 
(rather than when authoring widgets), where it would be very impractical to 
have to overwrite keyPress/ReleaseEvent to redirect from container to inner 
widget.

With the second use case, tab ordering was messy, as changing the tab order of 
the compound wasn’t possible - you had to change the order for each widget 
inside. Until 5.10.
</side-track>

So, your tab widget is a compound, and the QLineEdit “dataFile" is part of that 
compound, by being a child of the tab widget.

Your reproducer sets the tab order to go from line edit to tab widget. That 
results in a loop - the tab widget (or it’s tab bar) should come after the line 
edit, but since the line edit is the last focusable child in the tab widget 
compound, we end up with a situation where some widgets are no longer part of 
the focus chain.

I’m having a look. The question is: what should happen here - no assert of 
course, at least not in the QWidget destructor. But if you set the tab order so 
that you tab from lineEdit back into the parent tab widget, then you are 
somewhat asking for a loop (because the lineEdit is also after the tab widget). 
And we need to allow that as a temporary state.

So as a stop-gap, don’t set the tab order to move from line edit to tab widget.

Volker




On 3 Mar 2023, at 19:42, Scott Bloom <sc...@towel42.com> wrote:

Qt Creator? Never heard of it 😊  I use Visual Studio, but I did already try 
this.

The assert kicks off directly from the command line as well.

Scott

From: Interest <interest-boun...@qt-project.org> On Behalf Of Axel Spoerl via 
Interest
Sent: Friday, March 3, 2023 9:43 AM
To: interest@qt-project.org
Subject: Re: [Interest] Weird assert, how to debug?

Hi Scot,
Please try running the executable from outside Qt Creator to eliminate its 
debugger as the troublemaker.
Cheers
Axel


On 3 Mar 2023, at 18:33, Scott Bloom 
<sc...@towel42.com<mailto:sc...@towel42.com>> wrote:

As you can see by the sample, Im not doing anything special, not changing any 
ownership, and not doing any individual deletes + deleteLaters with a 
processEvents (The root cause of many a “double delete in my experience”), so 
Im really stumped on this one.

Something in designer with the UI file is causing this.

Scott

From: Interest 
<interest-boun...@qt-project.org<mailto:interest-boun...@qt-project.org>> On 
Behalf Of Axel Spoerl via Interest
Sent: Thursday, March 2, 2023 10:09 PM
To: interest@qt-project.org<mailto:interest@qt-project.org>
Subject: Re: [Interest] Weird assert, how to debug?

Hi Scott,

I can't make the reproducer crash on Qt 5.15.9 or on the latest dev (6.6).
The assertion happens in QWidget's destructor, when it wants to exit from the 
focus list.
I can imagine two cases, how this assertion kicks in.

  1.  The code path in the if statement right after the assertion has been 
reached before:
if (d->focus_next != this) {
        d->focus_next->d_func()->focus_prev = d->focus_prev;
        d->focus_prev->d_func()->focus_next = d->focus_next;
        d->focus_next = d->focus_prev = nullptr;
}

=> An already deleted widget is deleted a second time.

  1.  Focus chain has changed without events having been processed, so the 
destroyed widget doesn't know about it.
Happens in the debugger (e.g. Qt Creator) sometimes, because debugging output 
is a notirious focus thief.

Option 2 seems more likely to me, since the assertion doesn't shout in release 
builds.
Updating to the latest Qt Creator version may help. If it doesn't please file a 
bugreport in https://bugreports.qt.io<https://bugreports.qt.io/>,
upload the reproducer, specify the Qt and (Qt Creator if used for debugging) 
versions used.

Cheers
Axel
________________________________
Von: Interest 
<interest-boun...@qt-project.org<mailto:interest-boun...@qt-project.org>> im 
Auftrag von Scott Bloom <sc...@towel42.com<mailto:sc...@towel42.com>>
Gesendet: Freitag, 3. März 2023 01:37
An: interest@qt-project.org<mailto:interest@qt-project.org> 
<interest@qt-project.org<mailto:interest@qt-project.org>>
Betreff: [Interest] Weird assert, how to debug?

Im getting an assertion in qwidget.cpp
“d->focus_prev->d_func()->focus_next == this” in Qwidget.cpp line 1443 (Qt 
5.15.10)

I realize the response is going to be, create a minimal example, I created a 
repo on github with it, it’s a simple cmake based project. Any help figuring 
this out would be appreciated.

The application has a QApplication and a QDialog derived dialog.  The dialog 
has a tabwidget and a dialogbutton box.

The assertion gets thrown when the dialog is destroyed.

https://github.com/towel42-com/TestFocusAssert

This happens on Windows, I don’t have a linux box setup right now to test on 
linux.

Thanks
Scott




_______________________________________________
Interest mailing list
Interest@qt-project.org
https://lists.qt-project.org/listinfo/interest

_______________________________________________
Interest mailing list
Interest@qt-project.org
https://lists.qt-project.org/listinfo/interest

Reply via email to