Merge branch 'UI_2.0' into develop

This commit is contained in:
misyaguziya
2023-10-21 01:04:10 +09:00
108 changed files with 9057 additions and 3082 deletions

14
.gitignore vendored
View File

@@ -1,19 +1,7 @@
build/
dist/
config.json
VRC_ChatBox_translator.spec
memo.txt
app/
booth/
pyinstaller/
VRCT.build/
VRCT.dist/
VRCT.onefile-build/
VRCT.exe
VRCT.spec
deepl-translate/
translators/
test/
*.pyc
.vscode/
lib/
logs/

65
README.jp.md Normal file
View File

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

143
README.md
View File

@@ -1,116 +1,65 @@
<div align="center">
![](docs/vrct_logo.png)
[![GitHub release](https://img.shields.io/github/v/release/misyaguziya/VRCT.svg)](https://github.com/misyaguziya/VRCT/releases)
[![Downloads](https://img.shields.io/github/downloads/misyaguziya/VRCT/total)](https://github.com/misyaguziya/VRCT/releases)
[![Licence](https://img.shields.io/github/license/misyaguziya/VRCT)](https://github.com/misyaguziya/VRCT/blob/master/LICENSE)
[![Booth](https://img.shields.io/badge/Store-Booth.pm-red)](https://misyaguziya.booth.pm/items/5155325)
# VRCT (VRChat Chatbox Translator & Transcription)
| **English** | [日本語](./README.jp.md) |
<h3>
VRCT is software that supports VRChat conversations with translation and transcription.
</h3>
![](docs/main_window.png)
<div align="left">
## Overview
VRChatのChatBoxにOSC経由でメッセージを送信するツール
翻訳エンジンを使用してメッセージとその翻訳部分を同時に送信することができる
# Download & Install
Download from anywhere you like.
- [Github.com](https://github.com/misyaguziya/VRCT/releases/)
- [BOOTH.pm](https://misyaguziya.booth.pm/items/5155325)
## Requirement
- python 3.9.13
- pillow
- PyAudioWPatch
- python-osc
- customtkinter
- deepl
- deepl-translate(https://github.com/misyaguziya/deepl-translate)
- translators(https://github.com/misyaguziya/translators)
- custom_speech_recognition(https://github.com/misyaguziya/custom_speech_recognition)
Just download and run the exe.
**deepl-translate/translators/custom_speech_recognitionについては追加実装をしています**
**`pip install`でinstallした場合、動かないので注意**
# What is VRCT?
VRCT is software that supports conversations between people who speak different languages by providing chat or voice translation.
These features are designed for use within VRChat.
*Although not supported, it is also used for other purposes such as watching movies.
## install
```bash
./install.bat
```
VRCT supports your conversations with
- 💬 **Send chat to VRChat**
- 🌐 **Translation**
- 🎙 **Transcription of audio from microphone**
- 🔈 **Transcription of audio from Speaker**
## Usage
```bash
python VRCT.py
```
# Documents
Initial setup, basic functions, and other features are also described.
- [Documents Link](https://mzsoftware.notion.site/VRCT-Documents-be79b7a165f64442ad8f326d86c22246?pvs=4)
## Features
# How to Use (YouTube)
<div align="center">
### init
0. VRChatのOSCを有効にする重要
[![](https://img.youtube.com/vi/rUTad037n8Q/0.jpg)](https://www.youtube.com/watch?v=rUTad037n8Q)
(任意)
1. DeepLのAPIを使用するためにアカウント登録し、認証キーを取得する
2. ギアアイコンのボタンでconfigウィンドウを開く
3. ParameterタブのDeepL Auth Keyに認証キーを記載
4. configウィンドウを閉じる
<div align="left">
### Normal use
1. メッセージボックスにメッセージを記入
2. Enterキーを押し、メッセージを送信する
### About Checkboxes
- translation: 翻訳の有効無効
- voice2chatbox: マイクの音声を文字起こししてチャットボックスに送信する
- speaker2log: スピーカーの音声から文字起こししてログに表示する
- foreground: 最前面表示の有効無効
### About Textbox
- log tab: すべてのログを表示
- send tab: 送信したメッセージを表示
- receive tab: 受信したメッセージを表示
- system tab: 機能についてのメッセージを表示
### About Config Window
- UI tab
- Transparency: ウィンドウの透過度の調整
- Appearance Theme: ウィンドウテーマを選択
- UI Scaling: UIサイズを調整
- Font Family: 表示フォントを選択
- UI Language: UIの表示言語を選択
- Translation tab
- Select Translator: 翻訳エンジンの変更
- Send Language: 送信するメッセージに対して翻訳する言語[source, target]を選択
- Receive Language: 受信したメッセージに対して翻訳する言語[source, target]を選択
- Transcription tab
- Input Mic Host: マイクのホストAPIを選択
- Input Mic Device: マイクを選択
- Input Mic Voice Language: 入力する音声の言語
- Input Mic Energy Threshold: 音声取得のしきい値
- Check threshold point: Input Mic Energy Thresholdのしきい値を視覚化
- Input Mic Dynamic Energy Threshold: 音声取得のしきい値の自動調整
- Input Mic Phase Timeout: 文字起こしする音声時間の上限
- Input Mic Record Timeout: 音声の区切りの無音時間
- Input Mic Max Phrases: 保留する単語の上限
- Input Mic Word Filter: MICの文字起こし時にWord Filterで設定した文字が入っていた場合にChatboxに表示しない (ex AAA,BBB,CCC)
- Input Speaker Device: スピーカーを選択
- Input Speaker Voice Language: 受信する音声の言語
- Input Speaker Energy Threshold: 音声取得のしきい値
- Check threshold point: Input Speaker Energy Thresholdのしきい値を視覚化
- Input Speaker Dynamic Energy Threshold: 音声取得のしきい値の自動調整
- Input Speaker Record Timeout: 音声の区切りの無音時間
- Input Speaker Phase Timeout: 文字起こしする音声時間の上限
- Input Speaker Max Phrases: 保留する単語の上限
- Parameter tab
- OSC IP address: 変更不要
- OSC port: 変更不要
- DeepL Auth key: DeepLの認証キーの設定
- Message Format: 送信するメッセージのデコレーションの設定
- [message]がメッセージボックスに記入したメッセージに置換される
- [translation]が翻訳されたメッセージに置換される
- 初期フォーマット:`[message]([translation])`
- Others tab
- Auto clear chat box: メッセージ送信後に書き込んだメッセージを空にする
- **(New!) Notification XSOverlay: XSOverlayの通知機能を有効(VR only)**
# If you want to run it in python
1. Install the following version of python.
`python version 3.11.5`
2. Install package and run main.py.
```bash
./install.bat
python main.py
```
## Author
みしゃ(misyaguzi)
- Main開発
- twitter: https://twitter.com/misya_ai
- booth: https://misyaguziya.booth.pm/items/4814313
- [みしゃ(misyaguzi)](https://github.com/misyaguziya) (Main Development)
- [しいな(Shiina_12siy)](https://twitter.com/Shiina_12siy) (UI/UX, UI multilingual support)
- [レラ](https://github.com/soumt-r) (Translation:Korean)
- [どね]() (Logo Design)
しいな(Shiina_12siy)
- Main開発, 翻訳(英語)
---
レラ
- 翻訳(韓国語)
VRCT is not endorsed by VRChat and does not reflect the views or opinions of VRChat or anyone officially involved in producing or managing VRChat properties. VRChat and all associated properties are trademarks or registered trademarks of VRChat Inc. VRChat © VRChat Inc.

View File

@@ -4,129 +4,14 @@
# 概要
VRChatで使用されるChatBoxをOSC経由でメッセージを送信するツールになります。
翻訳エンジンを使用してメッセージとその翻訳部分を同時に送信することができます。
(翻訳エンジンはDeepL,Google,Bingに対応)
# 使用方法
初期設定時
0. VRChatのOSCを有効にする重要
(任意)
1. DeepLのAPIを使用するためにアカウント登録し、認証キーを取得する
2. ギアアイコンのボタンでconfigウィンドウを開く
3. ParameterタブのDeepL Auth Keyに認証キーを記載
4. configウィンドウを閉じる
通常使用時
1. メッセージボックスにメッセージを記入
2. Enterキーを押し、メッセージを送信する
# その他の設定
translation チェックボックス: 翻訳の有効無効
voice2chatbox チェックボックス : マイクの音声を文字起こししてチャットボックスに送信する
speaker2log チェックボックス : スピーカーの音声から文字起こししてログに表示する
foreground チェックボックス: 最前面表示の有効無効
テキストボックス
logタブ
すべてのログを表示
sendタブ
送信したメッセージを表示
receiveタブ
受信したメッセージを表示
systemタブ
機能についてのメッセージを表示
configウィンドウ
UIタブ
Transparency: ウィンドウの透過度の調整
Appearance Theme: ウィンドウテーマを選択
UI Scaling: UIサイズを調整
Font Family: 表示フォントを選択
UI Language: UIの表示言語を選択
Translationタブ
Select Translator: 翻訳エンジンの変更
Send Language: 送信するメッセージに対して翻訳する言語[source, target]を選択
Receive Language: 受信したメッセージに対して翻訳する言語[source, target]を選択
Transcriptionタブ
Input Mic Host: マイクのホストAPIを選択
Input Mic Device: マイクを選択
Input Mic Voice Language: 入力する音声の言語
Input Mic Energy Threshold: 音声取得のしきい値
Check threshold point: Input Mic Energy Thresholdのしきい値を視覚化
Input Mic Dynamic Energy Threshold: 音声取得のしきい値の自動調整
Input Mic Record Timeout: 音声の区切りの無音時間
Input Mic Phase Timeout: 文字起こしする音声時間の上限
Input Mic Max Phrases: 保留する単語の上限
Input Mic Word Filter: MICの文字起こし時にWord Filterで設定した文字が入っていた場合にChatboxに表示しない (ex AAA,BBB,CCC)
Input Speaker Device: スピーカーを選択
Input Speaker Voice Language: 受信する音声の言語
Input Speaker Energy Threshold: 音声取得のしきい値
Check threshold point: Input Speaker Energy Thresholdのしきい値を視覚化
Input Speaker Dynamic Energy Threshold: 音声取得のしきい値の自動調整
Input Speaker Record Timeout: 音声の区切りの無音時間
Input Speaker Phase Timeout: 文字起こしする音声時間の上限
Input Speaker Max Phrases: 保留する単語の上限
Parameterタブ
OSC IP address: 変更不要
OSC port: 変更不要
DeepL Auth key: DeepLの認証キーの設定
Message Format: 送信するメッセージのデコレーションの設定
[message]がメッセージボックスに記入したメッセージに置換される
[translation]が翻訳されたメッセージに置換される
初期フォーマット:"[message]([translation])"
Othersタブ
Auto clear chat box: メッセージ送信後に書き込んだメッセージを空にする
(New!) Notification XSOverlay: XSOverlayの通知機能を有効(VR only)
設定の初期化
config.jsonを削除
ドキュメント(JP):https://mzsoftware.notion.site/VRCT-Documents-be79b7a165f64442ad8f326d86c22246?pvs=4
# お問い合わせ
要望などはTwitterまで
https://twitter.com/misya_ai
Googleフォーム:https://t.co/lSlo4brZwm
Twitter:https://twitter.com/misya_ai
Github:https://github.com/misyaguziya/VRCT
# アップデート履歴
[2023-05-29: v0.1b] v0.1b リリース
[2023-05-30: v0.2b]
- configボタンをギアアイコンに変更
- 詳細情報のボタンを追加
- 翻訳機能有効無効のチェックボックスを追加
- 最前面表示の有効無効のチェックボックスを追加
- いくつかのバグを修正
[2023-06-03: v0.3b]
- 全体的にUIを刷新
- 透過機能を追加
- テーマのLight/Dark/Systemのモードの変更機能を追加
- UIのスケール変更機能を追加
- フォントの変更機能を追加
[2023-06-06: v0.4b]
- 翻訳エンジンを追加
- 入力と出力の翻訳言語を選択できるように変更
[2023-06-20: v1.0]
- マイクからの音声の文字起こし機能を追加
- スピーカーからの音声の文字起こし機能を追加
[2023-06-28: v1.1]
- いくつかのバクを修正
- 翻訳/文字起こし言語の表記を略称からわかりやすい文字に変更
- 文字起こしの処理の軽量化
[2023-07-05: v1.2]
- 文字起こし精度の向上
[2023-07-21: v1.3]
- UIの表示言語を日本語/英語を選択できる機能を追加
- Energy Thresholdの視覚化機能を追加
- 文字起こしの誤認識対策のため、Word Filterを追加
- WASAPI以外のHostAPIでもマイクとして使用できるようにHostAPIを選択できる機能を追加
- メッセージ送信後に書き込んだメッセージを空にするか選択できる機能を追加
- バグ対策のため、translation/voice2chatbox/speaker2log/foregroundは起動時はOFFになるように変更
- バグ対策のため、スピーカーについて既定デバイス以外を選択した時にERRORが出るように変更
- 半角入力時に一部の文字が書き込めないバグを修正
[2023-07-22: v1.3.1]
- UIの表示言語選択に韓国語を追加
[2023-07-30: v1.3.2]
- 試験的にXSOverlayへの通知機能を追加
- checkbox ONの状態でもConfigを開けるように変更
- 文字起こし言語の表示を修正
- いくつかのバグを修正
# 注意事項
再配布とかはやめてね
[2023-10-21: 2.0.0] v2.0.0 リリース

499
VRCT.py
View File

@@ -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("<Return>", self.entry_message_box_press_key_enter)
self.entry_message_box.bind("<Any-KeyPress>", self.entry_message_box_press_key_any)
self.entry_message_box.bind("<Leave>", self.entry_message_box_leave)
# 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)

4
batch/restart.bat Normal file
View File

@@ -0,0 +1,4 @@
@if not "%~0"=="%~dp0.\%~nx0" start /min cmd /c,"%~dp0.\%~nx0" %* & goto :eof
taskkill /im %1 /F
START "" %1

11
batch/update.bat Normal file
View File

@@ -0,0 +1,11 @@
@if not "%~0"=="%~dp0.\%~nx0" start /min cmd /c,"%~dp0.\%~nx0" %* & goto :eof
taskkill /im %1 /F
ping -n 2 127.0.0.1 > nul
del /f %1
ping -n 2 127.0.0.1 > nul
rename %2 %1
ping -n 2 127.0.0.1 > nul
if %3 == True (
START "" %1
)

1
build.bat Normal file
View File

@@ -0,0 +1 @@
pyinstaller --onedir --onefile --windowed --clean --icon="./img/vrct_logo_mark_black.ico" --add-data "./img;img/" --add-data "./locales;locales/" --add-data "./batch;batch/" --name VRCT --exclude-module numpy --exclude-module pandas --exclude-module matplotlib --exclude-module PyQt5 main.py

395
config.py
View File

@@ -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()

739
controller.py Normal file
View File

@@ -0,0 +1,739 @@
from time import sleep
from threading import Thread
from config import config
from model import model
from view import view
from utils import get_key_by_value
from languages import selectable_languages
# Common
def callbackUpdateSoftware():
model.updateSoftware()
def callbackRestartSoftware():
model.reStartSoftware()
# func transcription send message
def sendMicMessage(message):
if len(message) > 0:
translation = ""
if model.checkKeywords(message):
view.printToTextbox_DetectedByWordFilter(detected_message=message)
return
elif config.ENABLE_TRANSLATION is False:
pass
else:
translation = model.getInputTranslate(message)
if translation == False:
config.ENABLE_TRANSLATION = False
translation = ""
view.translationEngineLimitErrorProcess()
if config.ENABLE_TRANSCRIPTION_SEND is True:
if config.ENABLE_SEND_MESSAGE_TO_VRC is True:
if len(translation) > 0:
osc_message = config.MESSAGE_FORMAT.replace("[message]", message)
osc_message = osc_message.replace("[translation]", translation)
else:
osc_message = message
model.oscSendMessage(osc_message)
view.printToTextbox_SentMessage(message, translation)
if config.ENABLE_LOGGER is True:
if len(translation) > 0:
translation = f" ({translation})"
model.logger.info(f"[SENT] {message}{translation}")
def startTranscriptionSendMessage():
model.startMicTranscript(sendMicMessage, view.printToTextbox_TranscriptionSendNoDeviceError)
view.setMainWindowAllWidgetsStatusToNormal()
def stopTranscriptionSendMessage():
model.stopMicTranscript()
view.setMainWindowAllWidgetsStatusToNormal()
def startThreadingTranscriptionSendMessage():
view.printToTextbox_enableTranscriptionSend()
th_startTranscriptionSendMessage = Thread(target=startTranscriptionSendMessage)
th_startTranscriptionSendMessage.daemon = True
th_startTranscriptionSendMessage.start()
def stopThreadingTranscriptionSendMessage():
view.printToTextbox_disableTranscriptionSend()
th_stopTranscriptionSendMessage = Thread(target=stopTranscriptionSendMessage)
th_stopTranscriptionSendMessage.daemon = True
th_stopTranscriptionSendMessage.start()
def startTranscriptionSendMessageOnCloseConfigWindow():
model.startMicTranscript(sendMicMessage, view.printToTextbox_TranscriptionSendNoDeviceError)
def stopTranscriptionSendMessageOnOpenConfigWindow():
model.stopMicTranscript()
def startThreadingTranscriptionSendMessageOnCloseConfigWindow():
th_startTranscriptionSendMessage = Thread(target=startTranscriptionSendMessageOnCloseConfigWindow)
th_startTranscriptionSendMessage.daemon = True
th_startTranscriptionSendMessage.start()
def stopThreadingTranscriptionSendMessageOnOpenConfigWindow():
th_stopTranscriptionSendMessage = Thread(target=stopTranscriptionSendMessageOnOpenConfigWindow)
th_stopTranscriptionSendMessage.daemon = True
th_stopTranscriptionSendMessage.start()
# func transcription receive message
def receiveSpeakerMessage(message):
if len(message) > 0:
translation = ""
if config.ENABLE_TRANSLATION is False:
pass
else:
translation = model.getOutputTranslate(message)
if translation == False:
config.ENABLE_TRANSLATION = False
translation = ""
view.translationEngineLimitErrorProcess()
if config.ENABLE_TRANSCRIPTION_RECEIVE is True:
if config.ENABLE_NOTICE_XSOVERLAY is True:
if len(translation) > 0:
xsoverlay_message = config.MESSAGE_FORMAT.replace("[message]", message)
xsoverlay_message = xsoverlay_message.replace("[translation]", translation)
else:
xsoverlay_message = message
model.notificationXSOverlay(xsoverlay_message)
# update textbox message log (Received)
view.printToTextbox_ReceivedMessage(message, translation)
if config.ENABLE_LOGGER is True:
if len(translation) > 0:
translation = f" ({translation})"
model.logger.info(f"[RECEIVED] {message}{translation}")
def startTranscriptionReceiveMessage():
model.startSpeakerTranscript(receiveSpeakerMessage, view.printToTextbox_TranscriptionReceiveNoDeviceError)
view.setMainWindowAllWidgetsStatusToNormal()
def stopTranscriptionReceiveMessage():
model.stopSpeakerTranscript()
view.setMainWindowAllWidgetsStatusToNormal()
def startThreadingTranscriptionReceiveMessage():
view.printToTextbox_enableTranscriptionReceive()
th_startTranscriptionReceiveMessage = Thread(target=startTranscriptionReceiveMessage)
th_startTranscriptionReceiveMessage.daemon = True
th_startTranscriptionReceiveMessage.start()
def stopThreadingTranscriptionReceiveMessage():
view.printToTextbox_disableTranscriptionReceive()
th_stopTranscriptionReceiveMessage = Thread(target=stopTranscriptionReceiveMessage)
th_stopTranscriptionReceiveMessage.daemon = True
th_stopTranscriptionReceiveMessage.start()
def startTranscriptionReceiveMessageOnCloseConfigWindow():
model.startSpeakerTranscript(receiveSpeakerMessage, view.printToTextbox_TranscriptionReceiveNoDeviceError)
def stopTranscriptionReceiveMessageOnOpenConfigWindow():
model.stopSpeakerTranscript()
def startThreadingTranscriptionReceiveMessageOnCloseConfigWindow():
th_startTranscriptionReceiveMessage = Thread(target=startTranscriptionReceiveMessageOnCloseConfigWindow)
th_startTranscriptionReceiveMessage.daemon = True
th_startTranscriptionReceiveMessage.start()
def stopThreadingTranscriptionReceiveMessageOnOpenConfigWindow():
th_stopTranscriptionReceiveMessage = Thread(target=stopTranscriptionReceiveMessageOnOpenConfigWindow)
th_stopTranscriptionReceiveMessage.daemon = True
th_stopTranscriptionReceiveMessage.start()
# func message box
def sendChatMessage(message):
if len(message) > 0:
translation = ""
if config.ENABLE_TRANSLATION is False:
pass
else:
translation = model.getInputTranslate(message)
if translation == False:
config.ENABLE_TRANSLATION = False
translation = ""
view.translationEngineLimitErrorProcess()
# send OSC message
if config.ENABLE_SEND_MESSAGE_TO_VRC is True:
if len(translation) > 0:
osc_message = config.MESSAGE_FORMAT.replace("[message]", message)
osc_message = osc_message.replace("[translation]", translation)
else:
osc_message = message
model.oscSendMessage(osc_message)
# update textbox message log (Sent)
view.printToTextbox_SentMessage(message, translation)
if config.ENABLE_LOGGER is True:
if len(translation) > 0:
translation = f" ({translation})"
model.logger.info(f"[SENT] {message}{translation}")
# delete message in entry message box
if config.ENABLE_AUTO_CLEAR_MESSAGE_BOX is True:
view.clearMessageBox()
def messageBoxPressKeyEnter(e):
model.oscStopSendTyping()
message = view.getTextFromMessageBox()
sendChatMessage(message)
def messageBoxPressKeyAny(e):
if config.ENABLE_SEND_MESSAGE_TO_VRC is True:
model.oscStartSendTyping()
else:
model.oscStopSendTyping()
def messageBoxFocusIn(e):
view.foregroundOffIfForegroundEnabled()
def messageBoxFocusOut(e):
view.foregroundOnIfForegroundEnabled()
if config.ENABLE_SEND_MESSAGE_TO_VRC is True:
model.oscStopSendTyping()
# func select languages
def initSetLanguageAndCountry():
select = config.SELECTED_TAB_YOUR_LANGUAGES[config.SELECTED_TAB_NO]
language, country = model.getLanguageAndCountry(select)
config.SOURCE_LANGUAGE = language
config.SOURCE_COUNTRY = country
select = config.SELECTED_TAB_TARGET_LANGUAGES[config.SELECTED_TAB_NO]
language, country = model.getLanguageAndCountry(select)
config.TARGET_LANGUAGE = language
config.TARGET_COUNTRY = country
config.CHOICE_TRANSLATOR = model.findTranslationEngine(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE)
def setYourLanguageAndCountry(select):
languages = config.SELECTED_TAB_YOUR_LANGUAGES
languages[config.SELECTED_TAB_NO] = select
config.SELECTED_TAB_YOUR_LANGUAGES = languages
language, country = model.getLanguageAndCountry(select)
config.SOURCE_LANGUAGE = language
config.SOURCE_COUNTRY = country
config.CHOICE_TRANSLATOR = model.findTranslationEngine(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE)
view.printToTextbox_selectedYourLanguages(select)
def setTargetLanguageAndCountry(select):
languages = config.SELECTED_TAB_TARGET_LANGUAGES
languages[config.SELECTED_TAB_NO] = select
config.SELECTED_TAB_TARGET_LANGUAGES = languages
language, country = model.getLanguageAndCountry(select)
config.TARGET_LANGUAGE = language
config.TARGET_COUNTRY = country
config.CHOICE_TRANSLATOR = model.findTranslationEngine(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE)
view.printToTextbox_selectedTargetLanguages(select)
def callbackSelectedLanguagePresetTab(selected_tab_no):
config.SELECTED_TAB_NO = selected_tab_no
view.updateGuiVariableByPresetTabNo(config.SELECTED_TAB_NO)
languages = config.SELECTED_TAB_YOUR_LANGUAGES
select = languages[config.SELECTED_TAB_NO]
language, country = model.getLanguageAndCountry(select)
config.SOURCE_LANGUAGE = language
config.SOURCE_COUNTRY = country
languages = config.SELECTED_TAB_TARGET_LANGUAGES
select = languages[config.SELECTED_TAB_NO]
language, country = model.getLanguageAndCountry(select)
config.TARGET_LANGUAGE = language
config.TARGET_COUNTRY = country
config.CHOICE_TRANSLATOR = model.findTranslationEngine(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE)
view.printToTextbox_changedLanguagePresetTab(config.SELECTED_TAB_NO)
# command func
def callbackToggleTranslation(is_turned_on):
config.ENABLE_TRANSLATION = is_turned_on
if config.ENABLE_TRANSLATION is True:
view.printToTextbox_enableTranslation()
else:
view.printToTextbox_disableTranslation()
def callbackToggleTranscriptionSend(is_turned_on):
view.setMainWindowAllWidgetsStatusToDisabled()
config.ENABLE_TRANSCRIPTION_SEND = is_turned_on
if config.ENABLE_TRANSCRIPTION_SEND is True:
startThreadingTranscriptionSendMessage()
else:
stopThreadingTranscriptionSendMessage()
def callbackToggleTranscriptionReceive(is_turned_on):
view.setMainWindowAllWidgetsStatusToDisabled()
config.ENABLE_TRANSCRIPTION_RECEIVE = is_turned_on
if config.ENABLE_TRANSCRIPTION_RECEIVE is True:
startThreadingTranscriptionReceiveMessage()
else:
stopThreadingTranscriptionReceiveMessage()
def callbackToggleForeground(is_turned_on):
config.ENABLE_FOREGROUND = is_turned_on
if config.ENABLE_FOREGROUND is True:
view.printToTextbox_enableForeground()
view.foregroundOn()
else:
view.printToTextbox_disableForeground()
view.foregroundOff()
def callbackEnableMainWindowSidebarCompactMode():
config.IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE = True
view.enableMainWindowSidebarCompactMode()
def callbackDisableMainWindowSidebarCompactMode():
config.IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE = False
view.disableMainWindowSidebarCompactMode()
# Config Window
def callbackOpenConfigWindow():
view.setMainWindowAllWidgetsStatusToDisabled()
if config.ENABLE_TRANSCRIPTION_SEND is True:
stopThreadingTranscriptionSendMessageOnOpenConfigWindow()
if config.ENABLE_TRANSCRIPTION_RECEIVE is True:
stopThreadingTranscriptionReceiveMessageOnOpenConfigWindow()
if config.ENABLE_FOREGROUND is True:
view.foregroundOff()
def callbackCloseConfigWindow():
model.stopCheckMicEnergy()
model.stopCheckSpeakerEnergy()
view.initMicThresholdCheckButton()
# view.initProgressBar_MicEnergy() # ProgressBarに0をセットしたい
view.initSpeakerThresholdCheckButton()
# view.initProgressBar_SpeakerEnergy() # ProgressBarに0をセットしたい
if config.ENABLE_TRANSCRIPTION_SEND is True:
startThreadingTranscriptionSendMessageOnCloseConfigWindow()
if config.ENABLE_TRANSCRIPTION_RECEIVE is True:
sleep(2)
if config.ENABLE_TRANSCRIPTION_RECEIVE is True:
startThreadingTranscriptionReceiveMessageOnCloseConfigWindow()
if config.ENABLE_FOREGROUND is True:
view.foregroundOn()
view.setMainWindowAllWidgetsStatusToNormal()
# Compact Mode Switch
def callbackEnableConfigWindowCompactMode():
config.IS_CONFIG_WINDOW_COMPACT_MODE = True
model.stopCheckMicEnergy()
view.initMicThresholdCheckButton()
model.stopCheckSpeakerEnergy()
view.initSpeakerThresholdCheckButton()
view.enableConfigWindowCompactMode()
def callbackDisableConfigWindowCompactMode():
config.IS_CONFIG_WINDOW_COMPACT_MODE = False
model.stopCheckMicEnergy()
view.initMicThresholdCheckButton()
model.stopCheckSpeakerEnergy()
view.initSpeakerThresholdCheckButton()
view.disableConfigWindowCompactMode()
# Appearance Tab
def callbackSetTransparency(value):
print("callbackSetTransparency", int(value))
config.TRANSPARENCY = int(value)
view.setMainWindowTransparency(config.TRANSPARENCY/100)
def callbackSetAppearance(value):
print("callbackSetAppearance", value)
config.APPEARANCE_THEME = value
view.showRestartButtonIfRequired()
def callbackSetUiScaling(value):
print("callbackSetUiScaling", value)
config.UI_SCALING = value
new_scaling_float = int(value.replace("%", "")) / 100
print("callbackSetUiScaling_new_scaling_float", new_scaling_float)
view.showRestartButtonIfRequired()
def callbackSetFontFamily(value):
print("callbackSetFontFamily", value)
config.FONT_FAMILY = value
view.showRestartButtonIfRequired()
def callbackSetUiLanguage(value):
print("callbackSetUiLanguage", value)
value = get_key_by_value(selectable_languages, value)
print("callbackSetUiLanguage__after_get_key_by_value", value)
config.UI_LANGUAGE = value
view.showRestartButtonIfRequired(locale=config.UI_LANGUAGE)
# Translation Tab
def callbackSetDeeplAuthkey(value):
print("callbackSetDeeplAuthkey", str(value))
if len(value) == 39:
result = model.authenticationTranslator(choice_translator="DeepL_API", auth_key=value)
if result is True:
key = value
view.printToTextbox_AuthenticationSuccess()
else:
key = None
view.printToTextbox_AuthenticationError()
auth_keys = config.AUTH_KEYS
auth_keys["DeepL_API"] = key
config.AUTH_KEYS = auth_keys
config.CHOICE_TRANSLATOR = model.findTranslationEngine(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE)
elif len(value) == 0:
auth_keys = config.AUTH_KEYS
auth_keys["DeepL_API"] = None
config.AUTH_KEYS = auth_keys
config.CHOICE_TRANSLATOR = model.findTranslationEngine(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE)
# Transcription Tab (Mic)
def callbackSetMicHost(value):
print("callbackSetMicHost", value)
config.CHOICE_MIC_HOST = value
config.CHOICE_MIC_DEVICE = model.getInputDefaultDevice()
view.updateSelected_MicDevice(config.CHOICE_MIC_DEVICE)
view.updateList_MicDevice(model.getListInputDevice())
model.stopCheckMicEnergy()
view.replaceMicThresholdCheckButton_Passive()
def callbackSetMicDevice(value):
print("callbackSetMicDevice", value)
config.CHOICE_MIC_DEVICE = value
model.stopCheckMicEnergy()
view.replaceMicThresholdCheckButton_Passive()
def callbackSetMicEnergyThreshold(value):
print("callbackSetMicEnergyThreshold", value)
if value == "": return
try:
value = int(value)
if 0 <= value and value <= config.MAX_MIC_ENERGY_THRESHOLD:
view.clearErrorMessage()
config.INPUT_MIC_ENERGY_THRESHOLD = value
view.setGuiVariable_MicEnergyThreshold(config.INPUT_MIC_ENERGY_THRESHOLD)
else:
raise ValueError()
except:
view.showErrorMessage_MicEnergyThreshold()
def callbackSetMicDynamicEnergyThreshold(value):
print("callbackSetMicDynamicEnergyThreshold", value)
config.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD = value
if config.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD is True:
view.closeMicEnergyThresholdWidget()
else:
view.openMicEnergyThresholdWidget()
def setProgressBarMicEnergy(energy):
view.updateSetProgressBar_MicEnergy(energy)
def callbackCheckMicThreshold(is_turned_on):
print("callbackCheckMicThreshold", is_turned_on)
if is_turned_on is True:
view.replaceMicThresholdCheckButton_Disabled()
model.startCheckMicEnergy(setProgressBarMicEnergy, view.initProgressBar_MicEnergy)
view.replaceMicThresholdCheckButton_Active()
else:
view.replaceMicThresholdCheckButton_Disabled()
model.stopCheckMicEnergy()
view.replaceMicThresholdCheckButton_Passive()
def callbackSetMicRecordTimeout(value):
print("callbackSetMicRecordTimeout", value)
if value == "": return
try:
value = int(value)
if 0 <= value and value <= config.INPUT_MIC_PHRASE_TIMEOUT:
view.clearErrorMessage()
config.INPUT_MIC_RECORD_TIMEOUT = value
view.setGuiVariable_MicRecordTimeout(config.INPUT_MIC_RECORD_TIMEOUT)
else:
raise ValueError()
except:
view.showErrorMessage_MicRecordTimeout()
def callbackSetMicPhraseTimeout(value):
print("callbackSetMicPhraseTimeout", value)
if value == "": return
try:
value = int(value)
if 0 <= value and value >= config.INPUT_MIC_RECORD_TIMEOUT:
view.clearErrorMessage()
config.INPUT_MIC_PHRASE_TIMEOUT = value
view.setGuiVariable_MicPhraseTimeout(config.INPUT_MIC_PHRASE_TIMEOUT)
else:
raise ValueError()
except:
view.showErrorMessage_MicPhraseTimeout()
def callbackSetMicMaxPhrases(value):
print("callbackSetMicMaxPhrases", value)
if value == "": return
try:
value = int(value)
if 0 <= value:
view.clearErrorMessage()
config.INPUT_MIC_MAX_PHRASES = value
view.setGuiVariable_MicMaxPhrases(config.INPUT_MIC_MAX_PHRASES)
else:
raise ValueError()
except:
view.showErrorMessage_MicMaxPhrases()
def callbackSetMicWordFilter(value):
print("callbackSetMicWordFilter", value)
word_filter = str(value)
word_filter = [w.strip() for w in word_filter.split(",") if len(w.strip()) > 0]
word_filter = ",".join(word_filter)
print("callbackSetMicWordFilter_afterSplitting", word_filter)
if len(word_filter) > 0:
config.INPUT_MIC_WORD_FILTER = word_filter.split(",")
else:
config.INPUT_MIC_WORD_FILTER = []
model.resetKeywordProcessor()
model.addKeywords()
def callbackSetSpeakerEnergyThreshold(value):
print("callbackSetSpeakerEnergyThreshold", value)
if value == "": return
try:
value = int(value)
if 0 <= value and value <= config.MAX_SPEAKER_ENERGY_THRESHOLD:
view.clearErrorMessage()
config.INPUT_SPEAKER_ENERGY_THRESHOLD = value
view.setGuiVariable_SpeakerEnergyThreshold(config.INPUT_SPEAKER_ENERGY_THRESHOLD)
else:
raise ValueError()
except:
view.showErrorMessage_SpeakerEnergyThreshold()
def callbackSetSpeakerDynamicEnergyThreshold(value):
print("callbackSetSpeakerDynamicEnergyThreshold", value)
config.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD = value
if config.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD is True:
view.closeSpeakerEnergyThresholdWidget()
else:
view.openSpeakerEnergyThresholdWidget()
def setProgressBarSpeakerEnergy(energy):
view.updateSetProgressBar_SpeakerEnergy(energy)
def callbackCheckSpeakerThreshold(is_turned_on):
print("callbackCheckSpeakerThreshold", is_turned_on)
if is_turned_on is True:
view.replaceSpeakerThresholdCheckButton_Disabled()
model.startCheckSpeakerEnergy(
setProgressBarSpeakerEnergy,
view.initProgressBar_SpeakerEnergy,
view.showErrorMessage_CheckSpeakerThreshold_NoDevice
)
view.replaceSpeakerThresholdCheckButton_Active()
else:
view.replaceSpeakerThresholdCheckButton_Disabled()
model.stopCheckSpeakerEnergy()
view.replaceSpeakerThresholdCheckButton_Passive()
def callbackSetSpeakerRecordTimeout(value):
print("callbackSetSpeakerRecordTimeout", value)
if value == "": return
try:
value = int(value)
if 0 <= value and value <= config.INPUT_SPEAKER_PHRASE_TIMEOUT:
view.clearErrorMessage()
config.INPUT_SPEAKER_RECORD_TIMEOUT = value
view.setGuiVariable_SpeakerRecordTimeout(config.INPUT_SPEAKER_RECORD_TIMEOUT)
else:
raise ValueError()
except:
view.showErrorMessage_SpeakerRecordTimeout()
def callbackSetSpeakerPhraseTimeout(value):
print("callbackSetSpeakerPhraseTimeout", value)
if value == "": return
try:
value = int(value)
if 0 <= value and value >= config.INPUT_SPEAKER_RECORD_TIMEOUT:
view.clearErrorMessage()
config.INPUT_SPEAKER_PHRASE_TIMEOUT = value
view.setGuiVariable_SpeakerPhraseTimeout(config.INPUT_SPEAKER_PHRASE_TIMEOUT)
else:
raise ValueError()
except:
view.showErrorMessage_SpeakerPhraseTimeout()
def callbackSetSpeakerMaxPhrases(value):
print("callbackSetSpeakerMaxPhrases", value)
if value == "": return
try:
value = int(value)
if 0 <= value:
view.clearErrorMessage()
config.INPUT_SPEAKER_MAX_PHRASES = value
view.setGuiVariable_SpeakerMaxPhrases(config.INPUT_SPEAKER_MAX_PHRASES)
else:
raise ValueError()
except:
view.showErrorMessage_SpeakerMaxPhrases()
# Others Tab
def callbackSetEnableAutoClearMessageBox(value):
print("callbackSetEnableAutoClearMessageBox", value)
config.ENABLE_AUTO_CLEAR_MESSAGE_BOX = value
def callbackSetEnableNoticeXsoverlay(value):
print("callbackSetEnableNoticeXsoverlay", value)
config.ENABLE_NOTICE_XSOVERLAY = value
def callbackSetEnableAutoExportMessageLogs(value):
print("callbackSetEnableAutoExportMessageLogs", value)
config.ENABLE_LOGGER = value
if config.ENABLE_LOGGER is True:
model.startLogger()
else:
model.stopLogger()
def callbackSetMessageFormat(value):
print("callbackSetMessageFormat", value)
if len(value) > 0:
config.MESSAGE_FORMAT = value
def callbackSetEnableSendMessageToVrc(value):
print("callbackSetEnableSendMessageToVrc", value)
config.ENABLE_SEND_MESSAGE_TO_VRC = value
# [deprecated]
# def callbackSetStartupOscEnabledCheck(value):
# print("callbackSetStartupOscEnabledCheck", value)
# config.STARTUP_OSC_ENABLED_CHECK = value
# Advanced Settings Tab
def callbackSetOscIpAddress(value):
if value == "": return
print("callbackSetOscIpAddress", str(value))
config.OSC_IP_ADDRESS = str(value)
def callbackSetOscPort(value):
if value == "": return
print("callbackSetOscPort", int(value))
config.OSC_PORT = int(value)
def createMainWindow():
# create GUI
view.createGUI()
# init config
initSetLanguageAndCountry()
if model.authenticationTranslator(config.CHOICE_TRANSLATOR, config.AUTH_KEYS[config.CHOICE_TRANSLATOR]) is False:
# error update Auth key
auth_keys = config.AUTH_KEYS
auth_keys[config.CHOICE_TRANSLATOR] = None
config.AUTH_KEYS = auth_keys
view.printToTextbox_AuthenticationError()
config.CHOICE_TRANSLATOR = model.findTranslationEngine(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE)
# set word filter
model.addKeywords()
# check OSC started [deprecated]
# if config.STARTUP_OSC_ENABLED_CHECK is True and config.ENABLE_SEND_MESSAGE_TO_VRC is True:
# model.checkOSCStarted(view.printToTextbox_OSCError)
# check Software Updated
if model.checkSoftwareUpdated() is True:
view.showUpdateAvailableButton()
# init logger
if config.ENABLE_LOGGER is True:
model.startLogger()
# set UI and callback
view.register(
common_registers={
"callback_update_software": callbackUpdateSoftware,
"callback_restart_software": callbackRestartSoftware,
},
window_action_registers={
"callback_open_config_window": callbackOpenConfigWindow,
"callback_close_config_window": callbackCloseConfigWindow,
},
main_window_registers={
"callback_enable_main_window_sidebar_compact_mode": callbackEnableMainWindowSidebarCompactMode,
"callback_disable_main_window_sidebar_compact_mode": callbackDisableMainWindowSidebarCompactMode,
"callback_toggle_translation": callbackToggleTranslation,
"callback_toggle_transcription_send": callbackToggleTranscriptionSend,
"callback_toggle_transcription_receive": callbackToggleTranscriptionReceive,
"callback_toggle_foreground": callbackToggleForeground,
"callback_your_language": setYourLanguageAndCountry,
"callback_target_language": setTargetLanguageAndCountry,
"values": model.getListLanguageAndCountry(),
"callback_selected_language_preset_tab": callbackSelectedLanguagePresetTab,
"message_box_bind_Return": messageBoxPressKeyEnter,
"message_box_bind_Any_KeyPress": messageBoxPressKeyAny,
"message_box_bind_FocusIn": messageBoxFocusIn,
"message_box_bind_FocusOut": messageBoxFocusOut,
},
config_window_registers={
# Compact Mode Switch
"callback_disable_config_window_compact_mode": callbackEnableConfigWindowCompactMode,
"callback_enable_config_window_compact_mode": callbackDisableConfigWindowCompactMode,
# Appearance Tab
"callback_set_transparency": callbackSetTransparency,
"callback_set_appearance": callbackSetAppearance,
"callback_set_ui_scaling": callbackSetUiScaling,
"callback_set_font_family": callbackSetFontFamily,
"callback_set_ui_language": callbackSetUiLanguage,
# Translation Tab
"callback_set_deepl_authkey": callbackSetDeeplAuthkey,
# Transcription Tab (Mic)
"callback_set_mic_host": callbackSetMicHost,
"list_mic_host": model.getListInputHost(),
"callback_set_mic_device": callbackSetMicDevice,
"list_mic_device": model.getListInputDevice(),
"callback_set_mic_energy_threshold": callbackSetMicEnergyThreshold,
"callback_set_mic_dynamic_energy_threshold": callbackSetMicDynamicEnergyThreshold,
"callback_check_mic_threshold": callbackCheckMicThreshold,
"callback_set_mic_record_timeout": callbackSetMicRecordTimeout,
"callback_set_mic_phrase_timeout": callbackSetMicPhraseTimeout,
"callback_set_mic_max_phrases": callbackSetMicMaxPhrases,
"callback_set_mic_word_filter": callbackSetMicWordFilter,
# Transcription Tab (Speaker)
"callback_set_speaker_energy_threshold": callbackSetSpeakerEnergyThreshold,
"callback_set_speaker_dynamic_energy_threshold": callbackSetSpeakerDynamicEnergyThreshold,
"callback_check_speaker_threshold": callbackCheckSpeakerThreshold,
"callback_set_speaker_record_timeout": callbackSetSpeakerRecordTimeout,
"callback_set_speaker_phrase_timeout": callbackSetSpeakerPhraseTimeout,
"callback_set_speaker_max_phrases": callbackSetSpeakerMaxPhrases,
# Others Tab
"callback_set_enable_auto_clear_chatbox": callbackSetEnableAutoClearMessageBox,
"callback_set_enable_notice_xsoverlay": callbackSetEnableNoticeXsoverlay,
"callback_set_enable_auto_export_message_logs": callbackSetEnableAutoExportMessageLogs,
"callback_set_message_format": callbackSetMessageFormat,
"callback_set_enable_send_message_to_vrc": callbackSetEnableSendMessageToVrc,
# "callback_set_startup_osc_enabled_check": callbackSetStartupOscEnabledCheck, # [deprecated]
# Advanced Settings Tab
"callback_set_osc_ip_address": callbackSetOscIpAddress,
"callback_set_osc_port": callbackSetOscPort,
},
)
def showMainWindow():
view.startMainLoop()

View File

@@ -1,325 +0,0 @@
"""
CustomTkinter Scrollable Dropdown Menu
Author: Akash Bora
License: MIT
This is a custom dropdown menu for customtkinter.
Homepage: https://github.com/Akascape/CTkScrollableDropdown
Advanced Scrollable Dropdown class for customtkinter widgets
Author: Akash Bora
"""
import customtkinter
import sys
import time
class CTkScrollableDropdown(customtkinter.CTkToplevel):
def __init__(self, attach, x=None, y=None, button_color=None, height: int = 200, width: int = None,
fg_color=None, button_height: int = 20, justify="center", scrollbar_button_color=None,
scrollbar=True, scrollbar_button_hover_color=None, frame_border_width=2, values=[],
command=None, image_values=[], alpha: float = 0.97, frame_corner_radius=20, double_click=False,
resize=True, frame_border_color=None, text_color=None, autocomplete=False, **button_kwargs):
super().__init__(takefocus=1)
self.transient(self.master)
self.alpha = alpha
self.attach = attach
self.corner = frame_corner_radius
self.padding = 0
self.focus_something = False
self.disable = True
if sys.platform.startswith("win"):
self.after(100, lambda: self.overrideredirect(True))
self.transparent_color = self._apply_appearance_mode(self._fg_color)
self.attributes("-transparentcolor", self.transparent_color)
elif sys.platform.startswith("darwin"):
self.overrideredirect(True)
self.transparent_color = 'systemTransparent'
self.attributes("-transparent", True)
self.focus_something = True
else:
self.overrideredirect(True)
self.transparent_color = '#000001'
self.corner = 0
self.padding = 18
self.withdraw()
self.hide = True
self.attach.bind('<Configure>', lambda e: self._withdraw() if not self.disable else None, add="+")
self.attach.winfo_toplevel().bind('<Configure>', lambda e: self._withdraw() if not self.disable else None, add="+")
self.attach.winfo_toplevel().bind("<Triple-Button-1>", lambda e: self._withdraw() if not self.disable else None, add="+")
self.attach.winfo_toplevel().bind("<Button-3>", lambda e: self._withdraw() if not self.disable else None, add="+")
self.attach.winfo_toplevel().bind("<Button-2>", lambda e: self._withdraw() if not self.disable else None, add="+")
self.attributes('-alpha', 0)
self.disable = False
self.fg_color = customtkinter.ThemeManager.theme["CTkFrame"]["fg_color"] if fg_color is None else fg_color
self.scroll_button_color = customtkinter.ThemeManager.theme["CTkScrollbar"]["button_color"] if scrollbar_button_color is None else scrollbar_button_color
self.scroll_hover_color = customtkinter.ThemeManager.theme["CTkScrollbar"]["button_hover_color"] if scrollbar_button_hover_color is None else scrollbar_button_hover_color
self.frame_border_color = customtkinter.ThemeManager.theme["CTkFrame"]["border_color"] if frame_border_color is None else frame_border_color
self.button_color = customtkinter.ThemeManager.theme["CTkFrame"]["top_fg_color"] if button_color is None else button_color
self.text_color = customtkinter.ThemeManager.theme["CTkLabel"]["text_color"] if text_color is None else text_color
if scrollbar is False:
self.scroll_button_color = self.fg_color
self.scroll_hover_color = self.fg_color
self.frame = customtkinter.CTkScrollableFrame(self, bg_color=self.transparent_color, fg_color=self.fg_color,
scrollbar_button_hover_color=self.scroll_hover_color,
corner_radius=self.corner, border_width=frame_border_width,
scrollbar_button_color=self.scroll_button_color,
border_color=self.frame_border_color)
self.frame._scrollbar.grid_configure(padx=3)
self.frame.pack(expand=True, fill="both")
self.dummy_entry = customtkinter.CTkEntry(self.frame, fg_color="transparent", border_width=0, height=1, width=1)
self.no_match = customtkinter.CTkLabel(self.frame, text="No Match")
self.height = height
self.height_new = height
self.width = width
self.command = command
self.fade = False
self.resize = resize
self.autocomplete = autocomplete
self.var_update = customtkinter.StringVar()
self.appear = False
if justify.lower()=="left":
self.justify = "w"
elif justify.lower()=="right":
self.justify = "e"
else:
self.justify = "c"
self.button_height = button_height
self.values = values
self.button_num = len(self.values)
self.image_values = None if len(image_values)!=len(self.values) else image_values
self.resizable(width=False, height=False)
self._init_buttons(**button_kwargs)
# Add binding for different ctk widgets
if double_click or self.attach.winfo_name().startswith("!ctkentry") or self.attach.winfo_name().startswith("!ctkcombobox"):
self.attach.bind('<Double-Button-1>', lambda e: self._iconify(), add="+")
else:
self.attach.bind('<Button-1>', lambda e: self._iconify(), add="+")
if self.attach.winfo_name().startswith("!ctkcombobox"):
self.attach._canvas.tag_bind("right_parts", "<Button-1>", lambda e: self._iconify())
self.attach._canvas.tag_bind("dropdown_arrow", "<Button-1>", lambda e: self._iconify())
if self.command is None:
self.command = self.attach.set
if self.attach.winfo_name().startswith("!ctkoptionmenu"):
self.attach._canvas.bind("<Button-1>", lambda e: self._iconify())
self.attach._text_label.bind("<Button-1>", lambda e: self._iconify())
if self.command is None:
self.command = self.attach.set
self.update_idletasks()
self.x = x
self.y = y
if self.autocomplete:
self.bind_autocomplete()
# self.deiconify()
self.withdraw()
self.attributes("-alpha", self.alpha)
def _withdraw(self):
if self.hide is False: self.withdraw()
self.hide = True
def _update(self, a, b, c):
self.live_update(self.attach._entry.get())
def bind_autocomplete(self, ):
def appear(x):
self.appear = True
if self.attach.winfo_name().startswith("!ctkcombobox"):
self.attach._entry.configure(textvariable=self.var_update)
self.attach._entry.bind("<Key>", appear)
self.attach.set(self.values[0])
self.var_update.trace_add('write', self._update)
if self.attach.winfo_name().startswith("!ctkentry"):
self.attach.configure(textvariable=self.var_update)
self.attach.bind("<Key>", appear)
self.var_update.trace_add('write', self._update)
def fade_out(self):
for i in range(100,0,-10):
if not self.winfo_exists():
break
self.attributes("-alpha", i/100)
self.update()
time.sleep(1/100)
def fade_in(self):
for i in range(0,100,10):
if not self.winfo_exists():
break
self.attributes("-alpha", i/100)
self.update()
time.sleep(1/100)
def _init_buttons(self, **button_kwargs):
self.i = 0
self.widgets = {}
for row in self.values:
self.widgets[self.i] = customtkinter.CTkButton(self.frame,
text=row,
height=self.button_height,
fg_color=self.button_color,
text_color=self.text_color,
image=self.image_values[i] if self.image_values is not None else None,
anchor=self.justify,
command=lambda k=row: self._attach_key_press(k), **button_kwargs)
self.widgets[self.i].pack(fill="x", pady=2, padx=(self.padding, 0))
self.i+=1
self.hide = False
def destroy_popup(self):
self.destroy()
self.disable = True
def place_dropdown(self):
self.x_pos = self.attach.winfo_rootx() if self.x is None else self.x + self.attach.winfo_rootx()
self.y_pos = self.attach.winfo_rooty() + self.attach.winfo_reqheight() + 5 if self.y is None else self.y + self.attach.winfo_rooty()
self.width_new = self.attach.winfo_width() if self.width is None else self.width
if self.resize:
if self.button_num==1:
self.height_new = self.button_height * self.button_num + 45
else:
self.height_new = self.button_height * self.button_num + 35
if self.height_new>self.height:
self.height_new = self.height
self.geometry('{}x{}+{}+{}'.format(self.width_new, self.height_new,
self.x_pos, self.y_pos))
self.fade_in()
self.attributes('-alpha', self.alpha)
def _iconify(self):
if self.disable: return
if self.hide:
self._deiconify()
self.place_dropdown()
if self.focus_something:
self.dummy_entry.pack()
self.dummy_entry.focus_set()
self.after(100, self.dummy_entry.pack_forget)
self.hide = False
self.focus_set()
self.focus()
else:
self.withdraw()
self.hide = True
def _attach_key_press(self, k):
self.fade = True
if self.command:
self.command(k)
self.fade = False
self.fade_out()
self.withdraw()
self.hide = True
def live_update(self, string=None):
if not self.appear: return
if self.disable: return
if self.fade: return
if string:
self._deiconify()
i=1
for key in self.widgets.keys():
s = self.widgets[key].cget("text")
if not s.startswith(string):
self.widgets[key].pack_forget()
else:
self.widgets[key].pack(fill="x", pady=2, padx=(self.padding, 0))
i+=1
if i==1:
self.no_match.pack(fill="x", pady=2, padx=(self.padding, 0))
else:
self.no_match.pack_forget()
self.button_num = i
self.place_dropdown()
else:
self.no_match.pack_forget()
self.button_num = len(self.values)
for key in self.widgets.keys():
self.widgets[key].destroy()
self._init_buttons()
self.place_dropdown()
self.frame._parent_canvas.yview_moveto(0.0)
self.appear = False
def insert(self, value, **kwargs):
self.widgets[self.i] = customtkinter.CTkButton(self.frame,
text=value,
height=self.button_height,
fg_color=self.button_color,
text_color=self.text_color,
anchor=self.justify,
command=lambda k=value: self._attach_key_press(k), **kwargs)
self.widgets[self.i].pack(fill="x", pady=2, padx=(self.padding, 0))
self.i+=1
self.values.append(value)
def _deiconify(self):
if len(self.values)>0:
self.deiconify()
def popup(self, x=None, y=None):
self.x = x
self.y = y
self.hide = True
self._iconify()
def configure(self, **kwargs):
if "height" in kwargs:
self.height = kwargs.pop("height")
self.height_new = self.height
if "alpha" in kwargs:
self.alpha = kwargs.pop("alpha")
if "width" in kwargs:
self.width = kwargs.pop("width")
if "fg_color" in kwargs:
self.frame.configure(fg_color=kwargs.pop("fg_color"))
if "values" in kwargs:
self.values = kwargs.pop("values")
self.image_values = None
for key in self.widgets.keys():
self.widgets[key].destroy()
self._init_buttons()
if "image_values" in kwargs:
self.image_values = kwargs.pop("image_values")
self.image_values = None if len(self.image_values)!=len(self.values) else self.image_values
if self.image_values is not None:
i=0
for key in self.widgets.keys():
self.widgets[key].configure(image=self.image_values[i])
i+=1
if "button_color" in kwargs:
for key in self.widgets.keys():
self.widgets[key].configure(fg_color=kwargs.pop("button_color"))
for key in self.widgets.keys():
self.widgets[key].configure(**kwargs)

BIN
docs/main_window.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 155 KiB

BIN
img/arrow_left_disabled.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 B

BIN
img/arrow_left_white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 969 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
img/help_icon_disabled.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
img/help_icon_white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
img/mic_icon_disabled.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
img/mic_icon_white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 952 B

BIN
img/narrow_arrow_down.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 785 B

BIN
img/refresh_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
img/xsoverlay2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -1,6 +1,6 @@
selectable_languages = {
"en": "English",
"ja": "日本語",
"ko": "한국어"
"ko": "한국어(일부 지원)"
# 新しい言語とキーを追加する場合はここに追記してください
}

187
locales/en.yml Normal file
View File

@@ -0,0 +1,187 @@
main_window:
translation: Translation
transcription_send: Voice2Chatbox
transcription_receive: Speaker2Log
foreground: Foreground
language_settings: Language Settings
your_language: Your Language
both_direction_desc: Translate Each Other
target_language: Target Language
textbox_tab_all: All
textbox_tab_sent: Sent
textbox_tab_received: Received
textbox_tab_system: System
textbox_system_message:
enabled_translation: Translation feature is turned on.
disabled_translation: Translation feature is turned off.
enabled_voice2chatbox: Transcription from the microphone has started.
disabled_voice2chatbox: Transcription from the microphone has been stopped.
enabled_speaker2log: Transcription from the speaker has started.
disabled_speaker2log: Transcription from the speaker has been stopped.
enabled_foreground: The screen is fixed in the foreground.
disabled_foreground: The foreground fixation has been released.
auth_key_success: Auth key update completed.
auth_key_error: Auth Key is incorrect or Usage limit reached.
no_mic_device_detected_error: No mic device detected.
no_speaker_device_detected_error: No speaker device detected.
translation_engine_limit_error: It has automatically disabled the translation feature. Access has been temporarily restricted due to an excessive number of requests to the translation engine. Please wait for a while, restart VRCT, and try again.
detected_by_word_filter: The word %{detected_message} has not been sent due to detection by the word filter.
selected_your_language: "\"Your Language\" has set to %{your_language}."
selected_target_language: "\"Target Language\" has set to %{target_language}."
switched_language_preset_tab: Switched to Language Preset Tab No.%{tab_no}."
latest_language_setting: "Currently, \"Your Language\" is set to %{your_language}, and \"Target Language\" is set to %{target_language}."
opened_web_page_booth: Opened Booth page in your web browser.
opened_web_page_vrct_documents: "Opened VRCT Documents page in your web browser.\nFor any issues, requests, or inquiries, please feel free to contact us through the links at the bottom of the documents page, the \"Contact Form,\" or via X (formerly Twitter)!"
update_available: New version is here!
cover_message: The functionality is temporarily disabled until the settings window is closed.
confirmation_message:
update_software: "Download new version and restart automatically.\nIt'll take a while. Do it now?"
deny_update_software: Do it later
accept_update_software: Update and Restart
updating: Now updating...
detected_over_ui_size: "Current UI Size: %{current_ui_size}\nVRCT's window size may be larger than your display size.\n* Depending on your display size, you may need to adjust it multiple times."
deny_adjust_ui_size: "Keep it at this size"
accept_adjust_ui_size: "Set it smaller and restart"
translation_engine_limit_error: "It has automatically disabled the translation feature.\nAccess has been temporarily restricted\ndue to an excessive number of requests to the translation engine.\nPlease wait for a while, restart VRCT, and try again."
accept_translation_engine_limit_error: Accept and close
selectable_language_window:
title_your_language: Select Your Language
title_target_language: Select Target Language
go_back_button: Go Back
config_window:
config_title: Settings
compact_mode: Compact Mode
version: version %{version}
restart_message: Apply changes with a restart.
common_error_message:
invalid_value: Invalid value.
side_menu_labels:
appearance: Appearance
translation: Translation
transcription: Transcription
transcription_mic: Mic
transcription_speaker: Speaker
others: Others
advanced_settings: Advanced Settings
transparency:
label: Transparency
desc: Change the main window's transparency.
appearance_theme:
label: Theme [Under development]
desc: Change the color theme. Currently, only the Dark theme is supported. The Light theme is under development.
ui_size:
label: UI Size
font_family:
label: Font Family
ui_language:
label: UI Language
deepl_auth_key:
label: DeepL Auth Key
mic_host:
label: Mic Host/Driver
mic_device:
label: Mic Device
mic_dynamic_energy_threshold:
label_for_automatic: "Mic Energy Threshold (Current Setting: Automatic)"
desc_for_automatic: "Automatically determine microphone input sensitivity."
label_for_manual: "Mic Energy Threshold (Current Setting: Manual)"
desc_for_manual: "Manually determine the microphone input sensitivity using the slider. Press the microphone icon to input your voice and adjust the sensitivity while monitoring the volume."
error_message: You can set it with a value between 0 to %{max}.
mic_record_timeout:
label: Mic Record Timeout
desc: Detects silence and, when the specified number of seconds has passed, considers the mic input to have ended. (Second(s))
error_message: It cannot be greater than '%{mic_phrase_timeout_label}' with a value of 0 or more.
mic_phrase_timeout:
label: Mic Phrase Timeout
desc: Transcription processing is performed at intervals of the specified number of seconds.
error_message: It cannot be set lower than '%{mic_record_timeout_label}' with a value of 0 or more.
mic_max_phrase:
label: Mic Max Words
desc: It is the lower limit for the number of transcribed words, and only when this number is exceeded will the transcription results be displayed logs and send to VRChat.
error_message: You can set a number equal to or greater than 0.
mic_word_filter:
label: Mic Word Filter
desc: "It will not send the sentence if the word(s) included in the set list of words.\nHow to set: e.g. AAA,BBB,CCC"
speaker_dynamic_energy_threshold:
label_for_automatic: "Speaker Energy Threshold (Current Setting: Automatic)"
desc_for_automatic: "Automatically determine speaker input sensitivity."
label_for_manual: "Speaker Energy Threshold (Current Setting: Manual)"
desc_for_manual: "Manually determine the speaker input sensitivity using the slider. Press the headphones icon to listen to the audio and adjust the sensitivity while monitoring the volume."
error_message: You can set it with a value between 0 to %{max}.
no_device_error_message: No speaker device detected.
speaker_record_timeout:
label: Speaker Record Timeout
desc: Detects silence and, when the specified number of seconds has passed, considers the speaker input to have ended. (Second(s))
error_message: It cannot be greater than '%{speaker_phrase_timeout_label}' with a value of 0 or more.
speaker_phrase_timeout:
label: Speaker Phrase Timeout
desc: Transcription processing is performed at intervals of the specified number of seconds.
error_message: It cannot be set lower than '%{speaker_record_timeout_label}' with a value of 0 or more.
speaker_max_phrase:
label: Speaker Max Words
desc: It is the lower limit for the number of transcribed words, and only when this number is exceeded will the transcription results be displayed logs.
error_message: You can set a number equal to or greater than 0.
auto_clear_the_message_box:
label: Auto Clear The Message Box
notice_xsoverlay:
label: Notification XSOverlay (VR Only)
desc: Notify received messages by using XSOverlay's notification feature.
auto_export_message_logs:
label: Auto Export Message Logs
desc: Automatically export the conversation messages as a text file.
message_format:
label: Message Format
desc: "You can change the decoration of the message you want to send.\n[message] will be replaced with the message, and [translation] will be replaced with the translated message.\nIt will be used in Notification XSOverlay too."
send_message_to_vrc:
label: Send Message To VRChat
desc: There is a way to use it without sending messages to VRChat, but it is not supported. Enable this feature when you intend to send a message to VRChat.
osc_ip_address:
label: OSC IP Address
osc_port:
label: OSC Port

186
locales/ja.yml Normal file
View File

@@ -0,0 +1,186 @@
main_window:
translation: 翻訳
transcription_send: 音声認識(マイク)
transcription_receive: 音声認識(スピーカー)
foreground: 最前面表示
language_settings: 言語設定
your_language: あなたの言語
both_direction_desc: 双方向に翻訳
target_language: 相手の言語
textbox_tab_all: 全て
textbox_tab_sent: 送信
textbox_tab_received: 受信
textbox_tab_system: システム
textbox_system_message:
enabled_translation: 翻訳機能をONにしました。
disabled_translation: 翻訳機能をOFFしました。
enabled_voice2chatbox: マイクからの音声入力、文字起こしを開始します。
disabled_voice2chatbox: マイクからの音声入力、文字起こしを終了しました。
enabled_speaker2log: スピーカーからの音声聞き取り、文字起こしを開始します。
disabled_speaker2log: スピーカーからの音声聞き取り、文字起こしを終了しました。
enabled_foreground: 画面を常に最前面へ固定します。
disabled_foreground: 最前面への固定を解除しました。
auth_key_success: 認証キーの更新が完了しました。
auth_key_error: 認証キーが間違っているか、API使用制限が上限に達しています.
no_mic_device_detected_error: マイクデバイスが検出されませんでした。
no_speaker_device_detected_error: スピーカーデバイスが検出されませんでした。
translation_engine_limit_error: 翻訳機能を自動的に停止しました。翻訳エンジンへのリクエストが多すぎるため、一時的にアクセスが制限されています。しばらく待ってから、VRCTの再起動をしてもう一度試してみてください。
detected_by_word_filter: ワードフィルターに登録されている単語 %{detected_message} が検出されたため送信しませんでした。
selected_your_language: 「あなたの言語」 を %{your_language} に設定しました。
selected_target_language: 「相手の言語」 を %{target_language} に設定しました。
switched_language_preset_tab: 言語プリセット番号 %{tab_no} に切り替わりました。
latest_language_setting: 現在「あなたの言語」は %{your_language}、「相手の言語」は %{target_language} に設定されています。
opened_web_page_booth: お使いのブラウザで、Boothのページを開きました。
opened_web_page_vrct_documents: "お使いのブラウザで、VRCTのドキュメントを開きました。使用方法などはそちらに記載されています。\n不具合、ご要望、その他お問い合わせはドキュメント最下部にあるLinks、「お問合せフォーム」もしくはX(元Twitter)にて気軽にご連絡ください!"
update_available: 新しいバージョンが出ました!
cover_message: 設定画面が閉じられるまで、一時的に機能を停止しています。
confirmation_message:
update_software: "新しいバージョンをダウンロードして再起動します。\n少し時間がかかるかもしれません。今すぐ行いますか"
deny_update_software: 後でする
accept_update_software: アップデートして再起動
updating: アップデート中...
detected_over_ui_size: "現在のUI サイズ: %{current_ui_size}\nVRCTのウィンドウサイズが、お使いのディスプレイサイズより大きい可能性があります。\n※ディスプレイサイズによっては、何度か再設定が必要な場合があります。"
deny_adjust_ui_size: このサイズのままで良い
accept_adjust_ui_size: 小さく設定して再起動
translation_engine_limit_error: "翻訳機能を自動的に停止しました。\n翻訳エンジンへのリクエストが多すぎるため\n一時的にアクセスが制限されています。\nしばらく待ってから、VRCTの再起動をしてもう一度試してみてください。"
accept_translation_engine_limit_error: 了承して閉じる
selectable_language_window:
title_your_language: あなたの言語
title_target_language: 相手の言語
go_back_button: 戻る
config_window:
config_title: 設定
compact_mode: コンパクトモード
version: バージョン %{version}
restart_message: 再起動して変更を適用する。
common_error_message:
invalid_value: 無効な値です。
side_menu_labels:
appearance: デザイン
translation: 翻訳
transcription: 音声認識
transcription_mic: マイク
transcription_speaker: スピーカー
others: その他
advanced_settings: 高度な設定
transparency:
label: 透明度
desc: メイン画面の透明度を変更できます。
appearance_theme:
label: 外観テーマ [開発中]
desc: カラーテーマを変更できます。現在はDarkテーマのみ対応。Lightテーマは制作中です。
ui_size:
label: UIサイズ
font_family:
label: 使用フォント
ui_language:
label: UIの言語 / UI Language
deepl_auth_key:
label: DeepL 認証キー
mic_host:
label: マイク(ホスト/ドライバー)
mic_device:
label: マイク (デバイス)
mic_dynamic_energy_threshold:
label_for_automatic: "マイク入力感度の調整 (現在の設定: 自動)"
desc_for_automatic: マイクの入力感度を自動的に調節する。
label_for_manual: "マイク入力感度の調整 (現在の設定: 手動)"
desc_for_manual: スライダーを調整して入力感度を手動で決められます。マイクのアイコンを押すと、実際に声を入力し、音量を確認しながら調節できます。
error_message: 0 から %{max} までの数値で設定できます。
mic_record_timeout:
label: 入力が終了したとみなす無音時間
desc: 無音を検出し、設定された秒数経過すると、音声入力が終了したとみなします。
error_message: 0 以上で 「%{mic_phrase_timeout_label}」より大きくすることはできません。
mic_phrase_timeout:
label: 一度に文字起こしする時間の長さ
desc: 設定された秒数ごとに文字起こし処理が行われます。
error_message: 0 以上で 「%{mic_record_timeout_label}」より小さくすることはできません。
mic_max_phrase:
label: 送信するまでに保持する単語数
desc: 文字起こしされた単語数の下限値で、この数値を超えた場合のみ結果をVRChatへ送信し、ログに表示します。
error_message: 0以上の数値を設定できます。
mic_word_filter:
label: ワードフィルター
desc: "設定された単語を検出すると、その文章は送信されません。\n設定の例: AAA,BBB,CCC"
speaker_dynamic_energy_threshold:
label_for_automatic: "スピーカー入力感度の調整 (現在の設定: 自動)"
desc_for_automatic: スピーカーの入力感度を自動的に調節する。
label_for_manual: "スピーカー入力感度の調整 (現在の設定: 手動)"
desc_for_manual: スライダーを調整して入力感度を手動で決められます。ヘッドフォンのアイコンを押すと、実際に音声を聞き取り、音量を確認しながら調節できます。
error_message: 0 から %{max} までの数値で設定できます。
no_device_error_message: スピーカーデバイスが検出されませんでした。
speaker_record_timeout:
label: 入力が終了したとみなす無音時間
desc: 無音を検出し、設定された秒数経過すると、音声入力が終了したとみなします。
error_message: 0 以上で 「%{speaker_phrase_timeout_label}」より大きくすることはできません。
speaker_phrase_timeout:
label: 一度に文字起こしする時間の長さ
desc: 設定された秒数ごとに文字起こし処理が行われます。
error_message: 0 以上で 「%{speaker_record_timeout_label}」より小さくすることはできません。
speaker_max_phrase:
label: ログとして表示するまでに保持する単語数
desc: 文字起こしされた単語数の下限値で、この数値を超えた場合のみ結果をログに表示します。
error_message: 0以上の数値を設定できます。
auto_clear_the_message_box:
label: 送信後はチャットボックスを空にする
notice_xsoverlay:
label: XSOverlayでの通知受け取り機能を有効 (VR限定)
desc: 文字起こし受信されたメッセージをXSOverlayの機能を使って通知として受け取れます。
auto_export_message_logs:
label: 会話ログを自動的に保存する
desc: テキストファイルとしてログがlogsフォルダ内に保存されます。
message_format:
label: 送信するメッセージのフォーマット
desc: "VRChatで相手に実際に見えるフォーマットを変更できます。\n[message]がメッセージに置換され、\n[translation]が翻訳されたメッセージに置換されます。\n※XSOverlayでの通知受け取り機能でも使われます。"
send_message_to_vrc:
label: VRChatにメッセージを送信する
desc: "サポート対象外ですが、VRChatにメッセージを送信せずに使う方法があります。送信したい場合、この機能を有効にする事を忘れないでください。"
osc_ip_address:
label: OSC IP Address
osc_port:
label: OSC Port

187
locales/ko.yml Normal file
View File

@@ -0,0 +1,187 @@
main_window:
translation: 번역
transcription_send: 마이크 -> 챗박스
transcription_receive: 스피커 -> 로그
foreground: 항상 위로
# language_settings: Language Settings
# your_language: Your Language
# both_direction_desc: Translate Each Other
# target_language: Target Language
textbox_tab_all: 전체
textbox_tab_sent: 전송
textbox_tab_received: 수신
textbox_tab_system: 시스템
# textbox_system_message:
# enabled_translation: Translation feature is turned on.
# disabled_translation: Translation feature is turned off.
# enabled_voice2chatbox: Transcription from the microphone has started.
# disabled_voice2chatbox: Transcription from the microphone has been stopped.
# enabled_speaker2log: Transcription from the speaker has started.
# disabled_speaker2log: Transcription from the speaker has been stopped.
# enabled_foreground: The screen is fixed in the foreground.
# disabled_foreground: The foreground fixation has been released.
# auth_key_success: Auth key update completed.
# auth_key_error: Auth Key is incorrect or Usage limit reached.
# no_mic_device_detected_error: No mic device detected.
# no_speaker_device_detected_error: No speaker device detected.
# translation_engine_limit_error: It has automatically disabled the translation feature. Access has been temporarily restricted due to an excessive number of requests to the translation engine. Please wait for a while, restart VRCT, and try again.
# detected_by_word_filter: The word %{detected_message} has not been sent due to detection by the word filter.
# selected_your_language: "\"Your Language\" has set to %{your_language}."
# selected_target_language: "\"Target Language\" has set to %{target_language}."
# switched_language_preset_tab: Switched to Language Preset Tab No.%{tab_no}."
# latest_language_setting: "Currently, \"Your Language\" is set to %{your_language}, and \"Target Language\" is set to %{target_language}."
# opened_web_page_booth: Opened Booth page in your web browser.
# opened_web_page_vrct_documents: "Opened VRCT Documents page in your web browser.\nFor any issues, requests, or inquiries, please feel free to contact us through the links at the bottom of the documents page, the \"Contact Form,\" or via X (formerly Twitter)!"
# update_available: New version is here!
# cover_message: The functionality is temporarily disabled until the settings window is closed.
# confirmation_message:
# update_software: "Download new version and restart automatically.\nIt'll take a while. Do it now?"
# deny_update_software: Do it later
# accept_update_software: Update and Restart
# updating: Now updating...
# detected_over_ui_size: "Current UI Size: %{current_ui_size}\nVRCT's window size may be larger than your display size.\n* Depending on your display size, you may need to adjust it multiple times."
# deny_adjust_ui_size: "Keep it at this size"
# accept_adjust_ui_size: "Set it smaller and restart"
# translation_engine_limit_error: "It has automatically disabled the translation feature.\nAccess has been temporarily restricted\ndue to an excessive number of requests to the translation engine.\nPlease wait for a while, restart VRCT, and try again."
# accept_translation_engine_limit_error: Accept and close
# selectable_language_window:
# title_your_language: Select Your Language
# title_target_language: Select Target Language
# go_back_button: Go Back
config_window:
# config_title: Settings
# compact_mode: Compact Mode
# version: version %{version}
# restart_message: Apply changes with a restart.
# common_error_message:
# invalid_value: Invalid value.
side_menu_labels:
# appearance: Appearance
translation: 번역
transcription: 음성인식
transcription_mic: 마이크
transcription_speaker: 스피커
others: 기타
# advanced_settings: Advanced Settings
transparency:
label: 투명도
# desc: Change the main window's transparency.
appearance_theme:
label: 테마 [Under development]
# desc: Change the color theme. Currently, only the Dark theme is supported. The Light theme is under development.
ui_size:
label: UI 크기
font_family:
label: 폰트
ui_language:
label: UI 언어 / UI Language
deepl_auth_key:
label: DeepL 인증키
mic_host:
label: 마이크 호스트/Driver
mic_device:
label: 마이크 장치
mic_dynamic_energy_threshold:
label_for_automatic: "음성 입력 최소 볼륨 (Current Setting: Automatic)"
# desc_for_automatic: "Automatically determine microphone input sensitivity."
label_for_manual: "음성 입력 최소 볼륨 (Current Setting: Manual)"
# desc_for_manual: "Manually determine the microphone input sensitivity using the slider. Press the microphone icon to input your voice and adjust the sensitivity while monitoring the volume."
# error_message: You can set it with a value between 0 to %{max}.
mic_record_timeout:
label: 최대 무음 시간
# desc: Detects silence and, when the specified number of seconds has passed, considers the mic input to have ended. (Second(s))
# error_message: It cannot be greater than '%{mic_phrase_timeout_label}' with a value of 0 or more.
mic_phrase_timeout:
label: 최대 인식 시간
# desc: Transcription processing is performed at intervals of the specified number of seconds.
# error_message: It cannot be set lower than '%{mic_record_timeout_label}' with a value of 0 or more.
mic_max_phrase:
label: 최대 입력 절(phrases) 수
# desc: It is the lower limit for the number of transcribed words, and only when this number is exceeded will the transcription results be displayed logs and send to VRChat.
# error_message: You can set a number equal to or greater than 0.
mic_word_filter:
label: 단어 필터
# desc: "It will not send the sentence if the word(s) included in the set list of words.\nHow to set: e.g. AAA,BBB,CCC"
speaker_dynamic_energy_threshold:
label_for_automatic: "음성 입력 최소 볼륨 (Current Setting: Automatic)"
# desc_for_automatic: "Automatically determine speaker input sensitivity."
label_for_manual: "음성 입력 최소 볼륨 (Current Setting: Manual)"
# desc_for_manual: "Manually determine the speaker input sensitivity using the slider. Press the headphones icon to listen to the audio and adjust the sensitivity while monitoring the volume."
# error_message: You can set it with a value between 0 to %{max}.
# no_device_error_message: No speaker device detected.
speaker_record_timeout:
label: 최대 무음 시간
# desc: Detects silence and, when the specified number of seconds has passed, considers the speaker input to have ended. (Second(s))
# error_message: It cannot be greater than '%{speaker_phrase_timeout_label}' with a value of 0 or more.
speaker_phrase_timeout:
label: 최대 인식 시간
# desc: Transcription processing is performed at intervals of the specified number of seconds.
# error_message: It cannot be set lower than '%{speaker_record_timeout_label}' with a value of 0 or more.
speaker_max_phrase:
label: 최대 입력 절(phrases) 수
# desc: It is the lower limit for the number of transcribed words, and only when this number is exceeded will the transcription results be displayed logs.
# error_message: You can set a number equal to or greater than 0.
auto_clear_the_message_box:
label: 챗박스 자동 삭제
# notice_xsoverlay:
# label: Notification XSOverlay (VR Only)
# desc: Notify received messages by using XSOverlay's notification feature.
# auto_export_message_logs:
# label: Auto Export Message Logs
# desc: Automatically export the conversation messages as a text file.
message_format:
label: 전송 형식
# desc: "You can change the decoration of the message you want to send.\n[message] will be replaced with the message, and [translation] will be replaced with the translated message.\nIt will be used in Notification XSOverlay too."
# send_message_to_vrc:
# label: Send Message To VRChat
# desc: There is a way to use it without sending messages to VRChat, but it is not supported. Enable this feature when you intend to send a message to VRChat.
osc_ip_address:
label: OSC IP 주소
osc_port:
label: OSC 포트

18
main.py Normal file
View File

@@ -0,0 +1,18 @@
if __name__ == "__main__":
try:
import ctypes
ctypes.windll.shcore.SetProcessDpiAwareness(0)
from vrct_gui.splash_window import SplashWindow
splash = SplashWindow()
splash.showSplash()
import controller
controller.createMainWindow()
splash.destroySplash()
controller.showMainWindow()
except Exception as e:
import traceback
with open('error.log', 'a') as f:
traceback.print_exc(file=f)

398
model.py
View File

@@ -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}")

View File

@@ -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()
return server
if __name__ == "__main__":
sendChangeVoice()
sendChangeVoice()

View File

@@ -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"
},
}

View File

@@ -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)

View File

@@ -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:

View File

@@ -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
return default_device
return {"name":"NoDevice"}

View File

@@ -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,
}

View File

@@ -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

View File

@@ -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

View File

@@ -1,7 +1,8 @@
pillow
PyAudioWPatch
python-osc
customtkinter
deepl
flashtext
pyyaml
pillow == 10.0.0
PyAudioWPatch == 0.2.12.6
python-osc == 1.8.3
customtkinter == 5.2.0
deepl == 1.15.0
flashtext == 2.7
pyyaml == 6.0.1
python-i18n == 0.3.9

111
utils.py
View File

@@ -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)
def generatePercentageStringsList(start=40, end=200, step=10):
strings = []
for percent in range(start, end + 1, step):
strings.append(f"{percent}%")
return strings

1302
view.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,240 @@
from customtkinter import CTkToplevel, CTkFrame, CTkLabel, CTkFont
from .ui_utils import fadeInAnimation, setGeometryToCenterOfTheWidget, bindButtonFunctionAndColor
from utils import callFunctionIfCallable
class _CreateConfirmationModal(CTkToplevel):
def __init__(self, attach_window, settings, view_variable, modal_type=None):
super().__init__()
self.withdraw()
self.attach_window = attach_window
self.settings = settings
self._view_variable = view_variable
self.title("")
self.overrideredirect(True)
self.wm_attributes("-toolwindow", True)
self.BIND_FOCUS_OUT_FUNC_ID=None
self.configure(fg_color=self.settings.ctm.FAKE_BORDER_COLOR)
self.protocol("WM_DELETE_WINDOW", lambda: callFunctionIfCallable(self._view_variable.CALLBACK_HIDE_CONFIRMATION_MODAL))
self.grid_rowconfigure(0,weight=1)
self.grid_columnconfigure(0,weight=1)
self.modal_container = CTkFrame(self, corner_radius=0, fg_color=self.settings.ctm.BG_COLOR)
self.modal_container.grid(row=0, column=0, padx=self.settings.uism.FAKE_BORDER_SIZE, pady=self.settings.uism.FAKE_BORDER_SIZE)
self.modal_contents_wrapper = CTkFrame(self.modal_container, corner_radius=0, fg_color=self.settings.ctm.BG_COLOR)
self.modal_contents_wrapper.grid(row=0, column=0, padx=self.settings.uism.CONTENTS_WRAPPER, pady=self.settings.uism.CONTENTS_WRAPPER)
self.modal_contents_wrapper.grid_rowconfigure(1, minsize=self.settings.uism.MARGIN_BETWEEN_MESSAGE_AND_BUTTONS)
self.modal_message_label_wrapper = CTkFrame(self.modal_contents_wrapper, corner_radius=0, fg_color=self.settings.ctm.BG_COLOR)
self.modal_message_label_wrapper.grid(row=0, column=0)
self.modal_message_label_wrapper.grid_rowconfigure((0,2),weight=1)
self.modal_message_label_wrapper.grid_columnconfigure((0,2),weight=1)
self.modal_message_label = CTkLabel(
self.modal_message_label_wrapper,
textvariable=self._view_variable.VAR_MESSAGE_CONFIRMATION_MODAL,
height=0,
corner_radius=0,
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.MESSAGE_FONT_SIZE, weight="normal"),
anchor="w",
text_color=self.settings.ctm.MESSAGE_TEXT_COLOR,
)
self.modal_message_label.grid(row=1, column=1)
self.modal_buttons_container = CTkFrame(self.modal_contents_wrapper, corner_radius=0, fg_color=self.settings.ctm.BG_COLOR)
self.modal_buttons_container.grid(row=2, column=0, sticky="nsew")
self.modal_buttons_container.grid_rowconfigure((0,2),weight=1)
self.modal_buttons_container.grid_columnconfigure(0,weight=1)
self.modal_buttons_wrapper = CTkFrame(self.modal_buttons_container, corner_radius=0, fg_color=self.settings.ctm.BG_COLOR)
self.modal_buttons_wrapper.grid(row=1, column=0, sticky="ew")
if modal_type == "information":
# self.modal_buttons_wrapper.grid_columnconfigure(1, weight=1, minsize=self.settings.uism.BUTTONS_BETWEEN_PADDING)
self.modal_buttons_wrapper.grid_columnconfigure((0,2), weight=1)
self.accept_button = CTkFrame(self.modal_buttons_wrapper, corner_radius=self.settings.uism.BUTTONS_CORNER_RADIUS, fg_color=self.settings.ctm.ACCEPT_BUTTON_BG_COLOR, cursor="hand2")
self.accept_button.grid(row=0, column=1, sticky="ew")
self.accept_button.grid_columnconfigure(0, weight=1)
self.accept_button_label_wrapper = CTkFrame(self.accept_button, corner_radius=0, fg_color=self.settings.ctm.ACCEPT_BUTTON_BG_COLOR)
self.accept_button_label_wrapper.grid(row=0, column=0, padx=self.settings.uism.BUTTONS_IPADX, pady=self.settings.uism.BUTTONS_IPADY, sticky="ew")
self.accept_button_label_wrapper.grid_columnconfigure((0,2), weight=1)
self.accept_button_label = CTkLabel(
self.accept_button_label_wrapper,
textvariable=self._view_variable.VAR_LABEL_CONFIRMATION_MODAL_ACCEPT_BUTTON,
height=0,
corner_radius=0,
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.CONFIRMATION_BUTTONS_TEXT_FONT_SIZE, weight="normal"),
anchor="w",
text_color=self.settings.ctm.CONFIRMATION_BUTTONS_TEXT_COLOR,
)
self.accept_button_label.grid(row=0, column=1)
bindButtonFunctionAndColor(
target_widgets=[
self.accept_button,
self.accept_button_label_wrapper,
self.accept_button_label,
],
enter_color=settings.ctm.ACCEPT_BUTTON_HOVERED_BG_COLOR,
leave_color=settings.ctm.ACCEPT_BUTTON_BG_COLOR,
clicked_color=settings.ctm.ACCEPT_BUTTON_CLICKED_BG_COLOR,
buttonReleasedFunction=lambda _e: callFunctionIfCallable(self._view_variable.CALLBACK_ACCEPTED_CONFIRMATION_MODAL),
)
else:
self.modal_buttons_wrapper.grid_columnconfigure(1, weight=1, minsize=self.settings.uism.BUTTONS_BETWEEN_PADDING)
self.modal_buttons_wrapper.grid_columnconfigure((0,2), weight=0, uniform="button_wrapper")
self.deny_button = CTkFrame(self.modal_buttons_wrapper, corner_radius=self.settings.uism.BUTTONS_CORNER_RADIUS, fg_color=self.settings.ctm.DENY_BUTTON_BG_COLOR, cursor="hand2")
self.deny_button.grid(row=0, column=0, sticky="ew")
self.deny_button.grid_columnconfigure(0, weight=1)
self.deny_button_label_wrapper = CTkFrame(self.deny_button, corner_radius=0, fg_color=self.settings.ctm.DENY_BUTTON_BG_COLOR)
self.deny_button_label_wrapper.grid(row=0, column=0, padx=self.settings.uism.BUTTONS_IPADX, pady=self.settings.uism.BUTTONS_IPADY, sticky="ew")
self.deny_button_label_wrapper.grid_columnconfigure((0,2), weight=1)
self.deny_button_label_wrapper.grid_columnconfigure(0, weight=1)
self.deny_button_label = CTkLabel(
self.deny_button_label_wrapper,
textvariable=self._view_variable.VAR_LABEL_CONFIRMATION_MODAL_DENY_BUTTON,
height=0,
corner_radius=0,
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.CONFIRMATION_BUTTONS_TEXT_FONT_SIZE, weight="normal"),
anchor="w",
text_color=self.settings.ctm.CONFIRMATION_BUTTONS_TEXT_COLOR,
)
self.deny_button_label.grid(row=0, column=1)
bindButtonFunctionAndColor(
target_widgets=[
self.deny_button,
self.deny_button_label_wrapper,
self.deny_button_label,
],
enter_color=settings.ctm.DENY_BUTTON_HOVERED_BG_COLOR,
leave_color=settings.ctm.DENY_BUTTON_BG_COLOR,
clicked_color=settings.ctm.DENY_BUTTON_CLICKED_BG_COLOR,
buttonReleasedFunction=lambda _e: callFunctionIfCallable(self._view_variable.CALLBACK_DENIED_CONFIRMATION_MODAL),
)
self.accept_button = CTkFrame(self.modal_buttons_wrapper, corner_radius=self.settings.uism.BUTTONS_CORNER_RADIUS, fg_color=self.settings.ctm.ACCEPT_BUTTON_BG_COLOR, cursor="hand2")
self.accept_button.grid(row=0, column=2, sticky="ew")
self.accept_button.grid_columnconfigure(0, weight=1)
self.accept_button_label_wrapper = CTkFrame(self.accept_button, corner_radius=0, fg_color=self.settings.ctm.ACCEPT_BUTTON_BG_COLOR)
self.accept_button_label_wrapper.grid(row=0, column=0, padx=self.settings.uism.BUTTONS_IPADX, pady=self.settings.uism.BUTTONS_IPADY, sticky="ew")
self.accept_button_label_wrapper.grid_columnconfigure((0,2), weight=1)
self.accept_button_label = CTkLabel(
self.accept_button_label_wrapper,
textvariable=self._view_variable.VAR_LABEL_CONFIRMATION_MODAL_ACCEPT_BUTTON,
height=0,
corner_radius=0,
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.CONFIRMATION_BUTTONS_TEXT_FONT_SIZE, weight="normal"),
anchor="w",
text_color=self.settings.ctm.CONFIRMATION_BUTTONS_TEXT_COLOR,
)
self.accept_button_label.grid(row=0, column=1)
bindButtonFunctionAndColor(
target_widgets=[
self.accept_button,
self.accept_button_label_wrapper,
self.accept_button_label,
],
enter_color=settings.ctm.ACCEPT_BUTTON_HOVERED_BG_COLOR,
leave_color=settings.ctm.ACCEPT_BUTTON_BG_COLOR,
clicked_color=settings.ctm.ACCEPT_BUTTON_CLICKED_BG_COLOR,
buttonReleasedFunction=lambda _e: callFunctionIfCallable(self._view_variable.CALLBACK_ACCEPTED_CONFIRMATION_MODAL),
)
def hide_buttons(self):
self.modal_buttons_wrapper.grid_remove()
def show(self, hide_title_bar:bool=True, close_when_focusout:bool=True):
self.modal_buttons_wrapper.grid()
if hide_title_bar is False:
self.overrideredirect(False)
else:
self.overrideredirect(True)
self.close_when_focusout = close_when_focusout
if self.close_when_focusout is True:
self.BIND_FOCUS_OUT_FUNC_ID = self.bind("<FocusOut>", self.focusOutFunction, "+")
else:
self._grab_set()
self.attributes("-alpha", 0)
self.deiconify()
setGeometryToCenterOfTheWidget(
attach_widget=self.attach_window,
target_widget=self
)
fadeInAnimation(self, steps=5, interval=0.005, max_alpha=1)
self.focus_set()
def hide(self):
if self.BIND_FOCUS_OUT_FUNC_ID is not None:
self.unbind("<FocusOut>", self.BIND_FOCUS_OUT_FUNC_ID)
self.withdraw()
self.grab_release()
def focusOutFunction(self, e):
if str(e.widget) != ".!_createconfirmationmodal": return
callFunctionIfCallable(self._view_variable.CALLBACK_HIDE_CONFIRMATION_MODAL)
def _grab_set(self):
self.grab_set()

View File

@@ -0,0 +1,355 @@
from types import SimpleNamespace
from customtkinter import CTkToplevel, CTkFrame, CTkLabel, CTkFont, CTkScrollableFrame
from time import sleep
from .ui_utils import bindButtonReleaseFunction, bindEnterAndLeaveColor, bindButtonPressColor, getLatestHeight, applyUiScalingAndFixTheBugScrollBar, getLatestWidth, getLongestText
from functools import partial
from utils import isEven, makeEven
class _CreateDropdownMenuWindow(CTkToplevel):
def __init__(
self,
settings,
view_variable,
window_additional_y_pos,
window_border_width,
scrollbar_ipadx,
scrollbar_width,
value_ipadx,
value_ipady,
value_pady,
value_font_size,
dropdown_menu_default_min_width,
window_bg_color,
window_border_color,
values_bg_color,
values_hovered_bg_color,
values_clicked_bg_color,
values_text_color,
):
super().__init__()
self.withdraw()
self.hide = True
self.window_additional_y_pos=window_additional_y_pos
self.window_border_width=window_border_width
self.scrollbar_ipadx=scrollbar_ipadx
self.scrollbar_width=scrollbar_width
self.value_ipadx=value_ipadx
self.value_ipady=value_ipady
self.value_pady=value_pady
self.value_font_size=value_font_size
self.dropdown_menu_default_min_width=dropdown_menu_default_min_width
self.window_bg_color=window_bg_color
self.window_border_color=window_border_color
self.values_bg_color=values_bg_color
self.values_hovered_bg_color=values_hovered_bg_color
self.values_clicked_bg_color=values_clicked_bg_color
self.values_text_color=values_text_color
self.settings = settings
self.attach_widget = None
self._view_variable = view_variable
self.wrapper_widget = None
self.dropdown_menu_widgets = {}
self.active_dropdown_menu_widget = None
self.attach_widget_width = None
self.attach_widget_height = None
self.attach_widget_x_pos = None
self.attach_widget_y_pos = None
self.x_pos = None
self.y_pos = None
self.init_height = 200
self.new_height = self.init_height
self.init_width = 200
self.new_width = self.init_width
self.init_max_display_length = 8
self.max_display_length = self.init_max_display_length
self.title("")
self.overrideredirect(True)
self.wm_attributes("-alpha", 0)
self.wm_attributes("-toolwindow", True)
self.configure(fg_color=self.window_bg_color)
self.resizable(width=False, height=False)
def updateDropdownMenuValues(self, dropdown_menu_widget_id, dropdown_menu_values):
self.dropdown_menu_widgets[dropdown_menu_widget_id].widget.destroy()
self.createDropdownMenuBox(
dropdown_menu_widget_id=dropdown_menu_widget_id,
dropdown_menu_values=dropdown_menu_values,
command=self.dropdown_menu_widgets[dropdown_menu_widget_id].command,
wrapper_widget=self.dropdown_menu_widgets[dropdown_menu_widget_id].wrapper_widget,
attach_widget=self.dropdown_menu_widgets[dropdown_menu_widget_id].attach_widget,
dropdown_menu_min_width=self.dropdown_menu_widgets[dropdown_menu_widget_id].dropdown_menu_settings.dropdown_menu_min_width,
dropdown_menu_height=self.dropdown_menu_widgets[dropdown_menu_widget_id].dropdown_menu_settings.dropdown_menu_height,
max_display_length=self.dropdown_menu_widgets[dropdown_menu_widget_id].dropdown_menu_settings.max_display_length,
)
def createDropdownMenuBox(self, dropdown_menu_widget_id, dropdown_menu_values, command, wrapper_widget, attach_widget, dropdown_menu_min_width=None, dropdown_menu_height=None, max_display_length=None):
self.attach_widget = attach_widget
self.wrapper_widget = wrapper_widget
self.new_width = dropdown_menu_min_width if dropdown_menu_min_width is not None else self.dropdown_menu_default_min_width
self.new_height = dropdown_menu_height if dropdown_menu_height is not None else self.init_height
self.max_display_length = max_display_length if max_display_length is not None else self.init_max_display_length
self.dropdown_menu_container = CTkFrame(self, corner_radius=0, fg_color=self.window_border_color, width=0, height=0)
self.dropdown_menu_container.grid(row=0, column=0, sticky="nsew")
BORDER_WIDTH=self.window_border_width
self.scroll_frame_container = CTkScrollableFrame(
self.dropdown_menu_container,
corner_radius=0,
fg_color=self.window_bg_color,
width=0,
height=0,
border_width=0,
)
self.scroll_frame_container.grid(row=0, column=0, padx=BORDER_WIDTH, pady=BORDER_WIDTH, sticky="nsew")
self.scroll_frame_container.grid_columnconfigure(0, weight=1)
self._createDropdownMenuValues(dropdown_menu_widget_id, dropdown_menu_values, command)
applyUiScalingAndFixTheBugScrollBar(
scrollbar_widget=self.scroll_frame_container,
padx=self.scrollbar_ipadx,
width=self.scrollbar_width,
)
geometry_width = int(self.new_width + self.scroll_frame_container._scrollbar.winfo_width() + (BORDER_WIDTH*2) + (self.scrollbar_ipadx[0] + self.scrollbar_ipadx[1]))
geometry_height = int(self.new_height + (BORDER_WIDTH*2))
self.dropdown_menu_widgets[dropdown_menu_widget_id] = SimpleNamespace()
self.dropdown_menu_widgets[dropdown_menu_widget_id] = SimpleNamespace(
widget=self.dropdown_menu_container,
command=command,
wrapper_widget=wrapper_widget,
attach_widget=attach_widget,
dropdown_menu_settings=SimpleNamespace(
dropdown_menu_min_width=dropdown_menu_min_width,
dropdown_menu_height=dropdown_menu_height,
max_display_length=max_display_length,
),
_settings=SimpleNamespace(
geometry_width=geometry_width,
geometry_height=geometry_height,
),
)
self.dropdown_menu_container.grid_remove()
def _createDropdownMenuValues(self, dropdown_menu_widget_id, dropdown_menu_values, command):
longest_text = getLongestText(dropdown_menu_values)
self.dropdown_menu_values_wrapper = CTkFrame(self.scroll_frame_container, corner_radius=0, fg_color=self.window_bg_color)
self.dropdown_menu_values_wrapper.grid(row=0, column=0, sticky="nsew")
self.dropdown_menu_values_wrapper.grid_columnconfigure(0, weight=1)
# for get to the height__________________
__dropdown_menu_value_wrapper = CTkFrame(self.dropdown_menu_values_wrapper, corner_radius=0, fg_color=self.values_bg_color, width=0, height=0)
__dropdown_menu_value_wrapper.grid(row=0, column=0, pady=self.value_pady, sticky="nsew")
setattr(self, f"{dropdown_menu_widget_id}__{0}", __dropdown_menu_value_wrapper)
__dropdown_menu_value_wrapper.grid_rowconfigure((0,2), weight=1)
# __dropdown_menu_value_wrapper.grid_columnconfigure(0, weight=1)
__label_widget = CTkLabel(
__dropdown_menu_value_wrapper,
text=longest_text,
height=0,
corner_radius=0,
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.value_font_size, weight="normal"),
anchor="w",
text_color=self.values_text_color,
)
# setattr(self, f"l", __label_widget)
__label_widget.grid(row=1, column=0, padx=self.value_ipadx, pady=self.value_ipady, sticky="w")
label_height = getLatestHeight(__dropdown_menu_value_wrapper)
label_width = getLatestWidth(__label_widget)
label_width += self.scroll_frame_container._scrollbar.winfo_width() + (self.window_border_width*2) + (self.scrollbar_ipadx[0] + self.scrollbar_ipadx[1])
if label_width > self.new_width:
additional_width = int(label_width - self.new_width)
self.new_width += additional_width
# for fixing 1px bug
if isEven(label_height) is False:
self.value_ipady = (self.value_ipady[0], self.value_ipady[1] - 1)
__dropdown_menu_value_wrapper.destroy()
# ______________________________________
dropdown_menu_values_length = len(dropdown_menu_values)
if dropdown_menu_values_length < self.max_display_length:
self.new_height = int(dropdown_menu_values_length * label_height)
else:
self.new_height = int(self.max_display_length * label_height)
# for fixing 1px bug
self.new_height = makeEven(self.new_height)
self.new_width = makeEven(self.new_width)
self.scroll_frame_container.configure(width=self.new_width, height=self.new_height)
row=0
for dropdown_menu_value in dropdown_menu_values:
dropdown_menu_value_wrapper = CTkFrame(self.dropdown_menu_values_wrapper, corner_radius=0, fg_color=self.values_bg_color, width=0, height=0, cursor="hand2")
dropdown_menu_value_wrapper.grid(row=row, column=0, pady=self.value_pady, sticky="nsew")
setattr(self, f"{dropdown_menu_widget_id}__{row}", dropdown_menu_value_wrapper)
dropdown_menu_value_wrapper.grid_rowconfigure((0,2), weight=1)
label_widget = CTkLabel(
dropdown_menu_value_wrapper,
text=dropdown_menu_value,
height=0,
corner_radius=0,
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.value_font_size, weight="normal"),
anchor="w",
text_color=self.values_text_color,
)
label_widget.grid(row=1, column=0, padx=self.value_ipadx, pady=self.value_ipady, sticky="w")
bindEnterAndLeaveColor([dropdown_menu_value_wrapper, label_widget], self.values_hovered_bg_color, self.values_bg_color)
bindButtonPressColor([dropdown_menu_value_wrapper, label_widget], self.values_clicked_bg_color, self.values_bg_color)
def optimizedCommand(value, _e):
command(value)
self._withdraw()
callback = partial(optimizedCommand, dropdown_menu_value)
bindButtonReleaseFunction([dropdown_menu_value_wrapper, label_widget], callback)
row+=1
def show(self, dropdown_menu_widget_id):
if self.hide is False: return
self.wm_attributes("-alpha", 0)
if self.active_dropdown_menu_widget is not None:
self.active_dropdown_menu_widget.grid_remove()
target_data = self.dropdown_menu_widgets[dropdown_menu_widget_id]
self.attach_widget = target_data.attach_widget
target_data.widget.grid()
self.active_dropdown_menu_widget = target_data.widget
self.geometry("{}x{}".format(target_data._settings.geometry_width, target_data._settings.geometry_height))
self.deiconify()
self._adjustToTargetWidgetGeometry()
self.BIND_CONFIGURE_FUNC_ID = self.attach_widget.winfo_toplevel().bind("<Configure>", self._adjustToTargetWidgetGeometry, "+")
self.BIND_UNMAP_FUNC_ID = self.attach_widget.bind("<Unmap>", self._withdraw, "+")
self.BIND_BUTTON_1_FUNC_ID = self.attach_widget.winfo_toplevel().bind("<Button-1>", self._withdraw, "+")
self.hide = False
for i in range(0,91,10):
if not self.winfo_exists():
break
self.attributes("-alpha", i/100)
self.update()
sleep(1/100)
self.wm_attributes("-alpha", 1)
self.update()
def _withdraw(self, e=None):
self.withdraw()
self.attach_widget.winfo_toplevel().unbind("<Configure>", self.BIND_CONFIGURE_FUNC_ID)
self.attach_widget.unbind("<Unmap>", self.BIND_UNMAP_FUNC_ID)
self.attach_widget.winfo_toplevel().unbind("<Button-1>", self.BIND_BUTTON_1_FUNC_ID)
self.hide = True
def _adjustToTargetWidgetGeometry(self, e=None):
if not self.attach_widget.winfo_exists():
return
self.attach_widget.update_idletasks()
self.update()
if self.attach_widget_x_pos == self.attach_widget.winfo_rootx() and self.attach_widget_y_pos == self.attach_widget.winfo_rooty():
self.lift()
return
self.wrapper_widget_y_pos = self.wrapper_widget.winfo_rooty()
self.wrapper_widget_bottom_y_pos = self.wrapper_widget_y_pos + self.wrapper_widget.winfo_height()
self.attach_widget_width = self.attach_widget.winfo_width()
self.attach_widget_height = self.attach_widget.winfo_height()
self.attach_widget_x_pos = self.attach_widget.winfo_rootx()
self.attach_widget_y_pos = self.attach_widget.winfo_rooty()
self.y_pos = int(self.attach_widget_y_pos + self.attach_widget_height + self.window_additional_y_pos)
if self.wrapper_widget_y_pos > self.y_pos or self.y_pos > self.wrapper_widget_bottom_y_pos:
self.hideTemporarily()
else:
if self.winfo_exists():
self.deiconify()
if self.winfo_width() >= self.attach_widget_width:
self.x_pos = int(self.attach_widget_x_pos - (self.winfo_width() - self.attach_widget_width))
else:
self.x_pos = self.attach_widget_x_pos
self.geometry("+{}+{}".format(self.x_pos, self.y_pos))
self.lift()
def hideTemporarily(self):
self.withdraw()

View File

@@ -0,0 +1,177 @@
from customtkinter import CTkToplevel, CTkFrame, CTkLabel, CTkFont
from time import sleep
from .ui_utils import getLatestWidth, getLatestHeight
from utils import isEven
class _CreateErrorWindow(CTkToplevel):
def __init__(
self,
settings,
view_variable,
wrapper_widget,
message_ipadx,
message_ipady,
message_font_size,
message_bg_color,
message_text_color,
):
super().__init__()
self.withdraw()
self.hide = True
self.settings = settings
self.attach_widget = None
self._view_variable = view_variable
self.wrapper_widget = wrapper_widget
self.message_ipadx = message_ipadx
self.message_ipady = message_ipady
self.message_font_size = message_font_size
self.message_bg_color = message_bg_color
self.message_text_color = message_text_color
self.attach_widget_width = None
self.attach_widget_height = None
self.attach_widget_x_pos = None
self.attach_widget_y_pos = None
self.x_pos = None
self.y_pos = None
self.title("")
self.overrideredirect(True)
self.wm_attributes("-alpha", 0)
self.wm_attributes("-toolwindow", True)
self.configure(fg_color=self.message_bg_color)
self.grid_rowconfigure(0,weight=1)
self.grid_columnconfigure(0,weight=1)
self.error_message_container = CTkFrame(self, corner_radius=0, fg_color=self.message_bg_color, width=0, height=0)
self.error_message_container.grid(row=0, column=0, sticky="nsew")
self.error_message_container_label_wrapper = CTkLabel(
self.error_message_container,
# text=message,
textvariable=self._view_variable.VAR_ERROR_MESSAGE,
height=0,
corner_radius=0,
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.message_font_size, weight="normal"),
anchor="w",
justify="left",
text_color=self.message_text_color,
)
self.error_message_container_label_wrapper.grid(row=0, column=0, padx=self.message_ipadx, pady=self.message_ipady, sticky="nsew")
def show(self, target_widget):
if self.hide is False: return
self.attach_widget = target_widget
self.deiconify()
self._adjustToTargetWidgetGeometry()
self.BIND_CONFIGURE_FUNC_ID = self.attach_widget.winfo_toplevel().bind("<Configure>", self._adjustToTargetWidgetGeometry, "+")
self.BIND_UNMAP_FUNC_ID = self.attach_widget.bind("<Unmap>", self._withdraw, "+")
self.hide = False
label_width = getLatestWidth(self.error_message_container_label_wrapper)
label_height = getLatestHeight(self.error_message_container_label_wrapper)
# for fixing 1px bug
if isEven(label_width) is False:
self.error_message_container_label_wrapper.grid(padx=(self.message_ipadx[0], self.message_ipadx[1]-1))
else:
self.error_message_container_label_wrapper.grid(padx=self.message_ipadx)
# for fixing 1px bug
if isEven(label_height) is False:
self.error_message_container_label_wrapper.grid(pady=(self.message_ipady[0], self.message_ipady[1]-1))
else:
self.error_message_container_label_wrapper.grid(pady=self.message_ipady)
for i in range(0,101,20):
if not self.winfo_exists():
break
self.attributes("-alpha", i/100)
self.update()
sleep(1/100)
sleep(0.1)
for i in range(0,91,10):
if not self.winfo_exists():
break
self.attributes("-alpha", i/100)
self.update()
sleep(1/80)
def _withdraw(self, e=None):
self.withdraw()
self.attach_widget.winfo_toplevel().unbind("<Configure>", self.BIND_CONFIGURE_FUNC_ID)
self.attach_widget.unbind("<Unmap>", self.BIND_UNMAP_FUNC_ID)
self.hide = True
def _adjustToTargetWidgetGeometry(self, e=None):
if not self.attach_widget.winfo_exists():
return
self.attach_widget.update_idletasks()
self.update()
if self.attach_widget_x_pos == self.attach_widget.winfo_rootx() and self.attach_widget_y_pos == self.attach_widget.winfo_rooty():
self.lift()
return
self.wrapper_widget_y_pos = self.wrapper_widget.winfo_rooty()
self.wrapper_widget_bottom_y_pos = self.wrapper_widget_y_pos + self.wrapper_widget.winfo_height()
self.attach_widget_width = self.attach_widget.winfo_width()
self.attach_widget_height = self.attach_widget.winfo_height()
self.attach_widget_x_pos = self.attach_widget.winfo_rootx()
self.attach_widget_y_pos = self.attach_widget.winfo_rooty()
self.y_pos = int(self.attach_widget_y_pos + self.attach_widget_height + 4)
if self.wrapper_widget_y_pos > self.y_pos or self.y_pos > self.wrapper_widget_bottom_y_pos:
self.hideTemporarily()
else:
if self.winfo_exists():
self.deiconify()
if self.winfo_width() >= self.attach_widget_width:
self.x_pos = int(self.attach_widget_x_pos - (self.winfo_width() - self.attach_widget_width))
else:
self.x_pos = self.attach_widget_x_pos
self.geometry("+{}+{}".format(self.x_pos, self.y_pos))
self.lift()
def hideTemporarily(self):
self.withdraw()

View File

@@ -0,0 +1,182 @@
from functools import partial
from .ui_utils import bindButtonReleaseFunction, bindEnterAndLeaveColor, bindButtonPressColor, applyUiScalingAndFixTheBugScrollBar
from utils import callFunctionIfCallable, makeEven
from customtkinter import CTkToplevel, CTkFrame, CTkLabel, CTkFont, CTkScrollableFrame
class _CreateSelectableLanguagesWindow(CTkToplevel):
def __init__(self, vrct_gui, settings, view_variable):
super().__init__()
self.withdraw()
self.attach = vrct_gui.main_bg_container
self.vrct_gui = vrct_gui
self.settings = settings
self._view_variable = view_variable
self.is_created = False
self.selectable_language_window_type = None
self.title("_CreateSelectableLanguagesWindow")
self.overrideredirect(True)
self.configure(fg_color=self.settings.ctm.TOP_BG_COLOR)
self.protocol("WM_DELETE_WINDOW", vrct_gui._closeSelectableLanguagesWindow)
self.bind("<FocusOut>", self.focusOutFunction)
def createContainer(self, selectable_language_window_type):
self.selectable_language_window_type = selectable_language_window_type
self.attach.update_idletasks()
self.x_pos = self.attach.winfo_rootx()
self.y_pos = self.attach.winfo_rooty()
self.width_new = makeEven(self.attach.winfo_width())
self.height_new = makeEven(self.attach.winfo_height())
self.geometry("{}x{}+{}+{}".format(self.width_new, self.height_new, self.x_pos, self.y_pos))
if self.is_created is True:
pass
else:
self._createContainer()
def callbackSelectableLanguages(self, value, _e):
if self.selectable_language_window_type == "your_language":
callback = self._view_variable.CALLBACK_SELECTED_YOUR_LANGUAGE
target_variable = self._view_variable.VAR_YOUR_LANGUAGE
elif self.selectable_language_window_type == "target_language":
callback = self._view_variable.CALLBACK_SELECTED_TARGET_LANGUAGE
target_variable = self._view_variable.VAR_TARGET_LANGUAGE
target_variable.set(value)
callFunctionIfCallable(callback, value)
self.vrct_gui._closeSelectableLanguagesWindow()
def _createContainer(self):
self.grid_rowconfigure(0, minsize=self.settings.uism.TOP_BAR_MIN_HEIGHT)
self.grid_rowconfigure(1, weight=1)
self.grid_columnconfigure(0, weight=1)
self.top_container = CTkFrame(self, corner_radius=0, fg_color=self.settings.ctm.TOP_BG_COLOR, width=0, height=0)
self.top_container.grid(row=0, column=0, sticky="nsew")
self.top_container.grid_rowconfigure((0,2), weight=1)
self.top_container.grid_columnconfigure(1, weight=1)
self.go_back_button_container = CTkFrame(self.top_container, corner_radius=0, fg_color=self.settings.ctm.GO_BACK_BUTTON_BG_COLOR, width=0, height=0, cursor="hand2")
self.go_back_button_container.grid(row=1, column=0)
self.go_back_button_label = CTkLabel(
self.go_back_button_container,
textvariable=self._view_variable.VAR_GO_BACK_LABEL_SELECTABLE_LANGUAGE,
height=0,
corner_radius=0,
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.GO_BACK_BUTTON_LABEL_FONT_SIZE, weight="normal"),
anchor="w",
text_color=self.settings.ctm.BASIC_TEXT_COLOR,
)
self.go_back_button_label.grid(row=0, column=0, padx=self.settings.uism.GO_BACK_BUTTON_IPADX, pady=self.settings.uism.GO_BACK_BUTTON_IPADY)
bindEnterAndLeaveColor([self.go_back_button_container, self.go_back_button_label], self.settings.ctm.GO_BACK_BUTTON_BG_HOVERED_COLOR, self.settings.ctm.GO_BACK_BUTTON_BG_COLOR)
bindButtonPressColor([self.go_back_button_container, self.go_back_button_label], self.settings.ctm.GO_BACK_BUTTON_BG_CLICKED_COLOR, self.settings.ctm.GO_BACK_BUTTON_BG_COLOR)
bindButtonReleaseFunction([self.go_back_button_container, self.go_back_button_label], lambda _e: self.vrct_gui._closeSelectableLanguagesWindow())
self.title_container = CTkFrame(self.top_container, corner_radius=0, fg_color=self.settings.ctm.TOP_BG_COLOR, width=0, height=0)
self.title_container.grid(row=1, column=1, sticky="nsew")
self.title_container.grid_columnconfigure((0,2), weight=1)
self.title_container.grid_rowconfigure((0,2), weight=1)
self.title_label = CTkLabel(
self.title_container,
textvariable=self._view_variable.VAR_TITLE_LABEL_SELECTABLE_LANGUAGE,
height=0,
corner_radius=0,
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.TITLE_FONT_SIZE, weight="normal"),
anchor="w",
text_color=self.settings.ctm.TITLE_TEXT_COLOR,
)
self.title_label.grid(row=1, column=1)
self.scroll_frame_container = CTkScrollableFrame(self, corner_radius=0, fg_color=self.settings.ctm.MAIN_BG_COLOR, width=self.width_new, height=self.height_new)
self.scroll_frame_container.grid(row=1, column=0, sticky="nsew")
applyUiScalingAndFixTheBugScrollBar(
scrollbar_widget=self.scroll_frame_container,
padx=self.settings.uism.SCROLLBAR_IPADX,
width=self.settings.uism.SCROLLBAR_WIDTH,
)
self.container = CTkFrame(self.scroll_frame_container, corner_radius=0, fg_color=self.settings.ctm.MAIN_BG_COLOR, width=0, height=0)
self.container.grid(row=0, column=0, sticky="nsew")
max_row = int(len(self._view_variable.LIST_SELECTABLE_LANGUAGES)/3) + 1
max_row+=1
row=0
column=0
for selectable_language_name in self._view_variable.LIST_SELECTABLE_LANGUAGES:
self.wrapper = CTkFrame(self.container, corner_radius=0, fg_color=self.settings.ctm.LANGUAGE_BUTTON_BG_COLOR, width=0, height=0, cursor="hand2")
self.wrapper.grid(row=row, column=column, sticky="nsew")
setattr(self, f"{row}_{column}", self.wrapper)
self.wrapper.grid_rowconfigure((0,2), weight=1)
selectable_language_name_for_text = selectable_language_name.replace("\n", " ")
label_widget = CTkLabel(
self.wrapper,
text=selectable_language_name_for_text,
height=0,
corner_radius=0,
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.VALUES_TEXT_FONT_SIZE, weight="normal"),
anchor="w",
text_color=self.settings.ctm.BASIC_TEXT_COLOR,
)
label_widget.grid(row=1, column=0, padx=self.settings.uism.VALUES_TEXT_IPADX, pady=self.settings.uism.VALUES_TEXT_IPADY)
bindEnterAndLeaveColor([self.wrapper, label_widget], self.settings.ctm.LANGUAGE_BUTTON_BG_HOVERED_COLOR, self.settings.ctm.LANGUAGE_BUTTON_BG_COLOR)
bindButtonPressColor([self.wrapper, label_widget], self.settings.ctm.LANGUAGE_BUTTON_BG_CLICKED_COLOR, self.settings.ctm.LANGUAGE_BUTTON_BG_COLOR)
callback = partial(self.callbackSelectableLanguages, selectable_language_name)
bindButtonReleaseFunction([self.wrapper, label_widget], callback)
if row == max_row:
row=0
column+=1
else:
row+=1
self.is_created = True
def focusOutFunction(self, e):
if str(e.widget) != ".!_createselectablelanguageswindow": return
self.vrct_gui._closeSelectableLanguagesWindow()

View File

@@ -0,0 +1,80 @@
from customtkinter import CTkToplevel, CTkFrame, CTkLabel, CTkFont
from .ui_utils import fadeInAnimation
from utils import makeEven
class _CreateWindowCover(CTkToplevel):
def __init__(self, attach_window, settings, view_variable):
super().__init__()
self.withdraw()
self.BIND_CONFIGURE_ADJUSTED_GEOMETRY_FUNC_ID=None
self.BIND_FOCUS_IN_FUNC_ID=None
self.attach_window = attach_window
self.settings = settings
self._view_variable = view_variable
self.title("")
self.overrideredirect(True)
self.wm_attributes("-toolwindow", True)
self.configure(fg_color="black")
self.protocol("WM_DELETE_WINDOW", lambda: self.withdraw())
self.grid_rowconfigure(0,weight=1)
self.grid_columnconfigure(0,weight=1)
self.cover_container = CTkFrame(self, corner_radius=0, fg_color="black", width=0, height=0)
self.cover_container.grid(row=0, column=0, sticky="nsew")
self.cover_container_label_wrapper = CTkLabel(
self.cover_container,
textvariable=self._view_variable.VAR_LABEL_MAIN_WINDOW_COVER_MESSAGE,
height=0,
corner_radius=0,
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.TEXT_FONT_SIZE, weight="normal"),
anchor="w",
text_color=self.settings.ctm.TEXT_COLOR,
)
self.cover_container_label_wrapper.place(relx=0.5, rely=0.5, anchor="center")
def show(self, bind_focusin=None):
self.BIND_CONFIGURE_ADJUSTED_GEOMETRY_FUNC_ID = self.attach_window.bind("<Configure>", self._adjustToMainWindowGeometry, "+")
if bind_focusin is not None:
self.BIND_FOCUS_IN_FUNC_ID = self.bind("<FocusIn>", lambda _e: bind_focusin(), "+")
else:
self.BIND_FOCUS_IN_FUNC_ID = None
self.attributes("-alpha", 0)
self.deiconify()
self.attach_window.update_idletasks()
self.x_pos = self.attach_window.winfo_rootx()
self.y_pos = self.attach_window.winfo_rooty()
self.width_new = self.attach_window.winfo_width()
self.height_new = self.attach_window.winfo_height()
self.geometry("{}x{}+{}+{}".format(self.width_new, self.height_new, self.x_pos, self.y_pos))
fadeInAnimation(self, steps=5, interval=0.005, max_alpha=0.5)
def hide(self):
self.attach_window.unbind("<Configure>", self.BIND_CONFIGURE_ADJUSTED_GEOMETRY_FUNC_ID)
if self.BIND_FOCUS_IN_FUNC_ID is not None:
self.unbind("<FocusIn>", self.BIND_FOCUS_IN_FUNC_ID)
self.withdraw()
def _adjustToMainWindowGeometry(self, e=None):
self.attach_window.update_idletasks()
x_pos = self.attach_window.winfo_rootx()
y_pos = self.attach_window.winfo_rooty()
width_new = makeEven(self.attach_window.winfo_width())
height_new = makeEven(self.attach_window.winfo_height())
self.geometry("{}x{}+{}+{}".format(width_new, height_new, x_pos, y_pos))
self.lift()

1
vrct_gui/__init__.py Normal file
View File

@@ -0,0 +1 @@
from .vrct_gui import vrct_gui

View File

@@ -0,0 +1,40 @@
from customtkinter import CTkImage
def _changeConfigWindowWidgetsStatus(config_window, settings, view_variable, status, target_names):
# if target_names == "All":
# target_names = []
def disableOptionmenuWidget(target_widget):
target_widget.label_widget.configure(text_color=settings.ctm.LABELS_TEXT_DISABLED_COLOR)
if target_widget.desc_widget is not None:
target_widget.desc_widget.configure(text_color=settings.ctm.LABELS_TEXT_DISABLED_COLOR)
target_widget.optionmenu_label_widget.configure(text_color=settings.ctm.LABELS_TEXT_DISABLED_COLOR)
target_widget.optionmenu_img_widget.configure(image=CTkImage(settings.image_file.ARROW_LEFT_DISABLED.rotate(90), size=settings.uism.SB__OPTIONMENU_IMG_SIZE))
target_widget.optionmenu_box.unbindFunction()
target_widget.optionmenu_box.configure(cursor="")
for target_name in target_names:
match target_name:
case "sb__optionmenu_mic_host":
if status == "disabled":
target_widget = config_window.sb__widgets["sb__optionmenu_mic_host"]
disableOptionmenuWidget(target_widget)
case "sb__optionmenu_mic_device":
if status == "disabled":
target_widget = config_window.sb__widgets["sb__optionmenu_mic_device"]
disableOptionmenuWidget(target_widget)
case "sb__optionmenu_appearance_theme":
if status == "disabled":
target_widget = config_window.sb__widgets["sb__optionmenu_appearance_theme"]
disableOptionmenuWidget(target_widget)
case _:
raise ValueError(f"No matching case for target_name: {target_name}")
config_window.update()

View File

@@ -0,0 +1,158 @@
from customtkinter import CTkImage
hold_state_list=[]
def _changeMainWindowWidgetsStatus(vrct_gui, settings, view_variable, status, target_names:list, to_hold_state:bool=False):
global hold_state_list
if target_names == "All":
target_names = ["translation_switch", "transcription_send_switch", "transcription_receive_switch", "foreground_switch", "quick_language_settings", "config_button", "minimize_sidebar_button", "entry_message_box"]
for item in hold_state_list:
if item in target_names:
target_names.remove(item)
def update_switch_status(
widget_frame,
widget_label,
widget_switch_box,
widget_selected_mark,
widget_compact_mode_icon,
icon_name,
disabled_icon_name,
):
if status == "disabled":
widget_frame.configure(cursor="")
widget_label.configure(text_color=settings.ctm.SF__TEXT_DISABLED_COLOR)
widget_switch_box.configure(state="disabled", progress_color=settings.ctm.SF__SWITCH_BOX_DISABLE_BG_COLOR)
widget_selected_mark.configure(fg_color=settings.ctm.SF__SELECTED_MARK_DISABLE_BG_COLOR)
icon_file = disabled_icon_name
elif status == "normal":
widget_frame.configure(cursor="hand2")
widget_label.configure(text_color=settings.ctm.LABELS_TEXT_COLOR)
widget_switch_box.configure(state="normal", progress_color=settings.ctm.SF__SWITCH_BOX_ACTIVE_BG_COLOR)
widget_selected_mark.configure(fg_color=settings.ctm.SF__SELECTED_MARK_ACTIVE_BG_COLOR)
icon_file = icon_name
image = CTkImage(icon_file, size=settings.uism.SF__COMPACT_MODE_IMAGE_SIZE)
widget_compact_mode_icon.configure(image=image)
for target_name in target_names:
match target_name:
case "translation_switch":
update_switch_status(
widget_frame=vrct_gui.translation_frame,
widget_label=vrct_gui.label_translation,
widget_switch_box=vrct_gui.translation_switch_box,
widget_selected_mark=vrct_gui.translation_selected_mark,
widget_compact_mode_icon=vrct_gui.translation_compact_mode_icon,
icon_name=settings.image_file.TRANSLATION_ICON,
disabled_icon_name=settings.image_file.TRANSLATION_ICON_DISABLED
)
case "transcription_send_switch":
update_switch_status(
widget_frame=vrct_gui.transcription_send_frame,
widget_label=vrct_gui.label_transcription_send,
widget_switch_box=vrct_gui.transcription_send_switch_box,
widget_selected_mark=vrct_gui.transcription_send_selected_mark,
widget_compact_mode_icon=vrct_gui.transcription_send_compact_mode_icon,
icon_name=settings.image_file.MIC_ICON,
disabled_icon_name=settings.image_file.MIC_ICON_DISABLED
)
case "transcription_receive_switch":
update_switch_status(
widget_frame=vrct_gui.transcription_receive_frame,
widget_label=vrct_gui.label_transcription_receive,
widget_switch_box=vrct_gui.transcription_receive_switch_box,
widget_selected_mark=vrct_gui.transcription_receive_selected_mark,
widget_compact_mode_icon=vrct_gui.transcription_receive_compact_mode_icon,
icon_name=settings.image_file.HEADPHONES_ICON,
disabled_icon_name=settings.image_file.HEADPHONES_ICON_DISABLED
)
case "foreground_switch":
update_switch_status(
widget_frame=vrct_gui.foreground_frame,
widget_label=vrct_gui.label_foreground,
widget_switch_box=vrct_gui.foreground_switch_box,
widget_selected_mark=vrct_gui.foreground_selected_mark,
widget_compact_mode_icon=vrct_gui.foreground_compact_mode_icon,
icon_name=settings.image_file.FOREGROUND_ICON,
disabled_icon_name=settings.image_file.FOREGROUND_ICON_DISABLED
)
case "quick_language_settings":
if status == "disabled":
vrct_gui.sls__container_title.configure(text_color=settings.ctm.SF__TEXT_DISABLED_COLOR)
vrct_gui.sls__title_text_your_language.configure(text_color=settings.ctm.SF__TEXT_DISABLED_COLOR)
vrct_gui.sls__title_text_target_language.configure(text_color=settings.ctm.SF__TEXT_DISABLED_COLOR)
if view_variable.IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE is False:
vrct_gui.current_active_preset_tab.children["!ctklabel"].configure(text_color=settings.ctm.SLS__PRESETS_TAB_ACTIVE_TEXT_COLOR_PASSIVE)
elif status == "normal":
vrct_gui.sls__container_title.configure(text_color=settings.ctm.LABELS_TEXT_COLOR)
vrct_gui.sls__title_text_your_language.configure(text_color=settings.ctm.LABELS_TEXT_COLOR)
vrct_gui.sls__title_text_target_language.configure(text_color=settings.ctm.LABELS_TEXT_COLOR)
if view_variable.IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE is False:
vrct_gui.current_active_preset_tab.children["!ctklabel"].configure(text_color=settings.ctm.SLS__PRESETS_TAB_ACTIVE_TEXT_COLOR)
vrct_gui.current_active_preset_tab.children["!ctklabel"].configure(text_color=settings.ctm.SLS__PRESETS_TAB_ACTIVE_TEXT_COLOR)
case "config_button":
if status == "disabled":
vrct_gui.sidebar_config_button_wrapper.configure(cursor="")
vrct_gui.sidebar_config_button.configure(
image=CTkImage(settings.image_file.CONFIGURATION_ICON_DISABLED, size=settings.uism.SF__COMPACT_MODE_IMAGE_SIZE),
)
elif status == "normal":
vrct_gui.sidebar_config_button_wrapper.configure(cursor="hand2")
vrct_gui.sidebar_config_button.configure(
image=CTkImage(settings.image_file.CONFIGURATION_ICON, size=settings.uism.SF__COMPACT_MODE_IMAGE_SIZE),
)
case "minimize_sidebar_button":
MINIMIZE_SIDEBAR_IMAGE_SIZE = vrct_gui.minimize_sidebar_button__for_opening.cget("image").cget("size")
if status == "disabled":
vrct_gui.minimize_sidebar_button_container__for_opening.configure(cursor="")
vrct_gui.minimize_sidebar_button_container__for_closing.configure(cursor="")
image_file__for_opening = CTkImage((settings.image_file.ARROW_LEFT_DISABLED).rotate(180), size=MINIMIZE_SIDEBAR_IMAGE_SIZE)
image_file__for_closing = CTkImage((settings.image_file.ARROW_LEFT_DISABLED), size=MINIMIZE_SIDEBAR_IMAGE_SIZE)
elif status == "normal":
vrct_gui.minimize_sidebar_button_container__for_opening.configure(cursor="hand2")
vrct_gui.minimize_sidebar_button_container__for_closing.configure(cursor="hand2")
image_file__for_opening = CTkImage((settings.image_file.ARROW_LEFT).rotate(180), size=MINIMIZE_SIDEBAR_IMAGE_SIZE)
image_file__for_closing = CTkImage((settings.image_file.ARROW_LEFT), size=MINIMIZE_SIDEBAR_IMAGE_SIZE)
vrct_gui.minimize_sidebar_button__for_opening.configure(image=image_file__for_opening)
vrct_gui.minimize_sidebar_button__for_closing.configure(image=image_file__for_closing)
case "entry_message_box":
if status == "disabled":
vrct_gui.entry_message_box.configure(state="disabled", placeholder_text_color=settings.ctm.TEXTBOX_ENTRY_PLACEHOLDER_DISABLED_COLOR, text_color=settings.ctm.TEXTBOX_ENTRY_TEXT_DISABLED_COLOR)
elif status == "normal":
vrct_gui.entry_message_box.configure(state="normal", placeholder_text_color=settings.ctm.TEXTBOX_ENTRY_PLACEHOLDER_COLOR, text_color=settings.ctm.TEXTBOX_ENTRY_TEXT_COLOR)
case _:
raise ValueError(f"No matching case for target_name: {target_name}")
if to_hold_state is True:
for item in target_names:
if item not in hold_state_list:
hold_state_list.append(item)
vrct_gui.update()

View File

@@ -0,0 +1,98 @@
from datetime import datetime
from customtkinter import CTkFont
def _printToTextbox(vrct_gui,
settings,
target_type,
original_message=None,
translated_message=None,
tags=None,
disable_print_to_textbox_all:bool=False,
):
now_raw_data = datetime.now()
# now = now_raw_data.strftime("%H:%M:%S")
now_hm = now_raw_data.strftime("%H:%M")
# set target textbox widget
is_only_one_message = True if original_message is None or translated_message is None or translated_message == "" else False
match (target_type):
case "SYSTEM":
target_textbox = vrct_gui.textbox_system
case "SENT":
target_textbox = vrct_gui.textbox_sent
case "RECEIVED":
target_textbox = vrct_gui.textbox_received
case (_):
raise ValueError(f"No matching case for target_type: {target_type}")
def printEachTextbox(target_textbox):
target_textbox.tag_config("JUSTIFY_CENTER", justify="center")
target_textbox.tag_config("JUSTIFY_RIGHT", justify="right")
target_textbox.tag_config("JUSTIFY_LEFT", justify="left")
# common tag settings
# target_textbox._textbox.tag_configure("START", spacing1=16)
target_textbox._textbox.tag_configure("LABEL", font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TEXTBOX_FONT_SIZE__LABEL, weight="normal"))
target_textbox._textbox.tag_configure("TIMESTAMP", font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TEXTBOX_FONT_SIZE__TIMESTAMP, weight="normal"), foreground=settings.ctm.TEXTBOX_TIMESTAMP_TEXT_COLOR)
target_textbox._textbox.tag_configure("SECONDARY_TEXT_FONT", font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TEXTBOX_FONT_SIZE__SECONDARY_TEXT_FONT, weight="normal"))
target_textbox._textbox.tag_configure("MAIN_TEXT_FONT", font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TEXTBOX_FONT_SIZE__MAIN_TEXT_FONT, weight="normal"))
# System Tag Settings
target_textbox.tag_config("FIRST_INSERT_SPACING", spacing1=settings.uism.TEXTBOX_FIRST_INSERT_SPACING)
target_textbox.tag_config("SYSTEM_TAG", foreground=settings.ctm.TEXTBOX_SYSTEM_TAG_TEXT_COLOR)
target_textbox.tag_config("SYSTEM_TEXT", foreground=settings.ctm.TEXTBOX_TEXT_SUB_COLOR)
target_textbox._textbox.tag_configure("SYSTEM_TEXT_FONT", font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TEXTBOX_FONT_SIZE__SYSTEM_TEXT_FONT, weight="normal"))
# Sent Tag Settings
target_textbox.tag_config("SENT_TAG", foreground=settings.ctm.TEXTBOX_SENT_TAG_TEXT_COLOR)
target_textbox.tag_config("SENT_TEXT", foreground=settings.ctm.TEXTBOX_TEXT_COLOR)
target_textbox.tag_config("SENT_SUB_TEXT", foreground=settings.ctm.TEXTBOX_TEXT_SUB_COLOR)
# Received Tag Settings
target_textbox.tag_config("RECEIVED_TAG", foreground=settings.ctm.TEXTBOX_RECEIVED_TAG_TEXT_COLOR)
target_textbox.tag_config("RECEIVED_TEXT", foreground=settings.ctm.TEXTBOX_TEXT_COLOR)
target_textbox.tag_config("RECEIVED_SUB_TEXT", foreground=settings.ctm.TEXTBOX_TEXT_SUB_COLOR)
FAKE_MARGIN = " "
# insert
target_textbox.configure(state="normal")
target_textbox.insert("end", "\n")
match (target_type):
case "SYSTEM":
target_textbox.insert("end", "System", ("SYSTEM_TAG", "FIRST_INSERT_SPACING", "JUSTIFY_CENTER", "LABEL"))
target_textbox.insert("end", FAKE_MARGIN+original_message+FAKE_MARGIN, ("SYSTEM_TEXT", "SYSTEM_TEXT_FONT", "JUSTIFY_CENTER"))
target_textbox.insert("end", now_hm, ("TIMESTAMP", "JUSTIFY_CENTER"))
case "SENT":
target_textbox.insert("end", now_hm, ("TIMESTAMP", "FIRST_INSERT_SPACING", "JUSTIFY_RIGHT"))
target_textbox.insert("end", FAKE_MARGIN+"Sent", ("SENT_TAG", "LABEL"))
target_textbox.insert("end", "\n")
if is_only_one_message is False:
target_textbox.insert("end", original_message, ("SENT_SUB_TEXT", "SECONDARY_TEXT_FONT", "JUSTIFY_RIGHT"))
target_textbox.insert("end", "\n")
target_textbox.insert("end", translated_message, ("SENT_TEXT", "MAIN_TEXT_FONT", "JUSTIFY_RIGHT"))
else:
target_textbox.insert("end", original_message, ("SENT_TEXT", "MAIN_TEXT_FONT", "JUSTIFY_RIGHT"))
case "RECEIVED":
target_textbox.insert("end", "Received", ("RECEIVED_TAG", "FIRST_INSERT_SPACING", "JUSTIFY_LEFT", "LABEL"))
target_textbox.insert("end", FAKE_MARGIN+now_hm, ("TIMESTAMP"))
if is_only_one_message is False:
target_textbox.insert("end", "\n")
target_textbox.insert("end", original_message, ("RECEIVED_SUB_TEXT", "SECONDARY_TEXT_FONT"))
target_textbox.insert("end", "\n")
target_textbox.insert("end", translated_message, ("RECEIVED_TEXT", "MAIN_TEXT_FONT", "JUSTIFY_LEFT"))
else:
target_textbox.insert("end", "\n")
target_textbox.insert("end", original_message, ("RECEIVED_TEXT", "MAIN_TEXT_FONT", "JUSTIFY_LEFT"))
target_textbox.configure(state="disabled")
target_textbox.see("end")
printEachTextbox(target_textbox)
# To automatically print the same log to the textbox_all widget as well.
if disable_print_to_textbox_all is not True: printEachTextbox(vrct_gui.textbox_all)

