如何修复python pyside应用以检测所有显示器并在主显示器上居中对象
我想创建一个可以跨越所有显示器的窗口,我这里有三个显示器。这个窗口有一个半透明的背景,里面有一个可以拖动的方块。这个方块是用来在我的代码中提供一个位置的。
当我让窗口跨越所有三个显示器时,方块却出现在我的左边显示器上,而这个显示器并不是我的主显示器,位置也不太对。当我修改代码只关注主显示器时,所有功能都正常,但背景就不能跨越所有三个显示器了。
以下是代码,能够让所有三个显示器都有背景,但方块的位置不对。我想要的是背景可以跨越所有三个显示器,但方块的位置要正确。
class DynamicAlignmentWindow(QWidget):
positionSelected = Signal(int, int)
def __init__(self, main_window, width, height):
super().__init__()
self.main = main_window
self.main.hide()
self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
self.setWindowOpacity(0.5)
self.setStyleSheet("background-color: rgba(0, 0, 255, 38);")
# Set the geometry to span all monitors
total_screen_rect = calculate_total_geometry()
self.setGeometry(total_screen_rect)
# Get the geometry of the primary screen
primary_screen_geometry = QApplication.primaryScreen().geometry()
self.lastPosition = None # Initialize last known position
print(f"Total Screen Rect: {total_screen_rect}") # Debugging output for total geometry
print(f"Primary Screen Rect: {primary_screen_geometry}") # Debugging output for primary screen
# Calculate center position for the square on the primary screen
center_x = primary_screen_geometry.x() + (primary_screen_geometry.width() - width) // 2
center_y = primary_screen_geometry.y() + (primary_screen_geometry.height() - height) // 2
print(f"Centering at: X={center_x}, Y={center_y}") # Debugging output for centering
# Initialize the square and center it on the primary screen
self.rubber_band = QRubberBand(QRubberBand.Rectangle, self)
self.rubber_band.setGeometry(QRect(QPoint(center_x, center_y), QSize(width, height)))
self.rubber_band.show()
self.dragging = False
self.drag_position = QPoint()
我只想要这里的三个显示器背景。
这里是代码,只有主显示器有背景,方块的位置是正确的。我希望所有三个显示器都有像上面那样的背景,但方块的位置要像这里一样。
class DynamicAlignmentWindow(QWidget):
positionSelected = Signal(int, int)
def __init__(self, main_window, width, height):
super().__init__()
self.main = main_window
self.main.hide()
self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
self.setWindowOpacity(0.5)
self.setStyleSheet("background-color: rgba(0, 0, 255, 38);")
primary_screen_geometry = QApplication.primaryScreen().geometry()
self.setGeometry(primary_screen_geometry)
self.lastPosition = None # Initialize last known position
print(f"Primary Screen Rect: {primary_screen_geometry}") # Debugging output
center_x = primary_screen_geometry.x() + (primary_screen_geometry.width() - width) // 2
center_y = primary_screen_geometry.y() + (primary_screen_geometry.height() - height) // 2
print(f"Centering at: X={center_x}, Y={center_y}") # More debugging output
self.rubber_band = QRubberBand(QRubberBand.Rectangle, self)
self.rubber_band.setGeometry(QRect(QPoint(center_x, center_y), QSize(width, height)))
self.rubber_band.show()
self.dragging = False
self.drag_position = QPoint()
我只想要这里的方块位置。
我觉得下面这个函数不是问题所在,但我还是加上来,以防对解决这个问题有帮助:
获取所有屏幕的几何信息
def calculate_total_geometry():
total_rect = QRect()
for screen in QApplication.screens():
total_rect = total_rect.united(screen.geometry())
return total_rect
更新
当我使用下面的代码时,我相信我得到了正确的主显示器。我得到了:
总屏幕矩形:PySide6.QtCore.QRect(-1920, 0, 5760, 1081)
然而,这样会把方块居中在我最左边的显示器上,而这个显示器并不是我的主显示器。我对此并不介意,但位置后面就不对应了。我的应用程序使用选定的图像显示覆盖层。当方块在左边的显示器上居中时,我改变它的位置,图像却在下一个显示器上覆盖。
举个例子,如果我把方块放在左边显示器的左上角,它就会在中间显示器的左上角覆盖。所以位置并没有正确对应。我不太确定为什么会这样,但我觉得如果我能让方块在主显示器或用户的主显示器上生成,位置就会对应正确。
self.lastPosition = None # Initialize last known position
self.setGeometry(calculate_total_geometry())
total_screen_rect = calculate_total_geometry()
print(f"Total Screen Rect: {total_screen_rect}") # Debugging output
center_x = total_screen_rect.x() + (total_screen_rect.width() - width) // 2
center_y = total_screen_rect.y() + (total_screen_rect.height() - height) // 2
print(f"Centering at: X={center_x}, Y={center_y}") # More debugging output
1 个回答
你需要更仔细地检查屏幕的几何形状:根据你的输出,合并后的矩形从-1920开始,这表示在全局坐标中有一个负的x值:而主屏幕是从0开始的,最左边的屏幕则是从-1920开始。
但是你却用主屏幕的几何形状作为相对坐标来获取中心的参考位置,这样是不对的:小部件的坐标总是相对于父级的左上角,也就是0, 0
,但你的坐标系统实际上应该考虑整个几何形状,其中有负的全局位置。
为了获得准确的位置,你需要使用基于0的坐标来获取主屏幕的转换过的几何形状。看看下面的图片:
简化的公式也说明了为什么它可以应用于左侧屏幕,同时获得相同的结果,因为它的几何形状是-1920:
LeftScreen.x + LeftScreen.width / 2 - Desktop.x = Xcenter
-1920 + 1920 / 2 - -1920 = 960
虽然你可以简单地加上左侧屏幕的宽度,但更合适的解决方案应该独立于屏幕布局,这样无论整体屏幕几何形状是否从0, 0
开始,它都能始终保持一致:在你的情况下,它可能是负的,但也可能是正的,如果你使用不同高度的屏幕或垂直布局,布局可能还有不同的垂直偏移。
Qt简化了这一切,因为它提供了访问QRect角点的函数,这意味着我们可以简单地使用左上角的负值将几何形状转换为0, 0
(小部件)为基础的坐标:screen.translated(-desktop.topLeft())
。
# this may have an origin (top/left) different than 0, 0
total_screen_rect = calculate_total_geometry()
self.setGeometry(total_screen_rect)
# get the origin point of the screen geometry
origin = total_screen_rect.topLeft()
primary_screen_geometry = QApplication.primaryScreen().geometry()
# translate the geometry in "widget coordinates"; note the minus sign!
local_screen_geometry = primary_screen_geometry.translated(-origin)
center = local_screen_geometry.center()
self.rubber_band = QRubberBand(QRubberBand.Rectangle, self)
self.rubber_band.setGeometry(
center.x() - width // 2, center.y() - height // 2, width, height)
注意,使用QRect(QPoint(x, y), QSize(w, h))
是没有意义的,因为Qt内部会做和QRect(x, y, w, h)
一样的事情,这样创建QPoint和QSize对象就没有必要了;如果你不需要在其他地方使用这个矩形,其实可以跳过setGeometry()
中的QRect创建(就像我上面做的那样),因为Qt会在调用setGeometry(x, y, w, h)
重载时自动创建它。
最后,只有在父级已经显示的情况下,你才应该对子小部件调用show()
。