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,提问作者オパラ
相关产品推荐
相关产品推荐

