-### init
-0. VRChatのOSCを有効にする(重要)
+[](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 ff7e41c8..00000000
--- a/VRCT.py
+++ /dev/null
@@ -1,499 +0,0 @@
-from time import sleep
-from os import path as os_path
-
-import customtkinter
-from customtkinter import CTk, CTkFrame, CTkCheckBox, CTkFont, CTkButton, CTkImage, CTkTabview, CTkTextbox, CTkEntry
-from PIL.Image import open as Image_open
-
-from threading import Thread
-from utils import print_textbox, get_localized_text, widget_main_window_label_setter
-from window_config import ToplevelWindowConfig
-from window_information import ToplevelWindowInformation
-from config import config
-from model import model
-
-class App(CTk):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
-
- ## set UI theme
- customtkinter.set_appearance_mode(config.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)
- self.wm_attributes("-alpha", config.TRANSPARENCY/100)
- customtkinter.set_widget_scaling(int(config.UI_SCALING.replace("%", "")) / 100)
- self.protocol("WM_DELETE_WINDOW", self.delete_window)
-
- # add sidebar
- self.add_sidebar()
-
- # add entry message box
- self.entry_message_box = CTkEntry(
- self,
- placeholder_text="message",
- font=CTkFont(family=config.FONT_FAMILY),
- )
- self.entry_message_box.grid(row=1, column=1, columnspan=2, padx=5, pady=(5, 10), sticky="nsew")
- 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)
-
- # add tabview textbox
- self.add_tabview_logs(get_localized_text(f"{config.UI_LANGUAGE}"))
-
- self.config_window = ToplevelWindowConfig(self)
- self.information_window = ToplevelWindowInformation(self)
- self.init_process()
-
- def init_process(self):
- # set translator
- if model.authenticationTranslator() is False:
- # error update Auth key
- self.printLogAuthenticationError()
-
- # set word filter
- model.addKeywords()
-
- # check OSC started
- model.checkOSCStarted()
-
- # check Software Updated
- model.checkSoftwareUpdated()
-
- 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):
- config.ENABLE_TRANSLATION = self.checkbox_translation.get()
- if config.ENABLE_TRANSLATION is True:
- self.printLogStartTranslation()
- else:
- self.printLogStopTranslation()
-
- def transcription_send_start(self):
- model.startMicTranscript(self.sendMicMessage)
- self.printLogStartVoice2chatbox()
- 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):
- model.stopMicTranscript()
- self.printLogStopVoice2chatbox()
- 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):
- model.stopMicTranscript()
- self.printLogStopVoice2chatbox()
-
- def checkbox_transcription_send_callback(self):
- config.ENABLE_TRANSCRIPTION_SEND = self.checkbox_transcription_send.get()
- 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 config.ENABLE_TRANSCRIPTION_SEND 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):
- model.startSpeakerTranscript(self.receiveSpeakerMessage)
- self.printLogStartSpeaker2log()
- 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):
- model.stopSpeakerTranscript()
- self.printLogStopSpeaker2log()
- 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):
- model.stopSpeakerTranscript()
- self.printLogStopSpeaker2log()
-
- def checkbox_transcription_receive_callback(self):
- config.ENABLE_TRANSCRIPTION_RECEIVE = self.checkbox_transcription_receive.get()
- 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 config.ENABLE_TRANSCRIPTION_RECEIVE 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 config.ENABLE_TRANSCRIPTION_SEND 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 config.ENABLE_TRANSCRIPTION_RECEIVE 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 config.ENABLE_TRANSCRIPTION_SEND 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 config.ENABLE_TRANSCRIPTION_RECEIVE 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):
- config.ENABLE_FOREGROUND = self.checkbox_foreground.get()
- if config.ENABLE_FOREGROUND:
- self.attributes("-topmost", True)
- self.printLogStartForeground()
- else:
- self.attributes("-topmost", False)
- self.printLogStopForeground()
-
- def foreground_start(self):
- if config.ENABLE_FOREGROUND:
- self.attributes("-topmost", True)
- self.printLogStartForeground()
-
- def foreground_stop(self):
- if config.ENABLE_FOREGROUND:
- self.attributes("-topmost", False)
- self.printLogStopForeground()
-
- def entry_message_box_press_key_enter(self, event):
- # osc stop send typing
- model.oscStopSendTyping()
-
- if config.ENABLE_FOREGROUND:
- self.attributes("-topmost", True)
-
- message = self.entry_message_box.get()
- self.sendChatMessage(message)
-
- def entry_message_box_press_key_any(self, event):
- # osc start send typing
- model.oscStartSendTyping()
- if config.ENABLE_FOREGROUND:
- self.attributes("-topmost", False)
-
- if event.keysym != "??":
- if len(event.char) != 0 and event.keysym in config.BREAK_KEYSYM_LIST:
- self.entry_message_box.insert("end", event.char)
- return "break"
-
- def entry_message_box_leave(self, event):
- # osc stop send typing
- model.oscStopSendTyping()
- if config.ENABLE_FOREGROUND:
- self.attributes("-topmost", True)
-
- def delete_window(self):
- self.quit()
- self.destroy()
-
- def add_sidebar(self):
- init_lang_text = "Loading..."
- self.sidebar_frame = CTkFrame(master=self, corner_radius=0)
-
- # 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=config.FONT_FAMILY)
- )
-
- # 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=config.FONT_FAMILY)
- )
-
- # 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=config.FONT_FAMILY)
- )
-
- # 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=config.FONT_FAMILY)
- )
-
- # 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")))
- )
-
- # 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.sidebar_frame.grid(row=0, column=0, rowspan=4, sticky="nsw")
- self.sidebar_frame.grid_rowconfigure(5, weight=1)
- self.checkbox_translation.grid(row=0, column=0, columnspan=2, padx=10, pady=(5, 5), sticky="we")
- self.checkbox_transcription_send.grid(row=1, column=0, columnspan=2, padx=10, pady=(5, 5), sticky="we")
- self.checkbox_transcription_receive.grid(row=2, column=0, columnspan=2, padx=10, pady=(5, 5), sticky="we")
- self.checkbox_foreground.grid(row=3, column=0, columnspan=2, padx=10, pady=(5, 5), sticky="we")
- self.button_information.grid(row=5, column=0, padx=(10, 5), pady=(5, 5), sticky="wse")
- self.button_config.grid(row=5, column=1, padx=(5, 10), pady=(5, 5), sticky="wse")
-
- 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=config.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=config.FONT_FAMILY)
- )
-
- # add textbox message send log
- self.textbox_message_send_log = CTkTextbox(
- self.tabview_logs.tab(main_tab_title_send),
- font=CTkFont(family=config.FONT_FAMILY)
- )
-
- # add textbox message receive log
- self.textbox_message_receive_log = CTkTextbox(
- self.tabview_logs.tab(main_tab_title_receive),
- font=CTkFont(family=config.FONT_FAMILY)
- )
-
- # add textbox message system log
- self.textbox_message_system_log = CTkTextbox(
- self.tabview_logs.tab(main_tab_title_system),
- font=CTkFont(family=config.FONT_FAMILY)
- )
-
- self.textbox_message_log.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
- self.textbox_message_send_log.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
- self.textbox_message_receive_log.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
- self.textbox_message_system_log.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
- 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')
-
- widget_main_window_label_setter(self, language_yaml_data)
-
- def printLogAuthenticationError(self):
- 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")
-
- def printLogStartTranslation(self):
- print_textbox(self.textbox_message_log, "Start translation", "INFO")
- print_textbox(self.textbox_message_system_log, "Start translation", "INFO")
-
- def printLogStopTranslation(self):
- print_textbox(self.textbox_message_log, "Stop translation", "INFO")
- print_textbox(self.textbox_message_system_log, "Stop translation", "INFO")
-
- def printLogStartVoice2chatbox(self):
- print_textbox(self.textbox_message_log, "Start voice2chatbox", "INFO")
- print_textbox(self.textbox_message_system_log, "Start voice2chatbox", "INFO")
-
- def printLogStopVoice2chatbox(self):
- print_textbox(self.textbox_message_log, "Stop voice2chatbox", "INFO")
- print_textbox(self.textbox_message_system_log, "Stop voice2chatbox", "INFO")
-
- def printLogStartSpeaker2log(self):
- print_textbox(self.textbox_message_log, "Start speaker2log", "INFO")
- print_textbox(self.textbox_message_system_log, "Start speaker2log", "INFO")
-
- def printLogStopSpeaker2log(self):
- print_textbox(self.textbox_message_log, "Stop speaker2log", "INFO")
- print_textbox(self.textbox_message_system_log, "Stop speaker2log", "INFO")
-
- def printLogStartForeground(self):
- print_textbox(self.textbox_message_log, "Start foreground", "INFO")
- print_textbox(self.textbox_message_system_log, "Start foreground", "INFO")
-
- def printLogStopForeground(self):
- print_textbox(self.textbox_message_log, "Stop foreground", "INFO")
- print_textbox(self.textbox_message_system_log, "Stop foreground", "INFO")
-
- def printLogDetectWordFilter(self, message):
- print_textbox(self.textbox_message_log, f"Detect WordFilter :{message}", "INFO")
- print_textbox(self.textbox_message_system_log, f"Detect WordFilter :{message}", "INFO")
-
- def printLogOSCError(self):
- 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")
-
- def printLogSendMessage(self, message):
- print_textbox(self.textbox_message_log, f"{message}", "SEND")
- print_textbox(self.textbox_message_send_log, f"{message}", "SEND")
-
- def printLogReceiveMessage(self, message):
- print_textbox(self.textbox_message_log, f"{message}", "RECEIVE")
- print_textbox(self.textbox_message_receive_log, f"{message}", "RECEIVE")
-
- def sendChatMessage(self, message):
- if len(message) > 0:
- # translate
- if config.ENABLE_TRANSLATION is False:
- chat_message = f"{message}"
- elif model.getTranslatorStatus() is False:
- self.printLogAuthenticationError()
- chat_message = f"{message}"
- else:
- chat_message = model.getInputTranslate(message)
-
- # send OSC message
- if config.ENABLE_OSC is True:
- model.oscSendMessage(chat_message)
- else:
- self.printLogOSCError()
-
- # update textbox message log
- self.printLogSendMessage(chat_message)
-
- # delete message in entry message box
- if config.ENABLE_AUTO_CLEAR_CHATBOX is True:
- self.entry_message_box.delete(0, customtkinter.END)
-
- def sendMicMessage(self, message):
- if len(message) > 0:
- # word filter
- if model.checkKeywords(message):
- self.printLogDetectWordFilter(message)
- return
-
- # translate
- if config.ENABLE_TRANSLATION is False:
- voice_message = f"{message}"
- elif model.getTranslatorStatus() is False:
- self.printLogAuthenticationError()
- voice_message = f"{message}"
- else:
- voice_message = model.getInputTranslate(message)
-
- if config.ENABLE_TRANSCRIPTION_SEND is True:
- if config.ENABLE_OSC is True:
- # osc send message
- model.oscSendMessage(voice_message)
- else:
- self.printLogOSCError()
- # update textbox message log
- self.printLogSendMessage(voice_message)
-
- def receiveSpeakerMessage(self, message):
- if len(message) > 0:
- # translate
- if config.ENABLE_TRANSLATION is False:
- voice_message = f"{message}"
- elif model.getTranslatorStatus() is False:
- self.printLogAuthenticationError()
- voice_message = f"{message}"
- else:
- voice_message = model.getOutputTranslate(message)
-
- if config.ENABLE_TRANSCRIPTION_RECEIVE is True:
- # update textbox message receive log
- self.printLogReceiveMessage(voice_message)
- if config.ENABLE_NOTICE_XSOVERLAY is True:
- model.notificationXsoverlay(voice_message)
-
-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
index 2b99e4dc..65f8fa15 100644
--- a/config.py
+++ b/config.py
@@ -1,4 +1,4 @@
-from json import load, dump
+import sys
import inspect
from os import path as os_path
from json import load as json_load
@@ -6,16 +6,23 @@ 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, translation_lang
-from models.transcription.transcription_languages import transcription_lang
-from models.transcription.transcription_utils import getInputDevices, getOutputDevices, getDefaultInputDevice, getDefaultOutputDevice
+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") as fp:
- json_data = load(fp)
+ with open(path, "r", encoding="utf-8") as fp:
+ json_data = json_load(fp)
json_data[key] = value
- with open(path, "w") as fp:
- dump(json_data, fp, indent=4)
+ with open(path, "w", encoding="utf-8") as fp:
+ json_dump(json_data, fp, indent=4, ensure_ascii=False)
class Config:
_instance = None
@@ -27,6 +34,7 @@ class Config:
cls._instance.load_config()
return cls._instance
+ # Read Only
@property
def VERSION(self):
return self._VERSION
@@ -35,6 +43,27 @@ class Config:
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
@@ -72,6 +101,99 @@ class Config:
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
@@ -82,6 +204,7 @@ class Config:
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property
+ @json_serializable('APPEARANCE_THEME')
def APPEARANCE_THEME(self):
return self._APPEARANCE_THEME
@@ -92,16 +215,18 @@ class Config:
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 ["80%", "90%", "100%", "110%", "120%"]:
+ 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
@@ -115,6 +240,7 @@ class Config:
root.destroy()
@property
+ @json_serializable('UI_LANGUAGE')
def UI_LANGUAGE(self):
return self._UI_LANGUAGE
@@ -125,56 +251,7 @@ class Config:
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, 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
- saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
-
- @property
- def INPUT_SOURCE_LANG(self):
- return self._INPUT_SOURCE_LANG
-
- @INPUT_SOURCE_LANG.setter
- def INPUT_SOURCE_LANG(self, value):
- if value in list(translation_lang[self.CHOICE_TRANSLATOR]["source"].keys()):
- self._INPUT_SOURCE_LANG = value
- saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
-
- @property
- def INPUT_TARGET_LANG(self):
- return self._INPUT_TARGET_LANG
-
- @INPUT_TARGET_LANG.setter
- def INPUT_TARGET_LANG(self, value):
- if value in list(translation_lang[self.CHOICE_TRANSLATOR]["target"].keys()):
- self._INPUT_TARGET_LANG = value
- saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
-
- @property
- def OUTPUT_SOURCE_LANG(self):
- return self._OUTPUT_SOURCE_LANG
-
- @OUTPUT_SOURCE_LANG.setter
- def OUTPUT_SOURCE_LANG(self, value):
- if value in list(translation_lang[self.CHOICE_TRANSLATOR]["source"].keys()):
- self._OUTPUT_SOURCE_LANG = value
- saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
-
- @property
- def OUTPUT_TARGET_LANG(self):
- return self._OUTPUT_TARGET_LANG
-
- @OUTPUT_TARGET_LANG.setter
- def OUTPUT_TARGET_LANG(self, value):
- if value in list(translation_lang[self.CHOICE_TRANSLATOR]["target"].keys()):
- self._OUTPUT_TARGET_LANG = 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
@@ -185,6 +262,7 @@ class Config:
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
@@ -195,16 +273,7 @@ class Config:
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property
- def INPUT_MIC_VOICE_LANGUAGE(self):
- return self._INPUT_MIC_VOICE_LANGUAGE
-
- @INPUT_MIC_VOICE_LANGUAGE.setter
- def INPUT_MIC_VOICE_LANGUAGE(self, value):
- if value in list(transcription_lang.keys()):
- self._INPUT_MIC_VOICE_LANGUAGE = 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
@@ -215,6 +284,7 @@ class Config:
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
@@ -225,6 +295,7 @@ class Config:
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
@@ -235,6 +306,7 @@ class Config:
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
@@ -245,6 +317,7 @@ class Config:
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
@@ -255,6 +328,7 @@ class Config:
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
@@ -265,28 +339,7 @@ class Config:
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property
- def CHOICE_SPEAKER_DEVICE(self):
- return self._CHOICE_SPEAKER_DEVICE
-
- @CHOICE_SPEAKER_DEVICE.setter
- def CHOICE_SPEAKER_DEVICE(self, value):
- if value in [device["name"] for device in getOutputDevices()]:
- speaker_device = [device for device in getOutputDevices() if device["name"] == value][0]
- if getDefaultOutputDevice()["index"] == speaker_device["index"]:
- self._CHOICE_SPEAKER_DEVICE = value
- saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
-
- @property
- def INPUT_SPEAKER_VOICE_LANGUAGE(self):
- return self._INPUT_SPEAKER_VOICE_LANGUAGE
-
- @INPUT_SPEAKER_VOICE_LANGUAGE.setter
- def INPUT_SPEAKER_VOICE_LANGUAGE(self, value):
- if value in list(transcription_lang.keys()):
- self._INPUT_SPEAKER_VOICE_LANGUAGE = 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
@@ -297,6 +350,7 @@ class Config:
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
@@ -307,6 +361,7 @@ class Config:
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
@@ -317,6 +372,7 @@ class Config:
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
@@ -327,6 +383,7 @@ class Config:
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
@@ -337,6 +394,7 @@ class Config:
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
@@ -347,6 +405,7 @@ class Config:
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property
+ @json_serializable('OSC_PORT')
def OSC_PORT(self):
return self._OSC_PORT
@@ -357,6 +416,7 @@ class Config:
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property
+ @json_serializable('AUTH_KEYS')
def AUTH_KEYS(self):
return self._AUTH_KEYS
@@ -369,6 +429,7 @@ class Config:
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
@@ -379,16 +440,18 @@ class Config:
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property
- def ENABLE_AUTO_CLEAR_CHATBOX(self):
- return self._ENABLE_AUTO_CLEAR_CHATBOX
+ @json_serializable('ENABLE_AUTO_CLEAR_MESSAGE_BOX')
+ def ENABLE_AUTO_CLEAR_MESSAGE_BOX(self):
+ return self._ENABLE_AUTO_CLEAR_MESSAGE_BOX
- @ENABLE_AUTO_CLEAR_CHATBOX.setter
- def ENABLE_AUTO_CLEAR_CHATBOX(self, value):
+ @ENABLE_AUTO_CLEAR_MESSAGE_BOX.setter
+ def ENABLE_AUTO_CLEAR_MESSAGE_BOX(self, value):
if type(value) is bool:
- self._ENABLE_AUTO_CLEAR_CHATBOX = value
+ 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
@@ -399,109 +462,133 @@ class Config:
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property
- def ENABLE_OSC(self):
- return self._ENABLE_OSC
+ @json_serializable('ENABLE_SEND_MESSAGE_TO_VRC')
+ def ENABLE_SEND_MESSAGE_TO_VRC(self):
+ return self._ENABLE_SEND_MESSAGE_TO_VRC
- @ENABLE_OSC.setter
- def ENABLE_OSC(self, value):
+ @ENABLE_SEND_MESSAGE_TO_VRC.setter
+ def ENABLE_SEND_MESSAGE_TO_VRC(self, value):
if type(value) is bool:
- self._ENABLE_OSC = value
+ 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
- def UPDATE_FLAG(self):
- return self._UPDATE_FLAG
+ @json_serializable('ENABLE_LOGGER')
+ def ENABLE_LOGGER(self):
+ return self._ENABLE_LOGGER
- @UPDATE_FLAG.setter
- def UPDATE_FLAG(self, value):
+ @ENABLE_LOGGER.setter
+ def ENABLE_LOGGER(self, value):
if type(value) is bool:
- self._UPDATE_FLAG = value
+ self._ENABLE_LOGGER = value
+ saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property
- def GITHUB_URL(self):
- return self._GITHUB_URL
+ @json_serializable('IS_CONFIG_WINDOW_COMPACT_MODE')
+ def IS_CONFIG_WINDOW_COMPACT_MODE(self):
+ return self._IS_CONFIG_WINDOW_COMPACT_MODE
- @property
- def BREAK_KEYSYM_LIST(self):
- return self._BREAK_KEYSYM_LIST
-
- @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
+ @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):
- self._VERSION = "1.3.2"
- self._PATH_CONFIG = "./config.json"
+ # 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_TRANSLATOR = translatorEngine[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]
self._CHOICE_MIC_HOST = getDefaultInputDevice()["host"]["name"]
self._CHOICE_MIC_DEVICE = getDefaultInputDevice()["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_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._CHOICE_SPEAKER_DEVICE = getDefaultOutputDevice()["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_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(web)": None,
- "DeepL(auth)": None,
- "Bing(web)": None,
- "Google(web)": None,
+ "DeepL_API": None,
+ "DeepL": None,
+ "Bing": None,
+ "Google": None,
}
self._MESSAGE_FORMAT = "[message]([translation])"
- self._ENABLE_AUTO_CLEAR_CHATBOX = False
+ self._ENABLE_AUTO_CLEAR_MESSAGE_BOX = True
self._ENABLE_NOTICE_XSOVERLAY = False
- self._ENABLE_OSC = False
- self._UPDATE_FLAG = False
- self._GITHUB_URL = "https://api.github.com/repos/misyaguziya/VRCT/releases/latest"
- self._BREAK_KEYSYM_LIST = [
- "Delete", "Select", "Up", "Down", "Next", "End", "Print",
- "Prior","Insert","Home", "Left", "Clear", "Right", "Linefeed"
- ]
- self._MAX_MIC_ENERGY_THRESHOLD = 2000
- self._MAX_SPEAKER_ENERGY_THRESHOLD = 4000
+ 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') as fp:
+ 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') as fp:
- setter_methods = [
- name for name, obj in vars(type(self)).items()
- if isinstance(obj, property) and obj.fset is not None
- ]
+ with open(self.PATH_CONFIG, 'w', encoding="utf-8") as fp:
config = {}
- for method in setter_methods:
- config[method] = getattr(self, method)
- json_dump(config, fp, indent=4)
+ 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 bac2eb0f..4c7b8c94 100644
--- a/languages.py
+++ b/languages.py
@@ -1,6 +1,6 @@
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
index b61ee831..51a881dd 100644
--- a/model.py
+++ b/model.py
@@ -1,22 +1,34 @@
+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, getOutputDevices, getDefaultInputDevice, getDefaultOutputDevice
+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, daemon=True, *args, **kwargs):
+ 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()
@@ -25,10 +37,21 @@ class threadFnc(Thread):
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):
@@ -38,9 +61,14 @@ class Model:
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_energy_get_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()
@@ -50,7 +78,7 @@ class Model:
self.translator = Translator()
def resetKeywordProcessor(self):
- del self.translator
+ del self.keyword_processor
self.keyword_processor = KeywordProcessor()
def authenticationTranslator(self, choice_translator=None, auth_key=None):
@@ -60,37 +88,117 @@ class Model:
auth_key = config.AUTH_KEYS[choice_translator]
result = self.translator.authentication(choice_translator, auth_key)
- if result:
- auth_keys = config.AUTH_KEYS
- auth_keys[choice_translator] = auth_key
- config.AUTH_KEYS = auth_keys
return result
- def getTranslatorStatus(self):
- return self.translator.translator_status[config.CHOICE_TRANSLATOR]
+ 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 getListTranslatorName(self):
- return list(self.translator.translator_status.keys())
+ 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=config.CHOICE_TRANSLATOR,
- source_language=config.INPUT_SOURCE_LANG,
- target_language=config.INPUT_TARGET_LANG,
+ translator_name=translator_name,
+ source_language=source_language,
+ target_language=target_language,
message=message
)
- message = config.MESSAGE_FORMAT.replace("[message]", message).replace("[translation]", translation)
- return 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=config.CHOICE_TRANSLATOR,
- source_language=config.OUTPUT_SOURCE_LANG,
- target_language=config.OUTPUT_TARGET_LANG,
+ translator_name=translator_name,
+ source_language=source_language,
+ target_language=target_language,
message=message
)
- message = config.MESSAGE_FORMAT.replace("[message]", message).replace("[translation]", translation)
- return message
+ return translation
def addKeywords(self):
for f in config.INPUT_MIC_WORD_FILTER:
@@ -111,27 +219,85 @@ class Model:
def oscSendMessage(message):
sendMessage(message, config.OSC_IP_ADDRESS, config.OSC_PORT)
- @staticmethod
- def checkOSCStarted():
+ def checkOSCStarted(self, fnc):
+ self.is_valid_osc = False
def checkOscReceive(address, osc_arguments):
- if config.ENABLE_OSC is False:
- config.ENABLE_OSC = True
+ 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=receiveOscParameters, args=(checkOscReceive,))
+ th_receive_osc_parameters = Thread(target=oscListener)
th_receive_osc_parameters.daemon = True
th_receive_osc_parameters.start()
# check osc started
- sendTestAction()
+ 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)
- tag_name = response.json()["tag_name"]
- if tag_name != config.VERSION:
- config.UPDATE_FLAG = True
+ 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():
@@ -146,35 +312,44 @@ class Model:
return [device["name"] for device in getInputDevices()[config.CHOICE_MIC_HOST]][0]
@staticmethod
- def getListOutputDevice():
- return [device["name"] for device in getOutputDevices()]
+ def getOutputDefaultDevice():
+ return getDefaultOutputDevice()["name"]
- @staticmethod
- def checkSpeakerStatus(choice=config.CHOICE_SPEAKER_DEVICE):
- speaker_device = [device for device in getOutputDevices() if device["name"] == choice][0]
- if getDefaultOutputDevice()["index"] == speaker_device["index"]:
- return True
- return False
+ 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
- def startMicTranscript(self, fnc):
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 for device in getInputDevices()[config.CHOICE_MIC_HOST] if device["name"] == config.CHOICE_MIC_DEVICE][0],
- config.INPUT_MIC_ENERGY_THRESHOLD,
- config.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD,
- config.INPUT_MIC_RECORD_TIMEOUT,
+ 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=config.INPUT_MIC_PHRASE_TIMEOUT,
+ phrase_timeout=phase_timeout,
max_phrases=config.INPUT_MIC_MAX_PHRASES,
)
def sendMicTranscript():
- mic_transcriber.transcribeAudioQueue(mic_audio_queue, config.INPUT_MIC_VOICE_LANGUAGE)
+ mic_transcriber.transcribeAudioQueue(mic_audio_queue, config.SOURCE_LANGUAGE, config.SOURCE_COUNTRY)
message = mic_transcriber.getTranscript()
- fnc(message)
+ try:
+ fnc(message)
+ except:
+ pass
self.mic_print_transcript = threadFnc(sendMicTranscript)
self.mic_print_transcript.daemon = True
@@ -183,89 +358,124 @@ class Model:
def stopMicTranscript(self):
if isinstance(self.mic_print_transcript, threadFnc):
self.mic_print_transcript.stop()
- if self.mic_audio_recorder.stop != None:
+ self.mic_print_transcript = None
+ if isinstance(self.mic_audio_recorder, SelectedMicRecorder):
self.mic_audio_recorder.stop()
- self.mic_audio_recorder.stop = None
+ 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 startCheckMicEnergy(self, fnc):
def sendMicEnergy():
if mic_energy_queue.empty() is False:
energy = mic_energy_queue.get()
- fnc(energy)
+ 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)
+ 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 self.mic_energy_recorder != None:
- self.mic_energy_recorder.stop()
- if self.mic_energy_plot_progressbar != None:
+ 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):
- spk_audio_queue = Queue()
- spk_device = [device for device in getOutputDevices() if device["name"] == config.CHOICE_SPEAKER_DEVICE][0]
- self.spk_audio_recorder = SelectedSpeakerRecorder(
- spk_device,
- config.INPUT_SPEAKER_ENERGY_THRESHOLD,
- config.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD,
- config.INPUT_SPEAKER_RECORD_TIMEOUT,
+ 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.spk_audio_recorder.recordIntoQueue(spk_audio_queue)
- spk_transcriber = AudioTranscriber(
+ self.speaker_audio_recorder.recordIntoQueue(speaker_audio_queue)
+ speaker_transcriber = AudioTranscriber(
speaker=True,
- source=self.spk_audio_recorder.source,
- phrase_timeout=config.INPUT_SPEAKER_PHRASE_TIMEOUT,
+ source=self.speaker_audio_recorder.source,
+ phrase_timeout=phase_timeout,
max_phrases=config.INPUT_SPEAKER_MAX_PHRASES,
)
- def sendSpkTranscript():
- spk_transcriber.transcribeAudioQueue(spk_audio_queue, config.INPUT_SPEAKER_VOICE_LANGUAGE)
- message = spk_transcriber.getTranscript()
- fnc(message)
+ def sendSpeakerTranscript():
+ speaker_transcriber.transcribeAudioQueue(speaker_audio_queue, config.TARGET_LANGUAGE, config.TARGET_COUNTRY)
+ message = speaker_transcriber.getTranscript()
+ try:
+ fnc(message)
+ except:
+ pass
- self.spk_print_transcript = threadFnc(sendSpkTranscript)
- self.spk_print_transcript.daemon = True
- self.spk_print_transcript.start()
+ self.speaker_print_transcript = threadFnc(sendSpeakerTranscript)
+ self.speaker_print_transcript.daemon = True
+ self.speaker_print_transcript.start()
def stopSpeakerTranscript(self):
- if isinstance(self.spk_print_transcript, threadFnc):
- self.spk_print_transcript.stop()
- if self.spk_audio_recorder.stop != None:
- self.spk_audio_recorder.stop()
- self.spk_audio_recorder.stop = None
+ 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 startCheckSpeakerEnergy(self, fnc):
def sendSpeakerEnergy():
if speaker_energy_queue.empty() is False:
energy = speaker_energy_queue.get()
- fnc(energy)
+ try:
+ fnc(energy)
+ except:
+ pass
sleep(0.01)
- def getSpeakerEnergy():
- with self.speaker_energy_recorder.source as source:
- energy = self.speaker_energy_recorder.recorder.listen_energy(source)
- speaker_energy_queue.put(energy)
-
- speaker_device = [device for device in getOutputDevices() if device["name"] == config.CHOICE_SPEAKER_DEVICE][0]
speaker_energy_queue = Queue()
self.speaker_energy_recorder = SelectedSpeakeEnergyRecorder(speaker_device)
- self.speaker_energy_get_progressbar = threadFnc(getSpeakerEnergy)
- self.speaker_energy_get_progressbar.daemon = True
- self.speaker_energy_get_progressbar.start()
- self.speaker_energy_plot_progressbar = threadFnc(sendSpeakerEnergy)
+ 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 self.speaker_energy_get_progressbar != None:
- self.speaker_energy_get_progressbar.stop()
- if self.speaker_energy_plot_progressbar != None:
+ 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}")
diff --git a/models/osc/osc_tools.py b/models/osc/osc_tools.py
index b44e17cb..7cb926c2 100644
--- a/models/osc/osc_tools.py
+++ b/models/osc/osc_tools.py
@@ -30,8 +30,28 @@ def sendTestAction(ip_address="127.0.0.1", port=9000):
sleep(0.01)
client.send_message("/input/Vertical", False)
+# 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
index 2cf6ebd3..26f2c3f6 100644
--- a/models/transcription/transcription_languages.py
+++ b/models/transcription/transcription_languages.py
@@ -1,91 +1,177 @@
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"
+ "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/models/transcription/transcription_recorder.py b/models/transcription/transcription_recorder.py
index cf2cec04..9abe5eb4 100644
--- a/models/transcription/transcription_recorder.py
+++ b/models/transcription/transcription_recorder.py
@@ -84,7 +84,6 @@ 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)
diff --git a/models/transcription/transcription_transcriber.py b/models/transcription/transcription_transcriber.py
index e0b0eb6f..b058f4ec 100644
--- a/models/transcription/transcription_transcriber.py
+++ b/models/transcription/transcription_transcriber.py
@@ -27,7 +27,7 @@ class AudioTranscriber:
"process_data_func": self.processSpeakerData if speaker else self.processSpeakerData
}
- def transcribeAudioQueue(self, audio_queue, language):
+ def transcribeAudioQueue(self, audio_queue, language, country):
# while True:
audio, time_spoken = audio_queue.get()
self.updateLastSampleAndPhraseStatus(audio, time_spoken)
@@ -37,7 +37,7 @@ class AudioTranscriber:
# 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=transcription_lang[language])
+ text = self.audio_recognizer.recognize_google(audio_data, language=transcription_lang[language][country])
except Exception as e:
pass
finally:
diff --git a/models/transcription/transcription_utils.py b/models/transcription/transcription_utils.py
index 4c72a8fa..f40defeb 100644
--- a/models/transcription/transcription_utils.py
+++ b/models/transcription/transcription_utils.py
@@ -12,15 +12,8 @@ def getInputDevices():
devices[host["name"]].append(device)
else:
devices[host["name"]] = [device]
- return devices
-
-def getOutputDevices():
- 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)
+ if len(devices) == 0:
+ devices = {"NoHost": [{"name": "NoDevice"}]}
return devices
def getDefaultInputDevice():
@@ -33,7 +26,8 @@ def getDefaultInputDevice():
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 getDefaultOutputDevice():
with PyAudio() as p:
@@ -49,4 +43,5 @@ def getDefaultOutputDevice():
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
index 1b68bf81..ae57d4cc 100644
--- a/models/translation/translation_languages.py
+++ b/models/translation/translation_languages.py
@@ -1,6 +1,6 @@
-translatorEngine = ["DeepL(web)", "DeepL(auth)", "Google(web)", "Bing(web)"]
+translatorEngine = ["DeepL", "DeepL_API", "Google", "Bing"]
translation_lang = {}
-dict_deepl_web_languages = {
+dict_deepl_languages = {
"Japanese":"JA",
"English":"EN",
"Korean":"KO",
@@ -31,12 +31,12 @@ dict_deepl_web_languages = {
"Turkish":"TR",
"Norwegian":"NB",
}
-translation_lang["DeepL(web)"] = {
- "source":dict_deepl_web_languages,
- "target":dict_deepl_web_languages,
+translation_lang["DeepL"] = {
+ "source":dict_deepl_languages,
+ "target":dict_deepl_languages,
}
-dict_deepl_auth_source_languages = {
+dict_deepl_api_source_languages = {
"Japanese":"ja",
"English":"en",
"Bulgarian":"bg",
@@ -67,7 +67,7 @@ dict_deepl_auth_source_languages = {
"Ukrainian":"uk",
"Chinese":"zh"
}
-dict_deepl_auth_target_languages = {
+dict_deepl_api_target_languages = {
"Japanese":"ja",
"English American":"en-US",
"English British":"en-GB",
@@ -101,12 +101,12 @@ dict_deepl_auth_target_languages = {
"Ukrainian":"uk",
"Chinese":"zh"
}
-translation_lang["DeepL(auth)"] = {
- "source": dict_deepl_auth_source_languages,
- "target": dict_deepl_auth_target_languages,
+translation_lang["DeepL_API"] = {
+ "source": dict_deepl_api_source_languages,
+ "target": dict_deepl_api_target_languages,
}
-dict_google_web_languages = {
+dict_google_languages = {
"Japanese":"ja",
"English":"en",
"Chinese":"zh",
@@ -170,12 +170,12 @@ dict_google_web_languages = {
"Basque":"eu",
"Irish":"ga"
}
-translation_lang["Google(web)"] = {
- "source":dict_google_web_languages,
- "target":dict_google_web_languages,
+translation_lang["Google"] = {
+ "source":dict_google_languages,
+ "target":dict_google_languages,
}
-dict_bing_web_languages = {
+dict_bing_languages = {
"Japanese":"ja",
"English":"en",
"Chinese":"zh",
@@ -237,7 +237,7 @@ dict_bing_web_languages = {
"Punjabi":"pa",
"Irish":"ga"
}
-translation_lang["Bing(web)"] = {
- "source":dict_bing_web_languages,
- "target":dict_bing_web_languages,
+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
index b386835e..18d2c394 100644
--- a/models/translation/translation_translator.py
+++ b/models/translation/translation_translator.py
@@ -6,63 +6,55 @@ from .translation_languages import translatorEngine, translation_lang
# Translator
class Translator():
def __init__(self):
+ pass
self.translator_status = {}
- for translator in translatorEngine:
- 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
+ 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):
- result = ""
try:
+ result = ""
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
+ 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/models/xsoverlay/notification.py b/models/xsoverlay/notification.py
index f18adf60..7c179f5a 100644
--- a/models/xsoverlay/notification.py
+++ b/models/xsoverlay/notification.py
@@ -18,6 +18,7 @@
import socket
import json
import base64
+from os import path as os_path
def XSOverlay(
endpoint:tuple=("127.0.0.1", 42069), messageType:int=1, index:int=0, timeout:float=2,
@@ -63,7 +64,7 @@ def xsoverlayForVRCT(content:str="") -> int:
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
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/utils.py b/utils.py
index 2bee5814..6efe6d0a 100644
--- a/utils.py
+++ b/utils.py
@@ -1,39 +1,9 @@
from os import path as os_path
-import yaml
-from datetime import datetime
+from PIL.Image import open as Image_open
-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")
-
-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 getImageFile(file_name):
+ img = Image_open(os_path.join(os_path.dirname(__file__), "img", file_name))
+ return img
def get_key_by_value(dictionary, value):
for key, val in dictionary.items():
@@ -41,66 +11,19 @@ def get_key_by_value(dictionary, 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("