-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathglobal_cues.py
284 lines (218 loc) · 9.83 KB
/
global_cues.py
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
from configs.settings import *
import json
import os
from tkinter import *
from pprint import pprint
from os import path
from logzero import logger
from typing import List
from cue_creator import CueCreator
from cue_handler import CueHandler
from pco_plan import PcoPlan
from flask import Flask, request
from general_networking import is_local_port_in_use
from threading import Thread
class GlobalCues:
"""
All functionality for Global cues, including ui. This can be run independently of the main app.
Cues are stored in banks of 8, with 32 cues per bank in [[{'name': 'cue 1', 'cues': []}, {'name': 'cue 2', 'cues': []}, {'name': None, 'cues': []} ]]
format.
"""
def __init__(self):
self.folder_path = os.path.dirname(__file__)
# if global cues file does not exist, create an empty one
if not path.exists(path.join(self.folder_path, 'configs', 'global_cues.json')):
self._create_empty_global_cues_file()
with open(os.path.join(self.folder_path, 'configs', 'devices.json'), 'r') as f:
self.devices: List[dict] = json.loads(f.read())
self.cue_handler = CueHandler(devices=self.devices)
self.global_cues: List[List[dict]] = self.read_global_cues()
self._correct_old_pvp_cues()
self.root: Tk = Tk()
self.bank_frame: Frame = Frame(self.root)
self.cues_frame: Frame = Frame(self.root)
self.bank_buttons: List[Button] = []
self.cue_buttons: List[Button] = []
self.currently_selected_bank: int = 0
self.edit_mode: bool = False
self._start_self_contained_webserver()
def read_global_cues(self) -> list:
"""
Read global cues from file & return it as a python object
:return: global cues read from file
"""
with open(path.join(self.folder_path, 'configs', 'global_cues.json')) as f:
return json.loads(f.read())
def _correct_old_pvp_cues(self):
has_been_changed: list = [False]
for bank in self.global_cues:
for global_cue in bank:
for cue in global_cue['cues']:
device = self.cue_handler.get_device_from_uuid(cue['uuid'])
if device['type'] == 'pvp':
if 'cue_name' in cue.keys():
logger.info('Found old PVP cue in global cue, fixing...')
has_been_changed[0] = True
cue.pop('cue_name')
cue['cue_type'] = 'cue_cue'
if has_been_changed[0]:
with open(path.join(self.folder_path, 'configs', 'global_cues.json'), 'w') as f:
f.write(json.dumps(self.global_cues))
def open_global_cues_window(self) -> None:
"""
Opens the main global cues window
:return: None
"""
self.root.configure(bg=bg_color)
self.root.geometry('1600x900')
self.root.title('Global Cues Shotbox')
self.root.grid_columnconfigure(0, weight=1)
self.bank_frame.configure(bg=bg_color)
self.bank_frame.grid(row=0, column=0, pady=20)
self.cues_frame.configure(bg=bg_color)
self.cues_frame.grid(row=1, column=0)
# build bank buttons
for i, bank in enumerate(self.global_cues):
new_button = Button(self.bank_frame,
bg=bg_color,
width=11,
foreground=text_color,
font=(font, 20),
text=f'Bank {i + 1}',
command=lambda i=i: self._switch_to_bank(bank=i))
new_button.grid(row=0, column=i, padx=4, pady=10)
self.bank_buttons.append(new_button)
# build cue buttons
row_counter = 0
column_counter = 0
for i in range(32):
new_button = Button(self.cues_frame,
bg=bg_color,
width=12,
height=5,
foreground=text_color,
font=(font, 17),
wraplength=180,
command=lambda i=i: self._cue_button_clicked(index=i))
# Starting a new column
if column_counter >= 8:
row_counter += 1
column_counter = 0
new_button.grid(row=row_counter, column=column_counter, padx=5, pady=10)
self.cue_buttons.append(new_button)
column_counter += 1
self._switch_to_bank(0)
Button(self.root, bg=bg_color, foreground=text_color, font=(font, 14), text='Edit Mode',
command=self._toggle_edit_mode).grid(row=2, column=0, sticky='w', padx=25, pady=20)
self.root.mainloop()
def _create_empty_global_cues_file(self) -> None:
logger.info('Creating empty global cues file')
global_cues = []
for x in range(8):
global_cues.append([])
for i in range(32):
global_cues[x].append({
'name': '',
'cues': []
})
with open(path.join(self.folder_path, 'configs', 'global_cues.json'), 'w') as f:
f.write(json.dumps(global_cues))
def _cue_button_clicked(self, index: int) -> None:
"""
Activate cues associated with a cue button
:param index: index of the cue within the selected bank
:return: None
"""
logger.debug(f'Cue button clicked: {self.currently_selected_bank}:{index}')
if self.edit_mode:
self._toggle_edit_mode()
self._edit_cue(bank=self.currently_selected_bank, cue_index=index)
else:
Thread(target=lambda: self.cue_handler.activate_cues(cuelist=self.global_cues[self.currently_selected_bank][index]['cues'])).start()
def _switch_to_bank(self, bank: int) -> None:
"""
Changes functionality of cue buttons to match specific bank
:param bank: bank desired to swap to
:return: None
"""
logger.debug(f'Swapping to bank {bank}')
self.currently_selected_bank = bank
# Change name of cue buttons to match bank
for i, button in enumerate(self.cue_buttons):
button.configure(text=self.global_cues[self.currently_selected_bank][i]['name'])
# Color currently selected bank button
for i, button in enumerate(self.bank_buttons):
if i == self.currently_selected_bank:
button.configure(bg='#4a4a4a')
else:
button.configure(bg=bg_color)
self._color_cue_buttons()
def _toggle_edit_mode(self) -> None:
"""
Toggle functionality of buttons from cue mode to edit mode
:return: None
"""
# flip edit mode bool
self.edit_mode = not self.edit_mode
if self.edit_mode:
for button in self.cue_buttons:
button.configure(bg='#53804e')
else:
self._color_cue_buttons()
def _color_cue_buttons(self) -> None:
"""
Colors cue buttons accordingly. Dark grey if there's no cues on them, light grey if they contain cues, red if errored.
:return: None
"""
for cue, button in zip(self.global_cues[self.currently_selected_bank], self.cue_buttons):
cue_data = cue['cues']
if cue_data != []:
button.configure(bg='#4a4a4a')
else:
button.configure(bg=bg_color)
def check_if_cues_online() -> None:
"""
Checks to see if all cues within each cue button are valid. Colors red if not.
:return: None
"""
for i, global_cue in enumerate(self.global_cues[self.currently_selected_bank]):
cuelist_valid = self.cue_handler.cues_are_valid(cuelist=global_cue['cues'])
for cue in cuelist_valid:
if False in cue.keys():
self.cue_buttons[i].configure(bg='#a34444')
Thread(target=check_if_cues_online).start()
def _edit_cue(self, bank: int, cue_index: int):
class FakeUI:
def __init__(self):
self.fake: None
self.pco_plan = PcoPlan()
class FakeMain:
def __init__(self):
self.service_type_id = 0
self.service_id = 0
cue_creator = CueCreator(startup=FakeMain(), ui=FakeUI(), devices=self.devices)
cue_creator.edit_global_cue(global_cues=self.global_cues, cue_bank=bank, cue_index=cue_index,
global_cue_shotbox_init=self)
def _reload(self):
self.global_cues = self.read_global_cues()
self._switch_to_bank(self.currently_selected_bank)
def _start_self_contained_webserver(self) -> None:
"""
Start a webserver that takes post requests at http://localhost:80/activate_global_cue?&bank=2&cue_index=3
:return: None
"""
webserver_port = 7777
if not is_local_port_in_use(webserver_port):
logger.debug('Starting global cues webserver')
app = Flask(__name__)
@app.route('/activate_global_cue', methods=['POST'])
def activate():
bank: int = int(request.args.get('bank')) - 1
cue_index: int = int(request.args.get('cue_index')) - 1
logger.debug(f'Cueing global cues on bank {bank}, cue index {cue_index}')
pprint(self.global_cues[bank][cue_index]['cues'])
self.cue_handler.activate_cues(cuelist=self.global_cues[bank][cue_index]['cues'])
return ''
Thread(target=lambda: app.run('0.0.0.0', webserver_port)).start()
if __name__ == '__main__':
GlobalCues().open_global_cues_window()