View File

@@ -0,0 +1,66 @@
from .widgets import createConfigWindowTitle, createSideMenuAndSettingsBoxContainers, createSettingBoxTopBar
from customtkinter import CTkToplevel, CTkFrame, CTkLabel, CTkFont
from ..ui_utils import getImagePath, getLatestWidth
from utils import isEven
class ConfigWindow(CTkToplevel):
def __init__(self, vrct_gui, settings, view_variable):
super().__init__()
self.withdraw()
self.settings = settings
self._view_variable = view_variable
# configure window
self.after(200, lambda: self.iconbitmap(getImagePath("vrct_logo_mark_black.ico")))
self.geometry(f"{self.settings.uism.DEFAULT_WIDTH}x{self.settings.uism.DEFAULT_HEIGHT}")
self.configure(fg_color=self.settings.ctm.MAIN_BG_COLOR)
self.protocol("WM_DELETE_WINDOW", self._view_variable.CALLBACK_CLICKED_CLOSE_CONFIG_WINDOW_BUTTON)
self.title(self._view_variable.VAR_CONFIG_WINDOW_TITLE.get())
# When the configuration window's compact mode is turned on, it will call `grid_remove()` on each widget appended to this array. In the opposite case, `grid()` will be called.
self.additional_widgets = []
self.sb__widgets = {}
createConfigWindowTitle(config_window=self, settings=self.settings, view_variable=self._view_variable)
createSettingBoxTopBar(config_window=self, settings=self.settings, view_variable=self._view_variable)
createSideMenuAndSettingsBoxContainers(config_window=self, settings=self.settings, view_variable=self._view_variable)
# for fixing 1px bug
l_width = getLatestWidth(self.side_menu_bg_container)
if isEven(l_width) is False:
self.side_menu_bg_container.grid_columnconfigure(0, weight=0, minsize=l_width+1)
# for fixing 1px bug
self.side_menu_bg_container.grid_rowconfigure(2, weight=1)
sls__box_optionmenu_wrapper_fix_1px_bug = CTkFrame(self.side_menu_bg_container, corner_radius=0, width=0, height=0)
sls__box_optionmenu_wrapper_fix_1px_bug.grid(row=3, column=0, sticky="sew")
# for fixing 1px bug
l_width = getLatestWidth(self.side_menu_bg_container)
# VRCT Now Version Label(Tmp)
version_label = CTkLabel(
self.side_menu_bg_container,
textvariable=self._view_variable.VAR_VERSION,
height=0,
corner_radius=0,
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.NOW_VERSION_FONT_SIZE, weight="normal"),
anchor="w",
text_color=self.settings.ctm.NOW_VERSION_TEXT_COLOR,
)
version_label.place(relx=0.05, rely=0.99, anchor="sw")
self.bind_all("<Button-1>", lambda event: event.widget.focus_set(), "+")

