Merge branch 'develop'

This commit is contained in:
misyaguziya
2024-04-28 17:47:45 +09:00
117 changed files with 3140 additions and 128 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,4 @@
tmp/
build/
dist/
/config.json
@@ -10,3 +11,4 @@ weights/
.vscode
error.log
*.exe
*.ipynb

View File

@@ -1,2 +1,2 @@
pyinstaller --windowed --clean --noconfirm --icon="./img/vrct_logo_mark_black.ico" --add-data "./img;img/" --add-data "./locales;locales/" --add-data "./batch;batch/" --name VRCT --add-data ".venv\Lib\site-packages\customtkinter;customtkinter/" --add-data ".venv\Lib\site-packages\zeroconf;zeroconf/" --exclude-module pandas --exclude-module matplotlib --exclude-module PyQt5 main.py
pyinstaller --windowed --clean --noconfirm --icon="./img/vrct_logo_mark_black.ico" --add-data "./img;img/" --add-data "./fonts;fonts/" --add-data "./locales;locales/" --add-data "./batch;batch/" --name VRCT --add-data ".venv\Lib\site-packages\customtkinter;customtkinter/" --add-data ".venv\Lib\site-packages\zeroconf;zeroconf/" --add-data ".venv\Lib\site-packages\openvr;openvr/" --exclude-module pandas --exclude-module matplotlib --exclude-module PyQt5 main.py
"C:\Program Files (x86)\NSIS\makensis.exe" installer/installer.nsi

View File

@@ -6,7 +6,7 @@ from json import dump as json_dump
import tkinter as tk
from tkinter import font
from models.translation.translation_languages import translation_lang
from models.transcription.transcription_utils import getInputDevices, getDefaultInputDevice
from models.transcription.transcription_utils import getInputDevices, getDefaultInputDevice, getOutputDevices, getDefaultOutputDevice
from models.transcription.transcription_languages import transcription_lang
from utils import generatePercentageStringsList, isUniqueStrings
@@ -247,6 +247,15 @@ class Config:
if isinstance(value, bool):
self._IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER = value
@property
def IS_EASTER_EGG_ENABLED(self):
return self._IS_EASTER_EGG_ENABLED
@IS_EASTER_EGG_ENABLED.setter
def IS_EASTER_EGG_ENABLED(self, value):
if isinstance(value, bool):
self._IS_EASTER_EGG_ENABLED = value
# Save Json Data
## Main Window
@property
@@ -537,6 +546,17 @@ class Config:
self._INPUT_MIC_WORD_FILTER = sorted(set(value), key=value.index)
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property
@json_serializable('CHOICE_SPEAKER_DEVICE')
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()]:
self._CHOICE_SPEAKER_DEVICE = 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):
@@ -716,6 +736,59 @@ class Config:
self._ENABLE_NOTICE_XSOVERLAY = value
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
# @property
# @json_serializable('OVERLAY_SETTINGS')
# def OVERLAY_SETTINGS(self):
# return self._OVERLAY_SETTINGS
# @OVERLAY_SETTINGS.setter
# def OVERLAY_SETTINGS(self, value):
# if isinstance(value, dict) and set(value.keys()) == set(self.OVERLAY_SETTINGS.keys()):
# for key, value in value.items():
# if isinstance(value, float):
# self._OVERLAY_SETTINGS[key] = value
# saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, self.OVERLAY_SETTINGS)
# @property
# @json_serializable('ENABLE_OVERLAY_SMALL_LOG')
# def ENABLE_OVERLAY_SMALL_LOG(self):
# return self._ENABLE_OVERLAY_SMALL_LOG
# @ENABLE_OVERLAY_SMALL_LOG.setter
# def ENABLE_OVERLAY_SMALL_LOG(self, value):
# if isinstance(value, bool):
# self._ENABLE_OVERLAY_SMALL_LOG = value
# saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
# @property
# @json_serializable('OVERLAY_SMALL_LOG_SETTINGS')
# def OVERLAY_SMALL_LOG_SETTINGS(self):
# return self._OVERLAY_SMALL_LOG_SETTINGS
# @OVERLAY_SMALL_LOG_SETTINGS.setter
# def OVERLAY_SMALL_LOG_SETTINGS(self, value):
# if isinstance(value, dict) and set(value.keys()) == set(self.OVERLAY_SMALL_LOG_SETTINGS.keys()):
# for key, value in value.items():
# match (key):
# case "x_pos" | "y_pos" | "depth":
# if isinstance(value, float):
# self._OVERLAY_SMALL_LOG_SETTINGS[key] = value
# case "display_duration" | "fadeout_duration":
# if isinstance(value, int):
# self._OVERLAY_SMALL_LOG_SETTINGS[key] = value
# saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, self.OVERLAY_SMALL_LOG_SETTINGS)
# @property
# @json_serializable('OVERLAY_UI_TYPE')
# def OVERLAY_UI_TYPE(self):
# return self._OVERLAY_UI_TYPE
# @OVERLAY_UI_TYPE.setter
# def OVERLAY_UI_TYPE(self, value):
# if isinstance(value, str):
# self._OVERLAY_UI_TYPE = value
# saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property
@json_serializable('ENABLE_SEND_MESSAGE_TO_VRC')
def ENABLE_SEND_MESSAGE_TO_VRC(self):
@@ -832,9 +905,9 @@ class Config:
def init_config(self):
# Read Only
self._VERSION = "2.2.2"
self._VERSION = "2.2.3"
self._ENABLE_SPEAKER2CHATBOX = False # Speaker2Chatbox
self._ENABLE_SPEAKER2CHATBOX_PASS_CONFIRMATION = "123456789"
self._ENABLE_SPEAKER2CHATBOX_PASS_CONFIRMATION = "VRCT=0YEN"
self._PATH_LOCAL = os_path.dirname(sys.argv[0])
self._PATH_CONFIG = os_path.join(self._PATH_LOCAL, "config.json")
self._PATH_LOGS = os_path.join(self._PATH_LOCAL, "logs")
@@ -889,6 +962,7 @@ class Config:
self._CURRENT_SENT_MESSAGES_LOG_INDEX = 0
self._IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION = False
self._IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER = False
self._IS_EASTER_EGG_ENABLED = False
# Save Json Data
## Main Window
@@ -957,6 +1031,7 @@ class Config:
self._INPUT_MIC_PHRASE_TIMEOUT = 3
self._INPUT_MIC_MAX_PHRASES = 10
self._INPUT_MIC_WORD_FILTER = []
self._CHOICE_SPEAKER_DEVICE = getDefaultOutputDevice()["device"]["name"]
self._INPUT_SPEAKER_ENERGY_THRESHOLD = 300
self._INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD = False
self._INPUT_SPEAKER_RECORD_TIMEOUT = 3
@@ -979,6 +1054,19 @@ class Config:
self._ENABLE_SEND_ONLY_TRANSLATED_MESSAGES = False
self._SEND_MESSAGE_BUTTON_TYPE = "show"
self._ENABLE_NOTICE_XSOVERLAY = False
# self._OVERLAY_SETTINGS = {
# "opacity": 1.0,
# "ui_scaling": 1.0,
# }
# self._ENABLE_OVERLAY_SMALL_LOG = False
# self._OVERLAY_SMALL_LOG_SETTINGS = {
# "x_pos": 0.0,
# "y_pos": -0.41,
# "depth": 1.0,
# "display_duration": 5,
# "fadeout_duration": 2,
# }
# self._OVERLAY_UI_TYPE = "default"
self._ENABLE_SEND_MESSAGE_TO_VRC = True
self._ENABLE_SEND_RECEIVED_MESSAGE_TO_VRC = False # Speaker2Chatbox
self._ENABLE_SPEAKER2CHATBOX_PASS = "000000000"

View File

