2023-06-29 17:15:03 +01:00
|
|
|
import logging
|
2022-04-11 18:35:28 +01:00
|
|
|
import tkinter as tk
|
2023-06-26 16:08:58 +01:00
|
|
|
from functools import cached_property
|
2022-06-16 23:53:28 +01:00
|
|
|
from pathlib import Path
|
2022-04-11 18:35:28 +01:00
|
|
|
from tkinter import ttk
|
|
|
|
from typing import NamedTuple
|
|
|
|
|
2022-05-10 20:34:29 +01:00
|
|
|
from .builders import MainFrameBuilder
|
2023-06-26 16:08:58 +01:00
|
|
|
from .configurations import loader
|
2022-06-16 23:53:28 +01:00
|
|
|
from .data import _base_values, _configuration, _kinds_all
|
2023-06-26 13:55:50 +01:00
|
|
|
from .errors import VMCompactError
|
2022-04-11 18:35:28 +01:00
|
|
|
from .menu import Menus
|
2022-06-16 23:53:28 +01:00
|
|
|
from .subject import Subject
|
2022-04-11 18:35:28 +01:00
|
|
|
|
2023-06-29 17:15:03 +01:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2022-04-11 18:35:28 +01:00
|
|
|
|
|
|
|
class App(tk.Tk):
|
2022-05-10 20:34:29 +01:00
|
|
|
"""App mainframe"""
|
|
|
|
|
2022-04-11 18:35:28 +01:00
|
|
|
@classmethod
|
|
|
|
def make(cls, kind: NamedTuple):
|
|
|
|
"""
|
2022-05-10 20:34:29 +01:00
|
|
|
Factory function for App.
|
2022-04-11 18:35:28 +01:00
|
|
|
|
2022-05-10 20:34:29 +01:00
|
|
|
Returns an App class of a kind.
|
2022-04-11 18:35:28 +01:00
|
|
|
"""
|
2022-05-10 20:34:29 +01:00
|
|
|
|
2022-04-11 18:35:28 +01:00
|
|
|
APP_cls = type(
|
2022-06-20 00:09:27 +01:00
|
|
|
f"Voicemeeter{kind}.Compact",
|
2022-04-11 18:35:28 +01:00
|
|
|
(cls,),
|
|
|
|
{
|
|
|
|
"kind": kind,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
return APP_cls
|
|
|
|
|
|
|
|
def __init__(self, vmr):
|
|
|
|
super().__init__()
|
2023-06-29 17:15:03 +01:00
|
|
|
self.logger = logger.getChild(self.__class__.__name__)
|
2022-05-10 20:34:29 +01:00
|
|
|
self._vmr = vmr
|
2023-06-26 13:55:50 +01:00
|
|
|
self._vmr.event.add(["pdirty", "ldirty"])
|
2023-06-29 17:15:03 +01:00
|
|
|
self.after(12000 if not self._vmr.gui.initial_state else 1, self.start_updates)
|
2023-06-26 13:55:50 +01:00
|
|
|
self._vmr.init_thread()
|
2022-05-10 20:34:29 +01:00
|
|
|
icon_path = Path(__file__).parent.resolve() / "img" / "cat.ico"
|
|
|
|
if icon_path.is_file():
|
|
|
|
self.iconbitmap(str(icon_path))
|
|
|
|
self.minsize(275, False)
|
2022-06-20 00:09:27 +01:00
|
|
|
self.subject = Subject()
|
2023-06-26 16:08:58 +01:00
|
|
|
self._configs = None
|
2022-05-05 11:08:23 +01:00
|
|
|
self["menu"] = Menus(self, vmr)
|
2022-04-11 18:35:28 +01:00
|
|
|
self.styletable = ttk.Style()
|
2022-06-17 17:53:46 +01:00
|
|
|
if _configuration.config:
|
|
|
|
vmr.apply_config(_configuration.config)
|
2022-04-11 18:35:28 +01:00
|
|
|
|
2022-05-10 20:34:29 +01:00
|
|
|
self.build_app()
|
2022-04-11 18:35:28 +01:00
|
|
|
|
|
|
|
self.drag_id = ""
|
|
|
|
self.bind("<Configure>", self.dragging)
|
|
|
|
|
2023-06-29 17:15:03 +01:00
|
|
|
def start_updates(self):
|
|
|
|
self.logger.debug("updates started")
|
|
|
|
_base_values.run_update = True
|
|
|
|
|
2023-06-26 13:55:50 +01:00
|
|
|
def __str__(self):
|
|
|
|
return f"{type(self).__name__}App"
|
|
|
|
|
2022-04-11 18:35:28 +01:00
|
|
|
@property
|
|
|
|
def target(self):
|
|
|
|
"""returns the current interface"""
|
|
|
|
|
2022-05-10 20:34:29 +01:00
|
|
|
return self._vban if _base_values.vban_connected else self._vmr
|
2022-04-11 18:35:28 +01:00
|
|
|
|
|
|
|
@property
|
|
|
|
def configframes(self):
|
2022-05-10 20:34:29 +01:00
|
|
|
"""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)
|
|
|
|
)
|
|
|
|
|
2022-05-10 20:34:29 +01:00
|
|
|
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
|
2022-05-10 20:34:29 +01:00
|
|
|
if kind:
|
|
|
|
self.kind = kind
|
2022-05-14 14:05:48 +01:00
|
|
|
|
2023-06-26 13:56:06 +01:00
|
|
|
# register event callbacks
|
2023-06-26 13:55:50 +01:00
|
|
|
self.target.subject.add([self.on_pdirty, self.on_ldirty])
|
2022-04-11 18:35:28 +01:00
|
|
|
|
|
|
|
self.bus_frame = None
|
2022-05-10 20:34:29 +01:00
|
|
|
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()
|
2022-06-16 23:53:28 +01:00
|
|
|
if self.kind.name == "potato":
|
2022-05-10 20:34:29 +01:00
|
|
|
self.builder.create_banner()
|
|
|
|
|
2023-06-26 13:55:50 +01:00
|
|
|
def on_pdirty(self):
|
|
|
|
if _base_values.run_update:
|
2022-09-16 09:44:47 +01:00
|
|
|
self.after(1, self.subject.notify, "pdirty")
|
2023-06-26 13:55:50 +01:00
|
|
|
|
|
|
|
def on_ldirty(self):
|
|
|
|
if not _base_values.dragging:
|
2022-09-16 09:44:47 +01:00
|
|
|
self.after(1, self.subject.notify, "ldirty")
|
2022-04-11 18:35:28 +01:00
|
|
|
|
|
|
|
def _destroy_top_level_frames(self):
|
2022-05-10 20:34:29 +01:00
|
|
|
"""
|
|
|
|
Clear observables.
|
|
|
|
|
2022-05-14 14:05:48 +01:00
|
|
|
Deregister app as observer.
|
2022-05-10 20:34:29 +01:00
|
|
|
|
|
|
|
Destroy all top level frames.
|
|
|
|
"""
|
2022-05-14 14:05:48 +01:00
|
|
|
self.target.subject.remove(self)
|
2022-06-20 00:09:27 +01:00
|
|
|
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 == "":
|
2022-05-10 20:34:29 +01:00
|
|
|
_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 = ""
|
2022-09-16 09:44:47 +01:00
|
|
|
_base_values.dragging = False
|
2022-04-11 18:35:28 +01:00
|
|
|
|
2023-06-26 16:08:58 +01:00
|
|
|
@cached_property
|
|
|
|
def userconfigs(self):
|
|
|
|
self._configs = loader(self.kind.name)
|
|
|
|
return self._configs
|
|
|
|
|
2022-04-11 18:35:28 +01:00
|
|
|
|
2022-06-16 23:53: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-05-10 20:34:29 +01:00
|
|
|
|
2022-04-11 18:35:28 +01:00
|
|
|
try:
|
|
|
|
VMMIN_cls = _apps[kind_id]
|
|
|
|
except KeyError:
|
2023-06-26 13:55:50 +01:00
|
|
|
raise VMCompactError(f"Invalid kind: {kind_id}")
|
2022-08-02 10:06:35 +01:00
|
|
|
return VMMIN_cls(vmr)
|