Resize display resolution using python with cross platform support

19,141

Solution 1

Below is a solution that works on Windows (depends on pywin32). There are placeholders where you can put in your existing linux code, I'm not sure what to do about OS X though.

from __future__ import print_function
import sys

class ScreenRes(object):
    @classmethod
    def set(cls, width=None, height=None, depth=32):
        '''
        Set the primary display to the specified mode
        '''
        if width and height:
            print('Setting resolution to {}x{}'.format(width, height, depth))
        else:
            print('Setting resolution to defaults')

        if sys.platform == 'win32':
            cls._win32_set(width, height, depth)
        elif sys.platform.startswith('linux'):
            cls._linux_set(width, height, depth)
        elif sys.platform.startswith('darwin'):
            cls._osx_set(width, height, depth)

    @classmethod
    def get(cls):
        if sys.platform == 'win32':
            return cls._win32_get()
        elif sys.platform.startswith('linux'):
            return cls._linux_get()
        elif sys.platform.startswith('darwin'):
            return cls._osx_get()

    @classmethod
    def get_modes(cls):
        if sys.platform == 'win32':
            return cls._win32_get_modes()
        elif sys.platform.startswith('linux'):
            return cls._linux_get_modes()
        elif sys.platform.startswith('darwin'):
            return cls._osx_get_modes()

    @staticmethod
    def _win32_get_modes():
        '''
        Get the primary windows display width and height
        '''
        import win32api
        from pywintypes import DEVMODEType, error
        modes = []
        i = 0
        try:
            while True:
                mode = win32api.EnumDisplaySettings(None, i)
                modes.append((
                    int(mode.PelsWidth),
                    int(mode.PelsHeight),
                    int(mode.BitsPerPel),
                    ))
                i += 1
        except error:
            pass

        return modes

    @staticmethod
    def _win32_get():
        '''
        Get the primary windows display width and height
        '''
        import ctypes
        user32 = ctypes.windll.user32
        screensize = (
            user32.GetSystemMetrics(0), 
            user32.GetSystemMetrics(1),
            )
        return screensize

    @staticmethod
    def _win32_set(width=None, height=None, depth=32):
        '''
        Set the primary windows display to the specified mode
        '''
        # Gave up on ctypes, the struct is really complicated
        #user32.ChangeDisplaySettingsW(None, 0)
        import win32api
        from pywintypes import DEVMODEType
        if width and height:

            if not depth:
                depth = 32

            mode = win32api.EnumDisplaySettings()
            mode.PelsWidth = width
            mode.PelsHeight = height
            mode.BitsPerPel = depth

            win32api.ChangeDisplaySettings(mode, 0)
        else:
            win32api.ChangeDisplaySettings(None, 0)


    @staticmethod
    def _win32_set_default():
        '''
        Reset the primary windows display to the default mode
        '''
        # Interesting since it doesn't depend on pywin32
        import ctypes
        user32 = ctypes.windll.user32
        # set screen size
        user32.ChangeDisplaySettingsW(None, 0)

    @staticmethod
    def _linux_set(width=None, height=None, depth=32):
        raise NotImplementedError()

    @staticmethod
    def _linux_get():
        raise NotImplementedError()

    @staticmethod
    def _linux_get_modes():
        raise NotImplementedError()

    @staticmethod
    def _osx_set(width=None, height=None, depth=32):
        raise NotImplementedError()

    @staticmethod
    def _osx_get():
        raise NotImplementedError()

    @staticmethod
    def _osx_get_modes():
        raise NotImplementedError()


if __name__ == '__main__':
    print('Primary screen resolution: {}x{}'.format(
        *ScreenRes.get()
        ))
    print(ScreenRes.get_modes())
    #ScreenRes.set(1920, 1080)
    #ScreenRes.set() # Set defaults

Solution 2

Many of the answers are already scattered around StackOverflow and can be summarized as follows.

