<div dir="ltr"><div>Hi John!</div><div>You're quite welcome, but you don't have to feel stupid: realizing our own mistakes and being able to work with them (not just around them, but also realizing how our awareness of them can help us) actually makes us *less* "stupid"...</div><div><br></div><div>You just fell into an XY problem, which can still happen even with experience: it's just less common (usually) and difficult to detect when becoming more experienced (assuming one "knows" enough), but still not impossible to face.</div><div><br></div><div>But now I'm curious: you've been experiencing a quite sporadic but still somehow persistent crash on shutdown. In my experience (including indirect one), it's common to just ignore them (the program is quitting, who cares?), but that's also a possible symptom of an issue that may cause further, and unknown problems.</div><div><br></div><div>When coding with Python, we make a lot of assumptions, mostly related to the internal reference count and automatic garbage collection. That may still be an issue with pure C++ code, and even more when dealing with Python bindings, especially for large/complex programs. Qt objects (including non-QObjects) can still exist outside Python after they've been created and their references have been removed, just like their references can still be considered as valid even though their wrapped Qt objects have been deleted.<br></div><div><br></div><div>I'm no C++ programmer, but I believe that the quit-crash mentioned in this thread would still happen when using a pure C++ program. In fact, it could even cause further issues before quitting when going on with the program implementation.<br><br></div><div>It's always important to consider the multiple relations between objects. I'd suggest you create a proper MRE of your issue so that we could help you find the cause (and eventually post it as a separate question). It will be annoying and extenuating to create such an example, sure. But that process will also, most certainly, help you in any way, no matter what. Either you'll find the culprit on your own during the MRE creation, or we'll be able to help you inspect that issue.<br></div><div><br></div><div>For what we know, you may even find it's a Qt or PyQt bug, which would be quite important to others too.</div><div><br></div><div>Good luck!<br></div><div>MaurizioB<br></div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">Il giorno mar 10 giu 2025 alle ore 05:19 John Sturtz <<a href="mailto:john@sturtz.org">john@sturtz.org</a>> ha scritto:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div class="msg5468377749382947378"><div><div>Hi Maurizio.</div><div><br></div><div>Wow.  Thank you so much for your time and assistance.  This is great information!</div><div><br></div><div>I'm a bit embarrassed to admit this:  The reason I started down the path of using <font face="Cascadia Code">QProxyStyle</font> to display menu items is because I was trying to implement displaying each item's description and shortcut key, with the description left-justified and the shortcut key right-justified (as they typically are).</div><div><br></div><div>Which is embarrassing because if I'd just looked around a little, I would have discovered that capability is built into <font style="font-size:16px" size="3" face="Cascadia Code">QMenu</font>.  (And not just one way, but actually two:  either <font style="font-size:16px" size="3" face="Cascadia Code">.setShortcut()</font> for the menu item's <font style="font-size:16px" size="3" face="Cascadia Code">QAction</font>, or specify "<description>\t<shortcut>" for the <font style="font-size:16px" size="3" face="Cascadia Code">QAction</font>'s text).</div><div><br></div><div>So, color me stupid.</div><div><br></div><div>But, as always, by asking you guys, I've learned a tremendous amount, and for that I am appreciative!</div><div><br></div><div>In fact, in the app I've been working on (for years now, as you know), I already had implemented a simple <font style="font-size:16px" size="3" face="Cascadia Code">QProxyStyle</font> class.  And for some time, the app has had exhibited a mysterious (sporadic, infrequent) tendency to crash when shutting down.  I've never been able to figure out why, and I have a hunch failure to properly account for ownership of the base style of the proxy might be the reason.</div><div><br></div><div>And I had already begun to realize how much complexity there was going to be in reimplementing <font style="font-size:16px" size="3" face="Cascadia Code">QMenu</font>'s behavior.  In this case, it's pretty clear discretion is the better part of valor ...</div><div><br></div><div>Thanks again!</div><div><br></div><div>/John</div>
<div><br></div>
<div><br></div>
<div>
<div>------ Original Message ------</div>
<div>From "Maurizio Berti" <<a href="mailto:maurizio.berti@gmail.com" target="_blank">maurizio.berti@gmail.com</a>></div>
<div>To "John Sturtz" <<a href="mailto:john@sturtz.org" target="_blank">john@sturtz.org</a>></div>
<div>Cc <a href="mailto:pyqt@riverbankcomputing.com" target="_blank">pyqt@riverbankcomputing.com</a></div>
<div>Date 6/9/2025 8:47:55 PM</div>
<div>Subject Re: Customize QMenu display with QProxyStyle</div></div><div><br></div>
<div id="m_5468377749382947378xfa3bcbee1994452"><blockquote cite="http://CAPn+-XS+17XU0HO_f5E-G0jKF_FQFC=H7ORKZrAtVGaWxwA3wA@mail.gmail.com" type="cite" class="m_5468377749382947378cite2">
<div dir="ltr"><div>Although your question is not strictly pyqt-related (therefore a bit off-topic), I feel the urge to add further considerations, also considering what Charles has already written.</div><div><br></div><div>First of all, a QProxyStyle always has a "base style", and takes full ownership of it. That style is used as a base for everything that the proxy doesn't implement on its own.<br></div><div><ul><li>when using the QProxyStyle constructor without arguments, it always creates a new instance of the native style (the one normally used when a new QApplication is created) as its base: doing <span style="font-family:monospace">app.setStyle(Style())</span> will <b>*not*</b> use the style eventually used with a previous <span style="font-family:monospace">app.setStyle(<some other style>)</span> as its base;</li><li>a new style instance is also created when using the string constructor: <span style="font-family:monospace">QProxyStyle('fusion')</span> is syntactic sugar for <span style="font-family:monospace">QProxyStyle(QStyleFactory.create('fusion'))</span> (as explained in the related docs);<br></li><li>when explicitly using an *existing* QStyle instance for its constructor argument, it will use that specific style instance as its base;</li></ul></div><div>In all cases, though, the proxy <b>always</b> takes full ownership of the base style, no matter what.</div><div>This means that you should be <b>*very*</b> careful in trying to use the style of a widget as a base for the proxy: doing something like "widget.setStyle(MyStyle(widget.style())" can have catastrophic results.</div><div><br></div><div>Remember that, by default, all widgets use the application style object (meaning it's the same instance): <span style="font-family:monospace">QWidget.style()</span> does not return a "unique" style instance for the widget, but the style the widget is using: in normal conditions, <span style="font-family:monospace">widget.style()</span> is the *<b>same object</b>* as <span style="font-family:monospace">QApplication.style()</span>.</div><div><br></div><div>Creating a proxy style with a QStyle instance as its base, that is also (or potentially) used elsewhere, is quite dangerous: the most important issue is when the proxy is eventually deleted, which is something that happens when the widget it's used on is deleted: even though you may not explicitly delete the widget, the deletion may happen whenever any parent/owner of that widget is, something that also happens when the QApplication is being quit, as it needs to properly "clean up" all QObjects before actually quitting and returning its exit code.<br></div><div><br></div><div>This is exactly the reason for the delay and (possibly silent) crash you see when closing the program; while the deletion order of sibling widgets may be completely arbitrary, it always follows the object tree (a parent is deleted only as soon as all its children are): if you set a proxy style for a widget using a style that was not owned by that widget, when the widget is deleted it will also delete the proxy *and* the base style, but that style is potentially also (and still) being used by other widgets (and the application) as well, leading to a segmentation fault due to attempting to access a no-more existent object during the deletion process.</div><div><br></div><div>It may be possible to reparent the base style after setting the proxy (for instance, reparenting to the QApplication instance), but that would probably be inappropriate anyway: not only I'm not completely sure it would still make it safe enough, but QProxyStyle also calls an internal setProxy() function, meaning that some QStyle functions may still rely on the newly set proxy even for widgets that still use the original style.<br></div><div><br></div><div>If you only want to target *one* specific widget instance (or subclass instance), the safest approach is to use the string or QStyleFactory way, using the QApplication style. The following should suffice:</div><div><br></div><div><span style="font-family:monospace">baseStyleName = QApplication.style().objectName()</span></div><div><span style="font-family:monospace">myWidget.setStyle(MyStyle(baseStyleName))</span></div><div><span style="font-family:monospace"># which is identical to:</span></div><div><span style="font-family:monospace">myWidget.setStyle(MyStyle(QStyleFactory.create(baseStyleName)))</span></div><div><br></div><div>Note: the above relies on the style object name, it only works for standard and properly implemented QStyles (those that have object names that match the results of <span style="font-family:monospace">QStyleFactory.keys()</span>) and when *not* using stylesheets (read more below on this).<br></div><div><br></div><div>If you instead want to target all widgets of that same type (in this case, all menus), you can just create the proxy instance without arguments, and set it for the whole application.<br></div><div><br></div><div>That said, as Charles wrote, overriding drawItemText alone is inappropriate, as it's almost always insufficient.</div><div>The only occurrence I'm aware of a widget directly calling <span style="font-family:monospace">drawItemText()</span> is from for QLabels that only have plain text set (or that force the PlainText textFormat, instead of the default AutoText).<br></div><div><br></div><div>Any other widget type will use QStyle functions such as <span style="font-family:monospace">drawPrimitive()</span>, <span style="font-family:monospace">drawControl()</span> or <span style="font-family:monospace">drawComplexControl()</span>, and it's up to the style to *eventually* call drawItemText internally: that function is just provided as a convenience that *could* be used by a style, but styles are not required to use it.<br></div><div>Some styles do call it in some cases, but not in others (usually relying on the basic <span style="font-family:monospace">QPainter.drawText()</span>) and there is absolutely no consistency required for that. Some styles even have their own internal functions to draw text depending on the widget or [sub]control type, as a more advanced alternative to drawItemText.</div><div><br></div><div>QStyleSheetStyle (the private style used whenever a style sheet affects a widget) <i>does</i> use drawItemText for many widgets, and, in fact, that's one of the few functions that can be effectively overridden in a proxy style when using style sheets, but it's largely pointless as it doesn't provide any context of the widget that is being drawn, and also requires the stylesheet to actually affect the display of the widget: if the QSS rules don't affect the widget, then QStyleSheetStyle will just use the style it's currently based on: if its own base style is a proxy, and that proxy doesn't use drawItemText, then we're back to square one.<br></div><div>Interestingly enough, QStyleSheetStyle does not use drawItemText for QMenu items, therefore it's completely useless for this case.</div><div><br></div><div>Regarding the note about the code snippet above, since setting a stylesheet on the application (or on widgets) internally sets a QStyleSheetStyle as "primary" style, <span style="font-family:monospace">QApplication.style()</span> or <span style="font-family:monospace">QWidget.style()</span> will return a style that has an empty object name. Trying to go through the meta object system would be ineffective as well, as <span style="font-family:monospace">style().metaObject().className()</span> will obviously return "QStyleSheetStyle". QStyleSheetStyle *does* have a <span style="font-family:monospace">baseStyle()</span> function (similar to that of QProxyStyle), but it's unfortunately private (I've submitted <a href="https://bugreports.qt.io/browse/QTBUG-132201" target="_blank">https://bugreports.qt.io/browse/QTBUG-132201</a> about this, but it's been labeled for Qt7). The only way to work around this, in case you need to apply application-wide style sheets, is to get the default style name as soon as the QApplication is created (but *before* setting any QSS) and keep a reachable reference to it, either as a global variable, or as a dynamic property of the QApplication (eg: <span style="font-family:monospace">app.setProperty('defaultStyleName', app.style().objectName())</span>, eventually retrievable through <span style="font-family:monospace">QApplication.instance().property('defaultStyleName')</span>).<br></div><div><br></div><div><div>The reason for which you may see rounded corners when applying the 
proxy (or without setting the "fusion" style in any way) is that the default Qt style in 
your system does use rounded corners.
</div><div>There are only a few widgets that provide rounded corners (achieved through <span style="font-family:monospace">QWidget.setMask()</span>)
 for top level widgets: QToolTip, the popup of QComboBox, and QMenu. This
 only happens if the style requires it, though: for the above classes, the widget queries 
