Skip to content

Commit 0ef050d

Browse files
committed
Initial source
1 parent 9b7c0f1 commit 0ef050d

File tree

6 files changed

+201
-1
lines changed

6 files changed

+201
-1
lines changed

README.md

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,48 @@
1-
# rotate-screen
1+
# Rotate Screen
22
A small Python package for rotating the screen.
3+
4+
## Platforms Supported
5+
Windows is currently the only platform supported.
6+
7+
## Installation
8+
Clone the repo or download as zip then navigate to the project root directory and use the following command...
9+
```sh
10+
pip install .
11+
```
12+
13+
## Example: ![Ctrl+Alt+Arrow Shortcut](https://github.com/TheBrokenEstate/rotate-screen/examples/shortcut.py)
14+
This is a simple example that implements the 'Ctrl+Alt+Arrow' shortcut for rotating the display. Some graphics cards don't come with this capability by default.
15+
16+
This example requires the keyboard module...
17+
```sh
18+
pip install keyboard
19+
```
20+
Here is the code! This module adds hotkeys to rotate the main display to the corresponding arrow keys.
21+
```python
22+
import rotatescreen
23+
import keyboard
24+
25+
screen = rotatescreen.get_primary_display()
26+
27+
keyboard.add_hotkey('ctrl+alt+up', screen.set_landscape, suppress=True)
28+
keyboard.add_hotkey('ctrl+alt+right', screen.set_portrait_flipped, suppress=True)
29+
keyboard.add_hotkey('ctrl+alt+down', screen.set_landscape_flipped, suppress=True)
30+
keyboard.add_hotkey('ctrl+alt+left', screen.set_portrait, suppress=True)
31+
32+
keyboard.wait()
33+
```
34+
35+
## Example: ![Do A Barrel Roll](https://github.com/TheBrokenEstate/rotate-screen/examples/do-a-barrel-roll.py)
36+
This was a little joke script to show off some more of the modules functionality, due to the way windows rotates the display this is a pretty horrific looking, but entertaining. :)
37+
```python
38+
import rotatescreen
39+
import time
40+
41+
screen = rotatescreen.get_primary_display()
42+
start_pos = screen.current_orientation
43+
44+
for i in range(1, 5):
45+
pos = abs((start_pos - i*90) % 360)
46+
screen.rotate_to(pos)
47+
time.sleep(1.5)
48+
```

examples/do-a-barrel-roll.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import rotatescreen
2+
import time
3+
4+
screen = rotatescreen.get_primary_display()
5+
start_pos = screen.current_orientation
6+
7+
for i in range(1, 5):
8+
pos = abs((start_pos - i*90) % 360)
9+
screen.rotate_to(pos)
10+
time.sleep(1.5)

examples/shortcuts.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import rotatescreen
2+
import keyboard
3+
4+
screen = rotatescreen.get_primary_display()
5+
6+
keyboard.add_hotkey('ctrl+alt+up', screen.set_landscape, suppress=True)
7+
keyboard.add_hotkey('ctrl+alt+right', screen.set_portrait_flipped, suppress=True)
8+
keyboard.add_hotkey('ctrl+alt+down', screen.set_landscape_flipped, suppress=True)
9+
keyboard.add_hotkey('ctrl+alt+left', screen.set_portrait, suppress=True)
10+
11+
keyboard.wait()

rotatescreen/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .display import *