View File

@@ -0,0 +1 @@
from .ConfigWindow import ConfigWindow

View File

@@ -0,0 +1,4 @@
from .createConfigWindowTitle import createConfigWindowTitle
from .createSettingBoxTopBar import createSettingBoxTopBar
from .createSideMenuAndSettingsBoxContainers import createSideMenuAndSettingsBoxContainers

View File

@@ -0,0 +1,36 @@
from customtkinter import CTkFont, CTkFrame, CTkLabel, CTkImage
def createConfigWindowTitle(config_window, settings, view_variable):
config_window.grid_columnconfigure(0, weight=0, minsize=settings.uism.TOP_BAR_SIDE_AREA_MIN_WIDTH)
config_window.grid_rowconfigure(0, weight=0, minsize=settings.uism.TOP_BAR__MIN_HEIGHT)
config_window.side_menu_config_window_title_logo_frame = CTkFrame(config_window, corner_radius=0, fg_color=settings.ctm.TOP_BAR_BG_COLOR, width=0, height=0)
config_window.side_menu_config_window_title_logo_frame.grid(row=0, column=0, sticky="nsew")
config_window.side_menu_config_window_title_logo_frame.grid_rowconfigure(0,weight=1)
config_window.side_menu_config_window_title_logo_frame.grid_columnconfigure(0,weight=1)
config_window.side_menu_config_window_title_logo_wrapper = CTkFrame(config_window.side_menu_config_window_title_logo_frame, corner_radius=0, fg_color=settings.ctm.TOP_BAR_BG_COLOR, width=0, height=0)
config_window.side_menu_config_window_title_logo_wrapper.grid(row=0, column=0, padx=settings.uism.TOP_BAR_SIDE__TITLE_PADX, pady=settings.uism.TOP_BAR__IPADY, sticky="nsew")
config_window.side_menu_config_window_title_logo_wrapper.grid_rowconfigure(0,weight=1)
config_window.side_menu_config_window_title = CTkLabel(
config_window.side_menu_config_window_title_logo_frame,
textvariable=view_variable.VAR_CONFIG_WINDOW_TITLE,
height=0,
anchor="w",
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TOP_BAR_SIDE__CONFIG_TITLE_FONT_SIZE, weight="bold"),
text_color=settings.ctm.LABELS_TEXT_COLOR,
)
config_window.side_menu_config_window_title.place(relx=0.255, rely=0.5, anchor="w")
config_window.side_menu_config_window_title_logo = CTkLabel(
config_window.side_menu_config_window_title_logo_frame,
text=None,
height=0,
anchor="w",
image=CTkImage(settings.image_file.VRCT_LOGO_MARK, size=settings.uism.TOP_BAR_SIDE__CONFIG_LOGO_MARK_SIZE),
)
config_window.side_menu_config_window_title_logo.place(relx=0.08, rely=0.58, anchor="w")

