-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathvirtual_display.py
More file actions
133 lines (105 loc) · 4.03 KB
/
virtual_display.py
File metadata and controls
133 lines (105 loc) · 4.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import displayprovider
class VirtualDisplay(displayprovider.DisplayBase):
"""
Virtual display that can hold multiple smaller displays.
Smaller displays can be combined into a larger virtual display by
specifying the offset of each smaller display within the virtual display.
E.g. three displays of size 4x3, 5x3, and 10x3 can be combined into a
virtual display of size 10x6 by placing the first display at (0,0) in the
top left corner, the second display at (5,0), and the third display
at (0,3).
::
1111.22222
1111.22222
1111.22222
3333333333
3333333333
3333333333
"""
def __init__(self, width, height):
"""
Create virtual display with given dimension.
"""
super().__init__(width, height)
print("Creating virtual display: width", width, "height", height)
self.xy2display = {}
def add_display(self, display, x_offset, y_offset):
"Add a display at given offset within the virtual display."
assert 0 <= x_offset + display.width <= self.width, "x_offset out of bounds"
assert 0 <= y_offset + display.height <= self.height, "y_offset out of bounds"
print("Adding display", display.__class__.__name__, "at", x_offset, y_offset)
self.xy2display[(x_offset, y_offset)] = display
def _display_at(self, x, y):
"""
Return display placed at given coordinates in virtual display and the
offset of the display within the virtual display.
Return None if no display is present.
"""
for (x_offset, y_offset), display in self.xy2display.items():
if x_offset <= x < x_offset + display.width and \
y_offset <= y < y_offset + display.height:
return display, (x_offset, y_offset)
def px(self, x, y, val):
"Set pixel at (x,y) to val (True/False) in virtual display."
assert 0 <= x < self.width, "Pixel x out of bounds"
assert 0 <= y < self.height, "Pixel y out of bounds"
assert val in (True, False), "Pixel value must be True or False"
display_pos = self._display_at(x, y)
if display_pos is None:
return # no display at this position
display, (x_offset, y_offset) = display_pos
if display is not None:
# calculate local coordinates within the display
dx = x - x_offset
dy = y - y_offset
display.px(dx, dy, val)
def show(self):
"Update all displays within the virtual display."
for display in self.xy2display.values():
display.show()
def clear(self):
for display in self.xy2display.values():
display.clear()
def print_layout(self):
"Print the virtual display to console for debugging purposes."
displays = list(self.xy2display.values())
for y in range(self.height):
for x in range(self.width):
display_pos = self._display_at(x, y)
if display_pos is None:
print(".", end="")
else:
display, _ = display_pos
idx = displays.index(display)
print(idx, end="")
print()
for i, d in enumerate(displays):
print(f"{i}: {d.__class__.__name__} ({d.width}x{d.height})")
def test_virtual_display():
from displayprovider import DisplayBase
"""
1111.22222
1111.22222
1111.22222
3333333333
3333333333
3333333333
"""
vd = VirtualDisplay(10, 6)
d1 = DisplayBase(4, 3)
d2 = DisplayBase(5, 3)
d3 = DisplayBase(10, 3)
vd.add_display(d1, 0, 0)
vd.add_display(d2, 5, 0)
vd.add_display(d3, 0, 3)
vd.print_layout()
# set some pixels
vd.px(0, 0, True)
vd.px(3, 2, True)
vd.px(4, 0, True)
vd.px(9, 2, True)
vd.px(0, 3, True)
vd.px(9, 5, True)
vd.show()
if __name__ == "__main__":
test_virtual_display()