rotatescreen/display.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import win32api
2+
import win32con
3+
4+
5+
def get_displays():
6+
displays = [Display(hMonitor) for hMonitor,_,_ in win32api.EnumDisplayMonitors()]
7+
return displays
8+
9+
10+
def get_primary_display():
11+
for display in get_displays():
12+
if display.is_primary:
13+
return display
14+
15+
16+
def get_secondary_displays():
17+
displays = [display for display in get_displays() if not display.is_primary]
18+
return displays
19+
20+
21+
class Display:
22+
23+
def __init__(self, hMonitor):
24+
self.hMonitor = hMonitor
25+
26+
def __repr__(self):
27+
return f"<'{self.device_description[0]}' object>"
28+
29+
def rotate_to(self, degrees):
30+
if degrees == 90:
31+
rotation_val = win32con.DMDO_90
32+
elif degrees == 180:
33+
rotation_val = win32con.DMDO_180
34+
elif degrees == 270:
35+
rotation_val = win32con.DMDO_270
36+
elif degrees == 0:
37+
rotation_val = win32con.DMDO_DEFAULT
38+
else:
39+
raise ValueError("Display can only be rotated to 0, 90, 180, or 270 degrees.")
40+
41+
dm = self.devicemodeW
42+
if((dm.DisplayOrientation + rotation_val) % 2 == 1):
43+
dm.PelsWidth, dm.PelsHeight = dm.PelsHeight, dm.PelsWidth
44+
dm.DisplayOrientation = rotation_val
45+
win32api.ChangeDisplaySettingsEx(self.device, dm)
46+
47+
def set_landscape(self):
48+
self.rotate_to(0)
49+
50+
def set_landscape_flipped(self):
51+
self.rotate_to(180)
52+
53+
def set_portrait(self):
54+
self.rotate_to(90)
55+
56+
def set_portrait_flipped(self):
57+
self.rotate_to(270)
58+
59+
@property
60+
def current_orientation(self):
61+
state = self.devicemodeW.DisplayOrientation
62+
return state * 90
63+
64+
@property
65+
def info(self):
66+
return win32api.GetMonitorInfo(self.hMonitor)
67+
68+
@property
69+
def device(self):
70+
return self.info["Device"]
71+
72+
@property
73+
def is_primary(self):
74+
# The only flag is MONITORINFOF_PRIMARY which is 1 only for the primary monitor.
75+
return self.info["Flags"]
76+
77+
@property
78+
def device_description(self):
79+
display_device = win32api.EnumDisplayDevices(self.device)
80+
return display_device.DeviceString, display_device.DeviceID
81+
82+
@property
83+
def devicemodeW(self):
84+
return win32api.EnumDisplaySettings(self.device, win32con.ENUM_CURRENT_SETTINGS)

setup.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
import os
5+
from setuptools import find_packages, setup, Command
6+
7+
NAME = 'rotate-screen'
8+
DESCRIPTION = 'A small Python package for rotating the screen.'
9+
URL = 'https://github.com/TheBrokenEstate/rotate-screen'
10+
11+
AUTHOR = 'Dan Burrows'
12+
REQUIRES_PYTHON = '>=3.6.0'
13+
VERSION = '0.1.0'
14+
15+
# Optional package for the examples...
16+
EXTRAS = {
17+
'shortcuts_example': ['keyboard']
18+
}
19+
20+
here = os.path.abspath(os.path.dirname(__file__))
21+
with open(os.path.join(here, 'README.md'), encoding='utf-8') as f:
22+
long_description = f.read()
23+
24+
setup(
25+
name=NAME,
26+
version=VERSION,
27+
description=DESCRIPTION,
28+
long_description=long_description,
29+
long_description_content_type='text/markdown',
30+
author=AUTHOR,
31+
author_email=EMAIL,
32+
python_requires=REQUIRES_PYTHON,
33+
url=URL,
34+
packages=find_packages(exclude=["tests", "*.tests", "*.tests.*", "tests.*"]),
35+
extras_require=EXTRAS,
36+
license='MIT',
37+
classifiers=[
38+
'License :: OSI Approved :: MIT License',
39+
'Operating System :: Microsoft :: Windows',
40+
'Topic :: Desktop Environment',
41+
'Programming Language :: Python',
42+
'Programming Language :: Python :: 3',
43+
'Programming Language :: Python :: 3 :: Only',
44+
'Programming Language :: Python :: 3.6',
45+
'Programming Language :: Python :: 3.7',
46+
'Programming Language :: Python :: 3.8'
47+
]
48+
)

0 commit comments

Comments
 (0)