本文中所说的窗口都是指 decorated top-level widget,对于简单程序来说通常就是 QMainWindow 的子类对象。
之前玩 Qt 的时候一直以为 QMainWindow 的大小就是 .geometry()
报告的大小(也就是 .width()
和 .height()
),结果今天发现其实不对的。
起因是我想将一个窗口放在整个桌面的右下角。那么这里有几个问题。
首先 Windows 平台默认下面是任务栏,那么我们肯定不希望窗口的内容被任务栏挡住(或者挡住任务栏),所以这里需要使用 QDesktopWidget.availableGeometry()
来获取可用的屏幕。在 Windows 平台上,这个方法计算除了任务栏之外的屏幕区域;在 Mac 上会计算除了 Dock 栏和菜单栏的屏幕区域。(什么?Linux?抱歉我已经不在桌面上使用 Linux)
那么可能我们就会写出这样的代码
1
2
3
4
5
6
7
8
9
class MyWindow ( QtGui . QMainWindow ):
def __init__ ( self ):
QtGui . QMainWindow . __init__ ( self )
desktop_geopmetry = QtGui . qApp . desktop () . availableGeometry ()
self . move (
desktop_geometry . width () - self . width (),
desktop_geometry . height () - self . height ()
)
那么显然我们就是 too young。因为根据 Qt 文档 ,.geometry()
这一类的方法返回窗口 widget 区域(相对应地,.width()
和 .height()
都是返回窗口 widget 的宽高),而真实的窗口还带有操作系统窗口管理器附加的装饰,例如标题栏和额外的边框。看下图就懂了(图片来自 Qt 文档,链接在本段头部)。
好吧,那么我们现在改成这样就可以了吧?
1
2
3
4
5
6
7
8
9
10
class MyWindow ( QtGui . QMainWindow ):
def __init__ ( self ):
QtGui . QMainWindow . __init__ ( self )
desktop_geopmetry = QtGui . qApp . desktop () . availableGeometry ()
self_geometry = self . frameGeometry ()
self . move (
desktop_geometry . width () - self_geometry . width (),
desktop_geometry . height () - self_geometry . height ()
)
那么显然我们又是 too simple。因为我测试了发现,如果这个窗口还没显示(__init__
里显然还没显示),那么 .frameGeometry()
这类方法的返回结果其实跟 .geometry()
这一类是一样的。换句话说,只有窗口被至少显示过一次,窗口管理器才有机会去装饰它,这样 Qt 才能知道最终窗口的实际占用区域大小。
那么我们只要在首次 showEvent
信号被触发的时候来获取窗口大小,然后据此移动窗口就可以了(当然如果你想每次显示都移动一下我也没话说 =,=
)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyWindow ( QtGui . QMainWindow ):
def __init__ ( self ):
QtGui . QMainWindow . __init__ ( self )
self . __first_show = True
def showEvent ( self , evt ):
evt . accept ()
if self . __first_show :
desktop_geopmetry = QtGui . qApp . desktop () . availableGeometry ()
self_geometry = self . frameGeometry ()
self . move (
desktop_geometry . width () - self_geometry . width (),
desktop_geometry . height () - self_geometry . height ()
)
self . __first_show = False