View File

@@ -0,0 +1 @@
from .createSettingBoxTopBar import createSettingBoxTopBar

View File

@@ -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()

View File

@@ -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)

View File

@@ -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")

View File

@@ -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")

View File

@@ -0,0 +1 @@
from .createSideMenuAndSettingsBoxContainers import createSideMenuAndSettingsBoxContainers

View File

@@ -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)

View File

@@ -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,
)

View File

@@ -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()

View File

@@ -0,0 +1,570 @@
from functools import partial
from types import SimpleNamespace
from typing import Union
from customtkinter import CTkOptionMenu, CTkFont, CTkFrame, CTkLabel, CTkRadioButton, CTkEntry, CTkSlider, CTkSwitch, CTkCheckBox, CTkProgressBar
from vrct_gui.ui_utils import createButtonWithImage, getLatestWidth, createOptionMenuBox
from vrct_gui import vrct_gui
SETTING_BOX_COLUMN = 1
class _SettingBoxGenerator():
def __init__(self, parent_widget, config_window, settings, view_variable):
self.view_variable = view_variable
self.config_window = config_window
self.parent_widget = parent_widget
self.settings = settings
self.dropdown_menu_window = vrct_gui.vrct_gui.dropdown_menu_window
def _createSettingBoxFrame(self, sb__attr_name, for_var_label_text=None, for_var_desc_text=None):
self.config_window.sb__widgets[sb__attr_name] = SimpleNamespace()
setting_box_frame = CTkFrame(self.parent_widget, corner_radius=0, fg_color=self.settings.ctm.SB__BG_COLOR, width=0, height=0)
# "pady=(0,1)" is for bottom padding. It can be removed(override) when you do like "self.attr_name.grid(row=row, pady=0)"
setting_box_frame.grid(column=0, padx=0, pady=self.settings.uism.SB__FAKE_BOTTOM_BORDER_SIZE, sticky="ew")
setting_box_frame.grid_columnconfigure(0, weight=1)
setting_box_frame_wrapper = CTkFrame(setting_box_frame, corner_radius=0, fg_color=self.settings.ctm.SB__BG_COLOR, width=0, height=0)
setting_box_frame_wrapper.grid(row=0, column=0, padx=self.settings.uism.SB__IPADX, pady=self.settings.uism.SB__IPADY, sticky="ew")
setting_box_frame_wrapper.grid_columnconfigure(0, weight=0, minsize=int(self.settings.uism.MAIN_AREA_MIN_WIDTH / 2))
setting_box_frame_wrapper.grid_columnconfigure(2, weight=1, minsize=int(self.settings.uism.MAIN_AREA_MIN_WIDTH / 2))
setting_box_frame_wrapper_fix_border = CTkFrame(setting_box_frame, corner_radius=0, width=0, height=0)
setting_box_frame_wrapper_fix_border.grid(row=1, column=0, sticky="ew")
setting_box_frame_wrapper_fix_border2 = CTkFrame(setting_box_frame, corner_radius=0, width=0, height=0)
setting_box_frame_wrapper_fix_border2.grid(row=0, column=1, sticky="ns")
if for_var_label_text is not None:
self._setSettingBoxLabels(sb__attr_name, setting_box_frame_wrapper, for_var_label_text, for_var_desc_text)
# setting_box_item_frame = CTkFrame(setting_box_frame_wrapper, corner_radius=0, width=0, height=0, fg_color="red")
setting_box_item_frame = CTkFrame(setting_box_frame_wrapper, corner_radius=0, width=0, height=0, fg_color=self.settings.ctm.SB__BG_COLOR)
if for_var_label_text is not None:
setting_box_item_frame.grid(row=0, column=2, padx=0, sticky="nsew")
else:
setting_box_item_frame.grid(row=0, columnspan=3, padx=0, sticky="nsew")
setting_box_item_frame.grid_rowconfigure((0,2), weight=1)
setting_box_item_frame.grid_columnconfigure(0, weight=1)
return (setting_box_frame, setting_box_item_frame)
def _setSettingBoxLabels(self, sb__attr_name, setting_box_frame_wrapper, for_var_label_text, for_var_desc_text=None):
setting_box_labels_frame = CTkFrame(setting_box_frame_wrapper, corner_radius=0, fg_color=self.settings.ctm.SB__BG_COLOR, width=0, height=0)
setting_box_labels_frame.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
setting_box_labels_frame.grid_rowconfigure((0,3), weight=1)
setting_box_label = CTkLabel(
setting_box_labels_frame,
textvariable=for_var_label_text,
anchor="w",
height=0,
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__LABEL_FONT_SIZE, weight="normal"),
text_color=self.settings.ctm.LABELS_TEXT_COLOR
)
setting_box_label.grid(row=1, column=0, padx=0, pady=0, sticky="ew")
self.config_window.sb__widgets[sb__attr_name].label_widget = setting_box_label
if for_var_desc_text is not None:
setting_box_desc = CTkLabel(
setting_box_labels_frame,
textvariable=for_var_desc_text,
anchor="w",
justify="left",
height=0,
wraplength=int(self.settings.uism.MAIN_AREA_MIN_WIDTH / 2),
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__DESC_FONT_SIZE, weight="normal"),
text_color=self.settings.ctm.LABELS_DESC_TEXT_COLOR
)
setting_box_desc.grid(row=2, column=0, padx=0, pady=(self.settings.uism.SB__DESC_TOP_PADY,0), sticky="ew")
self.config_window.additional_widgets.append(setting_box_desc)
self.config_window.sb__widgets[sb__attr_name].desc_widget=setting_box_desc
else:
self.config_window.sb__widgets[sb__attr_name].desc_widget=None
def createSettingBoxDropdownMenu(
self,
for_var_label_text, for_var_desc_text,
optionmenu_attr_name,
command,
dropdown_menu_min_width=None,
dropdown_menu_values=None,
variable=None,
):
(setting_box_frame, setting_box_item_frame) = self._createSettingBoxFrame(optionmenu_attr_name, for_var_label_text, for_var_desc_text)
def adjustedCommand(value):
variable.set(value)
command(value)
(option_menu_widget, optionmenu_label_widget, optionmenu_img_widget) = createOptionMenuBox(
parent_widget=setting_box_item_frame,
optionmenu_bg_color=self.settings.ctm.SB__OPTIONMENU_BG_COLOR,
optionmenu_hovered_bg_color=self.settings.ctm.SB__OPTIONMENU_HOVERED_BG_COLOR,
optionmenu_clicked_bg_color=self.settings.ctm.SB__OPTIONMENU_CLICKED_BG_COLOR,
optionmenu_ipadx=self.settings.uism.SB__OPTIONMENU_IPADX,
optionmenu_ipady=self.settings.uism.SB__OPTIONMENU_IPADY,
optionmenu_padx_between_img=self.settings.uism.SB__OPTIONMENU_IPADX_BETWEEN_IMG,
optionmenu_min_height=self.settings.uism.SB__OPTIONMENU_MIN_HEIGHT,
optionmenu_min_width=self.settings.uism.SB__OPTIONMENU_MIN_WIDTH,
variable=variable,
font_family=self.settings.FONT_FAMILY,
font_size=self.settings.uism.SB__OPTION_MENU_FONT_SIZE,
text_color=self.settings.ctm.LABELS_TEXT_COLOR,
image_file=self.settings.image_file.ARROW_LEFT.rotate(90),
image_size=self.settings.uism.SB__OPTIONMENU_IMG_SIZE,
optionmenu_clicked_command=lambda _e: self.dropdown_menu_window.show(
dropdown_menu_widget_id=optionmenu_attr_name,
),
)
self.config_window.sb__widgets[optionmenu_attr_name].optionmenu_box = option_menu_widget
self.config_window.sb__widgets[optionmenu_attr_name].optionmenu_label_widget = optionmenu_label_widget
self.config_window.sb__widgets[optionmenu_attr_name].optionmenu_img_widget = optionmenu_img_widget
option_menu_widget.grid(row=1, column=SETTING_BOX_COLUMN, sticky="e")
setattr(self.config_window, optionmenu_attr_name, option_menu_widget)
self.dropdown_menu_window.createDropdownMenuBox(
dropdown_menu_widget_id=optionmenu_attr_name,
dropdown_menu_values=dropdown_menu_values,
command=adjustedCommand,
wrapper_widget=self.config_window.main_bg_container,
attach_widget=option_menu_widget,
dropdown_menu_min_width=dropdown_menu_min_width,
)
return setting_box_frame
def createSettingBoxSwitch(self,
for_var_label_text, for_var_desc_text,
switch_attr_name,
variable,
command,
):
(setting_box_frame, setting_box_item_frame) = self._createSettingBoxFrame(switch_attr_name, for_var_label_text, for_var_desc_text)
switch_widget = CTkSwitch(
setting_box_item_frame,
text=None,
height=0,
width=0,
corner_radius=int(self.settings.uism.SB__SWITCH_BOX_HEIGHT/2),
border_width=0,
switch_height=self.settings.uism.SB__SWITCH_BOX_HEIGHT,
switch_width=self.settings.uism.SB__SWITCH_BOX_WIDTH,
onvalue=True,
offvalue=False,
variable=variable,
command=command,
fg_color=self.settings.ctm.SB__SWITCH_BOX_BG_COLOR,
# bg_color="red",
progress_color=self.settings.ctm.SB__SWITCH_BOX_ACTIVE_BG_COLOR,
button_color=self.settings.ctm.SB__SWITCH_BOX_BUTTON_COLOR,
button_hover_color=self.settings.ctm.SB__SWITCH_BOX_BUTTON_HOVERED_COLOR,
)
setattr(self.config_window, switch_attr_name, switch_widget)
switch_widget.grid(row=1, column=SETTING_BOX_COLUMN, sticky="e")
return setting_box_frame
def createSettingBoxCheckbox(self,
for_var_label_text, for_var_desc_text,
checkbox_attr_name,
command,
variable,
):
(setting_box_frame, setting_box_item_frame) = self._createSettingBoxFrame(checkbox_attr_name, for_var_label_text, for_var_desc_text)
checkbox_widget = CTkCheckBox(
setting_box_item_frame,
text=None,
width=0,
checkbox_width=self.settings.uism.SB__CHECKBOX_SIZE,
checkbox_height=self.settings.uism.SB__CHECKBOX_SIZE,
onvalue=True,
offvalue=False,
variable=variable,
command=command,
corner_radius=self.settings.uism.SB__CHECKBOX_CORNER_RADIUS,
border_width=self.settings.uism.SB__CHECKBOX_BORDER_WIDTH,
border_color=self.settings.ctm.SB__CHECKBOX_BORDER_COLOR,
hover_color=self.settings.ctm.SB__CHECKBOX_HOVER_COLOR,
checkmark_color=self.settings.ctm.SB__CHECKBOX_CHECKMARK_COLOR,
fg_color=self.settings.ctm.SB__CHECKBOX_CHECKED_COLOR,
# fg_color=self.settings.ctm.SB__SWITCH_BOX_BG_COLOR,
# bg_color="red",
# progress_color=self.settings.ctm.SB__SWITCH_BOX_ACTIVE_BG_COLOR,
)
setattr(self.config_window, checkbox_attr_name, checkbox_widget)
checkbox_widget.grid(row=1, column=SETTING_BOX_COLUMN, sticky="e")
return setting_box_frame
def createSettingBoxSlider(
self,
for_var_label_text, for_var_desc_text,
slider_attr_name,
slider_range,
command,
variable,
slider_number_of_steps: Union[int,
None] = None,
slider_bind__ButtonPress=None,
slider_bind__ButtonRelease=None
):
(setting_box_frame, setting_box_item_frame) = self._createSettingBoxFrame(slider_attr_name, for_var_label_text, for_var_desc_text)
slider_widget = CTkSlider(
setting_box_item_frame,
width=self.settings.uism.SB__SLIDER_WIDTH,
height=self.settings.uism.SB__SLIDER_HEIGHT,
from_=slider_range[0],
to=slider_range[1],
number_of_steps=slider_number_of_steps,
fg_color=self.settings.ctm.SB__SLIDER_BG_COLOR,
progress_color=self.settings.ctm.SB__SLIDER_PROGRESS_BG_COLOR,
button_color=self.settings.ctm.SB__SLIDER_BUTTON_COLOR,
button_hover_color=self.settings.ctm.SB__SLIDER_BUTTON_HOVERED_COLOR,
command=command,
variable=variable,
)
setattr(self.config_window, slider_attr_name, slider_widget)
slider_widget.grid(row=1, column=SETTING_BOX_COLUMN, sticky="e")
if slider_bind__ButtonPress is not None:
def adjusted_slider_bind__ButtonPress(_e):
command(_e)
slider_bind__ButtonPress()
slider_widget.configure(command=adjusted_slider_bind__ButtonPress)
if slider_bind__ButtonRelease is not None:
def adjusted_slider_bind__ButtonRelease(_e):
slider_bind__ButtonRelease()
slider_widget.bind("<ButtonRelease>", adjusted_slider_bind__ButtonRelease, "+")
return setting_box_frame
def createSettingBoxProgressbarXSlider(
self,
command, progressbar_x_slider_attr_name,
entry_attr_name, entry_bind__FocusOut,
slider_attr_name, slider_range,
progressbar_attr_name,
passive_button_attr_name, passive_button_command,
active_button_attr_name, active_button_command,
disabled_button_attr_name, disabled_button_image_file,
button_image_file,
entry_variable,
slider_variable,
slider_number_of_steps: Union[int, None] = None,
):
(setting_box_frame, setting_box_item_frame) = self._createSettingBoxFrame(progressbar_x_slider_attr_name)
def adjusted_command__for_entry_bind__Any_KeyRelease(e):
command(e.widget.get())
def adjusted_command__for_slider(value):
command(value)
setting_box_item_frame.grid_columnconfigure((0,2), weight=0)
setting_box_item_frame.grid_columnconfigure(1, weight=1)
entry_widget = CTkEntry(
setting_box_item_frame,
text_color=self.settings.ctm.SB__ENTRY_TEXT_COLOR,
fg_color=self.settings.ctm.SB__ENTRY_BG_COLOR,
border_color=self.settings.ctm.SB__ENTRY_BORDER_COLOR,
width=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__ENTRY_WIDTH,
height=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__ENTRY_HEIGHT,
textvariable=entry_variable,
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__ENTRY_FONT_SIZE, weight="normal"),
)
entry_widget.bind("<Any-KeyRelease>", adjusted_command__for_entry_bind__Any_KeyRelease)
if entry_bind__FocusOut is not None:
entry_widget.bind("<FocusOut>", entry_bind__FocusOut, "+")
entry_widget.grid(row=1, column=2, padx=0, pady=0, sticky="e")
setattr(self.config_window, entry_attr_name, entry_widget)
# at least 2px is needed otherwise the slider button is gonna broken.
SLIDER_BORDER_WIDTH = max(2,self.settings.uism.SB__PROGRESSBAR_X_SLIDER__SLIDER_BUTTON_LENGTH)
SLIDER_BUTTON_LENGTH = int(SLIDER_BORDER_WIDTH/2)
slider_widget = CTkSlider(
setting_box_item_frame,
from_=slider_range[0],
to=slider_range[1],
number_of_steps=slider_number_of_steps,
command=adjusted_command__for_slider,
variable=slider_variable,
height=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__SLIDER_HEIGHT,
border_width=0,
button_length=SLIDER_BORDER_WIDTH,
button_corner_radius=SLIDER_BUTTON_LENGTH,
corner_radius=0,
button_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__SLIDER_BUTTON_COLOR,
button_hover_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__SLIDER_BUTTON_HOVERED_COLOR,
fg_color=self.settings.ctm.SB__BG_COLOR,
progress_color=self.settings.ctm.SB__BG_COLOR,
border_color=self.settings.ctm.SB__BG_COLOR,
)
slider_widget.grid(row=1, column=1, padx=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__BAR_PADX, sticky="ew")
setattr(self.config_window, slider_attr_name, slider_widget)
progressbar_widget = CTkProgressBar(
setting_box_item_frame,
height=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__PROGRESSBAR_HEIGHT,
corner_radius=0,
fg_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__PROGRESSBAR_BG_COLOR,
progress_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__PROGRESSBAR_PROGRESS_BG_COLOR,
)
setattr(self.config_window, progressbar_attr_name, progressbar_widget)
progressbar_widget.grid(row=1, column=1, padx=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__BAR_PADX, sticky="ew")
progressbar_widget.set(0)
passive_button_wrapper = self._createPassiveButtonForProgressbarXSlider(setting_box_item_frame, passive_button_command, button_image_file)
setattr(self.config_window, passive_button_attr_name, passive_button_wrapper)
disabled_button_wrapper = self._createDisabledButtonForProgressbarXSlider(setting_box_item_frame, disabled_button_image_file)
setattr(self.config_window, disabled_button_attr_name, disabled_button_wrapper)
active_button_wrapper = self._createActiveButtonForProgressbarXSlider(setting_box_item_frame, active_button_command, button_image_file)
setattr(self.config_window, active_button_attr_name, active_button_wrapper)
passive_button_wrapper.grid(row=1, column=0, padx=0, sticky="w")
passive_button_wrapper.configure(corner_radius=int(getLatestWidth(passive_button_wrapper)/2))
disabled_button_wrapper.grid(row=1, column=0, padx=0, sticky="w")
disabled_button_wrapper.configure(corner_radius=int(getLatestWidth(passive_button_wrapper)/2))
active_button_wrapper.grid(row=1, column=0, padx=0, sticky="w")
active_button_wrapper.configure(corner_radius=int(getLatestWidth(passive_button_wrapper)/2))
passive_button_wrapper.grid_remove()
disabled_button_wrapper.grid_remove()
active_button_wrapper.grid_remove()
passive_button_wrapper.grid()
return setting_box_frame
def createSettingBoxEntry(self,
for_var_label_text, for_var_desc_text,
entry_attr_name,
entry_width,
entry_textvariable,
entry_bind__Any_KeyRelease,
entry_bind__FocusOut=None,
):
(setting_box_frame, setting_box_item_frame) = self._createSettingBoxFrame(entry_attr_name, for_var_label_text, for_var_desc_text)
def adjusted_command__for_entry_bind__Any_KeyRelease(e):
entry_bind__Any_KeyRelease(e.widget.get())
entry_widget = CTkEntry(
setting_box_item_frame,
text_color=self.settings.ctm.SB__ENTRY_TEXT_COLOR,
fg_color=self.settings.ctm.SB__ENTRY_BG_COLOR,
border_color=self.settings.ctm.SB__ENTRY_BORDER_COLOR,
width=entry_width,
height=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__ENTRY_HEIGHT,
textvariable=entry_textvariable,
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__ENTRY_FONT_SIZE, weight="normal"),
)
entry_widget.bind("<Any-KeyRelease>", adjusted_command__for_entry_bind__Any_KeyRelease)
setattr(self.config_window, entry_attr_name, entry_widget)
entry_widget.grid(row=1, column=SETTING_BOX_COLUMN, sticky="e")
if entry_bind__FocusOut is not None:
entry_widget.bind("<FocusOut>", entry_bind__FocusOut, "+")
return setting_box_frame
# if setting_box_type == "dropdown_menu_x_dropdown_menu":
# self.setting_box_dropdown_menu_x_dropdown_menu = CTkFrame(self.setting_box, corner_radius=0, fg_color=self.settings.ctm.SB__BG_COLOR, width=0, height=0)
# self.setting_box_dropdown_menu_x_dropdown_menu.grid(row=0, column=1, padx=(0, self.settings.uism.SB__RIGHT_PADX), rowspan=2, sticky="e")
# # Labels
# self.optionmenu_label_left = CTkLabel(
# self.setting_box_dropdown_menu_x_dropdown_menu,
# text=kwargs["left_dropdown_menu_label"],
# font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__OPTION_MENU_FONT_SIZE, weight="normal"),
# )
# self.optionmenu_label_left.grid(row=0, column=0)
# self.the_space_between_optionmenu = CTkLabel(
# self.setting_box_dropdown_menu_x_dropdown_menu,
# text=None,
# )
# self.the_space_between_optionmenu.grid(row=0, column=1)
# self.optionmenu_label_right = CTkLabel(
# self.setting_box_dropdown_menu_x_dropdown_menu,
# text=kwargs["right_dropdown_menu_label"],
# font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__OPTION_MENU_FONT_SIZE, weight="normal"),
# )
# self.optionmenu_label_right.grid(row=0, column=2)
# # Option menus
# self.createOption_DropdownMenu(
# setattr_obj,
# self.setting_box_dropdown_menu_x_dropdown_menu,
# kwargs["left_optionmenu_attr_name"],
# kwargs["left_dropdown_menu_attr_name"],
# dropdown_menu_values=kwargs["left_dropdown_menu_values"],
# width=150,
# command=kwargs["left_dropdown_menu_command"],
# variable=kwargs["left_dropdown_menu_variable"],
# )
# getattr(setattr_obj, kwargs["left_optionmenu_attr_name"]).grid(row=1, column=0)
# self.the_label_between_optionmenu = CTkLabel(
# self.setting_box_dropdown_menu_x_dropdown_menu,
# text="-->",
# # anchor="w",
# font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__OPTION_MENU_FONT_SIZE, weight="normal"),
# text_color=self.settings.ctm.LABELS_TEXT_COLOR
# )
# self.the_label_between_optionmenu.grid(row=1, column=1, padx=self.settings.uism.SB__RIGHT_PADX/2)
# self.createOption_DropdownMenu(
# setattr_obj,
# self.setting_box_dropdown_menu_x_dropdown_menu,
# kwargs["right_optionmenu_attr_name"],
# kwargs["right_dropdown_menu_attr_name"],
# dropdown_menu_values=kwargs["right_dropdown_menu_values"],
# width=150,
# command=kwargs["right_dropdown_menu_command"],
# variable=kwargs["right_dropdown_menu_variable"],
# )
# getattr(setattr_obj, kwargs["right_optionmenu_attr_name"]).grid(row=1, column=2)
# if setting_box_type == "radio_buttons":
# self.setting_box_radio_buttons_frame = CTkFrame(self.setting_box, corner_radius=0, width=0, height=0)
# self.setting_box_radio_buttons_frame.grid(row=0, column=1, padx=(0, self.settings.uism.SB__RIGHT_PADX), rowspan=2, sticky="e")
# RADIO_BUTTON_RIGHT_PAD = 14
# self.setting_box_radio_button_1 = CTkRadioButton(
# self.setting_box_radio_buttons_frame,
# text="lorem ipsum",
# font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__RADIO_BUTTON_FONT_SIZE, weight="normal")
# )
# self.setting_box_radio_button_1.grid(row=0, column=0, padx=(0,RADIO_BUTTON_RIGHT_PAD), sticky="e")
# self.setting_box_radio_button_2 = CTkRadioButton(
# self.setting_box_radio_buttons_frame,
# text="lorem ipsum",
# font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__RADIO_BUTTON_FONT_SIZE, weight="normal")
# )
# self.setting_box_radio_button_2.grid(row=0, column=1, padx=(0,RADIO_BUTTON_RIGHT_PAD), sticky="e")
# self.setting_box_radio_button_3 = CTkRadioButton(
# self.setting_box_radio_buttons_frame,
# text="lorem ipsum",
# font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__RADIO_BUTTON_FONT_SIZE, weight="normal")
# )
# self.setting_box_radio_button_3.grid(row=0, column=2, padx=(0,RADIO_BUTTON_RIGHT_PAD), sticky="e")
def _createPassiveButtonForProgressbarXSlider(self, setting_box_progressbar_x_slider_frame, button_command, button_image_file):
button_wrapper = createButtonWithImage(
parent_widget=setting_box_progressbar_x_slider_frame,
button_fg_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_COLOR,
button_enter_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_HOVERED_COLOR,
button_clicked_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_CLICKED_COLOR,
button_image_file=button_image_file,
button_image_size=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__BUTTON_ICON_SIZE,
button_ipadxy=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__BUTTON_IPADXY,
button_command=button_command,
)
return button_wrapper
def _createActiveButtonForProgressbarXSlider(self, setting_box_progressbar_x_slider_frame, button_command, button_image_file):
button_wrapper = createButtonWithImage(
parent_widget=setting_box_progressbar_x_slider_frame,
button_fg_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__ACTIVE_BUTTON_COLOR,
button_enter_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__ACTIVE_BUTTON_HOVERED_COLOR,
button_clicked_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__ACTIVE_BUTTON_CLICKED_COLOR,
button_image_file=button_image_file,
button_image_size=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__BUTTON_ICON_SIZE,
button_ipadxy=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__BUTTON_IPADXY,
button_command=button_command,
)
return button_wrapper
def _createDisabledButtonForProgressbarXSlider(self, setting_box_progressbar_x_slider_frame, button_image_file):
button_wrapper = createButtonWithImage(
parent_widget=setting_box_progressbar_x_slider_frame,
button_fg_color=self.settings.ctm.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_DISABLED_COLOR,
button_image_file=button_image_file,
button_image_size=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__BUTTON_ICON_SIZE,
button_ipadxy=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__BUTTON_IPADXY,
no_bind=True,
)
return button_wrapper

