Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine an active screen (monitor) of my Application (window) using python PyQt5?

I am working on an application which is using many widgets (QGroupBox, QVBoxLayout, QHBoxLayout). Initially it was developed on normal HD monitors. But, recently many of us upgraded to 4K resolution monitors. Now some of the buttons and sliders are compressed so small that they are unusable.

Now I tried to make some changes so that the application can be used with both HD and 4K monitors.

I started reading the link below:

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

I thought whenever my window is opened in a particular monitor I can call the following code:

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)

Then I tried to get the monitor resolution (pixel_x and pixel_y) using below code from using related post here.

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 gives me the resolution of my primary monitor(mostly laptops in our case which are HD). screen_width = 78, screen_height = 79 gives me the combined resolution of virtual machines. But I do not understand how I can dynamically get these values depending upon where my application opened.

My application window is developed in such a way that it will open in the same monitor where it was closed last time. The problem is now I want to get the active monitor resolution whenever my GUI is called and adapt to that resolution. I would be glad if someone can help me out.

I am interested to know if I can call the screen resolution calculation every time that I drag my window from an HD monitor to a 4K monitor and Vice versa.

Edit: I have found something similar in this post here But I could not get much from this.

Edit2: Based on @Joe solution, Primary Screen Detection, Why is my primary screen always my laptop resolution even though I run the application on a 4K screen? enter image description here

I just tried to get the dpi of all the screens using the code below:

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
like image 770
Rohithsai Sai Avatar asked Dec 10 '19 08:12

Rohithsai Sai


2 Answers

One solution I came across is to use a temporary 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 ...

This function will detect all attached screens:

# 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

You can use the hints here and here to get the screen where the application is running.

But I think primaryScreen should also return this:

primaryScreen : QScreen* const

This property holds the primary (or default) screen of the application.

This will be the screen where QWindows are initially shown, unless otherwise specified.

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

like image 73
Joe Avatar answered Sep 28 '22 07:09

Joe


Well, after creating the MainWindow, you can just call QMainWindow.screen(). This returns the current screen the MainWindow is on. This would at least allow you to check the screen resolution at the start of your application. Right now there is no such thing as a screenChangeEvent. However i am sure you can create one by subclassing the MainWindow and overloading the QMainWindow.moveEvent

For example:

    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)

This checks if the screen has changed. If it has it emits a signal. Now you only need to connect this signal to a function that sets your dpi attributes. The event gives you access to the old and to the new screen.

Warning:

One of the screen can be None at the start of your application because there is no oldScreen when you first start your application. So please check this.

like image 41
Tim Körner Avatar answered Sep 28 '22 05:09

Tim Körner