voicemeeter-compact/vmcompact/app.py

143 lines
3.8 KiB
Python
Raw Permalink Normal View History

2022-04-11 18:35:28 +01:00
import tkinter as tk
from pathlib import Path
2022-04-11 18:35:28 +01:00
from tkinter import ttk
from typing import NamedTuple
from .builders import MainFrameBuilder
from .data import _base_values, _configuration, _kinds_all
from .errors import VMCompactErrors
2022-04-11 18:35:28 +01:00
from .menu import Menus
from .subject import Subject
2022-04-11 18:35:28 +01:00
class App(tk.Tk):
"""App mainframe"""
2022-04-11 18:35:28 +01:00
@classmethod
def make(cls, kind: NamedTuple):
"""
Factory function for App.
2022-04-11 18:35:28 +01:00
Returns an App class of a kind.
2022-04-11 18:35:28 +01:00
"""
2022-04-11 18:35:28 +01:00
APP_cls = type(
f"Voicemeeter{kind}.Compact",
2022-04-11 18:35:28 +01:00
(cls,),
{
"kind": kind,
},
)
return APP_cls
def __init__(self, vmr):
super().__init__()
self._vmr = vmr
self._vmr.event.add("ldirty")
self._vmr.event.remove("mdirty")
self._vmr.event.remove("midi")
icon_path = Path(__file__).parent.resolve() / "img" / "cat.ico"
if icon_path.is_file():
self.iconbitmap(str(icon_path))
self.minsize(275, False)
self.subject = Subject()
self["menu"] = Menus(self, vmr)
2022-04-11 18:35:28 +01:00
self.styletable = ttk.Style()
if _configuration.config:
vmr.apply_config(_configuration.config)
2022-04-11 18:35:28 +01:00
self.build_app()
2022-04-11 18:35:28 +01:00
self.drag_id = ""
self.bind("<Configure>", self.dragging)
@property
def target(self):
"""returns the current interface"""
return self._vban if _base_values.vban_connected else self._vmr
2022-04-11 18:35:28 +01:00
@property
def configframes(self):
"""returns the current configframes"""
return (
2022-04-11 18:35:28 +01:00
frame
for frame in self.winfo_children()
if isinstance(frame, ttk.Frame)
and "!stripconfig" in str(frame)
or "!busconfig" in str(frame)
)
def build_app(self, kind=None, vban=None):
"""builds the app frames according to a kind"""
2022-04-11 18:35:28 +01:00
self._vban = vban
if kind:
self.kind = kind
# register app as observer
self.target.subject.add(self)
2022-04-11 18:35:28 +01:00
self.bus_frame = None
self.submix_frame = None
self.builder = MainFrameBuilder(self)
self.builder.setup()
self.builder.create_channelframe("strip")
self.builder.create_separator()
self.builder.create_navframe()
if _configuration.extended:
2022-04-11 18:35:28 +01:00
self.nav_frame.extend.set(True)
self.nav_frame.extend_frame()
if self.kind.name == "potato":
self.builder.create_banner()
def on_update(self, subject):
"""called whenever notified of update"""
if subject == "pdirty" and _base_values.run_update:
self.after(1, self.subject.notify, "pdirty")
elif subject == "ldirty" and not _base_values.dragging:
self.after(1, self.subject.notify, "ldirty")
2022-04-11 18:35:28 +01:00
def _destroy_top_level_frames(self):
"""
Clear observables.
Deregister app as observer.
Destroy all top level frames.
"""
self.target.subject.remove(self)
self.subject.clear()
2022-04-11 18:35:28 +01:00
[
frame.destroy()
for frame in self.winfo_children()
if isinstance(frame, ttk.Frame)
]
def dragging(self, event, *args):
if event.widget is self:
if self.drag_id == "":
_base_values.dragging = True
2022-04-11 18:35:28 +01:00
else:
self.after_cancel(self.drag_id)
self.drag_id = self.after(100, self.stop_drag)
def stop_drag(self):
self.drag_id = ""
_base_values.dragging = False
2022-04-11 18:35:28 +01:00
_apps = {kind.name: App.make(kind) for kind in _kinds_all}
2022-04-11 18:35:28 +01:00
def connect(kind_id: str, vmr) -> App:
"""return App of the kind requested"""
2022-04-11 18:35:28 +01:00
try:
VMMIN_cls = _apps[kind_id]
except KeyError:
raise VMCompactErrors(f"Invalid kind: {kind_id}")
return VMMIN_cls(vmr)