To get the resolution on Windows in a purely pythonic fashion (reference: https://stackoverflow.com/a/3129524/2942522):

import ctypes
user32 = ctypes.windll.user32
screensize = user32.GetSystemMetrics(0), user32.GetSystemMetrics(1)

The MacOS solution also uses Python, but uses a package outside the standard library (reference: https://stackoverflow.com/a/3129567/2942522):

import AppKit
[(screen.frame().size.width, screen.frame().size.height)
    for screen in AppKit.NSScreen.screens()]

Apparently the list comprehension will iterate over the screens in a multiple monitor setup.

I think Alex Martelli's response to a related issue (https://stackoverflow.com/a/2662892/2942522) is also notable. He uses:

pygame.display.list_modes()
[(1920, 1080), (1768, 992), (1680, 1050), (1600, 1200), (1600, 1024), (1600, 900
), (1440, 900), (1400, 1050), (1360, 768), (1280, 1024), (1280, 960), (1280, 800
), (1280, 768), (1280, 720), (1152, 864), (1024, 768), (800, 600), (720, 576), (
720, 480), (640, 480)]

to get a list of largest to smallest resolutions available (although pygame would become a dependency if you went this route). Conversely, I suspect it would work just fine in a cross-platform setting. Furthermore, he mentions pygame.display.set_mode for setting the resolution (docs: http://www.pygame.org/docs/ref/display.html#pygame.display.set_mode). Here's a snippet of the docs for set_mode:

"The resolution argument is a pair of numbers representing the width and height. The flags argument is a collection of additional options. The depth argument represents the number of bits to use for color."

Maybe that will get you started. At the very least you could perhaps check the source code for set_mode to see if there's some possible inspiration there if you cannot use it directly.

Other potentially useful ideas:

  1. You can do a crude platform check with sys.platform (docs: http://docs.python.org/2/library/sys.html#sys.platform). This returns 'darwin' on MacOS.
  2. The bit architecture should be accessible with the Python platform module. If I run platform.architecture() on my machine it returns a tuple: ('64bit', '') (docs: http://docs.python.org/2/library/platform.html#platform.architecture)

Solution 3

To get and set the resolution under Windows (both 32 and 64 bits) you can use the ctypes and the user32 dll (ctypes.windll.user32). Don't care about the '32' in the name of the dll - it's a 64 bits dll on 64 bits Windows. This library you can use also to discover the allowed resolutions.

Alternatively you can use some command line tool like nircmd.exe :

nircmd.exe 1024 768 32

The last number is the colour depth.

Hope this helps.

Share:
19,141
sj7
Author by

sj7

Updated on June 05, 2022

Comments

  • sj7
    sj7 almost 2 years

    Resize display resolution using a python function. It should be cross platform, ie support for windows, linux and mac (it is okay to have multiple cases depending on the operating system)

    I have code which I think works on linux (Ubuntu) I am looking for a solution for windows and mac (should support both 32 and 64 bit machines)

    def SetResolution(width, height):
        os.popen("xrandr -s "+str(width)+'x'+str(height))
    

    I would also appreciate it if somebody could tell me how I could get the possible display resolutions for windows and mac

    my function on linux is this:

    def GetResolutions():
        screen = os.popen("xrandr").readlines()
        possibleResolutions = []
        for a in screen:
            data = a.split()
            if len(data)<4:
                width, height = data[0].split('x')
                fps = re.sub("[^0-9.]", "", data[1])
                possibleResolutions.append({'width':int(width),'height':int(height),'fps':float(fps)})
                if '*' in data[1]:
                    currentResolution = {'width':int(width),'height':int(height),'fps':float(fps)}
        return possibleResolutions, currentResolution
    
  • sj7
    sj7 over 10 years
    Hey thanks for summarizing a list of resources. The function that pygame has works for setting the display size of the frame that it creates for its gui as opposed to the one for the monitor.
  • sj7
    sj7 over 10 years
    Also could not find a way to get the resolutions supported on win64
  • martineau
    martineau over 10 years
    @sj7: FWIW, on Windows the display resolution can be changed using the Win API function ChangeDisplaySettings.
  • sj7
    sj7 over 10 years
    @treddy what I meant by I could not get it to work on win64 was that I can detect when it is a 64 bit machine. But I cannot use the win32 library for getting the resolutions
  • sj7
    sj7 over 10 years
    @martineaum thanks for the input I guess I will have to write a winapi call directly in c++ or something from python
  • treddy
    treddy over 10 years
    Indeed, I haven't found anything great about controlling win64 display resolution from Python.
  • Ehtesh Choudhury
    Ehtesh Choudhury over 10 years
    Nircmd is great for instrumentation of any kind on Windows and works across the spectrum from XP to Windows 8.
  • Sidath Asiri
    Sidath Asiri over 5 years
    why doesn't this support for 1440x810 resolution?
  • dwurf
    dwurf over 5 years
    It supports the resolutions pywin32 can use, which is shown by calling ScreenRes.get_modes()