I need to get the visible title of a Qt top level window (or MDI subwindow), because I want to list window titles in different places just like they're visible for the user.
Consider a program that supports multiple top level editor windows (or an MDI area with similar purposes) that should list the titles of those windows, like a menu or an internal "window manager".
If I want to list those windows and also support the [*]
placeholder for the windowModified
property, their windowTitle()
will return that annoying placeholder no matter of their state.
Unfortunately, the windowTitle
feature is a bit abstract, for the following reasons:
- due to the
windowModified
feature, it always returns the placeholder if it's set; - the
[*]
placeholder can be "escaped" with multiple, even occurrences, in order to actually display[*]
in the window title; - if the "actual"
windowTitle
property is an empty string (the default), it falls back to thewindowFilePath
property, which not only always has the[*]
placeholder, but could also behave oddly in the rare case that property contains the[*]
placeholder; while I realize that this is a very odd (and somehow irresponsible, assuming the system actually supports it) situation, I still want a reliable way to get the currently resulting window title, even in those rare (though "wrong") situations;
Is there a way to get the real title that Qt sets for the window, considering the above?
CodePudding user response:
There is no absolute and certain way to get the title that the OS will finally show in the title bar of a top level window.
As explained in the windowTitle
documentation, some systems might support displaying the applicationDisplayName
. Some highly customizable (Linux) OS might show an altered version of the provided title. There is almost no way to get the "final", displayed title, unless you want to dig into specific OS modules that interface with the Window Manager (and that might not be enough anyway, due to the high level of customization possible on *nix systems).
Considering that what's relevant for the OP is the "internal" window title (what Qt finally "relays" to the system), the solution is to implement what Qt actually does internally with qt_setWindowTitle_helperHelper()
.
Be aware that the Qt implementation is not perfect. There are some odd cases when specific combinations of the placeholder string are used. For instance:
- using
[*] [*] [*]
as window title results in "[*]
" being shown for an unmodified window and "* [*] *
" otherwise; - with
[*] [*][*] [*]
, the unmodified window title is "[*] [*]
" (note the leading space) and the other is "* [*]* [*]
"
While, as said above, the Qt implementation is far from perfect, what we're interested into is the actual window title relayed to the OS, so we must adhere to it, since the visual result is the important aspect, no matter whether it's "correct" or not.
Finally, remember that this implementation might become invalid in the future, in case Qt developers decide to change this behavior (and, I believe, they should).
The following code is a simple function that will return the actual window title relayed to the OS for a give widget, which can be used for any situation in which the visible title has to be displayed:
def realWindowTitle(widget):
title = widget.windowTitle()
placeHolder = '[*]'
if not placeHolder in title:
return title
phSize = len(placeHolder)
style = widget.style()
if (widget.isWindowModified()
and style.styleHint(
style.SH_TitleBar_ModifyNotification, None, widget)
# for PyQt6 or full Enum support use
# style.StyleHint.SH_TitleBar_ModifyNotification
):
replaceHolder = '*'
else:
replaceHolder = ''
index = title.find(placeHolder)
while index != -1:
index = phSize
count = 1
while title.find(placeHolder, index) == index:
count = 1
index = phSize
if count % 2: # odd number of [*] -> replace last one
lastIndex = title.rfind(placeHolder, 0, index)
title = (title[:lastIndex]
replaceHolder
title[lastIndex phSize:])
index = title.find(placeHolder, index)
# return "escaped" sequences of the remaining double placeholders
return title.replace('[*][*]', placeHolder)