【问题标题】:How to make a window always on top?如何使窗口始终在顶部?
【发布时间】:2014-01-01 01:51:25
【问题描述】:

我已经创建了一个无框架的 Qt/QML 窗口,我真的很想知道设置其“始终在顶部”系统菜单标志的任何编程方式。单击 ALT+SPACE 我可以调出无框窗口的系统菜单,通过单击“始终在顶部”选项,窗口确实始终位于顶部,但我还没有找到一种编程方式做同样的事情。 Qt.WindowStaysOnTopHint 不起作用,尝试 wmctrl -r "window name" -b add,above 也不起作用,即使 wmctrl 确实适用于其他窗口。 wmctrl 不适用于我感兴趣的窗口显然与 wmctrl -l 上机器名称列的 N/A 有关:

francisco@Ubuntu:~$ wmctrl -l
0x02600006  0 Ubuntu Área de trabalho
0x03c00002  0 Ubuntu XdndCollectionWindowImp
0x03c00005  0 Ubuntu unity-launcher
0x03c00008  0 Ubuntu unity-panel
0x03c0000b  0 Ubuntu unity-dash
0x03c0000c  0 Ubuntu Hud
0x046000b3  0 Ubuntu How to make a window aways on top? - Stack Overflow - Mozilla Firefox
0x0520000b  0    N/A Qt Creator
0x05002396  0 Ubuntu francisco@Ubuntu: ~
0x0540000b  0    N/A backlight

我也经历过this procedure,但至于用户询问,它对我也不起作用,同样的行为。 _NET_WM_STATE_ABOVE 已设置,但聚焦窗口,然后再次检查标志,它不再存在,仅在单击系统菜单时才会显示。

这是 QML:https://gist.github.com/oblitum/8050586

相关askubuntu问题:https://askubuntu.com/questions/394998

编辑

通知

在相关的 askubuntu 问题中,发现 wmctrl 上应该存在一个错误,用于通过名称定位某些窗口。使用wmctrl -i -r <window id> -b add,above 也可以解决问题。

【问题讨论】:

  • 问题是 Unity 是否正确实现了这种行为。 Unity 是否为用户提供了一种让窗口始终位于顶部的方法?
  • @FrankOsterfeld 正如我所说,它通过在系统菜单上设置“Aways On Top”来工作。我想以编程方式拥有相同的内容。
  • 我想弗兰克明白这一点,他分别回复了。 ;-)
  • @LaszloPapp 我没有得到,我是用户,我正在使用带有 Unity 的 Ubuntu,我可以用鼠标设置标志,所以?
  • 所以,他知道您希望以编程方式获取它,不用担心。这是第一个验证步骤。无论如何,“Qt.WindowStaysOnTopHint 不起作用”是什么意思?有什么比什么都没有改变?控制台输出等?您是否尝试过追踪其内部情况?

标签: ubuntu x11 qt5 window-managers compiz


【解决方案1】:

EWMH 规范明确指出:

_NET_WM_STATE_ABOVE 和 _NET_WM_STATE_BELOW 主要用于用户偏好,不应由应用程序使用,例如用于引起对他们对话的注意(在这种情况下应使用紧急提示,请参阅the section called “Urgency”)。

因此窗口管理器没有责任尊重直接设置此属性(即通过 XChangeProperty)自己的应用程序。只能通过向根窗口发送客户端消息来更改此属性 哪些窗口管理器监听。

我不知道如何在 Qt 等高级 gui 工具包中执行此操作,但这里是如何在普通 X11 中执行此操作。(请参阅 EWMH 规范或_wnck_change_state 以获取示例实现)。

//file: test.c
//to build it, run
//shell> gcc test.c -lX11

#include <X11/Xlib.h>   

#define _NET_WM_STATE_REMOVE        0    /* remove/unset property */
#define _NET_WM_STATE_ADD           1    /* add/set property */
#define _NET_WM_STATE_TOGGLE        2    /* toggle property  */


// change a window's _NET_WM_STATE property so that it can be kept on top.
// @display: x11 display singleton.
// @xid    : the window to set on top.
Status x11_window_set_on_top (Display* display, Window xid)
{
    XEvent event;
    event.xclient.type = ClientMessage;
    event.xclient.serial = 0;
    event.xclient.send_event = True;
    event.xclient.display = display;
    event.xclient.window  = xid;
    event.xclient.message_type = XInternAtom (display, "_NET_WM_STATE", False);
    event.xclient.format = 32;

    event.xclient.data.l[0] = _NET_WM_STATE_ADD;
    event.xclient.data.l[1] = XInternAtom (display, "_NET_WM_STATE_ABOVE", False);
    event.xclient.data.l[2] = 0; //unused.
    event.xclient.data.l[3] = 0;
    event.xclient.data.l[4] = 0;

    return XSendEvent (display, DefaultRootWindow(display), False,
                       SubstructureRedirectMask|SubstructureNotifyMask, &event);
}