View File

@@ -0,0 +1 @@
from .createSettingBox_AdvancedSettings import createSettingBox_AdvancedSettings

View File

@@ -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

View File

@@ -0,0 +1 @@
from .createSettingBox_Appearance import createSettingBox_Appearance

View File

@@ -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

View File

@@ -0,0 +1 @@
from .createSettingBox_Others import createSettingBox_Others

View File

@@ -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

View File

@@ -0,0 +1,2 @@
from .createSettingBox_Mic import createSettingBox_Mic
from .createSettingBox_Speaker import createSettingBox_Speaker

View File

@@ -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

View File

@@ -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
# __________

View File

@@ -0,0 +1 @@
from .createSettingBox_Translation import createSettingBox_Translation

View File

@@ -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

View File

@@ -0,0 +1 @@
from .createMainWindowWidgets import createMainWindowWidgets

View File

@@ -0,0 +1,125 @@
from .widgets import createSidebar, createMinimizeSidebarButton, createTextbox, createEntryMessageBox
from customtkinter import CTkFrame, CTkLabel, CTkFont, CTkImage
from utils import callFunctionIfCallable
from ..ui_utils import createButtonWithImage, getImagePath, bindButtonFunctionAndColor
def createMainWindowWidgets(vrct_gui, settings, view_variable):
vrct_gui.protocol("WM_DELETE_WINDOW", vrct_gui._quitVRCT)
vrct_gui.iconbitmap(getImagePath("vrct_logo_mark_black.ico"))
vrct_gui.title("VRCT")
# vrct_gui.minsize(200, 200)
# Main Container
vrct_gui.grid_columnconfigure(0, weight=1)
vrct_gui.grid_rowconfigure(0, weight=1)
# vrct_gui.grid_columnconfigure(0, weight=1, minsize=settings.uism.MAIN_AREA_MIN_WIDTH)
vrct_gui.configure(fg_color=settings.ctm.MAIN_BG_COLOR)
vrct_gui.toplevel_wrapper = CTkFrame(vrct_gui, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=0)
vrct_gui.toplevel_wrapper.grid(row=0, column=0, sticky="nsew")
vrct_gui.toplevel_wrapper.grid_columnconfigure(1, weight=1)
# Main Container
vrct_gui.main_bg_container = CTkFrame(vrct_gui.toplevel_wrapper, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=0)
vrct_gui.main_bg_container.grid(row=0, column=1, sticky="nsew")
# top bar
vrct_gui.main_bg_container.grid_columnconfigure(0, weight=1)
vrct_gui.main_topbar_container = CTkFrame(vrct_gui.main_bg_container, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=0)
vrct_gui.main_topbar_container.grid(row=0, column=0, sticky="ew")
vrct_gui.main_topbar_container.grid_columnconfigure(1,weight=1)
vrct_gui.main_topbar_center_container = CTkFrame(vrct_gui.main_topbar_container, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=0)
vrct_gui.main_topbar_center_container.grid(row=0, column=1, sticky="nsew")
# Main Top Bar Container - Right Side
# start from 3
main_topbar_column=3
# Update Available Button
vrct_gui.update_available_container = CTkFrame(
vrct_gui.main_topbar_container,
corner_radius=settings.uism.UPDATE_AVAILABLE_BUTTON_CORNER_RADIUS,
fg_color=settings.ctm.MAIN_BG_COLOR,
cursor="hand2",
)
vrct_gui.update_available_container.grid(row=0, column=main_topbar_column, padx=settings.uism.UPDATE_AVAILABLE_BUTTON_PADX, pady=settings.uism.TOP_BAR_BUTTON_PADY, sticky="nse")
vrct_gui.update_available_container.grid_remove()
main_topbar_column+=1
vrct_gui.update_available_container.grid_rowconfigure((0,2), weight=1)
vrct_gui.update_available_icon = CTkLabel(
vrct_gui.update_available_container,
text=None,
corner_radius=0,
height=0,
image=CTkImage(settings.image_file.REFRESH_ICON.rotate(25), size=settings.uism.UPDATE_AVAILABLE_BUTTON_SIZE)
)
vrct_gui.update_available_icon.grid(row=1, column=0, padx=(settings.uism.UPDATE_AVAILABLE_BUTTON_IPADX, settings.uism.UPDATE_AVAILABLE_PADX_BETWEEN_LABEL_AND_ICON), pady=0)
vrct_gui.update_available_label = CTkLabel(
vrct_gui.update_available_container,
textvariable=view_variable.VAR_UPDATE_AVAILABLE,
height=0,
corner_radius=0,
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.UPDATE_AVAILABLE_BUTTON_FONT_SIZE, weight="normal"),
anchor="e",
text_color=settings.ctm.UPDATE_AVAILABLE_BUTTON_TEXT_COLOR,
)
# This "right padx +1" is for fixing a bug that sticks out from the frame. I don't know why that happens...
vrct_gui.update_available_label.grid(row=1, column=1, padx=(0,settings.uism.UPDATE_AVAILABLE_BUTTON_IPADX+1), pady=0)
bindButtonFunctionAndColor(
target_widgets=[
vrct_gui.update_available_container,
vrct_gui.update_available_label,
vrct_gui.update_available_icon,
],
enter_color=settings.ctm.UPDATE_AVAILABLE_BUTTON_HOVERED_BG_COLOR,
leave_color=settings.ctm.UPDATE_AVAILABLE_BUTTON_BG_COLOR,
clicked_color=settings.ctm.UPDATE_AVAILABLE_BUTTON_CLICKED_BG_COLOR,
buttonReleasedFunction=lambda e: callFunctionIfCallable(view_variable.CALLBACK_CLICKED_UPDATE_AVAILABLE),
)
# Help and Info button
vrct_gui.help_and_info_button_container = createButtonWithImage(
parent_widget=vrct_gui.main_topbar_container,
button_fg_color=settings.ctm.HELP_AND_INFO_BUTTON_BG_COLOR,
button_enter_color=settings.ctm.HELP_AND_INFO_BUTTON_HOVERED_BG_COLOR,
button_clicked_color=settings.ctm.HELP_AND_INFO_BUTTON_CLICKED_BG_COLOR,
button_image_file=settings.image_file.HELP_ICON,
button_image_size=settings.uism.HELP_AND_INFO_BUTTON_SIZE,
button_ipadxy=settings.uism.HELP_AND_INFO_BUTTON_IPADXY,
button_command=lambda e: callFunctionIfCallable(view_variable.CALLBACK_CLICKED_HELP_AND_INFO),
corner_radius=settings.uism.HELP_AND_INFO_BUTTON_CORNER_RADIUS,
)
vrct_gui.help_and_info_button_container.grid(row=0, column=main_topbar_column, padx=settings.uism.HELP_AND_INFO_BUTTON_PADX, pady=settings.uism.TOP_BAR_BUTTON_PADY, sticky="e")
main_topbar_column+=1
createSidebar(settings, vrct_gui, view_variable)
createMinimizeSidebarButton(settings, vrct_gui, view_variable)
createTextbox(settings, vrct_gui, view_variable)
createEntryMessageBox(settings, vrct_gui)

