mirror of
				https://github.com/onyx-and-iris/voicemeeter-compact.git
				synced 2025-11-04 07:11:45 +00:00 
			
		
		
		
	sun valley theme now added as pip module added sv-ttk to setup.py changes to code to load as module
		
			
				
	
	
		
			328 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			328 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import tkinter as tk
 | 
						|
from tkinter import ttk, messagebox
 | 
						|
from functools import partial
 | 
						|
import webbrowser
 | 
						|
import sv_ttk
 | 
						|
 | 
						|
import vbancmd
 | 
						|
 | 
						|
from .configurations import configuration
 | 
						|
from .data import _base_vals, kind_get
 | 
						|
from .gainlayer import SubMixFrame
 | 
						|
 | 
						|
 | 
						|
class Menus(tk.Menu):
 | 
						|
    def __init__(self, parent, vmr):
 | 
						|
        super().__init__()
 | 
						|
        self._parent = parent
 | 
						|
        self._vmr = vmr
 | 
						|
        if self.configuration_vban is not None:
 | 
						|
            self.vban_conns = [None for i, _ in enumerate(self.configuration_vban)]
 | 
						|
        self.menubar = tk.Menu(self, tearoff=0)
 | 
						|
        parent["menu"] = self.menubar
 | 
						|
        self._is_topmost = tk.BooleanVar()
 | 
						|
        self._selected_bus = list(tk.BooleanVar() for _ in range(8))
 | 
						|
 | 
						|
        # voicemeeter menu
 | 
						|
        self.menu_voicemeeter = tk.Menu(self.menubar, tearoff=0)
 | 
						|
        self.menubar.add_cascade(menu=self.menu_voicemeeter, label="Voicemeeter")
 | 
						|
        self.menu_voicemeeter.add_checkbutton(
 | 
						|
            label="Always On Top",
 | 
						|
            onvalue=1,
 | 
						|
            offvalue=0,
 | 
						|
            variable=self._is_topmost,
 | 
						|
            command=self.always_on_top,
 | 
						|
        )
 | 
						|
        self.menu_voicemeeter.add_separator()
 | 
						|
        self.menu_voicemeeter.add_command(
 | 
						|
            label="Show",
 | 
						|
            underline=0,
 | 
						|
            command=partial(self.action_invoke_voicemeeter, "show"),
 | 
						|
        )
 | 
						|
        self.menu_voicemeeter.add_command(
 | 
						|
            label="Hide",
 | 
						|
            underline=0,
 | 
						|
            command=partial(self.action_invoke_voicemeeter, "hide"),
 | 
						|
        )
 | 
						|
        self.menu_voicemeeter.add_command(
 | 
						|
            label="Restart",
 | 
						|
            underline=0,
 | 
						|
            command=partial(self.action_invoke_voicemeeter, "restart"),
 | 
						|
        )
 | 
						|
        self.menu_voicemeeter.add_command(
 | 
						|
            label="Shutdown",
 | 
						|
            underline=0,
 | 
						|
            command=partial(self.action_invoke_voicemeeter, "shutdown"),
 | 
						|
        )
 | 
						|
        self.menu_voicemeeter.add_separator()
 | 
						|
        self.menu_lock = tk.Menu(self.menu_voicemeeter, tearoff=0)
 | 
						|
        self.menu_voicemeeter.add_cascade(
 | 
						|
            menu=self.menu_lock, label="GUI Lock", underline=0
 | 
						|
        )
 | 
						|
        self.menu_lock.add_command(
 | 
						|
            label="Lock", command=partial(self.action_set_voicemeeter, "lock")
 | 
						|
        )
 | 
						|
        self.menu_lock.add_command(
 | 
						|
            label="Unlock", command=partial(self.action_set_voicemeeter, "lock", False)
 | 
						|
        )
 | 
						|
 | 
						|
        # profiles menu
 | 
						|
        menu_profiles = tk.Menu(self.menubar, tearoff=0)
 | 
						|
        self.menubar.add_cascade(menu=menu_profiles, label="Profiles")
 | 
						|
        self.menu_profiles_load = tk.Menu(menu_profiles, tearoff=0)
 | 
						|
        menu_profiles.add_cascade(menu=self.menu_profiles_load, label="Load profile")
 | 
						|
        defaults = {"base", "blank"}
 | 
						|
        if len(vmr.profiles) > len(defaults) and all(
 | 
						|
            key in vmr.profiles for key in defaults
 | 
						|
        ):
 | 
						|
            [
 | 
						|
                self.menu_profiles_load.add_command(
 | 
						|
                    label=profile, command=partial(self.load_profile, profile)
 | 
						|
                )
 | 
						|
                for profile in vmr.profiles.keys()
 | 
						|
                if profile not in defaults
 | 
						|
            ]
 | 
						|
        else:
 | 
						|
            menu_profiles.entryconfig(0, state="disabled")
 | 
						|
        menu_profiles.add_separator()
 | 
						|
        menu_profiles.add_command(label="Reset to defaults", command=self.load_defaults)
 | 
						|
 | 
						|
        # vban connect menu
 | 
						|
        self.menu_vban = tk.Menu(self.menubar, tearoff=0)
 | 
						|
        self.menubar.add_cascade(menu=self.menu_vban, label="VBAN Connect")
 | 
						|
        if self.configuration_vban:
 | 
						|
            for i, _ in enumerate(self.configuration_vban):
 | 
						|
                setattr(self, f"menu_vban_{i+1}", tk.Menu(self.menu_vban, tearoff=0))
 | 
						|
                target_menu = getattr(self, f"menu_vban_{i+1}")
 | 
						|
                self.menu_vban.add_cascade(
 | 
						|
                    menu=target_menu, label=f"VBAN Connect #{i+1}", underline=0
 | 
						|
                )
 | 
						|
                target_menu.add_command(
 | 
						|
                    label="Connect", command=partial(self.vban_connect, i)
 | 
						|
                )
 | 
						|
                target_menu.add_command(
 | 
						|
                    label="Disconnect", command=partial(self.vban_disconnect, i)
 | 
						|
                )
 | 
						|
                target_menu.entryconfig(1, state="disabled")
 | 
						|
        else:
 | 
						|
            self.menubar.entryconfig(2, state="disabled")
 | 
						|
 | 
						|
        # extends menu
 | 
						|
        self.menu_extends = tk.Menu(self.menubar, tearoff=0)
 | 
						|
        self.menubar.add_cascade(menu=self.menu_extends, label="Extends")
 | 
						|
        self.menu_extends.add_command(
 | 
						|
            label="horizontal",
 | 
						|
            underline=0,
 | 
						|
            command=partial(self.switch_orientation, extends_horizontal=True),
 | 
						|
        )
 | 
						|
        self.menu_extends.add_command(
 | 
						|
            label="vertical",
 | 
						|
            underline=0,
 | 
						|
            command=partial(self.switch_orientation, extends_horizontal=False),
 | 
						|
        )
 | 
						|
        self.menu_extends.entryconfig(
 | 
						|
            0 if _base_vals.extends_horizontal else 1, state="disabled"
 | 
						|
        )
 | 
						|
 | 
						|
        # submixes menu
 | 
						|
        # here we build menu regardless of kind but disable if not Potato
 | 
						|
        buses = tuple(f"A{i+1}" for i in range(5)) + tuple(f"B{i+1}" for i in range(3))
 | 
						|
        self.menu_submixes = tk.Menu(self.menubar, tearoff=0)
 | 
						|
        self.menubar.add_cascade(menu=self.menu_submixes, label="Submixes")
 | 
						|
        [
 | 
						|
            self.menu_submixes.add_checkbutton(
 | 
						|
                label=f"Bus {buses[i]}",
 | 
						|
                underline=0,
 | 
						|
                onvalue=1,
 | 
						|
                offvalue=0,
 | 
						|
                variable=self._selected_bus[i],
 | 
						|
                command=partial(self.set_submix, i),
 | 
						|
            )
 | 
						|
            for i in range(8)
 | 
						|
        ]
 | 
						|
        self._selected_bus[_base_vals.submixes].set(True)
 | 
						|
        if self._parent.kind.name != "Potato":
 | 
						|
            self.menubar.entryconfig(4, state="disabled")
 | 
						|
 | 
						|
        # themes menu
 | 
						|
        self.menu_themes = tk.Menu(self.menubar, tearoff=0)
 | 
						|
        self.menubar.add_cascade(menu=self.menu_themes, label="Themes")
 | 
						|
        tcl_themes = [
 | 
						|
            "light",
 | 
						|
            "dark",
 | 
						|
        ]
 | 
						|
        [
 | 
						|
            self.menu_themes.add_command(
 | 
						|
                label=theme.capitalize(),
 | 
						|
                command=self.toggle_theme,
 | 
						|
            )
 | 
						|
            for theme in tcl_themes
 | 
						|
        ]
 | 
						|
        self.menu_themes.entryconfig(
 | 
						|
            0 if self.configuration_app["theme"]["mode"] == "light" else 1,
 | 
						|
            state="disabled",
 | 
						|
        )
 | 
						|
        if not _base_vals.themes_enabled:
 | 
						|
            self.menubar.entryconfig(6, state="disabled")
 | 
						|
 | 
						|
        # Help menu
 | 
						|
        self.menu_help = tk.Menu(self.menubar, tearoff=0)
 | 
						|
        self.menubar.add_cascade(menu=self.menu_help, label="Help")
 | 
						|
        self.menu_help.add_command(
 | 
						|
            label="Voicemeeter Site",
 | 
						|
            command=self.documentation,
 | 
						|
        )
 | 
						|
        self.menu_help.add_command(
 | 
						|
            label="Source Code",
 | 
						|
            command=self.github,
 | 
						|
        )
 | 
						|
        self.menu_help.add_command(
 | 
						|
            label="App Creator",
 | 
						|
            command=self.onyxandiris,
 | 
						|
        )
 | 
						|
 | 
						|
    @property
 | 
						|
    def target(self):
 | 
						|
        """use the correct interface"""
 | 
						|
        return self._parent.target
 | 
						|
 | 
						|
    @property
 | 
						|
    def configuration_app(self):
 | 
						|
        return configuration["app"]
 | 
						|
 | 
						|
    @configuration_app.setter
 | 
						|
    def configuration_app(self, val):
 | 
						|
        self.configuration_app = val
 | 
						|
 | 
						|
    @property
 | 
						|
    def configuration_vban(self):
 | 
						|
        if "vban" in configuration:
 | 
						|
            return configuration["vban"]
 | 
						|
 | 
						|
    def action_invoke_voicemeeter(self, cmd):
 | 
						|
        getattr(self.target.command, cmd)()
 | 
						|
 | 
						|
    def action_set_voicemeeter(self, cmd, val=True):
 | 
						|
        setattr(self.target.command, cmd, val)
 | 
						|
 | 
						|
    def load_profile(self, profile):
 | 
						|
        self.target.apply_profile(profile)
 | 
						|
 | 
						|
    def load_defaults(self):
 | 
						|
        resp = messagebox.askyesno(
 | 
						|
            message="Are you sure you want to Reset values to defaults?\nPhysical strips B1, Virtual strips A1\nMono, Solo, Mute, EQ all OFF"
 | 
						|
        )
 | 
						|
        if resp:
 | 
						|
            self.target.apply_profile("base")
 | 
						|
 | 
						|
    def always_on_top(self):
 | 
						|
        self._parent.attributes("-topmost", self._is_topmost.get())
 | 
						|
        self._parent.update()
 | 
						|
 | 
						|
    def switch_orientation(self, extends_horizontal: bool = True, *args):
 | 
						|
        _base_vals.extends_horizontal = extends_horizontal
 | 
						|
        if extends_horizontal:
 | 
						|
            self.menu_extends.entryconfig(0, state="disabled")
 | 
						|
            self.menu_extends.entryconfig(1, state="normal")
 | 
						|
        else:
 | 
						|
            self.menu_extends.entryconfig(1, state="disabled")
 | 
						|
            self.menu_extends.entryconfig(0, state="normal")
 | 
						|
 | 
						|
    def set_submix(self, i):
 | 
						|
        if _base_vals.submixes != i:
 | 
						|
            _base_vals.submixes = i
 | 
						|
            if self._parent.submix_frame is not None:
 | 
						|
                self._parent.submix_frame.destroy()
 | 
						|
                self._parent.nav_frame.show_submix()
 | 
						|
            for j, var in enumerate(self._selected_bus):
 | 
						|
                var.set(True if i == j else False)
 | 
						|
 | 
						|
    def toggle_theme(self):
 | 
						|
        sv_ttk.toggle_theme()
 | 
						|
        self.configuration_app["theme"]["mode"] = sv_ttk.get_theme()
 | 
						|
        self.menu_themes.entryconfig(
 | 
						|
            0,
 | 
						|
            state=f"{'disabled' if sv_ttk.get_theme() == 'light' else 'normal'}",
 | 
						|
        )
 | 
						|
        self.menu_themes.entryconfig(
 | 
						|
            1,
 | 
						|
            state=f"{'disabled' if sv_ttk.get_theme() == 'dark' else 'normal'}",
 | 
						|
        )
 | 
						|
        [
 | 
						|
            menu.config(bg=f"{'black' if sv_ttk.get_theme() == 'dark' else 'white'}")
 | 
						|
            for menu in self.menubar.winfo_children()
 | 
						|
            if isinstance(menu, tk.Menu)
 | 
						|
        ]
 | 
						|
        self.menu_lock.config(
 | 
						|
            bg=f"{'black' if sv_ttk.get_theme() == 'dark' else 'white'}"
 | 
						|
        )
 | 
						|
        self.menu_profiles_load.config(
 | 
						|
            bg=f"{'black' if sv_ttk.get_theme() == 'dark' else 'white'}"
 | 
						|
        )
 | 
						|
        [
 | 
						|
            menu.config(bg=f"{'black' if sv_ttk.get_theme() == 'dark' else 'white'}")
 | 
						|
            for menu in self.menu_vban.winfo_children()
 | 
						|
            if isinstance(menu, tk.Menu)
 | 
						|
        ]
 | 
						|
 | 
						|
    def vban_connect(self, i):
 | 
						|
        [
 | 
						|
            self.menu_vban.entryconfig(j, state="disabled")
 | 
						|
            for j, _ in enumerate(self.menu_vban.winfo_children())
 | 
						|
            if j != i
 | 
						|
        ]
 | 
						|
 | 
						|
        opts = {}
 | 
						|
        opts |= self.configuration_vban[f"connection-{i+1}"]
 | 
						|
        kind_id = opts.pop("kind")
 | 
						|
        self.vban_conns[i] = vbancmd.connect(kind_id, **opts)
 | 
						|
        # login to vban interface
 | 
						|
        self.vban_conns[i].login()
 | 
						|
        # destroy the current App frames
 | 
						|
        self._parent._destroy_top_level_frames()
 | 
						|
        _base_vals.vban_connected = True
 | 
						|
        # build new app frames according to a kind
 | 
						|
        kind = kind_get(kind_id)
 | 
						|
        self._parent._make_app(kind, self.vban_conns[i])
 | 
						|
        target_menu = getattr(self, f"menu_vban_{i+1}")
 | 
						|
        target_menu.entryconfig(0, state="disabled")
 | 
						|
        target_menu.entryconfig(1, state="normal")
 | 
						|
        self.menubar.entryconfig(
 | 
						|
            4, state=f"{'normal' if kind.name == 'Potato' else 'disabled'}"
 | 
						|
        )
 | 
						|
 | 
						|
    def vban_disconnect(self, i):
 | 
						|
        # destroy the current App frames
 | 
						|
        self._parent._destroy_top_level_frames()
 | 
						|
        _base_vals.vban_connected = False
 | 
						|
        # logout of vban interface
 | 
						|
        i_to_close = self.vban_conns[i]
 | 
						|
        self.vban_conns[i] = None
 | 
						|
        i_to_close.logout()
 | 
						|
        # build new app frames according to a kind
 | 
						|
        kind = kind_get(self._vmr.type)
 | 
						|
        self._parent._make_app(kind, None)
 | 
						|
        target_menu = getattr(self, f"menu_vban_{i+1}")
 | 
						|
        target_menu.entryconfig(0, state="normal")
 | 
						|
        target_menu.entryconfig(1, state="disabled")
 | 
						|
        self.menubar.entryconfig(
 | 
						|
            4, state=f"{'normal' if kind.name == 'Potato' else 'disabled'}"
 | 
						|
        )
 | 
						|
 | 
						|
        self.after(15000, self.enable_vban_menus)
 | 
						|
 | 
						|
    def enable_vban_menus(self):
 | 
						|
        [
 | 
						|
            self.menu_vban.entryconfig(j, state="normal")
 | 
						|
            for j, _ in enumerate(self.menu_vban.winfo_children())
 | 
						|
        ]
 | 
						|
 | 
						|
    def documentation(self):
 | 
						|
        webbrowser.open_new(r"https://voicemeeter.com/")
 | 
						|
 | 
						|
    def github(self):
 | 
						|
        webbrowser.open_new(r"https://github.com/onyx-and-iris")
 | 
						|
 | 
						|
    def onyxandiris(self):
 | 
						|
        webbrowser.open_new(r"https://onyxandiris.online")
 |