【问题标题】:How to determine an active screen (monitor) of my Application (window) using python PyQt5?如何使用 python PyQt5 确定我的应用程序(窗口)的活动屏幕(监视器)?
【发布时间】:2020-04-03 09:19:40
【问题描述】:

我正在开发一个使用许多小部件(QGroupBox、QVBoxLayout、QHBoxLayout)的应用程序。最初它是在普通高清显示器上开发的。但是,最近我们中的许多人升级到 4K 分辨率显示器。现在一些按钮和滑块被压缩得太小以至于无法使用。

现在我尝试进行一些更改,以便该应用程序可以同时用于高清和 4K 显示器。

我开始阅读下面的链接:

https://leomoon.com/journal/python/high-dpi-scaling-in-pyqt5/enter link description here

我认为每当我的窗口在特定监视器中打开时,我都可以调用以下代码:

if pixel_x > 1920 and pixel_y > 1080:
  Qapp.setAttribute(Qt.AA_EnableHighDpiScaling, True)
  Qapp.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
else:
  Qapp.setAttribute(Qt.AA_EnableHighDpiScaling, False)
  Qapp.setAttribute(Qt.AA_UseHighDpiPixmaps, False)

然后我尝试使用以下代码从相关帖子here 获取显示器分辨率(pixel_x 和 pixel_y)。

import sys, ctypes

user32 = ctypes.windll.user32
user32.SetProcessDPIAware()
screen_width  = 0 #78
screen_height = 1 #79
[pixel_x , pixel_y ] = [user32.GetSystemMetrics(screen_width), user32.GetSystemMetrics(screen_height)]

screen_width = 0, screen_height = 1 为我提供主显示器的分辨率(在我们的案例中主要是高清笔记本电脑)。 screen_width = 78, screen_height = 79 给了我虚拟机的组合分辨率。但我不明白如何根据应用程序打开的位置动态获取这些值。

我的应用程序窗口的开发方式是,它将在上次关闭的同一监视器中打开。现在的问题是,每当调用我的 GUI 并适应该分辨率时,我都想获得活动监视器分辨率。如果有人可以帮助我,我会很高兴。

我很想知道每次将窗口从高清显示器拖动到 4K 显示器时是否可以调用屏幕分辨率计算,反之亦然。

编辑:我在这篇文章here 中发现了类似的东西,但我不能从中得到太多。

Edit2:基于@Joe 解决方案,主屏幕检测,为什么我的主屏幕始终是笔记本电脑的分辨率,即使我在 4K 屏幕上运行应用程序?

我只是尝试使用以下代码获取所有屏幕的 dpi:

def screen_selection():
  app = QApplication(sys.argv)
  valid_screens = []
  for index, screen_no in enumerate(app.screens()):
    screen = app.screens()[index]
    dpi = screen.physicalDotsPerInch()
    valid_screens.append(dpi)
  return valid_screens

【问题讨论】:

标签: python user-interface pyqt5 resolution pythoninterpreter


【解决方案1】:

我来across 的一个解决方案是使用临时的QApplication()

import sys
from PyQt5 import QtWidgets, QtCore, QtGui

# fire up a temporary QApplication
def get_resolution():

    app = QtWidgets.QApplication(sys.argv)

    print(app.primaryScreen())

    d = app.desktop()

    print(d.screenGeometry())
    print(d.availableGeometry())
    print(d.screenCount())    

    g = d.screenGeometry()
    return (g.width(), g.height())

x, y = get_resolution()

if x > 1920 and y > 1080:
  QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True)
  QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True)
else:
  QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, False)
  QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, False)

# Now your code ...

此功能将检测所有附加的屏幕:

# fire up a temporary QApplication
def get_resolution_multiple_screens():

    app = QtGui.QGuiApplication(sys.argv)
    #QtWidgets.QtGui
    all_screens = app.screens()

    for s in all_screens:

        print()
        print(s.name())
        print(s.availableGeometry())
        print(s.availableGeometry().width())
        print(s.availableGeometry().height())
        print(s.size())
        print(s.size().width())
        print(s.size().height())

    print()
    print('primary:', app.primaryScreen())
    print('primary:', app.primaryScreen().availableGeometry().width())
    print('primary:', app.primaryScreen().availableGeometry().height())

    # now choose one

您可以使用提示herehere 来获取应用程序正在运行的屏幕。

但我认为primaryScreen 也应该返回这个:

primaryScreen : QScreen* 常量

此属性保存主屏幕(或默认屏幕) 应用。

这将是最初显示 QWindows 的屏幕,除非 另有规定。