View File

@@ -0,0 +1,4 @@
from .create_sidebar import createSidebar
from .create_minimize_sidebar_button import createMinimizeSidebarButton
from .create_textbox import createTextbox
from .create_entry_message_box import createEntryMessageBox

View File

@@ -0,0 +1,2 @@
from .createSidebarFeatures import createSidebarFeatures
from .createSidebarLanguagesSettings import createSidebarLanguagesSettings

View File

@@ -0,0 +1,287 @@
from functools import partial
from customtkinter import CTkFont, CTkFrame, CTkLabel, CTkSwitch, CTkImage
from ....ui_utils import openImageKeepAspectRatio, retag, getLatestHeight, bindEnterAndLeaveFunction, bindButtonReleaseFunction, bindButtonPressAndReleaseFunction
from utils import callFunctionIfCallable
def createSidebarFeatures(settings, main_window, view_variable):
def toggleSidebarFeatureSelectedMarkIfTurnedOn(is_turned_on, mark_widget):
mark_widget.place(relx=0.85) if is_turned_on else mark_widget.place(relx=-1)
def toggleTranslationFeature():
is_turned_on = main_window.translation_switch_box.get()
callFunctionIfCallable(view_variable.CALLBACK_TOGGLE_TRANSLATION, is_turned_on)
main_window.translation_frame.markToggleManually(is_turned_on=is_turned_on)
def toggleTranscriptionSendFeature():
is_turned_on = main_window.transcription_send_switch_box.get()
callFunctionIfCallable(view_variable.CALLBACK_TOGGLE_TRANSCRIPTION_SEND, is_turned_on)
main_window.transcription_send_frame.markToggleManually(is_turned_on=is_turned_on)
def toggleTranscriptionReceiveFeature():
is_turned_on = main_window.transcription_receive_switch_box.get()
callFunctionIfCallable(view_variable.CALLBACK_TOGGLE_TRANSCRIPTION_RECEIVE, is_turned_on)
main_window.transcription_receive_frame.markToggleManually(is_turned_on=is_turned_on)
def toggleForegroundFeature():
is_turned_on = main_window.foreground_switch_box.get()
callFunctionIfCallable(view_variable.CALLBACK_TOGGLE_FOREGROUND, is_turned_on)
main_window.foreground_frame.markToggleManually(is_turned_on=is_turned_on)
def changeSidebarFeaturesColorByEvents(ww, event_name):
target_frame_widget = getattr(main_window, ww)
target_compact_mode_frame_widget = getattr(main_window, "compact_mode_"+ww)
if event_name == "enter":
target_frame_widget.configure(fg_color=settings.ctm.SF__HOVERED_BG_COLOR)
target_frame_widget.children["!ctkswitch"].configure(fg_color=settings.ctm.SF__SWITCH_BOX_HOVERED_BG_COLOR, progress_color=settings.ctm.SF__SWITCH_BOX_ACTIVE_HOVERED_BG_COLOR)
target_compact_mode_frame_widget.configure(fg_color=settings.ctm.SF__HOVERED_BG_COLOR)
target_compact_mode_frame_widget.children["!ctkframe"].configure(fg_color=settings.ctm.SF__SELECTED_MARK_ACTIVE_HOVERED_BG_COLOR)
elif event_name == "leave":
target_frame_widget.configure(fg_color=settings.ctm.SF__BG_COLOR)
target_frame_widget.children["!ctkswitch"].configure(fg_color=settings.ctm.SF__SWITCH_BOX_BG_COLOR, progress_color=settings.ctm.SF__SWITCH_BOX_ACTIVE_BG_COLOR)
target_compact_mode_frame_widget.configure(fg_color=settings.ctm.SF__BG_COLOR)
target_compact_mode_frame_widget.children["!ctkframe"].configure(fg_color=settings.ctm.SF__SELECTED_MARK_ACTIVE_BG_COLOR)
elif event_name == "button_press":
target_frame_widget.configure(fg_color=settings.ctm.SF__CLICKED_BG_COLOR)
target_frame_widget.children["!ctkswitch"].configure(fg_color=settings.ctm.SF__SWITCH_BOX_CLICKED_BG_COLOR, progress_color=settings.ctm.SF__SWITCH_BOX_ACTIVE_CLICKED_BG_COLOR)
target_compact_mode_frame_widget.configure(fg_color=settings.ctm.SF__CLICKED_BG_COLOR)
target_compact_mode_frame_widget.children["!ctkframe"].configure(fg_color=settings.ctm.SF__SELECTED_MARK_ACTIVE_CLICKED_BG_COLOR)
elif event_name == "button_release":
target_frame_widget.configure(fg_color=settings.ctm.SF__BG_COLOR)
target_frame_widget.children["!ctkswitch"].configure(fg_color=settings.ctm.SF__SWITCH_BOX_BG_COLOR, progress_color=settings.ctm.SF__SWITCH_BOX_ACTIVE_BG_COLOR)
target_compact_mode_frame_widget.configure(fg_color=settings.ctm.SF__BG_COLOR)
target_compact_mode_frame_widget.children["!ctkframe"].configure(fg_color=settings.ctm.SF__SELECTED_MARK_ACTIVE_BG_COLOR)
(img, width, height) = openImageKeepAspectRatio(settings.image_file.VRCT_LOGO, settings.uism.SF__LOGO_MAX_SIZE)
main_window.sidebar_logo = CTkLabel(
main_window.sidebar_bg_container,
fg_color=settings.ctm.SIDEBAR_BG_COLOR,
text=None,
height=0,
image=CTkImage(img, size=(width,height))
)
main_window.sidebar_logo.grid(row=0, column=0, pady=settings.uism.SF__LOGO_PADY)
# "height-a_s" represents the adjustment in size, and the "pady+a_s/2" indicates a padding increase of 4 pixels to compensate for the reduction.
a_s = settings.uism.SF__LOGO_HEIGHT_FOR_ADJUSTMENT
(img, width, height) = openImageKeepAspectRatio(settings.image_file.VRCT_LOGO_MARK, height-a_s)
main_window.sidebar_compact_mode_logo = CTkLabel(
main_window.sidebar_compact_mode_bg_container,
fg_color=settings.ctm.SIDEBAR_BG_COLOR,
text=None,
height=0,
image=CTkImage(img, size=(width,height))
)
main_window.sidebar_compact_mode_logo.grid(row=0, column=0, pady=(
int(settings.uism.SF__LOGO_PADY[0]+a_s/2),
int(settings.uism.SF__LOGO_PADY[1]+a_s/2)
))
# Sidebar Features
main_window.sidebar_features_container = CTkFrame(main_window.sidebar_bg_container, corner_radius=0, fg_color="transparent", width=0, height=0)
main_window.sidebar_features_container.grid(row=1, column=0, pady=0, sticky="ew")
main_window.sidebar_features_container.grid_columnconfigure(0, weight=1)
# Sidebar Features Compact Mode
main_window.sidebar_compact_mode_features_container = CTkFrame(main_window.sidebar_compact_mode_bg_container, corner_radius=0, fg_color="transparent", width=0, height=0)
main_window.sidebar_compact_mode_features_container.grid(row=1, column=0, pady=0, sticky="ew")
main_window.sidebar_compact_mode_features_container.grid_columnconfigure(0, weight=1)
sidebar_features_settings = [
{
"frame_attr_name": "translation_frame",
"command": toggleTranslationFeature,
"switch_box_attr_name": "translation_switch_box",
"toggle_switch_box_command": lambda e: main_window.translation_switch_box.toggle(),
"label_attr_name": "label_translation",
"compact_mode_icon_attr_name": "translation_compact_mode_icon",
"compact_mode_frame_attr_name": "compact_mode_translation_frame",
"selected_mark_attr_name": "translation_selected_mark",
"var_label_text": view_variable.VAR_LABEL_TRANSLATION,
"icon_file": settings.image_file.TRANSLATION_ICON,
},
{
"frame_attr_name": "transcription_send_frame",
"command": toggleTranscriptionSendFeature,
"switch_box_attr_name": "transcription_send_switch_box",
"toggle_switch_box_command": lambda e: main_window.transcription_send_switch_box.toggle(),
"label_attr_name": "label_transcription_send",
"compact_mode_icon_attr_name": "transcription_send_compact_mode_icon",
"compact_mode_frame_attr_name": "compact_mode_transcription_send_frame",
"selected_mark_attr_name": "transcription_send_selected_mark",
"var_label_text": view_variable.VAR_LABEL_TRANSCRIPTION_SEND,
"icon_file": settings.image_file.MIC_ICON,
},
{
"frame_attr_name": "transcription_receive_frame",
"command": toggleTranscriptionReceiveFeature,
"switch_box_attr_name": "transcription_receive_switch_box",
"toggle_switch_box_command": lambda e: main_window.transcription_receive_switch_box.toggle(),
"label_attr_name": "label_transcription_receive",
"compact_mode_icon_attr_name": "transcription_receive_compact_mode_icon",
"compact_mode_frame_attr_name": "compact_mode_transcription_receive_frame",
"selected_mark_attr_name": "transcription_receive_selected_mark",
"var_label_text": view_variable.VAR_LABEL_TRANSCRIPTION_RECEIVE,
"icon_file": settings.image_file.HEADPHONES_ICON,
},
{
"frame_attr_name": "foreground_frame",
"command": toggleForegroundFeature,
"switch_box_attr_name": "foreground_switch_box",
"toggle_switch_box_command": lambda e: main_window.foreground_switch_box.toggle(),
"label_attr_name": "label_foreground",
"compact_mode_icon_attr_name": "foreground_compact_mode_icon",
"compact_mode_frame_attr_name": "compact_mode_foreground_frame",
"selected_mark_attr_name": "foreground_selected_mark",
"var_label_text": view_variable.VAR_LABEL_FOREGROUND,
"icon_file": settings.image_file.FOREGROUND_ICON,
},
]
row=0
for sfs in sidebar_features_settings:
frame_attr_name = sfs["frame_attr_name"]
command = sfs["command"]
switch_box_attr_name = sfs["switch_box_attr_name"]
toggle_switch_box_command = sfs["toggle_switch_box_command"]
label_attr_name = sfs["label_attr_name"]
compact_mode_icon_attr_name = sfs["compact_mode_icon_attr_name"]
compact_mode_frame_attr_name = sfs["compact_mode_frame_attr_name"]
selected_mark_attr_name = sfs["selected_mark_attr_name"]
var_label_text = sfs["var_label_text"]
icon_file = sfs["icon_file"]
frame_widget = CTkFrame(main_window.sidebar_features_container, corner_radius=0, fg_color=settings.ctm.SF__BG_COLOR, cursor="hand2", width=0, height=0)
setattr(main_window, frame_attr_name, frame_widget)
frame_widget.grid(row=row, column=0, pady=(0,1), sticky="ew")
frame_widget.grid_columnconfigure(0, weight=1)
compact_mode_frame_widget = CTkFrame(main_window.sidebar_compact_mode_features_container, corner_radius=0, fg_color=settings.ctm.SF__BG_COLOR, cursor="hand2", width=0, height=0)
setattr(main_window, compact_mode_frame_attr_name, compact_mode_frame_widget)
compact_mode_frame_widget.grid(row=row, column=0, pady=(0,1), sticky="ew")
compact_mode_frame_widget.grid_columnconfigure(0, weight=1)
label_widget = CTkLabel(
frame_widget,
textvariable=var_label_text,
height=0,
corner_radius=0,
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.SF__LABEL_FONT_SIZE, weight="normal"),
anchor="w",
text_color=settings.ctm.LABELS_TEXT_COLOR,
)
setattr(main_window, label_attr_name, label_widget)
switch_box_widget = CTkSwitch(
frame_widget,
text=None,
height=0,
width=0,
corner_radius=int(settings.uism.SF__SWITCH_BOX_HEIGHT/2),
border_width=0,
switch_height=settings.uism.SF__SWITCH_BOX_HEIGHT,
switch_width=settings.uism.SF__SWITCH_BOX_WIDTH,
onvalue=True,
offvalue=False,
command=command,
fg_color=settings.ctm.SF__SWITCH_BOX_BG_COLOR,
bg_color=settings.ctm.SF__BG_COLOR,
progress_color=settings.ctm.SF__SWITCH_BOX_ACTIVE_BG_COLOR,
button_color=settings.ctm.SF__SWITCH_BOX_BUTTON_COLOR,
# button_hover_color=settings.ctm.SF__SWITCH_BOX_BUTTON_HOVERED_COLOR,
)
setattr(main_window, switch_box_attr_name, switch_box_widget)
# for compact mode
compact_mode_icon_widget = CTkLabel(
compact_mode_frame_widget,
text=None,
height=0,
corner_radius=0,
image=CTkImage(icon_file, size=settings.uism.SF__COMPACT_MODE_IMAGE_SIZE),
)
setattr(main_window, compact_mode_icon_attr_name, compact_mode_icon_widget)
selected_mark_widget = CTkFrame(
compact_mode_frame_widget,
corner_radius=0,
fg_color=settings.ctm.SF__SELECTED_MARK_ACTIVE_BG_COLOR,
width=settings.uism.SF__SELECTED_MARK_WIDTH,
height=0
)
setattr(main_window, selected_mark_attr_name, selected_mark_widget)
# Arrange
compact_mode_icon_widget.grid(row=row, column=0, padx=settings.uism.SF__COMPACT_MODE_ICON_PADX, pady=settings.uism.SF__COMPACT_MODE_ICON_PADY)
selected_mark_widget.place(relx=-1, rely=0.5, relheight=0.75, anchor="center")
label_widget.grid(row=row, column=0, pady=settings.uism.SF__LABELS_IPADY, padx=(settings.uism.SF__LABEL_LEFT_PAD,0), sticky="ew")
switch_box_widget.grid(row=row, column=1, padx=settings.uism.SF__SWITCH_BOX_PADX, sticky="e")
# Unbind the event "<Button-1>" originally set up by the widget to manually control it.
switch_box_widget._canvas.unbind("<Button-1>")
switch_box_widget._text_label.unbind("<Button-1>")
bindButtonReleaseFunction([compact_mode_icon_widget, frame_widget, compact_mode_frame_widget, label_widget, selected_mark_widget, switch_box_widget._canvas, switch_box_widget._bg_canvas], toggle_switch_box_command)
retag("vrct_"+frame_attr_name, compact_mode_icon_widget, frame_widget, compact_mode_frame_widget, label_widget, selected_mark_widget, switch_box_widget)
def commonEventFunction(e, event_type):
for ww in e.widget.master.bindtags():
if ww.startswith("vrct_"):
ww = ww.replace("vrct_", "")
changeSidebarFeaturesColorByEvents(ww, event_type)
break
def enterFunction(e):
commonEventFunction(e, "enter")
def leaveFunction(e):
commonEventFunction(e, "leave")
def buttonPressFunction(e):
commonEventFunction(e, "button_press")
def buttonReleasedFunction(e):
commonEventFunction(e, "button_release")
bindEnterAndLeaveFunction([compact_mode_icon_widget, frame_widget, compact_mode_frame_widget, label_widget, selected_mark_widget, switch_box_widget._canvas, switch_box_widget._bg_canvas], enterFunction, leaveFunction)
bindButtonPressAndReleaseFunction([compact_mode_icon_widget, frame_widget, compact_mode_frame_widget, label_widget, selected_mark_widget, switch_box_widget._canvas, switch_box_widget._bg_canvas], buttonPressFunction, buttonReleasedFunction)
callback = partial(toggleSidebarFeatureSelectedMarkIfTurnedOn, mark_widget=selected_mark_widget)
frame_widget.markToggleManually = callback
row+=1

