Merge branch 'develop'
14
.gitignore
vendored
@@ -1,19 +1,7 @@
|
||||
build/
|
||||
dist/
|
||||
config.json
|
||||
VRC_ChatBox_translator.spec
|
||||
memo.txt
|
||||
app/
|
||||
booth/
|
||||
pyinstaller/
|
||||
VRCT.build/
|
||||
VRCT.dist/
|
||||
VRCT.onefile-build/
|
||||
VRCT.exe
|
||||
VRCT.spec
|
||||
deepl-translate/
|
||||
translators/
|
||||
test/
|
||||
*.pyc
|
||||
.vscode/
|
||||
lib/
|
||||
logs/
|
||||
65
README.jp.md
Normal file
@@ -0,0 +1,65 @@
|
||||
<div align="center">
|
||||
|
||||

|
||||
[](https://github.com/misyaguziya/VRCT/releases)
|
||||
[](https://github.com/misyaguziya/VRCT/releases)
|
||||
[](https://github.com/misyaguziya/VRCT/blob/master/LICENSE)
|
||||
[](https://misyaguziya.booth.pm/items/5155325)
|
||||
|
||||
| [English](./README.md) | **日本語** |
|
||||
|
||||
<h3>
|
||||
VRCTは翻訳や文字起こしでVRChatの会話をサポートするソフトウェアです。
|
||||
</h3>
|
||||
|
||||

|
||||
|
||||
<div align="left">
|
||||
|
||||
# ダウンロード&インストール
|
||||
好きな場所からダウンロードしてください。
|
||||
- [Github.com](https://github.com/misyaguziya/VRCT/releases/)
|
||||
- [BOOTH.pm](https://misyaguziya.booth.pm/items/5155325)
|
||||
|
||||
ダウンロードしてexeを起動するだけです。
|
||||
|
||||
# VRCTってなに?
|
||||
VRCTは話す言語の異なる人同士が会話を行うためにチャットもしくは音声の翻訳を行うことで会話をサポートするソフトウェアです。
|
||||
これらの機能はVRChat内で使用するために設計されています。
|
||||
※サポート対象外ですがその他の用途として映画鑑賞等でも使用されています。
|
||||
|
||||
VRCTはあなたの会話を以下でサポートをします。
|
||||
- 💬 **VRChatへのチャット送信機能**
|
||||
- 🌐 **翻訳機能**
|
||||
- 🎙 **マイクの文字起こし機能**
|
||||
- 🔈 **スピーカーの文字起こし機能**
|
||||
|
||||
# ドキュメント
|
||||
初期設定や基本機能、その他の機能についても記載してあります。
|
||||
- [Documents Link](https://mzsoftware.notion.site/VRCT-Documents-be79b7a165f64442ad8f326d86c22246?pvs=4)
|
||||
|
||||
# 使い方(Youtube)
|
||||
<div align="center">
|
||||
|
||||
[](https://www.youtube.com/watch?v=rUTad037n8Q)
|
||||
|
||||
<div align="left">
|
||||
|
||||
# pythonで実行したい場合
|
||||
1. 以下のバージョンのpythonをインストールしてください。
|
||||
`python version 3.11.5`
|
||||
2. packageのインストールとmain.pyを起動してください。
|
||||
```bash
|
||||
./install.bat
|
||||
python main.py
|
||||
```
|
||||
|
||||
## Author
|
||||
- [みしゃ(misyaguzi)](https://github.com/misyaguziya) (メイン開発)
|
||||
- [しいな(Shiina_12siy)](https://twitter.com/Shiina_12siy) (UI/UX, UI多言語対応)
|
||||
- [レラ](https://github.com/soumt-r) (翻訳:韓国語)
|
||||
- [どね]() (ロゴデザイン)
|
||||
|
||||
---
|
||||
|
||||
VRCT は VRChat によって承認されておらず、VRChat または VRChat の開発もしくは管理に公式に関与する者の見解や意見が反映されたものではありません。VRChat および関連するすべての財産は 米国VRChat, Incの商標または登録商標です。
|
||||
143
README.md
@@ -1,116 +1,65 @@
|
||||
<div align="center">
|
||||
|
||||

|
||||
[](https://github.com/misyaguziya/VRCT/releases)
|
||||
[](https://github.com/misyaguziya/VRCT/releases)
|
||||
[](https://github.com/misyaguziya/VRCT/blob/master/LICENSE)
|
||||
[](https://misyaguziya.booth.pm/items/5155325)
|
||||
|
||||
# VRCT (VRChat Chatbox Translator & Transcription)
|
||||
| **English** | [日本語](./README.jp.md) |
|
||||
|
||||
<h3>
|
||||
VRCT is software that supports VRChat conversations with translation and transcription.
|
||||
</h3>
|
||||
|
||||

|
||||
|
||||
<div align="left">
|
||||
|
||||
## Overview
|
||||
VRChatのChatBoxにOSC経由でメッセージを送信するツール
|
||||
翻訳エンジンを使用してメッセージとその翻訳部分を同時に送信することができる
|
||||
# Download & Install
|
||||
Download from anywhere you like.
|
||||
- [Github.com](https://github.com/misyaguziya/VRCT/releases/)
|
||||
- [BOOTH.pm](https://misyaguziya.booth.pm/items/5155325)
|
||||
|
||||
## Requirement
|
||||
- python 3.9.13
|
||||
- pillow
|
||||
- PyAudioWPatch
|
||||
- python-osc
|
||||
- customtkinter
|
||||
- deepl
|
||||
- deepl-translate(https://github.com/misyaguziya/deepl-translate)
|
||||
- translators(https://github.com/misyaguziya/translators)
|
||||
- custom_speech_recognition(https://github.com/misyaguziya/custom_speech_recognition)
|
||||
Just download and run the exe.
|
||||
|
||||
**deepl-translate/translators/custom_speech_recognitionについては追加実装をしています**
|
||||
**`pip install`でinstallした場合、動かないので注意**
|
||||
# What is VRCT?
|
||||
VRCT is software that supports conversations between people who speak different languages by providing chat or voice translation.
|
||||
These features are designed for use within VRChat.
|
||||
*Although not supported, it is also used for other purposes such as watching movies.
|
||||
|
||||
## install
|
||||
```bash
|
||||
./install.bat
|
||||
```
|
||||
VRCT supports your conversations with
|
||||
- 💬 **Send chat to VRChat**
|
||||
- 🌐 **Translation**
|
||||
- 🎙 **Transcription of audio from microphone**
|
||||
- 🔈 **Transcription of audio from Speaker**
|
||||
|
||||
## Usage
|
||||
```bash
|
||||
python VRCT.py
|
||||
```
|
||||
# Documents
|
||||
Initial setup, basic functions, and other features are also described.
|
||||
- [Documents Link](https://mzsoftware.notion.site/VRCT-Documents-be79b7a165f64442ad8f326d86c22246?pvs=4)
|
||||
|
||||
## Features
|
||||
# How to Use (YouTube)
|
||||
<div align="center">
|
||||
|
||||
### init
|
||||
0. VRChatのOSCを有効にする(重要)
|
||||
[](https://www.youtube.com/watch?v=rUTad037n8Q)
|
||||
|
||||
(任意)
|
||||
1. DeepLのAPIを使用するためにアカウント登録し、認証キーを取得する
|
||||
2. ギアアイコンのボタンでconfigウィンドウを開く
|
||||
3. ParameterタブのDeepL Auth Keyに認証キーを記載
|
||||
4. configウィンドウを閉じる
|
||||
<div align="left">
|
||||
|
||||
### Normal use
|
||||
1. メッセージボックスにメッセージを記入
|
||||
2. Enterキーを押し、メッセージを送信する
|
||||
|
||||
### About Checkboxes
|
||||
- translation: 翻訳の有効無効
|
||||
- voice2chatbox: マイクの音声を文字起こししてチャットボックスに送信する
|
||||
- speaker2log: スピーカーの音声から文字起こししてログに表示する
|
||||
- foreground: 最前面表示の有効無効
|
||||
|
||||
### About Textbox
|
||||
- log tab: すべてのログを表示
|
||||
- send tab: 送信したメッセージを表示
|
||||
- receive tab: 受信したメッセージを表示
|
||||
- system tab: 機能についてのメッセージを表示
|
||||
|
||||
### About Config Window
|
||||
- UI tab
|
||||
- Transparency: ウィンドウの透過度の調整
|
||||
- Appearance Theme: ウィンドウテーマを選択
|
||||
- UI Scaling: UIサイズを調整
|
||||
- Font Family: 表示フォントを選択
|
||||
- UI Language: UIの表示言語を選択
|
||||
- Translation tab
|
||||
- Select Translator: 翻訳エンジンの変更
|
||||
- Send Language: 送信するメッセージに対して翻訳する言語[source, target]を選択
|
||||
- Receive Language: 受信したメッセージに対して翻訳する言語[source, target]を選択
|
||||
- Transcription tab
|
||||
- Input Mic Host: マイクのホストAPIを選択
|
||||
- Input Mic Device: マイクを選択
|
||||
- Input Mic Voice Language: 入力する音声の言語
|
||||
- Input Mic Energy Threshold: 音声取得のしきい値
|
||||
- Check threshold point: Input Mic Energy Thresholdのしきい値を視覚化
|
||||
- Input Mic Dynamic Energy Threshold: 音声取得のしきい値の自動調整
|
||||
- Input Mic Phase Timeout: 文字起こしする音声時間の上限
|
||||
- Input Mic Record Timeout: 音声の区切りの無音時間
|
||||
- Input Mic Max Phrases: 保留する単語の上限
|
||||
- Input Mic Word Filter: MICの文字起こし時にWord Filterで設定した文字が入っていた場合にChatboxに表示しない (ex AAA,BBB,CCC)
|
||||
- Input Speaker Device: スピーカーを選択
|
||||
- Input Speaker Voice Language: 受信する音声の言語
|
||||
- Input Speaker Energy Threshold: 音声取得のしきい値
|
||||
- Check threshold point: Input Speaker Energy Thresholdのしきい値を視覚化
|
||||
- Input Speaker Dynamic Energy Threshold: 音声取得のしきい値の自動調整
|
||||
- Input Speaker Record Timeout: 音声の区切りの無音時間
|
||||
- Input Speaker Phase Timeout: 文字起こしする音声時間の上限
|
||||
- Input Speaker Max Phrases: 保留する単語の上限
|
||||
- Parameter tab
|
||||
- OSC IP address: 変更不要
|
||||
- OSC port: 変更不要
|
||||
- DeepL Auth key: DeepLの認証キーの設定
|
||||
- Message Format: 送信するメッセージのデコレーションの設定
|
||||
- [message]がメッセージボックスに記入したメッセージに置換される
|
||||
- [translation]が翻訳されたメッセージに置換される
|
||||
- 初期フォーマット:`[message]([translation])`
|
||||
- Others tab
|
||||
- Auto clear chat box: メッセージ送信後に書き込んだメッセージを空にする
|
||||
- **(New!) Notification XSOverlay: XSOverlayの通知機能を有効(VR only)**
|
||||
# If you want to run it in python
|
||||
1. Install the following version of python.
|
||||
`python version 3.11.5`
|
||||
2. Install package and run main.py.
|
||||
```bash
|
||||
./install.bat
|
||||
python main.py
|
||||
```
|
||||
|
||||
## Author
|
||||
みしゃ(misyaguzi)
|
||||
- Main開発
|
||||
- twitter: https://twitter.com/misya_ai
|
||||
- booth: https://misyaguziya.booth.pm/items/4814313
|
||||
- [みしゃ(misyaguzi)](https://github.com/misyaguziya) (Main Development)
|
||||
- [しいな(Shiina_12siy)](https://twitter.com/Shiina_12siy) (UI/UX, UI multilingual support)
|
||||
- [レラ](https://github.com/soumt-r) (Translation:Korean)
|
||||
- [どね]() (Logo Design)
|
||||
|
||||
しいな(Shiina_12siy)
|
||||
- Main開発, 翻訳(英語)
|
||||
---
|
||||
|
||||
レラ
|
||||
- 翻訳(韓国語)
|
||||
VRCT is not endorsed by VRChat and does not reflect the views or opinions of VRChat or anyone officially involved in producing or managing VRChat properties. VRChat and all associated properties are trademarks or registered trademarks of VRChat Inc. VRChat © VRChat Inc.
|
||||
125
README.txt
@@ -4,129 +4,14 @@
|
||||
# 概要
|
||||
VRChatで使用されるChatBoxをOSC経由でメッセージを送信するツールになります。
|
||||
翻訳エンジンを使用してメッセージとその翻訳部分を同時に送信することができます。
|
||||
(翻訳エンジンはDeepL,Google,Bingに対応)
|
||||
|
||||
# 使用方法
|
||||
初期設定時
|
||||
0. VRChatのOSCを有効にする(重要)
|
||||
|
||||
(任意)
|
||||
1. DeepLのAPIを使用するためにアカウント登録し、認証キーを取得する
|
||||
2. ギアアイコンのボタンでconfigウィンドウを開く
|
||||
3. ParameterタブのDeepL Auth Keyに認証キーを記載
|
||||
4. configウィンドウを閉じる
|
||||
|
||||
通常使用時
|
||||
1. メッセージボックスにメッセージを記入
|
||||
2. Enterキーを押し、メッセージを送信する
|
||||
|
||||
# その他の設定
|
||||
translation チェックボックス: 翻訳の有効無効
|
||||
voice2chatbox チェックボックス : マイクの音声を文字起こししてチャットボックスに送信する
|
||||
speaker2log チェックボックス : スピーカーの音声から文字起こししてログに表示する
|
||||
foreground チェックボックス: 最前面表示の有効無効
|
||||
|
||||
テキストボックス
|
||||
logタブ
|
||||
すべてのログを表示
|
||||
sendタブ
|
||||
送信したメッセージを表示
|
||||
receiveタブ
|
||||
受信したメッセージを表示
|
||||
systemタブ
|
||||
機能についてのメッセージを表示
|
||||
|
||||
configウィンドウ
|
||||
UIタブ
|
||||
Transparency: ウィンドウの透過度の調整
|
||||
Appearance Theme: ウィンドウテーマを選択
|
||||
UI Scaling: UIサイズを調整
|
||||
Font Family: 表示フォントを選択
|
||||
UI Language: UIの表示言語を選択
|
||||
Translationタブ
|
||||
Select Translator: 翻訳エンジンの変更
|
||||
Send Language: 送信するメッセージに対して翻訳する言語[source, target]を選択
|
||||
Receive Language: 受信したメッセージに対して翻訳する言語[source, target]を選択
|
||||
Transcriptionタブ
|
||||
Input Mic Host: マイクのホストAPIを選択
|
||||
Input Mic Device: マイクを選択
|
||||
Input Mic Voice Language: 入力する音声の言語
|
||||
Input Mic Energy Threshold: 音声取得のしきい値
|
||||
Check threshold point: Input Mic Energy Thresholdのしきい値を視覚化
|
||||
Input Mic Dynamic Energy Threshold: 音声取得のしきい値の自動調整
|
||||
Input Mic Record Timeout: 音声の区切りの無音時間
|
||||
Input Mic Phase Timeout: 文字起こしする音声時間の上限
|
||||
Input Mic Max Phrases: 保留する単語の上限
|
||||
Input Mic Word Filter: MICの文字起こし時にWord Filterで設定した文字が入っていた場合にChatboxに表示しない (ex AAA,BBB,CCC)
|
||||
Input Speaker Device: スピーカーを選択
|
||||
Input Speaker Voice Language: 受信する音声の言語
|
||||
Input Speaker Energy Threshold: 音声取得のしきい値
|
||||
Check threshold point: Input Speaker Energy Thresholdのしきい値を視覚化
|
||||
Input Speaker Dynamic Energy Threshold: 音声取得のしきい値の自動調整
|
||||
Input Speaker Record Timeout: 音声の区切りの無音時間
|
||||
Input Speaker Phase Timeout: 文字起こしする音声時間の上限
|
||||
Input Speaker Max Phrases: 保留する単語の上限
|
||||
Parameterタブ
|
||||
OSC IP address: 変更不要
|
||||
OSC port: 変更不要
|
||||
DeepL Auth key: DeepLの認証キーの設定
|
||||
Message Format: 送信するメッセージのデコレーションの設定
|
||||
[message]がメッセージボックスに記入したメッセージに置換される
|
||||
[translation]が翻訳されたメッセージに置換される
|
||||
初期フォーマット:"[message]([translation])"
|
||||
Othersタブ
|
||||
Auto clear chat box: メッセージ送信後に書き込んだメッセージを空にする
|
||||
(New!) Notification XSOverlay: XSOverlayの通知機能を有効(VR only)
|
||||
|
||||
設定の初期化
|
||||
config.jsonを削除
|
||||
ドキュメント(JP):https://mzsoftware.notion.site/VRCT-Documents-be79b7a165f64442ad8f326d86c22246?pvs=4
|
||||
|
||||
# お問い合わせ
|
||||
要望などはTwitterまで
|
||||
https://twitter.com/misya_ai
|
||||
Googleフォーム:https://t.co/lSlo4brZwm
|
||||
Twitter:https://twitter.com/misya_ai
|
||||
Github:https://github.com/misyaguziya/VRCT
|
||||
|
||||
# アップデート履歴
|
||||
[2023-05-29: v0.1b] v0.1b リリース
|
||||
[2023-05-30: v0.2b]
|
||||
- configボタンをギアアイコンに変更
|
||||
- 詳細情報のボタンを追加
|
||||
- 翻訳機能有効無効のチェックボックスを追加
|
||||
- 最前面表示の有効無効のチェックボックスを追加
|
||||
- いくつかのバグを修正
|
||||
[2023-06-03: v0.3b]
|
||||
- 全体的にUIを刷新
|
||||
- 透過機能を追加
|
||||
- テーマのLight/Dark/Systemのモードの変更機能を追加
|
||||
- UIのスケール変更機能を追加
|
||||
- フォントの変更機能を追加
|
||||
[2023-06-06: v0.4b]
|
||||
- 翻訳エンジンを追加
|
||||
- 入力と出力の翻訳言語を選択できるように変更
|
||||
[2023-06-20: v1.0]
|
||||
- マイクからの音声の文字起こし機能を追加
|
||||
- スピーカーからの音声の文字起こし機能を追加
|
||||
[2023-06-28: v1.1]
|
||||
- いくつかのバクを修正
|
||||
- 翻訳/文字起こし言語の表記を略称からわかりやすい文字に変更
|
||||
- 文字起こしの処理の軽量化
|
||||
[2023-07-05: v1.2]
|
||||
- 文字起こし精度の向上
|
||||
[2023-07-21: v1.3]
|
||||
- UIの表示言語を日本語/英語を選択できる機能を追加
|
||||
- Energy Thresholdの視覚化機能を追加
|
||||
- 文字起こしの誤認識対策のため、Word Filterを追加
|
||||
- WASAPI以外のHostAPIでもマイクとして使用できるようにHostAPIを選択できる機能を追加
|
||||
- メッセージ送信後に書き込んだメッセージを空にするか選択できる機能を追加
|
||||
- バグ対策のため、translation/voice2chatbox/speaker2log/foregroundは起動時はOFFになるように変更
|
||||
- バグ対策のため、スピーカーについて既定デバイス以外を選択した時にERRORが出るように変更
|
||||
- 半角入力時に一部の文字が書き込めないバグを修正
|
||||
[2023-07-22: v1.3.1]
|
||||
- UIの表示言語選択に韓国語を追加
|
||||
[2023-07-30: v1.3.2]
|
||||
- 試験的にXSOverlayへの通知機能を追加
|
||||
- checkbox ONの状態でもConfigを開けるように変更
|
||||
- 文字起こし言語の表示を修正
|
||||
- いくつかのバグを修正
|
||||
|
||||
# 注意事項
|
||||
再配布とかはやめてね
|
||||
[2023-10-21: 2.0.0] v2.0.0 リリース
|
||||
836
VRCT.py
@@ -1,836 +0,0 @@
|
||||
from time import sleep
|
||||
from os import path as os_path
|
||||
from json import load as json_load
|
||||
from json import dump as json_dump
|
||||
from queue import Queue
|
||||
import tkinter as tk
|
||||
import customtkinter
|
||||
from customtkinter import CTk, CTkFrame, CTkCheckBox, CTkFont, CTkButton, CTkImage, CTkTabview, CTkTextbox, CTkEntry
|
||||
from PIL.Image import open as Image_open
|
||||
from flashtext import KeywordProcessor
|
||||
|
||||
from threading import Thread
|
||||
from utils import save_json, print_textbox, thread_fnc, get_localized_text, widget_main_window_label_setter
|
||||
from osc_tools import send_typing, send_message, send_test_action, receive_osc_parameters
|
||||
from window_config import ToplevelWindowConfig
|
||||
from window_information import ToplevelWindowInformation
|
||||
from languages import transcription_lang, translators, translation_lang, selectable_languages
|
||||
from audio_utils import get_input_device_list, get_output_device_list, get_default_input_device, get_default_output_device
|
||||
from audio_recorder import SelectedMicRecorder, SelectedSpeakerRecorder
|
||||
from audio_transcriber import AudioTranscriber
|
||||
from translation import Translator
|
||||
from notification import notification_xsoverlay_for_vrct
|
||||
|
||||
class App(CTk):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# init instance
|
||||
self.translator = Translator()
|
||||
self.keyword_processor = KeywordProcessor()
|
||||
|
||||
# init config
|
||||
self.PATH_CONFIG = "./config.json"
|
||||
|
||||
## main window
|
||||
self.ENABLE_TRANSLATION = False
|
||||
# self.ENABLE_TRANSCRIPTION_SEND = False
|
||||
# self.ENABLE_TRANSCRIPTION_RECEIVE = False
|
||||
self.ENABLE_FOREGROUND = False
|
||||
## UI
|
||||
self.TRANSPARENCY = 100
|
||||
self.APPEARANCE_THEME = "System"
|
||||
self.UI_SCALING = "100%"
|
||||
self.FONT_FAMILY = "Yu Gothic UI"
|
||||
self.UI_LANGUAGE = "en"
|
||||
## Translation
|
||||
self.CHOICE_TRANSLATOR = translators[0]
|
||||
self.INPUT_SOURCE_LANG = list(translation_lang[self.CHOICE_TRANSLATOR]["source"].keys())[0]
|
||||
self.INPUT_TARGET_LANG = list(translation_lang[self.CHOICE_TRANSLATOR]["target"].keys())[1]
|
||||
self.OUTPUT_SOURCE_LANG = list(translation_lang[self.CHOICE_TRANSLATOR]["source"].keys())[1]
|
||||
self.OUTPUT_TARGET_LANG = list(translation_lang[self.CHOICE_TRANSLATOR]["target"].keys())[0]
|
||||
## Transcription Send
|
||||
self.CHOICE_MIC_HOST = get_default_input_device()["host"]["name"]
|
||||
self.CHOICE_MIC_DEVICE = get_default_input_device()["device"]["name"]
|
||||
self.INPUT_MIC_VOICE_LANGUAGE = list(transcription_lang.keys())[0]
|
||||
self.INPUT_MIC_ENERGY_THRESHOLD = 300
|
||||
self.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD = True
|
||||
self.INPUT_MIC_RECORD_TIMEOUT = 3
|
||||
self.INPUT_MIC_PHRASE_TIMEOUT = 3
|
||||
self.INPUT_MIC_MAX_PHRASES = 10
|
||||
self.INPUT_MIC_WORD_FILTER = []
|
||||
## Transcription Receive
|
||||
self.CHOICE_SPEAKER_DEVICE = get_default_output_device()["name"]
|
||||
self.INPUT_SPEAKER_VOICE_LANGUAGE = list(transcription_lang.keys())[1]
|
||||
self.INPUT_SPEAKER_ENERGY_THRESHOLD = 300
|
||||
self.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD = True
|
||||
self.INPUT_SPEAKER_RECORD_TIMEOUT = 3
|
||||
self.INPUT_SPEAKER_PHRASE_TIMEOUT = 3
|
||||
self.INPUT_SPEAKER_MAX_PHRASES = 10
|
||||
|
||||
## Parameter
|
||||
self.OSC_IP_ADDRESS = "127.0.0.1"
|
||||
self.OSC_PORT = 9000
|
||||
self.AUTH_KEYS = {
|
||||
"DeepL(web)": None,
|
||||
"DeepL(auth)": None,
|
||||
"Bing(web)": None,
|
||||
"Google(web)": None,
|
||||
}
|
||||
self.MESSAGE_FORMAT = "[message]([translation])"
|
||||
# Others
|
||||
self.ENABLE_AUTO_CLEAR_CHATBOX = False
|
||||
self.ENABLE_OSC = False
|
||||
self.ENABLE_NOTICE_XSOVERLAY =False
|
||||
|
||||
# load config
|
||||
if os_path.isfile(self.PATH_CONFIG) is not False:
|
||||
with open(self.PATH_CONFIG, 'r') as fp:
|
||||
config = json_load(fp)
|
||||
# main window
|
||||
# main windowは初期はすべてOFFにする
|
||||
# if "ENABLE_TRANSLATION" in config.keys():
|
||||
# if type(config["ENABLE_TRANSLATION"]) is bool:
|
||||
# self.ENABLE_TRANSLATION = config["ENABLE_TRANSLATION"]
|
||||
|
||||
# 環境に依ってマイクとスピーカーを同時起動するとエラーが発生するため、起動時は強制的にOFFにする
|
||||
# if "ENABLE_TRANSCRIPTION_SEND" in config.keys():
|
||||
# if type(config["ENABLE_TRANSCRIPTION_SEND"]) is bool:
|
||||
# self.ENABLE_TRANSCRIPTION_SEND = config["ENABLE_TRANSCRIPTION_SEND"]
|
||||
# if "ENABLE_TRANSCRIPTION_RECEIVE" in config.keys():
|
||||
# if type(config["ENABLE_TRANSCRIPTION_RECEIVE"]) is bool:
|
||||
# self.ENABLE_TRANSCRIPTION_RECEIVE = config["ENABLE_TRANSCRIPTION_RECEIVE"]
|
||||
|
||||
# if "ENABLE_FOREGROUND" in config.keys():
|
||||
# if type(config["ENABLE_FOREGROUND"]) is bool:
|
||||
# self.ENABLE_FOREGROUND = config["ENABLE_FOREGROUND"]
|
||||
|
||||
# tab ui
|
||||
if "TRANSPARENCY" in config.keys():
|
||||
if type(config["TRANSPARENCY"]) is int:
|
||||
if 0 <= config["TRANSPARENCY"] <= 100:
|
||||
self.TRANSPARENCY = config["TRANSPARENCY"]
|
||||
if "APPEARANCE_THEME" in config.keys():
|
||||
if config["APPEARANCE_THEME"] in ["Light", "Dark", "System"]:
|
||||
self.APPEARANCE_THEME = config["APPEARANCE_THEME"]
|
||||
if "UI_SCALING" in config.keys():
|
||||
if config["UI_SCALING"] in ["80%", "90%", "100%", "110%", "120%"]:
|
||||
self.UI_SCALING = config["UI_SCALING"]
|
||||
if "FONT_FAMILY" in config.keys():
|
||||
if config["FONT_FAMILY"] in list(tk.font.families()):
|
||||
self.FONT_FAMILY = config["FONT_FAMILY"]
|
||||
if "UI_LANGUAGE" in config.keys():
|
||||
if config["UI_LANGUAGE"] in list(selectable_languages.keys()):
|
||||
self.UI_LANGUAGE = config["UI_LANGUAGE"]
|
||||
|
||||
# translation
|
||||
if "CHOICE_TRANSLATOR" in config.keys():
|
||||
if config["CHOICE_TRANSLATOR"] in list(self.translator.translator_status.keys()):
|
||||
self.CHOICE_TRANSLATOR = config["CHOICE_TRANSLATOR"]
|
||||
if "INPUT_SOURCE_LANG" in config.keys():
|
||||
if config["INPUT_SOURCE_LANG"] in list(translation_lang[self.CHOICE_TRANSLATOR]["source"].keys()):
|
||||
self.INPUT_SOURCE_LANG = config["INPUT_SOURCE_LANG"]
|
||||
if "INPUT_TARGET_LANG" in config.keys():
|
||||
if config["INPUT_TARGET_LANG"] in list(translation_lang[self.CHOICE_TRANSLATOR]["target"].keys()):
|
||||
self.INPUT_TARGET_LANG = config["INPUT_TARGET_LANG"]
|
||||
if "OUTPUT_SOURCE_LANG" in config.keys():
|
||||
if config["OUTPUT_SOURCE_LANG"] in list(translation_lang[self.CHOICE_TRANSLATOR]["source"].keys()):
|
||||
self.OUTPUT_SOURCE_LANG = config["OUTPUT_SOURCE_LANG"]
|
||||
if "OUTPUT_TARGET_LANG" in config.keys():
|
||||
if config["OUTPUT_TARGET_LANG"] in list(translation_lang[self.CHOICE_TRANSLATOR]["target"].keys()):
|
||||
self.OUTPUT_TARGET_LANG = config["OUTPUT_TARGET_LANG"]
|
||||
|
||||
# Transcription
|
||||
if "CHOICE_MIC_HOST" in config.keys():
|
||||
if config["CHOICE_MIC_HOST"] in [host for host in get_input_device_list().keys()]:
|
||||
self.CHOICE_MIC_HOST = config["CHOICE_MIC_HOST"]
|
||||
if "CHOICE_MIC_DEVICE" in config.keys():
|
||||
if config["CHOICE_MIC_DEVICE"] in [device["name"] for device in get_input_device_list()[self.CHOICE_MIC_HOST]]:
|
||||
self.CHOICE_MIC_DEVICE = config["CHOICE_MIC_DEVICE"]
|
||||
if "INPUT_MIC_VOICE_LANGUAGE" in config.keys():
|
||||
if config["INPUT_MIC_VOICE_LANGUAGE"] in list(transcription_lang.keys()):
|
||||
self.INPUT_MIC_VOICE_LANGUAGE = config["INPUT_MIC_VOICE_LANGUAGE"]
|
||||
if "INPUT_MIC_ENERGY_THRESHOLD" in config.keys():
|
||||
if type(config["INPUT_MIC_ENERGY_THRESHOLD"]) is int:
|
||||
self.INPUT_MIC_ENERGY_THRESHOLD = config["INPUT_MIC_ENERGY_THRESHOLD"]
|
||||
if "INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD" in config.keys():
|
||||
if type(config["INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD"]) is bool:
|
||||
self.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD = config["INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD"]
|
||||
if "INPUT_MIC_RECORD_TIMEOUT" in config.keys():
|
||||
if type(config["INPUT_MIC_RECORD_TIMEOUT"]) is int:
|
||||
self.INPUT_MIC_RECORD_TIMEOUT = config["INPUT_MIC_RECORD_TIMEOUT"]
|
||||
if "INPUT_MIC_PHRASE_TIMEOUT" in config.keys():
|
||||
if type(config["INPUT_MIC_PHRASE_TIMEOUT"]) is int:
|
||||
self.INPUT_MIC_PHRASE_TIMEOUT = config["INPUT_MIC_PHRASE_TIMEOUT"]
|
||||
if "INPUT_MIC_MAX_PHRASES" in config.keys():
|
||||
if type(config["INPUT_MIC_MAX_PHRASES"]) is int:
|
||||
self.INPUT_MIC_MAX_PHRASES = config["INPUT_MIC_MAX_PHRASES"]
|
||||
if "INPUT_MIC_WORD_FILTER" in config.keys():
|
||||
if type(config["INPUT_MIC_WORD_FILTER"]) is list:
|
||||
self.INPUT_MIC_WORD_FILTER = config["INPUT_MIC_WORD_FILTER"]
|
||||
|
||||
if "CHOICE_SPEAKER_DEVICE" in config.keys():
|
||||
if config["CHOICE_SPEAKER_DEVICE"] in [device["name"] for device in get_output_device_list()]:
|
||||
speaker_device = [device for device in get_output_device_list() if device["name"] == config["CHOICE_SPEAKER_DEVICE"]][0]
|
||||
if get_default_output_device()["index"] == speaker_device["index"]:
|
||||
self.CHOICE_SPEAKER_DEVICE = config["CHOICE_SPEAKER_DEVICE"]
|
||||
if "INPUT_SPEAKER_VOICE_LANGUAGE" in config.keys():
|
||||
if config["INPUT_SPEAKER_VOICE_LANGUAGE"] in list(transcription_lang.keys()):
|
||||
self.INPUT_SPEAKER_VOICE_LANGUAGE = config["INPUT_SPEAKER_VOICE_LANGUAGE"]
|
||||
if "INPUT_SPEAKER_ENERGY_THRESHOLD" in config.keys():
|
||||
if type(config["INPUT_SPEAKER_ENERGY_THRESHOLD"]) is int:
|
||||
self.INPUT_SPEAKER_ENERGY_THRESHOLD = config["INPUT_SPEAKER_ENERGY_THRESHOLD"]
|
||||
if "INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD" in config.keys():
|
||||
if type(config["INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD"]) is bool:
|
||||
self.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD = config["INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD"]
|
||||
if "INPUT_SPEAKER_RECORD_TIMEOUT" in config.keys():
|
||||
if type(config["INPUT_SPEAKER_RECORD_TIMEOUT"]) is int:
|
||||
self.INPUT_SPEAKER_RECORD_TIMEOUT = config["INPUT_SPEAKER_RECORD_TIMEOUT"]
|
||||
if "INPUT_SPEAKER_PHRASE_TIMEOUT" in config.keys():
|
||||
if type(config["INPUT_SPEAKER_PHRASE_TIMEOUT"]) is int:
|
||||
self.INPUT_SPEAKER_PHRASE_TIMEOUT = config["INPUT_SPEAKER_PHRASE_TIMEOUT"]
|
||||
if "INPUT_SPEAKER_MAX_PHRASES" in config.keys():
|
||||
if type(config["INPUT_SPEAKER_MAX_PHRASES"]) is int:
|
||||
self.INPUT_MIC_MAX_PHRASES = config["INPUT_SPEAKER_MAX_PHRASES"]
|
||||
|
||||
# Parameter
|
||||
if "OSC_IP_ADDRESS" in config.keys():
|
||||
if type(config["OSC_IP_ADDRESS"]) is str:
|
||||
self.OSC_IP_ADDRESS = config["OSC_IP_ADDRESS"]
|
||||
if "OSC_PORT" in config.keys():
|
||||
if type(config["OSC_PORT"]) is int:
|
||||
self.OSC_PORT = config["OSC_PORT"]
|
||||
if "AUTH_KEYS" in config.keys():
|
||||
if type(config["AUTH_KEYS"]) is dict:
|
||||
if set(config["AUTH_KEYS"].keys()) == set(self.AUTH_KEYS.keys()):
|
||||
for key, value in config["AUTH_KEYS"].items():
|
||||
if type(value) is str:
|
||||
self.AUTH_KEYS[key] = config["AUTH_KEYS"][key]
|
||||
if "MESSAGE_FORMAT" in config.keys():
|
||||
if type(config["MESSAGE_FORMAT"]) is str:
|
||||
self.MESSAGE_FORMAT = config["MESSAGE_FORMAT"]
|
||||
|
||||
# Others
|
||||
if "ENABLE_AUTO_CLEAR_CHATBOX" in config.keys():
|
||||
if type(config["ENABLE_AUTO_CLEAR_CHATBOX"]) is bool:
|
||||
self.ENABLE_AUTO_CLEAR_CHATBOX = config["ENABLE_AUTO_CLEAR_CHATBOX"]
|
||||
if "ENABLE_NOTICE_XSOVERLAY" in config.keys():
|
||||
if type(config["ENABLE_NOTICE_XSOVERLAY"]) is bool:
|
||||
self.ENABLE_NOTICE_XSOVERLAY = config["ENABLE_NOTICE_XSOVERLAY"]
|
||||
|
||||
with open(self.PATH_CONFIG, 'w') as fp:
|
||||
config = {
|
||||
# "ENABLE_TRANSLATION": self.ENABLE_TRANSLATION,
|
||||
# "ENABLE_TRANSCRIPTION_SEND": self.ENABLE_TRANSCRIPTION_SEND,
|
||||
# "ENABLE_TRANSCRIPTION_RECEIVE": self.ENABLE_TRANSCRIPTION_RECEIVE,
|
||||
# "ENABLE_FOREGROUND": self.ENABLE_FOREGROUND,
|
||||
"TRANSPARENCY": self.TRANSPARENCY,
|
||||
"APPEARANCE_THEME": self.APPEARANCE_THEME,
|
||||
"UI_SCALING": self.UI_SCALING,
|
||||
"UI_LANGUAGE": self.UI_LANGUAGE,
|
||||
"FONT_FAMILY": self.FONT_FAMILY,
|
||||
"CHOICE_TRANSLATOR": self.CHOICE_TRANSLATOR,
|
||||
"INPUT_SOURCE_LANG": self.INPUT_SOURCE_LANG,
|
||||
"INPUT_TARGET_LANG": self.INPUT_TARGET_LANG,
|
||||
"OUTPUT_SOURCE_LANG": self.OUTPUT_SOURCE_LANG,
|
||||
"OUTPUT_TARGET_LANG": self.OUTPUT_TARGET_LANG,
|
||||
"CHOICE_MIC_HOST": self.CHOICE_MIC_HOST,
|
||||
"CHOICE_MIC_DEVICE": self.CHOICE_MIC_DEVICE,
|
||||
"INPUT_MIC_VOICE_LANGUAGE": self.INPUT_MIC_VOICE_LANGUAGE,
|
||||
"INPUT_MIC_ENERGY_THRESHOLD": self.INPUT_MIC_ENERGY_THRESHOLD,
|
||||
"INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD": self.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD,
|
||||
"INPUT_MIC_RECORD_TIMEOUT": self.INPUT_MIC_RECORD_TIMEOUT,
|
||||
"INPUT_MIC_PHRASE_TIMEOUT": self.INPUT_MIC_PHRASE_TIMEOUT,
|
||||
"INPUT_MIC_MAX_PHRASES": self.INPUT_MIC_MAX_PHRASES,
|
||||
"INPUT_MIC_WORD_FILTER": self.INPUT_MIC_WORD_FILTER,
|
||||
"CHOICE_SPEAKER_DEVICE": self.CHOICE_SPEAKER_DEVICE,
|
||||
"INPUT_SPEAKER_VOICE_LANGUAGE": self.INPUT_SPEAKER_VOICE_LANGUAGE,
|
||||
"INPUT_SPEAKER_ENERGY_THRESHOLD": self.INPUT_SPEAKER_ENERGY_THRESHOLD,
|
||||
"INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD": self.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD,
|
||||
"INPUT_SPEAKER_RECORD_TIMEOUT": self.INPUT_SPEAKER_RECORD_TIMEOUT,
|
||||
"INPUT_SPEAKER_PHRASE_TIMEOUT": self.INPUT_SPEAKER_PHRASE_TIMEOUT,
|
||||
"INPUT_SPEAKER_MAX_PHRASES": self.INPUT_SPEAKER_MAX_PHRASES,
|
||||
"OSC_IP_ADDRESS": self.OSC_IP_ADDRESS,
|
||||
"OSC_PORT": self.OSC_PORT,
|
||||
"AUTH_KEYS": self.AUTH_KEYS,
|
||||
"MESSAGE_FORMAT": self.MESSAGE_FORMAT,
|
||||
"ENABLE_AUTO_CLEAR_CHATBOX": self.ENABLE_AUTO_CLEAR_CHATBOX,
|
||||
"ENABLE_NOTICE_XSOVERLAY": self.ENABLE_NOTICE_XSOVERLAY,
|
||||
}
|
||||
json_dump(config, fp, indent=4)
|
||||
|
||||
## set UI theme
|
||||
customtkinter.set_appearance_mode(self.APPEARANCE_THEME)
|
||||
customtkinter.set_default_color_theme("blue")
|
||||
|
||||
# init main window
|
||||
self.iconbitmap(os_path.join(os_path.dirname(__file__), "img", "app.ico"))
|
||||
self.title("VRCT")
|
||||
self.geometry(f"{400}x{175}")
|
||||
self.minsize(400, 175)
|
||||
self.grid_columnconfigure(1, weight=1)
|
||||
self.grid_rowconfigure(0, weight=1)
|
||||
|
||||
# add sidebar left
|
||||
self.sidebar_frame = CTkFrame(self, corner_radius=0)
|
||||
self.sidebar_frame.grid(row=0, column=0, rowspan=4, sticky="nsw")
|
||||
self.sidebar_frame.grid_rowconfigure(5, weight=1)
|
||||
|
||||
init_lang_text = "Loading..."
|
||||
|
||||
# add checkbox translation
|
||||
self.checkbox_translation = CTkCheckBox(
|
||||
self.sidebar_frame,
|
||||
text=init_lang_text,
|
||||
onvalue=True,
|
||||
offvalue=False,
|
||||
command=self.checkbox_translation_callback,
|
||||
font=CTkFont(family=self.FONT_FAMILY)
|
||||
)
|
||||
self.checkbox_translation.grid(row=0, column=0, columnspan=2, padx=10, pady=(5, 5), sticky="we")
|
||||
|
||||
# add checkbox transcription send
|
||||
self.checkbox_transcription_send = CTkCheckBox(
|
||||
self.sidebar_frame,
|
||||
text=init_lang_text,
|
||||
onvalue=True,
|
||||
offvalue=False,
|
||||
command=self.checkbox_transcription_send_callback,
|
||||
font=CTkFont(family=self.FONT_FAMILY)
|
||||
)
|
||||
self.checkbox_transcription_send.grid(row=1, column=0, columnspan=2, padx=10, pady=(5, 5), sticky="we")
|
||||
|
||||
# add checkbox transcription receive
|
||||
self.checkbox_transcription_receive = CTkCheckBox(
|
||||
self.sidebar_frame,
|
||||
text=init_lang_text,
|
||||
onvalue=True,
|
||||
offvalue=False,
|
||||
command=self.checkbox_transcription_receive_callback,
|
||||
font=CTkFont(family=self.FONT_FAMILY)
|
||||
)
|
||||
self.checkbox_transcription_receive.grid(row=2, column=0, columnspan=2, padx=10, pady=(5, 5), sticky="we")
|
||||
|
||||
# add checkbox foreground
|
||||
self.checkbox_foreground = CTkCheckBox(
|
||||
self.sidebar_frame,
|
||||
text=init_lang_text,
|
||||
onvalue=True,
|
||||
offvalue=False,
|
||||
command=self.checkbox_foreground_callback,
|
||||
font=CTkFont(family=self.FONT_FAMILY)
|
||||
)
|
||||
self.checkbox_foreground.grid(row=3, column=0, columnspan=2, padx=10, pady=(5, 5), sticky="we")
|
||||
|
||||
# add button information
|
||||
self.button_information = CTkButton(
|
||||
self.sidebar_frame,
|
||||
text=None,
|
||||
width=36,
|
||||
command=self.button_information_callback,
|
||||
image=CTkImage(Image_open(os_path.join(os_path.dirname(__file__), "img", "info-icon-white.png")))
|
||||
)
|
||||
self.button_information.grid(row=5, column=0, padx=(10, 5), pady=(5, 5), sticky="wse")
|
||||
|
||||
# add button config
|
||||
self.button_config = CTkButton(
|
||||
self.sidebar_frame,
|
||||
text=None,
|
||||
width=36,
|
||||
command=self.button_config_callback,
|
||||
image=CTkImage(Image_open(os_path.join(os_path.dirname(__file__), "img", "config-icon-white.png")))
|
||||
)
|
||||
self.button_config.grid(row=5, column=1, padx=(5, 10), pady=(5, 5), sticky="wse")
|
||||
|
||||
# load ui language data
|
||||
language_yaml_data = get_localized_text(f"{self.UI_LANGUAGE}")
|
||||
# add tabview textbox
|
||||
self.add_tabview_logs(language_yaml_data)
|
||||
|
||||
# add entry message box
|
||||
self.entry_message_box = CTkEntry(
|
||||
self,
|
||||
placeholder_text="message",
|
||||
font=CTkFont(family=self.FONT_FAMILY),
|
||||
)
|
||||
self.entry_message_box.grid(row=1, column=1, columnspan=2, padx=5, pady=(5, 10), sticky="nsew")
|
||||
|
||||
# set default values
|
||||
## set translator
|
||||
if self.translator.authentication(self.CHOICE_TRANSLATOR, self.AUTH_KEYS[self.CHOICE_TRANSLATOR]) is False:
|
||||
# error update Auth key
|
||||
print_textbox(self.textbox_message_log, "Auth Key or language setting is incorrect", "ERROR")
|
||||
print_textbox(self.textbox_message_system_log, "Auth Key or language setting is incorrect", "ERROR")
|
||||
|
||||
# ## set checkbox enable translation
|
||||
# if self.ENABLE_TRANSLATION:
|
||||
# self.checkbox_translation.select()
|
||||
# self.checkbox_translation_callback()
|
||||
# else:
|
||||
# self.checkbox_translation.deselect()
|
||||
|
||||
# ## set checkbox enable transcription send
|
||||
# if self.ENABLE_TRANSCRIPTION_SEND:
|
||||
# self.checkbox_transcription_send.select()
|
||||
# self.checkbox_transcription_send_callback()
|
||||
# else:
|
||||
# self.checkbox_transcription_send.deselect()
|
||||
|
||||
# ## set checkbox enable transcription receive
|
||||
# if self.ENABLE_TRANSCRIPTION_RECEIVE:
|
||||
# self.checkbox_transcription_receive.select()
|
||||
# self.checkbox_transcription_receive_callback()
|
||||
# else:
|
||||
# self.checkbox_transcription_receive.deselect()
|
||||
|
||||
# ## set set checkbox enable foreground
|
||||
# if self.ENABLE_FOREGROUND:
|
||||
# self.checkbox_foreground.select()
|
||||
# self.checkbox_foreground_callback()
|
||||
# else:
|
||||
# self.checkbox_foreground.deselect()
|
||||
|
||||
## set word filter
|
||||
for f in self.INPUT_MIC_WORD_FILTER:
|
||||
self.keyword_processor.add_keyword(f)
|
||||
|
||||
## set bind entry message box
|
||||
self.entry_message_box.bind("<Return>", self.entry_message_box_press_key_enter)
|
||||
self.entry_message_box.bind("<Any-KeyPress>", self.entry_message_box_press_key_any)
|
||||
self.entry_message_box.bind("<Leave>", self.entry_message_box_leave)
|
||||
|
||||
## set transparency for main window
|
||||
self.wm_attributes("-alpha", self.TRANSPARENCY/100)
|
||||
|
||||
## set UI scale
|
||||
new_scaling_float = int(self.UI_SCALING.replace("%", "")) / 100
|
||||
customtkinter.set_widget_scaling(new_scaling_float)
|
||||
|
||||
# delete window
|
||||
self.protocol("WM_DELETE_WINDOW", self.delete_window)
|
||||
|
||||
self.config_window = ToplevelWindowConfig(self)
|
||||
self.information_window = ToplevelWindowInformation(self)
|
||||
|
||||
# start receive osc
|
||||
th_receive_osc_parameters = Thread(target=receive_osc_parameters, args=(self.check_osc_receive,))
|
||||
th_receive_osc_parameters.daemon = True
|
||||
th_receive_osc_parameters.start()
|
||||
|
||||
# check osc started
|
||||
send_test_action()
|
||||
|
||||
def button_config_callback(self):
|
||||
self.foreground_stop()
|
||||
self.transcription_stop()
|
||||
self.checkbox_translation.configure(state="disabled")
|
||||
self.checkbox_transcription_send.configure(state="disabled")
|
||||
self.checkbox_transcription_receive.configure(state="disabled")
|
||||
self.checkbox_foreground.configure(state="disabled")
|
||||
self.tabview_logs.configure(state="disabled")
|
||||
self.textbox_message_log.configure(state="disabled")
|
||||
self.textbox_message_send_log.configure(state="disabled")
|
||||
self.textbox_message_receive_log.configure(state="disabled")
|
||||
self.textbox_message_system_log.configure(state="disabled")
|
||||
self.entry_message_box.configure(state="disabled")
|
||||
self.button_config.configure(state="disabled", fg_color=["gray92", "gray14"])
|
||||
self.button_information.configure(state="disabled", fg_color=["gray92", "gray14"])
|
||||
self.config_window.deiconify()
|
||||
self.config_window.focus_set()
|
||||
self.config_window.focus()
|
||||
self.config_window.grab_set()
|
||||
|
||||
def button_information_callback(self):
|
||||
self.information_window.deiconify()
|
||||
self.information_window.focus_set()
|
||||
self.information_window.focus()
|
||||
|
||||
def checkbox_translation_callback(self):
|
||||
self.ENABLE_TRANSLATION = self.checkbox_translation.get()
|
||||
if self.ENABLE_TRANSLATION is True:
|
||||
print_textbox(self.textbox_message_log, "Start translation", "INFO")
|
||||
print_textbox(self.textbox_message_system_log, "Start translation", "INFO")
|
||||
else:
|
||||
print_textbox(self.textbox_message_log, "Stop translation", "INFO")
|
||||
print_textbox(self.textbox_message_system_log, "Stop translation", "INFO")
|
||||
|
||||
def transcription_send_start(self):
|
||||
self.mic_audio_queue = Queue()
|
||||
mic_device = [device for device in get_input_device_list()[self.CHOICE_MIC_HOST] if device["name"] == self.CHOICE_MIC_DEVICE][0]
|
||||
self.mic_audio_recorder = SelectedMicRecorder(
|
||||
mic_device,
|
||||
self.INPUT_MIC_ENERGY_THRESHOLD,
|
||||
self.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD,
|
||||
self.INPUT_MIC_RECORD_TIMEOUT,
|
||||
)
|
||||
self.mic_audio_recorder.record_into_queue(self.mic_audio_queue)
|
||||
self.mic_transcriber = AudioTranscriber(
|
||||
speaker=False,
|
||||
source=self.mic_audio_recorder.source,
|
||||
language=transcription_lang[self.INPUT_MIC_VOICE_LANGUAGE],
|
||||
phrase_timeout=self.INPUT_MIC_PHRASE_TIMEOUT,
|
||||
max_phrases=self.INPUT_MIC_MAX_PHRASES,
|
||||
)
|
||||
def mic_transcript_to_chatbox():
|
||||
self.mic_transcriber.transcribe_audio_queue(self.mic_audio_queue)
|
||||
message = self.mic_transcriber.get_transcript()
|
||||
if len(message) > 0:
|
||||
# word filter
|
||||
if len(self.keyword_processor.extract_keywords(message)) != 0:
|
||||
print_textbox(self.textbox_message_log, f"Detect WordFilter :{message}", "INFO")
|
||||
print_textbox(self.textbox_message_system_log, f"Detect WordFilter :{message}", "INFO")
|
||||
return
|
||||
|
||||
# translate
|
||||
if self.checkbox_translation.get() is False:
|
||||
voice_message = f"{message}"
|
||||
elif self.translator.translator_status[self.CHOICE_TRANSLATOR] is False:
|
||||
print_textbox(self.textbox_message_log, "Auth Key or language setting is incorrect", "ERROR")
|
||||
print_textbox(self.textbox_message_system_log, "Auth Key or language setting is incorrect", "ERROR")
|
||||
voice_message = f"{message}"
|
||||
else:
|
||||
result = self.translator.translate(
|
||||
translator_name=self.CHOICE_TRANSLATOR,
|
||||
source_language=self.INPUT_SOURCE_LANG,
|
||||
target_language=self.INPUT_TARGET_LANG,
|
||||
message=message
|
||||
)
|
||||
voice_message = self.MESSAGE_FORMAT.replace("[message]", message).replace("[translation]", result)
|
||||
|
||||
if self.checkbox_transcription_send.get() is True:
|
||||
if self.ENABLE_OSC is True:
|
||||
# send OSC message
|
||||
send_message(voice_message, self.OSC_IP_ADDRESS, self.OSC_PORT)
|
||||
else:
|
||||
print_textbox(self.textbox_message_log, "OSC is not enabled, please enable OSC and rejoin.", "ERROR")
|
||||
print_textbox(self.textbox_message_system_log, "OSC is not enabled, please enable OSC and rejoin.", "ERROR")
|
||||
# update textbox message log
|
||||
print_textbox(self.textbox_message_log, f"{voice_message}", "SEND")
|
||||
print_textbox(self.textbox_message_send_log, f"{voice_message}", "SEND")
|
||||
|
||||
self.mic_print_transcript = thread_fnc(mic_transcript_to_chatbox)
|
||||
self.mic_print_transcript.daemon = True
|
||||
self.mic_print_transcript.start()
|
||||
print_textbox(self.textbox_message_log, "Start voice2chatbox", "INFO")
|
||||
print_textbox(self.textbox_message_system_log, "Start voice2chatbox", "INFO")
|
||||
self.checkbox_transcription_send.configure(state="normal")
|
||||
self.checkbox_transcription_receive.configure(state="normal")
|
||||
self.button_config.configure(state="normal", fg_color=["#3B8ED0", "#1F6AA5"])
|
||||
|
||||
def transcription_send_stop(self):
|
||||
if isinstance(self.mic_print_transcript, thread_fnc):
|
||||
self.mic_print_transcript.stop()
|
||||
if self.mic_audio_recorder.stop != None:
|
||||
self.mic_audio_recorder.stop()
|
||||
self.mic_audio_recorder.stop = None
|
||||
|
||||
print_textbox(self.textbox_message_log, "Stop voice2chatbox", "INFO")
|
||||
print_textbox(self.textbox_message_system_log, "Stop voice2chatbox", "INFO")
|
||||
self.checkbox_transcription_send.configure(state="normal")
|
||||
self.checkbox_transcription_receive.configure(state="normal")
|
||||
self.button_config.configure(state="normal", fg_color=["#3B8ED0", "#1F6AA5"])
|
||||
|
||||
def transcription_send_stop_for_config(self):
|
||||
if isinstance(self.mic_print_transcript, thread_fnc):
|
||||
self.mic_print_transcript.stop()
|
||||
if self.mic_audio_recorder.stop != None:
|
||||
self.mic_audio_recorder.stop()
|
||||
self.mic_audio_recorder.stop = None
|
||||
|
||||
print_textbox(self.textbox_message_log, "Stop voice2chatbox", "INFO")
|
||||
print_textbox(self.textbox_message_system_log, "Stop voice2chatbox", "INFO")
|
||||
|
||||
def checkbox_transcription_send_callback(self):
|
||||
self.checkbox_transcription_send.configure(state="disabled")
|
||||
self.checkbox_transcription_receive.configure(state="disabled")
|
||||
self.button_config.configure(state="disabled", fg_color=["gray92", "gray14"])
|
||||
if self.checkbox_transcription_send.get() is True:
|
||||
th_transcription_send_start = Thread(target=self.transcription_send_start)
|
||||
th_transcription_send_start.daemon = True
|
||||
th_transcription_send_start.start()
|
||||
else:
|
||||
th_transcription_send_stop = Thread(target=self.transcription_send_stop)
|
||||
th_transcription_send_stop.daemon = True
|
||||
th_transcription_send_stop.start()
|
||||
|
||||
def transcription_receive_start(self):
|
||||
self.spk_audio_queue = Queue()
|
||||
spk_device = [device for device in get_output_device_list() if device["name"] == self.CHOICE_SPEAKER_DEVICE][0]
|
||||
self.spk_audio_recorder = SelectedSpeakerRecorder(
|
||||
spk_device,
|
||||
self.INPUT_SPEAKER_ENERGY_THRESHOLD,
|
||||
self.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD,
|
||||
self.INPUT_SPEAKER_RECORD_TIMEOUT,
|
||||
)
|
||||
self.spk_audio_recorder.record_into_queue(self.spk_audio_queue)
|
||||
self.spk_transcriber = AudioTranscriber(
|
||||
speaker=True,
|
||||
source=self.spk_audio_recorder.source,
|
||||
language=transcription_lang[self.INPUT_SPEAKER_VOICE_LANGUAGE],
|
||||
phrase_timeout=self.INPUT_SPEAKER_PHRASE_TIMEOUT,
|
||||
max_phrases=self.INPUT_SPEAKER_MAX_PHRASES,
|
||||
)
|
||||
|
||||
def spk_transcript_to_textbox():
|
||||
self.spk_transcriber.transcribe_audio_queue(self.spk_audio_queue)
|
||||
message = self.spk_transcriber.get_transcript()
|
||||
if len(message) > 0:
|
||||
# translate
|
||||
if self.checkbox_translation.get() is False:
|
||||
voice_message = f"{message}"
|
||||
elif self.translator.translator_status[self.CHOICE_TRANSLATOR] is False:
|
||||
print_textbox(self.textbox_message_log, "Auth Key or language setting is incorrect", "ERROR")
|
||||
print_textbox(self.textbox_message_system_log, "Auth Key or language setting is incorrect", "ERROR")
|
||||
voice_message = f"{message}"
|
||||
else:
|
||||
result = self.translator.translate(
|
||||
translator_name=self.CHOICE_TRANSLATOR,
|
||||
source_language=self.OUTPUT_SOURCE_LANG,
|
||||
target_language=self.OUTPUT_TARGET_LANG,
|
||||
message=message
|
||||
)
|
||||
voice_message = self.MESSAGE_FORMAT.replace("[message]", message).replace("[translation]", result)
|
||||
# send OSC message
|
||||
# send_message(voice_message, self.OSC_IP_ADDRESS, self.OSC_PORT)
|
||||
|
||||
if self.checkbox_transcription_receive.get() is True:
|
||||
# update textbox message receive log
|
||||
print_textbox(self.textbox_message_log, f"{voice_message}", "RECEIVE")
|
||||
print_textbox(self.textbox_message_receive_log, f"{voice_message}", "RECEIVE")
|
||||
if self.ENABLE_NOTICE_XSOVERLAY is True:
|
||||
notification_xsoverlay_for_vrct(content=f"{voice_message}")
|
||||
|
||||
self.spk_print_transcript = thread_fnc(spk_transcript_to_textbox)
|
||||
self.spk_print_transcript.daemon = True
|
||||
self.spk_print_transcript.start()
|
||||
print_textbox(self.textbox_message_log, "Start speaker2log", "INFO")
|
||||
print_textbox(self.textbox_message_system_log, "Start speaker2log", "INFO")
|
||||
self.checkbox_transcription_send.configure(state="normal")
|
||||
self.checkbox_transcription_receive.configure(state="normal")
|
||||
self.button_config.configure(state="normal", fg_color=["#3B8ED0", "#1F6AA5"])
|
||||
|
||||
def transcription_receive_stop(self):
|
||||
if isinstance(self.spk_print_transcript, thread_fnc):
|
||||
self.spk_print_transcript.stop()
|
||||
if self.spk_audio_recorder.stop != None:
|
||||
self.spk_audio_recorder.stop()
|
||||
self.spk_audio_recorder.stop = None
|
||||
|
||||
print_textbox(self.textbox_message_log, "Stop speaker2log", "INFO")
|
||||
print_textbox(self.textbox_message_system_log, "Stop speaker2log", "INFO")
|
||||
self.checkbox_transcription_send.configure(state="normal")
|
||||
self.checkbox_transcription_receive.configure(state="normal")
|
||||
self.button_config.configure(state="normal", fg_color=["#3B8ED0", "#1F6AA5"])
|
||||
|
||||
def transcription_receive_stop_for_config(self):
|
||||
if isinstance(self.spk_print_transcript, thread_fnc):
|
||||
self.spk_print_transcript.stop()
|
||||
if self.spk_audio_recorder.stop != None:
|
||||
self.spk_audio_recorder.stop()
|
||||
self.spk_audio_recorder.stop = None
|
||||
|
||||
print_textbox(self.textbox_message_log, "Stop speaker2log", "INFO")
|
||||
print_textbox(self.textbox_message_system_log, "Stop speaker2log", "INFO")
|
||||
|
||||
def checkbox_transcription_receive_callback(self):
|
||||
self.checkbox_transcription_send.configure(state="disabled")
|
||||
self.checkbox_transcription_receive.configure(state="disabled")
|
||||
self.button_config.configure(state="disabled", fg_color=["gray92", "gray14"])
|
||||
if self.checkbox_transcription_receive.get() is True:
|
||||
th_transcription_receive_start = Thread(target=self.transcription_receive_start)
|
||||
th_transcription_receive_start.daemon = True
|
||||
th_transcription_receive_start.start()
|
||||
else:
|
||||
th_transcription_receive_stop = Thread(target=self.transcription_receive_stop)
|
||||
th_transcription_receive_stop.daemon = True
|
||||
th_transcription_receive_stop.start()
|
||||
|
||||
def transcription_start(self):
|
||||
if self.checkbox_transcription_send.get() is True:
|
||||
th_transcription_send_start = Thread(target=self.transcription_send_start)
|
||||
th_transcription_send_start.daemon = True
|
||||
th_transcription_send_start.start()
|
||||
sleep(2)
|
||||
if self.checkbox_transcription_receive.get() is True:
|
||||
th_transcription_receive_start = Thread(target=self.transcription_receive_start)
|
||||
th_transcription_receive_start.daemon = True
|
||||
th_transcription_receive_start.start()
|
||||
|
||||
def transcription_stop(self):
|
||||
if self.checkbox_transcription_send.get() is True:
|
||||
th_transcription_send_stop = Thread(target=self.transcription_send_stop_for_config)
|
||||
th_transcription_send_stop.daemon = True
|
||||
th_transcription_send_stop.start()
|
||||
if self.checkbox_transcription_receive.get() is True:
|
||||
th_transcription_receive_stop = Thread(target=self.transcription_receive_stop_for_config)
|
||||
th_transcription_receive_stop.daemon = True
|
||||
th_transcription_receive_stop.start()
|
||||
|
||||
def checkbox_foreground_callback(self):
|
||||
self.ENABLE_FOREGROUND = self.checkbox_foreground.get()
|
||||
if self.ENABLE_FOREGROUND:
|
||||
self.attributes("-topmost", True)
|
||||
print_textbox(self.textbox_message_log, "Start foreground", "INFO")
|
||||
print_textbox(self.textbox_message_system_log, "Start foreground", "INFO")
|
||||
else:
|
||||
self.attributes("-topmost", False)
|
||||
print_textbox(self.textbox_message_log, "Stop foreground", "INFO")
|
||||
print_textbox(self.textbox_message_system_log, "Stop foreground", "INFO")
|
||||
|
||||
def foreground_start(self):
|
||||
self.ENABLE_FOREGROUND = self.checkbox_foreground.get()
|
||||
if self.ENABLE_FOREGROUND:
|
||||
self.attributes("-topmost", True)
|
||||
print_textbox(self.textbox_message_log, "Start foreground", "INFO")
|
||||
print_textbox(self.textbox_message_system_log, "Start foreground", "INFO")
|
||||
|
||||
def foreground_stop(self):
|
||||
if self.ENABLE_FOREGROUND:
|
||||
self.attributes("-topmost", False)
|
||||
print_textbox(self.textbox_message_log, "Stop foreground", "INFO")
|
||||
print_textbox(self.textbox_message_system_log, "Stop foreground", "INFO")
|
||||
self.ENABLE_FOREGROUND = False
|
||||
|
||||
def entry_message_box_press_key_enter(self, event):
|
||||
# send OSC typing
|
||||
send_typing(False, self.OSC_IP_ADDRESS, self.OSC_PORT)
|
||||
|
||||
if self.ENABLE_FOREGROUND:
|
||||
self.attributes("-topmost", True)
|
||||
|
||||
message = self.entry_message_box.get()
|
||||
if len(message) > 0:
|
||||
# translate
|
||||
if self.checkbox_translation.get() is False:
|
||||
chat_message = f"{message}"
|
||||
elif self.translator.translator_status[self.CHOICE_TRANSLATOR] is False:
|
||||
print_textbox(self.textbox_message_log, "Auth Key or language setting is incorrect", "ERROR")
|
||||
print_textbox(self.textbox_message_system_log, "Auth Key or language setting is incorrect", "ERROR")
|
||||
chat_message = f"{message}"
|
||||
else:
|
||||
result = self.translator.translate(
|
||||
translator_name=self.CHOICE_TRANSLATOR,
|
||||
source_language=self.INPUT_SOURCE_LANG,
|
||||
target_language=self.INPUT_TARGET_LANG,
|
||||
message=message
|
||||
)
|
||||
chat_message = self.MESSAGE_FORMAT.replace("[message]", message).replace("[translation]", result)
|
||||
|
||||
# send OSC message
|
||||
if self.ENABLE_OSC is True:
|
||||
send_message(chat_message, self.OSC_IP_ADDRESS, self.OSC_PORT)
|
||||
else:
|
||||
print_textbox(self.textbox_message_log, "OSC is not enabled, please enable OSC and rejoin.", "ERROR")
|
||||
print_textbox(self.textbox_message_system_log, "OSC is not enabled, please enable OSC and rejoin.", "ERROR")
|
||||
|
||||
# update textbox message log
|
||||
print_textbox(self.textbox_message_log, f"{chat_message}", "SEND")
|
||||
print_textbox(self.textbox_message_send_log, f"{chat_message}", "SEND")
|
||||
|
||||
# delete message in entry message box
|
||||
if self.ENABLE_AUTO_CLEAR_CHATBOX is True:
|
||||
self.entry_message_box.delete(0, customtkinter.END)
|
||||
|
||||
BREAK_KEYSYM_LIST = [
|
||||
"Delete", "Select", "Up", "Down", "Next", "End", "Print",
|
||||
"Prior","Insert","Home", "Left", "Clear", "Right", "Linefeed"
|
||||
]
|
||||
def entry_message_box_press_key_any(self, event):
|
||||
# send OSC typing
|
||||
send_typing(True, self.OSC_IP_ADDRESS, self.OSC_PORT)
|
||||
if self.ENABLE_FOREGROUND:
|
||||
self.attributes("-topmost", False)
|
||||
|
||||
if event.keysym != "??":
|
||||
if len(event.char) != 0 and event.keysym in self.BREAK_KEYSYM_LIST:
|
||||
self.entry_message_box.insert("end", event.char)
|
||||
return "break"
|
||||
|
||||
def entry_message_box_leave(self, event):
|
||||
# send OSC typing
|
||||
send_typing(False, self.OSC_IP_ADDRESS, self.OSC_PORT)
|
||||
if self.ENABLE_FOREGROUND:
|
||||
self.attributes("-topmost", True)
|
||||
|
||||
def delete_window(self):
|
||||
self.quit()
|
||||
self.destroy()
|
||||
|
||||
def delete_tabview_logs(self, pre_language_yaml_data):
|
||||
self.tabview_logs.delete(pre_language_yaml_data["main_tab_title_log"])
|
||||
self.tabview_logs.delete(pre_language_yaml_data["main_tab_title_send"])
|
||||
self.tabview_logs.delete(pre_language_yaml_data["main_tab_title_receive"])
|
||||
self.tabview_logs.delete(pre_language_yaml_data["main_tab_title_system"])
|
||||
|
||||
def add_tabview_logs(self, language_yaml_data):
|
||||
main_tab_title_log = language_yaml_data["main_tab_title_log"]
|
||||
main_tab_title_send = language_yaml_data["main_tab_title_send"]
|
||||
main_tab_title_receive = language_yaml_data["main_tab_title_receive"]
|
||||
main_tab_title_system = language_yaml_data["main_tab_title_system"]
|
||||
|
||||
# add tabview textbox
|
||||
self.tabview_logs = CTkTabview(master=self)
|
||||
self.tabview_logs.add(main_tab_title_log)
|
||||
self.tabview_logs.add(main_tab_title_send)
|
||||
self.tabview_logs.add(main_tab_title_receive)
|
||||
self.tabview_logs.add(main_tab_title_system)
|
||||
self.tabview_logs.grid(row=0, column=1, padx=0, pady=0, sticky="nsew")
|
||||
self.tabview_logs._segmented_button.configure(font=CTkFont(family=self.FONT_FAMILY))
|
||||
self.tabview_logs._segmented_button.grid(sticky="W")
|
||||
self.tabview_logs.tab(main_tab_title_log).grid_rowconfigure(0, weight=1)
|
||||
self.tabview_logs.tab(main_tab_title_log).grid_columnconfigure(0, weight=1)
|
||||
self.tabview_logs.tab(main_tab_title_send).grid_rowconfigure(0, weight=1)
|
||||
self.tabview_logs.tab(main_tab_title_send).grid_columnconfigure(0, weight=1)
|
||||
self.tabview_logs.tab(main_tab_title_receive).grid_rowconfigure(0, weight=1)
|
||||
self.tabview_logs.tab(main_tab_title_receive).grid_columnconfigure(0, weight=1)
|
||||
self.tabview_logs.tab(main_tab_title_system).grid_rowconfigure(0, weight=1)
|
||||
self.tabview_logs.tab(main_tab_title_system).grid_columnconfigure(0, weight=1)
|
||||
self.tabview_logs.configure(fg_color="transparent")
|
||||
|
||||
# add textbox message log
|
||||
self.textbox_message_log = CTkTextbox(
|
||||
self.tabview_logs.tab(main_tab_title_log),
|
||||
font=CTkFont(family=self.FONT_FAMILY)
|
||||
)
|
||||
self.textbox_message_log.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
|
||||
self.textbox_message_log.configure(state='disabled')
|
||||
|
||||
# add textbox message send log
|
||||
self.textbox_message_send_log = CTkTextbox(
|
||||
self.tabview_logs.tab(main_tab_title_send),
|
||||
font=CTkFont(family=self.FONT_FAMILY)
|
||||
)
|
||||
self.textbox_message_send_log.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
|
||||
self.textbox_message_send_log.configure(state='disabled')
|
||||
|
||||
# add textbox message receive log
|
||||
self.textbox_message_receive_log = CTkTextbox(
|
||||
self.tabview_logs.tab(main_tab_title_receive),
|
||||
font=CTkFont(family=self.FONT_FAMILY)
|
||||
)
|
||||
self.textbox_message_receive_log.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
|
||||
self.textbox_message_receive_log.configure(state='disabled')
|
||||
|
||||
# add textbox message system log
|
||||
self.textbox_message_system_log = CTkTextbox(
|
||||
self.tabview_logs.tab(main_tab_title_system),
|
||||
font=CTkFont(family=self.FONT_FAMILY)
|
||||
)
|
||||
self.textbox_message_system_log.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
|
||||
self.textbox_message_system_log.configure(state='disabled')
|
||||
|
||||
widget_main_window_label_setter(self, language_yaml_data)
|
||||
|
||||
def check_osc_receive(self, address, osc_arguments):
|
||||
if self.ENABLE_OSC is False:
|
||||
self.ENABLE_OSC = True
|
||||
# print(address, osc_arguments)
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
app = App()
|
||||
app.mainloop()
|
||||
except Exception as e:
|
||||
import traceback
|
||||
with open('error.log', 'a') as f:
|
||||
traceback.print_exc(file=f)
|
||||
4
batch/restart.bat
Normal file
@@ -0,0 +1,4 @@
|
||||
@if not "%~0"=="%~dp0.\%~nx0" start /min cmd /c,"%~dp0.\%~nx0" %* & goto :eof
|
||||
|
||||
taskkill /im %1 /F
|
||||
START "" %1
|
||||
11
batch/update.bat
Normal file
@@ -0,0 +1,11 @@
|
||||
@if not "%~0"=="%~dp0.\%~nx0" start /min cmd /c,"%~dp0.\%~nx0" %* & goto :eof
|
||||
|
||||
taskkill /im %1 /F
|
||||
ping -n 2 127.0.0.1 > nul
|
||||
del /f %1
|
||||
ping -n 2 127.0.0.1 > nul
|
||||
rename %2 %1
|
||||
ping -n 2 127.0.0.1 > nul
|
||||
if %3 == True (
|
||||
START "" %1
|
||||
)
|
||||
1
build.bat
Normal file
@@ -0,0 +1 @@
|
||||
pyinstaller --onedir --onefile --windowed --clean --icon="./img/vrct_logo_mark_black.ico" --add-data "./img;img/" --add-data "./locales;locales/" --add-data "./batch;batch/" --name VRCT --exclude-module numpy --exclude-module pandas --exclude-module matplotlib --exclude-module PyQt5 main.py
|
||||
594
config.py
Normal file
@@ -0,0 +1,594 @@
|
||||
import sys
|
||||
import inspect
|
||||
from os import path as os_path
|
||||
from json import load as json_load
|
||||
from json import dump as json_dump
|
||||
import tkinter as tk
|
||||
from tkinter import font
|
||||
from languages import selectable_languages
|
||||
from models.translation.translation_languages import translatorEngine
|
||||
from models.transcription.transcription_utils import getInputDevices, getDefaultInputDevice
|
||||
from utils import generatePercentageStringsList
|
||||
|
||||
json_serializable_vars = {}
|
||||
def json_serializable(var_name):
|
||||
def decorator(func):
|
||||
json_serializable_vars[var_name] = func
|
||||
return func
|
||||
return decorator
|
||||
|
||||
def saveJson(path, key, value):
|
||||
with open(path, "r", encoding="utf-8") as fp:
|
||||
json_data = json_load(fp)
|
||||
json_data[key] = value
|
||||
with open(path, "w", encoding="utf-8") as fp:
|
||||
json_dump(json_data, fp, indent=4, ensure_ascii=False)
|
||||
|
||||
class Config:
|
||||
_instance = None
|
||||
|
||||
def __new__(cls):
|
||||
if cls._instance is None:
|
||||
cls._instance = super(Config, cls).__new__(cls)
|
||||
cls._instance.init_config()
|
||||
cls._instance.load_config()
|
||||
return cls._instance
|
||||
|
||||
# Read Only
|
||||
@property
|
||||
def VERSION(self):
|
||||
return self._VERSION
|
||||
|
||||
@property
|
||||
def PATH_CONFIG(self):
|
||||
return self._PATH_CONFIG
|
||||
|
||||
@property
|
||||
def GITHUB_URL(self):
|
||||
return self._GITHUB_URL
|
||||
|
||||
@property
|
||||
def BOOTH_URL(self):
|
||||
return self._BOOTH_URL
|
||||
|
||||
@property
|
||||
def DOCUMENTS_URL(self):
|
||||
return self._DOCUMENTS_URL
|
||||
|
||||
@property
|
||||
def MAX_MIC_ENERGY_THRESHOLD(self):
|
||||
return self._MAX_MIC_ENERGY_THRESHOLD
|
||||
|
||||
@property
|
||||
def MAX_SPEAKER_ENERGY_THRESHOLD(self):
|
||||
return self._MAX_SPEAKER_ENERGY_THRESHOLD
|
||||
|
||||
# Read Write
|
||||
@property
|
||||
def ENABLE_TRANSLATION(self):
|
||||
return self._ENABLE_TRANSLATION
|
||||
|
||||
@ENABLE_TRANSLATION.setter
|
||||
def ENABLE_TRANSLATION(self, value):
|
||||
if type(value) is bool:
|
||||
self._ENABLE_TRANSLATION = value
|
||||
|
||||
@property
|
||||
def ENABLE_TRANSCRIPTION_SEND(self):
|
||||
return self._ENABLE_TRANSCRIPTION_SEND
|
||||
|
||||
@ENABLE_TRANSCRIPTION_SEND.setter
|
||||
def ENABLE_TRANSCRIPTION_SEND(self, value):
|
||||
if type(value) is bool:
|
||||
self._ENABLE_TRANSCRIPTION_SEND = value
|
||||
|
||||
@property
|
||||
def ENABLE_TRANSCRIPTION_RECEIVE(self):
|
||||
return self._ENABLE_TRANSCRIPTION_RECEIVE
|
||||
|
||||
@ENABLE_TRANSCRIPTION_RECEIVE.setter
|
||||
def ENABLE_TRANSCRIPTION_RECEIVE(self, value):
|
||||
if type(value) is bool:
|
||||
self._ENABLE_TRANSCRIPTION_RECEIVE = value
|
||||
|
||||
@property
|
||||
def ENABLE_FOREGROUND(self):
|
||||
return self._ENABLE_FOREGROUND
|
||||
|
||||
@ENABLE_FOREGROUND.setter
|
||||
def ENABLE_FOREGROUND(self, value):
|
||||
if type(value) is bool:
|
||||
self._ENABLE_FOREGROUND = value
|
||||
|
||||
@property
|
||||
def SOURCE_COUNTRY(self):
|
||||
return self._SOURCE_COUNTRY
|
||||
|
||||
@SOURCE_COUNTRY.setter
|
||||
def SOURCE_COUNTRY(self, value):
|
||||
if type(value) is str:
|
||||
self._SOURCE_COUNTRY = value
|
||||
|
||||
@property
|
||||
def SOURCE_LANGUAGE(self):
|
||||
return self._SOURCE_LANGUAGE
|
||||
|
||||
@SOURCE_LANGUAGE.setter
|
||||
def SOURCE_LANGUAGE(self, value):
|
||||
if type(value) is str:
|
||||
self._SOURCE_LANGUAGE = value
|
||||
|
||||
@property
|
||||
def TARGET_COUNTRY(self):
|
||||
return self._TARGET_COUNTRY
|
||||
|
||||
@TARGET_COUNTRY.setter
|
||||
def TARGET_COUNTRY(self, value):
|
||||
if type(value) is str:
|
||||
self._TARGET_COUNTRY = value
|
||||
|
||||
@property
|
||||
def TARGET_LANGUAGE(self):
|
||||
return self._TARGET_LANGUAGE
|
||||
|
||||
@TARGET_LANGUAGE.setter
|
||||
def TARGET_LANGUAGE(self, value):
|
||||
if type(value) is str:
|
||||
self._TARGET_LANGUAGE = value
|
||||
|
||||
@property
|
||||
def CHOICE_TRANSLATOR(self):
|
||||
return self._CHOICE_TRANSLATOR
|
||||
|
||||
@CHOICE_TRANSLATOR.setter
|
||||
def CHOICE_TRANSLATOR(self, value):
|
||||
if value in translatorEngine:
|
||||
self._CHOICE_TRANSLATOR = value
|
||||
|
||||
# Save Json Data
|
||||
## Main Window
|
||||
@property
|
||||
@json_serializable('SELECTED_TAB_NO')
|
||||
def SELECTED_TAB_NO(self):
|
||||
return self._SELECTED_TAB_NO
|
||||
|
||||
@SELECTED_TAB_NO.setter
|
||||
def SELECTED_TAB_NO(self, value):
|
||||
if type(value) is str:
|
||||
self._SELECTED_TAB_NO = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('SELECTED_TAB_YOUR_LANGUAGES')
|
||||
def SELECTED_TAB_YOUR_LANGUAGES(self):
|
||||
return self._SELECTED_TAB_YOUR_LANGUAGES
|
||||
|
||||
@SELECTED_TAB_YOUR_LANGUAGES.setter
|
||||
def SELECTED_TAB_YOUR_LANGUAGES(self, value):
|
||||
if type(value) is dict:
|
||||
self._SELECTED_TAB_YOUR_LANGUAGES = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('SELECTED_TAB_TARGET_LANGUAGES')
|
||||
def SELECTED_TAB_TARGET_LANGUAGES(self):
|
||||
return self._SELECTED_TAB_TARGET_LANGUAGES
|
||||
|
||||
@SELECTED_TAB_TARGET_LANGUAGES.setter
|
||||
def SELECTED_TAB_TARGET_LANGUAGES(self, value):
|
||||
if type(value) is dict:
|
||||
self._SELECTED_TAB_TARGET_LANGUAGES = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE')
|
||||
def IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE(self):
|
||||
return self._IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE
|
||||
|
||||
@IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE.setter
|
||||
def IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE(self, value):
|
||||
if type(value) is bool:
|
||||
self._IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
## Config Window
|
||||
@property
|
||||
@json_serializable('TRANSPARENCY')
|
||||
def TRANSPARENCY(self):
|
||||
return self._TRANSPARENCY
|
||||
|
||||
@TRANSPARENCY.setter
|
||||
def TRANSPARENCY(self, value):
|
||||
if type(value) is int and 0 <= value <= 100:
|
||||
self._TRANSPARENCY = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('APPEARANCE_THEME')
|
||||
def APPEARANCE_THEME(self):
|
||||
return self._APPEARANCE_THEME
|
||||
|
||||
@APPEARANCE_THEME.setter
|
||||
def APPEARANCE_THEME(self, value):
|
||||
if value in ["Light", "Dark", "System"]:
|
||||
self._APPEARANCE_THEME = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('UI_SCALING')
|
||||
def UI_SCALING(self):
|
||||
return self._UI_SCALING
|
||||
|
||||
@UI_SCALING.setter
|
||||
def UI_SCALING(self, value):
|
||||
if value in generatePercentageStringsList(start=40,end=200, step=10):
|
||||
self._UI_SCALING = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('FONT_FAMILY')
|
||||
def FONT_FAMILY(self):
|
||||
return self._FONT_FAMILY
|
||||
|
||||
@FONT_FAMILY.setter
|
||||
def FONT_FAMILY(self, value):
|
||||
root = tk.Tk()
|
||||
root.withdraw()
|
||||
if value in list(font.families()):
|
||||
self._FONT_FAMILY = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
root.destroy()
|
||||
|
||||
@property
|
||||
@json_serializable('UI_LANGUAGE')
|
||||
def UI_LANGUAGE(self):
|
||||
return self._UI_LANGUAGE
|
||||
|
||||
@UI_LANGUAGE.setter
|
||||
def UI_LANGUAGE(self, value):
|
||||
if value in list(selectable_languages.keys()):
|
||||
self._UI_LANGUAGE = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('CHOICE_MIC_HOST')
|
||||
def CHOICE_MIC_HOST(self):
|
||||
return self._CHOICE_MIC_HOST
|
||||
|
||||
@CHOICE_MIC_HOST.setter
|
||||
def CHOICE_MIC_HOST(self, value):
|
||||
if value in [host for host in getInputDevices().keys()]:
|
||||
self._CHOICE_MIC_HOST = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('CHOICE_MIC_DEVICE')
|
||||
def CHOICE_MIC_DEVICE(self):
|
||||
return self._CHOICE_MIC_DEVICE
|
||||
|
||||
@CHOICE_MIC_DEVICE.setter
|
||||
def CHOICE_MIC_DEVICE(self, value):
|
||||
if value in [device["name"] for device in getInputDevices()[self.CHOICE_MIC_HOST]]:
|
||||
self._CHOICE_MIC_DEVICE = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('INPUT_MIC_ENERGY_THRESHOLD')
|
||||
def INPUT_MIC_ENERGY_THRESHOLD(self):
|
||||
return self._INPUT_MIC_ENERGY_THRESHOLD
|
||||
|
||||
@INPUT_MIC_ENERGY_THRESHOLD.setter
|
||||
def INPUT_MIC_ENERGY_THRESHOLD(self, value):
|
||||
if type(value) is int:
|
||||
self._INPUT_MIC_ENERGY_THRESHOLD = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD')
|
||||
def INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD(self):
|
||||
return self._INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD
|
||||
|
||||
@INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD.setter
|
||||
def INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD(self, value):
|
||||
if type(value) is bool:
|
||||
self._INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('INPUT_MIC_RECORD_TIMEOUT')
|
||||
def INPUT_MIC_RECORD_TIMEOUT(self):
|
||||
return self._INPUT_MIC_RECORD_TIMEOUT
|
||||
|
||||
@INPUT_MIC_RECORD_TIMEOUT.setter
|
||||
def INPUT_MIC_RECORD_TIMEOUT(self, value):
|
||||
if type(value) is int:
|
||||
self._INPUT_MIC_RECORD_TIMEOUT = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('INPUT_MIC_PHRASE_TIMEOUT')
|
||||
def INPUT_MIC_PHRASE_TIMEOUT(self):
|
||||
return self._INPUT_MIC_PHRASE_TIMEOUT
|
||||
|
||||
@INPUT_MIC_PHRASE_TIMEOUT.setter
|
||||
def INPUT_MIC_PHRASE_TIMEOUT(self, value):
|
||||
if type(value) is int:
|
||||
self._INPUT_MIC_PHRASE_TIMEOUT = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('INPUT_MIC_MAX_PHRASES')
|
||||
def INPUT_MIC_MAX_PHRASES(self):
|
||||
return self._INPUT_MIC_MAX_PHRASES
|
||||
|
||||
@INPUT_MIC_MAX_PHRASES.setter
|
||||
def INPUT_MIC_MAX_PHRASES(self, value):
|
||||
if type(value) is int:
|
||||
self._INPUT_MIC_MAX_PHRASES = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('INPUT_MIC_WORD_FILTER')
|
||||
def INPUT_MIC_WORD_FILTER(self):
|
||||
return self._INPUT_MIC_WORD_FILTER
|
||||
|
||||
@INPUT_MIC_WORD_FILTER.setter
|
||||
def INPUT_MIC_WORD_FILTER(self, value):
|
||||
if type(value) is list:
|
||||
self._INPUT_MIC_WORD_FILTER = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('INPUT_SPEAKER_ENERGY_THRESHOLD')
|
||||
def INPUT_SPEAKER_ENERGY_THRESHOLD(self):
|
||||
return self._INPUT_SPEAKER_ENERGY_THRESHOLD
|
||||
|
||||
@INPUT_SPEAKER_ENERGY_THRESHOLD.setter
|
||||
def INPUT_SPEAKER_ENERGY_THRESHOLD(self, value):
|
||||
if type(value) is int:
|
||||
self._INPUT_SPEAKER_ENERGY_THRESHOLD = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD')
|
||||
def INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD(self):
|
||||
return self._INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD
|
||||
|
||||
@INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD.setter
|
||||
def INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD(self, value):
|
||||
if type(value) is bool:
|
||||
self._INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('INPUT_SPEAKER_RECORD_TIMEOUT')
|
||||
def INPUT_SPEAKER_RECORD_TIMEOUT(self):
|
||||
return self._INPUT_SPEAKER_RECORD_TIMEOUT
|
||||
|
||||
@INPUT_SPEAKER_RECORD_TIMEOUT.setter
|
||||
def INPUT_SPEAKER_RECORD_TIMEOUT(self, value):
|
||||
if type(value) is int:
|
||||
self._INPUT_SPEAKER_RECORD_TIMEOUT = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('INPUT_SPEAKER_PHRASE_TIMEOUT')
|
||||
def INPUT_SPEAKER_PHRASE_TIMEOUT(self):
|
||||
return self._INPUT_SPEAKER_PHRASE_TIMEOUT
|
||||
|
||||
@INPUT_SPEAKER_PHRASE_TIMEOUT.setter
|
||||
def INPUT_SPEAKER_PHRASE_TIMEOUT(self, value):
|
||||
if type(value) is int:
|
||||
self._INPUT_SPEAKER_PHRASE_TIMEOUT = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('INPUT_SPEAKER_MAX_PHRASES')
|
||||
def INPUT_SPEAKER_MAX_PHRASES(self):
|
||||
return self._INPUT_SPEAKER_MAX_PHRASES
|
||||
|
||||
@INPUT_SPEAKER_MAX_PHRASES.setter
|
||||
def INPUT_SPEAKER_MAX_PHRASES(self, value):
|
||||
if type(value) is int:
|
||||
self._INPUT_SPEAKER_MAX_PHRASES = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('OSC_IP_ADDRESS')
|
||||
def OSC_IP_ADDRESS(self):
|
||||
return self._OSC_IP_ADDRESS
|
||||
|
||||
@OSC_IP_ADDRESS.setter
|
||||
def OSC_IP_ADDRESS(self, value):
|
||||
if type(value) is str:
|
||||
self._OSC_IP_ADDRESS = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('OSC_PORT')
|
||||
def OSC_PORT(self):
|
||||
return self._OSC_PORT
|
||||
|
||||
@OSC_PORT.setter
|
||||
def OSC_PORT(self, value):
|
||||
if type(value) is int:
|
||||
self._OSC_PORT = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('AUTH_KEYS')
|
||||
def AUTH_KEYS(self):
|
||||
return self._AUTH_KEYS
|
||||
|
||||
@AUTH_KEYS.setter
|
||||
def AUTH_KEYS(self, value):
|
||||
if type(value) is dict and set(value.keys()) == set(self.AUTH_KEYS.keys()):
|
||||
for key, value in value.items():
|
||||
if type(value) is str:
|
||||
self._AUTH_KEYS[key] = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, self.AUTH_KEYS)
|
||||
|
||||
@property
|
||||
@json_serializable('MESSAGE_FORMAT')
|
||||
def MESSAGE_FORMAT(self):
|
||||
return self._MESSAGE_FORMAT
|
||||
|
||||
@MESSAGE_FORMAT.setter
|
||||
def MESSAGE_FORMAT(self, value):
|
||||
if type(value) is str:
|
||||
self._MESSAGE_FORMAT = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('ENABLE_AUTO_CLEAR_MESSAGE_BOX')
|
||||
def ENABLE_AUTO_CLEAR_MESSAGE_BOX(self):
|
||||
return self._ENABLE_AUTO_CLEAR_MESSAGE_BOX
|
||||
|
||||
@ENABLE_AUTO_CLEAR_MESSAGE_BOX.setter
|
||||
def ENABLE_AUTO_CLEAR_MESSAGE_BOX(self, value):
|
||||
if type(value) is bool:
|
||||
self._ENABLE_AUTO_CLEAR_MESSAGE_BOX = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('ENABLE_NOTICE_XSOVERLAY')
|
||||
def ENABLE_NOTICE_XSOVERLAY(self):
|
||||
return self._ENABLE_NOTICE_XSOVERLAY
|
||||
|
||||
@ENABLE_NOTICE_XSOVERLAY.setter
|
||||
def ENABLE_NOTICE_XSOVERLAY(self, value):
|
||||
if type(value) is bool:
|
||||
self._ENABLE_NOTICE_XSOVERLAY = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('ENABLE_SEND_MESSAGE_TO_VRC')
|
||||
def ENABLE_SEND_MESSAGE_TO_VRC(self):
|
||||
return self._ENABLE_SEND_MESSAGE_TO_VRC
|
||||
|
||||
@ENABLE_SEND_MESSAGE_TO_VRC.setter
|
||||
def ENABLE_SEND_MESSAGE_TO_VRC(self, value):
|
||||
if type(value) is bool:
|
||||
self._ENABLE_SEND_MESSAGE_TO_VRC = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
# [deprecated]
|
||||
# @property
|
||||
# @json_serializable('STARTUP_OSC_ENABLED_CHECK')
|
||||
# def STARTUP_OSC_ENABLED_CHECK(self):
|
||||
# return self._STARTUP_OSC_ENABLED_CHECK
|
||||
|
||||
# @STARTUP_OSC_ENABLED_CHECK.setter
|
||||
# def STARTUP_OSC_ENABLED_CHECK(self, value):
|
||||
# if type(value) is bool:
|
||||
# self._STARTUP_OSC_ENABLED_CHECK = value
|
||||
# saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('ENABLE_LOGGER')
|
||||
def ENABLE_LOGGER(self):
|
||||
return self._ENABLE_LOGGER
|
||||
|
||||
@ENABLE_LOGGER.setter
|
||||
def ENABLE_LOGGER(self, value):
|
||||
if type(value) is bool:
|
||||
self._ENABLE_LOGGER = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
@property
|
||||
@json_serializable('IS_CONFIG_WINDOW_COMPACT_MODE')
|
||||
def IS_CONFIG_WINDOW_COMPACT_MODE(self):
|
||||
return self._IS_CONFIG_WINDOW_COMPACT_MODE
|
||||
|
||||
@IS_CONFIG_WINDOW_COMPACT_MODE.setter
|
||||
def IS_CONFIG_WINDOW_COMPACT_MODE(self, value):
|
||||
if type(value) is bool:
|
||||
self._IS_CONFIG_WINDOW_COMPACT_MODE = value
|
||||
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
|
||||
|
||||
def init_config(self):
|
||||
# Read Only
|
||||
self._VERSION = "2.0.0"
|
||||
self._PATH_CONFIG = os_path.join(os_path.dirname(sys.argv[0]), "config.json")
|
||||
self._GITHUB_URL = "https://api.github.com/repos/misyaguziya/VRCT/releases/latest"
|
||||
self._BOOTH_URL = "https://misyaguziya.booth.pm/"
|
||||
self._DOCUMENTS_URL = "https://mzsoftware.notion.site/VRCT-Documents-be79b7a165f64442ad8f326d86c22246"
|
||||
self._MAX_MIC_ENERGY_THRESHOLD = 2000
|
||||
self._MAX_SPEAKER_ENERGY_THRESHOLD = 4000
|
||||
|
||||
# Read Write
|
||||
self._ENABLE_TRANSLATION = False
|
||||
self._ENABLE_TRANSCRIPTION_SEND = False
|
||||
self._ENABLE_TRANSCRIPTION_RECEIVE = False
|
||||
self._ENABLE_FOREGROUND = False
|
||||
self._CHOICE_TRANSLATOR = translatorEngine[0]
|
||||
self._SOURCE_LANGUAGE = "Japanese"
|
||||
self._SOURCE_COUNTRY = "Japan"
|
||||
self._TARGET_LANGUAGE = "English"
|
||||
self._TARGET_COUNTRY = "United States"
|
||||
|
||||
# Save Json Data
|
||||
## Main Window
|
||||
self._SELECTED_TAB_NO = "1"
|
||||
self._SELECTED_TAB_YOUR_LANGUAGES = {
|
||||
"1":"Japanese\n(Japan)",
|
||||
"2":"Japanese\n(Japan)",
|
||||
"3":"Japanese\n(Japan)",
|
||||
}
|
||||
self._SELECTED_TAB_TARGET_LANGUAGES = {
|
||||
"1":"English\n(United States)",
|
||||
"2":"English\n(United States)",
|
||||
"3":"English\n(United States)",
|
||||
}
|
||||
self._IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE = False
|
||||
|
||||
## Config Window
|
||||
self._TRANSPARENCY = 100
|
||||
self._APPEARANCE_THEME = "System"
|
||||
self._UI_SCALING = "100%"
|
||||
self._FONT_FAMILY = "Yu Gothic UI"
|
||||
self._UI_LANGUAGE = "en"
|
||||
self._CHOICE_MIC_HOST = getDefaultInputDevice()["host"]["name"]
|
||||
self._CHOICE_MIC_DEVICE = getDefaultInputDevice()["device"]["name"]
|
||||
self._INPUT_MIC_ENERGY_THRESHOLD = 300
|
||||
self._INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD = False
|
||||
self._INPUT_MIC_RECORD_TIMEOUT = 3
|
||||
self._INPUT_MIC_PHRASE_TIMEOUT = 3
|
||||
self._INPUT_MIC_MAX_PHRASES = 10
|
||||
self._INPUT_MIC_WORD_FILTER = []
|
||||
self._INPUT_SPEAKER_ENERGY_THRESHOLD = 300
|
||||
self._INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD = False
|
||||
self._INPUT_SPEAKER_RECORD_TIMEOUT = 3
|
||||
self._INPUT_SPEAKER_PHRASE_TIMEOUT = 3
|
||||
self._INPUT_SPEAKER_MAX_PHRASES = 10
|
||||
self._OSC_IP_ADDRESS = "127.0.0.1"
|
||||
self._OSC_PORT = 9000
|
||||
self._AUTH_KEYS = {
|
||||
"DeepL_API": None,
|
||||
"DeepL": None,
|
||||
"Bing": None,
|
||||
"Google": None,
|
||||
}
|
||||
self._MESSAGE_FORMAT = "[message]([translation])"
|
||||
self._ENABLE_AUTO_CLEAR_MESSAGE_BOX = True
|
||||
self._ENABLE_NOTICE_XSOVERLAY = False
|
||||
self._ENABLE_SEND_MESSAGE_TO_VRC = True
|
||||
# self._STARTUP_OSC_ENABLED_CHECK = True # [deprecated]
|
||||
self._ENABLE_LOGGER = False
|
||||
self._IS_CONFIG_WINDOW_COMPACT_MODE = False
|
||||
|
||||
def load_config(self):
|
||||
if os_path.isfile(self.PATH_CONFIG) is not False:
|
||||
with open(self.PATH_CONFIG, 'r', encoding="utf-8") as fp:
|
||||
config = json_load(fp)
|
||||
|
||||
for key in config.keys():
|
||||
setattr(self, key, config[key])
|
||||
|
||||
with open(self.PATH_CONFIG, 'w', encoding="utf-8") as fp:
|
||||
config = {}
|
||||
for var_name, var_func in json_serializable_vars.items():
|
||||
config[var_name] = var_func(self)
|
||||
json_dump(config, fp, indent=4, ensure_ascii=False)
|
||||
|
||||
config = Config()
|
||||
739
controller.py
Normal file
@@ -0,0 +1,739 @@
|
||||
from time import sleep
|
||||
from threading import Thread
|
||||
from config import config
|
||||
from model import model
|
||||
from view import view
|
||||
from utils import get_key_by_value
|
||||
from languages import selectable_languages
|
||||
|
||||
# Common
|
||||
def callbackUpdateSoftware():
|
||||
model.updateSoftware()
|
||||
|
||||
def callbackRestartSoftware():
|
||||
model.reStartSoftware()
|
||||
|
||||
# func transcription send message
|
||||
def sendMicMessage(message):
|
||||
if len(message) > 0:
|
||||
translation = ""
|
||||
if model.checkKeywords(message):
|
||||
view.printToTextbox_DetectedByWordFilter(detected_message=message)
|
||||
return
|
||||
elif config.ENABLE_TRANSLATION is False:
|
||||
pass
|
||||
else:
|
||||
translation = model.getInputTranslate(message)
|
||||
if translation == False:
|
||||
config.ENABLE_TRANSLATION = False
|
||||
translation = ""
|
||||
view.translationEngineLimitErrorProcess()
|
||||
|
||||
if config.ENABLE_TRANSCRIPTION_SEND is True:
|
||||
if config.ENABLE_SEND_MESSAGE_TO_VRC is True:
|
||||
if len(translation) > 0:
|
||||
osc_message = config.MESSAGE_FORMAT.replace("[message]", message)
|
||||
osc_message = osc_message.replace("[translation]", translation)
|
||||
else:
|
||||
osc_message = message
|
||||
model.oscSendMessage(osc_message)
|
||||
|
||||
view.printToTextbox_SentMessage(message, translation)
|
||||
if config.ENABLE_LOGGER is True:
|
||||
if len(translation) > 0:
|
||||
translation = f" ({translation})"
|
||||
model.logger.info(f"[SENT] {message}{translation}")
|
||||
|
||||
def startTranscriptionSendMessage():
|
||||
model.startMicTranscript(sendMicMessage, view.printToTextbox_TranscriptionSendNoDeviceError)
|
||||
view.setMainWindowAllWidgetsStatusToNormal()
|
||||
|
||||
def stopTranscriptionSendMessage():
|
||||
model.stopMicTranscript()
|
||||
view.setMainWindowAllWidgetsStatusToNormal()
|
||||
|
||||
def startThreadingTranscriptionSendMessage():
|
||||
view.printToTextbox_enableTranscriptionSend()
|
||||
th_startTranscriptionSendMessage = Thread(target=startTranscriptionSendMessage)
|
||||
th_startTranscriptionSendMessage.daemon = True
|
||||
th_startTranscriptionSendMessage.start()
|
||||
|
||||
def stopThreadingTranscriptionSendMessage():
|
||||
view.printToTextbox_disableTranscriptionSend()
|
||||
th_stopTranscriptionSendMessage = Thread(target=stopTranscriptionSendMessage)
|
||||
th_stopTranscriptionSendMessage.daemon = True
|
||||
th_stopTranscriptionSendMessage.start()
|
||||
|
||||
def startTranscriptionSendMessageOnCloseConfigWindow():
|
||||
model.startMicTranscript(sendMicMessage, view.printToTextbox_TranscriptionSendNoDeviceError)
|
||||
|
||||
def stopTranscriptionSendMessageOnOpenConfigWindow():
|
||||
model.stopMicTranscript()
|
||||
|
||||
def startThreadingTranscriptionSendMessageOnCloseConfigWindow():
|
||||
th_startTranscriptionSendMessage = Thread(target=startTranscriptionSendMessageOnCloseConfigWindow)
|
||||
th_startTranscriptionSendMessage.daemon = True
|
||||
th_startTranscriptionSendMessage.start()
|
||||
|
||||
def stopThreadingTranscriptionSendMessageOnOpenConfigWindow():
|
||||
th_stopTranscriptionSendMessage = Thread(target=stopTranscriptionSendMessageOnOpenConfigWindow)
|
||||
th_stopTranscriptionSendMessage.daemon = True
|
||||
th_stopTranscriptionSendMessage.start()
|
||||
|
||||
# func transcription receive message
|
||||
def receiveSpeakerMessage(message):
|
||||
if len(message) > 0:
|
||||
translation = ""
|
||||
if config.ENABLE_TRANSLATION is False:
|
||||
pass
|
||||
else:
|
||||
translation = model.getOutputTranslate(message)
|
||||
if translation == False:
|
||||
config.ENABLE_TRANSLATION = False
|
||||
translation = ""
|
||||
view.translationEngineLimitErrorProcess()
|
||||
|
||||
if config.ENABLE_TRANSCRIPTION_RECEIVE is True:
|
||||
if config.ENABLE_NOTICE_XSOVERLAY is True:
|
||||
if len(translation) > 0:
|
||||
xsoverlay_message = config.MESSAGE_FORMAT.replace("[message]", message)
|
||||
xsoverlay_message = xsoverlay_message.replace("[translation]", translation)
|
||||
else:
|
||||
xsoverlay_message = message
|
||||
model.notificationXSOverlay(xsoverlay_message)
|
||||
|
||||
# update textbox message log (Received)
|
||||
view.printToTextbox_ReceivedMessage(message, translation)
|
||||
if config.ENABLE_LOGGER is True:
|
||||
if len(translation) > 0:
|
||||
translation = f" ({translation})"
|
||||
model.logger.info(f"[RECEIVED] {message}{translation}")
|
||||
|
||||
def startTranscriptionReceiveMessage():
|
||||
model.startSpeakerTranscript(receiveSpeakerMessage, view.printToTextbox_TranscriptionReceiveNoDeviceError)
|
||||
view.setMainWindowAllWidgetsStatusToNormal()
|
||||
|
||||
def stopTranscriptionReceiveMessage():
|
||||
model.stopSpeakerTranscript()
|
||||
view.setMainWindowAllWidgetsStatusToNormal()
|
||||
|
||||
def startThreadingTranscriptionReceiveMessage():
|
||||
view.printToTextbox_enableTranscriptionReceive()
|
||||
th_startTranscriptionReceiveMessage = Thread(target=startTranscriptionReceiveMessage)
|
||||
th_startTranscriptionReceiveMessage.daemon = True
|
||||
th_startTranscriptionReceiveMessage.start()
|
||||
|
||||
def stopThreadingTranscriptionReceiveMessage():
|
||||
view.printToTextbox_disableTranscriptionReceive()
|
||||
th_stopTranscriptionReceiveMessage = Thread(target=stopTranscriptionReceiveMessage)
|
||||
th_stopTranscriptionReceiveMessage.daemon = True
|
||||
th_stopTranscriptionReceiveMessage.start()
|
||||
|
||||
def startTranscriptionReceiveMessageOnCloseConfigWindow():
|
||||
model.startSpeakerTranscript(receiveSpeakerMessage, view.printToTextbox_TranscriptionReceiveNoDeviceError)
|
||||
|
||||
|
||||
def stopTranscriptionReceiveMessageOnOpenConfigWindow():
|
||||
model.stopSpeakerTranscript()
|
||||
|
||||
def startThreadingTranscriptionReceiveMessageOnCloseConfigWindow():
|
||||
th_startTranscriptionReceiveMessage = Thread(target=startTranscriptionReceiveMessageOnCloseConfigWindow)
|
||||
th_startTranscriptionReceiveMessage.daemon = True
|
||||
th_startTranscriptionReceiveMessage.start()
|
||||
|
||||
def stopThreadingTranscriptionReceiveMessageOnOpenConfigWindow():
|
||||
th_stopTranscriptionReceiveMessage = Thread(target=stopTranscriptionReceiveMessageOnOpenConfigWindow)
|
||||
th_stopTranscriptionReceiveMessage.daemon = True
|
||||
th_stopTranscriptionReceiveMessage.start()
|
||||
|
||||
# func message box
|
||||
def sendChatMessage(message):
|
||||
if len(message) > 0:
|
||||
translation = ""
|
||||
if config.ENABLE_TRANSLATION is False:
|
||||
pass
|
||||
else:
|
||||
translation = model.getInputTranslate(message)
|
||||
if translation == False:
|
||||
config.ENABLE_TRANSLATION = False
|
||||
translation = ""
|
||||
view.translationEngineLimitErrorProcess()
|
||||
|
||||
# send OSC message
|
||||
if config.ENABLE_SEND_MESSAGE_TO_VRC is True:
|
||||
if len(translation) > 0:
|
||||
osc_message = config.MESSAGE_FORMAT.replace("[message]", message)
|
||||
osc_message = osc_message.replace("[translation]", translation)
|
||||
else:
|
||||
osc_message = message
|
||||
model.oscSendMessage(osc_message)
|
||||
|
||||
# update textbox message log (Sent)
|
||||
view.printToTextbox_SentMessage(message, translation)
|
||||
if config.ENABLE_LOGGER is True:
|
||||
if len(translation) > 0:
|
||||
translation = f" ({translation})"
|
||||
model.logger.info(f"[SENT] {message}{translation}")
|
||||
|
||||
# delete message in entry message box
|
||||
if config.ENABLE_AUTO_CLEAR_MESSAGE_BOX is True:
|
||||
view.clearMessageBox()
|
||||
|
||||
def messageBoxPressKeyEnter(e):
|
||||
model.oscStopSendTyping()
|
||||
message = view.getTextFromMessageBox()
|
||||
sendChatMessage(message)
|
||||
|
||||
def messageBoxPressKeyAny(e):
|
||||
if config.ENABLE_SEND_MESSAGE_TO_VRC is True:
|
||||
model.oscStartSendTyping()
|
||||
else:
|
||||
model.oscStopSendTyping()
|
||||
|
||||
def messageBoxFocusIn(e):
|
||||
view.foregroundOffIfForegroundEnabled()
|
||||
|
||||
def messageBoxFocusOut(e):
|
||||
view.foregroundOnIfForegroundEnabled()
|
||||
if config.ENABLE_SEND_MESSAGE_TO_VRC is True:
|
||||
model.oscStopSendTyping()
|
||||
|
||||
# func select languages
|
||||
def initSetLanguageAndCountry():
|
||||
select = config.SELECTED_TAB_YOUR_LANGUAGES[config.SELECTED_TAB_NO]
|
||||
language, country = model.getLanguageAndCountry(select)
|
||||
config.SOURCE_LANGUAGE = language
|
||||
config.SOURCE_COUNTRY = country
|
||||
select = config.SELECTED_TAB_TARGET_LANGUAGES[config.SELECTED_TAB_NO]
|
||||
language, country = model.getLanguageAndCountry(select)
|
||||
config.TARGET_LANGUAGE = language
|
||||
config.TARGET_COUNTRY = country
|
||||
config.CHOICE_TRANSLATOR = model.findTranslationEngine(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE)
|
||||
|
||||
def setYourLanguageAndCountry(select):
|
||||
languages = config.SELECTED_TAB_YOUR_LANGUAGES
|
||||
languages[config.SELECTED_TAB_NO] = select
|
||||
config.SELECTED_TAB_YOUR_LANGUAGES = languages
|
||||
language, country = model.getLanguageAndCountry(select)
|
||||
config.SOURCE_LANGUAGE = language
|
||||
config.SOURCE_COUNTRY = country
|
||||
config.CHOICE_TRANSLATOR = model.findTranslationEngine(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE)
|
||||
view.printToTextbox_selectedYourLanguages(select)
|
||||
|
||||
def setTargetLanguageAndCountry(select):
|
||||
languages = config.SELECTED_TAB_TARGET_LANGUAGES
|
||||
languages[config.SELECTED_TAB_NO] = select
|
||||
config.SELECTED_TAB_TARGET_LANGUAGES = languages
|
||||
language, country = model.getLanguageAndCountry(select)
|
||||
config.TARGET_LANGUAGE = language
|
||||
config.TARGET_COUNTRY = country
|
||||
config.CHOICE_TRANSLATOR = model.findTranslationEngine(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE)
|
||||
view.printToTextbox_selectedTargetLanguages(select)
|
||||
|
||||
def callbackSelectedLanguagePresetTab(selected_tab_no):
|
||||
config.SELECTED_TAB_NO = selected_tab_no
|
||||
view.updateGuiVariableByPresetTabNo(config.SELECTED_TAB_NO)
|
||||
languages = config.SELECTED_TAB_YOUR_LANGUAGES
|
||||
select = languages[config.SELECTED_TAB_NO]
|
||||
language, country = model.getLanguageAndCountry(select)
|
||||
config.SOURCE_LANGUAGE = language
|
||||
config.SOURCE_COUNTRY = country
|
||||
languages = config.SELECTED_TAB_TARGET_LANGUAGES
|
||||
select = languages[config.SELECTED_TAB_NO]
|
||||
language, country = model.getLanguageAndCountry(select)
|
||||
config.TARGET_LANGUAGE = language
|
||||
config.TARGET_COUNTRY = country
|
||||
config.CHOICE_TRANSLATOR = model.findTranslationEngine(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE)
|
||||
view.printToTextbox_changedLanguagePresetTab(config.SELECTED_TAB_NO)
|
||||
|
||||
# command func
|
||||
def callbackToggleTranslation(is_turned_on):
|
||||
config.ENABLE_TRANSLATION = is_turned_on
|
||||
if config.ENABLE_TRANSLATION is True:
|
||||
view.printToTextbox_enableTranslation()
|
||||
else:
|
||||
view.printToTextbox_disableTranslation()
|
||||
|
||||
def callbackToggleTranscriptionSend(is_turned_on):
|
||||
view.setMainWindowAllWidgetsStatusToDisabled()
|
||||
config.ENABLE_TRANSCRIPTION_SEND = is_turned_on
|
||||
if config.ENABLE_TRANSCRIPTION_SEND is True:
|
||||
startThreadingTranscriptionSendMessage()
|
||||
else:
|
||||
stopThreadingTranscriptionSendMessage()
|
||||
|
||||
def callbackToggleTranscriptionReceive(is_turned_on):
|
||||
view.setMainWindowAllWidgetsStatusToDisabled()
|
||||
config.ENABLE_TRANSCRIPTION_RECEIVE = is_turned_on
|
||||
if config.ENABLE_TRANSCRIPTION_RECEIVE is True:
|
||||
startThreadingTranscriptionReceiveMessage()
|
||||
else:
|
||||
stopThreadingTranscriptionReceiveMessage()
|
||||
|
||||
def callbackToggleForeground(is_turned_on):
|
||||
config.ENABLE_FOREGROUND = is_turned_on
|
||||
if config.ENABLE_FOREGROUND is True:
|
||||
view.printToTextbox_enableForeground()
|
||||
view.foregroundOn()
|
||||
else:
|
||||
view.printToTextbox_disableForeground()
|
||||
view.foregroundOff()
|
||||
|
||||
def callbackEnableMainWindowSidebarCompactMode():
|
||||
config.IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE = True
|
||||
view.enableMainWindowSidebarCompactMode()
|
||||
|
||||
def callbackDisableMainWindowSidebarCompactMode():
|
||||
config.IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE = False
|
||||
view.disableMainWindowSidebarCompactMode()
|
||||
|
||||
# Config Window
|
||||
def callbackOpenConfigWindow():
|
||||
view.setMainWindowAllWidgetsStatusToDisabled()
|
||||
if config.ENABLE_TRANSCRIPTION_SEND is True:
|
||||
stopThreadingTranscriptionSendMessageOnOpenConfigWindow()
|
||||
if config.ENABLE_TRANSCRIPTION_RECEIVE is True:
|
||||
stopThreadingTranscriptionReceiveMessageOnOpenConfigWindow()
|
||||
if config.ENABLE_FOREGROUND is True:
|
||||
view.foregroundOff()
|
||||
|
||||
def callbackCloseConfigWindow():
|
||||
model.stopCheckMicEnergy()
|
||||
model.stopCheckSpeakerEnergy()
|
||||
view.initMicThresholdCheckButton()
|
||||
# view.initProgressBar_MicEnergy() # ProgressBarに0をセットしたい
|
||||
view.initSpeakerThresholdCheckButton()
|
||||
# view.initProgressBar_SpeakerEnergy() # ProgressBarに0をセットしたい
|
||||
|
||||
if config.ENABLE_TRANSCRIPTION_SEND is True:
|
||||
startThreadingTranscriptionSendMessageOnCloseConfigWindow()
|
||||
if config.ENABLE_TRANSCRIPTION_RECEIVE is True:
|
||||
sleep(2)
|
||||
if config.ENABLE_TRANSCRIPTION_RECEIVE is True:
|
||||
startThreadingTranscriptionReceiveMessageOnCloseConfigWindow()
|
||||
if config.ENABLE_FOREGROUND is True:
|
||||
view.foregroundOn()
|
||||
view.setMainWindowAllWidgetsStatusToNormal()
|
||||
|
||||
# Compact Mode Switch
|
||||
def callbackEnableConfigWindowCompactMode():
|
||||
config.IS_CONFIG_WINDOW_COMPACT_MODE = True
|
||||
model.stopCheckMicEnergy()
|
||||
view.initMicThresholdCheckButton()
|
||||
model.stopCheckSpeakerEnergy()
|
||||
view.initSpeakerThresholdCheckButton()
|
||||
|
||||
view.enableConfigWindowCompactMode()
|
||||
|
||||
def callbackDisableConfigWindowCompactMode():
|
||||
config.IS_CONFIG_WINDOW_COMPACT_MODE = False
|
||||
model.stopCheckMicEnergy()
|
||||
view.initMicThresholdCheckButton()
|
||||
model.stopCheckSpeakerEnergy()
|
||||
view.initSpeakerThresholdCheckButton()
|
||||
|
||||
view.disableConfigWindowCompactMode()
|
||||
|
||||
# Appearance Tab
|
||||
def callbackSetTransparency(value):
|
||||
print("callbackSetTransparency", int(value))
|
||||
config.TRANSPARENCY = int(value)
|
||||
view.setMainWindowTransparency(config.TRANSPARENCY/100)
|
||||
|
||||
def callbackSetAppearance(value):
|
||||
print("callbackSetAppearance", value)
|
||||
config.APPEARANCE_THEME = value
|
||||
view.showRestartButtonIfRequired()
|
||||
|
||||
def callbackSetUiScaling(value):
|
||||
print("callbackSetUiScaling", value)
|
||||
config.UI_SCALING = value
|
||||
new_scaling_float = int(value.replace("%", "")) / 100
|
||||
print("callbackSetUiScaling_new_scaling_float", new_scaling_float)
|
||||
view.showRestartButtonIfRequired()
|
||||
|
||||
def callbackSetFontFamily(value):
|
||||
print("callbackSetFontFamily", value)
|
||||
config.FONT_FAMILY = value
|
||||
view.showRestartButtonIfRequired()
|
||||
|
||||
def callbackSetUiLanguage(value):
|
||||
print("callbackSetUiLanguage", value)
|
||||
value = get_key_by_value(selectable_languages, value)
|
||||
print("callbackSetUiLanguage__after_get_key_by_value", value)
|
||||
config.UI_LANGUAGE = value
|
||||
view.showRestartButtonIfRequired(locale=config.UI_LANGUAGE)
|
||||
|
||||
# Translation Tab
|
||||
def callbackSetDeeplAuthkey(value):
|
||||
print("callbackSetDeeplAuthkey", str(value))
|
||||
if len(value) == 39:
|
||||
result = model.authenticationTranslator(choice_translator="DeepL_API", auth_key=value)
|
||||
if result is True:
|
||||
key = value
|
||||
view.printToTextbox_AuthenticationSuccess()
|
||||
else:
|
||||
key = None
|
||||
view.printToTextbox_AuthenticationError()
|
||||
auth_keys = config.AUTH_KEYS
|
||||
auth_keys["DeepL_API"] = key
|
||||
config.AUTH_KEYS = auth_keys
|
||||
config.CHOICE_TRANSLATOR = model.findTranslationEngine(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE)
|
||||
elif len(value) == 0:
|
||||
auth_keys = config.AUTH_KEYS
|
||||
auth_keys["DeepL_API"] = None
|
||||
config.AUTH_KEYS = auth_keys
|
||||
config.CHOICE_TRANSLATOR = model.findTranslationEngine(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE)
|
||||
|
||||
# Transcription Tab (Mic)
|
||||
def callbackSetMicHost(value):
|
||||
print("callbackSetMicHost", value)
|
||||
config.CHOICE_MIC_HOST = value
|
||||
config.CHOICE_MIC_DEVICE = model.getInputDefaultDevice()
|
||||
|
||||
view.updateSelected_MicDevice(config.CHOICE_MIC_DEVICE)
|
||||
view.updateList_MicDevice(model.getListInputDevice())
|
||||
|
||||
model.stopCheckMicEnergy()
|
||||
view.replaceMicThresholdCheckButton_Passive()
|
||||
|
||||
def callbackSetMicDevice(value):
|
||||
print("callbackSetMicDevice", value)
|
||||
config.CHOICE_MIC_DEVICE = value
|
||||
|
||||
model.stopCheckMicEnergy()
|
||||
view.replaceMicThresholdCheckButton_Passive()
|
||||
|
||||
def callbackSetMicEnergyThreshold(value):
|
||||
print("callbackSetMicEnergyThreshold", value)
|
||||
if value == "": return
|
||||
try:
|
||||
value = int(value)
|
||||
if 0 <= value and value <= config.MAX_MIC_ENERGY_THRESHOLD:
|
||||
view.clearErrorMessage()
|
||||
config.INPUT_MIC_ENERGY_THRESHOLD = value
|
||||
view.setGuiVariable_MicEnergyThreshold(config.INPUT_MIC_ENERGY_THRESHOLD)
|
||||
else:
|
||||
raise ValueError()
|
||||
except:
|
||||
view.showErrorMessage_MicEnergyThreshold()
|
||||
|
||||
def callbackSetMicDynamicEnergyThreshold(value):
|
||||
print("callbackSetMicDynamicEnergyThreshold", value)
|
||||
config.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD = value
|
||||
if config.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD is True:
|
||||
view.closeMicEnergyThresholdWidget()
|
||||
else:
|
||||
view.openMicEnergyThresholdWidget()
|
||||
|
||||
def setProgressBarMicEnergy(energy):
|
||||
view.updateSetProgressBar_MicEnergy(energy)
|
||||
|
||||
def callbackCheckMicThreshold(is_turned_on):
|
||||
print("callbackCheckMicThreshold", is_turned_on)
|
||||
if is_turned_on is True:
|
||||
view.replaceMicThresholdCheckButton_Disabled()
|
||||
model.startCheckMicEnergy(setProgressBarMicEnergy, view.initProgressBar_MicEnergy)
|
||||
view.replaceMicThresholdCheckButton_Active()
|
||||
else:
|
||||
view.replaceMicThresholdCheckButton_Disabled()
|
||||
model.stopCheckMicEnergy()
|
||||
view.replaceMicThresholdCheckButton_Passive()
|
||||
|
||||
def callbackSetMicRecordTimeout(value):
|
||||
print("callbackSetMicRecordTimeout", value)
|
||||
if value == "": return
|
||||
try:
|
||||
value = int(value)
|
||||
if 0 <= value and value <= config.INPUT_MIC_PHRASE_TIMEOUT:
|
||||
view.clearErrorMessage()
|
||||
config.INPUT_MIC_RECORD_TIMEOUT = value
|
||||
view.setGuiVariable_MicRecordTimeout(config.INPUT_MIC_RECORD_TIMEOUT)
|
||||
else:
|
||||
raise ValueError()
|
||||
except:
|
||||
view.showErrorMessage_MicRecordTimeout()
|
||||
|
||||
def callbackSetMicPhraseTimeout(value):
|
||||
print("callbackSetMicPhraseTimeout", value)
|
||||
if value == "": return
|
||||
try:
|
||||
value = int(value)
|
||||
if 0 <= value and value >= config.INPUT_MIC_RECORD_TIMEOUT:
|
||||
view.clearErrorMessage()
|
||||
config.INPUT_MIC_PHRASE_TIMEOUT = value
|
||||
view.setGuiVariable_MicPhraseTimeout(config.INPUT_MIC_PHRASE_TIMEOUT)
|
||||
else:
|
||||
raise ValueError()
|
||||
except:
|
||||
view.showErrorMessage_MicPhraseTimeout()
|
||||
|
||||
def callbackSetMicMaxPhrases(value):
|
||||
print("callbackSetMicMaxPhrases", value)
|
||||
if value == "": return
|
||||
try:
|
||||
value = int(value)
|
||||
if 0 <= value:
|
||||
view.clearErrorMessage()
|
||||
config.INPUT_MIC_MAX_PHRASES = value
|
||||
view.setGuiVariable_MicMaxPhrases(config.INPUT_MIC_MAX_PHRASES)
|
||||
else:
|
||||
raise ValueError()
|
||||
except:
|
||||
view.showErrorMessage_MicMaxPhrases()
|
||||
|
||||
def callbackSetMicWordFilter(value):
|
||||
print("callbackSetMicWordFilter", value)
|
||||
word_filter = str(value)
|
||||
word_filter = [w.strip() for w in word_filter.split(",") if len(w.strip()) > 0]
|
||||
word_filter = ",".join(word_filter)
|
||||
print("callbackSetMicWordFilter_afterSplitting", word_filter)
|
||||
if len(word_filter) > 0:
|
||||
config.INPUT_MIC_WORD_FILTER = word_filter.split(",")
|
||||
else:
|
||||
config.INPUT_MIC_WORD_FILTER = []
|
||||
model.resetKeywordProcessor()
|
||||
model.addKeywords()
|
||||
|
||||
def callbackSetSpeakerEnergyThreshold(value):
|
||||
print("callbackSetSpeakerEnergyThreshold", value)
|
||||
if value == "": return
|
||||
try:
|
||||
value = int(value)
|
||||
if 0 <= value and value <= config.MAX_SPEAKER_ENERGY_THRESHOLD:
|
||||
view.clearErrorMessage()
|
||||
config.INPUT_SPEAKER_ENERGY_THRESHOLD = value
|
||||
view.setGuiVariable_SpeakerEnergyThreshold(config.INPUT_SPEAKER_ENERGY_THRESHOLD)
|
||||
else:
|
||||
raise ValueError()
|
||||
except:
|
||||
view.showErrorMessage_SpeakerEnergyThreshold()
|
||||
|
||||
def callbackSetSpeakerDynamicEnergyThreshold(value):
|
||||
print("callbackSetSpeakerDynamicEnergyThreshold", value)
|
||||
config.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD = value
|
||||
if config.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD is True:
|
||||
view.closeSpeakerEnergyThresholdWidget()
|
||||
else:
|
||||
view.openSpeakerEnergyThresholdWidget()
|
||||
|
||||
|
||||
def setProgressBarSpeakerEnergy(energy):
|
||||
view.updateSetProgressBar_SpeakerEnergy(energy)
|
||||
|
||||
def callbackCheckSpeakerThreshold(is_turned_on):
|
||||
print("callbackCheckSpeakerThreshold", is_turned_on)
|
||||
if is_turned_on is True:
|
||||
view.replaceSpeakerThresholdCheckButton_Disabled()
|
||||
model.startCheckSpeakerEnergy(
|
||||
setProgressBarSpeakerEnergy,
|
||||
view.initProgressBar_SpeakerEnergy,
|
||||
view.showErrorMessage_CheckSpeakerThreshold_NoDevice
|
||||
)
|
||||
|
||||
view.replaceSpeakerThresholdCheckButton_Active()
|
||||
else:
|
||||
view.replaceSpeakerThresholdCheckButton_Disabled()
|
||||
model.stopCheckSpeakerEnergy()
|
||||
view.replaceSpeakerThresholdCheckButton_Passive()
|
||||
|
||||
def callbackSetSpeakerRecordTimeout(value):
|
||||
print("callbackSetSpeakerRecordTimeout", value)
|
||||
if value == "": return
|
||||
try:
|
||||
value = int(value)
|
||||
if 0 <= value and value <= config.INPUT_SPEAKER_PHRASE_TIMEOUT:
|
||||
view.clearErrorMessage()
|
||||
config.INPUT_SPEAKER_RECORD_TIMEOUT = value
|
||||
view.setGuiVariable_SpeakerRecordTimeout(config.INPUT_SPEAKER_RECORD_TIMEOUT)
|
||||
else:
|
||||
raise ValueError()
|
||||
except:
|
||||
view.showErrorMessage_SpeakerRecordTimeout()
|
||||
|
||||
def callbackSetSpeakerPhraseTimeout(value):
|
||||
print("callbackSetSpeakerPhraseTimeout", value)
|
||||
if value == "": return
|
||||
try:
|
||||
value = int(value)
|
||||
if 0 <= value and value >= config.INPUT_SPEAKER_RECORD_TIMEOUT:
|
||||
view.clearErrorMessage()
|
||||
config.INPUT_SPEAKER_PHRASE_TIMEOUT = value
|
||||
view.setGuiVariable_SpeakerPhraseTimeout(config.INPUT_SPEAKER_PHRASE_TIMEOUT)
|
||||
else:
|
||||
raise ValueError()
|
||||
except:
|
||||
view.showErrorMessage_SpeakerPhraseTimeout()
|
||||
|
||||
def callbackSetSpeakerMaxPhrases(value):
|
||||
print("callbackSetSpeakerMaxPhrases", value)
|
||||
if value == "": return
|
||||
try:
|
||||
value = int(value)
|
||||
if 0 <= value:
|
||||
view.clearErrorMessage()
|
||||
config.INPUT_SPEAKER_MAX_PHRASES = value
|
||||
view.setGuiVariable_SpeakerMaxPhrases(config.INPUT_SPEAKER_MAX_PHRASES)
|
||||
else:
|
||||
raise ValueError()
|
||||
except:
|
||||
view.showErrorMessage_SpeakerMaxPhrases()
|
||||
|
||||
|
||||
# Others Tab
|
||||
def callbackSetEnableAutoClearMessageBox(value):
|
||||
print("callbackSetEnableAutoClearMessageBox", value)
|
||||
config.ENABLE_AUTO_CLEAR_MESSAGE_BOX = value
|
||||
|
||||
def callbackSetEnableNoticeXsoverlay(value):
|
||||
print("callbackSetEnableNoticeXsoverlay", value)
|
||||
config.ENABLE_NOTICE_XSOVERLAY = value
|
||||
|
||||
def callbackSetEnableAutoExportMessageLogs(value):
|
||||
print("callbackSetEnableAutoExportMessageLogs", value)
|
||||
config.ENABLE_LOGGER = value
|
||||
|
||||
if config.ENABLE_LOGGER is True:
|
||||
model.startLogger()
|
||||
else:
|
||||
model.stopLogger()
|
||||
|
||||
def callbackSetMessageFormat(value):
|
||||
print("callbackSetMessageFormat", value)
|
||||
if len(value) > 0:
|
||||
config.MESSAGE_FORMAT = value
|
||||
|
||||
def callbackSetEnableSendMessageToVrc(value):
|
||||
print("callbackSetEnableSendMessageToVrc", value)
|
||||
config.ENABLE_SEND_MESSAGE_TO_VRC = value
|
||||
|
||||
# [deprecated]
|
||||
# def callbackSetStartupOscEnabledCheck(value):
|
||||
# print("callbackSetStartupOscEnabledCheck", value)
|
||||
# config.STARTUP_OSC_ENABLED_CHECK = value
|
||||
|
||||
# Advanced Settings Tab
|
||||
def callbackSetOscIpAddress(value):
|
||||
if value == "": return
|
||||
print("callbackSetOscIpAddress", str(value))
|
||||
config.OSC_IP_ADDRESS = str(value)
|
||||
|
||||
def callbackSetOscPort(value):
|
||||
if value == "": return
|
||||
print("callbackSetOscPort", int(value))
|
||||
config.OSC_PORT = int(value)
|
||||
|
||||
def createMainWindow():
|
||||
# create GUI
|
||||
view.createGUI()
|
||||
|
||||
# init config
|
||||
initSetLanguageAndCountry()
|
||||
|
||||
if model.authenticationTranslator(config.CHOICE_TRANSLATOR, config.AUTH_KEYS[config.CHOICE_TRANSLATOR]) is False:
|
||||
# error update Auth key
|
||||
auth_keys = config.AUTH_KEYS
|
||||
auth_keys[config.CHOICE_TRANSLATOR] = None
|
||||
config.AUTH_KEYS = auth_keys
|
||||
view.printToTextbox_AuthenticationError()
|
||||
config.CHOICE_TRANSLATOR = model.findTranslationEngine(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE)
|
||||
|
||||
# set word filter
|
||||
model.addKeywords()
|
||||
|
||||
# check OSC started [deprecated]
|
||||
# if config.STARTUP_OSC_ENABLED_CHECK is True and config.ENABLE_SEND_MESSAGE_TO_VRC is True:
|
||||
# model.checkOSCStarted(view.printToTextbox_OSCError)
|
||||
|
||||
# check Software Updated
|
||||
if model.checkSoftwareUpdated() is True:
|
||||
view.showUpdateAvailableButton()
|
||||
|
||||
# init logger
|
||||
if config.ENABLE_LOGGER is True:
|
||||
model.startLogger()
|
||||
|
||||
# set UI and callback
|
||||
view.register(
|
||||
common_registers={
|
||||
"callback_update_software": callbackUpdateSoftware,
|
||||
"callback_restart_software": callbackRestartSoftware,
|
||||
},
|
||||
|
||||
window_action_registers={
|
||||
"callback_open_config_window": callbackOpenConfigWindow,
|
||||
"callback_close_config_window": callbackCloseConfigWindow,
|
||||
},
|
||||
|
||||
main_window_registers={
|
||||
"callback_enable_main_window_sidebar_compact_mode": callbackEnableMainWindowSidebarCompactMode,
|
||||
"callback_disable_main_window_sidebar_compact_mode": callbackDisableMainWindowSidebarCompactMode,
|
||||
|
||||
"callback_toggle_translation": callbackToggleTranslation,
|
||||
"callback_toggle_transcription_send": callbackToggleTranscriptionSend,
|
||||
"callback_toggle_transcription_receive": callbackToggleTranscriptionReceive,
|
||||
"callback_toggle_foreground": callbackToggleForeground,
|
||||
|
||||
"callback_your_language": setYourLanguageAndCountry,
|
||||
"callback_target_language": setTargetLanguageAndCountry,
|
||||
"values": model.getListLanguageAndCountry(),
|
||||
|
||||
"callback_selected_language_preset_tab": callbackSelectedLanguagePresetTab,
|
||||
"message_box_bind_Return": messageBoxPressKeyEnter,
|
||||
"message_box_bind_Any_KeyPress": messageBoxPressKeyAny,
|
||||
"message_box_bind_FocusIn": messageBoxFocusIn,
|
||||
"message_box_bind_FocusOut": messageBoxFocusOut,
|
||||
},
|
||||
|
||||
config_window_registers={
|
||||
# Compact Mode Switch
|
||||
"callback_disable_config_window_compact_mode": callbackEnableConfigWindowCompactMode,
|
||||
"callback_enable_config_window_compact_mode": callbackDisableConfigWindowCompactMode,
|
||||
|
||||
# Appearance Tab
|
||||
"callback_set_transparency": callbackSetTransparency,
|
||||
"callback_set_appearance": callbackSetAppearance,
|
||||
"callback_set_ui_scaling": callbackSetUiScaling,
|
||||
"callback_set_font_family": callbackSetFontFamily,
|
||||
"callback_set_ui_language": callbackSetUiLanguage,
|
||||
|
||||
# Translation Tab
|
||||
"callback_set_deepl_authkey": callbackSetDeeplAuthkey,
|
||||
|
||||
# Transcription Tab (Mic)
|
||||
"callback_set_mic_host": callbackSetMicHost,
|
||||
"list_mic_host": model.getListInputHost(),
|
||||
"callback_set_mic_device": callbackSetMicDevice,
|
||||
"list_mic_device": model.getListInputDevice(),
|
||||
"callback_set_mic_energy_threshold": callbackSetMicEnergyThreshold,
|
||||
"callback_set_mic_dynamic_energy_threshold": callbackSetMicDynamicEnergyThreshold,
|
||||
"callback_check_mic_threshold": callbackCheckMicThreshold,
|
||||
"callback_set_mic_record_timeout": callbackSetMicRecordTimeout,
|
||||
"callback_set_mic_phrase_timeout": callbackSetMicPhraseTimeout,
|
||||
"callback_set_mic_max_phrases": callbackSetMicMaxPhrases,
|
||||
"callback_set_mic_word_filter": callbackSetMicWordFilter,
|
||||
|
||||
# Transcription Tab (Speaker)
|
||||
"callback_set_speaker_energy_threshold": callbackSetSpeakerEnergyThreshold,
|
||||
"callback_set_speaker_dynamic_energy_threshold": callbackSetSpeakerDynamicEnergyThreshold,
|
||||
"callback_check_speaker_threshold": callbackCheckSpeakerThreshold,
|
||||
"callback_set_speaker_record_timeout": callbackSetSpeakerRecordTimeout,
|
||||
"callback_set_speaker_phrase_timeout": callbackSetSpeakerPhraseTimeout,
|
||||
"callback_set_speaker_max_phrases": callbackSetSpeakerMaxPhrases,
|
||||
|
||||
# Others Tab
|
||||
"callback_set_enable_auto_clear_chatbox": callbackSetEnableAutoClearMessageBox,
|
||||
"callback_set_enable_notice_xsoverlay": callbackSetEnableNoticeXsoverlay,
|
||||
"callback_set_enable_auto_export_message_logs": callbackSetEnableAutoExportMessageLogs,
|
||||
"callback_set_message_format": callbackSetMessageFormat,
|
||||
"callback_set_enable_send_message_to_vrc": callbackSetEnableSendMessageToVrc,
|
||||
# "callback_set_startup_osc_enabled_check": callbackSetStartupOscEnabledCheck, # [deprecated]
|
||||
|
||||
# Advanced Settings Tab
|
||||
"callback_set_osc_ip_address": callbackSetOscIpAddress,
|
||||
"callback_set_osc_port": callbackSetOscPort,
|
||||
},
|
||||
)
|
||||
|
||||
def showMainWindow():
|
||||
view.startMainLoop()
|
||||
@@ -1,325 +0,0 @@
|
||||
"""
|
||||
CustomTkinter Scrollable Dropdown Menu
|
||||
Author: Akash Bora
|
||||
License: MIT
|
||||
This is a custom dropdown menu for customtkinter.
|
||||
Homepage: https://github.com/Akascape/CTkScrollableDropdown
|
||||
|
||||
Advanced Scrollable Dropdown class for customtkinter widgets
|
||||
Author: Akash Bora
|
||||
"""
|
||||
import customtkinter
|
||||
import sys
|
||||
import time
|
||||
|
||||
class CTkScrollableDropdown(customtkinter.CTkToplevel):
|
||||
|
||||
def __init__(self, attach, x=None, y=None, button_color=None, height: int = 200, width: int = None,
|
||||
fg_color=None, button_height: int = 20, justify="center", scrollbar_button_color=None,
|
||||
scrollbar=True, scrollbar_button_hover_color=None, frame_border_width=2, values=[],
|
||||
command=None, image_values=[], alpha: float = 0.97, frame_corner_radius=20, double_click=False,
|
||||
resize=True, frame_border_color=None, text_color=None, autocomplete=False, **button_kwargs):
|
||||
|
||||
super().__init__(takefocus=1)
|
||||
self.transient(self.master)
|
||||
self.alpha = alpha
|
||||
self.attach = attach
|
||||
self.corner = frame_corner_radius
|
||||
self.padding = 0
|
||||
self.focus_something = False
|
||||
self.disable = True
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
self.after(100, lambda: self.overrideredirect(True))
|
||||
self.transparent_color = self._apply_appearance_mode(self._fg_color)
|
||||
self.attributes("-transparentcolor", self.transparent_color)
|
||||
elif sys.platform.startswith("darwin"):
|
||||
self.overrideredirect(True)
|
||||
self.transparent_color = 'systemTransparent'
|
||||
self.attributes("-transparent", True)
|
||||
self.focus_something = True
|
||||
else:
|
||||
self.overrideredirect(True)
|
||||
self.transparent_color = '#000001'
|
||||
self.corner = 0
|
||||
self.padding = 18
|
||||
self.withdraw()
|
||||
|
||||
self.hide = True
|
||||
self.attach.bind('<Configure>', lambda e: self._withdraw() if not self.disable else None, add="+")
|
||||
self.attach.winfo_toplevel().bind('<Configure>', lambda e: self._withdraw() if not self.disable else None, add="+")
|
||||
self.attach.winfo_toplevel().bind("<Triple-Button-1>", lambda e: self._withdraw() if not self.disable else None, add="+")
|
||||
self.attach.winfo_toplevel().bind("<Button-3>", lambda e: self._withdraw() if not self.disable else None, add="+")
|
||||
self.attach.winfo_toplevel().bind("<Button-2>", lambda e: self._withdraw() if not self.disable else None, add="+")
|
||||
|
||||
self.attributes('-alpha', 0)
|
||||
self.disable = False
|
||||
self.fg_color = customtkinter.ThemeManager.theme["CTkFrame"]["fg_color"] if fg_color is None else fg_color
|
||||
self.scroll_button_color = customtkinter.ThemeManager.theme["CTkScrollbar"]["button_color"] if scrollbar_button_color is None else scrollbar_button_color
|
||||
self.scroll_hover_color = customtkinter.ThemeManager.theme["CTkScrollbar"]["button_hover_color"] if scrollbar_button_hover_color is None else scrollbar_button_hover_color
|
||||
self.frame_border_color = customtkinter.ThemeManager.theme["CTkFrame"]["border_color"] if frame_border_color is None else frame_border_color
|
||||
self.button_color = customtkinter.ThemeManager.theme["CTkFrame"]["top_fg_color"] if button_color is None else button_color
|
||||
self.text_color = customtkinter.ThemeManager.theme["CTkLabel"]["text_color"] if text_color is None else text_color
|
||||
|
||||
if scrollbar is False:
|
||||
self.scroll_button_color = self.fg_color
|
||||
self.scroll_hover_color = self.fg_color
|
||||
|
||||
self.frame = customtkinter.CTkScrollableFrame(self, bg_color=self.transparent_color, fg_color=self.fg_color,
|
||||
scrollbar_button_hover_color=self.scroll_hover_color,
|
||||
corner_radius=self.corner, border_width=frame_border_width,
|
||||
scrollbar_button_color=self.scroll_button_color,
|
||||
border_color=self.frame_border_color)
|
||||
self.frame._scrollbar.grid_configure(padx=3)
|
||||
self.frame.pack(expand=True, fill="both")
|
||||
|
||||
self.dummy_entry = customtkinter.CTkEntry(self.frame, fg_color="transparent", border_width=0, height=1, width=1)
|
||||
self.no_match = customtkinter.CTkLabel(self.frame, text="No Match")
|
||||
self.height = height
|
||||
self.height_new = height
|
||||
self.width = width
|
||||
self.command = command
|
||||
self.fade = False
|
||||
self.resize = resize
|
||||
self.autocomplete = autocomplete
|
||||
self.var_update = customtkinter.StringVar()
|
||||
self.appear = False
|
||||
|
||||
if justify.lower()=="left":
|
||||
self.justify = "w"
|
||||
elif justify.lower()=="right":
|
||||
self.justify = "e"
|
||||
else:
|
||||
self.justify = "c"
|
||||
|
||||
self.button_height = button_height
|
||||
self.values = values
|
||||
self.button_num = len(self.values)
|
||||
self.image_values = None if len(image_values)!=len(self.values) else image_values
|
||||
|
||||
self.resizable(width=False, height=False)
|
||||
self._init_buttons(**button_kwargs)
|
||||
|
||||
# Add binding for different ctk widgets
|
||||
if double_click or self.attach.winfo_name().startswith("!ctkentry") or self.attach.winfo_name().startswith("!ctkcombobox"):
|
||||
self.attach.bind('<Double-Button-1>', lambda e: self._iconify(), add="+")
|
||||
else:
|
||||
self.attach.bind('<Button-1>', lambda e: self._iconify(), add="+")
|
||||
|
||||
if self.attach.winfo_name().startswith("!ctkcombobox"):
|
||||
self.attach._canvas.tag_bind("right_parts", "<Button-1>", lambda e: self._iconify())
|
||||
self.attach._canvas.tag_bind("dropdown_arrow", "<Button-1>", lambda e: self._iconify())
|
||||
if self.command is None:
|
||||
self.command = self.attach.set
|
||||
|
||||
if self.attach.winfo_name().startswith("!ctkoptionmenu"):
|
||||
self.attach._canvas.bind("<Button-1>", lambda e: self._iconify())
|
||||
self.attach._text_label.bind("<Button-1>", lambda e: self._iconify())
|
||||
if self.command is None:
|
||||
self.command = self.attach.set
|
||||
|
||||
self.update_idletasks()
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
if self.autocomplete:
|
||||
self.bind_autocomplete()
|
||||
|
||||
# self.deiconify()
|
||||
self.withdraw()
|
||||
|
||||
self.attributes("-alpha", self.alpha)
|
||||
|
||||
def _withdraw(self):
|
||||
if self.hide is False: self.withdraw()
|
||||
self.hide = True
|
||||
|
||||
def _update(self, a, b, c):
|
||||
self.live_update(self.attach._entry.get())
|
||||
|
||||
def bind_autocomplete(self, ):
|
||||
def appear(x):
|
||||
self.appear = True
|
||||
|
||||
if self.attach.winfo_name().startswith("!ctkcombobox"):
|
||||
self.attach._entry.configure(textvariable=self.var_update)
|
||||
self.attach._entry.bind("<Key>", appear)
|
||||
self.attach.set(self.values[0])
|
||||
self.var_update.trace_add('write', self._update)
|
||||
|
||||
if self.attach.winfo_name().startswith("!ctkentry"):
|
||||
self.attach.configure(textvariable=self.var_update)
|
||||
self.attach.bind("<Key>", appear)
|
||||
self.var_update.trace_add('write', self._update)
|
||||
|
||||
def fade_out(self):
|
||||
for i in range(100,0,-10):
|
||||
if not self.winfo_exists():
|
||||
break
|
||||
self.attributes("-alpha", i/100)
|
||||
self.update()
|
||||
time.sleep(1/100)
|
||||
|
||||
def fade_in(self):
|
||||
for i in range(0,100,10):
|
||||
if not self.winfo_exists():
|
||||
break
|
||||
self.attributes("-alpha", i/100)
|
||||
self.update()
|
||||
time.sleep(1/100)
|
||||
|
||||
def _init_buttons(self, **button_kwargs):
|
||||
self.i = 0
|
||||
self.widgets = {}
|
||||
for row in self.values:
|
||||
self.widgets[self.i] = customtkinter.CTkButton(self.frame,
|
||||
text=row,
|
||||
height=self.button_height,
|
||||
fg_color=self.button_color,
|
||||
text_color=self.text_color,
|
||||
image=self.image_values[i] if self.image_values is not None else None,
|
||||
anchor=self.justify,
|
||||
command=lambda k=row: self._attach_key_press(k), **button_kwargs)
|
||||
self.widgets[self.i].pack(fill="x", pady=2, padx=(self.padding, 0))
|
||||
self.i+=1
|
||||
|
||||
self.hide = False
|
||||
|
||||
def destroy_popup(self):
|
||||
self.destroy()
|
||||
self.disable = True
|
||||
|
||||
def place_dropdown(self):
|
||||
self.x_pos = self.attach.winfo_rootx() if self.x is None else self.x + self.attach.winfo_rootx()
|
||||
self.y_pos = self.attach.winfo_rooty() + self.attach.winfo_reqheight() + 5 if self.y is None else self.y + self.attach.winfo_rooty()
|
||||
self.width_new = self.attach.winfo_width() if self.width is None else self.width
|
||||
|
||||
if self.resize:
|
||||
if self.button_num==1:
|
||||
self.height_new = self.button_height * self.button_num + 45
|
||||
else:
|
||||
self.height_new = self.button_height * self.button_num + 35
|
||||
if self.height_new>self.height:
|
||||
self.height_new = self.height
|
||||
|
||||
self.geometry('{}x{}+{}+{}'.format(self.width_new, self.height_new,
|
||||
self.x_pos, self.y_pos))
|
||||
self.fade_in()
|
||||
self.attributes('-alpha', self.alpha)
|
||||
|
||||
def _iconify(self):
|
||||
if self.disable: return
|
||||
if self.hide:
|
||||
self._deiconify()
|
||||
self.place_dropdown()
|
||||
if self.focus_something:
|
||||
self.dummy_entry.pack()
|
||||
self.dummy_entry.focus_set()
|
||||
self.after(100, self.dummy_entry.pack_forget)
|
||||
self.hide = False
|
||||
self.focus_set()
|
||||
self.focus()
|
||||
else:
|
||||
self.withdraw()
|
||||
self.hide = True
|
||||
|
||||
def _attach_key_press(self, k):
|
||||
self.fade = True
|
||||
if self.command:
|
||||
self.command(k)
|
||||
self.fade = False
|
||||
self.fade_out()
|
||||
self.withdraw()
|
||||
self.hide = True
|
||||
|
||||
def live_update(self, string=None):
|
||||
if not self.appear: return
|
||||
if self.disable: return
|
||||
if self.fade: return
|
||||
if string:
|
||||
self._deiconify()
|
||||
i=1
|
||||
for key in self.widgets.keys():
|
||||
s = self.widgets[key].cget("text")
|
||||
if not s.startswith(string):
|
||||
self.widgets[key].pack_forget()
|
||||
else:
|
||||
self.widgets[key].pack(fill="x", pady=2, padx=(self.padding, 0))
|
||||
i+=1
|
||||
|
||||
if i==1:
|
||||
self.no_match.pack(fill="x", pady=2, padx=(self.padding, 0))
|
||||
else:
|
||||
self.no_match.pack_forget()
|
||||
self.button_num = i
|
||||
self.place_dropdown()
|
||||
|
||||
else:
|
||||
self.no_match.pack_forget()
|
||||
self.button_num = len(self.values)
|
||||
for key in self.widgets.keys():
|
||||
self.widgets[key].destroy()
|
||||
self._init_buttons()
|
||||
self.place_dropdown()
|
||||
|
||||
self.frame._parent_canvas.yview_moveto(0.0)
|
||||
self.appear = False
|
||||
|
||||
def insert(self, value, **kwargs):
|
||||
self.widgets[self.i] = customtkinter.CTkButton(self.frame,
|
||||
text=value,
|
||||
height=self.button_height,
|
||||
fg_color=self.button_color,
|
||||
text_color=self.text_color,
|
||||
anchor=self.justify,
|
||||
command=lambda k=value: self._attach_key_press(k), **kwargs)
|
||||
self.widgets[self.i].pack(fill="x", pady=2, padx=(self.padding, 0))
|
||||
self.i+=1
|
||||
self.values.append(value)
|
||||
|
||||
def _deiconify(self):
|
||||
if len(self.values)>0:
|
||||
self.deiconify()
|
||||
|
||||
def popup(self, x=None, y=None):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.hide = True
|
||||
self._iconify()
|
||||
|
||||
def configure(self, **kwargs):
|
||||
if "height" in kwargs:
|
||||
self.height = kwargs.pop("height")
|
||||
self.height_new = self.height
|
||||
|
||||
if "alpha" in kwargs:
|
||||
self.alpha = kwargs.pop("alpha")
|
||||
|
||||
if "width" in kwargs:
|
||||
self.width = kwargs.pop("width")
|
||||
|
||||
if "fg_color" in kwargs:
|
||||
self.frame.configure(fg_color=kwargs.pop("fg_color"))
|
||||
|
||||
if "values" in kwargs:
|
||||
self.values = kwargs.pop("values")
|
||||
self.image_values = None
|
||||
for key in self.widgets.keys():
|
||||
self.widgets[key].destroy()
|
||||
self._init_buttons()
|
||||
|
||||
if "image_values" in kwargs:
|
||||
self.image_values = kwargs.pop("image_values")
|
||||
self.image_values = None if len(self.image_values)!=len(self.values) else self.image_values
|
||||
if self.image_values is not None:
|
||||
i=0
|
||||
for key in self.widgets.keys():
|
||||
self.widgets[key].configure(image=self.image_values[i])
|
||||
i+=1
|
||||
|
||||
if "button_color" in kwargs:
|
||||
for key in self.widgets.keys():
|
||||
self.widgets[key].configure(fg_color=kwargs.pop("button_color"))
|
||||
|
||||
for key in self.widgets.keys():
|
||||
self.widgets[key].configure(**kwargs)
|
||||
BIN
docs/main_window.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 155 KiB |
BIN
img/arrow_left_disabled.png
Normal file
|
After Width: | Height: | Size: 974 B |
BIN
img/arrow_left_white.png
Normal file
|
After Width: | Height: | Size: 969 B |
BIN
img/configuration_icon_disabled.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
img/configuration_icon_white.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
img/foreground_icon_disabled.png
Normal file
|
After Width: | Height: | Size: 232 B |
BIN
img/foreground_icon_white.png
Normal file
|
After Width: | Height: | Size: 227 B |
BIN
img/headphones_icon_disabled.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
img/headphones_icon_white.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
img/help_icon_disabled.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
img/help_icon_white.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
img/mic_icon_disabled.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
img/mic_icon_white.png
Normal file
|
After Width: | Height: | Size: 952 B |
BIN
img/narrow_arrow_down.png
Normal file
|
After Width: | Height: | Size: 785 B |
BIN
img/refresh_icon.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
img/translation_icon_disabled.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
img/translation_icon_white.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
img/vrct_logo_for_dark_mode.png
Normal file
|
After Width: | Height: | Size: 197 KiB |
BIN
img/vrct_logo_for_light_mode.png
Normal file
|
After Width: | Height: | Size: 219 KiB |
BIN
img/vrct_logo_mark_black.ico
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
img/vrct_logo_mark_black.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
img/vrct_logo_mark_black_icon.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
img/vrct_logo_mark_white.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
img/xsoverlay2.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
338
languages.py
@@ -1,342 +1,6 @@
|
||||
transcription_lang = {
|
||||
"Japanese (Japan)":"ja-JP",
|
||||
"English (United States)":"en-US",
|
||||
"English (United Kingdom)":"en-GB",
|
||||
"Afrikaans (South Africa)":"af-ZA",
|
||||
"Arabic (Algeria)":"ar-DZ",
|
||||
"Arabic (Bahrain)":"ar-BH",
|
||||
"Arabic (Egypt)":"ar-EG",
|
||||
"Arabic (Israel)":"ar-IL",
|
||||
"Arabic (Iraq)":"ar-IQ",
|
||||
"Arabic (Jordan)":"ar-JO",
|
||||
"Arabic (Kuwait)":"ar-KW",
|
||||
"Arabic (Lebanon)":"ar-LB",
|
||||
"Arabic (Morocco)":"ar-MA",
|
||||
"Arabic (Oman)":"ar-OM",
|
||||
"Arabic (State of Palestine)":"ar-PS",
|
||||
"Arabic (Qatar)":"ar-QA",
|
||||
"Arabic (Saudi Arabia)":"ar-SA",
|
||||
"Arabic (Tunisia)":"ar-TN",
|
||||
"Arabic (United Arab Emirates)":"ar-AE",
|
||||
"Basque (Spain)":"eu-ES",
|
||||
"Bulgarian (Bulgaria)":"bg-BG",
|
||||
"Catalan (Spain)":"ca-ES",
|
||||
"Chinese, Mandarin (Simplified, China)":"cmn-Hans-CN",
|
||||
"Chinese, Mandarin (Simplified, Hong Kong)":"cmn-Hans-HK",
|
||||
"Chinese, Mandarin (Traditional, Taiwan)":"cmn-Hant-TW",
|
||||
"Chinese, Cantonese (Traditional Hong Kong)":"yue-Hant-HK",
|
||||
"Croatian (Croatia)":"hr-HR",
|
||||
"Czech (Czech Republic)":"cs-CZ",
|
||||
"Danish (Denmark)":"da-DK",
|
||||
"English (Australia)":"en-AU",
|
||||
"English (Canada)":"en-CA",
|
||||
"English (India)":"en-IN",
|
||||
"English (Ireland)":"en-IE",
|
||||
"English (New Zealand)":"en-NZ",
|
||||
"English (Philippines)":"en-PH",
|
||||
"English (South Africa)":"en-ZA",
|
||||
"Persian (Iran)":"fa-IR",
|
||||
"French (France)":"fr-FR",
|
||||
"Filipino (Philippines)":"fil-PH",
|
||||
"Galician (Spain)":"gl-ES",
|
||||
"German (Germany)":"de-DE",
|
||||
"Greek (Greece)":"el-GR",
|
||||
"Finnish (Finland)":"fi-FI",
|
||||
"Hebrew (Israel)":"he-IL",
|
||||
"Hindi (India)":"hi-IN",
|
||||
"Hungarian (Hungary)":"hu-HU",
|
||||
"Indonesian (Indonesia)":"id-ID",
|
||||
"Icelandic (Iceland)":"is-IS",
|
||||
"Italian (Italy)":"it-IT",
|
||||
"Italian (Switzerland)":"it-CH",
|
||||
"Korean (South Korea)":"ko-KR",
|
||||
"Lithuanian (Lithuania)":"lt-LT",
|
||||
"Malay (Malaysia)":"ms-MY",
|
||||
"Dutch (Netherlands)":"nl-NL",
|
||||
"Norwegian Bokmål (Norway)":"nb-NO",
|
||||
"Polish (Poland)":"pl-PL",
|
||||
"Portuguese (Brazil)":"pt-BR",
|
||||
"Portuguese (Portugal)":"pt-PT",
|
||||
"Romanian (Romania)":"ro-RO",
|
||||
"Russian (Russia)":"ru-RU",
|
||||
"Serbian (Serbia)":"sr-RS",
|
||||
"Slovak (Slovakia)":"sk-SK",
|
||||
"Slovenian (Slovenia)":"sl-SI",
|
||||
"Spanish (Argentina)":"es-AR",
|
||||
"Spanish (Bolivia)":"es-BO",
|
||||
"Spanish (Chile)":"es-CL",
|
||||
"Spanish (Colombia)":"es-CO",
|
||||
"Spanish (Costa Rica)":"es-CR",
|
||||
"Spanish (Dominican Republic)":"es-DO",
|
||||
"Spanish (Ecuador)":"es-EC",
|
||||
"Spanish (El Salvador)":"es-SV",
|
||||
"Spanish (Guatemala)":"es-GT",
|
||||
"Spanish (Honduras)":"es-HN",
|
||||
"Spanish (Mexico)":"es-MX",
|
||||
"Spanish (Nicaragua)":"es-NI",
|
||||
"Spanish (Panama)":"es-PA",
|
||||
"Spanish (Paraguay)":"es-PY",
|
||||
"Spanish (Peru)":"es-PE",
|
||||
"Spanish (Puerto Rico)":"es-PR",
|
||||
"Spanish (Spain)":"es-ES",
|
||||
"Spanish (Uruguay)":"es-UY",
|
||||
"Spanish (United States)":"es-US",
|
||||
"Spanish (Venezuela)":"es-VE",
|
||||
"Swedish (Sweden)":"sv-SE",
|
||||
"Thai (Thailand)":"th-TH",
|
||||
"Turkish (Turkey)":"tr-TR",
|
||||
"Ukrainian (Ukraine)":"uk-UA",
|
||||
"Vietnamese (Vietnam)":"vi-VN",
|
||||
"Zulu (South Africa)":"zu-ZA"
|
||||
}
|
||||
|
||||
translators = ["DeepL(web)", "DeepL(auth)", "Google(web)", "Bing(web)"]
|
||||
translation_lang = {}
|
||||
dict_deepl_web_languages = {
|
||||
"Japanese":"JA",
|
||||
"English":"EN",
|
||||
"Korean":"KO",
|
||||
"Bulgarian":"BG",
|
||||
"Chinese":"ZH",
|
||||
"Czech":"CS",
|
||||
"Danish":"DA",
|
||||
"Dutch":"NL",
|
||||
"Estonian":"ET",
|
||||
"Finnish":"FI",
|
||||
"French":"FR",
|
||||
"German":"DE",
|
||||
"Greek":"EL",
|
||||
"Hungarian":"HU",
|
||||
"Italian":"IT",
|
||||
"Latvian":"LV",
|
||||
"Lithuanian":"LT",
|
||||
"Polish":"PL",
|
||||
"Portuguese":"PT",
|
||||
"Romanian":"RO",
|
||||
"Russian":"RU",
|
||||
"Slovak":"SK",
|
||||
"Slovenian":"SL",
|
||||
"Spanish":"ES",
|
||||
"Swedish":"SV",
|
||||
"Indonesian":"ID",
|
||||
"Ukrainian":"UK",
|
||||
"Turkish":"TR",
|
||||
"Norwegian":"NB",
|
||||
}
|
||||
translation_lang["DeepL(web)"] = {
|
||||
"source":dict_deepl_web_languages,
|
||||
"target":dict_deepl_web_languages,
|
||||
}
|
||||
|
||||
dict_deepl_auth_source_languages = {
|
||||
"Japanese":"ja",
|
||||
"English":"en",
|
||||
"Bulgarian":"bg",
|
||||
"Czech":"cs",
|
||||
"Danish":"da",
|
||||
"German":"de",
|
||||
"Greek":"el",
|
||||
"Spanish":"es",
|
||||
"Estonian":"et",
|
||||
"Finnish":"fi",
|
||||
"French":"fr",
|
||||
"Hungarian":"hu",
|
||||
"Indonesian":"id",
|
||||
"Italian":"it",
|
||||
"Korean":"ko",
|
||||
"Lithuanian":"lt",
|
||||
"Latvian":"lv",
|
||||
"Norwegian":"nb",
|
||||
"Dutch":"nl",
|
||||
"Polish":"pl",
|
||||
"Portuguese":"pt",
|
||||
"Romanian":"ro",
|
||||
"Russian":"ru",
|
||||
"Slovak":"sk",
|
||||
"Slovenian":"sl",
|
||||
"Swedish":"sv",
|
||||
"Turkish":"tr",
|
||||
"Ukrainian":"uk",
|
||||
"Chinese":"zh"
|
||||
}
|
||||
dict_deepl_auth_target_languages = {
|
||||
"Japanese":"ja",
|
||||
"English American":"en-US",
|
||||
"English British":"en-GB",
|
||||
"Bulgarian":"bg",
|
||||
"Czech":"cs",
|
||||
"Danish":"da",
|
||||
"German":"de",
|
||||
"Greek":"el",
|
||||
"English":"en",
|
||||
"Spanish":"es",
|
||||
"Estonian":"et",
|
||||
"Finnish":"fi",
|
||||
"French":"fr",
|
||||
"Hungarian":"hu",
|
||||
"Indonesian":"id",
|
||||
"Italian":"it",
|
||||
"Korean":"ko",
|
||||
"Lithuanian":"lt",
|
||||
"Latvian":"lv",
|
||||
"Norwegian":"nb",
|
||||
"Dutch":"nl",
|
||||
"Polish":"pl",
|
||||
"Portuguese Brazilian":"pt-BR",
|
||||
"Portuguese European":"pt-PT",
|
||||
"Romanian":"ro",
|
||||
"Russian":"ru",
|
||||
"Slovak":"sk",
|
||||
"Slovenian":"sl",
|
||||
"Swedish":"sv",
|
||||
"Turkish":"tr",
|
||||
"Ukrainian":"uk",
|
||||
"Chinese":"zh"
|
||||
}
|
||||
translation_lang["DeepL(auth)"] = {
|
||||
"source": dict_deepl_auth_source_languages,
|
||||
"target": dict_deepl_auth_target_languages,
|
||||
}
|
||||
|
||||
dict_google_web_languages = {
|
||||
"Japanese":"ja",
|
||||
"English":"en",
|
||||
"Chinese":"zh",
|
||||
"Arabic":"ar",
|
||||
"Russian":"ru",
|
||||
"French":"fr",
|
||||
"German":"de",
|
||||
"Spanish":"es",
|
||||
"Portuguese":"pt",
|
||||
"Italian":"it",
|
||||
"Korean":"ko",
|
||||
"Greek":"el",
|
||||
"Dutch":"nl",
|
||||
"Hindi":"hi",
|
||||
"Turkish":"tr",
|
||||
"Malay":"ms",
|
||||
"Thai":"th",
|
||||
"Vietnamese":"vi",
|
||||
"Indonesian":"id",
|
||||
"Hebrew":"he",
|
||||
"Polish":"pl",
|
||||
"Mongolian":"mn",
|
||||
"Czech":"cs",
|
||||
"Hungarian":"hu",
|
||||
"Estonian":"et",
|
||||
"Bulgarian":"bg",
|
||||
"Danish":"da",
|
||||
"Finnish":"fi",
|
||||
"Romanian":"ro",
|
||||
"Swedish":"sv",
|
||||
"Slovenian":"sl",
|
||||
"Persian/Farsi":"fa",
|
||||
"Bosnian":"bs",
|
||||
"Serbian":"sr",
|
||||
"Filipino":"tl",
|
||||
"Haitiancreole":"ht",
|
||||
"Catalan":"ca",
|
||||
"Croatian":"hr",
|
||||
"Latvian":"lv",
|
||||
"Lithuanian":"lt",
|
||||
"Urdu":"ur",
|
||||
"Ukrainian":"uk",
|
||||
"Welsh":"cy",
|
||||
"Swahili":"sw",
|
||||
"Samoan":"sm",
|
||||
"Slovak":"sk",
|
||||
"Afrikaans":"af",
|
||||
"Norwegian":"no",
|
||||
"Bengali":"bn",
|
||||
"Malagasy":"mg",
|
||||
"Maltese":"mt",
|
||||
"Gujarati":"gu",
|
||||
"Tamil":"ta",
|
||||
"Telugu":"te",
|
||||
"Punjabi":"pa",
|
||||
"Amharic":"am",
|
||||
"Azerbaijani":"az",
|
||||
"Belarusian":"be",
|
||||
"Cebuano":"ceb",
|
||||
"Esperanto":"eo",
|
||||
"Basque":"eu",
|
||||
"Irish":"ga"
|
||||
}
|
||||
translation_lang["Google(web)"] = {
|
||||
"source":dict_google_web_languages,
|
||||
"target":dict_google_web_languages,
|
||||
}
|
||||
|
||||
dict_bing_web_languages = {
|
||||
"Japanese":"ja",
|
||||
"English":"en",
|
||||
"Chinese":"zh",
|
||||
"Arabic":"ar",
|
||||
"Russian":"ru",
|
||||
"French":"fr",
|
||||
"German":"de",
|
||||
"Spanish":"es",
|
||||
"Portuguese":"pt",
|
||||
"Italian":"it",
|
||||
"Korean":"ko",
|
||||
"Greek":"el",
|
||||
"Dutch":"nl",
|
||||
"Hindi":"hi",
|
||||
"Turkish":"tr",
|
||||
"Malay":"ms",
|
||||
"Thai":"th",
|
||||
"Vietnamese":"vi",
|
||||
"Indonesian":"id",
|
||||
"Hebrew":"he",
|
||||
"Polish":"pl",
|
||||
"Czech":"cs",
|
||||
"Hungarian":"hu",
|
||||
"Estonian":"et",
|
||||
"Bulgarian":"bg",
|
||||
"Danish":"da",
|
||||
"Finnish":"fi",
|
||||
"Romanian":"ro",
|
||||
"Swedish":"sv",
|
||||
"Slovenian":"sl",
|
||||
"Persian/Farsi":"fa",
|
||||
"Bosnian":"bs",
|
||||
"Serbian":"sr",
|
||||
"Fijian":"fj",
|
||||
"Filipino":"tl",
|
||||
"Haitiancreole":"ht",
|
||||
"Catalan":"ca",
|
||||
"Croatian":"hr",
|
||||
"Latvian":"lv",
|
||||
"Lithuanian":"lt",
|
||||
"Urdu":"ur",
|
||||
"Ukrainian":"uk",
|
||||
"Welsh":"cy",
|
||||
"Tahiti":"ty",
|
||||
"Tongan":"to",
|
||||
"Swahili":"sw",
|
||||
"Samoan":"sm",
|
||||
"Slovak":"sk",
|
||||
"Afrikaans":"af",
|
||||
"Norwegian":"no",
|
||||
"Bengali":"bn",
|
||||
"Malagasy":"mg",
|
||||
"Maltese":"mt",
|
||||
"Queretaro otomi":"otq",
|
||||
"Klingon/tlhingan Hol":"tlh",
|
||||
"Gujarati":"gu",
|
||||
"Tamil":"ta",
|
||||
"Telugu":"te",
|
||||
"Punjabi":"pa",
|
||||
"Irish":"ga"
|
||||
}
|
||||
translation_lang["Bing(web)"] = {
|
||||
"source":dict_bing_web_languages,
|
||||
"target":dict_bing_web_languages,
|
||||
}
|
||||
|
||||
selectable_languages = {
|
||||
"en": "English",
|
||||
"ja": "日本語",
|
||||
"ko": "한국어"
|
||||
"ko": "한국어(일부 지원)"
|
||||
# 新しい言語とキーを追加する場合はここに追記してください
|
||||
}
|
||||
187
locales/en.yml
Normal file
@@ -0,0 +1,187 @@
|
||||
main_window:
|
||||
translation: Translation
|
||||
transcription_send: Voice2Chatbox
|
||||
transcription_receive: Speaker2Log
|
||||
foreground: Foreground
|
||||
|
||||
language_settings: Language Settings
|
||||
your_language: Your Language
|
||||
both_direction_desc: Translate Each Other
|
||||
target_language: Target Language
|
||||
|
||||
textbox_tab_all: All
|
||||
textbox_tab_sent: Sent
|
||||
textbox_tab_received: Received
|
||||
textbox_tab_system: System
|
||||
|
||||
textbox_system_message:
|
||||
enabled_translation: Translation feature is turned on.
|
||||
disabled_translation: Translation feature is turned off.
|
||||
enabled_voice2chatbox: Transcription from the microphone has started.
|
||||
disabled_voice2chatbox: Transcription from the microphone has been stopped.
|
||||
enabled_speaker2log: Transcription from the speaker has started.
|
||||
disabled_speaker2log: Transcription from the speaker has been stopped.
|
||||
enabled_foreground: The screen is fixed in the foreground.
|
||||
disabled_foreground: The foreground fixation has been released.
|
||||
|
||||
auth_key_success: Auth key update completed.
|
||||
auth_key_error: Auth Key is incorrect or Usage limit reached.
|
||||
|
||||
no_mic_device_detected_error: No mic device detected.
|
||||
no_speaker_device_detected_error: No speaker device detected.
|
||||
translation_engine_limit_error: It has automatically disabled the translation feature. Access has been temporarily restricted due to an excessive number of requests to the translation engine. Please wait for a while, restart VRCT, and try again.
|
||||
|
||||
detected_by_word_filter: The word %{detected_message} has not been sent due to detection by the word filter.
|
||||
|
||||
selected_your_language: "\"Your Language\" has set to %{your_language}."
|
||||
selected_target_language: "\"Target Language\" has set to %{target_language}."
|
||||
switched_language_preset_tab: Switched to Language Preset Tab No.%{tab_no}."
|
||||
latest_language_setting: "Currently, \"Your Language\" is set to %{your_language}, and \"Target Language\" is set to %{target_language}."
|
||||
|
||||
opened_web_page_booth: Opened Booth page in your web browser.
|
||||
opened_web_page_vrct_documents: "Opened VRCT Documents page in your web browser.\nFor any issues, requests, or inquiries, please feel free to contact us through the links at the bottom of the documents page, the \"Contact Form,\" or via X (formerly Twitter)!"
|
||||
|
||||
update_available: New version is here!
|
||||
|
||||
cover_message: The functionality is temporarily disabled until the settings window is closed.
|
||||
|
||||
confirmation_message:
|
||||
update_software: "Download new version and restart automatically.\nIt'll take a while. Do it now?"
|
||||
deny_update_software: Do it later
|
||||
accept_update_software: Update and Restart
|
||||
updating: Now updating...
|
||||
|
||||
detected_over_ui_size: "Current UI Size: %{current_ui_size}\nVRCT's window size may be larger than your display size.\n* Depending on your display size, you may need to adjust it multiple times."
|
||||
deny_adjust_ui_size: "Keep it at this size"
|
||||
accept_adjust_ui_size: "Set it smaller and restart"
|
||||
|
||||
|
||||
translation_engine_limit_error: "It has automatically disabled the translation feature.\nAccess has been temporarily restricted\ndue to an excessive number of requests to the translation engine.\nPlease wait for a while, restart VRCT, and try again."
|
||||
accept_translation_engine_limit_error: Accept and close
|
||||
|
||||
|
||||
selectable_language_window:
|
||||
title_your_language: Select Your Language
|
||||
title_target_language: Select Target Language
|
||||
go_back_button: Go Back
|
||||
|
||||
|
||||
config_window:
|
||||
config_title: Settings
|
||||
compact_mode: Compact Mode
|
||||
version: version %{version}
|
||||
restart_message: Apply changes with a restart.
|
||||
common_error_message:
|
||||
invalid_value: Invalid value.
|
||||
|
||||
side_menu_labels:
|
||||
appearance: Appearance
|
||||
translation: Translation
|
||||
transcription: Transcription
|
||||
transcription_mic: Mic
|
||||
transcription_speaker: Speaker
|
||||
others: Others
|
||||
advanced_settings: Advanced Settings
|
||||
|
||||
|
||||
transparency:
|
||||
label: Transparency
|
||||
desc: Change the main window's transparency.
|
||||
|
||||
appearance_theme:
|
||||
label: Theme [Under development]
|
||||
desc: Change the color theme. Currently, only the Dark theme is supported. The Light theme is under development.
|
||||
|
||||
ui_size:
|
||||
label: UI Size
|
||||
|
||||
font_family:
|
||||
label: Font Family
|
||||
|
||||
ui_language:
|
||||
label: UI Language
|
||||
|
||||
deepl_auth_key:
|
||||
label: DeepL Auth Key
|
||||
|
||||
mic_host:
|
||||
label: Mic Host/Driver
|
||||
|
||||
mic_device:
|
||||
label: Mic Device
|
||||
|
||||
mic_dynamic_energy_threshold:
|
||||
label_for_automatic: "Mic Energy Threshold (Current Setting: Automatic)"
|
||||
desc_for_automatic: "Automatically determine microphone input sensitivity."
|
||||
label_for_manual: "Mic Energy Threshold (Current Setting: Manual)"
|
||||
desc_for_manual: "Manually determine the microphone input sensitivity using the slider. Press the microphone icon to input your voice and adjust the sensitivity while monitoring the volume."
|
||||
error_message: You can set it with a value between 0 to %{max}.
|
||||
|
||||
mic_record_timeout:
|
||||
label: Mic Record Timeout
|
||||
desc: Detects silence and, when the specified number of seconds has passed, considers the mic input to have ended. (Second(s))
|
||||
error_message: It cannot be greater than '%{mic_phrase_timeout_label}' with a value of 0 or more.
|
||||
|
||||
mic_phrase_timeout:
|
||||
label: Mic Phrase Timeout
|
||||
desc: Transcription processing is performed at intervals of the specified number of seconds.
|
||||
error_message: It cannot be set lower than '%{mic_record_timeout_label}' with a value of 0 or more.
|
||||
|
||||
mic_max_phrase:
|
||||
label: Mic Max Words
|
||||
desc: It is the lower limit for the number of transcribed words, and only when this number is exceeded will the transcription results be displayed logs and send to VRChat.
|
||||
error_message: You can set a number equal to or greater than 0.
|
||||
|
||||
mic_word_filter:
|
||||
label: Mic Word Filter
|
||||
desc: "It will not send the sentence if the word(s) included in the set list of words.\nHow to set: e.g. AAA,BBB,CCC"
|
||||
|
||||
|
||||
speaker_dynamic_energy_threshold:
|
||||
label_for_automatic: "Speaker Energy Threshold (Current Setting: Automatic)"
|
||||
desc_for_automatic: "Automatically determine speaker input sensitivity."
|
||||
label_for_manual: "Speaker Energy Threshold (Current Setting: Manual)"
|
||||
desc_for_manual: "Manually determine the speaker input sensitivity using the slider. Press the headphones icon to listen to the audio and adjust the sensitivity while monitoring the volume."
|
||||
error_message: You can set it with a value between 0 to %{max}.
|
||||
no_device_error_message: No speaker device detected.
|
||||
|
||||
speaker_record_timeout:
|
||||
label: Speaker Record Timeout
|
||||
desc: Detects silence and, when the specified number of seconds has passed, considers the speaker input to have ended. (Second(s))
|
||||
error_message: It cannot be greater than '%{speaker_phrase_timeout_label}' with a value of 0 or more.
|
||||
|
||||
speaker_phrase_timeout:
|
||||
label: Speaker Phrase Timeout
|
||||
desc: Transcription processing is performed at intervals of the specified number of seconds.
|
||||
error_message: It cannot be set lower than '%{speaker_record_timeout_label}' with a value of 0 or more.
|
||||
|
||||
speaker_max_phrase:
|
||||
label: Speaker Max Words
|
||||
desc: It is the lower limit for the number of transcribed words, and only when this number is exceeded will the transcription results be displayed logs.
|
||||
error_message: You can set a number equal to or greater than 0.
|
||||
|
||||
|
||||
auto_clear_the_message_box:
|
||||
label: Auto Clear The Message Box
|
||||
|
||||
notice_xsoverlay:
|
||||
label: Notification XSOverlay (VR Only)
|
||||
desc: Notify received messages by using XSOverlay's notification feature.
|
||||
|
||||
auto_export_message_logs:
|
||||
label: Auto Export Message Logs
|
||||
desc: Automatically export the conversation messages as a text file.
|
||||
|
||||
message_format:
|
||||
label: Message Format
|
||||
desc: "You can change the decoration of the message you want to send.\n[message] will be replaced with the message, and [translation] will be replaced with the translated message.\nIt will be used in Notification XSOverlay too."
|
||||
|
||||
send_message_to_vrc:
|
||||
label: Send Message To VRChat
|
||||
desc: There is a way to use it without sending messages to VRChat, but it is not supported. Enable this feature when you intend to send a message to VRChat.
|
||||
|
||||
osc_ip_address:
|
||||
label: OSC IP Address
|
||||
|
||||
osc_port:
|
||||
label: OSC Port
|
||||
186
locales/ja.yml
Normal file
@@ -0,0 +1,186 @@
|
||||
main_window:
|
||||
translation: 翻訳
|
||||
transcription_send: 音声認識(マイク)
|
||||
transcription_receive: 音声認識(スピーカー)
|
||||
foreground: 最前面表示
|
||||
|
||||
language_settings: 言語設定
|
||||
your_language: あなたの言語
|
||||
both_direction_desc: 双方向に翻訳
|
||||
target_language: 相手の言語
|
||||
|
||||
textbox_tab_all: 全て
|
||||
textbox_tab_sent: 送信
|
||||
textbox_tab_received: 受信
|
||||
textbox_tab_system: システム
|
||||
|
||||
textbox_system_message:
|
||||
enabled_translation: 翻訳機能をONにしました。
|
||||
disabled_translation: 翻訳機能をOFFしました。
|
||||
enabled_voice2chatbox: マイクからの音声入力、文字起こしを開始します。
|
||||
disabled_voice2chatbox: マイクからの音声入力、文字起こしを終了しました。
|
||||
enabled_speaker2log: スピーカーからの音声聞き取り、文字起こしを開始します。
|
||||
disabled_speaker2log: スピーカーからの音声聞き取り、文字起こしを終了しました。
|
||||
enabled_foreground: 画面を常に最前面へ固定します。
|
||||
disabled_foreground: 最前面への固定を解除しました。
|
||||
|
||||
auth_key_success: 認証キーの更新が完了しました。
|
||||
auth_key_error: 認証キーが間違っているか、API使用制限が上限に達しています.
|
||||
|
||||
no_mic_device_detected_error: マイクデバイスが検出されませんでした。
|
||||
no_speaker_device_detected_error: スピーカーデバイスが検出されませんでした。
|
||||
translation_engine_limit_error: 翻訳機能を自動的に停止しました。翻訳エンジンへのリクエストが多すぎるため、一時的にアクセスが制限されています。しばらく待ってから、VRCTの再起動をしてもう一度試してみてください。
|
||||
|
||||
detected_by_word_filter: ワードフィルターに登録されている単語 %{detected_message} が検出されたため送信しませんでした。
|
||||
|
||||
selected_your_language: 「あなたの言語」 を %{your_language} に設定しました。
|
||||
selected_target_language: 「相手の言語」 を %{target_language} に設定しました。
|
||||
switched_language_preset_tab: 言語プリセット番号 %{tab_no} に切り替わりました。
|
||||
latest_language_setting: 現在「あなたの言語」は %{your_language}、「相手の言語」は %{target_language} に設定されています。
|
||||
|
||||
opened_web_page_booth: お使いのブラウザで、Boothのページを開きました。
|
||||
opened_web_page_vrct_documents: "お使いのブラウザで、VRCTのドキュメントを開きました。使用方法などはそちらに記載されています。\n不具合、ご要望、その他お問い合わせはドキュメント最下部にあるLinks、「お問合せフォーム」もしくはX(元Twitter)にて気軽にご連絡ください!"
|
||||
|
||||
update_available: 新しいバージョンが出ました!
|
||||
|
||||
cover_message: 設定画面が閉じられるまで、一時的に機能を停止しています。
|
||||
|
||||
confirmation_message:
|
||||
update_software: "新しいバージョンをダウンロードして再起動します。\n少し時間がかかるかもしれません。今すぐ行いますか?"
|
||||
deny_update_software: 後でする
|
||||
accept_update_software: アップデートして再起動
|
||||
updating: アップデート中...
|
||||
|
||||
detected_over_ui_size: "現在のUI サイズ: %{current_ui_size}\nVRCTのウィンドウサイズが、お使いのディスプレイサイズより大きい可能性があります。\n※ディスプレイサイズによっては、何度か再設定が必要な場合があります。"
|
||||
deny_adjust_ui_size: このサイズのままで良い
|
||||
accept_adjust_ui_size: 小さく設定して再起動
|
||||
|
||||
|
||||
translation_engine_limit_error: "翻訳機能を自動的に停止しました。\n翻訳エンジンへのリクエストが多すぎるため\n一時的にアクセスが制限されています。\nしばらく待ってから、VRCTの再起動をしてもう一度試してみてください。"
|
||||
accept_translation_engine_limit_error: 了承して閉じる
|
||||
|
||||
|
||||
selectable_language_window:
|
||||
title_your_language: あなたの言語
|
||||
title_target_language: 相手の言語
|
||||
go_back_button: 戻る
|
||||
|
||||
|
||||
config_window:
|
||||
config_title: 設定
|
||||
compact_mode: コンパクトモード
|
||||
version: バージョン %{version}
|
||||
restart_message: 再起動して変更を適用する。
|
||||
common_error_message:
|
||||
invalid_value: 無効な値です。
|
||||
|
||||
side_menu_labels:
|
||||
appearance: デザイン
|
||||
translation: 翻訳
|
||||
transcription: 音声認識
|
||||
transcription_mic: マイク
|
||||
transcription_speaker: スピーカー
|
||||
others: その他
|
||||
advanced_settings: 高度な設定
|
||||
|
||||
|
||||
transparency:
|
||||
label: 透明度
|
||||
desc: メイン画面の透明度を変更できます。
|
||||
|
||||
appearance_theme:
|
||||
label: 外観テーマ [開発中]
|
||||
desc: カラーテーマを変更できます。現在はDarkテーマのみ対応。Lightテーマは制作中です。
|
||||
|
||||
ui_size:
|
||||
label: UIサイズ
|
||||
|
||||
font_family:
|
||||
label: 使用フォント
|
||||
|
||||
ui_language:
|
||||
label: UIの言語 / UI Language
|
||||
|
||||
deepl_auth_key:
|
||||
label: DeepL 認証キー
|
||||
|
||||
mic_host:
|
||||
label: マイク(ホスト/ドライバー)
|
||||
|
||||
mic_device:
|
||||
label: マイク (デバイス)
|
||||
|
||||
mic_dynamic_energy_threshold:
|
||||
label_for_automatic: "マイク入力感度の調整 (現在の設定: 自動)"
|
||||
desc_for_automatic: マイクの入力感度を自動的に調節する。
|
||||
label_for_manual: "マイク入力感度の調整 (現在の設定: 手動)"
|
||||
desc_for_manual: スライダーを調整して入力感度を手動で決められます。マイクのアイコンを押すと、実際に声を入力し、音量を確認しながら調節できます。
|
||||
error_message: 0 から %{max} までの数値で設定できます。
|
||||
|
||||
mic_record_timeout:
|
||||
label: 入力が終了したとみなす無音時間
|
||||
desc: 無音を検出し、設定された秒数経過すると、音声入力が終了したとみなします。
|
||||
error_message: 0 以上で 「%{mic_phrase_timeout_label}」より大きくすることはできません。
|
||||
|
||||
mic_phrase_timeout:
|
||||
label: 一度に文字起こしする時間の長さ
|
||||
desc: 設定された秒数ごとに文字起こし処理が行われます。
|
||||
error_message: 0 以上で 「%{mic_record_timeout_label}」より小さくすることはできません。
|
||||
|
||||
mic_max_phrase:
|
||||
label: 送信するまでに保持する単語数
|
||||
desc: 文字起こしされた単語数の下限値で、この数値を超えた場合のみ結果をVRChatへ送信し、ログに表示します。
|
||||
error_message: 0以上の数値を設定できます。
|
||||
|
||||
mic_word_filter:
|
||||
label: ワードフィルター
|
||||
desc: "設定された単語を検出すると、その文章は送信されません。\n設定の例: AAA,BBB,CCC"
|
||||
|
||||
|
||||
speaker_dynamic_energy_threshold:
|
||||
label_for_automatic: "スピーカー入力感度の調整 (現在の設定: 自動)"
|
||||
desc_for_automatic: スピーカーの入力感度を自動的に調節する。
|
||||
label_for_manual: "スピーカー入力感度の調整 (現在の設定: 手動)"
|
||||
desc_for_manual: スライダーを調整して入力感度を手動で決められます。ヘッドフォンのアイコンを押すと、実際に音声を聞き取り、音量を確認しながら調節できます。
|
||||
error_message: 0 から %{max} までの数値で設定できます。
|
||||
no_device_error_message: スピーカーデバイスが検出されませんでした。
|
||||
|
||||
speaker_record_timeout:
|
||||
label: 入力が終了したとみなす無音時間
|
||||
desc: 無音を検出し、設定された秒数経過すると、音声入力が終了したとみなします。
|
||||
error_message: 0 以上で 「%{speaker_phrase_timeout_label}」より大きくすることはできません。
|
||||
|
||||
speaker_phrase_timeout:
|
||||
label: 一度に文字起こしする時間の長さ
|
||||
desc: 設定された秒数ごとに文字起こし処理が行われます。
|
||||
error_message: 0 以上で 「%{speaker_record_timeout_label}」より小さくすることはできません。
|
||||
|
||||
speaker_max_phrase:
|
||||
label: ログとして表示するまでに保持する単語数
|
||||
desc: 文字起こしされた単語数の下限値で、この数値を超えた場合のみ結果をログに表示します。
|
||||
error_message: 0以上の数値を設定できます。
|
||||
|
||||
auto_clear_the_message_box:
|
||||
label: 送信後はチャットボックスを空にする
|
||||
|
||||
notice_xsoverlay:
|
||||
label: XSOverlayでの通知受け取り機能を有効 (VR限定)
|
||||
desc: 文字起こし(受信)されたメッセージをXSOverlayの機能を使って通知として受け取れます。
|
||||
|
||||
auto_export_message_logs:
|
||||
label: 会話ログを自動的に保存する
|
||||
desc: テキストファイルとしてログがlogsフォルダ内に保存されます。
|
||||
|
||||
message_format:
|
||||
label: 送信するメッセージのフォーマット
|
||||
desc: "VRChatで相手に実際に見えるフォーマットを変更できます。\n[message]がメッセージに置換され、\n[translation]が翻訳されたメッセージに置換されます。\n※XSOverlayでの通知受け取り機能でも使われます。"
|
||||
|
||||
send_message_to_vrc:
|
||||
label: VRChatにメッセージを送信する
|
||||
desc: "サポート対象外ですが、VRChatにメッセージを送信せずに使う方法があります。送信したい場合、この機能を有効にする事を忘れないでください。"
|
||||
|
||||
osc_ip_address:
|
||||
label: OSC IP Address
|
||||
|
||||
osc_port:
|
||||
label: OSC Port
|
||||
187
locales/ko.yml
Normal file
@@ -0,0 +1,187 @@
|
||||
main_window:
|
||||
translation: 번역
|
||||
transcription_send: 마이크 -> 챗박스
|
||||
transcription_receive: 스피커 -> 로그
|
||||
foreground: 항상 위로
|
||||
|
||||
# language_settings: Language Settings
|
||||
# your_language: Your Language
|
||||
# both_direction_desc: Translate Each Other
|
||||
# target_language: Target Language
|
||||
|
||||
textbox_tab_all: 전체
|
||||
textbox_tab_sent: 전송
|
||||
textbox_tab_received: 수신
|
||||
textbox_tab_system: 시스템
|
||||
|
||||
# textbox_system_message:
|
||||
# enabled_translation: Translation feature is turned on.
|
||||
# disabled_translation: Translation feature is turned off.
|
||||
# enabled_voice2chatbox: Transcription from the microphone has started.
|
||||
# disabled_voice2chatbox: Transcription from the microphone has been stopped.
|
||||
# enabled_speaker2log: Transcription from the speaker has started.
|
||||
# disabled_speaker2log: Transcription from the speaker has been stopped.
|
||||
# enabled_foreground: The screen is fixed in the foreground.
|
||||
# disabled_foreground: The foreground fixation has been released.
|
||||
|
||||
# auth_key_success: Auth key update completed.
|
||||
# auth_key_error: Auth Key is incorrect or Usage limit reached.
|
||||
|
||||
# no_mic_device_detected_error: No mic device detected.
|
||||
# no_speaker_device_detected_error: No speaker device detected.
|
||||
# translation_engine_limit_error: It has automatically disabled the translation feature. Access has been temporarily restricted due to an excessive number of requests to the translation engine. Please wait for a while, restart VRCT, and try again.
|
||||
|
||||
# detected_by_word_filter: The word %{detected_message} has not been sent due to detection by the word filter.
|
||||
|
||||
# selected_your_language: "\"Your Language\" has set to %{your_language}."
|
||||
# selected_target_language: "\"Target Language\" has set to %{target_language}."
|
||||
# switched_language_preset_tab: Switched to Language Preset Tab No.%{tab_no}."
|
||||
# latest_language_setting: "Currently, \"Your Language\" is set to %{your_language}, and \"Target Language\" is set to %{target_language}."
|
||||
|
||||
# opened_web_page_booth: Opened Booth page in your web browser.
|
||||
# opened_web_page_vrct_documents: "Opened VRCT Documents page in your web browser.\nFor any issues, requests, or inquiries, please feel free to contact us through the links at the bottom of the documents page, the \"Contact Form,\" or via X (formerly Twitter)!"
|
||||
|
||||
# update_available: New version is here!
|
||||
|
||||
# cover_message: The functionality is temporarily disabled until the settings window is closed.
|
||||
|
||||
# confirmation_message:
|
||||
# update_software: "Download new version and restart automatically.\nIt'll take a while. Do it now?"
|
||||
# deny_update_software: Do it later
|
||||
# accept_update_software: Update and Restart
|
||||
# updating: Now updating...
|
||||
|
||||
# detected_over_ui_size: "Current UI Size: %{current_ui_size}\nVRCT's window size may be larger than your display size.\n* Depending on your display size, you may need to adjust it multiple times."
|
||||
# deny_adjust_ui_size: "Keep it at this size"
|
||||
# accept_adjust_ui_size: "Set it smaller and restart"
|
||||
|
||||
|
||||
# translation_engine_limit_error: "It has automatically disabled the translation feature.\nAccess has been temporarily restricted\ndue to an excessive number of requests to the translation engine.\nPlease wait for a while, restart VRCT, and try again."
|
||||
# accept_translation_engine_limit_error: Accept and close
|
||||
|
||||
|
||||
# selectable_language_window:
|
||||
# title_your_language: Select Your Language
|
||||
# title_target_language: Select Target Language
|
||||
# go_back_button: Go Back
|
||||
|
||||
|
||||
config_window:
|
||||
# config_title: Settings
|
||||
# compact_mode: Compact Mode
|
||||
# version: version %{version}
|
||||
# restart_message: Apply changes with a restart.
|
||||
# common_error_message:
|
||||
# invalid_value: Invalid value.
|
||||
|
||||
side_menu_labels:
|
||||
# appearance: Appearance
|
||||
translation: 번역
|
||||
transcription: 음성인식
|
||||
transcription_mic: 마이크
|
||||
transcription_speaker: 스피커
|
||||
others: 기타
|
||||
# advanced_settings: Advanced Settings
|
||||
|
||||
|
||||
transparency:
|
||||
label: 투명도
|
||||
# desc: Change the main window's transparency.
|
||||
|
||||
appearance_theme:
|
||||
label: 테마 [Under development]
|
||||
# desc: Change the color theme. Currently, only the Dark theme is supported. The Light theme is under development.
|
||||
|
||||
ui_size:
|
||||
label: UI 크기
|
||||
|
||||
font_family:
|
||||
label: 폰트
|
||||
|
||||
ui_language:
|
||||
label: UI 언어 / UI Language
|
||||
|
||||
deepl_auth_key:
|
||||
label: DeepL 인증키
|
||||
|
||||
mic_host:
|
||||
label: 마이크 호스트/Driver
|
||||
|
||||
mic_device:
|
||||
label: 마이크 장치
|
||||
|
||||
mic_dynamic_energy_threshold:
|
||||
label_for_automatic: "음성 입력 최소 볼륨 (Current Setting: Automatic)"
|
||||
# desc_for_automatic: "Automatically determine microphone input sensitivity."
|
||||
label_for_manual: "음성 입력 최소 볼륨 (Current Setting: Manual)"
|
||||
# desc_for_manual: "Manually determine the microphone input sensitivity using the slider. Press the microphone icon to input your voice and adjust the sensitivity while monitoring the volume."
|
||||
# error_message: You can set it with a value between 0 to %{max}.
|
||||
|
||||
mic_record_timeout:
|
||||
label: 최대 무음 시간
|
||||
# desc: Detects silence and, when the specified number of seconds has passed, considers the mic input to have ended. (Second(s))
|
||||
# error_message: It cannot be greater than '%{mic_phrase_timeout_label}' with a value of 0 or more.
|
||||
|
||||
mic_phrase_timeout:
|
||||
label: 최대 인식 시간
|
||||
# desc: Transcription processing is performed at intervals of the specified number of seconds.
|
||||
# error_message: It cannot be set lower than '%{mic_record_timeout_label}' with a value of 0 or more.
|
||||
|
||||
mic_max_phrase:
|
||||
label: 최대 입력 절(phrases) 수
|
||||
# desc: It is the lower limit for the number of transcribed words, and only when this number is exceeded will the transcription results be displayed logs and send to VRChat.
|
||||
# error_message: You can set a number equal to or greater than 0.
|
||||
|
||||
mic_word_filter:
|
||||
label: 단어 필터
|
||||
# desc: "It will not send the sentence if the word(s) included in the set list of words.\nHow to set: e.g. AAA,BBB,CCC"
|
||||
|
||||
|
||||
speaker_dynamic_energy_threshold:
|
||||
label_for_automatic: "음성 입력 최소 볼륨 (Current Setting: Automatic)"
|
||||
# desc_for_automatic: "Automatically determine speaker input sensitivity."
|
||||
label_for_manual: "음성 입력 최소 볼륨 (Current Setting: Manual)"
|
||||
# desc_for_manual: "Manually determine the speaker input sensitivity using the slider. Press the headphones icon to listen to the audio and adjust the sensitivity while monitoring the volume."
|
||||
# error_message: You can set it with a value between 0 to %{max}.
|
||||
# no_device_error_message: No speaker device detected.
|
||||
|
||||
speaker_record_timeout:
|
||||
label: 최대 무음 시간
|
||||
# desc: Detects silence and, when the specified number of seconds has passed, considers the speaker input to have ended. (Second(s))
|
||||
# error_message: It cannot be greater than '%{speaker_phrase_timeout_label}' with a value of 0 or more.
|
||||
|
||||
speaker_phrase_timeout:
|
||||
label: 최대 인식 시간
|
||||
# desc: Transcription processing is performed at intervals of the specified number of seconds.
|
||||
# error_message: It cannot be set lower than '%{speaker_record_timeout_label}' with a value of 0 or more.
|
||||
|
||||
speaker_max_phrase:
|
||||
label: 최대 입력 절(phrases) 수
|
||||
# desc: It is the lower limit for the number of transcribed words, and only when this number is exceeded will the transcription results be displayed logs.
|
||||
# error_message: You can set a number equal to or greater than 0.
|
||||
|
||||
|
||||
auto_clear_the_message_box:
|
||||
label: 챗박스 자동 삭제
|
||||
|
||||
# notice_xsoverlay:
|
||||
# label: Notification XSOverlay (VR Only)
|
||||
# desc: Notify received messages by using XSOverlay's notification feature.
|
||||
|
||||
# auto_export_message_logs:
|
||||
# label: Auto Export Message Logs
|
||||
# desc: Automatically export the conversation messages as a text file.
|
||||
|
||||
message_format:
|
||||
label: 전송 형식
|
||||
# desc: "You can change the decoration of the message you want to send.\n[message] will be replaced with the message, and [translation] will be replaced with the translated message.\nIt will be used in Notification XSOverlay too."
|
||||
|
||||
# send_message_to_vrc:
|
||||
# label: Send Message To VRChat
|
||||
# desc: There is a way to use it without sending messages to VRChat, but it is not supported. Enable this feature when you intend to send a message to VRChat.
|
||||
|
||||
osc_ip_address:
|
||||
label: OSC IP 주소
|
||||
|
||||
osc_port:
|
||||
label: OSC 포트
|
||||
18
main.py
Normal file
@@ -0,0 +1,18 @@
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
import ctypes
|
||||
ctypes.windll.shcore.SetProcessDpiAwareness(0)
|
||||
|
||||
from vrct_gui.splash_window import SplashWindow
|
||||
splash = SplashWindow()
|
||||
splash.showSplash()
|
||||
|
||||
import controller
|
||||
controller.createMainWindow()
|
||||
splash.destroySplash()
|
||||
controller.showMainWindow()
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
with open('error.log', 'a') as f:
|
||||
traceback.print_exc(file=f)
|
||||
483
model.py
Normal file
@@ -0,0 +1,483 @@
|
||||
import sys
|
||||
from zipfile import ZipFile
|
||||
from subprocess import Popen
|
||||
from os import makedirs as os_makedirs
|
||||
from os import path as os_path, rename as os_rename
|
||||
from shutil import rmtree
|
||||
from datetime import datetime
|
||||
from logging import getLogger, FileHandler, Formatter, INFO
|
||||
from time import sleep
|
||||
from queue import Queue
|
||||
from threading import Thread, Event
|
||||
from requests import get as requests_get
|
||||
import webbrowser
|
||||
|
||||
from flashtext import KeywordProcessor
|
||||
from models.translation.translation_translator import Translator
|
||||
from models.transcription.transcription_utils import getInputDevices, getDefaultOutputDevice
|
||||
from models.osc.osc_tools import sendTyping, sendMessage, sendTestAction, receiveOscParameters
|
||||
from models.transcription.transcription_recorder import SelectedMicRecorder, SelectedSpeakerRecorder
|
||||
from models.transcription.transcription_recorder import SelectedMicEnergyRecorder, SelectedSpeakeEnergyRecorder
|
||||
from models.transcription.transcription_transcriber import AudioTranscriber
|
||||
from models.xsoverlay.notification import xsoverlayForVRCT
|
||||
from models.translation.translation_languages import translatorEngine, translation_lang
|
||||
from models.transcription.transcription_languages import transcription_lang
|
||||
from config import config
|
||||
|
||||
class threadFnc(Thread):
|
||||
def __init__(self, fnc, end_fnc=None, daemon=True, *args, **kwargs):
|
||||
super(threadFnc, self).__init__(daemon=daemon, *args, **kwargs)
|
||||
self.fnc = fnc
|
||||
self.end_fnc = end_fnc
|
||||
self._stop = Event()
|
||||
def stop(self):
|
||||
self._stop.set()
|
||||
def stopped(self):
|
||||
return self._stop.isSet()
|
||||
def run(self):
|
||||
while True:
|
||||
if self.stopped():
|
||||
if callable(self.end_fnc):
|
||||
self.end_fnc()
|
||||
return
|
||||
self.fnc(*self._args, **self._kwargs)
|
||||
|
||||
class Model:
|
||||
# Languages available for both transcription and translation
|
||||
SUPPORTED_LANGUAGES = [
|
||||
'Afrikaans', 'Arabic', 'Basque', 'Bulgarian', 'Catalan', 'Chinese', 'Croatian',
|
||||
'Czech', 'Danish', 'Dutch', 'English', 'Filipino', 'Finnish', 'French', 'German',
|
||||
'Greek', 'Hebrew', 'Hindi', 'Hungarian', 'Indonesian', 'Italian', 'Japanese',
|
||||
'Korean', 'Lithuanian', 'Malay', 'Norwegian', 'Polish', 'Portuguese', 'Romanian',
|
||||
'Russian', 'Serbian', 'Slovak', 'Slovenian', 'Spanish', 'Swedish', 'Thai', 'Turkish',
|
||||
'Ukrainian', 'Vietnamese'
|
||||
]
|
||||
_instance = None
|
||||
|
||||
def __new__(cls):
|
||||
if cls._instance is None:
|
||||
cls._instance = super(Model, cls).__new__(cls)
|
||||
cls._instance.init()
|
||||
return cls._instance
|
||||
|
||||
def init(self):
|
||||
self.logger = None
|
||||
self.mic_print_transcript = None
|
||||
self.mic_audio_recorder = None
|
||||
self.mic_energy_recorder = None
|
||||
self.mic_energy_plot_progressbar = None
|
||||
self.speaker_print_transcript = None
|
||||
self.speaker_audio_recorder = None
|
||||
self.speaker_energy_recorder = None
|
||||
self.speaker_energy_plot_progressbar = None
|
||||
self.translator = Translator()
|
||||
self.keyword_processor = KeywordProcessor()
|
||||
|
||||
def resetTranslator(self):
|
||||
del self.translator
|
||||
self.translator = Translator()
|
||||
|
||||
def resetKeywordProcessor(self):
|
||||
del self.keyword_processor
|
||||
self.keyword_processor = KeywordProcessor()
|
||||
|
||||
def authenticationTranslator(self, choice_translator=None, auth_key=None):
|
||||
if choice_translator == None:
|
||||
choice_translator = config.CHOICE_TRANSLATOR
|
||||
if auth_key == None:
|
||||
auth_key = config.AUTH_KEYS[choice_translator]
|
||||
|
||||
result = self.translator.authentication(choice_translator, auth_key)
|
||||
return result
|
||||
|
||||
def startLogger(self):
|
||||
os_makedirs(os_path.join(os_path.dirname(sys.argv[0]), "logs"), exist_ok=True)
|
||||
logger = getLogger()
|
||||
logger.setLevel(INFO)
|
||||
file_name = os_path.join(os_path.dirname(sys.argv[0]), "logs", f"{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.log")
|
||||
file_handler = FileHandler(file_name, encoding="utf-8", delay=True)
|
||||
formatter = Formatter("[%(asctime)s] %(message)s")
|
||||
file_handler.setFormatter(formatter)
|
||||
logger.addHandler(file_handler)
|
||||
self.logger = logger
|
||||
self.logger.disabled = False
|
||||
|
||||
def stopLogger(self):
|
||||
self.logger.disabled = True
|
||||
self.logger = None
|
||||
|
||||
@staticmethod
|
||||
def getListLanguageAndCountry():
|
||||
langs = []
|
||||
for lang in model.SUPPORTED_LANGUAGES:
|
||||
for country in transcription_lang[lang]:
|
||||
langs.append(f"{lang}\n({country})")
|
||||
return langs
|
||||
|
||||
@staticmethod
|
||||
def getLanguageAndCountry(select):
|
||||
parts = select.split("\n")
|
||||
language = parts[0]
|
||||
country = parts[1][1:-1]
|
||||
return language, country
|
||||
|
||||
def findTranslationEngine(self, source_lang, target_lang):
|
||||
compatible_engines = []
|
||||
for engine in translatorEngine:
|
||||
source_languages = translation_lang.get(engine, {}).get("source", {})
|
||||
target_languages = translation_lang.get(engine, {}).get("target", {})
|
||||
if source_lang in source_languages and target_lang in target_languages:
|
||||
compatible_engines.append(engine)
|
||||
engine_name = compatible_engines[0]
|
||||
|
||||
if engine_name == "DeepL" and config.AUTH_KEYS["DeepL_API"] != None:
|
||||
if self.authenticationTranslator(engine_name, config.AUTH_KEYS["DeepL_API"]) is True:
|
||||
engine_name = "DeepL_API"
|
||||
elif engine_name == "DeepL_API" and config.AUTH_KEYS["DeepL_API"] == None:
|
||||
engine_name = "DeepL"
|
||||
|
||||
return engine_name
|
||||
|
||||
def getInputTranslate(self, message):
|
||||
translator_name=config.CHOICE_TRANSLATOR
|
||||
source_language=config.SOURCE_LANGUAGE
|
||||
target_language=config.TARGET_LANGUAGE
|
||||
target_country = config.TARGET_COUNTRY
|
||||
|
||||
if translator_name == "DeepL_API":
|
||||
if target_language == "English":
|
||||
if target_country in ["United States", "Canada", "Philippines"]:
|
||||
target_language = "English American"
|
||||
else:
|
||||
target_language = "English British"
|
||||
elif target_language == "Portuguese":
|
||||
if target_country in ["Portugal"]:
|
||||
target_language = "Portuguese European"
|
||||
else:
|
||||
target_language = "Portuguese Brazilian"
|
||||
elif translator_name == "DeepL":
|
||||
if target_language in ["English American", "English British"]:
|
||||
target_language = "English"
|
||||
elif target_language in ["Portuguese European", "Portuguese Brazilian"]:
|
||||
target_language = "Portuguese"
|
||||
|
||||
translation = self.translator.translate(
|
||||
translator_name=translator_name,
|
||||
source_language=source_language,
|
||||
target_language=target_language,
|
||||
message=message
|
||||
)
|
||||
return translation
|
||||
|
||||
def getOutputTranslate(self, message):
|
||||
translator_name=config.CHOICE_TRANSLATOR
|
||||
source_language=config.TARGET_LANGUAGE
|
||||
target_language=config.SOURCE_LANGUAGE
|
||||
target_country = config.SOURCE_COUNTRY
|
||||
|
||||
if translator_name == "DeepL_API":
|
||||
if target_language == "English":
|
||||
if target_country in ["United States", "Canada", "Philippines"]:
|
||||
target_language = "English American"
|
||||
else:
|
||||
target_language = "English British"
|
||||
elif target_language == "Portuguese":
|
||||
if target_country in ["Portugal"]:
|
||||
target_language = "Portuguese European"
|
||||
else:
|
||||
target_language = "Portuguese Brazilian"
|
||||
elif translator_name == "DeepL":
|
||||
if target_language in ["English American", "English British"]:
|
||||
target_language = "English"
|
||||
elif target_language in ["Portuguese European", "Portuguese Brazilian"]:
|
||||
target_language = "Portuguese"
|
||||
|
||||
translation = self.translator.translate(
|
||||
translator_name=translator_name,
|
||||
source_language=source_language,
|
||||
target_language=target_language,
|
||||
message=message
|
||||
)
|
||||
return translation
|
||||
|
||||
def addKeywords(self):
|
||||
for f in config.INPUT_MIC_WORD_FILTER:
|
||||
self.keyword_processor.add_keyword(f)
|
||||
|
||||
def checkKeywords(self, message):
|
||||
return len(self.keyword_processor.extract_keywords(message)) != 0
|
||||
|
||||
@staticmethod
|
||||
def oscStartSendTyping():
|
||||
sendTyping(True, config.OSC_IP_ADDRESS, config.OSC_PORT)
|
||||
|
||||
@staticmethod
|
||||
def oscStopSendTyping():
|
||||
sendTyping(False, config.OSC_IP_ADDRESS, config.OSC_PORT)
|
||||
|
||||
@staticmethod
|
||||
def oscSendMessage(message):
|
||||
sendMessage(message, config.OSC_IP_ADDRESS, config.OSC_PORT)
|
||||
|
||||
def checkOSCStarted(self, fnc):
|
||||
self.is_valid_osc = False
|
||||
def checkOscReceive(address, osc_arguments):
|
||||
if self.is_valid_osc is False:
|
||||
self.is_valid_osc = True
|
||||
|
||||
self.listening_server = receiveOscParameters(checkOscReceive)
|
||||
def oscListener():
|
||||
self.listening_server.serve_forever()
|
||||
|
||||
def sendTestActionLoop():
|
||||
for _ in range(10):
|
||||
sendTestAction()
|
||||
if self.is_valid_osc is True:
|
||||
break
|
||||
sleep(0.1)
|
||||
self.listening_server.shutdown()
|
||||
|
||||
# start receive osc
|
||||
th_receive_osc_parameters = Thread(target=oscListener)
|
||||
th_receive_osc_parameters.daemon = True
|
||||
th_receive_osc_parameters.start()
|
||||
|
||||
# check osc started
|
||||
th_send_osc_test_action = Thread(target=sendTestActionLoop)
|
||||
th_send_osc_test_action.daemon = True
|
||||
th_send_osc_test_action.start()
|
||||
|
||||
th_receive_osc_parameters.join()
|
||||
th_send_osc_test_action.join()
|
||||
|
||||
if self.is_valid_osc is False:
|
||||
fnc()
|
||||
|
||||
@staticmethod
|
||||
def checkSoftwareUpdated():
|
||||
# check update
|
||||
update_flag = False
|
||||
response = requests_get(config.GITHUB_URL)
|
||||
new_version = response.json()["name"]
|
||||
if new_version != config.VERSION:
|
||||
update_flag = True
|
||||
print("software version", "now:", config.VERSION, "new:", new_version)
|
||||
return update_flag
|
||||
|
||||
@staticmethod
|
||||
def updateSoftware(restart:bool=True):
|
||||
filename = 'download.zip'
|
||||
program_name = 'VRCT.exe'
|
||||
temporary_name = '_VRCT.exe'
|
||||
tmp_directory_name = 'tmp'
|
||||
batch_name = 'update.bat'
|
||||
current_directory = os_path.dirname(sys.argv[0])
|
||||
program_directory = os_path.dirname(__file__)
|
||||
|
||||
try:
|
||||
res = requests_get(config.GITHUB_URL)
|
||||
url = res.json()['assets'][0]['browser_download_url']
|
||||
res = requests_get(url, stream=True)
|
||||
os_makedirs(os_path.join(current_directory, tmp_directory_name), exist_ok=True)
|
||||
with open(os_path.join(current_directory, tmp_directory_name, filename), 'wb') as file:
|
||||
for chunk in res.iter_content(chunk_size=1024):
|
||||
file.write(chunk)
|
||||
with ZipFile(os_path.join(current_directory, tmp_directory_name, filename)) as zf:
|
||||
zf.extract(program_name, os_path.join(current_directory, tmp_directory_name))
|
||||
os_rename(os_path.join(current_directory, tmp_directory_name, program_name), os_path.join(current_directory, temporary_name))
|
||||
rmtree(os_path.join(current_directory, tmp_directory_name))
|
||||
command = [os_path.join(program_directory, "batch", batch_name), program_name, temporary_name, str(restart)]
|
||||
Popen(command)
|
||||
except:
|
||||
webbrowser.open(config.BOOTH_URL, new=2, autoraise=True)
|
||||
|
||||
@staticmethod
|
||||
def reStartSoftware():
|
||||
program_name = 'VRCT.exe'
|
||||
batch_name = 'restart.bat'
|
||||
program_directory = os_path.dirname(__file__)
|
||||
command = [os_path.join(program_directory, "batch", batch_name), program_name]
|
||||
Popen(command)
|
||||
|
||||
@staticmethod
|
||||
def getListInputHost():
|
||||
return [host for host in getInputDevices().keys()]
|
||||
|
||||
@staticmethod
|
||||
def getListInputDevice():
|
||||
return [device["name"] for device in getInputDevices()[config.CHOICE_MIC_HOST]]
|
||||
|
||||
@staticmethod
|
||||
def getInputDefaultDevice():
|
||||
return [device["name"] for device in getInputDevices()[config.CHOICE_MIC_HOST]][0]
|
||||
|
||||
@staticmethod
|
||||
def getOutputDefaultDevice():
|
||||
return getDefaultOutputDevice()["name"]
|
||||
|
||||
def startMicTranscript(self, fnc, error_fnc=None):
|
||||
if config.CHOICE_MIC_HOST == "NoHost" or config.CHOICE_MIC_DEVICE == "NoDevice":
|
||||
try:
|
||||
error_fnc()
|
||||
except:
|
||||
pass
|
||||
return
|
||||
|
||||
mic_audio_queue = Queue()
|
||||
device = [device for device in getInputDevices()[config.CHOICE_MIC_HOST] if device["name"] == config.CHOICE_MIC_DEVICE][0]
|
||||
record_timeout = config.INPUT_MIC_RECORD_TIMEOUT
|
||||
phase_timeout = config.INPUT_MIC_PHRASE_TIMEOUT
|
||||
if record_timeout > phase_timeout:
|
||||
record_timeout = phase_timeout
|
||||
|
||||
self.mic_audio_recorder = SelectedMicRecorder(
|
||||
device=device,
|
||||
energy_threshold=config.INPUT_MIC_ENERGY_THRESHOLD,
|
||||
dynamic_energy_threshold=config.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD,
|
||||
record_timeout=record_timeout,
|
||||
)
|
||||
self.mic_audio_recorder.recordIntoQueue(mic_audio_queue)
|
||||
mic_transcriber = AudioTranscriber(
|
||||
speaker=False,
|
||||
source=self.mic_audio_recorder.source,
|
||||
phrase_timeout=phase_timeout,
|
||||
max_phrases=config.INPUT_MIC_MAX_PHRASES,
|
||||
)
|
||||
def sendMicTranscript():
|
||||
mic_transcriber.transcribeAudioQueue(mic_audio_queue, config.SOURCE_LANGUAGE, config.SOURCE_COUNTRY)
|
||||
message = mic_transcriber.getTranscript()
|
||||
try:
|
||||
fnc(message)
|
||||
except:
|
||||
pass
|
||||
|
||||
self.mic_print_transcript = threadFnc(sendMicTranscript)
|
||||
self.mic_print_transcript.daemon = True
|
||||
self.mic_print_transcript.start()
|
||||
|
||||
def stopMicTranscript(self):
|
||||
if isinstance(self.mic_print_transcript, threadFnc):
|
||||
self.mic_print_transcript.stop()
|
||||
self.mic_print_transcript = None
|
||||
if isinstance(self.mic_audio_recorder, SelectedMicRecorder):
|
||||
self.mic_audio_recorder.stop()
|
||||
self.mic_audio_recorder = None
|
||||
|
||||
def startCheckMicEnergy(self, fnc, end_fnc, error_fnc=None):
|
||||
if config.CHOICE_MIC_HOST == "NoHost" or config.CHOICE_MIC_DEVICE == "NoDevice":
|
||||
try:
|
||||
error_fnc()
|
||||
except:
|
||||
pass
|
||||
return
|
||||
|
||||
def sendMicEnergy():
|
||||
if mic_energy_queue.empty() is False:
|
||||
energy = mic_energy_queue.get()
|
||||
try:
|
||||
fnc(energy)
|
||||
except:
|
||||
pass
|
||||
sleep(0.01)
|
||||
|
||||
mic_energy_queue = Queue()
|
||||
mic_device = [device for device in getInputDevices()[config.CHOICE_MIC_HOST] if device["name"] == config.CHOICE_MIC_DEVICE][0]
|
||||
self.mic_energy_recorder = SelectedMicEnergyRecorder(mic_device)
|
||||
self.mic_energy_recorder.recordIntoQueue(mic_energy_queue)
|
||||
self.mic_energy_plot_progressbar = threadFnc(sendMicEnergy, end_fnc=end_fnc)
|
||||
self.mic_energy_plot_progressbar.daemon = True
|
||||
self.mic_energy_plot_progressbar.start()
|
||||
|
||||
def stopCheckMicEnergy(self):
|
||||
if isinstance(self.mic_energy_plot_progressbar, threadFnc):
|
||||
self.mic_energy_plot_progressbar.stop()
|
||||
self.mic_energy_plot_progressbar = None
|
||||
if isinstance(self.mic_energy_recorder, SelectedMicEnergyRecorder):
|
||||
self.mic_energy_recorder.stop()
|
||||
self.mic_energy_recorder = None
|
||||
|
||||
def startSpeakerTranscript(self, fnc, error_fnc=None):
|
||||
speaker_device = getDefaultOutputDevice()
|
||||
if speaker_device["name"] == "NoDevice":
|
||||
try:
|
||||
error_fnc()
|
||||
except:
|
||||
pass
|
||||
return
|
||||
|
||||
speaker_audio_queue = Queue()
|
||||
record_timeout = config.INPUT_SPEAKER_RECORD_TIMEOUT
|
||||
phase_timeout = config.INPUT_SPEAKER_PHRASE_TIMEOUT
|
||||
if record_timeout > phase_timeout:
|
||||
record_timeout = phase_timeout
|
||||
|
||||
self.speaker_audio_recorder = SelectedSpeakerRecorder(
|
||||
device=speaker_device,
|
||||
energy_threshold=config.INPUT_SPEAKER_ENERGY_THRESHOLD,
|
||||
dynamic_energy_threshold=config.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD,
|
||||
record_timeout=record_timeout,
|
||||
)
|
||||
self.speaker_audio_recorder.recordIntoQueue(speaker_audio_queue)
|
||||
speaker_transcriber = AudioTranscriber(
|
||||
speaker=True,
|
||||
source=self.speaker_audio_recorder.source,
|
||||
phrase_timeout=phase_timeout,
|
||||
max_phrases=config.INPUT_SPEAKER_MAX_PHRASES,
|
||||
)
|
||||
def sendSpeakerTranscript():
|
||||
speaker_transcriber.transcribeAudioQueue(speaker_audio_queue, config.TARGET_LANGUAGE, config.TARGET_COUNTRY)
|
||||
message = speaker_transcriber.getTranscript()
|
||||
try:
|
||||
fnc(message)
|
||||
except:
|
||||
pass
|
||||
|
||||
self.speaker_print_transcript = threadFnc(sendSpeakerTranscript)
|
||||
self.speaker_print_transcript.daemon = True
|
||||
self.speaker_print_transcript.start()
|
||||
|
||||
def stopSpeakerTranscript(self):
|
||||
if isinstance(self.speaker_print_transcript, threadFnc):
|
||||
self.speaker_print_transcript.stop()
|
||||
self.speaker_print_transcript = None
|
||||
if isinstance(self.speaker_audio_recorder, SelectedSpeakerRecorder):
|
||||
self.speaker_audio_recorder.stop()
|
||||
self.speaker_audio_recorder = None
|
||||
|
||||
def startCheckSpeakerEnergy(self, fnc, end_fnc, error_fnc=None):
|
||||
speaker_device = getDefaultOutputDevice()
|
||||
if speaker_device["name"] == "NoDevice":
|
||||
try:
|
||||
error_fnc()
|
||||
except:
|
||||
pass
|
||||
return
|
||||
|
||||
def sendSpeakerEnergy():
|
||||
if speaker_energy_queue.empty() is False:
|
||||
energy = speaker_energy_queue.get()
|
||||
try:
|
||||
fnc(energy)
|
||||
except:
|
||||
pass
|
||||
sleep(0.01)
|
||||
|
||||
speaker_energy_queue = Queue()
|
||||
self.speaker_energy_recorder = SelectedSpeakeEnergyRecorder(speaker_device)
|
||||
self.speaker_energy_recorder.recordIntoQueue(speaker_energy_queue)
|
||||
self.speaker_energy_plot_progressbar = threadFnc(sendSpeakerEnergy, end_fnc=end_fnc)
|
||||
self.speaker_energy_plot_progressbar.daemon = True
|
||||
self.speaker_energy_plot_progressbar.start()
|
||||
|
||||
def stopCheckSpeakerEnergy(self):
|
||||
if isinstance(self.speaker_energy_plot_progressbar, threadFnc):
|
||||
self.speaker_energy_plot_progressbar.stop()
|
||||
self.speaker_energy_plot_progressbar = None
|
||||
if isinstance(self.speaker_energy_recorder, SelectedSpeakeEnergyRecorder):
|
||||
self.speaker_energy_recorder.stop()
|
||||
self.speaker_energy_recorder = None
|
||||
|
||||
def notificationXSOverlay(self, message):
|
||||
xsoverlayForVRCT(content=f"{message}")
|
||||
|
||||
model = Model()
|
||||
@@ -6,7 +6,7 @@ from pythonosc import dispatcher
|
||||
from pythonosc import osc_server
|
||||
|
||||
# send OSC message typing
|
||||
def send_typing(flag=False, ip_address="127.0.0.1", port=9000):
|
||||
def sendTyping(flag=False, ip_address="127.0.0.1", port=9000):
|
||||
typing = osc_message_builder.OscMessageBuilder(address="/chatbox/typing")
|
||||
typing.add_arg(flag)
|
||||
b_typing = typing.build()
|
||||
@@ -14,7 +14,7 @@ def send_typing(flag=False, ip_address="127.0.0.1", port=9000):
|
||||
client.send(b_typing)
|
||||
|
||||
# send OSC message
|
||||
def send_message(message=None, ip_address="127.0.0.1", port=9000):
|
||||
def sendMessage(message=None, ip_address="127.0.0.1", port=9000):
|
||||
if message != None:
|
||||
msg = osc_message_builder.OscMessageBuilder(address="/chatbox/input")
|
||||
msg.add_arg(f"{message}")
|
||||
@@ -24,14 +24,34 @@ def send_message(message=None, ip_address="127.0.0.1", port=9000):
|
||||
client = udp_client.SimpleUDPClient(ip_address, port)
|
||||
client.send(b_msg)
|
||||
|
||||
def send_test_action(ip_address="127.0.0.1", port=9000):
|
||||
def sendTestAction(ip_address="127.0.0.1", port=9000):
|
||||
client = udp_client.SimpleUDPClient(ip_address, port)
|
||||
client.send_message("/input/Vertical", 1)
|
||||
sleep(0.01)
|
||||
client.send_message("/input/Vertical", False)
|
||||
|
||||
def receive_osc_parameters(target, filter="/*", ip_address="127.0.0.1", port=9001):
|
||||
# send Input Voice
|
||||
def sendInputVoice(flag=False, ip_address="127.0.0.1", port=9000):
|
||||
input_voice = osc_message_builder.OscMessageBuilder(address="/input/Voice")
|
||||
input_voice.add_arg(flag)
|
||||
b_input_voice = input_voice.build()
|
||||
client = udp_client.SimpleUDPClient(ip_address, port)
|
||||
client.send(b_input_voice)
|
||||
|
||||
def sendChangeVoice(ip_address="127.0.0.1", port=9000):
|
||||
sendInputVoice(flag=0, ip_address=ip_address, port=port)
|
||||
sleep(0.05)
|
||||
sendInputVoice(flag=1, ip_address=ip_address, port=port)
|
||||
sleep(0.05)
|
||||
sendInputVoice(flag=0, ip_address=ip_address, port=port)
|
||||
sleep(0.05)
|
||||
|
||||
def receiveOscParameters(target, filter="/*", ip_address="127.0.0.1", port=9001):
|
||||
_dispatcher = dispatcher.Dispatcher()
|
||||
_dispatcher.map(filter, target)
|
||||
server = osc_server.ThreadingOSCUDPServer((ip_address, port), _dispatcher)
|
||||
server.serve_forever()
|
||||
return server
|
||||
|
||||
if __name__ == "__main__":
|
||||
sendChangeVoice()
|
||||
sendChangeVoice()
|
||||
177
models/transcription/transcription_languages.py
Normal file
@@ -0,0 +1,177 @@
|
||||
transcription_lang = {
|
||||
"Afrikaans":{
|
||||
"South Africa":"af-ZA",
|
||||
},
|
||||
"Arabic":{
|
||||
"Algeria":"ar-DZ",
|
||||
"Bahrain":"ar-BH",
|
||||
"Egypt":"ar-EG",
|
||||
"Israel":"ar-IL",
|
||||
"Iraq":"ar-IQ",
|
||||
"Jordan":"ar-JO",
|
||||
"Kuwait":"ar-KW",
|
||||
"Lebanon":"ar-LB",
|
||||
"Morocco":"ar-MA",
|
||||
"Oman":"ar-OM",
|
||||
"State of Palestine":"ar-PS",
|
||||
"Qatar":"ar-QA",
|
||||
"Saudi Arabia":"ar-SA",
|
||||
"Tunisia":"ar-TN",
|
||||
"United Arab Emirates":"ar-AE",
|
||||
},
|
||||
"Basque":{
|
||||
"Spain":"eu-ES",
|
||||
},
|
||||
"Bulgarian":{
|
||||
"Bulgaria":"bg-BG",
|
||||
},
|
||||
"Catalan":{
|
||||
"Spain":"ca-ES",
|
||||
},
|
||||
"Chinese":{
|
||||
"Mandarin (Simplified, China)":"cmn-Hans-CN",
|
||||
"Mandarin (Simplified, Hong Kong)":"cmn-Hans-HK",
|
||||
"Mandarin (Traditional, Taiwan)":"cmn-Hant-TW",
|
||||
"Cantonese (Traditional Hong Kong)":"yue-Hant-HK",
|
||||
},
|
||||
"Croatian":{
|
||||
"Croatia":"hr-HR",
|
||||
},
|
||||
"Czech":{
|
||||
"Czech Republic":"cs-CZ",
|
||||
},
|
||||
"Danish":{
|
||||
"Denmark":"da-DK",
|
||||
},
|
||||
"Dutch":{
|
||||
"Netherlands":"nl-NL",
|
||||
},
|
||||
"English": {
|
||||
"United States":"en-US",
|
||||
"United Kingdom":"en-GB",
|
||||
"Australia":"en-AU",
|
||||
"Canada":"en-CA",
|
||||
"India":"en-IN",
|
||||
"Ireland":"en-IE",
|
||||
"New Zealand":"en-NZ",
|
||||
"Philippines":"en-PH",
|
||||
"South Africa":"en-ZA",
|
||||
},
|
||||
"Filipino":{
|
||||
"Philippines":"fil-PH",
|
||||
},
|
||||
"Finnish":{
|
||||
"Finland":"fi-FI",
|
||||
},
|
||||
"French":{
|
||||
"France":"fr-FR",
|
||||
},
|
||||
"Galician":{
|
||||
"Spain":"gl-ES",
|
||||
},
|
||||
"German":{
|
||||
"Germany":"de-DE",
|
||||
},
|
||||
"Greek":{
|
||||
"Greece":"el-GR",
|
||||
},
|
||||
"Hebrew":{
|
||||
"Israel":"he-IL",
|
||||
},
|
||||
"Hindi": {
|
||||
"India":"hi-IN",
|
||||
},
|
||||
"Hungarian":{
|
||||
"Hungary":"hu-HU",
|
||||
},
|
||||
"Indonesian":{
|
||||
"Indonesia":"id-ID",
|
||||
},
|
||||
"Icelandic":{
|
||||
"Iceland":"is-IS",
|
||||
},
|
||||
"Italian":{
|
||||
"Italy":"it-IT",
|
||||
"Switzerland":"it-CH",
|
||||
},
|
||||
"Japanese":{
|
||||
"Japan":"ja-JP",
|
||||
},
|
||||
"Korean":{
|
||||
"South Korea":"ko-KR",
|
||||
},
|
||||
"Lithuanian":{
|
||||
"Lithuania":"lt-LT",
|
||||
},
|
||||
"Malay":{
|
||||
"Malaysia":"ms-MY",
|
||||
},
|
||||
"Norwegian":{
|
||||
"Norway":"nb-NO",
|
||||
},
|
||||
"Persian":{
|
||||
"Iran":"fa-IR",
|
||||
},
|
||||
"Polish":{
|
||||
"Poland":"pl-PL",
|
||||
},
|
||||
"Portuguese":{
|
||||
"Brazil":"pt-BR",
|
||||
"Portugal":"pt-PT",
|
||||
},
|
||||
"Romanian":{
|
||||
"Romania":"ro-RO",
|
||||
},
|
||||
"Russian":{
|
||||
"Russia":"ru-RU",
|
||||
},
|
||||
"Serbian":{
|
||||
"Serbia":"sr-RS",
|
||||
},
|
||||
"Slovak":{
|
||||
"Slovakia":"sk-SK",
|
||||
},
|
||||
"Slovenian":{
|
||||
"Slovenia":"sl-SI",
|
||||
},
|
||||
"Spanish":{
|
||||
"Argentina":"es-AR",
|
||||
"Bolivia":"es-BO",
|
||||
"Chile":"es-CL",
|
||||
"Colombia":"es-CO",
|
||||
"Costa Rica":"es-CR",
|
||||
"Dominican Republic":"es-DO",
|
||||
"Ecuador":"es-EC",
|
||||
"El Salvador":"es-SV",
|
||||
"Guatemala":"es-GT",
|
||||
"Honduras":"es-HN",
|
||||
"Mexico":"es-MX",
|
||||
"Nicaragua":"es-NI",
|
||||
"Panama":"es-PA",
|
||||
"Paraguay":"es-PY",
|
||||
"Peru":"es-PE",
|
||||
"Puerto Rico":"es-PR",
|
||||
"Spain":"es-ES",
|
||||
"Uruguay":"es-UY",
|
||||
"United States":"es-US",
|
||||
"Venezuela":"es-VE",
|
||||
},
|
||||
"Swedish":{
|
||||
"Sweden":"sv-SE",
|
||||
},
|
||||
"Thai":{
|
||||
"Thailand":"th-TH",
|
||||
},
|
||||
"Turkish":{
|
||||
"Turkey":"tr-TR",
|
||||
},
|
||||
"Ukrainian":{
|
||||
"Ukraine":"uk-UA",
|
||||
},
|
||||
"Vietnamese":{
|
||||
"Vietnam":"vi-VN",
|
||||
},
|
||||
"Zulu":{
|
||||
"South Africa":"zu-ZA"
|
||||
},
|
||||
}
|
||||
@@ -15,11 +15,11 @@ class BaseRecorder:
|
||||
|
||||
self.source = source
|
||||
|
||||
def adjust_for_noise(self):
|
||||
def adjustForNoise(self):
|
||||
with self.source:
|
||||
self.recorder.adjust_for_ambient_noise(self.source)
|
||||
|
||||
def record_into_queue(self, audio_queue):
|
||||
def recordIntoQueue(self, audio_queue):
|
||||
def record_callback(_, audio):
|
||||
audio_queue.put((audio.get_raw_data(), datetime.now()))
|
||||
|
||||
@@ -32,7 +32,7 @@ class SelectedMicRecorder(BaseRecorder):
|
||||
sample_rate=int(device["defaultSampleRate"]),
|
||||
)
|
||||
super().__init__(source=source, energy_threshold=energy_threshold, dynamic_energy_threshold=dynamic_energy_threshold, record_timeout=record_timeout)
|
||||
self.adjust_for_noise()
|
||||
# self.adjustForNoise()
|
||||
|
||||
class SelectedSpeakerRecorder(BaseRecorder):
|
||||
def __init__(self, device, energy_threshold, dynamic_energy_threshold, record_timeout):
|
||||
@@ -44,7 +44,7 @@ class SelectedSpeakerRecorder(BaseRecorder):
|
||||
channels=device["maxInputChannels"]
|
||||
)
|
||||
super().__init__(source=source, energy_threshold=energy_threshold, dynamic_energy_threshold=dynamic_energy_threshold, record_timeout=record_timeout)
|
||||
self.adjust_for_noise()
|
||||
# self.adjustForNoise()
|
||||
|
||||
class BaseEnergyRecorder:
|
||||
def __init__(self, source):
|
||||
@@ -59,15 +59,15 @@ class BaseEnergyRecorder:
|
||||
|
||||
self.source = source
|
||||
|
||||
def adjust_for_noise(self):
|
||||
def adjustForNoise(self):
|
||||
with self.source:
|
||||
self.recorder.adjust_for_ambient_noise(self.source)
|
||||
|
||||
def record_into_queue(self, energy_queue):
|
||||
def record_callback(_, energy):
|
||||
def recordIntoQueue(self, energy_queue):
|
||||
def recordCallback(_, energy):
|
||||
energy_queue.put(energy)
|
||||
|
||||
self.stop = self.recorder.listen_energy_in_background(self.source, record_callback)
|
||||
self.stop = self.recorder.listen_energy_in_background(self.source, recordCallback)
|
||||
|
||||
class SelectedMicEnergyRecorder(BaseEnergyRecorder):
|
||||
def __init__(self, device):
|
||||
@@ -76,7 +76,7 @@ class SelectedMicEnergyRecorder(BaseEnergyRecorder):
|
||||
sample_rate=int(device["defaultSampleRate"]),
|
||||
)
|
||||
super().__init__(source=source)
|
||||
self.adjust_for_noise()
|
||||
# self.adjustForNoise()
|
||||
|
||||
class SelectedSpeakeEnergyRecorder(BaseEnergyRecorder):
|
||||
def __init__(self, device):
|
||||
@@ -84,8 +84,7 @@ class SelectedSpeakeEnergyRecorder(BaseEnergyRecorder):
|
||||
source = Microphone(speaker=True,
|
||||
device_index= device["index"],
|
||||
sample_rate=int(device["defaultSampleRate"]),
|
||||
chunk_size=get_sample_size(paInt16),
|
||||
channels=device["maxInputChannels"]
|
||||
)
|
||||
super().__init__(source=source)
|
||||
self.adjust_for_noise()
|
||||
# self.adjustForNoise()
|
||||
@@ -4,14 +4,14 @@ import wave
|
||||
from speech_recognition import Recognizer, AudioData, AudioFile
|
||||
from datetime import timedelta
|
||||
from pyaudiowpatch import get_sample_size, paInt16
|
||||
from .transcription_languages import transcription_lang
|
||||
|
||||
PHRASE_TIMEOUT = 3
|
||||
MAX_PHRASES = 10
|
||||
|
||||
class AudioTranscriber:
|
||||
def __init__(self, speaker, source, language, phrase_timeout, max_phrases):
|
||||
def __init__(self, speaker, source, phrase_timeout, max_phrases):
|
||||
self.speaker = speaker
|
||||
self.language = language
|
||||
self.phrase_timeout = phrase_timeout
|
||||
self.max_phrases = max_phrases
|
||||
self.transcript_data = []
|
||||
@@ -24,20 +24,20 @@ class AudioTranscriber:
|
||||
"last_sample": bytes(),
|
||||
"last_spoken": None,
|
||||
"new_phrase": True,
|
||||
"process_data_func": self.process_speaker_data if speaker else self.process_speaker_data
|
||||
"process_data_func": self.processSpeakerData if speaker else self.processSpeakerData
|
||||
}
|
||||
|
||||
def transcribe_audio_queue(self, audio_queue):
|
||||
def transcribeAudioQueue(self, audio_queue, language, country):
|
||||
# while True:
|
||||
audio, time_spoken = audio_queue.get()
|
||||
self.update_last_sample_and_phrase_status(audio, time_spoken)
|
||||
self.updateLastSampleAndPhraseStatus(audio, time_spoken)
|
||||
|
||||
text = ''
|
||||
try:
|
||||
# fd, path = tempfile.mkstemp(suffix=".wav")
|
||||
# os.close(fd)
|
||||
audio_data = self.audio_sources["process_data_func"]()
|
||||
text = self.audio_recognizer.recognize_google(audio_data, language=self.language)
|
||||
text = self.audio_recognizer.recognize_google(audio_data, language=transcription_lang[language][country])
|
||||
except Exception as e:
|
||||
pass
|
||||
finally:
|
||||
@@ -45,9 +45,9 @@ class AudioTranscriber:
|
||||
# os.unlink(path)
|
||||
|
||||
if text != '':
|
||||
self.update_transcript(text)
|
||||
self.updateTranscript(text)
|
||||
|
||||
def update_last_sample_and_phrase_status(self, data, time_spoken):
|
||||
def updateLastSampleAndPhraseStatus(self, data, time_spoken):
|
||||
source_info = self.audio_sources
|
||||
if source_info["last_spoken"] and time_spoken - source_info["last_spoken"] > timedelta(seconds=self.phrase_timeout):
|
||||
source_info["last_sample"] = bytes()
|
||||
@@ -58,11 +58,11 @@ class AudioTranscriber:
|
||||
source_info["last_sample"] += data
|
||||
source_info["last_spoken"] = time_spoken
|
||||
|
||||
def process_mic_data(self):
|
||||
def processMicData(self):
|
||||
audio_data = AudioData(self.audio_sources["last_sample"], self.audio_sources["sample_rate"], self.audio_sources["sample_width"])
|
||||
return audio_data
|
||||
|
||||
def process_speaker_data(self):
|
||||
def processSpeakerData(self):
|
||||
temp_file = BytesIO()
|
||||
with wave.open(temp_file, 'wb') as wf:
|
||||
wf.setnchannels(self.audio_sources["channels"])
|
||||
@@ -74,7 +74,7 @@ class AudioTranscriber:
|
||||
audio = self.audio_recognizer.record(source)
|
||||
return audio
|
||||
|
||||
def update_transcript(self, text):
|
||||
def updateTranscript(self, text):
|
||||
source_info = self.audio_sources
|
||||
transcript = self.transcript_data
|
||||
|
||||
@@ -85,14 +85,14 @@ class AudioTranscriber:
|
||||
else:
|
||||
transcript[0] = text
|
||||
|
||||
def get_transcript(self):
|
||||
def getTranscript(self):
|
||||
if len(self.transcript_data) > 0:
|
||||
text = self.transcript_data.pop(-1)
|
||||
else:
|
||||
text = ""
|
||||
return text
|
||||
|
||||
def clear_transcript_data(self):
|
||||
def clearTranscriptData(self):
|
||||
self.transcript_data.clear()
|
||||
self.audio_sources["last_sample"] = bytes()
|
||||
self.audio_sources["new_phrase"] = True
|
||||
@@ -1,6 +1,6 @@
|
||||
from pyaudiowpatch import PyAudio, paWASAPI
|
||||
|
||||
def get_input_device_list():
|
||||
def getInputDevices():
|
||||
devices = {}
|
||||
with PyAudio() as p:
|
||||
for host_index in range(0, p.get_host_api_count()):
|
||||
@@ -12,18 +12,11 @@ def get_input_device_list():
|
||||
devices[host["name"]].append(device)
|
||||
else:
|
||||
devices[host["name"]] = [device]
|
||||
if len(devices) == 0:
|
||||
devices = {"NoHost": [{"name": "NoDevice"}]}
|
||||
return devices
|
||||
|
||||
def get_output_device_list():
|
||||
devices =[]
|
||||
with PyAudio() as p:
|
||||
wasapi_info = p.get_host_api_info_by_type(paWASAPI)
|
||||
for device in p.get_loopback_device_info_generator():
|
||||
if device["hostApi"] == wasapi_info["index"] and device["isLoopbackDevice"] is True:
|
||||
devices.append(device)
|
||||
return devices
|
||||
|
||||
def get_default_input_device():
|
||||
def getDefaultInputDevice():
|
||||
with PyAudio() as p:
|
||||
api_info = p.get_default_host_api_info()
|
||||
defaultInputDevice = api_info["defaultInputDevice"]
|
||||
@@ -33,9 +26,10 @@ def get_default_input_device():
|
||||
for device_index in range(0, p. get_host_api_info_by_index(host_index)['deviceCount']):
|
||||
device = p.get_device_info_by_host_api_device_index(host_index, device_index)
|
||||
if device["index"] == defaultInputDevice:
|
||||
return {"host":host, "device": device}
|
||||
return {"host": host, "device": device}
|
||||
return {"host": {"name": "NoHost"}, "device": {"name": "NoDevice"}}
|
||||
|
||||
def get_default_output_device():
|
||||
def getDefaultOutputDevice():
|
||||
with PyAudio() as p:
|
||||
wasapi_info = p.get_host_api_info_by_type(paWASAPI)
|
||||
defaultOutputDevice = wasapi_info["defaultOutputDevice"]
|
||||
@@ -49,4 +43,5 @@ def get_default_output_device():
|
||||
for loopback in p.get_loopback_device_info_generator():
|
||||
if default_speakers["name"] in loopback["name"]:
|
||||
default_device = loopback
|
||||
return default_device
|
||||
return default_device
|
||||
return {"name":"NoDevice"}
|
||||
243
models/translation/translation_languages.py
Normal file
@@ -0,0 +1,243 @@
|
||||
translatorEngine = ["DeepL", "DeepL_API", "Google", "Bing"]
|
||||
translation_lang = {}
|
||||
dict_deepl_languages = {
|
||||
"Japanese":"JA",
|
||||
"English":"EN",
|
||||
"Korean":"KO",
|
||||
"Bulgarian":"BG",
|
||||
"Chinese":"ZH",
|
||||
"Czech":"CS",
|
||||
"Danish":"DA",
|
||||
"Dutch":"NL",
|
||||
"Estonian":"ET",
|
||||
"Finnish":"FI",
|
||||
"French":"FR",
|
||||
"German":"DE",
|
||||
"Greek":"EL",
|
||||
"Hungarian":"HU",
|
||||
"Italian":"IT",
|
||||
"Latvian":"LV",
|
||||
"Lithuanian":"LT",
|
||||
"Polish":"PL",
|
||||
"Portuguese":"PT",
|
||||
"Romanian":"RO",
|
||||
"Russian":"RU",
|
||||
"Slovak":"SK",
|
||||
"Slovenian":"SL",
|
||||
"Spanish":"ES",
|
||||
"Swedish":"SV",
|
||||
"Indonesian":"ID",
|
||||
"Ukrainian":"UK",
|
||||
"Turkish":"TR",
|
||||
"Norwegian":"NB",
|
||||
}
|
||||
translation_lang["DeepL"] = {
|
||||
"source":dict_deepl_languages,
|
||||
"target":dict_deepl_languages,
|
||||
}
|
||||
|
||||
dict_deepl_api_source_languages = {
|
||||
"Japanese":"ja",
|
||||
"English":"en",
|
||||
"Bulgarian":"bg",
|
||||
"Czech":"cs",
|
||||
"Danish":"da",
|
||||
"German":"de",
|
||||
"Greek":"el",
|
||||
"Spanish":"es",
|
||||
"Estonian":"et",
|
||||
"Finnish":"fi",
|
||||
"French":"fr",
|
||||
"Hungarian":"hu",
|
||||
"Indonesian":"id",
|
||||
"Italian":"it",
|
||||
"Korean":"ko",
|
||||
"Lithuanian":"lt",
|
||||
"Latvian":"lv",
|
||||
"Norwegian":"nb",
|
||||
"Dutch":"nl",
|
||||
"Polish":"pl",
|
||||
"Portuguese":"pt",
|
||||
"Romanian":"ro",
|
||||
"Russian":"ru",
|
||||
"Slovak":"sk",
|
||||
"Slovenian":"sl",
|
||||
"Swedish":"sv",
|
||||
"Turkish":"tr",
|
||||
"Ukrainian":"uk",
|
||||
"Chinese":"zh"
|
||||
}
|
||||
dict_deepl_api_target_languages = {
|
||||
"Japanese":"ja",
|
||||
"English American":"en-US",
|
||||
"English British":"en-GB",
|
||||
"Bulgarian":"bg",
|
||||
"Czech":"cs",
|
||||
"Danish":"da",
|
||||
"German":"de",
|
||||
"Greek":"el",
|
||||
"English":"en",
|
||||
"Spanish":"es",
|
||||
"Estonian":"et",
|
||||
"Finnish":"fi",
|
||||
"French":"fr",
|
||||
"Hungarian":"hu",
|
||||
"Indonesian":"id",
|
||||
"Italian":"it",
|
||||
"Korean":"ko",
|
||||
"Lithuanian":"lt",
|
||||
"Latvian":"lv",
|
||||
"Norwegian":"nb",
|
||||
"Dutch":"nl",
|
||||
"Polish":"pl",
|
||||
"Portuguese Brazilian":"pt-BR",
|
||||
"Portuguese European":"pt-PT",
|
||||
"Romanian":"ro",
|
||||
"Russian":"ru",
|
||||
"Slovak":"sk",
|
||||
"Slovenian":"sl",
|
||||
"Swedish":"sv",
|
||||
"Turkish":"tr",
|
||||
"Ukrainian":"uk",
|
||||
"Chinese":"zh"
|
||||
}
|
||||
translation_lang["DeepL_API"] = {
|
||||
"source": dict_deepl_api_source_languages,
|
||||
"target": dict_deepl_api_target_languages,
|
||||
}
|
||||
|
||||
dict_google_languages = {
|
||||
"Japanese":"ja",
|
||||
"English":"en",
|
||||
"Chinese":"zh",
|
||||
"Arabic":"ar",
|
||||
"Russian":"ru",
|
||||
"French":"fr",
|
||||
"German":"de",
|
||||
"Spanish":"es",
|
||||
"Portuguese":"pt",
|
||||
"Italian":"it",
|
||||
"Korean":"ko",
|
||||
"Greek":"el",
|
||||
"Dutch":"nl",
|
||||
"Hindi":"hi",
|
||||
"Turkish":"tr",
|
||||
"Malay":"ms",
|
||||
"Thai":"th",
|
||||
"Vietnamese":"vi",
|
||||
"Indonesian":"id",
|
||||
"Hebrew":"he",
|
||||
"Polish":"pl",
|
||||
"Mongolian":"mn",
|
||||
"Czech":"cs",
|
||||
"Hungarian":"hu",
|
||||
"Estonian":"et",
|
||||
"Bulgarian":"bg",
|
||||
"Danish":"da",
|
||||
"Finnish":"fi",
|
||||
"Romanian":"ro",
|
||||
"Swedish":"sv",
|
||||
"Slovenian":"sl",
|
||||
"Persian/Farsi":"fa",
|
||||
"Bosnian":"bs",
|
||||
"Serbian":"sr",
|
||||
"Filipino":"tl",
|
||||
"Haitiancreole":"ht",
|
||||
"Catalan":"ca",
|
||||
"Croatian":"hr",
|
||||
"Latvian":"lv",
|
||||
"Lithuanian":"lt",
|
||||
"Urdu":"ur",
|
||||
"Ukrainian":"uk",
|
||||
"Welsh":"cy",
|
||||
"Swahili":"sw",
|
||||
"Samoan":"sm",
|
||||
"Slovak":"sk",
|
||||
"Afrikaans":"af",
|
||||
"Norwegian":"no",
|
||||
"Bengali":"bn",
|
||||
"Malagasy":"mg",
|
||||
"Maltese":"mt",
|
||||
"Gujarati":"gu",
|
||||
"Tamil":"ta",
|
||||
"Telugu":"te",
|
||||
"Punjabi":"pa",
|
||||
"Amharic":"am",
|
||||
"Azerbaijani":"az",
|
||||
"Belarusian":"be",
|
||||
"Cebuano":"ceb",
|
||||
"Esperanto":"eo",
|
||||
"Basque":"eu",
|
||||
"Irish":"ga"
|
||||
}
|
||||
translation_lang["Google"] = {
|
||||
"source":dict_google_languages,
|
||||
"target":dict_google_languages,
|
||||
}
|
||||
|
||||
dict_bing_languages = {
|
||||
"Japanese":"ja",
|
||||
"English":"en",
|
||||
"Chinese":"zh",
|
||||
"Arabic":"ar",
|
||||
"Russian":"ru",
|
||||
"French":"fr",
|
||||
"German":"de",
|
||||
"Spanish":"es",
|
||||
"Portuguese":"pt",
|
||||
"Italian":"it",
|
||||
"Korean":"ko",
|
||||
"Greek":"el",
|
||||
"Dutch":"nl",
|
||||
"Hindi":"hi",
|
||||
"Turkish":"tr",
|
||||
"Malay":"ms",
|
||||
"Thai":"th",
|
||||
"Vietnamese":"vi",
|
||||
"Indonesian":"id",
|
||||
"Hebrew":"he",
|
||||
"Polish":"pl",
|
||||
"Czech":"cs",
|
||||
"Hungarian":"hu",
|
||||
"Estonian":"et",
|
||||
"Bulgarian":"bg",
|
||||
"Danish":"da",
|
||||
"Finnish":"fi",
|
||||
"Romanian":"ro",
|
||||
"Swedish":"sv",
|
||||
"Slovenian":"sl",
|
||||
"Persian/Farsi":"fa",
|
||||
"Bosnian":"bs",
|
||||
"Serbian":"sr",
|
||||
"Fijian":"fj",
|
||||
"Filipino":"tl",
|
||||
"Haitiancreole":"ht",
|
||||
"Catalan":"ca",
|
||||
"Croatian":"hr",
|
||||
"Latvian":"lv",
|
||||
"Lithuanian":"lt",
|
||||
"Urdu":"ur",
|
||||
"Ukrainian":"uk",
|
||||
"Welsh":"cy",
|
||||
"Tahiti":"ty",
|
||||
"Tongan":"to",
|
||||
"Swahili":"sw",
|
||||
"Samoan":"sm",
|
||||
"Slovak":"sk",
|
||||
"Afrikaans":"af",
|
||||
"Norwegian":"no",
|
||||
"Bengali":"bn",
|
||||
"Malagasy":"mg",
|
||||
"Maltese":"mt",
|
||||
"Queretaro otomi":"otq",
|
||||
"Klingon/tlhingan Hol":"tlh",
|
||||
"Gujarati":"gu",
|
||||
"Tamil":"ta",
|
||||
"Telugu":"te",
|
||||
"Punjabi":"pa",
|
||||
"Irish":"ga"
|
||||
}
|
||||
translation_lang["Bing"] = {
|
||||
"source":dict_bing_languages,
|
||||
"target":dict_bing_languages,
|
||||
}
|
||||
60
models/translation/translation_translator.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from deepl import Translator as deepl_Translator
|
||||
from deepl_translate import translate as deepl_web_Translator
|
||||
from translators import translate_text as other_web_Translator
|
||||
from .translation_languages import translatorEngine, translation_lang
|
||||
|
||||
# Translator
|
||||
class Translator():
|
||||
def __init__(self):
|
||||
pass
|
||||
self.translator_status = {}
|
||||
|
||||
def authentication(self, translator_name, authkey=None):
|
||||
result = True
|
||||
match translator_name:
|
||||
case "DeepL_API":
|
||||
try:
|
||||
self.deepl_client = deepl_Translator(authkey)
|
||||
self.deepl_client.translate_text(" ", target_lang="EN-US")
|
||||
except:
|
||||
result = False
|
||||
return result
|
||||
|
||||
def translate(self, translator_name, source_language, target_language, message):
|
||||
try:
|
||||
result = ""
|
||||
source_language=translation_lang[translator_name]["source"][source_language]
|
||||
target_language=translation_lang[translator_name]["target"][target_language]
|
||||
match translator_name:
|
||||
case "DeepL":
|
||||
result = deepl_web_Translator(
|
||||
source_language=source_language,
|
||||
target_language=target_language,
|
||||
text=message
|
||||
)
|
||||
case "DeepL_API":
|
||||
result = self.deepl_client.translate_text(
|
||||
message,
|
||||
source_lang=source_language,
|
||||
target_lang=target_language,
|
||||
).text
|
||||
case "Google":
|
||||
result = other_web_Translator(
|
||||
query_text=message,
|
||||
translator="google",
|
||||
from_language=source_language,
|
||||
to_language=target_language,
|
||||
)
|
||||
case "Bing":
|
||||
result = other_web_Translator(
|
||||
query_text=message,
|
||||
translator="bing",
|
||||
from_language=source_language,
|
||||
to_language=target_language,
|
||||
)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
with open('error.log', 'a') as f:
|
||||
traceback.print_exc(file=f)
|
||||
result = False
|
||||
return result
|
||||
@@ -18,8 +18,9 @@
|
||||
import socket
|
||||
import json
|
||||
import base64
|
||||
from os import path as os_path
|
||||
|
||||
def notification_xsoverlay(
|
||||
def XSOverlay(
|
||||
endpoint:tuple=("127.0.0.1", 42069), messageType:int=1, index:int=0, timeout:float=2,
|
||||
height:float=120.0, opacity:float=1.0, volume:float=0.0, audioPath:str="",
|
||||
title:str="", content:str="", useBase64Icon:bool=False, icon:str="default", sourceApp:str=""
|
||||
@@ -58,15 +59,15 @@ def notification_xsoverlay(
|
||||
sock.close()
|
||||
return response
|
||||
|
||||
def notification_xsoverlay_for_vrct(content:str="") -> int:
|
||||
response = notification_xsoverlay(
|
||||
def xsoverlayForVRCT(content:str="") -> int:
|
||||
response = XSOverlay(
|
||||
title="VRCT",
|
||||
content=content,
|
||||
useBase64Icon=True,
|
||||
icon="./img/xsoverlay.png",
|
||||
icon=os_path.join(os_path.dirname(__file__), "img", "xsoverlay2.png"),
|
||||
sourceApp="VRCT"
|
||||
)
|
||||
return response
|
||||
|
||||
if __name__ == "__main__":
|
||||
notification_xsoverlay_for_vrct(content="notification test")
|
||||
xsoverlayForVRCT(content="notification test")
|
||||
@@ -1,7 +1,8 @@
|
||||
pillow
|
||||
PyAudioWPatch
|
||||
python-osc
|
||||
customtkinter
|
||||
deepl
|
||||
flashtext
|
||||
pyyaml
|
||||
pillow == 10.0.0
|
||||
PyAudioWPatch == 0.2.12.6
|
||||
python-osc == 1.8.3
|
||||
customtkinter == 5.2.0
|
||||
deepl == 1.15.0
|
||||
flashtext == 2.7
|
||||
pyyaml == 6.0.1
|
||||
python-i18n == 0.3.9
|
||||
@@ -1,68 +0,0 @@
|
||||
from deepl import Translator as deepl_Translator
|
||||
from deepl_translate import translate as deepl_web_Translator
|
||||
from translators import translate_text as other_web_Translator
|
||||
from languages import translators, translation_lang
|
||||
|
||||
# Translator
|
||||
class Translator():
|
||||
def __init__(self):
|
||||
self.translator_status = {}
|
||||
for translator in translators:
|
||||
self.translator_status[translator] = False
|
||||
self.deepl_client = None
|
||||
|
||||
def authentication(self, translator_name, authkey=None):
|
||||
result = False
|
||||
try:
|
||||
if translator_name == "DeepL(web)":
|
||||
self.translator_status["DeepL(web)"] = True
|
||||
result = True
|
||||
elif translator_name == "DeepL(auth)":
|
||||
self.deepl_client = deepl_Translator(authkey)
|
||||
self.deepl_client.translate_text(" ", target_lang="EN-US")
|
||||
self.translator_status["DeepL(auth)"] = True
|
||||
result = True
|
||||
elif translator_name == "Google(web)":
|
||||
self.translator_status["Google(web)"] = True
|
||||
result = True
|
||||
elif translator_name == "Bing(web)":
|
||||
self.translator_status["Bing(web)"] = True
|
||||
result = True
|
||||
except:
|
||||
pass
|
||||
return result
|
||||
|
||||
def translate(self, translator_name, source_language, target_language, message):
|
||||
result = ""
|
||||
try:
|
||||
source_language=translation_lang[translator_name]["source"][source_language]
|
||||
target_language=translation_lang[translator_name]["target"][target_language]
|
||||
if translator_name == "DeepL(web)":
|
||||
result = deepl_web_Translator(
|
||||
source_language=source_language,
|
||||
target_language=target_language,
|
||||
text=message
|
||||
)
|
||||
elif translator_name == "DeepL(auth)":
|
||||
result = self.deepl_client.translate_text(
|
||||
message,
|
||||
source_lang=source_language,
|
||||
target_lang=target_language,
|
||||
).text
|
||||
elif translator_name == "Google(web)":
|
||||
result = other_web_Translator(
|
||||
query_text=message,
|
||||
translator="google",
|
||||
from_language=source_language,
|
||||
to_language=target_language,
|
||||
)
|
||||
elif translator_name == "Bing(web)":
|
||||
result = other_web_Translator(
|
||||
query_text=message,
|
||||
translator="bing",
|
||||
from_language=source_language,
|
||||
to_language=target_language,
|
||||
)
|
||||
except:
|
||||
pass
|
||||
return result
|
||||
135
utils.py
@@ -1,130 +1,29 @@
|
||||
from json import load, dump
|
||||
from os import path as os_path
|
||||
import yaml
|
||||
from datetime import datetime
|
||||
from threading import Thread, Event
|
||||
from PIL.Image import open as Image_open
|
||||
|
||||
def save_json(path, key, value):
|
||||
with open(path, "r") as fp:
|
||||
json_data = load(fp)
|
||||
json_data[key] = value
|
||||
with open(path, "w") as fp:
|
||||
dump(json_data, fp, indent=4)
|
||||
def getImageFile(file_name):
|
||||
img = Image_open(os_path.join(os_path.dirname(__file__), "img", file_name))
|
||||
return img
|
||||
|
||||
def print_textbox(textbox, message, tags=None):
|
||||
now = datetime.now()
|
||||
now = now.strftime('%H:%M:%S')
|
||||
|
||||
textbox.tag_config("ERROR", foreground="#FF0000")
|
||||
textbox.tag_config("INFO", foreground="#1BFF00")
|
||||
textbox.tag_config("SEND", foreground="#0378e2")
|
||||
textbox.tag_config("RECEIVE", foreground="#ffa500")
|
||||
|
||||
textbox.configure(state='normal')
|
||||
textbox.insert("end", f"[{now}][")
|
||||
textbox.insert("end", f"{tags}", tags)
|
||||
textbox.insert("end", f"]{message}\n")
|
||||
textbox.configure(state='disabled')
|
||||
textbox.see("end")
|
||||
|
||||
class thread_fnc(Thread):
|
||||
def __init__(self, fnc, daemon=True, *args, **kwargs):
|
||||
super(thread_fnc, self).__init__(daemon=daemon, *args, **kwargs)
|
||||
self.fnc = fnc
|
||||
self._stop = Event()
|
||||
def stop(self):
|
||||
self._stop.set()
|
||||
def stopped(self):
|
||||
return self._stop.isSet()
|
||||
def run(self):
|
||||
while True:
|
||||
if self.stopped():
|
||||
return
|
||||
self.fnc(*self._args, **self._kwargs)
|
||||
|
||||
def get_localized_text(language):
|
||||
file_path = os_path.join(os_path.dirname(__file__), "locales.yml")
|
||||
|
||||
with open(file_path, encoding="utf-8") as file:
|
||||
languages_yaml_data = yaml.safe_load(file)
|
||||
default_language = "en"
|
||||
if language in languages_yaml_data:
|
||||
localized_text = languages_yaml_data[language]
|
||||
if default_language in languages_yaml_data:
|
||||
default_text = languages_yaml_data[default_language]
|
||||
merged_text = {**default_text, **localized_text}
|
||||
return merged_text
|
||||
else:
|
||||
return localized_text
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_key_by_value(dictionary, value):
|
||||
for key, val in dictionary.items():
|
||||
if val == value:
|
||||
return key
|
||||
return None
|
||||
|
||||
def widget_config_window_label_setter(self, language_yaml_data):
|
||||
widget_names = [
|
||||
# tab UI
|
||||
"label_transparency",
|
||||
"label_appearance_theme",
|
||||
"label_ui_scaling",
|
||||
"label_font_family",
|
||||
"label_ui_language",
|
||||
def callFunctionIfCallable(function, *args):
|
||||
if callable(function) is True: function(*args)
|
||||
|
||||
# tab Translation
|
||||
"label_translation_translator",
|
||||
"label_translation_input_language",
|
||||
"label_translation_output_language",
|
||||
def isEven(number):
|
||||
return number % 2 == 0
|
||||
|
||||
# tab Transcription
|
||||
"label_input_mic_host",
|
||||
"label_input_mic_device",
|
||||
"label_input_mic_voice_language",
|
||||
"label_input_mic_energy_threshold",
|
||||
"checkbox_input_mic_threshold_check",
|
||||
"label_input_mic_dynamic_energy_threshold",
|
||||
"label_input_mic_record_timeout",
|
||||
"label_input_mic_phrase_timeout",
|
||||
"label_input_mic_max_phrases",
|
||||
"label_input_mic_word_filter",
|
||||
def makeEven(number, minus:bool=False):
|
||||
if minus is True:
|
||||
return number if isEven(number) else number - 1
|
||||
return number if isEven(number) else number + 1
|
||||
|
||||
"label_input_speaker_device",
|
||||
"label_input_speaker_voice_language",
|
||||
"label_input_speaker_energy_threshold",
|
||||
"checkbox_input_speaker_threshold_check",
|
||||
"label_input_speaker_dynamic_energy_threshold",
|
||||
"label_input_speaker_record_timeout",
|
||||
"label_input_speaker_phrase_timeout",
|
||||
"label_input_speaker_max_phrases",
|
||||
|
||||
# tab Parameter
|
||||
"label_ip_address",
|
||||
"label_port",
|
||||
"label_authkey",
|
||||
"label_message_format",
|
||||
|
||||
# tab Others
|
||||
"label_checkbox_auto_clear_chatbox",
|
||||
"label_checkbox_notice_xsoverlay",
|
||||
]
|
||||
for name in widget_names:
|
||||
widget = getattr(self, name)
|
||||
text_value = language_yaml_data.get(name)
|
||||
if widget is not None and text_value is not None:
|
||||
widget.configure(text=text_value + ":")
|
||||
|
||||
def widget_main_window_label_setter(self, language_yaml_data):
|
||||
widget_names = [
|
||||
"checkbox_translation",
|
||||
"checkbox_transcription_send",
|
||||
"checkbox_transcription_receive",
|
||||
"checkbox_foreground",
|
||||
]
|
||||
for name in widget_names:
|
||||
widget = getattr(self, name)
|
||||
text_value = language_yaml_data.get(name)
|
||||
if widget is not None and text_value is not None:
|
||||
widget.configure(text=text_value)
|
||||
def generatePercentageStringsList(start=40, end=200, step=10):
|
||||
strings = []
|
||||
for percent in range(start, end + 1, step):
|
||||
strings.append(f"{percent}%")
|
||||
return strings
|
||||
240
vrct_gui/_CreateConfirmationModal.py
Normal file
@@ -0,0 +1,240 @@
|
||||
from customtkinter import CTkToplevel, CTkFrame, CTkLabel, CTkFont
|
||||
|
||||
from .ui_utils import fadeInAnimation, setGeometryToCenterOfTheWidget, bindButtonFunctionAndColor
|
||||
|
||||
from utils import callFunctionIfCallable
|
||||
|
||||
class _CreateConfirmationModal(CTkToplevel):
|
||||
def __init__(self, attach_window, settings, view_variable, modal_type=None):
|
||||
super().__init__()
|
||||
self.withdraw()
|
||||
|
||||
self.attach_window = attach_window
|
||||
self.settings = settings
|
||||
self._view_variable = view_variable
|
||||
|
||||
|
||||
self.title("")
|
||||
self.overrideredirect(True)
|
||||
self.wm_attributes("-toolwindow", True)
|
||||
|
||||
self.BIND_FOCUS_OUT_FUNC_ID=None
|
||||
|
||||
|
||||
|
||||
self.configure(fg_color=self.settings.ctm.FAKE_BORDER_COLOR)
|
||||
self.protocol("WM_DELETE_WINDOW", lambda: callFunctionIfCallable(self._view_variable.CALLBACK_HIDE_CONFIRMATION_MODAL))
|
||||
|
||||
|
||||
|
||||
self.grid_rowconfigure(0,weight=1)
|
||||
self.grid_columnconfigure(0,weight=1)
|
||||
self.modal_container = CTkFrame(self, corner_radius=0, fg_color=self.settings.ctm.BG_COLOR)
|
||||
self.modal_container.grid(row=0, column=0, padx=self.settings.uism.FAKE_BORDER_SIZE, pady=self.settings.uism.FAKE_BORDER_SIZE)
|
||||
|
||||
|
||||
self.modal_contents_wrapper = CTkFrame(self.modal_container, corner_radius=0, fg_color=self.settings.ctm.BG_COLOR)
|
||||
self.modal_contents_wrapper.grid(row=0, column=0, padx=self.settings.uism.CONTENTS_WRAPPER, pady=self.settings.uism.CONTENTS_WRAPPER)
|
||||
|
||||
|
||||
|
||||
self.modal_contents_wrapper.grid_rowconfigure(1, minsize=self.settings.uism.MARGIN_BETWEEN_MESSAGE_AND_BUTTONS)
|
||||
|
||||
self.modal_message_label_wrapper = CTkFrame(self.modal_contents_wrapper, corner_radius=0, fg_color=self.settings.ctm.BG_COLOR)
|
||||
self.modal_message_label_wrapper.grid(row=0, column=0)
|
||||
|
||||
self.modal_message_label_wrapper.grid_rowconfigure((0,2),weight=1)
|
||||
self.modal_message_label_wrapper.grid_columnconfigure((0,2),weight=1)
|
||||
|
||||
self.modal_message_label = CTkLabel(
|
||||
self.modal_message_label_wrapper,
|
||||
textvariable=self._view_variable.VAR_MESSAGE_CONFIRMATION_MODAL,
|
||||
height=0,
|
||||
corner_radius=0,
|
||||
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.MESSAGE_FONT_SIZE, weight="normal"),
|
||||
anchor="w",
|
||||
text_color=self.settings.ctm.MESSAGE_TEXT_COLOR,
|
||||
)
|
||||
self.modal_message_label.grid(row=1, column=1)
|
||||
|
||||
|
||||
|
||||
self.modal_buttons_container = CTkFrame(self.modal_contents_wrapper, corner_radius=0, fg_color=self.settings.ctm.BG_COLOR)
|
||||
self.modal_buttons_container.grid(row=2, column=0, sticky="nsew")
|
||||
|
||||
self.modal_buttons_container.grid_rowconfigure((0,2),weight=1)
|
||||
self.modal_buttons_container.grid_columnconfigure(0,weight=1)
|
||||
|
||||
self.modal_buttons_wrapper = CTkFrame(self.modal_buttons_container, corner_radius=0, fg_color=self.settings.ctm.BG_COLOR)
|
||||
self.modal_buttons_wrapper.grid(row=1, column=0, sticky="ew")
|
||||
|
||||
|
||||
if modal_type == "information":
|
||||
# self.modal_buttons_wrapper.grid_columnconfigure(1, weight=1, minsize=self.settings.uism.BUTTONS_BETWEEN_PADDING)
|
||||
self.modal_buttons_wrapper.grid_columnconfigure((0,2), weight=1)
|
||||
|
||||
|
||||
self.accept_button = CTkFrame(self.modal_buttons_wrapper, corner_radius=self.settings.uism.BUTTONS_CORNER_RADIUS, fg_color=self.settings.ctm.ACCEPT_BUTTON_BG_COLOR, cursor="hand2")
|
||||
self.accept_button.grid(row=0, column=1, sticky="ew")
|
||||
|
||||
|
||||
self.accept_button.grid_columnconfigure(0, weight=1)
|
||||
self.accept_button_label_wrapper = CTkFrame(self.accept_button, corner_radius=0, fg_color=self.settings.ctm.ACCEPT_BUTTON_BG_COLOR)
|
||||
self.accept_button_label_wrapper.grid(row=0, column=0, padx=self.settings.uism.BUTTONS_IPADX, pady=self.settings.uism.BUTTONS_IPADY, sticky="ew")
|
||||
|
||||
self.accept_button_label_wrapper.grid_columnconfigure((0,2), weight=1)
|
||||
self.accept_button_label = CTkLabel(
|
||||
self.accept_button_label_wrapper,
|
||||
textvariable=self._view_variable.VAR_LABEL_CONFIRMATION_MODAL_ACCEPT_BUTTON,
|
||||
height=0,
|
||||
corner_radius=0,
|
||||
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.CONFIRMATION_BUTTONS_TEXT_FONT_SIZE, weight="normal"),
|
||||
anchor="w",
|
||||
text_color=self.settings.ctm.CONFIRMATION_BUTTONS_TEXT_COLOR,
|
||||
)
|
||||
self.accept_button_label.grid(row=0, column=1)
|
||||
|
||||
|
||||
|
||||
bindButtonFunctionAndColor(
|
||||
target_widgets=[
|
||||
self.accept_button,
|
||||
self.accept_button_label_wrapper,
|
||||
self.accept_button_label,
|
||||
],
|
||||
enter_color=settings.ctm.ACCEPT_BUTTON_HOVERED_BG_COLOR,
|
||||
leave_color=settings.ctm.ACCEPT_BUTTON_BG_COLOR,
|
||||
clicked_color=settings.ctm.ACCEPT_BUTTON_CLICKED_BG_COLOR,
|
||||
buttonReleasedFunction=lambda _e: callFunctionIfCallable(self._view_variable.CALLBACK_ACCEPTED_CONFIRMATION_MODAL),
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
else:
|
||||
self.modal_buttons_wrapper.grid_columnconfigure(1, weight=1, minsize=self.settings.uism.BUTTONS_BETWEEN_PADDING)
|
||||
self.modal_buttons_wrapper.grid_columnconfigure((0,2), weight=0, uniform="button_wrapper")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
self.deny_button = CTkFrame(self.modal_buttons_wrapper, corner_radius=self.settings.uism.BUTTONS_CORNER_RADIUS, fg_color=self.settings.ctm.DENY_BUTTON_BG_COLOR, cursor="hand2")
|
||||
self.deny_button.grid(row=0, column=0, sticky="ew")
|
||||
|
||||
|
||||
self.deny_button.grid_columnconfigure(0, weight=1)
|
||||
self.deny_button_label_wrapper = CTkFrame(self.deny_button, corner_radius=0, fg_color=self.settings.ctm.DENY_BUTTON_BG_COLOR)
|
||||
self.deny_button_label_wrapper.grid(row=0, column=0, padx=self.settings.uism.BUTTONS_IPADX, pady=self.settings.uism.BUTTONS_IPADY, sticky="ew")
|
||||
|
||||
self.deny_button_label_wrapper.grid_columnconfigure((0,2), weight=1)
|
||||
|
||||
|
||||
self.deny_button_label_wrapper.grid_columnconfigure(0, weight=1)
|
||||
self.deny_button_label = CTkLabel(
|
||||
self.deny_button_label_wrapper,
|
||||
textvariable=self._view_variable.VAR_LABEL_CONFIRMATION_MODAL_DENY_BUTTON,
|
||||
height=0,
|
||||
corner_radius=0,
|
||||
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.CONFIRMATION_BUTTONS_TEXT_FONT_SIZE, weight="normal"),
|
||||
anchor="w",
|
||||
text_color=self.settings.ctm.CONFIRMATION_BUTTONS_TEXT_COLOR,
|
||||
)
|
||||
self.deny_button_label.grid(row=0, column=1)
|
||||
|
||||
|
||||
|
||||
bindButtonFunctionAndColor(
|
||||
target_widgets=[
|
||||
self.deny_button,
|
||||
self.deny_button_label_wrapper,
|
||||
self.deny_button_label,
|
||||
],
|
||||
enter_color=settings.ctm.DENY_BUTTON_HOVERED_BG_COLOR,
|
||||
leave_color=settings.ctm.DENY_BUTTON_BG_COLOR,
|
||||
clicked_color=settings.ctm.DENY_BUTTON_CLICKED_BG_COLOR,
|
||||
buttonReleasedFunction=lambda _e: callFunctionIfCallable(self._view_variable.CALLBACK_DENIED_CONFIRMATION_MODAL),
|
||||
)
|
||||
|
||||
|
||||
|
||||
self.accept_button = CTkFrame(self.modal_buttons_wrapper, corner_radius=self.settings.uism.BUTTONS_CORNER_RADIUS, fg_color=self.settings.ctm.ACCEPT_BUTTON_BG_COLOR, cursor="hand2")
|
||||
self.accept_button.grid(row=0, column=2, sticky="ew")
|
||||
|
||||
|
||||
self.accept_button.grid_columnconfigure(0, weight=1)
|
||||
self.accept_button_label_wrapper = CTkFrame(self.accept_button, corner_radius=0, fg_color=self.settings.ctm.ACCEPT_BUTTON_BG_COLOR)
|
||||
self.accept_button_label_wrapper.grid(row=0, column=0, padx=self.settings.uism.BUTTONS_IPADX, pady=self.settings.uism.BUTTONS_IPADY, sticky="ew")
|
||||
|
||||
self.accept_button_label_wrapper.grid_columnconfigure((0,2), weight=1)
|
||||
self.accept_button_label = CTkLabel(
|
||||
self.accept_button_label_wrapper,
|
||||
textvariable=self._view_variable.VAR_LABEL_CONFIRMATION_MODAL_ACCEPT_BUTTON,
|
||||
height=0,
|
||||
corner_radius=0,
|
||||
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.CONFIRMATION_BUTTONS_TEXT_FONT_SIZE, weight="normal"),
|
||||
anchor="w",
|
||||
text_color=self.settings.ctm.CONFIRMATION_BUTTONS_TEXT_COLOR,
|
||||
)
|
||||
self.accept_button_label.grid(row=0, column=1)
|
||||
|
||||
|
||||
|
||||
bindButtonFunctionAndColor(
|
||||
target_widgets=[
|
||||
self.accept_button,
|
||||
self.accept_button_label_wrapper,
|
||||
self.accept_button_label,
|
||||
],
|
||||
enter_color=settings.ctm.ACCEPT_BUTTON_HOVERED_BG_COLOR,
|
||||
leave_color=settings.ctm.ACCEPT_BUTTON_BG_COLOR,
|
||||
clicked_color=settings.ctm.ACCEPT_BUTTON_CLICKED_BG_COLOR,
|
||||
buttonReleasedFunction=lambda _e: callFunctionIfCallable(self._view_variable.CALLBACK_ACCEPTED_CONFIRMATION_MODAL),
|
||||
)
|
||||
|
||||
|
||||
|
||||
def hide_buttons(self):
|
||||
self.modal_buttons_wrapper.grid_remove()
|
||||
|
||||
|
||||
def show(self, hide_title_bar:bool=True, close_when_focusout:bool=True):
|
||||
self.modal_buttons_wrapper.grid()
|
||||
|
||||
if hide_title_bar is False:
|
||||
self.overrideredirect(False)
|
||||
else:
|
||||
self.overrideredirect(True)
|
||||
|
||||
self.close_when_focusout = close_when_focusout
|
||||
if self.close_when_focusout is True:
|
||||
self.BIND_FOCUS_OUT_FUNC_ID = self.bind("<FocusOut>", self.focusOutFunction, "+")
|
||||
else:
|
||||
self._grab_set()
|
||||
|
||||
|
||||
self.attributes("-alpha", 0)
|
||||
self.deiconify()
|
||||
setGeometryToCenterOfTheWidget(
|
||||
attach_widget=self.attach_window,
|
||||
target_widget=self
|
||||
)
|
||||
fadeInAnimation(self, steps=5, interval=0.005, max_alpha=1)
|
||||
self.focus_set()
|
||||
|
||||
|
||||
def hide(self):
|
||||
if self.BIND_FOCUS_OUT_FUNC_ID is not None:
|
||||
self.unbind("<FocusOut>", self.BIND_FOCUS_OUT_FUNC_ID)
|
||||
|
||||
self.withdraw()
|
||||
self.grab_release()
|
||||
|
||||
def focusOutFunction(self, e):
|
||||
if str(e.widget) != ".!_createconfirmationmodal": return
|
||||
callFunctionIfCallable(self._view_variable.CALLBACK_HIDE_CONFIRMATION_MODAL)
|
||||
|
||||
def _grab_set(self):
|
||||
self.grab_set()
|
||||
355
vrct_gui/_CreateDropdownMenuWindow.py
Normal file
@@ -0,0 +1,355 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
from customtkinter import CTkToplevel, CTkFrame, CTkLabel, CTkFont, CTkScrollableFrame
|
||||
from time import sleep
|
||||
|
||||
from .ui_utils import bindButtonReleaseFunction, bindEnterAndLeaveColor, bindButtonPressColor, getLatestHeight, applyUiScalingAndFixTheBugScrollBar, getLatestWidth, getLongestText
|
||||
from functools import partial
|
||||
|
||||
from utils import isEven, makeEven
|
||||
|
||||
class _CreateDropdownMenuWindow(CTkToplevel):
|
||||
def __init__(
|
||||
self,
|
||||
settings,
|
||||
view_variable,
|
||||
|
||||
window_additional_y_pos,
|
||||
window_border_width,
|
||||
scrollbar_ipadx,
|
||||
scrollbar_width,
|
||||
value_ipadx,
|
||||
value_ipady,
|
||||
value_pady,
|
||||
value_font_size,
|
||||
dropdown_menu_default_min_width,
|
||||
|
||||
window_bg_color,
|
||||
window_border_color,
|
||||
values_bg_color,
|
||||
values_hovered_bg_color,
|
||||
values_clicked_bg_color,
|
||||
values_text_color,
|
||||
):
|
||||
|
||||
super().__init__()
|
||||
self.withdraw()
|
||||
self.hide = True
|
||||
|
||||
self.window_additional_y_pos=window_additional_y_pos
|
||||
self.window_border_width=window_border_width
|
||||
self.scrollbar_ipadx=scrollbar_ipadx
|
||||
self.scrollbar_width=scrollbar_width
|
||||
self.value_ipadx=value_ipadx
|
||||
self.value_ipady=value_ipady
|
||||
self.value_pady=value_pady
|
||||
self.value_font_size=value_font_size
|
||||
self.dropdown_menu_default_min_width=dropdown_menu_default_min_width
|
||||
|
||||
self.window_bg_color=window_bg_color
|
||||
self.window_border_color=window_border_color
|
||||
self.values_bg_color=values_bg_color
|
||||
self.values_hovered_bg_color=values_hovered_bg_color
|
||||
self.values_clicked_bg_color=values_clicked_bg_color
|
||||
self.values_text_color=values_text_color
|
||||
|
||||
|
||||
self.settings = settings
|
||||
self.attach_widget = None
|
||||
self._view_variable = view_variable
|
||||
self.wrapper_widget = None
|
||||
|
||||
self.dropdown_menu_widgets = {}
|
||||
self.active_dropdown_menu_widget = None
|
||||
|
||||
|
||||
|
||||
self.attach_widget_width = None
|
||||
self.attach_widget_height = None
|
||||
self.attach_widget_x_pos = None
|
||||
self.attach_widget_y_pos = None
|
||||
self.x_pos = None
|
||||
self.y_pos = None
|
||||
|
||||
self.init_height = 200
|
||||
self.new_height = self.init_height
|
||||
self.init_width = 200
|
||||
self.new_width = self.init_width
|
||||
|
||||
self.init_max_display_length = 8
|
||||
self.max_display_length = self.init_max_display_length
|
||||
|
||||
self.title("")
|
||||
self.overrideredirect(True)
|
||||
|
||||
self.wm_attributes("-alpha", 0)
|
||||
self.wm_attributes("-toolwindow", True)
|
||||
|
||||
self.configure(fg_color=self.window_bg_color)
|
||||
self.resizable(width=False, height=False)
|
||||
|
||||
|
||||
|
||||
def updateDropdownMenuValues(self, dropdown_menu_widget_id, dropdown_menu_values):
|
||||
self.dropdown_menu_widgets[dropdown_menu_widget_id].widget.destroy()
|
||||
self.createDropdownMenuBox(
|
||||
dropdown_menu_widget_id=dropdown_menu_widget_id,
|
||||
dropdown_menu_values=dropdown_menu_values,
|
||||
command=self.dropdown_menu_widgets[dropdown_menu_widget_id].command,
|
||||
wrapper_widget=self.dropdown_menu_widgets[dropdown_menu_widget_id].wrapper_widget,
|
||||
attach_widget=self.dropdown_menu_widgets[dropdown_menu_widget_id].attach_widget,
|
||||
|
||||
dropdown_menu_min_width=self.dropdown_menu_widgets[dropdown_menu_widget_id].dropdown_menu_settings.dropdown_menu_min_width,
|
||||
dropdown_menu_height=self.dropdown_menu_widgets[dropdown_menu_widget_id].dropdown_menu_settings.dropdown_menu_height,
|
||||
max_display_length=self.dropdown_menu_widgets[dropdown_menu_widget_id].dropdown_menu_settings.max_display_length,
|
||||
)
|
||||
|
||||
|
||||
def createDropdownMenuBox(self, dropdown_menu_widget_id, dropdown_menu_values, command, wrapper_widget, attach_widget, dropdown_menu_min_width=None, dropdown_menu_height=None, max_display_length=None):
|
||||
|
||||
self.attach_widget = attach_widget
|
||||
self.wrapper_widget = wrapper_widget
|
||||
|
||||
|
||||
self.new_width = dropdown_menu_min_width if dropdown_menu_min_width is not None else self.dropdown_menu_default_min_width
|
||||
self.new_height = dropdown_menu_height if dropdown_menu_height is not None else self.init_height
|
||||
self.max_display_length = max_display_length if max_display_length is not None else self.init_max_display_length
|
||||
|
||||
|
||||
self.dropdown_menu_container = CTkFrame(self, corner_radius=0, fg_color=self.window_border_color, width=0, height=0)
|
||||
self.dropdown_menu_container.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
|
||||
BORDER_WIDTH=self.window_border_width
|
||||
self.scroll_frame_container = CTkScrollableFrame(
|
||||
self.dropdown_menu_container,
|
||||
corner_radius=0,
|
||||
fg_color=self.window_bg_color,
|
||||
width=0,
|
||||
height=0,
|
||||
border_width=0,
|
||||
)
|
||||
self.scroll_frame_container.grid(row=0, column=0, padx=BORDER_WIDTH, pady=BORDER_WIDTH, sticky="nsew")
|
||||
self.scroll_frame_container.grid_columnconfigure(0, weight=1)
|
||||
|
||||
|
||||
|
||||
self._createDropdownMenuValues(dropdown_menu_widget_id, dropdown_menu_values, command)
|
||||
|
||||
applyUiScalingAndFixTheBugScrollBar(
|
||||
scrollbar_widget=self.scroll_frame_container,
|
||||
padx=self.scrollbar_ipadx,
|
||||
width=self.scrollbar_width,
|
||||
)
|
||||
|
||||
geometry_width = int(self.new_width + self.scroll_frame_container._scrollbar.winfo_width() + (BORDER_WIDTH*2) + (self.scrollbar_ipadx[0] + self.scrollbar_ipadx[1]))
|
||||
geometry_height = int(self.new_height + (BORDER_WIDTH*2))
|
||||
|
||||
self.dropdown_menu_widgets[dropdown_menu_widget_id] = SimpleNamespace()
|
||||
|
||||
self.dropdown_menu_widgets[dropdown_menu_widget_id] = SimpleNamespace(
|
||||
widget=self.dropdown_menu_container,
|
||||
command=command,
|
||||
wrapper_widget=wrapper_widget,
|
||||
attach_widget=attach_widget,
|
||||
dropdown_menu_settings=SimpleNamespace(
|
||||
dropdown_menu_min_width=dropdown_menu_min_width,
|
||||
dropdown_menu_height=dropdown_menu_height,
|
||||
max_display_length=max_display_length,
|
||||
),
|
||||
_settings=SimpleNamespace(
|
||||
geometry_width=geometry_width,
|
||||
geometry_height=geometry_height,
|
||||
),
|
||||
)
|
||||
|
||||
self.dropdown_menu_container.grid_remove()
|
||||
|
||||
|
||||
def _createDropdownMenuValues(self, dropdown_menu_widget_id, dropdown_menu_values, command):
|
||||
|
||||
longest_text = getLongestText(dropdown_menu_values)
|
||||
self.dropdown_menu_values_wrapper = CTkFrame(self.scroll_frame_container, corner_radius=0, fg_color=self.window_bg_color)
|
||||
self.dropdown_menu_values_wrapper.grid(row=0, column=0, sticky="nsew")
|
||||
self.dropdown_menu_values_wrapper.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# for get to the height__________________
|
||||
__dropdown_menu_value_wrapper = CTkFrame(self.dropdown_menu_values_wrapper, corner_radius=0, fg_color=self.values_bg_color, width=0, height=0)
|
||||
__dropdown_menu_value_wrapper.grid(row=0, column=0, pady=self.value_pady, sticky="nsew")
|
||||
setattr(self, f"{dropdown_menu_widget_id}__{0}", __dropdown_menu_value_wrapper)
|
||||
|
||||
|
||||
__dropdown_menu_value_wrapper.grid_rowconfigure((0,2), weight=1)
|
||||
# __dropdown_menu_value_wrapper.grid_columnconfigure(0, weight=1)
|
||||
__label_widget = CTkLabel(
|
||||
__dropdown_menu_value_wrapper,
|
||||
text=longest_text,
|
||||
height=0,
|
||||
corner_radius=0,
|
||||
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.value_font_size, weight="normal"),
|
||||
anchor="w",
|
||||
text_color=self.values_text_color,
|
||||
)
|
||||
# setattr(self, f"l", __label_widget)
|
||||
|
||||
__label_widget.grid(row=1, column=0, padx=self.value_ipadx, pady=self.value_ipady, sticky="w")
|
||||
|
||||
label_height = getLatestHeight(__dropdown_menu_value_wrapper)
|
||||
label_width = getLatestWidth(__label_widget)
|
||||
label_width += self.scroll_frame_container._scrollbar.winfo_width() + (self.window_border_width*2) + (self.scrollbar_ipadx[0] + self.scrollbar_ipadx[1])
|
||||
if label_width > self.new_width:
|
||||
additional_width = int(label_width - self.new_width)
|
||||
self.new_width += additional_width
|
||||
|
||||
# for fixing 1px bug
|
||||
if isEven(label_height) is False:
|
||||
self.value_ipady = (self.value_ipady[0], self.value_ipady[1] - 1)
|
||||
|
||||
__dropdown_menu_value_wrapper.destroy()
|
||||
# ______________________________________
|
||||
|
||||
dropdown_menu_values_length = len(dropdown_menu_values)
|
||||
if dropdown_menu_values_length < self.max_display_length:
|
||||
self.new_height = int(dropdown_menu_values_length * label_height)
|
||||
else:
|
||||
self.new_height = int(self.max_display_length * label_height)
|
||||
|
||||
|
||||
# for fixing 1px bug
|
||||
self.new_height = makeEven(self.new_height)
|
||||
self.new_width = makeEven(self.new_width)
|
||||
self.scroll_frame_container.configure(width=self.new_width, height=self.new_height)
|
||||
|
||||
|
||||
|
||||
row=0
|
||||
for dropdown_menu_value in dropdown_menu_values:
|
||||
|
||||
dropdown_menu_value_wrapper = CTkFrame(self.dropdown_menu_values_wrapper, corner_radius=0, fg_color=self.values_bg_color, width=0, height=0, cursor="hand2")
|
||||
dropdown_menu_value_wrapper.grid(row=row, column=0, pady=self.value_pady, sticky="nsew")
|
||||
setattr(self, f"{dropdown_menu_widget_id}__{row}", dropdown_menu_value_wrapper)
|
||||
|
||||
|
||||
|
||||
dropdown_menu_value_wrapper.grid_rowconfigure((0,2), weight=1)
|
||||
label_widget = CTkLabel(
|
||||
dropdown_menu_value_wrapper,
|
||||
text=dropdown_menu_value,
|
||||
height=0,
|
||||
corner_radius=0,
|
||||
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.value_font_size, weight="normal"),
|
||||
anchor="w",
|
||||
text_color=self.values_text_color,
|
||||
)
|
||||
|
||||
label_widget.grid(row=1, column=0, padx=self.value_ipadx, pady=self.value_ipady, sticky="w")
|
||||
|
||||
|
||||
bindEnterAndLeaveColor([dropdown_menu_value_wrapper, label_widget], self.values_hovered_bg_color, self.values_bg_color)
|
||||
bindButtonPressColor([dropdown_menu_value_wrapper, label_widget], self.values_clicked_bg_color, self.values_bg_color)
|
||||
|
||||
|
||||
|
||||
def optimizedCommand(value, _e):
|
||||
command(value)
|
||||
self._withdraw()
|
||||
|
||||
callback = partial(optimizedCommand, dropdown_menu_value)
|
||||
bindButtonReleaseFunction([dropdown_menu_value_wrapper, label_widget], callback)
|
||||
|
||||
row+=1
|
||||
|
||||
|
||||
|
||||
def show(self, dropdown_menu_widget_id):
|
||||
if self.hide is False: return
|
||||
self.wm_attributes("-alpha", 0)
|
||||
|
||||
|
||||
|
||||
if self.active_dropdown_menu_widget is not None:
|
||||
self.active_dropdown_menu_widget.grid_remove()
|
||||
|
||||
target_data = self.dropdown_menu_widgets[dropdown_menu_widget_id]
|
||||
self.attach_widget = target_data.attach_widget
|
||||
|
||||
target_data.widget.grid()
|
||||
self.active_dropdown_menu_widget = target_data.widget
|
||||
|
||||
self.geometry("{}x{}".format(target_data._settings.geometry_width, target_data._settings.geometry_height))
|
||||
|
||||
|
||||
self.deiconify()
|
||||
self._adjustToTargetWidgetGeometry()
|
||||
self.BIND_CONFIGURE_FUNC_ID = self.attach_widget.winfo_toplevel().bind("<Configure>", self._adjustToTargetWidgetGeometry, "+")
|
||||
self.BIND_UNMAP_FUNC_ID = self.attach_widget.bind("<Unmap>", self._withdraw, "+")
|
||||
|
||||
self.BIND_BUTTON_1_FUNC_ID = self.attach_widget.winfo_toplevel().bind("<Button-1>", self._withdraw, "+")
|
||||
|
||||
|
||||
self.hide = False
|
||||
|
||||
|
||||
|
||||
for i in range(0,91,10):
|
||||
if not self.winfo_exists():
|
||||
break
|
||||
self.attributes("-alpha", i/100)
|
||||
self.update()
|
||||
sleep(1/100)
|
||||
self.wm_attributes("-alpha", 1)
|
||||
self.update()
|
||||
|
||||
|
||||
|
||||
def _withdraw(self, e=None):
|
||||
self.withdraw()
|
||||
self.attach_widget.winfo_toplevel().unbind("<Configure>", self.BIND_CONFIGURE_FUNC_ID)
|
||||
self.attach_widget.unbind("<Unmap>", self.BIND_UNMAP_FUNC_ID)
|
||||
self.attach_widget.winfo_toplevel().unbind("<Button-1>", self.BIND_BUTTON_1_FUNC_ID)
|
||||
self.hide = True
|
||||
|
||||
|
||||
def _adjustToTargetWidgetGeometry(self, e=None):
|
||||
if not self.attach_widget.winfo_exists():
|
||||
return
|
||||
self.attach_widget.update_idletasks()
|
||||
|
||||
|
||||
|
||||
self.update()
|
||||
if self.attach_widget_x_pos == self.attach_widget.winfo_rootx() and self.attach_widget_y_pos == self.attach_widget.winfo_rooty():
|
||||
self.lift()
|
||||
return
|
||||
|
||||
self.wrapper_widget_y_pos = self.wrapper_widget.winfo_rooty()
|
||||
self.wrapper_widget_bottom_y_pos = self.wrapper_widget_y_pos + self.wrapper_widget.winfo_height()
|
||||
|
||||
self.attach_widget_width = self.attach_widget.winfo_width()
|
||||
self.attach_widget_height = self.attach_widget.winfo_height()
|
||||
self.attach_widget_x_pos = self.attach_widget.winfo_rootx()
|
||||
self.attach_widget_y_pos = self.attach_widget.winfo_rooty()
|
||||
|
||||
|
||||
self.y_pos = int(self.attach_widget_y_pos + self.attach_widget_height + self.window_additional_y_pos)
|
||||
|
||||
if self.wrapper_widget_y_pos > self.y_pos or self.y_pos > self.wrapper_widget_bottom_y_pos:
|
||||
self.hideTemporarily()
|
||||
else:
|
||||
if self.winfo_exists():
|
||||
self.deiconify()
|
||||
|
||||
|
||||
if self.winfo_width() >= self.attach_widget_width:
|
||||
self.x_pos = int(self.attach_widget_x_pos - (self.winfo_width() - self.attach_widget_width))
|
||||
else:
|
||||
self.x_pos = self.attach_widget_x_pos
|
||||
|
||||
self.geometry("+{}+{}".format(self.x_pos, self.y_pos))
|
||||
|
||||
self.lift()
|
||||
|
||||
def hideTemporarily(self):
|
||||
self.withdraw()
|
||||
|
||||
|
||||
177
vrct_gui/_CreateErrorWindow.py
Normal file
@@ -0,0 +1,177 @@
|
||||
from customtkinter import CTkToplevel, CTkFrame, CTkLabel, CTkFont
|
||||
from time import sleep
|
||||
|
||||
from .ui_utils import getLatestWidth, getLatestHeight
|
||||
from utils import isEven
|
||||
|
||||
|
||||
class _CreateErrorWindow(CTkToplevel):
|
||||
def __init__(
|
||||
self,
|
||||
settings,
|
||||
view_variable,
|
||||
wrapper_widget,
|
||||
|
||||
message_ipadx,
|
||||
message_ipady,
|
||||
message_font_size,
|
||||
|
||||
message_bg_color,
|
||||
message_text_color,
|
||||
):
|
||||
|
||||
super().__init__()
|
||||
self.withdraw()
|
||||
self.hide = True
|
||||
|
||||
self.settings = settings
|
||||
self.attach_widget = None
|
||||
self._view_variable = view_variable
|
||||
self.wrapper_widget = wrapper_widget
|
||||
|
||||
|
||||
self.message_ipadx = message_ipadx
|
||||
self.message_ipady = message_ipady
|
||||
self.message_font_size = message_font_size
|
||||
|
||||
self.message_bg_color = message_bg_color
|
||||
self.message_text_color = message_text_color
|
||||
|
||||
|
||||
self.attach_widget_width = None
|
||||
self.attach_widget_height = None
|
||||
self.attach_widget_x_pos = None
|
||||
self.attach_widget_y_pos = None
|
||||
self.x_pos = None
|
||||
self.y_pos = None
|
||||
|
||||
self.title("")
|
||||
self.overrideredirect(True)
|
||||
|
||||
self.wm_attributes("-alpha", 0)
|
||||
self.wm_attributes("-toolwindow", True)
|
||||
|
||||
self.configure(fg_color=self.message_bg_color)
|
||||
|
||||
|
||||
|
||||
|
||||
self.grid_rowconfigure(0,weight=1)
|
||||
self.grid_columnconfigure(0,weight=1)
|
||||
|
||||
self.error_message_container = CTkFrame(self, corner_radius=0, fg_color=self.message_bg_color, width=0, height=0)
|
||||
self.error_message_container.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
|
||||
self.error_message_container_label_wrapper = CTkLabel(
|
||||
self.error_message_container,
|
||||
# text=message,
|
||||
textvariable=self._view_variable.VAR_ERROR_MESSAGE,
|
||||
height=0,
|
||||
corner_radius=0,
|
||||
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.message_font_size, weight="normal"),
|
||||
anchor="w",
|
||||
justify="left",
|
||||
text_color=self.message_text_color,
|
||||
)
|
||||
self.error_message_container_label_wrapper.grid(row=0, column=0, padx=self.message_ipadx, pady=self.message_ipady, sticky="nsew")
|
||||
|
||||
|
||||
|
||||
|
||||
def show(self, target_widget):
|
||||
if self.hide is False: return
|
||||
|
||||
self.attach_widget = target_widget
|
||||
|
||||
self.deiconify()
|
||||
self._adjustToTargetWidgetGeometry()
|
||||
self.BIND_CONFIGURE_FUNC_ID = self.attach_widget.winfo_toplevel().bind("<Configure>", self._adjustToTargetWidgetGeometry, "+")
|
||||
self.BIND_UNMAP_FUNC_ID = self.attach_widget.bind("<Unmap>", self._withdraw, "+")
|
||||
|
||||
self.hide = False
|
||||
|
||||
label_width = getLatestWidth(self.error_message_container_label_wrapper)
|
||||
label_height = getLatestHeight(self.error_message_container_label_wrapper)
|
||||
|
||||
# for fixing 1px bug
|
||||
if isEven(label_width) is False:
|
||||
self.error_message_container_label_wrapper.grid(padx=(self.message_ipadx[0], self.message_ipadx[1]-1))
|
||||
else:
|
||||
self.error_message_container_label_wrapper.grid(padx=self.message_ipadx)
|
||||
|
||||
# for fixing 1px bug
|
||||
if isEven(label_height) is False:
|
||||
self.error_message_container_label_wrapper.grid(pady=(self.message_ipady[0], self.message_ipady[1]-1))
|
||||
else:
|
||||
self.error_message_container_label_wrapper.grid(pady=self.message_ipady)
|
||||
|
||||
|
||||
for i in range(0,101,20):
|
||||
if not self.winfo_exists():
|
||||
break
|
||||
self.attributes("-alpha", i/100)
|
||||
self.update()
|
||||
sleep(1/100)
|
||||
|
||||
sleep(0.1)
|
||||
|
||||
for i in range(0,91,10):
|
||||
if not self.winfo_exists():
|
||||
break
|
||||
self.attributes("-alpha", i/100)
|
||||
self.update()
|
||||
sleep(1/80)
|
||||
|
||||
|
||||
def _withdraw(self, e=None):
|
||||
self.withdraw()
|
||||
self.attach_widget.winfo_toplevel().unbind("<Configure>", self.BIND_CONFIGURE_FUNC_ID)
|
||||
self.attach_widget.unbind("<Unmap>", self.BIND_UNMAP_FUNC_ID)
|
||||
self.hide = True
|
||||
|
||||
|
||||
|
||||
def _adjustToTargetWidgetGeometry(self, e=None):
|
||||
if not self.attach_widget.winfo_exists():
|
||||
return
|
||||
self.attach_widget.update_idletasks()
|
||||
|
||||
|
||||
|
||||
self.update()
|
||||
if self.attach_widget_x_pos == self.attach_widget.winfo_rootx() and self.attach_widget_y_pos == self.attach_widget.winfo_rooty():
|
||||
self.lift()
|
||||
return
|
||||
|
||||
self.wrapper_widget_y_pos = self.wrapper_widget.winfo_rooty()
|
||||
self.wrapper_widget_bottom_y_pos = self.wrapper_widget_y_pos + self.wrapper_widget.winfo_height()
|
||||
|
||||
self.attach_widget_width = self.attach_widget.winfo_width()
|
||||
self.attach_widget_height = self.attach_widget.winfo_height()
|
||||
self.attach_widget_x_pos = self.attach_widget.winfo_rootx()
|
||||
self.attach_widget_y_pos = self.attach_widget.winfo_rooty()
|
||||
|
||||
|
||||
self.y_pos = int(self.attach_widget_y_pos + self.attach_widget_height + 4)
|
||||
|
||||
if self.wrapper_widget_y_pos > self.y_pos or self.y_pos > self.wrapper_widget_bottom_y_pos:
|
||||
self.hideTemporarily()
|
||||
else:
|
||||
if self.winfo_exists():
|
||||
self.deiconify()
|
||||
|
||||
|
||||
if self.winfo_width() >= self.attach_widget_width:
|
||||
self.x_pos = int(self.attach_widget_x_pos - (self.winfo_width() - self.attach_widget_width))
|
||||
else:
|
||||
self.x_pos = self.attach_widget_x_pos
|
||||
|
||||
self.geometry("+{}+{}".format(self.x_pos, self.y_pos))
|
||||
|
||||
self.lift()
|
||||
|
||||
def hideTemporarily(self):
|
||||
self.withdraw()
|
||||
|
||||
|
||||
182
vrct_gui/_CreateSelectableLanguagesWindow.py
Normal file
@@ -0,0 +1,182 @@
|
||||
from functools import partial
|
||||
|
||||
from .ui_utils import bindButtonReleaseFunction, bindEnterAndLeaveColor, bindButtonPressColor, applyUiScalingAndFixTheBugScrollBar
|
||||
from utils import callFunctionIfCallable, makeEven
|
||||
|
||||
from customtkinter import CTkToplevel, CTkFrame, CTkLabel, CTkFont, CTkScrollableFrame
|
||||
|
||||
class _CreateSelectableLanguagesWindow(CTkToplevel):
|
||||
def __init__(self, vrct_gui, settings, view_variable):
|
||||
super().__init__()
|
||||
self.withdraw()
|
||||
|
||||
self.attach = vrct_gui.main_bg_container
|
||||
self.vrct_gui = vrct_gui
|
||||
self.settings = settings
|
||||
self._view_variable = view_variable
|
||||
|
||||
self.is_created = False
|
||||
self.selectable_language_window_type = None
|
||||
|
||||
|
||||
self.title("_CreateSelectableLanguagesWindow")
|
||||
self.overrideredirect(True)
|
||||
self.configure(fg_color=self.settings.ctm.TOP_BG_COLOR)
|
||||
self.protocol("WM_DELETE_WINDOW", vrct_gui._closeSelectableLanguagesWindow)
|
||||
self.bind("<FocusOut>", self.focusOutFunction)
|
||||
|
||||
|
||||
|
||||
|
||||
def createContainer(self, selectable_language_window_type):
|
||||
self.selectable_language_window_type = selectable_language_window_type
|
||||
|
||||
self.attach.update_idletasks()
|
||||
self.x_pos = self.attach.winfo_rootx()
|
||||
self.y_pos = self.attach.winfo_rooty()
|
||||
self.width_new = makeEven(self.attach.winfo_width())
|
||||
self.height_new = makeEven(self.attach.winfo_height())
|
||||
|
||||
|
||||
self.geometry("{}x{}+{}+{}".format(self.width_new, self.height_new, self.x_pos, self.y_pos))
|
||||
|
||||
|
||||
|
||||
if self.is_created is True:
|
||||
pass
|
||||
else:
|
||||
self._createContainer()
|
||||
|
||||
|
||||
def callbackSelectableLanguages(self, value, _e):
|
||||
if self.selectable_language_window_type == "your_language":
|
||||
callback = self._view_variable.CALLBACK_SELECTED_YOUR_LANGUAGE
|
||||
target_variable = self._view_variable.VAR_YOUR_LANGUAGE
|
||||
elif self.selectable_language_window_type == "target_language":
|
||||
callback = self._view_variable.CALLBACK_SELECTED_TARGET_LANGUAGE
|
||||
target_variable = self._view_variable.VAR_TARGET_LANGUAGE
|
||||
|
||||
target_variable.set(value)
|
||||
callFunctionIfCallable(callback, value)
|
||||
self.vrct_gui._closeSelectableLanguagesWindow()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _createContainer(self):
|
||||
self.grid_rowconfigure(0, minsize=self.settings.uism.TOP_BAR_MIN_HEIGHT)
|
||||
self.grid_rowconfigure(1, weight=1)
|
||||
self.grid_columnconfigure(0, weight=1)
|
||||
self.top_container = CTkFrame(self, corner_radius=0, fg_color=self.settings.ctm.TOP_BG_COLOR, width=0, height=0)
|
||||
self.top_container.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
|
||||
self.top_container.grid_rowconfigure((0,2), weight=1)
|
||||
self.top_container.grid_columnconfigure(1, weight=1)
|
||||
self.go_back_button_container = CTkFrame(self.top_container, corner_radius=0, fg_color=self.settings.ctm.GO_BACK_BUTTON_BG_COLOR, width=0, height=0, cursor="hand2")
|
||||
self.go_back_button_container.grid(row=1, column=0)
|
||||
|
||||
self.go_back_button_label = CTkLabel(
|
||||
self.go_back_button_container,
|
||||
textvariable=self._view_variable.VAR_GO_BACK_LABEL_SELECTABLE_LANGUAGE,
|
||||
height=0,
|
||||
corner_radius=0,
|
||||
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.GO_BACK_BUTTON_LABEL_FONT_SIZE, weight="normal"),
|
||||
anchor="w",
|
||||
text_color=self.settings.ctm.BASIC_TEXT_COLOR,
|
||||
)
|
||||
self.go_back_button_label.grid(row=0, column=0, padx=self.settings.uism.GO_BACK_BUTTON_IPADX, pady=self.settings.uism.GO_BACK_BUTTON_IPADY)
|
||||
|
||||
|
||||
bindEnterAndLeaveColor([self.go_back_button_container, self.go_back_button_label], self.settings.ctm.GO_BACK_BUTTON_BG_HOVERED_COLOR, self.settings.ctm.GO_BACK_BUTTON_BG_COLOR)
|
||||
bindButtonPressColor([self.go_back_button_container, self.go_back_button_label], self.settings.ctm.GO_BACK_BUTTON_BG_CLICKED_COLOR, self.settings.ctm.GO_BACK_BUTTON_BG_COLOR)
|
||||
|
||||
|
||||
bindButtonReleaseFunction([self.go_back_button_container, self.go_back_button_label], lambda _e: self.vrct_gui._closeSelectableLanguagesWindow())
|
||||
|
||||
|
||||
|
||||
self.title_container = CTkFrame(self.top_container, corner_radius=0, fg_color=self.settings.ctm.TOP_BG_COLOR, width=0, height=0)
|
||||
self.title_container.grid(row=1, column=1, sticky="nsew")
|
||||
|
||||
self.title_container.grid_columnconfigure((0,2), weight=1)
|
||||
self.title_container.grid_rowconfigure((0,2), weight=1)
|
||||
self.title_label = CTkLabel(
|
||||
self.title_container,
|
||||
textvariable=self._view_variable.VAR_TITLE_LABEL_SELECTABLE_LANGUAGE,
|
||||
height=0,
|
||||
corner_radius=0,
|
||||
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.TITLE_FONT_SIZE, weight="normal"),
|
||||
anchor="w",
|
||||
text_color=self.settings.ctm.TITLE_TEXT_COLOR,
|
||||
)
|
||||
self.title_label.grid(row=1, column=1)
|
||||
|
||||
|
||||
|
||||
|
||||
self.scroll_frame_container = CTkScrollableFrame(self, corner_radius=0, fg_color=self.settings.ctm.MAIN_BG_COLOR, width=self.width_new, height=self.height_new)
|
||||
self.scroll_frame_container.grid(row=1, column=0, sticky="nsew")
|
||||
|
||||
applyUiScalingAndFixTheBugScrollBar(
|
||||
scrollbar_widget=self.scroll_frame_container,
|
||||
padx=self.settings.uism.SCROLLBAR_IPADX,
|
||||
width=self.settings.uism.SCROLLBAR_WIDTH,
|
||||
)
|
||||
|
||||
|
||||
self.container = CTkFrame(self.scroll_frame_container, corner_radius=0, fg_color=self.settings.ctm.MAIN_BG_COLOR, width=0, height=0)
|
||||
self.container.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
|
||||
|
||||
max_row = int(len(self._view_variable.LIST_SELECTABLE_LANGUAGES)/3) + 1
|
||||
max_row+=1
|
||||
row=0
|
||||
column=0
|
||||
for selectable_language_name in self._view_variable.LIST_SELECTABLE_LANGUAGES:
|
||||
|
||||
self.wrapper = CTkFrame(self.container, corner_radius=0, fg_color=self.settings.ctm.LANGUAGE_BUTTON_BG_COLOR, width=0, height=0, cursor="hand2")
|
||||
self.wrapper.grid(row=row, column=column, sticky="nsew")
|
||||
setattr(self, f"{row}_{column}", self.wrapper)
|
||||
|
||||
|
||||
|
||||
self.wrapper.grid_rowconfigure((0,2), weight=1)
|
||||
selectable_language_name_for_text = selectable_language_name.replace("\n", " ")
|
||||
label_widget = CTkLabel(
|
||||
self.wrapper,
|
||||
text=selectable_language_name_for_text,
|
||||
height=0,
|
||||
corner_radius=0,
|
||||
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.VALUES_TEXT_FONT_SIZE, weight="normal"),
|
||||
anchor="w",
|
||||
text_color=self.settings.ctm.BASIC_TEXT_COLOR,
|
||||
)
|
||||
|
||||
label_widget.grid(row=1, column=0, padx=self.settings.uism.VALUES_TEXT_IPADX, pady=self.settings.uism.VALUES_TEXT_IPADY)
|
||||
|
||||
|
||||
|
||||
bindEnterAndLeaveColor([self.wrapper, label_widget], self.settings.ctm.LANGUAGE_BUTTON_BG_HOVERED_COLOR, self.settings.ctm.LANGUAGE_BUTTON_BG_COLOR)
|
||||
bindButtonPressColor([self.wrapper, label_widget], self.settings.ctm.LANGUAGE_BUTTON_BG_CLICKED_COLOR, self.settings.ctm.LANGUAGE_BUTTON_BG_COLOR)
|
||||
|
||||
|
||||
|
||||
callback = partial(self.callbackSelectableLanguages, selectable_language_name)
|
||||
bindButtonReleaseFunction([self.wrapper, label_widget], callback)
|
||||
|
||||
if row == max_row:
|
||||
row=0
|
||||
column+=1
|
||||
else:
|
||||
row+=1
|
||||
|
||||
|
||||
self.is_created = True
|
||||
|
||||
|
||||
def focusOutFunction(self, e):
|
||||
if str(e.widget) != ".!_createselectablelanguageswindow": return
|
||||
self.vrct_gui._closeSelectableLanguagesWindow()
|
||||
80
vrct_gui/_CreateWindowCover.py
Normal file
@@ -0,0 +1,80 @@
|
||||
from customtkinter import CTkToplevel, CTkFrame, CTkLabel, CTkFont
|
||||
|
||||
from .ui_utils import fadeInAnimation
|
||||
from utils import makeEven
|
||||
|
||||
class _CreateWindowCover(CTkToplevel):
|
||||
def __init__(self, attach_window, settings, view_variable):
|
||||
super().__init__()
|
||||
self.withdraw()
|
||||
|
||||
self.BIND_CONFIGURE_ADJUSTED_GEOMETRY_FUNC_ID=None
|
||||
self.BIND_FOCUS_IN_FUNC_ID=None
|
||||
|
||||
self.attach_window = attach_window
|
||||
self.settings = settings
|
||||
self._view_variable = view_variable
|
||||
|
||||
self.title("")
|
||||
self.overrideredirect(True)
|
||||
self.wm_attributes("-toolwindow", True)
|
||||
self.configure(fg_color="black")
|
||||
self.protocol("WM_DELETE_WINDOW", lambda: self.withdraw())
|
||||
|
||||
|
||||
self.grid_rowconfigure(0,weight=1)
|
||||
self.grid_columnconfigure(0,weight=1)
|
||||
self.cover_container = CTkFrame(self, corner_radius=0, fg_color="black", width=0, height=0)
|
||||
self.cover_container.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
|
||||
self.cover_container_label_wrapper = CTkLabel(
|
||||
self.cover_container,
|
||||
textvariable=self._view_variable.VAR_LABEL_MAIN_WINDOW_COVER_MESSAGE,
|
||||
height=0,
|
||||
corner_radius=0,
|
||||
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.TEXT_FONT_SIZE, weight="normal"),
|
||||
anchor="w",
|
||||
text_color=self.settings.ctm.TEXT_COLOR,
|
||||
)
|
||||
self.cover_container_label_wrapper.place(relx=0.5, rely=0.5, anchor="center")
|
||||
|
||||
|
||||
def show(self, bind_focusin=None):
|
||||
self.BIND_CONFIGURE_ADJUSTED_GEOMETRY_FUNC_ID = self.attach_window.bind("<Configure>", self._adjustToMainWindowGeometry, "+")
|
||||
if bind_focusin is not None:
|
||||
self.BIND_FOCUS_IN_FUNC_ID = self.bind("<FocusIn>", lambda _e: bind_focusin(), "+")
|
||||
else:
|
||||
self.BIND_FOCUS_IN_FUNC_ID = None
|
||||
|
||||
|
||||
self.attributes("-alpha", 0)
|
||||
self.deiconify()
|
||||
self.attach_window.update_idletasks()
|
||||
self.x_pos = self.attach_window.winfo_rootx()
|
||||
self.y_pos = self.attach_window.winfo_rooty()
|
||||
self.width_new = self.attach_window.winfo_width()
|
||||
self.height_new = self.attach_window.winfo_height()
|
||||
self.geometry("{}x{}+{}+{}".format(self.width_new, self.height_new, self.x_pos, self.y_pos))
|
||||
fadeInAnimation(self, steps=5, interval=0.005, max_alpha=0.5)
|
||||
|
||||
|
||||
|
||||
def hide(self):
|
||||
self.attach_window.unbind("<Configure>", self.BIND_CONFIGURE_ADJUSTED_GEOMETRY_FUNC_ID)
|
||||
if self.BIND_FOCUS_IN_FUNC_ID is not None:
|
||||
self.unbind("<FocusIn>", self.BIND_FOCUS_IN_FUNC_ID)
|
||||
|
||||
self.withdraw()
|
||||
|
||||
|
||||
|
||||
def _adjustToMainWindowGeometry(self, e=None):
|
||||
self.attach_window.update_idletasks()
|
||||
x_pos = self.attach_window.winfo_rootx()
|
||||
y_pos = self.attach_window.winfo_rooty()
|
||||
width_new = makeEven(self.attach_window.winfo_width())
|
||||
height_new = makeEven(self.attach_window.winfo_height())
|
||||
self.geometry("{}x{}+{}+{}".format(width_new, height_new, x_pos, y_pos))
|
||||
|
||||
self.lift()
|
||||
1
vrct_gui/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .vrct_gui import vrct_gui
|
||||
40
vrct_gui/_changeConfigWindowWidgetsStatus.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from customtkinter import CTkImage
|
||||
|
||||
def _changeConfigWindowWidgetsStatus(config_window, settings, view_variable, status, target_names):
|
||||
# if target_names == "All":
|
||||
# target_names = []
|
||||
|
||||
|
||||
def disableOptionmenuWidget(target_widget):
|
||||
target_widget.label_widget.configure(text_color=settings.ctm.LABELS_TEXT_DISABLED_COLOR)
|
||||
if target_widget.desc_widget is not None:
|
||||
target_widget.desc_widget.configure(text_color=settings.ctm.LABELS_TEXT_DISABLED_COLOR)
|
||||
target_widget.optionmenu_label_widget.configure(text_color=settings.ctm.LABELS_TEXT_DISABLED_COLOR)
|
||||
target_widget.optionmenu_img_widget.configure(image=CTkImage(settings.image_file.ARROW_LEFT_DISABLED.rotate(90), size=settings.uism.SB__OPTIONMENU_IMG_SIZE))
|
||||
target_widget.optionmenu_box.unbindFunction()
|
||||
target_widget.optionmenu_box.configure(cursor="")
|
||||
|
||||
|
||||
for target_name in target_names:
|
||||
match target_name:
|
||||
case "sb__optionmenu_mic_host":
|
||||
if status == "disabled":
|
||||
target_widget = config_window.sb__widgets["sb__optionmenu_mic_host"]
|
||||
disableOptionmenuWidget(target_widget)
|
||||
|
||||
case "sb__optionmenu_mic_device":
|
||||
if status == "disabled":
|
||||
target_widget = config_window.sb__widgets["sb__optionmenu_mic_device"]
|
||||
disableOptionmenuWidget(target_widget)
|
||||
|
||||
case "sb__optionmenu_appearance_theme":
|
||||
if status == "disabled":
|
||||
target_widget = config_window.sb__widgets["sb__optionmenu_appearance_theme"]
|
||||
disableOptionmenuWidget(target_widget)
|
||||
|
||||
case _:
|
||||
raise ValueError(f"No matching case for target_name: {target_name}")
|
||||
|
||||
|
||||
|
||||
config_window.update()
|
||||
158
vrct_gui/_changeMainWindowWidgetsStatus.py
Normal file
@@ -0,0 +1,158 @@
|
||||
from customtkinter import CTkImage
|
||||
hold_state_list=[]
|
||||
def _changeMainWindowWidgetsStatus(vrct_gui, settings, view_variable, status, target_names:list, to_hold_state:bool=False):
|
||||
global hold_state_list
|
||||
if target_names == "All":
|
||||
target_names = ["translation_switch", "transcription_send_switch", "transcription_receive_switch", "foreground_switch", "quick_language_settings", "config_button", "minimize_sidebar_button", "entry_message_box"]
|
||||
|
||||
|
||||
for item in hold_state_list:
|
||||
if item in target_names:
|
||||
target_names.remove(item)
|
||||
|
||||
|
||||
def update_switch_status(
|
||||
widget_frame,
|
||||
widget_label,
|
||||
widget_switch_box,
|
||||
widget_selected_mark,
|
||||
widget_compact_mode_icon,
|
||||
icon_name,
|
||||
disabled_icon_name,
|
||||
):
|
||||
|
||||
if status == "disabled":
|
||||
widget_frame.configure(cursor="")
|
||||
widget_label.configure(text_color=settings.ctm.SF__TEXT_DISABLED_COLOR)
|
||||
widget_switch_box.configure(state="disabled", progress_color=settings.ctm.SF__SWITCH_BOX_DISABLE_BG_COLOR)
|
||||
widget_selected_mark.configure(fg_color=settings.ctm.SF__SELECTED_MARK_DISABLE_BG_COLOR)
|
||||
icon_file = disabled_icon_name
|
||||
elif status == "normal":
|
||||
widget_frame.configure(cursor="hand2")
|
||||
widget_label.configure(text_color=settings.ctm.LABELS_TEXT_COLOR)
|
||||
widget_switch_box.configure(state="normal", progress_color=settings.ctm.SF__SWITCH_BOX_ACTIVE_BG_COLOR)
|
||||
widget_selected_mark.configure(fg_color=settings.ctm.SF__SELECTED_MARK_ACTIVE_BG_COLOR)
|
||||
icon_file = icon_name
|
||||
|
||||
image = CTkImage(icon_file, size=settings.uism.SF__COMPACT_MODE_IMAGE_SIZE)
|
||||
widget_compact_mode_icon.configure(image=image)
|
||||
|
||||
|
||||
|
||||
|
||||
for target_name in target_names:
|
||||
match target_name:
|
||||
case "translation_switch":
|
||||
update_switch_status(
|
||||
widget_frame=vrct_gui.translation_frame,
|
||||
widget_label=vrct_gui.label_translation,
|
||||
widget_switch_box=vrct_gui.translation_switch_box,
|
||||
widget_selected_mark=vrct_gui.translation_selected_mark,
|
||||
widget_compact_mode_icon=vrct_gui.translation_compact_mode_icon,
|
||||
icon_name=settings.image_file.TRANSLATION_ICON,
|
||||
disabled_icon_name=settings.image_file.TRANSLATION_ICON_DISABLED
|
||||
)
|
||||
case "transcription_send_switch":
|
||||
update_switch_status(
|
||||
widget_frame=vrct_gui.transcription_send_frame,
|
||||
widget_label=vrct_gui.label_transcription_send,
|
||||
widget_switch_box=vrct_gui.transcription_send_switch_box,
|
||||
widget_selected_mark=vrct_gui.transcription_send_selected_mark,
|
||||
widget_compact_mode_icon=vrct_gui.transcription_send_compact_mode_icon,
|
||||
icon_name=settings.image_file.MIC_ICON,
|
||||
disabled_icon_name=settings.image_file.MIC_ICON_DISABLED
|
||||
)
|
||||
case "transcription_receive_switch":
|
||||
update_switch_status(
|
||||
widget_frame=vrct_gui.transcription_receive_frame,
|
||||
widget_label=vrct_gui.label_transcription_receive,
|
||||
widget_switch_box=vrct_gui.transcription_receive_switch_box,
|
||||
widget_selected_mark=vrct_gui.transcription_receive_selected_mark,
|
||||
widget_compact_mode_icon=vrct_gui.transcription_receive_compact_mode_icon,
|
||||
icon_name=settings.image_file.HEADPHONES_ICON,
|
||||
disabled_icon_name=settings.image_file.HEADPHONES_ICON_DISABLED
|
||||
)
|
||||
case "foreground_switch":
|
||||
update_switch_status(
|
||||
widget_frame=vrct_gui.foreground_frame,
|
||||
widget_label=vrct_gui.label_foreground,
|
||||
widget_switch_box=vrct_gui.foreground_switch_box,
|
||||
widget_selected_mark=vrct_gui.foreground_selected_mark,
|
||||
widget_compact_mode_icon=vrct_gui.foreground_compact_mode_icon,
|
||||
icon_name=settings.image_file.FOREGROUND_ICON,
|
||||
disabled_icon_name=settings.image_file.FOREGROUND_ICON_DISABLED
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
case "quick_language_settings":
|
||||
if status == "disabled":
|
||||
vrct_gui.sls__container_title.configure(text_color=settings.ctm.SF__TEXT_DISABLED_COLOR)
|
||||
vrct_gui.sls__title_text_your_language.configure(text_color=settings.ctm.SF__TEXT_DISABLED_COLOR)
|
||||
vrct_gui.sls__title_text_target_language.configure(text_color=settings.ctm.SF__TEXT_DISABLED_COLOR)
|
||||
if view_variable.IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE is False:
|
||||
vrct_gui.current_active_preset_tab.children["!ctklabel"].configure(text_color=settings.ctm.SLS__PRESETS_TAB_ACTIVE_TEXT_COLOR_PASSIVE)
|
||||
|
||||
|
||||
elif status == "normal":
|
||||
vrct_gui.sls__container_title.configure(text_color=settings.ctm.LABELS_TEXT_COLOR)
|
||||
vrct_gui.sls__title_text_your_language.configure(text_color=settings.ctm.LABELS_TEXT_COLOR)
|
||||
vrct_gui.sls__title_text_target_language.configure(text_color=settings.ctm.LABELS_TEXT_COLOR)
|
||||
if view_variable.IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE is False:
|
||||
vrct_gui.current_active_preset_tab.children["!ctklabel"].configure(text_color=settings.ctm.SLS__PRESETS_TAB_ACTIVE_TEXT_COLOR)
|
||||
vrct_gui.current_active_preset_tab.children["!ctklabel"].configure(text_color=settings.ctm.SLS__PRESETS_TAB_ACTIVE_TEXT_COLOR)
|
||||
|
||||
|
||||
|
||||
case "config_button":
|
||||
if status == "disabled":
|
||||
vrct_gui.sidebar_config_button_wrapper.configure(cursor="")
|
||||
vrct_gui.sidebar_config_button.configure(
|
||||
image=CTkImage(settings.image_file.CONFIGURATION_ICON_DISABLED, size=settings.uism.SF__COMPACT_MODE_IMAGE_SIZE),
|
||||
)
|
||||
elif status == "normal":
|
||||
vrct_gui.sidebar_config_button_wrapper.configure(cursor="hand2")
|
||||
vrct_gui.sidebar_config_button.configure(
|
||||
image=CTkImage(settings.image_file.CONFIGURATION_ICON, size=settings.uism.SF__COMPACT_MODE_IMAGE_SIZE),
|
||||
)
|
||||
|
||||
|
||||
case "minimize_sidebar_button":
|
||||
MINIMIZE_SIDEBAR_IMAGE_SIZE = vrct_gui.minimize_sidebar_button__for_opening.cget("image").cget("size")
|
||||
if status == "disabled":
|
||||
vrct_gui.minimize_sidebar_button_container__for_opening.configure(cursor="")
|
||||
vrct_gui.minimize_sidebar_button_container__for_closing.configure(cursor="")
|
||||
|
||||
image_file__for_opening = CTkImage((settings.image_file.ARROW_LEFT_DISABLED).rotate(180), size=MINIMIZE_SIDEBAR_IMAGE_SIZE)
|
||||
image_file__for_closing = CTkImage((settings.image_file.ARROW_LEFT_DISABLED), size=MINIMIZE_SIDEBAR_IMAGE_SIZE)
|
||||
|
||||
elif status == "normal":
|
||||
vrct_gui.minimize_sidebar_button_container__for_opening.configure(cursor="hand2")
|
||||
vrct_gui.minimize_sidebar_button_container__for_closing.configure(cursor="hand2")
|
||||
|
||||
image_file__for_opening = CTkImage((settings.image_file.ARROW_LEFT).rotate(180), size=MINIMIZE_SIDEBAR_IMAGE_SIZE)
|
||||
image_file__for_closing = CTkImage((settings.image_file.ARROW_LEFT), size=MINIMIZE_SIDEBAR_IMAGE_SIZE)
|
||||
vrct_gui.minimize_sidebar_button__for_opening.configure(image=image_file__for_opening)
|
||||
vrct_gui.minimize_sidebar_button__for_closing.configure(image=image_file__for_closing)
|
||||
|
||||
|
||||
case "entry_message_box":
|
||||
if status == "disabled":
|
||||
vrct_gui.entry_message_box.configure(state="disabled", placeholder_text_color=settings.ctm.TEXTBOX_ENTRY_PLACEHOLDER_DISABLED_COLOR, text_color=settings.ctm.TEXTBOX_ENTRY_TEXT_DISABLED_COLOR)
|
||||
elif status == "normal":
|
||||
vrct_gui.entry_message_box.configure(state="normal", placeholder_text_color=settings.ctm.TEXTBOX_ENTRY_PLACEHOLDER_COLOR, text_color=settings.ctm.TEXTBOX_ENTRY_TEXT_COLOR)
|
||||
|
||||
|
||||
case _:
|
||||
raise ValueError(f"No matching case for target_name: {target_name}")
|
||||
|
||||
|
||||
if to_hold_state is True:
|
||||
for item in target_names:
|
||||
if item not in hold_state_list:
|
||||
hold_state_list.append(item)
|
||||
|
||||
vrct_gui.update()
|
||||
98
vrct_gui/_printToTextbox.py
Normal file
@@ -0,0 +1,98 @@
|
||||
from datetime import datetime
|
||||
from customtkinter import CTkFont
|
||||
|
||||
def _printToTextbox(vrct_gui,
|
||||
settings,
|
||||
target_type,
|
||||
original_message=None,
|
||||
translated_message=None,
|
||||
tags=None,
|
||||
disable_print_to_textbox_all:bool=False,
|
||||
):
|
||||
|
||||
now_raw_data = datetime.now()
|
||||
# now = now_raw_data.strftime("%H:%M:%S")
|
||||
now_hm = now_raw_data.strftime("%H:%M")
|
||||
# set target textbox widget
|
||||
|
||||
is_only_one_message = True if original_message is None or translated_message is None or translated_message == "" else False
|
||||
|
||||
match (target_type):
|
||||
case "SYSTEM":
|
||||
target_textbox = vrct_gui.textbox_system
|
||||
case "SENT":
|
||||
target_textbox = vrct_gui.textbox_sent
|
||||
case "RECEIVED":
|
||||
target_textbox = vrct_gui.textbox_received
|
||||
case (_):
|
||||
raise ValueError(f"No matching case for target_type: {target_type}")
|
||||
|
||||
|
||||
def printEachTextbox(target_textbox):
|
||||
target_textbox.tag_config("JUSTIFY_CENTER", justify="center")
|
||||
target_textbox.tag_config("JUSTIFY_RIGHT", justify="right")
|
||||
target_textbox.tag_config("JUSTIFY_LEFT", justify="left")
|
||||
|
||||
# common tag settings
|
||||
# target_textbox._textbox.tag_configure("START", spacing1=16)
|
||||
target_textbox._textbox.tag_configure("LABEL", font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TEXTBOX_FONT_SIZE__LABEL, weight="normal"))
|
||||
target_textbox._textbox.tag_configure("TIMESTAMP", font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TEXTBOX_FONT_SIZE__TIMESTAMP, weight="normal"), foreground=settings.ctm.TEXTBOX_TIMESTAMP_TEXT_COLOR)
|
||||
target_textbox._textbox.tag_configure("SECONDARY_TEXT_FONT", font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TEXTBOX_FONT_SIZE__SECONDARY_TEXT_FONT, weight="normal"))
|
||||
target_textbox._textbox.tag_configure("MAIN_TEXT_FONT", font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TEXTBOX_FONT_SIZE__MAIN_TEXT_FONT, weight="normal"))
|
||||
|
||||
# System Tag Settings
|
||||
target_textbox.tag_config("FIRST_INSERT_SPACING", spacing1=settings.uism.TEXTBOX_FIRST_INSERT_SPACING)
|
||||
target_textbox.tag_config("SYSTEM_TAG", foreground=settings.ctm.TEXTBOX_SYSTEM_TAG_TEXT_COLOR)
|
||||
target_textbox.tag_config("SYSTEM_TEXT", foreground=settings.ctm.TEXTBOX_TEXT_SUB_COLOR)
|
||||
target_textbox._textbox.tag_configure("SYSTEM_TEXT_FONT", font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TEXTBOX_FONT_SIZE__SYSTEM_TEXT_FONT, weight="normal"))
|
||||
|
||||
# Sent Tag Settings
|
||||
target_textbox.tag_config("SENT_TAG", foreground=settings.ctm.TEXTBOX_SENT_TAG_TEXT_COLOR)
|
||||
target_textbox.tag_config("SENT_TEXT", foreground=settings.ctm.TEXTBOX_TEXT_COLOR)
|
||||
target_textbox.tag_config("SENT_SUB_TEXT", foreground=settings.ctm.TEXTBOX_TEXT_SUB_COLOR)
|
||||
|
||||
# Received Tag Settings
|
||||
target_textbox.tag_config("RECEIVED_TAG", foreground=settings.ctm.TEXTBOX_RECEIVED_TAG_TEXT_COLOR)
|
||||
target_textbox.tag_config("RECEIVED_TEXT", foreground=settings.ctm.TEXTBOX_TEXT_COLOR)
|
||||
target_textbox.tag_config("RECEIVED_SUB_TEXT", foreground=settings.ctm.TEXTBOX_TEXT_SUB_COLOR)
|
||||
|
||||
FAKE_MARGIN = " "
|
||||
# insert
|
||||
target_textbox.configure(state="normal")
|
||||
target_textbox.insert("end", "\n")
|
||||
match (target_type):
|
||||
case "SYSTEM":
|
||||
target_textbox.insert("end", "System", ("SYSTEM_TAG", "FIRST_INSERT_SPACING", "JUSTIFY_CENTER", "LABEL"))
|
||||
target_textbox.insert("end", FAKE_MARGIN+original_message+FAKE_MARGIN, ("SYSTEM_TEXT", "SYSTEM_TEXT_FONT", "JUSTIFY_CENTER"))
|
||||
target_textbox.insert("end", now_hm, ("TIMESTAMP", "JUSTIFY_CENTER"))
|
||||
|
||||
case "SENT":
|
||||
target_textbox.insert("end", now_hm, ("TIMESTAMP", "FIRST_INSERT_SPACING", "JUSTIFY_RIGHT"))
|
||||
target_textbox.insert("end", FAKE_MARGIN+"Sent", ("SENT_TAG", "LABEL"))
|
||||
target_textbox.insert("end", "\n")
|
||||
if is_only_one_message is False:
|
||||
target_textbox.insert("end", original_message, ("SENT_SUB_TEXT", "SECONDARY_TEXT_FONT", "JUSTIFY_RIGHT"))
|
||||
target_textbox.insert("end", "\n")
|
||||
target_textbox.insert("end", translated_message, ("SENT_TEXT", "MAIN_TEXT_FONT", "JUSTIFY_RIGHT"))
|
||||
else:
|
||||
target_textbox.insert("end", original_message, ("SENT_TEXT", "MAIN_TEXT_FONT", "JUSTIFY_RIGHT"))
|
||||
|
||||
case "RECEIVED":
|
||||
target_textbox.insert("end", "Received", ("RECEIVED_TAG", "FIRST_INSERT_SPACING", "JUSTIFY_LEFT", "LABEL"))
|
||||
target_textbox.insert("end", FAKE_MARGIN+now_hm, ("TIMESTAMP"))
|
||||
if is_only_one_message is False:
|
||||
target_textbox.insert("end", "\n")
|
||||
target_textbox.insert("end", original_message, ("RECEIVED_SUB_TEXT", "SECONDARY_TEXT_FONT"))
|
||||
target_textbox.insert("end", "\n")
|
||||
target_textbox.insert("end", translated_message, ("RECEIVED_TEXT", "MAIN_TEXT_FONT", "JUSTIFY_LEFT"))
|
||||
else:
|
||||
target_textbox.insert("end", "\n")
|
||||
target_textbox.insert("end", original_message, ("RECEIVED_TEXT", "MAIN_TEXT_FONT", "JUSTIFY_LEFT"))
|
||||
|
||||
target_textbox.configure(state="disabled")
|
||||
target_textbox.see("end")
|
||||
|
||||
printEachTextbox(target_textbox)
|
||||
|
||||
# To automatically print the same log to the textbox_all widget as well.
|
||||
if disable_print_to_textbox_all is not True: printEachTextbox(vrct_gui.textbox_all)
|
||||
66
vrct_gui/config_window/ConfigWindow.py
Normal file
@@ -0,0 +1,66 @@
|
||||
from .widgets import createConfigWindowTitle, createSideMenuAndSettingsBoxContainers, createSettingBoxTopBar
|
||||
|
||||
|
||||
from customtkinter import CTkToplevel, CTkFrame, CTkLabel, CTkFont
|
||||
|
||||
from ..ui_utils import getImagePath, getLatestWidth
|
||||
from utils import isEven
|
||||
|
||||
class ConfigWindow(CTkToplevel):
|
||||
def __init__(self, vrct_gui, settings, view_variable):
|
||||
super().__init__()
|
||||
self.withdraw()
|
||||
|
||||
self.settings = settings
|
||||
self._view_variable = view_variable
|
||||
|
||||
# configure window
|
||||
self.after(200, lambda: self.iconbitmap(getImagePath("vrct_logo_mark_black.ico")))
|
||||
self.geometry(f"{self.settings.uism.DEFAULT_WIDTH}x{self.settings.uism.DEFAULT_HEIGHT}")
|
||||
|
||||
|
||||
self.configure(fg_color=self.settings.ctm.MAIN_BG_COLOR)
|
||||
self.protocol("WM_DELETE_WINDOW", self._view_variable.CALLBACK_CLICKED_CLOSE_CONFIG_WINDOW_BUTTON)
|
||||
|
||||
|
||||
self.title(self._view_variable.VAR_CONFIG_WINDOW_TITLE.get())
|
||||
# When the configuration window's compact mode is turned on, it will call `grid_remove()` on each widget appended to this array. In the opposite case, `grid()` will be called.
|
||||
self.additional_widgets = []
|
||||
|
||||
self.sb__widgets = {}
|
||||
|
||||
createConfigWindowTitle(config_window=self, settings=self.settings, view_variable=self._view_variable)
|
||||
|
||||
createSettingBoxTopBar(config_window=self, settings=self.settings, view_variable=self._view_variable)
|
||||
|
||||
createSideMenuAndSettingsBoxContainers(config_window=self, settings=self.settings, view_variable=self._view_variable)
|
||||
|
||||
# for fixing 1px bug
|
||||
l_width = getLatestWidth(self.side_menu_bg_container)
|
||||
if isEven(l_width) is False:
|
||||
self.side_menu_bg_container.grid_columnconfigure(0, weight=0, minsize=l_width+1)
|
||||
|
||||
# for fixing 1px bug
|
||||
self.side_menu_bg_container.grid_rowconfigure(2, weight=1)
|
||||
sls__box_optionmenu_wrapper_fix_1px_bug = CTkFrame(self.side_menu_bg_container, corner_radius=0, width=0, height=0)
|
||||
sls__box_optionmenu_wrapper_fix_1px_bug.grid(row=3, column=0, sticky="sew")
|
||||
|
||||
# for fixing 1px bug
|
||||
l_width = getLatestWidth(self.side_menu_bg_container)
|
||||
|
||||
|
||||
|
||||
# VRCT Now Version Label(Tmp)
|
||||
version_label = CTkLabel(
|
||||
self.side_menu_bg_container,
|
||||
textvariable=self._view_variable.VAR_VERSION,
|
||||
height=0,
|
||||
corner_radius=0,
|
||||
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.NOW_VERSION_FONT_SIZE, weight="normal"),
|
||||
anchor="w",
|
||||
text_color=self.settings.ctm.NOW_VERSION_TEXT_COLOR,
|
||||
)
|
||||
version_label.place(relx=0.05, rely=0.99, anchor="sw")
|
||||
|
||||
|
||||
self.bind_all("<Button-1>", lambda event: event.widget.focus_set(), "+")
|
||||
1
vrct_gui/config_window/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .ConfigWindow import ConfigWindow
|
||||
4
vrct_gui/config_window/widgets/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from .createConfigWindowTitle import createConfigWindowTitle
|
||||
from .createSettingBoxTopBar import createSettingBoxTopBar
|
||||
|
||||
from .createSideMenuAndSettingsBoxContainers import createSideMenuAndSettingsBoxContainers
|
||||
36
vrct_gui/config_window/widgets/createConfigWindowTitle.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from customtkinter import CTkFont, CTkFrame, CTkLabel, CTkImage
|
||||
|
||||
def createConfigWindowTitle(config_window, settings, view_variable):
|
||||
|
||||
config_window.grid_columnconfigure(0, weight=0, minsize=settings.uism.TOP_BAR_SIDE_AREA_MIN_WIDTH)
|
||||
config_window.grid_rowconfigure(0, weight=0, minsize=settings.uism.TOP_BAR__MIN_HEIGHT)
|
||||
config_window.side_menu_config_window_title_logo_frame = CTkFrame(config_window, corner_radius=0, fg_color=settings.ctm.TOP_BAR_BG_COLOR, width=0, height=0)
|
||||
config_window.side_menu_config_window_title_logo_frame.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
config_window.side_menu_config_window_title_logo_frame.grid_rowconfigure(0,weight=1)
|
||||
config_window.side_menu_config_window_title_logo_frame.grid_columnconfigure(0,weight=1)
|
||||
config_window.side_menu_config_window_title_logo_wrapper = CTkFrame(config_window.side_menu_config_window_title_logo_frame, corner_radius=0, fg_color=settings.ctm.TOP_BAR_BG_COLOR, width=0, height=0)
|
||||
config_window.side_menu_config_window_title_logo_wrapper.grid(row=0, column=0, padx=settings.uism.TOP_BAR_SIDE__TITLE_PADX, pady=settings.uism.TOP_BAR__IPADY, sticky="nsew")
|
||||
|
||||
|
||||
|
||||
|
||||
config_window.side_menu_config_window_title_logo_wrapper.grid_rowconfigure(0,weight=1)
|
||||
config_window.side_menu_config_window_title = CTkLabel(
|
||||
config_window.side_menu_config_window_title_logo_frame,
|
||||
textvariable=view_variable.VAR_CONFIG_WINDOW_TITLE,
|
||||
height=0,
|
||||
anchor="w",
|
||||
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TOP_BAR_SIDE__CONFIG_TITLE_FONT_SIZE, weight="bold"),
|
||||
text_color=settings.ctm.LABELS_TEXT_COLOR,
|
||||
)
|
||||
config_window.side_menu_config_window_title.place(relx=0.255, rely=0.5, anchor="w")
|
||||
|
||||
config_window.side_menu_config_window_title_logo = CTkLabel(
|
||||
config_window.side_menu_config_window_title_logo_frame,
|
||||
text=None,
|
||||
height=0,
|
||||
anchor="w",
|
||||
image=CTkImage(settings.image_file.VRCT_LOGO_MARK, size=settings.uism.TOP_BAR_SIDE__CONFIG_LOGO_MARK_SIZE),
|
||||
)
|
||||
config_window.side_menu_config_window_title_logo.place(relx=0.08, rely=0.58, anchor="w")
|
||||
@@ -0,0 +1 @@
|
||||
from .createSettingBoxTopBar import createSettingBoxTopBar
|
||||
@@ -0,0 +1,37 @@
|
||||
from customtkinter import CTkFont, CTkFrame, CTkLabel
|
||||
from utils import callFunctionIfCallable
|
||||
from ....ui_utils import bindButtonFunctionAndColor
|
||||
|
||||
def _createRestartButton(parent_widget, config_window, settings, view_variable, column_num):
|
||||
|
||||
parent_widget.grid_columnconfigure(0, weight=1)
|
||||
config_window.restart_button_container = CTkFrame(parent_widget, corner_radius=settings.uism.RESTART_BUTTON_CORNER_RADIUS, fg_color=settings.ctm.RESTART_BUTTON_BG_COLOR, width=0, height=0, cursor="hand2")
|
||||
config_window.restart_button_container.grid(row=0, column=column_num, padx=settings.uism.RESTART_BUTTON_PADX, sticky="ew")
|
||||
|
||||
|
||||
config_window.restart_button_container.grid_rowconfigure(0, weight=1)
|
||||
config_window.restart_button_label = CTkLabel(
|
||||
config_window.restart_button_container,
|
||||
height=0,
|
||||
textvariable=view_variable.VAR_CONFIG_WINDOW_RESTART_BUTTON_LABEL,
|
||||
anchor="w",
|
||||
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.RESTART_BUTTON_LABEL_FONT_SIZE, weight="normal"),
|
||||
text_color=settings.ctm.LABELS_TEXT_COLOR
|
||||
)
|
||||
config_window.restart_button_label.grid(row=0, column=0, padx=settings.uism.RESTART_BUTTON_IPADX, pady=settings.uism.RESTART_BUTTON_IPADY)
|
||||
|
||||
|
||||
|
||||
bindButtonFunctionAndColor(
|
||||
target_widgets=[
|
||||
config_window.restart_button_container,
|
||||
config_window.restart_button_label,
|
||||
],
|
||||
enter_color=settings.ctm.RESTART_BUTTON_HOVERED_BG_COLOR,
|
||||
leave_color=settings.ctm.RESTART_BUTTON_BG_COLOR,
|
||||
clicked_color=settings.ctm.RESTART_BUTTON_CLICKED_BG_COLOR,
|
||||
buttonReleasedFunction=lambda _e: callFunctionIfCallable(view_variable.CALLBACK_RESTART_SOFTWARE),
|
||||
)
|
||||
|
||||
|
||||
config_window.restart_button_container.grid_remove()
|
||||
@@ -0,0 +1,66 @@
|
||||
from customtkinter import CTkFont, CTkFrame, CTkLabel, CTkSwitch
|
||||
|
||||
def _createSettingBoxCompactModeButton(parent_widget, config_window, settings, view_variable, column_num):
|
||||
|
||||
def switchConfigWindowCompactMode():
|
||||
if config_window.setting_box_compact_mode_switch_box.get() is True:
|
||||
if callable(view_variable.CALLBACK_ENABLE_CONFIG_WINDOW_COMPACT_MODE) is True:
|
||||
view_variable.CALLBACK_ENABLE_CONFIG_WINDOW_COMPACT_MODE()
|
||||
else:
|
||||
if callable(view_variable.CALLBACK_DISABLE_CONFIG_WINDOW_COMPACT_MODE) is True:
|
||||
view_variable.CALLBACK_DISABLE_CONFIG_WINDOW_COMPACT_MODE()
|
||||
|
||||
|
||||
|
||||
config_window.setting_box_compact_mode_button_container = CTkFrame(parent_widget, corner_radius=0, fg_color=settings.ctm.TOP_BAR_BG_COLOR, width=0, height=0)
|
||||
config_window.setting_box_compact_mode_button_container.grid(row=0, column=column_num, padx=settings.uism.COMPACT_MODE_PADX, sticky="nse")
|
||||
|
||||
|
||||
|
||||
config_window.setting_box_compact_mode_button_container.grid_rowconfigure((0,2), weight=1)
|
||||
config_window.setting_box_compact_mode_button_container = CTkFrame(config_window.setting_box_compact_mode_button_container, corner_radius=0, fg_color=settings.ctm.TOP_BAR_BG_COLOR, width=0, height=0)
|
||||
config_window.setting_box_compact_mode_button_container.grid(row=1, column=0, sticky="nsew")
|
||||
|
||||
|
||||
config_window.setting_box_compact_mode_button_container.grid_rowconfigure(0, weight=1)
|
||||
config_window.setting_box_compact_mode_label = CTkLabel(
|
||||
config_window.setting_box_compact_mode_button_container,
|
||||
height=0,
|
||||
# text="Compact Mode",
|
||||
textvariable=view_variable.VAR_CONFIG_WINDOW_COMPACT_MODE_LABEL,
|
||||
anchor="w",
|
||||
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.COMPACT_MODE_LABEL_FONT_SIZE, weight="normal"),
|
||||
text_color=settings.ctm.LABELS_TEXT_COLOR
|
||||
)
|
||||
config_window.setting_box_compact_mode_label.grid(row=0, column=0, padx=settings.uism.COMPACT_MODE_LABEL_PADX)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
config_window.setting_box_compact_mode_switch_frame = CTkFrame(config_window.setting_box_compact_mode_button_container, corner_radius=0, width=0, height=0, fg_color=settings.ctm.TOP_BAR_BG_COLOR)
|
||||
config_window.setting_box_compact_mode_switch_frame.grid(row=0, column=1, padx=0, sticky="e")
|
||||
|
||||
config_window.setting_box_compact_mode_switch_box = CTkSwitch(
|
||||
config_window.setting_box_compact_mode_switch_frame,
|
||||
text=None,
|
||||
height=0,
|
||||
width=0,
|
||||
# corner_radius=0,
|
||||
border_width=0,
|
||||
switch_width=settings.uism.COMPACT_MODE_SWITCH_WIDTH,
|
||||
switch_height=settings.uism.COMPACT_MODE_SWITCH_HEIGHT,
|
||||
onvalue=True,
|
||||
offvalue=False,
|
||||
command=switchConfigWindowCompactMode,
|
||||
fg_color=settings.ctm.COMPACT_MODE_SWITCH_BOX_BG_COLOR,
|
||||
# bg_color="red",
|
||||
progress_color=settings.ctm.COMPACT_MODE_SWITCH_BOX_ACTIVE_BG_COLOR,
|
||||
button_color=settings.ctm.COMPACT_MODE_SWITCH_BOX_BUTTON_COLOR,
|
||||
button_hover_color=settings.ctm.COMPACT_MODE_SWITCH_BOX_BUTTON_HOVERED_COLOR,
|
||||
)
|
||||
|
||||
config_window.setting_box_compact_mode_switch_box.grid(row=0, column=0)
|
||||
@@ -0,0 +1,24 @@
|
||||
from customtkinter import CTkFont, CTkFrame, CTkLabel
|
||||
|
||||
def _createSettingBoxTitle(parent_widget, config_window, settings, view_variable, column_num):
|
||||
|
||||
parent_widget.grid_columnconfigure(0, weight=1)
|
||||
config_window.main_current_active_config_title_container = CTkFrame(parent_widget, corner_radius=0, fg_color=settings.ctm.TOP_BAR_BG_COLOR, width=0, height=0)
|
||||
config_window.main_current_active_config_title_container.grid(row=0, column=column_num, sticky="nsew")
|
||||
|
||||
|
||||
config_window.main_current_active_config_title_container.grid_rowconfigure(0, weight=1)
|
||||
config_window.main_current_active_config_title = CTkLabel(
|
||||
config_window.main_current_active_config_title_container,
|
||||
height=0,
|
||||
textvariable=view_variable.VAR_CURRENT_ACTIVE_CONFIG_TITLE,
|
||||
anchor="w",
|
||||
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TOP_BAR_MAIN__TITLE_FONT_SIZE, weight="bold"),
|
||||
text_color=settings.ctm.LABELS_TEXT_COLOR
|
||||
)
|
||||
config_window.main_current_active_config_title.grid(row=0, column=0, padx=0, pady=settings.uism.TOP_BAR__IPADY)
|
||||
|
||||
|
||||
# for fixing 1px bug
|
||||
sls__box_optionmenu_wrapper_fix_1px_bug = CTkFrame(config_window.main_current_active_config_title, corner_radius=0, width=0, height=0)
|
||||
sls__box_optionmenu_wrapper_fix_1px_bug.grid(row=0, column=column_num, sticky="ns")
|
||||
@@ -0,0 +1,40 @@
|
||||
from customtkinter import CTkFrame
|
||||
|
||||
from ._createSettingBoxTitle import _createSettingBoxTitle
|
||||
from ._createRestartButton import _createRestartButton
|
||||
from ._createSettingBoxCompactModeButton import _createSettingBoxCompactModeButton
|
||||
|
||||
from ....ui_utils import getLatestHeight
|
||||
from utils import isEven
|
||||
|
||||
def createSettingBoxTopBar(config_window, settings, view_variable):
|
||||
|
||||
config_window.grid_columnconfigure(1, weight=1)
|
||||
config_window.setting_box_top_bar = CTkFrame(config_window, corner_radius=0, fg_color=settings.ctm.TOP_BAR_BG_COLOR, width=0, height=0)
|
||||
config_window.setting_box_top_bar.grid(row=0, column=1, sticky="nsew")
|
||||
|
||||
|
||||
config_window.setting_box_top_bar.grid_rowconfigure(0, weight=1)
|
||||
|
||||
column_num=0
|
||||
_createSettingBoxTitle(parent_widget=config_window.setting_box_top_bar, config_window=config_window, settings=settings, view_variable=view_variable, column_num=column_num)
|
||||
column_num+=1
|
||||
|
||||
config_window.setting_box_top_bar.grid_columnconfigure(column_num, weight=1)
|
||||
column_num+=1
|
||||
|
||||
# Restart Button(Tmp)
|
||||
_createRestartButton(parent_widget=config_window.setting_box_top_bar, config_window=config_window, settings=settings, view_variable=view_variable, column_num=column_num)
|
||||
column_num+=1
|
||||
|
||||
_createSettingBoxCompactModeButton(parent_widget=config_window.setting_box_top_bar, config_window=config_window, settings=settings, view_variable=view_variable, column_num=column_num)
|
||||
column_num+=1
|
||||
|
||||
|
||||
l_height = getLatestHeight(config_window.side_menu_config_window_title_logo_frame)
|
||||
if isEven(l_height) is False:
|
||||
config_window.grid_rowconfigure(0, weight=0, minsize=l_height+1)
|
||||
|
||||
# for fixing 1px bug
|
||||
setting_box_top_bar_fix_1px_bug = CTkFrame(config_window.setting_box_top_bar, corner_radius=0, width=0, height=0)
|
||||
setting_box_top_bar_fix_1px_bug.grid(row=0, column=column_num, sticky="nse")
|
||||
@@ -0,0 +1 @@
|
||||
from .createSideMenuAndSettingsBoxContainers import createSideMenuAndSettingsBoxContainers
|
||||
@@ -0,0 +1,107 @@
|
||||
from customtkinter import CTkFont, CTkFrame, CTkLabel
|
||||
|
||||
from ....ui_utils import bindEnterAndLeaveColor, bindButtonPressColor, bindButtonReleaseFunction, switchActiveTabAndPassiveTab, switchTabsColor
|
||||
|
||||
from utils import callFunctionIfCallable
|
||||
|
||||
|
||||
def _addConfigSideMenuItem(config_window, settings, view_variable, side_menu_settings, side_menu_row, all_side_menu_tab_attr_name):
|
||||
|
||||
|
||||
def switchActiveAndPassiveSettingBoxContainerTabsColor(target_active_widget):
|
||||
|
||||
setting_box_container_tabs = []
|
||||
for tab_attr_name in all_side_menu_tab_attr_name:
|
||||
tab_attr = getattr(config_window, tab_attr_name)
|
||||
setting_box_container_tabs.append(tab_attr)
|
||||
|
||||
switchTabsColor(
|
||||
target_widget=target_active_widget,
|
||||
tab_buttons=setting_box_container_tabs,
|
||||
active_bg_color=settings.ctm.SIDE_MENU_LABELS_BG_COLOR,
|
||||
active_text_color=settings.ctm.SIDE_MENU_LABELS_SELECTED_TEXT_COLOR,
|
||||
passive_bg_color=settings.ctm.SIDE_MENU_LABELS_BG_COLOR,
|
||||
passive_text_color=settings.ctm.LABELS_TEXT_COLOR
|
||||
)
|
||||
|
||||
for setting_box_container_tab in setting_box_container_tabs:
|
||||
setting_box_container_tab.children["!ctkframe"].place(relx=-1)
|
||||
|
||||
target_active_widget.children["!ctkframe"].place(relx=0)
|
||||
|
||||
|
||||
|
||||
|
||||
def switchSettingBoxContainerTabFunction(target_active_widget):
|
||||
switchActiveAndPassiveSettingBoxContainerTabsColor(target_active_widget)
|
||||
switchActiveTabAndPassiveTab(target_active_widget, config_window.current_active_side_menu_tab, config_window.current_active_side_menu_tab.passive_function, settings.ctm.SIDE_MENU_LABELS_HOVERED_BG_COLOR, settings.ctm.SIDE_MENU_LABELS_CLICKED_BG_COLOR, settings.ctm.SIDE_MENU_LABELS_BG_COLOR)
|
||||
config_window.current_active_side_menu_tab = target_active_widget
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def switchSettingBoxContainer(target_setting_box_container_attr_name):
|
||||
config_window.current_active_setting_box_container.grid_remove()
|
||||
config_window.current_active_setting_box_container = getattr(config_window, target_setting_box_container_attr_name)
|
||||
config_window.current_active_setting_box_container.grid()
|
||||
|
||||
# Move to the top position when the setting box is switched.
|
||||
config_window.main_setting_box_scrollable_container._parent_canvas.yview_moveto("0")
|
||||
|
||||
|
||||
def switchToTargetSettingBoxContainer(textvariable, target_active_tab_widget_attr_name, target_setting_box_container_attr_name):
|
||||
view_variable.VAR_CURRENT_ACTIVE_CONFIG_TITLE.set(textvariable.get())
|
||||
target_active_tab_widget = getattr(config_window, target_active_tab_widget_attr_name)
|
||||
switchSettingBoxContainerTabFunction(target_active_tab_widget)
|
||||
switchSettingBoxContainer(target_setting_box_container_attr_name)
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SELECTED_SETTING_BOX_TAB, target_active_tab_widget_attr_name)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
side_menu_tab_attr_name = side_menu_settings["side_menu_tab_attr_name"]
|
||||
label_attr_name = side_menu_settings["label_attr_name"]
|
||||
selected_mark_attr_name = side_menu_settings["selected_mark_attr_name"]
|
||||
textvariable = side_menu_settings["textvariable"]
|
||||
setting_box_container_attr_name = side_menu_settings["setting_box_container_settings"]["setting_box_container_attr_name"]
|
||||
command = lambda _e: switchToTargetSettingBoxContainer(
|
||||
textvariable=textvariable,
|
||||
target_active_tab_widget_attr_name=side_menu_tab_attr_name,
|
||||
target_setting_box_container_attr_name=setting_box_container_attr_name,
|
||||
)
|
||||
|
||||
|
||||
# Side menu
|
||||
frame_widget = CTkFrame(config_window.side_menu_container, corner_radius=0, fg_color=settings.ctm.SIDE_MENU_LABELS_BG_COLOR, cursor="hand2", width=0, height=0)
|
||||
setattr(config_window, side_menu_tab_attr_name, frame_widget)
|
||||
|
||||
frame_widget.grid(row=side_menu_row, column=0, pady=(0,1), sticky="ew")
|
||||
frame_widget.grid_columnconfigure(0, weight=1)
|
||||
|
||||
label_widget = CTkLabel(
|
||||
frame_widget,
|
||||
textvariable=textvariable,
|
||||
height=0,
|
||||
corner_radius=0,
|
||||
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.SIDE_MENU_LABELS_FONT_SIZE, weight="normal"),
|
||||
anchor="w",
|
||||
text_color=settings.ctm.LABELS_TEXT_COLOR,
|
||||
)
|
||||
setattr(config_window, label_attr_name, label_widget)
|
||||
|
||||
selected_mark_widget = CTkFrame(frame_widget, corner_radius=0, fg_color=settings.ctm.SIDE_MENU_SELECTED_MARK_ACTIVE_BG_COLOR, width=3, height=0)
|
||||
setattr(config_window, selected_mark_attr_name, selected_mark_widget)
|
||||
|
||||
|
||||
# Arrange
|
||||
selected_mark_widget.place(relx=-1, rely=0.5, relheight=1, anchor="w")
|
||||
label_widget.grid(row=0, column=0, padx=settings.uism.SIDE_MENU_LABELS_IPADX, pady=settings.uism.SIDE_MENU_LABELS_IPADY, sticky="ew")
|
||||
|
||||
bindEnterAndLeaveColor([frame_widget, label_widget], settings.ctm.SIDE_MENU_LABELS_HOVERED_BG_COLOR, settings.ctm.SIDE_MENU_LABELS_BG_COLOR)
|
||||
bindButtonPressColor([frame_widget, label_widget], settings.ctm.SIDE_MENU_LABELS_CLICKED_BG_COLOR, settings.ctm.SIDE_MENU_LABELS_BG_COLOR)
|
||||
|
||||
frame_widget.passive_function = command
|
||||
bindButtonReleaseFunction([frame_widget, label_widget], command)
|
||||
@@ -0,0 +1,63 @@
|
||||
from customtkinter import CTkFont, CTkFrame, CTkLabel
|
||||
|
||||
|
||||
def _createSettingBoxContainer(config_window, settings, view_variable, setting_box_container_settings):
|
||||
|
||||
|
||||
def createSectionTitle(container_widget, var_section_title):
|
||||
|
||||
setting_box_wrapper_section_title = CTkLabel(
|
||||
container_widget,
|
||||
textvariable=var_section_title,
|
||||
anchor="w",
|
||||
height=0,
|
||||
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.SB__SECTION_TITLE_FONT_SIZE, weight="normal"),
|
||||
text_color=settings.ctm.LABELS_TEXT_COLOR
|
||||
)
|
||||
setting_box_wrapper_section_title.place(relx=0, rely=0)
|
||||
|
||||
return container_widget
|
||||
|
||||
|
||||
# Setting box container
|
||||
setting_box_container_widget = CTkFrame(config_window.main_setting_box_bg_wrapper, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=0)
|
||||
setattr(config_window, setting_box_container_settings["setting_box_container_attr_name"], setting_box_container_widget)
|
||||
setting_box_container_widget.grid(row=0, pady=settings.uism.SB__BOTTOM_MARGIN)
|
||||
setting_box_container_widget.grid_remove()
|
||||
|
||||
|
||||
|
||||
setting_box_row=0
|
||||
for setting_box_setting in setting_box_container_settings["setting_boxes"]:
|
||||
# Top-Padding that can be container the section title
|
||||
setting_box_top_padding = CTkFrame(setting_box_container_widget, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=settings.uism.SB__TOP_PADY)
|
||||
setting_box_top_padding.grid(row=setting_box_row, column=0, sticky="ew", padx=0, pady=0)
|
||||
setting_box_top_padding.grid_columnconfigure(0, weight=1)
|
||||
setting_box_row+=1
|
||||
|
||||
if setting_box_setting["var_section_title"] is not None:
|
||||
setting_box_wrapper_section_title = CTkLabel(
|
||||
setting_box_top_padding,
|
||||
textvariable=setting_box_setting["var_section_title"],
|
||||
anchor="w",
|
||||
height=0,
|
||||
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.SB__SECTION_TITLE_FONT_SIZE, weight="normal"),
|
||||
text_color=settings.ctm.LABELS_TEXT_COLOR
|
||||
)
|
||||
setting_box_wrapper_section_title.place(relx=0, rely=0.4, anchor="nw")
|
||||
|
||||
|
||||
setting_box_wrapper = CTkFrame(setting_box_container_widget, fg_color=settings.ctm.SB__WRAPPER_BG_COLOR, corner_radius=0, width=0, height=0)
|
||||
setting_box_wrapper.grid(row=setting_box_row, column=0, sticky="ew")
|
||||
setting_box_wrapper.grid_columnconfigure(0, weight=1)
|
||||
setting_box_row+=1
|
||||
|
||||
|
||||
if setting_box_setting["setting_box"] is not None:
|
||||
setting_box_setting["setting_box"](
|
||||
setting_box_wrapper=setting_box_wrapper,
|
||||
config_window=config_window,
|
||||
settings=settings,
|
||||
view_variable=view_variable,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
from customtkinter import CTkFrame, CTkScrollableFrame
|
||||
|
||||
from ....ui_utils import setDefaultActiveTab, applyUiScalingAndFixTheBugScrollBar
|
||||
|
||||
from ._addConfigSideMenuItem import _addConfigSideMenuItem
|
||||
from ._createSettingBoxContainer import _createSettingBoxContainer
|
||||
|
||||
|
||||
from .setting_box_containers.setting_box_appearance import createSettingBox_Appearance
|
||||
from .setting_box_containers.setting_box_transcription import createSettingBox_Mic, createSettingBox_Speaker
|
||||
from .setting_box_containers.setting_box_others import createSettingBox_Others
|
||||
from .setting_box_containers.setting_box_advanced_settings import createSettingBox_AdvancedSettings
|
||||
from .setting_box_containers.setting_box_translation import createSettingBox_Translation
|
||||
|
||||
|
||||
def createSideMenuAndSettingsBoxContainers(config_window, settings, view_variable):
|
||||
|
||||
# Main container
|
||||
config_window.main_bg_container = CTkFrame(config_window, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=0)
|
||||
config_window.main_bg_container.grid(row=1, column=1, sticky="nsew")
|
||||
|
||||
config_window.main_bg_container.grid_columnconfigure(0, weight=1)
|
||||
config_window.main_bg_container.grid_rowconfigure(0, weight=0)
|
||||
|
||||
|
||||
|
||||
|
||||
# Side menu Base
|
||||
config_window.grid_rowconfigure(1, weight=1)
|
||||
config_window.side_menu_bg_container = CTkFrame(config_window, corner_radius=0, fg_color=settings.ctm.SIDE_MENU_BG_COLOR, width=0, height=0)
|
||||
config_window.side_menu_bg_container.grid(row=1, column=0, sticky="nsew")
|
||||
config_window.side_menu_bg_container.grid_columnconfigure(0, weight=1)
|
||||
|
||||
config_window.side_menu_container = CTkFrame(config_window.side_menu_bg_container, corner_radius=0, fg_color=settings.ctm.SIDE_MENU_LABELS_BG_FOR_FAKE_BORDER_COLOR, width=0, height=0)
|
||||
config_window.side_menu_container.grid(row=0, column=0, padx=settings.uism.TOP_BAR_SIDE__TITLE_PADX, pady=(settings.uism.SIDE_MENU_TOP_PADY, 0), sticky="nsew")
|
||||
|
||||
|
||||
|
||||
# Setting box container
|
||||
config_window.main_bg_container.grid_rowconfigure(1, weight=1)
|
||||
config_window.main_setting_box_scrollable_container = CTkScrollableFrame(config_window.main_bg_container, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR)
|
||||
config_window.main_setting_box_scrollable_container.grid(row=1, column=0, sticky="nsew")
|
||||
|
||||
applyUiScalingAndFixTheBugScrollBar(
|
||||
scrollbar_widget=config_window.main_setting_box_scrollable_container,
|
||||
padx=settings.uism.SCROLLBAR_IPADX,
|
||||
width=settings.uism.SCROLLBAR_WIDTH,
|
||||
)
|
||||
|
||||
|
||||
config_window.main_setting_box_bg_wrapper = CTkFrame(config_window.main_setting_box_scrollable_container, corner_radius=0, width=0, height=0, fg_color=settings.ctm.MAIN_BG_COLOR)
|
||||
config_window.main_setting_box_bg_wrapper.grid(row=0, column=0, pady=settings.uism.SB__BOTTOM_MARGIN, sticky="n")
|
||||
|
||||
|
||||
|
||||
side_menu_and_setting_box_containers_settings = [
|
||||
{
|
||||
"side_menu_tab_attr_name": "side_menu_tab_appearance",
|
||||
"label_attr_name": "label_appearance",
|
||||
"selected_mark_attr_name": "selected_mark_appearance",
|
||||
"textvariable": view_variable.VAR_SIDE_MENU_LABEL_APPEARANCE,
|
||||
"setting_box_container_settings": {
|
||||
"setting_box_container_attr_name": "setting_box_container_appearance",
|
||||
"setting_boxes": [
|
||||
{ "var_section_title": None, "setting_box": createSettingBox_Appearance },
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
"side_menu_tab_attr_name": "side_menu_tab_translation",
|
||||
"label_attr_name": "label_translation",
|
||||
"selected_mark_attr_name": "selected_mark_translation",
|
||||
"textvariable": view_variable.VAR_SIDE_MENU_LABEL_TRANSLATION,
|
||||
"setting_box_container_settings": {
|
||||
"setting_box_container_attr_name": "setting_box_container_translation",
|
||||
"setting_boxes": [
|
||||
{ "var_section_title": None, "setting_box": createSettingBox_Translation },
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
"side_menu_tab_attr_name": "side_menu_tab_transcription",
|
||||
"label_attr_name": "label_transcription",
|
||||
"selected_mark_attr_name": "selected_mark_transcription",
|
||||
"textvariable": view_variable.VAR_SIDE_MENU_LABEL_TRANSCRIPTION,
|
||||
"setting_box_container_settings": {
|
||||
"setting_box_container_attr_name": "setting_box_container_transcription",
|
||||
"setting_boxes": [
|
||||
{
|
||||
"var_section_title": view_variable.VAR_SECOND_TITLE_TRANSCRIPTION_MIC,
|
||||
"setting_box": createSettingBox_Mic
|
||||
},
|
||||
{
|
||||
"var_section_title": view_variable.VAR_SECOND_TITLE_TRANSCRIPTION_SPEAKER,
|
||||
"setting_box": createSettingBox_Speaker
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
"side_menu_tab_attr_name": "side_menu_tab_others",
|
||||
"label_attr_name": "label_others",
|
||||
"selected_mark_attr_name": "selected_mark_others",
|
||||
"textvariable": view_variable.VAR_SIDE_MENU_LABEL_OTHERS,
|
||||
"setting_box_container_settings": {
|
||||
"setting_box_container_attr_name": "setting_box_container_others",
|
||||
"setting_boxes": [
|
||||
{ "var_section_title": None, "setting_box": createSettingBox_Others },
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
"side_menu_tab_attr_name": "side_menu_tab_advanced",
|
||||
"label_attr_name": "label_advanced",
|
||||
"selected_mark_attr_name": "selected_mark_advanced",
|
||||
"textvariable": view_variable.VAR_SIDE_MENU_LABEL_ADVANCED_SETTINGS,
|
||||
"setting_box_container_settings": {
|
||||
"setting_box_container_attr_name": "setting_box_container_advanced",
|
||||
"setting_boxes": [
|
||||
{ "var_section_title": None, "setting_box": createSettingBox_AdvancedSettings },
|
||||
]
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
all_side_menu_tab_attr_name = [item["side_menu_tab_attr_name"] for item in side_menu_and_setting_box_containers_settings]
|
||||
|
||||
side_menu_row=0
|
||||
for sm_and_sbc_setting in side_menu_and_setting_box_containers_settings:
|
||||
_addConfigSideMenuItem(
|
||||
config_window=config_window,
|
||||
settings=settings,
|
||||
view_variable=view_variable,
|
||||
# view_variable=view_variable,
|
||||
side_menu_settings=sm_and_sbc_setting,
|
||||
side_menu_row=side_menu_row,
|
||||
all_side_menu_tab_attr_name=all_side_menu_tab_attr_name,
|
||||
)
|
||||
side_menu_row+=1
|
||||
|
||||
|
||||
_createSettingBoxContainer(
|
||||
config_window=config_window,
|
||||
settings=settings,
|
||||
view_variable=view_variable,
|
||||
setting_box_container_settings=sm_and_sbc_setting["setting_box_container_settings"],
|
||||
|
||||
)
|
||||
|
||||
|
||||
if sm_and_sbc_setting["side_menu_tab_attr_name"] == view_variable.ACTIVE_SETTING_BOX_TAB_ATTR_NAME:
|
||||
# Set default active side menu tab
|
||||
view_variable.VAR_CURRENT_ACTIVE_CONFIG_TITLE.set(sm_and_sbc_setting["textvariable"].get())
|
||||
config_window.current_active_side_menu_tab = getattr(config_window, sm_and_sbc_setting["side_menu_tab_attr_name"])
|
||||
setDefaultActiveTab(
|
||||
active_tab_widget=config_window.current_active_side_menu_tab,
|
||||
active_bg_color=settings.ctm.SIDE_MENU_LABELS_BG_COLOR,
|
||||
active_text_color=settings.ctm.SIDE_MENU_LABELS_SELECTED_TEXT_COLOR
|
||||
)
|
||||
config_window.current_active_side_menu_tab.children["!ctkframe"].place(relx=0)
|
||||
|
||||
# Set default active setting box container
|
||||
config_window.current_active_setting_box_container = getattr(config_window, sm_and_sbc_setting["setting_box_container_settings"]["setting_box_container_attr_name"])
|
||||
config_window.current_active_setting_box_container.grid()
|
||||
@@ -0,0 +1,570 @@
|
||||
from functools import partial
|
||||
from types import SimpleNamespace
|
||||
from typing import Union
|
||||
|
||||
from customtkinter import CTkOptionMenu, CTkFont, CTkFrame, CTkLabel, CTkRadioButton, CTkEntry, CTkSlider, CTkSwitch, CTkCheckBox, CTkProgressBar
|
||||
|
||||
from vrct_gui.ui_utils import createButtonWithImage, getLatestWidth, createOptionMenuBox
|
||||
from vrct_gui import vrct_gui
|
||||
|
||||
SETTING_BOX_COLUMN = 1
|
||||
|
||||
class _SettingBoxGenerator():
|
||||
def __init__(self, parent_widget, config_window, settings, view_variable):
|
||||
self.view_variable = view_variable
|
||||
self.config_window = config_window
|
||||
self.parent_widget = parent_widget
|
||||
self.settings = settings
|
||||
|
||||
self.dropdown_menu_window = vrct_gui.vrct_gui.dropdown_menu_window
|
||||
|
||||
def _createSettingBoxFrame(self, sb__attr_name, for_var_label_text=None, for_var_desc_text=None):
|
||||
self.config_window.sb__widgets[sb__attr_name] = SimpleNamespace()
|
||||
|
||||
setting_box_frame = CTkFrame(self.parent_widget, corner_radius=0, fg_color=self.settings.ctm.SB__BG_COLOR, width=0, height=0)
|
||||
|
||||
# "pady=(0,1)" is for bottom padding. It can be removed(override) when you do like "self.attr_name.grid(row=row, pady=0)"
|
||||
setting_box_frame.grid(column=0, padx=0, pady=self.settings.uism.SB__FAKE_BOTTOM_BORDER_SIZE, sticky="ew")
|
||||
setting_box_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
|
||||
setting_box_frame_wrapper = CTkFrame(setting_box_frame, corner_radius=0, fg_color=self.settings.ctm.SB__BG_COLOR, width=0, height=0)
|
||||
setting_box_frame_wrapper.grid(row=0, column=0, padx=self.settings.uism.SB__IPADX, pady=self.settings.uism.SB__IPADY, sticky="ew")
|
||||
setting_box_frame_wrapper.grid_columnconfigure(0, weight=0, minsize=int(self.settings.uism.MAIN_AREA_MIN_WIDTH / 2))
|
||||
setting_box_frame_wrapper.grid_columnconfigure(2, weight=1, minsize=int(self.settings.uism.MAIN_AREA_MIN_WIDTH / 2))
|
||||
|
||||
setting_box_frame_wrapper_fix_border = CTkFrame(setting_box_frame, corner_radius=0, width=0, height=0)
|
||||
setting_box_frame_wrapper_fix_border.grid(row=1, column=0, sticky="ew")
|
||||
|
||||
setting_box_frame_wrapper_fix_border2 = CTkFrame(setting_box_frame, corner_radius=0, width=0, height=0)
|
||||
setting_box_frame_wrapper_fix_border2.grid(row=0, column=1, sticky="ns")
|
||||
|
||||
if for_var_label_text is not None:
|
||||
self._setSettingBoxLabels(sb__attr_name, setting_box_frame_wrapper, for_var_label_text, for_var_desc_text)
|
||||
|
||||
# setting_box_item_frame = CTkFrame(setting_box_frame_wrapper, corner_radius=0, width=0, height=0, fg_color="red")
|
||||
setting_box_item_frame = CTkFrame(setting_box_frame_wrapper, corner_radius=0, width=0, height=0, fg_color=self.settings.ctm.SB__BG_COLOR)
|
||||
if for_var_label_text is not None:
|
||||
setting_box_item_frame.grid(row=0, column=2, padx=0, sticky="nsew")
|
||||
else:
|
||||
setting_box_item_frame.grid(row=0, columnspan=3, padx=0, sticky="nsew")
|
||||
setting_box_item_frame.grid_rowconfigure((0,2), weight=1)
|
||||
setting_box_item_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
return (setting_box_frame, setting_box_item_frame)
|
||||
|
||||
def _setSettingBoxLabels(self, sb__attr_name, setting_box_frame_wrapper, for_var_label_text, for_var_desc_text=None):
|
||||
|
||||
setting_box_labels_frame = CTkFrame(setting_box_frame_wrapper, corner_radius=0, fg_color=self.settings.ctm.SB__BG_COLOR, width=0, height=0)
|
||||
setting_box_labels_frame.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
|
||||
|
||||
setting_box_labels_frame.grid_rowconfigure((0,3), weight=1)
|
||||
setting_box_label = CTkLabel(
|
||||
setting_box_labels_frame,
|
||||
textvariable=for_var_label_text,
|
||||
anchor="w",
|
||||
height=0,
|
||||
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__LABEL_FONT_SIZE, weight="normal"),
|
||||
text_color=self.settings.ctm.LABELS_TEXT_COLOR
|
||||
)
|
||||
setting_box_label.grid(row=1, column=0, padx=0, pady=0, sticky="ew")
|
||||
self.config_window.sb__widgets[sb__attr_name].label_widget = setting_box_label
|
||||
|
||||
|
||||
if for_var_desc_text is not None:
|
||||
setting_box_desc = CTkLabel(
|
||||
setting_box_labels_frame,
|
||||
textvariable=for_var_desc_text,
|
||||
anchor="w",
|
||||
justify="left",
|
||||
height=0,
|
||||
wraplength=int(self.settings.uism.MAIN_AREA_MIN_WIDTH / 2),
|
||||
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__DESC_FONT_SIZE, weight="normal"),
|
||||
text_color=self.settings.ctm.LABELS_DESC_TEXT_COLOR
|
||||
)
|
||||
setting_box_desc.grid(row=2, column=0, padx=0, pady=(self.settings.uism.SB__DESC_TOP_PADY,0), sticky="ew")
|
||||
self.config_window.additional_widgets.append(setting_box_desc)
|
||||
self.config_window.sb__widgets[sb__attr_name].desc_widget=setting_box_desc
|
||||
else:
|
||||
self.config_window.sb__widgets[sb__attr_name].desc_widget=None
|
||||
|
||||
|
||||
|
||||
|
||||
def createSettingBoxDropdownMenu(
|
||||
self,
|
||||
for_var_label_text, for_var_desc_text,
|
||||
optionmenu_attr_name,
|
||||
command,
|
||||
dropdown_menu_min_width=None,
|
||||
dropdown_menu_values=None,
|
||||
variable=None,
|
||||
):
|
||||
|
||||
(setting_box_frame, setting_box_item_frame) = self._createSettingBoxFrame(optionmenu_attr_name, for_var_label_text, for_var_desc_text)
|
||||
|
||||
def adjustedCommand(value):
|
||||
variable.set(value)
|
||||
command(value)
|
||||
|
||||
|
||||
(option_menu_widget, optionmenu_label_widget, optionmenu_img_widget) = createOptionMenuBox(
|
||||
parent_widget=setting_box_item_frame,
|
||||
optionmenu_bg_color=self.settings.ctm.SB__OPTIONMENU_BG_COLOR,
|
||||
optionmenu_hovered_bg_color=self.settings.ctm.SB__OPTIONMENU_HOVERED_BG_COLOR,
|
||||
optionmenu_clicked_bg_color=self.settings.ctm.SB__OPTIONMENU_CLICKED_BG_COLOR,
|
||||
optionmenu_ipadx=self.settings.uism.SB__OPTIONMENU_IPADX,
|
||||
optionmenu_ipady=self.settings.uism.SB__OPTIONMENU_IPADY,
|
||||
optionmenu_padx_between_img=self.settings.uism.SB__OPTIONMENU_IPADX_BETWEEN_IMG,
|
||||
optionmenu_min_height=self.settings.uism.SB__OPTIONMENU_MIN_HEIGHT,
|
||||
optionmenu_min_width=self.settings.uism.SB__OPTIONMENU_MIN_WIDTH,
|
||||
variable=variable,
|
||||
font_family=self.settings.FONT_FAMILY,
|
||||
font_size=self.settings.uism.SB__OPTION_MENU_FONT_SIZE,
|
||||
text_color=self.settings.ctm.LABELS_TEXT_COLOR,
|
||||
image_file=self.settings.image_file.ARROW_LEFT.rotate(90),
|
||||
image_size=self.settings.uism.SB__OPTIONMENU_IMG_SIZE,
|
||||
optionmenu_clicked_command=lambda _e: self.dropdown_menu_window.show(
|
||||
dropdown_menu_widget_id=optionmenu_attr_name,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
self.config_window.sb__widgets[optionmenu_attr_name].optionmenu_box = option_menu_widget
|
||||
self.config_window.sb__widgets[optionmenu_attr_name].optionmenu_label_widget = optionmenu_label_widget
|
||||
self.config_window.sb__widgets[optionmenu_attr_name].optionmenu_img_widget = optionmenu_img_widget
|
||||
|
||||
|
||||
option_menu_widget.grid(row=1, column=SETTING_BOX_COLUMN, sticky="e")
|
||||
setattr(self.config_window, optionmenu_attr_name, option_menu_widget)
|
||||
|
||||
self.dropdown_menu_window.createDropdownMenuBox(
|
||||
dropdown_menu_widget_id=optionmenu_attr_name,
|
||||
dropdown_menu_values=dropdown_menu_values,
|
||||
command=adjustedCommand,
|
||||
wrapper_widget=self.config_window.main_bg_container,
|
||||
attach_widget=option_menu_widget,
|
||||
dropdown_menu_min_width=dropdown_menu_min_width,
|
||||
)
|
||||
|
||||
return setting_box_frame
|
||||
|
||||
|
||||
|
||||
|
||||
def createSettingBoxSwitch(self,
|
||||
for_var_label_text, for_var_desc_text,
|
||||
switch_attr_name,
|
||||
variable,
|
||||
command,
|
||||
):
|
||||
|
||||
(setting_box_frame, setting_box_item_frame) = self._createSettingBoxFrame(switch_attr_name, for_var_label_text, for_var_desc_text)
|
||||
|
||||
switch_widget = CTkSwitch(
|
||||
setting_box_item_frame,
|
||||
text=None,
|
||||
height=0,
|
||||
width=0,
|
||||
corner_radius=int(self.settings.uism.SB__SWITCH_BOX_HEIGHT/2),
|
||||
border_width=0,
|
||||
switch_height=self.settings.uism.SB__SWITCH_BOX_HEIGHT,
|
||||
switch_width=self.settings.uism.SB__SWITCH_BOX_WIDTH,
|
||||
onvalue=True,
|
||||
offvalue=False,
|
||||
variable=variable,
|
||||
command=command,
|
||||
fg_color=self.settings.ctm.SB__SWITCH_BOX_BG_COLOR,
|
||||
# bg_color="red",
|
||||
progress_color=self.settings.ctm.SB__SWITCH_BOX_ACTIVE_BG_COLOR,
|
||||
button_color=self.settings.ctm.SB__SWITCH_BOX_BUTTON_COLOR,
|
||||
button_hover_color=self.settings.ctm.SB__SWITCH_BOX_BUTTON_HOVERED_COLOR,
|
||||
)
|
||||
setattr(self.config_window, switch_attr_name, switch_widget)
|
||||
|
||||
switch_widget.grid(row=1, column=SETTING_BOX_COLUMN, sticky="e")
|
||||
|
||||
return setting_box_frame
|
||||
|
||||
|
||||
|
||||
def createSettingBoxCheckbox(self,
|
||||
for_var_label_text, for_var_desc_text,
|
||||
checkbox_attr_name,
|
||||
command,
|
||||
variable,
|
||||
):
|
||||
|
||||
(setting_box_frame, setting_box_item_frame) = self._createSettingBoxFrame(checkbox_attr_name, for_var_label_text, for_var_desc_text)
|
||||
|
||||
checkbox_widget = CTkCheckBox(
|
||||
setting_box_item_frame,
|
||||
text=None,
|
||||
width=0,
|
||||
checkbox_width=self.settings.uism.SB__CHECKBOX_SIZE,
|
||||
checkbox_height=self.settings.uism.SB__CHECKBOX_SIZE,
|
||||
onvalue=True,
|
||||
offvalue=False,
|
||||
variable=variable,
|
||||
command=command,
|
||||
corner_radius=self.settings.uism.SB__CHECKBOX_CORNER_RADIUS,
|
||||
border_width=self.settings.uism.SB__CHECKBOX_BORDER_WIDTH,
|
||||
border_color=self.settings.ctm.SB__CHECKBOX_BORDER_COLOR,
|
||||
hover_color=self.settings.ctm.SB__CHECKBOX_HOVER_COLOR,
|
||||
checkmark_color=self.settings.ctm.SB__CHECKBOX_CHECKMARK_COLOR,
|
||||
fg_color=self.settings.ctm.SB__CHECKBOX_CHECKED_COLOR,
|
||||
# fg_color=self.settings.ctm.SB__SWITCH_BOX_BG_COLOR,
|
||||
# bg_color="red",
|
||||
# progress_color=self.settings.ctm.SB__SWITCH_BOX_ACTIVE_BG_COLOR,
|
||||
)
|
||||
setattr(self.config_window, checkbox_attr_name, checkbox_widget)
|
||||
|
||||
checkbox_widget.grid(row=1, column=SETTING_BOX_COLUMN, sticky="e")
|
||||
|
||||
return setting_box_frame
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def createSettingBoxSlider(
|
||||
self,
|
||||
for_var_label_text, for_var_desc_text,
|
||||
slider_attr_name,
|
||||
slider_range,
|
||||
command,
|
||||
variable,
|
||||
slider_number_of_steps: Union[int,
|
||||
None] = None,
|
||||
slider_bind__ButtonPress=None,
|
||||
slider_bind__ButtonRelease=None
|
||||
):
|
||||
|
||||
(setting_box_frame, setting_box_item_frame) = self._createSettingBoxFrame(slider_attr_name, for_var_label_text, for_var_desc_text)
|
||||
|
||||
|
||||
slider_widget = CTkSlider(
|
||||
setting_box_item_frame,
|
||||
width=self.settings.uism.SB__SLIDER_WIDTH,
|
||||
height=self.settings.uism.SB__SLIDER_HEIGHT,
|
||||
from_=slider_range[0],
|
||||
to=slider_range[1],
|
||||
number_of_steps=slider_number_of_steps,
|
||||
fg_color=self.settings.ctm.SB__SLIDER_BG_COLOR,
|
||||
progress_color=self.settings.ctm.SB__SLIDER_PROGRESS_BG_COLOR,
|
||||
button_color=self.settings.ctm.SB__SLIDER_BUTTON_COLOR,
|
||||
button_hover_color=self.settings.ctm.SB__SLIDER_BUTTON_HOVERED_COLOR,
|
||||
command=command,
|
||||
variable=variable,
|
||||
)
|
||||
setattr(self.config_window, slider_attr_name, slider_widget)
|
||||
|
||||
slider_widget.grid(row=1, column=SETTING_BOX_COLUMN, sticky="e")
|
||||
|
||||
if slider_bind__ButtonPress is not None:
|
||||
def adjusted_slider_bind__ButtonPress(_e):
|
||||
command(_e)
|
||||
slider_bind__ButtonPress()
|
||||
slider_widget.configure(command=adjusted_slider_bind__ButtonPress)
|
||||
|
||||
if slider_bind__ButtonRelease is not None:
|
||||
def adjusted_slider_bind__ButtonRelease(_e):
|
||||
slider_bind__ButtonRelease()
|
||||
slider_widget.bind("<ButtonRelease>", adjusted_slider_bind__ButtonRelease, "+")
|
||||
|
||||
return setting_box_frame
|
||||
|
||||
|
||||
|
||||
|
||||
def createSettingBoxProgressbarXSlider(
|
||||
self,
|
||||
command, progressbar_x_slider_attr_name,
|
||||
entry_attr_name, entry_bind__FocusOut,
|
||||
slider_attr_name, slider_range,
|
||||
progressbar_attr_name,
|
||||
passive_button_attr_name, passive_button_command,
|
||||
active_button_attr_name, active_button_command,
|
||||
disabled_button_attr_name, disabled_button_image_file,
|
||||
button_image_file,
|
||||
|
||||
entry_variable,
|
||||
slider_variable,
|
||||
|
||||
slider_number_of_steps: Union[int, None] = None,
|
||||
):
|
||||
|
||||
(setting_box_frame, setting_box_item_frame) = self._createSettingBoxFrame(progressbar_x_slider_attr_name)
|
||||
|
||||
def adjusted_command__for_entry_bind__Any_KeyRelease(e):
|
||||
command(e.widget.get())
|
||||
def adjusted_command__for_slider(value):
|
||||
command(value)
|
||||
|
||||
setting_box_item_frame.grid_columnconfigure((0,2), weight=0)
|
||||
setting_box_item_frame.grid_columnconfigure(1, weight=1)
|
||||
entry_widget = CTkEntry(
|
||||
setting_box_item_frame,
|
||||
text_color=self.settings.ctm.SB__ENTRY_TEXT_COLOR,
|
||||
fg_color=self.settings.ctm.SB__ENTRY_BG_COLOR,
|
||||
border_color=self.settings.ctm.SB__ENTRY_BORDER_COLOR,
|
||||
width=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__ENTRY_WIDTH,
|
||||
height=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__ENTRY_HEIGHT,
|
||||
textvariable=entry_variable,
|
||||
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__ENTRY_FONT_SIZE, weight="normal"),
|
||||
)
|
||||
|
||||
entry_widget.bind("<Any-KeyRelease>", adjusted_command__for_entry_bind__Any_KeyRelease)
|
||||
if entry_bind__FocusOut is not None:
|
||||
entry_widget.bind("<FocusOut>", entry_bind__FocusOut, "+")
|
||||
|
||||
entry_widget.grid(row=1, column=2, padx=0, pady=0, sticky="e")
|
||||
setattr(self.config_window, entry_attr_name, entry_widget)
|
||||
|
||||
|
||||
|
||||
# at least 2px is needed otherwise the slider button is gonna broken.
|
||||
SLIDER_BORDER_WIDTH = max(2,self.settings.uism.SB__PROGRESSBAR_X_SLIDER__SLIDER_BUTTON_LENGTH)
|
||||
SLIDER_BUTTON_LENGTH = int(SLIDER_BORDER_WIDTH/2)
|
||||
slider_widget = CTkSlider(
|
||||
setting_box_item_frame,
|
||||
from_=slider_range[0],
|
||||
to=slider_range[1],
|
||||
number_of_steps=slider_number_of_steps,
|
||||
command=adjusted_command__for_slider,
|
||||
variable=slider_variable,
|
||||
height=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__SLIDER_HEIGHT,
|
||||
border_width=0,
|
||||
button_length=SLIDER_BORDER_WIDTH,
|
||||
button_corner_radius=SLIDER_BUTTON_LENGTH,
|
||||
corner_radius=0,
|
||||
button_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__SLIDER_BUTTON_COLOR,
|
||||
button_hover_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__SLIDER_BUTTON_HOVERED_COLOR,
|
||||
fg_color=self.settings.ctm.SB__BG_COLOR,
|
||||
progress_color=self.settings.ctm.SB__BG_COLOR,
|
||||
border_color=self.settings.ctm.SB__BG_COLOR,
|
||||
)
|
||||
slider_widget.grid(row=1, column=1, padx=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__BAR_PADX, sticky="ew")
|
||||
setattr(self.config_window, slider_attr_name, slider_widget)
|
||||
|
||||
|
||||
|
||||
|
||||
progressbar_widget = CTkProgressBar(
|
||||
setting_box_item_frame,
|
||||
height=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__PROGRESSBAR_HEIGHT,
|
||||
corner_radius=0,
|
||||
fg_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__PROGRESSBAR_BG_COLOR,
|
||||
progress_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__PROGRESSBAR_PROGRESS_BG_COLOR,
|
||||
)
|
||||
setattr(self.config_window, progressbar_attr_name, progressbar_widget)
|
||||
progressbar_widget.grid(row=1, column=1, padx=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__BAR_PADX, sticky="ew")
|
||||
progressbar_widget.set(0)
|
||||
|
||||
|
||||
|
||||
|
||||
passive_button_wrapper = self._createPassiveButtonForProgressbarXSlider(setting_box_item_frame, passive_button_command, button_image_file)
|
||||
setattr(self.config_window, passive_button_attr_name, passive_button_wrapper)
|
||||
|
||||
disabled_button_wrapper = self._createDisabledButtonForProgressbarXSlider(setting_box_item_frame, disabled_button_image_file)
|
||||
setattr(self.config_window, disabled_button_attr_name, disabled_button_wrapper)
|
||||
|
||||
active_button_wrapper = self._createActiveButtonForProgressbarXSlider(setting_box_item_frame, active_button_command, button_image_file)
|
||||
setattr(self.config_window, active_button_attr_name, active_button_wrapper)
|
||||
|
||||
passive_button_wrapper.grid(row=1, column=0, padx=0, sticky="w")
|
||||
passive_button_wrapper.configure(corner_radius=int(getLatestWidth(passive_button_wrapper)/2))
|
||||
|
||||
disabled_button_wrapper.grid(row=1, column=0, padx=0, sticky="w")
|
||||
disabled_button_wrapper.configure(corner_radius=int(getLatestWidth(passive_button_wrapper)/2))
|
||||
|
||||
active_button_wrapper.grid(row=1, column=0, padx=0, sticky="w")
|
||||
active_button_wrapper.configure(corner_radius=int(getLatestWidth(passive_button_wrapper)/2))
|
||||
|
||||
passive_button_wrapper.grid_remove()
|
||||
disabled_button_wrapper.grid_remove()
|
||||
active_button_wrapper.grid_remove()
|
||||
|
||||
passive_button_wrapper.grid()
|
||||
return setting_box_frame
|
||||
|
||||
|
||||
|
||||
|
||||
def createSettingBoxEntry(self,
|
||||
for_var_label_text, for_var_desc_text,
|
||||
entry_attr_name,
|
||||
entry_width,
|
||||
entry_textvariable,
|
||||
entry_bind__Any_KeyRelease,
|
||||
entry_bind__FocusOut=None,
|
||||
):
|
||||
|
||||
(setting_box_frame, setting_box_item_frame) = self._createSettingBoxFrame(entry_attr_name, for_var_label_text, for_var_desc_text)
|
||||
|
||||
def adjusted_command__for_entry_bind__Any_KeyRelease(e):
|
||||
entry_bind__Any_KeyRelease(e.widget.get())
|
||||
|
||||
entry_widget = CTkEntry(
|
||||
setting_box_item_frame,
|
||||
text_color=self.settings.ctm.SB__ENTRY_TEXT_COLOR,
|
||||
fg_color=self.settings.ctm.SB__ENTRY_BG_COLOR,
|
||||
border_color=self.settings.ctm.SB__ENTRY_BORDER_COLOR,
|
||||
width=entry_width,
|
||||
height=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__ENTRY_HEIGHT,
|
||||
textvariable=entry_textvariable,
|
||||
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__ENTRY_FONT_SIZE, weight="normal"),
|
||||
)
|
||||
entry_widget.bind("<Any-KeyRelease>", adjusted_command__for_entry_bind__Any_KeyRelease)
|
||||
setattr(self.config_window, entry_attr_name, entry_widget)
|
||||
|
||||
|
||||
entry_widget.grid(row=1, column=SETTING_BOX_COLUMN, sticky="e")
|
||||
|
||||
if entry_bind__FocusOut is not None:
|
||||
entry_widget.bind("<FocusOut>", entry_bind__FocusOut, "+")
|
||||
|
||||
return setting_box_frame
|
||||
|
||||
|
||||
|
||||
# if setting_box_type == "dropdown_menu_x_dropdown_menu":
|
||||
# self.setting_box_dropdown_menu_x_dropdown_menu = CTkFrame(self.setting_box, corner_radius=0, fg_color=self.settings.ctm.SB__BG_COLOR, width=0, height=0)
|
||||
# self.setting_box_dropdown_menu_x_dropdown_menu.grid(row=0, column=1, padx=(0, self.settings.uism.SB__RIGHT_PADX), rowspan=2, sticky="e")
|
||||
|
||||
|
||||
|
||||
# # Labels
|
||||
# self.optionmenu_label_left = CTkLabel(
|
||||
# self.setting_box_dropdown_menu_x_dropdown_menu,
|
||||
# text=kwargs["left_dropdown_menu_label"],
|
||||
# font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__OPTION_MENU_FONT_SIZE, weight="normal"),
|
||||
# )
|
||||
# self.optionmenu_label_left.grid(row=0, column=0)
|
||||
|
||||
# self.the_space_between_optionmenu = CTkLabel(
|
||||
# self.setting_box_dropdown_menu_x_dropdown_menu,
|
||||
# text=None,
|
||||
# )
|
||||
# self.the_space_between_optionmenu.grid(row=0, column=1)
|
||||
|
||||
|
||||
# self.optionmenu_label_right = CTkLabel(
|
||||
# self.setting_box_dropdown_menu_x_dropdown_menu,
|
||||
# text=kwargs["right_dropdown_menu_label"],
|
||||
# font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__OPTION_MENU_FONT_SIZE, weight="normal"),
|
||||
# )
|
||||
# self.optionmenu_label_right.grid(row=0, column=2)
|
||||
|
||||
|
||||
|
||||
# # Option menus
|
||||
# self.createOption_DropdownMenu(
|
||||
# setattr_obj,
|
||||
# self.setting_box_dropdown_menu_x_dropdown_menu,
|
||||
# kwargs["left_optionmenu_attr_name"],
|
||||
# kwargs["left_dropdown_menu_attr_name"],
|
||||
# dropdown_menu_values=kwargs["left_dropdown_menu_values"],
|
||||
# width=150,
|
||||
# command=kwargs["left_dropdown_menu_command"],
|
||||
# variable=kwargs["left_dropdown_menu_variable"],
|
||||
# )
|
||||
# getattr(setattr_obj, kwargs["left_optionmenu_attr_name"]).grid(row=1, column=0)
|
||||
|
||||
|
||||
|
||||
# self.the_label_between_optionmenu = CTkLabel(
|
||||
# self.setting_box_dropdown_menu_x_dropdown_menu,
|
||||
# text="-->",
|
||||
# # anchor="w",
|
||||
# font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__OPTION_MENU_FONT_SIZE, weight="normal"),
|
||||
# text_color=self.settings.ctm.LABELS_TEXT_COLOR
|
||||
# )
|
||||
# self.the_label_between_optionmenu.grid(row=1, column=1, padx=self.settings.uism.SB__RIGHT_PADX/2)
|
||||
|
||||
|
||||
# self.createOption_DropdownMenu(
|
||||
# setattr_obj,
|
||||
# self.setting_box_dropdown_menu_x_dropdown_menu,
|
||||
# kwargs["right_optionmenu_attr_name"],
|
||||
# kwargs["right_dropdown_menu_attr_name"],
|
||||
# dropdown_menu_values=kwargs["right_dropdown_menu_values"],
|
||||
# width=150,
|
||||
# command=kwargs["right_dropdown_menu_command"],
|
||||
# variable=kwargs["right_dropdown_menu_variable"],
|
||||
# )
|
||||
# getattr(setattr_obj, kwargs["right_optionmenu_attr_name"]).grid(row=1, column=2)
|
||||
|
||||
|
||||
|
||||
|
||||
# if setting_box_type == "radio_buttons":
|
||||
# self.setting_box_radio_buttons_frame = CTkFrame(self.setting_box, corner_radius=0, width=0, height=0)
|
||||
# self.setting_box_radio_buttons_frame.grid(row=0, column=1, padx=(0, self.settings.uism.SB__RIGHT_PADX), rowspan=2, sticky="e")
|
||||
|
||||
# RADIO_BUTTON_RIGHT_PAD = 14
|
||||
# self.setting_box_radio_button_1 = CTkRadioButton(
|
||||
# self.setting_box_radio_buttons_frame,
|
||||
# text="lorem ipsum",
|
||||
# font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__RADIO_BUTTON_FONT_SIZE, weight="normal")
|
||||
# )
|
||||
# self.setting_box_radio_button_1.grid(row=0, column=0, padx=(0,RADIO_BUTTON_RIGHT_PAD), sticky="e")
|
||||
|
||||
# self.setting_box_radio_button_2 = CTkRadioButton(
|
||||
# self.setting_box_radio_buttons_frame,
|
||||
# text="lorem ipsum",
|
||||
# font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__RADIO_BUTTON_FONT_SIZE, weight="normal")
|
||||
# )
|
||||
# self.setting_box_radio_button_2.grid(row=0, column=1, padx=(0,RADIO_BUTTON_RIGHT_PAD), sticky="e")
|
||||
|
||||
# self.setting_box_radio_button_3 = CTkRadioButton(
|
||||
# self.setting_box_radio_buttons_frame,
|
||||
# text="lorem ipsum",
|
||||
# font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__RADIO_BUTTON_FONT_SIZE, weight="normal")
|
||||
# )
|
||||
# self.setting_box_radio_button_3.grid(row=0, column=2, padx=(0,RADIO_BUTTON_RIGHT_PAD), sticky="e")
|
||||
|
||||
|
||||
|
||||
def _createPassiveButtonForProgressbarXSlider(self, setting_box_progressbar_x_slider_frame, button_command, button_image_file):
|
||||
button_wrapper = createButtonWithImage(
|
||||
parent_widget=setting_box_progressbar_x_slider_frame,
|
||||
button_fg_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_COLOR,
|
||||
button_enter_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_HOVERED_COLOR,
|
||||
button_clicked_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_CLICKED_COLOR,
|
||||
button_image_file=button_image_file,
|
||||
button_image_size=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__BUTTON_ICON_SIZE,
|
||||
button_ipadxy=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__BUTTON_IPADXY,
|
||||
button_command=button_command,
|
||||
)
|
||||
return button_wrapper
|
||||
|
||||
|
||||
|
||||
def _createActiveButtonForProgressbarXSlider(self, setting_box_progressbar_x_slider_frame, button_command, button_image_file):
|
||||
button_wrapper = createButtonWithImage(
|
||||
parent_widget=setting_box_progressbar_x_slider_frame,
|
||||
button_fg_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__ACTIVE_BUTTON_COLOR,
|
||||
button_enter_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__ACTIVE_BUTTON_HOVERED_COLOR,
|
||||
button_clicked_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__ACTIVE_BUTTON_CLICKED_COLOR,
|
||||
button_image_file=button_image_file,
|
||||
button_image_size=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__BUTTON_ICON_SIZE,
|
||||
button_ipadxy=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__BUTTON_IPADXY,
|
||||
button_command=button_command,
|
||||
)
|
||||
return button_wrapper
|
||||
|
||||
|
||||
|
||||
def _createDisabledButtonForProgressbarXSlider(self, setting_box_progressbar_x_slider_frame, button_image_file):
|
||||
button_wrapper = createButtonWithImage(
|
||||
parent_widget=setting_box_progressbar_x_slider_frame,
|
||||
button_fg_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_DISABLED_COLOR,
|
||||
button_image_file=button_image_file,
|
||||
button_image_size=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__BUTTON_ICON_SIZE,
|
||||
button_ipadxy=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__BUTTON_IPADXY,
|
||||
no_bind=True,
|
||||
)
|
||||
return button_wrapper
|
||||
@@ -0,0 +1 @@
|
||||
from .createSettingBox_AdvancedSettings import createSettingBox_AdvancedSettings
|
||||
@@ -0,0 +1,38 @@
|
||||
from utils import callFunctionIfCallable
|
||||
|
||||
from .._SettingBoxGenerator import _SettingBoxGenerator
|
||||
|
||||
def createSettingBox_AdvancedSettings(setting_box_wrapper, config_window, settings, view_variable):
|
||||
sbg = _SettingBoxGenerator(setting_box_wrapper, config_window, settings, view_variable)
|
||||
createSettingBoxEntry = sbg.createSettingBoxEntry
|
||||
|
||||
|
||||
def entry_ip_address_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_OSC_IP_ADDRESS, value)
|
||||
|
||||
def entry_port_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_OSC_PORT, value)
|
||||
|
||||
row=0
|
||||
config_window.sb__ip_address = createSettingBoxEntry(
|
||||
for_var_label_text=view_variable.VAR_LABEL_OSC_IP_ADDRESS,
|
||||
for_var_desc_text=view_variable.VAR_DESC_OSC_IP_ADDRESS,
|
||||
entry_attr_name="sb__entry_ip_address",
|
||||
entry_width=settings.uism.RESPONSIVE_UI_SIZE_INT_150,
|
||||
entry_bind__Any_KeyRelease=lambda value: entry_ip_address_callback(value),
|
||||
entry_textvariable=view_variable.VAR_OSC_IP_ADDRESS,
|
||||
)
|
||||
config_window.sb__ip_address.grid(row=row)
|
||||
row+=1
|
||||
|
||||
|
||||
config_window.sb__port = createSettingBoxEntry(
|
||||
for_var_label_text=view_variable.VAR_LABEL_OSC_PORT,
|
||||
for_var_desc_text=view_variable.VAR_DESC_OSC_PORT,
|
||||
entry_attr_name="sb__entry_port",
|
||||
entry_width=settings.uism.RESPONSIVE_UI_SIZE_INT_150,
|
||||
entry_bind__Any_KeyRelease=lambda value: entry_port_callback(value),
|
||||
entry_textvariable=view_variable.VAR_OSC_PORT,
|
||||
)
|
||||
config_window.sb__port.grid(row=row, pady=0)
|
||||
row+=1
|
||||
@@ -0,0 +1 @@
|
||||
from .createSettingBox_Appearance import createSettingBox_Appearance
|
||||
@@ -0,0 +1,89 @@
|
||||
from utils import callFunctionIfCallable
|
||||
|
||||
from .._SettingBoxGenerator import _SettingBoxGenerator
|
||||
|
||||
def createSettingBox_Appearance(setting_box_wrapper, config_window, settings, view_variable):
|
||||
sbg = _SettingBoxGenerator(setting_box_wrapper, config_window, settings, view_variable)
|
||||
createSettingBoxDropdownMenu = sbg.createSettingBoxDropdownMenu
|
||||
createSettingBoxSlider = sbg.createSettingBoxSlider
|
||||
|
||||
# 関数名は変えるかもしれない。
|
||||
# テーマ変更、フォント変更時、 Widget再生成か再起動かは検討中
|
||||
def slider_transparency_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_TRANSPARENCY, value)
|
||||
|
||||
def optionmenu_appearance_theme_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_APPEARANCE, value)
|
||||
|
||||
def optionmenu_ui_scaling_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_UI_SCALING, value)
|
||||
|
||||
def optionmenu_font_family_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_FONT_FAMILY, value)
|
||||
|
||||
def optionmenu_ui_language_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_UI_LANGUAGE, value)
|
||||
|
||||
|
||||
row=0
|
||||
config_window.sb__transparency = createSettingBoxSlider(
|
||||
for_var_label_text=view_variable.VAR_LABEL_TRANSPARENCY,
|
||||
for_var_desc_text=view_variable.VAR_DESC_TRANSPARENCY,
|
||||
slider_attr_name="sb__slider_transparency",
|
||||
slider_range=view_variable.SLIDER_RANGE_TRANSPARENCY,
|
||||
command=lambda value: slider_transparency_callback(value),
|
||||
variable=view_variable.VAR_TRANSPARENCY,
|
||||
slider_bind__ButtonPress=view_variable.CALLBACK_BUTTON_PRESS_TRANSPARENCY,
|
||||
slider_bind__ButtonRelease=view_variable.CALLBACK_BUTTON_RELEASE_TRANSPARENCY,
|
||||
)
|
||||
config_window.sb__transparency.grid(row=row)
|
||||
row+=1
|
||||
|
||||
|
||||
config_window.sb__appearance_theme = createSettingBoxDropdownMenu(
|
||||
for_var_label_text=view_variable.VAR_LABEL_APPEARANCE_THEME,
|
||||
for_var_desc_text=view_variable.VAR_DESC_APPEARANCE_THEME,
|
||||
optionmenu_attr_name="sb__optionmenu_appearance_theme",
|
||||
dropdown_menu_values=view_variable.LIST_APPEARANCE_THEME,
|
||||
command=lambda value: optionmenu_appearance_theme_callback(value),
|
||||
variable=view_variable.VAR_APPEARANCE_THEME,
|
||||
)
|
||||
config_window.sb__appearance_theme.grid(row=row)
|
||||
row+=1
|
||||
|
||||
|
||||
|
||||
config_window.sb__ui_scaling = createSettingBoxDropdownMenu(
|
||||
for_var_label_text=view_variable.VAR_LABEL_UI_SCALING,
|
||||
for_var_desc_text=view_variable.VAR_DESC_UI_SCALING,
|
||||
optionmenu_attr_name="sb__optionmenu_ui_scaling",
|
||||
dropdown_menu_values=view_variable.LIST_UI_SCALING,
|
||||
command=lambda value: optionmenu_ui_scaling_callback(value),
|
||||
variable=view_variable.VAR_UI_SCALING,
|
||||
)
|
||||
config_window.sb__ui_scaling.grid(row=row)
|
||||
row+=1
|
||||
|
||||
|
||||
config_window.sb__font_family = createSettingBoxDropdownMenu(
|
||||
for_var_label_text=view_variable.VAR_LABEL_FONT_FAMILY,
|
||||
for_var_desc_text=view_variable.VAR_DESC_FONT_FAMILY,
|
||||
optionmenu_attr_name="sb__optionmenu_font_family",
|
||||
dropdown_menu_values=view_variable.LIST_FONT_FAMILY,
|
||||
command=lambda value: optionmenu_font_family_callback(value),
|
||||
variable=view_variable.VAR_FONT_FAMILY,
|
||||
)
|
||||
config_window.sb__font_family.grid(row=row)
|
||||
row+=1
|
||||
|
||||
|
||||
config_window.sb__ui_language = createSettingBoxDropdownMenu(
|
||||
for_var_label_text=view_variable.VAR_LABEL_UI_LANGUAGE,
|
||||
for_var_desc_text=view_variable.VAR_DESC_UI_LANGUAGE,
|
||||
optionmenu_attr_name="sb__optionmenu_ui_language",
|
||||
dropdown_menu_values=view_variable.LIST_UI_LANGUAGE,
|
||||
command=lambda value: optionmenu_ui_language_callback(value),
|
||||
variable=view_variable.VAR_UI_LANGUAGE,
|
||||
)
|
||||
config_window.sb__ui_language.grid(row=row, pady=0)
|
||||
row+=1
|
||||
@@ -0,0 +1 @@
|
||||
from .createSettingBox_Others import createSettingBox_Others
|
||||
@@ -0,0 +1,98 @@
|
||||
from utils import callFunctionIfCallable
|
||||
|
||||
from .._SettingBoxGenerator import _SettingBoxGenerator
|
||||
|
||||
def createSettingBox_Others(setting_box_wrapper, config_window, settings, view_variable):
|
||||
sbg = _SettingBoxGenerator(setting_box_wrapper, config_window, settings, view_variable)
|
||||
createSettingBoxCheckbox = sbg.createSettingBoxCheckbox
|
||||
createSettingBoxEntry = sbg.createSettingBoxEntry
|
||||
|
||||
|
||||
# 元 checkbox_auto_clear_chatbox_callback
|
||||
def checkbox_auto_clear_message_box_callback(checkbox_box_widget):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_ENABLE_AUTO_CLEAR_MESSAGE_BOX, checkbox_box_widget.get())
|
||||
|
||||
def checkbox_notice_xsoverlay_callback(checkbox_box_widget):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_ENABLE_NOTICE_XSOVERLAY, checkbox_box_widget.get())
|
||||
|
||||
def checkbox_auto_export_message_logs_callback(checkbox_box_widget):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_ENABLE_AUTO_EXPORT_MESSAGE_LOGS, checkbox_box_widget.get())
|
||||
|
||||
def checkbox_enable_send_message_to_vrc_callback(checkbox_box_widget):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_ENABLE_SEND_MESSAGE_TO_VRC, checkbox_box_widget.get())
|
||||
|
||||
# [deprecated]
|
||||
# def checkbox_startup_osc_enabled_check_callback(checkbox_box_widget):
|
||||
# callFunctionIfCallable(view_variable.CALLBACK_SET_STARTUP_OSC_ENABLED_CHECK, checkbox_box_widget.get())
|
||||
|
||||
def entry_message_format_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_MESSAGE_FORMAT, value)
|
||||
|
||||
|
||||
row=0
|
||||
config_window.sb__auto_clear_message_box = createSettingBoxCheckbox(
|
||||
for_var_label_text=view_variable.VAR_LABEL_ENABLE_AUTO_CLEAR_MESSAGE_BOX,
|
||||
for_var_desc_text=view_variable.VAR_DESC_ENABLE_AUTO_CLEAR_MESSAGE_BOX,
|
||||
checkbox_attr_name="sb__checkbox_auto_clear_message_box",
|
||||
command=lambda: checkbox_auto_clear_message_box_callback(config_window.sb__checkbox_auto_clear_message_box),
|
||||
variable=view_variable.VAR_ENABLE_AUTO_CLEAR_MESSAGE_BOX,
|
||||
)
|
||||
config_window.sb__auto_clear_message_box.grid(row=row)
|
||||
row+=1
|
||||
|
||||
|
||||
config_window.sb__notice_xsoverlay = createSettingBoxCheckbox(
|
||||
for_var_label_text=view_variable.VAR_LABEL_ENABLE_NOTICE_XSOVERLAY,
|
||||
for_var_desc_text=view_variable.VAR_DESC_ENABLE_NOTICE_XSOVERLAY,
|
||||
checkbox_attr_name="sb__checkbox_notice_xsoverlay",
|
||||
command=lambda: checkbox_notice_xsoverlay_callback(config_window.sb__checkbox_notice_xsoverlay),
|
||||
variable=view_variable.VAR_ENABLE_NOTICE_XSOVERLAY,
|
||||
)
|
||||
config_window.sb__notice_xsoverlay.grid(row=row)
|
||||
row+=1
|
||||
|
||||
|
||||
config_window.sb__auto_export_message_logs = createSettingBoxCheckbox(
|
||||
for_var_label_text=view_variable.VAR_LABEL_ENABLE_AUTO_EXPORT_MESSAGE_LOGS,
|
||||
for_var_desc_text=view_variable.VAR_DESC_ENABLE_AUTO_EXPORT_MESSAGE_LOGS,
|
||||
checkbox_attr_name="sb__checkbox_auto_export_message_logs",
|
||||
command=lambda: checkbox_auto_export_message_logs_callback(config_window.sb__checkbox_auto_export_message_logs),
|
||||
variable=view_variable.VAR_ENABLE_AUTO_EXPORT_MESSAGE_LOGS,
|
||||
)
|
||||
config_window.sb__auto_export_message_logs.grid(row=row)
|
||||
row+=1
|
||||
|
||||
|
||||
config_window.sb__message_format = createSettingBoxEntry(
|
||||
for_var_label_text=view_variable.VAR_LABEL_MESSAGE_FORMAT,
|
||||
for_var_desc_text=view_variable.VAR_DESC_MESSAGE_FORMAT,
|
||||
entry_attr_name="sb__entry_message_format",
|
||||
entry_width=settings.uism.RESPONSIVE_UI_SIZE_INT_250,
|
||||
entry_bind__Any_KeyRelease=lambda value: entry_message_format_callback(value),
|
||||
entry_textvariable=view_variable.VAR_MESSAGE_FORMAT,
|
||||
)
|
||||
config_window.sb__message_format.grid(row=row)
|
||||
row+=1
|
||||
|
||||
|
||||
config_window.sb__enable_send_message_to_vrc = createSettingBoxCheckbox(
|
||||
for_var_label_text=view_variable.VAR_LABEL_ENABLE_SEND_MESSAGE_TO_VRC,
|
||||
for_var_desc_text=view_variable.VAR_DESC_ENABLE_SEND_MESSAGE_TO_VRC,
|
||||
checkbox_attr_name="sb__checkbox_enable_send_message_to_vrc",
|
||||
command=lambda: checkbox_enable_send_message_to_vrc_callback(config_window.sb__checkbox_enable_send_message_to_vrc),
|
||||
variable=view_variable.VAR_ENABLE_SEND_MESSAGE_TO_VRC,
|
||||
)
|
||||
config_window.sb__enable_send_message_to_vrc.grid(row=row, pady=0)
|
||||
row+=1
|
||||
|
||||
# [deprecated]
|
||||
# config_window.sb__startup_osc_enabled_check = createSettingBoxCheckbox(
|
||||
# for_var_label_text=view_variable.VAR_LABEL_STARTUP_OSC_ENABLED_CHECK,
|
||||
# for_var_desc_text=view_variable.VAR_DESC_STARTUP_OSC_ENABLED_CHECK,
|
||||
# checkbox_attr_name="sb__checkbox_startup_osc_enabled_check",
|
||||
# command=lambda: checkbox_startup_osc_enabled_check_callback(config_window.sb__checkbox_startup_osc_enabled_check),
|
||||
# variable=view_variable.VAR_STARTUP_OSC_ENABLED_CHECK,
|
||||
# )
|
||||
# config_window.sb__startup_osc_enabled_check.grid(row=row, pady=0)
|
||||
# row+=1
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
from .createSettingBox_Mic import createSettingBox_Mic
|
||||
from .createSettingBox_Speaker import createSettingBox_Speaker
|
||||
@@ -0,0 +1,152 @@
|
||||
from utils import callFunctionIfCallable
|
||||
|
||||
from .._SettingBoxGenerator import _SettingBoxGenerator
|
||||
|
||||
def createSettingBox_Mic(setting_box_wrapper, config_window, settings, view_variable):
|
||||
sbg = _SettingBoxGenerator(setting_box_wrapper, config_window, settings, view_variable)
|
||||
createSettingBoxDropdownMenu = sbg.createSettingBoxDropdownMenu
|
||||
createSettingBoxSwitch = sbg.createSettingBoxSwitch
|
||||
createSettingBoxProgressbarXSlider = sbg.createSettingBoxProgressbarXSlider
|
||||
createSettingBoxEntry = sbg.createSettingBoxEntry
|
||||
|
||||
|
||||
def checkbox_input_mic_threshold_check_callback(is_turned_on):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_CHECK_MIC_THRESHOLD, is_turned_on)
|
||||
|
||||
|
||||
def optionmenu_mic_host_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_MIC_HOST, value)
|
||||
|
||||
def optionmenu_input_mic_device_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_MIC_DEVICE, value)
|
||||
|
||||
def slider_input_mic_energy_threshold_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_MIC_ENERGY_THRESHOLD, value)
|
||||
|
||||
def checkbox_input_mic_dynamic_energy_threshold_callback(checkbox_box_widget):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_MIC_DYNAMIC_ENERGY_THRESHOLD, checkbox_box_widget.get())
|
||||
|
||||
|
||||
def entry_input_mic_record_timeout_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_MIC_RECORD_TIMEOUT, value)
|
||||
|
||||
def entry_input_mic_phrase_timeout_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_MIC_PHRASE_TIMEOUT, value)
|
||||
|
||||
def entry_input_mic_max_phrases_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_MIC_MAX_PHRASES, value)
|
||||
|
||||
def entry_input_mic_word_filters_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_MIC_WORD_FILTER, value)
|
||||
|
||||
|
||||
row=0
|
||||
# Mic Host と Mic Device は一つの項目として引っ付ける予定
|
||||
config_window.sb__mic_host = createSettingBoxDropdownMenu(
|
||||
for_var_label_text=view_variable.VAR_LABEL_MIC_HOST,
|
||||
for_var_desc_text=view_variable.VAR_DESC_MIC_HOST,
|
||||
optionmenu_attr_name="sb__optionmenu_mic_host",
|
||||
dropdown_menu_values=view_variable.LIST_MIC_HOST,
|
||||
command=lambda value: optionmenu_mic_host_callback(value),
|
||||
variable=view_variable.VAR_MIC_HOST,
|
||||
)
|
||||
config_window.sb__mic_host.grid(row=row)
|
||||
row+=1
|
||||
|
||||
config_window.sb__mic_device = createSettingBoxDropdownMenu(
|
||||
for_var_label_text=view_variable.VAR_LABEL_MIC_DEVICE,
|
||||
for_var_desc_text=view_variable.VAR_DESC_MIC_DEVICE,
|
||||
optionmenu_attr_name="sb__optionmenu_mic_device",
|
||||
dropdown_menu_values=view_variable.LIST_MIC_DEVICE,
|
||||
command=lambda value: optionmenu_input_mic_device_callback(value),
|
||||
variable=view_variable.VAR_MIC_DEVICE,
|
||||
)
|
||||
config_window.sb__mic_device.grid(row=row)
|
||||
row+=1
|
||||
|
||||
config_window.sb__mic_dynamic_energy_threshold = createSettingBoxSwitch(
|
||||
for_var_label_text=view_variable.VAR_LABEL_MIC_DYNAMIC_ENERGY_THRESHOLD,
|
||||
for_var_desc_text=view_variable.VAR_DESC_MIC_DYNAMIC_ENERGY_THRESHOLD,
|
||||
switch_attr_name="sb__checkbox_mic_dynamic_energy_threshold",
|
||||
command=lambda: checkbox_input_mic_dynamic_energy_threshold_callback(config_window.sb__checkbox_mic_dynamic_energy_threshold),
|
||||
variable=view_variable.VAR_MIC_DYNAMIC_ENERGY_THRESHOLD
|
||||
)
|
||||
config_window.sb__mic_dynamic_energy_threshold.grid(row=row, pady=0)
|
||||
row+=1
|
||||
|
||||
config_window.sb__mic_energy_threshold = createSettingBoxProgressbarXSlider(
|
||||
command=slider_input_mic_energy_threshold_callback,
|
||||
progressbar_x_slider_attr_name="sb__mic_energy_threshold",
|
||||
|
||||
entry_attr_name="sb__progressbar_x_slider__entry_mic_energy_threshold",
|
||||
entry_variable=view_variable.VAR_MIC_ENERGY_THRESHOLD__ENTRY,
|
||||
entry_bind__FocusOut=view_variable.CALLBACK_FOCUS_OUT_MIC_ENERGY_THRESHOLD,
|
||||
|
||||
|
||||
slider_attr_name="progressbar_x_slider__slider_mic_energy_threshold",
|
||||
slider_range=view_variable.SLIDER_RANGE_MIC_ENERGY_THRESHOLD,
|
||||
slider_variable=view_variable.VAR_MIC_ENERGY_THRESHOLD__SLIDER,
|
||||
|
||||
progressbar_attr_name="sb__progressbar_x_slider__progressbar_mic_energy_threshold",
|
||||
|
||||
passive_button_attr_name="sb__progressbar_x_slider__passive_button_mic_energy_threshold",
|
||||
passive_button_command=lambda _e: checkbox_input_mic_threshold_check_callback(True),
|
||||
active_button_attr_name="sb__progressbar_x_slider__active_button_mic_energy_threshold",
|
||||
active_button_command=lambda _e: checkbox_input_mic_threshold_check_callback(False),
|
||||
button_image_file=settings.image_file.MIC_ICON,
|
||||
disabled_button_attr_name="sb__progressbar_x_slider__disabled_button_mic_energy_threshold",
|
||||
disabled_button_image_file=settings.image_file.MIC_ICON_DISABLED,
|
||||
)
|
||||
config_window.sb__mic_energy_threshold.grid(row=row)
|
||||
row+=1
|
||||
|
||||
|
||||
# 以下3つも一つの項目にまとめるかもしれない
|
||||
config_window.sb__mic_record_timeout = createSettingBoxEntry(
|
||||
for_var_label_text=view_variable.VAR_LABEL_MIC_RECORD_TIMEOUT,
|
||||
for_var_desc_text=view_variable.VAR_DESC_MIC_RECORD_TIMEOUT,
|
||||
entry_attr_name="sb__entry_mic_record_timeout",
|
||||
entry_width=settings.uism.RESPONSIVE_UI_SIZE_INT_100,
|
||||
entry_bind__Any_KeyRelease=lambda value: entry_input_mic_record_timeout_callback(value),
|
||||
entry_textvariable=view_variable.VAR_MIC_RECORD_TIMEOUT,
|
||||
entry_bind__FocusOut=view_variable.CALLBACK_FOCUS_OUT_MIC_RECORD_TIMEOUT,
|
||||
)
|
||||
config_window.sb__mic_record_timeout.grid(row=row)
|
||||
row+=1
|
||||
|
||||
config_window.sb__mic_phrase_timeout = createSettingBoxEntry(
|
||||
for_var_label_text=view_variable.VAR_LABEL_MIC_PHRASE_TIMEOUT,
|
||||
for_var_desc_text=view_variable.VAR_DESC_MIC_PHRASE_TIMEOUT,
|
||||
entry_attr_name="sb__entry_mic_phrase_timeout",
|
||||
entry_width=settings.uism.RESPONSIVE_UI_SIZE_INT_100,
|
||||
entry_bind__Any_KeyRelease=lambda value: entry_input_mic_phrase_timeout_callback(value),
|
||||
entry_textvariable=view_variable.VAR_MIC_PHRASE_TIMEOUT,
|
||||
entry_bind__FocusOut=view_variable.CALLBACK_FOCUS_OUT_MIC_PHRASE_TIMEOUT,
|
||||
)
|
||||
config_window.sb__mic_phrase_timeout.grid(row=row)
|
||||
row+=1
|
||||
|
||||
config_window.sb__mic_max_phrases = createSettingBoxEntry(
|
||||
for_var_label_text=view_variable.VAR_LABEL_MIC_MAX_PHRASES,
|
||||
for_var_desc_text=view_variable.VAR_DESC_MIC_MAX_PHRASES,
|
||||
entry_attr_name="sb__entry_mic_max_phrases",
|
||||
entry_width=settings.uism.RESPONSIVE_UI_SIZE_INT_100,
|
||||
entry_bind__Any_KeyRelease=lambda value: entry_input_mic_max_phrases_callback(value),
|
||||
entry_textvariable=view_variable.VAR_MIC_MAX_PHRASES,
|
||||
entry_bind__FocusOut=view_variable.CALLBACK_FOCUS_OUT_MIC_MAX_PHRASES,
|
||||
)
|
||||
config_window.sb__mic_max_phrases.grid(row=row)
|
||||
row+=1
|
||||
# # __________
|
||||
|
||||
|
||||
config_window.sb__mic_word_filter = createSettingBoxEntry(
|
||||
for_var_label_text=view_variable.VAR_LABEL_MIC_WORD_FILTER,
|
||||
for_var_desc_text=view_variable.VAR_DESC_MIC_WORD_FILTER,
|
||||
entry_attr_name="sb__entry_mic_word_filter",
|
||||
entry_width=settings.uism.RESPONSIVE_UI_SIZE_INT_300,
|
||||
entry_bind__Any_KeyRelease=lambda value: entry_input_mic_word_filters_callback(value),
|
||||
entry_textvariable=view_variable.VAR_MIC_WORD_FILTER,
|
||||
)
|
||||
config_window.sb__mic_word_filter.grid(row=row, pady=0)
|
||||
row+=1
|
||||
@@ -0,0 +1,108 @@
|
||||
from utils import callFunctionIfCallable
|
||||
|
||||
from .._SettingBoxGenerator import _SettingBoxGenerator
|
||||
|
||||
def createSettingBox_Speaker(setting_box_wrapper, config_window, settings, view_variable):
|
||||
sbg = _SettingBoxGenerator(setting_box_wrapper, config_window, settings, view_variable)
|
||||
createSettingBoxSwitch = sbg.createSettingBoxSwitch
|
||||
createSettingBoxProgressbarXSlider = sbg.createSettingBoxProgressbarXSlider
|
||||
createSettingBoxEntry = sbg.createSettingBoxEntry
|
||||
|
||||
|
||||
def checkbox_input_speaker_threshold_check_callback(is_turned_on):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_CHECK_SPEAKER_THRESHOLD, is_turned_on)
|
||||
|
||||
|
||||
def slider_input_speaker_energy_threshold_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_SPEAKER_ENERGY_THRESHOLD, value)
|
||||
|
||||
def checkbox_input_speaker_dynamic_energy_threshold_callback(checkbox_box_widget):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_SPEAKER_DYNAMIC_ENERGY_THRESHOLD, checkbox_box_widget.get())
|
||||
|
||||
|
||||
def entry_input_speaker_record_timeout_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_SPEAKER_RECORD_TIMEOUT, value)
|
||||
|
||||
def entry_input_speaker_phrase_timeout_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_SPEAKER_PHRASE_TIMEOUT, value)
|
||||
|
||||
def entry_input_speaker_max_phrases_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_SPEAKER_MAX_PHRASES, value)
|
||||
|
||||
|
||||
|
||||
row=0
|
||||
config_window.sb__speaker_dynamic_energy_threshold = createSettingBoxSwitch(
|
||||
for_var_label_text=view_variable.VAR_LABEL_SPEAKER_DYNAMIC_ENERGY_THRESHOLD,
|
||||
for_var_desc_text=view_variable.VAR_DESC_SPEAKER_DYNAMIC_ENERGY_THRESHOLD,
|
||||
switch_attr_name="sb__checkbox_speaker_dynamic_energy_threshold",
|
||||
command=lambda: checkbox_input_speaker_dynamic_energy_threshold_callback(config_window.sb__checkbox_speaker_dynamic_energy_threshold),
|
||||
variable=view_variable.VAR_SPEAKER_DYNAMIC_ENERGY_THRESHOLD,
|
||||
)
|
||||
config_window.sb__speaker_dynamic_energy_threshold.grid(row=row, pady=0)
|
||||
row+=1
|
||||
|
||||
config_window.sb__speaker_energy_threshold = createSettingBoxProgressbarXSlider(
|
||||
command=slider_input_speaker_energy_threshold_callback,
|
||||
progressbar_x_slider_attr_name="sb__speaker_energy_threshold",
|
||||
|
||||
entry_variable=view_variable.VAR_SPEAKER_ENERGY_THRESHOLD__ENTRY,
|
||||
entry_attr_name="sb__progressbar_x_slider__entry_speaker_energy_threshold",
|
||||
entry_bind__FocusOut=view_variable.CALLBACK_FOCUS_OUT_SPEAKER_ENERGY_THRESHOLD,
|
||||
|
||||
|
||||
slider_attr_name="progressbar_x_slider__slider_speaker_energy_threshold",
|
||||
slider_range=view_variable.SLIDER_RANGE_SPEAKER_ENERGY_THRESHOLD,
|
||||
slider_variable=view_variable.VAR_SPEAKER_ENERGY_THRESHOLD__SLIDER,
|
||||
|
||||
progressbar_attr_name="sb__progressbar_x_slider__progressbar_speaker_energy_threshold",
|
||||
|
||||
passive_button_attr_name="sb__progressbar_x_slider__passive_button_speaker_energy_threshold",
|
||||
passive_button_command=lambda _e: checkbox_input_speaker_threshold_check_callback(True),
|
||||
active_button_attr_name="sb__progressbar_x_slider__active_button_speaker_energy_threshold",
|
||||
active_button_command=lambda _e: checkbox_input_speaker_threshold_check_callback(False),
|
||||
button_image_file=settings.image_file.HEADPHONES_ICON,
|
||||
disabled_button_attr_name="sb__progressbar_x_slider__disabled_button_speaker_energy_threshold",
|
||||
disabled_button_image_file=settings.image_file.HEADPHONES_ICON_DISABLED,
|
||||
)
|
||||
config_window.sb__speaker_energy_threshold.grid(row=row)
|
||||
row+=1
|
||||
|
||||
|
||||
# 以下3つも一つの項目にまとめるかもしれない
|
||||
config_window.sb__speaker_record_timeout = createSettingBoxEntry(
|
||||
for_var_label_text=view_variable.VAR_LABEL_SPEAKER_RECORD_TIMEOUT,
|
||||
for_var_desc_text=view_variable.VAR_DESC_SPEAKER_RECORD_TIMEOUT,
|
||||
entry_attr_name="sb__entry_speaker_record_timeout",
|
||||
entry_width=settings.uism.RESPONSIVE_UI_SIZE_INT_100,
|
||||
entry_bind__Any_KeyRelease=lambda value: entry_input_speaker_record_timeout_callback(value),
|
||||
entry_textvariable=view_variable.VAR_SPEAKER_RECORD_TIMEOUT,
|
||||
entry_bind__FocusOut=view_variable.CALLBACK_FOCUS_OUT_SPEAKER_RECORD_TIMEOUT,
|
||||
)
|
||||
config_window.sb__speaker_record_timeout.grid(row=row)
|
||||
row+=1
|
||||
|
||||
config_window.sb__speaker_phrase_timeout = createSettingBoxEntry(
|
||||
for_var_label_text=view_variable.VAR_LABEL_SPEAKER_PHRASE_TIMEOUT,
|
||||
for_var_desc_text=view_variable.VAR_DESC_SPEAKER_PHRASE_TIMEOUT,
|
||||
entry_attr_name="sb__entry_speaker_phrase_timeout",
|
||||
entry_width=settings.uism.RESPONSIVE_UI_SIZE_INT_100,
|
||||
entry_bind__Any_KeyRelease=lambda value: entry_input_speaker_phrase_timeout_callback(value),
|
||||
entry_textvariable=view_variable.VAR_SPEAKER_PHRASE_TIMEOUT,
|
||||
entry_bind__FocusOut=view_variable.CALLBACK_FOCUS_OUT_SPEAKER_PHRASE_TIMEOUT,
|
||||
)
|
||||
config_window.sb__speaker_phrase_timeout.grid(row=row)
|
||||
row+=1
|
||||
|
||||
config_window.sb__speaker_max_phrases = createSettingBoxEntry(
|
||||
for_var_label_text=view_variable.VAR_LABEL_SPEAKER_MAX_PHRASES,
|
||||
for_var_desc_text=view_variable.VAR_DESC_SPEAKER_MAX_PHRASES,
|
||||
entry_attr_name="sb__entry_speaker_max_phrases",
|
||||
entry_width=settings.uism.RESPONSIVE_UI_SIZE_INT_100,
|
||||
entry_bind__Any_KeyRelease=lambda value: entry_input_speaker_max_phrases_callback(value),
|
||||
entry_textvariable=view_variable.VAR_SPEAKER_MAX_PHRASES,
|
||||
entry_bind__FocusOut=view_variable.CALLBACK_FOCUS_OUT_SPEAKER_MAX_PHRASES,
|
||||
)
|
||||
config_window.sb__speaker_max_phrases.grid(row=row, pady=0)
|
||||
row+=1
|
||||
# __________
|
||||
@@ -0,0 +1 @@
|
||||
from .createSettingBox_Translation import createSettingBox_Translation
|
||||
@@ -0,0 +1,24 @@
|
||||
from utils import callFunctionIfCallable
|
||||
|
||||
from .._SettingBoxGenerator import _SettingBoxGenerator
|
||||
|
||||
def createSettingBox_Translation(setting_box_wrapper, config_window, settings, view_variable):
|
||||
sbg = _SettingBoxGenerator(setting_box_wrapper, config_window, settings, view_variable)
|
||||
createSettingBoxEntry = sbg.createSettingBoxEntry
|
||||
|
||||
|
||||
def deepl_authkey_callback(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SET_DEEPL_AUTHKEY, value)
|
||||
|
||||
|
||||
row=0
|
||||
config_window.sb__deepl_authkey = createSettingBoxEntry(
|
||||
for_var_label_text=view_variable.VAR_LABEL_DEEPL_AUTH_KEY,
|
||||
for_var_desc_text=view_variable.VAR_DESC_DEEPL_AUTH_KEY,
|
||||
entry_attr_name="sb__entry_deepl_authkey",
|
||||
entry_width=settings.uism.RESPONSIVE_UI_SIZE_INT_300,
|
||||
entry_bind__Any_KeyRelease=lambda value: deepl_authkey_callback(value),
|
||||
entry_textvariable=view_variable.VAR_DEEPL_AUTH_KEY,
|
||||
)
|
||||
config_window.sb__deepl_authkey.grid(row=row, pady=0)
|
||||
row+=1
|
||||
1
vrct_gui/main_window/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .createMainWindowWidgets import createMainWindowWidgets
|
||||
125
vrct_gui/main_window/createMainWindowWidgets.py
Normal file
@@ -0,0 +1,125 @@
|
||||
from .widgets import createSidebar, createMinimizeSidebarButton, createTextbox, createEntryMessageBox
|
||||
|
||||
from customtkinter import CTkFrame, CTkLabel, CTkFont, CTkImage
|
||||
|
||||
from utils import callFunctionIfCallable
|
||||
from ..ui_utils import createButtonWithImage, getImagePath, bindButtonFunctionAndColor
|
||||
|
||||
def createMainWindowWidgets(vrct_gui, settings, view_variable):
|
||||
vrct_gui.protocol("WM_DELETE_WINDOW", vrct_gui._quitVRCT)
|
||||
|
||||
|
||||
vrct_gui.iconbitmap(getImagePath("vrct_logo_mark_black.ico"))
|
||||
vrct_gui.title("VRCT")
|
||||
# vrct_gui.minsize(200, 200)
|
||||
|
||||
|
||||
# Main Container
|
||||
vrct_gui.grid_columnconfigure(0, weight=1)
|
||||
vrct_gui.grid_rowconfigure(0, weight=1)
|
||||
# vrct_gui.grid_columnconfigure(0, weight=1, minsize=settings.uism.MAIN_AREA_MIN_WIDTH)
|
||||
|
||||
vrct_gui.configure(fg_color=settings.ctm.MAIN_BG_COLOR)
|
||||
|
||||
vrct_gui.toplevel_wrapper = CTkFrame(vrct_gui, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=0)
|
||||
vrct_gui.toplevel_wrapper.grid(row=0, column=0, sticky="nsew")
|
||||
vrct_gui.toplevel_wrapper.grid_columnconfigure(1, weight=1)
|
||||
|
||||
|
||||
# Main Container
|
||||
vrct_gui.main_bg_container = CTkFrame(vrct_gui.toplevel_wrapper, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=0)
|
||||
vrct_gui.main_bg_container.grid(row=0, column=1, sticky="nsew")
|
||||
|
||||
|
||||
# top bar
|
||||
vrct_gui.main_bg_container.grid_columnconfigure(0, weight=1)
|
||||
vrct_gui.main_topbar_container = CTkFrame(vrct_gui.main_bg_container, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=0)
|
||||
vrct_gui.main_topbar_container.grid(row=0, column=0, sticky="ew")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
vrct_gui.main_topbar_container.grid_columnconfigure(1,weight=1)
|
||||
vrct_gui.main_topbar_center_container = CTkFrame(vrct_gui.main_topbar_container, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=0)
|
||||
vrct_gui.main_topbar_center_container.grid(row=0, column=1, sticky="nsew")
|
||||
|
||||
|
||||
|
||||
# Main Top Bar Container - Right Side
|
||||
# start from 3
|
||||
main_topbar_column=3
|
||||
# Update Available Button
|
||||
vrct_gui.update_available_container = CTkFrame(
|
||||
vrct_gui.main_topbar_container,
|
||||
corner_radius=settings.uism.UPDATE_AVAILABLE_BUTTON_CORNER_RADIUS,
|
||||
fg_color=settings.ctm.MAIN_BG_COLOR,
|
||||
cursor="hand2",
|
||||
)
|
||||
vrct_gui.update_available_container.grid(row=0, column=main_topbar_column, padx=settings.uism.UPDATE_AVAILABLE_BUTTON_PADX, pady=settings.uism.TOP_BAR_BUTTON_PADY, sticky="nse")
|
||||
vrct_gui.update_available_container.grid_remove()
|
||||
main_topbar_column+=1
|
||||
|
||||
|
||||
vrct_gui.update_available_container.grid_rowconfigure((0,2), weight=1)
|
||||
|
||||
vrct_gui.update_available_icon = CTkLabel(
|
||||
vrct_gui.update_available_container,
|
||||
text=None,
|
||||
corner_radius=0,
|
||||
height=0,
|
||||
image=CTkImage(settings.image_file.REFRESH_ICON.rotate(25), size=settings.uism.UPDATE_AVAILABLE_BUTTON_SIZE)
|
||||
)
|
||||
vrct_gui.update_available_icon.grid(row=1, column=0, padx=(settings.uism.UPDATE_AVAILABLE_BUTTON_IPADX, settings.uism.UPDATE_AVAILABLE_PADX_BETWEEN_LABEL_AND_ICON), pady=0)
|
||||
|
||||
|
||||
vrct_gui.update_available_label = CTkLabel(
|
||||
vrct_gui.update_available_container,
|
||||
textvariable=view_variable.VAR_UPDATE_AVAILABLE,
|
||||
height=0,
|
||||
corner_radius=0,
|
||||
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.UPDATE_AVAILABLE_BUTTON_FONT_SIZE, weight="normal"),
|
||||
anchor="e",
|
||||
text_color=settings.ctm.UPDATE_AVAILABLE_BUTTON_TEXT_COLOR,
|
||||
)
|
||||
# This "right padx +1" is for fixing a bug that sticks out from the frame. I don't know why that happens...
|
||||
vrct_gui.update_available_label.grid(row=1, column=1, padx=(0,settings.uism.UPDATE_AVAILABLE_BUTTON_IPADX+1), pady=0)
|
||||
|
||||
|
||||
bindButtonFunctionAndColor(
|
||||
target_widgets=[
|
||||
vrct_gui.update_available_container,
|
||||
vrct_gui.update_available_label,
|
||||
vrct_gui.update_available_icon,
|
||||
],
|
||||
enter_color=settings.ctm.UPDATE_AVAILABLE_BUTTON_HOVERED_BG_COLOR,
|
||||
leave_color=settings.ctm.UPDATE_AVAILABLE_BUTTON_BG_COLOR,
|
||||
clicked_color=settings.ctm.UPDATE_AVAILABLE_BUTTON_CLICKED_BG_COLOR,
|
||||
buttonReleasedFunction=lambda e: callFunctionIfCallable(view_variable.CALLBACK_CLICKED_UPDATE_AVAILABLE),
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Help and Info button
|
||||
vrct_gui.help_and_info_button_container = createButtonWithImage(
|
||||
parent_widget=vrct_gui.main_topbar_container,
|
||||
button_fg_color=settings.ctm.HELP_AND_INFO_BUTTON_BG_COLOR,
|
||||
button_enter_color=settings.ctm.HELP_AND_INFO_BUTTON_HOVERED_BG_COLOR,
|
||||
button_clicked_color=settings.ctm.HELP_AND_INFO_BUTTON_CLICKED_BG_COLOR,
|
||||
button_image_file=settings.image_file.HELP_ICON,
|
||||
button_image_size=settings.uism.HELP_AND_INFO_BUTTON_SIZE,
|
||||
button_ipadxy=settings.uism.HELP_AND_INFO_BUTTON_IPADXY,
|
||||
button_command=lambda e: callFunctionIfCallable(view_variable.CALLBACK_CLICKED_HELP_AND_INFO),
|
||||
corner_radius=settings.uism.HELP_AND_INFO_BUTTON_CORNER_RADIUS,
|
||||
)
|
||||
vrct_gui.help_and_info_button_container.grid(row=0, column=main_topbar_column, padx=settings.uism.HELP_AND_INFO_BUTTON_PADX, pady=settings.uism.TOP_BAR_BUTTON_PADY, sticky="e")
|
||||
main_topbar_column+=1
|
||||
createSidebar(settings, vrct_gui, view_variable)
|
||||
|
||||
createMinimizeSidebarButton(settings, vrct_gui, view_variable)
|
||||
|
||||
createTextbox(settings, vrct_gui, view_variable)
|
||||
|
||||
createEntryMessageBox(settings, vrct_gui)
|
||||
4
vrct_gui/main_window/widgets/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from .create_sidebar import createSidebar
|
||||
from .create_minimize_sidebar_button import createMinimizeSidebarButton
|
||||
from .create_textbox import createTextbox
|
||||
from .create_entry_message_box import createEntryMessageBox
|
||||
2
vrct_gui/main_window/widgets/_create_sidebar/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from .createSidebarFeatures import createSidebarFeatures
|
||||
from .createSidebarLanguagesSettings import createSidebarLanguagesSettings
|
||||
@@ -0,0 +1,287 @@
|
||||
from functools import partial
|
||||
|
||||
from customtkinter import CTkFont, CTkFrame, CTkLabel, CTkSwitch, CTkImage
|
||||
|
||||
from ....ui_utils import openImageKeepAspectRatio, retag, getLatestHeight, bindEnterAndLeaveFunction, bindButtonReleaseFunction, bindButtonPressAndReleaseFunction
|
||||
|
||||
from utils import callFunctionIfCallable
|
||||
|
||||
def createSidebarFeatures(settings, main_window, view_variable):
|
||||
|
||||
def toggleSidebarFeatureSelectedMarkIfTurnedOn(is_turned_on, mark_widget):
|
||||
mark_widget.place(relx=0.85) if is_turned_on else mark_widget.place(relx=-1)
|
||||
|
||||
|
||||
def toggleTranslationFeature():
|
||||
is_turned_on = main_window.translation_switch_box.get()
|
||||
callFunctionIfCallable(view_variable.CALLBACK_TOGGLE_TRANSLATION, is_turned_on)
|
||||
main_window.translation_frame.markToggleManually(is_turned_on=is_turned_on)
|
||||
|
||||
def toggleTranscriptionSendFeature():
|
||||
is_turned_on = main_window.transcription_send_switch_box.get()
|
||||
callFunctionIfCallable(view_variable.CALLBACK_TOGGLE_TRANSCRIPTION_SEND, is_turned_on)
|
||||
main_window.transcription_send_frame.markToggleManually(is_turned_on=is_turned_on)
|
||||
|
||||
def toggleTranscriptionReceiveFeature():
|
||||
is_turned_on = main_window.transcription_receive_switch_box.get()
|
||||
callFunctionIfCallable(view_variable.CALLBACK_TOGGLE_TRANSCRIPTION_RECEIVE, is_turned_on)
|
||||
main_window.transcription_receive_frame.markToggleManually(is_turned_on=is_turned_on)
|
||||
|
||||
def toggleForegroundFeature():
|
||||
is_turned_on = main_window.foreground_switch_box.get()
|
||||
callFunctionIfCallable(view_variable.CALLBACK_TOGGLE_FOREGROUND, is_turned_on)
|
||||
main_window.foreground_frame.markToggleManually(is_turned_on=is_turned_on)
|
||||
|
||||
|
||||
|
||||
|
||||
def changeSidebarFeaturesColorByEvents(ww, event_name):
|
||||
target_frame_widget = getattr(main_window, ww)
|
||||
target_compact_mode_frame_widget = getattr(main_window, "compact_mode_"+ww)
|
||||
if event_name == "enter":
|
||||
target_frame_widget.configure(fg_color=settings.ctm.SF__HOVERED_BG_COLOR)
|
||||
target_frame_widget.children["!ctkswitch"].configure(fg_color=settings.ctm.SF__SWITCH_BOX_HOVERED_BG_COLOR, progress_color=settings.ctm.SF__SWITCH_BOX_ACTIVE_HOVERED_BG_COLOR)
|
||||
target_compact_mode_frame_widget.configure(fg_color=settings.ctm.SF__HOVERED_BG_COLOR)
|
||||
target_compact_mode_frame_widget.children["!ctkframe"].configure(fg_color=settings.ctm.SF__SELECTED_MARK_ACTIVE_HOVERED_BG_COLOR)
|
||||
|
||||
elif event_name == "leave":
|
||||
target_frame_widget.configure(fg_color=settings.ctm.SF__BG_COLOR)
|
||||
target_frame_widget.children["!ctkswitch"].configure(fg_color=settings.ctm.SF__SWITCH_BOX_BG_COLOR, progress_color=settings.ctm.SF__SWITCH_BOX_ACTIVE_BG_COLOR)
|
||||
target_compact_mode_frame_widget.configure(fg_color=settings.ctm.SF__BG_COLOR)
|
||||
target_compact_mode_frame_widget.children["!ctkframe"].configure(fg_color=settings.ctm.SF__SELECTED_MARK_ACTIVE_BG_COLOR)
|
||||
|
||||
elif event_name == "button_press":
|
||||
target_frame_widget.configure(fg_color=settings.ctm.SF__CLICKED_BG_COLOR)
|
||||
target_frame_widget.children["!ctkswitch"].configure(fg_color=settings.ctm.SF__SWITCH_BOX_CLICKED_BG_COLOR, progress_color=settings.ctm.SF__SWITCH_BOX_ACTIVE_CLICKED_BG_COLOR)
|
||||
target_compact_mode_frame_widget.configure(fg_color=settings.ctm.SF__CLICKED_BG_COLOR)
|
||||
target_compact_mode_frame_widget.children["!ctkframe"].configure(fg_color=settings.ctm.SF__SELECTED_MARK_ACTIVE_CLICKED_BG_COLOR)
|
||||
|
||||
elif event_name == "button_release":
|
||||
target_frame_widget.configure(fg_color=settings.ctm.SF__BG_COLOR)
|
||||
target_frame_widget.children["!ctkswitch"].configure(fg_color=settings.ctm.SF__SWITCH_BOX_BG_COLOR, progress_color=settings.ctm.SF__SWITCH_BOX_ACTIVE_BG_COLOR)
|
||||
target_compact_mode_frame_widget.configure(fg_color=settings.ctm.SF__BG_COLOR)
|
||||
target_compact_mode_frame_widget.children["!ctkframe"].configure(fg_color=settings.ctm.SF__SELECTED_MARK_ACTIVE_BG_COLOR)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
(img, width, height) = openImageKeepAspectRatio(settings.image_file.VRCT_LOGO, settings.uism.SF__LOGO_MAX_SIZE)
|
||||
main_window.sidebar_logo = CTkLabel(
|
||||
main_window.sidebar_bg_container,
|
||||
fg_color=settings.ctm.SIDEBAR_BG_COLOR,
|
||||
text=None,
|
||||
height=0,
|
||||
image=CTkImage(img, size=(width,height))
|
||||
)
|
||||
main_window.sidebar_logo.grid(row=0, column=0, pady=settings.uism.SF__LOGO_PADY)
|
||||
|
||||
# "height-a_s" represents the adjustment in size, and the "pady+a_s/2" indicates a padding increase of 4 pixels to compensate for the reduction.
|
||||
a_s = settings.uism.SF__LOGO_HEIGHT_FOR_ADJUSTMENT
|
||||
(img, width, height) = openImageKeepAspectRatio(settings.image_file.VRCT_LOGO_MARK, height-a_s)
|
||||
main_window.sidebar_compact_mode_logo = CTkLabel(
|
||||
main_window.sidebar_compact_mode_bg_container,
|
||||
fg_color=settings.ctm.SIDEBAR_BG_COLOR,
|
||||
text=None,
|
||||
height=0,
|
||||
image=CTkImage(img, size=(width,height))
|
||||
)
|
||||
main_window.sidebar_compact_mode_logo.grid(row=0, column=0, pady=(
|
||||
int(settings.uism.SF__LOGO_PADY[0]+a_s/2),
|
||||
int(settings.uism.SF__LOGO_PADY[1]+a_s/2)
|
||||
))
|
||||
|
||||
# Sidebar Features
|
||||
main_window.sidebar_features_container = CTkFrame(main_window.sidebar_bg_container, corner_radius=0, fg_color="transparent", width=0, height=0)
|
||||
main_window.sidebar_features_container.grid(row=1, column=0, pady=0, sticky="ew")
|
||||
main_window.sidebar_features_container.grid_columnconfigure(0, weight=1)
|
||||
|
||||
|
||||
|
||||
# Sidebar Features Compact Mode
|
||||
main_window.sidebar_compact_mode_features_container = CTkFrame(main_window.sidebar_compact_mode_bg_container, corner_radius=0, fg_color="transparent", width=0, height=0)
|
||||
main_window.sidebar_compact_mode_features_container.grid(row=1, column=0, pady=0, sticky="ew")
|
||||
main_window.sidebar_compact_mode_features_container.grid_columnconfigure(0, weight=1)
|
||||
|
||||
|
||||
|
||||
sidebar_features_settings = [
|
||||
{
|
||||
"frame_attr_name": "translation_frame",
|
||||
"command": toggleTranslationFeature,
|
||||
"switch_box_attr_name": "translation_switch_box",
|
||||
"toggle_switch_box_command": lambda e: main_window.translation_switch_box.toggle(),
|
||||
"label_attr_name": "label_translation",
|
||||
"compact_mode_icon_attr_name": "translation_compact_mode_icon",
|
||||
"compact_mode_frame_attr_name": "compact_mode_translation_frame",
|
||||
"selected_mark_attr_name": "translation_selected_mark",
|
||||
"var_label_text": view_variable.VAR_LABEL_TRANSLATION,
|
||||
"icon_file": settings.image_file.TRANSLATION_ICON,
|
||||
},
|
||||
{
|
||||
"frame_attr_name": "transcription_send_frame",
|
||||
"command": toggleTranscriptionSendFeature,
|
||||
"switch_box_attr_name": "transcription_send_switch_box",
|
||||
"toggle_switch_box_command": lambda e: main_window.transcription_send_switch_box.toggle(),
|
||||
"label_attr_name": "label_transcription_send",
|
||||
"compact_mode_icon_attr_name": "transcription_send_compact_mode_icon",
|
||||
"compact_mode_frame_attr_name": "compact_mode_transcription_send_frame",
|
||||
"selected_mark_attr_name": "transcription_send_selected_mark",
|
||||
"var_label_text": view_variable.VAR_LABEL_TRANSCRIPTION_SEND,
|
||||
"icon_file": settings.image_file.MIC_ICON,
|
||||
},
|
||||
{
|
||||
"frame_attr_name": "transcription_receive_frame",
|
||||
"command": toggleTranscriptionReceiveFeature,
|
||||
"switch_box_attr_name": "transcription_receive_switch_box",
|
||||
"toggle_switch_box_command": lambda e: main_window.transcription_receive_switch_box.toggle(),
|
||||
"label_attr_name": "label_transcription_receive",
|
||||
"compact_mode_icon_attr_name": "transcription_receive_compact_mode_icon",
|
||||
"compact_mode_frame_attr_name": "compact_mode_transcription_receive_frame",
|
||||
"selected_mark_attr_name": "transcription_receive_selected_mark",
|
||||
"var_label_text": view_variable.VAR_LABEL_TRANSCRIPTION_RECEIVE,
|
||||
"icon_file": settings.image_file.HEADPHONES_ICON,
|
||||
},
|
||||
{
|
||||
"frame_attr_name": "foreground_frame",
|
||||
"command": toggleForegroundFeature,
|
||||
"switch_box_attr_name": "foreground_switch_box",
|
||||
"toggle_switch_box_command": lambda e: main_window.foreground_switch_box.toggle(),
|
||||
"label_attr_name": "label_foreground",
|
||||
"compact_mode_icon_attr_name": "foreground_compact_mode_icon",
|
||||
"compact_mode_frame_attr_name": "compact_mode_foreground_frame",
|
||||
"selected_mark_attr_name": "foreground_selected_mark",
|
||||
"var_label_text": view_variable.VAR_LABEL_FOREGROUND,
|
||||
"icon_file": settings.image_file.FOREGROUND_ICON,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
|
||||
row=0
|
||||
for sfs in sidebar_features_settings:
|
||||
frame_attr_name = sfs["frame_attr_name"]
|
||||
command = sfs["command"]
|
||||
switch_box_attr_name = sfs["switch_box_attr_name"]
|
||||
toggle_switch_box_command = sfs["toggle_switch_box_command"]
|
||||
label_attr_name = sfs["label_attr_name"]
|
||||
compact_mode_icon_attr_name = sfs["compact_mode_icon_attr_name"]
|
||||
compact_mode_frame_attr_name = sfs["compact_mode_frame_attr_name"]
|
||||
selected_mark_attr_name = sfs["selected_mark_attr_name"]
|
||||
var_label_text = sfs["var_label_text"]
|
||||
icon_file = sfs["icon_file"]
|
||||
|
||||
frame_widget = CTkFrame(main_window.sidebar_features_container, corner_radius=0, fg_color=settings.ctm.SF__BG_COLOR, cursor="hand2", width=0, height=0)
|
||||
setattr(main_window, frame_attr_name, frame_widget)
|
||||
|
||||
frame_widget.grid(row=row, column=0, pady=(0,1), sticky="ew")
|
||||
frame_widget.grid_columnconfigure(0, weight=1)
|
||||
|
||||
compact_mode_frame_widget = CTkFrame(main_window.sidebar_compact_mode_features_container, corner_radius=0, fg_color=settings.ctm.SF__BG_COLOR, cursor="hand2", width=0, height=0)
|
||||
setattr(main_window, compact_mode_frame_attr_name, compact_mode_frame_widget)
|
||||
|
||||
compact_mode_frame_widget.grid(row=row, column=0, pady=(0,1), sticky="ew")
|
||||
compact_mode_frame_widget.grid_columnconfigure(0, weight=1)
|
||||
|
||||
|
||||
label_widget = CTkLabel(
|
||||
frame_widget,
|
||||
textvariable=var_label_text,
|
||||
height=0,
|
||||
corner_radius=0,
|
||||
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.SF__LABEL_FONT_SIZE, weight="normal"),
|
||||
anchor="w",
|
||||
text_color=settings.ctm.LABELS_TEXT_COLOR,
|
||||
)
|
||||
setattr(main_window, label_attr_name, label_widget)
|
||||
|
||||
|
||||
switch_box_widget = CTkSwitch(
|
||||
frame_widget,
|
||||
text=None,
|
||||
height=0,
|
||||
width=0,
|
||||
corner_radius=int(settings.uism.SF__SWITCH_BOX_HEIGHT/2),
|
||||
border_width=0,
|
||||
switch_height=settings.uism.SF__SWITCH_BOX_HEIGHT,
|
||||
switch_width=settings.uism.SF__SWITCH_BOX_WIDTH,
|
||||
onvalue=True,
|
||||
offvalue=False,
|
||||
command=command,
|
||||
fg_color=settings.ctm.SF__SWITCH_BOX_BG_COLOR,
|
||||
bg_color=settings.ctm.SF__BG_COLOR,
|
||||
progress_color=settings.ctm.SF__SWITCH_BOX_ACTIVE_BG_COLOR,
|
||||
button_color=settings.ctm.SF__SWITCH_BOX_BUTTON_COLOR,
|
||||
# button_hover_color=settings.ctm.SF__SWITCH_BOX_BUTTON_HOVERED_COLOR,
|
||||
)
|
||||
setattr(main_window, switch_box_attr_name, switch_box_widget)
|
||||
|
||||
|
||||
|
||||
# for compact mode
|
||||
compact_mode_icon_widget = CTkLabel(
|
||||
compact_mode_frame_widget,
|
||||
text=None,
|
||||
height=0,
|
||||
corner_radius=0,
|
||||
image=CTkImage(icon_file, size=settings.uism.SF__COMPACT_MODE_IMAGE_SIZE),
|
||||
)
|
||||
setattr(main_window, compact_mode_icon_attr_name, compact_mode_icon_widget)
|
||||
|
||||
selected_mark_widget = CTkFrame(
|
||||
compact_mode_frame_widget,
|
||||
corner_radius=0,
|
||||
fg_color=settings.ctm.SF__SELECTED_MARK_ACTIVE_BG_COLOR,
|
||||
width=settings.uism.SF__SELECTED_MARK_WIDTH,
|
||||
height=0
|
||||
)
|
||||
setattr(main_window, selected_mark_attr_name, selected_mark_widget)
|
||||
|
||||
|
||||
# Arrange
|
||||
compact_mode_icon_widget.grid(row=row, column=0, padx=settings.uism.SF__COMPACT_MODE_ICON_PADX, pady=settings.uism.SF__COMPACT_MODE_ICON_PADY)
|
||||
selected_mark_widget.place(relx=-1, rely=0.5, relheight=0.75, anchor="center")
|
||||
|
||||
label_widget.grid(row=row, column=0, pady=settings.uism.SF__LABELS_IPADY, padx=(settings.uism.SF__LABEL_LEFT_PAD,0), sticky="ew")
|
||||
switch_box_widget.grid(row=row, column=1, padx=settings.uism.SF__SWITCH_BOX_PADX, sticky="e")
|
||||
|
||||
|
||||
# Unbind the event "<Button-1>" originally set up by the widget to manually control it.
|
||||
switch_box_widget._canvas.unbind("<Button-1>")
|
||||
switch_box_widget._text_label.unbind("<Button-1>")
|
||||
|
||||
bindButtonReleaseFunction([compact_mode_icon_widget, frame_widget, compact_mode_frame_widget, label_widget, selected_mark_widget, switch_box_widget._canvas, switch_box_widget._bg_canvas], toggle_switch_box_command)
|
||||
|
||||
retag("vrct_"+frame_attr_name, compact_mode_icon_widget, frame_widget, compact_mode_frame_widget, label_widget, selected_mark_widget, switch_box_widget)
|
||||
|
||||
def commonEventFunction(e, event_type):
|
||||
for ww in e.widget.master.bindtags():
|
||||
if ww.startswith("vrct_"):
|
||||
ww = ww.replace("vrct_", "")
|
||||
changeSidebarFeaturesColorByEvents(ww, event_type)
|
||||
break
|
||||
|
||||
def enterFunction(e):
|
||||
commonEventFunction(e, "enter")
|
||||
|
||||
def leaveFunction(e):
|
||||
commonEventFunction(e, "leave")
|
||||
|
||||
def buttonPressFunction(e):
|
||||
commonEventFunction(e, "button_press")
|
||||
|
||||
def buttonReleasedFunction(e):
|
||||
commonEventFunction(e, "button_release")
|
||||
|
||||
bindEnterAndLeaveFunction([compact_mode_icon_widget, frame_widget, compact_mode_frame_widget, label_widget, selected_mark_widget, switch_box_widget._canvas, switch_box_widget._bg_canvas], enterFunction, leaveFunction)
|
||||
|
||||
bindButtonPressAndReleaseFunction([compact_mode_icon_widget, frame_widget, compact_mode_frame_widget, label_widget, selected_mark_widget, switch_box_widget._canvas, switch_box_widget._bg_canvas], buttonPressFunction, buttonReleasedFunction)
|
||||
|
||||
callback = partial(toggleSidebarFeatureSelectedMarkIfTurnedOn, mark_widget=selected_mark_widget)
|
||||
frame_widget.markToggleManually = callback
|
||||
|
||||
row+=1
|
||||
@@ -0,0 +1,271 @@
|
||||
from customtkinter import CTkFont, CTkFrame, CTkLabel, CTkImage
|
||||
|
||||
from ....ui_utils import bindEnterAndLeaveColor, bindButtonPressColor, bindButtonReleaseFunction, switchActiveTabAndPassiveTab, switchTabsColor, createOptionMenuBox
|
||||
|
||||
from utils import callFunctionIfCallable
|
||||
|
||||
|
||||
def createSidebarLanguagesSettings(settings, main_window, view_variable):
|
||||
|
||||
|
||||
def switchActiveAndPassivePresetsTabsColor(target_active_widget):
|
||||
quick_setting_tabs = [
|
||||
getattr(main_window, "sls__presets_button_1"),
|
||||
getattr(main_window, "sls__presets_button_2"),
|
||||
getattr(main_window, "sls__presets_button_3")
|
||||
]
|
||||
|
||||
switchTabsColor(
|
||||
target_widget=target_active_widget,
|
||||
tab_buttons=quick_setting_tabs,
|
||||
active_bg_color=settings.ctm.SLS__PRESETS_TAB_BG_ACTIVE_COLOR,
|
||||
active_text_color=settings.ctm.SLS__PRESETS_TAB_ACTIVE_TEXT_COLOR,
|
||||
passive_bg_color=settings.ctm.SLS__PRESETS_TAB_BG_PASSIVE_COLOR,
|
||||
passive_text_color=settings.ctm.SLS__PRESETS_TAB_ACTIVE_TEXT_COLOR_PASSIVE
|
||||
)
|
||||
|
||||
def switchPresetTabFunction(target_active_widget):
|
||||
switchActiveAndPassivePresetsTabsColor(target_active_widget)
|
||||
switchActiveTabAndPassiveTab(target_active_widget, main_window.current_active_preset_tab, main_window.current_active_preset_tab.passive_function, settings.ctm.SLS__PRESETS_TAB_BG_HOVERED_COLOR, settings.ctm.SLS__PRESETS_TAB_BG_CLICKED_COLOR, settings.ctm.SLS__PRESETS_TAB_BG_PASSIVE_COLOR)
|
||||
main_window.current_active_preset_tab = target_active_widget
|
||||
|
||||
|
||||
|
||||
def switchToPreset1(e):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SELECTED_LANGUAGE_PRESET_TAB, "1")
|
||||
target_active_widget = getattr(main_window, "sls__presets_button_1")
|
||||
switchPresetTabFunction(target_active_widget)
|
||||
|
||||
def switchToPreset2(e):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SELECTED_LANGUAGE_PRESET_TAB, "2")
|
||||
target_active_widget = getattr(main_window, "sls__presets_button_2")
|
||||
switchPresetTabFunction(target_active_widget)
|
||||
|
||||
def switchToPreset3(e):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_SELECTED_LANGUAGE_PRESET_TAB, "3")
|
||||
target_active_widget = getattr(main_window, "sls__presets_button_3")
|
||||
switchPresetTabFunction(target_active_widget)
|
||||
|
||||
|
||||
|
||||
def createLanguageSettingBox(parent_widget, var_title_text, title_text_attr_name, arrow_img_attr_name, open_selectable_language_window_command, variable):
|
||||
sls__box = CTkFrame(parent_widget, corner_radius=0, fg_color=settings.ctm.SLS__BOX_BG_COLOR, width=0, height=0)
|
||||
|
||||
sls__box.grid_columnconfigure(1, weight=1)
|
||||
|
||||
sls__box_wrapper = CTkFrame(sls__box, corner_radius=0, fg_color=settings.ctm.SLS__BOX_BG_COLOR, width=0, height=0)
|
||||
sls__box_wrapper.grid(row=2, column=1, padx=settings.uism.SLS__BOX_IPADX, pady=settings.uism.SLS__BOX_IPADY, sticky="ew")
|
||||
|
||||
sls__box_wrapper.grid_columnconfigure(0, weight=1)
|
||||
|
||||
|
||||
sls__box_label_wrapper = CTkFrame(sls__box_wrapper, corner_radius=0, fg_color=settings.ctm.SLS__BOX_BG_COLOR, width=0, height=0)
|
||||
sls__box_label_wrapper.grid(row=0, column=0)
|
||||
|
||||
sls__box_label_wrapper.grid_columnconfigure((0,2), weight=1)
|
||||
sls__label = CTkLabel(
|
||||
sls__box_label_wrapper,
|
||||
textvariable=var_title_text,
|
||||
height=0,
|
||||
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.SLS__BOX_SECTION_TITLE_FONT_SIZE, weight="normal"),
|
||||
text_color=settings.ctm.SLS__BOX_SECTION_TITLE_TEXT_COLOR
|
||||
)
|
||||
sls__label.grid(row=0, column=1, pady=(0,settings.uism.SLS__BOX_SECTION_TITLE_BOTTOM_PADY))
|
||||
setattr(main_window, title_text_attr_name, sls__label)
|
||||
|
||||
|
||||
|
||||
|
||||
sls__box_optionmenu_wrapper = CTkFrame(sls__box_wrapper, corner_radius=0, fg_color=settings.ctm.SLS__BOX_BG_COLOR, width=0, height=0)
|
||||
sls__box_optionmenu_wrapper.grid(row=1, column=0, sticky="ew")
|
||||
|
||||
sls__box_optionmenu_wrapper.grid_columnconfigure(0, weight=1)
|
||||
(sls__selected_language_box, optionmenu_label_widget, optionmenu_img_widget) = createOptionMenuBox(
|
||||
parent_widget=sls__box_optionmenu_wrapper,
|
||||
optionmenu_bg_color=settings.ctm.SLS__OPTIONMENU_BG_COLOR,
|
||||
optionmenu_hovered_bg_color=settings.ctm.SLS__OPTIONMENU_HOVERED_BG_COLOR,
|
||||
optionmenu_clicked_bg_color=settings.ctm.SLS__OPTIONMENU_CLICKED_BG_COLOR,
|
||||
optionmenu_ipadx=(0,0),
|
||||
optionmenu_ipady=settings.uism.SLS__BOX_OPTION_MENU_IPADY,
|
||||
variable=variable,
|
||||
font_family=settings.FONT_FAMILY,
|
||||
font_size=settings.uism.SLS__BOX_OPTION_MENU_FONT_SIZE,
|
||||
text_color=settings.ctm.LABELS_TEXT_COLOR,
|
||||
image_file=settings.image_file.ARROW_LEFT.rotate(180),
|
||||
image_size=settings.uism.SLS__BOX_OPTION_MENU_ARROW_IMAGE_SIZE,
|
||||
optionmenu_clicked_command=open_selectable_language_window_command,
|
||||
|
||||
optionmenu_position="center",
|
||||
setattr_widget=main_window,
|
||||
image_widget_attr_name=arrow_img_attr_name,
|
||||
)
|
||||
sls__selected_language_box.grid(row=0, column=0, sticky="ew")
|
||||
|
||||
sls__box_optionmenu_wrapper_fix_1px_bug = CTkFrame(optionmenu_label_widget, corner_radius=0, width=0, height=0)
|
||||
sls__box_optionmenu_wrapper_fix_1px_bug.grid(row=0, column=1, sticky="ns")
|
||||
|
||||
return sls__box
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Sidebar Languages Settings, SLS
|
||||
main_window.sls__container = CTkFrame(main_window.sidebar_bg_container, corner_radius=0, fg_color=settings.ctm.SIDEBAR_BG_COLOR, width=0, height=0)
|
||||
|
||||
main_window.sls__container.grid(row=2, column=0, sticky="new")
|
||||
|
||||
main_window.sls__container.grid_columnconfigure(0, weight=1)
|
||||
|
||||
|
||||
main_window.sls__container_title = CTkLabel(main_window.sls__container,
|
||||
textvariable=view_variable.VAR_LABEL_LANGUAGE_SETTINGS,
|
||||
height=0,
|
||||
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.SLS__TITLE_FONT_SIZE, weight="normal"),
|
||||
text_color=settings.ctm.SLS__TITLE_TEXT_COLOR
|
||||
)
|
||||
main_window.sls__container_title.grid(row=0, column=0, pady=settings.uism.SLS__TITLE_PADY, sticky="nsew")
|
||||
|
||||
|
||||
|
||||
# Presets buttons
|
||||
main_window.sidebar_bg_container.grid_rowconfigure(2, weight=1)
|
||||
main_window.sls__presets_buttons_container = CTkFrame(main_window.sls__container, corner_radius=0, fg_color=settings.ctm.SIDEBAR_BG_COLOR, width=0, height=settings.uism.SLS__PRESET_TAB_NUMBER_HEIGHT)
|
||||
main_window.sls__presets_buttons_container.grid(row=1, column=0, sticky="nsew")
|
||||
|
||||
main_window.sls__presets_buttons_box = CTkFrame(main_window.sls__presets_buttons_container, corner_radius=0, fg_color=settings.ctm.SIDEBAR_BG_COLOR, width=0, height=0)
|
||||
main_window.sls__presets_buttons_box.place(relwidth=1, relx=0, rely=1.15, anchor="sw")
|
||||
|
||||
main_window.sls__presets_buttons_box.grid_columnconfigure((0,1,2), weight=1)
|
||||
|
||||
|
||||
preset_tabs_settings = [
|
||||
{
|
||||
"preset_tab_attr_name": "sls__presets_button_1",
|
||||
"command": switchToPreset1,
|
||||
"text": "1",
|
||||
},
|
||||
{
|
||||
"preset_tab_attr_name": "sls__presets_button_2",
|
||||
"command": switchToPreset2,
|
||||
"text": "2",
|
||||
},
|
||||
{
|
||||
"preset_tab_attr_name": "sls__presets_button_3",
|
||||
"command": switchToPreset3,
|
||||
"text": "3",
|
||||
},
|
||||
]
|
||||
|
||||
column=0
|
||||
for preset_tab_settings in preset_tabs_settings:
|
||||
preset_tab_attr_name = preset_tab_settings["preset_tab_attr_name"]
|
||||
command = preset_tab_settings["command"]
|
||||
text = preset_tab_settings["text"]
|
||||
|
||||
setattr(
|
||||
main_window,
|
||||
preset_tab_attr_name,
|
||||
CTkFrame(
|
||||
main_window.sls__presets_buttons_box,
|
||||
corner_radius=settings.uism.SLS__PRESET_TAB_NUMBER_CORNER_RADIUS,
|
||||
fg_color=settings.ctm.SLS__PRESETS_TAB_BG_PASSIVE_COLOR,
|
||||
width=0,
|
||||
height=settings.uism.SLS__PRESET_TAB_NUMBER_ADJUSTED_HEIGHT,
|
||||
cursor="hand2",
|
||||
)
|
||||
)
|
||||
parent_widget = getattr(main_window, preset_tab_attr_name)
|
||||
parent_widget.grid(row=0, column=column, sticky="ew")
|
||||
|
||||
label_widget = CTkLabel(
|
||||
parent_widget,
|
||||
text=text,
|
||||
height=0,
|
||||
fg_color=settings.ctm.SLS__PRESETS_TAB_BG_PASSIVE_COLOR,
|
||||
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.SLS__PRESET_TAB_NUMBER_FONT_SIZE, weight="bold"),
|
||||
anchor="center",
|
||||
text_color=settings.ctm.SLS__PRESETS_TAB_ACTIVE_TEXT_COLOR_PASSIVE
|
||||
)
|
||||
label_widget.place(relx=0.5, rely=0.44, anchor="center")
|
||||
|
||||
|
||||
|
||||
bindEnterAndLeaveColor([parent_widget, label_widget], settings.ctm.SLS__PRESETS_TAB_BG_HOVERED_COLOR, settings.ctm.SLS__PRESETS_TAB_BG_PASSIVE_COLOR)
|
||||
bindButtonPressColor([parent_widget, label_widget], settings.ctm.SLS__PRESETS_TAB_BG_CLICKED_COLOR, settings.ctm.SLS__PRESETS_TAB_BG_PASSIVE_COLOR)
|
||||
|
||||
parent_widget.passive_function = command
|
||||
bindButtonReleaseFunction([parent_widget, label_widget], command)
|
||||
|
||||
column+=1
|
||||
|
||||
|
||||
def callbackOpenSelectableYourLanguageWindow(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_OPEN_SELECTABLE_YOUR_LANGUAGE_WINDOW, value)
|
||||
|
||||
|
||||
def callbackOpenSelectableTargetLanguageWindow(value):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_OPEN_SELECTABLE_TARGET_LANGUAGE_WINDOW, value)
|
||||
|
||||
# Language Settings BOX
|
||||
main_window.sls__box_frame = CTkFrame(main_window.sls__container, corner_radius=0, fg_color=settings.ctm.SLS__BG_COLOR, width=0, height=0)
|
||||
main_window.sls__box_frame.grid(row=2, column=0, sticky="ew")
|
||||
main_window.sls__box_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# Your language
|
||||
main_window.sls__box_your_language = createLanguageSettingBox(
|
||||
parent_widget=main_window.sls__box_frame,
|
||||
var_title_text=view_variable.VAR_LABEL_YOUR_LANGUAGE,
|
||||
title_text_attr_name="sls__title_text_your_language",
|
||||
arrow_img_attr_name="sls__arrow_img_your_language",
|
||||
open_selectable_language_window_command=callbackOpenSelectableYourLanguageWindow,
|
||||
variable=view_variable.VAR_YOUR_LANGUAGE
|
||||
)
|
||||
main_window.sls__box_your_language.grid(row=2, column=0, pady=(settings.uism.SLS__BOX_TOP_PADY,0),sticky="ew")
|
||||
|
||||
|
||||
# Both direction arrow icon
|
||||
main_window.sls__arrow_direction_box = CTkFrame(main_window.sls__box_frame, corner_radius=0, fg_color=settings.ctm.SLS__BG_COLOR, width=0, height=0)
|
||||
main_window.sls__arrow_direction_box.grid(row=3, column=0, pady=settings.uism.SLS__BOX_ARROWS_PADY,sticky="ew")
|
||||
|
||||
main_window.sls__arrow_direction_box.grid_columnconfigure((0,4), weight=1)
|
||||
|
||||
main_window.sls__both_direction_up = CTkLabel(
|
||||
main_window.sls__arrow_direction_box,
|
||||
text=None,
|
||||
height=0,
|
||||
image=CTkImage((settings.image_file.NARROW_ARROW_DOWN).rotate(180),size=settings.uism.SLS__BOX_ARROWS_IMAGE_SIZE)
|
||||
|
||||
)
|
||||
main_window.sls__both_direction_up.grid(row=0, column=1, pady=0)
|
||||
|
||||
main_window.sls__both_direction_desc = CTkLabel(
|
||||
main_window.sls__arrow_direction_box,
|
||||
textvariable=view_variable.VAR_LABEL_BOTH_DIRECTION_DESC,
|
||||
height=0,
|
||||
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.SLS__BOX_ARROWS_DESC_FONT_SIZE, weight="normal"),
|
||||
text_color=settings.ctm.SLS__BOX_ARROWS_TEXT_COLOR,
|
||||
)
|
||||
main_window.sls__both_direction_desc.grid(row=0, column=2, padx=settings.uism.SLS__BOX_ARROWS_DESC_PADX)
|
||||
|
||||
main_window.sls__both_direction_label_down = CTkLabel(
|
||||
main_window.sls__arrow_direction_box,
|
||||
text=None,
|
||||
height=0,
|
||||
image=CTkImage((settings.image_file.NARROW_ARROW_DOWN).rotate(0),size=settings.uism.SLS__BOX_ARROWS_IMAGE_SIZE)
|
||||
|
||||
)
|
||||
main_window.sls__both_direction_label_down.grid(row=0, column=3)
|
||||
|
||||
|
||||
|
||||
# Target language
|
||||
main_window.sls__box_target_language = createLanguageSettingBox(
|
||||
parent_widget=main_window.sls__box_frame,
|
||||
var_title_text=view_variable.VAR_LABEL_TARGET_LANGUAGE,
|
||||
title_text_attr_name="sls__title_text_target_language",
|
||||
arrow_img_attr_name="sls__arrow_img_target_language",
|
||||
open_selectable_language_window_command=callbackOpenSelectableTargetLanguageWindow,
|
||||
variable=view_variable.VAR_TARGET_LANGUAGE
|
||||
)
|
||||
main_window.sls__box_target_language.grid(row=4, column=0, sticky="ew")
|
||||
34
vrct_gui/main_window/widgets/create_entry_message_box.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from customtkinter import CTkFont, CTkFrame, CTkEntry
|
||||
|
||||
def createEntryMessageBox(settings, main_window):
|
||||
main_window.main_entry_message_container = CTkFrame(main_window.main_bg_container, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=0)
|
||||
main_window.main_entry_message_container.grid(row=2, column=0, sticky="ew")
|
||||
|
||||
|
||||
main_window.main_entry_message_container.grid_columnconfigure(0, weight=1)
|
||||
main_window.entry_message_box = CTkEntry(
|
||||
main_window.main_entry_message_container,
|
||||
border_color=settings.ctm.TEXTBOX_ENTRY_BORDER_COLOR,
|
||||
fg_color=settings.ctm.TEXTBOX_ENTRY_BG_COLOR,
|
||||
text_color=settings.ctm.TEXTBOX_ENTRY_TEXT_COLOR,
|
||||
placeholder_text="Enter your message...",
|
||||
placeholder_text_color=settings.ctm.TEXTBOX_ENTRY_PLACEHOLDER_COLOR,
|
||||
height=settings.uism.TEXTBOX_ENTRY_HEIGHT,
|
||||
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TEXTBOX_ENTRY_FONT_SIZE, weight="normal"),
|
||||
)
|
||||
main_window.entry_message_box.grid(row=0, column=0, padx=settings.uism.TEXTBOX_ENTRY_PADX, pady=settings.uism.TEXTBOX_ENTRY_PADY, sticky="nsew")
|
||||
main_window.entry_message_box._entry.grid(padx=settings.uism.TEXTBOX_ENTRY_IPADX, pady=settings.uism.TEXTBOX_ENTRY_IPADY)
|
||||
|
||||
|
||||
def messageBoxAnyKeyPress(e):
|
||||
BREAK_KEYSYM_LIST = [
|
||||
"Delete", "Select", "Up", "Down", "Next", "End", "Print",
|
||||
"Prior","Insert","Home", "Left", "Clear", "Right", "Linefeed"
|
||||
]
|
||||
if e.keysym != "??":
|
||||
if len(e.char) != 0 and e.keysym in BREAK_KEYSYM_LIST:
|
||||
main_window.entry_message_box.insert("end", e.char)
|
||||
return "break"
|
||||
|
||||
main_window.entry_message_box.bind("<Any-KeyPress>", messageBoxAnyKeyPress)
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
|
||||
from customtkinter import CTkFrame, CTkLabel, CTkImage
|
||||
|
||||
from ...ui_utils import bindEnterAndLeaveColor, bindButtonPressColor, bindButtonReleaseFunction
|
||||
|
||||
from utils import callFunctionIfCallable
|
||||
|
||||
def createMinimizeSidebarButton(settings, main_window, view_variable):
|
||||
|
||||
def enableCompactMode(e):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_ENABLE_MAIN_WINDOW_SIDEBAR_COMPACT_MODE)
|
||||
|
||||
def disableCompactMode(e):
|
||||
callFunctionIfCallable(view_variable.CALLBACK_DISABLE_MAIN_WINDOW_SIDEBAR_COMPACT_MODE)
|
||||
|
||||
|
||||
|
||||
main_window.minimize_sidebar_button_container__for_closing = CTkFrame(main_window.main_topbar_container, corner_radius=0, fg_color=settings.ctm.MINIMIZE_SIDEBAR_BUTTON_BG_COLOR, cursor="hand2", width=0, height=0)
|
||||
main_window.minimize_sidebar_button_container__for_opening = CTkFrame(main_window.main_topbar_container, corner_radius=0, fg_color=settings.ctm.MINIMIZE_SIDEBAR_BUTTON_BG_COLOR, cursor="hand2", width=0, height=0)
|
||||
|
||||
|
||||
|
||||
|
||||
# For Closing [<]
|
||||
main_window.minimize_sidebar_button__for_closing = CTkLabel(
|
||||
main_window.minimize_sidebar_button_container__for_closing,
|
||||
text=None,
|
||||
corner_radius=0,
|
||||
height=0,
|
||||
image=CTkImage((settings.image_file.ARROW_LEFT),size=(settings.uism.MINIMIZE_SIDEBAR_BUTTON_ICON_SIZE_X,settings.uism.MINIMIZE_SIDEBAR_BUTTON_ICON_SIZE_Y))
|
||||
)
|
||||
|
||||
main_window.minimize_sidebar_button_container__for_closing.grid_rowconfigure((0,2), weight=1)
|
||||
main_window.minimize_sidebar_button__for_closing.grid(row=1, column=0, padx=0, pady=0)
|
||||
|
||||
|
||||
bindEnterAndLeaveColor([main_window.minimize_sidebar_button__for_closing, main_window.minimize_sidebar_button_container__for_closing], settings.ctm.MINIMIZE_SIDEBAR_BUTTON_HOVERED_BG_COLOR, settings.ctm.MINIMIZE_SIDEBAR_BUTTON_BG_COLOR)
|
||||
bindButtonPressColor([main_window.minimize_sidebar_button__for_closing, main_window.minimize_sidebar_button_container__for_closing], settings.ctm.MINIMIZE_SIDEBAR_BUTTON_CLICKED_BG_COLOR, settings.ctm.MINIMIZE_SIDEBAR_BUTTON_BG_COLOR)
|
||||
bindButtonReleaseFunction([main_window.minimize_sidebar_button_container__for_closing, main_window.minimize_sidebar_button__for_closing], enableCompactMode)
|
||||
|
||||
|
||||
|
||||
|
||||
# For Opening [>]
|
||||
main_window.minimize_sidebar_button__for_opening = CTkLabel(
|
||||
main_window.minimize_sidebar_button_container__for_opening,
|
||||
text=None,
|
||||
corner_radius=0,
|
||||
height=0,
|
||||
image=CTkImage((settings.image_file.ARROW_LEFT).rotate(180),size=(settings.uism.MINIMIZE_SIDEBAR_BUTTON_ICON_SIZE_X,settings.uism.MINIMIZE_SIDEBAR_BUTTON_ICON_SIZE_Y))
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
main_window.minimize_sidebar_button_container__for_opening.grid_rowconfigure((0,2), weight=1)
|
||||
main_window.minimize_sidebar_button__for_opening.grid(row=1, column=0, padx=0, pady=0)
|
||||
|
||||
|
||||
bindEnterAndLeaveColor([main_window.minimize_sidebar_button__for_opening, main_window.minimize_sidebar_button_container__for_opening], settings.ctm.MINIMIZE_SIDEBAR_BUTTON_HOVERED_BG_COLOR, settings.ctm.MINIMIZE_SIDEBAR_BUTTON_BG_COLOR)
|
||||
bindButtonPressColor([main_window.minimize_sidebar_button__for_opening, main_window.minimize_sidebar_button_container__for_opening], settings.ctm.MINIMIZE_SIDEBAR_BUTTON_CLICKED_BG_COLOR, settings.ctm.MINIMIZE_SIDEBAR_BUTTON_BG_COLOR)
|
||||
bindButtonReleaseFunction([main_window.minimize_sidebar_button_container__for_opening, main_window.minimize_sidebar_button__for_opening], disableCompactMode)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
main_window.minimize_sidebar_button_container__for_opening.grid(row=0, column=0, sticky="nsw")
|
||||
main_window.minimize_sidebar_button_container__for_closing.grid(row=0, column=0, sticky="nsw")
|
||||
main_window.minimize_sidebar_button_container__for_opening.grid_remove()
|
||||
64
vrct_gui/main_window/widgets/create_sidebar.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from customtkinter import CTkFrame, CTkLabel, CTkImage
|
||||
|
||||
from ...ui_utils import bindButtonFunctionAndColor
|
||||
from utils import callFunctionIfCallable
|
||||
|
||||
from ._create_sidebar import createSidebarFeatures, createSidebarLanguagesSettings
|
||||
|
||||
def createSidebar(settings, main_window, view_variable):
|
||||
# Side Bar Container
|
||||
main_window.toplevel_wrapper.grid_rowconfigure(0, weight=1)
|
||||
|
||||
main_window.sidebar_bg_container_wrapper = CTkFrame(main_window.toplevel_wrapper, corner_radius=0, fg_color=settings.ctm.SIDEBAR_BG_COLOR, width=0, height=0)
|
||||
main_window.sidebar_bg_container_wrapper.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
|
||||
main_window.sidebar_bg_container = CTkFrame(main_window.sidebar_bg_container_wrapper, corner_radius=0, fg_color=settings.ctm.SIDEBAR_BG_COLOR, width=0, height=0)
|
||||
main_window.sidebar_compact_mode_bg_container = CTkFrame(main_window.sidebar_bg_container_wrapper, corner_radius=0, fg_color=settings.ctm.SIDEBAR_BG_COLOR, width=0, height=0)
|
||||
|
||||
|
||||
main_window.sidebar_bg_container.grid_columnconfigure(0, weight=1, minsize=settings.uism.SIDEBAR_MIN_WIDTH)
|
||||
main_window.sidebar_compact_mode_bg_container.grid_columnconfigure(0, weight=1)
|
||||
|
||||
|
||||
createSidebarFeatures(settings, main_window, view_variable)
|
||||
createSidebarLanguagesSettings(settings, main_window, view_variable)
|
||||
|
||||
|
||||
main_window.sidebar_bg_container.grid(row=0, column=0, sticky="nsew")
|
||||
main_window.sidebar_compact_mode_bg_container.grid(row=0, column=0, sticky="nsew")
|
||||
main_window.sidebar_compact_mode_bg_container.grid_remove()
|
||||
|
||||
|
||||
# Config Button
|
||||
main_window.sidebar_bg_container_wrapper.grid_rowconfigure(3, weight=1)
|
||||
|
||||
main_window.sidebar_config_button_container = CTkFrame(main_window.sidebar_bg_container_wrapper, corner_radius=0, fg_color=settings.ctm.CONFIG_BUTTON_BG_COLOR, width=0, height=0)
|
||||
main_window.sidebar_config_button_container.grid(row=4, column=0, sticky="sew")
|
||||
|
||||
|
||||
main_window.sidebar_config_button_container.grid_columnconfigure(0, weight=1)
|
||||
main_window.sidebar_config_button_wrapper = CTkFrame(main_window.sidebar_config_button_container, corner_radius=settings.uism.SIDEBAR_CONFIG_BUTTON_CORNER_RADIUS, fg_color=settings.ctm.CONFIG_BUTTON_BG_COLOR, height=0, width=0, cursor="hand2")
|
||||
main_window.sidebar_config_button_wrapper.grid(row=0, column=0, padx=settings.uism.SIDEBAR_CONFIG_BUTTON_PADX, pady=settings.uism.SIDEBAR_CONFIG_BUTTON_PADY, sticky="ew")
|
||||
|
||||
|
||||
|
||||
|
||||
main_window.sidebar_config_button_wrapper.grid_columnconfigure(0, weight=1)
|
||||
|
||||
main_window.sidebar_config_button = CTkLabel(
|
||||
main_window.sidebar_config_button_wrapper,
|
||||
text=None,
|
||||
height=0,
|
||||
image=CTkImage(settings.image_file.CONFIGURATION_ICON, size=settings.uism.SIDEBAR_CONFIG_BUTTON_IMAGE_SIZE)
|
||||
)
|
||||
main_window.sidebar_config_button.grid(row=0, column=0, padx=0, pady=settings.uism.SIDEBAR_CONFIG_BUTTON_IPADY)
|
||||
|
||||
|
||||
bindButtonFunctionAndColor(
|
||||
target_widgets=[main_window.sidebar_config_button_wrapper, main_window.sidebar_config_button],
|
||||
enter_color=settings.ctm.CONFIG_BUTTON_HOVERED_BG_COLOR,
|
||||
leave_color=settings.ctm.CONFIG_BUTTON_BG_COLOR,
|
||||
clicked_color=settings.ctm.CONFIG_BUTTON_CLICKED_BG_COLOR,
|
||||
buttonReleasedFunction=lambda _e: callFunctionIfCallable(view_variable.CALLBACK_CLICKED_OPEN_CONFIG_WINDOW_BUTTON),
|
||||
)
|
||||
162
vrct_gui/main_window/widgets/create_textbox.py
Normal file
@@ -0,0 +1,162 @@
|
||||
from customtkinter import CTkFont, CTkFrame, CTkLabel, CTkTextbox
|
||||
|
||||
from ...ui_utils import bindEnterAndLeaveColor, bindButtonPressColor, bindButtonReleaseFunction, setDefaultActiveTab, switchActiveTabAndPassiveTab, switchTabsColor
|
||||
|
||||
|
||||
def createTextbox(settings, main_window, view_variable):
|
||||
|
||||
def switchTextbox(target_textbox_attr_name):
|
||||
main_window.current_active_textbox.grid_remove()
|
||||
main_window.current_active_textbox = getattr(main_window, target_textbox_attr_name)
|
||||
main_window.current_active_textbox.grid()
|
||||
|
||||
def switchToTextboxAll(e):
|
||||
target_active_widget = getattr(main_window, "textbox_tab_all")
|
||||
switchTextboxTabFunction(target_active_widget)
|
||||
switchTextbox("textbox_all")
|
||||
|
||||
def switchToTextboxSent(e):
|
||||
target_active_widget = getattr(main_window, "textbox_tab_sent")
|
||||
switchTextboxTabFunction(target_active_widget)
|
||||
switchTextbox("textbox_sent")
|
||||
|
||||
def switchToTextboxReceived(e):
|
||||
target_active_widget = getattr(main_window, "textbox_tab_received")
|
||||
switchTextboxTabFunction(target_active_widget)
|
||||
switchTextbox("textbox_received")
|
||||
|
||||
def switchToTextboxSystem(e):
|
||||
target_active_widget = getattr(main_window, "textbox_tab_system")
|
||||
switchTextboxTabFunction(target_active_widget)
|
||||
switchTextbox("textbox_system")
|
||||
|
||||
|
||||
def switchTextboxTabFunction(target_active_widget):
|
||||
switchActiveAndPassiveTextboxTabsColor(target_active_widget)
|
||||
switchActiveTabAndPassiveTab(target_active_widget, main_window.current_active_textbox_tab, main_window.current_active_textbox_tab.passive_function, settings.ctm.TEXTBOX_TAB_BG_HOVERED_COLOR, settings.ctm.TEXTBOX_TAB_BG_CLICKED_COLOR, settings.ctm.TEXTBOX_TAB_BG_PASSIVE_COLOR)
|
||||
main_window.current_active_textbox_tab = target_active_widget
|
||||
|
||||
def switchActiveAndPassiveTextboxTabsColor(target_active_widget):
|
||||
textbox_tabs = [
|
||||
getattr(main_window, "textbox_tab_all"),
|
||||
getattr(main_window, "textbox_tab_sent"),
|
||||
getattr(main_window, "textbox_tab_received"),
|
||||
getattr(main_window, "textbox_tab_system")
|
||||
]
|
||||
|
||||
switchTabsColor(
|
||||
target_widget=target_active_widget,
|
||||
tab_buttons=textbox_tabs,
|
||||
active_bg_color=settings.ctm.TEXTBOX_BG_COLOR,
|
||||
active_text_color=settings.ctm.SLS__PRESETS_TAB_ACTIVE_TEXT_COLOR,
|
||||
passive_bg_color=settings.ctm.TEXTBOX_TAB_BG_PASSIVE_COLOR,
|
||||
passive_text_color=settings.ctm.TEXTBOX_TAB_TEXT_PASSIVE_COLOR
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
# Text box
|
||||
main_window.main_bg_container.grid_rowconfigure(1, weight=1)
|
||||
main_window.main_textbox_container = CTkFrame(main_window.main_bg_container, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=0)
|
||||
main_window.main_textbox_container.grid(row=1, column=0, sticky="nsew")
|
||||
|
||||
main_window.main_textbox_container.grid_columnconfigure(0,weight=1)
|
||||
main_window.main_textbox_container.grid_rowconfigure(0,weight=1)
|
||||
|
||||
main_window.textbox_switch_tabs_container = CTkFrame(main_window.main_topbar_center_container, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=0)
|
||||
main_window.textbox_switch_tabs_container.place(relx=0.07, rely=1.15, anchor="sw")
|
||||
|
||||
main_window.textbox_switch_tabs_container.grid_columnconfigure((0,1,2,3), weight=1, uniform="textbox_tabs")
|
||||
|
||||
textbox_settings = [
|
||||
{
|
||||
"textbox_tab_attr_name": "textbox_tab_all",
|
||||
"command": switchToTextboxAll,
|
||||
"textbox_attr_name": "textbox_all",
|
||||
"textvariable": view_variable.VAR_LABEL_TEXTBOX_ALL
|
||||
},
|
||||
{
|
||||
"textbox_tab_attr_name": "textbox_tab_sent",
|
||||
"command": switchToTextboxSent,
|
||||
"textbox_attr_name": "textbox_sent",
|
||||
"textvariable": view_variable.VAR_LABEL_TEXTBOX_SENT
|
||||
},
|
||||
{
|
||||
"textbox_tab_attr_name": "textbox_tab_received",
|
||||
"command": switchToTextboxReceived,
|
||||
"textbox_attr_name": "textbox_received",
|
||||
"textvariable": view_variable.VAR_LABEL_TEXTBOX_RECEIVED
|
||||
},
|
||||
{
|
||||
"textbox_tab_attr_name": "textbox_tab_system",
|
||||
"command": switchToTextboxSystem,
|
||||
"textbox_attr_name": "textbox_system",
|
||||
"textvariable": view_variable.VAR_LABEL_TEXTBOX_SYSTEM
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
column=0
|
||||
for textbox_setting in textbox_settings:
|
||||
setattr(main_window, textbox_setting["textbox_tab_attr_name"],
|
||||
CTkFrame(
|
||||
main_window.textbox_switch_tabs_container,
|
||||
corner_radius=settings.uism.TEXTBOX_TAB_CORNER_RADIUS,
|
||||
fg_color=settings.ctm.TEXTBOX_TAB_BG_PASSIVE_COLOR,
|
||||
cursor="hand2",
|
||||
width=0,
|
||||
height=0
|
||||
)
|
||||
)
|
||||
target_widget = getattr(main_window, textbox_setting["textbox_tab_attr_name"])
|
||||
target_widget.grid(row=0, column=column, pady=0, padx=(0,2), sticky="ew")
|
||||
|
||||
|
||||
|
||||
target_widget.grid_columnconfigure((0,2), weight=1)
|
||||
setattr(main_window, "label_widget", CTkLabel(
|
||||
target_widget,
|
||||
textvariable=textbox_setting["textvariable"],
|
||||
corner_radius=0,
|
||||
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TEXTBOX_TAB_FONT_SIZE, weight="normal"),
|
||||
height=0,
|
||||
width=0,
|
||||
anchor="center",
|
||||
text_color=settings.ctm.TEXTBOX_TAB_TEXT_PASSIVE_COLOR,
|
||||
))
|
||||
label_widget = getattr(main_window, "label_widget")
|
||||
label_widget.grid(row=0, column=1, pady=settings.uism.TEXTBOX_TAB_PADY, padx=settings.uism.TEXTBOX_TAB_PADX)
|
||||
|
||||
bindEnterAndLeaveColor([target_widget, label_widget], settings.ctm.TEXTBOX_TAB_BG_HOVERED_COLOR, settings.ctm.TEXTBOX_TAB_BG_PASSIVE_COLOR)
|
||||
bindButtonPressColor([target_widget, label_widget], settings.ctm.TEXTBOX_TAB_BG_CLICKED_COLOR, settings.ctm.TEXTBOX_TAB_BG_PASSIVE_COLOR)
|
||||
|
||||
target_widget.passive_function = textbox_setting["command"]
|
||||
bindButtonReleaseFunction([target_widget, label_widget], textbox_setting["command"])
|
||||
|
||||
|
||||
|
||||
setattr(main_window, textbox_setting["textbox_attr_name"], CTkTextbox(
|
||||
main_window.main_textbox_container,
|
||||
corner_radius=settings.uism.TEXTBOX_CORNER_RADIUS,
|
||||
fg_color=settings.ctm.TEXTBOX_BG_COLOR,
|
||||
text_color="lime", # Textbox's text_color is set when printing. so this is for prevent from non-setting text_color like the gloves used in food factories are blue.
|
||||
wrap="word",
|
||||
))
|
||||
textbox_widget = getattr(main_window, textbox_setting["textbox_attr_name"])
|
||||
textbox_widget.grid(row=0, column=0, padx=settings.uism.TEXTBOX_PADX, pady=0, sticky="nsew")
|
||||
textbox_widget.grid_remove()
|
||||
textbox_widget.configure(state="disabled")
|
||||
|
||||
column+=1
|
||||
|
||||
# Set default active textbox tab
|
||||
main_window.current_active_textbox_tab = getattr(main_window, "textbox_tab_all")
|
||||
setDefaultActiveTab(
|
||||
active_tab_widget=main_window.current_active_textbox_tab,
|
||||
active_bg_color=settings.ctm.TEXTBOX_TAB_BG_ACTIVE_COLOR,
|
||||
active_text_color=settings.ctm.TEXTBOX_TAB_TEXT_ACTIVE_COLOR
|
||||
)
|
||||
|
||||
main_window.current_active_textbox = getattr(main_window, "textbox_all")
|
||||
main_window.current_active_textbox.grid()
|
||||
40
vrct_gui/splash_window/SplashWindow.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from customtkinter import CTkImage, CTkLabel, CTkToplevel
|
||||
from ..ui_utils import openImageKeepAspectRatio, getImageFileFromUiUtils, setGeometryToCenterOfScreen, fadeInAnimation
|
||||
|
||||
class SplashWindow(CTkToplevel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.withdraw()
|
||||
self.overrideredirect(True)
|
||||
self.configure(fg_color="#292a2d")
|
||||
self.title("SplashWindow")
|
||||
self.wm_attributes("-toolwindow", True)
|
||||
|
||||
|
||||
sw=self.winfo_screenwidth()
|
||||
# sh=self.winfo_screenheight()
|
||||
|
||||
pw=int(sw/4)
|
||||
|
||||
self.grid_columnconfigure((0,2), weight=1)
|
||||
self.grid_rowconfigure((0,2), weight=1)
|
||||
(img, desired_width, height) = openImageKeepAspectRatio(getImageFileFromUiUtils("vrct_logo_for_dark_mode.png"), pw)
|
||||
label = CTkLabel(
|
||||
self,
|
||||
text=None,
|
||||
height=0,
|
||||
fg_color="#292a2d",
|
||||
image=CTkImage(img, size=(desired_width, height))
|
||||
)
|
||||
label.grid(row=1, column=1, padx=int(desired_width/7), pady=int(height/3))
|
||||
|
||||
|
||||
def showSplash(self):
|
||||
self.attributes("-alpha", 0)
|
||||
self.deiconify()
|
||||
setGeometryToCenterOfScreen(root_widget=self)
|
||||
fadeInAnimation(self, steps=5, interval=0.02)
|
||||
|
||||
|
||||
def destroySplash(self):
|
||||
self.destroy()
|
||||
1
vrct_gui/splash_window/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .SplashWindow import *
|
||||