musicamante
musicamante

Reputation: 48260

Get the actual Qt window title, properly replacing the [*] placeholder

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:

Is there a way to get the real title that Qt sets for the window, considering the above?

Upvotes: -2

Views: 905

Answers (2)

m7913d
m7913d

Reputation: 11064

The best approach for top level windows (not MDI subwindows) is to use QWindow::title:

This property holds the window's title in the windowing system

The window title might appear in the title area of the window decorations, depending on the windowing system and the window flags. It might also be used by the windowing system to identify the window in other contexts, such as in the task switcher.

How to obtain the QWindow?

If you already know the QWidget, you can use QWidget::windowHandle. Note that the QWindow object is only available for native widgets (f.ex. top level widgets, but not for MDI subwindow).

Another approach is to directly query for all the top level windows using QGuiApplication::topLevelWindows

MDI subwindow support?

As those MDI subwindows are not top level windows, the QWindow doesn't exists. So, this approach doesn't work in that case. Possible solutions are:

  • See @musicamente's answer, mimicking the Qt placeholder replacement procedure.
  • Create a Qt bug report to expose the resolved window title or alter the Qt source code yourself to do so and distribute it with your application.

Upvotes: 1

musicamante
musicamante

Reputation: 48260

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)

Upvotes: 0

Related Questions