View File

@@ -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")

View File

@@ -0,0 +1,34 @@
from customtkinter import CTkFont, CTkFrame, CTkEntry
def createEntryMessageBox(settings, main_window):
main_window.main_entry_message_container = CTkFrame(main_window.main_bg_container, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=0)
main_window.main_entry_message_container.grid(row=2, column=0, sticky="ew")
main_window.main_entry_message_container.grid_columnconfigure(0, weight=1)
main_window.entry_message_box = CTkEntry(
main_window.main_entry_message_container,
border_color=settings.ctm.TEXTBOX_ENTRY_BORDER_COLOR,
fg_color=settings.ctm.TEXTBOX_ENTRY_BG_COLOR,
text_color=settings.ctm.TEXTBOX_ENTRY_TEXT_COLOR,
placeholder_text="Enter your message...",
placeholder_text_color=settings.ctm.TEXTBOX_ENTRY_PLACEHOLDER_COLOR,
height=settings.uism.TEXTBOX_ENTRY_HEIGHT,
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TEXTBOX_ENTRY_FONT_SIZE, weight="normal"),
)
main_window.entry_message_box.grid(row=0, column=0, padx=settings.uism.TEXTBOX_ENTRY_PADX, pady=settings.uism.TEXTBOX_ENTRY_PADY, sticky="nsew")
main_window.entry_message_box._entry.grid(padx=settings.uism.TEXTBOX_ENTRY_IPADX, pady=settings.uism.TEXTBOX_ENTRY_IPADY)
def messageBoxAnyKeyPress(e):
BREAK_KEYSYM_LIST = [
"Delete", "Select", "Up", "Down", "Next", "End", "Print",
"Prior","Insert","Home", "Left", "Clear", "Right", "Linefeed"
]
if e.keysym != "??":
if len(e.char) != 0 and e.keysym in BREAK_KEYSYM_LIST:
main_window.entry_message_box.insert("end", e.char)
return "break"
main_window.entry_message_box.bind("<Any-KeyPress>", messageBoxAnyKeyPress)

