Как переключить «всегда сверху» для QMainWindow в Qt, не вызывая мерцания или вспышки?

void MainWindow::on_actionAlways_on_Top_triggered(bool checked)
{
    Qt::WindowFlags flags = this->windowFlags();
    if (checked)
    {
        this->setWindowFlags(flags | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint);
        this->show();
    }
    else
    {
        this->setWindowFlags(flags ^ (Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint));
        this->show();
    }
}

Приведенное выше решение работает, но поскольку setWindowFlags скрывает окно, его необходимо повторно показать, и, конечно, это выглядит не очень элегантно. Итак, как мне включить «всегда сверху» для QMainWindow без этого побочного эффекта «мигания»?


person Jake Petroules    schedule 18.05.2010    source источник


Ответы (4)


Nokia говорит нет:

It is not possible to make changes to the window flags once the window has been created without causing flicker. Flicker is unavoidable since the window needs to be recreated.

Но иногда, если вы застряли с таким уродливым эффектом мигания, вы можете намеренно перетащить его, чтобы он выглядел так, как будто только что произошло что-то «крутое».

Может быть, появится небольшой индикатор выполнения, которого нет в окне, скажите «Настройка свойств окна!» ... исчезнет окно, а затем снова появится, и закройте всплывающее окно индикатора выполнения.

person HostileFork says dont trust SE    schedule 18.05.2010
comment
Для функции «всегда наверху» это должно быть возможно. Многие приложения делают это без мерцания; возможно, мне просто нужно использовать некоторые родные функции Windows? - person Jake Petroules; 18.05.2010
comment
Если вы хотите пойти дальше или обойти реализацию Qt (разрушить окно и создать его снова), то, возможно. Но по крайней мере некоторые из этих вещей являются свойствами Window Class IIRC, а не HWND; и вы не сможете изменить класс окна после его создания. В любом случае, я думаю, что ценность ответа, который вы можете принять, не нарушая уровень абстракции Qt, лучше, чем погрязнуть в взломе Win32. Это радость Qt в первую очередь... - person HostileFork says dont trust SE; 18.05.2010
comment
@ereOn: Да, у меня есть некоторые вещи, которые отображают индикатор выполнения и (независимо от завершения задачи) не снимают его, пока не пройдет минимальный период мерцания с момента установки индикатора. Таким образом, компьютер в каком-то смысле замедляет работу, но опыт лучше, потому что он не сделал тревожного сигнала, который исчез сразу. Там много психологии индикатора прогресса. :) scribd.com/doc/2226848/Rethinking-The-Progress- Бар - person HostileFork says dont trust SE; 18.05.2010
comment
@HostileFork На самом деле я собираюсь принять ваш ответ, а не свой собственный, поскольку я попросил решение Qt, на которое вы дали правильный ответ. - person Jake Petroules; 23.05.2010
comment
Спасибо Джейк! Что ж, оба ответа все еще здесь, так что люди могут сделать свой собственный выбор. Удачи в отправке патча! ;) - person HostileFork says dont trust SE; 23.05.2010
comment
Получите HWND через winId() и используйте для этого SetWindowLongPtr. Проверьте документы win32, есть также API, такие как ShowWindow, которые не вызывают мерцания. Кроме того, когда-нибудь напишите об этом отчет об ошибке :) - person Петър Петров; 17.09.2013

Что ж, для решения я решил поискать в источниках Mono, так как я знаю, что класс .NET Form (System.Windows.Forms) имеет свойство TopMost.

Решение, которое я нашел для своей программы Qt, было:

void MainWindow::on_actionAlways_on_Top_triggered(bool checked)
{
#ifdef Q_OS_WIN
    // #include <windows.h>
    if (checked)
    {
        SetWindowPos(this->winId(), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
    }
    else
    {
        SetWindowPos(this->winId(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
    }
#else
    Qt::WindowFlags flags = this->windowFlags();
    if (checked)
    {
        this->setWindowFlags(flags | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint);
        this->show();
    }
    else
    {
        this->setWindowFlags(flags ^ (Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint));
        this->show();
    }
#endif
}
person Jake Petroules    schedule 18.05.2010
comment
Если вы действительно думаете, что это того стоит (!) Но я бы предложил хотя бы выделить его как отдельный метод, например. void setWindowStaysOnTopHint(bool windowStaysOnTopHint). Тогда ваш ifdef не будет охватывать вашу кнопку и будет более точно отражать ту процедуру, которую вы хотели, которую Nokia может добавить когда-нибудь, если вы отправите патч для всех платформ. В этой рутине я также обязательно оставлю ссылку на этот вопрос SO, чтобы кто-то задавался вопросом, почему он там, сможет узнать... - person HostileFork says dont trust SE; 19.05.2010
comment
Я уже сделал это, для этого есть несколько методов в отдельном классе. Я просто разместил это здесь для удобства, если кто-то еще когда-либо смотрел на этот вопрос. Интересная идея представить его в Nokia, но я обязательно подумаю об этом, когда буду внедрять рабочие решения Carbon и X11. - person Jake Petroules; 19.05.2010
comment
Ответ правильный, однако для Qt5 необходимо добавить reinterpret_cast к HWND для this->winId() - person Predelnik; 26.05.2015

Так как я недавно столкнулся с той же проблемой:

Вы можете сделать это в обход Qt. Для части Windows см. Ответ @JakePetroules.
Версия XCB (X11), которую я использую:

#ifdef Q_OS_LINUX
#include <QX11Info>
#include <xcb/xcb.h>

// Just a simple atom cache helper
xcb_atom_t xcb_get_atom(const char *name){
    if (!QX11Info::isPlatformX11()){
        return XCB_ATOM_NONE;
    }
    auto key = QString(name);
    if(_xcb_atom_cache.contains(key)){
        return _xcb_atom_cache[key];
    }
    xcb_connection_t *connection = QX11Info::connection();
    xcb_intern_atom_cookie_t request = xcb_intern_atom(connection, 1, strlen(name), name);
    xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, request, NULL);
    if(!reply){
        return XCB_ATOM_NONE;
    }
    xcb_atom_t atom = reply->atom;
    if(atom == XCB_ATOM_NONE){
        DEBUG("Unknown Atom response from XServer: " << name);
    } else {
        _xcb_atom_cache.insert(key, atom);
    }
    free(reply);
    return atom;
}

void xcb_update_prop(bool set, WId window, const char *type, const char *prop, const char *prop2)
{
    auto connection = QX11Info::connection();
    xcb_atom_t type_atom = xcb_get_atom(type);
    xcb_atom_t prop_atom = xcb_get_atom(prop);
    xcb_client_message_event_t event;
    event.response_type = XCB_CLIENT_MESSAGE;
    event.format = 32;
    event.sequence = 0;
    event.window = window;
    event.type = type_atom;
    event.data.data32[0] = set ? 1 : 0;
    event.data.data32[1] = prop_atom;
    event.data.data32[2] = prop2 ? xcb_get_atom(prop2) : 0;
    event.data.data32[3] = 0;
    event.data.data32[4] = 0;

    xcb_send_event(connection, 0, window,
                   XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_PROPERTY_CHANGE,
                   (const char *)&event);
    xcb_flush(connection);
}
#endif

Используйте как: xcb_update_prop(true, window()->winId(), "_NET_WM_STATE", "_NET_WM_STATE_ABOVE", "_NET_WM_STATE_STAYS_ON_TOP");

Это немного хакерски, но отлично работало на Mate, KDE, GNOME3, XFCE и openbox.

person SleepProgger    schedule 25.08.2020

Протестировано с

  • Qt 5.2.1 на Windows XP
  • Qt 5.2 on OS X 10.9

    void ConsoleUI::onAllwaysTop(bool checked)
    {
        Qt::WindowFlags flags = windowFlags();
        if (checked)
        {
            flags ^= Qt::WindowStaysOnBottomHint;
            flags |= Qt::WindowStaysOnTopHint;
        }
        else
        {
            flags ^= Qt::WindowStaysOnTopHint;
            flags |= Qt::WindowStaysOnBottomHint;
        }
        setWindowFlags(flags);
        show();
    }

person jimmy    schedule 24.03.2014
comment
Это совершенно неправильно. Прежде всего, он устанавливает окно всегда внизу, что вам почти никогда не нужно (и, конечно, не нужно в этом вопросе). Во-вторых, это не отвечает на вопрос как сделать это без мерцания. - person Timmmm; 27.11.2014