You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyQt5实现:如何在每个已连接的显示器上各打开一个窗口?

解决PyQt5在Linux多显示器下创建全屏无工具栏窗口的问题

我之前开发Linux截图工具时也碰到过一模一样的问题,PyQt5在多显示器环境下的窗口定位确实有不少需要注意的细节,再加上Linux不同窗口管理器的特性,很容易出现窗口只固定在一个显示器的情况。咱们一步步来解决:

问题根源分析

你的代码目前存在几个关键问题:

  • 只创建了一个窗口实例:你遍历了所有显示器,但最终只实例化了一个InvisWindow,自然只能在一个显示器上显示
  • setScreen调用时机错误:应该在窗口显示前设置目标屏幕,而且单纯调用setScreen有时候会被窗口管理器忽略,最好结合屏幕的几何信息来强制定位
  • 窗口标志不适合Linux环境:Qt.Tool标志在部分Linux窗口管理器下会导致窗口无法全屏或被正确定位,需要调整标志组合

修正后的完整代码

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

class SelectionWindow(qtw.QWidget):
    selection_made = qtc.pyqtSignal(qtc.QRect)

    def __init__(self, screen):
        super().__init__()
        # 调整窗口标志:改用更适合全屏遮罩的组合,适配Linux窗口管理器
        self.setWindowFlags(
            qtc.Qt.FramelessWindowHint |
            qtc.Qt.WindowStaysOnTopHint |
            qtc.Qt.SplashScreen
        )
        # 设置半透明背景,方便用户看到下方内容
        self.setWindowOpacity(0.3)
        self.setStyleSheet("background-color: black;")
        
        # 获取当前屏幕的几何信息,强制窗口定位到该屏幕
        screen_geo = screen.geometry()
        self.setGeometry(screen_geo)
        
        # 记录选择区域的坐标
        self.start_pos = None
        self.end_pos = None
        self.selection_rect = qtc.QRect()
        
        self.showFullScreen()

    def mousePressEvent(self, event):
        if event.button() == qtc.Qt.LeftButton:
            self.start_pos = event.globalPos()
            self.selection_rect = qtc.QRect()
            self.update()

    def mouseMoveEvent(self, event):
        if self.start_pos is not None:
            self.end_pos = event.globalPos()
            # 标准化矩形,避免宽高为负的情况
            self.selection_rect = qtc.QRect(
                self.start_pos, self.end_pos
            ).normalized()
            self.update()

    def mouseReleaseEvent(self, event):
        if event.button() == qtc.Qt.LeftButton and self.start_pos is not None:
            # 发射选择完成信号,携带选择区域
            self.selection_made.emit(self.selection_rect)
            self.close()

class ScreenSelector:
    def __init__(self):
        self.app = qtw.QApplication(sys.argv)
        self.windows = []
        
        # 为每个显示器创建独立的选择窗口
        for screen in self.app.screens():
            window = SelectionWindow(screen)
            window.selection_made.connect(self.on_selection_made)
            self.windows.append(window)

    def on_selection_made(self, rect):
        # 获取选择区域所在的屏幕并截图
        target_screen = self.app.screenAt(rect.topLeft())
        screenshot = target_screen.grabWindow(0, rect.x(), rect.y(), rect.width(), rect.height())
        # 示例:保存截图到本地
        screenshot.save("selection_screenshot.png")
        # 完成后退出程序
        self.app.quit()

    def run(self):
        sys.exit(self.app.exec_())

if __name__ == '__main__':
    selector = ScreenSelector()
    selector.run()

关键修改点说明

  • 为每个显示器创建独立窗口:遍历app.screens(),给每个显示器单独实例化SelectionWindow,确保每个屏幕都有遮罩窗口
  • 优化窗口标志:用Qt.SplashScreen替代Qt.Tool,结合WindowStaysOnTopHint保证窗口始终在最上层,FramelessWindowHint去掉边框
  • 强制窗口定位:通过screen.geometry()获取显示器的位置和大小,用setGeometry直接设置窗口位置,避免setScreen被窗口管理器忽略
  • 添加选择区域逻辑:重写鼠标事件记录选择坐标,计算出正确的选择矩形,完成后触发截图逻辑

Linux下的额外注意事项

  • 如果使用Wayland窗口管理器,可能需要设置环境变量QT_QPA_PLATFORM=xcb强制使用X11后端,部分Wayland环境对Qt全屏窗口的支持还不完善
  • 窗口透明度的效果在不同窗口管理器下可能有差异,可以根据实际需求调整setWindowOpacity的值

内容的提问来源于stack exchange,提问作者オパラ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 12:17:28