¿Cómo determinar una pantalla activa (monitor) de mi aplicación (ventana) usando Python PyQt5?

8

Estoy trabajando en una aplicación que utiliza muchos widgets (QGroupBox, QVBoxLayout, QHBoxLayout). Inicialmente se desarrolló en monitores HD normales. Pero, recientemente, muchos de nosotros actualizamos a monitores de resolución 4K. Ahora, algunos de los botones y controles deslizantes están comprimidos tan pequeños que no se pueden usar.

Ahora intenté hacer algunos cambios para que la aplicación se pueda usar con monitores HD y 4K.

Empecé a leer el siguiente enlace:

https://leomoon.com/journal/python/high-dpi-scaling-in-pyqt5/ ingrese la descripción del enlace aquí

Pensé que cada vez que mi ventana se abre en un monitor en particular, puedo llamar al siguiente código:

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)

Luego traté de obtener la resolución del monitor (pixel_x y pixel_y) usando el código a continuación usando la publicación relacionada aquí .

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 = 1me da la resolución de mi monitor primario (en su mayoría computadoras portátiles en nuestro caso que son HD). screen_width = 78, screen_height = 79me da la resolución combinada de máquinas virtuales. Pero no entiendo cómo puedo obtener dinámicamente estos valores dependiendo de dónde se abrió mi aplicación.

La ventana de mi aplicación está desarrollada de tal manera que se abrirá en el mismo monitor donde se cerró la última vez. El problema ahora es que quiero obtener la resolución del monitor activo cada vez que se llama a mi GUI y adaptarme a esa resolución. Me alegraría si alguien me puede ayudar.

Me interesa saber si puedo llamar al cálculo de resolución de pantalla cada vez que arrastro mi ventana desde un monitor HD a un monitor 4K y viceversa.

Editar: he encontrado algo similar en esta publicación aquí, pero no pude obtener mucho de esto.

Edit2: Basado en la solución @Joe, Detección de pantalla primaria, ¿por qué mi pantalla principal siempre es la resolución de mi computadora portátil aunque ejecuto la aplicación en una pantalla 4K? ingrese la descripción de la imagen aquí

Solo traté de obtener el dpi de todas las pantallas usando el siguiente código:

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
Rohithsai Sai
fuente

Respuestas:

3

Bueno, después de crear MainWindow, puede llamar a QMainWindow.screen () . Esto devuelve la pantalla actual en la que está MainWindow. Esto al menos le permitirá verificar la resolución de la pantalla al inicio de su aplicación. En este momento no existe un screenChangeEvent. Sin embargo, estoy seguro de que puede crear uno subclasificando MainWindow y sobrecargando elQMainWindow.moveEvent

Por ejemplo:

    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)

Esto verifica si la pantalla ha cambiado. Si lo tiene, emite una señal. Ahora solo necesita conectar esta señal a una función que establezca sus atributos de dpi. El evento le da acceso a la pantalla anterior y a la nueva.

Advertencia:

Una de las pantallas puede estar Noneal inicio de su aplicación porque no existe oldScreencuando inicia su aplicación por primera vez. Así que por favor revisa esto.

Tim Körner
fuente
Körnet gracias por la solución. Pero, no hay ningún método llamado QMainWindow.screen () en PyQt5 al menos lo que he entendido. Si me equivoco amablemente muéstrame algún enlace para Python (PyQt5)
Rohithsai Sai
Tampoco pude encontrarlo en la documentación. Sin embargo, todos QWidgetadmiten este método, como puede ver aquí . QMainWindowsubclases QWidget.También probé mi código y funcionó. Por favor, comprueba si también funciona para ti.
Tim Körner el
También recomendaría usar la solución con subclasificar QMainWindowy sobrecargar el moveEvent. Esto funciona al 100% y proporciona más funcionalidad para usted. Puede tomar el código que le proporcioné y usarlo. El código funciona bien y obtengo la screenChangedseñal correcta cada vez que muevo la `` Ventana principal '' en otra pantalla.
Tim Körner el
Estoy interesado en saber cómo dijiste que probaste y trabajaste. En primer lugar, cuando uso QMainWindow.screen () arroja un error que dice que no tiene ningún atributo. En segundo lugar, para moveEvent obtener un error 'QApplication' no tiene el atributo 'screenAt'. Quiero entender si lo has implementado en Python.
Rohithsai Sai
Sí, por supuesto, estoy usando Python. Mi PyQt5versión es 5.13.2 Puede ver aquí que la referencia oficial de PyQt5 también enumera el QApplication.screenAtmétodo.
Tim Körner el
6

Una de las soluciones que llegué a través de es utilizar un temporal 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 ...

Esta función detectará todas las pantallas adjuntas:

# 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

Puede usar las sugerencias aquí y aquí para obtener la pantalla donde se ejecuta la aplicación.

Pero creo primaryScreenque también debería devolver esto:

primaryScreen: QScreen * const

Esta propiedad contiene la pantalla principal (o predeterminada) de la aplicación.

Esta será la pantalla donde se muestran inicialmente QWindows, a menos que se especifique lo contrario.

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

Joe
fuente
Gracias por la respuesta. Lamentablemente esto no está funcionando. Traté de implementar la función anterior en Spyder IDE. Ejecuto la aplicación en un monitor 4K, pero aún así devuelve las resoluciones de mi computadora portátil.
Rohithsai Sai
¿Estaba el 4K conectado a la computadora portátil como segunda pantalla?
Joe
sí, y mi aplicación activa se abre en un monitor 4K
Rohithsai Sai
Por favor, intente la otra función que adjunté. Compruebe también app.primaryScreen()para ver qué pantalla se utiliza.
Joe
1
enlaces agregados a mi respuesta
Joe
3

Aunque no pude obtener la solución directa, puedo desarrollar un método para obtener lo que estaba buscando. Con la ayuda de unos pocos enlaces y una publicación anterior, puedo lograrlo. con esta publicación tuve la idea de rastrear el evento del mouse.

Desarrollé un método para rastrear todos los monitores y las respectivas posiciones de mira. si mi nombre variable no es apropiado, me complace aceptar los cambios

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)

Con las dos funciones anteriores, puedo rastrear la posición de mi aplicación (ventana) y también puedo rastrear cuando se arrastra entre ventanas

Rohithsai Sai
fuente