Como determinar uma tela ativa (monitor) do meu aplicativo (janela) usando python PyQt5?

8

Estou trabalhando em um aplicativo que está usando muitos widgets (QGroupBox, QVBoxLayout, QHBoxLayout). Inicialmente, foi desenvolvido em monitores HD normais. Recentemente, porém, muitos de nós atualizamos para monitores de resolução 4K. Agora, alguns dos botões e controles deslizantes são compactados tão pequenos que são inutilizáveis.

Agora, tentei fazer algumas alterações para que o aplicativo pudesse ser usado com os monitores HD e 4K.

Comecei a ler o link abaixo:

https://leomoon.com/journal/python/high-dpi-scaling-in-pyqt5/ digite a descrição do link aqui

Eu pensei que sempre que minha janela é aberta em um monitor específico, eu posso chamar o seguinte 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)

Então tentei obter a resolução do monitor (pixel_x e pixel_y) usando o código abaixo usando post relacionado aqui .

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 = 1fornece a resolução do meu monitor principal (na maioria dos casos, laptops que são HD). screen_width = 78, screen_height = 79me dá a resolução combinada de máquinas virtuais. Mas não entendo como posso obter esses valores dinamicamente, dependendo de onde meu aplicativo foi aberto.

Minha janela do aplicativo é desenvolvida de tal maneira que será aberta no mesmo monitor em que foi fechada pela última vez. O problema agora é que quero obter a resolução do monitor ativo sempre que minha GUI for chamada e me adaptar a essa resolução. Eu ficaria feliz se alguém puder me ajudar.

Estou interessado em saber se posso chamar o cálculo da resolução da tela toda vez que arrasto minha janela de um monitor HD para um monitor 4K e vice-versa.

Edit: Eu encontrei algo semelhante neste post aqui, mas eu não poderia tirar muito disso.

Edit2: Com base na solução @ Joe, Detecção de tela primária, Por que minha tela principal é sempre a resolução do meu laptop, mesmo que eu execute o aplicativo em uma tela 4K? insira a descrição da imagem aqui

Eu apenas tentei obter o dpi de todas as telas usando o código abaixo:

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
fonte

Respostas:

3

Bem, depois de criar o MainWindow, você pode simplesmente chamar QMainWindow.screen () . Isso retorna a tela atual em que a MainWindow está ativada. Isso permitiria, pelo menos, verificar a resolução da tela no início do seu aplicativo. No momento, não existe um screenChangeEvent. No entanto, tenho certeza de que você pode criar uma subclassificando a MainWindow e sobrecarregando oQMainWindow.moveEvent

Por exemplo:

    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)

Isso verifica se a tela mudou. Se houver, emite um sinal. Agora você só precisa conectar esse sinal a uma função que defina seus atributos de dpi. O evento fornece acesso à tela antiga e à nova tela.

Atenção:

Uma das telas pode estar Noneno início do seu aplicativo porque não existe oldScreenquando você o inicia. Então, por favor, verifique isso.

Tim Körner
fonte
Körnet, obrigado pela solução. Mas, não existe um método chamado QMainWindow.screen () no PyQt5 pelo menos o que eu entendi. Se estiver errado por favor me mostre alguma ligação para python (PyQt5)
Rohithsai Sai
Também não consegui encontrá-lo na documentação. No entanto, todos QWidgetsuportam esse método, como você pode ver aqui . QMainWindowsubclasses QWidget.Também testei meu código e funcionou. Por favor, verifique se ele também funciona para você.
Tim Körner
Eu também recomendaria o uso da solução com subclassificação QMainWindowe sobrecarga da moveEvent. Isso funciona 100% e fornece mais funcionalidade para você. Você pode simplesmente pegar o código que eu forneci e usá-lo. O trecho de código funciona bem e eu recebo o screenChangedsinal correto sempre que movo o `` MainWindow` para outra tela.
Tim Körner
Estou interessado em saber como você disse que testou e funcionou. Em primeiro lugar, quando eu uso QMainWindow.screen (), ele gera um erro dizendo que não tem atributo. Em segundo lugar, para o moveEvent receber um erro 'QApplication' não tem atributo 'screenAt'. Eu quero entender se você implementou em Python.
Rohithsai Sai 13/12/19
Sim, claro que estou usando python. Minha PyQt5versão é 5.13.2 Você pode ver aqui que a referência oficial do PyQt5 também lista o QApplication.screenAtmétodo.
Tim Körner
6

Uma solução que me deparei é usar um temporário 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 função detectará todas as telas anexadas:

# 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

Você pode usar as dicas aqui e aqui para obter a tela em que o aplicativo está sendo executado.

Mas acho primaryScreenque também deve retornar isso:

primaryScreen: QScreen * const

Esta propriedade mantém a tela principal (ou padrão) do aplicativo.

Essa será a tela em que o QWindows é mostrado inicialmente, a menos que seja especificado de outra forma.

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

Joe
fonte
Obrigado pela resposta. Infelizmente isso não está funcionando. Eu tentei implementar a função acima no Spyder IDE. Eu executo o aplicativo no monitor 4K, mas ele ainda retorna as resoluções do meu laptop.
Rohithsai Sai 10/12/19
O 4K foi anexado ao laptop como segunda tela?
Joe
sim e meu aplicativo ativo é aberto no monitor 4K
Rohithsai Sai
Por favor, tente a outra função anexada. Verifique também app.primaryScreen()para ver qual tela é usada.
Joe
1
links adicionados à minha resposta
Joe
3

Embora eu não tenha conseguido a solução direta, sou capaz de desenvolver um método para obter o que estava procurando. Com a ajuda de alguns links e post anterior eu sou capaz de alcançar. com este post , tive uma ideia de acompanhar o evento do mouse.

Eu desenvolvi um método para rastrear todos os monitores e respectivas posições de observação. se minha nomeação de variável não for apropriada, fico feliz em aceitar as alterações

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)

Com o aboe, duas funções são capazes de rastrear a posição do meu aplicativo (janela) e também de rastrear quando ele é arrastado pelas janelas

Rohithsai Sai
fonte