streamdeck-gui-ng 4.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. streamdeck_gui_ng-4.1.3.dist-info/METADATA +141 -0
  2. streamdeck_gui_ng-4.1.3.dist-info/RECORD +62 -0
  3. streamdeck_gui_ng-4.1.3.dist-info/WHEEL +4 -0
  4. streamdeck_gui_ng-4.1.3.dist-info/entry_points.txt +4 -0
  5. streamdeck_gui_ng-4.1.3.dist-info/licenses/LICENSE +21 -0
  6. streamdeck_ui/__init__.py +6 -0
  7. streamdeck_ui/api.py +712 -0
  8. streamdeck_ui/button.ui +1214 -0
  9. streamdeck_ui/cli/__init__.py +0 -0
  10. streamdeck_ui/cli/commands.py +191 -0
  11. streamdeck_ui/cli/server.py +292 -0
  12. streamdeck_ui/config.py +244 -0
  13. streamdeck_ui/dimmer.py +93 -0
  14. streamdeck_ui/display/__init__.py +0 -0
  15. streamdeck_ui/display/background_color_filter.py +41 -0
  16. streamdeck_ui/display/display_grid.py +265 -0
  17. streamdeck_ui/display/empty_filter.py +43 -0
  18. streamdeck_ui/display/filter.py +65 -0
  19. streamdeck_ui/display/image_filter.py +144 -0
  20. streamdeck_ui/display/keypress_filter.py +63 -0
  21. streamdeck_ui/display/pipeline.py +74 -0
  22. streamdeck_ui/display/pulse_filter.py +54 -0
  23. streamdeck_ui/display/text_filter.py +142 -0
  24. streamdeck_ui/fonts/roboto/LICENSE.txt +202 -0
  25. streamdeck_ui/fonts/roboto/Roboto-Black.ttf +0 -0
  26. streamdeck_ui/fonts/roboto/Roboto-BlackItalic.ttf +0 -0
  27. streamdeck_ui/fonts/roboto/Roboto-Bold.ttf +0 -0
  28. streamdeck_ui/fonts/roboto/Roboto-BoldItalic.ttf +0 -0
  29. streamdeck_ui/fonts/roboto/Roboto-Italic.ttf +0 -0
  30. streamdeck_ui/fonts/roboto/Roboto-Light.ttf +0 -0
  31. streamdeck_ui/fonts/roboto/Roboto-LightItalic.ttf +0 -0
  32. streamdeck_ui/fonts/roboto/Roboto-Medium.ttf +0 -0
  33. streamdeck_ui/fonts/roboto/Roboto-MediumItalic.ttf +0 -0
  34. streamdeck_ui/fonts/roboto/Roboto-Regular.ttf +0 -0
  35. streamdeck_ui/fonts/roboto/Roboto-Thin.ttf +0 -0
  36. streamdeck_ui/fonts/roboto/Roboto-ThinItalic.ttf +0 -0
  37. streamdeck_ui/gui.py +1423 -0
  38. streamdeck_ui/icons/add_page.png +0 -0
  39. streamdeck_ui/icons/cross.png +0 -0
  40. streamdeck_ui/icons/gear.png +0 -0
  41. streamdeck_ui/icons/horizontal-align.png +0 -0
  42. streamdeck_ui/icons/remove_page.png +0 -0
  43. streamdeck_ui/icons/vertical-align.png +0 -0
  44. streamdeck_ui/icons/warning_icon_button.png +0 -0
  45. streamdeck_ui/logger.py +11 -0
  46. streamdeck_ui/logo.png +0 -0
  47. streamdeck_ui/main.ui +407 -0
  48. streamdeck_ui/mock_streamdeck.py +204 -0
  49. streamdeck_ui/model.py +78 -0
  50. streamdeck_ui/modules/__init__.py +0 -0
  51. streamdeck_ui/modules/fonts.py +150 -0
  52. streamdeck_ui/modules/keyboard.py +447 -0
  53. streamdeck_ui/modules/utils/__init__.py +0 -0
  54. streamdeck_ui/modules/utils/timers.py +35 -0
  55. streamdeck_ui/resources.qrc +10 -0
  56. streamdeck_ui/resources_rc.py +324 -0
  57. streamdeck_ui/semaphore.py +38 -0
  58. streamdeck_ui/settings.ui +155 -0
  59. streamdeck_ui/stream_deck_monitor.py +157 -0
  60. streamdeck_ui/ui_button.py +421 -0
  61. streamdeck_ui/ui_main.py +267 -0
  62. streamdeck_ui/ui_settings.py +119 -0
