voicemeeter-compact/vmcompact/config.py
onyx-and-iris 14a43770cd ldirty, pdelay, ldelay added. changes to how levels are fetched
ldirty added, true iff level value has changed.

fetching level arrays moved to highest level of the app.

pdelay and ldelay added to _base_vals dataclass.
2022-04-13 06:50:49 +01:00

459 lines
16 KiB
Python

import tkinter as tk
from tkinter import ttk
from functools import partial
from .data import _base_vals
class Config(ttk.Frame):
def __init__(self, parent, index, _id):
super().__init__(parent)
self._parent = parent
self.index = index
self.id = _id
self.s = parent.styletable
self.phys_in, self.virt_in = parent.kind.ins
self.phys_out, self.virt_out = parent.kind.outs
self.watch_pdirty()
@property
def identifier(self):
return self.id
@property
def target(self):
"""returns the current interface"""
return self._parent.target
def getter(self, param):
if param in dir(self.target):
return getattr(self.target, param)
def setter(self, param, value):
if param in dir(self.target):
setattr(self.target, param, value)
def scale_enter(self, *args):
_base_vals.in_scale_button_1 = True
def scale_leave(self, *args):
_base_vals.in_scale_button_1 = False
self._parent.nav_frame.info_text.set("")
def scale_callback(self, param, *args):
"""callback function for scale widget"""
val = self.slider_vars[self.slider_params.index(param)].get()
self.setter(param, val)
self._parent.nav_frame.info_text.set(round(val, 1))
def reset_scale(self, param, val, *args):
self.setter(param, val)
self.slider_vars[self.slider_params.index(param)].set(val)
def col_row_configure(self):
[
child.grid_configure(padx=1, pady=1, sticky=(tk.W, tk.E))
for child in self.winfo_children()
if isinstance(child, ttk.Checkbutton)
]
self.grid(sticky=(tk.W))
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.pdirty and not _base_vals.in_scale_button_1:
self.sync()
self.after(_base_vals.pdelay, self.watch_pdirty_step)
class StripConfig(Config):
def __init__(self, parent, index, _id):
super().__init__(parent, index, _id)
# create parameter variables
if self._parent.kind.name == "Basic":
self.slider_params = ("audibility",)
self.slider_vars = (tk.DoubleVar(),)
else:
self.slider_params = ("comp", "gate", "limit")
self.slider_vars = [
tk.DoubleVar() for i, _ in enumerate(self.slider_params)
]
self.phys_out_params = [f"A{i+1}" for i in range(self.phys_out)]
self.phys_out_params_vars = [
tk.BooleanVar() for i, _ in enumerate(self.phys_out_params)
]
self.virt_out_params = [f"B{i+1}" for i in range(self.virt_out)]
self.virt_out_params_vars = [
tk.BooleanVar() for i, _ in enumerate(self.virt_out_params)
]
self.params = ("mono", "solo")
self.param_vars = list(tk.BooleanVar() for i, _ in enumerate(self.params))
self.make_row0()
self.make_row1()
self.make_row2()
# sync all parameters
self.sync()
self.sync_sliders()
self.col_row_configure()
@property
def target(self):
"""use the correct interface"""
_target = super(StripConfig, self).target
return getattr(_target, self.identifier)[self.index]
def make_row0(self):
# Create sliders
if self.index < self.phys_in:
if self._parent.kind.name == "Basic":
# audibility
aud_label = ttk.Label(self, text="Audibility")
aud_scale = ttk.Scale(
self,
from_=0.0,
to=10.0,
orient="horizontal",
length=_base_vals.level_width,
variable=self.slider_vars[self.slider_params.index("audibility")],
command=partial(self.scale_callback, "audibility"),
)
aud_scale.bind(
"<Double-Button-1>", partial(self.reset_scale, "audibility", 0)
)
aud_scale.bind("<Button-1>", self.scale_enter)
aud_scale.bind("<ButtonRelease-1>", self.scale_leave)
aud_label.grid(column=0, row=0)
aud_scale.grid(column=1, row=0)
else:
# comp
comp_label = ttk.Label(self, text="Comp")
comp_scale = ttk.Scale(
self,
from_=0.0,
to=10.0,
orient="horizontal",
length=_base_vals.level_width,
variable=self.slider_vars[self.slider_params.index("comp")],
command=partial(self.scale_callback, "comp"),
)
comp_scale.bind(
"<Double-Button-1>", partial(self.reset_scale, "comp", 0)
)
comp_scale.bind("<Button-1>", self.scale_enter)
comp_scale.bind("<ButtonRelease-1>", self.scale_leave)
# gate
gate_label = ttk.Label(self, text="Gate")
gate_scale = ttk.Scale(
self,
from_=0.0,
to=10.0,
orient="horizontal",
length=_base_vals.level_width,
variable=self.slider_vars[self.slider_params.index("gate")],
command=partial(self.scale_callback, "gate"),
)
gate_scale.bind(
"<Double-Button-1>", partial(self.reset_scale, "gate", 0)
)
gate_scale.bind("<Button-1>", self.scale_enter)
gate_scale.bind("<ButtonRelease-1>", self.scale_leave)
# limit
limit_label = ttk.Label(self, text="Limit")
limit_scale = ttk.Scale(
self,
from_=-40,
to=12,
orient="horizontal",
length=_base_vals.level_width,
variable=self.slider_vars[self.slider_params.index("limit")],
command=partial(self.scale_callback, "limit"),
)
limit_scale.bind(
"<Double-Button-1>", partial(self.reset_scale, "limit", 12)
)
limit_scale.bind("<Button-1>", self.scale_enter)
limit_scale.bind("<ButtonRelease-1>", self.scale_leave)
# Position sliders
comp_label.grid(column=0, row=0)
comp_scale.grid(column=1, row=0)
gate_label.grid(column=2, row=0)
gate_scale.grid(column=3, row=0)
limit_label.grid(column=4, row=0)
limit_scale.grid(column=5, row=0)
def make_row1(self):
# create buttons
self.a_buttons = [
ttk.Checkbutton(
self,
text=param,
command=partial(self.toggle_a, param),
style=f"{'Toggle.TButton' if _base_vals.using_theme else f'{param}.TButton'}",
variable=self.phys_out_params_vars[self.phys_out_params.index(param)],
)
for param in self.phys_out_params
]
self.b_buttons = [
ttk.Checkbutton(
self,
text=param,
command=partial(self.toggle_b, param),
style=f"{'Toggle.TButton' if _base_vals.using_theme else f'{param}.TButton'}",
variable=self.virt_out_params_vars[self.virt_out_params.index(param)],
)
for param in self.virt_out_params
]
# set button positions
[
button.grid(
column=self.a_buttons.index(button),
row=1,
)
for button in self.a_buttons
]
[
button.grid(
column=len(self.a_buttons) + self.b_buttons.index(button),
row=1,
)
for button in self.b_buttons
]
def toggle_a(self, param):
val = self.phys_out_params_vars[self.phys_out_params.index(param)].get()
self.setter(param, val)
if not _base_vals.using_theme:
self.s.configure(
f"{param}.TButton", background=f'{"green" if val else "white"}'
)
def toggle_b(self, param):
val = self.virt_out_params_vars[self.virt_out_params.index(param)].get()
self.setter(param, val)
if not _base_vals.using_theme:
self.s.configure(
f"{param}.TButton", background=f'{"green" if val else "white"}'
)
def make_row2(self):
if self._parent.kind.name in ("Banana", "Potato"):
if self.index == self.phys_in:
self.params = list(map(lambda x: x.replace("mono", "mc"), self.params))
if self._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.index == self.phys_in + self.virt_in - 1:
self.params = list(
map(lambda x: x.replace("mono", "mc"), self.params)
)
param_buttons = [
ttk.Checkbutton(
self,
text=param,
command=partial(self.toggle_p, param),
style=f"{'Toggle.TButton' if _base_vals.using_theme else f'{param}.TButton'}",
variable=self.param_vars[self.params.index(param)],
)
for param in self.params
]
[
button.grid(
column=param_buttons.index(button),
row=2,
)
for button in param_buttons
]
def toggle_p(self, param):
val = self.param_vars[self.params.index(param)].get()
self.setter(param, val)
if not _base_vals.using_theme:
self.s.configure(
f"{param}.TButton", background=f'{"green" if val else "white"}'
)
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.virt_out_params_vars[self.virt_out_params.index(param)].set(
self.getter(param)
)
for param in self.virt_out_params
]
[
self.param_vars[self.params.index(param)].set(self.getter(param))
for param in self.params
]
if not _base_vals.using_theme:
[
self.s.configure(
f"{param}.TButton",
background=f'{"green" if self.phys_out_params_vars[self.phys_out_params.index(param)].get() else "white"}',
)
for param in self.phys_out_params
]
[
self.s.configure(
f"{param}.TButton",
background=f'{"green" if self.virt_out_params_vars[self.virt_out_params.index(param)].get() else "white"}',
)
for param in self.virt_out_params
]
[
self.s.configure(
f"{param}.TButton",
background=f'{"green" if self.param_vars[self.params.index(param)].get() else "white"}',
)
for param in self.params
]
def sync_sliders(self):
[
self.slider_vars[self.slider_params.index(param)].set(self.getter(param))
for param in self.slider_params
]
def col_row_configure(self):
super(StripConfig, self).col_row_configure()
[
self.columnconfigure(i, minsize=80)
for i in range(self.phys_out + self.virt_out)
]
class BusConfig(Config):
def __init__(self, parent, index, _id):
super().__init__(parent, index, _id)
# fmt: off
# create parameter variables
self.bus_modes = (
"normal", "Amix", "Bmix", "Repeat", "Composite", "TVMix", "UpMix21",
"UpMix41", "UpMix61", "CenterOnly", "LFEOnly", "RearOnly",
)
# fmt: on
self.params = ("mono", "eq", "eq_ab")
self.param_vars = [tk.BooleanVar() for i, _ in enumerate(self.params)]
self.make_row0()
self.make_row1()
# sync all parameters
self.sync()
self.col_row_configure()
@property
def target(self):
"""returns the current interface"""
_target = super(BusConfig, self).target
return getattr(_target, self.identifier)[self.index]
def make_row0(self):
self._cur_bus_mode = tk.StringVar()
self._cur_bus_mode.set(f"Bus Mode: {self.bus_modes[self.get_current_index()]}")
self.busmode_button = ttk.Button(self, textvariable=self._cur_bus_mode)
self.busmode_button.grid(column=0, row=0, columnspan=2, sticky=(tk.W))
self.busmode_button.bind("<Button-1>", self.rotate_bus_modes_right)
self.busmode_button.bind("<Button-3>", self.rotate_bus_modes_left)
def get_current_index(self):
for mode in self.bus_modes:
if getattr(self.target.mode, mode.lower()):
return self.bus_modes.index(mode)
def rotate_bus_modes_right(self, *args):
current_index = self.get_current_index()
if current_index + 1 < len(self.bus_modes):
next = self.bus_modes[current_index + 1]
else:
next = self.bus_modes[0]
setattr(self.target.mode, next.lower(), True)
self._cur_bus_mode.set(f"Bus Mode: {next}")
def rotate_bus_modes_left(self, *args):
current_index = self.get_current_index()
if current_index == 0:
next = self.bus_modes[-1]
else:
next = self.bus_modes[current_index - 1]
setattr(self.target.mode, next.lower(), True)
self._cur_bus_mode.set(f"Bus Mode: {next}")
def make_row1(self):
param_buttons = [
ttk.Checkbutton(
self,
text=param,
command=partial(self.toggle_p, param),
style=f"{'Toggle.TButton' if _base_vals.using_theme else f'{param}.TButton'}",
variable=self.param_vars[self.params.index(param)],
)
for param in self.params
]
[
button.grid(
column=param_buttons.index(button),
row=1,
)
for button in param_buttons
]
def toggle_p(self, param):
val = self.param_vars[self.params.index(param)].get()
self.setter(param, val)
if not _base_vals.using_theme:
self.s.configure(
f"{param}.TButton", background=f'{"green" if val else "white"}'
)
def col_row_configure(self):
super(BusConfig, self).col_row_configure()
[
self.columnconfigure(i, minsize=80)
for i in range(self.phys_out + self.virt_out)
]
def sync(self):
[
self.param_vars[self.params.index(param)].set(self.getter(param))
for param in self.params
]
if not _base_vals.using_theme:
[
self.s.configure(
f"{param}.TButton",
background=f'{"green" if self.param_vars[self.params.index(param)].get() else "white"}',
)
for param in self.params
]