vban-cmd-python/examples/gui/__main__.py
onyx-and-iris b076aaa393 Update __main__.py
update example/gui to reflect changes to interface
2022-06-16 16:25:18 +01:00

160 lines
4.6 KiB
Python

import tkinter as tk
from functools import partial
from tkinter import ttk
from typing import NamedTuple
import vban_cmd
from vban_cmd import kinds
class ExampleAppErrors(Exception):
pass
class App(tk.Tk):
"""Topmost Level of App"""
@classmethod
def make(cls, kind: NamedTuple):
"""
Factory function for App
Returns an App class of a kind
"""
APP_cls = type(
f"App{kind.name}",
(cls,),
{
"name": kind.name,
"ins": kind.ins,
"outs": kind.outs,
},
)
return APP_cls
def __init__(self):
super().__init__()
self.title(f"Voicemeeter{self.name} Example Program")
self.phys_in, self.virt_in = self.ins
self.col = self.phys_in + self.virt_in
self.row = 3
self.w = {"basic": 300, "banana": 600, "potato": 800}
self.h = 150
self.defaultsizes = {
"basic": f"{self.w[self.name]}x{self.h}",
"banana": f"{self.w[self.name]}x{self.h}",
"potato": f"{self.w[self.name]}x{self.h}",
}
self.geometry(self.defaultsizes[self.name])
""" create tkinter variables, generate widgets and configure rows/cols """
self.gains = {
"strip": [tk.DoubleVar() for i in range(self.phys_in + self.virt_in)],
}
self.levels = {
"strip": [tk.DoubleVar() for i in range(self.phys_in + self.virt_in)],
}
[
self._make_single_channel(i, j)
for i, j in enumerate(i for i in range(0, self.col * 2, 2))
]
scales = [
widget for widget in self.winfo_children() if isinstance(widget, tk.Scale)
]
[
scale.bind("<Double-Button-1>", partial(self.reset_gain, index=i))
for i, scale in enumerate(scales)
]
""" configure grid """
self.col_row_configure()
""" initiate watchers """
[self.watch_levels(i) for i in range(self.col)]
@property
def id_(self):
return "strip"
def _make_single_channel(self, i, j):
"""
Creates a label, progressbar, scale, and mute
"""
ttk.Label(self, text=f"{vban.strip[i].label}").grid(
column=j, row=0, columnspan=2
)
ttk.Progressbar(
self,
maximum=72,
orient="vertical",
mode="determinate",
variable=self.levels[self.id_][i],
).grid(column=j, row=1)
ttk.Scale(
self,
from_=12.0,
to=-60.0,
orient="vertical",
variable=self.gains[self.id_][i],
command=partial(self.scale_callback, index=i),
).grid(column=j + 1, row=1)
ttk.Button(
self,
text="MUTE",
command=partial(self.toggle, "mute", i),
style=f"Mute{i}.TButton",
).grid(column=j, row=2, columnspan=2, sticky=(tk.W, tk.E))
def scale_callback(self, *args, index=None):
"""callback function for scale widgets"""
vban.strip[index].gain = self.gains[self.id_][index].get()
def reset_gain(self, *args, index=None):
"""reset gain to 0 when double click mouse"""
vban.strip[index].gain = 0
self.gains[self.id_][index].set(0)
def toggle(self, param, index):
"""toggles a strip parameter"""
setattr(vban.strip[index], param, not getattr(vban.strip[index], param))
def col_row_configure(self):
[self.columnconfigure(i, weight=1) for i in range(self.col * 2)]
[child.grid_configure(padx=1, pady=1) for child in self.winfo_children()]
def watch_levels(self, i):
self.after(1, self.watch_levels_step, i)
def watch_levels_step(self, i):
val = vban.strip[i].levels.prefader[0] + vban.strip[i].gain
self.levels[self.id_][i].set((0 if vban.strip[i].mute else 100 + (val - 30)))
self.after(20, self.watch_levels_step, i)
_apps = {kind.name: App.make(kind) for kind in kinds.kinds_all}
def connect(kind_id: str) -> App:
"""return App of the kind requested"""
try:
APP_cls = _apps[kind_id]
return APP_cls()
except KeyError:
raise ExampleAppErrors(f"Invalid kind: {kind_id}")
if __name__ == "__main__":
kind_id = "potato"
opts = {
# make sure VBAN is configured on remote machine then set IP accordingly
"ip": "ws.local",
"streamname": "workstation",
"port": 6990,
}
with vban_cmd.api(kind_id, **opts) as vban:
app = connect(kind_id)
app.mainloop()