@@ -8,9 +8,9 @@ from utils import getKeyByValue, isUniqueStrings, strPctToInt
import argparse
# Common
def callbackUpdateSoftware():
def callbackUpdateSoftware(func=None):
setMainWindowGeometry()
model.updateSoftware()
model.updateSoftware(restart=True, func=func)
def callbackRestartSoftware():
setMainWindowGeometry()
@@ -27,6 +27,11 @@ def callbackFilepathConfigFile():
def callbackQuitVrct():
setMainWindowGeometry()
# def callbackEnableEasterEgg():
# config.IS_EASTER_EGG_ENABLED = True
# config.OVERLAY_UI_TYPE = "sakura"
# view.printToTextbox_enableEasterEgg()
def setMainWindowGeometry():
PRE_SCALING_INT = strPctToInt(view.getPreUiScaling())
NEW_SCALING_INT = strPctToInt(config.UI_SCALING)
@@ -69,6 +74,8 @@ def sendMicMessage(message):
if model.checkKeywords(message):
view.printToTextbox_DetectedByWordFilter(detected_message=message)
return
elif model.detectRepeatSendMessage(message):
return
elif config.ENABLE_TRANSLATION is False:
pass
else:
@@ -94,6 +101,12 @@ def sendMicMessage(message):
translation = f" ({translation})"
model.logger.info(f"[SENT] {message}{translation}")
# if config.ENABLE_OVERLAY_SMALL_LOG is True:
# overlay_image = model.createOverlayImageShort(message, translation)
# model.updateOverlay(overlay_image)
# overlay_image = model.createOverlayImageLong("send", message, translation)
# model.updateOverlay(overlay_image)
def startTranscriptionSendMessage():
model.startMicTranscript(sendMicMessage, view.printToTextbox_TranscriptionSendNoDeviceError)
view.setMainWindowAllWidgetsStatusToNormal()
@@ -134,7 +147,9 @@ def stopThreadingTranscriptionSendMessageOnOpenConfigWindow():
def receiveSpeakerMessage(message):
if len(message) > 0:
translation = ""
if config.ENABLE_TRANSLATION is False:
if model.detectRepeatReceiveMessage(message):
return
elif config.ENABLE_TRANSLATION is False:
pass
else:
translation, success = model.getOutputTranslate(message)
@@ -146,13 +161,22 @@ def receiveSpeakerMessage(message):
xsoverlay_message = messageFormatter("RECEIVED", translation, message)
model.notificationXSOverlay(xsoverlay_message)
# if model.overlay.initialized is False:
# model.startOverlay()
# else:
# if config.ENABLE_OVERLAY_SMALL_LOG is True:
# overlay_image = model.createOverlayImageShort(message, translation)
# model.updateOverlay(overlay_image)
# # overlay_image = model.createOverlayImageLong("receive", message, translation)
# # model.updateOverlay(overlay_image)
# ------------Speaker2Chatbox------------
if config.ENABLE_SPEAKER2CHATBOX is True:
# send OSC message
if config.ENABLE_SEND_RECEIVED_MESSAGE_TO_VRC is True:
osc_message = messageFormatter("RECEIVED", translation, message)
model.oscSendMessage(osc_message)
# ------------Speaker2Chatbox------------
# ------------Speaker2Chatbox------------
# update textbox message log (Received)
view.printToTextbox_ReceivedMessage(message, translation)
@@ -221,6 +245,12 @@ def sendChatMessage(message):
osc_message = messageFormatter("SEND", translation, message)
model.oscSendMessage(osc_message)
# if config.ENABLE_OVERLAY_SMALL_LOG is True:
# overlay_image = model.createOverlayImageShort(message, translation)
# model.updateOverlay(overlay_image)
# overlay_image = model.createOverlayImageLong("send", message, translation)
# model.updateOverlay(overlay_image)
# update textbox message log (Sent)
view.printToTextbox_SentMessage(message, translation)
if config.ENABLE_LOGGER is True:
@@ -704,6 +734,13 @@ def callbackDeleteMicWordFilter(value):
print("There was no the target word in config.INPUT_MIC_WORD_FILTER")
# Transcription (Speaker)
def callbackSetSpeakerDevice(value):
print("callbackSetSpeakerDevice", value)
config.CHOICE_SPEAKER_DEVICE = value
model.stopCheckSpeakerEnergy()
view.replaceSpeakerThresholdCheckButton_Passive()
def callbackSetSpeakerEnergyThreshold(value):
print("callbackSetSpeakerEnergyThreshold", value)
if value == "":
@@ -821,6 +858,45 @@ def callbackSetWhisperWeightType(value):
config.SELECTED_TRANSCRIPTION_ENGINE = "Google"
view.showRestartButtonIfRequired()
# # VR Tab
# def callbackSetOverlaySettings(value, set_type:str):
# print("callbackSetOverlaySettings", value, set_type)
# pre_settings = config.OVERLAY_SETTINGS
# pre_settings[set_type] = value
# config.OVERLAY_SETTINGS = pre_settings
# match (set_type):
# case "opacity":
# model.updateOverlayImageOpacity()
# case "ui_scaling":
# model.updateOverlayImageUiScaling()
# def callbackSetEnableOverlaySmallLog(value):
# print("callbackSetEnableOverlaySmallLog", value)
# config.ENABLE_OVERLAY_SMALL_LOG = value
# if config.ENABLE_OVERLAY_SMALL_LOG is True:
# pass
# else:
# if model.overlay.initialized is True:
# model.clearOverlayImage()
# def callbackSetOverlaySmallLogSettings(value, set_type:str):
# print("callbackSetOverlaySmallLogSettings", value, set_type)
# pre_settings = config.OVERLAY_SMALL_LOG_SETTINGS
# pre_settings[set_type] = value
# config.OVERLAY_SMALL_LOG_SETTINGS = pre_settings
# match (set_type):
# case "x_pos":
# model.updateOverlayPosition()
# case "y_pos":
# model.updateOverlayPosition()
# case "depth":
# model.updateOverlayPosition()
# case "display_duration":
# model.updateOverlayTimes()
# case "fadeout_duration":
# model.updateOverlayTimes()
# Others Tab
def callbackSetEnableAutoClearMessageBox(value):
print("callbackSetEnableAutoClearMessageBox", value)
@@ -973,6 +1049,8 @@ def createMainWindow(splash):
# set UI and callback
view.register(
common_registers={
# "callback_enable_easter_egg": callbackEnableEasterEgg,
"callback_update_software": callbackUpdateSoftware,
"callback_restart_software": callbackRestartSoftware,
"callback_filepath_logs": callbackFilepathLogs,
@@ -1046,6 +1124,8 @@ def createMainWindow(splash):
"callback_delete_mic_word_filter": callbackDeleteMicWordFilter,
# Transcription Tab (Speaker)
"callback_set_speaker_device": callbackSetSpeakerDevice,
"list_speaker_device": model.getListOutputDevice(),
"callback_set_speaker_energy_threshold": callbackSetSpeakerEnergyThreshold,
"callback_set_speaker_dynamic_energy_threshold": callbackSetSpeakerDynamicEnergyThreshold,
"callback_check_speaker_threshold": callbackCheckSpeakerThreshold,
@@ -1057,6 +1137,11 @@ def createMainWindow(splash):
"callback_set_use_whisper_feature": callbackSetUserWhisperFeature,
"callback_set_whisper_weight_type": callbackSetWhisperWeightType,
# # VR Tab
# "callback_set_overlay_settings": callbackSetOverlaySettings,
# "callback_set_enable_overlay_small_log": callbackSetEnableOverlaySmallLog,
# "callback_set_overlay_small_log_settings": callbackSetOverlaySmallLogSettings,
# Others Tab
"callback_set_enable_auto_clear_chatbox": callbackSetEnableAutoClearMessageBox,
"callback_set_send_only_translated_messages": callbackSetEnableSendOnlyTranslatedMessages,

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 763 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 939 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

BIN
img/chato_delivering.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
img/chato_unpackaging.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
img/overlay_br_sakura.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
img/overlay_tl_sakura.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
img/unpackage_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
img/vrct_update_process.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -18,6 +18,7 @@ main_window:
textbox_tab_system: System
textbox_system_message:
enabled_easter_egg: Whoa! You caught us! There is something...like...easter-egg-ish function has enabled!
enabled_translation: Translation feature is turned on.
disabled_translation: Translation feature is turned off.
enabled_voice2chatbox: Transcription from the microphone has started.
@@ -49,7 +50,7 @@ main_window:
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?"
update_software: "Download the new version and automatically restart the app.\nIt'll take a while. Do it now?"
deny_update_software: Do it later
accept_update_software: Update and Restart
updating: Now updating...
@@ -65,6 +66,17 @@ selectable_language_window:
go_back_button: Go Back
overlay_settings:
restore_default_settings: Restore Default Settings
opacity: Opacity
ui_scaling: UI Scaling
x_position: X-axis (left-right)
y_position: Y-axis (up-down)
depth: Z-axis (front-back)
display_duration: Display duration
fadeout_duration: Fadeout duration
config_window:
config_title: Settings
compact_mode: Compact Mode
@@ -80,6 +92,7 @@ config_window:
transcription_mic: Mic
transcription_speaker: Speaker
transcription_internal_model: Transcription Model
vr: VR
others: Others
others_send_message_formats: Message Formats (Send)
others_received_message_formats: Message Formats (XSOverlay & Speaker2Chatbox)
@@ -168,6 +181,9 @@ config_window:
count_desc: "Current registered word count: %{count}"
speaker_device:
label: Speaker Device
speaker_dynamic_energy_threshold:
label_for_automatic: "Speaker Energy Threshold (Current Setting: Automatic)"
desc_for_automatic: "Automatically determine speaker input sensitivity."
@@ -201,6 +217,12 @@ config_window:
model_template: "%{model_name} model (%{capacity})"
recommended_model_template: "%{model_name} model (%{capacity}) (Recommended)"
enable_overlay_small_log:
label: Enable Overlay
# desc:
open_overlay_settings: Open Overlay Customized Settings
auto_clear_the_message_box:
label: Auto Clear The Message Box
@@ -214,7 +236,7 @@ config_window:
show_and_disable_enter_key: Show and disable to send when pressed enter key
notice_xsoverlay:
label: Notification XSOverlay (VR Only)
label: Notification XSOverlay
desc: Notify received messages by using XSOverlay's notification feature.
auto_export_message_logs:

View File

@@ -49,7 +49,7 @@ main_window:
cover_message: 設定画面が閉じられるまで、一時的に機能を停止しています。
confirmation_message:
update_software: "新しいバージョンをダウンロードして再起動します。\n少し時間がかかるかもしれません。今すぐ行いますか?"
update_software: "新しいバージョンをダウンロードしてアプリを再起動します。\n少し時間がかかります。今すぐ行いますか?"
deny_update_software: 後でする
accept_update_software: アップデートして再起動
updating: アップデート中...
@@ -65,6 +65,17 @@ selectable_language_window:
go_back_button: 戻る
overlay_settings:
restore_default_settings: 初期値に戻す
opacity: 透明度
ui_scaling: サイズ
x_position: X軸左右
y_position: Y軸上下
depth: Z軸前後
display_duration: 表示時間
fadeout_duration: フェードアウト時間
config_window:
config_title: 設定
compact_mode: コンパクトモード
@@ -167,6 +178,9 @@ config_window:
count_desc: "現在登録されている単語数: %{count}"
speaker_device:
label: スピーカー (デバイス)
speaker_dynamic_energy_threshold:
label_for_automatic: "スピーカー入力感度の調整 (現在の設定: 自動)"
desc_for_automatic: スピーカーの入力感度を自動的に調節する。
@@ -201,6 +215,13 @@ config_window:
recommended_model_template: "%{model_name} モデル (%{capacity}) (推奨)"
enable_overlay_small_log:
label: Overlay機能を有効
# desc:
open_overlay_settings: Overlay詳細設定を開く
auto_clear_the_message_box:
label: 送信後はチャットボックスを空にする
@@ -214,7 +235,7 @@ config_window:
show_and_disable_enter_key: 表示し、エンターキーでの送信を無効
notice_xsoverlay:
label: XSOverlayでの通知受け取り機能を有効 (VR限定)
label: XSOverlayでの通知受け取り機能を有効
desc: 文字起こし (受信) されたメッセージをXSOverlayの機能を使って通知として受け取れます。
auto_export_message_logs:

View File

@@ -32,7 +32,7 @@ main_window:
no_mic_device_detected_error: 마이크 디바이스를 찾지 못했습니다.
no_speaker_device_detected_error: 스피커 디바이스를 찾지 못했습니다.
translation_engine_limit_error: 번역 엔진을 자동으로 변경했습니다. 대상 번역 엔진에 대한 요청이 너무 많아 일시적으로 접근이 제한되었습니다. 해당 번역 엔진을 사용하려면 잠시 기다린 후 VRCT를 재부팅하여 다시 시도해 보시기 바랍니다
translation_engine_limit_error: 번역 엔진을 자동으로 변경했습니다. 대상 번역 엔진에 대한 요청이 너무 많아 일시적으로 접근이 제한되었습니다. 해당 번역 엔진을 사용하려면 잠시 기다린 후 VRCT를 재시작하여 다시 시도해 보시기 바랍니다
detected_by_word_filter: 단어 필터에 등록된 단어 %{detected_message}(이)가 감지되어 전송하지 않았습니다.
@@ -49,14 +49,14 @@ main_window:
cover_message: 설정 화면이 닫힐 때까지 일시적으로 기능을 정지하고 있습니다.
confirmation_message:
update_software: "새 버전을 다운로드하고 재부팅합니다. \n 조금 시간이 걸릴 수 있습니다. 지금 시작할까요?"
update_software: "새 버전을 다운로드하고 재시작합니다. \n 조금 시간이 걸니다. 지금 시작할까요?"
deny_update_software: 나중에 하기
accept_update_software: 업데이트 및 재부팅
accept_update_software: 업데이트 및 재시작
updating: 업데이트 중...
detected_over_ui_size: "현재 UI 크기: %{current_ui_size}\nVRCT의 창 크기가 사용자의 디스플레이 크기보다 클 수 있습니다. \n* 디스플레이 크기에 따라 여러 번 재설정해야 할 수도 있습니다."
deny_adjust_ui_size: "지금 상태를 유지"
accept_adjust_ui_size: "작게 줄이고 재부팅"
accept_adjust_ui_size: "작게 줄이고 재시작"
selectable_language_window:
@@ -69,7 +69,7 @@ config_window:
config_title: 설정
compact_mode: 컴팩트 모드
version: 버전 %{version}
restart_message: 부팅하여 변경 사항을 적용합니다.
restart_message: 시작하여 변경 사항을 적용합니다.
common_error_message:
invalid_value: 유효하지 않은 값입니다.
@@ -79,6 +79,7 @@ config_window:
transcription: 음성인식
transcription_mic: 마이크
transcription_speaker: 스피커
transcription_internal_model: 내부 엔진
others: 기타
others_send_message_formats: 메시지 형식 (전송)
others_received_message_formats: 메시지 형식 (수신)
@@ -127,10 +128,13 @@ config_window:
deepl_auth_key:
label: DeepL 인증키
desc: "사용시 메인화면에 있는 %{translator}를 DeepL_API로 변경해 주세요.\n지원하지 않는 언어도 있습니다."
open_auth_key_webpage: DeepL 계정 페이지 열기
auth_key_success: 인증키 갱신이 완료되었습니다.
auth_key_error: 인증키가 잘못되었거나 API 사용 제한이 상한에 도달했습니다.
mic_host:
label: 마이크 호스트/드라이버
# desc:
mic_device:
label: 마이크 장치
@@ -164,6 +168,9 @@ config_window:
count_desc: "현재 등록되어 있는 단어 수: %{count}"
speaker_device:
label: 스피커 장치
speaker_dynamic_energy_threshold:
label_for_automatic: "음성 입력 최소 볼륨 (현재 설정: 자동)"
desc_for_automatic: "스피커의 입력 감도를 자동으로 조절합니다."
@@ -187,6 +194,15 @@ config_window:
desc: 식된 단어 수의 하한값으로, 이 수치를 초과하는 경우에만 결과를 로그에 표시합니다.
error_message: 0 이상의 숫자만 설정할 수 있습니다.
use_whisper_feature:
label: 음성 인식에 Whisper 모델을 사용
desc: 일부 언어에서는 음성 인식의 정확도가 향상될 수 있어요. 음성 인식 중 CPU 사용률이 올라가기 때문에 사용하시는 PC의 사양을 고려하여 이 기능을 사용해주세요.
whisper_weight_type:
label: Whisper 모델 타입
desc: "기본적으로 용량이 많은 모델일수록 정밀도는 높지만, 음성 인식의 시간이 늘어나며 CPU 사용률도 늘어나요.각 모델의 설명은 문서를 참조해주세요.\n※특히 medium보다 용량이 큰 모델은 CPU의 성능에 따라서는 사용조차 어려울 수 있어요. "
model_template: "%{model_name} 모델 (%{capacity})"
recommended_model_template: "%{model_name} 모델 (%{capacity}) (권장)"
auto_clear_the_message_box:
label: 챗박스 자동 삭제
@@ -201,7 +217,7 @@ config_window:
show_and_disable_enter_key: 표시 (Enter 키 전송 비활성화)
notice_xsoverlay:
label: XSOverlay에서 알림 수신 기능 활성화 (VR 전용)
label: XSOverlay에서 알림 수신 기능 활성화
desc: 수신된 메시지를 XSOverlay의 기능을 통해 알림으로 받아볼 수 있습니다.
auto_export_message_logs:

214
model.py
View File

@@ -16,7 +16,7 @@ import webbrowser
from typing import Callable
from flashtext import KeywordProcessor
from models.translation.translation_translator import Translator
from models.transcription.transcription_utils import getInputDevices, getDefaultOutputDevice
from models.transcription.transcription_utils import getInputDevices, getOutputDevices
from models.osc.osc_tools import sendTyping, sendMessage, sendTestAction, receiveOscParameters
from models.transcription.transcription_recorder import SelectedMicEnergyAndAudioRecorder, SelectedSpeakerEnergyAndAudioRecorder
from models.transcription.transcription_recorder import SelectedMicEnergyRecorder, SelectedSpeakerEnergyRecorder
@@ -26,6 +26,9 @@ from models.translation.translation_languages import translation_lang
from models.transcription.transcription_languages import transcription_lang
from models.translation.translation_utils import checkCTranslate2Weight
from models.transcription.transcription_whisper import checkWhisperWeight
# from models.overlay.overlay import Overlay
# from models.overlay.overlay_image import OverlayImage
from config import config
class threadFnc(Thread):
@@ -37,7 +40,7 @@ class threadFnc(Thread):
def stop(self):
self._stop.set()
def stopped(self):
return self._stop.isSet()
return self._stop.is_set()
def run(self):
while True:
if self.stopped():
@@ -65,8 +68,22 @@ class Model:
self.speaker_audio_recorder = None
self.speaker_energy_recorder = None
self.speaker_energy_plot_progressbar = None
self.previous_send_message = ""
self.previous_receive_message = ""
self.translator = Translator()
self.keyword_processor = KeywordProcessor()
# self.overlay = Overlay(
# config.OVERLAY_SMALL_LOG_SETTINGS["x_pos"],
# config.OVERLAY_SMALL_LOG_SETTINGS["y_pos"],
# config.OVERLAY_SMALL_LOG_SETTINGS["depth"],
# config.OVERLAY_SMALL_LOG_SETTINGS["display_duration"],
# config.OVERLAY_SMALL_LOG_SETTINGS["fadeout_duration"],
# config.OVERLAY_SETTINGS["opacity"],
# config.OVERLAY_SETTINGS["ui_scaling"],
# )
# self.overlay_image = OverlayImage()
# self.pre_overlay_message = None
# self.th_overlay = None
def checkCTranslatorCTranslate2ModelWeight(self):
return checkCTranslate2Weight(config.PATH_LOCAL, config.CTRANSLATE2_WEIGHT_TYPE)
@@ -197,6 +214,20 @@ class Model:
def checkKeywords(self, message):
return len(self.keyword_processor.extract_keywords(message)) != 0
def detectRepeatSendMessage(self, message):
repeat_flag = False
if self.previous_send_message == message:
repeat_flag = True
self.previous_send_message = message
return repeat_flag
def detectRepeatReceiveMessage(self, message):
repeat_flag = False
if self.previous_receive_message == message:
repeat_flag = True
self.previous_receive_message = message
return repeat_flag
@staticmethod
def oscStartSendTyping():
sendTyping(True, config.OSC_IP_ADDRESS, config.OSC_PORT)
@@ -256,35 +287,51 @@ class Model:
@staticmethod
def updateSoftware(restart:bool=True, func=None):
filename = 'VRCT.zip'
program_name = 'VRCT.exe'
folder_name = '_internal'
tmp_directory_name = 'tmp'
batch_name = 'update.bat'
current_directory = config.PATH_LOCAL
def updateSoftwareTask():
filename = 'VRCT.zip'
program_name = 'VRCT.exe'
folder_name = '_internal'
tmp_directory_name = 'tmp'
batch_name = 'update.bat'
current_directory = config.PATH_LOCAL
try:
res = requests_get(config.GITHUB_URL)
assets = res.json()['assets']
url = [i["browser_download_url"] for i in assets if i["name"] == filename][0]
with tempfile.TemporaryDirectory() as tmp_path:
res = requests_get(url, stream=True)
file_size = int(res.headers.get('content-length', 0))
total_chunk = 0
with open(os_path.join(tmp_path, filename), 'wb') as file:
for chunk in res.iter_content(chunk_size=1024*5):
file.write(chunk)
if isinstance(func, Callable):
try:
res = requests_get(config.GITHUB_URL)
assets = res.json()['assets']
url = [i["browser_download_url"] for i in assets if i["name"] == filename][0]
with tempfile.TemporaryDirectory() as tmp_path:
res = requests_get(url, stream=True)
file_size = int(res.headers.get('content-length', 0))
total_chunk = 0
with open(os_path.join(tmp_path, filename), 'wb') as file:
for chunk in res.iter_content(chunk_size=1024*5):
file.write(chunk)
total_chunk += len(chunk)
func(total_chunk/file_size)
if isinstance(func, Callable):
func(progress=total_chunk/file_size, progress_type="downloading")
print(f"downloaded {total_chunk}/{file_size}")
with ZipFile(os_path.join(tmp_path, filename)) as zf:
zf.extractall(os_path.join(current_directory, tmp_directory_name))
copyfile(os_path.join(current_directory, folder_name, "batch", batch_name), os_path.join(current_directory, batch_name))
command = [os_path.join(current_directory, batch_name), program_name, folder_name, tmp_directory_name, str(restart)]
Popen(command, cwd=current_directory)
except Exception:
webbrowser.open(config.BOOTH_URL, new=2, autoraise=True)
with ZipFile(os_path.join(tmp_path, filename)) as zf:
total_files = len(zf.infolist())
extracted_files = 0
for file_info in zf.infolist():
extracted_files += 1
zf.extract(file_info, os_path.join(current_directory, tmp_directory_name))
if isinstance(func, Callable):
func(progress=extracted_files/total_files, progress_type="extracting")
print(f"extracted {extracted_files}/{total_files}")
copyfile(os_path.join(current_directory, folder_name, "batch", batch_name), os_path.join(current_directory, batch_name))
command = [os_path.join(current_directory, batch_name), program_name, folder_name, tmp_directory_name, str(restart)]
Popen(command, cwd=current_directory)
except Exception:
import traceback
with open('error.log', 'a') as f:
traceback.print_exc(file=f)
webbrowser.open(config.BOOTH_URL, new=2, autoraise=True)
th_update_software = Thread(target=updateSoftwareTask)
th_update_software.daemon = True
th_update_software.start()
@staticmethod
def reStartSoftware():
@@ -305,15 +352,13 @@ class Model:
return [device["name"] for device in getInputDevices()[config.CHOICE_MIC_HOST]]
@staticmethod
def getInputDefaultDevice():
return [device["name"] for device in getInputDevices()[config.CHOICE_MIC_HOST]][0]
@staticmethod
def getOutputDefaultDevice():
return getDefaultOutputDevice()["name"]
def getListOutputDevice():
return [device["name"] for device in getOutputDevices()]
def startMicTranscript(self, fnc, error_fnc=None):
if config.CHOICE_MIC_HOST == "NoHost" or config.CHOICE_MIC_DEVICE == "NoDevice":
mic_device_list = getInputDevices().get(config.CHOICE_MIC_HOST, [{"name": "NoDevice"}])
choice_mic_device = [device for device in mic_device_list if device["name"] == config.CHOICE_MIC_DEVICE]
if len(choice_mic_device) == 0:
try:
error_fnc()
except Exception:
@@ -322,14 +367,14 @@ class Model:
mic_audio_queue = Queue()
# mic_energy_queue = Queue()
device = [device for device in getInputDevices()[config.CHOICE_MIC_HOST] if device["name"] == config.CHOICE_MIC_DEVICE][0]
mic_device = 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 = SelectedMicEnergyAndAudioRecorder(
device=device,
device=mic_device,
energy_threshold=config.INPUT_MIC_ENERGY_THRESHOLD,
dynamic_energy_threshold=config.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD,
record_timeout=record_timeout,
@@ -346,9 +391,9 @@ class Model:
whisper_weight_type=config.WHISPER_WEIGHT_TYPE,
)
def sendMicTranscript():
self.mic_transcriber.transcribeAudioQueue(mic_audio_queue, config.SOURCE_LANGUAGE, config.SOURCE_COUNTRY)
message = self.mic_transcriber.getTranscript()
try:
self.mic_transcriber.transcribeAudioQueue(mic_audio_queue, config.SOURCE_LANGUAGE, config.SOURCE_COUNTRY)
message = self.mic_transcriber.getTranscript()
fnc(message)
except Exception:
pass
@@ -382,14 +427,16 @@ class Model:
self.mic_print_transcript.stop()
self.mic_print_transcript = None
if isinstance(self.mic_audio_recorder, SelectedMicEnergyAndAudioRecorder):
self.mic_audio_recorder.stop()
self.mic_audio_recorder.stop(wait_for_stop=True)
self.mic_audio_recorder = None
# if isinstance(self.mic_get_energy, threadFnc):
# self.mic_get_energy.stop()
# self.mic_get_energy = None
def startCheckMicEnergy(self, fnc, end_fnc, error_fnc=None):
if config.CHOICE_MIC_HOST == "NoHost" or config.CHOICE_MIC_DEVICE == "NoDevice":
mic_device_list = getInputDevices().get(config.CHOICE_MIC_HOST, [{"name": "NoDevice"}])
choice_mic_device = [device for device in mic_device_list if device["name"] == config.CHOICE_MIC_DEVICE]
if len(choice_mic_device) == 0:
try:
error_fnc()
except Exception:
@@ -406,7 +453,7 @@ class Model:
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]
mic_device = choice_mic_device[0]
self.mic_energy_recorder = SelectedMicEnergyRecorder(mic_device)
self.mic_energy_recorder.recordIntoQueue(mic_energy_queue)
self.mic_energy_plot_progressbar = threadFnc(sendMicEnergy, end_fnc=end_fnc)
@@ -418,12 +465,13 @@ class Model:
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.stop(wait_for_stop=True)
self.mic_energy_recorder = None
def startSpeakerTranscript(self, fnc, error_fnc=None):
speaker_device = getDefaultOutputDevice()
if speaker_device["name"] == "NoDevice":
speaker_device_list = getOutputDevices()
choice_speaker_device = [device for device in speaker_device_list if device["name"] == config.CHOICE_SPEAKER_DEVICE]
if len(choice_speaker_device) == 0:
try:
error_fnc()
except Exception:
@@ -432,6 +480,7 @@ class Model:
speaker_audio_queue = Queue()
# speaker_energy_queue = Queue()
speaker_device = choice_speaker_device[0]
record_timeout = config.INPUT_SPEAKER_RECORD_TIMEOUT
phase_timeout = config.INPUT_SPEAKER_PHRASE_TIMEOUT
if record_timeout > phase_timeout:
@@ -455,9 +504,9 @@ class Model:
whisper_weight_type=config.WHISPER_WEIGHT_TYPE,
)
def sendSpeakerTranscript():
self.speaker_transcriber.transcribeAudioQueue(speaker_audio_queue, config.TARGET_LANGUAGE, config.TARGET_COUNTRY)
message = self.speaker_transcriber.getTranscript()
try:
self.speaker_transcriber.transcribeAudioQueue(speaker_audio_queue, config.TARGET_LANGUAGE, config.TARGET_COUNTRY)
message = self.speaker_transcriber.getTranscript()
fnc(message)
except Exception:
pass
@@ -491,15 +540,16 @@ class Model:
self.speaker_print_transcript.stop()
self.speaker_print_transcript = None
if isinstance(self.speaker_audio_recorder, SelectedSpeakerEnergyAndAudioRecorder):
self.speaker_audio_recorder.stop()
self.speaker_audio_recorder.stop(wait_for_stop=True)
self.speaker_audio_recorder = None
# if isinstance(self.speaker_get_energy, threadFnc):
# self.speaker_get_energy.stop()
# self.speaker_get_energy = None
def startCheckSpeakerEnergy(self, fnc, end_fnc, error_fnc=None):
speaker_device = getDefaultOutputDevice()
if speaker_device["name"] == "NoDevice":
speaker_device_list = getOutputDevices()
choice_speaker_device = [device for device in speaker_device_list if device["name"] == config.CHOICE_SPEAKER_DEVICE]
if len(choice_speaker_device) == 0:
try:
error_fnc()
except Exception:
@@ -516,6 +566,7 @@ class Model:
sleep(0.01)
speaker_energy_queue = Queue()
speaker_device = choice_speaker_device[0]
self.speaker_energy_recorder = SelectedSpeakerEnergyRecorder(speaker_device)
self.speaker_energy_recorder.recordIntoQueue(speaker_energy_queue)
self.speaker_energy_plot_progressbar = threadFnc(sendSpeakerEnergy, end_fnc=end_fnc)
@@ -527,10 +578,71 @@ class Model:
self.speaker_energy_plot_progressbar.stop()
self.speaker_energy_plot_progressbar = None
if isinstance(self.speaker_energy_recorder, SelectedSpeakerEnergyRecorder):
self.speaker_energy_recorder.stop()
self.speaker_energy_recorder.stop(wait_for_stop=True)
self.speaker_energy_recorder = None
def notificationXSOverlay(self, message):
xsoverlayForVRCT(content=f"{message}")
# def createOverlayImageShort(self, message, translation):
# your_language = config.TARGET_LANGUAGE
# target_language = config.SOURCE_LANGUAGE
# ui_type = config.OVERLAY_UI_TYPE
# self.pre_overlay_message = {
# "message" : message,
# "your_language" : your_language,
# "translation" : translation,
# "target_language" : target_language,
# "ui_type" : ui_type,
# }
# return self.overlay_image.createOverlayImageShort(message, your_language, translation, target_language, ui_type)
# def createOverlayImageLong(self, message_type, message, translation):
# your_language = config.TARGET_LANGUAGE if message_type == "receive" else config.SOURCE_LANGUAGE
# target_language = config.SOURCE_LANGUAGE if message_type == "receive" else config.TARGET_LANGUAGE
# return self.overlay_image.create_overlay_image_long(message_type, message, your_language, translation, target_language)
# def clearOverlayImage(self):
# if self.overlay.initialized is True:
# self.overlay.uiManager.uiClear()
# def updateOverlay(self, img):
# if self.overlay.initialized is True:
# self.overlay.uiManager.uiUpdate(img)
# def startOverlay(self):
# if self.overlay.initialized is False:
# self.overlay.init()
# if self.overlay.initialized is True and self.th_overlay is None:
# self.th_overlay = Thread(target=self.overlay.startOverlay)
# self.th_overlay.daemon = True
# self.th_overlay.start()
# def updateOverlayPosition(self):
# if self.overlay.initialized is True:
# pos = (config.OVERLAY_SMALL_LOG_SETTINGS["x_pos"], config.OVERLAY_SMALL_LOG_SETTINGS["y_pos"])
# self.overlay.uiManager.setPosition(pos)
# depth = config.OVERLAY_SMALL_LOG_SETTINGS["depth"]
# self.overlay.uiManager.setDepth(depth)
# self.overlay.uiManager.posUpdate()
# def updateOverlayTimes(self):
# if self.overlay.initialized is True:
# display_duration = config.OVERLAY_SMALL_LOG_SETTINGS["display_duration"]
# self.overlay.uiManager.setFadeTime(display_duration)
# fadeout_duration = config.OVERLAY_SMALL_LOG_SETTINGS["fadeout_duration"]
# self.overlay.uiManager.setFadeInterval(fadeout_duration)
# self.overlay.uiManager.update()
# def updateOverlayImageOpacity(self):
# if self.overlay.initialized is True:
# opacity = config.OVERLAY_SETTINGS["opacity"]
# self.overlay.uiManager.setTransparency(opacity)
# def updateOverlayImageUiScaling(self):
# if self.overlay.initialized is True:
# ui_scaling = config.OVERLAY_SETTINGS["ui_scaling"]
# self.overlay.uiManager.setUiScaling(ui_scaling)
model = Model()

