mirror of
https://github.com/onyx-and-iris/voicemeeter-compact.git
synced 2026-04-08 17:03:32 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c684ed9981 | |||
| 1498daf36f | |||
| e964c94d07 | |||
| 9f20225a59 | |||
| f63c36c94a | |||
| c2db0f2757 | |||
| bfb0482c32 | |||
| 6222ab1e62 | |||
| 0ad40ab708 | |||
| b809bcb28f | |||
| a0b9a92a2a | |||
| 9faf8ae10c | |||
| 82cf0e914b | |||
| 3e68488231 | |||
| 6d46d9a9a5 | |||
| e4068277f7 | |||
| 674999a461 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -132,4 +132,11 @@ dmypy.json
|
|||||||
# Pyre type checker
|
# Pyre type checker
|
||||||
.pyre/
|
.pyre/
|
||||||
|
|
||||||
|
# build
|
||||||
|
sv_ttk/
|
||||||
|
theme/
|
||||||
|
|
||||||
|
sv_*.py
|
||||||
|
fst_*.py
|
||||||
|
|
||||||
.vscode/
|
.vscode/
|
||||||
11
CHANGELOG.md
11
CHANGELOG.md
@@ -9,6 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
- [ ] Add support for forest theme (if rbende adds it to pypi)
|
- [ ] Add support for forest theme (if rbende adds it to pypi)
|
||||||
|
|
||||||
|
## [1.9.0] - 2023-07-10
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Should the voicemeeter-compact app lose communication with Voicemeeter GUI a popup will show asking to restart the GUI.
|
||||||
|
- If yes is selected the app's mainframe will redraw, there will be a grace period before updates start again due to Voicemeeter engine startup.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- From the menu, Voicemeeter->Shutdown now closes both the compact app and the main Voicemeeter GUI.
|
||||||
|
|
||||||
## [1.8.0] - 2023-06-29
|
## [1.8.0] - 2023-06-29
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
17
README.md
17
README.md
@@ -1,5 +1,6 @@
|
|||||||
[](https://badge.fury.io/py/voicemeeter-compact)
|
[](https://badge.fury.io/py/voicemeeter-compact)
|
||||||
[](https://github.com/onyx-and-iris/voicemeeter-compact/blob/main/LICENSE)
|
[](https://github.com/onyx-and-iris/voicemeeter-compact/blob/main/LICENSE)
|
||||||
|
[](https://python-poetry.org/)
|
||||||
[](https://github.com/psf/black)
|
[](https://github.com/psf/black)
|
||||||

|

|
||||||
|
|
||||||
@@ -34,16 +35,16 @@ import vmcompact
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# pass the kind_id and the vm object to the app
|
# choose the kind of Voicemeeter (Local connection)
|
||||||
with voicemeeterlib.api(kind_id) as vm:
|
KIND_ID = "banana"
|
||||||
app = vmcompact.connect(kind_id, vm)
|
|
||||||
|
# pass the KIND_ID and the vm object to the app
|
||||||
|
with voicemeeterlib.api(KIND_ID) as vm:
|
||||||
|
app = vmcompact.connect(KIND_ID, vm)
|
||||||
app.mainloop()
|
app.mainloop()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# choose the kind of Voicemeeter (Local connection)
|
|
||||||
kind_id = "banana"
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -53,9 +54,9 @@ It's important to know that only labelled strips and buses will appear in the Ch
|
|||||||
|
|
||||||
If the GUI looks like the above when you first load it, then no channels are labelled. From the menu, `Configs->Load config` you may load an example config. Save your current Voicemeeter settings first :).
|
If the GUI looks like the above when you first load it, then no channels are labelled. From the menu, `Configs->Load config` you may load an example config. Save your current Voicemeeter settings first :).
|
||||||
|
|
||||||
### kind_id
|
### KIND_ID
|
||||||
|
|
||||||
Set the kind of Voicemeeter, kind_id may be:
|
Set the kind of Voicemeeter, KIND_ID may be:
|
||||||
|
|
||||||
- `basic`
|
- `basic`
|
||||||
- `banana`
|
- `banana`
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import vmcompact
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
with voicemeeterlib.api(kind_id) as vmr:
|
KIND_ID = "banana"
|
||||||
app = vmcompact.connect(kind_id, vmr)
|
|
||||||
|
with voicemeeterlib.api(KIND_ID) as vmr:
|
||||||
|
app = vmcompact.connect(KIND_ID, vmr)
|
||||||
app.mainloop()
|
app.mainloop()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
kind_id = "banana"
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|||||||
40
build.ps1
Normal file
40
build.ps1
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$prefix,
|
||||||
|
[string]$theme
|
||||||
|
)
|
||||||
|
|
||||||
|
function Format-Path {
|
||||||
|
param($Kind)
|
||||||
|
return @(
|
||||||
|
$prefix,
|
||||||
|
(& { if ($theme) { $theme } else { "" } }),
|
||||||
|
"${Kind}"
|
||||||
|
).Where({ $_ -ne "" }) -Join "_"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Compress-Builds {
|
||||||
|
$target = Join-Path -Path $PSScriptRoot -ChildPath "dist"
|
||||||
|
@("basic", "banana", "potato") | ForEach-Object {
|
||||||
|
$compress_path = Format-Path -Kind $_
|
||||||
|
Compress-Archive -Path $(Join-Path -Path $target -ChildPath $compress_path) -DestinationPath $(Join-Path -Path $target -ChildPath "${compress_path}.zip") -Force
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-Builds {
|
||||||
|
@("basic", "banana", "potato") | ForEach-Object {
|
||||||
|
$spec_path = Format-Path -Kind $_
|
||||||
|
|
||||||
|
"building $spec_path" | Write-Host
|
||||||
|
|
||||||
|
poetry run pyinstaller "$spec_path.spec" --noconfirm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function main {
|
||||||
|
Get-Builds
|
||||||
|
|
||||||
|
Compress-Builds
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($MyInvocation.InvocationName -ne '.') { main }
|
||||||
209
poetry.lock
generated
209
poetry.lock
generated
@@ -1,10 +1,36 @@
|
|||||||
|
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "altgraph"
|
||||||
|
version = "0.17.3"
|
||||||
|
description = "Python graph (network) package"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "altgraph-0.17.3-py2.py3-none-any.whl", hash = "sha256:c8ac1ca6772207179ed8003ce7687757c04b0b71536f81e2ac5755c6226458fe"},
|
||||||
|
{file = "altgraph-0.17.3.tar.gz", hash = "sha256:ad33358114df7c9416cdb8fa1eaa5852166c505118717021c6a8c7c7abbd03dd"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "black"
|
name = "black"
|
||||||
version = "22.12.0"
|
version = "22.12.0"
|
||||||
description = "The uncompromising code formatter."
|
description = "The uncompromising code formatter."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"},
|
||||||
|
{file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"},
|
||||||
|
{file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"},
|
||||||
|
{file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"},
|
||||||
|
{file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"},
|
||||||
|
{file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"},
|
||||||
|
{file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"},
|
||||||
|
{file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"},
|
||||||
|
{file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"},
|
||||||
|
{file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"},
|
||||||
|
{file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"},
|
||||||
|
{file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"},
|
||||||
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
click = ">=8.0.0"
|
click = ">=8.0.0"
|
||||||
@@ -21,11 +47,14 @@ uvloop = ["uvloop (>=0.15.2)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "click"
|
name = "click"
|
||||||
version = "8.1.3"
|
version = "8.1.4"
|
||||||
description = "Composable command line interface toolkit"
|
description = "Composable command line interface toolkit"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "click-8.1.4-py3-none-any.whl", hash = "sha256:2739815aaa5d2c986a88f1e9230c55e17f0caad3d958a5e13ad0797c166db9e3"},
|
||||||
|
{file = "click-8.1.4.tar.gz", hash = "sha256:b97d0c74955da062a7d4ef92fadb583806a585b2ea81958a81bd72726cbb8e37"},
|
||||||
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||||
@@ -34,104 +63,214 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
|||||||
name = "colorama"
|
name = "colorama"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
description = "Cross-platform colored terminal text."
|
description = "Cross-platform colored terminal text."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||||
|
files = [
|
||||||
|
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||||
|
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "isort"
|
name = "isort"
|
||||||
version = "5.12.0"
|
version = "5.12.0"
|
||||||
description = "A Python utility / library to sort Python imports."
|
description = "A Python utility / library to sort Python imports."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8.0"
|
python-versions = ">=3.8.0"
|
||||||
|
files = [
|
||||||
|
{file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"},
|
||||||
|
{file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"},
|
||||||
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
colors = ["colorama (>=0.4.3)"]
|
colors = ["colorama (>=0.4.3)"]
|
||||||
requirements-deprecated-finder = ["pip-api", "pipreqs"]
|
|
||||||
pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"]
|
pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"]
|
||||||
plugins = ["setuptools"]
|
plugins = ["setuptools"]
|
||||||
|
requirements-deprecated-finder = ["pip-api", "pipreqs"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "macholib"
|
||||||
|
version = "1.16.2"
|
||||||
|
description = "Mach-O header analysis and editing"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "macholib-1.16.2-py2.py3-none-any.whl", hash = "sha256:44c40f2cd7d6726af8fa6fe22549178d3a4dfecc35a9cd15ea916d9c83a688e0"},
|
||||||
|
{file = "macholib-1.16.2.tar.gz", hash = "sha256:557bbfa1bb255c20e9abafe7ed6cd8046b48d9525db2f9b77d3122a63a2a8bf8"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
altgraph = ">=0.17"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mypy-extensions"
|
name = "mypy-extensions"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
description = "Type system extensions for programs checked with the mypy type checker."
|
description = "Type system extensions for programs checked with the mypy type checker."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
|
files = [
|
||||||
|
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
|
||||||
|
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pathspec"
|
name = "pathspec"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
description = "Utility library for gitignore style pattern matching of file paths."
|
description = "Utility library for gitignore style pattern matching of file paths."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"},
|
||||||
|
{file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pefile"
|
||||||
|
version = "2023.2.7"
|
||||||
|
description = "Python PE parsing module"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6.0"
|
||||||
|
files = [
|
||||||
|
{file = "pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"},
|
||||||
|
{file = "pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "platformdirs"
|
name = "platformdirs"
|
||||||
version = "3.8.0"
|
version = "3.8.1"
|
||||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "platformdirs-3.8.1-py3-none-any.whl", hash = "sha256:cec7b889196b9144d088e4c57d9ceef7374f6c39694ad1577a0aab50d27ea28c"},
|
||||||
|
{file = "platformdirs-3.8.1.tar.gz", hash = "sha256:f87ca4fcff7d2b0f81c6a748a77973d7af0f4d526f98f308477c3c436c74d528"},
|
||||||
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)", "sphinx (>=7.0.1)"]
|
docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
|
||||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest (>=7.3.1)"]
|
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyinstaller"
|
||||||
|
version = "5.13.1"
|
||||||
|
description = "PyInstaller bundles a Python application and all its dependencies into a single package."
|
||||||
|
optional = false
|
||||||
|
python-versions = "<3.13,>=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "pyinstaller-5.13.1-py3-none-macosx_10_13_universal2.whl", hash = "sha256:3c9cfe6d5d2f392d5d47389f6d377a8f225db460cdd01048b5a3de1d99c24ebe"},
|
||||||
|
{file = "pyinstaller-5.13.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:29341d2e86d5ce7df993e797ee96ef679041fc85376d31c35c7b714085a21299"},
|
||||||
|
{file = "pyinstaller-5.13.1-py3-none-manylinux2014_i686.whl", hash = "sha256:ad6e31a8f35a463c6140e4cf979859197edc9831a1039253408b0fe5eec274dc"},
|
||||||
|
{file = "pyinstaller-5.13.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:5d801db3ceee58d01337473ea897e96e4bb21421a169dd7cf8716754617ff7fc"},
|
||||||
|
{file = "pyinstaller-5.13.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:2519db3edec87d8c33924c2c4b7e176d8c1bbd9ba892d77efb67281925e621d6"},
|
||||||
|
{file = "pyinstaller-5.13.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e033218c8922f0342b6095fb444ecb3bc6747dfa58cac5eac2b985350f4b681e"},
|
||||||
|
{file = "pyinstaller-5.13.1-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:086e68aa1e72f6aa13b9d170a395755e2b194b8ab410caeed02d16b432410c8c"},
|
||||||
|
{file = "pyinstaller-5.13.1-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:aa609aca62edd8cdcf7740677a21525e6c23b5e9a8f821ec8a80c68947771b5d"},
|
||||||
|
{file = "pyinstaller-5.13.1-py3-none-win32.whl", hash = "sha256:b8d4000af72bf72f8185d420cd0a0aee0961f03a5c3511dc3ff08cdaef0583de"},
|
||||||
|
{file = "pyinstaller-5.13.1-py3-none-win_amd64.whl", hash = "sha256:b70ebc10811b30bbea4cf5b81fd1477db992c2614cf215edc987cda9c5468911"},
|
||||||
|
{file = "pyinstaller-5.13.1-py3-none-win_arm64.whl", hash = "sha256:78d1601a11475b95dceff6eaf0c9cd74d93e3f47b5ce4ad63cd76e7a369d3d04"},
|
||||||
|
{file = "pyinstaller-5.13.1.tar.gz", hash = "sha256:a2e7a1d76a7ac26f1db849d691a374f2048b0e204233028d25d79a90ecd1fec8"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
altgraph = "*"
|
||||||
|
macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""}
|
||||||
|
pefile = {version = ">=2022.5.30", markers = "sys_platform == \"win32\""}
|
||||||
|
pyinstaller-hooks-contrib = ">=2021.4"
|
||||||
|
pywin32-ctypes = {version = ">=0.2.1", markers = "sys_platform == \"win32\""}
|
||||||
|
setuptools = ">=42.0.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
encryption = ["tinyaes (>=1.0.0)"]
|
||||||
|
hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyinstaller-hooks-contrib"
|
||||||
|
version = "2023.7"
|
||||||
|
description = "Community maintained hooks for PyInstaller"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "pyinstaller-hooks-contrib-2023.7.tar.gz", hash = "sha256:0c436a4c3506020e34116a8a7ddfd854c1ad6ddca9a8cd84500bd6e69c9e68f9"},
|
||||||
|
{file = "pyinstaller_hooks_contrib-2023.7-py2.py3-none-any.whl", hash = "sha256:3c10df14c0f71ab388dfbf1625375b087e7330d9444cbfd2b310ba027fa0cff0"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pywin32-ctypes"
|
||||||
|
version = "0.2.2"
|
||||||
|
description = "A (partial) reimplementation of pywin32 using ctypes/cffi"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"},
|
||||||
|
{file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "setuptools"
|
||||||
|
version = "68.1.2"
|
||||||
|
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "setuptools-68.1.2-py3-none-any.whl", hash = "sha256:3d8083eed2d13afc9426f227b24fd1659489ec107c0e86cec2ffdde5c92e790b"},
|
||||||
|
{file = "setuptools-68.1.2.tar.gz", hash = "sha256:3d4dfa6d95f1b101d695a6160a7626e15583af71a5f52176efa5d39a054d475d"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5,<=7.1.2)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
|
||||||
|
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||||
|
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sv-ttk"
|
name = "sv-ttk"
|
||||||
version = "2.5.1"
|
version = "2.5.5"
|
||||||
description = "A gorgeous theme for Tkinter, based on Windows 11's UI"
|
description = "A gorgeous theme for Tkinter, based on Windows 11's UI"
|
||||||
category = "main"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "sv_ttk-2.5.5-py3-none-any.whl", hash = "sha256:49d1cd03c032728c183d1fe2318f88cdb658ef3e87157e1ca3fcf6661054965b"},
|
||||||
|
{file = "sv_ttk-2.5.5.tar.gz", hash = "sha256:9bbfe2aba6cc6f9fdf70d79331046543c9666fcccc78bad5ff648a9987e3cedb"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tomli"
|
name = "tomli"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
description = "A lil' TOML parser"
|
description = "A lil' TOML parser"
|
||||||
category = "main"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||||
|
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vban-cmd"
|
name = "vban-cmd"
|
||||||
version = "2.0.0"
|
version = "2.4.4"
|
||||||
description = "Python interface for the VBAN RT Packet Service (Sendtext)"
|
description = "Python interface for the VBAN RT Packet Service (Sendtext)"
|
||||||
category = "main"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.10,<4.0"
|
python-versions = ">=3.10,<4.0"
|
||||||
|
files = [
|
||||||
|
{file = "vban_cmd-2.4.4-py3-none-any.whl", hash = "sha256:f439219a2dc6a45123bb70fc94d2aabfc643f26dffc9206da2228e2520f83e01"},
|
||||||
|
{file = "vban_cmd-2.4.4.tar.gz", hash = "sha256:31dbf6abb681d57772c9082722b024d0798eff99b4c622bfbb539179852a935d"},
|
||||||
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""}
|
tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""}
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "voicemeeter-api"
|
name = "voicemeeter-api"
|
||||||
version = "2.0.2"
|
version = "2.4.4"
|
||||||
description = "A Python wrapper for the Voiceemeter API"
|
description = "A Python wrapper for the Voiceemeter API"
|
||||||
category = "main"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.10,<4.0"
|
python-versions = ">=3.10,<4.0"
|
||||||
|
files = [
|
||||||
|
{file = "voicemeeter_api-2.4.4-py3-none-any.whl", hash = "sha256:2e2f0b475de7cfc0d1c397838498162f0b405ab82a6f6ca0095434b43385b43e"},
|
||||||
|
{file = "voicemeeter_api-2.4.4.tar.gz", hash = "sha256:a4f8ecaa7f5d6b9e9a8545dcf047754711af9a120628b3a98821466c7d65c1e7"},
|
||||||
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""}
|
tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""}
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.10"
|
python-versions = ">=3.10,<3.13"
|
||||||
content-hash = "3a59de3a76e4c0ca11c0166750fa1af7d7c887750f855b48c45359068ef04798"
|
content-hash = "2eaf6fcdc32697296201706d983c0d336af4e6644bae2a0d38d11fff8fa21985"
|
||||||
|
|
||||||
[metadata.files]
|
|
||||||
black = []
|
|
||||||
click = []
|
|
||||||
colorama = []
|
|
||||||
isort = []
|
|
||||||
mypy-extensions = []
|
|
||||||
pathspec = []
|
|
||||||
platformdirs = []
|
|
||||||
sv-ttk = []
|
|
||||||
tomli = []
|
|
||||||
vban-cmd = []
|
|
||||||
voicemeeter-api = []
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "voicemeeter-compact"
|
name = "voicemeeter-compact"
|
||||||
version = "1.8.1"
|
version = "1.9.3"
|
||||||
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"
|
||||||
@@ -11,16 +11,24 @@ packages = [{ include = "vmcompact" }]
|
|||||||
include = ["vmcompact/img/cat.ico"]
|
include = ["vmcompact/img/cat.ico"]
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.10"
|
python = ">=3.10,<3.13"
|
||||||
sv-ttk = "^2.5.1"
|
sv-ttk = "^2.5.5"
|
||||||
tomli = { version = "^2.0.1", python = "<3.11" }
|
tomli = { version = "^2.0.1", python = "<3.11" }
|
||||||
voicemeeter-api = "^2.0.2"
|
voicemeeter-api = "^2.4.4"
|
||||||
vban-cmd = "^2.0.0"
|
vban-cmd = "^2.4.4"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
black = { version = "^22.6.0", allow-prereleases = true }
|
black = { version = "^22.6.0", allow-prereleases = true }
|
||||||
isort = "^5.12.0"
|
isort = "^5.12.0"
|
||||||
|
|
||||||
|
[tool.poetry.group.build.dependencies]
|
||||||
|
pyinstaller = "^5.13.1"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core>=1.0.0"]
|
requires = ["poetry-core>=1.0.0"]
|
||||||
build-backend = "poetry.core.masonry.api"
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
|
[tool.poetry.scripts]
|
||||||
|
build_sunvalley = "scripts:build_sunvalley"
|
||||||
|
build_forest = "scripts:build_forest"
|
||||||
|
build_all = "scripts:build_all"
|
||||||
24
scripts.py
Normal file
24
scripts.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def build_sunvalley():
|
||||||
|
buildscript = Path.cwd() / "build.ps1"
|
||||||
|
subprocess.run(["powershell", str(buildscript), "sv"])
|
||||||
|
|
||||||
|
|
||||||
|
def build_forest():
|
||||||
|
rewriter = Path.cwd() / "tools" / "rewriter.py"
|
||||||
|
subprocess.run([sys.executable, str(rewriter), "-r"])
|
||||||
|
|
||||||
|
buildscript = Path.cwd() / "build.ps1"
|
||||||
|
for theme in ("light", "dark"):
|
||||||
|
subprocess.run(["powershell", str(buildscript), "fst", theme])
|
||||||
|
|
||||||
|
subprocess.run([sys.executable, str(rewriter), "-c"])
|
||||||
|
|
||||||
|
|
||||||
|
def build_all():
|
||||||
|
steps = (build_sunvalley, build_forest)
|
||||||
|
[step() for step in steps]
|
||||||
250
tools/rewriter.py
Normal file
250
tools/rewriter.py
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
|
logger = logging.getLogger("vm-compact-rewriter")
|
||||||
|
|
||||||
|
PACKAGE_DIR = Path(__file__).parent.parent / "vmcompact"
|
||||||
|
|
||||||
|
SRC_DIR = Path(__file__).parent / "src"
|
||||||
|
|
||||||
|
|
||||||
|
def write_outs(output, outs: tuple):
|
||||||
|
for out in outs:
|
||||||
|
output.write(out)
|
||||||
|
|
||||||
|
|
||||||
|
def rewrite_app():
|
||||||
|
app_logger = logger.getChild("app")
|
||||||
|
app_logger.info("rewriting app.py")
|
||||||
|
infile = Path(SRC_DIR) / "app.bk"
|
||||||
|
outfile = Path(PACKAGE_DIR) / "app.py"
|
||||||
|
with open(infile, "r") as input:
|
||||||
|
with open(outfile, "w") as output:
|
||||||
|
for line in input:
|
||||||
|
match line:
|
||||||
|
# App init()
|
||||||
|
case " def __init__(self, vmr):\n":
|
||||||
|
output.write(" def __init__(self, vmr, theme):\n")
|
||||||
|
case " self._vmr = vmr\n":
|
||||||
|
write_outs(
|
||||||
|
output,
|
||||||
|
(
|
||||||
|
" self._vmr = vmr\n",
|
||||||
|
" self._theme = theme\n",
|
||||||
|
' tcldir = Path.cwd() / "theme"\n',
|
||||||
|
" if not tcldir.is_dir():\n",
|
||||||
|
' tcldir = Path.cwd() / "builds" / "theme"\n',
|
||||||
|
' self.tk.call("source", tcldir.resolve() / f"forest-{self._theme}.tcl")\n',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
# def connect()
|
||||||
|
case "def connect(kind_id: str, vmr) -> App:\n":
|
||||||
|
output.write(
|
||||||
|
'def connect(kind_id: str, vmr, theme="light") -> App:\n'
|
||||||
|
)
|
||||||
|
case " return VMMIN_cls(vmr)\n":
|
||||||
|
output.write(" return VMMIN_cls(vmr, theme)\n")
|
||||||
|
case _:
|
||||||
|
output.write(line)
|
||||||
|
|
||||||
|
|
||||||
|
def rewrite_builders():
|
||||||
|
builders_logger = logger.getChild("builders")
|
||||||
|
builders_logger.info("rewriting builders.py")
|
||||||
|
infile = Path(SRC_DIR) / "builders.bk"
|
||||||
|
outfile = Path(PACKAGE_DIR) / "builders.py"
|
||||||
|
with open(infile, "r") as input:
|
||||||
|
with open(outfile, "w") as output:
|
||||||
|
ignore_next_lines = 0
|
||||||
|
|
||||||
|
for line in input:
|
||||||
|
if ignore_next_lines > 0:
|
||||||
|
builders_logger.info(f"ignoring: {line}")
|
||||||
|
ignore_next_lines -= 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
match line:
|
||||||
|
# loading themes
|
||||||
|
case "import sv_ttk\n":
|
||||||
|
output.write("#import sv_ttk\n")
|
||||||
|
case " self.app.resizable(False, False)\n":
|
||||||
|
write_outs(
|
||||||
|
output,
|
||||||
|
(
|
||||||
|
" self.app.resizable(False, False)\n"
|
||||||
|
" if _configuration.themes_enabled:\n",
|
||||||
|
' ttk.Style().theme_use(f"forest-{self.app._theme}")\n',
|
||||||
|
' self.logger.info(f"Forest Theme applied")\n',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
ignore_next_lines = 6
|
||||||
|
# setting navframe button widths
|
||||||
|
case " variable=self.navframe.submix,\n":
|
||||||
|
write_outs(
|
||||||
|
output,
|
||||||
|
(
|
||||||
|
" variable=self.navframe.submix,\n"
|
||||||
|
" width=8,\n",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
case " variable=self.navframe.channel,\n":
|
||||||
|
write_outs(
|
||||||
|
output,
|
||||||
|
(
|
||||||
|
" variable=self.navframe.channel,\n"
|
||||||
|
" width=8,\n",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
case " variable=self.navframe.extend,\n":
|
||||||
|
write_outs(
|
||||||
|
output,
|
||||||
|
(
|
||||||
|
" variable=self.navframe.extend,\n"
|
||||||
|
" width=8,\n",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
case " variable=self.navframe.info,\n":
|
||||||
|
write_outs(
|
||||||
|
output,
|
||||||
|
(
|
||||||
|
" variable=self.navframe.info,\n"
|
||||||
|
" width=8,\n",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
# set channelframe button widths
|
||||||
|
case " variable=self.labelframe.mute,\n":
|
||||||
|
write_outs(
|
||||||
|
output,
|
||||||
|
(
|
||||||
|
" variable=self.labelframe.mute,\n"
|
||||||
|
" width=7,\n",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
case " variable=self.labelframe.conf,\n":
|
||||||
|
write_outs(
|
||||||
|
output,
|
||||||
|
(
|
||||||
|
" variable=self.labelframe.conf,\n"
|
||||||
|
" width=7,\n",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
case " variable=self.labelframe.on,\n":
|
||||||
|
write_outs(
|
||||||
|
output,
|
||||||
|
(
|
||||||
|
" variable=self.labelframe.on,\n"
|
||||||
|
" width=7,\n",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
# set stripconfigframe button widths
|
||||||
|
case " self.configframe.phys_out_params.index(param)\n":
|
||||||
|
write_outs(
|
||||||
|
output,
|
||||||
|
(
|
||||||
|
" self.configframe.phys_out_params.index(param)\n",
|
||||||
|
" ],\n",
|
||||||
|
" width=6,\n",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
ignore_next_lines = 1
|
||||||
|
case " self.configframe.virt_out_params.index(param)\n":
|
||||||
|
write_outs(
|
||||||
|
output,
|
||||||
|
(
|
||||||
|
" self.configframe.virt_out_params.index(param)\n",
|
||||||
|
" ],\n",
|
||||||
|
" width=6,\n",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
ignore_next_lines = 1
|
||||||
|
# This does both strip and bus param vars buttons
|
||||||
|
case " variable=self.configframe.param_vars[i],\n":
|
||||||
|
write_outs(
|
||||||
|
output,
|
||||||
|
(
|
||||||
|
" variable=self.configframe.param_vars[i],\n",
|
||||||
|
" width=6,\n",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
case _:
|
||||||
|
if "Toggle.TButton" in line:
|
||||||
|
output.write(line.replace("Toggle.TButton", "ToggleButton"))
|
||||||
|
else:
|
||||||
|
output.write(line)
|
||||||
|
|
||||||
|
|
||||||
|
def rewrite_menu():
|
||||||
|
menu_logger = logger.getChild("menu")
|
||||||
|
menu_logger.info("rewriting menu.py")
|
||||||
|
infile = Path(SRC_DIR) / "menu.bk"
|
||||||
|
outfile = Path(PACKAGE_DIR) / "menu.py"
|
||||||
|
with open(infile, "r") as input:
|
||||||
|
with open(outfile, "w") as output:
|
||||||
|
ignore_next_lines = 0
|
||||||
|
|
||||||
|
for line in input:
|
||||||
|
if ignore_next_lines > 0:
|
||||||
|
menu_logger.info(f"ignoring: {line}")
|
||||||
|
ignore_next_lines -= 1
|
||||||
|
continue
|
||||||
|
match line:
|
||||||
|
case "import sv_ttk\n":
|
||||||
|
output.write("#import sv_ttk\n")
|
||||||
|
case " # layout/themes\n":
|
||||||
|
ignore_next_lines = 14
|
||||||
|
case _:
|
||||||
|
output.write(line)
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_for_build():
|
||||||
|
################# MOVE FILES FROM PACKAGE DIR INTO SRC DIR #########################
|
||||||
|
for file in (
|
||||||
|
PACKAGE_DIR / "app.py",
|
||||||
|
PACKAGE_DIR / "builders.py",
|
||||||
|
PACKAGE_DIR / "menu.py",
|
||||||
|
):
|
||||||
|
if file.exists():
|
||||||
|
logger.debug(f"moving {str(file)}")
|
||||||
|
file.rename(SRC_DIR / f"{file.stem}.bk")
|
||||||
|
|
||||||
|
###################### RUN THE FILE REWRITER FOR EACH *.BK #########################
|
||||||
|
steps = (
|
||||||
|
rewrite_app,
|
||||||
|
rewrite_builders,
|
||||||
|
rewrite_menu,
|
||||||
|
)
|
||||||
|
[step() for step in steps]
|
||||||
|
|
||||||
|
|
||||||
|
def cleanup():
|
||||||
|
########################## RESTORE *.BK FILES #####################################
|
||||||
|
for file in (
|
||||||
|
PACKAGE_DIR / "app.py",
|
||||||
|
PACKAGE_DIR / "builders.py",
|
||||||
|
PACKAGE_DIR / "menu.py",
|
||||||
|
):
|
||||||
|
file.unlink()
|
||||||
|
|
||||||
|
for file in (
|
||||||
|
SRC_DIR / "app.bk",
|
||||||
|
SRC_DIR / "builders.bk",
|
||||||
|
SRC_DIR / "menu.bk",
|
||||||
|
):
|
||||||
|
file.rename(PACKAGE_DIR / f"{file.stem}.py")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("-r", "--rewrite", action="store_true")
|
||||||
|
parser.add_argument("-c", "--cleanup", action="store_true")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.rewrite:
|
||||||
|
logger.info("preparing files for build")
|
||||||
|
prepare_for_build()
|
||||||
|
elif args.cleanup:
|
||||||
|
logger.info("cleaning up files")
|
||||||
|
cleanup()
|
||||||
0
tools/src/.gitkeep
Normal file
0
tools/src/.gitkeep
Normal file
@@ -2,12 +2,14 @@ import logging
|
|||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tkinter import ttk
|
from tkinter import messagebox, ttk
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
|
|
||||||
|
import voicemeeterlib
|
||||||
|
|
||||||
from .builders import MainFrameBuilder
|
from .builders import MainFrameBuilder
|
||||||
from .configurations import loader
|
from .configurations import loader
|
||||||
from .data import _base_values, _configuration, _kinds_all
|
from .data import _base_values, _configuration, _kinds_all, get_configuration
|
||||||
from .errors import VMCompactError
|
from .errors import VMCompactError
|
||||||
from .menu import Menus
|
from .menu import Menus
|
||||||
from .subject import Subject
|
from .subject import Subject
|
||||||
@@ -40,15 +42,16 @@ class App(tk.Tk):
|
|||||||
self.logger = logger.getChild(self.__class__.__name__)
|
self.logger = logger.getChild(self.__class__.__name__)
|
||||||
self._vmr = vmr
|
self._vmr = vmr
|
||||||
self._vmr.event.add(["pdirty", "ldirty"])
|
self._vmr.event.add(["pdirty", "ldirty"])
|
||||||
self.after(12000 if self._vmr.gui.launched_by_api else 1, self.start_updates)
|
self.subject = Subject()
|
||||||
|
self.start_updates()
|
||||||
self._vmr.init_thread()
|
self._vmr.init_thread()
|
||||||
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))
|
||||||
self.minsize(275, False)
|
self.minsize(275, False)
|
||||||
self.subject = Subject()
|
|
||||||
self._configs = None
|
self._configs = None
|
||||||
self["menu"] = Menus(self, vmr)
|
self.protocol("WM_DELETE_WINDOW", self.on_close_window)
|
||||||
|
self.menu = self["menu"] = Menus(self, vmr)
|
||||||
self.styletable = ttk.Style()
|
self.styletable = ttk.Style()
|
||||||
if _configuration.config:
|
if _configuration.config:
|
||||||
vmr.apply_config(_configuration.config)
|
vmr.apply_config(_configuration.config)
|
||||||
@@ -58,11 +61,7 @@ class App(tk.Tk):
|
|||||||
self.drag_id = ""
|
self.drag_id = ""
|
||||||
self.bind("<Configure>", self.dragging)
|
self.bind("<Configure>", self.dragging)
|
||||||
|
|
||||||
def start_updates(self):
|
self.after(1, self.healthcheck_step)
|
||||||
self.logger.debug("updates started")
|
|
||||||
_base_values.run_update = True
|
|
||||||
if self._vmr.gui.launched_by_api:
|
|
||||||
self.on_pdirty()
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{type(self).__name__}App"
|
return f"{type(self).__name__}App"
|
||||||
@@ -123,7 +122,7 @@ class App(tk.Tk):
|
|||||||
|
|
||||||
Destroy all top level frames.
|
Destroy all top level frames.
|
||||||
"""
|
"""
|
||||||
self.target.subject.remove(self)
|
self.target.subject.remove([self.on_pdirty, self.on_ldirty])
|
||||||
self.subject.clear()
|
self.subject.clear()
|
||||||
[
|
[
|
||||||
frame.destroy()
|
frame.destroy()
|
||||||
@@ -145,9 +144,55 @@ class App(tk.Tk):
|
|||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def userconfigs(self):
|
def userconfigs(self):
|
||||||
self._configs = loader(self.kind.name)
|
self._configs = loader(self.kind.name, self.target)
|
||||||
return self._configs
|
return self._configs
|
||||||
|
|
||||||
|
def start_updates(self):
|
||||||
|
def init():
|
||||||
|
self.logger.debug("updates started")
|
||||||
|
_base_values.run_update = True
|
||||||
|
|
||||||
|
if self._vmr.gui.launched_by_api:
|
||||||
|
self.subject.notify("pdirty")
|
||||||
|
self.after(12000, init)
|
||||||
|
else:
|
||||||
|
init()
|
||||||
|
|
||||||
|
def healthcheck_step(self):
|
||||||
|
if not _base_values.vban_connected:
|
||||||
|
try:
|
||||||
|
self._vmr.version
|
||||||
|
except voicemeeterlib.error.CAPIError:
|
||||||
|
resp = messagebox.askyesno(message="Restart Voicemeeter GUI?")
|
||||||
|
if resp:
|
||||||
|
self.logger.debug(
|
||||||
|
"healthcheck failed, rebuilding the app after GUI restart."
|
||||||
|
)
|
||||||
|
self._vmr.end_thread()
|
||||||
|
self._vmr.run_voicemeeter(self._vmr.kind.name)
|
||||||
|
_base_values.run_update = False
|
||||||
|
self._vmr.init_thread()
|
||||||
|
self.after(8000, self.start_updates)
|
||||||
|
self._destroy_top_level_frames()
|
||||||
|
self.build_app(self._vmr.kind)
|
||||||
|
vban_config = get_configuration("vban")
|
||||||
|
for i, _ in enumerate(vban_config):
|
||||||
|
target = getattr(self.menu, f"menu_vban_{i+1}")
|
||||||
|
target.entryconfig(0, state="normal")
|
||||||
|
target.entryconfig(1, state="disabled")
|
||||||
|
[
|
||||||
|
self.menu.menu_vban.entryconfig(j, state="normal")
|
||||||
|
for j, _ in enumerate(self.menu.menu_vban.winfo_children())
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
self.destroy()
|
||||||
|
self.after(250, self.healthcheck_step)
|
||||||
|
|
||||||
|
def on_close_window(self):
|
||||||
|
if _base_values.vban_connected:
|
||||||
|
self._vban.logout()
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
|
|
||||||
_apps = {kind.name: App.make(kind) for kind in _kinds_all}
|
_apps = {kind.name: App.make(kind) for kind in _kinds_all}
|
||||||
|
|
||||||
|
|||||||
@@ -249,7 +249,13 @@ class ChannelLabelFrameBuilder(AbstractBuilder):
|
|||||||
self.scale.bind("<Double-Button-1>", self.labelframe.reset_gain)
|
self.scale.bind("<Double-Button-1>", self.labelframe.reset_gain)
|
||||||
self.scale.bind("<Button-1>", self.labelframe.scale_press)
|
self.scale.bind("<Button-1>", self.labelframe.scale_press)
|
||||||
self.scale.bind("<ButtonRelease-1>", self.labelframe.scale_release)
|
self.scale.bind("<ButtonRelease-1>", self.labelframe.scale_release)
|
||||||
self.scale.bind("<MouseWheel>", self.labelframe._on_mousewheel)
|
self.scale.bind(
|
||||||
|
"<MouseWheel>",
|
||||||
|
partial(
|
||||||
|
self.labelframe.pause_updates,
|
||||||
|
self.labelframe._on_mousewheel,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
def add_gain_label(self):
|
def add_gain_label(self):
|
||||||
self.labelframe.gain_label = ttk.Label(
|
self.labelframe.gain_label = ttk.Label(
|
||||||
@@ -263,7 +269,7 @@ class ChannelLabelFrameBuilder(AbstractBuilder):
|
|||||||
self.button_mute = ttk.Checkbutton(
|
self.button_mute = ttk.Checkbutton(
|
||||||
self.labelframe,
|
self.labelframe,
|
||||||
text="MUTE",
|
text="MUTE",
|
||||||
command=partial(self.labelframe.toggle_mute, "mute"),
|
command=partial(self.labelframe.pause_updates, self.labelframe.toggle_mute),
|
||||||
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{self.identifier}Mute{self.index}.TButton'}",
|
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{self.identifier}Mute{self.index}.TButton'}",
|
||||||
variable=self.labelframe.mute,
|
variable=self.labelframe.mute,
|
||||||
)
|
)
|
||||||
@@ -283,7 +289,7 @@ class ChannelLabelFrameBuilder(AbstractBuilder):
|
|||||||
self.button_on = ttk.Checkbutton(
|
self.button_on = ttk.Checkbutton(
|
||||||
self.labelframe,
|
self.labelframe,
|
||||||
text="ON",
|
text="ON",
|
||||||
command=self.labelframe.set_on,
|
command=partial(self.labelframe.pause_updates, self.labelframe.set_on),
|
||||||
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{self.identifier}On{self.index}.TButton'}",
|
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{self.identifier}On{self.index}.TButton'}",
|
||||||
variable=self.labelframe.on,
|
variable=self.labelframe.on,
|
||||||
)
|
)
|
||||||
@@ -486,7 +492,9 @@ class StripConfigFrameBuilder(ChannelConfigFrameBuilder):
|
|||||||
ttk.Checkbutton(
|
ttk.Checkbutton(
|
||||||
self.configframe,
|
self.configframe,
|
||||||
text=param,
|
text=param,
|
||||||
command=partial(self.configframe.toggle_a, param),
|
command=partial(
|
||||||
|
self.configframe.pause_updates, self.configframe.toggle_a, param
|
||||||
|
),
|
||||||
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{param}.TButton'}",
|
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{param}.TButton'}",
|
||||||
variable=self.configframe.phys_out_params_vars[
|
variable=self.configframe.phys_out_params_vars[
|
||||||
self.configframe.phys_out_params.index(param)
|
self.configframe.phys_out_params.index(param)
|
||||||
@@ -507,7 +515,9 @@ class StripConfigFrameBuilder(ChannelConfigFrameBuilder):
|
|||||||
ttk.Checkbutton(
|
ttk.Checkbutton(
|
||||||
self.configframe,
|
self.configframe,
|
||||||
text=param,
|
text=param,
|
||||||
command=partial(self.configframe.toggle_b, param),
|
command=partial(
|
||||||
|
self.configframe.pause_updates, self.configframe.toggle_b, param
|
||||||
|
),
|
||||||
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{param}.TButton'}",
|
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{param}.TButton'}",
|
||||||
variable=self.configframe.virt_out_params_vars[
|
variable=self.configframe.virt_out_params_vars[
|
||||||
self.configframe.virt_out_params.index(param)
|
self.configframe.virt_out_params.index(param)
|
||||||
@@ -528,7 +538,9 @@ class StripConfigFrameBuilder(ChannelConfigFrameBuilder):
|
|||||||
ttk.Checkbutton(
|
ttk.Checkbutton(
|
||||||
self.configframe,
|
self.configframe,
|
||||||
text=param,
|
text=param,
|
||||||
command=partial(self.configframe.toggle_p, param),
|
command=partial(
|
||||||
|
self.configframe.pause_updates, self.configframe.toggle_p, param
|
||||||
|
),
|
||||||
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{param}.TButton'}",
|
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{param}.TButton'}",
|
||||||
variable=self.configframe.param_vars[i],
|
variable=self.configframe.param_vars[i],
|
||||||
)
|
)
|
||||||
@@ -578,10 +590,16 @@ class BusConfigFrameBuilder(ChannelConfigFrameBuilder):
|
|||||||
column=0, row=0, columnspan=2, sticky=(tk.W)
|
column=0, row=0, columnspan=2, sticky=(tk.W)
|
||||||
)
|
)
|
||||||
self.configframe.busmode_button.bind(
|
self.configframe.busmode_button.bind(
|
||||||
"<Button-1>", self.configframe.rotate_bus_modes_right
|
"<Button-1>",
|
||||||
|
partial(
|
||||||
|
self.configframe.pause_updates, self.configframe.rotate_bus_modes_right
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.configframe.busmode_button.bind(
|
self.configframe.busmode_button.bind(
|
||||||
"<Button-3>", self.configframe.rotate_bus_modes_left
|
"<Button-3>",
|
||||||
|
partial(
|
||||||
|
self.configframe.pause_updates, self.configframe.rotate_bus_modes_left
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_param_buttons(self):
|
def create_param_buttons(self):
|
||||||
@@ -589,7 +607,9 @@ class BusConfigFrameBuilder(ChannelConfigFrameBuilder):
|
|||||||
ttk.Checkbutton(
|
ttk.Checkbutton(
|
||||||
self.configframe,
|
self.configframe,
|
||||||
text=param,
|
text=param,
|
||||||
command=partial(self.configframe.toggle_p, param),
|
command=partial(
|
||||||
|
self.configframe.pause_updates, self.configframe.toggle_p, param
|
||||||
|
),
|
||||||
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{param}.TButton'}",
|
style=f"{'Toggle.TButton' if _configuration.themes_enabled else f'{param}.TButton'}",
|
||||||
variable=self.configframe.param_vars[i],
|
variable=self.configframe.param_vars[i],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -88,17 +88,27 @@ class ChannelLabelFrame(ttk.LabelFrame):
|
|||||||
self.parent.target.event.add("ldirty")
|
self.parent.target.event.add("ldirty")
|
||||||
self.after(500, self.resume_updates)
|
self.after(500, self.resume_updates)
|
||||||
|
|
||||||
|
def pause_updates(self, func, *args):
|
||||||
|
"""function wrapper, adds a 50ms delay on updates"""
|
||||||
|
_base_values.run_update = False
|
||||||
|
|
||||||
|
func(*args)
|
||||||
|
|
||||||
|
self.after(50, self.resume_updates)
|
||||||
|
|
||||||
def resume_updates(self):
|
def resume_updates(self):
|
||||||
_base_values.run_update = True
|
_base_values.run_update = True
|
||||||
|
|
||||||
def _on_mousewheel(self, event):
|
def _on_mousewheel(self, event):
|
||||||
_base_values.run_update = False
|
|
||||||
self.gain.set(
|
self.gain.set(
|
||||||
self.gain.get()
|
round(
|
||||||
+ (
|
self.gain.get()
|
||||||
_configuration.mwscroll_step
|
+ (
|
||||||
if event.delta > 0
|
_configuration.mwscroll_step
|
||||||
else -_configuration.mwscroll_step
|
if event.delta > 0
|
||||||
|
else -_configuration.mwscroll_step
|
||||||
|
),
|
||||||
|
1,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if self.gain.get() > 12:
|
if self.gain.get() > 12:
|
||||||
@@ -106,7 +116,7 @@ class ChannelLabelFrame(ttk.LabelFrame):
|
|||||||
elif self.gain.get() < -60:
|
elif self.gain.get() < -60:
|
||||||
self.gain.set(-60)
|
self.gain.set(-60)
|
||||||
self.setter("gain", self.gain.get())
|
self.setter("gain", self.gain.get())
|
||||||
self.after(1, self.resume_updates)
|
self.gainlabel.set(round(self.gain.get(), 1))
|
||||||
|
|
||||||
def open_config(self):
|
def open_config(self):
|
||||||
if self.conf.get():
|
if self.conf.get():
|
||||||
@@ -141,17 +151,18 @@ class ChannelLabelFrame(ttk.LabelFrame):
|
|||||||
def sync_labels(self):
|
def sync_labels(self):
|
||||||
"""sync labelframes according to label text"""
|
"""sync labelframes according to label text"""
|
||||||
retval = self.getter("label")
|
retval = self.getter("label")
|
||||||
self.parent.label_cache[self.id].insert(self.index, retval)
|
if self.parent.label_cache[self.id][self.index] != retval:
|
||||||
if len(retval) > 10:
|
self.parent.label_cache[self.id][self.index] = retval
|
||||||
retval = f"{retval[:8]}.."
|
if len(retval) > 10:
|
||||||
if not retval:
|
retval = f"{retval[:8]}.."
|
||||||
self.parent.columnconfigure(self.index, minsize=0)
|
if not retval:
|
||||||
self.parent.parent.subject.remove(self)
|
self.parent.columnconfigure(self.index, minsize=0)
|
||||||
self.grid_remove()
|
self.parent.parent.subject.remove(self)
|
||||||
else:
|
self.grid_remove()
|
||||||
self.parent.parent.subject.add(self)
|
else:
|
||||||
self.grid()
|
self.parent.parent.subject.add(self)
|
||||||
self.configure(text=retval)
|
self.grid()
|
||||||
|
self.configure(text=retval)
|
||||||
|
|
||||||
def grid_configure(self):
|
def grid_configure(self):
|
||||||
self.grid(padx=_configuration.channel_xpadding, sticky=(tk.N, tk.S))
|
self.grid(padx=_configuration.channel_xpadding, sticky=(tk.N, tk.S))
|
||||||
@@ -218,15 +229,18 @@ class Bus(ChannelLabelFrame):
|
|||||||
|
|
||||||
|
|
||||||
class ChannelFrame(ttk.Frame):
|
class ChannelFrame(ttk.Frame):
|
||||||
label_cache = {"strip": list(), "bus": list()}
|
|
||||||
|
|
||||||
def init(self, parent, id):
|
def init(self, parent, id):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.id = id
|
self.id = id
|
||||||
self.phys_in, self.virt_in = parent.kind.ins
|
self.phys_in, self.virt_in = parent.kind.ins
|
||||||
self.phys_out, self.virt_out = parent.kind.outs
|
self.phys_out, self.virt_out = parent.kind.outs
|
||||||
|
self.label_cache = {
|
||||||
|
"strip": [""] * (self.phys_in + self.virt_in),
|
||||||
|
"bus": [""] * (self.phys_out + self.virt_out),
|
||||||
|
}
|
||||||
self.parent.subject.add(self)
|
self.parent.subject.add(self)
|
||||||
|
self.update_labels()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def target(self):
|
def target(self):
|
||||||
@@ -248,13 +262,13 @@ class ChannelFrame(ttk.Frame):
|
|||||||
if isinstance(frame, ttk.LabelFrame)
|
if isinstance(frame, ttk.LabelFrame)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def update_labels(self):
|
||||||
|
for labelframe in self.labelframes:
|
||||||
|
labelframe.on_update("labelframe")
|
||||||
|
|
||||||
def on_update(self, subject):
|
def on_update(self, subject):
|
||||||
if subject == "pdirty":
|
if subject == "pdirty":
|
||||||
target = getattr(self.target, self.id)
|
self.update_labels()
|
||||||
num = getattr(self.parent.kind, f"num_{self.id}")
|
|
||||||
if self.label_cache[self.id] != [target[i].label for i in range(num)]:
|
|
||||||
for labelframe in self.labelframes:
|
|
||||||
labelframe.on_update("labelframe")
|
|
||||||
|
|
||||||
def grid_configure(self):
|
def grid_configure(self):
|
||||||
[
|
[
|
||||||
@@ -270,7 +284,7 @@ class ChannelFrame(ttk.Frame):
|
|||||||
setattr(self.parent, f"{self.identifier}_frame", None)
|
setattr(self.parent, f"{self.identifier}_frame", None)
|
||||||
|
|
||||||
|
|
||||||
def _make_channelframe(parent, id):
|
def _make_channelframe(parent, identifier):
|
||||||
"""
|
"""
|
||||||
Creates a Channel Frame class of type strip or bus
|
Creates a Channel Frame class of type strip or bus
|
||||||
"""
|
"""
|
||||||
@@ -278,29 +292,33 @@ def _make_channelframe(parent, id):
|
|||||||
phys_in, virt_in = parent.kind.ins
|
phys_in, virt_in = parent.kind.ins
|
||||||
phys_out, virt_out = parent.kind.outs
|
phys_out, virt_out = parent.kind.outs
|
||||||
|
|
||||||
def init_labels(self, id):
|
def init_labels(self):
|
||||||
"""
|
"""
|
||||||
Grids each labelframe, grid_removes any without a label
|
Grids each labelframe, grid_removes any without a label
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for i, labelframe in enumerate(
|
for i, labelframe in enumerate(
|
||||||
getattr(self, "strips" if id == "strip" else "buses")
|
getattr(self, "strips" if identifier == "strip" else "buses")
|
||||||
):
|
):
|
||||||
labelframe.grid(row=0, column=i)
|
labelframe.grid(row=0, column=i)
|
||||||
if not labelframe.target.label:
|
label = labelframe.target.label
|
||||||
|
if not label:
|
||||||
self.columnconfigure(i, minsize=0)
|
self.columnconfigure(i, minsize=0)
|
||||||
labelframe.grid_remove()
|
labelframe.grid_remove()
|
||||||
|
self.label_cache[identifier][i] = label
|
||||||
|
|
||||||
def init_strip(self, *args, **kwargs):
|
def init_strip(self, *args, **kwargs):
|
||||||
self.init(parent, id)
|
self.init(parent, identifier)
|
||||||
self.strips = tuple(Strip(self, i, id) for i in range(phys_in + virt_in))
|
self.strips = tuple(
|
||||||
|
Strip(self, i, identifier) for i in range(phys_in + virt_in)
|
||||||
|
)
|
||||||
self.grid(row=0, column=0, sticky=(tk.W))
|
self.grid(row=0, column=0, sticky=(tk.W))
|
||||||
self.grid_configure()
|
self.grid_configure()
|
||||||
init_labels(self, id)
|
init_labels(self)
|
||||||
|
|
||||||
def init_bus(self, *args, **kwargs):
|
def init_bus(self, *args, **kwargs):
|
||||||
self.init(parent, id)
|
self.init(parent, identifier)
|
||||||
self.buses = tuple(Bus(self, i, id) for i in range(phys_out + virt_out))
|
self.buses = tuple(Bus(self, i, identifier) for i in range(phys_out + virt_out))
|
||||||
if _configuration.extended:
|
if _configuration.extended:
|
||||||
if _configuration.extends_horizontal:
|
if _configuration.extends_horizontal:
|
||||||
self.grid(row=0, column=2, sticky=(tk.W))
|
self.grid(row=0, column=2, sticky=(tk.W))
|
||||||
@@ -309,11 +327,11 @@ def _make_channelframe(parent, id):
|
|||||||
else:
|
else:
|
||||||
self.grid(row=0, column=0)
|
self.grid(row=0, column=0)
|
||||||
self.grid_configure()
|
self.grid_configure()
|
||||||
init_labels(self, id)
|
init_labels(self)
|
||||||
|
|
||||||
if id == "strip":
|
if identifier == "strip":
|
||||||
CHANNELFRAME_cls = type(
|
CHANNELFRAME_cls = type(
|
||||||
f"ChannelFrame{id.capitalize()}",
|
f"ChannelFrame{identifier.capitalize()}",
|
||||||
(ChannelFrame,),
|
(ChannelFrame,),
|
||||||
{
|
{
|
||||||
"__init__": init_strip,
|
"__init__": init_strip,
|
||||||
@@ -321,7 +339,7 @@ def _make_channelframe(parent, id):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
CHANNELFRAME_cls = type(
|
CHANNELFRAME_cls = type(
|
||||||
f"ChannelFrame{id.capitalize()}",
|
f"ChannelFrame{identifier.capitalize()}",
|
||||||
(ChannelFrame,),
|
(ChannelFrame,),
|
||||||
{
|
{
|
||||||
"__init__": init_bus,
|
"__init__": init_bus,
|
||||||
|
|||||||
@@ -68,6 +68,14 @@ class Config(ttk.Frame):
|
|||||||
self.parent.target.event.add("ldirty")
|
self.parent.target.event.add("ldirty")
|
||||||
self.after(350, self.resume_updates)
|
self.after(350, self.resume_updates)
|
||||||
|
|
||||||
|
def pause_updates(self, func, *args):
|
||||||
|
"""function wrapper, adds a 50ms delay on updates"""
|
||||||
|
_base_values.run_update = False
|
||||||
|
|
||||||
|
func(*args)
|
||||||
|
|
||||||
|
self.after(50, self.resume_updates)
|
||||||
|
|
||||||
def resume_updates(self):
|
def resume_updates(self):
|
||||||
_base_values.run_update = True
|
_base_values.run_update = True
|
||||||
|
|
||||||
|
|||||||
@@ -10,28 +10,32 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
configuration = {}
|
configuration = {}
|
||||||
|
|
||||||
configpaths = [
|
|
||||||
Path.cwd() / "configs",
|
|
||||||
Path.home() / ".config" / "vm-compact" / "configs",
|
|
||||||
Path.home() / "Documents" / "Voicemeeter" / "configs",
|
|
||||||
]
|
|
||||||
for configpath in configpaths:
|
|
||||||
if configpath.is_dir():
|
|
||||||
filepaths = list(configpath.glob("*.toml"))
|
|
||||||
if any(f.stem in ("app", "vban") for f in filepaths):
|
|
||||||
configs = {}
|
|
||||||
for filepath in filepaths:
|
|
||||||
filename = filepath.with_suffix("").stem
|
|
||||||
if filename in ("app", "vban"):
|
|
||||||
try:
|
|
||||||
with open(filepath, "rb") as f:
|
|
||||||
configs[filename] = tomllib.load(f)
|
|
||||||
logger.info(f"configuration: {filename} loaded into memory")
|
|
||||||
except tomllib.TOMLDecodeError:
|
|
||||||
logger.error(f"Invalid TOML config: configs/{filename.stem}")
|
|
||||||
|
|
||||||
configuration |= configs
|
def get_configpath():
|
||||||
break
|
configpaths = [
|
||||||
|
Path.cwd() / "configs",
|
||||||
|
Path.home() / ".config" / "vm-compact" / "configs",
|
||||||
|
Path.home() / "Documents" / "Voicemeeter" / "configs",
|
||||||
|
]
|
||||||
|
for configpath in configpaths:
|
||||||
|
if configpath.exists():
|
||||||
|
return configpath
|
||||||
|
|
||||||
|
|
||||||
|
if configpath := get_configpath():
|
||||||
|
filepaths = list(configpath.glob("*.toml"))
|
||||||
|
if any(f.stem in ("app", "vban") for f in filepaths):
|
||||||
|
configs = {}
|
||||||
|
for filepath in filepaths:
|
||||||
|
filename = filepath.with_suffix("").stem
|
||||||
|
if filename in ("app", "vban"):
|
||||||
|
try:
|
||||||
|
with open(filepath, "rb") as f:
|
||||||
|
configs[filename] = tomllib.load(f)
|
||||||
|
logger.info(f"configuration: {filename} loaded into memory")
|
||||||
|
except tomllib.TOMLDecodeError:
|
||||||
|
logger.error(f"Invalid TOML config: configs/{filename.stem}")
|
||||||
|
configuration |= configs
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
"configs": {
|
"configs": {
|
||||||
@@ -75,17 +79,20 @@ def get_configuration(key):
|
|||||||
return configuration[key]
|
return configuration[key]
|
||||||
|
|
||||||
|
|
||||||
def loader(kind_id):
|
def loader(kind_id, target):
|
||||||
configs = {}
|
configs = {"reset": target.configs["reset"]}
|
||||||
userconfigpath = Path.home() / ".config" / "vm-compact" / "configs" / kind_id
|
if configpath := get_configpath():
|
||||||
if userconfigpath.exists():
|
userconfigpath = configpath / kind_id
|
||||||
filepaths = list(userconfigpath.glob("*.toml"))
|
if userconfigpath.exists():
|
||||||
for filepath in filepaths:
|
filepaths = list(userconfigpath.glob("*.toml"))
|
||||||
identifier = filepath.with_suffix("").stem
|
for filepath in filepaths:
|
||||||
try:
|
identifier = filepath.with_suffix("").stem
|
||||||
with open(filepath, "rb") as f:
|
try:
|
||||||
configs[identifier] = tomllib.load(f)
|
with open(filepath, "rb") as f:
|
||||||
logger.info(f"loader: {identifier} loaded into memory")
|
configs[identifier] = tomllib.load(f)
|
||||||
except tomllib.TOMLDecodeError:
|
logger.info(f"loader: {identifier} loaded into memory")
|
||||||
logger.error(f"Invalid TOML config: configs/{filename.stem}")
|
except tomllib.TOMLDecodeError:
|
||||||
return configs
|
logger.error(f"Invalid TOML config: configs/{filename.stem}")
|
||||||
|
|
||||||
|
target.configs = configs
|
||||||
|
return target.configs
|
||||||
|
|||||||
@@ -78,6 +78,14 @@ class GainLayer(ttk.LabelFrame):
|
|||||||
self.parent.target.event.add("ldirty")
|
self.parent.target.event.add("ldirty")
|
||||||
self.after(500, self.resume_updates)
|
self.after(500, self.resume_updates)
|
||||||
|
|
||||||
|
def pause_updates(self, func, *args):
|
||||||
|
"""function wrapper, adds a 50ms delay on updates"""
|
||||||
|
_base_values.run_update = False
|
||||||
|
|
||||||
|
func(*args)
|
||||||
|
|
||||||
|
self.after(50, self.resume_updates)
|
||||||
|
|
||||||
def resume_updates(self):
|
def resume_updates(self):
|
||||||
_base_values.run_update = True
|
_base_values.run_update = True
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ import webbrowser
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
from tkinter import messagebox
|
from tkinter import messagebox
|
||||||
|
|
||||||
import sv_ttk
|
|
||||||
import vban_cmd
|
import vban_cmd
|
||||||
from vban_cmd.error import VBANCMDConnectionError
|
from vban_cmd.error import VBANCMDConnectionError
|
||||||
|
|
||||||
|
import sv_ttk
|
||||||
|
|
||||||
from .data import _base_values, _configuration, get_configuration, kind_get
|
from .data import _base_values, _configuration, get_configuration, kind_get
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -85,24 +86,16 @@ class Menus(tk.Menu):
|
|||||||
self.menu_configs_load = tk.Menu(self.menu_configs, tearoff=0)
|
self.menu_configs_load = tk.Menu(self.menu_configs, tearoff=0)
|
||||||
self.menu_configs.add_cascade(menu=self.menu_configs_load, label="Load config")
|
self.menu_configs.add_cascade(menu=self.menu_configs_load, label="Load config")
|
||||||
self.config_defaults = {"reset"}
|
self.config_defaults = {"reset"}
|
||||||
if len(self.target.configs) > len(self.config_defaults) and all(
|
if len(self.parent.userconfigs) > len(self.config_defaults) and all(
|
||||||
key in self.target.configs for key in self.config_defaults
|
key in self.parent.userconfigs for key in self.config_defaults
|
||||||
):
|
):
|
||||||
[
|
[
|
||||||
self.menu_configs_load.add_command(
|
self.menu_configs_load.add_command(
|
||||||
label=profile, command=partial(self.load_profile, profile)
|
label=profile, command=partial(self.load_profile, profile)
|
||||||
)
|
)
|
||||||
for profile in self.target.configs.keys()
|
for profile in self.parent.userconfigs.keys()
|
||||||
if profile not in self.config_defaults
|
if profile not in self.config_defaults
|
||||||
]
|
]
|
||||||
elif self.parent.userconfigs:
|
|
||||||
[
|
|
||||||
self.menu_configs_load.add_command(
|
|
||||||
label=name, command=partial(self.load_custom_profile, data)
|
|
||||||
)
|
|
||||||
for name, data in self.parent.userconfigs.items()
|
|
||||||
if name not in self.config_defaults
|
|
||||||
]
|
|
||||||
else:
|
else:
|
||||||
self.menu_configs.entryconfig(0, state="disabled")
|
self.menu_configs.entryconfig(0, state="disabled")
|
||||||
self.menu_configs.add_command(
|
self.menu_configs.add_command(
|
||||||
@@ -232,7 +225,10 @@ class Menus(tk.Menu):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def action_invoke_voicemeeter(self, cmd):
|
def action_invoke_voicemeeter(self, cmd):
|
||||||
getattr(self.target.command, cmd)()
|
if fn := getattr(self.target.command, cmd):
|
||||||
|
fn()
|
||||||
|
if cmd == "shutdown":
|
||||||
|
self.parent.on_close_window()
|
||||||
|
|
||||||
def action_set_voicemeeter(self, cmd, val=True):
|
def action_set_voicemeeter(self, cmd, val=True):
|
||||||
if cmd == "lock":
|
if cmd == "lock":
|
||||||
@@ -243,10 +239,14 @@ class Menus(tk.Menu):
|
|||||||
def load_custom_profile(self, profile):
|
def load_custom_profile(self, profile):
|
||||||
self.logger.info(f"loading user profile {profile}")
|
self.logger.info(f"loading user profile {profile}")
|
||||||
self.target.apply(profile)
|
self.target.apply(profile)
|
||||||
|
if not _base_values.run_update:
|
||||||
|
self.parent.subject.notify("pdirty")
|
||||||
|
|
||||||
def load_profile(self, profile):
|
def load_profile(self, profile):
|
||||||
self.logger.info(f"loading user profile {profile}")
|
self.logger.info(f"loading user profile {profile}")
|
||||||
self.target.apply_config(profile)
|
self.target.apply_config(profile)
|
||||||
|
if not _base_values.run_update:
|
||||||
|
self.parent.subject.notify("pdirty")
|
||||||
|
|
||||||
def load_defaults(self):
|
def load_defaults(self):
|
||||||
msg = (
|
msg = (
|
||||||
@@ -315,16 +315,13 @@ class Menus(tk.Menu):
|
|||||||
|
|
||||||
def menu_teardown(self, i):
|
def menu_teardown(self, i):
|
||||||
# remove config load menus
|
# remove config load menus
|
||||||
[
|
if len(self.parent.userconfigs) > len(self.config_defaults):
|
||||||
self.menu_configs_load.delete(key)
|
for profile in self.parent.userconfigs:
|
||||||
for key in self.target.configs.keys()
|
if profile not in self.config_defaults:
|
||||||
if key not in self.config_defaults
|
try:
|
||||||
]
|
self.menu_configs_load.delete(profile)
|
||||||
[
|
except tk._tkinter.tclError as e:
|
||||||
self.menu_configs_load.delete(key)
|
self.logger.warning(f"{type(e).__name__}: {e}")
|
||||||
for key in self.parent.userconfigs.keys()
|
|
||||||
if key not in self.config_defaults
|
|
||||||
]
|
|
||||||
|
|
||||||
[
|
[
|
||||||
self.menu_vban.entryconfig(j, state="disabled")
|
self.menu_vban.entryconfig(j, state="disabled")
|
||||||
@@ -333,24 +330,13 @@ class Menus(tk.Menu):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def menu_setup(self):
|
def menu_setup(self):
|
||||||
if len(self.target.configs) > len(self.config_defaults) and all(
|
if len(self.parent.userconfigs) > len(self.config_defaults):
|
||||||
key in self.target.configs for key in self.config_defaults
|
for profile in self.parent.userconfigs:
|
||||||
):
|
if profile not in self.config_defaults:
|
||||||
[
|
self.menu_configs_load.add_command(
|
||||||
self.menu_configs_load.add_command(
|
label=profile, command=partial(self.load_profile, profile)
|
||||||
label=profile, command=partial(self.load_profile, profile)
|
)
|
||||||
)
|
self.menu_configs.entryconfig(0, state="normal")
|
||||||
for profile in self.target.configs.keys()
|
|
||||||
if profile not in self.config_defaults
|
|
||||||
]
|
|
||||||
elif self.parent.userconfigs:
|
|
||||||
[
|
|
||||||
self.menu_configs_load.add_command(
|
|
||||||
label=name, command=partial(self.load_custom_profile, data)
|
|
||||||
)
|
|
||||||
for name, data in self.parent.userconfigs.items()
|
|
||||||
if name not in self.config_defaults
|
|
||||||
]
|
|
||||||
else:
|
else:
|
||||||
self.menu_configs.entryconfig(0, state="disabled")
|
self.menu_configs.entryconfig(0, state="disabled")
|
||||||
|
|
||||||
@@ -420,7 +406,7 @@ class Menus(tk.Menu):
|
|||||||
self.vban.logout()
|
self.vban.logout()
|
||||||
# build new app frames according to a kind
|
# build new app frames according to a kind
|
||||||
kind = kind_get(self.vmr.type)
|
kind = kind_get(self.vmr.type)
|
||||||
self.parent.build_app(kind, None)
|
self.parent.build_app(kind)
|
||||||
target_menu = getattr(self, f"menu_vban_{i+1}")
|
target_menu = getattr(self, f"menu_vban_{i+1}")
|
||||||
target_menu.entryconfig(0, state="normal")
|
target_menu.entryconfig(0, state="normal")
|
||||||
target_menu.entryconfig(1, state="disabled")
|
target_menu.entryconfig(1, state="disabled")
|
||||||
|
|||||||
Reference in New Issue
Block a user