PyMonCtl


NamePyMonCtl JSON
Version 0.91 PyPI version JSON
download
home_pagehttps://github.com/Kalmat/PyMonCtl
SummaryCross-Platform toolkit to get info on and control monitors connected
upload_time2024-04-18 19:52:02
maintainerNone
docs_urlNone
authorKalmat
requires_pythonNone
licenseBSD 3
keywords screen display monitor control geometry size position frequency scale orientation screen-size mouse-position
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # PyMonCtl
[![Type Checking](https://github.com/Kalmat/PyMonCtl/actions/workflows/type-checking.yml/badge.svg)](https://github.com/Kalmat/PyMonCtl/actions/workflows/type-checking.yml)
[![PyPI version](https://badge.fury.io/py/PyMonCtl.svg)](https://badge.fury.io/py/PyMonCtl)

Cross-Platform module which provides a set of features to get info on and control monitors.

Additional tools/extensions/APIs used:
- Linux:
  - Xlib's randr extension
  - xrandr command-line tool
  - xset command-line tool
- Windows:
  - VCP MCCS API interface
- macOS:
  - pmset command-line tool
    

## General Features

Functions to get monitor instances, get info and arrange monitors plugged to the system.

|   General functions:    |
|:-----------------------:|
|     getAllMonitors      |
|   getAllMonitorsDict    |
|    getMonitorsCount     |
|       getPrimary        |
|   findMonitorsAtPoint   |
| findMonitorsAtPointInfo |
|   findMonitorWithName   |
| findMonitorWithNameInfo |
|        saveSetup        |
|      restoreSetup       |
|     arrangeMonitors     |
|       getMousePos       |


## Monitor Class

Class to access all methods and functions to get info and control a given monitor plugged to the system.

This class is not meant to be directly instantiated. Instead, use convenience functions like `getAllMonitors()`,
`getPrimary()` or `findMonitorsAtPoint(x, y)`. Use [PyWinCtl](https://github.com/Kalmat/PyWinCtl) module in case you need to 
find the monitor a given window is in, by using `getDisplay()` method which returns the name of the monitor that
can directly be used to invoke `findMonitorWithName(name)` function.

To instantiate it, you need to pass the monitor handle (OS-dependent). It can raise ValueError exception in case 
the provided handle is not valid.

|    Methods     | Windows | Linux | macOS |
|:--------------:|:-------:|:-----:|:-----:|
|      size      |    X    |   X   |   X   |
|    workarea    |    X    |   X   |   X   |
|    position    |    X    |   X   |   X   |
|  setPosition   |    X    |   X   |   X   |
|      box       |    X    |   X   |   X   |
|      rect      |    X    |   X   |   X   |
|   frequency    |    X    |   X   |   X   |
|   colordepth   |    X    |   X   |   X   |
|      dpi       |    X    |   X   |   X   |
|     scale      |    X    |   X   |   X   |
|    setScale    |    X    |   X   |   X   |
|  orientation   |    X    |   X   |   X   |
| setOrientation |    X    |   X   | X (1) |
|   brightness   |  X (2)  |   X   |   X   |
| setBrightness  |  X (2)  |   X   |   X   |
|    contrast    |  X (2)  | X (3) | X (3) |
|  setContrast   |  X (2)  | X (3) | X (3) |
|      mode      |    X    |   X   |   X   |
|    setMode     |    X    |   X   |   X   |
|  defaultMode   |    X    |   X   |   X   |
| setDefaultMode |    X    |   X   |   X   |
|    allModes    |    X    |   X   |   X   |
|   setPrimary   |    X    |   X   |   X   |
|   isPrimary    |    X    |   X   |   X   |
|     turnOn     |  X (4)  |   X   | X (4) |
|    turnOff     |  X (4)  |   X   | X (4) |
|      isOn      |  X (2)  |   X   |   X   |
|    suspend     |  X (4)  | X (4) | X (4) |
|  isSuspended   |  X (2)  |   X   |   X   |
|     attach     |    X    |   X   |       |
|     detach     |    X    |   X   |       |
|   isAttached   |    X    |   X   |   X   |


(1) Working only in versions older than Catalina (thanks to University of Utah - Marriott Library - Apple Infrastructure)

(2) If monitor has no VCP MCCS support, these methods won't likely work.

(3) It doesn't exactly return / change contrast, but gamma values.

(4) Different behaviour according to OS:
- Windows: Working with VCP MCCS support only.
- Linux: It will suspend ALL monitors. To address just one monitor, try using turnOff() / turnOn() / detach() / attach() methods.
- macOS: It will suspend ALL monitors. Use turnOn() to wake them up again


#### WARNING: Most of these properties may return ''None'' in case the value can not be obtained

### Important OS-dependent behaviors and limitations:

  - Windows:
      - Primary monitor is mandatory, and it is always placed at (0, 0) coordinates. 
      - Monitors can not overlap.
      - To set a monitor as Primary, it is necessary to reposition primary monitor first, so the rest of monitors will sequentially be repositioned to RIGHT_TOP.
      - If you attach / detach / plug / unplug a monitor, all IDs may change. The module will try to refresh the IDs for all Monitor class instances, but take into account it may fail!
  - Linux:
      - Primary monitor can be anywhere, and even there can be no primary monitor. 
      - Monitors can overlap, so take this into account when setting a new monitor position. 
      - xrandr won't accept negative values, so the whole setup will be referenced to (0, 0) coordinates.
      - xrandr will sort primary monitors first. Because of this and for homegeneity, when positioning a monitor as primary (only with setPosition() method), it will be placed at (0 ,0) and all the rest to RIGHT_TOP.
  - macOS:
      - Primary monitor is mandatory, and it is always placed at (0, 0) coordinates. 
      - Monitors can overlap, so take this into account when setting a new monitor position. 
      - To set a monitor as Primary, it is necessary to reposition primary monitor first, so the rest of monitors will sequentially be repositioned to RIGHT_TOP.
      - setScale() method uses a workaround by applying the nearest monitor mode to magnify text to given value

It is highly recommended to use `arrangeMonitors()` function for complex setups or just in case there are two or more monitors.   

## Keep track of Monitor(s) changes

You can activate a watchdog, running in a separate Thread, which will allow you to keep monitors 
information updated, without negatively impacting your main process, and define hooks and its callbacks to be  
notified when monitors are plugged / unplugged or their properties change.

|   Watchdog methods:    |
|:----------------------:|
|   isWatchdogEnabled    |
| updateWatchdogInterval |

The watchdog will automatically start while the update information is enabled and / or there are any listeners 
registered, and will automatically stop otherwise or if the script finishes.

You can check if the watchdog is working (`isWatchdogEnabled()`) and also change its update interval 
(`updateWatchdogInterval()`) in case you need a custom period (default is 0.5 seconds). Adjust this value to your needs, 
but take into account that higher values will take longer to detect and notify changes; whilst lower values will 
consume more CPU and may produce additional notifications for intermediate (non-final) status.

### Keep Monitors info updated

|    Info update methods:    |
|:--------------------------:|
|      enableUpdateInfo      |
|     disableUpdateInfo      |
|    isUpdateInfoEnabled     |

Enable this only if you need to keep track of monitor-related events like changing its resolution, position, scale,
or if monitors can be dynamically plugged or unplugged in a multi-monitor setup. If you need monitors info updated 
at a given moment, but not continuously updated, just invoke `getAllMonitors()` at your convenience.

If enabled, it will activate a separate thread which will periodically update the list of monitors and
their properties (see `getAllMonitors()` and `getAllMonitorsDict()` function).

### Get notified on Monitors changes

It is possible to register listeners to be invoked in case the number of connected monitors or their 
properties change.

|     Listeners methods:     |
|:--------------------------:|
|    plugListenerRegister    |
|   changeListenerRegister   |
|   plugListenerUnregister   |
|  changeListenerUnregister  |
|  isPlugListenerRegistered  |
| isChangeListenerRegistered |

The information passed to the listeners is as follows:

   - Names of the monitors which have changed (as a list of strings)
   - All monitors info, as returned by `getAllMonitorsDict()`. To access monitors properties, use monitor name/s as dictionary key

Example:

    import pymonctl as pmc
    import time

    def countChanged(names, screensInfo):
        print("MONITOR PLUGGED/UNPLUGGED:", names)
        for name in names:
            print("MONITORS INFO:", screensInfo[name])

    def propsChanged(names, screensInfo):
        print("MONITOR CHANGED:", names)
        for name in names:
            print("MONITORS INFO:", screensInfo[name])

    pmc.plugListenerRegister(countChanged)
    pmc.changeListenerRegister(propsChanged)

    print("Plug/Unplug monitors, or change monitor properties while running")
    print("Press Ctl-C to Quit")
    while True:
        try:
            time.sleep(1)
        except KeyboardInterrupt:
            break

    pmc.plugListenerUnregister(countChanged)
    pmc.changeListenerUnregister(propsChanged)


## INSTALL <a name="install"></a>

To install this module on your system, you can use pip: 

    pip install pymonctl

or

    python3 -m pip install pymonctl

Alternatively, you can download the wheel file (.whl) available in the [Download page](https://pypi.org/project/PyMonCtl/#files) and the [dist folder](https://github.com/Kalmat/PyMonCtl/tree/master/dist), and run this (don't forget to replace 'x.x.xx' with proper version number):

    pip install PyMonCtl-x.x.xx-py3-none-any.whl

You may want to add `--force-reinstall` option to be sure you are installing the right dependencies version.

Then, you can use it on your own projects just importing it:

    import pymonctl

## SUPPORT <a name="support"></a>

In case you have a problem, comments or suggestions, do not hesitate to [open issues](https://github.com/Kalmat/PyMonCtl/issues) on the [project homepage](https://github.com/Kalmat/PyMonCtl)

## USING THIS CODE <a name="using"></a>

If you want to use this code or contribute, you can either:

* Create a fork of the [repository](https://github.com/Kalmat/PyMonCtl), or 
* [Download the repository](https://github.com/Kalmat/PyMonCtl/archive/refs/heads/master.zip), uncompress, and open it on your IDE of choice (e.g. PyCharm)

Be sure you install all dependencies described on `requirements.txt` by using pip
    
    python3 -m pip install -r requirements.txt

## TEST <a name="test"></a>

To test this module on your own system, cd to `tests` folder and run:

    python3 test_pymonctl.py

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/Kalmat/PyMonCtl",
    "name": "PyMonCtl",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": "screen display monitor control geometry size position frequency scale orientation screen-size mouse-position",
    "author": "Kalmat",
    "author_email": "palookjones@gmail.com",
    "download_url": null,
    "platform": null,
    "description": "# PyMonCtl\r\n[![Type Checking](https://github.com/Kalmat/PyMonCtl/actions/workflows/type-checking.yml/badge.svg)](https://github.com/Kalmat/PyMonCtl/actions/workflows/type-checking.yml)\r\n[![PyPI version](https://badge.fury.io/py/PyMonCtl.svg)](https://badge.fury.io/py/PyMonCtl)\r\n\r\nCross-Platform module which provides a set of features to get info on and control monitors.\r\n\r\nAdditional tools/extensions/APIs used:\r\n- Linux:\r\n  - Xlib's randr extension\r\n  - xrandr command-line tool\r\n  - xset command-line tool\r\n- Windows:\r\n  - VCP MCCS API interface\r\n- macOS:\r\n  - pmset command-line tool\r\n    \r\n\r\n## General Features\r\n\r\nFunctions to get monitor instances, get info and arrange monitors plugged to the system.\r\n\r\n|   General functions:    |\r\n|:-----------------------:|\r\n|     getAllMonitors      |\r\n|   getAllMonitorsDict    |\r\n|    getMonitorsCount     |\r\n|       getPrimary        |\r\n|   findMonitorsAtPoint   |\r\n| findMonitorsAtPointInfo |\r\n|   findMonitorWithName   |\r\n| findMonitorWithNameInfo |\r\n|        saveSetup        |\r\n|      restoreSetup       |\r\n|     arrangeMonitors     |\r\n|       getMousePos       |\r\n\r\n\r\n## Monitor Class\r\n\r\nClass to access all methods and functions to get info and control a given monitor plugged to the system.\r\n\r\nThis class is not meant to be directly instantiated. Instead, use convenience functions like `getAllMonitors()`,\r\n`getPrimary()` or `findMonitorsAtPoint(x, y)`. Use [PyWinCtl](https://github.com/Kalmat/PyWinCtl) module in case you need to \r\nfind the monitor a given window is in, by using `getDisplay()` method which returns the name of the monitor that\r\ncan directly be used to invoke `findMonitorWithName(name)` function.\r\n\r\nTo instantiate it, you need to pass the monitor handle (OS-dependent). It can raise ValueError exception in case \r\nthe provided handle is not valid.\r\n\r\n|    Methods     | Windows | Linux | macOS |\r\n|:--------------:|:-------:|:-----:|:-----:|\r\n|      size      |    X    |   X   |   X   |\r\n|    workarea    |    X    |   X   |   X   |\r\n|    position    |    X    |   X   |   X   |\r\n|  setPosition   |    X    |   X   |   X   |\r\n|      box       |    X    |   X   |   X   |\r\n|      rect      |    X    |   X   |   X   |\r\n|   frequency    |    X    |   X   |   X   |\r\n|   colordepth   |    X    |   X   |   X   |\r\n|      dpi       |    X    |   X   |   X   |\r\n|     scale      |    X    |   X   |   X   |\r\n|    setScale    |    X    |   X   |   X   |\r\n|  orientation   |    X    |   X   |   X   |\r\n| setOrientation |    X    |   X   | X (1) |\r\n|   brightness   |  X (2)  |   X   |   X   |\r\n| setBrightness  |  X (2)  |   X   |   X   |\r\n|    contrast    |  X (2)  | X (3) | X (3) |\r\n|  setContrast   |  X (2)  | X (3) | X (3) |\r\n|      mode      |    X    |   X   |   X   |\r\n|    setMode     |    X    |   X   |   X   |\r\n|  defaultMode   |    X    |   X   |   X   |\r\n| setDefaultMode |    X    |   X   |   X   |\r\n|    allModes    |    X    |   X   |   X   |\r\n|   setPrimary   |    X    |   X   |   X   |\r\n|   isPrimary    |    X    |   X   |   X   |\r\n|     turnOn     |  X (4)  |   X   | X (4) |\r\n|    turnOff     |  X (4)  |   X   | X (4) |\r\n|      isOn      |  X (2)  |   X   |   X   |\r\n|    suspend     |  X (4)  | X (4) | X (4) |\r\n|  isSuspended   |  X (2)  |   X   |   X   |\r\n|     attach     |    X    |   X   |       |\r\n|     detach     |    X    |   X   |       |\r\n|   isAttached   |    X    |   X   |   X   |\r\n\r\n\r\n(1) Working only in versions older than Catalina (thanks to University of Utah - Marriott Library - Apple Infrastructure)\r\n\r\n(2) If monitor has no VCP MCCS support, these methods won't likely work.\r\n\r\n(3) It doesn't exactly return / change contrast, but gamma values.\r\n\r\n(4) Different behaviour according to OS:\r\n- Windows: Working with VCP MCCS support only.\r\n- Linux: It will suspend ALL monitors. To address just one monitor, try using turnOff() / turnOn() / detach() / attach() methods.\r\n- macOS: It will suspend ALL monitors. Use turnOn() to wake them up again\r\n\r\n\r\n#### WARNING: Most of these properties may return ''None'' in case the value can not be obtained\r\n\r\n### Important OS-dependent behaviors and limitations:\r\n\r\n  - Windows:\r\n      - Primary monitor is mandatory, and it is always placed at (0, 0) coordinates. \r\n      - Monitors can not overlap.\r\n      - To set a monitor as Primary, it is necessary to reposition primary monitor first, so the rest of monitors will sequentially be repositioned to RIGHT_TOP.\r\n      - If you attach / detach / plug / unplug a monitor, all IDs may change. The module will try to refresh the IDs for all Monitor class instances, but take into account it may fail!\r\n  - Linux:\r\n      - Primary monitor can be anywhere, and even there can be no primary monitor. \r\n      - Monitors can overlap, so take this into account when setting a new monitor position. \r\n      - xrandr won't accept negative values, so the whole setup will be referenced to (0, 0) coordinates.\r\n      - xrandr will sort primary monitors first. Because of this and for homegeneity, when positioning a monitor as primary (only with setPosition() method), it will be placed at (0 ,0) and all the rest to RIGHT_TOP.\r\n  - macOS:\r\n      - Primary monitor is mandatory, and it is always placed at (0, 0) coordinates. \r\n      - Monitors can overlap, so take this into account when setting a new monitor position. \r\n      - To set a monitor as Primary, it is necessary to reposition primary monitor first, so the rest of monitors will sequentially be repositioned to RIGHT_TOP.\r\n      - setScale() method uses a workaround by applying the nearest monitor mode to magnify text to given value\r\n\r\nIt is highly recommended to use `arrangeMonitors()` function for complex setups or just in case there are two or more monitors.   \r\n\r\n## Keep track of Monitor(s) changes\r\n\r\nYou can activate a watchdog, running in a separate Thread, which will allow you to keep monitors \r\ninformation updated, without negatively impacting your main process, and define hooks and its callbacks to be  \r\nnotified when monitors are plugged / unplugged or their properties change.\r\n\r\n|   Watchdog methods:    |\r\n|:----------------------:|\r\n|   isWatchdogEnabled    |\r\n| updateWatchdogInterval |\r\n\r\nThe watchdog will automatically start while the update information is enabled and / or there are any listeners \r\nregistered, and will automatically stop otherwise or if the script finishes.\r\n\r\nYou can check if the watchdog is working (`isWatchdogEnabled()`) and also change its update interval \r\n(`updateWatchdogInterval()`) in case you need a custom period (default is 0.5 seconds). Adjust this value to your needs, \r\nbut take into account that higher values will take longer to detect and notify changes; whilst lower values will \r\nconsume more CPU and may produce additional notifications for intermediate (non-final) status.\r\n\r\n### Keep Monitors info updated\r\n\r\n|    Info update methods:    |\r\n|:--------------------------:|\r\n|      enableUpdateInfo      |\r\n|     disableUpdateInfo      |\r\n|    isUpdateInfoEnabled     |\r\n\r\nEnable this only if you need to keep track of monitor-related events like changing its resolution, position, scale,\r\nor if monitors can be dynamically plugged or unplugged in a multi-monitor setup. If you need monitors info updated \r\nat a given moment, but not continuously updated, just invoke `getAllMonitors()` at your convenience.\r\n\r\nIf enabled, it will activate a separate thread which will periodically update the list of monitors and\r\ntheir properties (see `getAllMonitors()` and `getAllMonitorsDict()` function).\r\n\r\n### Get notified on Monitors changes\r\n\r\nIt is possible to register listeners to be invoked in case the number of connected monitors or their \r\nproperties change.\r\n\r\n|     Listeners methods:     |\r\n|:--------------------------:|\r\n|    plugListenerRegister    |\r\n|   changeListenerRegister   |\r\n|   plugListenerUnregister   |\r\n|  changeListenerUnregister  |\r\n|  isPlugListenerRegistered  |\r\n| isChangeListenerRegistered |\r\n\r\nThe information passed to the listeners is as follows:\r\n\r\n   - Names of the monitors which have changed (as a list of strings)\r\n   - All monitors info, as returned by `getAllMonitorsDict()`. To access monitors properties, use monitor name/s as dictionary key\r\n\r\nExample:\r\n\r\n    import pymonctl as pmc\r\n    import time\r\n\r\n    def countChanged(names, screensInfo):\r\n        print(\"MONITOR PLUGGED/UNPLUGGED:\", names)\r\n        for name in names:\r\n            print(\"MONITORS INFO:\", screensInfo[name])\r\n\r\n    def propsChanged(names, screensInfo):\r\n        print(\"MONITOR CHANGED:\", names)\r\n        for name in names:\r\n            print(\"MONITORS INFO:\", screensInfo[name])\r\n\r\n    pmc.plugListenerRegister(countChanged)\r\n    pmc.changeListenerRegister(propsChanged)\r\n\r\n    print(\"Plug/Unplug monitors, or change monitor properties while running\")\r\n    print(\"Press Ctl-C to Quit\")\r\n    while True:\r\n        try:\r\n            time.sleep(1)\r\n        except KeyboardInterrupt:\r\n            break\r\n\r\n    pmc.plugListenerUnregister(countChanged)\r\n    pmc.changeListenerUnregister(propsChanged)\r\n\r\n\r\n## INSTALL <a name=\"install\"></a>\r\n\r\nTo install this module on your system, you can use pip: \r\n\r\n    pip install pymonctl\r\n\r\nor\r\n\r\n    python3 -m pip install pymonctl\r\n\r\nAlternatively, you can download the wheel file (.whl) available in the [Download page](https://pypi.org/project/PyMonCtl/#files) and the [dist folder](https://github.com/Kalmat/PyMonCtl/tree/master/dist), and run this (don't forget to replace 'x.x.xx' with proper version number):\r\n\r\n    pip install PyMonCtl-x.x.xx-py3-none-any.whl\r\n\r\nYou may want to add `--force-reinstall` option to be sure you are installing the right dependencies version.\r\n\r\nThen, you can use it on your own projects just importing it:\r\n\r\n    import pymonctl\r\n\r\n## SUPPORT <a name=\"support\"></a>\r\n\r\nIn case you have a problem, comments or suggestions, do not hesitate to [open issues](https://github.com/Kalmat/PyMonCtl/issues) on the [project homepage](https://github.com/Kalmat/PyMonCtl)\r\n\r\n## USING THIS CODE <a name=\"using\"></a>\r\n\r\nIf you want to use this code or contribute, you can either:\r\n\r\n* Create a fork of the [repository](https://github.com/Kalmat/PyMonCtl), or \r\n* [Download the repository](https://github.com/Kalmat/PyMonCtl/archive/refs/heads/master.zip), uncompress, and open it on your IDE of choice (e.g. PyCharm)\r\n\r\nBe sure you install all dependencies described on `requirements.txt` by using pip\r\n    \r\n    python3 -m pip install -r requirements.txt\r\n\r\n## TEST <a name=\"test\"></a>\r\n\r\nTo test this module on your own system, cd to `tests` folder and run:\r\n\r\n    python3 test_pymonctl.py\r\n",
    "bugtrack_url": null,
    "license": "BSD 3",
    "summary": "Cross-Platform toolkit to get info on and control monitors connected",
    "version": "0.91",
    "project_urls": {
        "Homepage": "https://github.com/Kalmat/PyMonCtl"
    },
    "split_keywords": [
        "screen",
        "display",
        "monitor",
        "control",
        "geometry",
        "size",
        "position",
        "frequency",
        "scale",
        "orientation",
        "screen-size",
        "mouse-position"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "538ebf1d577345cdf40fc7ff50318e50f3fbd6cb4db31e908b700de9ff59b7e4",
                "md5": "86058569f21366c35e25fb20e59be273",
                "sha256": "48b40fb543a05f457e5d2625bf18b2a9878fff3e267d19876c23d983a7eb82ed"
            },
            "downloads": -1,
            "filename": "PyMonCtl-0.91-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "86058569f21366c35e25fb20e59be273",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 45551,
            "upload_time": "2024-04-18T19:52:02",
            "upload_time_iso_8601": "2024-04-18T19:52:02.604106Z",
            "url": "https://files.pythonhosted.org/packages/53/8e/bf1d577345cdf40fc7ff50318e50f3fbd6cb4db31e908b700de9ff59b7e4/PyMonCtl-0.91-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-04-18 19:52:02",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "Kalmat",
    "github_project": "PyMonCtl",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [],
    "lcname": "pymonctl"
}
        
Elapsed time: 0.23737s