diff --git a/.gitignore b/.gitignore index 22ce9ed3..678a4536 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ \ No newline at end of file +logs/ \ No newline at end of file diff --git a/README.jp.md b/README.jp.md new file mode 100644 index 00000000..586912c5 --- /dev/null +++ b/README.jp.md @@ -0,0 +1,65 @@ +
+ +![](docs/vrct_logo.png) +[![GitHub release](https://img.shields.io/github/v/release/misyaguziya/VRCT.svg)](https://github.com/misyaguziya/VRCT/releases) +[![Downloads](https://img.shields.io/github/downloads/misyaguziya/VRCT/total)](https://github.com/misyaguziya/VRCT/releases) +[![Licence](https://img.shields.io/github/license/misyaguziya/VRCT)](https://github.com/misyaguziya/VRCT/blob/master/LICENSE) +[![Booth](https://img.shields.io/badge/Store-Booth.pm-red)](https://misyaguziya.booth.pm/items/5155325) + +| [English](./README.md) | **日本語** | + +

+VRCTは翻訳や文字起こしでVRChatの会話をサポートするソフトウェアです。 +

+ +![](docs/main_window.png) + +
+ +# ダウンロード&インストール +好きな場所からダウンロードしてください。 +- [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) +
+ +[![](https://img.youtube.com/vi/rUTad037n8Q/0.jpg)](https://www.youtube.com/watch?v=rUTad037n8Q) + +
+ +# 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の商標または登録商標です。 \ No newline at end of file diff --git a/README.md b/README.md index 20da4395..39faab7f 100644 --- a/README.md +++ b/README.md @@ -1,116 +1,65 @@
![](docs/vrct_logo.png) +[![GitHub release](https://img.shields.io/github/v/release/misyaguziya/VRCT.svg)](https://github.com/misyaguziya/VRCT/releases) +[![Downloads](https://img.shields.io/github/downloads/misyaguziya/VRCT/total)](https://github.com/misyaguziya/VRCT/releases) +[![Licence](https://img.shields.io/github/license/misyaguziya/VRCT)](https://github.com/misyaguziya/VRCT/blob/master/LICENSE) +[![Booth](https://img.shields.io/badge/Store-Booth.pm-red)](https://misyaguziya.booth.pm/items/5155325) -# VRCT (VRChat Chatbox Translator & Transcription) +| **English** | [日本語](./README.jp.md) | + +

+VRCT is software that supports VRChat conversations with translation and transcription. +

+ +![](docs/main_window.png)
-## 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) +
-### init -0. VRChatのOSCを有効にする(重要) +[![](https://img.youtube.com/vi/rUTad037n8Q/0.jpg)](https://www.youtube.com/watch?v=rUTad037n8Q) -(任意) -1. DeepLのAPIを使用するためにアカウント登録し、認証キーを取得する -2. ギアアイコンのボタンでconfigウィンドウを開く -3. ParameterタブのDeepL Auth Keyに認証キーを記載 -4. configウィンドウを閉じる +
-### 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開発, 翻訳(英語) +--- -レラ -- 翻訳(韓国語) \ No newline at end of file +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. \ No newline at end of file diff --git a/README.txt b/README.txt index 10647b74..a769afc6 100644 --- a/README.txt +++ b/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を開けるように変更 -- 文字起こし言語の表示を修正 -- いくつかのバグを修正 - -# 注意事項 -再配布とかはやめてね \ No newline at end of file +[2023-10-21: 2.0.0] v2.0.0 リリース \ No newline at end of file diff --git a/VRCT.py b/VRCT.py deleted file mode 100644 index b3d3cd92..00000000 --- a/VRCT.py +++ /dev/null @@ -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("", self.entry_message_box_press_key_enter) - self.entry_message_box.bind("", self.entry_message_box_press_key_any) - self.entry_message_box.bind("", 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) \ No newline at end of file diff --git a/batch/restart.bat b/batch/restart.bat new file mode 100644 index 00000000..b86be544 --- /dev/null +++ b/batch/restart.bat @@ -0,0 +1,4 @@ +@if not "%~0"=="%~dp0.\%~nx0" start /min cmd /c,"%~dp0.\%~nx0" %* & goto :eof + +taskkill /im %1 /F +START "" %1 \ No newline at end of file diff --git a/batch/update.bat b/batch/update.bat new file mode 100644 index 00000000..2a7fc7b2 --- /dev/null +++ b/batch/update.bat @@ -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 +) \ No newline at end of file diff --git a/build.bat b/build.bat new file mode 100644 index 00000000..6dcbbf71 --- /dev/null +++ b/build.bat @@ -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 \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 00000000..65f8fa15 --- /dev/null +++ b/config.py @@ -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() \ No newline at end of file diff --git a/controller.py b/controller.py new file mode 100644 index 00000000..d8dfc1fb --- /dev/null +++ b/controller.py @@ -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() \ No newline at end of file diff --git a/ctk_scrollable_dropdown.py b/ctk_scrollable_dropdown.py deleted file mode 100644 index 81b98205..00000000 --- a/ctk_scrollable_dropdown.py +++ /dev/null @@ -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('', lambda e: self._withdraw() if not self.disable else None, add="+") - self.attach.winfo_toplevel().bind('', lambda e: self._withdraw() if not self.disable else None, add="+") - self.attach.winfo_toplevel().bind("", lambda e: self._withdraw() if not self.disable else None, add="+") - self.attach.winfo_toplevel().bind("", lambda e: self._withdraw() if not self.disable else None, add="+") - self.attach.winfo_toplevel().bind("", 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('', lambda e: self._iconify(), add="+") - else: - self.attach.bind('', lambda e: self._iconify(), add="+") - - if self.attach.winfo_name().startswith("!ctkcombobox"): - self.attach._canvas.tag_bind("right_parts", "", lambda e: self._iconify()) - self.attach._canvas.tag_bind("dropdown_arrow", "", 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("", lambda e: self._iconify()) - self.attach._text_label.bind("", 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("", 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("", 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) diff --git a/docs/main_window.png b/docs/main_window.png new file mode 100644 index 00000000..eb2beb52 Binary files /dev/null and b/docs/main_window.png differ diff --git a/docs/vrct_logo.png b/docs/vrct_logo.png index de568b6e..11bf16b9 100644 Binary files a/docs/vrct_logo.png and b/docs/vrct_logo.png differ diff --git a/img/arrow_left_disabled.png b/img/arrow_left_disabled.png new file mode 100644 index 00000000..5b5f3afa Binary files /dev/null and b/img/arrow_left_disabled.png differ diff --git a/img/arrow_left_white.png b/img/arrow_left_white.png new file mode 100644 index 00000000..463d8a74 Binary files /dev/null and b/img/arrow_left_white.png differ diff --git a/img/configuration_icon_disabled.png b/img/configuration_icon_disabled.png new file mode 100644 index 00000000..03a4a217 Binary files /dev/null and b/img/configuration_icon_disabled.png differ diff --git a/img/configuration_icon_white.png b/img/configuration_icon_white.png new file mode 100644 index 00000000..ee6e050e Binary files /dev/null and b/img/configuration_icon_white.png differ diff --git a/img/foreground_icon_disabled.png b/img/foreground_icon_disabled.png new file mode 100644 index 00000000..91778b7b Binary files /dev/null and b/img/foreground_icon_disabled.png differ diff --git a/img/foreground_icon_white.png b/img/foreground_icon_white.png new file mode 100644 index 00000000..50370ad1 Binary files /dev/null and b/img/foreground_icon_white.png differ diff --git a/img/headphones_icon_disabled.png b/img/headphones_icon_disabled.png new file mode 100644 index 00000000..f81f812e Binary files /dev/null and b/img/headphones_icon_disabled.png differ diff --git a/img/headphones_icon_white.png b/img/headphones_icon_white.png new file mode 100644 index 00000000..54f7359d Binary files /dev/null and b/img/headphones_icon_white.png differ diff --git a/img/help_icon_disabled.png b/img/help_icon_disabled.png new file mode 100644 index 00000000..03e1c086 Binary files /dev/null and b/img/help_icon_disabled.png differ diff --git a/img/help_icon_white.png b/img/help_icon_white.png new file mode 100644 index 00000000..ee5cb936 Binary files /dev/null and b/img/help_icon_white.png differ diff --git a/img/mic_icon_disabled.png b/img/mic_icon_disabled.png new file mode 100644 index 00000000..542d2824 Binary files /dev/null and b/img/mic_icon_disabled.png differ diff --git a/img/mic_icon_white.png b/img/mic_icon_white.png new file mode 100644 index 00000000..e1837463 Binary files /dev/null and b/img/mic_icon_white.png differ diff --git a/img/narrow_arrow_down.png b/img/narrow_arrow_down.png new file mode 100644 index 00000000..309560c8 Binary files /dev/null and b/img/narrow_arrow_down.png differ diff --git a/img/refresh_icon.png b/img/refresh_icon.png new file mode 100644 index 00000000..c5acad15 Binary files /dev/null and b/img/refresh_icon.png differ diff --git a/img/translation_icon_disabled.png b/img/translation_icon_disabled.png new file mode 100644 index 00000000..533feb81 Binary files /dev/null and b/img/translation_icon_disabled.png differ diff --git a/img/translation_icon_white.png b/img/translation_icon_white.png new file mode 100644 index 00000000..706b0bbc Binary files /dev/null and b/img/translation_icon_white.png differ diff --git a/img/vrct_logo_for_dark_mode.png b/img/vrct_logo_for_dark_mode.png new file mode 100644 index 00000000..53add076 Binary files /dev/null and b/img/vrct_logo_for_dark_mode.png differ diff --git a/img/vrct_logo_for_light_mode.png b/img/vrct_logo_for_light_mode.png new file mode 100644 index 00000000..ad2b0b95 Binary files /dev/null and b/img/vrct_logo_for_light_mode.png differ diff --git a/img/vrct_logo_mark_black.ico b/img/vrct_logo_mark_black.ico new file mode 100644 index 00000000..7b711af3 Binary files /dev/null and b/img/vrct_logo_mark_black.ico differ diff --git a/img/vrct_logo_mark_black.png b/img/vrct_logo_mark_black.png new file mode 100644 index 00000000..4e4fce17 Binary files /dev/null and b/img/vrct_logo_mark_black.png differ diff --git a/img/vrct_logo_mark_black_icon.png b/img/vrct_logo_mark_black_icon.png new file mode 100644 index 00000000..1d73b8a8 Binary files /dev/null and b/img/vrct_logo_mark_black_icon.png differ diff --git a/img/vrct_logo_mark_white.png b/img/vrct_logo_mark_white.png new file mode 100644 index 00000000..f99621f7 Binary files /dev/null and b/img/vrct_logo_mark_white.png differ diff --git a/img/xsoverlay2.png b/img/xsoverlay2.png new file mode 100644 index 00000000..20b4021a Binary files /dev/null and b/img/xsoverlay2.png differ diff --git a/languages.py b/languages.py index 3862f4ff..4c7b8c94 100644 --- a/languages.py +++ b/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": "한국어(일부 지원)" # 新しい言語とキーを追加する場合はここに追記してください } \ No newline at end of file diff --git a/locales/en.yml b/locales/en.yml new file mode 100644 index 00000000..3e5c4aff --- /dev/null +++ b/locales/en.yml @@ -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 \ No newline at end of file diff --git a/locales/ja.yml b/locales/ja.yml new file mode 100644 index 00000000..952dbfc3 --- /dev/null +++ b/locales/ja.yml @@ -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 \ No newline at end of file diff --git a/locales/ko.yml b/locales/ko.yml new file mode 100644 index 00000000..ad08942f --- /dev/null +++ b/locales/ko.yml @@ -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 포트 \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 00000000..5db42c69 --- /dev/null +++ b/main.py @@ -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) \ No newline at end of file diff --git a/model.py b/model.py new file mode 100644 index 00000000..51a881dd --- /dev/null +++ b/model.py @@ -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() diff --git a/osc_tools.py b/models/osc/osc_tools.py similarity index 52% rename from osc_tools.py rename to models/osc/osc_tools.py index 7c7d9504..7cb926c2 100644 --- a/osc_tools.py +++ b/models/osc/osc_tools.py @@ -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() \ No newline at end of file + return server + +if __name__ == "__main__": + sendChangeVoice() + sendChangeVoice() \ No newline at end of file diff --git a/models/transcription/transcription_languages.py b/models/transcription/transcription_languages.py new file mode 100644 index 00000000..26f2c3f6 --- /dev/null +++ b/models/transcription/transcription_languages.py @@ -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" + }, +} \ No newline at end of file diff --git a/audio_recorder.py b/models/transcription/transcription_recorder.py similarity index 88% rename from audio_recorder.py rename to models/transcription/transcription_recorder.py index ab03d78b..9abe5eb4 100644 --- a/audio_recorder.py +++ b/models/transcription/transcription_recorder.py @@ -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() \ No newline at end of file + # self.adjustForNoise() \ No newline at end of file diff --git a/audio_transcriber.py b/models/transcription/transcription_transcriber.py similarity index 80% rename from audio_transcriber.py rename to models/transcription/transcription_transcriber.py index aadd6adf..b058f4ec 100644 --- a/audio_transcriber.py +++ b/models/transcription/transcription_transcriber.py @@ -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 \ No newline at end of file diff --git a/audio_utils.py b/models/transcription/transcription_utils.py similarity index 78% rename from audio_utils.py rename to models/transcription/transcription_utils.py index b66172c2..f40defeb 100644 --- a/audio_utils.py +++ b/models/transcription/transcription_utils.py @@ -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 \ No newline at end of file + return default_device + return {"name":"NoDevice"} \ No newline at end of file diff --git a/models/translation/translation_languages.py b/models/translation/translation_languages.py new file mode 100644 index 00000000..ae57d4cc --- /dev/null +++ b/models/translation/translation_languages.py @@ -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, +} \ No newline at end of file diff --git a/models/translation/translation_translator.py b/models/translation/translation_translator.py new file mode 100644 index 00000000..18d2c394 --- /dev/null +++ b/models/translation/translation_translator.py @@ -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 \ No newline at end of file diff --git a/notification.py b/models/xsoverlay/notification.py similarity index 92% rename from notification.py rename to models/xsoverlay/notification.py index 61e18725..7c179f5a 100644 --- a/notification.py +++ b/models/xsoverlay/notification.py @@ -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") \ No newline at end of file + xsoverlayForVRCT(content="notification test") \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 0b8705f5..a399733d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,8 @@ -pillow -PyAudioWPatch -python-osc -customtkinter -deepl -flashtext -pyyaml \ No newline at end of file +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 \ No newline at end of file diff --git a/translation.py b/translation.py deleted file mode 100644 index e251c8a9..00000000 --- a/translation.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/utils.py b/utils.py index ac2eb35d..6efe6d0a 100644 --- a/utils.py +++ b/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) \ No newline at end of file +def generatePercentageStringsList(start=40, end=200, step=10): + strings = [] + for percent in range(start, end + 1, step): + strings.append(f"{percent}%") + return strings \ No newline at end of file diff --git a/view.py b/view.py new file mode 100644 index 00000000..3e348a96 --- /dev/null +++ b/view.py @@ -0,0 +1,1302 @@ +from typing import Union +from os import path as os_path +from types import SimpleNamespace +from tkinter import font as tk_font +import webbrowser +import i18n + +from languages import selectable_languages + +from customtkinter import StringVar, IntVar, BooleanVar, END as CTK_END, get_appearance_mode +from vrct_gui.ui_managers import ColorThemeManager, ImageFileManager, UiScalingManager +from vrct_gui import vrct_gui +from utils import callFunctionIfCallable, generatePercentageStringsList + +from config import config + +class View(): + def __init__(self): + self.settings = SimpleNamespace() + # theme = get_appearance_mode() if config.APPEARANCE_THEME == "System" else config.APPEARANCE_THEME + theme = "Dark" + all_ctm = ColorThemeManager(theme) + all_uism = UiScalingManager(config.UI_SCALING) + image_file = ImageFileManager(theme) + + i18n.load_path.append(os_path.join(os_path.dirname(__file__), "locales")) + i18n.set("fallback", "en") # The fallback language is English. + i18n.set("skip_locale_root_data", True) + i18n.set("filename_format", "{locale}.{format}") + i18n.set("enable_memoization", True) + + i18n.set("locale", config.UI_LANGUAGE) + + self.restart_required_configs_pre_data = SimpleNamespace( + appearance_theme=config.APPEARANCE_THEME, + ui_scaling=config.UI_SCALING, + font_family=config.FONT_FAMILY, + ui_language=config.UI_LANGUAGE, + ) + + + common_args = { + "image_file": image_file, + "FONT_FAMILY": config.FONT_FAMILY, + } + + self.settings.main = SimpleNamespace( + ctm=all_ctm.main, + uism=all_uism.main, + **common_args + ) + + self.settings.config_window = SimpleNamespace( + ctm=all_ctm.config_window, + uism=all_uism.config_window, + **common_args + ) + + self.settings.selectable_language_window = SimpleNamespace( + ctm=all_ctm.selectable_language_window, + uism=all_uism.selectable_language_window, + **common_args + ) + + self.settings.main_window_cover = SimpleNamespace( + ctm=all_ctm.main_window_cover, + uism=all_uism.main_window_cover, + **common_args + ) + + self.settings.error_message_window = SimpleNamespace( + ctm=all_ctm.error_message_window, + uism=all_uism.error_message_window, + **common_args + ) + + self.settings.confirmation_modal = SimpleNamespace( + ctm=all_ctm.confirmation_modal, + uism=all_uism.confirmation_modal, + **common_args + ) + + self.view_variable = SimpleNamespace( + # Common + CALLBACK_RESTART_SOFTWARE=None, + CALLBACK_UPDATE_SOFTWARE=None, + + CALLBACK_WHEN_DETECT_WINDOW_OVERED_SIZE=self._showDisplayOverUiSizeConfirmationModal, + + # Confirmation Modal + CALLBACK_HIDE_CONFIRMATION_MODAL=None, + CALLBACK_ACCEPTED_CONFIRMATION_MODAL=None, + CALLBACK_DENIED_CONFIRMATION_MODAL=None, + VAR_MESSAGE_CONFIRMATION_MODAL=StringVar(value=""), + VAR_LABEL_CONFIRMATION_MODAL_DENY_BUTTON=StringVar(value=""), + VAR_LABEL_CONFIRMATION_MODAL_ACCEPT_BUTTON=StringVar(value=""), + + # Open Config Window + CALLBACK_CLICKED_OPEN_CONFIG_WINDOW_BUTTON=self._openConfigWindow, + CALLBACK_CLICKED_CLOSE_CONFIG_WINDOW_BUTTON=self._closeConfigWindow, + CALLBACK_OPEN_CONFIG_WINDOW=None, + CALLBACK_CLOSE_CONFIG_WINDOW=None, + + # Open Help and Information Page + CALLBACK_CLICKED_HELP_AND_INFO=self.openWebPage_VrctDocuments, + + # Open Update Confirmation Modal + CALLBACK_CLICKED_UPDATE_AVAILABLE=self._showUpdateSoftwareConfirmationModal, + + + + # Main Window + # Sidebar + # Sidebar Compact Mode + IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE=config.IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE, + CALLBACK_TOGGLE_MAIN_WINDOW_SIDEBAR_COMPACT_MODE=None, + + # Sidebar Features + VAR_LABEL_TRANSLATION=StringVar(value=i18n.t("main_window.translation")), + CALLBACK_TOGGLE_TRANSLATION=None, + + VAR_LABEL_TRANSCRIPTION_SEND=StringVar(value=i18n.t("main_window.transcription_send")), + CALLBACK_TOGGLE_TRANSCRIPTION_SEND=None, + + VAR_LABEL_TRANSCRIPTION_RECEIVE=StringVar(value=i18n.t("main_window.transcription_receive")), + CALLBACK_TOGGLE_TRANSCRIPTION_RECEIVE=None, + + VAR_LABEL_FOREGROUND=StringVar(value=i18n.t("main_window.foreground")), + CALLBACK_TOGGLE_FOREGROUND=None, + + # Sidebar Language Settings + VAR_LABEL_LANGUAGE_SETTINGS=StringVar(value=i18n.t("main_window.language_settings")), + LIST_SELECTABLE_LANGUAGES=[], + CALLBACK_SELECTED_LANGUAGE_PRESET_TAB=None, + + VAR_LABEL_YOUR_LANGUAGE=StringVar(value=i18n.t("main_window.your_language")), + VAR_YOUR_LANGUAGE = StringVar(value="Japanese\n(Japan)"), + CALLBACK_OPEN_SELECTABLE_YOUR_LANGUAGE_WINDOW=None, + IS_OPENED_SELECTABLE_YOUR_LANGUAGE_WINDOW=False, + CALLBACK_SELECTED_YOUR_LANGUAGE=None, + + VAR_LABEL_BOTH_DIRECTION_DESC=StringVar(value=i18n.t("main_window.both_direction_desc")), + + VAR_LABEL_TARGET_LANGUAGE=StringVar(value=i18n.t("main_window.target_language")), + VAR_TARGET_LANGUAGE = StringVar(value="English\n(United States)"), + CALLBACK_OPEN_SELECTABLE_TARGET_LANGUAGE_WINDOW=None, + IS_OPENED_SELECTABLE_TARGET_LANGUAGE_WINDOW=False, + CALLBACK_SELECTED_TARGET_LANGUAGE=None, + + + VAR_LABEL_TEXTBOX_ALL=StringVar(value=i18n.t("main_window.textbox_tab_all")), + VAR_LABEL_TEXTBOX_SENT=StringVar(value=i18n.t("main_window.textbox_tab_sent")), + VAR_LABEL_TEXTBOX_RECEIVED=StringVar(value=i18n.t("main_window.textbox_tab_received")), + VAR_LABEL_TEXTBOX_SYSTEM=StringVar(value=i18n.t("main_window.textbox_tab_system")), + + VAR_UPDATE_AVAILABLE=StringVar(value=i18n.t("main_window.update_available")), + + + # Main Window Cover + VAR_LABEL_MAIN_WINDOW_COVER_MESSAGE=StringVar(value=""), + + # Selectable Language Window + VAR_TITLE_LABEL_SELECTABLE_LANGUAGE=StringVar(value=""), + VAR_GO_BACK_LABEL_SELECTABLE_LANGUAGE=StringVar(value=i18n.t("selectable_language_window.go_back_button")), + + + + # Config Window + ACTIVE_SETTING_BOX_TAB_ATTR_NAME="side_menu_tab_appearance", + CALLBACK_SELECTED_SETTING_BOX_TAB=None, + VAR_ERROR_MESSAGE=StringVar(value=""), + VAR_VERSION=StringVar(value=i18n.t("config_window.version", version=config.VERSION)), + VAR_CONFIG_WINDOW_TITLE=StringVar(value=i18n.t("config_window.config_title")), + VAR_CONFIG_WINDOW_COMPACT_MODE_LABEL=StringVar(value=i18n.t("config_window.compact_mode")), + VAR_CONFIG_WINDOW_RESTART_BUTTON_LABEL=StringVar(value=i18n.t("config_window.restart_message")), + + + # Side Menu Labels + VAR_SIDE_MENU_LABEL_APPEARANCE=StringVar(value=i18n.t("config_window.side_menu_labels.appearance")), + VAR_SIDE_MENU_LABEL_TRANSLATION=StringVar(value=i18n.t("config_window.side_menu_labels.translation")), + VAR_SIDE_MENU_LABEL_TRANSCRIPTION=StringVar(value=i18n.t("config_window.side_menu_labels.transcription")), + VAR_SECOND_TITLE_TRANSCRIPTION_MIC=StringVar(value=i18n.t("config_window.side_menu_labels.transcription_mic")), + VAR_SECOND_TITLE_TRANSCRIPTION_SPEAKER=StringVar(value=i18n.t("config_window.side_menu_labels.transcription_speaker")), + VAR_SIDE_MENU_LABEL_OTHERS=StringVar(value=i18n.t("config_window.side_menu_labels.others")), + VAR_SIDE_MENU_LABEL_ADVANCED_SETTINGS=StringVar(value=i18n.t("config_window.side_menu_labels.advanced_settings")), + + VAR_CURRENT_ACTIVE_CONFIG_TITLE=StringVar(value=""), + + # Appearance Tab + VAR_LABEL_TRANSPARENCY=StringVar(value=i18n.t("config_window.transparency.label")), + VAR_DESC_TRANSPARENCY=StringVar(value=i18n.t("config_window.transparency.desc")), + SLIDER_RANGE_TRANSPARENCY=(50, 100), + CALLBACK_SET_TRANSPARENCY=None, + VAR_TRANSPARENCY=IntVar(value=config.TRANSPARENCY), + CALLBACK_BUTTON_PRESS_TRANSPARENCY=self._closeTheCoverOfMainWindow, + CALLBACK_BUTTON_RELEASE_TRANSPARENCY=self._openTheCoverOfMainWindow, + + VAR_LABEL_APPEARANCE_THEME=StringVar(value=i18n.t("config_window.appearance_theme.label")), + VAR_DESC_APPEARANCE_THEME=StringVar(value=i18n.t("config_window.appearance_theme.desc")), + LIST_APPEARANCE_THEME=["Dark"], + # LIST_APPEARANCE_THEME=["Light", "Dark", "System"], + CALLBACK_SET_APPEARANCE_THEME=None, + VAR_APPEARANCE_THEME=StringVar(value="Dark"), + # VAR_APPEARANCE_THEME=StringVar(value=config.APPEARANCE_THEME), + + VAR_LABEL_UI_SCALING=StringVar(value=i18n.t("config_window.ui_size.label")), + VAR_DESC_UI_SCALING=None, + LIST_UI_SCALING=generatePercentageStringsList(start=40,end=200, step=10), + CALLBACK_SET_UI_SCALING=None, + VAR_UI_SCALING=StringVar(value=config.UI_SCALING), + + VAR_LABEL_FONT_FAMILY=StringVar(value=i18n.t("config_window.font_family.label")), + VAR_DESC_FONT_FAMILY=None, + LIST_FONT_FAMILY=self.getAvailableFonts(), + CALLBACK_SET_FONT_FAMILY=None, + VAR_FONT_FAMILY=StringVar(value=config.FONT_FAMILY), + + VAR_LABEL_UI_LANGUAGE=StringVar(value=i18n.t("config_window.ui_language.label")), + VAR_DESC_UI_LANGUAGE=None, + LIST_UI_LANGUAGE=list(selectable_languages.values()), + CALLBACK_SET_UI_LANGUAGE=None, + VAR_UI_LANGUAGE=StringVar(value=selectable_languages[config.UI_LANGUAGE]), + + + # Translation Tab + VAR_LABEL_DEEPL_AUTH_KEY=StringVar(value=i18n.t("config_window.deepl_auth_key.label")), + VAR_DESC_DEEPL_AUTH_KEY=None, + CALLBACK_SET_DEEPL_AUTH_KEY=None, + VAR_DEEPL_AUTH_KEY=StringVar(value=config.AUTH_KEYS["DeepL_API"]), + + + # Transcription Tab (Mic) + VAR_TAB_SECOND_LABEL_TRANSCRIPTION_MIC=StringVar(value=i18n.t("config_window.tab_transcription.label")), + VAR_LABEL_MIC_HOST=StringVar(value=i18n.t("config_window.mic_host.label")), + VAR_DESC_MIC_HOST=None, + LIST_MIC_HOST=[], + CALLBACK_SET_MIC_HOST=None, + VAR_MIC_HOST=StringVar(value=config.CHOICE_MIC_HOST), + + VAR_LABEL_MIC_DEVICE=StringVar(value=i18n.t("config_window.mic_device.label")), + VAR_DESC_MIC_DEVICE=None, + LIST_MIC_DEVICE=[], + CALLBACK_SET_MIC_DEVICE=None, + VAR_MIC_DEVICE=StringVar(value=config.CHOICE_MIC_DEVICE), + + + VAR_LABEL_MIC_DYNAMIC_ENERGY_THRESHOLD=StringVar(value=""), + VAR_DESC_MIC_DYNAMIC_ENERGY_THRESHOLD=StringVar(value=""), + CALLBACK_SET_MIC_DYNAMIC_ENERGY_THRESHOLD=None, + VAR_MIC_DYNAMIC_ENERGY_THRESHOLD=BooleanVar(value=config.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD), + + SLIDER_RANGE_MIC_ENERGY_THRESHOLD=(0, config.MAX_MIC_ENERGY_THRESHOLD), + CALLBACK_CHECK_MIC_THRESHOLD=None, + VAR_MIC_ENERGY_THRESHOLD__SLIDER=IntVar(value=config.INPUT_MIC_ENERGY_THRESHOLD), + VAR_MIC_ENERGY_THRESHOLD__ENTRY=StringVar(value=config.INPUT_MIC_ENERGY_THRESHOLD), + CALLBACK_FOCUS_OUT_MIC_ENERGY_THRESHOLD=self.setLatestConfigVariable_MicEnergyThreshold, + + + VAR_LABEL_MIC_RECORD_TIMEOUT=StringVar(value=i18n.t("config_window.mic_record_timeout.label")), + VAR_DESC_MIC_RECORD_TIMEOUT=StringVar(value=i18n.t("config_window.mic_record_timeout.desc")), + CALLBACK_SET_MIC_RECORD_TIMEOUT=None, + VAR_MIC_RECORD_TIMEOUT=StringVar(value=config.INPUT_MIC_RECORD_TIMEOUT), + CALLBACK_FOCUS_OUT_MIC_RECORD_TIMEOUT=self.setLatestConfigVariable_MicRecordTimeout, + + VAR_LABEL_MIC_PHRASE_TIMEOUT=StringVar(value=i18n.t("config_window.mic_phrase_timeout.label")), + VAR_DESC_MIC_PHRASE_TIMEOUT=StringVar(value=i18n.t("config_window.mic_phrase_timeout.desc")), + CALLBACK_SET_MIC_PHRASE_TIMEOUT=None, + VAR_MIC_PHRASE_TIMEOUT=StringVar(value=config.INPUT_MIC_PHRASE_TIMEOUT), + CALLBACK_FOCUS_OUT_MIC_PHRASE_TIMEOUT=self.setLatestConfigVariable_MicPhraseTimeout, + + VAR_LABEL_MIC_MAX_PHRASES=StringVar(value=i18n.t("config_window.mic_max_phrase.label")), + VAR_DESC_MIC_MAX_PHRASES=StringVar(value=i18n.t("config_window.mic_max_phrase.desc")), + CALLBACK_SET_MIC_MAX_PHRASES=None, + VAR_MIC_MAX_PHRASES=StringVar(value=config.INPUT_MIC_MAX_PHRASES), + CALLBACK_FOCUS_OUT_MIC_MAX_PHRASES=self.setLatestConfigVariable_MicMaxPhrases, + + VAR_LABEL_MIC_WORD_FILTER=StringVar(value=i18n.t("config_window.mic_word_filter.label")), + VAR_DESC_MIC_WORD_FILTER=StringVar(value=i18n.t("config_window.mic_word_filter.desc")), + CALLBACK_SET_MIC_WORD_FILTER=None, + VAR_MIC_WORD_FILTER=StringVar(value=",".join(config.INPUT_MIC_WORD_FILTER) if len(config.INPUT_MIC_WORD_FILTER) > 0 else ""), + + + # Transcription Tab (Speaker) + VAR_LABEL_SPEAKER_DYNAMIC_ENERGY_THRESHOLD=StringVar(value=""), + VAR_DESC_SPEAKER_DYNAMIC_ENERGY_THRESHOLD=StringVar(value=""), + CALLBACK_SET_SPEAKER_DYNAMIC_ENERGY_THRESHOLD=None, + VAR_SPEAKER_DYNAMIC_ENERGY_THRESHOLD=BooleanVar(value=config.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD), + + SLIDER_RANGE_SPEAKER_ENERGY_THRESHOLD=(0, config.MAX_SPEAKER_ENERGY_THRESHOLD), + CALLBACK_CHECK_SPEAKER_THRESHOLD=None, + VAR_SPEAKER_ENERGY_THRESHOLD__SLIDER=IntVar(value=config.INPUT_SPEAKER_ENERGY_THRESHOLD), + VAR_SPEAKER_ENERGY_THRESHOLD__ENTRY=StringVar(value=config.INPUT_SPEAKER_ENERGY_THRESHOLD), + CALLBACK_FOCUS_OUT_SPEAKER_ENERGY_THRESHOLD=self.setLatestConfigVariable_SpeakerEnergyThreshold, + + + VAR_LABEL_SPEAKER_RECORD_TIMEOUT=StringVar(value=i18n.t("config_window.speaker_record_timeout.label")), + VAR_DESC_SPEAKER_RECORD_TIMEOUT=StringVar(value=i18n.t("config_window.speaker_record_timeout.desc")), + CALLBACK_SET_SPEAKER_RECORD_TIMEOUT=None, + VAR_SPEAKER_RECORD_TIMEOUT=StringVar(value=config.INPUT_SPEAKER_RECORD_TIMEOUT), + CALLBACK_FOCUS_OUT_SPEAKER_RECORD_TIMEOUT=self.setLatestConfigVariable_SpeakerRecordTimeout, + + VAR_LABEL_SPEAKER_PHRASE_TIMEOUT=StringVar(value=i18n.t("config_window.speaker_phrase_timeout.label")), + VAR_DESC_SPEAKER_PHRASE_TIMEOUT=StringVar(value=i18n.t("config_window.speaker_phrase_timeout.desc")), + CALLBACK_SET_SPEAKER_PHRASE_TIMEOUT=None, + VAR_SPEAKER_PHRASE_TIMEOUT=StringVar(value=config.INPUT_SPEAKER_PHRASE_TIMEOUT), + CALLBACK_FOCUS_OUT_SPEAKER_PHRASE_TIMEOUT=self.setLatestConfigVariable_SpeakerPhraseTimeout, + + VAR_LABEL_SPEAKER_MAX_PHRASES=StringVar(value=i18n.t("config_window.speaker_max_phrase.label")), + VAR_DESC_SPEAKER_MAX_PHRASES=StringVar(value=i18n.t("config_window.speaker_max_phrase.desc")), + CALLBACK_SET_SPEAKER_MAX_PHRASES=None, + VAR_SPEAKER_MAX_PHRASES=StringVar(value=config.INPUT_SPEAKER_MAX_PHRASES), + CALLBACK_FOCUS_OUT_SPEAKER_MAX_PHRASES=self.setLatestConfigVariable_SpeakerMaxPhrases, + + + # Others Tab + VAR_LABEL_ENABLE_AUTO_CLEAR_MESSAGE_BOX=StringVar(value=i18n.t("config_window.auto_clear_the_message_box.label")), + VAR_DESC_ENABLE_AUTO_CLEAR_MESSAGE_BOX=None, + CALLBACK_SET_ENABLE_AUTO_CLEAR_MESSAGE_BOX=None, + VAR_ENABLE_AUTO_CLEAR_MESSAGE_BOX=BooleanVar(value=config.ENABLE_AUTO_CLEAR_MESSAGE_BOX), + + VAR_LABEL_ENABLE_NOTICE_XSOVERLAY=StringVar(value=i18n.t("config_window.notice_xsoverlay.label")), + VAR_DESC_ENABLE_NOTICE_XSOVERLAY=StringVar(value=i18n.t("config_window.notice_xsoverlay.desc")), + CALLBACK_SET_ENABLE_NOTICE_XSOVERLAY=None, + VAR_ENABLE_NOTICE_XSOVERLAY=BooleanVar(value=config.ENABLE_NOTICE_XSOVERLAY), + + VAR_LABEL_ENABLE_AUTO_EXPORT_MESSAGE_LOGS=StringVar(value=i18n.t("config_window.auto_export_message_logs.label")), + VAR_DESC_ENABLE_AUTO_EXPORT_MESSAGE_LOGS=StringVar(value=i18n.t("config_window.auto_export_message_logs.desc")), + CALLBACK_SET_ENABLE_AUTO_EXPORT_MESSAGE_LOGS=None, + VAR_ENABLE_AUTO_EXPORT_MESSAGE_LOGS=BooleanVar(value=config.ENABLE_LOGGER), + + + VAR_LABEL_MESSAGE_FORMAT=StringVar(value=i18n.t("config_window.message_format.label")), + VAR_DESC_MESSAGE_FORMAT=StringVar(value=i18n.t("config_window.message_format.desc")), + CALLBACK_SET_MESSAGE_FORMAT=None, + VAR_MESSAGE_FORMAT=StringVar(value=config.MESSAGE_FORMAT), + + + VAR_LABEL_ENABLE_SEND_MESSAGE_TO_VRC=StringVar(value=i18n.t("config_window.send_message_to_vrc.label")), + VAR_DESC_ENABLE_SEND_MESSAGE_TO_VRC=StringVar(value=i18n.t("config_window.send_message_to_vrc.desc")), + CALLBACK_SET_ENABLE_SEND_MESSAGE_TO_VRC=None, + VAR_ENABLE_SEND_MESSAGE_TO_VRC=BooleanVar(value=config.ENABLE_SEND_MESSAGE_TO_VRC), + + # [deprecated] + # VAR_LABEL_STARTUP_OSC_ENABLED_CHECK=StringVar(value=i18n.t("config_window.startup_osc_enabled_check.label")), + # VAR_DESC_STARTUP_OSC_ENABLED_CHECK=StringVar(value=i18n.t("config_window.startup_osc_enabled_check.desc")), + # CALLBACK_SET_STARTUP_OSC_ENABLED_CHECK=None, + # VAR_STARTUP_OSC_ENABLED_CHECK=BooleanVar(value=config.STARTUP_OSC_ENABLED_CHECK), + + + + + # Advanced Settings Tab + VAR_LABEL_OSC_IP_ADDRESS=StringVar(value=i18n.t("config_window.osc_ip_address.label")), + VAR_DESC_OSC_IP_ADDRESS=None, + CALLBACK_SET_OSC_IP_ADDRESS=None, + VAR_OSC_IP_ADDRESS=StringVar(value=config.OSC_IP_ADDRESS), + + VAR_LABEL_OSC_PORT=StringVar(value=i18n.t("config_window.osc_port.label")), + VAR_DESC_OSC_PORT=None, + CALLBACK_SET_OSC_PORT=None, + VAR_OSC_PORT=StringVar(value=config.OSC_PORT), + ) + + + + def register( + self, + common_registers=None, + window_action_registers=None, + main_window_registers=None, + config_window_registers=None + ): + + + if common_registers is not None: + self.view_variable.CALLBACK_UPDATE_SOFTWARE=common_registers.get("callback_update_software", None) + self.view_variable.CALLBACK_RESTART_SOFTWARE=common_registers.get("callback_restart_software", None) + + + if window_action_registers is not None: + self.view_variable.CALLBACK_OPEN_CONFIG_WINDOW=window_action_registers.get("callback_open_config_window", None) + self.view_variable.CALLBACK_CLOSE_CONFIG_WINDOW=window_action_registers.get("callback_close_config_window", None) + + + if main_window_registers is not None: + self.view_variable.CALLBACK_ENABLE_MAIN_WINDOW_SIDEBAR_COMPACT_MODE = main_window_registers.get("callback_enable_main_window_sidebar_compact_mode", None) + self.view_variable.CALLBACK_DISABLE_MAIN_WINDOW_SIDEBAR_COMPACT_MODE = main_window_registers.get("callback_disable_main_window_sidebar_compact_mode", None) + + + self.view_variable.CALLBACK_TOGGLE_TRANSLATION = main_window_registers.get("callback_toggle_translation", None) + self.view_variable.CALLBACK_TOGGLE_TRANSCRIPTION_SEND = main_window_registers.get("callback_toggle_transcription_send", None) + self.view_variable.CALLBACK_TOGGLE_TRANSCRIPTION_RECEIVE = main_window_registers.get("callback_toggle_transcription_receive", None) + self.view_variable.CALLBACK_TOGGLE_FOREGROUND = main_window_registers.get("callback_toggle_foreground", None) + + self.view_variable.CALLBACK_SELECTED_YOUR_LANGUAGE = main_window_registers.get("callback_your_language", None) + self.view_variable.CALLBACK_SELECTED_TARGET_LANGUAGE = main_window_registers.get("callback_target_language", None) + main_window_registers.get("values", None) and self.updateList_selectableLanguages(main_window_registers["values"]) + + self.view_variable.CALLBACK_SELECTED_LANGUAGE_PRESET_TAB = main_window_registers.get("callback_selected_language_preset_tab", None) + + + entry_message_box = getattr(vrct_gui, "entry_message_box") + entry_message_box.bind("", main_window_registers.get("message_box_bind_Return")) + entry_message_box.bind("", main_window_registers.get("message_box_bind_Any_KeyPress")) + + + entry_message_box.bind("", main_window_registers.get("message_box_bind_FocusIn")) + entry_message_box.bind("", main_window_registers.get("message_box_bind_FocusOut")) + + + self.updateGuiVariableByPresetTabNo(config.SELECTED_TAB_NO) + vrct_gui._setDefaultActiveLanguagePresetTab(tab_no=config.SELECTED_TAB_NO) + + self.view_variable.CALLBACK_OPEN_SELECTABLE_YOUR_LANGUAGE_WINDOW = self.openSelectableLanguagesWindow_YourLanguage + self.view_variable.CALLBACK_OPEN_SELECTABLE_TARGET_LANGUAGE_WINDOW = self.openSelectableLanguagesWindow_TargetLanguage + + + # Config Window + self.view_variable.CALLBACK_SELECTED_SETTING_BOX_TAB=self._updateActiveSettingBoxTabNo + + + if config_window_registers is not None: + # Compact Mode Switch + self.view_variable.CALLBACK_ENABLE_CONFIG_WINDOW_COMPACT_MODE = config_window_registers.get("callback_disable_config_window_compact_mode", None) + self.view_variable.CALLBACK_DISABLE_CONFIG_WINDOW_COMPACT_MODE = config_window_registers.get("callback_enable_config_window_compact_mode", None) + + + # Appearance Tab + self.view_variable.CALLBACK_SET_TRANSPARENCY = config_window_registers.get("callback_set_transparency", None) + + self.view_variable.CALLBACK_SET_APPEARANCE = config_window_registers.get("callback_set_appearance", None) + self.view_variable.CALLBACK_SET_UI_SCALING = config_window_registers.get("callback_set_ui_scaling", None) + self.view_variable.CALLBACK_SET_FONT_FAMILY = config_window_registers.get("callback_set_font_family", None) + self.view_variable.CALLBACK_SET_UI_LANGUAGE = config_window_registers.get("callback_set_ui_language", None) + + + # Translation Tab + self.view_variable.CALLBACK_SET_DEEPL_AUTHKEY = config_window_registers.get("callback_set_deepl_authkey", None) + + # Transcription Tab (Mic) + self.view_variable.CALLBACK_SET_MIC_HOST = config_window_registers.get("callback_set_mic_host", None) + config_window_registers.get("list_mic_host", None) and self.updateList_MicHost(config_window_registers["list_mic_host"]) + + self.view_variable.CALLBACK_SET_MIC_DEVICE = config_window_registers.get("callback_set_mic_device", None) + config_window_registers.get("list_mic_device", None) and self.updateList_MicDevice(config_window_registers["list_mic_device"]) + + self.view_variable.CALLBACK_SET_MIC_ENERGY_THRESHOLD = config_window_registers.get("callback_set_mic_energy_threshold", None) + self.view_variable.CALLBACK_SET_MIC_DYNAMIC_ENERGY_THRESHOLD = config_window_registers.get("callback_set_mic_dynamic_energy_threshold", None) + self.view_variable.CALLBACK_CHECK_MIC_THRESHOLD = config_window_registers.get("callback_check_mic_threshold", None) + self.view_variable.CALLBACK_SET_MIC_RECORD_TIMEOUT = config_window_registers.get("callback_set_mic_record_timeout", None) + self.view_variable.CALLBACK_SET_MIC_PHRASE_TIMEOUT = config_window_registers.get("callback_set_mic_phrase_timeout", None) + self.view_variable.CALLBACK_SET_MIC_MAX_PHRASES = config_window_registers.get("callback_set_mic_max_phrases", None) + self.view_variable.CALLBACK_SET_MIC_WORD_FILTER = config_window_registers.get("callback_set_mic_word_filter", None) + + # Transcription Tab (Speaker) + self.view_variable.CALLBACK_SET_SPEAKER_ENERGY_THRESHOLD = config_window_registers.get("callback_set_speaker_energy_threshold", None) + self.view_variable.CALLBACK_SET_SPEAKER_DYNAMIC_ENERGY_THRESHOLD = config_window_registers.get("callback_set_speaker_dynamic_energy_threshold", None) + self.view_variable.CALLBACK_CHECK_SPEAKER_THRESHOLD = config_window_registers.get("callback_check_speaker_threshold", None) + self.view_variable.CALLBACK_SET_SPEAKER_RECORD_TIMEOUT = config_window_registers.get("callback_set_speaker_record_timeout", None) + self.view_variable.CALLBACK_SET_SPEAKER_PHRASE_TIMEOUT = config_window_registers.get("callback_set_speaker_phrase_timeout", None) + self.view_variable.CALLBACK_SET_SPEAKER_MAX_PHRASES = config_window_registers.get("callback_set_speaker_max_phrases", None) + + # Others Tab + self.view_variable.CALLBACK_SET_ENABLE_AUTO_CLEAR_MESSAGE_BOX = config_window_registers.get("callback_set_enable_auto_clear_chatbox", None) + self.view_variable.CALLBACK_SET_ENABLE_NOTICE_XSOVERLAY = config_window_registers.get("callback_set_enable_notice_xsoverlay", None) + self.view_variable.CALLBACK_SET_ENABLE_AUTO_EXPORT_MESSAGE_LOGS = config_window_registers.get("callback_set_enable_auto_export_message_logs", None) + self.view_variable.CALLBACK_SET_MESSAGE_FORMAT = config_window_registers.get("callback_set_message_format", None) + + self.view_variable.CALLBACK_SET_ENABLE_SEND_MESSAGE_TO_VRC = config_window_registers.get("callback_set_enable_send_message_to_vrc", None) + # self.view_variable.CALLBACK_SET_STARTUP_OSC_ENABLED_CHECK = config_window_registers.get("callback_set_startup_osc_enabled_check", None) #[deprecated] + + # Advanced Settings Tab + self.view_variable.CALLBACK_SET_OSC_IP_ADDRESS = config_window_registers.get("callback_set_osc_ip_address", None) + self.view_variable.CALLBACK_SET_OSC_PORT = config_window_registers.get("callback_set_osc_port", None) + + # The initial processing after registration. + if config.IS_CONFIG_WINDOW_COMPACT_MODE is True: + self.enableConfigWindowCompactMode() + vrct_gui.config_window.setting_box_compact_mode_switch_box.select() + + vrct_gui._changeConfigWindowWidgetsStatus( + status="disabled", + target_names=[ + "sb__optionmenu_appearance_theme", + ] + ) + + + if config.CHOICE_MIC_HOST == "NoHost": + self.view_variable.VAR_MIC_HOST.set("No Mic Host Detected") + + if config.CHOICE_MIC_DEVICE == "NoDevice": + self.view_variable.VAR_MIC_DEVICE.set("No Mic Device Detected") + + if config.CHOICE_MIC_HOST == "NoHost" or config.CHOICE_MIC_DEVICE == "NoDevice": + vrct_gui._changeConfigWindowWidgetsStatus( + status="disabled", + target_names=[ + "sb__optionmenu_mic_host", + "sb__optionmenu_mic_device", + ] + ) + self.replaceMicThresholdCheckButton_Disabled() + + + if config.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD is True: + self.closeMicEnergyThresholdWidget() + else: + self.openMicEnergyThresholdWidget() + + if config.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD is True: + self.closeSpeakerEnergyThresholdWidget() + else: + self.openSpeakerEnergyThresholdWidget() + + + # Insert sample conversation for testing. + # self._insertSampleConversationToTextbox() + + + @staticmethod + def getAvailableFonts(): + available_fonts = list(tk_font.families()) + available_fonts.sort() + filtered_available_fonts = list(filter(lambda x: x.startswith("@") is False, available_fonts)) + return filtered_available_fonts + + @staticmethod + def openWebPage(url:str): + webbrowser.open_new_tab(url) + + def openWebPage_Booth(self): + self.openWebPage(config.BOOTH_URL) + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.opened_web_page_booth")) + + def openWebPage_VrctDocuments(self): + self.openWebPage(config.DOCUMENTS_URL) + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.opened_web_page_vrct_documents")) + + @staticmethod + def showUpdateAvailableButton(): + vrct_gui.update_available_container.grid() + + @staticmethod + def setMainWindowAllWidgetsStatusToNormal(): + vrct_gui._changeMainWindowWidgetsStatus("normal", "All") + + @staticmethod + def setMainWindowAllWidgetsStatusToDisabled(): + vrct_gui._changeMainWindowWidgetsStatus("disabled", "All") + + + + def foregroundOnIfForegroundEnabled(self): + if config.ENABLE_FOREGROUND: + self.foregroundOn() + + def foregroundOffIfForegroundEnabled(self): + if config.ENABLE_FOREGROUND: + self.foregroundOff() + + + @staticmethod + def foregroundOn(): + vrct_gui.attributes("-topmost", True) + + @staticmethod + def foregroundOff(): + vrct_gui.attributes("-topmost", False) + + + def _adjustUiSizeAndRestart(self): + current_percentage = int(config.UI_SCALING.replace("%","")) + target_percentage = current_percentage - 20 + if target_percentage >= 40 and str(target_percentage) + "%" in self.view_variable.LIST_UI_SCALING: + index = self.view_variable.LIST_UI_SCALING.index(str(target_percentage) + "%") + callFunctionIfCallable(self.view_variable.CALLBACK_SET_UI_SCALING, self.view_variable.LIST_UI_SCALING[index]) + callFunctionIfCallable(self.view_variable.CALLBACK_RESTART_SOFTWARE) + else: + self._hideConfirmationModal() + # ※Below 40% of the UI size is not supported, and we cannot handle it at this time. + + + + + + def _showDisplayOverUiSizeConfirmationModal(self): + self.foregroundOffIfForegroundEnabled() + + self.view_variable.VAR_LABEL_MAIN_WINDOW_COVER_MESSAGE.set("") + vrct_gui.main_window_cover.show() + + self.view_variable.CALLBACK_HIDE_CONFIRMATION_MODAL=self._hideConfirmationModal + self.view_variable.CALLBACK_ACCEPTED_CONFIRMATION_MODAL=self._adjustUiSizeAndRestart + self.view_variable.CALLBACK_DENIED_CONFIRMATION_MODAL=self._hideConfirmationModal + + self.view_variable.VAR_MESSAGE_CONFIRMATION_MODAL.set(i18n.t("main_window.confirmation_message.detected_over_ui_size", current_ui_size=config.UI_SCALING)) + self.view_variable.VAR_LABEL_CONFIRMATION_MODAL_DENY_BUTTON.set(i18n.t("main_window.confirmation_message.deny_adjust_ui_size")) + self.view_variable.VAR_LABEL_CONFIRMATION_MODAL_ACCEPT_BUTTON.set(i18n.t("main_window.confirmation_message.accept_adjust_ui_size")) + + vrct_gui.confirmation_modal.show(hide_title_bar=False, close_when_focusout=False) + + + + def _showUpdateSoftwareConfirmationModal(self): + self.foregroundOffIfForegroundEnabled() + + self.view_variable.VAR_LABEL_MAIN_WINDOW_COVER_MESSAGE.set("") + vrct_gui.main_window_cover.show() + + self.view_variable.CALLBACK_HIDE_CONFIRMATION_MODAL=self._hideConfirmationModal + self.view_variable.CALLBACK_ACCEPTED_CONFIRMATION_MODAL=self._startUpdateSoftware + self.view_variable.CALLBACK_DENIED_CONFIRMATION_MODAL=self._hideConfirmationModal + + self.view_variable.VAR_MESSAGE_CONFIRMATION_MODAL.set(i18n.t("main_window.confirmation_message.update_software")) + self.view_variable.VAR_LABEL_CONFIRMATION_MODAL_DENY_BUTTON.set(i18n.t("main_window.confirmation_message.deny_update_software")) + self.view_variable.VAR_LABEL_CONFIRMATION_MODAL_ACCEPT_BUTTON.set(i18n.t("main_window.confirmation_message.accept_update_software")) + vrct_gui.confirmation_modal.show() + + + + + + def showTheLimitOfTranslationEngineConfirmationModal(self): + self.foregroundOffIfForegroundEnabled() + + self.view_variable.VAR_LABEL_MAIN_WINDOW_COVER_MESSAGE.set("") + vrct_gui.main_window_cover.show() + + self.view_variable.CALLBACK_HIDE_CONFIRMATION_MODAL=self._hideInformationModal + self.view_variable.CALLBACK_ACCEPTED_CONFIRMATION_MODAL=self._hideInformationModal + # self.view_variable.CALLBACK_DENIED_CONFIRMATION_MODAL=self._hideConfirmationModal + + self.view_variable.VAR_MESSAGE_CONFIRMATION_MODAL.set(i18n.t("main_window.confirmation_message.translation_engine_limit_error")) + # self.view_variable.VAR_LABEL_CONFIRMATION_MODAL_DENY_BUTTON.set(i18n.t("main_window.confirmation_message.deny_update_software")) + self.view_variable.VAR_LABEL_CONFIRMATION_MODAL_ACCEPT_BUTTON.set(i18n.t("main_window.confirmation_message.accept_translation_engine_limit_error")) + vrct_gui.information_modal.show(hide_title_bar=False, close_when_focusout=False) + + + + def translationEngineLimitErrorProcess(self): + # turn off translation switch. + vrct_gui.translation_switch_box.deselect() + vrct_gui.translation_frame.markToggleManually(False) + + # disable translation feature. + vrct_gui._changeMainWindowWidgetsStatus("disabled", ["translation_switch"], to_hold_state=True) + + # print system message that mention to stopped translation feature. + view.printToTextbox_TranslationEngineLimitError() + view.showTheLimitOfTranslationEngineConfirmationModal() + + + + + + + + def _hideInformationModal(self): + vrct_gui.information_modal.hide() + vrct_gui.main_window_cover.hide() + self.foregroundOnIfForegroundEnabled() + + + def _hideConfirmationModal(self): + vrct_gui.confirmation_modal.hide() + vrct_gui.main_window_cover.hide() + self.foregroundOnIfForegroundEnabled() + + # def _deniedUpdateSoftware(self): + # self._hideConfirmationModal() + + def _startUpdateSoftware(self): + self.view_variable.VAR_MESSAGE_CONFIRMATION_MODAL.set(i18n.t("main_window.confirmation_message.updating")) + vrct_gui.confirmation_modal.hide_buttons() + vrct_gui.update() + vrct_gui.confirmation_modal.update() + callFunctionIfCallable(self.view_variable.CALLBACK_UPDATE_SOFTWARE) + + + + def _openConfigWindow(self): + self.view_variable.VAR_LABEL_MAIN_WINDOW_COVER_MESSAGE.set(i18n.t("main_window.cover_message")) + callFunctionIfCallable(self.view_variable.CALLBACK_OPEN_CONFIG_WINDOW) + vrct_gui._openConfigWindow() + + def _closeConfigWindow(self): + callFunctionIfCallable(self.view_variable.CALLBACK_CLOSE_CONFIG_WINDOW) + vrct_gui._closeConfigWindow() + + + + def _openTheCoverOfMainWindow(self): + vrct_gui.main_window_cover.show() + vrct_gui.config_window.lift() + + @staticmethod + def _closeTheCoverOfMainWindow(): + vrct_gui.main_window_cover.withdraw() + + def enableMainWindowSidebarCompactMode(self): + self.view_variable.IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE = True + vrct_gui._enableMainWindowSidebarCompactMode() + + def disableMainWindowSidebarCompactMode(self): + self.view_variable.IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE = False + vrct_gui._disableMainWindowSidebarCompactMode() + + def openSelectableLanguagesWindow_YourLanguage(self, _e): + self.view_variable.VAR_TITLE_LABEL_SELECTABLE_LANGUAGE.set(i18n.t("selectable_language_window.title_your_language")) + vrct_gui._openSelectableLanguagesWindow("your_language") + + def openSelectableLanguagesWindow_TargetLanguage(self, _e): + self.view_variable.VAR_TITLE_LABEL_SELECTABLE_LANGUAGE.set(i18n.t("selectable_language_window.title_target_language")) + vrct_gui._openSelectableLanguagesWindow("target_language") + + + def updateGuiVariableByPresetTabNo(self, tab_no:str): + self.view_variable.VAR_YOUR_LANGUAGE.set(config.SELECTED_TAB_YOUR_LANGUAGES[tab_no]) + self.view_variable.VAR_TARGET_LANGUAGE.set(config.SELECTED_TAB_TARGET_LANGUAGES[tab_no]) + + + def updateList_selectableLanguages(self, new_selectable_language_list:list): + self.view_variable.LIST_SELECTABLE_LANGUAGES = new_selectable_language_list + + + def printToTextbox_enableTranslation(self): + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.enabled_translation")) + def printToTextbox_disableTranslation(self): + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.disabled_translation")) + + def printToTextbox_enableTranscriptionSend(self): + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.enabled_voice2chatbox")) + def printToTextbox_disableTranscriptionSend(self): + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.disabled_voice2chatbox")) + + def printToTextbox_enableTranscriptionReceive(self): + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.enabled_speaker2log")) + def printToTextbox_disableTranscriptionReceive(self): + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.disabled_speaker2log")) + + def printToTextbox_enableForeground(self): + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.enabled_foreground")) + def printToTextbox_disableForeground(self): + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.disabled_foreground")) + + def printToTextbox_AuthenticationSuccess(self): + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.auth_key_success")) + def printToTextbox_AuthenticationError(self): + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.auth_key_error")) + + + def printToTextbox_TranscriptionSendNoDeviceError(self): + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.no_mic_device_detected_error")) + + def printToTextbox_TranscriptionReceiveNoDeviceError(self): + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.no_speaker_device_detected_error")) + + + def printToTextbox_TranslationEngineLimitError(self): + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.translation_engine_limit_error")) + + + # def printToTextbox_OSCError(self): [Deprecated] + # self._printToTextbox_Info("OSC is not enabled, please enable OSC and rejoin. or turn off the \"Send Message To VRChat\" setting") + + def printToTextbox_DetectedByWordFilter(self, detected_message): + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.detected_by_word_filter"), detected_message=detected_message) + + + + def printToTextbox_selectedYourLanguages(self, selected_your_language): + your_language = selected_your_language.replace("\n", " ") + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.selected_your_language", your_language=your_language)) + + def printToTextbox_selectedTargetLanguages(self, selected_target_language): + target_language = selected_target_language.replace("\n", " ") + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.selected_target_language", target_language=target_language)) + + def printToTextbox_changedLanguagePresetTab(self, tab_no:str): + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.switched_language_preset_tab", tab_no=tab_no)) + self.printToTextbox_latestSelectedLanguages() + + def printToTextbox_latestSelectedLanguages(self): + your_language = self.view_variable.VAR_YOUR_LANGUAGE.get().replace("\n", " ") + target_language = self.view_variable.VAR_TARGET_LANGUAGE.get().replace("\n", " ") + self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.latest_language_setting", your_language=your_language, target_language=target_language)) + + + @staticmethod + def _printToTextbox_Info(info_message): + vrct_gui._printToTextbox( + target_type="SYSTEM", + original_message=info_message, + # translated_message="", + ) + + + + def printToTextbox_SentMessage(self, original_message, translated_message): + self._printToTextbox_Sent(original_message, translated_message) + + @staticmethod + def _printToTextbox_Sent(original_message, translated_message): + vrct_gui._printToTextbox( + target_type="SENT", + original_message=original_message, + translated_message=translated_message, + ) + + + def printToTextbox_ReceivedMessage(self, original_message, translated_message): + self._printToTextbox_Received(original_message, translated_message) + + @staticmethod + def _printToTextbox_Received(original_message, translated_message): + vrct_gui._printToTextbox( + target_type="RECEIVED", + original_message=original_message, + translated_message=translated_message, + ) + + + @staticmethod + def getTextFromMessageBox(): + return vrct_gui.entry_message_box.get() + + @staticmethod + def clearMessageBox(): + vrct_gui.entry_message_box.delete(0, CTK_END) + + @staticmethod + def setMainWindowTransparency(transparency:float): + vrct_gui.wm_attributes("-alpha", transparency) + + + def enableConfigWindowCompactMode(self): + for additional_widget in vrct_gui.config_window.additional_widgets: + additional_widget.grid_remove() + + def disableConfigWindowCompactMode(self): + for additional_widget in vrct_gui.config_window.additional_widgets: + additional_widget.grid() + + + + def createGUI(self): + vrct_gui._createGUI(settings=self.settings, view_variable=self.view_variable) + + @staticmethod + def showGUI(): + vrct_gui._showGUI() + + @staticmethod + def startMainLoop(): + vrct_gui._showGUI() + vrct_gui._startMainLoop() + + + # Config Window + def showRestartButtonIfRequired(self, locale:Union[None,str]=None): + is_restart_required = not ( + self.restart_required_configs_pre_data.appearance_theme == config.APPEARANCE_THEME and + self.restart_required_configs_pre_data.ui_scaling == config.UI_SCALING and + self.restart_required_configs_pre_data.font_family == config.FONT_FAMILY and + self.restart_required_configs_pre_data.ui_language == config.UI_LANGUAGE + ) + + if locale is None: + locale = config.UI_LANGUAGE + + if is_restart_required is True: + self._showRestartButton(locale) + else: + self._hideRestartButton() + + + def _showRestartButton(self, locale:Union[None,str]=None): + self.view_variable.VAR_CONFIG_WINDOW_RESTART_BUTTON_LABEL.set(i18n.t("config_window.restart_message", locale=locale)) + vrct_gui.config_window.restart_button_container.grid() + def _hideRestartButton(self): + vrct_gui.config_window.restart_button_container.grid_remove() + + def _updateActiveSettingBoxTabNo(self, active_setting_box_tab_attr_name:str): + self.view_variable.ACTIVE_SETTING_BOX_TAB_ATTR_NAME = active_setting_box_tab_attr_name + + @staticmethod + def setWidgetsStatus_ConfigWindowCompactModeSwitch_Disabled(): + vrct_gui.config_window.setting_box_compact_mode_switch_box.configure(state="disabled") + + @staticmethod + def setWidgetsStatus_ConfigWindowCompactModeSwitch_Normal(): + vrct_gui.config_window.setting_box_compact_mode_switch_box.configure(state="normal") + + def openMicEnergyThresholdWidget(self): + self.view_variable.VAR_LABEL_MIC_DYNAMIC_ENERGY_THRESHOLD.set(i18n.t("config_window.mic_dynamic_energy_threshold.label_for_manual")) + self.view_variable.VAR_DESC_MIC_DYNAMIC_ENERGY_THRESHOLD.set(i18n.t("config_window.mic_dynamic_energy_threshold.desc_for_manual")) + vrct_gui.config_window.sb__mic_dynamic_energy_threshold.grid(pady=0) + vrct_gui.config_window.sb__mic_energy_threshold.grid() + + def closeMicEnergyThresholdWidget(self): + self.view_variable.VAR_LABEL_MIC_DYNAMIC_ENERGY_THRESHOLD.set(i18n.t("config_window.mic_dynamic_energy_threshold.label_for_automatic")) + self.view_variable.VAR_DESC_MIC_DYNAMIC_ENERGY_THRESHOLD.set(i18n.t("config_window.mic_dynamic_energy_threshold.desc_for_automatic")) + vrct_gui.config_window.sb__mic_dynamic_energy_threshold.grid(pady=(0,1)) + vrct_gui.config_window.sb__mic_energy_threshold.grid_remove() + + def openSpeakerEnergyThresholdWidget(self): + self.view_variable.VAR_LABEL_SPEAKER_DYNAMIC_ENERGY_THRESHOLD.set(i18n.t("config_window.speaker_dynamic_energy_threshold.label_for_manual")) + self.view_variable.VAR_DESC_SPEAKER_DYNAMIC_ENERGY_THRESHOLD.set(i18n.t("config_window.speaker_dynamic_energy_threshold.desc_for_manual")) + vrct_gui.config_window.sb__speaker_dynamic_energy_threshold.grid(pady=0) + vrct_gui.config_window.sb__speaker_energy_threshold.grid() + + def closeSpeakerEnergyThresholdWidget(self): + self.view_variable.VAR_LABEL_SPEAKER_DYNAMIC_ENERGY_THRESHOLD.set(i18n.t("config_window.speaker_dynamic_energy_threshold.label_for_automatic")) + self.view_variable.VAR_DESC_SPEAKER_DYNAMIC_ENERGY_THRESHOLD.set(i18n.t("config_window.speaker_dynamic_energy_threshold.desc_for_automatic")) + vrct_gui.config_window.sb__speaker_dynamic_energy_threshold.grid(pady=(0,1)) + vrct_gui.config_window.sb__speaker_energy_threshold.grid_remove() + + + + def initMicThresholdCheckButton(self): + if config.CHOICE_MIC_HOST == "NoHost" or config.CHOICE_MIC_DEVICE == "NoDevice": + self.replaceMicThresholdCheckButton_Disabled() + else: + self.replaceMicThresholdCheckButton_Passive() + + @staticmethod + def replaceMicThresholdCheckButton_Active(): + vrct_gui.config_window.sb__progressbar_x_slider__passive_button_mic_energy_threshold.grid_remove() + vrct_gui.config_window.sb__progressbar_x_slider__disabled_button_mic_energy_threshold.grid_remove() + vrct_gui.config_window.sb__progressbar_x_slider__active_button_mic_energy_threshold.grid() + + @staticmethod + def replaceMicThresholdCheckButton_Disabled(): + vrct_gui.config_window.sb__progressbar_x_slider__passive_button_mic_energy_threshold.grid_remove() + vrct_gui.config_window.sb__progressbar_x_slider__active_button_mic_energy_threshold.grid_remove() + vrct_gui.config_window.sb__progressbar_x_slider__disabled_button_mic_energy_threshold.grid() + + @staticmethod + def replaceMicThresholdCheckButton_Passive(): + vrct_gui.config_window.sb__progressbar_x_slider__active_button_mic_energy_threshold.grid_remove() + vrct_gui.config_window.sb__progressbar_x_slider__disabled_button_mic_energy_threshold.grid_remove() + vrct_gui.config_window.sb__progressbar_x_slider__passive_button_mic_energy_threshold.grid() + + + + def initSpeakerThresholdCheckButton(self): + self.replaceSpeakerThresholdCheckButton_Passive() + + @staticmethod + def replaceSpeakerThresholdCheckButton_Active(): + vrct_gui.config_window.sb__progressbar_x_slider__passive_button_speaker_energy_threshold.grid_remove() + vrct_gui.config_window.sb__progressbar_x_slider__disabled_button_speaker_energy_threshold.grid_remove() + vrct_gui.config_window.sb__progressbar_x_slider__active_button_speaker_energy_threshold.grid() + + @staticmethod + def replaceSpeakerThresholdCheckButton_Disabled(): + vrct_gui.config_window.sb__progressbar_x_slider__passive_button_speaker_energy_threshold.grid_remove() + vrct_gui.config_window.sb__progressbar_x_slider__active_button_speaker_energy_threshold.grid_remove() + vrct_gui.config_window.sb__progressbar_x_slider__disabled_button_speaker_energy_threshold.grid() + + @staticmethod + def replaceSpeakerThresholdCheckButton_Passive(): + vrct_gui.config_window.sb__progressbar_x_slider__active_button_speaker_energy_threshold.grid_remove() + vrct_gui.config_window.sb__progressbar_x_slider__disabled_button_speaker_energy_threshold.grid_remove() + vrct_gui.config_window.sb__progressbar_x_slider__passive_button_speaker_energy_threshold.grid() + + + + def updateList_MicHost(self, new_mic_host_list:list): + self.view_variable.LIST_MIC_HOST = new_mic_host_list + vrct_gui.dropdown_menu_window.updateDropdownMenuValues( + dropdown_menu_widget_id="sb__optionmenu_mic_host", + dropdown_menu_values=new_mic_host_list, + ) + + def updateSelected_MicHost(self, selected_mic_host_name:str): + self.view_variable.VAR_MIC_HOST.set(selected_mic_host_name) + + def updateList_MicDevice(self, new_mic_device_list:list): + self.view_variable.LIST_MIC_DEVICE = new_mic_device_list + vrct_gui.dropdown_menu_window.updateDropdownMenuValues( + dropdown_menu_widget_id="sb__optionmenu_mic_device", + dropdown_menu_values=new_mic_device_list, + ) + + def updateSelected_MicDevice(self, default_selected_mic_device_name:str): + self.view_variable.VAR_MIC_DEVICE.set(default_selected_mic_device_name) + + + @staticmethod + def updateSetProgressBar_MicEnergy(new_mic_energy): + vrct_gui.config_window.sb__progressbar_x_slider__progressbar_mic_energy_threshold.set(new_mic_energy/config.MAX_MIC_ENERGY_THRESHOLD) + + @staticmethod + def initProgressBar_MicEnergy(): + vrct_gui.config_window.sb__progressbar_x_slider__progressbar_mic_energy_threshold.set(0) + + + @staticmethod + def updateSetProgressBar_SpeakerEnergy(new_speaker_energy): + vrct_gui.config_window.sb__progressbar_x_slider__progressbar_speaker_energy_threshold.set(new_speaker_energy/config.MAX_SPEAKER_ENERGY_THRESHOLD) + + @staticmethod + def initProgressBar_SpeakerEnergy(): + vrct_gui.config_window.sb__progressbar_x_slider__progressbar_speaker_energy_threshold.set(0) + + + + def setGuiVariable_MicEnergyThreshold(self, value): + self.view_variable.VAR_MIC_ENERGY_THRESHOLD__SLIDER.set(int(value)) + self.view_variable.VAR_MIC_ENERGY_THRESHOLD__ENTRY.set(str(value)) + + def setLatestConfigVariable_MicEnergyThreshold(self, _e=None): + self.setGuiVariable_MicEnergyThreshold(config.INPUT_MIC_ENERGY_THRESHOLD) + self.clearErrorMessage() + + + def setGuiVariable_SpeakerEnergyThreshold(self, value): + self.view_variable.VAR_SPEAKER_ENERGY_THRESHOLD__SLIDER.set(int(value)) + self.view_variable.VAR_SPEAKER_ENERGY_THRESHOLD__ENTRY.set(str(value)) + + def setLatestConfigVariable_SpeakerEnergyThreshold(self, _e=None): + self.setGuiVariable_SpeakerEnergyThreshold(config.INPUT_SPEAKER_ENERGY_THRESHOLD) + self.clearErrorMessage() + + + + def setGuiVariable_MicRecordTimeout(self, value, delete=False): + if delete is True: self._clearEntryBox(vrct_gui.config_window.sb__entry_mic_record_timeout) + self.view_variable.VAR_MIC_RECORD_TIMEOUT.set(str(value)) + + def setLatestConfigVariable_MicRecordTimeout(self, _e=None): + self.setGuiVariable_MicRecordTimeout(config.INPUT_MIC_RECORD_TIMEOUT) + self.clearErrorMessage() + + + def setGuiVariable_MicPhraseTimeout(self, value, delete=False): + if delete is True: self._clearEntryBox(vrct_gui.config_window.sb__entry_mic_phrase_timeout) + self.view_variable.VAR_MIC_PHRASE_TIMEOUT.set(str(value)) + + def setLatestConfigVariable_MicPhraseTimeout(self, _e=None): + self.setGuiVariable_MicPhraseTimeout(config.INPUT_MIC_PHRASE_TIMEOUT) + self.clearErrorMessage() + + + def setGuiVariable_MicMaxPhrases(self, value, delete=False): + if delete is True: self._clearEntryBox(vrct_gui.config_window.sb__entry_mic_max_phrases) + self.view_variable.VAR_MIC_MAX_PHRASES.set(str(value)) + + def setLatestConfigVariable_MicMaxPhrases(self, _e=None): + self.setGuiVariable_MicMaxPhrases(config.INPUT_MIC_MAX_PHRASES) + self.clearErrorMessage() + + + + def setGuiVariable_SpeakerRecordTimeout(self, value, delete=False): + if delete is True: self._clearEntryBox(vrct_gui.config_window.sb__entry_speaker_record_timeout) + self.view_variable.VAR_SPEAKER_RECORD_TIMEOUT.set(str(value)) + + def setLatestConfigVariable_SpeakerRecordTimeout(self, _e=None): + self.setGuiVariable_SpeakerRecordTimeout(config.INPUT_SPEAKER_RECORD_TIMEOUT) + self.clearErrorMessage() + + + def setGuiVariable_SpeakerPhraseTimeout(self, value, delete=False): + if delete is True: self._clearEntryBox(vrct_gui.config_window.sb__entry_speaker_phrase_timeout) + self.view_variable.VAR_SPEAKER_PHRASE_TIMEOUT.set(str(value)) + + def setLatestConfigVariable_SpeakerPhraseTimeout(self, _e=None): + self.setGuiVariable_SpeakerPhraseTimeout(config.INPUT_SPEAKER_PHRASE_TIMEOUT) + self.clearErrorMessage() + + + def setGuiVariable_SpeakerMaxPhrases(self, value, delete=False): + if delete is True: self._clearEntryBox(vrct_gui.config_window.sb__entry_speaker_max_phrases) + self.view_variable.VAR_SPEAKER_MAX_PHRASES.set(str(value)) + + def setLatestConfigVariable_SpeakerMaxPhrases(self, _e=None): + self.setGuiVariable_SpeakerMaxPhrases(config.INPUT_SPEAKER_MAX_PHRASES) + self.clearErrorMessage() + + + @staticmethod + def _clearEntryBox(entry_widget): + entry_widget.delete(0, CTK_END) + + + def showErrorMessage_MicEnergyThreshold(self): + self._showErrorMessage( + vrct_gui.config_window.sb__progressbar_x_slider__entry_mic_energy_threshold, + self._makeInvalidValueErrorMessage(i18n.t("config_window.mic_dynamic_energy_threshold.error_message", max=config.MAX_MIC_ENERGY_THRESHOLD)) + ) + + def showErrorMessage_MicRecordTimeout(self): + self._showErrorMessage( + vrct_gui.config_window.sb__entry_mic_record_timeout, + self._makeInvalidValueErrorMessage( + i18n.t( + "config_window.mic_record_timeout.error_message", + mic_phrase_timeout_label=i18n.t("config_window.mic_phrase_timeout.label") + ) + ) + ) + + def showErrorMessage_MicPhraseTimeout(self): + self._showErrorMessage( + vrct_gui.config_window.sb__entry_mic_phrase_timeout, + self._makeInvalidValueErrorMessage( + i18n.t( + "config_window.mic_phrase_timeout.error_message", + mic_record_timeout_label=i18n.t("config_window.mic_record_timeout.label") + ) + ) + ) + + def showErrorMessage_MicMaxPhrases(self): + self._showErrorMessage( + vrct_gui.config_window.sb__entry_mic_max_phrases, + self._makeInvalidValueErrorMessage(i18n.t("config_window.mic_max_phrase.error_message")) + ) + + + def showErrorMessage_SpeakerEnergyThreshold(self): + self._showErrorMessage( + vrct_gui.config_window.sb__progressbar_x_slider__entry_speaker_energy_threshold, + self._makeInvalidValueErrorMessage(i18n.t("config_window.speaker_dynamic_energy_threshold.error_message", max=config.MAX_SPEAKER_ENERGY_THRESHOLD)) + ) + + def showErrorMessage_SpeakerRecordTimeout(self): + self._showErrorMessage( + vrct_gui.config_window.sb__entry_speaker_record_timeout, + self._makeInvalidValueErrorMessage( + i18n.t( + "config_window.speaker_record_timeout.error_message", + speaker_phrase_timeout_label=i18n.t("config_window.speaker_phrase_timeout.label") + ) + ) + ) + + def showErrorMessage_SpeakerPhraseTimeout(self): + self._showErrorMessage( + vrct_gui.config_window.sb__entry_speaker_phrase_timeout, + self._makeInvalidValueErrorMessage( + i18n.t( + "config_window.speaker_phrase_timeout.error_message", + speaker_record_timeout_label=i18n.t("config_window.speaker_record_timeout.label") + ) + ) + ) + + def showErrorMessage_SpeakerMaxPhrases(self): + self._showErrorMessage( + vrct_gui.config_window.sb__entry_speaker_max_phrases, + self._makeInvalidValueErrorMessage(i18n.t("config_window.speaker_max_phrase.error_message")) + ) + + + def showErrorMessage_CheckSpeakerThreshold_NoDevice(self): + self._showErrorMessage( + vrct_gui.config_window.sb__progressbar_x_slider__active_button_speaker_energy_threshold, + self._makeInvalidValueErrorMessage(i18n.t("config_window.speaker_dynamic_energy_threshold.no_device_error_message")) + ) + + def _showErrorMessage(self, target_widget, message): + self.view_variable.VAR_ERROR_MESSAGE.set(message) + vrct_gui._showErrorMessage(target_widget=target_widget) + + @staticmethod + def _makeInvalidValueErrorMessage(error_message): + return i18n.t("config_window.common_error_message.invalid_value") + "\n" + error_message + + def clearErrorMessage(self): + vrct_gui._clearErrorMessage() + + + + + @staticmethod + def showSplash(): + vrct_gui.showSplash() + + @staticmethod + def destroySplash(): + vrct_gui.destroySplash() + + # These conversations are generated by ChatGPT + def _insertSampleConversationToTextbox(self): + + self.printToTextbox_enableTranscriptionSend() + self.printToTextbox_enableTranscriptionReceive() + + conversation_data_without_translation = [ + { + "me": "おはよう。", + }, + { + "me": "おはよう。", + "target": "やぁ。", + }, + { + "me": "今日の天気はどうかな?", + "target": "天気予報を見てないけど、晴れるといいね。", + }, + { + "me": "そうだね。昨日は雨だったから。", + "target": "それで、今日の予定は?", + }, + ] + + for data in conversation_data_without_translation: + if data.get("me", None) is not None: + self.printToTextbox_SentMessage(data.get("me", None), data.get("me_t", None)) + if data.get("target", None) is not None: + self.printToTextbox_ReceivedMessage(data.get("target", None), data.get("target_t", None)) + + self.printToTextbox_enableTranslation() + + conversation_data = [ + { + "me": "I have work in the morning, but I'm meeting friends for dinner in the evening.", + "me_t": "아침에 일이 있지만 저녁에 친구들과 만나 저녁 식사할 예정이에요.", + "target": "재미있어 보여요! 무엇을 먹을 예정이에요?", + "target_t": "Sounds fun! What are you planning to eat?" + }, + { + "me": "We're going to an Italian restaurant, and I'm going to have pizza.", + "me_t": "우리는 이탈리안 레스토랑에 가서 피자를 먹을 거에요.", + "target": "그걸 듣자마자 배가 고파져요. 언젠가 함께하고 싶어요.", + "target_t": "Just hearing that makes me hungry. I'd love to join you sometime." + }, + { + "me": "Let's plan it for next time!", + "me_t": "다음 번에 계획해 봐요!", + "target": "그래요!", + "target_t": "Sure!" + }, + { + "me": "When would be a good time for you?", + "me_t": "너에게 언제가 좋을까?", + "target": "나는 주말이 가장 좋을 것 같아요. 토요일은 어때요?", + "target_t": "I think the weekend works best for me. How about Saturday?" + }, + { + "me": "Saturday sounds perfect. What time would be convenient?", + "me_t": "토요일이 완벽해 보여. 편한 시간은 언제인가요?", + "target": "저는 저녁이 괜찮아요. 7시쯤 괜찮을까요?", + "target_t": "Evening works for me. Is around 7 PM okay?" + }, + { + "me": "7 PM works great. Do you have any preferences for food other than Italian?", + "me_t": "7시가 아주 적당해. 이탈리안 음식 이외에 어떤 음식을 좋아하세요?", + "target": "특별한 선호도는 없어요. 무엇이든 괜찮아요. 추천 디저트가 있다면 알려주세요.", + "target_t": "I don't have any particular preferences, so anything is fine. If there's a recommended dessert, let me know." + }, + + + { + "me": "朝は仕事があるけど、夜は友達と食事に行く予定だよ。", + "me_t": "I have work in the morning, but I'm meeting friends for dinner in the evening.", + "target": "Sounds fun! What are you planning to eat?", + "target_t": "楽しそう!何を食べる予定?", + }, + { + "me": "イタリアンレストランに行って、ピザを食べるつもりだよ。", + "me_t": "We're going to an Italian restaurant, and I'm going to have pizza.", + "target": "Just hearing that makes me hungry. I'd love to join you sometime.", + "target_t": "それ聞いただけでおなかすいたよ。私も一緒に行きたいな。", + }, + { + "me": "次回にぜひ一緒に行こう!", + "me_t": "Let's plan it for next time!", + "target": "Sure!", + "target_t": "そうだね!", + }, + { + "me": "次回はいつがいいかな?", + "me_t": "When would be a good time for you?", + "target": "I think the weekend works best for me. How about Saturday?", + "target_t": "私は週末が一番いいかな。土曜日はどう?" + }, + { + "me": "土曜日はちょうどいいね。何時ごろが良いかな?", + "me_t": "Saturday sounds perfect. What time would be convenient?", + "target": "Evening works for me. Is around 7 PM okay?", + "target_t": "夜がいいかな。7時くらいからがちょうど良いかな。" + }, + { + "me": "7時からはちょうどいいよ。イタリアン以外の食べ物について何か好みがある?", + "me_t": "7 PM works great. Do you have any preferences for food other than Italian?", + "target": "I don't have any particular preferences, so anything is fine. If there's a recommended dessert, let me know.", + "target_t": "特に好みはないから、何でも大丈夫。おすすめのデザートがあれば教えてね。" + }, + ] + for data in conversation_data: + if data.get("me", None) is not None: + self.printToTextbox_SentMessage(data.get("me", None), data.get("me_t", None)) + if data.get("target", None) is not None: + self.printToTextbox_ReceivedMessage(data.get("target", None), data.get("target_t", None)) + + +view = View() \ No newline at end of file diff --git a/vrct_gui/_CreateConfirmationModal.py b/vrct_gui/_CreateConfirmationModal.py new file mode 100644 index 00000000..1d7a5de4 --- /dev/null +++ b/vrct_gui/_CreateConfirmationModal.py @@ -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("", 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("", 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() diff --git a/vrct_gui/_CreateDropdownMenuWindow.py b/vrct_gui/_CreateDropdownMenuWindow.py new file mode 100644 index 00000000..948e00e1 --- /dev/null +++ b/vrct_gui/_CreateDropdownMenuWindow.py @@ -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("", self._adjustToTargetWidgetGeometry, "+") + self.BIND_UNMAP_FUNC_ID = self.attach_widget.bind("", self._withdraw, "+") + + self.BIND_BUTTON_1_FUNC_ID = self.attach_widget.winfo_toplevel().bind("", 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("", self.BIND_CONFIGURE_FUNC_ID) + self.attach_widget.unbind("", self.BIND_UNMAP_FUNC_ID) + self.attach_widget.winfo_toplevel().unbind("", 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() + + diff --git a/vrct_gui/_CreateErrorWindow.py b/vrct_gui/_CreateErrorWindow.py new file mode 100644 index 00000000..4b00047a --- /dev/null +++ b/vrct_gui/_CreateErrorWindow.py @@ -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("", self._adjustToTargetWidgetGeometry, "+") + self.BIND_UNMAP_FUNC_ID = self.attach_widget.bind("", 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("", self.BIND_CONFIGURE_FUNC_ID) + self.attach_widget.unbind("", 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() + + diff --git a/vrct_gui/_CreateSelectableLanguagesWindow.py b/vrct_gui/_CreateSelectableLanguagesWindow.py new file mode 100644 index 00000000..daa369c0 --- /dev/null +++ b/vrct_gui/_CreateSelectableLanguagesWindow.py @@ -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("", 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() \ No newline at end of file diff --git a/vrct_gui/_CreateWindowCover.py b/vrct_gui/_CreateWindowCover.py new file mode 100644 index 00000000..0ee3b606 --- /dev/null +++ b/vrct_gui/_CreateWindowCover.py @@ -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("", self._adjustToMainWindowGeometry, "+") + if bind_focusin is not None: + self.BIND_FOCUS_IN_FUNC_ID = self.bind("", 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("", self.BIND_CONFIGURE_ADJUSTED_GEOMETRY_FUNC_ID) + if self.BIND_FOCUS_IN_FUNC_ID is not None: + self.unbind("", 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() \ No newline at end of file diff --git a/vrct_gui/__init__.py b/vrct_gui/__init__.py new file mode 100644 index 00000000..5c2e8cca --- /dev/null +++ b/vrct_gui/__init__.py @@ -0,0 +1 @@ +from .vrct_gui import vrct_gui \ No newline at end of file diff --git a/vrct_gui/_changeConfigWindowWidgetsStatus.py b/vrct_gui/_changeConfigWindowWidgetsStatus.py new file mode 100644 index 00000000..95ebb187 --- /dev/null +++ b/vrct_gui/_changeConfigWindowWidgetsStatus.py @@ -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() \ No newline at end of file diff --git a/vrct_gui/_changeMainWindowWidgetsStatus.py b/vrct_gui/_changeMainWindowWidgetsStatus.py new file mode 100644 index 00000000..53c29f2a --- /dev/null +++ b/vrct_gui/_changeMainWindowWidgetsStatus.py @@ -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() \ No newline at end of file diff --git a/vrct_gui/_printToTextbox.py b/vrct_gui/_printToTextbox.py new file mode 100644 index 00000000..943b047d --- /dev/null +++ b/vrct_gui/_printToTextbox.py @@ -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) \ No newline at end of file diff --git a/vrct_gui/config_window/ConfigWindow.py b/vrct_gui/config_window/ConfigWindow.py new file mode 100644 index 00000000..fc56daec --- /dev/null +++ b/vrct_gui/config_window/ConfigWindow.py @@ -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("", lambda event: event.widget.focus_set(), "+") \ No newline at end of file diff --git a/vrct_gui/config_window/__init__.py b/vrct_gui/config_window/__init__.py new file mode 100644 index 00000000..c6dbe941 --- /dev/null +++ b/vrct_gui/config_window/__init__.py @@ -0,0 +1 @@ +from .ConfigWindow import ConfigWindow \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/__init__.py b/vrct_gui/config_window/widgets/__init__.py new file mode 100644 index 00000000..e3651955 --- /dev/null +++ b/vrct_gui/config_window/widgets/__init__.py @@ -0,0 +1,4 @@ +from .createConfigWindowTitle import createConfigWindowTitle +from .createSettingBoxTopBar import createSettingBoxTopBar + +from .createSideMenuAndSettingsBoxContainers import createSideMenuAndSettingsBoxContainers \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createConfigWindowTitle.py b/vrct_gui/config_window/widgets/createConfigWindowTitle.py new file mode 100644 index 00000000..4db6b4f4 --- /dev/null +++ b/vrct_gui/config_window/widgets/createConfigWindowTitle.py @@ -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") diff --git a/vrct_gui/config_window/widgets/createSettingBoxTopBar/__init__.py b/vrct_gui/config_window/widgets/createSettingBoxTopBar/__init__.py new file mode 100644 index 00000000..b5be1139 --- /dev/null +++ b/vrct_gui/config_window/widgets/createSettingBoxTopBar/__init__.py @@ -0,0 +1 @@ +from .createSettingBoxTopBar import createSettingBoxTopBar \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSettingBoxTopBar/_createRestartButton.py b/vrct_gui/config_window/widgets/createSettingBoxTopBar/_createRestartButton.py new file mode 100644 index 00000000..91e08761 --- /dev/null +++ b/vrct_gui/config_window/widgets/createSettingBoxTopBar/_createRestartButton.py @@ -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() diff --git a/vrct_gui/config_window/widgets/createSettingBoxTopBar/_createSettingBoxCompactModeButton.py b/vrct_gui/config_window/widgets/createSettingBoxTopBar/_createSettingBoxCompactModeButton.py new file mode 100644 index 00000000..80a5d152 --- /dev/null +++ b/vrct_gui/config_window/widgets/createSettingBoxTopBar/_createSettingBoxCompactModeButton.py @@ -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) \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSettingBoxTopBar/_createSettingBoxTitle.py b/vrct_gui/config_window/widgets/createSettingBoxTopBar/_createSettingBoxTitle.py new file mode 100644 index 00000000..7c0ec386 --- /dev/null +++ b/vrct_gui/config_window/widgets/createSettingBoxTopBar/_createSettingBoxTitle.py @@ -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") \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSettingBoxTopBar/createSettingBoxTopBar.py b/vrct_gui/config_window/widgets/createSettingBoxTopBar/createSettingBoxTopBar.py new file mode 100644 index 00000000..12a0403c --- /dev/null +++ b/vrct_gui/config_window/widgets/createSettingBoxTopBar/createSettingBoxTopBar.py @@ -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") \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/__init__.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/__init__.py new file mode 100644 index 00000000..245472c9 --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/__init__.py @@ -0,0 +1 @@ +from .createSideMenuAndSettingsBoxContainers import createSideMenuAndSettingsBoxContainers \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/_addConfigSideMenuItem.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/_addConfigSideMenuItem.py new file mode 100644 index 00000000..39d83009 --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/_addConfigSideMenuItem.py @@ -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) diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/_createSettingBoxContainer.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/_createSettingBoxContainer.py new file mode 100644 index 00000000..97859e3a --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/_createSettingBoxContainer.py @@ -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, + ) + diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/createSideMenuAndSettingsBoxContainers.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/createSideMenuAndSettingsBoxContainers.py new file mode 100644 index 00000000..f9694a1a --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/createSideMenuAndSettingsBoxContainers.py @@ -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() \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/_SettingBoxGenerator.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/_SettingBoxGenerator.py new file mode 100644 index 00000000..f11c56b5 --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/_SettingBoxGenerator.py @@ -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("", 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("", adjusted_command__for_entry_bind__Any_KeyRelease) + if entry_bind__FocusOut is not None: + entry_widget.bind("", 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("", 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("", 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 \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_advanced_settings/__init__.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_advanced_settings/__init__.py new file mode 100644 index 00000000..0ceb6231 --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_advanced_settings/__init__.py @@ -0,0 +1 @@ +from .createSettingBox_AdvancedSettings import createSettingBox_AdvancedSettings \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_advanced_settings/createSettingBox_AdvancedSettings.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_advanced_settings/createSettingBox_AdvancedSettings.py new file mode 100644 index 00000000..4c6ef337 --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_advanced_settings/createSettingBox_AdvancedSettings.py @@ -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 diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_appearance/__init__.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_appearance/__init__.py new file mode 100644 index 00000000..7dd435d8 --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_appearance/__init__.py @@ -0,0 +1 @@ +from .createSettingBox_Appearance import createSettingBox_Appearance \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_appearance/createSettingBox_Appearance.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_appearance/createSettingBox_Appearance.py new file mode 100644 index 00000000..cc33684c --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_appearance/createSettingBox_Appearance.py @@ -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 \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_others/__init__.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_others/__init__.py new file mode 100644 index 00000000..c115d627 --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_others/__init__.py @@ -0,0 +1 @@ +from .createSettingBox_Others import createSettingBox_Others \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_others/createSettingBox_Others.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_others/createSettingBox_Others.py new file mode 100644 index 00000000..e4f46fb6 --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_others/createSettingBox_Others.py @@ -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 + diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_transcription/__init__.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_transcription/__init__.py new file mode 100644 index 00000000..5383094e --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_transcription/__init__.py @@ -0,0 +1,2 @@ +from .createSettingBox_Mic import createSettingBox_Mic +from .createSettingBox_Speaker import createSettingBox_Speaker \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_transcription/createSettingBox_Mic.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_transcription/createSettingBox_Mic.py new file mode 100644 index 00000000..472e5baa --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_transcription/createSettingBox_Mic.py @@ -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 \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_transcription/createSettingBox_Speaker.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_transcription/createSettingBox_Speaker.py new file mode 100644 index 00000000..a9d1ad9b --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_transcription/createSettingBox_Speaker.py @@ -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 + # __________ \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_translation/__init__.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_translation/__init__.py new file mode 100644 index 00000000..d965431e --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_translation/__init__.py @@ -0,0 +1 @@ +from .createSettingBox_Translation import createSettingBox_Translation \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_translation/createSettingBox_Translation.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_translation/createSettingBox_Translation.py new file mode 100644 index 00000000..d2975d0d --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_translation/createSettingBox_Translation.py @@ -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 \ No newline at end of file diff --git a/vrct_gui/main_window/__init__.py b/vrct_gui/main_window/__init__.py new file mode 100644 index 00000000..a34b6566 --- /dev/null +++ b/vrct_gui/main_window/__init__.py @@ -0,0 +1 @@ +from .createMainWindowWidgets import createMainWindowWidgets \ No newline at end of file diff --git a/vrct_gui/main_window/createMainWindowWidgets.py b/vrct_gui/main_window/createMainWindowWidgets.py new file mode 100644 index 00000000..2bcea306 --- /dev/null +++ b/vrct_gui/main_window/createMainWindowWidgets.py @@ -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) \ No newline at end of file diff --git a/vrct_gui/main_window/widgets/__init__.py b/vrct_gui/main_window/widgets/__init__.py new file mode 100644 index 00000000..ac037cb8 --- /dev/null +++ b/vrct_gui/main_window/widgets/__init__.py @@ -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 diff --git a/vrct_gui/main_window/widgets/_create_sidebar/__init__.py b/vrct_gui/main_window/widgets/_create_sidebar/__init__.py new file mode 100644 index 00000000..cda02e0d --- /dev/null +++ b/vrct_gui/main_window/widgets/_create_sidebar/__init__.py @@ -0,0 +1,2 @@ +from .createSidebarFeatures import createSidebarFeatures +from .createSidebarLanguagesSettings import createSidebarLanguagesSettings \ No newline at end of file diff --git a/vrct_gui/main_window/widgets/_create_sidebar/createSidebarFeatures.py b/vrct_gui/main_window/widgets/_create_sidebar/createSidebarFeatures.py new file mode 100644 index 00000000..b46ab3cc --- /dev/null +++ b/vrct_gui/main_window/widgets/_create_sidebar/createSidebarFeatures.py @@ -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 "" originally set up by the widget to manually control it. + switch_box_widget._canvas.unbind("") + switch_box_widget._text_label.unbind("") + + 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 \ No newline at end of file diff --git a/vrct_gui/main_window/widgets/_create_sidebar/createSidebarLanguagesSettings.py b/vrct_gui/main_window/widgets/_create_sidebar/createSidebarLanguagesSettings.py new file mode 100644 index 00000000..346afae5 --- /dev/null +++ b/vrct_gui/main_window/widgets/_create_sidebar/createSidebarLanguagesSettings.py @@ -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") \ No newline at end of file diff --git a/vrct_gui/main_window/widgets/create_entry_message_box.py b/vrct_gui/main_window/widgets/create_entry_message_box.py new file mode 100644 index 00000000..e583234f --- /dev/null +++ b/vrct_gui/main_window/widgets/create_entry_message_box.py @@ -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("", messageBoxAnyKeyPress) + diff --git a/vrct_gui/main_window/widgets/create_minimize_sidebar_button.py b/vrct_gui/main_window/widgets/create_minimize_sidebar_button.py new file mode 100644 index 00000000..83585e98 --- /dev/null +++ b/vrct_gui/main_window/widgets/create_minimize_sidebar_button.py @@ -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() \ No newline at end of file diff --git a/vrct_gui/main_window/widgets/create_sidebar.py b/vrct_gui/main_window/widgets/create_sidebar.py new file mode 100644 index 00000000..9f30d269 --- /dev/null +++ b/vrct_gui/main_window/widgets/create_sidebar.py @@ -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), + ) \ No newline at end of file diff --git a/vrct_gui/main_window/widgets/create_textbox.py b/vrct_gui/main_window/widgets/create_textbox.py new file mode 100644 index 00000000..99e977f4 --- /dev/null +++ b/vrct_gui/main_window/widgets/create_textbox.py @@ -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() diff --git a/vrct_gui/splash_window/SplashWindow.py b/vrct_gui/splash_window/SplashWindow.py new file mode 100644 index 00000000..96537c5e --- /dev/null +++ b/vrct_gui/splash_window/SplashWindow.py @@ -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() \ No newline at end of file diff --git a/vrct_gui/splash_window/__init__.py b/vrct_gui/splash_window/__init__.py new file mode 100644 index 00000000..3ce5e580 --- /dev/null +++ b/vrct_gui/splash_window/__init__.py @@ -0,0 +1 @@ +from .SplashWindow import * \ No newline at end of file diff --git a/vrct_gui/ui_managers/ColorThemeManager.py b/vrct_gui/ui_managers/ColorThemeManager.py new file mode 100644 index 00000000..ab55e996 --- /dev/null +++ b/vrct_gui/ui_managers/ColorThemeManager.py @@ -0,0 +1,485 @@ +from types import SimpleNamespace + +class ColorThemeManager(): + def __init__(self, theme): + self.main = SimpleNamespace() + self.config_window = SimpleNamespace() + self.selectable_language_window = SimpleNamespace() + self.main_window_cover = SimpleNamespace() + self.error_message_window = SimpleNamespace() + self.confirmation_modal = SimpleNamespace() + + # old one. But leave it here for now. + # self.PRIMARY_100_COLOR = "#c4eac1" + # self.PRIMARY_200_COLOR = "#9cdd98" + # self.PRIMARY_300_COLOR = "#70d16c" + # self.PRIMARY_400_COLOR = "#49c649" + # self.PRIMARY_500_COLOR = "#0abb1d" + # self.PRIMARY_600_COLOR = "#00ac11" + # self.PRIMARY_650_COLOR = "#00A309" + # self.PRIMARY_700_COLOR = "#009900" + # self.PRIMARY_800_COLOR = "#008800" + # self.PRIMARY_900_COLOR = "#006900" + + + # new one. + self.PRIMARY_100_COLOR = "#b7ded8" + self.PRIMARY_200_COLOR = "#8acac0" + self.PRIMARY_300_COLOR = "#61b4a7" + self.PRIMARY_400_COLOR = "#48a495" + self.PRIMARY_450_COLOR = "#429c8c" + self.PRIMARY_500_COLOR = "#3b9483" + self.PRIMARY_600_COLOR = "#368777" + self.PRIMARY_650_COLOR = "#347f6f" + self.PRIMARY_700_COLOR = "#317767" + self.PRIMARY_750_COLOR = "#2F6F60" + self.PRIMARY_800_COLOR = "#2c6759" + self.PRIMARY_900_COLOR = "#214b3f" + + + self.DARK_100_COLOR = "#f5f7fb" + self.DARK_200_COLOR = "#f1f2f6" + self.DARK_300_COLOR = "#e9eaee" + self.DARK_350_COLOR = "#d8d9dd" + self.DARK_400_COLOR = "#c7c8cc" + self.DARK_450_COLOR = "#b8b9bd" + self.DARK_500_COLOR = "#a9aaae" + self.DARK_600_COLOR = "#7f8084" + self.DARK_650_COLOR = "#75767a" + self.DARK_700_COLOR = "#6a6c6f" + self.DARK_725_COLOR = "#636467" + self.DARK_750_COLOR = "#5b5c5f" + self.DARK_775_COLOR = "#535457" + self.DARK_800_COLOR = "#4b4c4f" + self.DARK_825_COLOR = "#434447" + self.DARK_850_COLOR = "#3a3b3e" + self.DARK_863_COLOR = "#36373a" + self.DARK_875_COLOR = "#323336" + self.DARK_888_COLOR = "#2e2f32" + self.DARK_900_COLOR = "#292a2d" + self.DARK_925_COLOR = "#242528" + self.DARK_950_COLOR = "#1f2022" + self.DARK_975_COLOR = "#1a1b1d" + self.DARK_1000_COLOR = "#151517" # THE DARKEST COLOR + + + self.LIGHT_100_COLOR = "#f2f2f2" # THE LIGHTEST COLOR + self.LIGHT_200_COLOR = "#e9e9e9" + self.LIGHT_250_COLOR = "#e1e1e1" + self.LIGHT_300_COLOR = "#d9d9d9" + self.LIGHT_325_COLOR = "#d0d0d0" + self.LIGHT_350_COLOR = "#c7c7c7" + self.LIGHT_375_COLOR = "#bebebe" + self.LIGHT_400_COLOR = "#b5b5b5" + self.LIGHT_450_COLOR = "#a5a5a5" + self.LIGHT_500_COLOR = "#959595" + self.LIGHT_600_COLOR = "#6d6d6d" + self.LIGHT_700_COLOR = "#5a5a5a" + self.LIGHT_750_COLOR = "#515151" + self.LIGHT_800_COLOR = "#3b3b3b" + self.LIGHT_850_COLOR = "#323232" + self.LIGHT_875_COLOR = "#2b2b2b" + self.LIGHT_900_COLOR = "#1b1b1b" + # self.LIGHT_925_COLOR = "#121212" + # self.LIGHT_950_COLOR = "#0c0c0c" + # self.LIGHT_975_COLOR = "#070707" + self.LIGHT_1000_COLOR = "#010101" + + + if theme == "Dark": + self._createDarkModeColor() + elif theme == "Light": + self._createLightModeColor() + + + def _createDarkModeColor(self): + # Common + self.main.BASIC_TEXT_COLOR = self.LIGHT_100_COLOR + self.main.LABELS_TEXT_COLOR = self.main.BASIC_TEXT_COLOR + + # Main + self.main.MAIN_BG_COLOR = self.DARK_888_COLOR + + self.main.TEXTBOX_BG_COLOR = self.DARK_900_COLOR + self.main.TEXTBOX_TEXT_COLOR = self.main.BASIC_TEXT_COLOR + self.main.TEXTBOX_TEXT_SUB_COLOR = self.DARK_450_COLOR + self.main.TEXTBOX_SYSTEM_TAG_TEXT_COLOR = self.PRIMARY_300_COLOR + self.main.TEXTBOX_SENT_TAG_TEXT_COLOR = "#6197b4" + self.main.TEXTBOX_RECEIVED_TAG_TEXT_COLOR = "#a861b4" + self.main.TEXTBOX_ERROR_TAG_TEXT_COLOR = "#c27583" + self.main.TEXTBOX_TIMESTAMP_TEXT_COLOR = self.DARK_600_COLOR + + self.main.TEXTBOX_TAB_BG_PASSIVE_COLOR = self.DARK_850_COLOR + self.main.TEXTBOX_TAB_BG_ACTIVE_COLOR = self.main.TEXTBOX_BG_COLOR + self.main.TEXTBOX_TAB_BG_HOVERED_COLOR = self.DARK_800_COLOR + self.main.TEXTBOX_TAB_BG_CLICKED_COLOR = self.DARK_925_COLOR + self.main.TEXTBOX_TAB_TEXT_ACTIVE_COLOR = self.main.BASIC_TEXT_COLOR + self.main.TEXTBOX_TAB_TEXT_PASSIVE_COLOR = self.DARK_500_COLOR + + self.main.TEXTBOX_ENTRY_TEXT_COLOR = self.DARK_300_COLOR + self.main.TEXTBOX_ENTRY_TEXT_DISABLED_COLOR = self.DARK_500_COLOR + self.main.TEXTBOX_ENTRY_BG_COLOR = self.DARK_875_COLOR + self.main.TEXTBOX_ENTRY_BORDER_COLOR = self.DARK_750_COLOR + self.main.TEXTBOX_ENTRY_PLACEHOLDER_COLOR = self.DARK_500_COLOR + self.main.TEXTBOX_ENTRY_PLACEHOLDER_DISABLED_COLOR = self.DARK_700_COLOR + + + + # Sidebar + self.main.SIDEBAR_BG_COLOR = self.DARK_850_COLOR + + # Sidebar Features + self.main.SF__BG_COLOR = self.DARK_825_COLOR + self.main.SF__HOVERED_BG_COLOR = self.DARK_800_COLOR + self.main.SF__CLICKED_BG_COLOR = self.DARK_875_COLOR + self.main.SF__TEXT_DISABLED_COLOR = self.DARK_500_COLOR + + self.main.SF__SWITCH_BOX_BG_COLOR = self.DARK_775_COLOR + self.main.SF__SWITCH_BOX_HOVERED_BG_COLOR = self.DARK_725_COLOR + self.main.SF__SWITCH_BOX_CLICKED_BG_COLOR = self.DARK_825_COLOR + self.main.SF__SWITCH_BOX_ACTIVE_BG_COLOR = self.PRIMARY_500_COLOR + self.main.SF__SWITCH_BOX_ACTIVE_HOVERED_BG_COLOR = self.PRIMARY_400_COLOR + self.main.SF__SWITCH_BOX_ACTIVE_CLICKED_BG_COLOR = self.PRIMARY_700_COLOR + self.main.SF__SWITCH_BOX_DISABLE_BG_COLOR = self.PRIMARY_800_COLOR + + self.main.SF__SWITCH_BOX_BUTTON_COLOR = self.DARK_400_COLOR + # It's not working because It overrode internally. + self.main.SF__SWITCH_BOX_BUTTON_HOVERED_COLOR = self.DARK_350_COLOR + + self.main.SF__SELECTED_MARK_ACTIVE_BG_COLOR = self.main.SF__SWITCH_BOX_ACTIVE_BG_COLOR + self.main.SF__SELECTED_MARK_ACTIVE_HOVERED_BG_COLOR = self.main.SF__SWITCH_BOX_ACTIVE_HOVERED_BG_COLOR + self.main.SF__SELECTED_MARK_ACTIVE_CLICKED_BG_COLOR = self.main.SF__SWITCH_BOX_ACTIVE_CLICKED_BG_COLOR + self.main.SF__SELECTED_MARK_DISABLE_BG_COLOR = self.main.SF__SWITCH_BOX_DISABLE_BG_COLOR + + + # Sidebar Languages Settings + self.main.SLS__TITLE_TEXT_COLOR = self.DARK_400_COLOR + + self.main.SLS__BG_COLOR = self.DARK_800_COLOR + + self.main.SLS__PRESETS_TAB_BG_HOVERED_COLOR = self.DARK_825_COLOR + self.main.SLS__PRESETS_TAB_BG_CLICKED_COLOR = self.DARK_875_COLOR + self.main.SLS__PRESETS_TAB_BG_PASSIVE_COLOR = self.main.SIDEBAR_BG_COLOR + self.main.SLS__PRESETS_TAB_BG_ACTIVE_COLOR = self.main.SLS__BG_COLOR + self.main.SLS__PRESETS_TAB_ACTIVE_TEXT_COLOR_PASSIVE = self.DARK_600_COLOR + self.main.SLS__PRESETS_TAB_ACTIVE_TEXT_COLOR = self.main.BASIC_TEXT_COLOR + + self.main.SLS__BOX_BG_COLOR = self.DARK_825_COLOR + self.main.SLS__BOX_SECTION_TITLE_TEXT_COLOR = self.DARK_400_COLOR + self.main.SLS__BOX_ARROWS_TEXT_COLOR = self.DARK_500_COLOR + + self.main.SLS__OPTIONMENU_BG_COLOR = self.DARK_888_COLOR + self.main.SLS__OPTIONMENU_HOVERED_BG_COLOR = self.DARK_875_COLOR + self.main.SLS__OPTIONMENU_CLICKED_BG_COLOR = self.DARK_900_COLOR + + + self.main.CONFIG_BUTTON_BG_COLOR = self.main.SIDEBAR_BG_COLOR + self.main.CONFIG_BUTTON_HOVERED_BG_COLOR = self.DARK_800_COLOR + self.main.CONFIG_BUTTON_CLICKED_BG_COLOR = self.DARK_875_COLOR + # self.main.CONFIG_BUTTON_DISABLE_COLOR = self.DARK_900_COLOR + + self.main.MINIMIZE_SIDEBAR_BUTTON_BG_COLOR = self.main.SIDEBAR_BG_COLOR + self.main.MINIMIZE_SIDEBAR_BUTTON_HOVERED_BG_COLOR = self.DARK_800_COLOR + self.main.MINIMIZE_SIDEBAR_BUTTON_CLICKED_BG_COLOR = self.DARK_900_COLOR + # self.main.MINIMIZE_SIDEBAR_BUTTON_DISABLE_COLOR = self.DARK_900_COLOR + + + + self.main.TOP_BAR_BUTTON_BG_COLOR = self.main.MAIN_BG_COLOR + self.main.TOP_BAR_BUTTON_HOVERED_BG_COLOR = self.DARK_850_COLOR + self.main.TOP_BAR_BUTTON_CLICKED_BG_COLOR = self.DARK_900_COLOR + # self.main.TOP_BAR_BUTTON_DISABLE_COLOR = self.DARK_900_COLOR + + self.main.UPDATE_AVAILABLE_BUTTON_BG_COLOR = self.main.TOP_BAR_BUTTON_BG_COLOR + self.main.UPDATE_AVAILABLE_BUTTON_HOVERED_BG_COLOR = self.main.TOP_BAR_BUTTON_HOVERED_BG_COLOR + self.main.UPDATE_AVAILABLE_BUTTON_CLICKED_BG_COLOR = self.main.TOP_BAR_BUTTON_CLICKED_BG_COLOR + # self.main.UPDATE_AVAILABLE_BUTTON_DISABLE_COLOR = self.main.TOP_BAR_BUTTON_DISABLE_COLOR + self.main.UPDATE_AVAILABLE_BUTTON_TEXT_COLOR = self.PRIMARY_300_COLOR + + self.main.HELP_AND_INFO_BUTTON_BG_COLOR = self.main.TOP_BAR_BUTTON_BG_COLOR + self.main.HELP_AND_INFO_BUTTON_HOVERED_BG_COLOR = self.main.TOP_BAR_BUTTON_HOVERED_BG_COLOR + self.main.HELP_AND_INFO_BUTTON_CLICKED_BG_COLOR = self.main.TOP_BAR_BUTTON_CLICKED_BG_COLOR + # self.main.HELP_AND_INFO_BUTTON_DISABLE_COLOR = self.main.TOP_BAR_BUTTON_DISABLE_COLOR + + + + # Selectable Language Window + self.selectable_language_window.BASIC_TEXT_COLOR = self.main.BASIC_TEXT_COLOR + + self.selectable_language_window.MAIN_BG_COLOR = self.DARK_875_COLOR + + self.selectable_language_window.GO_BACK_BUTTON_BG_COLOR = self.DARK_800_COLOR + self.selectable_language_window.GO_BACK_BUTTON_BG_HOVERED_COLOR = self.DARK_750_COLOR + self.selectable_language_window.GO_BACK_BUTTON_BG_CLICKED_COLOR = self.DARK_875_COLOR + + + self.selectable_language_window.TOP_BG_COLOR = self.main.SIDEBAR_BG_COLOR + self.selectable_language_window.TITLE_TEXT_COLOR = self.DARK_400_COLOR + self.selectable_language_window.LANGUAGE_BUTTON_BG_COLOR = self.selectable_language_window.MAIN_BG_COLOR + self.selectable_language_window.LANGUAGE_BUTTON_BG_HOVERED_COLOR = self.DARK_825_COLOR + self.selectable_language_window.LANGUAGE_BUTTON_BG_CLICKED_COLOR = self.DARK_888_COLOR + + + # Modal Window (Main Window) + self.main_window_cover.TEXT_COLOR = self.LIGHT_100_COLOR + + + self.confirmation_modal.MESSAGE_TEXT_COLOR = self.LIGHT_100_COLOR + self.confirmation_modal.FAKE_BORDER_COLOR = self.DARK_600_COLOR + self.confirmation_modal.BG_COLOR = self.DARK_800_COLOR + self.confirmation_modal.CONFIRMATION_BUTTONS_TEXT_COLOR = self.LIGHT_100_COLOR + + self.confirmation_modal.ACCEPT_BUTTON_BG_COLOR = self.PRIMARY_600_COLOR + self.confirmation_modal.ACCEPT_BUTTON_HOVERED_BG_COLOR = self.PRIMARY_450_COLOR + self.confirmation_modal.ACCEPT_BUTTON_CLICKED_BG_COLOR = self.PRIMARY_750_COLOR + self.confirmation_modal.DENY_BUTTON_BG_COLOR = self.DARK_750_COLOR + self.confirmation_modal.DENY_BUTTON_HOVERED_BG_COLOR = self.DARK_700_COLOR + self.confirmation_modal.DENY_BUTTON_CLICKED_BG_COLOR = self.DARK_825_COLOR + + + # Common + self.config_window.BASIC_TEXT_COLOR = self.main.BASIC_TEXT_COLOR + self.config_window.LABELS_TEXT_COLOR = self.config_window.BASIC_TEXT_COLOR + self.config_window.LABELS_DESC_TEXT_COLOR = self.DARK_500_COLOR + + self.config_window.LABELS_TEXT_DISABLED_COLOR = self.DARK_600_COLOR + + + # Top bar + self.config_window.TOP_BAR_BG_COLOR = self.DARK_850_COLOR + + # Restart Button + self.config_window.RESTART_BUTTON_BG_COLOR = self.PRIMARY_600_COLOR + self.config_window.RESTART_BUTTON_HOVERED_BG_COLOR = self.PRIMARY_500_COLOR + self.config_window.RESTART_BUTTON_CLICKED_BG_COLOR = self.PRIMARY_700_COLOR + + + # Compact Mode + self.config_window.COMPACT_MODE_SWITCH_BOX_BG_COLOR = self.DARK_775_COLOR + self.config_window.COMPACT_MODE_SWITCH_BOX_ACTIVE_BG_COLOR = self.PRIMARY_500_COLOR + self.config_window.COMPACT_MODE_SWITCH_BOX_BUTTON_COLOR = self.DARK_350_COLOR + self.config_window.COMPACT_MODE_SWITCH_BOX_BUTTON_HOVERED_COLOR = self.DARK_300_COLOR + + # Main + self.config_window.MAIN_BG_COLOR = self.DARK_950_COLOR + + # This is for fake border color + self.config_window.SB__WRAPPER_BG_COLOR = self.DARK_750_COLOR + + self.config_window.SB__BG_COLOR = self.DARK_888_COLOR + + self.config_window.SB__OPTIONMENU_BG_COLOR = self.DARK_925_COLOR + self.config_window.SB__OPTIONMENU_HOVERED_BG_COLOR = self.DARK_850_COLOR + self.config_window.SB__OPTIONMENU_CLICKED_BG_COLOR = self.DARK_950_COLOR + self.config_window.SB__DROPDOWN_MENU_WINDOW_BG_COLOR = self.config_window.MAIN_BG_COLOR + self.config_window.SB__DROPDOWN_MENU_WINDOW_BORDER_COLOR = self.DARK_600_COLOR + # self.config_window.SB__DROPDOWN_MENU_WINDOW_BG_COLOR = self.DARK_700_COLOR + self.config_window.SB__DROPDOWN_MENU_BG_COLOR = self.DARK_875_COLOR + self.config_window.SB__DROPDOWN_MENU_HOVERED_BG_COLOR = self.DARK_800_COLOR + self.config_window.SB__DROPDOWN_MENU_CLICKED_BG_COLOR = self.DARK_900_COLOR + + self.config_window.SB__SLIDER_BG_COLOR = self.DARK_700_COLOR + self.config_window.SB__SLIDER_PROGRESS_BG_COLOR = self.DARK_500_COLOR + self.config_window.SB__SLIDER_BUTTON_COLOR = self.DARK_700_COLOR + self.config_window.SB__SLIDER_BUTTON_HOVERED_COLOR = self.DARK_600_COLOR + + self.config_window.SB__SWITCH_BOX_BG_COLOR = self.DARK_800_COLOR + self.config_window.SB__SWITCH_BOX_ACTIVE_BG_COLOR = self.PRIMARY_500_COLOR + self.config_window.SB__SWITCH_BOX_BUTTON_COLOR = self.DARK_400_COLOR + self.config_window.SB__SWITCH_BOX_BUTTON_HOVERED_COLOR = self.DARK_350_COLOR + + self.config_window.SB__CHECKBOX_BORDER_COLOR = self.DARK_600_COLOR + self.config_window.SB__CHECKBOX_HOVER_COLOR = self.DARK_800_COLOR + self.config_window.SB__CHECKBOX_CHECKED_COLOR = self.PRIMARY_700_COLOR + self.config_window.SB__CHECKBOX_CHECKMARK_COLOR = self.config_window.BASIC_TEXT_COLOR + + self.config_window.SB__ENTRY_TEXT_COLOR = self.DARK_300_COLOR + self.config_window.SB__ENTRY_BG_COLOR = self.DARK_863_COLOR + self.config_window.SB__ENTRY_BORDER_COLOR = self.DARK_775_COLOR + + + self.config_window.SB__PROGRESSBAR_X_SLIDER__PROGRESSBAR_BG_COLOR = self.DARK_800_COLOR + self.config_window.SB__PROGRESSBAR_X_SLIDER__PROGRESSBAR_PROGRESS_BG_COLOR = self.PRIMARY_400_COLOR + + self.config_window.SB__PROGRESSBAR_X_SLIDER__SLIDER_BUTTON_COLOR = self.PRIMARY_600_COLOR + self.config_window.SB__PROGRESSBAR_X_SLIDER__SLIDER_BUTTON_HOVERED_COLOR = self.PRIMARY_400_COLOR + self.config_window.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_COLOR = self.DARK_800_COLOR + + self.config_window.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_COLOR = self.DARK_800_COLOR + self.config_window.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_HOVERED_COLOR = self.DARK_700_COLOR + self.config_window.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_CLICKED_COLOR = self.DARK_900_COLOR + self.config_window.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_DISABLED_COLOR = self.DARK_850_COLOR + + self.config_window.SB__PROGRESSBAR_X_SLIDER__ACTIVE_BUTTON_COLOR = self.PRIMARY_600_COLOR + self.config_window.SB__PROGRESSBAR_X_SLIDER__ACTIVE_BUTTON_HOVERED_COLOR = self.PRIMARY_500_COLOR + self.config_window.SB__PROGRESSBAR_X_SLIDER__ACTIVE_BUTTON_CLICKED_COLOR = self.PRIMARY_800_COLOR + # self.config_window.SB__PROGRESSBAR_X_SLIDER__ACTIVE_BUTTON_DISABLED_COLOR = self.PRIMARY_900_COLOR + + + # Side menu + self.config_window.SIDE_MENU_BG_COLOR = self.config_window.MAIN_BG_COLOR + + self.config_window.SIDE_MENU_LABELS_BG_COLOR = self.config_window.SIDE_MENU_BG_COLOR + self.config_window.SIDE_MENU_LABELS_BG_FOR_FAKE_BORDER_COLOR = self.config_window.SIDE_MENU_BG_COLOR + self.config_window.SIDE_MENU_LABELS_HOVERED_BG_COLOR = self.DARK_850_COLOR + self.config_window.SIDE_MENU_LABELS_CLICKED_BG_COLOR = self.PRIMARY_750_COLOR + self.config_window.SIDE_MENU_LABELS_SELECTED_TEXT_COLOR = self.PRIMARY_200_COLOR + + self.config_window.SIDE_MENU_SELECTED_MARK_ACTIVE_BG_COLOR = self.main.SF__SWITCH_BOX_ACTIVE_BG_COLOR + + self.config_window.NOW_VERSION_TEXT_COLOR = self.DARK_300_COLOR + + # Error Message Window for Config Window + # The color code [#bb4448] is a mixture of [#a9555c] and [#cc3333] (for a redder shade). + self.config_window.SB__ERROR_MESSAGE_BG_COLOR = "#bb4448" + self.config_window.SB__ERROR_MESSAGE_TEXT_COLOR = "#fff" + + + + + + + + + + # def _createLightModeColor(self): + # # Common + # self.main.BASIC_TEXT_COLOR = self.DARK_1000_COLOR + # self.main.LABELS_TEXT_COLOR = self.main.BASIC_TEXT_COLOR + + # # Main + # self.main.MAIN_BG_COLOR = self.LIGHT_300_COLOR + + # self.main.TEXTBOX_BG_COLOR = self.LIGHT_200_COLOR + # self.main.TEXTBOX_TEXT_COLOR = self.main.BASIC_TEXT_COLOR + # self.main.TEXTBOX_TAB_BG_PASSIVE_COLOR = self.LIGHT_350_COLOR + # self.main.TEXTBOX_TAB_BG_ACTIVE_COLOR = self.main.TEXTBOX_BG_COLOR + # self.main.TEXTBOX_TAB_BG_HOVERED_COLOR = self.LIGHT_300_COLOR + # self.main.TEXTBOX_TAB_BG_CLICKED_COLOR = self.LIGHT_350_COLOR + # self.main.TEXTBOX_TAB_TEXT_ACTIVE_COLOR = self.main.BASIC_TEXT_COLOR + # self.main.TEXTBOX_TAB_TEXT_PASSIVE_COLOR = self.LIGHT_600_COLOR + + # self.main.TEXTBOX_ENTRY_TEXT_COLOR = self.LIGHT_800_COLOR + # self.main.TEXTBOX_ENTRY_TEXT_DISABLED_COLOR = self.LIGHT_500_COLOR + # self.main.TEXTBOX_ENTRY_BG_COLOR = self.LIGHT_325_COLOR + # self.main.TEXTBOX_ENTRY_BORDER_COLOR = self.LIGHT_400_COLOR + # self.main.TEXTBOX_ENTRY_PLACEHOLDER_COLOR = self.LIGHT_600_COLOR + # self.main.TEXTBOX_ENTRY_PLACEHOLDER_DISABLED_COLOR = self.LIGHT_700_COLOR + + + + # # Sidebar + # self.main.SIDEBAR_BG_COLOR = self.LIGHT_350_COLOR + + # # Sidebar Features + # self.main.SF__BG_COLOR = self.LIGHT_375_COLOR + # self.main.SF__HOVERED_BG_COLOR = self.LIGHT_300_COLOR + # self.main.SF__CLICKED_BG_COLOR = self.LIGHT_200_COLOR + # self.main.SF__TEXT_DISABLED_COLOR = self.LIGHT_500_COLOR + + # self.main.SF__SWITCH_BOX_BG_COLOR = self.LIGHT_300_COLOR + # self.main.SF__SWITCH_BOX_HOVERED_BG_COLOR = self.LIGHT_450_COLOR + # self.main.SF__SWITCH_BOX_CLICKED_BG_COLOR = self.LIGHT_350_COLOR + # self.main.SF__SWITCH_BOX_ACTIVE_BG_COLOR = self.PRIMARY_650_COLOR + # self.main.SF__SWITCH_BOX_ACTIVE_HOVERED_BG_COLOR = self.PRIMARY_500_COLOR + # self.main.SF__SWITCH_BOX_ACTIVE_CLICKED_BG_COLOR = self.PRIMARY_700_COLOR + # self.main.SF__SWITCH_BOX_DISABLE_BG_COLOR = self.PRIMARY_900_COLOR + + # self.main.SF__SELECTED_MARK_ACTIVE_BG_COLOR = self.main.SF__SWITCH_BOX_ACTIVE_BG_COLOR + # self.main.SF__SELECTED_MARK_ACTIVE_HOVERED_BG_COLOR = self.main.SF__SWITCH_BOX_ACTIVE_HOVERED_BG_COLOR + # self.main.SF__SELECTED_MARK_ACTIVE_CLICKED_BG_COLOR = self.main.SF__SWITCH_BOX_ACTIVE_CLICKED_BG_COLOR + # self.main.SF__SELECTED_MARK_DISABLE_BG_COLOR = self.main.SF__SWITCH_BOX_DISABLE_BG_COLOR + + + # # Sidebar quick settings + # self.main.SLS__TITLE_TEXT_COLOR = self.LIGHT_800_COLOR + + # self.main.SLS__BG_COLOR = self.LIGHT_300_COLOR + + # self.main.SLS__PRESETS_TAB_BG_HOVERED_COLOR = self.LIGHT_350_COLOR + # self.main.SLS__PRESETS_TAB_BG_CLICKED_COLOR = self.LIGHT_800_COLOR + # self.main.SLS__PRESETS_TAB_BG_PASSIVE_COLOR = self.main.SIDEBAR_BG_COLOR + # self.main.SLS__PRESETS_TAB_BG_ACTIVE_COLOR = self.main.SLS__BG_COLOR + # self.main.SLS__PRESETS_TAB_ACTIVE_TEXT_COLOR_PASSIVE = self.LIGHT_600_COLOR + # self.main.SLS__PRESETS_TAB_ACTIVE_TEXT_COLOR = self.main.BASIC_TEXT_COLOR + + # self.main.SLS__BOX_BG_COLOR = self.LIGHT_350_COLOR + # self.main.SLS__BOX_SECTION_TITLE_TEXT_COLOR = self.LIGHT_800_COLOR + # self.main.SLS__BOX_ARROWS_TEXT_COLOR = self.LIGHT_700_COLOR + + # self.main.SLS__OPTIONMENU_BG_COLOR = self.LIGHT_500_COLOR + + + # self.main.CONFIG_BUTTON_BG_COLOR = self.main.SIDEBAR_BG_COLOR + # self.main.CONFIG_BUTTON_HOVERED_BG_COLOR = self.LIGHT_800_COLOR + # self.main.CONFIG_BUTTON_CLICKED_BG_COLOR = self.LIGHT_900_COLOR + # # self.main.CONFIG_BUTTON_DISABLE_COLOR = self.LIGHT_900_COLOR + + # self.main.MINIMIZE_SIDEBAR_BUTTON_BG_COLOR = self.main.SIDEBAR_BG_COLOR + # self.main.MINIMIZE_SIDEBAR_BUTTON_HOVERED_BG_COLOR = self.LIGHT_800_COLOR + # self.main.MINIMIZE_SIDEBAR_BUTTON_CLICKED_BG_COLOR = self.LIGHT_900_COLOR + # # self.main.MINIMIZE_SIDEBAR_BUTTON_DISABLE_COLOR = self.LIGHT_900_COLOR + + # self.main.HELP_AND_INFO_BUTTON_BG_COLOR = self.main.MAIN_BG_COLOR + # self.main.HELP_AND_INFO_BUTTON_HOVERED_BG_COLOR = self.LIGHT_350_COLOR + # self.main.HELP_AND_INFO_BUTTON_CLICKED_BG_COLOR = self.LIGHT_450_COLOR + # # self.main.HELP_AND_INFO_BUTTON_DISABLE_COLOR = self.LIGHT_900_COLOR + + + # # Common + # self.config_window.BASIC_TEXT_COLOR = self.main.BASIC_TEXT_COLOR + # self.config_window.LABELS_TEXT_COLOR = self.config_window.BASIC_TEXT_COLOR + # self.config_window.LABELS_DESC_TEXT_COLOR = self.DARK_500_COLOR + + + # # Top bar + # self.config_window.TOP_BAR_BG_COLOR = self.DARK_850_COLOR + + + # # Main + # self.config_window.MAIN_BG_COLOR = self.DARK_950_COLOR + + # # This is for fake border color + # self.config_window.SB__WRAPPER_BG_COLOR = self.DARK_750_COLOR + + # self.config_window.SB__BG_COLOR = self.DARK_888_COLOR + + # self.config_window.SB__OPTIONMENU_BG_COLOR = self.DARK_925_COLOR + # self.config_window.SB__OPTIONMENU_HOVERED_BG_COLOR = self.DARK_875_COLOR + + # self.config_window.SB__SLIDER_BUTTON_COLOR = self.DARK_700_COLOR + # self.config_window.SB__SLIDER_BUTTON_HOVERED_COLOR = self.DARK_600_COLOR + + # self.config_window.SB__SWITCH_BOX_BG_COLOR = self.main.SF__SWITCH_BOX_BG_COLOR + # self.config_window.SB__SWITCH_BOX_ACTIVE_BG_COLOR = self.main.SF__SWITCH_BOX_ACTIVE_BG_COLOR + + # self.config_window.SB__CHECKBOX_BORDER_COLOR = self.DARK_500_COLOR + # self.config_window.SB__CHECKBOX_HOVER_COLOR = self.DARK_800_COLOR + # self.config_window.SB__CHECKBOX_CHECKED_COLOR = self.PRIMARY_700_COLOR + # self.config_window.SB__CHECKBOX_CHECKMARK_COLOR = self.config_window.BASIC_TEXT_COLOR + + # self.config_window.SB__PROGRESSBAR_X_SLIDER__SLIDER_BUTTON_COLOR = self.PRIMARY_700_COLOR + # self.config_window.SB__PROGRESSBAR_X_SLIDER__SLIDER_BUTTON_HOVERED_COLOR = self.PRIMARY_500_COLOR + # self.config_window.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_COLOR = self.DARK_800_COLOR + + # self.config_window.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_COLOR = self.DARK_800_COLOR + # self.config_window.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_HOVERED_COLOR = self.DARK_700_COLOR + # self.config_window.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_CLICKED_COLOR = self.DARK_900_COLOR + # self.config_window.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_DISABLED_COLOR = self.DARK_850_COLOR + + # self.config_window.SB__PROGRESSBAR_X_SLIDER__ACTIVE_BUTTON_COLOR = self.PRIMARY_700_COLOR + # self.config_window.SB__PROGRESSBAR_X_SLIDER__ACTIVE_BUTTON_HOVERED_COLOR = self.PRIMARY_600_COLOR + # self.config_window.SB__PROGRESSBAR_X_SLIDER__ACTIVE_BUTTON_CLICKED_COLOR = self.PRIMARY_900_COLOR + # # self.config_window.SB__PROGRESSBAR_X_SLIDER__ACTIVE_BUTTON_DISABLED_COLOR = self.PRIMARY_900_COLOR + + + # # Side menu + # self.config_window.SIDE_MENU_BG_COLOR = self.config_window.MAIN_BG_COLOR + + # self.config_window.SIDE_MENU_LABELS_BG_COLOR = self.config_window.SIDE_MENU_BG_COLOR + # self.config_window.SIDE_MENU_LABELS_BG_FOR_FAKE_BORDER_COLOR = self.config_window.SIDE_MENU_BG_COLOR + # self.config_window.SIDE_MENU_LABELS_HOVERED_BG_COLOR = self.DARK_850_COLOR + # self.config_window.SIDE_MENU_LABELS_CLICKED_BG_COLOR = self.PRIMARY_900_COLOR + # self.config_window.SIDE_MENU_LABELS_SELECTED_TEXT_COLOR = self.PRIMARY_300_COLOR + + # self.config_window.SIDE_MENU_SELECTED_MARK_ACTIVE_BG_COLOR = self.main.SF__SWITCH_BOX_ACTIVE_BG_COLOR diff --git a/vrct_gui/ui_managers/ImageFileManager.py b/vrct_gui/ui_managers/ImageFileManager.py new file mode 100644 index 00000000..8bb71b4a --- /dev/null +++ b/vrct_gui/ui_managers/ImageFileManager.py @@ -0,0 +1,58 @@ +from ..ui_utils import getImageFileFromUiUtils + + +class ImageFileManager(): + def __init__(self, theme:str ="Dark"): + if theme == "Dark": + self._createDarkModeImages() + elif theme == "Light": + self._createLightModeImages() + + + def _createDarkModeImages(self): + self.VRCT_LOGO = getImageFileFromUiUtils("vrct_logo_for_dark_mode.png") + self.VRCT_LOGO_MARK = getImageFileFromUiUtils("vrct_logo_mark_white.png") + + self.TRANSLATION_ICON = getImageFileFromUiUtils("translation_icon_white.png") + self.TRANSLATION_ICON_DISABLED = getImageFileFromUiUtils("translation_icon_disabled.png") + self.MIC_ICON = getImageFileFromUiUtils("mic_icon_white.png") + self.MIC_ICON_DISABLED = getImageFileFromUiUtils("mic_icon_disabled.png") + self.HEADPHONES_ICON = getImageFileFromUiUtils("headphones_icon_white.png") + self.HEADPHONES_ICON_DISABLED = getImageFileFromUiUtils("headphones_icon_disabled.png") + self.FOREGROUND_ICON = getImageFileFromUiUtils("foreground_icon_white.png") + self.FOREGROUND_ICON_DISABLED = getImageFileFromUiUtils("foreground_icon_disabled.png") + + self.NARROW_ARROW_DOWN = getImageFileFromUiUtils("narrow_arrow_down.png") + + self.CONFIGURATION_ICON = getImageFileFromUiUtils("configuration_icon_white.png") + self.CONFIGURATION_ICON_DISABLED = getImageFileFromUiUtils("configuration_icon_disabled.png") + + self.ARROW_LEFT = getImageFileFromUiUtils("arrow_left_white.png") + self.ARROW_LEFT_DISABLED = getImageFileFromUiUtils("arrow_left_disabled.png") + + self.REFRESH_ICON = getImageFileFromUiUtils("refresh_icon.png") + self.HELP_ICON = getImageFileFromUiUtils("help_icon_white.png") + + def _createLightModeImages(self): + self.VRCT_LOGO = getImageFileFromUiUtils("vrct_logo_for_light_mode.png") + self.VRCT_LOGO_MARK = getImageFileFromUiUtils("vrct_logo_mark_black.png") + + + self.TRANSLATION_ICON = getImageFileFromUiUtils("translation_icon_white.png") + self.TRANSLATION_ICON_DISABLED = getImageFileFromUiUtils("translation_icon_disabled.png") + self.MIC_ICON = getImageFileFromUiUtils("mic_icon_white.png") + self.MIC_ICON_DISABLED = getImageFileFromUiUtils("mic_icon_disabled.png") + self.HEADPHONES_ICON = getImageFileFromUiUtils("headphones_icon_white.png") + self.HEADPHONES_ICON_DISABLED = getImageFileFromUiUtils("headphones_icon_disabled.png") + self.FOREGROUND_ICON = getImageFileFromUiUtils("foreground_icon_white.png") + self.FOREGROUND_ICON_DISABLED = getImageFileFromUiUtils("foreground_icon_disabled.png") + + self.NARROW_ARROW_DOWN = getImageFileFromUiUtils("narrow_arrow_down.png") + + self.CONFIGURATION_ICON = getImageFileFromUiUtils("configuration_icon_white.png") + self.CONFIGURATION_ICON_DISABLED = getImageFileFromUiUtils("configuration_icon_disabled.png") + + self.ARROW_LEFT = getImageFileFromUiUtils("arrow_left_white.png") + self.ARROW_LEFT_DISABLED = getImageFileFromUiUtils("arrow_left_disabled.png") + + self.HELP_ICON = getImageFileFromUiUtils("help_icon_white.png") \ No newline at end of file diff --git a/vrct_gui/ui_managers/UiScalingManager.py b/vrct_gui/ui_managers/UiScalingManager.py new file mode 100644 index 00000000..dc385b34 --- /dev/null +++ b/vrct_gui/ui_managers/UiScalingManager.py @@ -0,0 +1,286 @@ +from types import SimpleNamespace + +class UiScalingManager(): + def __init__(self, scaling_percentage): + scaling_float = int(scaling_percentage.replace("%", "")) / 100 + self.SCALING_FLOAT = max(scaling_float, 0.4) + self.common = SimpleNamespace() + self.main = SimpleNamespace() + self.config_window = SimpleNamespace() + self.selectable_language_window = SimpleNamespace() + self.main_window_cover = SimpleNamespace() + self.error_message_window = SimpleNamespace() + self.confirmation_modal = SimpleNamespace() + + self._calculatedUiSizes() + + + + + def _calculatedUiSizes(self): + # Common + # RESPONSIVE_UI_SIZE_INT_10 ... RESPONSIVE_UI_SIZE_INT_300 + self.common.SCROLLBAR_IPADX = (self._calculateUiSize(2), self._calculateUiSize(2)) + self.common.SCROLLBAR_WIDTH = self._calculateUiSize(16) + + for i in range(10, 501, 10): + setattr(self.main, f"RESPONSIVE_UI_SIZE_INT_{i}", self._calculateUiSize(i)) + setattr(self.config_window, f"RESPONSIVE_UI_SIZE_INT_{i}", self._calculateUiSize(i)) + + + # Main + self.main.MAIN_AREA_MIN_WIDTH = self._calculateUiSize(640) + self.main.SIDEBAR_MIN_WIDTH = self._calculateUiSize(230) + + self.main.TEXTBOX_PADX = self._calculateUiSize(16) + self.main.TEXTBOX_CORNER_RADIUS = self._calculateUiSize(6) + + self.main.TEXTBOX_TAB_CORNER_RADIUS = self._calculateUiSize(8) + self.main.TEXTBOX_TAB_FONT_SIZE = self._calculateUiSize(12) + self.main.TEXTBOX_TAB_PADX = self._calculateUiSize(10) + self.main.TEXTBOX_TAB_PADY = (self._calculateUiSize(4), self._calculateUiSize(10)) + + self.main.TEXTBOX_FIRST_INSERT_SPACING = self._calculateUiSize(16) + self.main.TEXTBOX_FONT_SIZE__LABEL = self._calculateUiSize(12) + self.main.TEXTBOX_FONT_SIZE__TIMESTAMP = self._calculateUiSize(12) + self.main.TEXTBOX_FONT_SIZE__SYSTEM_TEXT_FONT = self._calculateUiSize(12) + self.main.TEXTBOX_FONT_SIZE__SECONDARY_TEXT_FONT = self._calculateUiSize(12) + self.main.TEXTBOX_FONT_SIZE__MAIN_TEXT_FONT = self._calculateUiSize(16) + + self.main.TEXTBOX_ENTRY_FONT_SIZE = self._calculateUiSize(16) + self.main.TEXTBOX_ENTRY_HEIGHT = self._calculateUiSize(40) + self.main.TEXTBOX_ENTRY_PADX = self.main.TEXTBOX_PADX + self.main.TEXTBOX_ENTRY_PADY = self._calculateUiSize(10) + self.main.TEXTBOX_ENTRY_IPADX = self._calculateUiSize(14) + self.main.TEXTBOX_ENTRY_IPADY = (self._calculateUiSize(2, True), self._calculateUiSize(3, True)) + + + # Sidebar + # Sidebar Features + self.main.SF__LOGO_MAX_SIZE = self._calculateUiSize(120) + self.main.SF__LOGO_PADY = (self._calculateUiSize(12),self._calculateUiSize(8)) + self.main.SF__LOGO_HEIGHT_FOR_ADJUSTMENT = (self._calculateUiSize(6)) + + self.main.SF__LABELS_IPADY = self._calculateUiSize(16) + self.main.SF__COMPACT_MODE_ICON_PADY = self.main.SF__LABELS_IPADY + self.main.SF__COMPACT_MODE_ICON_PADX = self.main.SF__COMPACT_MODE_ICON_PADY + self.main.SF__LABEL_LEFT_PAD = self._calculateUiSize(20) + self.main.SF__LABEL_FONT_SIZE = self._calculateUiSize(16) + self.main.SF__COMPACT_MODE_IMAGE_SIZE = (self._calculateUiSize(20), self._calculateUiSize(20)) + + self.main.SF__SWITCH_BOX_PADX = (self.main.SF__LABEL_LEFT_PAD, self._calculateUiSize(10)) + self.main.SF__SWITCH_BOX_WIDTH = self._calculateUiSize(40) + self.main.SF__SWITCH_BOX_HEIGHT = self._calculateUiSize(16) + + self.main.SF__SELECTED_MARK_WIDTH = self._calculateUiSize(3, True) + + + # Sidebar Quick Language Settings, SQLS + self.main.SLS__TITLE_FONT_SIZE = self._calculateUiSize(16) + self.main.SLS__TITLE_PADY = (self._calculateUiSize(12), self._calculateUiSize(6)) + + self.main.SLS__PRESET_TAB_NUMBER_FONT_SIZE = self._calculateUiSize(16) + self.main.SLS__PRESET_TAB_NUMBER_HEIGHT = self._calculateUiSize(30) + self.main.SLS__PRESET_TAB_NUMBER_CORNER_RADIUS = self._calculateUiSize(6) + self.main.SLS__PRESET_TAB_NUMBER_ADJUSTED_HEIGHT = self._calculateUiSize(36) + + self.main.SLS__BOX_SECTION_TITLE_FONT_SIZE = self._calculateUiSize(16) + self.main.SLS__BOX_SECTION_TITLE_BOTTOM_PADY = self._calculateUiSize(10) + self.main.SLS__BOX_IPADX = self._calculateUiSize(10) + self.main.SLS__BOX_IPADY = (self._calculateUiSize(8),self._calculateUiSize(18)) + self.main.SLS__BOX_OPTION_MENU_FONT_SIZE = self._calculateUiSize(14) + self.main.SLS__BOX_OPTION_MENU_IPADY = self._calculateUiSize(2) + self.main.SLS__BOX_OPTION_MENU_ARROW_IMAGE_SIZE = (self._calculateUiSize(20), self._calculateUiSize(20)) + # self.main.SLS__BOX_OPTION_MENU_WIDTH = self._calculateUiSize(200) + self.main.SLS__BOX_ARROWS_PADY = self._calculateUiSize(10) + self.main.SLS__BOX_ARROWS_IMAGE_SIZE = self.dupTuple(self._calculateUiSize(16)) + self.main.SLS__BOX_ARROWS_DESC_FONT_SIZE = self._calculateUiSize(12) + self.main.SLS__BOX_ARROWS_DESC_PADX = self._calculateUiSize(6) + self.main.SLS__BOX_TOP_PADY = self._calculateUiSize(16) + + self.main.SIDEBAR_CONFIG_BUTTON_CORNER_RADIUS = self._calculateUiSize(6) + self.main.SIDEBAR_CONFIG_BUTTON_IMAGE_SIZE = self.main.SF__COMPACT_MODE_IMAGE_SIZE + self.main.SIDEBAR_CONFIG_BUTTON_PADX = self._calculateUiSize(10) + self.main.SIDEBAR_CONFIG_BUTTON_PADY = self._calculateUiSize(10) + self.main.SIDEBAR_CONFIG_BUTTON_IPADY = self._calculateUiSize(8) + + self.main.TOP_BAR_BUTTON_PADY = (self._calculateUiSize(6),0) + + self.main.UPDATE_AVAILABLE_BUTTON_CORNER_RADIUS = self._calculateUiSize(6) + self.main.UPDATE_AVAILABLE_BUTTON_SIZE = (self._calculateUiSize(18), self._calculateUiSize(18)) + self.main.UPDATE_AVAILABLE_BUTTON_FONT_SIZE = self._calculateUiSize(12) + self.main.UPDATE_AVAILABLE_BUTTON_PADX = (0, self._calculateUiSize(4)) + self.main.UPDATE_AVAILABLE_BUTTON_IPADX = self._calculateUiSize(6) + self.main.UPDATE_AVAILABLE_ICON_PADX = (self._calculateUiSize(6), self._calculateUiSize(4)) + self.main.UPDATE_AVAILABLE_PADX_BETWEEN_LABEL_AND_ICON = self._calculateUiSize(4) + + + + self.main.HELP_AND_INFO_BUTTON_CORNER_RADIUS = self._calculateUiSize(6) + self.main.HELP_AND_INFO_BUTTON_SIZE = self._calculateUiSize(24) + self.main.HELP_AND_INFO_BUTTON_PADX = (0, self._calculateUiSize(6)) + self.main.HELP_AND_INFO_BUTTON_IPADXY = self._calculateUiSize(6) + + self.main.MINIMIZE_SIDEBAR_BUTTON_ICON_SIZE_X = int(self.main.TEXTBOX_PADX/2+self.main.TEXTBOX_CORNER_RADIUS*2) + self.main.MINIMIZE_SIDEBAR_BUTTON_ICON_SIZE_Y = self._calculateUiSize(26) + + + + # Selectable Language Window + self.selectable_language_window.TOP_BAR_MIN_HEIGHT = self._calculateUiSize(50) + self.selectable_language_window.SCROLLBAR_IPADX = self.common.SCROLLBAR_IPADX + self.selectable_language_window.SCROLLBAR_WIDTH = self.common.SCROLLBAR_WIDTH + + self.selectable_language_window.GO_BACK_BUTTON_LABEL_FONT_SIZE = self._calculateUiSize(14) + self.selectable_language_window.GO_BACK_BUTTON_IPADX = self._calculateUiSize(10) + self.selectable_language_window.GO_BACK_BUTTON_IPADY = self._calculateUiSize(8) + self.selectable_language_window.TITLE_FONT_SIZE = self._calculateUiSize(18) + + self.selectable_language_window.VALUES_TEXT_FONT_SIZE = self._calculateUiSize(14) + self.selectable_language_window.VALUES_TEXT_IPADX = (self._calculateUiSize(8), 0) + self.selectable_language_window.VALUES_TEXT_IPADY = self._calculateUiSize(8) + + + self.main_window_cover.TEXT_FONT_SIZE = self._calculateUiSize(20) + + + self.confirmation_modal.FAKE_BORDER_SIZE = self._calculateUiSize(1, is_allowed_odd=True) + self.confirmation_modal.CONTENTS_WRAPPER = self._calculateUiSize(20) + self.confirmation_modal.MARGIN_BETWEEN_MESSAGE_AND_BUTTONS = self._calculateUiSize(40) + self.confirmation_modal.MESSAGE_FONT_SIZE = self._calculateUiSize(20) + self.confirmation_modal.CONFIRMATION_BUTTONS_TEXT_FONT_SIZE = self._calculateUiSize(18) + self.confirmation_modal.BUTTONS_BETWEEN_PADDING = self._calculateUiSize(100) + self.confirmation_modal.BUTTONS_CORNER_RADIUS = self._calculateUiSize(6) + self.confirmation_modal.BUTTONS_IPADX = self._calculateUiSize(10) + self.confirmation_modal.BUTTONS_IPADY = self._calculateUiSize(6) + + + # Config Window + self.config_window.DEFAULT_WIDTH = self._calculateUiSize(1080) + self.config_window.DEFAULT_HEIGHT = self._calculateUiSize(680) + + # Top bar common + self.config_window.TOP_BAR__MIN_HEIGHT = self._calculateUiSize(40) + self.config_window.TOP_BAR__IPADY = self._calculateUiSize(12) + + # Top bar Side + self.config_window.TOP_BAR_SIDE_AREA_MIN_WIDTH = self._calculateUiSize(220) + self.config_window.TOP_BAR_SIDE__CONFIG_LOGO_MARK_SIZE = self.dupTuple(self._calculateUiSize(28)) + self.config_window.TOP_BAR_SIDE__CONFIG_TITLE_FONT_SIZE = self._calculateUiSize(22) + self.config_window.TOP_BAR_SIDE__CONFIG_TITLE_LEFT_PADX = int(self.config_window.TOP_BAR_SIDE__CONFIG_TITLE_FONT_SIZE + self._calculateUiSize(16)) + self.config_window.TOP_BAR_SIDE__TITLE_PADX= self._calculateUiSize(30) + + # Restart Button + self.config_window.RESTART_BUTTON_LABEL_FONT_SIZE = self._calculateUiSize(12) + self.config_window.RESTART_BUTTON_PADX = (0, self._calculateUiSize(20)) + self.config_window.RESTART_BUTTON_CORNER_RADIUS = self._calculateUiSize(20) + self.config_window.RESTART_BUTTON_IPADX = self._calculateUiSize(20) + self.config_window.RESTART_BUTTON_IPADY = self._calculateUiSize(10) + + # Compact Mode + self.config_window.COMPACT_MODE_PADX = (0, self._calculateUiSize(20)) + self.config_window.COMPACT_MODE_LABEL_FONT_SIZE = self._calculateUiSize(12) + self.config_window.COMPACT_MODE_LABEL_PADX = (0, self._calculateUiSize(10)) + self.config_window.COMPACT_MODE_SWITCH_WIDTH = self._calculateUiSize(40) + self.config_window.COMPACT_MODE_SWITCH_HEIGHT = self._calculateUiSize(16) + + + # Side menu + self.config_window.SIDE_MENU_TOP_PADY = self._calculateUiSize(54) + self.config_window.SIDE_MENU_LABELS_IPADX = self._calculateUiSize(20) + self.config_window.SIDE_MENU_LABELS_IPADY = self._calculateUiSize(8) + self.config_window.SIDE_MENU_LABELS_FONT_SIZE = self._calculateUiSize(18) + + self.config_window.NOW_VERSION_FONT_SIZE = self._calculateUiSize(12) + + # Top bar Main + self.config_window.TOP_BAR_MAIN__TITLE_FONT_SIZE = self._calculateUiSize(22) + self.config_window.SCROLLBAR_IPADX = self.common.SCROLLBAR_IPADX + self.config_window.SCROLLBAR_WIDTH = self.common.SCROLLBAR_WIDTH + + + # Setting Box + self.config_window.MAIN_AREA_MIN_WIDTH = self._calculateUiSize(720) + self.config_window.SB__TOP_PADY = (self._calculateUiSize(60)) + self.config_window.SB__IPADX = self._calculateUiSize(20) + self.config_window.SB__IPADY = self._calculateUiSize(12) + self.config_window.SB__BOTTOM_MARGIN = (0, self._calculateUiSize(60)) + self.config_window.SB__FAKE_BOTTOM_BORDER_SIZE = (0, self._calculateUiSize(1, is_allowed_odd=True)) + + self.config_window.SB__SECTION_TITLE_FONT_SIZE = self._calculateUiSize(20) + self.config_window.SB__SECTION_TITLE_BOTTOM_PADY = (0, self._calculateUiSize(10)) + + self.config_window.SB__LABEL_FONT_SIZE = self._calculateUiSize(16) + self.config_window.SB__DESC_FONT_SIZE = self._calculateUiSize(14) + self.config_window.SB__DESC_TOP_PADY = self._calculateUiSize(2) + + + self.config_window.SB__ERROR_MESSAGE_IPADX = (self._calculateUiSize(10), self._calculateUiSize(10)) + self.config_window.SB__ERROR_MESSAGE_IPADY = (self._calculateUiSize(6), self._calculateUiSize(6)) + self.config_window.SB__ERROR_MESSAGE_FONT_SIZE = self._calculateUiSize(14) + + + self.config_window.SB__SELECTOR_FONT_SIZE = self._calculateUiSize(14) + self.config_window.SB__RADIO_BUTTON_FONT_SIZE = self.config_window.SB__SELECTOR_FONT_SIZE + self.config_window.SB__BUTTON_FONT_SIZE = self.config_window.SB__SELECTOR_FONT_SIZE + + + + self.config_window.SB__OPTION_MENU_FONT_SIZE = self.config_window.SB__SELECTOR_FONT_SIZE + self.config_window.SB__OPTIONMENU_MIN_HEIGHT = self._calculateUiSize(30) + self.config_window.SB__OPTIONMENU_MIN_WIDTH = self._calculateUiSize(200) + self.config_window.SB__OPTIONMENU_IPADX = (self._calculateUiSize(8), self._calculateUiSize(8)) + self.config_window.SB__OPTIONMENU_IPADY = self._calculateUiSize(2) + self.config_window.SB__OPTIONMENU_IPADX_BETWEEN_IMG = self._calculateUiSize(8) + self.config_window.SB__OPTIONMENU_IMG_SIZE = (self._calculateUiSize(14), self._calculateUiSize(14)) + + self.config_window.SB__DROPDOWN_MENU_WINDOW_ADDITIONAL_Y_POS = self._calculateUiSize(4) + self.config_window.SB__DROPDOWN_MENU_WIDTH = self.config_window.SB__OPTIONMENU_MIN_WIDTH + self.config_window.SB__DROPDOWN_MENU_WINDOW_BORDER_WIDTH = self._calculateUiSize(1, is_allowed_odd=True) + self.config_window.SB__DROPDOWN_MENU_SCROLLBAR_IPADX = self.common.SCROLLBAR_IPADX + self.config_window.SB__DROPDOWN_MENU_SCROLLBAR_WIDTH = self.common.SCROLLBAR_WIDTH + self.config_window.SB__DROPDOWN_MENU_VALUE_IPADX = (self._calculateUiSize(8), 0) + self.config_window.SB__DROPDOWN_MENU_VALUE_IPADY = (self._calculateUiSize(6), self._calculateUiSize(6)) + self.config_window.SB__DROPDOWN_MENU_VALUE_PADY = (0, self._calculateUiSize(1, is_allowed_odd=True)) + self.config_window.SB__DROPDOWN_MENU_VALUE_FONT_SIZE = self._calculateUiSize(14) + self.config_window.SB__DROPDOWN_MENU_VALUE_DEFAULT_MIN_WIDTH = self._calculateUiSize(200) + + + self.config_window.SB__SWITCH_WIDTH = self._calculateUiSize(50) + + self.config_window.SB__SWITCH_BOX_WIDTH = self._calculateUiSize(40) + self.config_window.SB__SWITCH_BOX_HEIGHT = self._calculateUiSize(16) + + self.config_window.SB__CHECKBOX_SIZE = self._calculateUiSize(24) + self.config_window.SB__CHECKBOX_BORDER_WIDTH = self._calculateUiSize(2) + self.config_window.SB__CHECKBOX_CORNER_RADIUS = self._calculateUiSize(4) + + self.config_window.SB__ENTRY_FONT_SIZE = self.config_window.SB__SELECTOR_FONT_SIZE + self.config_window.SB__ENTRY_HEIGHT = self._calculateUiSize(30) + + self.config_window.SB__SLIDER_WIDTH = self._calculateUiSize(200) + self.config_window.SB__SLIDER_HEIGHT = self._calculateUiSize(16) + + self.config_window.SB__PROGRESSBAR_X_SLIDER__ENTRY_WIDTH = self.config_window.RESPONSIVE_UI_SIZE_INT_50 + self.config_window.SB__PROGRESSBAR_X_SLIDER__ENTRY_HEIGHT = self.config_window.SB__ENTRY_HEIGHT + self.config_window.SB__PROGRESSBAR_X_SLIDER__SLIDER_HEIGHT = self._calculateUiSize(40) + self.config_window.SB__PROGRESSBAR_X_SLIDER__PROGRESSBAR_HEIGHT = self._calculateUiSize(8) + self.config_window.SB__PROGRESSBAR_X_SLIDER__SLIDER_BUTTON_LENGTH = self._calculateUiSize(2) + self.config_window.SB__PROGRESSBAR_X_SLIDER__BAR_PADX = (self._calculateUiSize(30), self._calculateUiSize(30)) + + self.config_window.SB__PROGRESSBAR_X_SLIDER__BUTTON_IPADXY = self._calculateUiSize(10) + self.config_window.SB__PROGRESSBAR_X_SLIDER__BUTTON_ICON_SIZE = self._calculateUiSize(20) + + + + def _calculateUiSize(self, default_size, is_allowed_odd:bool=False, is_zero_allowed:bool=False): + size = int(default_size * self.SCALING_FLOAT) + size += 1 if not is_allowed_odd and size % 2 != 0 else 0 + if size <= 0: + size = 0 if is_zero_allowed else 1 + + return size + + @staticmethod + def dupTuple(value): + return (value, value) \ No newline at end of file diff --git a/vrct_gui/ui_managers/__init__.py b/vrct_gui/ui_managers/__init__.py new file mode 100644 index 00000000..f0dd1edb --- /dev/null +++ b/vrct_gui/ui_managers/__init__.py @@ -0,0 +1,3 @@ +from .ColorThemeManager import ColorThemeManager +from .ImageFileManager import ImageFileManager +from .UiScalingManager import UiScalingManager \ No newline at end of file diff --git a/vrct_gui/ui_utils/__init__.py b/vrct_gui/ui_utils/__init__.py new file mode 100644 index 00000000..bc2a1337 --- /dev/null +++ b/vrct_gui/ui_utils/__init__.py @@ -0,0 +1 @@ +from .ui_utils import * \ No newline at end of file diff --git a/vrct_gui/ui_utils/ui_utils.py b/vrct_gui/ui_utils/ui_utils.py new file mode 100644 index 00000000..bf64a985 --- /dev/null +++ b/vrct_gui/ui_utils/ui_utils.py @@ -0,0 +1,257 @@ +from os import path as os_path +from PIL.Image import open as Image_open, LANCZOS +from time import sleep + +from customtkinter import CTkFrame, CTkLabel, CTkImage, CTkFont + +def getImagePath(file_name): + # root\img\file_name + return os_path.join(os_path.dirname(os_path.dirname(os_path.dirname(__file__))), "img", file_name) + +def getImageFileFromUiUtils(file_name): + # root\img\file_name + img = Image_open(os_path.join(os_path.dirname(os_path.dirname(os_path.dirname(__file__))), "img", file_name)) + return img + +def openImageKeepAspectRatio(image_file, desired_width): + wpercent = (desired_width/float(image_file.size[0])) + hsize = int((float(image_file.size[1])*float(wpercent))) + img = image_file.resize((desired_width,hsize), LANCZOS) + return (img, desired_width, hsize) + +def retag(tag, *args): + for widget in args: + widget.bindtags((tag,) + widget.bindtags()) + + +def getLatestWidth(target_widget): + target_widget.update_idletasks() + return target_widget.winfo_width() + +def getLatestHeight(target_widget): + target_widget.update_idletasks() + return target_widget.winfo_height() + +def getLongestText(text_list:list): + max_length = 0 + longest_text = "" + + for text in text_list: + if len(text) > max_length: + max_length = len(text) + longest_text = text + return longest_text + +def bindEnterAndLeaveColor(target_widgets, enter_color, leave_color): + for target_widget in target_widgets: + target_widget.bind("", lambda e, widgets=target_widgets: [w.configure(fg_color=enter_color) for w in widgets], "+") + target_widget.bind("", lambda e, widgets=target_widgets: [w.configure(fg_color=leave_color) for w in widgets], "+") + + +def bindButtonPressColor(target_widgets, clicked_color, released_color): + for target_widget in target_widgets: + target_widget.bind("", lambda e, widgets=target_widgets: [w.configure(fg_color=clicked_color) for w in widgets], "+") + target_widget.bind("", lambda e, widgets=target_widgets: [w.configure(fg_color=released_color) for w in widgets], "+") + +def bindEnterAndLeaveFunction(target_widgets, enterFunction, leaveFunction): + for target_widget in target_widgets: + target_widget.bind("", enterFunction, "+") + target_widget.bind("", leaveFunction, "+") + +def bindButtonPressFunction(target_widgets, buttonPressedFunction): + for target_widget in target_widgets: + target_widget.bind("", buttonPressedFunction, "+") + +def bindButtonReleaseFunction(target_widgets, buttonReleasedFunction): + for target_widget in target_widgets: + target_widget.bind("", buttonReleasedFunction, "+") + +def bindButtonPressAndReleaseFunction(target_widgets, buttonPressedFunction, buttonReleasedFunction): + for target_widget in target_widgets: + target_widget.bind("", buttonPressedFunction, "+") + target_widget.bind("", buttonReleasedFunction, "+") + + +def bindButtonFunctionAndColor(target_widgets, enter_color, leave_color, clicked_color, buttonReleasedFunction): + bindEnterAndLeaveColor(target_widgets, enter_color, leave_color) + bindButtonPressColor(target_widgets, clicked_color, enter_color) + bindButtonReleaseFunction(target_widgets, buttonReleasedFunction) + +def unbindEnterLEaveButtonPressButtonReleaseFunction(target_widgets): + for target_widget in target_widgets: + for event_name in ["", "", "", ""]: + target_widget.unbind(event_name) + +def unbindEventFromActiveTabWidget(active_tab_widget): + for event_name in ["", "", "", ""]: + active_tab_widget.unbind(event_name) + active_tab_widget.children["!ctklabel"].unbind(event_name) + +def setDefaultActiveTab(active_tab_widget, active_bg_color, active_text_color): + active_tab_widget.configure(fg_color=active_bg_color, cursor="") + active_tab_widget.children["!ctklabel"].configure(fg_color=active_bg_color, text_color=active_text_color) + unbindEventFromActiveTabWidget(active_tab_widget) + + +def switchActiveTabAndPassiveTab(active_tab_widget, current_active_tab_widget, current_active_tab_passive_function, hovered_color, clicked_color, passive_color): + + + active_tab_widget.configure(cursor="") + unbindEventFromActiveTabWidget(active_tab_widget) + + + rebindFunctionToTab(current_active_tab_widget, current_active_tab_passive_function, hovered_color, clicked_color, passive_color) + +def rebindFunctionToTab(passive_tab_widget, passive_tab_function, hovered_color, clicked_color, passive_color): + + passive_tab_widget.configure(cursor="hand2") + bindEnterAndLeaveColor([passive_tab_widget, passive_tab_widget.children["!ctklabel"]], hovered_color, passive_color) + bindButtonPressColor([passive_tab_widget, passive_tab_widget.children["!ctklabel"]], clicked_color, passive_color) + + bindButtonReleaseFunction([passive_tab_widget, passive_tab_widget.children["!ctklabel"]], passive_tab_function) + +def switchTabsColor(target_widget, tab_buttons, active_bg_color, active_text_color, passive_bg_color, passive_text_color): + # Change all tabs' color to passive color at first + for tab_button in tab_buttons: + tab_button.configure(fg_color=passive_bg_color) + tab_button.children["!ctklabel"].configure(fg_color=passive_bg_color, text_color=passive_text_color) + + # Then, set active color to the active tab + target_widget.configure(fg_color=active_bg_color) + target_widget.children["!ctklabel"].configure(fg_color=active_bg_color, text_color=active_text_color) + + + + + + +def createButtonWithImage(parent_widget, button_image_size, button_ipadxy, button_fg_color, button_enter_color=None, button_clicked_color=None, button_image_file=None, button_command=None, corner_radius:int=0, no_bind:bool=False): + button_wrapper = CTkFrame(parent_widget, corner_radius=corner_radius, fg_color=button_fg_color, height=0, width=0) + + button_widget = CTkLabel( + button_wrapper, + text=None, + height=0, + image=CTkImage((button_image_file),size=(button_image_size,button_image_size)), + ) + button_widget.grid(row=0, column=0, padx=button_ipadxy, pady=button_ipadxy) + + if no_bind is False: + button_wrapper.configure(cursor="hand2") + bindButtonFunctionAndColor( + target_widgets=[button_wrapper, button_widget], + enter_color=button_enter_color, + leave_color=button_fg_color, + clicked_color=button_clicked_color, + buttonReleasedFunction=button_command, + ) + + return button_wrapper + + +def createOptionMenuBox(parent_widget, optionmenu_bg_color, optionmenu_hovered_bg_color, optionmenu_clicked_bg_color, optionmenu_ipadx, optionmenu_ipady, variable, font_family, font_size, text_color, image_file, image_size, optionmenu_clicked_command, optionmenu_position=None, optionmenu_padx_between_img=0, optionmenu_min_height=None, optionmenu_min_width=None, setattr_widget=None, image_widget_attr_name=None): + + option_menu_box = CTkFrame(parent_widget, corner_radius=6, fg_color=optionmenu_bg_color, cursor="hand2") + + option_menu_box.grid_rowconfigure(0, weight=1) + if optionmenu_min_height is not None: option_menu_box.grid_rowconfigure(0, minsize=optionmenu_min_height) + + option_menu_box.grid_columnconfigure(0, weight=1) + if optionmenu_min_width is not None: option_menu_box.grid_columnconfigure(0, minsize=optionmenu_min_width) + + optionmenu_label_wrapper = CTkFrame(option_menu_box, corner_radius=0, fg_color=optionmenu_bg_color) + optionmenu_label_wrapper.grid(row=0, column=0, padx=(optionmenu_ipadx[0],0), pady=optionmenu_ipady, sticky="ew") + + LABEL_COLUMN=0 + if optionmenu_position == "center": + optionmenu_label_wrapper.grid_columnconfigure((0,2), weight=1) + LABEL_COLUMN=1 + + optionmenu_label_widget = CTkLabel( + optionmenu_label_wrapper, + textvariable=variable, + height=0, + font=CTkFont(family=font_family, size=font_size, weight="normal"), + text_color=text_color + ) + optionmenu_label_widget.grid(row=0, column=LABEL_COLUMN, padx=(0, optionmenu_padx_between_img)) + + + optionmenu_img_widget = CTkLabel( + option_menu_box, + text=None, + corner_radius=0, + height=0, + image=CTkImage(image_file, size=image_size) + ) + + if image_widget_attr_name is not None: + setattr(setattr_widget, image_widget_attr_name, optionmenu_img_widget) + + optionmenu_img_widget.grid(row=0, column=1, padx=(0, optionmenu_ipadx[1]), pady=optionmenu_ipady) + + + bindEnterAndLeaveColor([optionmenu_label_wrapper, option_menu_box, optionmenu_label_widget, optionmenu_img_widget], optionmenu_hovered_bg_color, optionmenu_bg_color) + bindButtonPressColor([optionmenu_label_wrapper, option_menu_box, optionmenu_label_widget, optionmenu_img_widget], optionmenu_clicked_bg_color, optionmenu_hovered_bg_color) + + + + bindButtonReleaseFunction([optionmenu_label_wrapper, option_menu_box, optionmenu_label_widget, optionmenu_img_widget], optionmenu_clicked_command) + + def unbindEventFromWidgets(): + unbindEnterLEaveButtonPressButtonReleaseFunction([optionmenu_label_wrapper, option_menu_box, optionmenu_label_widget, optionmenu_img_widget]) + + option_menu_box.unbindFunction = unbindEventFromWidgets + + + return (option_menu_box, optionmenu_label_widget, optionmenu_img_widget) + + +def applyUiScalingAndFixTheBugScrollBar(scrollbar_widget, padx, width): + scrollbar_widget._scrollbar.grid_configure(padx=padx) + + # This is for CustomTkinter's spec change or bug fix. + scrollbar_widget._scrollbar.configure(height=0) + scrollbar_widget._scrollbar.configure(width=width) + + +def setGeometryToCenterOfScreen(root_widget): + root_widget.update() + sw=root_widget.winfo_screenwidth() + sh=root_widget.winfo_screenheight() + geometry_width = root_widget.winfo_width() + geometry_height = root_widget.winfo_height() + + root_widget.geometry(str(geometry_width)+"x"+str(geometry_height)+"+"+str((sw-geometry_width)//2)+"+"+str((sh-geometry_height)//2)) + + +def setGeometryToCenterOfTheWidget(attach_widget, target_widget): + attach_widget.update() + target_widget.update() + current_window_x = attach_widget.winfo_rootx() + current_window_y = attach_widget.winfo_rooty() + current_window_width = attach_widget.winfo_width() + current_window_height = attach_widget.winfo_height() + desired_window_width = target_widget.winfo_width() + desired_window_height = target_widget.winfo_height() + + desired_window_x = int((current_window_x + current_window_width / 2) - (desired_window_width / 2)) + desired_window_y = int((current_window_y + current_window_height / 2) - (desired_window_height / 2)) + + target_widget.geometry(str(desired_window_width) + "x" + str(desired_window_height) + "+" + str(desired_window_x) + "+" + str(desired_window_y)) + + +def fadeInAnimation(root_widget, steps:int=10, interval:float=0.1, max_alpha:float=1): + alpha_steps = 100 + alpha_steps*=max_alpha + step_size = alpha_steps/steps + root_widget.attributes("-alpha", 0) + num = 0 + while num < alpha_steps: + if not root_widget.winfo_exists(): + break + root_widget.attributes("-alpha", num / 100) + root_widget.update() + sleep(interval) + num += step_size + root_widget.attributes("-alpha", max_alpha) \ No newline at end of file diff --git a/vrct_gui/vrct_gui.py b/vrct_gui/vrct_gui.py new file mode 100644 index 00000000..73e3d805 --- /dev/null +++ b/vrct_gui/vrct_gui.py @@ -0,0 +1,291 @@ +from customtkinter import CTk, CTkImage + +from ._CreateSelectableLanguagesWindow import _CreateSelectableLanguagesWindow + +from ._CreateWindowCover import _CreateWindowCover +from ._CreateErrorWindow import _CreateErrorWindow +from ._CreateDropdownMenuWindow import _CreateDropdownMenuWindow +from ._changeMainWindowWidgetsStatus import _changeMainWindowWidgetsStatus +from ._changeConfigWindowWidgetsStatus import _changeConfigWindowWidgetsStatus +from ._CreateConfirmationModal import _CreateConfirmationModal +from ._printToTextbox import _printToTextbox + +from .main_window import createMainWindowWidgets +from .config_window import ConfigWindow +from .ui_utils import setDefaultActiveTab, setGeometryToCenterOfScreen, fadeInAnimation + +from utils import callFunctionIfCallable + +class VRCT_GUI(CTk): + def __init__(self): + super().__init__() + self.withdraw() + self.is_config_window_already_opened_once=False + self.BIND_UNMAP_DETECT_MAIN_WINDOW_STATE_FUNC_ID = None + self.BIND_MAP_DETECT_MAIN_WINDOW_STATE_FUNC_ID = None + + self.window_state = None + + def detectMainWindowState(self, _e=None): + self.new_window_state = self.wm_state() + if self.window_state == self.new_window_state: + return + else: + self.window_state = self.new_window_state + + if self.window_state == "iconic": + self.main_window_cover.withdraw() + elif self.window_state == "normal": + self.main_window_cover.show() + + + + def _showGUI(self): + self.attributes("-alpha", 0) + self.deiconify() + self.geometry("{}x{}".format( + self.settings.main.uism.MAIN_AREA_MIN_WIDTH + self.settings.main.uism.SIDEBAR_MIN_WIDTH, + self.winfo_height() + )) + setGeometryToCenterOfScreen(root_widget=self) + if self._view_variable.IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE is True: + self._enableMainWindowSidebarCompactMode() + fadeInAnimation(self, steps=5, interval=0.008) + + + if self._isOverWindowSizeCheck() is True: + callFunctionIfCallable(self._view_variable.CALLBACK_WHEN_DETECT_WINDOW_OVERED_SIZE) + + + def _createGUI(self, settings, view_variable): + self.settings = settings + self._view_variable = view_variable + + createMainWindowWidgets( + vrct_gui=self, + settings=self.settings.main, + view_variable=self._view_variable + ) + + self.dropdown_menu_window = _CreateDropdownMenuWindow( + settings=self.settings.config_window, + view_variable=self._view_variable, + + window_additional_y_pos=self.settings.config_window.uism.SB__DROPDOWN_MENU_WINDOW_ADDITIONAL_Y_POS, + window_border_width=self.settings.config_window.uism.SB__DROPDOWN_MENU_WINDOW_BORDER_WIDTH, + scrollbar_ipadx=self.settings.config_window.uism.SB__DROPDOWN_MENU_SCROLLBAR_IPADX, + scrollbar_width=self.settings.config_window.uism.SB__DROPDOWN_MENU_SCROLLBAR_WIDTH, + value_ipadx=self.settings.config_window.uism.SB__DROPDOWN_MENU_VALUE_IPADX, + value_ipady=self.settings.config_window.uism.SB__DROPDOWN_MENU_VALUE_IPADY, + value_pady=self.settings.config_window.uism.SB__DROPDOWN_MENU_VALUE_PADY, + value_font_size=self.settings.config_window.uism.SB__DROPDOWN_MENU_VALUE_FONT_SIZE, + dropdown_menu_default_min_width=self.settings.config_window.uism.SB__DROPDOWN_MENU_VALUE_DEFAULT_MIN_WIDTH, + + window_bg_color=self.settings.config_window.ctm.SB__DROPDOWN_MENU_WINDOW_BG_COLOR, + window_border_color=self.settings.config_window.ctm.SB__DROPDOWN_MENU_WINDOW_BORDER_COLOR, + values_bg_color=self.settings.config_window.ctm.SB__DROPDOWN_MENU_BG_COLOR, + values_hovered_bg_color=self.settings.config_window.ctm.SB__DROPDOWN_MENU_HOVERED_BG_COLOR, + values_clicked_bg_color=self.settings.config_window.ctm.SB__DROPDOWN_MENU_CLICKED_BG_COLOR, + values_text_color=self.settings.config_window.ctm.BASIC_TEXT_COLOR, + ) + + self.config_window = ConfigWindow( + vrct_gui=self, + settings=self.settings.config_window, + view_variable=self._view_variable + ) + + self.selectable_languages_window = _CreateSelectableLanguagesWindow( + vrct_gui=self, + settings=self.settings.selectable_language_window, + view_variable=self._view_variable + ) + + self.main_window_cover = _CreateWindowCover( + attach_window=self, + settings=self.settings.main_window_cover, + view_variable=self._view_variable + ) + + self.error_message_window = _CreateErrorWindow( + settings=self.settings.error_message_window, + view_variable=self._view_variable, + wrapper_widget=self.config_window.main_bg_container, + + message_ipadx=self.settings.config_window.uism.SB__ERROR_MESSAGE_IPADX, + message_ipady=self.settings.config_window.uism.SB__ERROR_MESSAGE_IPADY, + message_font_size=self.settings.config_window.uism.SB__ERROR_MESSAGE_FONT_SIZE, + + message_bg_color=self.settings.config_window.ctm.SB__ERROR_MESSAGE_BG_COLOR, + message_text_color=self.settings.config_window.ctm.SB__ERROR_MESSAGE_TEXT_COLOR, + ) + + self.confirmation_modal = _CreateConfirmationModal( + attach_window=self.toplevel_wrapper, + settings=self.settings.confirmation_modal, + view_variable=self._view_variable + ) + + self.information_modal = _CreateConfirmationModal( + attach_window=self.toplevel_wrapper, + settings=self.settings.confirmation_modal, + view_variable=self._view_variable, + modal_type="information" + ) + + + # self.update() + # self.geometry("{}x{}".format(self.winfo_width(), self.winfo_height())) + + + def _startMainLoop(self): + self.mainloop() + + + def _quitVRCT(self): + self.quit() + self.destroy() + + + def _openConfigWindow(self): + self.main_window_cover.show(bind_focusin=self.config_window.lift) + + self.BIND_UNMAP_DETECT_MAIN_WINDOW_STATE_FUNC_ID = self.bind("", self.detectMainWindowState, "+") + self.BIND_MAP_DETECT_MAIN_WINDOW_STATE_FUNC_ID = self.bind("", self.detectMainWindowState, "+") + + + self.config_window.attributes("-alpha", 0) + self.config_window.deiconify() + if self.is_config_window_already_opened_once is False: + setGeometryToCenterOfScreen(self.config_window) + self.is_config_window_already_opened_once = True + fadeInAnimation(self.config_window, steps=5, interval=0.005) + self.config_window.attributes("-alpha", 1) + self.config_window.focus_set() + + def _closeConfigWindow(self): + self.config_window.withdraw() + + self.main_window_cover.hide() + self.unbind("", self.BIND_UNMAP_DETECT_MAIN_WINDOW_STATE_FUNC_ID) + self.unbind("", self.BIND_MAP_DETECT_MAIN_WINDOW_STATE_FUNC_ID) + self.adjusted_event=None + + + + def _openSelectableLanguagesWindow(self, selectable_language_window_type): + # print("___________________________________open____________________________________________________") + # print("your", self._view_variable.IS_OPENED_SELECTABLE_YOUR_LANGUAGE_WINDOW) + # print("target", self._view_variable.IS_OPENED_SELECTABLE_TARGET_LANGUAGE_WINDOW) + if selectable_language_window_type == "your_language": + if self._view_variable.IS_OPENED_SELECTABLE_YOUR_LANGUAGE_WINDOW is False: + self.sls__arrow_img_your_language.configure(image=CTkImage(self.settings.main.image_file.ARROW_LEFT, size=self.settings.main.uism.SLS__BOX_OPTION_MENU_ARROW_IMAGE_SIZE)) + self._view_variable.IS_OPENED_SELECTABLE_YOUR_LANGUAGE_WINDOW = True + self._view_variable.IS_OPENED_SELECTABLE_TARGET_LANGUAGE_WINDOW = False + else: + self._view_variable.IS_OPENED_SELECTABLE_YOUR_LANGUAGE_WINDOW = False + return + + elif selectable_language_window_type == "target_language": + if self._view_variable.IS_OPENED_SELECTABLE_TARGET_LANGUAGE_WINDOW is False: + self.sls__arrow_img_target_language.configure(image=CTkImage(self.settings.main.image_file.ARROW_LEFT, size=self.settings.main.uism.SLS__BOX_OPTION_MENU_ARROW_IMAGE_SIZE)) + self._view_variable.IS_OPENED_SELECTABLE_TARGET_LANGUAGE_WINDOW = True + self._view_variable.IS_OPENED_SELECTABLE_YOUR_LANGUAGE_WINDOW = False + else: + self._view_variable.IS_OPENED_SELECTABLE_TARGET_LANGUAGE_WINDOW = False + return + + + self.selectable_languages_window.createContainer(selectable_language_window_type) + self.selectable_languages_window.deiconify() + self.selectable_languages_window.focus_set() + self.selectable_languages_window.attributes("-topmost", True) + + + def _closeSelectableLanguagesWindow(self): + self.sls__arrow_img_your_language.configure(image=CTkImage(self.settings.main.image_file.ARROW_LEFT.rotate(180), size=self.settings.main.uism.SLS__BOX_OPTION_MENU_ARROW_IMAGE_SIZE)) + self.sls__arrow_img_target_language.configure(image=CTkImage(self.settings.main.image_file.ARROW_LEFT.rotate(180), size=self.settings.main.uism.SLS__BOX_OPTION_MENU_ARROW_IMAGE_SIZE)) + self.selectable_languages_window.withdraw() + + + # print("______________________________________close_________________________________________________") + # print("your", self._view_variable.IS_OPENED_SELECTABLE_YOUR_LANGUAGE_WINDOW) + # print("target", self._view_variable.IS_OPENED_SELECTABLE_TARGET_LANGUAGE_WINDOW) + if self._view_variable.IS_OPENED_SELECTABLE_TARGET_LANGUAGE_WINDOW is not False or self._view_variable.IS_OPENED_SELECTABLE_YOUR_LANGUAGE_WINDOW is not False: + def callback(): + self._view_variable.IS_OPENED_SELECTABLE_TARGET_LANGUAGE_WINDOW = False + self._view_variable.IS_OPENED_SELECTABLE_YOUR_LANGUAGE_WINDOW = False + self.after(500,callback) + + + + def _changeMainWindowWidgetsStatus(self, status, target_names, to_hold_state:bool=False): + _changeMainWindowWidgetsStatus( + vrct_gui=self, + settings=self.settings.main, + view_variable=self._view_variable, + status=status, + target_names=target_names, + to_hold_state=to_hold_state, + ) + + def _changeConfigWindowWidgetsStatus(self, status, target_names): + _changeConfigWindowWidgetsStatus( + config_window=self.config_window, + settings=self.settings.config_window, + view_variable=self._view_variable, + status=status, + target_names=target_names, + ) + + def _printToTextbox(self, target_type, original_message=None, translated_message=None): + _printToTextbox( + vrct_gui=self, + settings=self.settings.main, + target_type=target_type, + original_message=original_message, + translated_message=translated_message, + tags=target_type, + ) + + def _setDefaultActiveLanguagePresetTab(self, tab_no:str): + self.current_active_preset_tab = getattr(self, f"sls__presets_button_{tab_no}") + setDefaultActiveTab( + active_tab_widget=self.current_active_preset_tab, + active_bg_color=self.settings.main.ctm.SLS__PRESETS_TAB_BG_ACTIVE_COLOR, + active_text_color=self.settings.main.ctm.SLS__PRESETS_TAB_ACTIVE_TEXT_COLOR + ) + + def _enableMainWindowSidebarCompactMode(self): + self.sidebar_bg_container.grid_remove() + self.sidebar_compact_mode_bg_container.grid() + self.minimize_sidebar_button_container__for_closing.grid_remove() + self.minimize_sidebar_button_container__for_opening.grid() + + def _disableMainWindowSidebarCompactMode(self): + self.sidebar_compact_mode_bg_container.grid_remove() + self.sidebar_bg_container.grid() + self.minimize_sidebar_button_container__for_opening.grid_remove() + self.minimize_sidebar_button_container__for_closing.grid() + + + def _showErrorMessage(self, target_widget): + self.error_message_window.show(target_widget=target_widget) + + def _clearErrorMessage(self): + try: + self.error_message_window._withdraw() + except: + pass + + + def _isOverWindowSizeCheck(self): + self.update() + screen_height = self.winfo_screenheight() + window_height = self.winfo_height() + if screen_height < window_height: + return True + else: + return False + +vrct_gui = VRCT_GUI() \ No newline at end of file diff --git a/window_config.py b/window_config.py deleted file mode 100644 index 346c4a03..00000000 --- a/window_config.py +++ /dev/null @@ -1,1443 +0,0 @@ -from time import sleep -from queue import Queue -from os import path as os_path -from tkinter import DoubleVar, IntVar -from tkinter import font as tk_font -import customtkinter -from customtkinter import CTkToplevel, CTkTabview, CTkFont, CTkLabel, CTkSlider, CTkOptionMenu, StringVar, CTkEntry, CTkCheckBox, CTkProgressBar -from flashtext import KeywordProcessor - -from threading import Thread -from utils import save_json, print_textbox, thread_fnc, get_localized_text, get_key_by_value, widget_config_window_label_setter -from audio_utils import get_input_device_list, get_output_device_list, get_default_output_device -from audio_recorder import SelectedMicEnergyRecorder, SelectedSpeakeEnergyRecorder -from languages import translation_lang, transcription_lang, selectable_languages - -from ctk_scrollable_dropdown import CTkScrollableDropdown - -SCROLLABLE_DROPDOWN = False - -class ToplevelWindowConfig(CTkToplevel): - - def __init__(self, parent, *args, **kwargs): - super().__init__(parent, *args, **kwargs) - - self.withdraw() - self.parent = parent - # self.geometry(f"{350}x{270}") - # self.resizable(False, False) - self.grid_columnconfigure(0, weight=1) - self.grid_rowconfigure(0, weight=1) - - self.after(200, lambda: self.iconbitmap(os_path.join(os_path.dirname(__file__), "img", "app.ico"))) - self.title("Config") - - # init parameter - self.MAX_MIC_ENERGY_THRESHOLD = 2000 - self.MAX_SPEAKER_ENERGY_THRESHOLD = 4000 - self.mic_energy_recorder = None - self.mic_energy_plot_progressbar = None - self.speaker_energy_recorder = None - self.speaker_energy_get_progressbar = None - self.speaker_energy_plot_progressbar = None - - # load ui language data - language_yaml_data = get_localized_text(f"{self.parent.UI_LANGUAGE}") - # add tabview config - self.add_tabview_config(language_yaml_data, selectable_languages) - # set all config window labels - widget_config_window_label_setter(self, language_yaml_data) - - self.protocol("WM_DELETE_WINDOW", self.delete_window) - - def slider_transparency_callback(self, value): - self.parent.wm_attributes("-alpha", value/100) - self.parent.TRANSPARENCY = value - save_json(self.parent.PATH_CONFIG, "TRANSPARENCY", self.parent.TRANSPARENCY) - - def optionmenu_appearance_theme_callback(self, choice): - self.optionmenu_appearance_theme.set(choice) - - customtkinter.set_appearance_mode(choice) - self.parent.APPEARANCE_THEME = choice - save_json(self.parent.PATH_CONFIG, "APPEARANCE_THEME", self.parent.APPEARANCE_THEME) - - def optionmenu_ui_scaling_callback(self, choice): - self.optionmenu_ui_scaling.set(choice) - - new_scaling_float = int(choice.replace("%", "")) / 100 - customtkinter.set_widget_scaling(new_scaling_float) - self.parent.UI_SCALING = choice - save_json(self.parent.PATH_CONFIG, "UI_SCALING", self.parent.UI_SCALING) - - def optionmenu_font_family_callback(self, choice): - self.optionmenu_font_family.set(choice) - - # tab menu - self.tabview_config._segmented_button.configure(font=CTkFont(family=choice)) - - # tab UI - self.label_transparency.configure(font=CTkFont(family=choice)) - self.label_appearance_theme.configure(font=CTkFont(family=choice)) - self.optionmenu_appearance_theme.configure(font=CTkFont(family=choice)) - self.optionmenu_appearance_theme._dropdown_menu.configure(font=CTkFont(family=choice)) - self.label_ui_scaling.configure(font=CTkFont(family=choice)) - self.optionmenu_ui_scaling.configure(font=CTkFont(family=choice)) - self.optionmenu_ui_scaling._dropdown_menu.configure(font=CTkFont(family=choice)) - self.label_font_family.configure(font=CTkFont(family=choice)) - self.optionmenu_font_family.configure(font=CTkFont(family=choice)) - self.optionmenu_font_family._dropdown_menu.configure(font=CTkFont(family=choice)) - self.label_ui_language.configure(font=CTkFont(family=choice)) - self.optionmenu_ui_language.configure(font=CTkFont(family=choice)) - self.optionmenu_ui_language._dropdown_menu.configure(font=CTkFont(family=choice)) - - # tab Translation - self.label_translation_translator.configure(font=CTkFont(family=choice)) - self.optionmenu_translation_translator.configure(font=CTkFont(family=choice)) - self.optionmenu_translation_translator._dropdown_menu.configure(font=CTkFont(family=choice)) - self.label_translation_input_language.configure(font=CTkFont(family=choice)) - self.optionmenu_translation_input_source_language.configure(font=CTkFont(family=choice)) - self.optionmenu_translation_input_source_language._dropdown_menu.configure(font=CTkFont(family=choice)) - self.label_translation_input_arrow.configure(font=CTkFont(family=choice)) - self.optionmenu_translation_input_target_language.configure(font=CTkFont(family=choice)) - self.optionmenu_translation_input_target_language._dropdown_menu.configure(font=CTkFont(family=choice)) - self.label_translation_output_language.configure(font=CTkFont(family=choice)) - self.optionmenu_translation_output_source_language.configure(font=CTkFont(family=choice)) - self.optionmenu_translation_output_source_language._dropdown_menu.configure(font=CTkFont(family=choice)) - self.label_translation_output_arrow.configure(font=CTkFont(family=choice)) - self.optionmenu_translation_output_target_language.configure(font=CTkFont(family=choice)) - self.optionmenu_translation_output_target_language._dropdown_menu.configure(font=CTkFont(family=choice)) - - # tab Transcription - self.label_input_mic_host.configure(font=CTkFont(family=choice)) - self.optionmenu_input_mic_host.configure(font=CTkFont(family=choice)) - self.optionmenu_input_mic_host._dropdown_menu.configure(font=CTkFont(family=choice)) - self.label_input_mic_device.configure(font=CTkFont(family=choice)) - self.optionmenu_input_mic_device.configure(font=CTkFont(family=choice)) - self.optionmenu_input_mic_device._dropdown_menu.configure(font=CTkFont(family=choice)) - self.label_input_mic_voice_language.configure(font=CTkFont(family=choice)) - self.optionmenu_input_mic_voice_language.configure(font=CTkFont(family=choice)) - self.optionmenu_input_mic_voice_language._dropdown_menu.configure(font=CTkFont(family=choice)) - self.checkbox_input_mic_threshold_check.configure(font=CTkFont(family=choice)) - self.label_input_mic_energy_threshold.configure(font=CTkFont(family=choice)) - self.label_input_mic_dynamic_energy_threshold.configure(font=CTkFont(family=choice)) - self.label_input_mic_record_timeout.configure(font=CTkFont(family=choice)) - self.entry_input_mic_record_timeout.configure(font=CTkFont(family=choice)) - self.label_input_mic_phrase_timeout.configure(font=CTkFont(family=choice)) - self.entry_input_mic_phrase_timeout.configure(font=CTkFont(family=choice)) - self.label_input_mic_max_phrases.configure(font=CTkFont(family=choice)) - self.entry_input_mic_max_phrases.configure(font=CTkFont(family=choice)) - self.label_input_mic_word_filter.configure(font=CTkFont(family=choice)) - self.entry_input_mic_word_filter.configure(font=CTkFont(family=choice)) - self.label_input_speaker_device.configure(font=CTkFont(family=choice)) - self.optionmenu_input_speaker_device.configure(font=CTkFont(family=choice)) - self.optionmenu_input_speaker_device._dropdown_menu.configure(font=CTkFont(family=choice)) - self.label_input_speaker_voice_language.configure(font=CTkFont(family=choice)) - self.optionmenu_input_speaker_voice_language.configure(font=CTkFont(family=choice)) - self.optionmenu_input_speaker_voice_language._dropdown_menu.configure(font=CTkFont(family=choice)) - self.checkbox_input_speaker_threshold_check.configure(font=CTkFont(family=choice)) - self.label_input_speaker_energy_threshold.configure(font=CTkFont(family=choice)) - self.label_input_speaker_dynamic_energy_threshold.configure(font=CTkFont(family=choice)) - self.label_input_speaker_record_timeout.configure(font=CTkFont(family=choice)) - self.entry_input_speaker_record_timeout.configure(font=CTkFont(family=choice)) - self.label_input_speaker_phrase_timeout.configure(font=CTkFont(family=choice)) - self.entry_input_speaker_phrase_timeout.configure(font=CTkFont(family=choice)) - self.label_input_speaker_max_phrases.configure(font=CTkFont(family=choice)) - self.entry_input_speaker_max_phrases.configure(font=CTkFont(family=choice)) - - # tab Parameter - self.label_ip_address.configure(font=CTkFont(family=choice)) - self.entry_ip_address.configure(font=CTkFont(family=choice)) - self.label_port.configure(font=CTkFont(family=choice)) - self.entry_port.configure(font=CTkFont(family=choice)) - self.label_authkey.configure(font=CTkFont(family=choice)) - self.entry_authkey.configure(font=CTkFont(family=choice)) - self.label_message_format.configure(font=CTkFont(family=choice)) - self.entry_message_format.configure(font=CTkFont(family=choice)) - - # tab Others - self.label_checkbox_auto_clear_chatbox.configure(font=CTkFont(family=choice)) - - # main window - self.parent.checkbox_translation.configure(font=CTkFont(family=choice)) - self.parent.checkbox_transcription_send.configure(font=CTkFont(family=choice)) - self.parent.checkbox_transcription_receive.configure(font=CTkFont(family=choice)) - self.parent.checkbox_foreground.configure(font=CTkFont(family=choice)) - self.parent.textbox_message_log.configure(font=CTkFont(family=choice)) - self.parent.textbox_message_send_log.configure(font=CTkFont(family=choice)) - self.parent.textbox_message_receive_log.configure(font=CTkFont(family=choice)) - self.parent.textbox_message_system_log.configure(font=CTkFont(family=choice)) - self.parent.entry_message_box.configure(font=CTkFont(family=choice)) - self.parent.tabview_logs._segmented_button.configure(font=CTkFont(family=choice)) - - # window information - try: - self.parent.information_window.textbox_information.configure(font=CTkFont(family=choice)) - except: - pass - - self.parent.FONT_FAMILY = choice - save_json(self.parent.PATH_CONFIG, "FONT_FAMILY", self.parent.FONT_FAMILY) - - def optionmenu_ui_language_callback(self, choice): - self.optionmenu_ui_language.set(choice) - - self.withdraw() - pre_language_yaml_data = get_localized_text(f"{self.parent.UI_LANGUAGE}") - self.parent.UI_LANGUAGE = get_key_by_value(selectable_languages, choice) - language_yaml_data = get_localized_text(f"{self.parent.UI_LANGUAGE}") - save_json(self.parent.PATH_CONFIG, "UI_LANGUAGE", self.parent.UI_LANGUAGE) - - - # delete - self.parent.delete_tabview_logs(pre_language_yaml_data) - self.delete_tabview_config(pre_language_yaml_data) - # add tabview textbox - self.parent.add_tabview_logs(language_yaml_data) - self.add_tabview_config(language_yaml_data, selectable_languages) - - # 翻訳予定 - # window information - # try: - # self.parent.information_window.textbox_information.configure(font=customtkinter.CTkFont(family=choice)) - # except: - # pass - self.deiconify() - - def optionmenu_translation_translator_callback(self, choice): - self.optionmenu_translation_translator.set(choice) - - if self.parent.translator.authentication(choice, self.parent.AUTH_KEYS[choice]) is False: - print_textbox(self.parent.textbox_message_log, "Auth Key or language setting is incorrect", "ERROR") - print_textbox(self.parent.textbox_message_system_log, "Auth Key or language setting is incorrect", "ERROR") - else: - self.optionmenu_translation_input_source_language.configure( - values=list(translation_lang[choice]["source"].keys()), - variable=StringVar(value=list(translation_lang[choice]["source"].keys())[0])) - self.optionmenu_translation_input_target_language.configure( - values=list(translation_lang[choice]["target"].keys()), - variable=StringVar(value=list(translation_lang[choice]["target"].keys())[1])) - self.optionmenu_translation_output_source_language.configure( - values=list(translation_lang[choice]["source"].keys()), - variable=StringVar(value=list(translation_lang[choice]["source"].keys())[1])) - self.optionmenu_translation_output_target_language.configure( - values=list(translation_lang[choice]["target"].keys()), - variable=StringVar(value=list(translation_lang[choice]["target"].keys())[0])) - - if SCROLLABLE_DROPDOWN: - self.scrollableDropdown_translation_input_source_language.configure( - values=list(translation_lang[choice]["source"].keys())) - self.scrollableDropdown_translation_input_target_language.configure( - values=list(translation_lang[choice]["target"].keys())) - self.scrollableDropdown_translation_output_source_language.configure( - values=list(translation_lang[choice]["source"].keys())) - self.scrollableDropdown_translation_output_target_language.configure( - values=list(translation_lang[choice]["target"].keys())) - - self.parent.CHOICE_TRANSLATOR = choice - self.parent.INPUT_SOURCE_LANG = list(translation_lang[choice]["source"].keys())[0] - self.parent.INPUT_TARGET_LANG = list(translation_lang[choice]["target"].keys())[1] - self.parent.OUTPUT_SOURCE_LANG = list(translation_lang[choice]["source"].keys())[1] - self.parent.OUTPUT_TARGET_LANG = list(translation_lang[choice]["target"].keys())[0] - save_json(self.parent.PATH_CONFIG, "CHOICE_TRANSLATOR", self.parent.CHOICE_TRANSLATOR) - save_json(self.parent.PATH_CONFIG, "INPUT_SOURCE_LANG", self.parent.INPUT_SOURCE_LANG) - save_json(self.parent.PATH_CONFIG, "INPUT_TARGET_LANG", self.parent.INPUT_TARGET_LANG) - save_json(self.parent.PATH_CONFIG, "OUTPUT_SOURCE_LANG", self.parent.OUTPUT_SOURCE_LANG) - save_json(self.parent.PATH_CONFIG, "OUTPUT_TARGET_LANG", self.parent.OUTPUT_TARGET_LANG) - - def optionmenu_translation_input_source_language_callback(self, choice): - self.optionmenu_translation_input_source_language.set(choice) - - self.parent.INPUT_SOURCE_LANG = choice - save_json(self.parent.PATH_CONFIG, "INPUT_SOURCE_LANG", self.parent.INPUT_SOURCE_LANG) - - def optionmenu_translation_input_target_language_callback(self, choice): - self.optionmenu_translation_input_target_language.set(choice) - - self.parent.INPUT_TARGET_LANG = choice - save_json(self.parent.PATH_CONFIG, "INPUT_TARGET_LANG", self.parent.INPUT_TARGET_LANG) - - def optionmenu_translation_output_source_language_callback(self, choice): - self.optionmenu_translation_output_source_language.set(choice) - - self.parent.OUTPUT_SOURCE_LANG = choice - save_json(self.parent.PATH_CONFIG, "OUTPUT_SOURCE_LANG", self.parent.OUTPUT_SOURCE_LANG) - - def optionmenu_translation_output_target_language_callback(self, choice): - self.optionmenu_translation_output_target_language.set(choice) - - self.parent.OUTPUT_TARGET_LANG = choice - save_json(self.parent.PATH_CONFIG, "OUTPUT_TARGET_LANG", self.parent.OUTPUT_TARGET_LANG) - - def optionmenu_input_mic_host_callback(self, choice): - self.optionmenu_input_mic_host.set(choice) - - self.optionmenu_input_mic_device.configure( - values=[device["name"] for device in get_input_device_list()[choice]], - variable=StringVar(value=[device["name"] for device in get_input_device_list()[choice]][0])) - - if SCROLLABLE_DROPDOWN: - self.scrollableDropdown_input_mic_device.configure(values=[device["name"] for device in get_input_device_list()[choice]]) - - self.parent.CHOICE_MIC_HOST = choice - self.parent.CHOICE_MIC_DEVICE = [device["name"] for device in get_input_device_list()[choice]][0] - save_json(self.parent.PATH_CONFIG, "CHOICE_MIC_HOST", self.parent.CHOICE_MIC_HOST) - save_json(self.parent.PATH_CONFIG, "CHOICE_MIC_DEVICE", self.parent.CHOICE_MIC_DEVICE) - - def optionmenu_input_mic_device_callback(self, choice): - self.optionmenu_input_mic_device.set(choice) - - self.parent.CHOICE_MIC_DEVICE = choice - save_json(self.parent.PATH_CONFIG, "CHOICE_MIC_DEVICE", self.parent.CHOICE_MIC_DEVICE) - self.checkbox_input_mic_threshold_check.deselect() - self.checkbox_input_mic_threshold_check_callback() - - def optionmenu_input_mic_voice_language_callback(self, choice): - self.optionmenu_input_mic_voice_language.set(choice) - - self.parent.INPUT_MIC_VOICE_LANGUAGE = choice - save_json(self.parent.PATH_CONFIG, "INPUT_MIC_VOICE_LANGUAGE", self.parent.INPUT_MIC_VOICE_LANGUAGE) - - def progressBar_input_mic_energy_plot(self): - if self.mic_energy_queue.empty() is False: - energy = self.mic_energy_queue.get() - try: - self.progressBar_input_mic_energy_threshold.set(energy/self.MAX_MIC_ENERGY_THRESHOLD) - except: - pass - sleep(0.01) - - def mic_threshold_check_start(self): - self.mic_energy_queue = Queue() - mic_device = [device for device in get_input_device_list()[self.parent.CHOICE_MIC_HOST] if device["name"] == self.parent.CHOICE_MIC_DEVICE][0] - self.mic_energy_recorder = SelectedMicEnergyRecorder(mic_device) - self.mic_energy_recorder.record_into_queue(self.mic_energy_queue) - self.mic_energy_plot_progressbar = thread_fnc(self.progressBar_input_mic_energy_plot) - self.mic_energy_plot_progressbar.daemon = True - self.mic_energy_plot_progressbar.start() - self.checkbox_input_mic_threshold_check.configure(state="normal") - self.checkbox_input_speaker_threshold_check.configure(state="normal") - - def mic_threshold_check_stop(self): - if self.mic_energy_recorder != None: - self.mic_energy_recorder.stop() - if self.mic_energy_plot_progressbar != None: - self.mic_energy_plot_progressbar.stop() - self.progressBar_input_mic_energy_threshold.set(0) - self.checkbox_input_mic_threshold_check.configure(state="normal") - self.checkbox_input_speaker_threshold_check.configure(state="normal") - - def checkbox_input_mic_threshold_check_callback(self): - self.checkbox_input_mic_threshold_check.configure(state="disabled") - self.checkbox_input_speaker_threshold_check.configure(state="disabled") - self.update() - if self.checkbox_input_mic_threshold_check.get(): - th_mic_threshold_check_start = Thread(target=self.mic_threshold_check_start) - th_mic_threshold_check_start.daemon = True - th_mic_threshold_check_start.start() - else: - th_mic_threshold_check_stop = Thread(target=self.mic_threshold_check_stop) - th_mic_threshold_check_stop.daemon = True - th_mic_threshold_check_stop.start() - - def slider_input_mic_energy_threshold_callback(self, value): - self.parent.INPUT_MIC_ENERGY_THRESHOLD = int(value) - save_json(self.parent.PATH_CONFIG, "INPUT_MIC_ENERGY_THRESHOLD", self.parent.INPUT_MIC_ENERGY_THRESHOLD) - - def checkbox_input_mic_dynamic_energy_threshold_callback(self): - value = self.checkbox_input_mic_dynamic_energy_threshold.get() - self.parent.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD = value - save_json(self.parent.PATH_CONFIG, "INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD", self.parent.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD) - - def entry_input_mic_record_timeout_callback(self, event): - self.parent.INPUT_MIC_RECORD_TIMEOUT = int(self.entry_input_mic_record_timeout.get()) - save_json(self.parent.PATH_CONFIG, "INPUT_MIC_RECORD_TIMEOUT", self.parent.INPUT_MIC_RECORD_TIMEOUT) - - def entry_input_mic_phrase_timeout_callback(self, event): - self.parent.INPUT_MIC_PHRASE_TIMEOUT = int(self.entry_input_mic_phrase_timeout.get()) - save_json(self.parent.PATH_CONFIG, "INPUT_MIC_PHRASE_TIMEOUT", self.parent.INPUT_MIC_PHRASE_TIMEOUT) - - def entry_input_mic_max_phrases_callback(self, event): - self.parent.INPUT_MIC_MAX_PHRASES = int(self.entry_input_mic_max_phrases.get()) - save_json(self.parent.PATH_CONFIG, "INPUT_MIC_MAX_PHRASES", self.parent.INPUT_MIC_MAX_PHRASES) - - def entry_input_mic_word_filters_callback(self, event): - word_filter = self.entry_input_mic_word_filter.get() - word_filter = [w.strip() for w in word_filter.split(",") if len(w.strip()) > 0] - word_filter = ",".join(word_filter) - if len(word_filter) > 0: - self.parent.INPUT_MIC_WORD_FILTER = word_filter.split(",") - else: - self.parent.INPUT_MIC_WORD_FILTER = [] - self.parent.keyword_processor = KeywordProcessor() - for f in self.parent.INPUT_MIC_WORD_FILTER: - self.parent.keyword_processor.add_keyword(f) - save_json(self.parent.PATH_CONFIG, "INPUT_MIC_WORD_FILTER", self.parent.INPUT_MIC_WORD_FILTER) - - def optionmenu_input_speaker_device_callback(self, choice): - speaker_device = [device for device in get_output_device_list() if device["name"] == choice][0] - if get_default_output_device()["index"] == speaker_device["index"]: - self.optionmenu_input_speaker_device.set(choice) - self.parent.CHOICE_SPEAKER_DEVICE = choice - save_json(self.parent.PATH_CONFIG, "CHOICE_SPEAKER_DEVICE", self.parent.CHOICE_SPEAKER_DEVICE) - else: - print_textbox(self.parent.textbox_message_log, "Windows playback device and selected device do not match. Change the Windows playback device.", "ERROR") - print_textbox(self.parent.textbox_message_system_log, "Windows playback device and selected device do not match. Change the Windows playback device.", "ERROR") - self.optionmenu_input_speaker_device.configure(variable=StringVar(value=self.parent.CHOICE_SPEAKER_DEVICE)) - - def optionmenu_input_speaker_voice_language_callback(self, choice): - self.optionmenu_input_speaker_voice_language.set(choice) - - self.parent.INPUT_SPEAKER_VOICE_LANGUAGE = choice - save_json(self.parent.PATH_CONFIG, "INPUT_SPEAKER_VOICE_LANGUAGE", self.parent.INPUT_SPEAKER_VOICE_LANGUAGE) - - def progressBar_input_speaker_energy_plot(self): - if self.speaker_energy_queue.empty() is False: - energy = self.speaker_energy_queue.get() - try: - self.progressBar_input_speaker_energy_threshold.set(energy/self.MAX_SPEAKER_ENERGY_THRESHOLD) - except: - pass - sleep(0.01) - - def progressBar_input_speaker_energy_get(self): - with self.speaker_energy_recorder.source as source: - energy = self.speaker_energy_recorder.recorder.listen_energy(source) - self.speaker_energy_queue.put(energy) - - def speaker_threshold_check_start(self): - speaker_device = [device for device in get_output_device_list() if device["name"] == self.parent.CHOICE_SPEAKER_DEVICE][0] - - if get_default_output_device()["index"] == speaker_device["index"]: - self.speaker_energy_queue = Queue() - self.speaker_energy_recorder = SelectedSpeakeEnergyRecorder(speaker_device) - self.speaker_energy_get_progressbar = thread_fnc(self.progressBar_input_speaker_energy_get) - self.speaker_energy_get_progressbar.daemon = True - self.speaker_energy_get_progressbar.start() - self.speaker_energy_plot_progressbar = thread_fnc(self.progressBar_input_speaker_energy_plot) - self.speaker_energy_plot_progressbar.daemon = True - self.speaker_energy_plot_progressbar.start() - else: - print_textbox(self.parent.textbox_message_log, "Windows playback device and selected device do not match. Change the Windows playback device.", "ERROR") - print_textbox(self.parent.textbox_message_system_log, "Windows playback device and selected device do not match. Change the Windows playback device.", "ERROR") - self.checkbox_input_speaker_threshold_check.deselect() - self.checkbox_input_mic_threshold_check.configure(state="normal") - self.checkbox_input_speaker_threshold_check.configure(state="normal") - - def speaker_threshold_check_stop(self): - if self.speaker_energy_get_progressbar != None: - self.speaker_energy_get_progressbar.stop() - if self.speaker_energy_plot_progressbar != None: - self.speaker_energy_plot_progressbar.stop() - - self.progressBar_input_speaker_energy_threshold.set(0) - self.checkbox_input_mic_threshold_check.configure(state="normal") - self.checkbox_input_speaker_threshold_check.configure(state="normal") - - def checkbox_input_speaker_threshold_check_callback(self): - self.checkbox_input_mic_threshold_check.configure(state="disabled") - self.checkbox_input_speaker_threshold_check.configure(state="disabled") - self.update() - if self.checkbox_input_speaker_threshold_check.get(): - th_speaker_threshold_check_start = Thread(target=self.speaker_threshold_check_start) - th_speaker_threshold_check_start.daemon = True - th_speaker_threshold_check_start.start() - else: - th_speaker_threshold_check_stop = Thread(target=self.speaker_threshold_check_stop) - th_speaker_threshold_check_stop.daemon = True - th_speaker_threshold_check_stop.start() - - def slider_input_speaker_energy_threshold_callback(self, value): - self.parent.INPUT_SPEAKER_ENERGY_THRESHOLD = int(value) - save_json(self.parent.PATH_CONFIG, "INPUT_SPEAKER_ENERGY_THRESHOLD", self.parent.INPUT_SPEAKER_ENERGY_THRESHOLD) - - def checkbox_input_speaker_dynamic_energy_threshold_callback(self): - value = self.checkbox_input_speaker_dynamic_energy_threshold.get() - self.parent.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD = value - save_json(self.parent.PATH_CONFIG, "INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD", self.parent.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD) - - def entry_input_speaker_record_timeout_callback(self, event): - self.parent.INPUT_SPEAKER_RECORD_TIMEOUT = int(self.entry_input_speaker_record_timeout.get()) - save_json(self.parent.PATH_CONFIG, "INPUT_SPEAKER_RECORD_TIMEOUT", self.parent.INPUT_SPEAKER_RECORD_TIMEOUT) - - def entry_input_speaker_phrase_timeout_callback(self, event): - self.parent.INPUT_SPEAKER_PHRASE_TIMEOUT = int(self.entry_input_speaker_phrase_timeout.get()) - save_json(self.parent.PATH_CONFIG, "INPUT_SPEAKER_PHRASE_TIMEOUT", self.parent.INPUT_SPEAKER_PHRASE_TIMEOUT) - - def entry_input_speaker_max_phrases_callback(self, event): - self.parent.INPUT_SPEAKER_MAX_PHRASES = int(self.entry_input_speaker_max_phrases.get()) - save_json(self.parent.PATH_CONFIG, "INPUT_SPEAKER_MAX_PHRASES", self.parent.INPUT_SPEAKER_MAX_PHRASES) - - def entry_ip_address_callback(self, event): - self.parent.OSC_IP_ADDRESS = self.entry_ip_address.get() - save_json(self.parent.PATH_CONFIG, "OSC_IP_ADDRESS", self.parent.OSC_IP_ADDRESS) - - def entry_port_callback(self, event): - self.parent.OSC_PORT = self.entry_port.get() - save_json(self.parent.PATH_CONFIG, "OSC_PORT", self.parent.OSC_PORT) - - def entry_authkey_callback(self, event): - value = self.entry_authkey.get() - if len(value) > 0: - if self.parent.translator.authentication("DeepL(auth)", value) is True: - self.parent.AUTH_KEYS["DeepL(auth)"] = value - save_json(self.parent.PATH_CONFIG, "AUTH_KEYS", self.parent.AUTH_KEYS) - print_textbox(self.parent.textbox_message_log, "Auth key update completed", "INFO") - print_textbox(self.parent.textbox_message_system_log, "Auth key update completed", "INFO") - else: - pass - - def checkbox_auto_clear_chatbox_callback(self): - value = self.checkbox_auto_clear_chatbox.get() - self.parent.ENABLE_AUTO_CLEAR_CHATBOX = value - save_json(self.parent.PATH_CONFIG, "ENABLE_AUTO_CLEAR_CHATBOX", self.parent.ENABLE_AUTO_CLEAR_CHATBOX) - - def checkbox_notice_xsoverlay_callback(self): - value = self.checkbox_notice_xsoverlay.get() - self.parent.ENABLE_NOTICE_XSOVERLAY = value - save_json(self.parent.PATH_CONFIG, "ENABLE_NOTICE_XSOVERLAY", self.parent.ENABLE_NOTICE_XSOVERLAY) - - def delete_window(self): - self.checkbox_input_mic_threshold_check.deselect() - self.checkbox_input_speaker_threshold_check.deselect() - self.checkbox_input_mic_threshold_check_callback() - self.checkbox_input_speaker_threshold_check_callback() - self.parent.transcription_start() - self.parent.foreground_start() - self.parent.checkbox_translation.configure(state="normal") - self.parent.checkbox_transcription_send.configure(state="normal") - self.parent.checkbox_transcription_receive.configure(state="normal") - self.parent.checkbox_foreground.configure(state="normal") - self.parent.tabview_logs.configure(state="normal") - self.parent.textbox_message_log.configure(state="normal") - self.parent.textbox_message_send_log.configure(state="normal") - self.parent.textbox_message_receive_log.configure(state="normal") - self.parent.textbox_message_system_log.configure(state="normal") - self.parent.entry_message_box.configure(state="normal") - self.parent.button_config.configure(state="normal", fg_color=["#3B8ED0", "#1F6AA5"]) - self.parent.button_information.configure(state="normal", fg_color=["#3B8ED0", "#1F6AA5"]) - self.withdraw() - self.grab_release() - - def entry_message_format_callback(self, event): - value = self.entry_message_format.get() - if len(value) > 0: - self.parent.MESSAGE_FORMAT = value - save_json(self.parent.PATH_CONFIG, "MESSAGE_FORMAT", self.parent.MESSAGE_FORMAT) - - def delete_tabview_config(self, pre_language_yaml_data): - self.tabview_config.delete(pre_language_yaml_data["config_tab_title_ui"]) - self.tabview_config.delete(pre_language_yaml_data["config_tab_title_translation"]) - self.tabview_config.delete(pre_language_yaml_data["config_tab_title_transcription"]) - self.tabview_config.delete(pre_language_yaml_data["config_tab_title_parameter"]) - self.tabview_config.delete(pre_language_yaml_data["config_tab_title_others"]) - - def add_tabview_config(self, language_yaml_data, selectable_languages): - config_tab_title_ui = language_yaml_data["config_tab_title_ui"] - config_tab_title_translation = language_yaml_data["config_tab_title_translation"] - config_tab_title_transcription = language_yaml_data["config_tab_title_transcription"] - config_tab_title_parameter = language_yaml_data["config_tab_title_parameter"] - config_tab_title_others = language_yaml_data["config_tab_title_others"] - - init_lang_text = "Loading..." - - # tabwiew config - self.tabview_config = CTkTabview(self) - self.tabview_config.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") - self.tabview_config.add(config_tab_title_ui) - self.tabview_config.add(config_tab_title_translation) - self.tabview_config.add(config_tab_title_transcription) - self.tabview_config.add(config_tab_title_parameter) - self.tabview_config.add(config_tab_title_others) - self.tabview_config.tab(config_tab_title_ui).grid_columnconfigure(1, weight=1) - self.tabview_config.tab(config_tab_title_translation).grid_columnconfigure([1,2,3], weight=1) - self.tabview_config.tab(config_tab_title_transcription).grid_columnconfigure(1, weight=1) - self.tabview_config.tab(config_tab_title_parameter).grid_columnconfigure(1, weight=1) - self.tabview_config.tab(config_tab_title_others).grid_columnconfigure(1, weight=1) - self.tabview_config._segmented_button.configure(font=CTkFont(family=self.parent.FONT_FAMILY)) - self.tabview_config._segmented_button.grid(sticky="W") - - # tab UI - ## slider transparency - row = 0 - padx = 5 - pady = 1 - self.label_transparency = CTkLabel( - self.tabview_config.tab(config_tab_title_ui), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_transparency.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.slider_transparency = CTkSlider( - self.tabview_config.tab(config_tab_title_ui), - from_=50, - to=100, - command=self.slider_transparency_callback, - variable=DoubleVar(value=self.parent.TRANSPARENCY), - ) - self.slider_transparency.grid(row=row, column=1, columnspan=1, padx=padx, pady=10, sticky="nsew") - - ## optionmenu theme - row += 1 - self.label_appearance_theme = CTkLabel( - self.tabview_config.tab(config_tab_title_ui), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_appearance_theme.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.optionmenu_appearance_theme = CTkOptionMenu( - self.tabview_config.tab(config_tab_title_ui), - values=["Light", "Dark", "System"], - command=self.optionmenu_appearance_theme_callback, - variable=StringVar(value=self.parent.APPEARANCE_THEME), - font=CTkFont(family=self.parent.FONT_FAMILY), - dropdown_font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.optionmenu_appearance_theme.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - - ## scrollableDropdown appearance theme - if SCROLLABLE_DROPDOWN: - self.scrollableDropdown_appearance_theme = CTkScrollableDropdown( - self.optionmenu_appearance_theme, - values=["Light", "Dark", "System"], - justify="left", - button_color="transparent", - command=self.optionmenu_appearance_theme_callback, - font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.scrollableDropdown_appearance_theme.bind( - "", - lambda e: self.scrollableDropdown_appearance_theme._withdraw() if not str(e.widget).startswith(str(self.scrollableDropdown_appearance_theme.frame._parent_frame)) else None, - ) - - ## optionmenu UI scaling - row += 1 - self.label_ui_scaling = CTkLabel( - self.tabview_config.tab(config_tab_title_ui), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_ui_scaling.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.optionmenu_ui_scaling = CTkOptionMenu( - self.tabview_config.tab(config_tab_title_ui), - values=["80%", "90%", "100%", "110%", "120%"], - command=self.optionmenu_ui_scaling_callback, - variable=StringVar(value=self.parent.UI_SCALING), - font=CTkFont(family=self.parent.FONT_FAMILY), - dropdown_font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.optionmenu_ui_scaling.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - - ## scrollableDropdown ui scaling - if SCROLLABLE_DROPDOWN: - self.scrollableDropdown_ui_scaling = CTkScrollableDropdown( - self.optionmenu_ui_scaling, - values=["80%", "90%", "100%", "110%", "120%"], - justify="left", - button_color="transparent", - command=self.optionmenu_ui_scaling_callback, - font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.scrollableDropdown_ui_scaling.bind( - "", - lambda e: self.scrollableDropdown_ui_scaling._iconify() if not str(e.widget).startswith(str(self.scrollableDropdown_ui_scaling.frame._parent_frame)) else None, - ) - - ## optionmenu font family - row += 1 - self.label_font_family = CTkLabel( - self.tabview_config.tab(config_tab_title_ui), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_font_family.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - font_families = list(tk_font.families()) - self.optionmenu_font_family = CTkOptionMenu( - self.tabview_config.tab(config_tab_title_ui), - values=font_families, - command=self.optionmenu_font_family_callback, - variable=StringVar(value=self.parent.FONT_FAMILY), - font=CTkFont(family=self.parent.FONT_FAMILY), - dropdown_font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.optionmenu_font_family.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - - ## scrollableDropdown font family - if SCROLLABLE_DROPDOWN: - self.scrollableDropdown_font_family = CTkScrollableDropdown( - self.optionmenu_font_family, - values=font_families, - justify="left", - button_color="transparent", - command=self.optionmenu_font_family_callback, - font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.scrollableDropdown_font_family.bind( - "", - lambda e: self.scrollableDropdown_font_family._withdraw() if not str(e.widget).startswith(str(self.scrollableDropdown_font_family.frame._parent_frame)) else None, - ) - - ## optionmenu ui language - row += 1 - self.label_ui_language = CTkLabel( - self.tabview_config.tab(config_tab_title_ui), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_ui_language.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - selectable_languages_values = list(selectable_languages.values()) - self.optionmenu_ui_language = CTkOptionMenu( - self.tabview_config.tab(config_tab_title_ui), - values=selectable_languages_values, - command=self.optionmenu_ui_language_callback, - variable=StringVar(value=selectable_languages[self.parent.UI_LANGUAGE]), - font=CTkFont(family=self.parent.FONT_FAMILY), - dropdown_font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.optionmenu_ui_language.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - - ## scrollableDropdown ui language - if SCROLLABLE_DROPDOWN: - self.scrollableDropdown_ui_language = CTkScrollableDropdown( - self.optionmenu_ui_language, - values=selectable_languages_values, - justify="left", - button_color="transparent", - command=self.optionmenu_ui_language_callback, - font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.scrollableDropdown_ui_language.bind( - "", - lambda e: self.scrollableDropdown_ui_language._withdraw() if not str(e.widget).startswith(str(self.scrollableDropdown_ui_language.frame._parent_frame)) else None, - ) - - # tab Translation - ## optionmenu translation translator - row = 0 - padx = 5 - pady = 1 - self.label_translation_translator = CTkLabel( - self.tabview_config.tab(config_tab_title_translation), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.label_translation_translator.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.optionmenu_translation_translator = CTkOptionMenu( - self.tabview_config.tab(config_tab_title_translation), - values=list(self.parent.translator.translator_status.keys()), - command=self.optionmenu_translation_translator_callback, - variable=StringVar(value=self.parent.CHOICE_TRANSLATOR), - font=CTkFont(family=self.parent.FONT_FAMILY), - dropdown_font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.optionmenu_translation_translator.grid(row=row, column=1, columnspan=3, padx=padx, pady=pady, sticky="nsew") - - ## scrollableDropdown translation translator - if SCROLLABLE_DROPDOWN: - self.scrollableDropdown_translation_translator = CTkScrollableDropdown( - self.optionmenu_translation_translator, - values=list(self.parent.translator.translator_status.keys()), - justify="left", - button_color="transparent", - command=self.optionmenu_translation_translator_callback, - font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.scrollableDropdown_translation_translator.bind( - "", - lambda e: self.scrollableDropdown_translation_translator._withdraw() if not str(e.widget).startswith(str(self.scrollableDropdown_translation_translator.frame._parent_frame)) else None, - ) - - ## optionmenu translation input language - row +=1 - self.label_translation_input_language = CTkLabel( - self.tabview_config.tab(config_tab_title_translation), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_translation_input_language.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - - ## select translation input source language - self.optionmenu_translation_input_source_language = CTkOptionMenu( - self.tabview_config.tab(config_tab_title_translation), - command=self.optionmenu_translation_input_source_language_callback, - values=list(translation_lang[self.parent.CHOICE_TRANSLATOR]["source"].keys()), - variable=StringVar(value=self.parent.INPUT_SOURCE_LANG), - font=CTkFont(family=self.parent.FONT_FAMILY), - dropdown_font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.optionmenu_translation_input_source_language.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - - ## scrollableDropdown translation input source language - if SCROLLABLE_DROPDOWN: - self.scrollableDropdown_translation_input_source_language = CTkScrollableDropdown( - self.optionmenu_translation_input_source_language, - values=list(translation_lang[self.parent.CHOICE_TRANSLATOR]["source"].keys()), - justify="left", - button_color="transparent", - command=self.optionmenu_translation_input_source_language_callback, - font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.scrollableDropdown_translation_input_source_language.bind( - "", - lambda e: self.scrollableDropdown_translation_input_source_language._withdraw() if not str(e.widget).startswith(str(self.scrollableDropdown_translation_input_source_language.frame._parent_frame)) else None, - ) - - ## label translation input arrow - self.label_translation_input_arrow = CTkLabel( - self.tabview_config.tab(config_tab_title_translation), - text="-->", - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_translation_input_arrow.grid(row=row, column=2, columnspan=1, padx=padx, pady=pady, sticky="nsew") - - ## select translation input target language - self.optionmenu_translation_input_target_language = CTkOptionMenu( - self.tabview_config.tab(config_tab_title_translation), - command=self.optionmenu_translation_input_target_language_callback, - values=list(translation_lang[self.parent.CHOICE_TRANSLATOR]["target"].keys()), - variable=StringVar(value=self.parent.INPUT_TARGET_LANG), - font=CTkFont(family=self.parent.FONT_FAMILY), - dropdown_font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.optionmenu_translation_input_target_language.grid(row=row, column=3, columnspan=1, padx=padx, pady=pady, sticky="nsew") - - ## scrollableDropdown translation input target language - if SCROLLABLE_DROPDOWN: - self.scrollableDropdown_translation_input_target_language = CTkScrollableDropdown( - self.optionmenu_translation_input_target_language, - values=list(translation_lang[self.parent.CHOICE_TRANSLATOR]["target"].keys()), - justify="left", - button_color="transparent", - command=self.optionmenu_translation_input_target_language_callback, - font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.scrollableDropdown_translation_input_target_language.bind( - "", - lambda e: self.scrollableDropdown_translation_input_target_language._withdraw() if not str(e.widget).startswith(str(self.scrollableDropdown_translation_input_target_language.frame._parent_frame)) else None, - ) - - ## optionmenu translation output language - row +=1 - self.label_translation_output_language = CTkLabel( - self.tabview_config.tab(config_tab_title_translation), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_translation_output_language.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - - ## select translation output source language - self.optionmenu_translation_output_source_language = CTkOptionMenu( - self.tabview_config.tab(config_tab_title_translation), - command=self.optionmenu_translation_output_source_language_callback, - values=list(translation_lang[self.parent.CHOICE_TRANSLATOR]["source"].keys()), - variable=StringVar(value=self.parent.OUTPUT_SOURCE_LANG), - font=CTkFont(family=self.parent.FONT_FAMILY), - dropdown_font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.optionmenu_translation_output_source_language.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - - ## scrollableDropdown translation output source language - if SCROLLABLE_DROPDOWN: - self.scrollableDropdown_translation_output_source_language = CTkScrollableDropdown( - self.optionmenu_translation_output_source_language, - values=list(translation_lang[self.parent.CHOICE_TRANSLATOR]["source"].keys()), - justify="left", - button_color="transparent", - command=self.optionmenu_translation_output_source_language_callback, - font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.scrollableDropdown_translation_output_source_language.bind( - "", - lambda e: self.scrollableDropdown_translation_output_source_language._withdraw() if not str(e.widget).startswith(str(self.scrollableDropdown_translation_output_source_language.frame._parent_frame)) else None, - ) - - ## label translation output arrow - self.label_translation_output_arrow = CTkLabel( - self.tabview_config.tab(config_tab_title_translation), - text="-->", - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_translation_output_arrow.grid(row=row, column=2, columnspan=1, padx=padx, pady=pady, sticky="nsew") - - ## select translation output target language - self.optionmenu_translation_output_target_language = CTkOptionMenu( - self.tabview_config.tab(config_tab_title_translation), - command=self.optionmenu_translation_output_target_language_callback, - values=list(translation_lang[self.parent.CHOICE_TRANSLATOR]["target"].keys()), - variable=StringVar(value=self.parent.OUTPUT_TARGET_LANG), - font=CTkFont(family=self.parent.FONT_FAMILY), - dropdown_font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.optionmenu_translation_output_target_language.grid(row=row, column=3, columnspan=1, padx=padx, pady=pady, sticky="nsew") - - ## scrollableDropdown translation output target language - if SCROLLABLE_DROPDOWN: - self.scrollableDropdown_translation_output_target_language = CTkScrollableDropdown( - self.optionmenu_translation_output_target_language, - values=list(translation_lang[self.parent.CHOICE_TRANSLATOR]["target"].keys()), - justify="left", - button_color="transparent", - command=self.optionmenu_translation_output_target_language_callback, - font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.scrollableDropdown_translation_output_target_language.bind( - "", - lambda e: self.scrollableDropdown_translation_output_target_language._withdraw() if not str(e.widget).startswith(str(self.scrollableDropdown_translation_output_target_language.frame._parent_frame)) else None, - ) - - # tab Transcription - ## optionmenu input mic device's host - row = 0 - padx = 5 - pady = 1 - self.label_input_mic_host = CTkLabel( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_input_mic_host.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.optionmenu_input_mic_host = CTkOptionMenu( - self.tabview_config.tab(config_tab_title_transcription), - values=[host for host in get_input_device_list().keys()], - command=self.optionmenu_input_mic_host_callback, - variable=StringVar(value=self.parent.CHOICE_MIC_HOST), - font=CTkFont(family=self.parent.FONT_FAMILY), - dropdown_font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.optionmenu_input_mic_host.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - - ## scrollableDropdown input mic device's host - if SCROLLABLE_DROPDOWN: - self.scrollableDropdown_input_mic_host = CTkScrollableDropdown( - self.optionmenu_input_mic_host, - values=[host for host in get_input_device_list().keys()], - justify="left", - button_color="transparent", - command=self.optionmenu_input_mic_host_callback, - font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.scrollableDropdown_input_mic_host.bind( - "", - lambda e: self.scrollableDropdown_input_mic_host._withdraw() if not str(e.widget).startswith(str(self.scrollableDropdown_input_mic_host.frame._parent_frame)) else None, - ) - - ## optionmenu input mic device - row += 1 - self.label_input_mic_device = CTkLabel( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_input_mic_device.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.optionmenu_input_mic_device = CTkOptionMenu( - self.tabview_config.tab(config_tab_title_transcription), - values=[device["name"] for device in get_input_device_list()[self.parent.CHOICE_MIC_HOST]], - command=self.optionmenu_input_mic_device_callback, - variable=StringVar(value=self.parent.CHOICE_MIC_DEVICE), - font=CTkFont(family=self.parent.FONT_FAMILY), - dropdown_font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.optionmenu_input_mic_device.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - - ## scrollableDropdown input mic device - if SCROLLABLE_DROPDOWN: - self.scrollableDropdown_input_mic_device = CTkScrollableDropdown( - self.optionmenu_input_mic_device, - values=[device["name"] for device in get_input_device_list()[self.parent.CHOICE_MIC_HOST]], - justify="left", - button_color="transparent", - command=self.optionmenu_input_mic_device_callback, - font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.scrollableDropdown_input_mic_device.bind( - "", - lambda e: self.scrollableDropdown_input_mic_device._withdraw() if not str(e.widget).startswith(str(self.scrollableDropdown_input_mic_device.frame._parent_frame)) else None, - ) - - ## optionmenu input mic voice language - row +=1 - self.label_input_mic_voice_language = CTkLabel( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_input_mic_voice_language.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.optionmenu_input_mic_voice_language = CTkOptionMenu( - self.tabview_config.tab(config_tab_title_transcription), - values=list(transcription_lang.keys()), - command=self.optionmenu_input_mic_voice_language_callback, - variable=StringVar(value=self.parent.INPUT_MIC_VOICE_LANGUAGE), - font=CTkFont(family=self.parent.FONT_FAMILY), - dropdown_font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.optionmenu_input_mic_voice_language.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - - ## scrollableDropdown input mic voice language - if SCROLLABLE_DROPDOWN: - self.scrollableDropdown_input_voice_language = CTkScrollableDropdown( - self.optionmenu_input_mic_voice_language, - values=list(transcription_lang.keys()), - justify="left", - button_color="transparent", - command=self.optionmenu_input_mic_voice_language_callback, - font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.scrollableDropdown_input_voice_language.bind( - "", - lambda e: self.scrollableDropdown_input_voice_language._withdraw() if not str(e.widget).startswith(str(self.scrollableDropdown_input_voice_language.frame._parent_frame)) else None, - ) - - ## slider input mic energy threshold - row +=1 - self.label_input_mic_energy_threshold = CTkLabel( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_input_mic_energy_threshold.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - - self.slider_input_mic_energy_threshold = CTkSlider( - self.tabview_config.tab(config_tab_title_transcription), - from_=0, - to=self.MAX_MIC_ENERGY_THRESHOLD, - border_width=7, - button_length=0, - button_corner_radius=3, - number_of_steps=self.MAX_MIC_ENERGY_THRESHOLD, - command=self.slider_input_mic_energy_threshold_callback, - variable=IntVar(value=self.parent.INPUT_MIC_ENERGY_THRESHOLD), - ) - self.slider_input_mic_energy_threshold.grid(row=row, column=1, columnspan=1, padx=0, pady=5, sticky="nsew") - - ## progressBar input mic energy threshold - row +=1 - self.checkbox_input_mic_threshold_check = CTkCheckBox( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - onvalue=True, - offvalue=False, - command=self.checkbox_input_mic_threshold_check_callback, - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.checkbox_input_mic_threshold_check.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - - self.progressBar_input_mic_energy_threshold = CTkProgressBar( - self.tabview_config.tab(config_tab_title_transcription), - corner_radius=0 - ) - self.progressBar_input_mic_energy_threshold.grid(row=row, column=1, columnspan=1, padx=padx, pady=5, sticky="nsew") - self.progressBar_input_mic_energy_threshold.set(0) - - ## checkbox input mic dynamic energy threshold - row +=1 - self.label_input_mic_dynamic_energy_threshold = CTkLabel( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_input_mic_dynamic_energy_threshold.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.checkbox_input_mic_dynamic_energy_threshold = CTkCheckBox( - self.tabview_config.tab(config_tab_title_transcription), - text="", - onvalue=True, - offvalue=False, - command=self.checkbox_input_mic_dynamic_energy_threshold_callback, - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.checkbox_input_mic_dynamic_energy_threshold.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - if self.parent.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD is True: - self.checkbox_input_mic_dynamic_energy_threshold.select() - else: - self.checkbox_input_mic_dynamic_energy_threshold.deselect() - - ## entry input mic record timeout - row +=1 - self.label_input_mic_record_timeout = CTkLabel( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_input_mic_record_timeout.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.entry_input_mic_record_timeout = CTkEntry( - self.tabview_config.tab(config_tab_title_transcription), - textvariable=StringVar(value=self.parent.INPUT_MIC_RECORD_TIMEOUT), - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.entry_input_mic_record_timeout.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - self.entry_input_mic_record_timeout.bind("", self.entry_input_mic_record_timeout_callback) - - ## entry input mic phrase timeout - row +=1 - self.label_input_mic_phrase_timeout = CTkLabel( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_input_mic_phrase_timeout.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.entry_input_mic_phrase_timeout = CTkEntry( - self.tabview_config.tab(config_tab_title_transcription), - textvariable=StringVar(value=self.parent.INPUT_MIC_PHRASE_TIMEOUT), - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.entry_input_mic_phrase_timeout.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - self.entry_input_mic_phrase_timeout.bind("", self.entry_input_mic_phrase_timeout_callback) - - ## entry input mic max phrases - row +=1 - self.label_input_mic_max_phrases = CTkLabel( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_input_mic_max_phrases.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.entry_input_mic_max_phrases = CTkEntry( - self.tabview_config.tab(config_tab_title_transcription), - textvariable=StringVar(value=self.parent.INPUT_MIC_MAX_PHRASES), - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.entry_input_mic_max_phrases.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - self.entry_input_mic_max_phrases.bind("", self.entry_input_mic_max_phrases_callback) - - ## entry input mic word filter - row +=1 - self.label_input_mic_word_filter = CTkLabel( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_input_mic_word_filter.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - if len(self.parent.INPUT_MIC_WORD_FILTER) > 0: - textvariable=StringVar(value=",".join(self.parent.INPUT_MIC_WORD_FILTER)) - else: - textvariable=None - self.entry_input_mic_word_filter = CTkEntry( - self.tabview_config.tab(config_tab_title_transcription), - textvariable=textvariable, - placeholder_text="AAA,BBB,CCC", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.entry_input_mic_word_filter.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - self.entry_input_mic_word_filter.bind("", self.entry_input_mic_word_filters_callback) - - ## optionmenu input speaker device - row +=1 - self.label_input_speaker_device = CTkLabel( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_input_speaker_device.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.optionmenu_input_speaker_device = CTkOptionMenu( - self.tabview_config.tab(config_tab_title_transcription), - values=[device["name"] for device in get_output_device_list()], - command=self.optionmenu_input_speaker_device_callback, - variable=StringVar(value=self.parent.CHOICE_SPEAKER_DEVICE), - font=CTkFont(family=self.parent.FONT_FAMILY), - dropdown_font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.optionmenu_input_speaker_device.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - - ## scrollableDropdown input speaker device - if SCROLLABLE_DROPDOWN: - self.scrollableDropdown_input_speaker_device = CTkScrollableDropdown( - self.optionmenu_input_speaker_device, - values=[device["name"] for device in get_output_device_list()], - justify="left", - button_color="transparent", - command=self.optionmenu_input_speaker_device_callback, - font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.scrollableDropdown_input_speaker_device.bind( - "", - lambda e: self.scrollableDropdown_input_speaker_device._withdraw() if not str(e.widget).startswith(str(self.scrollableDropdown_input_speaker_device.frame._parent_frame)) else None, - ) - - ## optionmenu input speaker voice language - row +=1 - self.label_input_speaker_voice_language = CTkLabel( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_input_speaker_voice_language.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.optionmenu_input_speaker_voice_language = CTkOptionMenu( - self.tabview_config.tab(config_tab_title_transcription), - values=list(transcription_lang.keys()), - command=self.optionmenu_input_speaker_voice_language_callback, - variable=StringVar(value=self.parent.INPUT_SPEAKER_VOICE_LANGUAGE), - font=CTkFont(family=self.parent.FONT_FAMILY), - dropdown_font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.optionmenu_input_speaker_voice_language.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - - ## scrollableDropdown input speaker voice language - if SCROLLABLE_DROPDOWN: - self.scrollableDropdown_input_speaker_voice_language = CTkScrollableDropdown( - self.optionmenu_input_speaker_voice_language, - values=list(transcription_lang.keys()), - justify="left", - button_color="transparent", - command=self.optionmenu_input_speaker_voice_language_callback, - font=CTkFont(family=self.parent.FONT_FAMILY), - ) - self.scrollableDropdown_input_speaker_voice_language.bind( - "", - lambda e: self.scrollableDropdown_input_speaker_voice_language._withdraw() if not str(e.widget).startswith(str(self.scrollableDropdown_input_speaker_voice_language.frame._parent_frame)) else None, - ) - - ## entry input speaker energy threshold - row +=1 - self.label_input_speaker_energy_threshold = CTkLabel( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_input_speaker_energy_threshold.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - - ## progressBar input speaker energy threshold - self.slider_input_speaker_energy_threshold = CTkSlider( - self.tabview_config.tab(config_tab_title_transcription), - from_=0, - to=self.MAX_SPEAKER_ENERGY_THRESHOLD, - border_width=7, - button_length=0, - button_corner_radius=3, - number_of_steps=self.MAX_SPEAKER_ENERGY_THRESHOLD, - command=self.slider_input_speaker_energy_threshold_callback, - variable=IntVar(value=self.parent.INPUT_SPEAKER_ENERGY_THRESHOLD), - ) - self.slider_input_speaker_energy_threshold.grid(row=row, column=1, columnspan=1, padx=0, pady=5, sticky="nsew") - - ## progressBar input speaker energy threshold - row +=1 - self.checkbox_input_speaker_threshold_check = CTkCheckBox( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - onvalue=True, - offvalue=False, - command=self.checkbox_input_speaker_threshold_check_callback, - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.checkbox_input_speaker_threshold_check.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - - self.progressBar_input_speaker_energy_threshold = CTkProgressBar( - self.tabview_config.tab(config_tab_title_transcription), - corner_radius=0 - ) - self.progressBar_input_speaker_energy_threshold.grid(row=row, column=1, columnspan=1, padx=padx, pady=5, sticky="nsew") - self.progressBar_input_speaker_energy_threshold.set(0) - - ## checkbox input speaker dynamic energy threshold - row +=1 - self.label_input_speaker_dynamic_energy_threshold = CTkLabel( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_input_speaker_dynamic_energy_threshold.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.checkbox_input_speaker_dynamic_energy_threshold = CTkCheckBox( - self.tabview_config.tab(config_tab_title_transcription), - text="", - onvalue=True, - offvalue=False, - command=self.checkbox_input_speaker_dynamic_energy_threshold_callback, - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.checkbox_input_speaker_dynamic_energy_threshold.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - if self.parent.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD is True: - self.checkbox_input_speaker_dynamic_energy_threshold.select() - else: - self.checkbox_input_speaker_dynamic_energy_threshold.deselect() - - ## entry input speaker record timeout - row +=1 - self.label_input_speaker_record_timeout = CTkLabel( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_input_speaker_record_timeout.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.entry_input_speaker_record_timeout = CTkEntry( - self.tabview_config.tab(config_tab_title_transcription), - textvariable=StringVar(value=self.parent.INPUT_SPEAKER_RECORD_TIMEOUT), - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.entry_input_speaker_record_timeout.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - self.entry_input_speaker_record_timeout.bind("", self.entry_input_speaker_record_timeout_callback) - - ## entry input speaker phrase timeout - row +=1 - self.label_input_speaker_phrase_timeout = CTkLabel( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_input_speaker_phrase_timeout.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.entry_input_speaker_phrase_timeout = CTkEntry( - self.tabview_config.tab(config_tab_title_transcription), - textvariable=StringVar(value=self.parent.INPUT_SPEAKER_PHRASE_TIMEOUT), - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.entry_input_speaker_phrase_timeout.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - self.entry_input_speaker_phrase_timeout.bind("", self.entry_input_speaker_phrase_timeout_callback) - - ## entry input speaker max phrases - row +=1 - self.label_input_speaker_max_phrases = CTkLabel( - self.tabview_config.tab(config_tab_title_transcription), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_input_speaker_max_phrases.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.entry_input_speaker_max_phrases = CTkEntry( - self.tabview_config.tab(config_tab_title_transcription), - textvariable=StringVar(value=self.parent.INPUT_SPEAKER_MAX_PHRASES), - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.entry_input_speaker_max_phrases.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - self.entry_input_speaker_max_phrases.bind("", self.entry_input_speaker_max_phrases_callback) - - # tab Parameter - ## entry ip address - row = 0 - padx = 5 - pady = 1 - self.label_ip_address = CTkLabel( - self.tabview_config.tab(config_tab_title_parameter), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_ip_address.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.entry_ip_address = CTkEntry( - self.tabview_config.tab(config_tab_title_parameter), - textvariable=StringVar(value=self.parent.OSC_IP_ADDRESS), - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.entry_ip_address.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - self.entry_ip_address.bind("", self.entry_ip_address_callback) - - ## entry port - row +=1 - self.label_port = CTkLabel( - self.tabview_config.tab(config_tab_title_parameter), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_port.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.entry_port = CTkEntry( - self.tabview_config.tab(config_tab_title_parameter), - textvariable=StringVar(value=self.parent.OSC_PORT), - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.entry_port.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - self.entry_port.bind("", self.entry_port_callback) - - ## entry authkey - row +=1 - self.label_authkey = CTkLabel( - self.tabview_config.tab(config_tab_title_parameter), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_authkey.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.entry_authkey = CTkEntry( - self.tabview_config.tab(config_tab_title_parameter), - textvariable=StringVar(value=self.parent.AUTH_KEYS["DeepL(auth)"]), - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.entry_authkey.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - self.entry_authkey.bind("", self.entry_authkey_callback) - - ## entry message format - row +=1 - self.label_message_format = CTkLabel( - self.tabview_config.tab(config_tab_title_parameter), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_message_format.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.entry_message_format = CTkEntry( - self.tabview_config.tab(config_tab_title_parameter), - textvariable=StringVar(value=self.parent.MESSAGE_FORMAT), - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.entry_message_format.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - self.entry_message_format.bind("", self.entry_message_format_callback) - - # tab Others - ## checkbox auto clear chat box - row = 0 - self.label_checkbox_auto_clear_chatbox = CTkLabel( - self.tabview_config.tab(config_tab_title_others), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_checkbox_auto_clear_chatbox.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.checkbox_auto_clear_chatbox = CTkCheckBox( - self.tabview_config.tab(config_tab_title_others), - text="", - onvalue=True, - offvalue=False, - command=self.checkbox_auto_clear_chatbox_callback, - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.checkbox_auto_clear_chatbox.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - if self.parent.ENABLE_AUTO_CLEAR_CHATBOX is True: - self.checkbox_auto_clear_chatbox.select() - else: - self.checkbox_auto_clear_chatbox.deselect() - - # checkbox notice xsoverlay - row += 1 - self.label_checkbox_notice_xsoverlay = CTkLabel( - self.tabview_config.tab(config_tab_title_others), - text=init_lang_text, - fg_color="transparent", - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_checkbox_notice_xsoverlay.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw") - self.checkbox_notice_xsoverlay = CTkCheckBox( - self.tabview_config.tab(config_tab_title_others), - text="", - onvalue=True, - offvalue=False, - command=self.checkbox_notice_xsoverlay_callback, - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.checkbox_notice_xsoverlay.grid(row=row, column=1, columnspan=1, padx=padx, pady=pady, sticky="nsew") - if self.parent.ENABLE_NOTICE_XSOVERLAY is True: - self.checkbox_notice_xsoverlay.select() - else: - self.checkbox_notice_xsoverlay.deselect() - widget_config_window_label_setter(self, language_yaml_data) \ No newline at end of file diff --git a/window_information.py b/window_information.py deleted file mode 100644 index 8e912c0d..00000000 --- a/window_information.py +++ /dev/null @@ -1,160 +0,0 @@ -import os -from customtkinter import CTkToplevel, CTkTextbox, CTkFont - -class ToplevelWindowInformation(CTkToplevel): - def __init__(self, parent, *args, **kwargs): - super().__init__(parent, *args, **kwargs) - self.withdraw() - self.parent = parent - self.grid_columnconfigure(0, weight=1) - self.grid_rowconfigure(0, weight=1) - # self.geometry(f"{500}x{300}") - self.minsize(500, 300) - - self.after(200, lambda: self.iconbitmap(os.path.join(os.path.dirname(__file__), "img", "app.ico"))) - self.title("Information") - # create textbox information - self.textbox_information = CTkTextbox( - self, - font=CTkFont(family=self.parent.FONT_FAMILY) - ) - self.textbox_information.grid(row=0, column=0, padx=(10, 10), pady=(10, 10), sticky="nsew") - textbox_information_message = """VRCT(v1.3.2) - -# 概要 -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を削除 - -# お問い合わせ -要望などはTwitterまで -https://twitter.com/misya_ai - -# アップデート履歴 -[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を開けるように変更 -- 文字起こし言語の表示を修正 -- いくつかのバグを修正 - -# 注意事項 -再配布とかはやめてね -""" - - self.textbox_information.insert("end", textbox_information_message) - self.textbox_information.configure(state='disabled') - self.protocol("WM_DELETE_WINDOW", self.delete_window) - - def delete_window(self): - self.withdraw() \ No newline at end of file