如何修复python pyside应用以检测所有显示器并在主显示器上居中对象

0 投票
1 回答
45 浏览
提问于 2025-04-14 17:30

我想创建一个可以跨越所有显示器的窗口,我这里有三个显示器。这个窗口有一个半透明的背景,里面有一个可以拖动的方块。这个方块是用来在我的代码中提供一个位置的。

当我让窗口跨越所有三个显示器时,方块却出现在我的左边显示器上,而这个显示器并不是我的主显示器,位置也不太对。当我修改代码只关注主显示器时,所有功能都正常,但背景就不能跨越所有三个显示器了。

以下是代码,能够让所有三个显示器都有背景,但方块的位置不对。我想要的是背景可以跨越所有三个显示器,但方块的位置要正确。

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 个回答

0

你需要更仔细地检查屏幕的几何形状:根据你的输出,合并后的矩形从-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()

撰写回答