<span style="font-family:monospace">QStyle.styleHint()</span>, and eventually calls <span style="font-family:monospace">setMask()</span> on itself with the returned value.</div><div>If
 you get rounded corners by default (without setting any style), it 
means that the default style for your system uses them, therefore doing 
<span style="font-family:monospace">setStyle(Style())</span> (without arguments) will still get you those rounded corners, while <span style="font-family:monospace">setStyle(Style('fusion'))</span> will not, because it will then follow the behavior of "fusion" as its base style (which has straight corners), just like doing <span style="font-family:monospace">setStyle('fusion')</span> would.</div><div><br></div></div><div>Considering all the above, overriding <span style="font-family:monospace">drawControl()</span> and checking <span style="font-family:monospace">CE_MenuItem</span> is normally appropriate, but there are many aspects you should consider.</div><div><br></div><div>First of all, you must remember that not all widgets are completely managed by Qt. This is the case of native dialogs, or the menu bar for macos and some linux distros. I haven't used macos for years and have never used such unified menubars on Linux, so I'm not completely sure that overriding QStyle painting for menu items would work in these situations.</div><div><br></div><div>No matter what, your attempts are quite flawed.</div><div>Even though the code obviously is a "proof of concept", your <span style="font-family:monospace">drawControl()</span> override is based on wrong and too simplistic assumptions; there are lots of issues that could or should be consider, but, at the very least, you failed to consider:</div><ul><li>the margins that the default style needs to properly paint an item *within* the menu; the vertical ones may be not that relevant, but the horizontal one are important, otherwise the text may be shown too close to the horizontal margins;</li><li>actions may be disabled and therefore shown differently;</li><li>actions may contain an icon and/or a check/radio indicator (the layout system of QMenu should make that unimportant, but still relevant to consider);</li><li>proper color roles (disabled/highlighted actions normally have different background and text colors);</li><li>border/background drawing (styles usually draw items differently depending on their enabled/disabled and/or normal/selected states);</li><li>hints for actions related to submenus (some styles normally display an arrow or something similar);</li><li>the font of the item (actions do have a font property!)</li><li>the action shortcuts (both using the "&" prefix for "accelerators", or key sequences);</li></ul><div><br></div><div>At the very least, the override should try to use the default implementation without the action text, and eventually attempt to draw the text considering all possible options and some educated guesses.</div><div><br></div><div>An example of this attempt is shown in the answer to this post: <a href="https://stackoverflow.com/q/59218378" target="_blank">https://stackoverflow.com/q/59218378</a></div><div>It basically calls the base implementation after clearing the option text, and then proceeds to draw the text on its own. It still makes some assumptions (eg: the margin, no shortcuts/accelerator, no text font/color), but it's certainly more appropriate.</div><div><br></div><div>Remember: as with other complex widgets, overriding the behavior of QMenu is not an easy task. Changing the text alignment of menu items may seem a relatively easy task, but it actually implies lots of low-level aspects, most of which cannot be assumed.<br></div><div><br></div><div>Best regards,</div><div>MaurizioB<br></div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Il giorno lun 9 giu 2025 alle ore 06:14 John Sturtz <<a href="mailto:john@sturtz.org" target="_blank">john@sturtz.org</a>> ha scritto:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>



