如何将QWidget放置在指定显示器上(PySide6多屏场景)
问题
基于Python 3.11、PySide6及Windows 10 Enterprise 22H2开发项目,需要创建作为其他应用窗口子窗口的QDialog,并将其置于父应用主窗口内。但在多显示器场景下遇到问题:对话框始终显示在主显示器(左上角坐标为0,0的显示器),当设置的对话框位置超出主显示器范围时,会被自动居中显示在主显示器。
现有代码如下:
from PySide6 import QtWidgets as qw import win32gui as win32 class DialogApp(qw.QDialog): global iRect def __init__(self, parent=None): super(DialogApp, self).__init__(parent) self.initUI() def initUI(self): layout = qw.QGridLayout() layout.setColumnStretch(0,1) self.setLayout(layout) self.setWindowTitle("Test Dialog") self.setGeometry(iRect[0] + 100, iRect[1] + 100, 200, 20) self.setSizeGripEnabled(True) def mainWindow(): parent_window_ptr = getMainWindow() # 该函数由父应用提供 return wrapInstance(int(parent_window_ptr), qw.QMainWindow) iRect = win32.GetWindowRect(parent_window_handle) # 通过Windows API获取父窗口句柄 da = DialogApp(mainWindow()) da.show()
上述代码仅在父窗口位于主显示器时,才能让对话框显示在父窗口左上角偏移100像素的位置。想知道是否可通过指定显示器来强制放置QWidget,例如使用类似monitor_info = win32api.GetMonitorInfo(win32api.MonitorFromWindow(other_window_handle, win32con.MONITOR_DEFAULTTOPRIMARY))的方法。
解决方案
可以结合Windows API获取父窗口所在显示器信息,配合PySide6的窗口定位逻辑解决多显示器下的位置问题,具体实现如下:
核心步骤
- 获取父窗口所在显示器的工作区域(排除任务栏等系统占用区域)
- 明确对话框的子窗口属性,避免系统将其视为独立窗口调整位置
- 精准计算对话框位置并完成定位
修改后的代码
from PySide6 import QtWidgets as qw import win32gui import win32api import win32con class DialogApp(qw.QDialog): def __init__(self, parent=None): super(DialogApp, self).__init__(parent) # 设置窗口标志,明确为父窗口的子窗口,继承显示器上下文 self.setWindowFlags(qw.Qt.Dialog | qw.Qt.WindowStaysOnTopHint | qw.Qt.SubWindow) self.initUI() def initUI(self): layout = qw.QGridLayout() layout.setColumnStretch(0, 1) self.setLayout(layout) self.setWindowTitle("Test Dialog") self.resize(200, 20) self.setSizeGripEnabled(True) def mainWindow(): parent_window_ptr = getMainWindow() # 该函数由父应用提供 return wrapInstance(int(parent_window_ptr), qw.QMainWindow) # 获取父窗口句柄与位置 parent_handle = parent_window_handle # 替换为实际父窗口句柄 parent_rect = win32gui.GetWindowRect(parent_handle) # 获取父窗口所在显示器的工作区域 monitor_handle = win32api.MonitorFromWindow(parent_handle, win32con.MONITOR_DEFAULTTONEAREST) monitor_info = win32api.GetMonitorInfo(monitor_handle) monitor_work_area = monitor_info['Work'] # 格式:(x1, y1, x2, y2) # 计算对话框目标位置:父窗口左上角偏移100像素 target_x = parent_rect[0] + 100 target_y = parent_rect[1] + 100 # 可选:确保对话框位置不超出显示器工作区域 target_x = max(monitor_work_area[0], min(target_x, monitor_work_area[2] - 200)) target_y = max(monitor_work_area[1], min(target_y, monitor_work_area[3] - 20)) # 创建并显示对话框 parent_win = mainWindow() da = DialogApp(parent_win) da.move(target_x, target_y) da.show()
关键说明
- 窗口标志设置:添加
Qt.SubWindow标志,明确对话框是父窗口的子窗口,继承父窗口的显示器上下文,避免系统强制将其迁移到主显示器。 - 显示器精准匹配:使用
MonitorFromWindow结合MONITOR_DEFAULTTONEAREST参数,确保获取的是父窗口所在的显示器,而非默认主显示器。 - 位置校验:可选的范围校验逻辑,避免对话框超出显示器可视区域,防止系统自动调整位置。
内容的提问来源于stack exchange,提问作者user2619035
相关产品推荐
相关产品推荐

