gui rewritten with builder classes and observables

gui rewritten with builder classes and observables
This commit is contained in:
onyx-and-iris
2022-05-10 20:34:29 +01:00
parent 69eed318c9
commit 92aead8754
12 changed files with 1251 additions and 1366 deletions

View File

@@ -2,7 +2,8 @@ import tkinter as tk
from tkinter import ttk
from math import log
from .data import _base_vals
from .data import _base_values, _configuration
from . import builders
class GainLayer(ttk.LabelFrame):
@@ -10,29 +11,28 @@ class GainLayer(ttk.LabelFrame):
def __init__(self, parent, index, j):
super().__init__(parent)
self._parent = parent
self.parent = parent
self.index = index
self.j = j
self.gain = tk.DoubleVar()
self.level = tk.DoubleVar()
self.on = tk.BooleanVar()
self.s = self._parent._parent.styletable
self.styletable = self.parent.parent.styletable
if index <= parent.phys_in:
self.level_offset = index * 2
else:
self.level_offset = parent.phys_in * 2 + (index - parent.phys_in) * 8
self.builder = builders.ChannelLabelFrameBuilder(self, index, id="gainlayer")
self.builder.setup()
self.builder.add_progressbar()
self.builder.add_scale()
self.builder.add_on_button()
self.sync()
self._make_widgets()
self.col_row_configure()
self.watch_pdirty()
self.watch_levels()
self.grid_configure()
@property
def target(self):
"""returns the current interface"""
_target = self._parent.target
"""returns the strip[i].gainlayer class in the current interface"""
_target = self.parent.target
return _target.strip[self.index].gainlayer[self.j]
def getter(self, param):
@@ -46,27 +46,33 @@ class GainLayer(ttk.LabelFrame):
def reset_gain(self, *args):
self.setter("gain", 0)
self.gain.set(0)
self._parent._parent.nav_frame.info_text.set(0)
self.parent.parent.nav_frame.info_text.set(0)
def scale_enter(self, *args):
self._parent._parent.nav_frame.info_text.set(round(self.gain.get(), 1))
def scale_callback(self, *args):
"""callback function for scale widget"""
def scale_leave(self, *args):
self._parent._parent.nav_frame.info_text.set("")
self.setter("gain", self.gain.get())
self.parent.parent.nav_frame.info_text.set(round(self.gain.get(), 1))
def scale_press(self, *args):
_base_vals.in_scale_button_1 = True
_base_values.in_scale_button_1 = True
def scale_release(self, *args):
_base_vals.in_scale_button_1 = False
_base_values.in_scale_button_1 = False
def scale_enter(self, *args):
self.parent.parent.nav_frame.info_text.set(round(self.gain.get(), 1))
def scale_leave(self, *args):
self.parent.parent.nav_frame.info_text.set("")
def _on_mousewheel(self, event):
self.gain.set(
self.gain.get()
+ (
_base_vals.mwscroll_step
_base_values.mwscroll_step
if event.delta > 0
else -_base_vals.mwscroll_step
else -_base_values.mwscroll_step
)
)
if self.gain.get() > 12:
@@ -74,72 +80,23 @@ class GainLayer(ttk.LabelFrame):
elif self.gain.get() < -60:
self.gain.set(-60)
self.setter("gain", self.gain.get())
self._parent._parent.nav_frame.info_text.set(round(self.gain.get(), 1))
def scale_callback(self, *args):
"""callback function for scale widget"""
self.setter("gain", self.gain.get())
self._parent._parent.nav_frame.info_text.set(round(self.gain.get(), 1))
self.parent.parent.nav_frame.info_text.set(round(self.gain.get(), 1))
def set_on(self):
"""enables a gainlayer. sets its button colour"""
setattr(
self._parent.target.strip[self.index],
self._parent.buses[self.j],
self.parent.target.strip[self.index],
self.parent.buses[self.j],
self.on.get(),
)
if not _base_vals.using_theme:
self.s.configure(
if not _configuration.themes_enabled:
self.styletable.configure(
f"On.TButton",
background=f'{"green" if self.on.get() else "white"}',
)
def convert_level(self, val):
if _base_vals.vban_connected:
return round(-val * 0.01, 1)
return round(20 * log(val, 10), 1) if val > 0 else -200.0
def _make_widgets(self):
"""Creates a progressbar, scale, on button and config button for a single channel"""
# Progress bar
self.pb = ttk.Progressbar(
self,
maximum=100,
orient="vertical",
mode="determinate",
variable=self.level,
)
self.pb.grid(column=0, row=0)
# Scale
self.scale = ttk.Scale(
self,
from_=12.0,
to=-60.0,
orient="vertical",
variable=self.gain,
command=self.scale_callback,
length=self._parent.height,
)
self.scale.grid(column=1, row=0)
self.scale.bind("<Double-Button-1>", self.reset_gain)
self.scale.bind("<Button-1>", self.scale_press)
self.scale.bind("<Enter>", self.scale_enter)
self.scale.bind("<ButtonRelease-1>", self.scale_release)
self.scale.bind("<Leave>", self.scale_leave)
self.scale.bind("<MouseWheel>", self._on_mousewheel)
# On button
self.button_on = ttk.Checkbutton(
self,
text="ON",
command=self.set_on,
style=f"{'Toggle.TButton' if _base_vals.using_theme else 'On.TButton'}",
variable=self.on,
)
self.button_on.grid(column=0, row=1, columnspan=2)
def col_row_configure(self):
def grid_configure(self):
[
child.grid_configure(padx=1, pady=1, sticky=(tk.N, tk.S, tk.W, tk.E))
for child in self.winfo_children()
@@ -150,141 +107,119 @@ class GainLayer(ttk.LabelFrame):
for child in self.winfo_children()
if isinstance(child, ttk.Progressbar) or isinstance(child, ttk.Scale)
]
# pb and scale
self.columnconfigure(0, minsize=36)
self.columnconfigure(1, minsize=36)
self.rowconfigure(1, minsize=70)
def watch_pdirty(self):
self.after(1, self.watch_pdirty_step)
def watch_pdirty_step(self):
"""keeps params synced but ensures sliders are responsive"""
if self._parent._parent.pdirty and not _base_vals.in_scale_button_1:
self.sync()
self.after(_base_vals.pdelay, self.watch_pdirty_step)
# on button
if _configuration.themes_enabled:
self.rowconfigure(1, minsize=70)
else:
self.rowconfigure(1, minsize=55)
def sync(self):
"""sync params with voicemeeter"""
retval = self._parent.target.strip[self.index].label
retval = self.parent.target.strip[self.index].label
if len(retval) > 10:
retval = f"{retval[:8]}.."
if not retval:
self.parent.columnconfigure(self.index, minsize=0)
self.parent.parent.subject_ldirty.remove(self)
self.grid_remove()
else:
self.parent.parent.subject_ldirty.add(self)
self.grid()
self.configure(text=retval)
self.gain.set(self.getter("gain"))
self.on.set(
getattr(
self._parent.target.strip[self.index],
self._parent.buses[self.j],
self.parent.target.strip[self.index],
self.parent.buses[self.j],
)
)
def watch_levels(self):
self.after(1, self.watch_levels_step)
def convert_level(self, val):
if _base_values.vban_connected:
return round(-val * 0.01, 1)
return round(20 * log(val, 10), 1) if val > 0 else -200.0
def watch_levels_step(self):
if not _base_vals.dragging:
if (
self._parent._parent.ldirty
and any(
self._parent._parent.comp_strip[
self.level_offset : self.level_offset + 1
]
)
and _base_vals.strip_level_array_size
== len(self._parent._parent.comp_strip)
):
vals = (
self.convert_level(
self._parent._parent.strip_levels[self.level_offset]
),
self.convert_level(
self._parent._parent.strip_levels[self.level_offset + 1]
),
)
self.level.set(
(
0
if self._parent._parent.channel_frame.strips[
self.index
].mute.get()
or not self.on.get()
else 100 + (max(vals) - 18) + self.gain.get()
)
)
self.after(
_base_vals.ldelay if not _base_vals.in_scale_button_1 else 100,
self.watch_levels_step,
def update(self):
"""update levels"""
vals = (
self.convert_level(self.parent.target.strip_levels[self.level_offset]),
self.convert_level(self.parent.target.strip_levels[self.level_offset + 1]),
)
self.level.set(
(
0
if self.parent.parent.strip_frame.strips[self.index].mute.get()
or not self.on.get()
else 100 + (max(vals) - 18) + self.gain.get()
)
)
class SubMixFrame(ttk.Frame):
def __init__(self, parent):
super().__init__(parent)
self._parent = parent
self.parent = parent
self.phys_in, self.virt_in = parent.kind.ins
self.phys_out, self.virt_out = parent.kind.outs
self.buses = tuple(f"A{i+1}" for i in range(self.phys_out)) + tuple(
f"B{i+1}" for i in range(self.virt_out)
)
defaults = {
"width": 80,
"height": 150,
}
self.configuration = defaults | self.configuration
self.width = self.configuration["width"]
self.height = self.configuration["height"]
self.gainlayers = [
GainLayer(self, index, _base_vals.submixes) for index in range(8)
GainLayer(self, index, _base_values.submixes) for index in range(8)
]
[
gainlayer.grid(row=0, column=self.gainlayers.index(gainlayer))
for gainlayer in self.gainlayers
]
self.col_row_configure()
# destroy any without label text
for i, gainlayer in enumerate(self.gainlayers):
gainlayer.grid(row=0, column=i)
if not gainlayer.cget("text"):
for i, labelframe in enumerate(self.labelframes):
labelframe.grid(row=0, column=i)
if not self.target.strip[i].label:
self.columnconfigure(i, minsize=0)
gainlayer.grid_remove()
labelframe.grid_remove()
self.watch_pdirty()
if _configuration.extends_horizontal:
self.grid(row=0, column=2)
if parent.bus_frame:
parent.bus_frame.grid_remove()
else:
self._parent.submix_frame.grid(row=2, column=0)
if parent.bus_frame:
parent.bus_frame.grid_remove()
# registers submixframe as pdirty observer
self.parent.subject_pdirty.add(self)
@property
def target(self):
"""returns the current interface"""
return self._parent.target
return self.parent.target
@property
def configuration(self):
return self._parent.configuration["channel"]
def labelframes(self):
"""returns a tuple of current gainlayer labelframe addresses"""
@configuration.setter
def configuration(self, val):
self._parent.configuration["channel"] = val
return tuple(
frame
for frame in self.winfo_children()
if isinstance(frame, ttk.LabelFrame)
)
def col_row_configure(self):
def grid_configure(self):
[
self.columnconfigure(i, minsize=self.width)
for i, _ in enumerate(self.gainlayers)
self.columnconfigure(i, minsize=_configuration.level_width)
for i, _ in enumerate(self.labelframes)
]
[
self.rowconfigure(0, minsize=_configuration.level_height)
for i, _ in enumerate(self.labelframes)
]
[self.rowconfigure(0, minsize=130) for i, _ in enumerate(self.gainlayers)]
def watch_pdirty(self):
self.after(1, self.watch_pdirty_step)
def update(self):
for labelframe in self.labelframes:
labelframe.sync()
def watch_pdirty_step(self):
if self._parent.pdirty:
self.watch_labels()
self.after(_base_vals.pdelay, self.watch_pdirty_step)
def watch_labels(self):
for i, gainlayer in enumerate(self.gainlayers):
if not self.target.strip[gainlayer.index].label:
self.columnconfigure(i, minsize=0)
gainlayer.grid_remove()
else:
self.columnconfigure(i, minsize=80)
gainlayer.grid()
def teardown(self):
# deregisters submixframe as pdirty observer
self.parent.subject_pdirty.remove(self)
self.destroy()