Skip to content

Commit ca71d11

Browse files
authored
Merge pull request #9 from mchilli/group-concept
Update to group concept
2 parents 2a6edd4 + 04a473e commit ca71d11

File tree

12 files changed

+1817
-1973
lines changed

12 files changed

+1817
-1973
lines changed

README.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ This is an [Adafruit MacroPad](https://www.adafruit.com/product/5128) script tha
1212
- Groups can store more macros or groups
1313
- Define encoder macros for different groups
1414
- Choose colors for every single macro or group
15-
- You can have an almost infinite number of pages
1615
- Save your configurations locally by downloading it as a JSON file
1716

1817
- Device settings:
@@ -36,15 +35,11 @@ If you are happy with your configuration don't forget to `Store`! Otherwise your
3635

3736
#### Other Informations:
3837

39-
The first row of keys is for navigating through your macros and groups. To be honest the middle button in this row actually has no function. If you have a smart idea for it, let me know!
40-
It allows you to define different macros for the encoder that are available within groups. So there are nine macros or groups per page.
41-
4238
Your can enable the USB storage either by pressing the yellow blinking key (top, left) when plugin the device, you can enable it through the WebUI under "reboot" or you can set a macro with the device function "enable_usb".
4339

4440
#### Ideas:
4541

4642
- Multilingual WebUI
47-
- Find a usecase for the middle navigation key
4843
- You tell me, feel free to contribute
4944

5045
---

circuitpython/code.py

Lines changed: 61 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
from adafruit_hid.consumer_control_code import ConsumerControlCode
1818
from adafruit_hid.mouse import Mouse
1919

20-
from utils.utils import to_chunks
2120
from utils.devices import Encoder, Key
2221
from utils.system import System
2322

@@ -88,21 +87,24 @@ class MacroApp():
8887
""" Main Class """
8988
def __init__(self) -> None:
9089
self.macropad = MacroPad(layout_class=KeyboardLayout, rotation=180 if SETTINGS["fliprotation"] else 0)
90+
9191
self.macropad.display.auto_refresh = False
9292
self.macropad.display.brightness = SETTINGS["brightness"]
93+
self.macropad.display.root_group = displayio.Group()
94+
9395
self.macropad.pixels.auto_write = False
9496
self.macropad.pixels.brightness = SETTINGS["brightness"]
9597

9698
self.readonly = storage.getmount('/').readonly
9799
self.serial_data = usb_cdc.data
98100
self.serial_last_state = False
99-
100-
self.macros = self._init_macros()
101+
102+
self.macroStack = [self._init_macros()]
101103
self.keys = self._init_keys()
102-
self.toolbar = self._init_toolbar()
104+
self.group_label = self._init_group_label()
103105
self.encoder = Encoder(self.macropad)
104106

105-
self.show_homescreen()
107+
self._init_group()
106108

107109
def _save_settings(self, new_settings) -> None:
108110
""" store the new settings in the settingsfile
@@ -119,18 +121,19 @@ def _init_macros(self) -> list[dict]:
119121
Returns:
120122
dict: the json file as dict
121123
"""
124+
rootLabel = "Macros"
122125
try:
123126
with open(MACROFILE, "r") as f:
124127
macros = json.load(f)
125128
if isinstance(macros, list):
126129
return {
127-
"label": "Macros",
130+
"label": rootLabel,
128131
"content": macros,
129132
}
130133
return macros
131134
except OSError:
132135
return {
133-
"label": "Macros",
136+
"label": rootLabel,
134137
"content": [],
135138
}
136139

@@ -140,102 +143,60 @@ def _save_macros(self) -> None:
140143
if self.readonly:
141144
return False
142145
with open(MACROFILE, "w") as f:
143-
f.write(json.dumps(self.macros, separators=(",", ":")))
146+
f.write(json.dumps(self.macroStack[0], separators=(",", ":")))
144147
return True
145148

149+
def _init_group_label(self) -> dict[str, Key]:
150+
group_label = Label(
151+
font=load_font("/fonts/6x12.pcf") if SETTINGS["useunicodefont"] else terminalio.FONT,
152+
text="",
153+
padding_top=0,
154+
padding_bottom=0,
155+
padding_left=0,
156+
padding_right=0,
157+
color=0xFFFFFF,
158+
anchored_position=(self.macropad.display.width // 2, self.macropad.display.height - 10),
159+
anchor_point=(0.5, 0.0)
160+
)
161+
162+
self.macropad.display.root_group.append(group_label)
163+
164+
return group_label
165+
146166
def _init_keys(self) -> list[Key]:
147167
""" Initiate the keys and a display group for each key
148168
149169
Returns:
150170
list[Key]: a list of Keys
151171
"""
152172
keys = []
153-
group = displayio.Group()
154173

155174
for i in range(self.macropad.keys.key_count):
156175
label = Label(
157176
font=load_font("/fonts/6x12.pcf") if SETTINGS["useunicodefont"] else terminalio.FONT,
158177
text="",
159-
padding_top=1,
160-
padding_bottom=2,
178+
padding_top=0,
179+
padding_bottom=1,
161180
padding_left=4,
162181
padding_right=4,
163182
color=0xFFFFFF,
164183
anchored_position=(
165184
(self.macropad.display.width - 2) / 2 * (i % 3) + 1,
166-
self.macropad.display.height / 4 * (i // 3) + 2),
185+
self.macropad.display.height / 5 * (i // 3) + 2),
167186
anchor_point=((i % 3) / 2, 0.0)
168187
)
169188

170189
keys.append(Key(self.macropad, i, label))
171-
group.append(label)
190+
self.macropad.display.root_group.append(label)
172191

173-
self.macropad.display.root_group = group
174192
return keys
175193

176-
def _init_toolbar(self) -> dict[str, Key]:
177-
""" Return a dict for the toolbar keys
178-
179-
Returns:
180-
dict[str, Key]: position of key, Key
181-
"""
182-
return {
183-
"left": self.keys[0],
184-
"center": self.keys[1],
185-
"right": self.keys[2],
186-
}
187-
188194
def _init_group(self) -> None:
189195
""" initiate the group content
190196
"""
191197
self._update_encoder_macros()
192198

193-
self.tabs_content = list(to_chunks(self.group_stack[-1]["content"], 9))
194199
self._update_tab()
195-
196-
def show_homescreen(self, *args) -> None:
197-
""" Show or return to Homescreen
198-
"""
199-
self.current_tab = 0
200-
self.tabs_content = []
201-
self.tab_index_stack = []
202-
self.group_stack = [self.macros]
203-
204-
self._init_group()
205-
206-
def _set_toolbar(self, position:str, label:str, func:dict) -> None:
207-
""" set the label and function for the given toolbar key
208-
209-
Args:
210-
position (str): ("left"|"center"|"right")
211-
label (str): the displayed label
212-
func (dict): the function that will be called on key press
213-
"""
214-
self.toolbar[position].label = label
215-
self.toolbar[position].type = "macro"
216-
self.toolbar[position].color = (100, 100, 100)
217-
self.toolbar[position].set_func(func)
218-
219-
def _update_toolbar(self) -> None:
220-
""" update the toolbar keys based on tab or folder hierarchy
221-
"""
222-
if self.current_tab > 0:
223-
self._set_toolbar("left", "<-", self.prev_tab)
224-
elif len(self.group_stack) > 1:
225-
self._set_toolbar("left", "<-", self.close_group)
226-
else:
227-
self.toolbar["left"].clear_props()
228-
229-
if len(self.group_stack) > 1 and self.current_tab > 0:
230-
self._set_toolbar("center", self.group_stack[-1]["label"], self.show_homescreen)
231-
else:
232-
self.toolbar["center"].clear_props()
233-
self.toolbar["center"].label = self.group_stack[-1]["label"]
234-
235-
if len(self.tabs_content) > 1 and self.current_tab < len(self.tabs_content) - 1:
236-
self._set_toolbar("right", "->", self.next_tab)
237-
else:
238-
self.toolbar["right"].clear_props()
239200

240201
def run_macro(self, item:dict, *args) -> None:
241202
""" run the macro, can be:
@@ -273,6 +234,8 @@ def run_macro(self, item:dict, *args) -> None:
273234
if control_code:
274235
self.macropad.consumer_control.press(control_code)
275236
self.macropad.consumer_control.release()
237+
if 'tone' in key:
238+
self.macropad.play_tone(key['tone']['frequency'], key['tone']['duration'])
276239
if 'mse' in key:
277240
if "b" in key["mse"]:
278241
btn = getattr(Mouse, f"{key['mse']['b'].upper()}_BUTTON", None)
@@ -296,75 +259,63 @@ def open_group(self, item:dict, *args) -> None:
296259
Args:
297260
item (dict): the group item containing data
298261
"""
299-
self.tab_index_stack.append(self.current_tab)
300-
self.current_tab = 0
301262

302-
self.group_stack.append(item)
263+
self.macroStack.append(item)
303264
self._init_group()
304265

305266
def close_group(self, *args) -> None:
306267
""" close a group and go a level up
307268
"""
308-
self.current_tab = self.tab_index_stack.pop()
269+
if len(self.macroStack) > 1:
270+
self.macroStack.pop()
271+
self._init_group()
309272

310-
self.group_stack.pop()
273+
def go_to_root(self, *args) -> None:
274+
""" close a group and go to root
275+
"""
276+
del self.macroStack[1:]
311277
self._init_group()
312278

313279
def _update_tab(self) -> None:
314280
""" update the current displayed group tab
315281
"""
316-
for key in self.keys[3:]:
282+
for key in self.keys:
317283
key.clear_props()
318284

319-
if len(self.tabs_content) > 0:
320-
for i, item in enumerate(self.tabs_content[self.current_tab], start=3):
321-
self.keys[i].type = item["type"]
322-
self.keys[i].label = item["label"] if item["type"] in ["group", "macro"] else ""
323-
self.keys[i].color = item["color"] if item["type"] in ["group", "macro"] else (0, 0, 0)
324-
self.keys[i].set_func(self._get_key_func(item["type"]), item)
285+
for i, item in enumerate(self.macroStack[-1]["content"][:self.macropad.keys.key_count]):
286+
self.keys[i].type = item["type"]
287+
self.keys[i].label = "" if item["type"] == "blank" else item["label"]
288+
self.keys[i].color = (0, 0, 0) if item["type"] == "blank" else item["color"]
289+
self.keys[i].set_func(self._get_key_func(item["type"]), item)
325290

326-
self._update_toolbar()
291+
self.group_label.text = self.macroStack[-1]["label"]
327292

328293
for key in self.keys:
329294
key.update_colors()
330295

331-
def next_tab(self, *args) -> None:
332-
""" increase the tab index and update the tab
333-
"""
334-
if self.current_tab < len(self.tabs_content) - 1:
335-
self.current_tab += 1
336-
self._update_tab()
337-
338-
def prev_tab(self, *args) -> None:
339-
""" decrease the tab index and update the tab
340-
"""
341-
if self.current_tab > 0:
342-
self.current_tab -= 1
343-
self._update_tab()
344-
345296
def _get_key_func(self, type:str) -> function:
346297
""" get the specific function for the type
347298
348299
Args:
349-
type (str): the item type (group|macro)
300+
type (str): the item type
350301
351302
Returns:
352303
function: return the function for type
353304
"""
354305
key_funcs = {
355-
"group": self.open_group,
356-
"macro": self.run_macro
306+
"blank": None,
307+
"group": self.open_group
357308
}
358309

359-
return key_funcs.get(type)
310+
return key_funcs.get(type, self.run_macro)
360311

361312
def _update_encoder_macros(self) -> None:
362313
""" update the rotary encoder macros defined for opened group
363314
"""
364315
self.encoder.update_encoder_macros(
365-
on_switch = self.group_stack[-1].get("encoder", {}).get("switch"),
366-
on_increased = self.group_stack[-1].get("encoder", {}).get("increased"),
367-
on_decreased = self.group_stack[-1].get("encoder", {}).get("decreased")
316+
on_switch = self.macroStack[-1].get("encoder", {}).get("switch"),
317+
on_increased = self.macroStack[-1].get("encoder", {}).get("increased"),
318+
on_decreased = self.macroStack[-1].get("encoder", {}).get("decreased")
368319
)
369320

370321
def _handle_serial_data(self, payload:str) -> dict:
@@ -407,7 +358,7 @@ def _handle_serial_data(self, payload:str) -> dict:
407358

408359
elif command == 'get_macros':
409360
response['ACK'] = 'macros'
410-
response['CONTENT'] = self.macros
361+
response['CONTENT'] = self.macroStack[0]
411362
return response
412363

413364
elif command == 'set_macros':
@@ -416,9 +367,9 @@ def _handle_serial_data(self, payload:str) -> dict:
416367
return response
417368

418369
content = payload['content']
419-
self.macros = content
370+
self.macroStack = [content]
420371
self._display_on()
421-
self.show_homescreen()
372+
self._init_group()
422373

423374
response['ACK'] = 'Macros received'
424375
return response
@@ -466,6 +417,8 @@ def _send_serial_data(self, payload:dict) -> None:
466417
self.serial_data.write(bytearray(payloads.encode()))
467418

468419
def _display_on(self) -> None:
420+
""" Turn on the display if it's in sleep mode and reset the sleep timer.
421+
"""
469422
if self.macropad.display_sleep:
470423
self.macropad.display_sleep = False
471424
self.sleep_timer = time.monotonic()

circuitpython/utils/devices.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,12 @@ def color(self, color:tuple) -> None:
108108
def update_colors(self) -> None:
109109
""" update the backgroundcolor and color based on type
110110
"""
111-
if self.type == "macro":
112-
self._label.background_color = 0xffffff
113-
self._label.color = 0x000000
114-
else:
111+
if self.type in ["blank", "group"]:
115112
self._label.background_color = 0x000000
116113
self._label.color = 0xffffff
114+
else:
115+
self._label.background_color = 0xffffff
116+
self._label.color = 0x000000
117117

118118
self._set_led(self.color)
119119

circuitpython/utils/system.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ def soft_reset(app=None) -> None:
1717
def hard_reset(app=None) -> None:
1818
microcontroller.reset()
1919

20+
def close_group(app=None) -> None:
21+
app.close_group()
22+
23+
def go_to_root(app=None) -> None:
24+
app.go_to_root()
25+
2026
def decrease_brightness(app=None) -> None:
2127
if app.macropad.display.brightness > 0:
2228
brightness = (round(app.macropad.display.brightness * 10) - 1) / 10

0 commit comments

Comments
 (0)