(https://doc.qt.io/qt-5/qguiapplication.html#primaryScreen-prop)

【讨论】:

  • 感谢您的回答。不幸的是,这不起作用。我试图在 Spyder IDE 中实现上述功能。我在 4K 显示器上运行该应用程序,但它仍然返回我的笔记本电脑分辨率。
  • 4K 是否作为第二个屏幕连接到笔记本电脑?
  • 是的,我的活动应用程序已在 4K 显示器中打开
  • 请尝试我附加的其他功能。还要检查app.primaryScreen() 以查看使用的是哪个屏幕。
  • 添加了我的答案的链接
【解决方案2】:

好吧,在创建 MainWindow 之后,您可以调用 QMainWindow.screen()。这将返回 MainWindow 所在的当前屏幕。这至少可以让您在应用程序开始时检查屏幕分辨率。 现在还没有 screenChangeEvent 之类的东西。但是我相信您可以通过子类化 MainWindow 并重载QMainWindow.moveEvent 来创建一个@

例如:

    class MainWindow(QtWidgets.QMainWindow):
        screenChanged = QtCore.pyqtSignal(QtGui.QScreen, QtGui.QScreen)

        def moveEvent(self, event):
            oldScreen = QtWidgets.QApplication.screenAt(event.oldPos())
            newScreen = QtWidgets.QApplication.screenAt(event.pos())

            if not oldScreen == newScreen:
                self.screenChanged.emit(oldScreen, newScreen)

            return super().moveEvent(event)

这会检查屏幕是否发生了变化。如果有,它会发出一个信号。现在您只需将此信号连接到设置 dpi 属性的函数。该活动使您可以访问旧屏幕和新屏幕。

警告:

其中一个屏幕可以在您的应用程序开始时为None,因为在您第一次启动应用程序时没有oldScreen。所以请检查一下。

【讨论】:

  • Körnet 感谢您的解决方案。但是,至少我所理解的 PyQt5 中没有称为 QMainWindow.screen() 的方法。如果我错了,请给我一些 python (PyQt5) 的链接
  • 我在文档中也找不到。但是每个QWidget 都支持这种方法,如您所见hereQMainWindow 子类 QWidget. 我也测试了我的代码并且它有效。请检查它是否也适合您。
  • 我还建议使用将QMainWindow 子类化并重载moveEvent 的解决方案。这 100% 有效,并为您提供更多功能。您可以获取我提供的代码并使用它。这段代码工作正常,每当我在另一个屏幕上移动“MainWindow”时,我都会得到正确的screenChanged 信号。
  • 我很想知道您所说的测试和工作方式。首先,当我使用 QMainWindow.screen() 时,它会抛出一个错误,说它没有属性。其次,对于 moveEvent 获取错误“QApplication”没有属性“screenAt”。我想了解您是否已在 Python 中实现。
  • 是的,我当然在使用 python。我的PyQt5版本是5.13.2你可以看到右边here官方PyQt5参考也列出了QApplication.screenAt方法。
【解决方案3】:

虽然我无法获得直接的解决方案,但我能够开发一种方法来获得我正在寻找的东西。在几个链接和以前的帖子的帮助下,我能够实现。通过this post,我有了一个跟踪鼠标事件的想法。

我开发了一种方法来跟踪所有显示器和各自的凝视位置。如果我的变量命名不合适,我很乐意接受更改

def get_screen_resolution():
  app = QApplication(sys.argv)
  screen_count = QGuiApplication.screens()
  resolutions_in_x = []

  for index, screen_names in enumerate(screen_count):
    resolution = screen_count[index].size()
    height = resolution.height()
    width = resolution.width()
    resolutions_in_x.append(width)

  low_resolution_monitors = {}
  high_resolution_monitors = {}

  for i, wid_res in enumerate(resolutions_in_x):
    if wid_res > 1920:
      high_resolution_monitors.update({i: wid_res})
    else:
      low_resolution_monitors.update({'L': wid_res})    
  temp_value = 0
  high_res_monitors_x_position = []
  low_res_monitors_x_position = []
  for i in range(len(screen_count)):
    temp_value = temp_value+resolutions_in_x[i]
      if resolutions_in_x[i] in high_resolution_monitors.values():
        high_res_monitors_x_position.append(temp_value-resolutions_in_x[i])
      else:
        low_res_monitors_x_position.append(temp_value-resolutions_in_x[i])

  total_width_res = []
  pixel_value = 0
  first_pixel = 0
  for i, wid_value in enumerate(resolutions_in_x):
    pixel_value = pixel_value + wid_value
    total_width_res.append(tuple((first_pixel, pixel_value-1)))
    first_pixel = pixel_value

  return high_res_monitors_x_position, low_res_monitors_x_position, total_width_res


def moveEvent(self, event):

screen_pos = self.pos()
screen_dimensions = [screen_pos.x(),screen_pos.y()]
super(MainWindow, self).moveEvent(event)

Window_starting_pt = screen_pos.x()
for i, value in enumerate(self.total_width_res):
  if value[0]<=Window_starting_pt+30 <=value[1] or value[0]<=Window_starting_pt-30 <=value[1]: #taking 30pixels as tolerance since widgets are staring at some negative pixel values
    if value[0] in self.high_res_monitors_x_position:
      QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True)
      QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True)
    else:
      QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, False)
      QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, False)

使用 aboe 两个函数可以跟踪我的应用程序(窗口)的位置,也可以跟踪它何时在窗口之间拖动

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多