// a sample main function for testing.
// shell> ./a.out window_xid
int main (int argc, char** argv)
{
    Window xid = strtol (argv[1], NULL, 0); 
    Display* display = XOpenDisplay (NULL);

    x11_window_set_on_top (display, xid);

    XFlush (display); //for simplicity, no event loops here.

    XCloseDisplay (display);
}

另请注意,在某些 x11 环境(例如 compiz)中,系统菜单由单独的装饰器程序而不是合成窗口管理器提供。

【讨论】:

    【解决方案2】:

    在 Go 中,你是这样做的:

    import (
        "github.com/BurntSushi/xgb"
        "github.com/BurntSushi/xgb/xproto"
    )
    
    func (window *Window) AlwaysOnTop() {
        xid := xproto.Window(window.WinId())
        X, err := xgb.NewConn()
        if err != nil {
            log.Println(err)
            return
        }
        defer X.Close()
    
        state, err := xproto.InternAtom(X, false, uint16(len("_NET_WM_STATE")),
            "_NET_WM_STATE").Reply()
        if err != nil {
            log.Println(err)
            return
        }
    
        stateAbove, err := xproto.InternAtom(X, false,
            uint16(len("_NET_WM_STATE_ABOVE")), "_NET_WM_STATE_ABOVE").Reply()
        if err != nil {
            log.Println(err)
            return
        }
    
        evt := xproto.ClientMessageEvent{
            Window: xid,
            Format: 32,
            Type:   state.Atom,
            Data: xproto.ClientMessageDataUnionData32New([]uint32{
                _NET_WM_STATE_ADD,
                uint32(stateAbove.Atom),
                0,
                0,
                0,
            }),
        }
    
        err = xproto.SendEventChecked(X, false, xproto.Setup(X).DefaultScreen(X).Root,
            xproto.EventMaskSubstructureRedirect|xproto.EventMaskSubstructureNotify,
            string(evt.Bytes())).Check()
        if err != nil {
            log.Println(err)
        }
    }
    

    其中window.WinId() 是窗口的原生 X11 句柄。

    【讨论】:

      【解决方案3】:

      我希望当您尝试通过菜单选项将 QML 视图启动到“始终在顶部”模式时,我正确理解了这个问题。

      我在 Windows 上尝试了以下代码到我的 main 中,它对我来说总是在顶部显示窗口,所以我相信视图对象也可以从菜单选项中更改。

      QApplication app(argc, argv);
      QDeclarativeView viewer;
      **viewer.setWindowFlags(Qt::WindowStaysOnTopHint);**
      viewer.setSource(QUrl::fromLocalFile("TestView.qml"));
      viewer.showNormal();
      return app.exec();
      

      谢谢, 泽山

      【讨论】:

      • 对不起,但正如我所说,这个WindowStaysOnTopHint 不起作用,QML 窗口启动时已经设置了这个标志。
      • 感谢您的调查。当我评论使用 WindowStaysOnTopHint 设置窗口样式时,我发现我的窗口并不总是在顶部,因此只要我应用 setWindowFlags(Qt::WindowStaysOnTopHint);窗口始终保持在顶部。所以我相信 QML 在启动它时不会默认设置这个标志。我建议运行示例并观察行为
      • 我尝试为 QQuickWindow(我得到的根对象)设置它的方式是 mywindow-&gt;setFlags(mywindow-&gt;flags() | Qt::WindowStaysOnTopHint)。这不起作用
      • 我刚刚阅读了关于 Qt:WindowsStaysOnTopHint 的文档,其中说在某些情况下您可能还需要传递 Qt::X11BypassWindowManagerHint 标志以使其正常工作。有关更多详细信息,请访问qt-project.org/doc/qt-5.0/qtcore/qt.html#WindowType-enum 链接,您将在该链接中找到一条说明“请注意,在 X11 上的某些窗口管理器上,您还必须传递 Qt::X11BypassWindowManagerHint 才能使此标志正常工作。”希望这能解决问题
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-19
      • 2015-02-08
      • 2018-05-13
      • 2010-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多