为什么显示后窗口的位置仍为零?

时间:2019-07-29 13:47:15

标签: python geometry window pyside2

我正在尝试创建MainWindow的屏幕位置。 从thisthis中,我了解到调用show()之后就设置了窗口几何形状-但在我的情况下,它保持其默认值,因此位置保持为零。


我要解决的实际问题是使QFileDialog出现在主窗口的中间和前面。实际上,这应该是默认的行为,但是由于主窗口的位置为零,因此对话框出现在屏幕的左上角。 UI是使用Qt设计器创建的,我正在运行时通过QUiLoader加载ui文件。 我注意到了几件事:

  • 如果通过move()setGeometry()将窗口的位置设置为show()之后在屏幕上的实际位置,则可以使对话框出现在中间。
  • 如果我与窗口互动(例如,在ui内,则窗口成员QUiLoader的几何形状(即QPushButton正在加载ui文件的位置)包含正确的值。 -callback),然后可以将窗口的几何形状设置为ui的几何形状,如下所示:self.setGeometry(self.ui.frameGeometry())这是我目前正在做的使对话框显示在右中间的操作在某个按钮的回调中显示对话框之前。

(但这只是为了上下文。)


但是我仍然想知道在show()设置后是否可以使主窗口具有正确的位置。为此,我写了一个最小的示例:一个完全用Python编码的窗口,一个加载ui文件的外观与第一个相同的窗口,以及使其中一个窗口出现的一些代码:

import sys, os
from PySide2 import QtCore, QtWidgets, QtUiTools

class Window1(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(Window1, self).__init__(parent)
        self.resize(300, 200)
        self.pushButton = QtWidgets.QPushButton(self, text="Print geometry")
        self.pushButton.clicked.connect(self.print_geometry)
        self.show()                                    # (A)
        # self.move(300, 200)
        self.print_geometry()                          # (C)

    def print_geometry(self):
        self.updateGeometry()                          # (E)
        print(self.frameGeometry())
        print(self.geometry())


class Window2(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(Window2, self).__init__(parent)
        loader = QtUiTools.QUiLoader()
        file = QtCore.QFile(os.path.abspath("TestWindow.ui"))
        file.open(QtCore.QFile.ReadOnly)
        self.ui = loader.load(file, parent)
        file.close()
        self.ui.pushButton.clicked.connect(self.print_geometry)
        self.ui.show()                                 # (A)
        # self.ui.move(300, 200)
        self.print_geometry()                          # (C)

    def print_geometry(self):
        self.ui.updateGeometry()                       # (E)
        print(self.ui.frameGeometry())
        print(self.ui.geometry())


app = QtWidgets.QApplication(sys.argv)

window = Window1()
# window = Window2()
# window.show()                                        # (B)
window.print_geometry()                                # (D)

sys.exit(app.exec_())

首先我要提一下:

  • show()的结尾还是在创建窗口之后调用__init__()似乎并不重要。 (A)或(B)
  • 是否在print_geometry()内或在创建窗口之后调用__init__()似乎并不重要。 (C)或(D)
  • updateGeometry()调用似乎没有任何作用。 (E)

两个窗口的输出始终为:

PySide2.QtCore.QRect(0, 0, 300, 200)
PySide2.QtCore.QRect(0, 0, 300, 200)
PySide2.QtCore.QRect(0, 0, 300, 200)
PySide2.QtCore.QRect(0, 0, 300, 200)

当我单击按钮时,输出始终为:

PySide2.QtCore.QRect(800, 418, 302, 227)
PySide2.QtCore.QRect(801, 444, 300, 200)

我的问题是:为什么窗口的几何图形即使在show()之后仍具有默认值,并且show()与按钮的回调之间发生了什么,因此几何图形突然包含正确的值?我可以在__init__()之后在窗口的show()中模拟吗?这是我正在使用的Linux Mint的窗口管理器Cinnamon特有的行为吗?

TestWindow.ui:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>300</width>
    <height>200</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QPushButton" name="pushButton">
    <property name="text">
     <string>Print geometry</string>
    </property>
   </widget>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

1 个答案:

答案 0 :(得分:1)

此行为仅在X11平台(例如Linux)上发生-请参见Window GeometryX11 Peculiarities部分。

关键是在X11上,某些与窗口相关的事件异步发生。这意味着您必须等到窗口管理器为窗口指定一个框架并将其放置在屏幕上,然后才能请求几何图形。如果您单击按钮以打印几何图形,则不需要直接(即同步)请求它-这说明了为什么看到不同的输出。

如果需要在显示窗口后立即执行某些操作,要求其有关几何形状的信息,则一个简单的解决方法是使用单次计时器:

class Window1(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        ...
        self.show()
        # self.print_geometry()
        QtCore.QTimer.singleShot(50, self.print_geometry)

在实践中,延迟的实际长度将取决于系统,并且可能在1ms到25ms之间。您可能需要尝试一下,但50ms应该是安全的。