Skip to content

Commit edecee0

Browse files
committed
[app] Checkpoint 1
1 parent 0088bc1 commit edecee0

File tree

2 files changed

+72
-8
lines changed

2 files changed

+72
-8
lines changed

dvr_scan/app/application.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from dvr_scan.app.common import register_icon
2828
from dvr_scan.app.region_editor import RegionEditor
2929
from dvr_scan.app.scan_window import ScanWindow
30-
from dvr_scan.app.widgets import ColorPicker, Spinbox, TimecodeEntry
30+
from dvr_scan.app.widgets import CollapsibleFrame, ColorPicker, Spinbox, TimecodeEntry
3131
from dvr_scan.config import (
3232
CHOICE_MAP,
3333
CONFIG_MAP,
@@ -1297,22 +1297,44 @@ def __init__(self, settings: ScanSettings, initial_videos: ty.List[str]):
12971297
register_icon(self._root)
12981298
self._root.resizable(True, True)
12991299
self._root.minsize(width=MIN_WINDOW_WIDTH, height=MIN_WINDOW_HEIGHT)
1300-
self._root.columnconfigure(0, weight=1, pad=PADDING)
1300+
self._root.columnconfigure(0, weight=1)
13011301
self._root.rowconfigure(0, weight=1)
13021302

1303+
# Create a canvas and a scrollbar.
1304+
canvas = tk.Canvas(self._root)
1305+
scrollbar = ttk.Scrollbar(self._root, orient="vertical", command=canvas.yview)
1306+
scrollable_frame = ttk.Frame(canvas)
1307+
1308+
scrollable_frame.bind(
1309+
"<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
1310+
)
1311+
1312+
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
1313+
canvas.configure(yscrollcommand=scrollbar.set)
1314+
1315+
def _on_mousewheel(event):
1316+
canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
1317+
1318+
canvas.bind_all("<MouseWheel>", _on_mousewheel)
1319+
1320+
canvas.grid(row=0, column=0, sticky="nsew")
1321+
scrollbar.grid(row=0, column=1, sticky="ns")
1322+
1323+
scrollable_frame.columnconfigure(0, weight=1)
1324+
13031325
self._input_settings_window = InputSettingsWindow(self._root)
13041326
self._motion_settings_window = MotionSettingsWindow(self._root)
13051327

1306-
input_frame = ttk.Labelframe(self._root, text="Input", padding=PADDING)
1307-
self._input_area = InputArea(input_frame)
1328+
input_frame = CollapsibleFrame(scrollable_frame, text="Input")
1329+
self._input_area = InputArea(input_frame.content_frame)
13081330
input_frame.grid(row=0, sticky=tk.NSEW, padx=PADDING, pady=(PADDING, 0))
13091331

1310-
output_frame = ttk.Labelframe(self._root, text="Output", padding=PADDING)
1311-
self._output_area = OutputArea(output_frame)
1332+
output_frame = CollapsibleFrame(scrollable_frame, text="Output")
1333+
self._output_area = OutputArea(output_frame.content_frame)
13121334
output_frame.grid(row=2, sticky=EXPAND_HORIZONTAL, padx=PADDING, pady=(PADDING, 0))
13131335

1314-
scan_frame = ttk.Labelframe(self._root, text="Run", padding=PADDING)
1315-
self._scan_area = ScanArea(self._root, scan_frame)
1336+
scan_frame = CollapsibleFrame(scrollable_frame, text="Run")
1337+
self._scan_area = ScanArea(self._root, scan_frame.content_frame)
13161338
scan_frame.grid(row=3, sticky=EXPAND_HORIZONTAL, padx=PADDING, pady=PADDING)
13171339

13181340
self._scan_window: ty.Optional[ScanWindow] = None

dvr_scan/app/widgets.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,45 @@ def _validate(self, newval: str, event: str):
211211
self._refresh()
212212

213213
return True
214+
215+
216+
class CollapsibleFrame(ttk.Frame):
217+
"""A collapsible frame widget that can be expanded or collapsed."""
218+
219+
def __init__(self, parent, text="", initially_collapsed: bool = False):
220+
super().__init__(parent)
221+
self.columnconfigure(0, weight=1)
222+
self.rowconfigure(1, weight=1)
223+
224+
self._text = text
225+
self._is_collapsed = tk.BooleanVar(value=initially_collapsed)
226+
227+
style = ttk.Style(self)
228+
# TODO(v1.0): Move this into a global style configuration.
229+
style.configure("Collapsible.TButton", borderwidth=0, relief=tk.FLAT)
230+
231+
self._toggle_button = ttk.Button(
232+
self,
233+
text=self._get_toggle_text(),
234+
command=self._toggle,
235+
style="Collapsible.TButton",
236+
)
237+
self._toggle_button.grid(row=0, column=0, sticky=tk.W)
238+
239+
self.content_frame = ttk.Frame(self, padding=PADDING)
240+
self.content_frame.grid(row=1, column=0, sticky=tk.NSEW)
241+
242+
if self._is_collapsed.get():
243+
self.content_frame.grid_remove()
244+
245+
def _get_toggle_text(self):
246+
arrow = "▶" if self._is_collapsed.get() else "▼"
247+
return f"{arrow} {self._text}"
248+
249+
def _toggle(self, event=None):
250+
self._is_collapsed.set(not self._is_collapsed.get())
251+
if self._is_collapsed.get():
252+
self.content_frame.grid_remove()
253+
else:
254+
self.content_frame.grid()
255+
self._toggle_button.config(text=self._get_toggle_text())

0 commit comments

Comments
 (0)