256
models/overlay/overlay.py Normal file
View File

@@ -0,0 +1,256 @@
import psutil
import ctypes
import time
import asyncio
import openvr
from PIL import Image
def checkSteamvrRunning():
for proc in psutil.process_iter():
if "vrserver.exe" == proc.name().lower():
return True
return False
# This code is based on the following source:
# [GOpy](https://github.com/MeroFune/GOpy)
def mat34Id():
arr = openvr.HmdMatrix34_t()
arr[0][0] = 1
arr[1][1] = 1
arr[2][2] = 1
return arr
class UIElement:
def __init__(self, overlayRoot, key: str, name: str, settings: dict = None) -> None:
"""
pos is a 2-tuple representing (x, y) normalized position of the overlay on the screen
"""
self.overlay = overlayRoot
self.overlayKey = key
self.overlayName = name
self.settings = settings
self.handle = self.overlay.createOverlay(self.overlayKey, self.overlayName)
self.setImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) # blank image for default
self.setColor(self.settings['Color'])
self.setTransparency(self.settings['Transparency'])
self.overlay.setOverlayWidthInMeters(
self.handle,
self.settings['Ui_scaling']
)
self.updatePosition()
self.overlay.showOverlay(self.handle)
def setImage(self, img):
# configure overlay appearance
width, height = img.size
img = img.tobytes()
img = (ctypes.c_char * len(img)).from_buffer_copy(img)
self.overlay.setOverlayRaw(self.handle, img, width, height, 4)
def setColor(self, col):
"""
col is a 3-tuple representing (r, g, b)
"""
self.overlay.setOverlayColor(self.handle, col[0], col[1], col[2])
def setTransparency(self, a):
self.overlay.setOverlayAlpha(self.handle, a)
def updatePosition(self):
self.transform = mat34Id() # no rotation required for HMD attachment
# assign position
self.transform[0][3] = self.settings["Normalized_icon_X_position"] * self.settings['Icon_plane_depth']
self.transform[1][3] = self.settings["Normalized_icon_Y_position"] * self.settings['Icon_plane_depth']
self.transform[2][3] = - self.settings['Icon_plane_depth']
self.overlay.setOverlayTransformTrackedDeviceRelative(
self.handle,
openvr.k_unTrackedDeviceIndex_Hmd,
self.transform
)
def setPosition(self, pos):
"""
pos is a 2-tuple representing normalized (x, y)
"""
self.settings["Normalized_icon_X_position"] = pos[0]
self.settings["Normalized_icon_Y_position"] = pos[1]
def setDepth(self, depth):
self.settings["Icon_plane_depth"] = depth
def setUiScaling(self, ui_scaling):
self.overlay.setOverlayWidthInMeters(
self.handle,
ui_scaling,
)
class UIManager:
def __init__(self, overlay_key, overlay_name, settings):
self.overlay = openvr.IVROverlay()
self.settings = settings
self.overlayUI = UIElement(
self.overlay,
overlay_key,
overlay_name,
self.settings,
)
self.fadeRatio = 1
self.lastUpdate = time.monotonic()
def update(self):
currTime = time.monotonic()
if self.settings['Fade_interval'] != 0:
self.evaluateTransparencyFade(self.lastUpdate, currTime)
def uiClear(self):
self.overlayUI.setImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0)))
self.overlayUI.setTransparency(self.settings['Transparency'])
self.lastUpdate = time.monotonic()
def uiUpdate(self, img):
self.overlayUI.setImage(img)
self.overlayUI.setTransparency(self.settings['Transparency'])
self.lastUpdate = time.monotonic()
def evaluateTransparencyFade(self, lastUpdate, currentTime):
if (currentTime - lastUpdate) > self.settings['Fade_time']:
timeThroughInterval = currentTime - lastUpdate - self.settings['Fade_time']
self.fadeRatio = 1 - timeThroughInterval / self.settings['Fade_interval']
if self.fadeRatio < 0:
self.fadeRatio = 0
self.overlayUI.setTransparency(self.fadeRatio * self.settings['Transparency'])
def posUpdate(self):
self.overlayUI.updatePosition()
def setPosition(self, pos):
self.overlayUI.setPosition(pos)
def setDepth(self, depth):
self.overlayUI.setDepth(depth)
def setFadeTime(self, fade_time):
self.settings["Fade_time"] = fade_time
def setFadeInterval(self, fade_interval):
self.settings["Fade_interval"] = fade_interval
def setUiScaling(self, ui_scaling):
self.overlayUI.setUiScaling(ui_scaling)
def setTransparency(self, transparency):
self.settings["Transparency"] = transparency
self.overlayUI.setTransparency(self.fadeRatio * self.settings['Transparency'])
class Overlay:
def __init__(self, x, y , depth, fade_time, fade_interval, transparency, ui_scaling):
self.initialized = False
settings = {
"Color": [1, 1, 1],
"Transparency": transparency,
"Normalized_icon_X_position": x,
"Normalized_icon_Y_position": y,
"Icon_plane_depth": depth,
"Fade_time": fade_time,
"Fade_interval": fade_interval,
"Ui_scaling": ui_scaling,
}
self.settings = settings
self.system = None
def init(self):
try:
if checkSteamvrRunning() is True:
self.system = openvr.init(openvr.VRApplication_Background)
self.initialized = True
except Exception as e:
print("Could not initialise OpenVR")
print(e)
async def mainLoop(self):
while self.checkActive() is True:
startTime = time.monotonic()
self.uiManager.update()
sleepTime = (1 / 60) - (time.monotonic() - startTime)
if sleepTime > 0:
await asyncio.sleep(sleepTime)
async def initMain(self):
if self.initialized is True:
self.uiManager = UIManager("Overlay_Speaker2log", "SOverlay_Speaker2log_UI", self.settings)
await self.mainLoop()
def startOverlay(self):
asyncio.run(self.initMain())
def shutdown(self):
self.system = None
self.initialized = False
openvr.shutdown()
def checkActive(self):
try:
if self.system is not None and self.initialized is True:
new_event = openvr.VREvent_t()
while self.system.pollNextEvent(new_event):
if new_event.eventType == openvr.VREvent_Quit:
self.shutdown()
return False
return True
except Exception as e:
print("Could not check SteamVR running")
print(e)
return False
if __name__ == '__main__':
from overlay_image import OverlayImage
from threading import Thread, Event
class threadFnc(Thread):
def __init__(self, fnc, end_fnc=None, daemon=True, *args, **kwargs):
super(threadFnc, self).__init__(daemon=daemon, *args, **kwargs)
self.fnc = fnc
self.end_fnc = end_fnc
self._stop = Event()
def stop(self):
self._stop.set()
def stopped(self):
return self._stop.is_set()
def run(self):
while True:
if self.stopped():
if callable(self.end_fnc):
self.end_fnc()
return
self.fnc(*self._args, **self._kwargs)
overlay = Overlay()
overlay_image = OverlayImage()
if overlay.initialized is False:
overlay.init()
if overlay.initialized is True:
t = threadFnc(overlay.startOverlay)
t.start()
time.sleep(1)
img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "Hello,World!Goodbye", "Japanese", ui_type="sakura")
# img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", ui_type="sakura")
if overlay.initialized is True:
overlay.uiManager.uiUpdate(img)
time.sleep(10)
img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "안녕하세요, 세계!안녕", "Korean")
if overlay.initialized is True:
overlay.uiManager.uiUpdate(img)
time.sleep(10)
img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "你好世界!再见", "Chinese Simplified")
if overlay.initialized is True:
overlay.uiManager.uiUpdate(img)
time.sleep(10)

View File