View File

@@ -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()

View File

@@ -0,0 +1,64 @@
from customtkinter import CTkFrame, CTkLabel, CTkImage
from ...ui_utils import bindButtonFunctionAndColor
from utils import callFunctionIfCallable
from ._create_sidebar import createSidebarFeatures, createSidebarLanguagesSettings
def createSidebar(settings, main_window, view_variable):
# Side Bar Container
main_window.toplevel_wrapper.grid_rowconfigure(0, weight=1)
main_window.sidebar_bg_container_wrapper = CTkFrame(main_window.toplevel_wrapper, corner_radius=0, fg_color=settings.ctm.SIDEBAR_BG_COLOR, width=0, height=0)
main_window.sidebar_bg_container_wrapper.grid(row=0, column=0, sticky="nsew")
main_window.sidebar_bg_container = CTkFrame(main_window.sidebar_bg_container_wrapper, corner_radius=0, fg_color=settings.ctm.SIDEBAR_BG_COLOR, width=0, height=0)
main_window.sidebar_compact_mode_bg_container = CTkFrame(main_window.sidebar_bg_container_wrapper, corner_radius=0, fg_color=settings.ctm.SIDEBAR_BG_COLOR, width=0, height=0)
main_window.sidebar_bg_container.grid_columnconfigure(0, weight=1, minsize=settings.uism.SIDEBAR_MIN_WIDTH)
main_window.sidebar_compact_mode_bg_container.grid_columnconfigure(0, weight=1)
createSidebarFeatures(settings, main_window, view_variable)
createSidebarLanguagesSettings(settings, main_window, view_variable)
main_window.sidebar_bg_container.grid(row=0, column=0, sticky="nsew")
main_window.sidebar_compact_mode_bg_container.grid(row=0, column=0, sticky="nsew")
main_window.sidebar_compact_mode_bg_container.grid_remove()
# Config Button
main_window.sidebar_bg_container_wrapper.grid_rowconfigure(3, weight=1)
main_window.sidebar_config_button_container = CTkFrame(main_window.sidebar_bg_container_wrapper, corner_radius=0, fg_color=settings.ctm.CONFIG_BUTTON_BG_COLOR, width=0, height=0)
main_window.sidebar_config_button_container.grid(row=4, column=0, sticky="sew")
main_window.sidebar_config_button_container.grid_columnconfigure(0, weight=1)
main_window.sidebar_config_button_wrapper = CTkFrame(main_window.sidebar_config_button_container, corner_radius=settings.uism.SIDEBAR_CONFIG_BUTTON_CORNER_RADIUS, fg_color=settings.ctm.CONFIG_BUTTON_BG_COLOR, height=0, width=0, cursor="hand2")
main_window.sidebar_config_button_wrapper.grid(row=0, column=0, padx=settings.uism.SIDEBAR_CONFIG_BUTTON_PADX, pady=settings.uism.SIDEBAR_CONFIG_BUTTON_PADY, sticky="ew")
main_window.sidebar_config_button_wrapper.grid_columnconfigure(0, weight=1)
main_window.sidebar_config_button = CTkLabel(
main_window.sidebar_config_button_wrapper,
text=None,
height=0,
image=CTkImage(settings.image_file.CONFIGURATION_ICON, size=settings.uism.SIDEBAR_CONFIG_BUTTON_IMAGE_SIZE)
)
main_window.sidebar_config_button.grid(row=0, column=0, padx=0, pady=settings.uism.SIDEBAR_CONFIG_BUTTON_IPADY)
bindButtonFunctionAndColor(
target_widgets=[main_window.sidebar_config_button_wrapper, main_window.sidebar_config_button],
enter_color=settings.ctm.CONFIG_BUTTON_HOVERED_BG_COLOR,
leave_color=settings.ctm.CONFIG_BUTTON_BG_COLOR,
clicked_color=settings.ctm.CONFIG_BUTTON_CLICKED_BG_COLOR,
buttonReleasedFunction=lambda _e: callFunctionIfCallable(view_variable.CALLBACK_CLICKED_OPEN_CONFIG_WINDOW_BUTTON),
)

View File

@@ -0,0 +1,162 @@
from customtkinter import CTkFont, CTkFrame, CTkLabel, CTkTextbox
from ...ui_utils import bindEnterAndLeaveColor, bindButtonPressColor, bindButtonReleaseFunction, setDefaultActiveTab, switchActiveTabAndPassiveTab, switchTabsColor
def createTextbox(settings, main_window, view_variable):
def switchTextbox(target_textbox_attr_name):
main_window.current_active_textbox.grid_remove()
main_window.current_active_textbox = getattr(main_window, target_textbox_attr_name)
main_window.current_active_textbox.grid()
def switchToTextboxAll(e):
target_active_widget = getattr(main_window, "textbox_tab_all")
switchTextboxTabFunction(target_active_widget)
switchTextbox("textbox_all")
def switchToTextboxSent(e):
target_active_widget = getattr(main_window, "textbox_tab_sent")
switchTextboxTabFunction(target_active_widget)
switchTextbox("textbox_sent")
def switchToTextboxReceived(e):
target_active_widget = getattr(main_window, "textbox_tab_received")
switchTextboxTabFunction(target_active_widget)
switchTextbox("textbox_received")
def switchToTextboxSystem(e):
target_active_widget = getattr(main_window, "textbox_tab_system")
switchTextboxTabFunction(target_active_widget)
switchTextbox("textbox_system")
def switchTextboxTabFunction(target_active_widget):
switchActiveAndPassiveTextboxTabsColor(target_active_widget)
switchActiveTabAndPassiveTab(target_active_widget, main_window.current_active_textbox_tab, main_window.current_active_textbox_tab.passive_function, settings.ctm.TEXTBOX_TAB_BG_HOVERED_COLOR, settings.ctm.TEXTBOX_TAB_BG_CLICKED_COLOR, settings.ctm.TEXTBOX_TAB_BG_PASSIVE_COLOR)
main_window.current_active_textbox_tab = target_active_widget
def switchActiveAndPassiveTextboxTabsColor(target_active_widget):
textbox_tabs = [
getattr(main_window, "textbox_tab_all"),
getattr(main_window, "textbox_tab_sent"),
getattr(main_window, "textbox_tab_received"),
getattr(main_window, "textbox_tab_system")
]
switchTabsColor(
target_widget=target_active_widget,
tab_buttons=textbox_tabs,
active_bg_color=settings.ctm.TEXTBOX_BG_COLOR,
active_text_color=settings.ctm.SLS__PRESETS_TAB_ACTIVE_TEXT_COLOR,
passive_bg_color=settings.ctm.TEXTBOX_TAB_BG_PASSIVE_COLOR,
passive_text_color=settings.ctm.TEXTBOX_TAB_TEXT_PASSIVE_COLOR
)
# Text box
main_window.main_bg_container.grid_rowconfigure(1, weight=1)
main_window.main_textbox_container = CTkFrame(main_window.main_bg_container, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=0)
main_window.main_textbox_container.grid(row=1, column=0, sticky="nsew")
main_window.main_textbox_container.grid_columnconfigure(0,weight=1)
main_window.main_textbox_container.grid_rowconfigure(0,weight=1)
main_window.textbox_switch_tabs_container = CTkFrame(main_window.main_topbar_center_container, corner_radius=0, fg_color=settings.ctm.MAIN_BG_COLOR, width=0, height=0)
main_window.textbox_switch_tabs_container.place(relx=0.07, rely=1.15, anchor="sw")
main_window.textbox_switch_tabs_container.grid_columnconfigure((0,1,2,3), weight=1, uniform="textbox_tabs")
textbox_settings = [
{
"textbox_tab_attr_name": "textbox_tab_all",
"command": switchToTextboxAll,
"textbox_attr_name": "textbox_all",
"textvariable": view_variable.VAR_LABEL_TEXTBOX_ALL
},
{
"textbox_tab_attr_name": "textbox_tab_sent",
"command": switchToTextboxSent,
"textbox_attr_name": "textbox_sent",
"textvariable": view_variable.VAR_LABEL_TEXTBOX_SENT
},
{
"textbox_tab_attr_name": "textbox_tab_received",
"command": switchToTextboxReceived,
"textbox_attr_name": "textbox_received",
"textvariable": view_variable.VAR_LABEL_TEXTBOX_RECEIVED
},
{
"textbox_tab_attr_name": "textbox_tab_system",
"command": switchToTextboxSystem,
"textbox_attr_name": "textbox_system",
"textvariable": view_variable.VAR_LABEL_TEXTBOX_SYSTEM
},
]
column=0
for textbox_setting in textbox_settings:
setattr(main_window, textbox_setting["textbox_tab_attr_name"],
CTkFrame(
main_window.textbox_switch_tabs_container,
corner_radius=settings.uism.TEXTBOX_TAB_CORNER_RADIUS,
fg_color=settings.ctm.TEXTBOX_TAB_BG_PASSIVE_COLOR,
cursor="hand2",
width=0,
height=0
)
)
target_widget = getattr(main_window, textbox_setting["textbox_tab_attr_name"])
target_widget.grid(row=0, column=column, pady=0, padx=(0,2), sticky="ew")
target_widget.grid_columnconfigure((0,2), weight=1)
setattr(main_window, "label_widget", CTkLabel(
target_widget,
textvariable=textbox_setting["textvariable"],
corner_radius=0,
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TEXTBOX_TAB_FONT_SIZE, weight="normal"),
height=0,
width=0,
anchor="center",
text_color=settings.ctm.TEXTBOX_TAB_TEXT_PASSIVE_COLOR,
))
label_widget = getattr(main_window, "label_widget")
label_widget.grid(row=0, column=1, pady=settings.uism.TEXTBOX_TAB_PADY, padx=settings.uism.TEXTBOX_TAB_PADX)
bindEnterAndLeaveColor([target_widget, label_widget], settings.ctm.TEXTBOX_TAB_BG_HOVERED_COLOR, settings.ctm.TEXTBOX_TAB_BG_PASSIVE_COLOR)
bindButtonPressColor([target_widget, label_widget], settings.ctm.TEXTBOX_TAB_BG_CLICKED_COLOR, settings.ctm.TEXTBOX_TAB_BG_PASSIVE_COLOR)
target_widget.passive_function = textbox_setting["command"]
bindButtonReleaseFunction([target_widget, label_widget], textbox_setting["command"])
setattr(main_window, textbox_setting["textbox_attr_name"], CTkTextbox(
main_window.main_textbox_container,
corner_radius=settings.uism.TEXTBOX_CORNER_RADIUS,
fg_color=settings.ctm.TEXTBOX_BG_COLOR,
text_color="lime", # Textbox's text_color is set when printing. so this is for prevent from non-setting text_color like the gloves used in food factories are blue.
wrap="word",
))
textbox_widget = getattr(main_window, textbox_setting["textbox_attr_name"])
textbox_widget.grid(row=0, column=0, padx=settings.uism.TEXTBOX_PADX, pady=0, sticky="nsew")
textbox_widget.grid_remove()
textbox_widget.configure(state="disabled")
column+=1
# Set default active textbox tab
main_window.current_active_textbox_tab = getattr(main_window, "textbox_tab_all")
setDefaultActiveTab(
active_tab_widget=main_window.current_active_textbox_tab,
active_bg_color=settings.ctm.TEXTBOX_TAB_BG_ACTIVE_COLOR,
active_text_color=settings.ctm.TEXTBOX_TAB_TEXT_ACTIVE_COLOR
)
main_window.current_active_textbox = getattr(main_window, "textbox_all")
main_window.current_active_textbox.grid()

View File

@@ -0,0 +1,40 @@
from customtkinter import CTkImage, CTkLabel, CTkToplevel
from ..ui_utils import openImageKeepAspectRatio, getImageFileFromUiUtils, setGeometryToCenterOfScreen, fadeInAnimation
class SplashWindow(CTkToplevel):
def __init__(self):
super().__init__()
self.withdraw()
self.overrideredirect(True)
self.configure(fg_color="#292a2d")
self.title("SplashWindow")
self.wm_attributes("-toolwindow", True)
sw=self.winfo_screenwidth()
# sh=self.winfo_screenheight()
pw=int(sw/4)
self.grid_columnconfigure((0,2), weight=1)
self.grid_rowconfigure((0,2), weight=1)
(img, desired_width, height) = openImageKeepAspectRatio(getImageFileFromUiUtils("vrct_logo_for_dark_mode.png"), pw)
label = CTkLabel(
self,
text=None,
height=0,
fg_color="#292a2d",
image=CTkImage(img, size=(desired_width, height))
)
label.grid(row=1, column=1, padx=int(desired_width/7), pady=int(height/3))
def showSplash(self):
self.attributes("-alpha", 0)
self.deiconify()
setGeometryToCenterOfScreen(root_widget=self)
fadeInAnimation(self, steps=5, interval=0.02)
def destroySplash(self):
self.destroy()

View File

@@ -0,0 +1 @@
from .SplashWindow import *

View File

@@ -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

Some files were not shown because too many files have changed in this diff Show More