<div>Hello again PyQt sages.  Hoping for some insight here -- despite a few hours' time fiddling with this, I don't seem to even be getting past square one.<div><br></div><div>I'm trying to modify display of items in a <font face="Cascadia Code">QMenu</font> using <font style="font-size:16px" size="3" face="Cascadia Code">QProxyStyle</font>.  Basically, I've defined a class named <font style="font-size:16px" size="3" face="Cascadia Code">Style</font> that derives from <font style="font-size:16px" size="3" face="Cascadia Code">QProxyStyle</font>, and re-implements <font style="font-size:16px" size="3" face="Cascadia Code">drawItemText()</font> (which, just for starters, tries to right-justify the menu item text).</div><div><br></div><div>I create a <font style="font-size:16px" size="3" face="Cascadia Code">QMenu</font> object, create an object of the <font style="font-size:16px" size="3" face="Cascadia Code">Style</font> class, and call <font style="font-size:16px" size="3" face="Cascadia Code">.setStyle()</font> to set it as the menu's style.</div><div><br></div><div>It may or may not be the case that my <font style="font-size:16px" size="3" face="Cascadia Code">drawItemText()</font> implementation successfully right-justifies the text.  I'll never know, because it never gets called.  What (probably really basic thing) am I missing?</div><div><br></div><div>Thanks!  [short sample code attached]</div><div><br></div><div>/John</div></div></div></blockquote></div><div><br clear="all"></div><br><span class="gmail_signature_prefix">-- </span><br><div dir="ltr" class="gmail_signature">È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi<br><a href="http://www.jidesk.net" target="_blank">http://www.jidesk.net</a></div>
</blockquote></div>
</div></div></blockquote></div><div><br clear="all"></div><br><span class="gmail_signature_prefix">-- </span><br><div dir="ltr" class="gmail_signature">È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi<br><a href="http://www.jidesk.net" target="_blank">http://www.jidesk.net</a></div>