mirror of
https://github.com/onyx-and-iris/voicemeeter-compact.git
synced 2026-04-08 17:03:32 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35775f5024 | ||
|
|
d4b2b90fc0 | ||
|
|
6de79977cc |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,5 @@
|
|||||||
# quick test
|
# quick test
|
||||||
z_*.py
|
quick.py
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
- [ ] Add support for forest theme (should be coming soon)
|
- [ ] Add support for forest theme (should be coming soon)
|
||||||
|
|
||||||
|
## [1.6.0] - 2022-09-29
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Logging module used in place of print statements across the interface.
|
||||||
|
|
||||||
## [1.5.1] - 2022-09-16
|
## [1.5.1] - 2022-09-16
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@@ -20,8 +26,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- sv_ttk updated to v2.0.
|
- sv_ttk updated to v2.0.
|
||||||
- event toggles used to pause updates when dragging sliders.
|
- event toggles used to pause updates when dragging sliders.
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
## [1.4.2] - 2022-09-03
|
## [1.4.2] - 2022-09-03
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
31
poetry.lock
generated
31
poetry.lock
generated
@@ -84,7 +84,7 @@ python-versions = ">=3.7"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vban-cmd"
|
name = "vban-cmd"
|
||||||
version = "1.4.2"
|
version = "1.5.2"
|
||||||
description = "Python interface for the VBAN RT Packet Service (Sendtext)"
|
description = "Python interface for the VBAN RT Packet Service (Sendtext)"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -95,7 +95,7 @@ tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""}
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "voicemeeter-api"
|
name = "voicemeeter-api"
|
||||||
version = "0.7.0"
|
version = "0.8.1"
|
||||||
description = "A Python wrapper for the Voiceemeter API"
|
description = "A Python wrapper for the Voiceemeter API"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -107,31 +107,16 @@ tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""}
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.10"
|
python-versions = "^3.10"
|
||||||
content-hash = "738f40473a635574dc00427d128af1c3bf45f4290aca9b4d8ae78b6992a486c7"
|
content-hash = "0fc1f7b08a87f389504c898142c5a0f2ed20c8f56deabb3028fa815774b7fc98"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
black = []
|
black = []
|
||||||
click = [
|
click = []
|
||||||
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
|
colorama = []
|
||||||
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
|
mypy-extensions = []
|
||||||
]
|
|
||||||
colorama = [
|
|
||||||
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
|
|
||||||
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
|
|
||||||
]
|
|
||||||
mypy-extensions = [
|
|
||||||
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
|
||||||
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
|
||||||
]
|
|
||||||
pathspec = []
|
pathspec = []
|
||||||
platformdirs = [
|
platformdirs = []
|
||||||
{file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
|
|
||||||
{file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
|
|
||||||
]
|
|
||||||
sv-ttk = []
|
sv-ttk = []
|
||||||
tomli = [
|
tomli = []
|
||||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
|
||||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
|
||||||
]
|
|
||||||
vban-cmd = []
|
vban-cmd = []
|
||||||
voicemeeter-api = []
|
voicemeeter-api = []
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "voicemeeter-compact"
|
name = "voicemeeter-compact"
|
||||||
version = "1.5.1"
|
version = "1.6.0"
|
||||||
description = "A Compact Voicemeeter Remote App"
|
description = "A Compact Voicemeeter Remote App"
|
||||||
authors = ["onyx-and-iris <code@onyxandiris.online>"]
|
authors = ["onyx-and-iris <code@onyxandiris.online>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@@ -16,8 +16,8 @@ include = ["vmcompact/img/cat.ico"]
|
|||||||
python = "^3.10"
|
python = "^3.10"
|
||||||
sv-ttk = "^2.0"
|
sv-ttk = "^2.0"
|
||||||
tomli = { version = "^2.0.1", python = "<3.11" }
|
tomli = { version = "^2.0.1", python = "<3.11" }
|
||||||
voicemeeter-api = "^0.7.0"
|
voicemeeter-api = "^0.8.1"
|
||||||
vban-cmd = "^1.4.2"
|
vban-cmd = "^1.5.2"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
black = {version = "^22.6.0", allow-prereleases = true}
|
black = {version = "^22.6.0", allow-prereleases = true}
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ class App(tk.Tk):
|
|||||||
|
|
||||||
self._vmr = vmr
|
self._vmr = vmr
|
||||||
self._vmr.event.add("ldirty")
|
self._vmr.event.add("ldirty")
|
||||||
|
self._vmr.event.remove("mdirty")
|
||||||
|
self._vmr.event.remove("midi")
|
||||||
icon_path = Path(__file__).parent.resolve() / "img" / "cat.ico"
|
icon_path = Path(__file__).parent.resolve() / "img" / "cat.ico"
|
||||||
if icon_path.is_file():
|
if icon_path.is_file():
|
||||||
self.iconbitmap(str(icon_path))
|
self.iconbitmap(str(icon_path))
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import abc
|
import abc
|
||||||
|
import logging
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
@@ -27,6 +28,8 @@ class AbstractBuilder(abc.ABC):
|
|||||||
class MainFrameBuilder(AbstractBuilder):
|
class MainFrameBuilder(AbstractBuilder):
|
||||||
"""Responsible for building the frames that sit directly on the mainframe"""
|
"""Responsible for building the frames that sit directly on the mainframe"""
|
||||||
|
|
||||||
|
logger = logging.getLogger("builders.mainframebuilder")
|
||||||
|
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
self.kind = app.kind
|
self.kind = app.kind
|
||||||
self.app = app
|
self.app = app
|
||||||
@@ -39,24 +42,26 @@ class MainFrameBuilder(AbstractBuilder):
|
|||||||
if _configuration.themes_enabled:
|
if _configuration.themes_enabled:
|
||||||
if sv_ttk.get_theme() not in ("light", "dark"):
|
if sv_ttk.get_theme() not in ("light", "dark"):
|
||||||
sv_ttk.set_theme(_configuration.theme_mode)
|
sv_ttk.set_theme(_configuration.theme_mode)
|
||||||
print(f"Sunvalley {sv_ttk.get_theme().capitalize()} Theme applied")
|
self.logger.info(
|
||||||
|
f"Sunvalley {sv_ttk.get_theme().capitalize()} Theme applied"
|
||||||
self.app.target.event.remove("mdirty")
|
)
|
||||||
self.app.target.event.remove("midi")
|
|
||||||
|
|
||||||
def create_channelframe(self, type_):
|
def create_channelframe(self, type_):
|
||||||
if type_ == "strip":
|
if type_ == "strip":
|
||||||
self.app.strip_frame = _make_channelframe(self.app, type_)
|
self.app.strip_frame = _make_channelframe(self.app, type_)
|
||||||
else:
|
else:
|
||||||
self.app.bus_frame = _make_channelframe(self.app, type_)
|
self.app.bus_frame = _make_channelframe(self.app, type_)
|
||||||
|
self.logger.info(f"Finished building channelframe type {type_}")
|
||||||
|
|
||||||
def create_separator(self):
|
def create_separator(self):
|
||||||
self.app.sep = ttk.Separator(self.app, orient="vertical")
|
self.app.sep = ttk.Separator(self.app, orient="vertical")
|
||||||
self.app.sep.grid(row=0, column=1, sticky=(tk.N, tk.S))
|
self.app.sep.grid(row=0, column=1, sticky=(tk.N, tk.S))
|
||||||
self.app.columnconfigure(1, minsize=15)
|
self.app.columnconfigure(1, minsize=15)
|
||||||
|
self.logger.info(f"Finished building separator")
|
||||||
|
|
||||||
def create_navframe(self):
|
def create_navframe(self):
|
||||||
self.app.nav_frame = Navigation(self.app)
|
self.app.nav_frame = Navigation(self.app)
|
||||||
|
self.logger.info(f"Finished building navframe")
|
||||||
|
|
||||||
def create_configframe(self, type_, index, id):
|
def create_configframe(self, type_, index, id):
|
||||||
if type_ == "strip":
|
if type_ == "strip":
|
||||||
@@ -102,6 +107,7 @@ class MainFrameBuilder(AbstractBuilder):
|
|||||||
)
|
)
|
||||||
for _, frame in enumerate(self.app.bus_frame.labelframes)
|
for _, frame in enumerate(self.app.bus_frame.labelframes)
|
||||||
]
|
]
|
||||||
|
self.logger.info(f"Finished building configframe for {type_}[{index}]")
|
||||||
self.app.after(5, self.reset_config_frames)
|
self.app.after(5, self.reset_config_frames)
|
||||||
|
|
||||||
def reset_config_frames(self):
|
def reset_config_frames(self):
|
||||||
@@ -114,6 +120,7 @@ class MainFrameBuilder(AbstractBuilder):
|
|||||||
def create_banner(self):
|
def create_banner(self):
|
||||||
self.app.banner = Banner(self.app)
|
self.app.banner = Banner(self.app)
|
||||||
self.app.banner.grid(row=4, column=0, columnspan=3)
|
self.app.banner.grid(row=4, column=0, columnspan=3)
|
||||||
|
self.logger.info(f"Finished building banner")
|
||||||
|
|
||||||
def teardown(self):
|
def teardown(self):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -5,6 +6,8 @@ try:
|
|||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
import tomli as tomllib
|
import tomli as tomllib
|
||||||
|
|
||||||
|
LOGGER = logging.getLogger("configurations")
|
||||||
|
|
||||||
configuration = {}
|
configuration = {}
|
||||||
|
|
||||||
config_path = [Path.cwd() / "configs"]
|
config_path = [Path.cwd() / "configs"]
|
||||||
@@ -21,7 +24,7 @@ for path in config_path:
|
|||||||
print(f"Invalid TOML config: configs/{filename.stem}")
|
print(f"Invalid TOML config: configs/{filename.stem}")
|
||||||
|
|
||||||
for name, cfg in configs.items():
|
for name, cfg in configs.items():
|
||||||
print(f"Loaded configuration configs/{name}")
|
LOGGER.info(f"Loaded configuration configs/{name}")
|
||||||
configuration[name] = cfg
|
configuration[name] = cfg
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import logging
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
import webbrowser
|
import webbrowser
|
||||||
from functools import partial
|
from functools import partial
|
||||||
@@ -5,11 +6,14 @@ from tkinter import messagebox, ttk
|
|||||||
|
|
||||||
import sv_ttk
|
import sv_ttk
|
||||||
import vban_cmd
|
import vban_cmd
|
||||||
|
from vban_cmd.error import VBANCMDError
|
||||||
|
|
||||||
from .data import _base_values, _configuration, get_configuration, kind_get
|
from .data import _base_values, _configuration, get_configuration, kind_get
|
||||||
|
|
||||||
|
|
||||||
class Menus(tk.Menu):
|
class Menus(tk.Menu):
|
||||||
|
logger = logging.getLogger("menu.menus")
|
||||||
|
|
||||||
def __init__(self, parent, vmr):
|
def __init__(self, parent, vmr):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
@@ -267,12 +271,15 @@ class Menus(tk.Menu):
|
|||||||
for menu in self.menu_layout.winfo_children()
|
for menu in self.menu_layout.winfo_children()
|
||||||
if isinstance(menu, tk.Menu)
|
if isinstance(menu, tk.Menu)
|
||||||
]
|
]
|
||||||
|
self.logger.info(
|
||||||
|
f"Finished loading theme Sunvalley {sv_ttk.get_theme().capitalize()} theme"
|
||||||
|
)
|
||||||
|
|
||||||
def vban_connect(self, i):
|
def menu_teardown(self, i):
|
||||||
# remove config load menus
|
# remove config load menus
|
||||||
[
|
[
|
||||||
self.menu_configs_load.delete(key)
|
self.menu_configs_load.delete(key)
|
||||||
for key in self.vmr.configs.keys()
|
for key in self.target.configs.keys()
|
||||||
if key not in self.config_defaults
|
if key not in self.config_defaults
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -282,12 +289,38 @@ class Menus(tk.Menu):
|
|||||||
if j != i
|
if j != i
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def menu_setup(self):
|
||||||
|
if len(self.target.configs) > len(self.config_defaults) and all(
|
||||||
|
key in self.target.configs for key in self.config_defaults
|
||||||
|
):
|
||||||
|
[
|
||||||
|
self.menu_configs_load.add_command(
|
||||||
|
label=profile, command=partial(self.load_profile, profile)
|
||||||
|
)
|
||||||
|
for profile in self.target.configs.keys()
|
||||||
|
if profile not in self.config_defaults
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
self.menu_configs.entryconfig(0, state="disabled")
|
||||||
|
|
||||||
|
def vban_connect(self, i):
|
||||||
opts = {}
|
opts = {}
|
||||||
opts |= self.vban_config[f"connection-{i+1}"]
|
opts |= self.vban_config[f"connection-{i+1}"]
|
||||||
kind_id = opts.pop("kind")
|
kind_id = opts.pop("kind")
|
||||||
self.vban = vban_cmd.api(kind_id, **opts)
|
self.vban = vban_cmd.api(kind_id, **opts)
|
||||||
# login to vban interface
|
# login to vban interface
|
||||||
self.vban.login()
|
try:
|
||||||
|
self.logger.info(f"Attempting vban connection to {opts.get('ip')}")
|
||||||
|
self.vban.login()
|
||||||
|
except VBANCMDError as e:
|
||||||
|
self.vban.logout()
|
||||||
|
msg = (str(e), f"Please check your connection settings")
|
||||||
|
messagebox.showerror("Connection Error", "\n".join(msg))
|
||||||
|
msg = (str(e), f"resuming local connection")
|
||||||
|
self.logger.error(", ".join(msg))
|
||||||
|
self.after(1, self.enable_vban_menus)
|
||||||
|
return
|
||||||
|
self.menu_teardown(i)
|
||||||
self.vban.event.add("ldirty")
|
self.vban.event.add("ldirty")
|
||||||
# destroy the current App frames
|
# destroy the current App frames
|
||||||
self.parent._destroy_top_level_frames()
|
self.parent._destroy_top_level_frames()
|
||||||
@@ -303,27 +336,10 @@ class Menus(tk.Menu):
|
|||||||
self.menu_layout.entryconfig(
|
self.menu_layout.entryconfig(
|
||||||
0, state=f"{'normal' if kind.name == 'potato' else 'disabled'}"
|
0, state=f"{'normal' if kind.name == 'potato' else 'disabled'}"
|
||||||
)
|
)
|
||||||
# rebuild config load menus
|
self.menu_setup()
|
||||||
if len(self.target.configs) > len(self.config_defaults) and all(
|
|
||||||
key in self.target.configs for key in self.config_defaults
|
|
||||||
):
|
|
||||||
[
|
|
||||||
self.menu_configs_load.add_command(
|
|
||||||
label=profile, command=partial(self.load_profile, profile)
|
|
||||||
)
|
|
||||||
for profile in self.target.configs.keys()
|
|
||||||
if profile not in self.config_defaults
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
self.menu_configs.entryconfig(0, state="disabled")
|
|
||||||
|
|
||||||
def vban_disconnect(self, i):
|
def vban_disconnect(self, i):
|
||||||
# remove config load menus
|
self.menu_teardown(i)
|
||||||
[
|
|
||||||
self.menu_configs_load.delete(key)
|
|
||||||
for key in self.vban.configs.keys()
|
|
||||||
if key not in self.config_defaults
|
|
||||||
]
|
|
||||||
|
|
||||||
# destroy the current App frames
|
# destroy the current App frames
|
||||||
self.parent._destroy_top_level_frames()
|
self.parent._destroy_top_level_frames()
|
||||||
@@ -340,19 +356,7 @@ class Menus(tk.Menu):
|
|||||||
self.menu_layout.entryconfig(
|
self.menu_layout.entryconfig(
|
||||||
0, state=f"{'normal' if kind.name == 'potato' else 'disabled'}"
|
0, state=f"{'normal' if kind.name == 'potato' else 'disabled'}"
|
||||||
)
|
)
|
||||||
# rebuild config load menus
|
self.menu_setup()
|
||||||
if len(self.target.configs) > len(self.config_defaults) and all(
|
|
||||||
key in self.target.configs for key in self.config_defaults
|
|
||||||
):
|
|
||||||
[
|
|
||||||
self.menu_configs_load.add_command(
|
|
||||||
label=profile, command=partial(self.load_profile, profile)
|
|
||||||
)
|
|
||||||
for profile in self.target.configs.keys()
|
|
||||||
if profile not in self.config_defaults
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
self.menu_configs.entryconfig(0, state="disabled")
|
|
||||||
|
|
||||||
self.after(15000, self.enable_vban_menus)
|
self.after(15000, self.enable_vban_menus)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import logging
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
|
|
||||||
@@ -7,6 +8,8 @@ from .gainlayer import SubMixFrame
|
|||||||
|
|
||||||
|
|
||||||
class Navigation(ttk.Frame):
|
class Navigation(ttk.Frame):
|
||||||
|
logger = logging.getLogger("navigation.navigation")
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
@@ -26,6 +29,9 @@ class Navigation(ttk.Frame):
|
|||||||
def show_submix(self):
|
def show_submix(self):
|
||||||
if self.submix.get():
|
if self.submix.get():
|
||||||
self.parent.submix_frame = SubMixFrame(self.parent)
|
self.parent.submix_frame = SubMixFrame(self.parent)
|
||||||
|
self.logger.info(
|
||||||
|
f"Finished building submixframe for submix {_configuration.submixes}"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if _configuration.extends_horizontal:
|
if _configuration.extends_horizontal:
|
||||||
self.parent.submix_frame.teardown()
|
self.parent.submix_frame.teardown()
|
||||||
@@ -39,6 +45,9 @@ class Navigation(ttk.Frame):
|
|||||||
self.parent.bus_frame.grid()
|
self.parent.bus_frame.grid()
|
||||||
else:
|
else:
|
||||||
self.parent.rowconfigure(2, weight=0, minsize=0)
|
self.parent.rowconfigure(2, weight=0, minsize=0)
|
||||||
|
self.logger.info(
|
||||||
|
f"Finished tearing down submixframe for submix {_configuration.submixes}"
|
||||||
|
)
|
||||||
|
|
||||||
if not _configuration.themes_enabled:
|
if not _configuration.themes_enabled:
|
||||||
self.styletable.configure(
|
self.styletable.configure(
|
||||||
|
|||||||
Reference in New Issue
Block a user