mirror of
https://github.com/onyx-and-iris/voicemeeter-compact.git
synced 2026-04-13 11:03:31 +00:00
gain_label added to chanenl labelframes.
couple of bug fixes for submix frame minor version bump
This commit is contained in:
@@ -13,8 +13,6 @@ from .menu import Menus
|
||||
class App(tk.Tk):
|
||||
"""App mainframe"""
|
||||
|
||||
_instances = {}
|
||||
|
||||
@classmethod
|
||||
def make(cls, kind: NamedTuple):
|
||||
"""
|
||||
@@ -34,6 +32,7 @@ class App(tk.Tk):
|
||||
|
||||
def __init__(self, vmr):
|
||||
super().__init__()
|
||||
|
||||
self._vmr = vmr
|
||||
icon_path = Path(__file__).parent.resolve() / "img" / "cat.ico"
|
||||
if icon_path.is_file():
|
||||
@@ -41,6 +40,8 @@ class App(tk.Tk):
|
||||
self.minsize(275, False)
|
||||
self.subject_pdirty = Subject()
|
||||
self.subject_ldirty = Subject()
|
||||
self.strip_levels = None
|
||||
self.bus_levels = None
|
||||
self["menu"] = Menus(self, vmr)
|
||||
self.styletable = ttk.Style()
|
||||
if _configuration.profile:
|
||||
@@ -74,7 +75,8 @@ class App(tk.Tk):
|
||||
self._vban = vban
|
||||
if kind:
|
||||
self.kind = kind
|
||||
# register as observer
|
||||
|
||||
# register app as observer
|
||||
self.target.subject.add(self)
|
||||
|
||||
self.bus_frame = None
|
||||
@@ -90,16 +92,20 @@ class App(tk.Tk):
|
||||
if self.kind.name == "Potato":
|
||||
self.builder.create_banner()
|
||||
|
||||
def update(self, subject):
|
||||
"""
|
||||
called whenever notified of update
|
||||
def on_update(self, subject, data):
|
||||
"""called whenever notified of update"""
|
||||
|
||||
after 1 to prevent vmr,vban interface waiting.
|
||||
"""
|
||||
if subject == "pdirty" and not _base_values.in_scale_button_1:
|
||||
self.after(1, self.notify_pdirty)
|
||||
elif subject == "ldirty" and not _base_values.dragging:
|
||||
self.after(1, self.notify_ldirty)
|
||||
if not _base_values.in_scale_button_1:
|
||||
if subject == "pdirty":
|
||||
self.after(1, self.notify_pdirty)
|
||||
elif subject == "ldirty" and not _base_values.dragging:
|
||||
(
|
||||
self.strip_levels,
|
||||
self.strip_comp,
|
||||
self.bus_levels,
|
||||
self.bus_comp,
|
||||
) = data
|
||||
self.after(1, self.notify_ldirty)
|
||||
|
||||
def notify_pdirty(self):
|
||||
self.subject_pdirty.notify()
|
||||
@@ -111,13 +117,13 @@ class App(tk.Tk):
|
||||
"""
|
||||
Clear observables.
|
||||
|
||||
Unregister app as observer.
|
||||
Deregister app as observer.
|
||||
|
||||
Destroy all top level frames.
|
||||
"""
|
||||
self.target.subject.remove(self)
|
||||
self.subject_pdirty.clear()
|
||||
self.subject_ldirty.clear()
|
||||
self.target.subject.remove(self)
|
||||
[
|
||||
frame.destroy()
|
||||
for frame in self.winfo_children()
|
||||
|
||||
@@ -21,7 +21,8 @@ class Banner(ttk.Frame):
|
||||
|
||||
@property
|
||||
def target(self):
|
||||
"""use the correct interface"""
|
||||
"""returns the current interface"""
|
||||
|
||||
return self.parent.target
|
||||
|
||||
def upd_submix(self):
|
||||
|
||||
@@ -20,7 +20,7 @@ class AbstractBuilder(abc.ABC):
|
||||
|
||||
@abc.abstractmethod
|
||||
def teardown(self):
|
||||
"""unregister as observable"""
|
||||
"""deregister as observable"""
|
||||
pass
|
||||
|
||||
|
||||
@@ -56,11 +56,12 @@ class MainFrameBuilder(AbstractBuilder):
|
||||
def create_configframe(self, type_, index, id):
|
||||
if type_ == "strip":
|
||||
self.app.config_frame = StripConfig(self.app, index, id)
|
||||
[
|
||||
frame.conf.set(False)
|
||||
for i, frame in enumerate(self.app.strip_frame.labelframes)
|
||||
if i != index
|
||||
]
|
||||
if self.app.strip_frame:
|
||||
[
|
||||
frame.conf.set(False)
|
||||
for i, frame in enumerate(self.app.strip_frame.labelframes)
|
||||
if i != index
|
||||
]
|
||||
if self.app.bus_frame:
|
||||
[
|
||||
frame.conf.set(False)
|
||||
@@ -68,11 +69,12 @@ class MainFrameBuilder(AbstractBuilder):
|
||||
]
|
||||
else:
|
||||
self.app.config_frame = BusConfig(self.app, index, id)
|
||||
[
|
||||
frame.conf.set(False)
|
||||
for i, frame in enumerate(self.app.bus_frame.labelframes)
|
||||
if i != index
|
||||
]
|
||||
if self.app.bus_frame:
|
||||
[
|
||||
frame.conf.set(False)
|
||||
for i, frame in enumerate(self.app.bus_frame.labelframes)
|
||||
if i != index
|
||||
]
|
||||
if self.app.strip_frame:
|
||||
[
|
||||
frame.conf.set(False)
|
||||
@@ -180,7 +182,7 @@ class NavigationFrameBuilder(AbstractBuilder):
|
||||
if isinstance(child, ttk.Checkbutton)
|
||||
]
|
||||
if _configuration.themes_enabled:
|
||||
self.navframe.rowconfigure(1, minsize=_configuration.level_height - 25)
|
||||
self.navframe.rowconfigure(1, minsize=_configuration.level_height - 16)
|
||||
else:
|
||||
self.navframe.rowconfigure(1, minsize=_configuration.level_height - 5)
|
||||
|
||||
@@ -200,9 +202,10 @@ class ChannelLabelFrameBuilder(AbstractBuilder):
|
||||
def setup(self):
|
||||
"""Create class variables for widgets"""
|
||||
self.labelframe.gain = tk.DoubleVar()
|
||||
self.labelframe.level = tk.DoubleVar()
|
||||
self.labelframe.level = tk.DoubleVar(value=0)
|
||||
self.labelframe.mute = tk.BooleanVar()
|
||||
self.labelframe.conf = tk.BooleanVar()
|
||||
self.labelframe.gainlabel = tk.StringVar()
|
||||
|
||||
"""for gainlayers"""
|
||||
self.labelframe.on = tk.BooleanVar()
|
||||
@@ -227,7 +230,7 @@ class ChannelLabelFrameBuilder(AbstractBuilder):
|
||||
orient="vertical",
|
||||
variable=self.labelframe.gain,
|
||||
command=self.labelframe.scale_callback,
|
||||
length=100,
|
||||
length=_configuration.level_height,
|
||||
)
|
||||
self.scale.grid(column=1, row=0)
|
||||
self.scale.bind("<Double-Button-1>", self.labelframe.reset_gain)
|
||||
@@ -237,6 +240,13 @@ class ChannelLabelFrameBuilder(AbstractBuilder):
|
||||
self.scale.bind("<Leave>", self.labelframe.scale_leave)
|
||||
self.scale.bind("<MouseWheel>", self.labelframe._on_mousewheel)
|
||||
|
||||
def add_gain_label(self):
|
||||
self.labelframe.gain_label = ttk.Label(
|
||||
self.labelframe,
|
||||
textvariable=self.labelframe.gainlabel,
|
||||
)
|
||||
self.labelframe.gain_label.grid(column=0, row=1, columnspan=2)
|
||||
|
||||
def add_mute_button(self):
|
||||
"""Adds a mute button widget to a single label frame"""
|
||||
self.button_mute = ttk.Checkbutton(
|
||||
@@ -246,7 +256,7 @@ class ChannelLabelFrameBuilder(AbstractBuilder):
|
||||
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{self.identifier}Mute{self.index}.TButton'}",
|
||||
variable=self.labelframe.mute,
|
||||
)
|
||||
self.button_mute.grid(column=0, row=1, columnspan=2)
|
||||
self.button_mute.grid(column=0, row=2, columnspan=2)
|
||||
|
||||
def add_conf_button(self):
|
||||
self.button_conf = ttk.Checkbutton(
|
||||
@@ -256,14 +266,14 @@ class ChannelLabelFrameBuilder(AbstractBuilder):
|
||||
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{self.identifier}Conf{self.index}.TButton'}",
|
||||
variable=self.labelframe.conf,
|
||||
)
|
||||
self.button_conf.grid(column=0, row=2, columnspan=2)
|
||||
self.button_conf.grid(column=0, row=3, columnspan=2)
|
||||
|
||||
def add_on_button(self):
|
||||
self.button_on = ttk.Checkbutton(
|
||||
self.labelframe,
|
||||
text="ON",
|
||||
command=self.labelframe.set_on,
|
||||
style=f"{'Toggle.TButton' if _configuration.themes_enabled else 'On.TButton'}",
|
||||
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{self.identifier}On{self.index}.TButton'}",
|
||||
variable=self.labelframe.on,
|
||||
)
|
||||
self.button_on.grid(column=0, row=1, columnspan=2)
|
||||
@@ -291,7 +301,7 @@ class ChannelConfigFrameBuilder(AbstractBuilder):
|
||||
pass
|
||||
|
||||
def teardown(self):
|
||||
"""Unregister as observable, then destroy frame"""
|
||||
"""Deregister as observable, then destroy frame"""
|
||||
self.configframe.parent.subject_pdirty.remove(self.configframe)
|
||||
self.configframe.destroy()
|
||||
|
||||
@@ -312,7 +322,7 @@ class StripConfigFrameBuilder(ChannelConfigFrameBuilder):
|
||||
"""Responsible for building channel configframe widgets"""
|
||||
|
||||
def setup(self):
|
||||
if self.configframe.parent.kind.ins == "Basic":
|
||||
if self.configframe.parent.kind.name == "Basic":
|
||||
self.configframe.slider_params = ("audibility",)
|
||||
self.configframe.slider_vars = (tk.DoubleVar(),)
|
||||
else:
|
||||
@@ -340,6 +350,30 @@ class StripConfigFrameBuilder(ChannelConfigFrameBuilder):
|
||||
tk.BooleanVar() for _ in self.configframe.params
|
||||
)
|
||||
|
||||
if self.configframe.parent.kind.name in ("Banana", "Potato"):
|
||||
if self.configframe.index == self.configframe.phys_in:
|
||||
self.configframe.params = list(
|
||||
map(lambda x: x.replace("mono", "mc"), self.configframe.params)
|
||||
)
|
||||
if self.configframe.parent.kind.name == "Banana":
|
||||
pass
|
||||
# karaoke modes not in RT Packet yet. May implement in future
|
||||
"""
|
||||
if self.index == self.phys_in + 1:
|
||||
self.params = list(
|
||||
map(lambda x: x.replace("mono", "k"), self.params)
|
||||
)
|
||||
self.param_vars[self.params.index("k")] = tk.IntVar
|
||||
"""
|
||||
else:
|
||||
if (
|
||||
self.configframe.index
|
||||
== self.configframe.phys_in + self.configframe.virt_in - 1
|
||||
):
|
||||
self.configframe.params = list(
|
||||
map(lambda x: x.replace("mono", "mc"), self.configframe.params)
|
||||
)
|
||||
|
||||
def create_comp_slider(self):
|
||||
comp_label = ttk.Label(self.configframe, text="Comp")
|
||||
comp_scale = ttk.Scale(
|
||||
@@ -356,8 +390,10 @@ class StripConfigFrameBuilder(ChannelConfigFrameBuilder):
|
||||
comp_scale.bind(
|
||||
"<Double-Button-1>", partial(self.configframe.reset_scale, "comp", 0)
|
||||
)
|
||||
comp_scale.bind("<Button-1>", self.configframe.scale_enter)
|
||||
comp_scale.bind("<ButtonRelease-1>", self.configframe.scale_leave)
|
||||
comp_scale.bind("<Button-1>", self.configframe.scale_press)
|
||||
comp_scale.bind("<ButtonRelease-1>", self.configframe.scale_release)
|
||||
comp_scale.bind("<Enter>", partial(self.configframe.scale_enter, "comp"))
|
||||
comp_scale.bind("<Leave>", self.configframe.scale_leave)
|
||||
|
||||
comp_label.grid(column=0, row=0)
|
||||
comp_scale.grid(column=1, row=0)
|
||||
@@ -378,8 +414,10 @@ class StripConfigFrameBuilder(ChannelConfigFrameBuilder):
|
||||
gate_scale.bind(
|
||||
"<Double-Button-1>", partial(self.configframe.reset_scale, "gate", 0)
|
||||
)
|
||||
gate_scale.bind("<Button-1>", self.configframe.scale_enter)
|
||||
gate_scale.bind("<ButtonRelease-1>", self.configframe.scale_leave)
|
||||
gate_scale.bind("<Button-1>", self.configframe.scale_press)
|
||||
gate_scale.bind("<ButtonRelease-1>", self.configframe.scale_release)
|
||||
gate_scale.bind("<Enter>", partial(self.configframe.scale_enter, "gate"))
|
||||
gate_scale.bind("<Leave>", self.configframe.scale_leave)
|
||||
|
||||
gate_label.grid(column=2, row=0)
|
||||
gate_scale.grid(column=3, row=0)
|
||||
@@ -400,8 +438,10 @@ class StripConfigFrameBuilder(ChannelConfigFrameBuilder):
|
||||
limit_scale.bind(
|
||||
"<Double-Button-1>", partial(self.configframe.reset_scale, "limit", 12)
|
||||
)
|
||||
limit_scale.bind("<Button-1>", self.configframe.scale_enter)
|
||||
limit_scale.bind("<ButtonRelease-1>", self.configframe.scale_leave)
|
||||
limit_scale.bind("<Button-1>", self.configframe.scale_press)
|
||||
limit_scale.bind("<ButtonRelease-1>", self.configframe.scale_release)
|
||||
limit_scale.bind("<Enter>", partial(self.configframe.scale_enter, "limit"))
|
||||
limit_scale.bind("<Leave>", self.configframe.scale_leave)
|
||||
|
||||
limit_label.grid(column=4, row=0)
|
||||
limit_scale.grid(column=5, row=0)
|
||||
@@ -409,11 +449,11 @@ class StripConfigFrameBuilder(ChannelConfigFrameBuilder):
|
||||
def create_audibility_slider(self):
|
||||
aud_label = ttk.Label(self.configframe, text="Audibility")
|
||||
aud_scale = ttk.Scale(
|
||||
self,
|
||||
self.configframe,
|
||||
from_=0.0,
|
||||
to=10.0,
|
||||
orient="horizontal",
|
||||
length=_base_values.level_width,
|
||||
length=_configuration.level_width,
|
||||
variable=self.configframe.slider_vars[
|
||||
self.configframe.slider_params.index("audibility")
|
||||
],
|
||||
@@ -422,8 +462,10 @@ class StripConfigFrameBuilder(ChannelConfigFrameBuilder):
|
||||
aud_scale.bind(
|
||||
"<Double-Button-1>", partial(self.configframe.reset_scale, "audibility", 0)
|
||||
)
|
||||
aud_scale.bind("<Button-1>", self.configframe.scale_enter)
|
||||
aud_scale.bind("<ButtonRelease-1>", self.configframe.scale_leave)
|
||||
aud_scale.bind("<Button-1>", self.configframe.scale_press)
|
||||
aud_scale.bind("<ButtonRelease-1>", self.configframe.scale_release)
|
||||
aud_scale.bind("<Enter>", partial(self.configframe.scale_enter, "audibility"))
|
||||
aud_scale.bind("<Leave>", self.configframe.scale_leave)
|
||||
|
||||
aud_label.grid(column=0, row=0)
|
||||
aud_scale.grid(column=1, row=0)
|
||||
@@ -477,11 +519,9 @@ class StripConfigFrameBuilder(ChannelConfigFrameBuilder):
|
||||
text=param,
|
||||
command=partial(self.configframe.toggle_p, param),
|
||||
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{param}.TButton'}",
|
||||
variable=self.configframe.param_vars[
|
||||
self.configframe.params.index(param)
|
||||
],
|
||||
variable=self.configframe.param_vars[i],
|
||||
)
|
||||
for param in self.configframe.params
|
||||
for i, param in enumerate(self.configframe.params)
|
||||
]
|
||||
[
|
||||
button.grid(
|
||||
@@ -540,11 +580,9 @@ class BusConfigFrameBuilder(ChannelConfigFrameBuilder):
|
||||
text=param,
|
||||
command=partial(self.configframe.toggle_p, param),
|
||||
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{param}.TButton'}",
|
||||
variable=self.configframe.param_vars[
|
||||
self.configframe.params.index(param)
|
||||
],
|
||||
variable=self.configframe.param_vars[i],
|
||||
)
|
||||
for param in self.configframe.params
|
||||
for i, param in enumerate(self.configframe.params)
|
||||
]
|
||||
[
|
||||
button.grid(
|
||||
|
||||
@@ -22,6 +22,7 @@ class ChannelLabelFrame(ttk.LabelFrame):
|
||||
self.builder.add_scale()
|
||||
self.builder.add_mute_button()
|
||||
self.builder.add_conf_button()
|
||||
self.builder.add_gain_label()
|
||||
self.sync()
|
||||
self.grid_configure()
|
||||
|
||||
@@ -49,7 +50,7 @@ class ChannelLabelFrame(ttk.LabelFrame):
|
||||
"""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.gainlabel.set(round(self.gain.get(), 1))
|
||||
|
||||
def toggle_mute(self, *args):
|
||||
self.target.mute = self.mute.get()
|
||||
@@ -62,13 +63,12 @@ class ChannelLabelFrame(ttk.LabelFrame):
|
||||
def reset_gain(self, *args):
|
||||
self.setter("gain", 0)
|
||||
self.gain.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))
|
||||
pass
|
||||
|
||||
def scale_leave(self, *args):
|
||||
self.parent.parent.nav_frame.info_text.set("")
|
||||
pass
|
||||
|
||||
def scale_press(self, *args):
|
||||
_base_values.in_scale_button_1 = True
|
||||
@@ -104,7 +104,22 @@ class ChannelLabelFrame(ttk.LabelFrame):
|
||||
)
|
||||
|
||||
def sync(self):
|
||||
"""sync parameters"""
|
||||
self.after(_base_values.pdelay, self.sync_params)
|
||||
self.after(100, self.sync_labels)
|
||||
|
||||
def sync_params(self):
|
||||
"""sync parameter states, update button colours"""
|
||||
self.gain.set(self.getter("gain"))
|
||||
self.gainlabel.set(round(self.gain.get(), 1))
|
||||
self.mute.set(self.getter("mute"))
|
||||
if not _configuration.themes_enabled:
|
||||
self.styletable.configure(
|
||||
f"{self.identifier}Mute{self.index}.TButton",
|
||||
background=f'{"red" if self.mute.get() else "white"}',
|
||||
)
|
||||
|
||||
def sync_labels(self):
|
||||
"""sync labelframes according to label text"""
|
||||
retval = self.getter("label")
|
||||
if len(retval) > 10:
|
||||
retval = f"{retval[:8]}.."
|
||||
@@ -116,13 +131,6 @@ class ChannelLabelFrame(ttk.LabelFrame):
|
||||
self.parent.parent.subject_ldirty.add(self)
|
||||
self.grid()
|
||||
self.configure(text=retval)
|
||||
self.gain.set(self.getter("gain"))
|
||||
self.mute.set(self.getter("mute"))
|
||||
if not _configuration.themes_enabled:
|
||||
self.styletable.configure(
|
||||
f"{self.identifier}Mute{self.index}.TButton",
|
||||
background=f'{"red" if self.mute.get() else "white"}',
|
||||
)
|
||||
|
||||
def convert_level(self, val):
|
||||
if _base_values.vban_connected:
|
||||
@@ -160,15 +168,31 @@ class Strip(ChannelLabelFrame):
|
||||
_target = super(Strip, self).target
|
||||
return getattr(_target, self.identifier)[self.index]
|
||||
|
||||
def update(self):
|
||||
def upd_levels(self):
|
||||
"""
|
||||
Updates level values.
|
||||
|
||||
Checks offset against expected level array size to avoid a race condition
|
||||
"""
|
||||
if self.level_offset + 1 < len(self.parent.parent.strip_levels):
|
||||
if any(
|
||||
self.parent.parent.strip_comp[self.level_offset : self.level_offset + 1]
|
||||
):
|
||||
val = self.convert_level(
|
||||
max(
|
||||
self.parent.parent.strip_levels[
|
||||
self.level_offset : self.level_offset + 1
|
||||
]
|
||||
)
|
||||
)
|
||||
self.level.set(
|
||||
(0 if self.mute.get() else 100 + val - 18 + self.gain.get())
|
||||
)
|
||||
|
||||
def on_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.mute.get() else 100 + (max(vals) - 18) + self.gain.get())
|
||||
)
|
||||
|
||||
self.after(_base_values.ldelay, self.upd_levels)
|
||||
|
||||
|
||||
class Bus(ChannelLabelFrame):
|
||||
@@ -185,14 +209,24 @@ class Bus(ChannelLabelFrame):
|
||||
_target = super(Bus, self).target
|
||||
return getattr(_target, self.identifier)[self.index]
|
||||
|
||||
def update(self):
|
||||
def upd_levels(self):
|
||||
if self.level_offset + 1 < len(self.parent.parent.bus_levels):
|
||||
if any(
|
||||
self.parent.parent.bus_comp[self.level_offset : self.level_offset + 1]
|
||||
):
|
||||
val = self.convert_level(
|
||||
max(
|
||||
self.parent.parent.bus_levels[
|
||||
self.level_offset : self.level_offset + 1
|
||||
]
|
||||
)
|
||||
)
|
||||
self.level.set((0 if self.mute.get() else 100 + val - 18))
|
||||
|
||||
def on_update(self):
|
||||
"""update levels"""
|
||||
|
||||
vals = (
|
||||
self.convert_level(self.parent.target.bus_levels[self.level_offset]),
|
||||
self.convert_level(self.parent.target.bus_levels[self.level_offset + 1]),
|
||||
)
|
||||
self.level.set((0 if self.mute.get() else 100 + (max(vals) - 18)))
|
||||
self.after(_base_values.ldelay, self.upd_levels)
|
||||
|
||||
|
||||
class ChannelFrame(ttk.Frame):
|
||||
@@ -231,20 +265,23 @@ class ChannelFrame(ttk.Frame):
|
||||
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=100) for i, _ in enumerate(self.labelframes)]
|
||||
|
||||
def upd_labelframe(self, labelframe):
|
||||
labelframe.sync()
|
||||
|
||||
def on_update(self):
|
||||
"""update parameters"""
|
||||
|
||||
def update(self):
|
||||
for labelframe in self.labelframes:
|
||||
labelframe.sync()
|
||||
self.after(1, self.upd_labelframe, labelframe)
|
||||
|
||||
def teardown(self):
|
||||
# deregisters channelframe as pdirty observer
|
||||
|
||||
self.parent.subject_pdirty.remove(self)
|
||||
self.destroy()
|
||||
setattr(self.parent, f"{self.identifier}_frame", None)
|
||||
|
||||
|
||||
def _make_channelframe(parent, id):
|
||||
@@ -280,7 +317,7 @@ def _make_channelframe(parent, id):
|
||||
self.buses = tuple(Bus(self, i, id) for i in range(phys_out + virt_out))
|
||||
if _configuration.extended:
|
||||
if _configuration.extends_horizontal:
|
||||
self.grid(row=0, column=2)
|
||||
self.grid(row=0, column=2, sticky=(tk.W))
|
||||
else:
|
||||
self.grid(row=2, column=0, sticky=(tk.W))
|
||||
else:
|
||||
|
||||
@@ -29,16 +29,24 @@ class Config(ttk.Frame):
|
||||
return self.parent.target
|
||||
|
||||
def getter(self, param):
|
||||
return getattr(self.target, param)
|
||||
if param in dir(self.target):
|
||||
return getattr(self.target, param)
|
||||
|
||||
def setter(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
if param in dir(self.target):
|
||||
setattr(self.target, param, value)
|
||||
|
||||
def scale_enter(self, *args):
|
||||
def scale_press(self, *args):
|
||||
_base_values.in_scale_button_1 = True
|
||||
|
||||
def scale_leave(self, *args):
|
||||
def scale_release(self, *args):
|
||||
_base_values.in_scale_button_1 = False
|
||||
|
||||
def scale_enter(self, param, *args):
|
||||
val = self.slider_vars[self.slider_params.index(param)].get()
|
||||
self.parent.nav_frame.info_text.set(round(val, 1))
|
||||
|
||||
def scale_leave(self, *args):
|
||||
self.parent.nav_frame.info_text.set("")
|
||||
|
||||
def scale_callback(self, param, *args):
|
||||
@@ -60,14 +68,16 @@ class Config(ttk.Frame):
|
||||
f"{param}.TButton", background=f'{"green" if val else "white"}'
|
||||
)
|
||||
|
||||
def update(self):
|
||||
self.sync()
|
||||
def on_update(self):
|
||||
"""update parameters"""
|
||||
|
||||
self.after(_base_values.pdelay, self.sync)
|
||||
|
||||
|
||||
class StripConfig(Config):
|
||||
def __init__(self, parent, index, _id):
|
||||
super().__init__(parent, index, _id)
|
||||
self.grid(column=0, row=1, columnspan=4)
|
||||
self.grid(column=0, row=1, columnspan=4, padx=(2,))
|
||||
self.builder = builders.StripConfigFrameBuilder(self)
|
||||
self.builder.setup()
|
||||
self.make_row_0()
|
||||
@@ -121,43 +131,39 @@ class StripConfig(Config):
|
||||
|
||||
def sync(self):
|
||||
[
|
||||
self.phys_out_params_vars[self.phys_out_params.index(param)].set(
|
||||
self.getter(param)
|
||||
)
|
||||
for param in self.phys_out_params
|
||||
self.phys_out_params_vars[i].set(self.getter(param))
|
||||
for i, param in enumerate(self.phys_out_params)
|
||||
]
|
||||
[
|
||||
self.virt_out_params_vars[self.virt_out_params.index(param)].set(
|
||||
self.getter(param)
|
||||
)
|
||||
for param in self.virt_out_params
|
||||
self.virt_out_params_vars[i].set(self.getter(param))
|
||||
for i, param in enumerate(self.virt_out_params)
|
||||
]
|
||||
[
|
||||
self.param_vars[self.params.index(param)].set(self.getter(param))
|
||||
for param in self.params
|
||||
self.param_vars[i].set(self.getter(param))
|
||||
for i, param in enumerate(self.params)
|
||||
]
|
||||
|
||||
if not _configuration.themes_enabled:
|
||||
[
|
||||
self.styletable.configure(
|
||||
f"{param}.TButton",
|
||||
background=f'{"green" if self.phys_out_params_vars[self.phys_out_params.index(param)].get() else "white"}',
|
||||
background=f'{"green" if self.phys_out_params_vars[i].get() else "white"}',
|
||||
)
|
||||
for param in self.phys_out_params
|
||||
for i, param in enumerate(self.phys_out_params)
|
||||
]
|
||||
[
|
||||
self.styletable.configure(
|
||||
f"{param}.TButton",
|
||||
background=f'{"green" if self.virt_out_params_vars[self.virt_out_params.index(param)].get() else "white"}',
|
||||
background=f'{"green" if self.virt_out_params_vars[i].get() else "white"}',
|
||||
)
|
||||
for param in self.virt_out_params
|
||||
for i, param in enumerate(self.virt_out_params)
|
||||
]
|
||||
[
|
||||
self.styletable.configure(
|
||||
f"{param}.TButton",
|
||||
background=f'{"green" if self.param_vars[self.params.index(param)].get() else "white"}',
|
||||
background=f'{"green" if self.param_vars[i].get() else "white"}',
|
||||
)
|
||||
for param in self.params
|
||||
for i, param in enumerate(self.params)
|
||||
]
|
||||
|
||||
|
||||
@@ -165,9 +171,9 @@ class BusConfig(Config):
|
||||
def __init__(self, parent, index, _id):
|
||||
super().__init__(parent, index, _id)
|
||||
if _configuration.extends_horizontal:
|
||||
self.grid(column=0, row=1, columnspan=4)
|
||||
self.grid(column=0, row=1, columnspan=4, padx=(2,))
|
||||
else:
|
||||
self.grid(column=0, row=3, columnspan=4)
|
||||
self.grid(column=0, row=3, columnspan=4, padx=(2,))
|
||||
self.builder = builders.BusConfigFrameBuilder(self)
|
||||
self.builder.setup()
|
||||
self.make_row_0()
|
||||
@@ -227,19 +233,15 @@ class BusConfig(Config):
|
||||
|
||||
def sync(self):
|
||||
[
|
||||
self.param_vars[self.params.index(param)].set(self.getter(param))
|
||||
for param in self.params
|
||||
self.param_vars[i].set(self.getter(param))
|
||||
for i, param in enumerate(self.params)
|
||||
]
|
||||
self.bus_mode_label_text.set(self.bus_mode_map[self.current_bus_mode()])
|
||||
if not _configuration.themes_enabled:
|
||||
[
|
||||
self.styletable.configure(
|
||||
f"{param}.TButton",
|
||||
background=f'{"green" if self.param_vars[self.params.index(param)].get() else "white"}',
|
||||
background=f'{"green" if self.param_vars[i].get() else "white"}',
|
||||
)
|
||||
for param in self.params
|
||||
for i, param in enumerate(self.params)
|
||||
]
|
||||
|
||||
|
||||
class Iterator:
|
||||
pass
|
||||
|
||||
@@ -19,9 +19,9 @@ class SingletonMeta(type):
|
||||
@dataclass
|
||||
class Configurations(metaclass=SingletonMeta):
|
||||
# width of a single labelframe
|
||||
level_width: int = 75
|
||||
level_width: int = configuration["channel"]["width"]
|
||||
# height of a single labelframe
|
||||
level_height: int = 100
|
||||
level_height: int = configuration["channel"]["height"]
|
||||
|
||||
# is the gui extended
|
||||
extended: bool = configuration["extends"]["extended"]
|
||||
@@ -53,11 +53,7 @@ class BaseValues(metaclass=SingletonMeta):
|
||||
# pdirty delay
|
||||
pdelay: int = 5
|
||||
# ldirty delay
|
||||
ldelay: int = 50
|
||||
# size of strip level array for a kind
|
||||
strip_level_array_size: int = None
|
||||
# size of bus level array for a kind
|
||||
bus_level_array_size: int = None
|
||||
ldelay: int = 5
|
||||
|
||||
|
||||
_base_values = BaseValues()
|
||||
|
||||
@@ -2,8 +2,8 @@ import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from math import log
|
||||
|
||||
from .data import _base_values, _configuration
|
||||
from . import builders
|
||||
from .data import _base_values, _configuration
|
||||
|
||||
|
||||
class GainLayer(ttk.LabelFrame):
|
||||
@@ -35,6 +35,10 @@ class GainLayer(ttk.LabelFrame):
|
||||
_target = self.parent.target
|
||||
return _target.strip[self.index].gainlayer[self.j]
|
||||
|
||||
@property
|
||||
def identifier(self):
|
||||
return "gainlayer"
|
||||
|
||||
def getter(self, param):
|
||||
if param in dir(self.target):
|
||||
return getattr(self.target, param)
|
||||
@@ -92,10 +96,78 @@ class GainLayer(ttk.LabelFrame):
|
||||
)
|
||||
if not _configuration.themes_enabled:
|
||||
self.styletable.configure(
|
||||
f"On.TButton",
|
||||
f"{self.identifier}On{self.index}.TButton",
|
||||
background=f'{"green" if self.on.get() else "white"}',
|
||||
)
|
||||
|
||||
def sync(self):
|
||||
self.after(_base_values.pdelay, self.sync_params)
|
||||
self.after(100, self.sync_labels)
|
||||
|
||||
def sync_params(self):
|
||||
self.gain.set(self.getter("gain"))
|
||||
self.on.set(
|
||||
getattr(
|
||||
self.parent.target.strip[self.index],
|
||||
self.parent.buses[self.j],
|
||||
)
|
||||
)
|
||||
if not _configuration.themes_enabled:
|
||||
self.styletable.configure(
|
||||
f"{self.identifier}On{self.index}.TButton",
|
||||
background=f'{"green" if self.on.get() else "white"}',
|
||||
)
|
||||
|
||||
def sync_labels(self):
|
||||
"""sync params with voicemeeter"""
|
||||
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)
|
||||
|
||||
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 upd_levels(self):
|
||||
"""
|
||||
Updates level values.
|
||||
|
||||
Checks offset against expected level array size to avoid a race condition
|
||||
"""
|
||||
if self.level_offset + 1 < len(self.parent.parent.strip_levels):
|
||||
if any(
|
||||
self.parent.parent.strip_comp[self.level_offset : self.level_offset + 1]
|
||||
):
|
||||
val = self.convert_level(
|
||||
max(
|
||||
self.parent.parent.strip_levels[
|
||||
self.level_offset : self.level_offset + 1
|
||||
]
|
||||
)
|
||||
)
|
||||
self.level.set(
|
||||
(
|
||||
0
|
||||
if self.parent.target.strip[self.index].mute
|
||||
or not self.on.get()
|
||||
else 100 + val - 18 + self.gain.get()
|
||||
)
|
||||
)
|
||||
|
||||
def on_update(self):
|
||||
"""update levels"""
|
||||
|
||||
self.after(_base_values.ldelay, self.upd_levels)
|
||||
|
||||
def grid_configure(self):
|
||||
[
|
||||
child.grid_configure(padx=1, pady=1, sticky=(tk.N, tk.S, tk.W, tk.E))
|
||||
@@ -116,47 +188,6 @@ class GainLayer(ttk.LabelFrame):
|
||||
else:
|
||||
self.rowconfigure(1, minsize=55)
|
||||
|
||||
def sync(self):
|
||||
"""sync params with voicemeeter"""
|
||||
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],
|
||||
)
|
||||
)
|
||||
|
||||
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 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):
|
||||
@@ -182,13 +213,27 @@ class SubMixFrame(ttk.Frame):
|
||||
if parent.bus_frame:
|
||||
parent.bus_frame.grid_remove()
|
||||
else:
|
||||
self._parent.submix_frame.grid(row=2, column=0)
|
||||
if parent.bus_frame:
|
||||
self.grid(
|
||||
row=parent.bus_frame.grid_info()["row"], column=0, sticky=(tk.W)
|
||||
)
|
||||
parent.bus_frame.grid_remove()
|
||||
else:
|
||||
self.grid(row=2, column=0, sticky=(tk.W))
|
||||
|
||||
# registers submixframe as pdirty observer
|
||||
self.parent.subject_pdirty.add(self)
|
||||
|
||||
self.grid_configure()
|
||||
"""
|
||||
Grids each labelframe, grid_removes any without a label
|
||||
"""
|
||||
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)
|
||||
labelframe.grid_remove()
|
||||
|
||||
@property
|
||||
def target(self):
|
||||
"""returns the current interface"""
|
||||
@@ -215,9 +260,12 @@ class SubMixFrame(ttk.Frame):
|
||||
for i, _ in enumerate(self.labelframes)
|
||||
]
|
||||
|
||||
def update(self):
|
||||
def upd_labelframe(self, labelframe):
|
||||
labelframe.sync()
|
||||
|
||||
def on_update(self):
|
||||
for labelframe in self.labelframes:
|
||||
labelframe.sync()
|
||||
self.after(1, self.upd_labelframe, labelframe)
|
||||
|
||||
def teardown(self):
|
||||
# deregisters submixframe as pdirty observer
|
||||
|
||||
@@ -21,6 +21,8 @@ class Menus(tk.Menu):
|
||||
self.vban_config = get_configuration("vban")
|
||||
self.app_config = get_configuration("app")
|
||||
self._is_topmost = tk.BooleanVar()
|
||||
self._lock = tk.BooleanVar()
|
||||
self._unlock = tk.BooleanVar()
|
||||
self._selected_bus = list(tk.BooleanVar() for _ in range(8))
|
||||
|
||||
# voicemeeter menu
|
||||
@@ -59,11 +61,19 @@ class Menus(tk.Menu):
|
||||
self.menu_voicemeeter.add_cascade(
|
||||
menu=self.menu_lock, label="GUI Lock", underline=0
|
||||
)
|
||||
self.menu_lock.add_command(
|
||||
label="Lock", command=partial(self.action_set_voicemeeter, "lock")
|
||||
self.menu_lock.add_checkbutton(
|
||||
label="Lock",
|
||||
onvalue=1,
|
||||
offvalue=0,
|
||||
variable=self._lock,
|
||||
command=partial(self.action_set_voicemeeter, "lock"),
|
||||
)
|
||||
self.menu_lock.add_command(
|
||||
label="Unlock", command=partial(self.action_set_voicemeeter, "lock", False)
|
||||
self.menu_lock.add_checkbutton(
|
||||
label="Unlock",
|
||||
onvalue=1,
|
||||
offvalue=0,
|
||||
variable=self._unlock,
|
||||
command=partial(self.action_set_voicemeeter, "lock", False),
|
||||
)
|
||||
|
||||
# profiles menu
|
||||
@@ -140,7 +150,7 @@ class Menus(tk.Menu):
|
||||
state="disabled",
|
||||
)
|
||||
if not _configuration.themes_enabled:
|
||||
self.entryconfig(6, state="disabled")
|
||||
self.menu_layout.entryconfig(2, state="disabled")
|
||||
|
||||
# vban connect menu
|
||||
self.menu_vban = tk.Menu(self, tearoff=0)
|
||||
@@ -162,7 +172,7 @@ class Menus(tk.Menu):
|
||||
)
|
||||
target_menu.entryconfig(1, state="disabled")
|
||||
else:
|
||||
self.entryconfig(3, state="disabled")
|
||||
self.entryconfig(4, state="disabled")
|
||||
|
||||
# Help menu
|
||||
self.menu_help = tk.Menu(self, tearoff=0)
|
||||
@@ -195,6 +205,9 @@ class Menus(tk.Menu):
|
||||
getattr(self.target.command, cmd)()
|
||||
|
||||
def action_set_voicemeeter(self, cmd, val=True):
|
||||
if cmd == "lock":
|
||||
self._lock.set(val)
|
||||
self._unlock.set(not self._lock.get())
|
||||
setattr(self.target.command, cmd, val)
|
||||
|
||||
def load_profile(self, profile):
|
||||
@@ -226,11 +239,11 @@ class Menus(tk.Menu):
|
||||
self.parent.submix_frame.teardown()
|
||||
self.parent.nav_frame.show_submix()
|
||||
for j, var in enumerate(self._selected_bus):
|
||||
var.set(True if i == j else False)
|
||||
var.set(i == j)
|
||||
|
||||
def load_theme(self, theme):
|
||||
sv_ttk.set_theme(theme)
|
||||
self.app_config["theme"]["mode"] = theme
|
||||
_configuration.theme_mode = theme
|
||||
self.menu_themes.entryconfig(
|
||||
0,
|
||||
state=f"{'disabled' if theme == 'light' else 'normal'}",
|
||||
@@ -273,6 +286,7 @@ class Menus(tk.Menu):
|
||||
# destroy the current App frames
|
||||
self.parent._destroy_top_level_frames()
|
||||
_base_values.vban_connected = True
|
||||
self.vmr.end_thread()
|
||||
# build new app frames according to a kind
|
||||
kind = kind_get(kind_id)
|
||||
self.parent.build_app(kind, self.vban)
|
||||
@@ -288,6 +302,7 @@ class Menus(tk.Menu):
|
||||
self.parent._destroy_top_level_frames()
|
||||
_base_values.vban_connected = False
|
||||
# logout of vban interface
|
||||
self.vmr.init_thread()
|
||||
self.vban.logout()
|
||||
# build new app frames according to a kind
|
||||
kind = kind_get(self.vmr.type)
|
||||
|
||||
@@ -10,7 +10,7 @@ class Navigation(ttk.Frame):
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
self.parent = parent
|
||||
self.grid(row=0, column=3, sticky=(tk.W, tk.E))
|
||||
self.grid(row=0, column=3, padx=(0, 5), pady=(5, 5), sticky=(tk.W, tk.E))
|
||||
self.styletable = self.parent.styletable
|
||||
|
||||
self.builder = builders.NavigationFrameBuilder(self)
|
||||
|
||||
@@ -5,10 +5,10 @@ class Subject:
|
||||
self._observables = []
|
||||
|
||||
def notify(self, modifier=None):
|
||||
"""Alert the observers"""
|
||||
"""run callbacks on update"""
|
||||
|
||||
for observer in self._observables:
|
||||
observer.update()
|
||||
observer.on_update()
|
||||
|
||||
def add(self, observer):
|
||||
"""adds an observer to observables"""
|
||||
@@ -26,7 +26,10 @@ class Subject:
|
||||
|
||||
def get(self) -> list:
|
||||
"""returns the current observables"""
|
||||
|
||||
return self._observables
|
||||
|
||||
def clear(self):
|
||||
"""clears the observables list"""
|
||||
|
||||
self._observables.clear()
|
||||
|
||||
Reference in New Issue
Block a user