PyQt5:如何在每个连接的屏幕中打开一个 window?

PyQt5: how to open one window in each connected screen?

我正在尝试使用 python 为 linux 创建屏幕截图实用程序。现在我一直在尝试实现一个功能,让用户 select 从实时屏幕中选择一个区域并对其进行截图。想来想去,我得出的结论是在每个屏幕上创建一个全屏window来获取鼠标的点击和拖动坐标
如何让我的程序为连接到系统的每个屏幕创建一个全屏 window(没有工具栏图标)?

import sys

from PyQt5 import QtWidgets as qtw
from PyQt5 import QtGui as qtg
from PyQt5 import QtCore as qtc

class InvisWindow(qtw.QWidget):
    def __init__(self, screens):
        super().__init__()
        self.setWindowFlags(qtc.Qt.Tool | qtc.Qt.FramelessWindowHint)
        self.show()
        self.showFullScreen()
        self.windowHandle().setScreen(screens[0])

if __name__ == '__main__':
    app = qtw.QApplication(sys.argv)
    mw = InvisWindow(app.screens())
    sys.exit(app.exec_())

我发现这段代码正在寻找一种方法,但无论我将哪个 screen 传递给 setScreen(),它总是出现在一个屏幕上,即改变参数不会改变它出现在哪个屏幕上。

有两个问题:

  1. 作为 documentation explains:

If the screen is part of a virtual desktop of multiple screens, the window will not move automatically to newScreen.

  1. 在 Linux 上,在调用 show 和 window 实际上 映射 之间有一些时间和系统事件第一次出现在屏幕上(参见 Initial Geometry),如果未明确设置几何图形,则可以由 window 管理器覆盖;

也就是说,不需要为此使用 QWindow,因为通常使用 move 就足够了,您只需要在 before 任何调用到 show() 或相关函数:

class InvisWindow(qtw.QWidget):
    def __init__(self, screens):
        super().__init__()
        self.setWindowFlags(qtc.Qt.Tool | qtc.Qt.FramelessWindowHint)
        self.move(screens[0].geometry().topLeft())
        self.showFullScreen()

请注意,在 showFullScreen() 之前调用 show() 是没有用的,因为它隐式调用了 setVisible(True).

如果您想要在所有内容之上显示一个单个 window,那么您可以尝试以下操作:

class InvisWindow(qtw.QWidget):
    mapped = False
    def __init__(self, screens):
        super().__init__()
        self.setWindowFlags(
            qtc.Qt.WindowStaysOnTopHint | 
            qtc.Qt.Tool | 
            qtc.Qt.FramelessWindowHint
        )
        self.show()

    def moveEvent(self, event):
        if not self.mapped:
            geometry = qtc.QRect()
            for screen in qtw.QApplication.screens():
                geometry |= screen.geometry()
            if self.pos() != geometry.topLeft():
                self.setGeometry(geometry)
                self.mapped = True

请考虑最后几行,因为它们非常重要,因为尝试在几何更改事件(moveEventresizeEvent)中进行几何更改会导致递归。