@@ -0,0 +1,231 @@
from os import path as os_path
# from datetime import datetime
from typing import Tuple
from PIL import Image, ImageDraw, ImageFont
class OverlayImage:
# TEXT_COLOR_LARGE = (223, 223, 223)
# TEXT_COLOR_SMALL = (190, 190, 190)
# TEXT_COLOR_SEND = (70, 161, 146)
# TEXT_COLOR_RECEIVE = (220, 20, 60)
# TEXT_COLOR_TIME = (120, 120, 120)
# FONT_SIZE_LARGE = HEIGHT
# FONT_SIZE_SMALL = int(FONT_SIZE_LARGE * 2 / 3)
LANGUAGES = {
"Japanese": "NotoSansJP-Regular",
"Korean": "NotoSansKR-Regular",
"Chinese Simplified": "NotoSansSC-Regular",
"Chinese Traditional": "NotoSansTC-Regular",
}
def __init__(self):
pass
@staticmethod
def concatenateImagesVertically(img1: Image, img2: Image) -> Image:
dst = Image.new('RGBA', (img1.width, img1.height + img2.height))
dst.paste(img1, (0, 0))
dst.paste(img2, (0, img1.height))
return dst
@staticmethod
def addImageMargin(image: Image, top: int, right: int, bottom: int, left: int, color: Tuple[int, int, int, int]) -> Image:
width, height = image.size
new_width = width + right + left
new_height = height + top + bottom
result = Image.new(image.mode, (new_width, new_height), color)
result.paste(image, (left, top))
return result
# def create_textimage(self, message_type, size, text, language):
# font_size = self.FONT_SIZE_LARGE if size == "large" else self.FONT_SIZE_SMALL
# text_color = self.TEXT_COLOR_LARGE if size == "large" else self.TEXT_COLOR_SMALL
# anchor = "lm" if message_type == "receive" else "rm"
# text_x = 0 if message_type == "receive" else self.WIDTH
# align = "left" if message_type == "receive" else "right"
# font_family = self.LANGUAGES.get(language, "NotoSansJP-Regular")
# img = Image.new("RGBA", (0, 0), (0, 0, 0, 0))
# draw = ImageDraw.Draw(img)
# font = ImageFont.truetype(os_path.join(os_path.dirname(__file__), "fonts", f"{font_family}.ttf"), font_size)
# # font = ImageFont.truetype(os_path.join("./fonts", f"{font_family}.ttf"), font_size)
# text_width = draw.textlength(text, font)
# character_width = text_width // len(text)
# character_line_num = int(self.WIDTH // character_width)
# if len(text) > character_line_num:
# text = "\n".join([text[i:i+character_line_num] for i in range(0, len(text), character_line_num)])
# n_num = len(text.split("\n")) - 1
# text_height = int(font_size*(n_num+2))
# img = Image.new("RGBA", (self.WIDTH, text_height), (0, 0, 0, 0))
# draw = ImageDraw.Draw(img)
# text_y = text_height // 2
# draw.multiline_text((text_x, text_y), text, text_color, anchor=anchor, stroke_width=0, font=font, align=align)
# return img
# def create_textimage_message_type(self, message_type):
# anchor = "lm" if message_type == "receive" else "rm"
# text = "Receive" if message_type == "receive" else "Send"
# text_color = self.TEXT_COLOR_RECEIVE if message_type == "receive" else self.TEXT_COLOR_SEND
# text_color_time = self.TEXT_COLOR_TIME
# now = datetime.now()
# formatted_time = now.strftime("%H:%M")
# font_size = self.FONT_SIZE_SMALL
# img = Image.new("RGBA", (0, 0), (0, 0, 0, 0))
# draw = ImageDraw.Draw(img)
# font = ImageFont.truetype(os_path.join(os_path.dirname(__file__), "fonts", "NotoSansJP-Regular.ttf"), font_size)
# # font = ImageFont.truetype(os_path.join("./fonts", "NotoSansJP-Regular.ttf"), font_size)
# text_height = font_size*2
# text_width = draw.textlength(formatted_time, font)
# character_width = text_width // len(formatted_time)
# img = Image.new("RGBA", (self.WIDTH, text_height), (0, 0, 0, 0))
# draw = ImageDraw.Draw(img)
# text_y = text_height // 2
# text_time_x = 0 if message_type == "receive" else self.WIDTH - (text_width + character_width)
# text_x = (text_width + character_width) if message_type == "receive" else self.WIDTH
# draw.text((text_time_x, text_y), formatted_time, text_color_time, anchor=anchor, stroke_width=0, font=font)
# draw.text((text_x, text_y), text, text_color, anchor=anchor, stroke_width=0, font=font)
# return img
# def create_textbox(self, message_type, message, your_language, translation, target_language):
# message_type_img = self.create_textimage_message_type(message_type)
# if len(translation) > 0 and target_language is not None:
# img = self.create_textimage(message_type, "small", message, your_language)
# translation_img = self.create_textimage(message_type, "large",translation, target_language)
# img = self.concatenateImagesVertically(img, translation_img)
# else:
# img = self.create_textimage(message_type, "large", message, your_language)
# return self.concatenateImagesVertically(message_type_img, img)
# def create_overlay_image_long(self, message_type, message, your_language, translation="", target_language=None):
# if len(self.log_data) > 10:
# self.log_data.pop(0)
# self.log_data.append(
# {
# "message_type":message_type,
# "message":message,
# "your_language":your_language,
# "translation":translation,
# "target_language":target_language,
# }
# )
# imgs = []
# for log in self.log_data:
# message_type = log["message_type"]
# message = log["message"]
# your_language = log["your_language"]
# translation = log["translation"]
# target_language = log["target_language"]
# img = self.create_textbox(message_type, message, your_language, translation, target_language)
# imgs.append(img)
# img = imgs[0]
# for i in imgs[1:]:
# img = self.concatenateImagesVertically(img, i)
# img = self.addImageMargin(img, 0, 20, 0, 20, (0, 0, 0, 0))
# width, height = img.size
# background = Image.new("RGBA", (width, height), (0, 0, 0, 0))
# draw = ImageDraw.Draw(background)
# draw.rounded_rectangle([(0, 0), (width, height)], radius=15, fill=self.BACKGROUND_COLOR, outline=self.BACKGROUND_OUTLINE_COLOR, width=5)
# img = Image.alpha_composite(background, img)
# return img
def getUiSize(self):
return {
"width": int(960*4),
"height": int(23*4),
"font_size": int(23*4),
}
def getUiColors(self, ui_type):
match ui_type:
case "default":
background_color = (41, 42, 45)
background_outline_color = (41, 42, 45)
text_color = (223, 223, 223)
case "sakura":
background_color = (225, 40, 30)
background_outline_color = (255, 255, 255)
text_color = (223, 223, 223)
return {
"background_color": background_color,
"background_outline_color": background_outline_color,
"text_color": text_color
}
def createDecorationImage(self, ui_type, image_size):
decoration_image = Image.new("RGBA", image_size, (0, 0, 0, 0))
match ui_type:
case "default":
pass
case "sakura":
margin = 7
alpha_ratio = 0.4
overlay_tl = Image.open(os_path.join(os_path.dirname(os_path.dirname(os_path.dirname(__file__))), "img", "overlay_tl_sakura.png"))
overlay_br = Image.open(os_path.join(os_path.dirname(os_path.dirname(os_path.dirname(__file__))), "img", "overlay_br_sakura.png"))
if overlay_tl.size[1] > image_size[1]:
overlay_tl = overlay_tl.resize((image_size[1]-margin, image_size[1]-margin))
if overlay_br.size[1] > image_size[1]:
overlay_br = overlay_br.resize((image_size[1]-margin, image_size[1]-margin))
alpha = overlay_tl.getchannel("A")
alpha = alpha.point(lambda x: x * alpha_ratio)
overlay_tl.putalpha(alpha)
alpha = overlay_br.getchannel("A")
alpha = alpha.point(lambda x: x * alpha_ratio)
overlay_br.putalpha(alpha)
decoration_image.paste(overlay_tl, (margin, margin))
decoration_image.paste(overlay_br, (image_size[0]-overlay_br.size[0]-margin, image_size[1]-overlay_br.size[1]-margin))
return decoration_image
def createTextboxShort(self, text, language, text_color, base_width, base_height, font_size):
font_family = self.LANGUAGES.get(language, "NotoSansJP-Regular")
img = Image.new("RGBA", (base_width, base_height), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
font = ImageFont.truetype(os_path.join(os_path.dirname(os_path.dirname(os_path.dirname(__file__))), "fonts", f"{font_family}.ttf"), font_size)
text_width = draw.textlength(text, font)
character_width = text_width // len(text)
character_line_num = int((base_width) // character_width) - 12
if len(text) > character_line_num:
text = "\n".join([text[i:i+character_line_num] for i in range(0, len(text), character_line_num)])
text_height = font_size * (len(text.split("\n")) + 1) + 20
img = Image.new("RGBA", (base_width, text_height), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
text_x = base_width // 2
text_y = text_height // 2
draw.text((text_x, text_y), text, text_color, anchor="mm", stroke_width=0, font=font, align="center")
return img
def createOverlayImageShort(self, message, your_language, translation="", target_language=None, ui_type="default"):
ui_size = self.getUiSize()
height = ui_size["height"]
width = ui_size["width"]
font_size = ui_size["font_size"]
ui_colors = self.getUiColors(ui_type)
text_color = ui_colors["text_color"]
background_color = ui_colors["background_color"]
background_outline_color = ui_colors["background_outline_color"]
img = self.createTextboxShort(message, your_language, text_color, width, height, font_size)
if len(translation) > 0 and target_language is not None:
translation_img = self.createTextboxShort(translation, target_language, text_color, width, height, font_size)
img = self.concatenateImagesVertically(img, translation_img)
background = Image.new("RGBA", img.size, (0, 0, 0, 0))
draw = ImageDraw.Draw(background)
draw.rounded_rectangle([(0, 0), img.size], radius=30, fill=background_color, outline=background_outline_color, width=5)
decoration_image = self.createDecorationImage(ui_type, img.size)
background = Image.alpha_composite(background, decoration_image)
img = Image.alpha_composite(background, img)
return img

View File

@@ -23,12 +23,32 @@ def getDefaultInputDevice():
for host_index in range(0, p.get_host_api_count()):
host = p.get_host_api_info_by_index(host_index)
for device_index in range(0, p. get_host_api_info_by_index(host_index)['deviceCount']):
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": {"name": "NoHost"}, "device": {"name": "NoDevice"}}
def getOutputDevices():
devices = []
with PyAudio() as p:
wasapi_info = p.get_host_api_info_by_type(paWASAPI)
for host_index in range(0, p.get_host_api_count()):
host = p.get_host_api_info_by_index(host_index)
if host["name"] == wasapi_info["name"]:
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 not device["isLoopbackDevice"]:
for loopback in p.get_loopback_device_info_generator():
if device["name"] in loopback["name"]:
devices.append(loopback)
if len(devices) == 0:
devices = [{"name": "NoDevice"}]
else:
devices = [dict(t) for t in {tuple(d.items()) for d in devices}]
return devices
def getDefaultOutputDevice():
with PyAudio() as p:
wasapi_info = p.get_host_api_info_by_type(paWASAPI)
@@ -42,6 +62,9 @@ def getDefaultOutputDevice():
if not default_speakers["isLoopbackDevice"]:
for loopback in p.get_loopback_device_info_generator():
if default_speakers["name"] in loopback["name"]:
default_device = loopback
return default_device
return {"name":"NoDevice"}
return {"device": loopback}
return {"device": {"name": "NoDevice"}}
if __name__ == "__main__":
print("getOutputDevices()", getOutputDevices())
print("getDefaultOutputDevice()", getDefaultOutputDevice())

View File

@@ -10,8 +10,9 @@ CTkToolTip == 0.8
pyinstaller==6.2.0
transformers[torch]==4.37.2
sentencepiece==0.1.99
ctranslate2==3.24.0
faster-whisper==0.10.0
ctranslate2==4.1.0
faster-whisper==1.0.1
openvr==1.26.701
translators @ git+https://github.com/misyaguziya/translators@5.8.9
SpeechRecognition @ git+https://github.com/misyaguziya/custom_speech_recognition@3.10.2
tinyoscquery @ git+https://github.com/cyberkitsune/tinyoscquery@0.1.2

View File

@@ -1,3 +1,4 @@
import random
from typing import Union
from os import path as os_path, rename as os_rename
from PIL.Image import open as Image_open
@@ -33,6 +34,9 @@ def generatePercentageStringsList(start:int, end:int, step:int):
def intToPctStr(value:int):
return f"{value}%"
def floatToPctStr(value:float):
return f"{int(value*100)}%"
def strPctToInt(value:str):
return int(value.replace("%", ""))
@@ -55,4 +59,14 @@ def isUniqueStrings(unique_strings:Union[str, list], input_string:str, require=F
def renameWeightFolder(path):
weight_path = os_path.join(path, "weight")
if os_path.exists(weight_path):
os_rename(weight_path, os_path.join(path, "weights"))
os_rename(weight_path, os_path.join(path, "weights"))
def splitList(lst:list, split_count:int, to_shuffle:bool=False):
if to_shuffle is True:
random.shuffle(lst)
split_lists = []
for i in range(0, len(lst), split_count):
sub_list = lst[i:i+split_count]
split_lists.append(sub_list)
return split_lists

291
view.py
View File

@@ -5,10 +5,10 @@ from tkinter import font as tk_font
import webbrowser
import i18n
from customtkinter import StringVar, IntVar, BooleanVar, get_appearance_mode
from vrct_gui.ui_managers import ColorThemeManager, UiScalingManager
from customtkinter import StringVar, IntVar, DoubleVar, BooleanVar, get_appearance_mode
from vrct_gui.ui_managers import ColorThemeManager, UiScalingManager, AboutVrctManager
from vrct_gui import vrct_gui
from utils import callFunctionIfCallable, intToPctStr
from utils import callFunctionIfCallable, intToPctStr, floatToPctStr
from config import config
@@ -62,9 +62,11 @@ class View():
**common_args
)
about_vrct = AboutVrctManager(config.UI_SCALING, config.UI_LANGUAGE, all_ctm.config_window)
self.settings.config_window = SimpleNamespace(
ctm=all_ctm.config_window,
uism=all_uism.config_window,
about_vrct=about_vrct,
**common_args
)
@@ -99,6 +101,8 @@ class View():
self.view_variable = SimpleNamespace(
# Common
# CALLBACK_ENABLE_EASTER_EGG=None,
CALLBACK_RESTART_SOFTWARE=None,
CALLBACK_UPDATE_SOFTWARE=None,
CALLBACK_OPEN_FILEPATH_LOGS=None,
@@ -134,6 +138,67 @@ class View():
# Overlay Settings
# VAR_OVERLAY_SETTINGS=StringVar(value="Overlay Settings"),
# CALLBACK_SET_OPEN_OVERLAY_SETTINGS_WINDOW=self._openVrSettingsWindow,
# VAR_TO_DEFAULT_OVERLAY_SETTINGS=StringVar(value=i18n.t("overlay_settings.restore_default_settings")),
# CALLBACK_SET_TO_DEFAULT_OVERLAY_SETTINGS=self._toDefaultOverlaySettings,
# VAR_LABEL_OVERLAY_OPACITY=StringVar(value=i18n.t("overlay_settings.opacity")),
# SLIDER_RANGE_OVERLAY_OPACITY=(0.1, 1.0),
# NUMBER_OF_STEPS_OVERLAY_OPACITY=18,
# VAR_OVERLAY_OPACITY=DoubleVar(value=config.OVERLAY_SETTINGS["opacity"]),
# VAR_CURRENT_VALUE_OVERLAY_OPACITY=StringVar(value=floatToPctStr(config.OVERLAY_SETTINGS["opacity"])),
# VAR_LABEL_OVERLAY_UI_SCALING=StringVar(value=i18n.t("overlay_settings.ui_scaling")),
# SLIDER_RANGE_OVERLAY_UI_SCALING=(0.4, 2.0),
# NUMBER_OF_STEPS_OVERLAY_UI_SCALING=16,
# VAR_OVERLAY_UI_SCALING=DoubleVar(value=config.OVERLAY_SETTINGS["ui_scaling"]),
# VAR_CURRENT_VALUE_OVERLAY_UI_SCALING=StringVar(value=floatToPctStr(config.OVERLAY_SETTINGS["ui_scaling"])),
# # CALLBACK_SET_OVERLAY_SMALL_LOG_SETTINGS=None,
# VAR_LABEL_OVERLAY_SMALL_LOG_X_POS=StringVar(value=i18n.t("overlay_settings.x_position")),
# SLIDER_RANGE_OVERLAY_SMALL_LOG_X_POS=(-0.5, 0.5),
# NUMBER_OF_STEPS_OVERLAY_SMALL_LOG_X_POS=100,
# VAR_OVERLAY_SMALL_LOG_X_POS=DoubleVar(value=config.OVERLAY_SMALL_LOG_SETTINGS["x_pos"]),
# VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_X_POS=StringVar(value=config.OVERLAY_SMALL_LOG_SETTINGS["x_pos"]),
# VAR_LABEL_OVERLAY_SMALL_LOG_Y_POS=StringVar(value=i18n.t("overlay_settings.y_position")),
# SLIDER_RANGE_OVERLAY_SMALL_LOG_Y_POS=(-0.8, 0.8),
# NUMBER_OF_STEPS_OVERLAY_SMALL_LOG_Y_POS=160,
# VAR_OVERLAY_SMALL_LOG_Y_POS=DoubleVar(value=config.OVERLAY_SMALL_LOG_SETTINGS["y_pos"]),
# VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_Y_POS=StringVar(value=config.OVERLAY_SMALL_LOG_SETTINGS["y_pos"]),
# VAR_LABEL_OVERLAY_SMALL_LOG_DEPTH=StringVar(value=i18n.t("overlay_settings.depth")),
# SLIDER_RANGE_OVERLAY_SMALL_LOG_DEPTH=(0.5, 1.5),
# NUMBER_OF_STEPS_OVERLAY_SMALL_LOG_DEPTH=100,
# VAR_OVERLAY_SMALL_LOG_DEPTH=DoubleVar(value=config.OVERLAY_SMALL_LOG_SETTINGS["depth"]),
# VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_DEPTH=StringVar(value=config.OVERLAY_SMALL_LOG_SETTINGS["depth"]),
# VAR_LABEL_OVERLAY_SMALL_LOG_DISPLAY_DURATION=StringVar(value=i18n.t("overlay_settings.display_duration")),
# SLIDER_RANGE_OVERLAY_SMALL_LOG_DISPLAY_DURATION=(1, 60),
# NUMBER_OF_STEPS_OVERLAY_SMALL_LOG_DISPLAY_DURATION=59,
# VAR_OVERLAY_SMALL_LOG_DISPLAY_DURATION=IntVar(value=config.OVERLAY_SMALL_LOG_SETTINGS["display_duration"]),
# VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_DISPLAY_DURATION=StringVar(value=f"{config.OVERLAY_SMALL_LOG_SETTINGS['display_duration']} second(s)"),
# VAR_LABEL_OVERLAY_SMALL_LOG_FADEOUT_DURATION=StringVar(value=i18n.t("overlay_settings.fadeout_duration")),
# SLIDER_RANGE_OVERLAY_SMALL_LOG_FADEOUT_DURATION=(0, 5),
# NUMBER_OF_STEPS_OVERLAY_SMALL_LOG_FADEOUT_DURATION=5,
# VAR_OVERLAY_SMALL_LOG_FADEOUT_DURATION=IntVar(value=config.OVERLAY_SMALL_LOG_SETTINGS["fadeout_duration"]),
# VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_FADEOUT_DURATION=StringVar(value=f"{config.OVERLAY_SMALL_LOG_SETTINGS['fadeout_duration']} second(s)"),
# Main Window
# Sidebar
# Sidebar Compact Mode
@@ -216,9 +281,13 @@ class View():
VAR_SECOND_TITLE_TRANSCRIPTION_MIC=StringVar(value=i18n.t("config_window.side_menu_labels.transcription_mic")),
VAR_SECOND_TITLE_TRANSCRIPTION_SPEAKER=StringVar(value=i18n.t("config_window.side_menu_labels.transcription_speaker")),
VAR_SECOND_TITLE_TRANSCRIPTION_INTERNAL_MODEL=StringVar(value=i18n.t("config_window.side_menu_labels.transcription_internal_model")),
VAR_SIDE_MENU_LABEL_VR=StringVar(value=i18n.t("config_window.side_menu_labels.vr")),
VAR_SIDE_MENU_LABEL_OTHERS=StringVar(value=i18n.t("config_window.side_menu_labels.others")),
VAR_SIDE_MENU_LABEL_ADVANCED_SETTINGS=StringVar(value=i18n.t("config_window.side_menu_labels.advanced_settings")),
VAR_SIDE_MENU_LABEL_ABOUT_VRCT=StringVar(value="About VRCT"),
# VAR_SIDE_MENU_LABEL_ABOUT_VRCT=StringVar(value=i18n.t("config_window.side_menu_labels.advanced_settings")),
VAR_CURRENT_ACTIVE_CONFIG_TITLE=StringVar(value=""),
# Appearance Tab
@@ -358,6 +427,13 @@ class View():
# Transcription Tab (Speaker)
VAR_LABEL_SPEAKER_DEVICE=StringVar(value=i18n.t("config_window.speaker_device.label")),
VAR_DESC_SPEAKER_DEVICE=None,
LIST_SPEAKER_DEVICE=[],
CALLBACK_SET_SPEAKER_DEVICE=None,
VAR_SPEAKER_DEVICE=StringVar(value=config.CHOICE_SPEAKER_DEVICE),
VAR_LABEL_SPEAKER_DYNAMIC_ENERGY_THRESHOLD=StringVar(value=""),
VAR_DESC_SPEAKER_DYNAMIC_ENERGY_THRESHOLD=StringVar(value=""),
CALLBACK_SET_SPEAKER_DYNAMIC_ENERGY_THRESHOLD=None,
@@ -402,6 +478,15 @@ class View():
VAR_WHISPER_WEIGHT_TYPE=StringVar(value=self.getSelectableWhisperWeightTypeDict()[config.WHISPER_WEIGHT_TYPE]),
# # VR Tab
# VAR_LABEL_ENABLE_OVERLAY_SMALL_LOG=StringVar(value=i18n.t("config_window.enable_overlay_small_log.label")),
# VAR_DESC_ENABLE_OVERLAY_SMALL_LOG=None,
# # VAR_DESC_ENABLE_OVERLAY_SMALL_LOG=StringVar(value=i18n.t("config_window.enable_overlay_small_log.desc")),
# CALLBACK_SET_ENABLE_OVERLAY_SMALL_LOG=None,
# VAR_ENABLE_OVERLAY_SMALL_LOG=BooleanVar(value=config.ENABLE_OVERLAY_SMALL_LOG),
# VAR_OPEN_OVERLAY_SETTINGS_BUTTON=StringVar(value=i18n.t("config_window.enable_overlay_small_log.open_overlay_settings")),
# Others Tab
VAR_LABEL_ENABLE_AUTO_CLEAR_MESSAGE_BOX=StringVar(value=i18n.t("config_window.auto_clear_the_message_box.label")),
VAR_DESC_ENABLE_AUTO_CLEAR_MESSAGE_BOX=None,
@@ -521,6 +606,17 @@ class View():
VAR_LABEL_OPEN_CONFIG_FILEPATH=StringVar(value=i18n.t("config_window.open_config_filepath.label")),
VAR_DESC_OPEN_CONFIG_FILEPATH=None,
# About VRCT Tab
CALLBACK_OPEN_WEBPAGE_ABOUT_VRCT=self.openWebPage_AboutVrct,
CALLBACK_ABOUT_VRCT_POSTER_NEXT_BUTTON=None,
CALLBACK_ABOUT_VRCT_POSTER_PREV_BUTTON=None,
CALLBACK_ABOUT_VRCT_POSTER_IMAGE_CURRENT_PAGE_NUM=0,
CALLBACK_ABOUT_VRCT_CHANGE_POSTER_SHOWCASE_WORLD_LIST=None,
CALLBACK_ABOUT_VRCT_POSTER_SHOWCASE_CURRENT_PAGE_NUM=0,
)
@@ -535,6 +631,8 @@ class View():
if common_registers is not None:
# self.view_variable.CALLBACK_ENABLE_EASTER_EGG=common_registers.get("callback_enable_easter_egg", None)
self.view_variable.CALLBACK_UPDATE_SOFTWARE=common_registers.get("callback_update_software", None)
self.view_variable.CALLBACK_RESTART_SOFTWARE=common_registers.get("callback_restart_software", None)
self.view_variable.CALLBACK_OPEN_FILEPATH_LOGS=common_registers.get("callback_filepath_logs", None)
@@ -642,6 +740,9 @@ class View():
self.view_variable.CALLBACK_DELETE_MIC_WORD_FILTER=config_window_registers.get("callback_delete_mic_word_filter", None)
# Transcription Tab (Speaker)
self.view_variable.CALLBACK_SET_SPEAKER_DEVICE = config_window_registers.get("callback_set_speaker_device", None)
config_window_registers.get("list_speaker_device", None) and self.updateList_SpeakerDevice(config_window_registers["list_speaker_device"])
self.view_variable.CALLBACK_SET_SPEAKER_ENERGY_THRESHOLD=config_window_registers.get("callback_set_speaker_energy_threshold", None)
self.view_variable.CALLBACK_SET_SPEAKER_DYNAMIC_ENERGY_THRESHOLD=config_window_registers.get("callback_set_speaker_dynamic_energy_threshold", None)
self.view_variable.CALLBACK_CHECK_SPEAKER_THRESHOLD=config_window_registers.get("callback_check_speaker_threshold", None)
@@ -653,6 +754,14 @@ class View():
self.view_variable.CALLBACK_SET_USE_WHISPER_FEATURE=config_window_registers.get("callback_set_use_whisper_feature", None)
self.view_variable.CALLBACK_SET_WHISPER_WEIGHT_TYPE=config_window_registers.get("callback_set_whisper_weight_type", None)
# VR Tab
# VR Tab (Quick Settings)
# self.view_variable.CALLBACK_SET_OVERLAY_SETTINGS=config_window_registers.get("callback_set_overlay_settings", None)
# self.view_variable.CALLBACK_SET_ENABLE_OVERLAY_SMALL_LOG=config_window_registers.get("callback_set_enable_overlay_small_log", None)
# # VR Tab (Quick Settings)
# self.view_variable.CALLBACK_SET_OVERLAY_SMALL_LOG_SETTINGS=config_window_registers.get("callback_set_overlay_small_log_settings", None)
# Others Tab
self.view_variable.CALLBACK_SET_ENABLE_AUTO_CLEAR_MESSAGE_BOX=config_window_registers.get("callback_set_enable_auto_clear_chatbox", None)
@@ -708,6 +817,16 @@ class View():
)
self.replaceMicThresholdCheckButton_Disabled()
if config.CHOICE_SPEAKER_DEVICE == "NoDevice":
self.view_variable.VAR_SPEAKER_DEVICE.set("No Speaker Device Detected")
vrct_gui._changeConfigWindowWidgetsStatus(
status="disabled",
target_names=[
"sb__optionmenu_speaker_device",
]
)
self.replaceSpeakerThresholdCheckButton_Disabled()
if config.USE_WHISPER_FEATURE is True:
self.openWhisperWeightTypeWidget()
else:
@@ -737,9 +856,30 @@ class View():
self.setReceivedMessageFormat_EntryWidgets(config.RECEIVED_MESSAGE_FORMAT)
self.setReceivedMessageFormatWithT_EntryWidgets(config.RECEIVED_MESSAGE_FORMAT_WITH_T)
# Set Easter Egg
# self.count = 0
# def clickedCounter(_e):
# if self.count < 2:
# self.count+=1
# print("Easter egg count:", self.count)
# else:
# print("Easter egg count:", self.count, "Easter egg has enabled.")
# callFunctionIfCallable(self.view_variable.CALLBACK_ENABLE_EASTER_EGG)
# print(config.OVERLAY_UI_TYPE)
# vrct_gui.sidebar_logo.bind(
# "<ButtonRelease>",
# clickedCounter,
# "+"
# )
# Insert sample conversation for testing.
# self._insertSampleConversationToTextbox()
# vrct_gui.updating_window.showUpdatingWindow()
# Send Message Format
def setSendMessageFormat_EntryWidgets(self, message_format:str):
result = self.extractMessageFormat(message_format)
@@ -973,6 +1113,34 @@ class View():
DICT_DATA["large-v3"]: callI18n("large-v3", "2.87GB"),
}
# def _toDefaultOverlaySettings(self):
# INIT_OVERLAY_SETTINGS = {
# "opacity": 1.0,
# "ui_scaling": 1.0,
# }
# INIT_OVERLAY_SMALL_LOG_SETTINGS = {
# "x_pos": 0.0,
# "y_pos": -0.41,
# "depth": 1.0,
# "display_duration": 5,
# "fadeout_duration": 2,
# }
# for key in INIT_OVERLAY_SETTINGS.keys():
# callFunctionIfCallable(self.view_variable.CALLBACK_SET_OVERLAY_SETTINGS, INIT_OVERLAY_SETTINGS[key], key)
# for key in INIT_OVERLAY_SMALL_LOG_SETTINGS.keys():
# callFunctionIfCallable(self.view_variable.CALLBACK_SET_OVERLAY_SMALL_LOG_SETTINGS, INIT_OVERLAY_SMALL_LOG_SETTINGS[key], key)
# self.setLatestConfigVariable("OverlayOpacity")
# self.setLatestConfigVariable("OverlayUiScaling")
# self.setLatestConfigVariable("OverlaySmallLogXPos")
# self.setLatestConfigVariable("OverlaySmallLogYPos")
# self.setLatestConfigVariable("OverlaySmallLogDepth")
# self.setLatestConfigVariable("OverlaySmallLogDisplayDuration")
# self.setLatestConfigVariable("OverlaySmallLogFadeoutDuration")
# Open Webpage Functions
def openWebPage_Booth(self):
self.openWebPage(config.BOOTH_URL)
@@ -985,6 +1153,65 @@ class View():
def openWebPage_DeepL_Auth_Key(self):
self.openWebPage(config.DEEPL_AUTH_KEY_PAGE_URL)
def openWebPage_AboutVrct(self, target_type:str, arg=None):
url = ""
match (target_type):
case ("X_MISYA"):
url = "https://twitter.com/misya_ai"
case ("GITHUB_MISYA"):
url = "https://github.com/misyaguziya"
case ("X_SHIINA"):
url = "https://twitter.com/Shiina_12siy"
case ("X_DONE_SAN"):
url = "https://twitter.com/done_vrc"
case ("X_IYA"):
url = "https://twitter.com/IYAA_HHHH"
case ("X_RERA"):
url = "https://twitter.com/rerassi"
case ("GITHUB_RERA"):
url = "https://github.com/soumt-r"
case ("X_POPOSUKE"):
url = "https://twitter.com/sig_popo"
case ("X_KUMAGUMA"):
url = "https://twitter.com/K_kumaguma_A"
case ("BOOTH"):
url = "https://misyaguziya.booth.pm/items/5155325"
case ("VRCT_DOCUMENTS"):
url = config.DOCUMENTS_URL
case ("VRCT_GITHUB"):
url = "https://github.com/misyaguziya/VRCT"
case ("CONTACT_US"):
url = "https://docs.google.com/forms/d/e/1FAIpQLSei-xoydOY60ivXqhOjaTzNN8PiBQIDcNhzfy6cw2sjYkcg_g/viewform"
case ("SUPPORTER_REGISTRATION"):
url = "https://docs.google.com/forms/d/e/1FAIpQLSepLzdEOTJQFVHdOOxAA0dix3zCmnNBlmH4XWon5FldXkIiqw/viewform"
case ("POSTER_CONTACT_US"):
url = "https://docs.google.com/forms/d/e/1FAIpQLScwt19eX4Lkj_4w9J5H_3a-bkzXs6rkOc0B-0ZTVVfHKyiU7g/viewform"
case ("X_SHIINA_POSTER_SHOWCASE_POST"):
if arg is None:
print("arg that received is None. it mus be something number")
return
url = "https://twitter.com/Shiina_12siy/status/" + arg
case "TEMP":
print("here is still under construction.")
return
case (_):
raise ValueError(f"No matching case for target_type: {target_type}")
self.openWebPage(url)
# Widget Control
# Common
@@ -1210,7 +1437,10 @@ class View():
def initSpeakerThresholdCheckButton(self):
self.replaceSpeakerThresholdCheckButton_Passive()
if config.CHOICE_SPEAKER_DEVICE == "NoDevice":
self.replaceSpeakerThresholdCheckButton_Disabled()
else:
self.replaceSpeakerThresholdCheckButton_Passive()
@staticmethod
def replaceSpeakerThresholdCheckButton_Active():
@@ -1268,6 +1498,13 @@ class View():
vrct_gui.config_window.sb__progressbar_x_slider__progressbar_mic_energy_threshold.set(0)
def updateList_SpeakerDevice(self, new_speaker_device_list:list):
self.view_variable.LIST_SPEAKER_DEVICE = new_speaker_device_list
vrct_gui.dropdown_menu_window.updateDropdownMenuValues(
dropdown_menu_widget_id="sb__optionmenu_speaker_device",
dropdown_menu_values=new_speaker_device_list,
)
def updateSetProgressBar_SpeakerEnergy(self, new_speaker_energy):
self.updateProgressBar(
target_progressbar_widget=vrct_gui.config_window.sb__progressbar_x_slider__progressbar_speaker_energy_threshold,
@@ -1474,7 +1711,14 @@ class View():
vrct_gui.confirmation_modal.hide_buttons()
vrct_gui.update()
vrct_gui.confirmation_modal.update()
callFunctionIfCallable(self.view_variable.CALLBACK_UPDATE_SOFTWARE)
self._hideConfirmationModal()
vrct_gui.withdraw()
vrct_gui.updating_window.showUpdatingWindow()
def func(**kwargs):
vrct_gui.updating_window.updateDownloadProgress(**kwargs)
callFunctionIfCallable(self.view_variable.CALLBACK_UPDATE_SOFTWARE, func)
@@ -1489,6 +1733,9 @@ class View():
self._closeMicWordFilterList()
vrct_gui._closeConfigWindow()
def _openVrSettingsWindow(self):
vrct_gui.quick_settings_window.show()
# Window Control (Main Window Cover)
def _openTheCoverOfMainWindow(self):
vrct_gui.main_window_cover.show()
@@ -1611,11 +1858,45 @@ class View():
case "ReceivedMessageFormatWithT":
self.setReceivedMessageFormatWithT_EntryWidgets(config.RECEIVED_MESSAGE_FORMAT_WITH_T)
case "OverlayOpacity":
self.view_variable.VAR_OVERLAY_OPACITY.set(config.OVERLAY_SETTINGS["opacity"])
self.view_variable.VAR_CURRENT_VALUE_OVERLAY_OPACITY.set(floatToPctStr(config.OVERLAY_SETTINGS["opacity"]))
case "OverlayUiScaling":
self.view_variable.VAR_OVERLAY_UI_SCALING.set(config.OVERLAY_SETTINGS["ui_scaling"])
self.view_variable.VAR_CURRENT_VALUE_OVERLAY_UI_SCALING.set(floatToPctStr(config.OVERLAY_SETTINGS["ui_scaling"]))
case "OverlaySmallLogXPos":
self.view_variable.VAR_OVERLAY_SMALL_LOG_X_POS.set(config.OVERLAY_SMALL_LOG_SETTINGS["x_pos"])
self.view_variable.VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_X_POS.set(config.OVERLAY_SMALL_LOG_SETTINGS["x_pos"])
case "OverlaySmallLogYPos":
self.view_variable.VAR_OVERLAY_SMALL_LOG_Y_POS.set(config.OVERLAY_SMALL_LOG_SETTINGS["y_pos"])
self.view_variable.VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_Y_POS.set(config.OVERLAY_SMALL_LOG_SETTINGS["y_pos"])
case "OverlaySmallLogDepth":
self.view_variable.VAR_OVERLAY_SMALL_LOG_DEPTH.set(config.OVERLAY_SMALL_LOG_SETTINGS["depth"])
self.view_variable.VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_DEPTH.set(config.OVERLAY_SMALL_LOG_SETTINGS["depth"])
case "OverlaySmallLogDisplayDuration":
self.view_variable.VAR_OVERLAY_SMALL_LOG_DISPLAY_DURATION.set(config.OVERLAY_SMALL_LOG_SETTINGS["display_duration"])
self.view_variable.VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_DISPLAY_DURATION.set(f"{config.OVERLAY_SMALL_LOG_SETTINGS['display_duration']} second(s)")
case "OverlaySmallLogFadeoutDuration":
self.view_variable.VAR_OVERLAY_SMALL_LOG_FADEOUT_DURATION.set(config.OVERLAY_SMALL_LOG_SETTINGS["fadeout_duration"])
self.view_variable.VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_FADEOUT_DURATION.set(f"{config.OVERLAY_SMALL_LOG_SETTINGS['fadeout_duration']} second(s)")
case _:
raise ValueError(f"No matching case for target_name: {target_name}")
# Print To Textbox.
# def printToTextbox_enableEasterEgg(self):
# self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.enabled_easter_egg"))
def printToTextbox_enableTranslation(self):
self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.enabled_translation"))
def printToTextbox_disableTranslation(self):

View File

@@ -1,6 +1,6 @@
from customtkinter import CTkToplevel, CTkFrame, CTkLabel, CTkFont
from customtkinter import CTkToplevel, CTkFrame, CTkLabel, CTkFont, CTkProgressBar
from .ui_utils import fadeInAnimation, setGeometryToCenterOfTheWidget, bindButtonFunctionAndColor
from .ui_utils import fadeInAnimation, setGeometryToCenterOfTheWidget, bindButtonFunctionAndColor, generateGradientColor
from utils import callFunctionIfCallable
@@ -13,6 +13,7 @@ class _CreateConfirmationModal(CTkToplevel):
self.settings = settings
self._view_variable = view_variable
self.is_showed_progressbar = False
self.title("")
self.overrideredirect(True)
@@ -69,6 +70,18 @@ class _CreateConfirmationModal(CTkToplevel):
self.modal_buttons_wrapper.grid(row=1, column=0, sticky="ew")
# Progress bar
self.progressbar_widget = CTkProgressBar(
self.modal_contents_wrapper,
height=8,
corner_radius=0,
fg_color="black",
# fg_color="#4b4c4f",
progress_color="gray",
)
self.progressbar_widget.set(0)
if modal_type == "information":
self.modal_buttons_wrapper.grid_columnconfigure((0,2), weight=1)
@@ -237,5 +250,21 @@ class _CreateConfirmationModal(CTkToplevel):
return
callFunctionIfCallable(self._view_variable.CALLBACK_HIDE_CONFIRMATION_MODAL)
def updateDownloadProgress(self, progress:float):
if self.is_showed_progressbar is False:
self.progressbar_widget.place(relwidth=0.9, relx=0.5, rely=0.84, anchor="s")
self.is_showed_progressbar = True
self.update()
progress_color = generateGradientColor(
value=progress,
color_start=[242, 242, 242], # RGB values for #f2f2f2
color_end=[72, 164, 149], # RGB values for #48a495
)
self.progressbar_widget.configure(progress_color=progress_color)
self.progressbar_widget.set(progress)
self.update_idletasks()
def _grab_set(self):
self.grab_set()

View File

@@ -163,6 +163,6 @@ class _PrintToTextbox():
case "RECEIVED":
target_textbox = self.vrct_gui.textbox_received
case (_):
raise ValueError(f"No matching case for target_type: {target_type}")
raise ValueError(f"No matching case for target_type: {target_type}")
return target_textbox

View File

@@ -43,6 +43,12 @@ def _changeConfigWindowWidgetsStatus(config_window, settings, view_variable, sta
disableLabelsWidgets(target_widget)
disableOptionmenuWidget(target_widget)
case "sb__optionmenu_speaker_device":
if status == "disabled":
target_widget = config_window.sb__widgets["sb__optionmenu_speaker_device"]
disableLabelsWidgets(target_widget)
disableOptionmenuWidget(target_widget)
case "sb__optionmenu_appearance_theme":
if status == "disabled":
target_widget = config_window.sb__widgets["sb__optionmenu_appearance_theme"]

View File

@@ -41,9 +41,9 @@ class ConfigWindow(CTkToplevel):
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")
# 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)

View File

@@ -30,7 +30,13 @@ def _createSettingBoxContainer(config_window, settings, view_variable, setting_b
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)
if setting_box_setting.get("about_vrct", False) is True:
setting_box_top_padding = CTkFrame(setting_box_container_widget, corner_radius=0, fg_color=settings.ctm.ABOUT_VRCT_BG, width=0, height=settings.uism.ABOUT_VRCT_SB__TOP_PADY)
setting_box_wrapper = CTkFrame(setting_box_container_widget, fg_color=settings.ctm.MAIN_BG_COLOR, corner_radius=0, width=0, height=0)
else:
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_wrapper = CTkFrame(setting_box_container_widget, fg_color=settings.ctm.SB__WRAPPER_BG_COLOR, corner_radius=0, width=0, height=0)
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
@@ -47,7 +53,6 @@ def _createSettingBoxContainer(config_window, settings, view_variable, setting_b
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

View File

@@ -11,6 +11,9 @@ from .setting_box_containers.setting_box_transcription import createSettingBox_M
from .setting_box_containers.setting_box_others import createSettingBox_Others, createSettingBox_Others_SendMessageFormats, createSettingBox_Others_ReceivedMessageFormats, createSettingBox_Others_Additional
from .setting_box_containers.setting_box_advanced_settings import createSettingBox_AdvancedSettings
from .setting_box_containers.setting_box_translation import createSettingBox_Translation
from .setting_box_containers.setting_box_vr import createSettingBox_Vr
from .setting_box_containers.setting_box_about_vrct import createSettingBox_AboutVrct
def createSideMenuAndSettingsBoxContainers(config_window, settings, view_variable):
@@ -30,9 +33,9 @@ def createSideMenuAndSettingsBoxContainers(config_window, settings, view_variabl
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_bg_container.grid_rowconfigure(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")
config_window.side_menu_container.grid(row=0, column=0, padx=settings.uism.TOP_BAR_SIDE__TITLE_PADX, pady=settings.uism.SIDE_MENU_PADY, sticky="nsew")
@@ -101,6 +104,21 @@ def createSideMenuAndSettingsBoxContainers(config_window, settings, view_variabl
]
},
},
# {
# "side_menu_tab_attr_name": "side_menu_tab_vr",
# "label_attr_name": "label_vr",
# "selected_mark_attr_name": "selected_mark_vr",
# "textvariable": view_variable.VAR_SIDE_MENU_LABEL_VR,
# "setting_box_container_settings": {
# "setting_box_container_attr_name": "setting_box_container_vr",
# "setting_boxes": [
# {
# "var_section_title": None,
# "setting_box": createSettingBox_Vr
# }
# ]
# },
# },
{
"side_menu_tab_attr_name": "side_menu_tab_others",
"label_attr_name": "label_others",
@@ -128,12 +146,36 @@ def createSideMenuAndSettingsBoxContainers(config_window, settings, view_variabl
]
},
},
# About VRCT. It is separated from the others.
{
"side_menu_tab_attr_name": "side_menu_tab_about_vrct",
"label_attr_name": "label_about_vrct",
"selected_mark_attr_name": "selected_mark_about_vrct",
"textvariable": view_variable.VAR_SIDE_MENU_LABEL_ABOUT_VRCT,
"setting_box_container_settings": {
"setting_box_container_attr_name": "setting_box_container_about_vrct",
"setting_boxes": [
{ "var_section_title": None, "setting_box": createSettingBox_AboutVrct, "about_vrct": True },
]
},
},
]
SEPARATE_ROW_COUNT=1 # It's just count the row and separate from below.
SEPARATE_ROW_NUMBER = len(side_menu_and_setting_box_containers_settings) - SEPARATE_ROW_COUNT
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:
if side_menu_row == SEPARATE_ROW_NUMBER:
side_menu_separator = CTkFrame(config_window.side_menu_container, corner_radius=0, fg_color=settings.ctm.SIDE_MENU_LABELS_BG_COLOR, width=0, height=0)
config_window.side_menu_container.grid_rowconfigure(side_menu_row, weight=1, minsize=settings.uism.SIDE_MENU_LABELS_SEPARATE_MIN_HEIGHT)
side_menu_separator.grid(row=side_menu_row, column=0, sticky="nsew")
side_menu_row+=1
_addConfigSideMenuItem(
config_window=config_window,
settings=settings,

View File

@@ -998,6 +998,72 @@ class _SettingBoxGenerator():
def createSettingBox_Overlay(self,
for_var_label_text, for_var_desc_text,
switch_attr_name,
variable,
command,
for_var_button_label,
label_button_clicked_command,
):
(setting_box_frame, setting_box_item_frame) = self._createSettingBoxFrame(switch_attr_name, for_var_label_text, for_var_desc_text)
all_wrapper = CTkFrame(setting_box_item_frame, corner_radius=0, fg_color=self.settings.ctm.SB__BG_COLOR, width=0, height=0)
all_wrapper.grid(row=1, column=0, sticky="ew")
all_wrapper.grid_columnconfigure(1, weight=1)
switch_widget = CTkSwitch(
all_wrapper,
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,
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)
self.config_window.sb__widgets[switch_attr_name].switch_box = switch_widget
switch_widget.grid(row=0, pady=20, column=SETTING_BOX_COLUMN)
(open_page_button, label_button_label_widget) = createLabelButton(
parent_widget=all_wrapper,
label_button_bg_color=self.settings.ctm.SB__BUTTON_COLOR,
label_button_hovered_bg_color=self.settings.ctm.SB__BUTTON_HOVERED_COLOR,
label_button_clicked_bg_color=self.settings.ctm.SB__BUTTON_CLICKED_COLOR,
label_button_ipadx=self.settings.uism.SB__AUTHKEY_WEBPAGE_BUTTON_IPADX,
label_button_ipady=self.settings.uism.SB__AUTHKEY_WEBPAGE_BUTTON_IPADY,
variable=for_var_button_label,
font_family=self.settings.FONT_FAMILY,
font_size=self.settings.uism.SB__AUTHKEY_WEBPAGE_BUTTON_LABEL_FONT_SIZE,
text_color=self.settings.ctm.LABELS_TEXT_COLOR,
label_button_clicked_command=label_button_clicked_command,
label_button_position="center",
)
open_page_button.grid(row=1, column=SETTING_BOX_COLUMN, pady=(self.settings.uism.SB__OPEN_OVERLAY_SETTINGS_WINDOW,0))
return setting_box_frame
def createSettingBoxButtonWithImage(
self,
for_var_label_text, for_var_desc_text,

View File

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

View File

@@ -0,0 +1,122 @@
poster_showcase_worlds_settings = [
# トサカひよ
{
"author_name": "tosaka_hiyo",
"data": [
{ "image_file_name": "kokekkopiyopiyo.png", "x_post_num": "1779076974369276014" },
]
},
# MiuJepang
{
"author_name": "miu_jepang",
"data": [
{ "image_file_name": "ippaidou.png", "x_post_num": None },
{ "image_file_name": "nihongokurabu.png", "x_post_num": "1779004631936614893" },
{ "image_file_name": "language_exchange_tervern.png", "x_post_num": "1779749425923150317" },
{ "image_file_name": "japanese_culture_osenbeito.png", "x_post_num": None },
{ "image_file_name": "silakan_datang_ke_rumahku.png", "x_post_num": None },
{ "image_file_name": "uj_club.png", "x_post_num": "1780791654196388201" },
{ "image_file_name": "sushi_stand_guruguru.png", "x_post_num": None },
]
},
# poposuke_sig
{
"author_name": "poposuke_sig",
"data": [
{ "image_file_name": "usanezumi_shrine2.png", "x_post_num": "1781224020383506649" },
]
},
# KUROINU_YOUHEI
{
"author_name": "kuroinu_youhei",
"data": [
{ "image_file_name": "kuroinu_work_room.png", "x_post_num": "1779750007564112146" },
]
},
# いちや_ICHIYA
{
"author_name": "ichiya",
"data": [
{ "image_file_name": "ehon_no_heikousekai_jimusho.png", "x_post_num": "1780792306976850285" },
{ "image_file_name": "ikoiba.png", "x_post_num": "1782723006923780580" },
{ "image_file_name": "kimodameshi.png", "x_post_num": "1781224391692714133" },
{ "image_file_name": "parallel_collar.png", "x_post_num": None },
]
},
# HayaTikaze
{
"author_name": "haya_tikaze",
"data": [
{ "image_file_name": "study_japanese_world_japanichijou.png", "x_post_num": "1781539871829766550" },
]
},
# aji_3
{
"author_name": "aji_3",
"data": [
{ "image_file_name": "yuttari_eikaiwa.png", "x_post_num": "1779002892999078046" },
]
},
# 八葉そるち
{
"author_name": "yatuha_soruti",
"data": [
{ "image_file_name": "re_yatuha_room.png", "x_post_num": "1779830390435590196" },
]
},
# chakamoto
{
"author_name": "chakamoto",
"data": [
{ "image_file_name": "coffee_keisyoku_chakachaka.png", "x_post_num": None },
]
},
# MloYolM (よるむ)
{
"author_name": "yolm",
"data": [
{ "image_file_name": "cafe_cian.png", "x_post_num": None },
]
},
# ミラクル・オルカ
{
"author_name": "miracle_orca",
"data": [
{ "image_file_name": "mamehinata_dogrun.png", "x_post_num": "1782723423179100471" },
]
},
# いんくEenkoo
{
"author_name": "eenkoo",
"data": [
{ "image_file_name": "tyuuniti_kouryuukai.png", "x_post_num": None },
]
},
# 1ban_meno
{
"author_name": "1ban_meno",
"data": [
{ "image_file_name": "bar_asagao.png", "x_post_num": None },
]
},
# 沈黙静寂
{
"author_name": "sizudama_sizusabi",
"data": [
{ "image_file_name": "monogatari_meetup.png", "x_post_num": "1781538415789674976" },
]
},
]

View File

@@ -0,0 +1,614 @@
from random import randint
from types import SimpleNamespace
from customtkinter import CTkFrame, CTkLabel, CTkImage, CTkFont
from utils import callFunctionIfCallable, splitList
from ......ui_utils import bindButtonFunctionAndColor, animateRotation, bindEnterAndLeaveFunction
from .about_vrct_store import poster_showcase_worlds_settings
def createSettingBox_AboutVrct(setting_box_wrapper, config_window, settings, view_variable):
setting_box_wrapper.grid_columnconfigure(0, weight=1, minsize=settings.uism.MAIN_AREA_MIN_WIDTH)
about_vrct_uism = settings.about_vrct.uism
ABOUT_VRCT_BG = settings.ctm.ABOUT_VRCT_BG
# For padding left. without this, setting_box_wrapper's bg shows...
about_vrct_container_wrapper = CTkFrame(setting_box_wrapper, fg_color=ABOUT_VRCT_BG, corner_radius=0, width=0, height=0)
about_vrct_container_wrapper.grid(column=0, row=0, padx=0, pady=0, sticky="nsew")
about_vrct_container_wrapper.grid_columnconfigure(0, weight=1)
about_vrct_container = CTkFrame(about_vrct_container_wrapper, fg_color=ABOUT_VRCT_BG, corner_radius=0, width=0, height=0)
about_vrct_container.grid(column=0, row=0, padx=about_vrct_uism.ABOUT_VRCT_CONTAINER_LEFT_PADX, pady=0, sticky="nsew")
about_vrct_container.grid_columnconfigure(0, weight=1)
def createSectionContainer(section_row, section_title_image_file_name:str=None, section_bottom_padding:int=0, section_title_bottom_padding:int=0):
section_container = CTkFrame(about_vrct_container, fg_color=ABOUT_VRCT_BG, corner_radius=0, width=0, height=0)
section_container.grid(column=0, row=section_row, padx=0, pady=(0, section_bottom_padding), sticky="nsew")
section_container.grid_columnconfigure(0, weight=1)
contents_row=0
if section_title_image_file_name is not None:
section_title_frame = settings.about_vrct.embedImageCTkLabel(section_container, section_title_image_file_name)
section_title_frame.grid(column=0, row=contents_row, padx=0, pady=(0,section_title_bottom_padding), sticky="nw")
contents_row = 1
section_contents_wrapper = CTkFrame(section_container, fg_color=ABOUT_VRCT_BG, corner_radius=0, width=0, height=0)
section_contents_wrapper.grid(column=0, row=contents_row, padx=0, pady=0, sticky="nsew")
section_contents_wrapper.grid_columnconfigure(0, weight=1)
return (section_container, section_contents_wrapper)
def createImageButtonRows(parent_frame, image_buttons_settings:list, bottom_pady, directly_type:str=None, corner_radius:int=0, ipadx:int=0, ipady:int=0):
button_row=0
setting_length = len(image_buttons_settings)
for index, each_setting in enumerate(image_buttons_settings):
each_button = settings.about_vrct.embedImageButtonCTkLabel(
parent_frame=parent_frame,
image_file_name=each_setting["image_file_name"],
callback=each_setting.get("callback", None),
directly_type=directly_type,
corner_radius=corner_radius,
no_bind=each_setting.get("no_bind", False),
)
each_button.grid(column=0, row=button_row, padx=0, pady=(0, bottom_pady), sticky="nsew")
each_button.img_label.grid(padx=ipadx, pady=ipady, sticky="nsew")
if index == setting_length-1:
each_button.grid(pady=0)
button_row+=1
def createContactButton(parent_frame, image_file_name, callback_arg, fg_color=ABOUT_VRCT_BG):
frame = settings.about_vrct.embedImageButtonCTkLabel(
parent_frame=parent_frame,
image_file_name=image_file_name,
callback=lambda _e: callFunctionIfCallable(view_variable.CALLBACK_OPEN_WEBPAGE_ABOUT_VRCT, callback_arg),
fg_color=fg_color,
hovered_color=fg_color,
clicked_color=fg_color,
)
return frame
def createTellUsButton(parent_frame, image_file_name, callback):
tell_us_button_frame = settings.about_vrct.embedImageButtonCTkLabel(
parent_frame=parent_frame,
image_file_name=image_file_name,
callback=callback,
corner_radius=about_vrct_uism.TELL_US_BUTTON_CORNER_RADIUS,
)
tell_us_button_frame.img_label.grid(padx=about_vrct_uism.TELL_US_BUTTON_PADX, pady=about_vrct_uism.TELL_US_BUTTON_PADY, sticky="nsew")
tell_us_button_frame.configure(border_width=about_vrct_uism.TELL_US_BUTTON_BORDER_WIDTH, border_color=settings.ctm.ABOUT_VRCT_TELL_US_BUTTON_BORDER_COLOR)
return tell_us_button_frame
section_row=0
# The Developers ----------------------------------
_the_developers, the_developers_contents_wrapper = createSectionContainer(
section_row=section_row,
section_title_image_file_name="dev_section_title.png",
section_bottom_padding=about_vrct_uism.SECTION_BOTTOM_PADY,
section_title_bottom_padding=about_vrct_uism.THE_DEVELOPERS_SECTION_TITLE_BOTTOM_PADY
)
dev_misya_label = settings.about_vrct.embedImageCTkLabel(the_developers_contents_wrapper, "dev_misya.png")
dev_misya_label.grid(column=0, row=0, padx=0, pady=0, sticky="nsw")
dev_misya_x = createContactButton(
parent_frame=dev_misya_label,
image_file_name="dev_x_icon.png",
callback_arg="X_MISYA",
fg_color=settings.ctm.ABOUT_VRCT_DEV_BG
)
dev_misya_x.place(x=about_vrct_uism.DEVS_MISYA_X_X, y=about_vrct_uism.DEVS_CONTACTS_Y1, anchor="nw")
dev_misya_github = createContactButton(
parent_frame=dev_misya_label,
image_file_name="dev_github_icon.png",
callback_arg="GITHUB_MISYA",
fg_color=settings.ctm.ABOUT_VRCT_DEV_BG
)
dev_misya_github.place(x=about_vrct_uism.DEVS_MISYA_GITHUB_X, y=about_vrct_uism.DEVS_CONTACTS_Y1, anchor="nw")
dev_shiina_label = settings.about_vrct.embedImageCTkLabel(the_developers_contents_wrapper, "dev_shiina.png")
dev_shiina_label.grid(column=1, row=0, padx=0, pady=0, sticky="nse")
dev_shiina_x = createContactButton(
parent_frame=dev_shiina_label,
image_file_name="dev_x_icon.png",
callback_arg="X_SHIINA",
fg_color=settings.ctm.ABOUT_VRCT_DEV_BG
)
dev_shiina_x.place(x=about_vrct_uism.DEVS_SHIINA_X_X, y=about_vrct_uism.DEVS_CONTACTS_Y1, anchor="nw")
section_row+=1
# Project Links And Logo ----------------------------------
_project_links_and_logo, project_links_and_logo_contents_wrapper = createSectionContainer(
section_bottom_padding=about_vrct_uism.PROJECT_LINKS_SECTION_BOTTOM_PADDING,
section_row=section_row,
)
project_links_and_logo_wrapper = CTkFrame(project_links_and_logo_contents_wrapper, fg_color=ABOUT_VRCT_BG, corner_radius=0, width=0, height=0)
project_links_and_logo_wrapper.grid(column=0, row=0, padx=about_vrct_uism.PROJECT_LINK_CONTENTS_PADX, pady=0, sticky="nsew")
project_links_and_logo_wrapper.grid_columnconfigure(1, weight=1)
vrct_logo_label = settings.about_vrct.embedImageCTkLabel(project_links_and_logo_wrapper, "vrct_logo_for_about_vrct.png")
vrct_logo_label.grid(column=0, row=0, padx=0, pady=0, sticky="nsw")
project_links_wrapper = CTkFrame(project_links_and_logo_wrapper, fg_color=ABOUT_VRCT_BG, corner_radius=0, width=0, height=0)
project_links_wrapper.grid(column=2, row=0, padx=0, pady=0, sticky="nse")
project_link_settings = [
{
"image_file_name": "project_link_booth.png",
"callback": lambda _e: callFunctionIfCallable(view_variable.CALLBACK_OPEN_WEBPAGE_ABOUT_VRCT, "BOOTH")
},
{
"image_file_name": "project_link_documents.png",
"callback": lambda _e: callFunctionIfCallable(view_variable.CALLBACK_OPEN_WEBPAGE_ABOUT_VRCT, "VRCT_DOCUMENTS")
},
{
"image_file_name": "project_link_vrct_github.png",
"callback": lambda _e: callFunctionIfCallable(view_variable.CALLBACK_OPEN_WEBPAGE_ABOUT_VRCT, "VRCT_GITHUB")
},
{
"image_file_name": "project_link_contact_us.png",
"callback": lambda _e: callFunctionIfCallable(view_variable.CALLBACK_OPEN_WEBPAGE_ABOUT_VRCT, "CONTACT_US")
},
]
createImageButtonRows(
parent_frame=project_links_wrapper,
image_buttons_settings=project_link_settings,
bottom_pady=about_vrct_uism.PROJECT_LINK_BOTTOM_PADY,
corner_radius=about_vrct_uism.PROJECT_LINK_CORNER_RADIUS,
ipadx=about_vrct_uism.PROJECT_LINK_ITEM_IPADX,
ipady=about_vrct_uism.PROJECT_LINK_ITEM_IPADY,
)
section_row+=1
# Contributors ----------------------------------
_contributors, contributors_contents_wrapper = createSectionContainer(
section_row=section_row,
section_title_image_file_name="contributors_section_title.png",
section_bottom_padding=about_vrct_uism.SECTION_BOTTOM_PADY,
section_title_bottom_padding=about_vrct_uism.CONTRIBUTORS_SECTION_TITLE_BOTTOM_PADY
)
contributors_members = settings.about_vrct.embedImageCTkLabel(contributors_contents_wrapper, "contributors_members.png")
contributors_members.grid(column=0, row=0, padx=0, pady=0, sticky="nsew")
# done_san
contributors_done_san_x = createContactButton(
parent_frame=contributors_members,
image_file_name="contributors_x_icon.png",
callback_arg="X_DONE_SAN",
)
contributors_done_san_x.place(x=about_vrct_uism.CONTRIBUTORS_DONE_SAN_X_X, y=about_vrct_uism.CONTRIBUTORS_CONTACTS_Y1, anchor="nw")
# IYA
contributors_iya_x = createContactButton(
parent_frame=contributors_members,
image_file_name="contributors_x_icon.png",
callback_arg="X_IYA",
)
contributors_iya_x.place(x=about_vrct_uism.CONTRIBUTORS_IYA_X_X, y=about_vrct_uism.CONTRIBUTORS_CONTACTS_Y1, anchor="nw")
# RERA
contributors_rera_x = createContactButton(
parent_frame=contributors_members,
image_file_name="contributors_x_icon.png",
callback_arg="X_RERA",
)
contributors_rera_x.place(x=about_vrct_uism.CONTRIBUTORS_RERA_X_X, y=about_vrct_uism.CONTRIBUTORS_CONTACTS_Y1, anchor="nw")
contributors_rera_github = createContactButton(
parent_frame=contributors_members,
image_file_name="contributors_github_icon.png",
callback_arg="GITHUB_RERA",
)
contributors_rera_github.place(x=about_vrct_uism.CONTRIBUTORS_RERA_GITHUB_X, y=about_vrct_uism.CONTRIBUTORS_CONTACTS_Y1, anchor="nw")
# POPOSUKE
contributors_poposuke_x = createContactButton(
parent_frame=contributors_members,
image_file_name="contributors_x_icon.png",
callback_arg="X_POPOSUKE",
)
contributors_poposuke_x.place(x=about_vrct_uism.CONTRIBUTORS_POPOSUKE_X_X, y=about_vrct_uism.CONTRIBUTORS_CONTACTS_Y2, anchor="nw")
# KUMAGUMA
contributors_kumaguma_x = createContactButton(
parent_frame=contributors_members,
image_file_name="contributors_x_icon.png",
callback_arg="X_KUMAGUMA",
)
contributors_kumaguma_x.place(x=about_vrct_uism.CONTRIBUTORS_KUMAGUMA_X_X, y=about_vrct_uism.CONTRIBUTORS_CONTACTS_Y2, anchor="nw")
section_row+=1
# Special Thanks & Supporters ----------------------------------
_special_thanks, special_thanks_contents_wrapper = createSectionContainer(
section_row=section_row,
section_title_image_file_name="special_thanks_section_title.png",
section_bottom_padding=about_vrct_uism.SECTION_BOTTOM_PADY,
section_title_bottom_padding=about_vrct_uism.SPECIAL_THANKS_SECTION_TITLE_BOTTOM_PADY
)
special_thanks_members = settings.about_vrct.embedImageCTkLabel(special_thanks_contents_wrapper, "special_thanks_members.png")
special_thanks_members.grid(column=0, row=0, padx=0, pady=(0,about_vrct_uism.SPECIAL_THANKS_MEMBERS_BOTTOM_PADY), sticky="nsew")
special_thanks_message = settings.about_vrct.embedImageCTkLabel(special_thanks_contents_wrapper, settings.about_vrct.image_file.SPECIAL_THANKS_MESSAGE)
special_thanks_message.grid(column=0, row=1, padx=0, pady=(0,about_vrct_uism.SPECIAL_THANKS_MESSAGE_BOTTOM_PADY), sticky="nsew")
special_thanks_message_and_you = settings.about_vrct.embedImageCTkLabel(special_thanks_contents_wrapper, "special_thanks_message_and_you.png")
special_thanks_message_and_you.grid(column=0, row=2, padx=0, pady=(0,about_vrct_uism.SPECIAL_THANKS_MESSAGE_AND_YOU_BOTTOM_PADY), sticky="nsw")
special_thanks_tell_us_message = createTellUsButton(
parent_frame=special_thanks_contents_wrapper,
image_file_name=settings.about_vrct.image_file.SPECIAL_THANKS_TELL_US_MESSAGE,
callback=lambda _e: callFunctionIfCallable(view_variable.CALLBACK_OPEN_WEBPAGE_ABOUT_VRCT, "SUPPORTER_REGISTRATION"),
)
special_thanks_tell_us_message.grid(column=0, row=3)
section_row+=1
# Special Thanks & Supporters ----------------------------------
_poster_showcase, poster_showcase_contents_wrapper = createSectionContainer(
section_row=section_row,
section_title_image_file_name="poster_showcase_section_title.png",
section_bottom_padding=about_vrct_uism.SECTION_BOTTOM_PADY,
section_title_bottom_padding=about_vrct_uism.POSTER_SHOWCASE_SECTION_TITLE_BOTTOM_PADY
)
poster_showcase_worlds_wrapper = CTkFrame(poster_showcase_contents_wrapper, fg_color=ABOUT_VRCT_BG, corner_radius=0, width=0, height=0)
poster_showcase_worlds_wrapper.grid(column=0, row=0, padx=0, pady=0, sticky="nsew")
poster_showcase_worlds_wrapper.grid_columnconfigure(0, weight=1)
poster_showcase_worlds = CTkFrame(poster_showcase_worlds_wrapper, fg_color=ABOUT_VRCT_BG, corner_radius=0, width=0, height=0)
poster_showcase_worlds.grid(column=0, row=0, padx=0, pady=(0,about_vrct_uism.POSTER_SHOWCASE_WORLD_BOTTOM_PADY), sticky="nsew")
poster_showcase_worlds.grid_columnconfigure(0, weight=1)
compounded_poster_showcase_worlds_list = []
for each_author_settings in poster_showcase_worlds_settings:
for data in each_author_settings["data"]:
if data["x_post_num"] is None:
append_settings = {
"image_file_name": data["image_file_name"],
"no_bind": True,
}
else:
x_post_num = data["x_post_num"]
callback = lambda _e,arg=x_post_num: view_variable.CALLBACK_OPEN_WEBPAGE_ABOUT_VRCT("X_SHIINA_POSTER_SHOWCASE_POST", arg)
append_settings = {
"image_file_name": data["image_file_name"],
"callback": callback,
}
compounded_poster_showcase_worlds_list.append(append_settings)
result = splitList(compounded_poster_showcase_worlds_list, 8)
poster_showcase_worlds_frame_list = []
for split_poster_showcase_worlds_settings in result:
poster_showcase_worlds_frame = CTkFrame(poster_showcase_worlds_wrapper, fg_color=ABOUT_VRCT_BG, corner_radius=0, width=0, height=0)
poster_showcase_worlds_frame.grid_columnconfigure(0, weight=1)
createImageButtonRows(
parent_frame=poster_showcase_worlds_frame,
image_buttons_settings=split_poster_showcase_worlds_settings,
bottom_pady=about_vrct_uism.POSTER_SHOWCASE_WORLD_ITEM_BOTTOM_PADY,
directly_type="showcased_worlds",
corner_radius=about_vrct_uism.POSTER_SHOWCASE_WORLD_CORNER_RADIUS,
ipadx=about_vrct_uism.POSTER_SHOWCASE_WORLD_ITEM_IPADX,
ipady=about_vrct_uism.POSTER_SHOWCASE_WORLD_ITEM_IPADY,
)
poster_showcase_worlds_frame_list.append(poster_showcase_worlds_frame)
pagination_button_settings = settings.about_vrct.image_file.POSTER_SHOWCASE_WORLD_PAGINATION_BUTTON
def defineAngles(index):
start_angle = 0
goal_angle = 90
if index == 0:
start_angle = 0
goal_angle = 90
elif index == 1:
start_angle = 90
goal_angle = 180
elif index == 2:
start_angle = 180
goal_angle = 360
return(start_angle, goal_angle)
def toNextPagePosterShowcase():
current_function_index = view_variable.CALLBACK_ABOUT_VRCT_POSTER_SHOWCASE_CURRENT_PAGE_NUM
view_variable.CALLBACK_ABOUT_VRCT_CHANGE_POSTER_SHOWCASE_WORLD_LIST=None
poster_showcase_worlds_frame_list[current_function_index].grid_remove()
pre_function_index = current_function_index
current_function_index = (current_function_index + 1) % len(poster_showcase_worlds_frame_list)
poster_showcase_worlds_frame_list[current_function_index].grid(column=0, row=0, padx=0, pady=(0,about_vrct_uism.POSTER_SHOWCASE_WORLD_BOTTOM_PADY), sticky="nsew")
view_variable.CALLBACK_ABOUT_VRCT_POSTER_SHOWCASE_CURRENT_PAGE_NUM = current_function_index
start_angle, goal_angle = defineAngles(pre_function_index)
animateRotation(
tk_root=config_window,
img_frame=config_window.poster_showcase_pagination_button.img_label,
img=pagination_button_settings.img,
img_width=pagination_button_settings.width,
img_height=pagination_button_settings.height,
start_angle=start_angle,
goal_angle=goal_angle,
duration=0.5,
)
view_variable.CALLBACK_ABOUT_VRCT_CHANGE_POSTER_SHOWCASE_WORLD_LIST=toNextPagePosterShowcase
# Initialize
view_variable.CALLBACK_ABOUT_VRCT_CHANGE_POSTER_SHOWCASE_WORLD_LIST=toNextPagePosterShowcase
view_variable.CALLBACK_ABOUT_VRCT_POSTER_SHOWCASE_CURRENT_PAGE_NUM = randint(0, len(poster_showcase_worlds_frame_list)-1)
start_angle, _goal_angle = defineAngles(view_variable.CALLBACK_ABOUT_VRCT_POSTER_SHOWCASE_CURRENT_PAGE_NUM)
poster_showcase_worlds_frame_list[view_variable.CALLBACK_ABOUT_VRCT_POSTER_SHOWCASE_CURRENT_PAGE_NUM].grid(column=0, row=0, padx=0, pady=(0,about_vrct_uism.POSTER_SHOWCASE_WORLD_BOTTOM_PADY), sticky="nsew")
poster_showcase_worlds_wrapper.grid_rowconfigure(1, weight=1)
poster_showcase_pagination_button_wrapper = CTkFrame(poster_showcase_worlds_wrapper, fg_color=ABOUT_VRCT_BG, corner_radius=0, width=0, height=0)
poster_showcase_pagination_button_wrapper.grid(column=0, row=2, padx=0, pady=(0, about_vrct_uism.POSTER_SHOWCASE_WORLD_PAGINATION_BUTTON_BOTTOM_PADY), sticky="nsew")
poster_showcase_pagination_button_wrapper.grid_columnconfigure((0,2), weight=1)
config_window.poster_showcase_pagination_button = settings.about_vrct.embedImageButtonCTkLabel(
parent_frame=poster_showcase_pagination_button_wrapper,
image_file_name="poster_showcase_pagination_button.png",
callback=lambda _e: callFunctionIfCallable(view_variable.CALLBACK_ABOUT_VRCT_CHANGE_POSTER_SHOWCASE_WORLD_LIST),
hovered_color="transparent",
clicked_color="transparent",
rotate_angle=-start_angle # for clockwise angle, put negative "-"
)
config_window.poster_showcase_pagination_button.grid(column=1, row=0, padx=0, pady=0, sticky="nsew")
# poster_showcase_pagination_button_wrapper.grid_columnconfigure((0,2), weight=1)
poster_showcase_pagination_button_chato = settings.about_vrct.embedImageButtonCTkLabel(
parent_frame=poster_showcase_pagination_button_wrapper,
image_file_name="poster_showcase_pagination_button_chato.png",
callback=lambda _e: callFunctionIfCallable(view_variable.CALLBACK_ABOUT_VRCT_CHANGE_POSTER_SHOWCASE_WORLD_LIST),
hovered_color="transparent",
clicked_color="transparent",
)
poster_showcase_pagination_button_chato.place(relx=0.502, rely=0.51, anchor="center")
pagination_button_chato_settings = settings.about_vrct.image_file.POSTER_SHOWCASE_WORLD_PAGINATION_BUTTON_CHATO
def rotateChatoAnimation():
view_variable.CALLBACK_ABOUT_VRCT_CHANGE_POSTER_SHOWCASE_BUTTON_HOVERED = None
animateRotation(
tk_root=config_window,
img_frame=poster_showcase_pagination_button_chato.img_label,
img=pagination_button_chato_settings.img,
img_width=pagination_button_chato_settings.width,
img_height=pagination_button_chato_settings.height,
start_angle=0,
goal_angle=360,
duration=0.5,
)
view_variable.CALLBACK_ABOUT_VRCT_CHANGE_POSTER_SHOWCASE_BUTTON_HOVERED = rotateChatoAnimation
view_variable.CALLBACK_ABOUT_VRCT_CHANGE_POSTER_SHOWCASE_BUTTON_HOVERED = rotateChatoAnimation
bindEnterAndLeaveFunction(
target_widgets=[config_window.poster_showcase_pagination_button.img_label,poster_showcase_pagination_button_chato.img_label],
enterFunction=lambda _e: callFunctionIfCallable(view_variable.CALLBACK_ABOUT_VRCT_CHANGE_POSTER_SHOWCASE_BUTTON_HOVERED),
leaveFunction=None,
)
poster_container = CTkFrame(poster_showcase_contents_wrapper, fg_color=ABOUT_VRCT_BG, corner_radius=0, width=0, height=0)
poster_container.grid(column=1, row=0, padx=0, pady=0, sticky="nsew")
poster_container.grid_columnconfigure(1, weight=1)
poster_images_wrapper = CTkFrame(poster_container, fg_color=ABOUT_VRCT_BG, corner_radius=0, width=0, height=0)
poster_images_wrapper.grid(column=0, row=0, padx=0, pady=(0,about_vrct_uism.POSTER_SHOWCASE_POSTER_IMAGES_BOTTOM_PADY), sticky="nsew")
poster_images_wrapper.grid_columnconfigure(1, weight=1)
poster_image_arrow_left = settings.about_vrct.embedImageButtonCTkLabel(poster_images_wrapper, "arrow_left.png", lambda _e: callFunctionIfCallable(view_variable.CALLBACK_ABOUT_VRCT_POSTER_PREV_BUTTON))
poster_image_arrow_left.grid(column=0, row=0, padx=0, pady=0, sticky="nsew")
poster_image_arrow_left.configure(corner_radius=about_vrct_uism.POSTER_CHANGE_BUTTON_CORNER_RADIUS)
bindButtonFunctionAndColor(
target_widgets=[poster_image_arrow_left],
enter_color=settings.ctm.ABOUT_VRCT_BUTTON_HOVERED_BG_COLOR,
leave_color=ABOUT_VRCT_BG,
clicked_color=settings.ctm.ABOUT_VRCT_BUTTON_CLICKED_BG_COLOR,
buttonReleasedFunction=None,
)
poster_image_frame_settings_list = [
{
"file_name": "iya_vrct_poster_ja.png",
"poster_type": "poster",
},
{
"file_name": "iya_vrct_poster_en.png",
"poster_type": "poster",
},
{
"file_name": "iya_vrct_poster_cn.png",
"poster_type": "poster",
},
{
"file_name": "iya_vrct_poster_ko.png",
"poster_type": "poster",
},
{
"file_name": "iya_vrct_manga_ja.png",
"poster_type": "manga",
},
{
"file_name": "iya_vrct_manga_en.png",
"poster_type": "manga",
},
{
"file_name": "iya_vrct_manga_ko.png",
"poster_type": "manga",
},
]
poster_frame_list = []
for poster_frame_settings in poster_image_frame_settings_list:
poster_frame = settings.about_vrct.embedImageCTkLabel(poster_images_wrapper, poster_frame_settings["file_name"], directly_type="vrct_posters")
poster_frame_list.append(poster_frame)
poster_images_authors_wrapper = CTkFrame(poster_container, fg_color=ABOUT_VRCT_BG, corner_radius=0, width=0, height=0)
poster_images_authors_wrapper.grid(column=0, row=1, padx=0, pady=0, sticky="nsew")
config_window.poster_images_authors = settings.about_vrct.embedImageCTkLabel(poster_images_authors_wrapper, settings.about_vrct.image_file.POSTER_IMAGES_AUTHOR)
config_window.poster_images_authors_m = settings.about_vrct.embedImageCTkLabel(poster_images_authors_wrapper, settings.about_vrct.image_file.POSTER_IMAGES_AUTHOR_M)
def toPrevPagePosterImage():
current_function_index = view_variable.CALLBACK_ABOUT_VRCT_POSTER_IMAGE_CURRENT_PAGE_NUM
view_variable.CALLBACK_ABOUT_VRCT_POSTER_PREV_BUTTON=None
view_variable.CALLBACK_ABOUT_VRCT_POSTER_NEXT_BUTTON=None
poster_frame_list[current_function_index].grid_remove()
current_function_index = (current_function_index - 1) % len(poster_frame_list)
poster_frame_list[current_function_index].grid(column=1, row=0, padx=0, pady=0, sticky="nsew")
view_variable.CALLBACK_ABOUT_VRCT_POSTER_IMAGE_CURRENT_PAGE_NUM = current_function_index
if poster_image_frame_settings_list[current_function_index]["poster_type"] == "poster":
config_window.poster_images_authors_m.grid_remove()
config_window.poster_images_authors.grid(column=0, row=0, padx=0, pady=0, sticky="nsew")
elif poster_image_frame_settings_list[current_function_index]["poster_type"] == "manga":
config_window.poster_images_authors.grid_remove()
config_window.poster_images_authors_m.grid(column=0, row=0, padx=0, pady=0, sticky="nsew")
view_variable.CALLBACK_ABOUT_VRCT_POSTER_PREV_BUTTON=toPrevPagePosterImage
view_variable.CALLBACK_ABOUT_VRCT_POSTER_NEXT_BUTTON=toNextPagePosterImage
def toNextPagePosterImage():
current_function_index = view_variable.CALLBACK_ABOUT_VRCT_POSTER_IMAGE_CURRENT_PAGE_NUM
view_variable.CALLBACK_ABOUT_VRCT_POSTER_PREV_BUTTON=None
view_variable.CALLBACK_ABOUT_VRCT_POSTER_NEXT_BUTTON=None
poster_frame_list[current_function_index].grid_remove()
current_function_index = (current_function_index + 1) % len(poster_frame_list)
poster_frame_list[current_function_index].grid(column=1, row=0, padx=0, pady=0, sticky="nsew")
view_variable.CALLBACK_ABOUT_VRCT_POSTER_IMAGE_CURRENT_PAGE_NUM = current_function_index
if poster_image_frame_settings_list[current_function_index]["poster_type"] == "poster":
config_window.poster_images_authors_m.grid_remove()
config_window.poster_images_authors.grid(column=0, row=0, padx=0, pady=0, sticky="nsew")
elif poster_image_frame_settings_list[current_function_index]["poster_type"] == "manga":
config_window.poster_images_authors.grid_remove()
config_window.poster_images_authors_m.grid(column=0, row=0, padx=0, pady=0, sticky="nsew")
view_variable.CALLBACK_ABOUT_VRCT_POSTER_PREV_BUTTON=toPrevPagePosterImage
view_variable.CALLBACK_ABOUT_VRCT_POSTER_NEXT_BUTTON=toNextPagePosterImage
# Initialize
view_variable.CALLBACK_ABOUT_VRCT_POSTER_PREV_BUTTON=toPrevPagePosterImage
view_variable.CALLBACK_ABOUT_VRCT_POSTER_NEXT_BUTTON=toNextPagePosterImage
config_window.poster_images_authors.grid(column=0, row=0, padx=0, pady=0, sticky="nsew")
poster_frame_list[0].grid(column=1, row=0, padx=0, pady=0, sticky="nsew")
poster_image_arrow_right = settings.about_vrct.embedImageButtonCTkLabel(poster_images_wrapper, "arrow_right.png", lambda _e: callFunctionIfCallable(view_variable.CALLBACK_ABOUT_VRCT_POSTER_NEXT_BUTTON))
poster_image_arrow_right.grid(column=2, row=0, padx=0, pady=0, sticky="nsew")
poster_image_arrow_right.configure(corner_radius=about_vrct_uism.POSTER_CHANGE_BUTTON_CORNER_RADIUS)
bindButtonFunctionAndColor(
target_widgets=[poster_image_arrow_right],
enter_color=settings.ctm.ABOUT_VRCT_BUTTON_HOVERED_BG_COLOR,
leave_color=ABOUT_VRCT_BG,
clicked_color=settings.ctm.ABOUT_VRCT_BUTTON_CLICKED_BG_COLOR,
buttonReleasedFunction=None,
)
poster_tell_us_message = createTellUsButton(
parent_frame=poster_showcase_contents_wrapper,
image_file_name=settings.about_vrct.image_file.POSTER_TELL_US_MESSAGE,
callback=lambda _e: callFunctionIfCallable(view_variable.CALLBACK_OPEN_WEBPAGE_ABOUT_VRCT, "POSTER_CONTACT_US"),
)
poster_tell_us_message.grid(column=0, row=1, columnspan=2, padx=0, pady=(about_vrct_uism.POSTER_TELL_US_MESSAGE_TOP_PADY,0), sticky="nse")
section_row+=1
# VRChat disclaimer ----------------------------------
vrchat_disclaimer, vrchat_disclaimer_contents_wrapper = createSectionContainer(
section_row=section_row,
)
vrchat_disclaimer_label = settings.about_vrct.embedImageCTkLabel(vrchat_disclaimer_contents_wrapper, "vrchat_disclaimer.png")
vrchat_disclaimer_label.grid(column=0, row=0, padx=0, pady=(about_vrct_uism.VRCHAT_DISCLAIMER_SECTION_TOP_PADDING, 0), sticky="nsew")

View File

@@ -21,6 +21,7 @@ def createSettingBox_Others(setting_box_wrapper, config_window, settings, view_v
def checkboxNoticeXsoverlayCallback(checkbox_box_widget):
callFunctionIfCallable(view_variable.CALLBACK_SET_ENABLE_NOTICE_XSOVERLAY, checkbox_box_widget.get())
def checkboxAutoExportMessageLogsCallback(checkbox_box_widget):
callFunctionIfCallable(view_variable.CALLBACK_SET_ENABLE_AUTO_EXPORT_MESSAGE_LOGS, checkbox_box_widget.get())
@@ -73,7 +74,6 @@ def createSettingBox_Others(setting_box_wrapper, config_window, settings, view_v
config_window.sb__notice_xsoverlay.grid(row=row)
row+=1
config_window.sb__auto_export_message_logs = createSettingBoxAutoExportMessageLogs(
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,

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