diff --git a/.gitignore b/.gitignore index 52825c27..e7b75066 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +tmp/ build/ dist/ /config.json @@ -10,3 +11,4 @@ weights/ .vscode error.log *.exe +*.ipynb diff --git a/build.bat b/build.bat index 84c7eb30..b1e119f4 100644 --- a/build.bat +++ b/build.bat @@ -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 \ No newline at end of file diff --git a/config.py b/config.py index ce6bfd1f..64b7c31c 100644 --- a/config.py +++ b/config.py @@ -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" diff --git a/controller.py b/controller.py index 79fb88be..dbc88560 100644 --- a/controller.py +++ b/controller.py @@ -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, diff --git a/fonts/NotoSansJP-Regular.ttf b/fonts/NotoSansJP-Regular.ttf new file mode 100644 index 00000000..1583096a Binary files /dev/null and b/fonts/NotoSansJP-Regular.ttf differ diff --git a/fonts/NotoSansKR-Regular.ttf b/fonts/NotoSansKR-Regular.ttf new file mode 100644 index 00000000..1b14d324 Binary files /dev/null and b/fonts/NotoSansKR-Regular.ttf differ diff --git a/fonts/NotoSansSC-Regular.ttf b/fonts/NotoSansSC-Regular.ttf new file mode 100644 index 00000000..4d4cadb9 Binary files /dev/null and b/fonts/NotoSansSC-Regular.ttf differ diff --git a/fonts/NotoSansTC-Regular.ttf b/fonts/NotoSansTC-Regular.ttf new file mode 100644 index 00000000..a785aae5 Binary files /dev/null and b/fonts/NotoSansTC-Regular.ttf differ diff --git a/img/about_vrct/arrow_left.png b/img/about_vrct/arrow_left.png new file mode 100644 index 00000000..a1c4b35e Binary files /dev/null and b/img/about_vrct/arrow_left.png differ diff --git a/img/about_vrct/arrow_right.png b/img/about_vrct/arrow_right.png new file mode 100644 index 00000000..69e005ba Binary files /dev/null and b/img/about_vrct/arrow_right.png differ diff --git a/img/about_vrct/contributors_github_icon.png b/img/about_vrct/contributors_github_icon.png new file mode 100644 index 00000000..1c6e0727 Binary files /dev/null and b/img/about_vrct/contributors_github_icon.png differ diff --git a/img/about_vrct/contributors_members.png b/img/about_vrct/contributors_members.png new file mode 100644 index 00000000..61305751 Binary files /dev/null and b/img/about_vrct/contributors_members.png differ diff --git a/img/about_vrct/contributors_section_title.png b/img/about_vrct/contributors_section_title.png new file mode 100644 index 00000000..641a535a Binary files /dev/null and b/img/about_vrct/contributors_section_title.png differ diff --git a/img/about_vrct/contributors_x_icon.png b/img/about_vrct/contributors_x_icon.png new file mode 100644 index 00000000..c17fb57d Binary files /dev/null and b/img/about_vrct/contributors_x_icon.png differ diff --git a/img/about_vrct/dev_github_icon.png b/img/about_vrct/dev_github_icon.png new file mode 100644 index 00000000..8cbaaff7 Binary files /dev/null and b/img/about_vrct/dev_github_icon.png differ diff --git a/img/about_vrct/dev_misya.png b/img/about_vrct/dev_misya.png new file mode 100644 index 00000000..60317fff Binary files /dev/null and b/img/about_vrct/dev_misya.png differ diff --git a/img/about_vrct/dev_section_title.png b/img/about_vrct/dev_section_title.png new file mode 100644 index 00000000..4f1da8a2 Binary files /dev/null and b/img/about_vrct/dev_section_title.png differ diff --git a/img/about_vrct/dev_shiina.png b/img/about_vrct/dev_shiina.png new file mode 100644 index 00000000..63b5785e Binary files /dev/null and b/img/about_vrct/dev_shiina.png differ diff --git a/img/about_vrct/dev_x_icon.png b/img/about_vrct/dev_x_icon.png new file mode 100644 index 00000000..dfd6d109 Binary files /dev/null and b/img/about_vrct/dev_x_icon.png differ diff --git a/img/about_vrct/poster_images_authors_en.png b/img/about_vrct/poster_images_authors_en.png new file mode 100644 index 00000000..c3488c62 Binary files /dev/null and b/img/about_vrct/poster_images_authors_en.png differ diff --git a/img/about_vrct/poster_images_authors_ja.png b/img/about_vrct/poster_images_authors_ja.png new file mode 100644 index 00000000..3f2b1b00 Binary files /dev/null and b/img/about_vrct/poster_images_authors_ja.png differ diff --git a/img/about_vrct/poster_images_authors_m_en.png b/img/about_vrct/poster_images_authors_m_en.png new file mode 100644 index 00000000..95241ae1 Binary files /dev/null and b/img/about_vrct/poster_images_authors_m_en.png differ diff --git a/img/about_vrct/poster_images_authors_m_ja.png b/img/about_vrct/poster_images_authors_m_ja.png new file mode 100644 index 00000000..28df62d0 Binary files /dev/null and b/img/about_vrct/poster_images_authors_m_ja.png differ diff --git a/img/about_vrct/poster_showcase_pagination_button.png b/img/about_vrct/poster_showcase_pagination_button.png new file mode 100644 index 00000000..bbfee311 Binary files /dev/null and b/img/about_vrct/poster_showcase_pagination_button.png differ diff --git a/img/about_vrct/poster_showcase_pagination_button_chato.png b/img/about_vrct/poster_showcase_pagination_button_chato.png new file mode 100644 index 00000000..a4df1d52 Binary files /dev/null and b/img/about_vrct/poster_showcase_pagination_button_chato.png differ diff --git a/img/about_vrct/poster_showcase_section_title.png b/img/about_vrct/poster_showcase_section_title.png new file mode 100644 index 00000000..5a4b00fa Binary files /dev/null and b/img/about_vrct/poster_showcase_section_title.png differ diff --git a/img/about_vrct/poster_tell_us_message_en.png b/img/about_vrct/poster_tell_us_message_en.png new file mode 100644 index 00000000..1078b42f Binary files /dev/null and b/img/about_vrct/poster_tell_us_message_en.png differ diff --git a/img/about_vrct/poster_tell_us_message_ja.png b/img/about_vrct/poster_tell_us_message_ja.png new file mode 100644 index 00000000..8e2b096d Binary files /dev/null and b/img/about_vrct/poster_tell_us_message_ja.png differ diff --git a/img/about_vrct/project_link_booth.png b/img/about_vrct/project_link_booth.png new file mode 100644 index 00000000..14af933a Binary files /dev/null and b/img/about_vrct/project_link_booth.png differ diff --git a/img/about_vrct/project_link_contact_us.png b/img/about_vrct/project_link_contact_us.png new file mode 100644 index 00000000..c21bc7ef Binary files /dev/null and b/img/about_vrct/project_link_contact_us.png differ diff --git a/img/about_vrct/project_link_documents.png b/img/about_vrct/project_link_documents.png new file mode 100644 index 00000000..cd9723e4 Binary files /dev/null and b/img/about_vrct/project_link_documents.png differ diff --git a/img/about_vrct/project_link_vrct_github.png b/img/about_vrct/project_link_vrct_github.png new file mode 100644 index 00000000..eb05f15f Binary files /dev/null and b/img/about_vrct/project_link_vrct_github.png differ diff --git a/img/about_vrct/showcased_worlds/bar_asagao.png b/img/about_vrct/showcased_worlds/bar_asagao.png new file mode 100644 index 00000000..001523b4 Binary files /dev/null and b/img/about_vrct/showcased_worlds/bar_asagao.png differ diff --git a/img/about_vrct/showcased_worlds/cafe_cian.png b/img/about_vrct/showcased_worlds/cafe_cian.png new file mode 100644 index 00000000..f8f5e8c3 Binary files /dev/null and b/img/about_vrct/showcased_worlds/cafe_cian.png differ diff --git a/img/about_vrct/showcased_worlds/coffee_keisyoku_chakachaka.png b/img/about_vrct/showcased_worlds/coffee_keisyoku_chakachaka.png new file mode 100644 index 00000000..47bbc41a Binary files /dev/null and b/img/about_vrct/showcased_worlds/coffee_keisyoku_chakachaka.png differ diff --git a/img/about_vrct/showcased_worlds/ehon_no_heikousekai_jimusho.png b/img/about_vrct/showcased_worlds/ehon_no_heikousekai_jimusho.png new file mode 100644 index 00000000..2e2b64cf Binary files /dev/null and b/img/about_vrct/showcased_worlds/ehon_no_heikousekai_jimusho.png differ diff --git a/img/about_vrct/showcased_worlds/ikoiba.png b/img/about_vrct/showcased_worlds/ikoiba.png new file mode 100644 index 00000000..04a85165 Binary files /dev/null and b/img/about_vrct/showcased_worlds/ikoiba.png differ diff --git a/img/about_vrct/showcased_worlds/ippaidou.png b/img/about_vrct/showcased_worlds/ippaidou.png new file mode 100644 index 00000000..9d673f61 Binary files /dev/null and b/img/about_vrct/showcased_worlds/ippaidou.png differ diff --git a/img/about_vrct/showcased_worlds/japanese_culture_osenbeito.png b/img/about_vrct/showcased_worlds/japanese_culture_osenbeito.png new file mode 100644 index 00000000..fc8f2b36 Binary files /dev/null and b/img/about_vrct/showcased_worlds/japanese_culture_osenbeito.png differ diff --git a/img/about_vrct/showcased_worlds/kimodameshi.png b/img/about_vrct/showcased_worlds/kimodameshi.png new file mode 100644 index 00000000..7bee627b Binary files /dev/null and b/img/about_vrct/showcased_worlds/kimodameshi.png differ diff --git a/img/about_vrct/showcased_worlds/kokekkopiyopiyo.png b/img/about_vrct/showcased_worlds/kokekkopiyopiyo.png new file mode 100644 index 00000000..dc59ee56 Binary files /dev/null and b/img/about_vrct/showcased_worlds/kokekkopiyopiyo.png differ diff --git a/img/about_vrct/showcased_worlds/kuroinu_work_room.png b/img/about_vrct/showcased_worlds/kuroinu_work_room.png new file mode 100644 index 00000000..c7cb7209 Binary files /dev/null and b/img/about_vrct/showcased_worlds/kuroinu_work_room.png differ diff --git a/img/about_vrct/showcased_worlds/language_exchange_tervern.png b/img/about_vrct/showcased_worlds/language_exchange_tervern.png new file mode 100644 index 00000000..17470129 Binary files /dev/null and b/img/about_vrct/showcased_worlds/language_exchange_tervern.png differ diff --git a/img/about_vrct/showcased_worlds/mamehinata_dogrun.png b/img/about_vrct/showcased_worlds/mamehinata_dogrun.png new file mode 100644 index 00000000..da443ffe Binary files /dev/null and b/img/about_vrct/showcased_worlds/mamehinata_dogrun.png differ diff --git a/img/about_vrct/showcased_worlds/monogatari_meetup.png b/img/about_vrct/showcased_worlds/monogatari_meetup.png new file mode 100644 index 00000000..3f10ed22 Binary files /dev/null and b/img/about_vrct/showcased_worlds/monogatari_meetup.png differ diff --git a/img/about_vrct/showcased_worlds/nihongokurabu.png b/img/about_vrct/showcased_worlds/nihongokurabu.png new file mode 100644 index 00000000..9425e0b5 Binary files /dev/null and b/img/about_vrct/showcased_worlds/nihongokurabu.png differ diff --git a/img/about_vrct/showcased_worlds/parallel_collar.png b/img/about_vrct/showcased_worlds/parallel_collar.png new file mode 100644 index 00000000..fae8c770 Binary files /dev/null and b/img/about_vrct/showcased_worlds/parallel_collar.png differ diff --git a/img/about_vrct/showcased_worlds/re_yatuha_room.png b/img/about_vrct/showcased_worlds/re_yatuha_room.png new file mode 100644 index 00000000..505c63a1 Binary files /dev/null and b/img/about_vrct/showcased_worlds/re_yatuha_room.png differ diff --git a/img/about_vrct/showcased_worlds/silakan_datang_ke_rumahku.png b/img/about_vrct/showcased_worlds/silakan_datang_ke_rumahku.png new file mode 100644 index 00000000..12071f1e Binary files /dev/null and b/img/about_vrct/showcased_worlds/silakan_datang_ke_rumahku.png differ diff --git a/img/about_vrct/showcased_worlds/study_japanese_world_japanichijou.png b/img/about_vrct/showcased_worlds/study_japanese_world_japanichijou.png new file mode 100644 index 00000000..95a4cf67 Binary files /dev/null and b/img/about_vrct/showcased_worlds/study_japanese_world_japanichijou.png differ diff --git a/img/about_vrct/showcased_worlds/sushi_stand_guruguru.png b/img/about_vrct/showcased_worlds/sushi_stand_guruguru.png new file mode 100644 index 00000000..4ea74eaa Binary files /dev/null and b/img/about_vrct/showcased_worlds/sushi_stand_guruguru.png differ diff --git a/img/about_vrct/showcased_worlds/tyuuniti_kouryuukai.png b/img/about_vrct/showcased_worlds/tyuuniti_kouryuukai.png new file mode 100644 index 00000000..ad6c10a7 Binary files /dev/null and b/img/about_vrct/showcased_worlds/tyuuniti_kouryuukai.png differ diff --git a/img/about_vrct/showcased_worlds/uj_club.png b/img/about_vrct/showcased_worlds/uj_club.png new file mode 100644 index 00000000..9118f48f Binary files /dev/null and b/img/about_vrct/showcased_worlds/uj_club.png differ diff --git a/img/about_vrct/showcased_worlds/usanezumi_shrine2.png b/img/about_vrct/showcased_worlds/usanezumi_shrine2.png new file mode 100644 index 00000000..f10cce6c Binary files /dev/null and b/img/about_vrct/showcased_worlds/usanezumi_shrine2.png differ diff --git a/img/about_vrct/showcased_worlds/yuttari_eikaiwa.png b/img/about_vrct/showcased_worlds/yuttari_eikaiwa.png new file mode 100644 index 00000000..c9616217 Binary files /dev/null and b/img/about_vrct/showcased_worlds/yuttari_eikaiwa.png differ diff --git a/img/about_vrct/special_thanks_members.png b/img/about_vrct/special_thanks_members.png new file mode 100644 index 00000000..15487230 Binary files /dev/null and b/img/about_vrct/special_thanks_members.png differ diff --git a/img/about_vrct/special_thanks_message_and_you.png b/img/about_vrct/special_thanks_message_and_you.png new file mode 100644 index 00000000..f006ff15 Binary files /dev/null and b/img/about_vrct/special_thanks_message_and_you.png differ diff --git a/img/about_vrct/special_thanks_message_en.png b/img/about_vrct/special_thanks_message_en.png new file mode 100644 index 00000000..58db5a87 Binary files /dev/null and b/img/about_vrct/special_thanks_message_en.png differ diff --git a/img/about_vrct/special_thanks_message_ja.png b/img/about_vrct/special_thanks_message_ja.png new file mode 100644 index 00000000..493a6bf0 Binary files /dev/null and b/img/about_vrct/special_thanks_message_ja.png differ diff --git a/img/about_vrct/special_thanks_section_title.png b/img/about_vrct/special_thanks_section_title.png new file mode 100644 index 00000000..ea64a970 Binary files /dev/null and b/img/about_vrct/special_thanks_section_title.png differ diff --git a/img/about_vrct/special_thanks_tell_us_message_en.png b/img/about_vrct/special_thanks_tell_us_message_en.png new file mode 100644 index 00000000..c213e5bc Binary files /dev/null and b/img/about_vrct/special_thanks_tell_us_message_en.png differ diff --git a/img/about_vrct/special_thanks_tell_us_message_ja.png b/img/about_vrct/special_thanks_tell_us_message_ja.png new file mode 100644 index 00000000..f3274966 Binary files /dev/null and b/img/about_vrct/special_thanks_tell_us_message_ja.png differ diff --git a/img/about_vrct/vrchat_disclaimer.png b/img/about_vrct/vrchat_disclaimer.png new file mode 100644 index 00000000..c6e3dbb0 Binary files /dev/null and b/img/about_vrct/vrchat_disclaimer.png differ diff --git a/img/about_vrct/vrct_logo_for_about_vrct.png b/img/about_vrct/vrct_logo_for_about_vrct.png new file mode 100644 index 00000000..4f461409 Binary files /dev/null and b/img/about_vrct/vrct_logo_for_about_vrct.png differ diff --git a/img/about_vrct/vrct_posters/iya_vrct_manga_en.png b/img/about_vrct/vrct_posters/iya_vrct_manga_en.png new file mode 100644 index 00000000..f73e762a Binary files /dev/null and b/img/about_vrct/vrct_posters/iya_vrct_manga_en.png differ diff --git a/img/about_vrct/vrct_posters/iya_vrct_manga_ja.png b/img/about_vrct/vrct_posters/iya_vrct_manga_ja.png new file mode 100644 index 00000000..3fc0ff40 Binary files /dev/null and b/img/about_vrct/vrct_posters/iya_vrct_manga_ja.png differ diff --git a/img/about_vrct/vrct_posters/iya_vrct_manga_ko.png b/img/about_vrct/vrct_posters/iya_vrct_manga_ko.png new file mode 100644 index 00000000..e40f769c Binary files /dev/null and b/img/about_vrct/vrct_posters/iya_vrct_manga_ko.png differ diff --git a/img/about_vrct/vrct_posters/iya_vrct_poster_cn.png b/img/about_vrct/vrct_posters/iya_vrct_poster_cn.png new file mode 100644 index 00000000..6e3a5be4 Binary files /dev/null and b/img/about_vrct/vrct_posters/iya_vrct_poster_cn.png differ diff --git a/img/about_vrct/vrct_posters/iya_vrct_poster_en.png b/img/about_vrct/vrct_posters/iya_vrct_poster_en.png new file mode 100644 index 00000000..22352320 Binary files /dev/null and b/img/about_vrct/vrct_posters/iya_vrct_poster_en.png differ diff --git a/img/about_vrct/vrct_posters/iya_vrct_poster_ja.png b/img/about_vrct/vrct_posters/iya_vrct_poster_ja.png new file mode 100644 index 00000000..041d346e Binary files /dev/null and b/img/about_vrct/vrct_posters/iya_vrct_poster_ja.png differ diff --git a/img/about_vrct/vrct_posters/iya_vrct_poster_ko.png b/img/about_vrct/vrct_posters/iya_vrct_poster_ko.png new file mode 100644 index 00000000..268a6e24 Binary files /dev/null and b/img/about_vrct/vrct_posters/iya_vrct_poster_ko.png differ diff --git a/img/chato_delivering.png b/img/chato_delivering.png new file mode 100644 index 00000000..d38f1447 Binary files /dev/null and b/img/chato_delivering.png differ diff --git a/img/chato_unpackaging.png b/img/chato_unpackaging.png new file mode 100644 index 00000000..52eb66dc Binary files /dev/null and b/img/chato_unpackaging.png differ diff --git a/img/downloading_unpackaging_d.png b/img/downloading_unpackaging_d.png new file mode 100644 index 00000000..5c3c7031 Binary files /dev/null and b/img/downloading_unpackaging_d.png differ diff --git a/img/downloading_unpackaging_u.png b/img/downloading_unpackaging_u.png new file mode 100644 index 00000000..684433d5 Binary files /dev/null and b/img/downloading_unpackaging_u.png differ diff --git a/img/overlay_br_sakura.png b/img/overlay_br_sakura.png new file mode 100644 index 00000000..61e8ccd7 Binary files /dev/null and b/img/overlay_br_sakura.png differ diff --git a/img/overlay_tl_sakura.png b/img/overlay_tl_sakura.png new file mode 100644 index 00000000..d6211f41 Binary files /dev/null and b/img/overlay_tl_sakura.png differ diff --git a/img/unpackage_icon.png b/img/unpackage_icon.png new file mode 100644 index 00000000..8fd8e7d2 Binary files /dev/null and b/img/unpackage_icon.png differ diff --git a/img/vrct_update_process.png b/img/vrct_update_process.png new file mode 100644 index 00000000..27a77150 Binary files /dev/null and b/img/vrct_update_process.png differ diff --git a/locales/en.yml b/locales/en.yml index 500a00f3..2951af23 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -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: diff --git a/locales/ja.yml b/locales/ja.yml index e9a4492c..bd550712 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -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: diff --git a/locales/ko.yml b/locales/ko.yml index d27438b1..ed8f8ddf 100644 --- a/locales/ko.yml +++ b/locales/ko.yml @@ -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: diff --git a/model.py b/model.py index 2af4135a..f1d00f0c 100644 --- a/model.py +++ b/model.py @@ -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() \ No newline at end of file diff --git a/models/overlay/overlay.py b/models/overlay/overlay.py new file mode 100644 index 00000000..87cd12b1 --- /dev/null +++ b/models/overlay/overlay.py @@ -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) \ No newline at end of file diff --git a/models/overlay/overlay_image.py b/models/overlay/overlay_image.py new file mode 100644 index 00000000..d1607179 --- /dev/null +++ b/models/overlay/overlay_image.py @@ -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 \ No newline at end of file diff --git a/models/transcription/transcription_utils.py b/models/transcription/transcription_utils.py index f40defeb..4292ba23 100644 --- a/models/transcription/transcription_utils.py +++ b/models/transcription/transcription_utils.py @@ -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"} \ No newline at end of file + return {"device": loopback} + return {"device": {"name": "NoDevice"}} + +if __name__ == "__main__": + print("getOutputDevices()", getOutputDevices()) + print("getDefaultOutputDevice()", getDefaultOutputDevice()) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 350a73fb..f92a7ddf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 \ No newline at end of file diff --git a/utils.py b/utils.py index 6add2b43..b80923f8 100644 --- a/utils.py +++ b/utils.py @@ -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")) \ No newline at end of file + 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 \ No newline at end of file diff --git a/view.py b/view.py index 840a2774..c76a90ae 100644 --- a/view.py +++ b/view.py @@ -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( + # "", + # 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): diff --git a/vrct_gui/_CreateConfirmationModal.py b/vrct_gui/_CreateConfirmationModal.py index 7391a594..898e9c84 100644 --- a/vrct_gui/_CreateConfirmationModal.py +++ b/vrct_gui/_CreateConfirmationModal.py @@ -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() diff --git a/vrct_gui/_PrintToTextbox.py b/vrct_gui/_PrintToTextbox.py index 6d68cfdc..5d5d7419 100644 --- a/vrct_gui/_PrintToTextbox.py +++ b/vrct_gui/_PrintToTextbox.py @@ -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 \ No newline at end of file diff --git a/vrct_gui/_changeConfigWindowWidgetsStatus.py b/vrct_gui/_changeConfigWindowWidgetsStatus.py index 02dddd42..8f3b6b80 100644 --- a/vrct_gui/_changeConfigWindowWidgetsStatus.py +++ b/vrct_gui/_changeConfigWindowWidgetsStatus.py @@ -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"] diff --git a/vrct_gui/config_window/ConfigWindow.py b/vrct_gui/config_window/ConfigWindow.py index fc56daec..42272110 100644 --- a/vrct_gui/config_window/ConfigWindow.py +++ b/vrct_gui/config_window/ConfigWindow.py @@ -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) diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/_createSettingBoxContainer.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/_createSettingBoxContainer.py index 97859e3a..28b7c552 100644 --- a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/_createSettingBoxContainer.py +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/_createSettingBoxContainer.py @@ -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 diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/createSideMenuAndSettingsBoxContainers.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/createSideMenuAndSettingsBoxContainers.py index 49272afc..52e72f01 100644 --- a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/createSideMenuAndSettingsBoxContainers.py +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/createSideMenuAndSettingsBoxContainers.py @@ -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, diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/_SettingBoxGenerator.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/_SettingBoxGenerator.py index 60386162..751211fc 100644 --- a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/_SettingBoxGenerator.py +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/_SettingBoxGenerator.py @@ -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, diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_about_vrct/__init__.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_about_vrct/__init__.py new file mode 100644 index 00000000..61b104ec --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_about_vrct/__init__.py @@ -0,0 +1 @@ +from .createSettingBox_AboutVrct import createSettingBox_AboutVrct \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_about_vrct/about_vrct_store.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_about_vrct/about_vrct_store.py new file mode 100644 index 00000000..a431619f --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_about_vrct/about_vrct_store.py @@ -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" }, + ] + }, +] \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_about_vrct/createSettingBox_AboutVrct.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_about_vrct/createSettingBox_AboutVrct.py new file mode 100644 index 00000000..261a4a79 --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_about_vrct/createSettingBox_AboutVrct.py @@ -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") \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_others/createSettingBox_Others.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_others/createSettingBox_Others.py index b32116fe..0d7d0ffb 100644 --- a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_others/createSettingBox_Others.py +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_others/createSettingBox_Others.py @@ -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, diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_transcription/createSettingBox_Speaker.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_transcription/createSettingBox_Speaker.py index 846dbd8e..402adaa1 100644 --- a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_transcription/createSettingBox_Speaker.py +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_transcription/createSettingBox_Speaker.py @@ -4,6 +4,7 @@ from .._SettingBoxGenerator import _SettingBoxGenerator def createSettingBox_Speaker(setting_box_wrapper, config_window, settings, view_variable): sbg = _SettingBoxGenerator(setting_box_wrapper, config_window, settings, view_variable) + createSettingBoxDropdownMenu = sbg.createSettingBoxDropdownMenu createSettingBoxSwitch = sbg.createSettingBoxSwitch createSettingBoxProgressbarXSlider = sbg.createSettingBoxProgressbarXSlider createSettingBoxEntry = sbg.createSettingBoxEntry @@ -13,6 +14,9 @@ def createSettingBox_Speaker(setting_box_wrapper, config_window, settings, view_ callFunctionIfCallable(view_variable.CALLBACK_CHECK_SPEAKER_THRESHOLD, is_turned_on) + def optionmenuInputSpeakerDeviceCallback(value): + callFunctionIfCallable(view_variable.CALLBACK_SET_SPEAKER_DEVICE, value) + def sliderInputSpeakerEnergyThresholdCallback(value): callFunctionIfCallable(view_variable.CALLBACK_SET_SPEAKER_ENERGY_THRESHOLD, value) @@ -32,6 +36,17 @@ def createSettingBox_Speaker(setting_box_wrapper, config_window, settings, view_ row=0 + config_window.sb__speaker_device = createSettingBoxDropdownMenu( + for_var_label_text=view_variable.VAR_LABEL_SPEAKER_DEVICE, + for_var_desc_text=view_variable.VAR_DESC_SPEAKER_DEVICE, + optionmenu_attr_name="sb__optionmenu_speaker_device", + dropdown_menu_values=view_variable.LIST_SPEAKER_DEVICE, + command=lambda value: optionmenuInputSpeakerDeviceCallback(value), + variable=view_variable.VAR_SPEAKER_DEVICE, + ) + config_window.sb__speaker_device.grid(row=row) + row+=1 + config_window.sb__speaker_dynamic_energy_threshold = createSettingBoxSwitch( for_var_label_text=view_variable.VAR_LABEL_SPEAKER_DYNAMIC_ENERGY_THRESHOLD, for_var_desc_text=view_variable.VAR_DESC_SPEAKER_DYNAMIC_ENERGY_THRESHOLD, diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_vr/__init__.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_vr/__init__.py new file mode 100644 index 00000000..78090aef --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_vr/__init__.py @@ -0,0 +1 @@ +from .createSettingBox_Vr import createSettingBox_Vr \ No newline at end of file diff --git a/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_vr/createSettingBox_Vr.py b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_vr/createSettingBox_Vr.py new file mode 100644 index 00000000..2afa8748 --- /dev/null +++ b/vrct_gui/config_window/widgets/createSideMenuAndSettingsBoxContainers/setting_box_containers/setting_box_vr/createSettingBox_Vr.py @@ -0,0 +1,29 @@ +from utils import callFunctionIfCallable + +from .._SettingBoxGenerator import _SettingBoxGenerator + +def createSettingBox_Vr(setting_box_wrapper, config_window, settings, view_variable): + sbg = _SettingBoxGenerator(setting_box_wrapper, config_window, settings, view_variable) + createSettingBox_Overlay = sbg.createSettingBox_Overlay + createSettingBoxCheckbox = sbg.createSettingBoxCheckbox + + def switchEnableOverlayUiCallback(switch_widget): + callFunctionIfCallable(view_variable.CALLBACK_SET_ENABLE_OVERLAY_SMALL_LOG, switch_widget.get()) + + def buttonOpenOverlaySettingsWindow(_e): + print(_e) + callFunctionIfCallable(view_variable.CALLBACK_SET_OPEN_OVERLAY_SETTINGS_WINDOW) + + + row=0 + config_window.sb__enable_overlay_small_log = createSettingBox_Overlay( + for_var_label_text=view_variable.VAR_LABEL_ENABLE_OVERLAY_SMALL_LOG, + for_var_desc_text=view_variable.VAR_DESC_ENABLE_OVERLAY_SMALL_LOG, + switch_attr_name="sb__switch_enable_overlay_small_log", + command=lambda: switchEnableOverlayUiCallback(config_window.sb__switch_enable_overlay_small_log), + variable=view_variable.VAR_ENABLE_OVERLAY_SMALL_LOG, + for_var_button_label=view_variable.VAR_OPEN_OVERLAY_SETTINGS_BUTTON, + label_button_clicked_command=buttonOpenOverlaySettingsWindow, + ) + config_window.sb__enable_overlay_small_log.grid(row=row) + row+=1 \ No newline at end of file diff --git a/vrct_gui/main_window/createMainWindowWidgets.py b/vrct_gui/main_window/createMainWindowWidgets.py index c09f4bca..06c24932 100644 --- a/vrct_gui/main_window/createMainWindowWidgets.py +++ b/vrct_gui/main_window/createMainWindowWidgets.py @@ -47,6 +47,60 @@ def createMainWindowWidgets(vrct_gui, settings, view_variable): # Main Top Bar Container - Right Side # start from 3 main_topbar_column=3 + + # # Overlay Settings Button + # vrct_gui.overlay_settings_container = CTkFrame( + # vrct_gui.main_topbar_container, + # corner_radius=settings.uism.UPDATE_AVAILABLE_BUTTON_CORNER_RADIUS, + # fg_color=settings.ctm.MAIN_BG_COLOR, + # cursor="hand2", + # ) + # vrct_gui.overlay_settings_container.grid(row=0, column=main_topbar_column, padx=settings.uism.UPDATE_AVAILABLE_BUTTON_PADX, pady=settings.uism.TOP_BAR_BUTTON_PADY, sticky="nsw") + # # vrct_gui.overlay_settings_container.grid_remove() + + + # vrct_gui.overlay_settings_container.grid_rowconfigure((0,2), weight=1) + + # vrct_gui.overlay_settings_icon = CTkLabel( + # vrct_gui.overlay_settings_container, + # text=None, + # corner_radius=0, + # height=0, + # image=CTkImage(settings.image_file.CONFIGURATION_ICON_DISABLED, size=settings.uism.UPDATE_AVAILABLE_BUTTON_SIZE) + # ) + # vrct_gui.overlay_settings_icon.grid(row=1, column=0, padx=(settings.uism.UPDATE_AVAILABLE_BUTTON_IPADX, settings.uism.UPDATE_AVAILABLE_PADX_BETWEEN_LABEL_AND_ICON), pady=0) + + + # vrct_gui.overlay_settings_label = CTkLabel( + # vrct_gui.overlay_settings_container, + # textvariable=view_variable.VAR_OVERLAY_SETTINGS, + # height=0, + # corner_radius=0, + # font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.UPDATE_AVAILABLE_BUTTON_FONT_SIZE, weight="normal"), + # anchor="e", + # text_color=settings.ctm.TOP_BAR_BUTTON_TEXT_COLOR, + # # text_color=settings.ctm.UPDATE_AVAILABLE_BUTTON_TEXT_COLOR, + # ) + # # This "right padx +1" is for fixing a bug that sticks out from the frame. I don't know why that happens... + # vrct_gui.overlay_settings_label.grid(row=1, column=1, padx=(0,settings.uism.UPDATE_AVAILABLE_BUTTON_IPADX+1), pady=0) + + + # bindButtonFunctionAndColor( + # target_widgets=[ + # vrct_gui.overlay_settings_container, + # vrct_gui.overlay_settings_label, + # vrct_gui.overlay_settings_icon, + # ], + # enter_color=settings.ctm.TOP_BAR_BUTTON_HOVERED_BG_COLOR, + # leave_color=settings.ctm.TOP_BAR_BUTTON_BG_COLOR, + # clicked_color=settings.ctm.TOP_BAR_BUTTON_CLICKED_BG_COLOR, + # buttonReleasedFunction=lambda e: callFunctionIfCallable(view_variable.CALLBACK_SET_OPEN_OVERLAY_SETTINGS_WINDOW), + # ) + + + + + # main_topbar_column+=1 # Update Available Button vrct_gui.update_available_container = CTkFrame( vrct_gui.main_topbar_container, diff --git a/vrct_gui/quick_settings_window/QuickSettingsWindow.py b/vrct_gui/quick_settings_window/QuickSettingsWindow.py new file mode 100644 index 00000000..0d294b8e --- /dev/null +++ b/vrct_gui/quick_settings_window/QuickSettingsWindow.py @@ -0,0 +1,244 @@ +from utils import callFunctionIfCallable, floatToPctStr + +from customtkinter import CTkImage, CTkLabel, CTkToplevel, CTkProgressBar, CTkFrame, CTkSlider +from ..ui_utils import getImagePath, setGeometryToCenterOfScreen, fadeInAnimation, createLabelButton + +from ._CreateQuickSettingBox import _CreateQuickSettingBox + +class QuickSettingsWindow(CTkToplevel): + def __init__(self, vrct_gui, settings, view_variable): + super().__init__() + self.withdraw() + self.title("Overlay Settings") + self.protocol("WM_DELETE_WINDOW", self.withdraw) + self.after(200, lambda: self.iconbitmap(getImagePath("vrct_logo_mark_black.ico"))) + + + self.settings = settings + + self.configure(fg_color=self.settings.ctm.SB__BG_COLOR) + + BG_HEX_COLOR = "#292a2d" + + self.grid_columnconfigure(0, weight=1, minsize=400) + self.grid_rowconfigure(0, weight=1) + self.qsw_background = CTkFrame(self, corner_radius=0, fg_color=self.settings.ctm.SB__BG_COLOR) + self.qsw_background.grid(row=0, column=0, pady=(0,18), sticky="nsew") + self.qsw_background.grid_columnconfigure(0, weight=1) + + self.qsw_setting_box = CTkFrame(self.qsw_background, corner_radius=0, fg_color=BG_HEX_COLOR) + self.qsw_setting_box.grid(row=0, column=0, sticky="nsew") + self.qsw_setting_box.grid_columnconfigure(0, weight=1) + + + cqsb = _CreateQuickSettingBox(self.qsw_setting_box, vrct_gui, settings, view_variable) + createSettingBoxSlider = cqsb.createSettingBoxSlider + createSettingBoxSwitch = cqsb.createSettingBoxSwitch + + + + + + # Overlay General Settings + row=0 + def switchCallback(switch_widget): + callFunctionIfCallable(view_variable.CALLBACK_SET_ENABLE_OVERLAY_SMALL_LOG, switch_widget.get()) + + self.qsb__enable_overlay_small_log = createSettingBoxSwitch( + for_var_label_text=view_variable.VAR_LABEL_ENABLE_OVERLAY_SMALL_LOG, + switch_attr_name="qsb__enable_overlay_small_log_switch", + command=lambda: switchCallback(vrct_gui.qsb__enable_overlay_small_log_switch), + variable=view_variable.VAR_ENABLE_OVERLAY_SMALL_LOG, + ) + self.qsb__enable_overlay_small_log.grid(row=row) + + + row+=1 + def sliderCallback(e): + value = round(e,2) + callFunctionIfCallable(view_variable.CALLBACK_SET_OVERLAY_SETTINGS, value, "opacity") + view_variable.VAR_CURRENT_VALUE_OVERLAY_OPACITY.set(floatToPctStr(value)) + + self.qsb__overlay_opacity = createSettingBoxSlider( + for_var_label_text=view_variable.VAR_LABEL_OVERLAY_OPACITY, + for_var_current_value=view_variable.VAR_CURRENT_VALUE_OVERLAY_OPACITY, + slider_attr_name="qsb__overlay_opacity_slider", + slider_range=view_variable.SLIDER_RANGE_OVERLAY_OPACITY, + slider_number_of_steps=view_variable.NUMBER_OF_STEPS_OVERLAY_OPACITY, + command=sliderCallback, + variable=view_variable.VAR_OVERLAY_OPACITY, + ) + self.qsb__overlay_opacity.grid(row=row) + + + row+=1 + def sliderCallback(e): + value = round(e,2) + callFunctionIfCallable(view_variable.CALLBACK_SET_OVERLAY_SETTINGS, value, "ui_scaling") + view_variable.VAR_CURRENT_VALUE_OVERLAY_UI_SCALING.set(floatToPctStr(value)) + + self.qsb__overlay_ui_scaling = createSettingBoxSlider( + for_var_label_text=view_variable.VAR_LABEL_OVERLAY_UI_SCALING, + for_var_current_value=view_variable.VAR_CURRENT_VALUE_OVERLAY_UI_SCALING, + slider_attr_name="qsb__overlay_ui_scaling_slider", + slider_range=view_variable.SLIDER_RANGE_OVERLAY_UI_SCALING, + slider_number_of_steps=view_variable.NUMBER_OF_STEPS_OVERLAY_UI_SCALING, + command=sliderCallback, + variable=view_variable.VAR_OVERLAY_UI_SCALING, + ) + self.qsb__overlay_ui_scaling.grid(row=row) + + + + # Overlay Small Log Settings + + # row+=1 + # def switchCallback(switch_widget): + # callFunctionIfCallable(view_variable.CALLBACK_SET_ENABLE_OVERLAY_SMALL_LOG, switch_widget.get()) + + # self.qsb__enable_overlay_small_log = createSettingBoxSwitch( + # for_var_label_text=view_variable.VAR_LABEL_ENABLE_OVERLAY_SMALL_LOG, + # switch_attr_name="qsb__enable_overlay_small_log_switch", + # command=lambda: switchCallback(vrct_gui.qsb__enable_overlay_small_log_switch), + # variable=view_variable.VAR_ENABLE_OVERLAY_SMALL_LOG, + # ) + # self.qsb__enable_overlay_small_log.grid(row=row) + + + + row+=1 + def sliderCallback(e): + value = round(e,2) + callFunctionIfCallable(view_variable.CALLBACK_SET_OVERLAY_SMALL_LOG_SETTINGS, value, "x_pos") + view_variable.VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_X_POS.set(str(value)) + + self.qsb__overlay_small_log_settings_x_pos = createSettingBoxSlider( + for_var_label_text=view_variable.VAR_LABEL_OVERLAY_SMALL_LOG_X_POS, + for_var_current_value=view_variable.VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_X_POS, + slider_attr_name="qsb__overlay_small_log_settings_x_pos_slider", + slider_range=view_variable.SLIDER_RANGE_OVERLAY_SMALL_LOG_X_POS, + slider_number_of_steps=view_variable.NUMBER_OF_STEPS_OVERLAY_SMALL_LOG_X_POS, + command=sliderCallback, + variable=view_variable.VAR_OVERLAY_SMALL_LOG_X_POS, + ) + self.qsb__overlay_small_log_settings_x_pos.grid(row=row) + + + row+=1 + def sliderCallback(e): + value = round(e,2) + callFunctionIfCallable(view_variable.CALLBACK_SET_OVERLAY_SMALL_LOG_SETTINGS, value, "y_pos") + view_variable.VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_Y_POS.set(str(value)) + + self.qsb__overlay_small_log_settings_y_pos = createSettingBoxSlider( + for_var_label_text=view_variable.VAR_LABEL_OVERLAY_SMALL_LOG_Y_POS, + for_var_current_value=view_variable.VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_Y_POS, + slider_attr_name="qsb__overlay_small_log_settings_y_pos_slider", + slider_range=view_variable.SLIDER_RANGE_OVERLAY_SMALL_LOG_Y_POS, + slider_number_of_steps=view_variable.NUMBER_OF_STEPS_OVERLAY_SMALL_LOG_Y_POS, + command=sliderCallback, + variable=view_variable.VAR_OVERLAY_SMALL_LOG_Y_POS, + ) + self.qsb__overlay_small_log_settings_y_pos.grid(row=row) + + + row+=1 + def sliderCallback(e): + value = round(e,2) + callFunctionIfCallable(view_variable.CALLBACK_SET_OVERLAY_SMALL_LOG_SETTINGS, value, "depth") + view_variable.VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_DEPTH.set(str(value)) + + self.qsb__overlay_small_log_settings_depth = createSettingBoxSlider( + for_var_label_text=view_variable.VAR_LABEL_OVERLAY_SMALL_LOG_DEPTH, + for_var_current_value=view_variable.VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_DEPTH, + slider_attr_name="qsb__overlay_small_log_settings_depth_slider", + slider_range=view_variable.SLIDER_RANGE_OVERLAY_SMALL_LOG_DEPTH, + slider_number_of_steps=view_variable.NUMBER_OF_STEPS_OVERLAY_SMALL_LOG_DEPTH, + command=sliderCallback, + variable=view_variable.VAR_OVERLAY_SMALL_LOG_DEPTH, + ) + self.qsb__overlay_small_log_settings_depth.grid(row=row) + + + row+=1 + def sliderCallback(e): + value = int(e) + callFunctionIfCallable(view_variable.CALLBACK_SET_OVERLAY_SMALL_LOG_SETTINGS, value, "display_duration") + view_variable.VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_DISPLAY_DURATION.set(f"{value} second(s)") + + self.qsb__overlay_small_log_settings_display_duration = createSettingBoxSlider( + for_var_label_text=view_variable.VAR_LABEL_OVERLAY_SMALL_LOG_DISPLAY_DURATION, + for_var_current_value=view_variable.VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_DISPLAY_DURATION, + slider_attr_name="qsb__overlay_small_log_settings_display_duration_slider", + slider_range=view_variable.SLIDER_RANGE_OVERLAY_SMALL_LOG_DISPLAY_DURATION, + slider_number_of_steps=view_variable.NUMBER_OF_STEPS_OVERLAY_SMALL_LOG_DISPLAY_DURATION, + command=sliderCallback, + variable=view_variable.VAR_OVERLAY_SMALL_LOG_DISPLAY_DURATION, + ) + self.qsb__overlay_small_log_settings_display_duration.grid(row=row) + + + + row+=1 + def sliderCallback(e): + value = int(e) + callFunctionIfCallable(view_variable.CALLBACK_SET_OVERLAY_SMALL_LOG_SETTINGS, value, "fadeout_duration") + view_variable.VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_FADEOUT_DURATION.set(f"{value} second(s)") + + self.qsb__overlay_small_log_settings_fadeout_duration = createSettingBoxSlider( + for_var_label_text=view_variable.VAR_LABEL_OVERLAY_SMALL_LOG_FADEOUT_DURATION, + for_var_current_value=view_variable.VAR_CURRENT_VALUE_OVERLAY_SMALL_LOG_FADEOUT_DURATION, + slider_attr_name="qsb__overlay_small_log_settings_fadeout_duration_slider", + slider_range=view_variable.SLIDER_RANGE_OVERLAY_SMALL_LOG_FADEOUT_DURATION, + slider_number_of_steps=view_variable.NUMBER_OF_STEPS_OVERLAY_SMALL_LOG_FADEOUT_DURATION, + command=sliderCallback, + variable=view_variable.VAR_OVERLAY_SMALL_LOG_FADEOUT_DURATION, + ) + self.qsb__overlay_small_log_settings_fadeout_duration.grid(row=row) + + + + + + + + + + self.qsw_setting_box_bottom = CTkFrame(self.qsw_background, corner_radius=0, fg_color=self.settings.ctm.SB__BG_COLOR) + self.qsw_setting_box_bottom.grid(row=1, column=0, sticky="nsew") + + self.qsw_setting_box_bottom.grid_columnconfigure((0,2), weight=1) + self.qsw_setting_box_bottom.grid_rowconfigure((0,2), weight=1) + + self.qsw_setting_box_bottom_restore_default_button = CTkFrame(self.qsw_setting_box_bottom, corner_radius=0, fg_color=self.settings.ctm.SB__BG_COLOR) + self.qsw_setting_box_bottom_restore_default_button.grid(row=1, column=1, sticky="nsew") + + + def toDefaultOverlaySettingsCallback(_e): + callFunctionIfCallable(view_variable.CALLBACK_SET_TO_DEFAULT_OVERLAY_SETTINGS) + + + + (restore_default_settings_button, label_button_label_widget) = createLabelButton( + parent_widget=self.qsw_setting_box_bottom_restore_default_button, + 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=view_variable.VAR_TO_DEFAULT_OVERLAY_SETTINGS, + 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=toDefaultOverlaySettingsCallback, + + label_button_position="center", + ) + restore_default_settings_button.grid(row=0, column=0, pady=self.settings.uism.QSB__RESTORE_DEFAULT_SETTINGS_BUTTON_PADY) + + + def show(self): + self.attributes("-alpha", 0) + self.deiconify() + setGeometryToCenterOfScreen(root_widget=self) + fadeInAnimation(self, steps=5, interval=0.02) \ No newline at end of file diff --git a/vrct_gui/quick_settings_window/_CreateQuickSettingBox.py b/vrct_gui/quick_settings_window/_CreateQuickSettingBox.py new file mode 100644 index 00000000..e2bbe271 --- /dev/null +++ b/vrct_gui/quick_settings_window/_CreateQuickSettingBox.py @@ -0,0 +1,185 @@ +from typing import Union + +from utils import callFunctionIfCallable + +from customtkinter import CTkImage, CTkLabel, CTkToplevel, CTkProgressBar, CTkFrame, CTkSlider, CTkFont, CTkSwitch +from ..ui_utils import openImageKeepAspectRatio, getImageFileFromUiUtils, setGeometryToCenterOfScreen, fadeInAnimation + +class _CreateQuickSettingBox(): + def __init__(self, parent_frame, vrct_gui, settings, view_variable): + self.view_variable = view_variable + self.vrct_gui = vrct_gui + self.settings = settings + self.parent_frame = parent_frame + + + + + + + + + + def _createSettingBoxFrame(self, for_var_label_text=None, for_var_current_value=None): + setting_box_frame = CTkFrame(self.parent_frame, corner_radius=0, fg_color=self.settings.ctm.SB__BG_COLOR, width=0, height=0) + + setting_box_frame.grid(row=0, column=0, pady=(0,1), sticky="ew") + setting_box_frame.grid_columnconfigure(0, weight=1) + + + setting_box_frame_wrapper = CTkFrame(setting_box_frame, corner_radius=0, fg_color=self.settings.ctm.SB__BG_COLOR, width=0, height=0) + setting_box_frame_wrapper.grid(row=0, column=0, padx=self.settings.uism.QSB__IPADX, pady=self.settings.uism.QSB__IPADY, sticky="nsew") + setting_box_frame_wrapper.grid_columnconfigure(0, weight=1) + + + # Labels + setting_box_labels_frame = CTkFrame(setting_box_frame_wrapper, corner_radius=0, fg_color=self.settings.ctm.SB__BG_COLOR, width=0, height=0) + setting_box_labels_frame.grid(row=0, column=0, padx=0, pady=(0,self.settings.uism.QSB__LABEL_BOTTOM_PADY), sticky="nsew") + + setting_box_labels_frame.grid_rowconfigure((0,2), weight=1) + setting_box_labels_frame.grid_columnconfigure(1, weight=1) + setting_box_label = CTkLabel( + setting_box_labels_frame, + textvariable=for_var_label_text, + anchor="w", + height=0, + font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__LABEL_FONT_SIZE, weight="normal"), + text_color=self.settings.ctm.LABELS_TEXT_COLOR + ) + setting_box_label.grid(row=1, column=0, padx=0, pady=0, sticky="nse") + + + if for_var_current_value is not None: + setting_box_label = CTkLabel( + setting_box_labels_frame, + textvariable=for_var_current_value, + anchor="w", + height=0, + font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__LABEL_FONT_SIZE, weight="normal"), + text_color=self.settings.ctm.LABELS_TEXT_COLOR + ) + setting_box_label.grid(row=1, column=2, padx=0, pady=0, sticky="nsw") + + + + + + + + + # Items + setting_box_item_frame = CTkFrame(setting_box_frame_wrapper, corner_radius=0, width=0, height=0, fg_color=self.settings.ctm.SB__BG_COLOR) + setting_box_item_frame.grid(row=1, column=0, padx=0, sticky="nsew") + + setting_box_item_frame.grid_rowconfigure((0,2), weight=1) + setting_box_item_frame.grid_columnconfigure(1, weight=1) + + return (setting_box_frame, setting_box_item_frame) + + + + + + + + + + + + + + + + + def createSettingBoxSlider( + self, + for_var_label_text, + for_var_current_value, + slider_attr_name, + slider_range, + command, + variable, + slider_number_of_steps: Union[int, + None] = None, + slider_bind__ButtonPress=None, + slider_bind__ButtonRelease=None, + sliderTooltipFormatter=None, + ): + + (setting_box_frame, setting_box_item_frame) = self._createSettingBoxFrame(for_var_label_text, for_var_current_value) + + + + slider_widget = CTkSlider( + setting_box_item_frame, + width=self.settings.uism.SB__SLIDER_WIDTH, + height=self.settings.uism.SB__SLIDER_HEIGHT, + from_=slider_range[0], + to=slider_range[1], + number_of_steps=slider_number_of_steps, + fg_color=self.settings.ctm.SB__SLIDER_BG_COLOR, + progress_color=self.settings.ctm.SB__SLIDER_PROGRESS_BG_COLOR, + button_color=self.settings.ctm.SB__SLIDER_BUTTON_COLOR, + button_hover_color=self.settings.ctm.SB__SLIDER_BUTTON_HOVERED_COLOR, + command=command, + variable=variable, + ) + setattr(self.vrct_gui, slider_attr_name, slider_widget) + + + slider_widget.grid(row=1, column=1, sticky="ew") + + if slider_bind__ButtonPress is not None: + def adjusted_slider_bind__ButtonPress(e): + command(e) + slider_bind__ButtonPress() + slider_widget.configure(command=adjusted_slider_bind__ButtonPress) + + if slider_bind__ButtonRelease is not None: + def adjusted_slider_bind__ButtonRelease(_e): + slider_bind__ButtonRelease() + slider_widget.bind("", adjusted_slider_bind__ButtonRelease, "+") + + return setting_box_frame + + + + + + + + def createSettingBoxSwitch( + self, + for_var_label_text, + switch_attr_name, + variable, + command, + for_var_current_value=None, + ): + + (setting_box_frame, setting_box_item_frame) = self._createSettingBoxFrame(for_var_label_text, for_var_current_value) + + switch_widget = CTkSwitch( + setting_box_item_frame, + text=None, + height=0, + width=0, + corner_radius=int(self.settings.uism.SB__SWITCH_BOX_HEIGHT/2), + border_width=0, + switch_height=self.settings.uism.SB__SWITCH_BOX_HEIGHT, + switch_width=self.settings.uism.SB__SWITCH_BOX_WIDTH, + onvalue=True, + offvalue=False, + variable=variable, + command=command, + fg_color=self.settings.ctm.SB__SWITCH_BOX_BG_COLOR, + 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.vrct_gui, switch_attr_name, switch_widget) + + + switch_widget.grid(row=1, column=1, sticky="w") + + return setting_box_frame \ No newline at end of file diff --git a/vrct_gui/quick_settings_window/__init__.py b/vrct_gui/quick_settings_window/__init__.py new file mode 100644 index 00000000..4ee26014 --- /dev/null +++ b/vrct_gui/quick_settings_window/__init__.py @@ -0,0 +1 @@ +from .QuickSettingsWindow import * \ No newline at end of file diff --git a/vrct_gui/splash_window/SplashWindow.py b/vrct_gui/splash_window/SplashWindow.py index 3a1084c0..f1a186c4 100644 --- a/vrct_gui/splash_window/SplashWindow.py +++ b/vrct_gui/splash_window/SplashWindow.py @@ -2,7 +2,7 @@ import math import time from customtkinter import CTkImage, CTkLabel, CTkToplevel, CTkProgressBar, CTkFrame -from ..ui_utils import openImageKeepAspectRatio, getImageFileFromUiUtils, setGeometryToCenterOfScreen, fadeInAnimation +from ..ui_utils import openImageKeepAspectRatio, getImageFileFromUiUtils, getImagePath, setGeometryToCenterOfScreen, fadeInAnimation, generateGradientColor class SplashWindow(CTkToplevel): def __init__(self): @@ -10,8 +10,9 @@ class SplashWindow(CTkToplevel): self.withdraw() self.overrideredirect(True) self.configure(fg_color="#292a2d") - self.title("SplashWindow") - self.wm_attributes("-toolwindow", True) + self.title("Starting Up...") + self.after(200, lambda: self.iconbitmap(getImagePath("vrct_logo_mark_black.ico"))) + # self.wm_attributes("-toolwindow", True) self.is_showed_weight_download_progressbar = False @@ -200,25 +201,6 @@ class SplashWindow(CTkToplevel): rotated_image = image.rotate(angle, expand=True) return rotated_image - # This making gradient color process was made by ChatGPT. - def generateGradientColor(self, value): - # 0の時の色と1の時の色を指定 - color_start = [242, 242, 242] # RGB values for #f2f2f2 - color_end = [72, 164, 149] # RGB values for #48a495 - - # 補完色を計算 - interpolated_color = [ - int(start + (end - start) * value) for start, end in zip(color_start, color_end) - ] - - # RGB値を0から255の範囲にクリップ - interpolated_color = [max(0, min(255, val)) for val in interpolated_color] - - # RGBを16進数に変換 - hex_color = "#{:02x}{:02x}{:02x}".format(*interpolated_color) - - return hex_color - def updateDownloadProgress(self, progress:float): if self.is_showed_weight_download_progressbar is False: @@ -232,7 +214,11 @@ class SplashWindow(CTkToplevel): self.is_showed_weight_download_progressbar = True self.update() - progress_color = self.generateGradientColor(progress) + progress_color = generateGradientColor( + value=progress, + color_start=[242, 242, 242], # RGB values for #f2f2f2 + color_end=[72, 164, 149], # RGB values for #48a495 + ) self.weight_download_progressbar_widget.configure(progress_color=progress_color) self.weight_download_progressbar_widget.set(progress) self.update_idletasks() diff --git a/vrct_gui/ui_managers/AboutVrctManager.py b/vrct_gui/ui_managers/AboutVrctManager.py new file mode 100644 index 00000000..9e45db63 --- /dev/null +++ b/vrct_gui/ui_managers/AboutVrctManager.py @@ -0,0 +1,188 @@ +from types import SimpleNamespace + +from ..ui_utils import calculateUiSize, getImageFileFromUiUtils_AboutVrct, bindButtonReleaseFunction, createButtonWithImage, bindButtonFunctionAndColor +from customtkinter import CTkFrame, CTkLabel, CTkImage, CTkFont + +IMAGE_STANDARD_SCALING = 2 +class AboutVrctManager(): + def __init__(self, scaling_percentage, ui_language, ctm): + self.ctm = ctm + scaling_float = int(scaling_percentage.replace("%", "")) / 100 + self.SCALING_FLOAT = max(scaling_float, 0.4) + + self.uism = SimpleNamespace() + + self.uism.ABOUT_VRCT_CONTAINER_LEFT_PADX = self.dupTuple(self._calculateUiSize(32)) + + self.uism.SECTION_BOTTOM_PADY = self._calculateUiSize(22) + self.uism.PROJECT_LINKS_SECTION_BOTTOM_PADDING = self._calculateUiSize(18) # Exception pady + + self.uism.VRCHAT_DISCLAIMER_SECTION_TOP_PADDING = self._calculateUiSize(80) # Exception pady + + self.uism.THE_DEVELOPERS_SECTION_TITLE_BOTTOM_PADY = self._calculateUiSize(8) + self.uism.DEVS_CONTACTS_Y1 = self._calculateUiSize(118) + self.uism.DEVS_MISYA_X_X = self._calculateUiSize(269) + self.uism.DEVS_MISYA_GITHUB_X = self._calculateUiSize(297) + self.uism.DEVS_SHIINA_X_X = self._calculateUiSize(298) + + self.uism.PROJECT_LINK_BOTTOM_PADY = self._calculateUiSize(2) + self.uism.PROJECT_LINK_CORNER_RADIUS = self._calculateUiSize(4) + self.uism.PROJECT_LINK_CONTENTS_PADX = self._calculateUiSize(55) + self.uism.PROJECT_LINK_ITEM_IPADX = self._calculateUiSize(10) + self.uism.PROJECT_LINK_ITEM_IPADY = self._calculateUiSize(4) + + self.uism.CONTRIBUTORS_SECTION_TITLE_BOTTOM_PADY = self._calculateUiSize(10) + + self.uism.CONTRIBUTORS_CONTACTS_Y1 = self._calculateUiSize(66) + self.uism.CONTRIBUTORS_DONE_SAN_X_X = self._calculateUiSize(25) + self.uism.CONTRIBUTORS_IYA_X_X = self._calculateUiSize(281) + self.uism.CONTRIBUTORS_RERA_X_X = self._calculateUiSize(530) + self.uism.CONTRIBUTORS_RERA_GITHUB_X = self._calculateUiSize(554) + + self.uism.CONTRIBUTORS_CONTACTS_Y2 = self._calculateUiSize(170) + self.uism.CONTRIBUTORS_POPOSUKE_X_X = self._calculateUiSize(154) + self.uism.CONTRIBUTORS_KUMAGUMA_X_X = self._calculateUiSize(413) + + + self.uism.TELL_US_BUTTON_CORNER_RADIUS = self._calculateUiSize(6) + self.uism.TELL_US_BUTTON_PADX = self._calculateUiSize(8) + self.uism.TELL_US_BUTTON_PADY = self._calculateUiSize(8) + self.uism.TELL_US_BUTTON_BORDER_WIDTH = self._calculateUiSize(1) + + + self.uism.SPECIAL_THANKS_SECTION_TITLE_BOTTOM_PADY = self._calculateUiSize(6) + self.uism.SPECIAL_THANKS_MEMBERS_BOTTOM_PADY = self._calculateUiSize(4) + self.uism.SPECIAL_THANKS_MESSAGE_BOTTOM_PADY = self._calculateUiSize(0) + self.uism.SPECIAL_THANKS_MESSAGE_AND_YOU_BOTTOM_PADY = self._calculateUiSize(8) + + self.uism.POSTER_SHOWCASE_SECTION_TITLE_BOTTOM_PADY = self._calculateUiSize(6) + self.uism.POSTER_SHOWCASE_POSTER_IMAGES_BOTTOM_PADY = self._calculateUiSize(6) + self.uism.POSTER_SHOWCASE_WORLD_ITEM_BOTTOM_PADY = self._calculateUiSize(4) + self.uism.POSTER_SHOWCASE_WORLD_ITEM_IPADX = self._calculateUiSize(12) + self.uism.POSTER_SHOWCASE_WORLD_ITEM_IPADY = self._calculateUiSize(4) + self.uism.POSTER_SHOWCASE_WORLD_BOTTOM_PADY = self._calculateUiSize(4) + self.uism.POSTER_SHOWCASE_WORLD_CORNER_RADIUS = self._calculateUiSize(4) + self.uism.POSTER_SHOWCASE_WORLD_PAGINATION_BUTTON_BOTTOM_PADY = self._calculateUiSize(18) + self.uism.POSTER_TELL_US_MESSAGE_TOP_PADY = self._calculateUiSize(20) + self.uism.POSTER_CHANGE_BUTTON_CORNER_RADIUS = self._calculateUiSize(6) + + + self.image_file = SimpleNamespace() + + + if ui_language == "ja": + self.image_file.SPECIAL_THANKS_MESSAGE = "special_thanks_message_ja.png" + self.image_file.SPECIAL_THANKS_TELL_US_MESSAGE = "special_thanks_tell_us_message_ja.png" + + self.image_file.POSTER_IMAGES_AUTHOR = "poster_images_authors_ja.png" + self.image_file.POSTER_IMAGES_AUTHOR_M = "poster_images_authors_m_ja.png" + self.image_file.POSTER_TELL_US_MESSAGE = "poster_tell_us_message_ja.png" + else: + self.image_file.SPECIAL_THANKS_MESSAGE = "special_thanks_message_en.png" + self.image_file.SPECIAL_THANKS_TELL_US_MESSAGE = "special_thanks_tell_us_message_en.png" + + self.image_file.POSTER_IMAGES_AUTHOR = "poster_images_authors_en.png" + self.image_file.POSTER_IMAGES_AUTHOR_M = "poster_images_authors_m_en.png" + self.image_file.POSTER_TELL_US_MESSAGE = "poster_tell_us_message_en.png" + + + poster_showcase_pagination_button_image = getImageFileFromUiUtils_AboutVrct("poster_showcase_pagination_button.png") + self.image_file.POSTER_SHOWCASE_WORLD_PAGINATION_BUTTON = SimpleNamespace( + img = poster_showcase_pagination_button_image, + width = calculateUiSize( + default_size = int(poster_showcase_pagination_button_image.width / IMAGE_STANDARD_SCALING), + scaling_float=self.SCALING_FLOAT, + is_allowed_odd=True, + ), + height = calculateUiSize( + default_size = int(poster_showcase_pagination_button_image.height / IMAGE_STANDARD_SCALING), + scaling_float=self.SCALING_FLOAT, + is_allowed_odd=True, + ), + ) + + + poster_showcase_pagination_button_chato_image = getImageFileFromUiUtils_AboutVrct("poster_showcase_pagination_button_chato.png") + self.image_file.POSTER_SHOWCASE_WORLD_PAGINATION_BUTTON_CHATO = SimpleNamespace( + img = poster_showcase_pagination_button_chato_image, + width = calculateUiSize( + default_size = int(poster_showcase_pagination_button_chato_image.width / IMAGE_STANDARD_SCALING), + scaling_float=self.SCALING_FLOAT, + is_allowed_odd=True, + ), + height = calculateUiSize( + default_size = int(poster_showcase_pagination_button_chato_image.height / IMAGE_STANDARD_SCALING), + scaling_float=self.SCALING_FLOAT, + is_allowed_odd=True, + ), + ) + + + + + def _calculateUiSize(self, default_size, is_allowed_odd:bool=True, is_zero_allowed:bool=False): + size = calculateUiSize(default_size, self.SCALING_FLOAT, is_allowed_odd, is_zero_allowed) + return size + + + def embedImageCTkLabel(self, parent_frame, image_file_name, image_scaling=IMAGE_STANDARD_SCALING, directly_type:str=None, fg_color:str="transparent", anchor:str="w", rotate_angle:int=0): + + img = getImageFileFromUiUtils_AboutVrct(image_file_name, directly_type) + + image_width = calculateUiSize( + default_size = int(img.width / image_scaling), + scaling_float=self.SCALING_FLOAT, + is_allowed_odd=True, + ) + image_height = calculateUiSize( + default_size = int(img.height / image_scaling), + scaling_float=self.SCALING_FLOAT, + is_allowed_odd=True, + ) + + img_label = CTkLabel( + parent_frame, + text=None, + corner_radius=0, + height=image_height, + fg_color=fg_color, + anchor=anchor, + image=CTkImage((img).rotate(rotate_angle), size=(image_width, image_height)) + ) + + return img_label + + def embedImageButtonCTkLabel(self, parent_frame, image_file_name, callback, image_scaling=IMAGE_STANDARD_SCALING, directly_type:str=None, fg_color:str=None, hovered_color:str=None, clicked_color:str=None, anchor:str="w", corner_radius:int=0, no_bind:bool=False, rotate_angle:int=0): + + fg_color = self.ctm.ABOUT_VRCT_BG if fg_color is None else fg_color + + if hovered_color is None: + hovered_color = self.ctm.ABOUT_VRCT_BUTTON_HOVERED_BG_COLOR + if clicked_color is None: + clicked_color = self.ctm.ABOUT_VRCT_BUTTON_CLICKED_BG_COLOR + + img_label_frame = CTkFrame(parent_frame, fg_color=fg_color, corner_radius=corner_radius, width=0, height=0) + + img_label = self.embedImageCTkLabel(img_label_frame, image_file_name, image_scaling, directly_type, fg_color, anchor, rotate_angle) + + if no_bind is False: + img_label_frame.configure(cursor="hand2") + img_label.configure(cursor="hand2") + img_label._canvas.configure(cursor="hand2") + bindButtonFunctionAndColor( + target_widgets=[img_label_frame, img_label], + enter_color=hovered_color, + leave_color=fg_color, + clicked_color=clicked_color, + buttonReleasedFunction=callback, + ) + + img_label.grid() + img_label_frame.img_label = img_label + + return img_label_frame + + + @staticmethod + def dupTuple(value): + return (value, value) \ No newline at end of file diff --git a/vrct_gui/ui_managers/Themes/_darkTheme.py b/vrct_gui/ui_managers/Themes/_darkTheme.py index 71cbc292..c90a8e5f 100644 --- a/vrct_gui/ui_managers/Themes/_darkTheme.py +++ b/vrct_gui/ui_managers/Themes/_darkTheme.py @@ -110,6 +110,7 @@ def _darkTheme(base_color): TOP_BAR_BUTTON_BG_COLOR = base_color.DARK_888_COLOR, TOP_BAR_BUTTON_HOVERED_BG_COLOR = base_color.DARK_850_COLOR, TOP_BAR_BUTTON_CLICKED_BG_COLOR = base_color.DARK_900_COLOR, + TOP_BAR_BUTTON_TEXT_COLOR = base_color.DARK_BASIC_TEXT_COLOR, UPDATE_AVAILABLE_BUTTON_BG_COLOR = base_color.DARK_888_COLOR, UPDATE_AVAILABLE_BUTTON_HOVERED_BG_COLOR = base_color.DARK_850_COLOR, @@ -296,6 +297,16 @@ def _darkTheme(base_color): SB__ERROR_MESSAGE_BG_COLOR = "#bb4448", SB__SUCCESS_MESSAGE_BG_COLOR = "#368777", SB__ERROR_MESSAGE_TEXT_COLOR = "#fff", + + + + # About VRCT + ABOUT_VRCT_BG = base_color.DARK_950_COLOR, + ABOUT_VRCT_DEV_BG = base_color.DARK_888_COLOR, + ABOUT_VRCT_BUTTON_HOVERED_BG_COLOR = base_color.DARK_900_COLOR, + ABOUT_VRCT_BUTTON_CLICKED_BG_COLOR = base_color.DARK_925_COLOR, + + ABOUT_VRCT_TELL_US_BUTTON_BORDER_COLOR = base_color.DARK_888_COLOR, ), diff --git a/vrct_gui/ui_managers/Themes/_lightTheme.py b/vrct_gui/ui_managers/Themes/_lightTheme.py index fbaf1201..503be63a 100644 --- a/vrct_gui/ui_managers/Themes/_lightTheme.py +++ b/vrct_gui/ui_managers/Themes/_lightTheme.py @@ -110,6 +110,7 @@ def _lightTheme(base_color): TOP_BAR_BUTTON_BG_COLOR = base_color.LIGHT_175_COLOR, TOP_BAR_BUTTON_HOVERED_BG_COLOR = base_color.LIGHT_300_COLOR, TOP_BAR_BUTTON_CLICKED_BG_COLOR = base_color.LIGHT_350_COLOR, + TOP_BAR_BUTTON_TEXT_COLOR = base_color.LIGHT_BASIC_TEXT_COLOR, UPDATE_AVAILABLE_BUTTON_TEXT_COLOR = base_color.PRIMARY_400_COLOR, ), diff --git a/vrct_gui/ui_managers/UiScalingManager.py b/vrct_gui/ui_managers/UiScalingManager.py index 798f3412..b7e5f5e3 100644 --- a/vrct_gui/ui_managers/UiScalingManager.py +++ b/vrct_gui/ui_managers/UiScalingManager.py @@ -6,6 +6,7 @@ class UiScalingManager(): def __init__(self, scaling_percentage): scaling_float = int(scaling_percentage.replace("%", "")) / 100 self.SCALING_FLOAT = max(scaling_float, 0.4) + self.common = SimpleNamespace() self.main = SimpleNamespace() self.config_window = SimpleNamespace() @@ -191,6 +192,16 @@ class UiScalingManager(): # Dropdown Menu Window self.dropdown_menu_window.MARGIN_WIDTH = self._calculateUiSize(16) + + + # Quick Settings Box + self.config_window.QSB__IPADX = self._calculateUiSize(20) + self.config_window.QSB__IPADY = (self._calculateUiSize(14), self._calculateUiSize(8)) + self.config_window.QSB__LABEL_BOTTOM_PADY = self._calculateUiSize(6) + self.config_window.QSB__RESTORE_DEFAULT_SETTINGS_BUTTON_PADY = (self._calculateUiSize(40),0) + + + # Config Window self.config_window.DEFAULT_WIDTH = self._calculateUiSize(1080) self.config_window.DEFAULT_HEIGHT = self._calculateUiSize(680) @@ -222,7 +233,8 @@ class UiScalingManager(): # Side menu - self.config_window.SIDE_MENU_TOP_PADY = self._calculateUiSize(54) + self.config_window.SIDE_MENU_PADY = (self._calculateUiSize(54), self._calculateUiSize(72)) + self.config_window.SIDE_MENU_LABELS_SEPARATE_MIN_HEIGHT = self._calculateUiSize(40) self.config_window.SIDE_MENU_LABELS_IPADX = self._calculateUiSize(20) self.config_window.SIDE_MENU_LABELS_IPADY = self._calculateUiSize(8) self.config_window.SIDE_MENU_LABELS_FONT_SIZE = self._calculateUiSize(18) @@ -237,7 +249,10 @@ class UiScalingManager(): # Setting Box self.config_window.MAIN_AREA_MIN_WIDTH = self._calculateUiSize(720) - self.config_window.SB__TOP_PADY = (self._calculateUiSize(60)) + + self.config_window.ABOUT_VRCT_SB__TOP_PADY = self._calculateUiSize(42) # Exception + self.config_window.SB__TOP_PADY = self._calculateUiSize(60) + self.config_window.SB__IPADX = self._calculateUiSize(20) self.config_window.SB__IPADY = self._calculateUiSize(12) self.config_window.SB__BOTTOM_MARGIN = (0, self._calculateUiSize(60)) @@ -356,6 +371,8 @@ class UiScalingManager(): self.config_window.SB__AUTHKEY_WEBPAGE_PADX_BETWEEN_LABEL_AND_ICON = self._calculateUiSize(10) self.config_window.SB__AUTHKEY_WEBPAGE_BUTTON_TOP_PADY = self._calculateUiSize(10) + self.config_window.SB__OPEN_OVERLAY_SETTINGS_WINDOW = self._calculateUiSize(28) + self.config_window.SB__BUTTON_IPADXY = self._calculateUiSize(16) self.config_window.SB__BUTTON_ICON_SIZE = self._calculateUiSize(24) diff --git a/vrct_gui/ui_managers/__init__.py b/vrct_gui/ui_managers/__init__.py index 6304cb9e..06adde02 100644 --- a/vrct_gui/ui_managers/__init__.py +++ b/vrct_gui/ui_managers/__init__.py @@ -1,2 +1,4 @@ from .ColorThemeManager import ColorThemeManager -from .UiScalingManager import UiScalingManager \ No newline at end of file +from .UiScalingManager import UiScalingManager + +from .AboutVrctManager import AboutVrctManager \ No newline at end of file diff --git a/vrct_gui/ui_utils/ui_utils.py b/vrct_gui/ui_utils/ui_utils.py index 94f9adae..7af40855 100644 --- a/vrct_gui/ui_utils/ui_utils.py +++ b/vrct_gui/ui_utils/ui_utils.py @@ -1,6 +1,7 @@ from os import path as os_path from PIL.Image import open as Image_open, LANCZOS -from time import sleep +from time import sleep, time +import math from customtkinter import CTkFrame, CTkLabel, CTkImage, CTkFont @@ -10,8 +11,17 @@ def getImagePath(file_name): def getImageFileFromUiUtils(file_name): # root\img\file_name - img = Image_open(os_path.join(os_path.dirname(os_path.dirname(os_path.dirname(__file__))), "img", file_name)) - return img + return Image_open(os_path.join(os_path.dirname(os_path.dirname(os_path.dirname(__file__))), "img", file_name)) + +def getImageFileFromUiUtils_AboutVrct(file_name, directly_type:str=None): + # root\img\about_vrct\file_name or directly_name... + directly_path = ["img", "about_vrct"] + if directly_type == "showcased_worlds": + directly_path.append("showcased_worlds") + elif directly_type == "vrct_posters": + directly_path.append("vrct_posters") + + return Image_open(os_path.join(os_path.dirname(os_path.dirname(os_path.dirname(__file__))), *directly_path, file_name)) def openImageKeepAspectRatio(image_file, desired_width): wpercent = (desired_width/float(image_file.size[0])) @@ -54,12 +64,28 @@ def getLongestText_Dict(text_dict:dict): return longest_text def calculateUiSize(default_size, scaling_float, is_allowed_odd:bool=False, is_zero_allowed:bool=False): - size = int(default_size * scaling_float) - size += 1 if not is_allowed_odd and size % 2 != 0 else 0 - if size <= 0: - size = 0 if is_zero_allowed else 1 + size = int(default_size * scaling_float) + size += 1 if not is_allowed_odd and size % 2 != 0 else 0 + if size <= 0: + size = 0 if is_zero_allowed else 1 + + return size + +# This making gradient color process was made by ChatGPT. +def generateGradientColor(value, color_start, color_end): + # 補完色を計算 + interpolated_color = [ + int(start + (end - start) * value) for start, end in zip(color_start, color_end) + ] + + # RGB値を0から255の範囲にクリップ + interpolated_color = [max(0, min(255, val)) for val in interpolated_color] + + # RGBを16進数に変換 + hex_color = "#{:02x}{:02x}{:02x}".format(*interpolated_color) + + return hex_color - return size def bindEnterAndLeaveColor(target_widgets, enter_color, leave_color): for target_widget in target_widgets: @@ -372,4 +398,29 @@ def fadeInAnimation(root_widget, steps:int=10, interval:float=0.1, max_alpha:flo root_widget.update() sleep(interval) num += step_size - root_widget.attributes("-alpha", max_alpha) \ No newline at end of file + root_widget.attributes("-alpha", max_alpha) + + +def rotateImage(image, angle, to_expand=False): + rotated_image = image.rotate(angle, expand=to_expand) + return rotated_image + +def animateRotation(tk_root, img_frame, img, img_width, img_height, start_angle:int, goal_angle:int, duration=0.5, to_expand:bool=False): + start_time = time() + while True: + elapsed_time = time() - start_time + progress = min(elapsed_time / duration, 1.0) + eased_progress = 1 - math.pow(1 - progress, 4) + + angle = start_angle + (goal_angle - start_angle) * eased_progress + angle = -angle + + rotated_img = rotateImage(img, angle, to_expand) + img_frame.configure(image=CTkImage(rotated_img, size=(img_width, img_height))) + + tk_root.update() + + if elapsed_time >= duration: + break + + sleep(0.01) diff --git a/vrct_gui/updating_window/UpdatingWindow.py b/vrct_gui/updating_window/UpdatingWindow.py new file mode 100644 index 00000000..d84fd7dd --- /dev/null +++ b/vrct_gui/updating_window/UpdatingWindow.py @@ -0,0 +1,177 @@ +import math +import time + +from customtkinter import CTkImage, CTkLabel, CTkToplevel, CTkProgressBar, CTkFrame +from ..ui_utils import openImageKeepAspectRatio, getImageFileFromUiUtils, setGeometryToCenterOfScreen, fadeInAnimation, generateGradientColor, getImagePath + +class UpdatingWindow(CTkToplevel): + def __init__(self, vrct_gui): + super().__init__() + self.withdraw() + self.overrideredirect(True) + self.configure(fg_color="#292a2d") + self.title("Updating...") + self.after(200, lambda: self.iconbitmap(getImagePath("vrct_logo_mark_black.ico"))) + self.protocol("WM_DELETE_WINDOW", vrct_gui._quitVRCT) + # self.wm_attributes("-toolwindow", True) + self.is_showed_downloading_process = False + self.is_showed_unpackaging_process = False + BG_WIDTH= 300 + BG_HEIGHT= 350 + self.BG_HEX_COLOR = "#292a2d" + + self.grid_columnconfigure(0, weight=1) + self.grid_rowconfigure(0, weight=1) + self.updating_background = CTkFrame(self, corner_radius=0, fg_color=self.BG_HEX_COLOR, width=BG_WIDTH, height=BG_HEIGHT) + self.updating_background.grid() + + + self.PROGRESSBAR_HEIGHT = 2 + self.PROGRESSBAR_WIDTH = 240 + self.PROGRESSBAR_Y = 240 + self.PROGRESSBAR_X = 30 + + + + + self.downloading_unpackaging_d = getImageFileFromUiUtils("downloading_unpackaging_d.png") + self.downloading_unpackaging_d_label = CTkLabel( + self.updating_background, + text=None, + height=0, + fg_color=self.BG_HEX_COLOR, + image=CTkImage(self.downloading_unpackaging_d, size=(self.downloading_unpackaging_d.width, self.downloading_unpackaging_d.height)) + ) + + + self.downloading_unpackaging_u = getImageFileFromUiUtils("downloading_unpackaging_u.png") + self.downloading_unpackaging_u_label = CTkLabel( + self.updating_background, + text=None, + height=0, + fg_color=self.BG_HEX_COLOR, + image=CTkImage(self.downloading_unpackaging_u, size=(self.downloading_unpackaging_u.width, self.downloading_unpackaging_u.height)) + ) + + + + + + + + + self.unpackage_img = getImageFileFromUiUtils("unpackage_icon.png") + + self.unpackage_img_label = CTkLabel( + self.updating_background, + text=None, + height=0, + fg_color=self.BG_HEX_COLOR, + image=CTkImage(self.unpackage_img, size=(self.unpackage_img.width, self.unpackage_img.height)) + ) + + + + + + + + self.progressbar = CTkProgressBar( + self.updating_background, + height=self.PROGRESSBAR_HEIGHT, + width=self.PROGRESSBAR_WIDTH, + corner_radius=0, + fg_color=self.BG_HEX_COLOR, + progress_color=self.BG_HEX_COLOR, + ) + self.progressbar.set(0) + self.progressbar.place(x=self.PROGRESSBAR_X, y=self.PROGRESSBAR_Y, anchor="nw") + + + self.chato_delivering_img = getImageFileFromUiUtils("chato_delivering.png") + + self.chato_delivering_img_label = CTkLabel( + self.updating_background, + text=None, + height=0, + fg_color=self.BG_HEX_COLOR, + image=CTkImage(self.chato_delivering_img, size=(self.chato_delivering_img.width, self.chato_delivering_img.height)) + ) + self.chato_delivering_img_label.place(x=-30, y=self.PROGRESSBAR_Y - 1, anchor="s") + + + + self.chato_unpackaging_img = getImageFileFromUiUtils("chato_unpackaging.png") + + self.chato_unpackaging_img_label = CTkLabel( + self.updating_background, + text=None, + height=0, + fg_color=self.BG_HEX_COLOR, + image=CTkImage(self.chato_unpackaging_img, size=(self.chato_unpackaging_img.width, self.chato_unpackaging_img.height)) + ) + self.chato_unpackaging_img_label.place(x=-30, y=self.PROGRESSBAR_Y + self.PROGRESSBAR_HEIGHT + 1, anchor="n") + + + + + + + + self.vrct_update_process_img = getImageFileFromUiUtils("vrct_update_process.png") + + self.vrct_update_process_img_label = CTkLabel( + self.updating_background, + text=None, + height=0, + fg_color=self.BG_HEX_COLOR, + image=CTkImage(self.vrct_update_process_img, size=(self.vrct_update_process_img.width, self.vrct_update_process_img.height)) + ) + self.vrct_update_process_img_label.place(x=87, y=300, anchor="nw") + + + + + + + def updateDownloadProgress(self, progress:float, progress_type:str): + if progress_type == "downloading": + if self.is_showed_downloading_process is False: + self.downloading_unpackaging_d_label.place(x=50, y=56, anchor="nw") + self.is_showed_downloading_process = True + + fg_color = generateGradientColor( + value=progress, + color_start=[242, 242, 242], # RGB values for #f2f2f2 + color_end=[72, 164, 149], # RGB values for #48a495 + ) + self.progressbar.configure(fg_color=fg_color) + + chato_x = self.PROGRESSBAR_X + (progress * self.PROGRESSBAR_WIDTH) + self.chato_delivering_img_label.place(x=chato_x) + self.progressbar.set(progress) + self.update_idletasks() + + elif progress_type == "extracting": + if self.is_showed_unpackaging_process is False: + self.chato_delivering_img_label.place_forget() + self.downloading_unpackaging_u_label.place(x=50, y=56, anchor="nw") + self.unpackage_img_label.place(x=130, y=174, anchor="nw") + self.progressbar.configure(fg_color=self.BG_HEX_COLOR, progress_color="#4B4C4F") + self.is_showed_unpackaging_process = True + + chato_x = (self.PROGRESSBAR_X - 3) + (self.PROGRESSBAR_WIDTH - (progress * self.PROGRESSBAR_WIDTH)) + self.chato_unpackaging_img_label.place(x=chato_x) + self.progressbar.set(1 - progress) + self.update_idletasks() + + + def showUpdatingWindow(self): + self.attributes("-alpha", 0) + self.deiconify() + setGeometryToCenterOfScreen(root_widget=self) + fadeInAnimation(self, steps=5, interval=0.02) + + + def destroyUpdatingWindow(self): + self.destroy() \ No newline at end of file diff --git a/vrct_gui/updating_window/__init__.py b/vrct_gui/updating_window/__init__.py new file mode 100644 index 00000000..feed896e --- /dev/null +++ b/vrct_gui/updating_window/__init__.py @@ -0,0 +1 @@ +from .UpdatingWindow import UpdatingWindow \ No newline at end of file diff --git a/vrct_gui/vrct_gui.py b/vrct_gui/vrct_gui.py index 0a795436..d234fd3e 100644 --- a/vrct_gui/vrct_gui.py +++ b/vrct_gui/vrct_gui.py @@ -2,6 +2,8 @@ from customtkinter import CTk, CTkImage from ._CreateSelectableLanguagesWindow import _CreateSelectableLanguagesWindow +from .updating_window import UpdatingWindow + from ._CreateWindowCover import _CreateWindowCover from ._CreateNotificationWindow import _CreateNotificationWindow from ._CreateDropdownMenuWindow import _CreateDropdownMenuWindow @@ -12,6 +14,7 @@ from ._PrintToTextbox import _PrintToTextbox from .main_window import createMainWindowWidgets from .config_window import ConfigWindow +# from .quick_settings_window import QuickSettingsWindow from .ui_utils import setDefaultActiveTab, setGeometryToCenterOfScreen, fadeInAnimation from utils import callFunctionIfCallable @@ -128,6 +131,13 @@ class VRCT_GUI(CTk): view_variable=self._view_variable ) + # self.quick_settings_window = QuickSettingsWindow( + # vrct_gui=self, + # settings=self.settings.config_window, + # view_variable=self._view_variable + # ) + # self.quick_settings_window.show() + self.selectable_languages_window = _CreateSelectableLanguagesWindow( vrct_gui=self, settings=self.settings.selectable_language_window, @@ -173,6 +183,8 @@ class VRCT_GUI(CTk): init_scaling=(self._view_variable.VAR_TEXTBOX_UI_SCALING.get()/100) ) + self.updating_window = UpdatingWindow(vrct_gui=self) +