voicemeeter-compact/vmcompact/app.py
onyx-and-iris 3eec86e5ed initial commit
initial commit
2022-04-11 18:35:28 +01:00

178 lines
4.9 KiB
Python

import tkinter as tk
from tkinter import ttk
from typing import NamedTuple
from functools import partial
from pathlib import Path
from .errors import VMCompactErrors
from .data import _base_vals, _kinds_all
from .channels import ChannelFrame
from .navigation import Navigation
from .menu import Menus
from .banner import Banner
from .configurations import configuration
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"Voicemeeter{kind.name}.Compact",
(cls,),
{
"kind": kind,
},
)
return APP_cls
def __init__(self, vmr):
super().__init__()
defaults = {
"theme": {
"mode": "light",
},
"extends": {
"extended": False,
"extends_horizontal": True,
},
}
self.configuration = defaults | self.configuration
_base_vals.themes_enabled = self.configuration["theme"]["enabled"]
_base_vals.extends_horizontal = self.configuration["extends"][
"extends_horizontal"
]
_base_vals.submixes = self.configuration["submixes"]["default"]
# create menus
self.menus = Menus(self, vmr)
self.styletable = ttk.Style()
self._vmr = vmr
# start pdirty watcher
self.upd_pdirty()
self.resizable(False, False)
if _base_vals.themes_enabled:
self.apply_theme()
self._make_app(self.kind)
self.drag_id = ""
self.bind("<Configure>", self.dragging)
self.iconbitmap(Path.cwd() / "vmcompact" / "img" / "cat.ico")
@property
def target(self):
"""returns the current interface"""
return self._vban if _base_vals.vban_connected else self._vmr
@property
def pdirty(self):
return self._pdirty
@pdirty.setter
def pdirty(self, val):
self._pdirty = val
@property
def configuration(self):
return configuration["app"]
@configuration.setter
def configuration(self, val):
self.configuration["app"] = val
@property
def configframes(self):
"""returns a tuple of current config frame addresses"""
return tuple(
frame
for frame in self.winfo_children()
if isinstance(frame, ttk.Frame)
and "!stripconfig" in str(frame)
or "!busconfig" in str(frame)
)
def apply_theme(self):
_base_vals.using_theme = True
self.tk.call("source", "./vmcompact/sun-valley-theme/sun-valley.tcl")
self.tk.call("set_theme", self.configuration["theme"]["mode"])
def _make_app(self, kind, vban=None):
self.title(
f'Voicemeeter{kind.name}.Compact [{"Local" if not vban else "Network"} Connection]'
)
self._vban = vban
self.kind = kind
self._make_top_level_frames()
def _make_top_level_frames(self):
# initialize bus frame variable
self.bus_frame = None
# channel_frame, left aligned
self.channel_frame = ChannelFrame.make_strips(self)
self.channel_frame.grid(row=0, column=0, sticky=(tk.W))
# separator
self.sep = ttk.Separator(self, orient="vertical")
self.sep.grid(row=0, column=1, sticky=(tk.S, tk.N))
self.columnconfigure(1, minsize=15)
# navigation frame
self.nav_frame = Navigation(self)
self.nav_frame.grid(row=0, column=3)
if self.configuration["extends"]["extended"]:
self.nav_frame.extend.set(True)
self.nav_frame.extend_frame()
self.banner = Banner(self)
self.banner.grid(row=4, column=0, columnspan=3)
def _destroy_top_level_frames(self):
[
frame.destroy()
for frame in self.winfo_children()
if isinstance(frame, ttk.Frame)
]
def upd_pdirty(self):
self.after(1, self.upd_pdirty_step)
def upd_pdirty_step(self):
self.pdirty = self.target.pdirty
self.after(1, self.upd_pdirty_step)
def dragging(self, event, *args):
if event.widget is self:
if self.drag_id == "":
_base_vals.in_scale_button_1 = True
_base_vals.dragging = True
else:
self.after_cancel(self.drag_id)
self.drag_id = self.after(100, self.stop_drag)
def stop_drag(self):
_base_vals.dragging = False
_base_vals.in_scale_button_1 = False
self.drag_id = ""
_apps = {kind.id: App.make(kind) for kind in _kinds_all}
def connect(kind_id: str, vmr) -> App:
"""return App of the kind requested"""
try:
VMMIN_cls = _apps[kind_id]
return VMMIN_cls(vmr)
except KeyError:
raise VMCompactErrors(f"Invalid kind: {kind_id}")