File without changes
@@ -0,0 +1,191 @@
1
+ import typing as tp
2
+
3
+ from streamdeck_ui.api import StreamDeckServer
4
+ from streamdeck_ui.ui_main import Ui_MainWindow
5
+
6
+
7
+ class Command(tp.Protocol):
8
+ def execute(self, api: StreamDeckServer, ui: tp.Any) -> None: ... # noqa: E704
9
+
10
+
11
+ class SetPageCommand:
12
+ def __init__(self, cfg):
13
+ self.deck_index = cfg["deck"]
14
+ self.page_index = cfg["page"]
15
+
16
+ def execute(self, api: StreamDeckServer, ui):
17
+ deck_id = ui.device_list.itemData(ui.device_list.currentIndex())
18
+ if self.deck_index is not None and ui.device_list.itemData(self.deck_index) is not None:
19
+ deck_id = ui.device_list.itemData(self.deck_index)
20
+ if api.get_page(deck_id) == self.page_index:
21
+ return
22
+ api.set_page(deck_id, self.page_index)
23
+ ui.pages.setCurrentIndex(self.page_index)
24
+
25
+
26
+ class SetButtonStateCommand:
27
+ def __init__(self, cfg):
28
+ self.deck_index = cfg["deck"]
29
+ self.page_index = cfg["page"]
30
+ self.button_index = cfg["button"]
31
+ self.button_state_index = cfg["state"]
32
+
33
+ def execute(self, api: StreamDeckServer, ui: Ui_MainWindow):
34
+ deck_id = ui.device_list.itemData(ui.device_list.currentIndex())
35
+ if self.deck_index is not None and ui.device_list.itemData(self.deck_index) is not None:
36
+ deck_id = ui.device_list.itemData(self.deck_index)
37
+ page_id = api.get_page(deck_id) if self.page_index is None else self.page_index
38
+ if api.get_button_state(deck_id, page_id, self.button_index) == self.button_state_index:
39
+ return
40
+ api.set_button_state(deck_id, page_id, self.button_index, self.button_state_index)
41
+ ui.button_states.setCurrentIndex(self.button_state_index)
42
+ ui.redraw_button(self.button_index) # type: ignore [attr-defined]
43
+
44
+
45
+ class SetBrightnessCommand:
46
+ def __init__(self, cfg):
47
+ self.deck_index = cfg["deck"]
48
+ self.brightness = cfg["value"]
49
+
50
+ def execute(self, api: StreamDeckServer, ui):
51
+ deck_id = ui.device_list.itemData(ui.device_list.currentIndex())
52
+ if self.deck_index is not None and ui.device_list.itemData(self.deck_index) is not None:
53
+ deck_id = ui.device_list.itemData(self.deck_index)
54
+ api.set_brightness(deck_id, self.brightness)
55
+
56
+
57
+ class SetButtonTextCommand:
58
+ def __init__(self, cfg):
59
+ self.deck_index = cfg["deck"]
60
+ self.page_index = cfg["page"]
61
+ self.button_index = cfg["button"]
62
+ self.button_text = cfg["text"]
63
+
64
+ def execute(self, api: StreamDeckServer, ui):
65
+ deck_id = ui.device_list.itemData(ui.device_list.currentIndex())
66
+ if self.deck_index is not None and ui.device_list.itemData(self.deck_index) is not None:
67
+ deck_id = ui.device_list.itemData(self.deck_index)
68
+ if self.page_index is None:
69
+ self.page_index = api.get_page(deck_id)
70
+ api.set_button_text(deck_id, self.page_index, self.button_index, self.button_text)
71
+
72
+
73
+ class SetButtonTextAlignmentCommand:
74
+ def __init__(self, cfg):
75
+ self.deck_index = cfg["deck"]
76
+ self.page_index = cfg["page"]
77
+ self.button_index = cfg["button"]
78
+ self.button_text_alignment = cfg["alignment"]
79
+
80
+ def execute(self, api: StreamDeckServer, ui):
81
+ deck_id = ui.device_list.itemData(ui.device_list.currentIndex())
82
+ if self.deck_index is not None and ui.device_list.itemData(self.deck_index) is not None:
83
+ deck_id = ui.device_list.itemData(self.deck_index)
84
+ if self.page_index is None:
85
+ self.page_index = api.get_page(deck_id)
86
+ api.set_button_text_vertical_align(deck_id, self.page_index, self.button_index, self.button_text_alignment)
87
+
88
+
89
+ class SetButtonWriteCommand:
90
+ def __init__(self, cfg):
91
+ self.deck_index = cfg["deck"]
92
+ self.page_index = cfg["page"]
93
+ self.button_index = cfg["button"]
94
+ self.button_write = cfg["write"]
95
+
96
+ def execute(self, api: StreamDeckServer, ui):
97
+ deck_id = ui.device_list.itemData(ui.device_list.currentIndex())
98
+ if self.deck_index is not None and ui.device_list.itemData(self.deck_index) is not None:
99
+ deck_id = ui.device_list.itemData(self.deck_index)
100
+ if self.page_index is None:
101
+ self.page_index = api.get_page(deck_id)
102
+ api.set_button_write(deck_id, self.page_index, self.button_index, self.button_write)
103
+
104
+
105
+ class SetButtonCmdCommand:
106
+ def __init__(self, cfg):
107
+ self.deck_index = cfg["deck"]
108
+ self.page_index = cfg["page"]
109
+ self.button_index = cfg["button"]
110
+ self.button_cmd = cfg["button_cmd"]
111
+
112
+ def execute(self, api: StreamDeckServer, ui):
113
+ print(self.button_cmd)
114
+ deck_id = ui.device_list.itemData(ui.device_list.currentIndex())
115
+ if self.deck_index is not None and ui.device_list.itemData(self.deck_index) is not None:
116
+ deck_id = ui.device_list.itemData(self.deck_index)
117
+ if self.page_index is None:
118
+ self.page_index = api.get_page(deck_id)
119
+ api.set_button_command(deck_id, self.page_index, self.button_index, self.button_cmd)
120
+
121
+
122
+ class SetButtonKeysCommand:
123
+ def __init__(self, cfg):
124
+ self.deck_index = cfg["deck"]
125
+ self.page_index = cfg["page"]
126
+ self.button_index = cfg["button"]
127
+ self.button_keys = cfg["button_keys"]
128
+
129
+ def execute(self, api: StreamDeckServer, ui):
130
+ print(self.button_keys)
131
+ deck_id = ui.device_list.itemData(ui.device_list.currentIndex())
132
+ if self.deck_index is not None and ui.device_list.itemData(self.deck_index) is not None:
133
+ deck_id = ui.device_list.itemData(self.deck_index)
134
+ if self.page_index is None:
135
+ self.page_index = api.get_page(deck_id)
136
+ api.set_button_keys(deck_id, self.page_index, self.button_index, self.button_keys)
137
+
138
+
139
+ class SetButtonIconCommand:
140
+ def __init__(self, cfg):
141
+ self.deck_index = cfg["deck"]
142
+ self.page_index = cfg["page"]
143
+ self.button_index = cfg["button"]
144
+ self.icon_path = cfg["icon"]
145
+
146
+ def execute(self, api: StreamDeckServer, ui):
147
+ deck_id = ui.device_list.itemData(ui.device_list.currentIndex())
148
+ if self.deck_index is not None and ui.device_list.itemData(self.deck_index) is not None:
149
+ deck_id = ui.device_list.itemData(self.deck_index)
150
+ if self.page_index is None:
151
+ self.page_index = api.get_page(deck_id)
152
+ api.set_button_icon(deck_id, self.page_index, self.button_index, self.icon_path)
153
+
154
+
155
+ class ClearButtonIconCommand:
156
+ def __init__(self, cfg):
157
+ self.deck_index = cfg["deck"]
158
+ self.page_index = cfg["page"]
159
+ self.button_index = cfg["button"]
160
+
161
+ def execute(self, api: StreamDeckServer, ui):
162
+ deck_id = ui.device_list.itemData(ui.device_list.currentIndex())
163
+ if self.deck_index is not None and ui.device_list.itemData(self.deck_index) is not None:
164
+ deck_id = ui.device_list.itemData(self.deck_index)
165
+ if self.page_index is None:
166
+ self.page_index = api.get_page(deck_id)
167
+ api.set_button_icon(deck_id, self.page_index, self.button_index, "")
168
+
169
+
170
+ def create_command(cfg: dict) -> tp.Optional[Command]:
171
+ if cfg["command"] == "set_page":
172
+ return SetPageCommand(cfg)
173
+ elif cfg["command"] == "set_brightness":
174
+ return SetBrightnessCommand(cfg)
175
+ elif cfg["command"] == "set_text":
176
+ return SetButtonTextCommand(cfg)
177
+ elif cfg["command"] == "set_alignment":
178
+ return SetButtonTextAlignmentCommand(cfg)
179
+ elif cfg["command"] == "set_write":
180
+ return SetButtonWriteCommand(cfg)
181
+ elif cfg["command"] == "set_cmd":
182
+ return SetButtonCmdCommand(cfg)
183
+ elif cfg["command"] == "set_keys":
184
+ return SetButtonKeysCommand(cfg)
185
+ elif cfg["command"] == "set_icon":
186
+ return SetButtonIconCommand(cfg)
187
+ elif cfg["command"] == "clear_icon":
188
+ return ClearButtonIconCommand(cfg)
189
+ elif cfg["command"] == "set_state":
190
+ return SetButtonStateCommand(cfg)
191
+ return None
@@ -0,0 +1,292 @@
1
+ import json
2
+ import optparse
3
+ import os
4
+ import socket
5
+ import sys
6
+ import tempfile
7
+ from threading import Event, Thread
8
+
9
+ from streamdeck_ui.api import StreamDeckServer
10
+ from streamdeck_ui.cli.commands import create_command
11
+
12
+
13
+ def read_json(sock: socket.socket) -> dict:
14
+ header = sock.recv(4)
15
+ num_bytes = int.from_bytes(header, "little")
16
+
17
+ return json.loads(sock.recv(num_bytes))
18
+
19
+
20
+ def write_json(sock: socket.socket, data: dict) -> None:
21
+ binary_data = json.dumps(data).encode("utf-8")
22
+ num_bytes = len(binary_data)
23
+
24
+ sock.send(num_bytes.to_bytes(4, "little"))
25
+ sock.send(binary_data)
26
+
27
+
28
+ class CLIStreamDeckServer:
29
+ SOCKET_CONNECTION_TIMEOUT_SECOND = 0.5
30
+
31
+ def __init__(self, api: StreamDeckServer, ui):
32
+ self.quit = Event()
33
+ self.cli_thread = None
34
+
35
+ self.api = api
36
+ self.ui = ui
37
+
38
+ self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
39
+
40
+ def start(self):
41
+ if not self.quit.is_set:
42
+ return
43
+
44
+ self.cli_thread = Thread(target=self._run)
45
+ self.quit.clear()
46
+ self.cli_thread.start()
47
+
48
+ def stop(self):
49
+ if self.quit.is_set():
50
+ return
51
+
52
+ self.quit.set()
53
+ try:
54
+ self.cli_thread.join()
55
+ except RuntimeError:
56
+ pass
57
+
58
+ def _run(self):
59
+ try:
60
+ tmpdir = tempfile.gettempdir()
61
+ filename = "streamdeck_ui.sock"
62
+
63
+ saved_umask = os.umask(0o077)
64
+ path = os.path.join(tmpdir, filename)
65
+
66
+ if os.path.exists(path):
67
+ os.remove(path)
68
+
69
+ self.sock.bind(path)
70
+ self.sock.listen(1)
71
+ self.sock.settimeout(CLIStreamDeckServer.SOCKET_CONNECTION_TIMEOUT_SECOND)
72
+ except OSError:
73
+ print("warning: for some reason, unable to utilize CLI commands.")
74
+ pass
75
+
76
+ while not self.quit.is_set():
77
+ try:
78
+ conn, _ = self.sock.accept()
79
+ cfg = read_json(conn)
80
+ cmd = create_command(cfg)
81
+ cmd.execute(self.api, self.ui)
82
+ conn.close()
83
+ except BaseException: # noqa: B036
84
+ pass
85
+ try:
86
+ os.remove(path)
87
+ except OSError:
88
+ pass
89
+ finally:
90
+ os.umask(saved_umask)
91
+
92
+
93
+ def execute():
94
+ parser = optparse.OptionParser()
95
+
96
+ parser.add_option(
97
+ "-a",
98
+ "--action",
99
+ type="string",
100
+ dest="action",
101
+ help="the action to be performed. valid options (case-insensitive): "
102
+ + "SET_PAGE, SET_BRIGHTNESS, SET_TEXT, SET_ALIGNMENT, SET_CMD, SET_KEYS, SET_WRITE, SET_ICON, CLEAR_ICON, SET_STATE",
103
+ metavar="NAME",
104
+ )
105
+
106
+ parser.add_option(
107
+ "-d",
108
+ "--deck",
109
+ type="int",
110
+ dest="deck_index",
111
+ help="the deck to be manipulated. defaults to the currently selected deck in the ui",
112
+ metavar="INDEX",
113
+ )
114
+ parser.add_option(
115
+ "-p",
116
+ "--page",
117
+ type="int",
118
+ dest="page_index",
119
+ help="the page to be manipulated. defaults to the currently active page",
120
+ metavar="INDEX",
121
+ )
122
+ parser.add_option(
123
+ "-b", "--button", type="int", dest="button_index", help="the button to be manipulated", metavar="INDEX"
124
+ )
125
+ parser.add_option(
126
+ "-s", "--state", type="int", dest="state_index", help="the button state to be manipulated", metavar="INDEX"
127
+ )
128
+ parser.add_option(
129
+ "--icon", type="string", dest="icon_path", help="path to an icon. used with SET_ICON", metavar="PATH"
130
+ )
131
+ parser.add_option(
132
+ "--brightness",
133
+ type="int",
134
+ dest="brightness",
135
+ help="brightness to set, 0-100. used with SET_BRIGHTNESS",
136
+ metavar="VALUE",
137
+ )
138
+ parser.add_option(
139
+ "--text", type="string", dest="button_text", help="button text to set. used with SET_TEXT", metavar="VALUE"
140
+ )
141
+ parser.add_option(
142
+ "--write",
143
+ type="string",
144
+ dest="button_write",
145
+ help="text to be written when the button is pressed. used with SET_WRITE",
146
+ metavar="VALUE",
147
+ )
148
+ parser.add_option(
149
+ "--command", type="string", dest="button_cmd", help="button command to set. used with SET_CMD", metavar="VALUE"
150
+ )
151
+ parser.add_option(
152
+ "--keys", type="string", dest="button_keys", help="button keys to set. used with SET_KEYS", metavar="VALUE"
153
+ )
154
+ parser.add_option(
155
+ "--alignment",
156
+ type="string",
157
+ dest="button_text_alignment",
158
+ help="button text alignment. used with SET_ALIGNMENT. valid values: top, middle-top, middle, middle-bottom, bottom",
159
+ metavar="VALUE",
160
+ )
161
+
162
+ (options, args) = parser.parse_args(sys.argv)
163
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
164
+ tmpdir = tempfile.gettempdir()
165
+ file = "streamdeck_ui.sock"
166
+ path = os.path.join(tmpdir, file)
167
+ sock.connect(path)
168
+ data = None
169
+
170
+ if options.action is not None:
171
+ action_name = options.action.lower()
172
+ if action_name == "set_page":
173
+ if options.page_index is None:
174
+ print("error: --page not set...")
175
+ return
176
+ data = {"command": "set_page", "deck": options.deck_index, "page": options.page_index}
177
+ elif action_name == "set_brightness":
178
+ if options.brightness is None:
179
+ print("error: --brightness not set...")
180
+ return
181
+ data = {"command": "set_brightness", "deck": options.deck_index, "value": options.brightness}
182
+ elif action_name == "set_text":
183
+ if options.button_text is None:
184
+ print("error: --text not set...")
185
+ return
186
+ if options.button_index is None:
187
+ print("error: --button not set...")
188
+ return
189
+ data = {
190
+ "command": "set_text",
191
+ "deck": options.deck_index,
192
+ "page": options.page_index,
193
+ "button": options.button_index,
194
+ "text": options.button_text,
195
+ }
196
+ elif action_name == "set_write":
197
+ if options.button_write is None:
198
+ print("error: --write not set...")
199
+ return
200
+ if options.button_index is None:
201
+ print("error: --button not set...")
202
+ return
203
+ data = {
204
+ "command": "set_write",
205
+ "deck": options.deck_index,
206
+ "page": options.page_index,
207
+ "button": options.button_index,
208
+ "write": options.button_write,
209
+ }
210
+ elif action_name == "set_alignment":
211
+ if options.button_text_alignment is None:
212
+ print("error: --alignment not set...")
213
+ return
214
+ if options.button_index is None:
215
+ print("error: --button not set...")
216
+ return
217
+ data = {
218
+ "command": "set_alignment",
219
+ "deck": options.deck_index,
220
+ "page": options.page_index,
221
+ "button": options.button_index,
222
+ "alignment": options.button_text_alignment,
223
+ }
224
+ elif action_name == "set_cmd":
225
+ if options.button_cmd is None:
226
+ print("error: --command not set...")
227
+ return
228
+ if options.button_index is None:
229
+ print("error: --button not set...")
230
+ return
231
+ data = {
232
+ "command": "set_cmd",
233
+ "deck": options.deck_index,
234
+ "page": options.page_index,
235
+ "button": options.button_index,
236
+ "button_cmd": options.button_cmd,
237
+ }
238
+ elif action_name == "set_keys":
239
+ if options.button_keys is None:
240
+ print("error: --keys not set...")
241
+ return
242
+ if options.button_index is None:
243
+ print("error: --button not set...")
244
+ return
245
+ data = {
246
+ "command": "set_keys",
247
+ "deck": options.deck_index,
248
+ "page": options.page_index,
249
+ "button": options.button_index,
250
+ "button_keys": options.button_keys,
251
+ }
252
+ elif action_name == "set_icon":
253
+ if options.icon_path is None:
254
+ print("error: --icon not set...")
255
+ return
256
+ if options.button_index is None:
257
+ print("error: --button not set...")
258
+ return
259
+ data = {
260
+ "command": "set_icon",
261
+ "deck": options.deck_index,
262
+ "page": options.page_index,
263
+ "button": options.button_index,
264
+ "icon": options.icon_path,
265
+ }
266
+ elif action_name == "clear_icon":
267
+ if options.button_index is None:
268
+ print("error: --button not set...")
269
+ return
270
+ data = {
271
+ "command": "clear_icon",
272
+ "deck": options.deck_index,
273
+ "page": options.page_index,
274
+ "button": options.button_index,
275
+ }
276
+ elif action_name == "set_state":
277
+ if options.button_index is None:
278
+ print("error: --button not set...")
279
+ return
280
+ if options.state_index is None:
281
+ print("error: --state not set...")
282
+ return
283
+ data = {
284
+ "command": "set_state",
285
+ "deck": options.deck_index,
286
+ "page": options.page_index,
287
+ "button": options.button_index,
288
+ "state": options.state_index,
289
+ }
290
+
291
+ if data is not None:
292
+ write_json(sock, data)