Merge branch 'develop'

This commit is contained in:
misyaguziya
2024-02-15 23:27:24 +09:00
32 changed files with 1301 additions and 273 deletions

2
.gitignore vendored
View File

@@ -6,7 +6,7 @@ VRCT.spec
*.pyc *.pyc
logs/ logs/
.venv/ .venv/
weight/ weights/
.vscode .vscode
error.log error.log
*.exe *.exe

View File

@@ -1,2 +1,2 @@
pyinstaller --windowed --clean --noconfirm --icon="./img/vrct_logo_mark_black.ico" --add-data "./img;img/" --add-data "./locales;locales/" --add-data "./batch;batch/" --name VRCT --add-data ".venv\Lib\site-packages\customtkinter;customtkinter/" --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 "./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
"C:\Program Files (x86)\NSIS\makensis.exe" installer/installer.nsi "C:\Program Files (x86)\NSIS\makensis.exe" installer/installer.nsi

142
config.py
View File

@@ -39,12 +39,8 @@ class Config:
return self._VERSION return self._VERSION
@property @property
def ENABLE_SPEAKER2CHATBOX(self): def ENABLE_SPEAKER2CHATBOX_PASS_CONFIRMATION(self):
return self._ENABLE_SPEAKER2CHATBOX return self._ENABLE_SPEAKER2CHATBOX_PASS_CONFIRMATION
@property
def ENABLE_SPEAKER2CHATBOX(self):
return self._ENABLE_SPEAKER2CHATBOX
@property @property
def PATH_LOCAL(self): def PATH_LOCAL(self):
@@ -70,6 +66,10 @@ class Config:
def DOCUMENTS_URL(self): def DOCUMENTS_URL(self):
return self._DOCUMENTS_URL return self._DOCUMENTS_URL
@property
def DEEPL_AUTH_KEY_PAGE_URL(self):
return self._DEEPL_AUTH_KEY_PAGE_URL
@property @property
def TRANSPARENCY_RANGE(self): def TRANSPARENCY_RANGE(self):
return self._TRANSPARENCY_RANGE return self._TRANSPARENCY_RANGE
@@ -98,6 +98,10 @@ class Config:
def SELECTABLE_CTRANSLATE2_WEIGHT_TYPE_DICT(self): def SELECTABLE_CTRANSLATE2_WEIGHT_TYPE_DICT(self):
return self._SELECTABLE_CTRANSLATE2_WEIGHT_TYPE_DICT return self._SELECTABLE_CTRANSLATE2_WEIGHT_TYPE_DICT
@property
def SELECTABLE_WHISPER_WEIGHT_TYPE_DICT(self):
return self._SELECTABLE_WHISPER_WEIGHT_TYPE_DICT
@property @property
def MAX_MIC_ENERGY_THRESHOLD(self): def MAX_MIC_ENERGY_THRESHOLD(self):
return self._MAX_MIC_ENERGY_THRESHOLD return self._MAX_MIC_ENERGY_THRESHOLD
@@ -107,6 +111,15 @@ class Config:
return self._MAX_SPEAKER_ENERGY_THRESHOLD return self._MAX_SPEAKER_ENERGY_THRESHOLD
# Read Write # Read Write
@property
def ENABLE_SPEAKER2CHATBOX(self):
return self._ENABLE_SPEAKER2CHATBOX
@ENABLE_SPEAKER2CHATBOX.setter
def ENABLE_SPEAKER2CHATBOX(self, value):
if isinstance(value, bool):
self._ENABLE_SPEAKER2CHATBOX = value
@property @property
def ENABLE_TRANSLATION(self): def ENABLE_TRANSLATION(self):
return self._ENABLE_TRANSLATION return self._ENABLE_TRANSLATION
@@ -197,6 +210,24 @@ class Config:
if value in list(translation_lang.keys()): if value in list(translation_lang.keys()):
self._CHOICE_OUTPUT_TRANSLATOR = value self._CHOICE_OUTPUT_TRANSLATOR = value
@property
def SENT_MESSAGES_LOG(self):
return self._SENT_MESSAGES_LOG
@SENT_MESSAGES_LOG.setter
def SENT_MESSAGES_LOG(self, value):
if isinstance(value, list):
self._SENT_MESSAGES_LOG = value
@property
def CURRENT_SENT_MESSAGES_LOG_INDEX(self):
return self._CURRENT_SENT_MESSAGES_LOG_INDEX
@CURRENT_SENT_MESSAGES_LOG_INDEX.setter
def CURRENT_SENT_MESSAGES_LOG_INDEX(self, value):
if isinstance(value, int):
self._CURRENT_SENT_MESSAGES_LOG_INDEX = value
@property @property
def IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION(self): def IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION(self):
return self._IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION return self._IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION
@@ -206,6 +237,15 @@ class Config:
if isinstance(value, bool): if isinstance(value, bool):
self._IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION = value self._IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION = value
@property
def IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER(self):
return self._IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER
@IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER.setter
def IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER(self, value):
if isinstance(value, bool):
self._IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER = value
# Save Json Data # Save Json Data
## Main Window ## Main Window
@property @property
@@ -263,6 +303,17 @@ class Config:
self._SELECTED_TAB_TARGET_LANGUAGES = value self._SELECTED_TAB_TARGET_LANGUAGES = value
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value) saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property
@json_serializable('SELECTED_TRANSCRIPTION_ENGINE')
def SELECTED_TRANSCRIPTION_ENGINE(self):
return self._SELECTED_TRANSCRIPTION_ENGINE
@SELECTED_TRANSCRIPTION_ENGINE.setter
def SELECTED_TRANSCRIPTION_ENGINE(self, value):
if isinstance(value, str):
self._SELECTED_TRANSCRIPTION_ENGINE = value
# saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property @property
@json_serializable('IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE') @json_serializable('IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE')
def IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE(self): def IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE(self):
@@ -569,15 +620,37 @@ class Config:
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value) saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property @property
@json_serializable('WEIGHT_TYPE') @json_serializable('USE_WHISPER_FEATURE')
def WEIGHT_TYPE(self): def USE_WHISPER_FEATURE(self):
return self._WEIGHT_TYPE return self._USE_WHISPER_FEATURE
@WEIGHT_TYPE.setter @USE_WHISPER_FEATURE.setter
def WEIGHT_TYPE(self, value): def USE_WHISPER_FEATURE(self, value):
if isinstance(value, bool):
self._USE_WHISPER_FEATURE = value
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property
@json_serializable('CTRANSLATE2_WEIGHT_TYPE')
def CTRANSLATE2_WEIGHT_TYPE(self):
return self._CTRANSLATE2_WEIGHT_TYPE
@CTRANSLATE2_WEIGHT_TYPE.setter
def CTRANSLATE2_WEIGHT_TYPE(self, value):
# if isinstance(value, str) and value in self.SELECTABLE_CTRANSLATE2_WEIGHT_TYPE_DICT: # if isinstance(value, str) and value in self.SELECTABLE_CTRANSLATE2_WEIGHT_TYPE_DICT:
if isinstance(value, str): if isinstance(value, str):
self._WEIGHT_TYPE = value self._CTRANSLATE2_WEIGHT_TYPE = value
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property
@json_serializable('WHISPER_WEIGHT_TYPE')
def WHISPER_WEIGHT_TYPE(self):
return self._WHISPER_WEIGHT_TYPE
@WHISPER_WEIGHT_TYPE.setter
def WHISPER_WEIGHT_TYPE(self, value):
if isinstance(value, str):
self._WHISPER_WEIGHT_TYPE = value
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value) saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property @property
@@ -687,9 +760,18 @@ class Config:
self._RECEIVED_MESSAGE_FORMAT_WITH_T = value self._RECEIVED_MESSAGE_FORMAT_WITH_T = value
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value) saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
# Speaker2Chatbox------------------ # Speaker2Chatbox------------------
@property
@json_serializable('ENABLE_SPEAKER2CHATBOX_PASS')
def ENABLE_SPEAKER2CHATBOX_PASS(self):
return self._ENABLE_SPEAKER2CHATBOX_PASS
@ENABLE_SPEAKER2CHATBOX_PASS.setter
def ENABLE_SPEAKER2CHATBOX_PASS(self, value):
if isinstance(value, str):
self._ENABLE_SPEAKER2CHATBOX_PASS = value
saveJson(self.PATH_CONFIG, inspect.currentframe().f_code.co_name, value)
@property @property
@json_serializable('ENABLE_SEND_RECEIVED_MESSAGE_TO_VRC') @json_serializable('ENABLE_SEND_RECEIVED_MESSAGE_TO_VRC')
def ENABLE_SEND_RECEIVED_MESSAGE_TO_VRC(self): def ENABLE_SEND_RECEIVED_MESSAGE_TO_VRC(self):
@@ -731,8 +813,9 @@ class Config:
def init_config(self): def init_config(self):
# Read Only # Read Only
self._VERSION = "2.1.1" self._VERSION = "2.2.0"
self._ENABLE_SPEAKER2CHATBOX = False # Speaker2Chatbox self._ENABLE_SPEAKER2CHATBOX = False # Speaker2Chatbox
self._ENABLE_SPEAKER2CHATBOX_PASS_CONFIRMATION = "123456789"
self._PATH_LOCAL = os_path.dirname(sys.argv[0]) self._PATH_LOCAL = os_path.dirname(sys.argv[0])
self._PATH_CONFIG = os_path.join(self._PATH_LOCAL, "config.json") self._PATH_CONFIG = os_path.join(self._PATH_LOCAL, "config.json")
self._PATH_LOGS = os_path.join(self._PATH_LOCAL, "logs") self._PATH_LOGS = os_path.join(self._PATH_LOCAL, "logs")
@@ -740,6 +823,7 @@ class Config:
self._GITHUB_URL = "https://api.github.com/repos/misyaguziya/VRCT/releases/latest" self._GITHUB_URL = "https://api.github.com/repos/misyaguziya/VRCT/releases/latest"
self._BOOTH_URL = "https://misyaguziya.booth.pm/" self._BOOTH_URL = "https://misyaguziya.booth.pm/"
self._DOCUMENTS_URL = "https://mzsoftware.notion.site/VRCT-Documents-be79b7a165f64442ad8f326d86c22246" self._DOCUMENTS_URL = "https://mzsoftware.notion.site/VRCT-Documents-be79b7a165f64442ad8f326d86c22246"
self._DEEPL_AUTH_KEY_PAGE_URL = "https://www.deepl.com/ja/account/summary"
self._TRANSPARENCY_RANGE = (50, 100) self._TRANSPARENCY_RANGE = (50, 100)
self._APPEARANCE_THEME_LIST = ["Light", "Dark", "System"] self._APPEARANCE_THEME_LIST = ["Light", "Dark", "System"]
self._UI_SCALING_LIST = generatePercentageStringsList(start=40, end=200, step=10) self._UI_SCALING_LIST = generatePercentageStringsList(start=40, end=200, step=10)
@@ -756,6 +840,18 @@ class Config:
"Small": "Small", "Small": "Small",
"Large": "Large", "Large": "Large",
} }
self._SELECTABLE_WHISPER_WEIGHT_TYPE_DICT = {
# {Save json str}: {i18n_placeholder} pairs
"tiny": "tiny",
"base": "base",
"small": "small",
"medium": "medium",
"large-v1": "large-v1",
"large-v2": "large-v2",
"large-v3": "large-v3",
}
self._MAX_MIC_ENERGY_THRESHOLD = 2000 self._MAX_MIC_ENERGY_THRESHOLD = 2000
self._MAX_SPEAKER_ENERGY_THRESHOLD = 4000 self._MAX_SPEAKER_ENERGY_THRESHOLD = 4000
@@ -770,7 +866,10 @@ class Config:
self._SOURCE_COUNTRY = "Japan" self._SOURCE_COUNTRY = "Japan"
self._TARGET_LANGUAGE = "English" self._TARGET_LANGUAGE = "English"
self._TARGET_COUNTRY = "United States" self._TARGET_COUNTRY = "United States"
self._SENT_MESSAGES_LOG = []
self._CURRENT_SENT_MESSAGES_LOG_INDEX = 0
self._IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION = False self._IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION = False
self._IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER = False
# Save Json Data # Save Json Data
## Main Window ## Main Window
@@ -795,6 +894,7 @@ class Config:
"2":"English\n(United States)", "2":"English\n(United States)",
"3":"English\n(United States)", "3":"English\n(United States)",
} }
self._SELECTED_TRANSCRIPTION_ENGINE = "Google"
self._IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE = False self._IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE = False
## Config Window ## Config Window
@@ -810,7 +910,7 @@ class Config:
"x_pos": "0", "x_pos": "0",
"y_pos": "0", "y_pos": "0",
"width": "870", "width": "870",
"height": "640", "height": "654",
} }
self._CHOICE_MIC_HOST = getDefaultInputDevice()["host"]["name"] self._CHOICE_MIC_HOST = getDefaultInputDevice()["host"]["name"]
self._CHOICE_MIC_DEVICE = getDefaultInputDevice()["device"]["name"] self._CHOICE_MIC_DEVICE = getDefaultInputDevice()["device"]["name"]
@@ -831,7 +931,9 @@ class Config:
"DeepL_API": None, "DeepL_API": None,
} }
self._USE_TRANSLATION_FEATURE = True self._USE_TRANSLATION_FEATURE = True
self._WEIGHT_TYPE = "Small" self._CTRANSLATE2_WEIGHT_TYPE = "Small"
self._USE_WHISPER_FEATURE = False
self._WHISPER_WEIGHT_TYPE = "base"
self._SEND_MESSAGE_FORMAT = "[message]" self._SEND_MESSAGE_FORMAT = "[message]"
self._SEND_MESSAGE_FORMAT_WITH_T = "[message]([translation])" self._SEND_MESSAGE_FORMAT_WITH_T = "[message]([translation])"
self._RECEIVED_MESSAGE_FORMAT = "[message]" self._RECEIVED_MESSAGE_FORMAT = "[message]"
@@ -842,6 +944,7 @@ class Config:
self._ENABLE_NOTICE_XSOVERLAY = False self._ENABLE_NOTICE_XSOVERLAY = False
self._ENABLE_SEND_MESSAGE_TO_VRC = True self._ENABLE_SEND_MESSAGE_TO_VRC = True
self._ENABLE_SEND_RECEIVED_MESSAGE_TO_VRC = False # Speaker2Chatbox self._ENABLE_SEND_RECEIVED_MESSAGE_TO_VRC = False # Speaker2Chatbox
self._ENABLE_SPEAKER2CHATBOX_PASS = "000000000"
self._ENABLE_LOGGER = False self._ENABLE_LOGGER = False
self._IS_CONFIG_WINDOW_COMPACT_MODE = False self._IS_CONFIG_WINDOW_COMPACT_MODE = False
@@ -855,6 +958,11 @@ class Config:
if key == "MESSAGE_FORMAT": if key == "MESSAGE_FORMAT":
old_message_format = config[key] old_message_format = config[key]
setattr(self, key, config[key]) setattr(self, key, config[key])
if key == "ENABLE_SPEAKER2CHATBOX_PASS":
if self.ENABLE_SPEAKER2CHATBOX_PASS_CONFIRMATION == config[key]:
self.ENABLE_SPEAKER2CHATBOX = True
if old_message_format is not None: if old_message_format is not None:
setattr(self, "SEND_MESSAGE_FORMAT_WITH_T", old_message_format) setattr(self, "SEND_MESSAGE_FORMAT_WITH_T", old_message_format)

View File

@@ -64,6 +64,7 @@ def changeToCTranslate2Process():
# func transcription send message # func transcription send message
def sendMicMessage(message): def sendMicMessage(message):
if len(message) > 0: if len(message) > 0:
addSentMessageLog(message)
translation = "" translation = ""
if model.checkKeywords(message): if model.checkKeywords(message):
view.printToTextbox_DetectedByWordFilter(detected_message=message) view.printToTextbox_DetectedByWordFilter(detected_message=message)
@@ -200,6 +201,7 @@ def stopThreadingTranscriptionReceiveMessageOnOpenConfigWindow():
# func message box # func message box
def sendChatMessage(message): def sendChatMessage(message):
if len(message) > 0: if len(message) > 0:
addSentMessageLog(message)
translation = "" translation = ""
if config.ENABLE_TRANSLATION is False: if config.ENABLE_TRANSLATION is False:
pass pass
@@ -249,6 +251,29 @@ def messageBoxFocusOut(e):
if config.ENABLE_SEND_MESSAGE_TO_VRC is True: if config.ENABLE_SEND_MESSAGE_TO_VRC is True:
model.oscStopSendTyping() model.oscStopSendTyping()
def addSentMessageLog(sent_message):
config.SENT_MESSAGES_LOG.append(sent_message)
config.CURRENT_SENT_MESSAGES_LOG_INDEX = len(config.SENT_MESSAGES_LOG)
def updateMessageBox(index_offset):
if len(config.SENT_MESSAGES_LOG) == 0:
return
try:
new_index = config.CURRENT_SENT_MESSAGES_LOG_INDEX + index_offset
target_message_text = config.SENT_MESSAGES_LOG[new_index]
view.replaceMessageBox(target_message_text)
config.CURRENT_SENT_MESSAGES_LOG_INDEX = new_index
except IndexError:
pass
def messageBoxUpKeyPress():
if config.CURRENT_SENT_MESSAGES_LOG_INDEX > 0:
updateMessageBox(-1)
def messageBoxDownKeyPress():
if config.CURRENT_SENT_MESSAGES_LOG_INDEX < len(config.SENT_MESSAGES_LOG) - 1:
updateMessageBox(1)
def updateTranslationEngineAndEngineList(): def updateTranslationEngineAndEngineList():
engine = config.CHOICE_INPUT_TRANSLATOR engine = config.CHOICE_INPUT_TRANSLATOR
engines = model.findTranslationEngines(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE) engines = model.findTranslationEngines(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE)
@@ -352,8 +377,10 @@ def callbackSelectedTranslationEngine(selected_translation_engine):
def callbackToggleTranslation(is_turned_on): def callbackToggleTranslation(is_turned_on):
config.ENABLE_TRANSLATION = is_turned_on config.ENABLE_TRANSLATION = is_turned_on
if config.ENABLE_TRANSLATION is True: if config.ENABLE_TRANSLATION is True:
model.changeTranslatorCTranslate2Model()
view.printToTextbox_enableTranslation() view.printToTextbox_enableTranslation()
else: else:
model.clearTranslatorCTranslate2Model()
view.printToTextbox_disableTranslation() view.printToTextbox_disableTranslation()
def callbackToggleTranscriptionSend(is_turned_on): def callbackToggleTranscriptionSend(is_turned_on):
@@ -505,8 +532,8 @@ def callbackSetUseTranslationFeature(value):
def callbackSetCtranslate2WeightType(value): def callbackSetCtranslate2WeightType(value):
print("callbackSetCtranslate2WeightType", value) print("callbackSetCtranslate2WeightType", value)
config.WEIGHT_TYPE = str(value) config.CTRANSLATE2_WEIGHT_TYPE = str(value)
view.updateSelectedCtranslate2WeightType(config.WEIGHT_TYPE) view.updateSelectedCtranslate2WeightType(config.CTRANSLATE2_WEIGHT_TYPE)
view.setWidgetsStatus_changeWeightType_Pending() view.setWidgetsStatus_changeWeightType_Pending()
if model.checkCTranslatorCTranslate2ModelWeight(): if model.checkCTranslatorCTranslate2ModelWeight():
config.IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION = False config.IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION = False
@@ -767,6 +794,35 @@ def callbackSetSpeakerMaxPhrases(value):
except Exception: except Exception:
view.showErrorMessage_SpeakerMaxPhrases() view.showErrorMessage_SpeakerMaxPhrases()
# Transcription (Internal AI Model)
def callbackSetUserWhisperFeature(value):
print("callbackSetUserWhisperFeature", value)
config.USE_WHISPER_FEATURE = value
if config.USE_WHISPER_FEATURE is True:
view.openWhisperWeightTypeWidget()
if model.checkTranscriptionWhisperModelWeight() is True:
config.IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER = False
config.SELECTED_TRANSCRIPTION_ENGINE = "Whisper"
else:
config.IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER = True
config.SELECTED_TRANSCRIPTION_ENGINE = "Google"
else:
view.closeWhisperWeightTypeWidget()
config.IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER = False
config.SELECTED_TRANSCRIPTION_ENGINE = "Google"
view.showRestartButtonIfRequired()
def callbackSetWhisperWeightType(value):
print("callbackSetWhisperWeightType", value)
config.WHISPER_WEIGHT_TYPE = str(value)
view.updateSelectedWhisperWeightType(config.WHISPER_WEIGHT_TYPE)
if model.checkTranscriptionWhisperModelWeight() is True:
config.IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER = False
config.SELECTED_TRANSCRIPTION_ENGINE = "Whisper"
else:
config.IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER = True
config.SELECTED_TRANSCRIPTION_ENGINE = "Google"
view.showRestartButtonIfRequired()
# Others Tab # Others Tab
def callbackSetEnableAutoClearMessageBox(value): def callbackSetEnableAutoClearMessageBox(value):
@@ -898,11 +954,16 @@ def createMainWindow(splash):
# set Translation Engine # set Translation Engine
updateTranslationEngineAndEngineList() updateTranslationEngineAndEngineList()
# set Transcription Engine
if config.USE_WHISPER_FEATURE is True:
config.SELECTED_TRANSCRIPTION_ENGINE = "Whisper"
else:
config.SELECTED_TRANSCRIPTION_ENGINE = "Google"
# set word filter # set word filter
model.addKeywords() model.addKeywords()
# check Software Updated # check Software Updated
if config.ENABLE_SPEAKER2CHATBOX is False:
if model.checkSoftwareUpdated() is True: if model.checkSoftwareUpdated() is True:
view.showUpdateAvailableButton() view.showUpdateAvailableButton()
@@ -949,6 +1010,8 @@ def createMainWindow(splash):
"message_box_bind_Any_KeyPress": messageBoxPressKeyAny, "message_box_bind_Any_KeyPress": messageBoxPressKeyAny,
"message_box_bind_FocusIn": messageBoxFocusIn, "message_box_bind_FocusIn": messageBoxFocusIn,
"message_box_bind_FocusOut": messageBoxFocusOut, "message_box_bind_FocusOut": messageBoxFocusOut,
"message_box_bind_Up_KeyPress": messageBoxUpKeyPress,
"message_box_bind_Down_KeyPress": messageBoxDownKeyPress,
}, },
config_window_registers={ config_window_registers={
@@ -993,6 +1056,10 @@ def createMainWindow(splash):
"callback_set_speaker_phrase_timeout": callbackSetSpeakerPhraseTimeout, "callback_set_speaker_phrase_timeout": callbackSetSpeakerPhraseTimeout,
"callback_set_speaker_max_phrases": callbackSetSpeakerMaxPhrases, "callback_set_speaker_max_phrases": callbackSetSpeakerMaxPhrases,
# Transcription Tab (Internal AI Model)
"callback_set_use_whisper_feature": callbackSetUserWhisperFeature,
"callback_set_whisper_weight_type": callbackSetWhisperWeightType,
# Others Tab # Others Tab
"callback_set_enable_auto_clear_chatbox": callbackSetEnableAutoClearMessageBox, "callback_set_enable_auto_clear_chatbox": callbackSetEnableAutoClearMessageBox,
"callback_set_send_only_translated_messages": callbackSetEnableSendOnlyTranslatedMessages, "callback_set_send_only_translated_messages": callbackSetEnableSendOnlyTranslatedMessages,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 30 KiB

BIN
img/link_icon_black.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

BIN
img/link_icon_white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

View File

@@ -1,4 +1,2 @@
python.exe -m pip install --upgrade pip python.exe -m pip install --upgrade pip
pip install -r requirements.txt pip install -r requirements.txt
pip install git+https://github.com/misyaguziya/translators
pip install git+https://github.com/misyaguziya/custom_speech_recognition

View File

@@ -79,9 +79,10 @@ config_window:
transcription: Transcription transcription: Transcription
transcription_mic: Mic transcription_mic: Mic
transcription_speaker: Speaker transcription_speaker: Speaker
transcription_internal_model: Transcription Model
others: Others others: Others
others_send_message_formats: Message Formats (Send) others_send_message_formats: Message Formats (Send)
others_received_message_formats: Message Formats (Received) others_received_message_formats: Message Formats (XSOverlay & Speaker2Chatbox)
others_speaker2chatbox: Speaker2Chatbox others_speaker2chatbox: Speaker2Chatbox
advanced_settings: Advanced Settings advanced_settings: Advanced Settings
@@ -128,6 +129,7 @@ config_window:
deepl_auth_key: deepl_auth_key:
label: DeepL Auth Key label: DeepL Auth Key
desc: Please select %{translator} on the main screen with DeepL_API when using. ※Some languages may not be supported. desc: Please select %{translator} on the main screen with DeepL_API when using. ※Some languages may not be supported.
open_auth_key_webpage: Open DeepL Account Webpage
mic_host: mic_host:
label: Mic Host/Driver label: Mic Host/Driver
@@ -187,6 +189,15 @@ config_window:
desc: It is the lower limit for the number of transcribed words, and only when this number is exceeded will the transcription results be displayed logs. desc: It is the lower limit for the number of transcribed words, and only when this number is exceeded will the transcription results be displayed logs.
error_message: You can set a number equal to or greater than 0. error_message: You can set a number equal to or greater than 0.
use_whisper_feature:
label: Use Whisper Model As Transcription
desc: In some languages, the accuracy of speech recognition may improve. During speech recognition usage, CPU usage increases, so please consider your PC specs before using this feature.
whisper_weight_type:
label: Select Whisper Model
desc: "Generally, models with larger capacity tend to have higher accuracy, but this also results in longer transcription times and increased CPU usage. Please refer to the documentation for explanations of each model.\n※Larger models, especially those exceeding medium size, can be challenging to run even depending on the CPU's performance."
model_template: "%{model_name} model (%{capacity})"
recommended_model_template: "%{model_name} model (%{capacity}) (Recommended)"
auto_clear_the_message_box: auto_clear_the_message_box:
label: Auto Clear The Message Box label: Auto Clear The Message Box
@@ -217,25 +228,26 @@ config_window:
label: Message Format label: Message Format
desc: "You can change the decoration of the message you want to send.\n[message] will be replaced with the message." desc: "You can change the decoration of the message you want to send.\n[message] will be replaced with the message."
example_text: This is an example sentence. Fonts, line breaks, etc. may differ from the actual display. example_text: This is an example sentence. Fonts, line breaks, etc. may differ from the actual display.
error_message: "The characters '[message]' cannot be used." error_message: "Cannot use the term '[message]'."
send_message_format_with_t: send_message_format_with_t:
label: Message Format (With translation) label: Message Format (with Translation)
desc: "You can change the decoration of the message you want to send.\n[message] will be replaced with the message, and [translation] will be replaced with the translated message." desc: "You can change the decoration of the message you want to send.\n[message] will be replaced with the message, and [translation] will be replaced with the translated message."
example_text: This is an example sentence. Fonts, line breaks, etc. may differ from the actual display. example_text: This is an example sentence. Fonts, line breaks, etc. may differ from the actual display.
error_message: "The characters '[message]' and '[translation]' cannot be used." error_message: "Cannot use the terms '[message]' and '[translation]'."
received_message_format: received_message_format:
label: Message Format label: Format of Received Messages
desc: "You can change the decoration of the received message you want to send.\n[message] will be replaced with the message.\nIt will be used in Notification XSOverlay too." desc: "Used for XSOverlay notification receiving feature.\n[message] will be replaced with the message. \nIt will be used in Speaker2Chatbox too."
example_text: This is an example sentence. Fonts, line breaks, etc. may differ from the actual display. example_text: This is an example sentence. Actual display may vary, including font and line breaks.
error_message: "The characters '[message]' cannot be used." error_message: "Cannot use the term '[message]'."
received_message_format_with_t: received_message_format_with_t:
label: Message Format (With translation) label: Format of Received Messages (with Translation)
desc: It will be used in Notification XSOverlay too. desc: "Used for XSOverlay notification receiving feature.\n[message] will be replaced with the message, and [translation] will be replaced with the translated message.\n※It will be used in Speaker2Chatbox too."
example_text: This is an example sentence. Fonts, line breaks, etc. may differ from the actual display. example_text: This is an example sentence. Actual display may vary, including font and line breaks.
error_message: "The characters '[message]' and '[translation]' cannot be used." error_message: "Cannot use the terms '[message]' and '[translation]'."
# Note: Speaker2Chatbox localization is fine only in English. Do not translate it into other languages. # Note: Speaker2Chatbox localization is fine only in English. Do not translate it into other languages.
# Speaker2Chatbox # Speaker2Chatbox

View File

@@ -79,7 +79,10 @@ config_window:
transcription: 音声認識 transcription: 音声認識
transcription_mic: マイク transcription_mic: マイク
transcription_speaker: スピーカー transcription_speaker: スピーカー
transcription_internal_model: 音声認識モデル
others: その他 others: その他
others_send_message_formats: メッセージフォーマット (送信)
others_received_message_formats: メッセージフォーマット (XSOverlay & Speaker2Chatbox)
advanced_settings: 高度な設定 advanced_settings: 高度な設定
@@ -125,6 +128,7 @@ config_window:
deepl_auth_key: deepl_auth_key:
label: DeepL 認証キー label: DeepL 認証キー
desc: "使用の際は、メイン画面にある %{translator} をDeepL_APIに変更してください。\n※対応していない言語もあります。" desc: "使用の際は、メイン画面にある %{translator} をDeepL_APIに変更してください。\n※対応していない言語もあります。"
open_auth_key_webpage: DeepLアカウントページを開く
mic_host: mic_host:
label: マイク(ホスト/ドライバー) label: マイク(ホスト/ドライバー)
@@ -184,6 +188,16 @@ config_window:
desc: 文字起こしされた単語数の下限値で、この数値を超えた場合のみ結果をログに表示します。 desc: 文字起こしされた単語数の下限値で、この数値を超えた場合のみ結果をログに表示します。
error_message: 0以上の数値を設定できます。 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: auto_clear_the_message_box:
label: 送信後はチャットボックスを空にする label: 送信後はチャットボックスを空にする
@@ -223,14 +237,14 @@ config_window:
error_message: "[message]と[translation]という文字は使えません。" error_message: "[message]と[translation]という文字は使えません。"
received_message_format: received_message_format:
label: 信するメッセージのフォーマット label: 信するメッセージのフォーマット
desc: "VRChatで相手に実際に見えるフォーマットを変更できます。\n[message]がメッセージに置換されます。\n※XSOverlayでの通知受け取り機能にも使われます。" desc: "XSOverlay通知受け取り機能で使用されます。\n[message]がメッセージに置換されます。\n※Speaker2Chatboxでの送信機能にも使われます。"
example_text: これは例文です。フォントや改行箇所など、実際の表示とは異なる場合があります。 example_text: これは例文です。フォントや改行箇所など、実際の表示とは異なる場合があります。
error_message: "[message]という文字は使えません。" error_message: "[message]という文字は使えません。"
received_message_format_with_t: received_message_format_with_t:
label: 信するメッセージのフォーマット(翻訳付き) label: 信するメッセージのフォーマット(翻訳付き)
desc: "VRChatで相手に実際に見えるフォーマットを変更できます。\n[message]がメッセージに置換され、[translation]が翻訳されたメッセージに置換されます。\n※XSOverlayでの通知受け取り機能にも使われます。" desc: "XSOverlay通知受け取り機能で使用されます。\n[message]がメッセージに置換され、[translation]が翻訳されたメッセージに置換されます。\n※Speaker2Chatboxでの送信機能にも使われます。"
example_text: これは例文です。フォントや改行箇所など、実際の表示とは異なる場合があります。 example_text: これは例文です。フォントや改行箇所など、実際の表示とは異なる場合があります。
error_message: "[message]と[translation]という文字は使えません。" error_message: "[message]と[translation]という文字は使えません。"

13
main.py
View File

@@ -8,11 +8,20 @@ if __name__ == "__main__":
splash.showSplash() splash.showSplash()
from config import config from config import config
from models.translation.utils import downloadCTranslate2Weight # version 2.2.0からweightフォルダをweightsに変更する
from utils import renameWeightFolder
renameWeightFolder(config.PATH_LOCAL)
from models.translation.translation_utils import downloadCTranslate2Weight
if config.USE_TRANSLATION_FEATURE is True: if config.USE_TRANSLATION_FEATURE is True:
downloadCTranslate2Weight(config.PATH_LOCAL, config.WEIGHT_TYPE, splash.updateDownloadProgress) downloadCTranslate2Weight(config.PATH_LOCAL, config.CTRANSLATE2_WEIGHT_TYPE, splash.updateDownloadProgress)
from models.transcription.transcription_whisper import downloadWhisperWeight
if config.USE_WHISPER_FEATURE is True:
downloadWhisperWeight(config.PATH_LOCAL, config.WHISPER_WEIGHT_TYPE, splash.updateDownloadProgress)
splash.toProgress(0) splash.toProgress(0)
import controller import controller
controller.createMainWindow(splash) controller.createMainWindow(splash)
splash.destroySplash() splash.destroySplash()

108
model.py
View File

@@ -1,3 +1,4 @@
import gc
import tempfile import tempfile
from zipfile import ZipFile from zipfile import ZipFile
from subprocess import Popen from subprocess import Popen
@@ -17,13 +18,14 @@ from flashtext import KeywordProcessor
from models.translation.translation_translator import Translator from models.translation.translation_translator import Translator
from models.transcription.transcription_utils import getInputDevices, getDefaultOutputDevice from models.transcription.transcription_utils import getInputDevices, getDefaultOutputDevice
from models.osc.osc_tools import sendTyping, sendMessage, sendTestAction, receiveOscParameters from models.osc.osc_tools import sendTyping, sendMessage, sendTestAction, receiveOscParameters
from models.transcription.transcription_recorder import SelectedMicRecorder, SelectedSpeakerRecorder from models.transcription.transcription_recorder import SelectedMicEnergyAndAudioRecorder, SelectedSpeakerEnergyAndAudioRecorder
from models.transcription.transcription_recorder import SelectedMicEnergyRecorder, SelectedSpeakeEnergyRecorder from models.transcription.transcription_recorder import SelectedMicEnergyRecorder, SelectedSpeakerEnergyRecorder
from models.transcription.transcription_transcriber import AudioTranscriber from models.transcription.transcription_transcriber import AudioTranscriber
from models.xsoverlay.notification import xsoverlayForVRCT from models.xsoverlay.notification import xsoverlayForVRCT
from models.translation.translation_languages import translation_lang from models.translation.translation_languages import translation_lang
from models.transcription.transcription_languages import transcription_lang from models.transcription.transcription_languages import transcription_lang
from models.translation.utils import checkCTranslate2Weight from models.translation.translation_utils import checkCTranslate2Weight
from models.transcription.transcription_whisper import checkWhisperWeight
from config import config from config import config
class threadFnc(Thread): class threadFnc(Thread):
@@ -64,15 +66,19 @@ class Model:
self.speaker_energy_recorder = None self.speaker_energy_recorder = None
self.speaker_energy_plot_progressbar = None self.speaker_energy_plot_progressbar = None
self.translator = Translator() self.translator = Translator()
if config.USE_TRANSLATION_FEATURE is True:
self.translator.changeCTranslate2Model(config.PATH_LOCAL, config.WEIGHT_TYPE)
self.keyword_processor = KeywordProcessor() self.keyword_processor = KeywordProcessor()
def checkCTranslatorCTranslate2ModelWeight(self): def checkCTranslatorCTranslate2ModelWeight(self):
return checkCTranslate2Weight(config.PATH_LOCAL, config.WEIGHT_TYPE) return checkCTranslate2Weight(config.PATH_LOCAL, config.CTRANSLATE2_WEIGHT_TYPE)
def changeTranslatorCTranslate2Model(self): def changeTranslatorCTranslate2Model(self):
self.translator.changeCTranslate2Model(config.PATH_LOCAL, config.WEIGHT_TYPE) self.translator.changeCTranslate2Model(config.PATH_LOCAL, config.CTRANSLATE2_WEIGHT_TYPE)
def clearTranslatorCTranslate2Model(self):
self.translator.clearCTranslate2Model()
def checkTranscriptionWhisperModelWeight(self):
return checkWhisperWeight(config.PATH_LOCAL, config.WHISPER_WEIGHT_TYPE)
def resetKeywordProcessor(self): def resetKeywordProcessor(self):
del self.keyword_processor del self.keyword_processor
@@ -317,44 +323,72 @@ class Model:
return return
mic_audio_queue = Queue() 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] device = [device for device in getInputDevices()[config.CHOICE_MIC_HOST] if device["name"] == config.CHOICE_MIC_DEVICE][0]
record_timeout = config.INPUT_MIC_RECORD_TIMEOUT record_timeout = config.INPUT_MIC_RECORD_TIMEOUT
phase_timeout = config.INPUT_MIC_PHRASE_TIMEOUT phase_timeout = config.INPUT_MIC_PHRASE_TIMEOUT
if record_timeout > phase_timeout: if record_timeout > phase_timeout:
record_timeout = phase_timeout record_timeout = phase_timeout
self.mic_audio_recorder = SelectedMicRecorder( self.mic_audio_recorder = SelectedMicEnergyAndAudioRecorder(
device=device, device=device,
energy_threshold=config.INPUT_MIC_ENERGY_THRESHOLD, energy_threshold=config.INPUT_MIC_ENERGY_THRESHOLD,
dynamic_energy_threshold=config.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD, dynamic_energy_threshold=config.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD,
record_timeout=record_timeout, record_timeout=record_timeout,
) )
self.mic_audio_recorder.recordIntoQueue(mic_audio_queue) # self.mic_audio_recorder.recordIntoQueue(mic_audio_queue, mic_energy_queue)
mic_transcriber = AudioTranscriber( self.mic_audio_recorder.recordIntoQueue(mic_audio_queue, None)
self.mic_transcriber = AudioTranscriber(
speaker=False, speaker=False,
source=self.mic_audio_recorder.source, source=self.mic_audio_recorder.source,
phrase_timeout=phase_timeout, phrase_timeout=phase_timeout,
max_phrases=config.INPUT_MIC_MAX_PHRASES, max_phrases=config.INPUT_MIC_MAX_PHRASES,
transcription_engine=config.SELECTED_TRANSCRIPTION_ENGINE,
root=config.PATH_LOCAL,
whisper_weight_type=config.WHISPER_WEIGHT_TYPE,
) )
def sendMicTranscript(): def sendMicTranscript():
mic_transcriber.transcribeAudioQueue(mic_audio_queue, config.SOURCE_LANGUAGE, config.SOURCE_COUNTRY) self.mic_transcriber.transcribeAudioQueue(mic_audio_queue, config.SOURCE_LANGUAGE, config.SOURCE_COUNTRY)
message = mic_transcriber.getTranscript() message = self.mic_transcriber.getTranscript()
try: try:
fnc(message) fnc(message)
except Exception: except Exception:
pass pass
self.mic_print_transcript = threadFnc(sendMicTranscript) def endMicTranscript():
mic_audio_queue.queue.clear()
# mic_energy_queue.queue.clear()
del self.mic_transcriber
gc.collect()
# def sendMicEnergy():
# if mic_energy_queue.empty() is False:
# energy = mic_energy_queue.get()
# # print("mic energy:", energy)
# try:
# fnc(energy)
# except Exception:
# pass
# sleep(0.01)
self.mic_print_transcript = threadFnc(sendMicTranscript, end_fnc=endMicTranscript)
self.mic_print_transcript.daemon = True self.mic_print_transcript.daemon = True
self.mic_print_transcript.start() self.mic_print_transcript.start()
# self.mic_get_energy = threadFnc(sendMicEnergy)
# self.mic_get_energy.daemon = True
# self.mic_get_energy.start()
def stopMicTranscript(self): def stopMicTranscript(self):
if isinstance(self.mic_print_transcript, threadFnc): if isinstance(self.mic_print_transcript, threadFnc):
self.mic_print_transcript.stop() self.mic_print_transcript.stop()
self.mic_print_transcript = None self.mic_print_transcript = None
if isinstance(self.mic_audio_recorder, SelectedMicRecorder): if isinstance(self.mic_audio_recorder, SelectedMicEnergyAndAudioRecorder):
self.mic_audio_recorder.stop() self.mic_audio_recorder.stop()
self.mic_audio_recorder = None 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): def startCheckMicEnergy(self, fnc, end_fnc, error_fnc=None):
if config.CHOICE_MIC_HOST == "NoHost" or config.CHOICE_MIC_DEVICE == "NoDevice": if config.CHOICE_MIC_HOST == "NoHost" or config.CHOICE_MIC_DEVICE == "NoDevice":
@@ -399,43 +433,71 @@ class Model:
return return
speaker_audio_queue = Queue() speaker_audio_queue = Queue()
# speaker_energy_queue = Queue()
record_timeout = config.INPUT_SPEAKER_RECORD_TIMEOUT record_timeout = config.INPUT_SPEAKER_RECORD_TIMEOUT
phase_timeout = config.INPUT_SPEAKER_PHRASE_TIMEOUT phase_timeout = config.INPUT_SPEAKER_PHRASE_TIMEOUT
if record_timeout > phase_timeout: if record_timeout > phase_timeout:
record_timeout = phase_timeout record_timeout = phase_timeout
self.speaker_audio_recorder = SelectedSpeakerRecorder( self.speaker_audio_recorder = SelectedSpeakerEnergyAndAudioRecorder(
device=speaker_device, device=speaker_device,
energy_threshold=config.INPUT_SPEAKER_ENERGY_THRESHOLD, energy_threshold=config.INPUT_SPEAKER_ENERGY_THRESHOLD,
dynamic_energy_threshold=config.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD, dynamic_energy_threshold=config.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD,
record_timeout=record_timeout, record_timeout=record_timeout,
) )
self.speaker_audio_recorder.recordIntoQueue(speaker_audio_queue) # self.speaker_audio_recorder.recordIntoQueue(speaker_audio_queue, speaker_energy_queue)
speaker_transcriber = AudioTranscriber( self.speaker_audio_recorder.recordIntoQueue(speaker_audio_queue ,None)
self.speaker_transcriber = AudioTranscriber(
speaker=True, speaker=True,
source=self.speaker_audio_recorder.source, source=self.speaker_audio_recorder.source,
phrase_timeout=phase_timeout, phrase_timeout=phase_timeout,
max_phrases=config.INPUT_SPEAKER_MAX_PHRASES, max_phrases=config.INPUT_SPEAKER_MAX_PHRASES,
transcription_engine=config.SELECTED_TRANSCRIPTION_ENGINE,
root=config.PATH_LOCAL,
whisper_weight_type=config.WHISPER_WEIGHT_TYPE,
) )
def sendSpeakerTranscript(): def sendSpeakerTranscript():
speaker_transcriber.transcribeAudioQueue(speaker_audio_queue, config.TARGET_LANGUAGE, config.TARGET_COUNTRY) self.speaker_transcriber.transcribeAudioQueue(speaker_audio_queue, config.TARGET_LANGUAGE, config.TARGET_COUNTRY)
message = speaker_transcriber.getTranscript() message = self.speaker_transcriber.getTranscript()
try: try:
fnc(message) fnc(message)
except Exception: except Exception:
pass pass
self.speaker_print_transcript = threadFnc(sendSpeakerTranscript) def endSpeakerTranscript():
speaker_audio_queue.queue.clear()
# speaker_energy_queue.queue.clear()
del self.speaker_transcriber
gc.collect()
# def sendSpeakerEnergy():
# if speaker_energy_queue.empty() is False:
# energy = speaker_energy_queue.get()
# # print("speaker energy:", energy)
# try:
# fnc(energy)
# except Exception:
# pass
# sleep(0.01)
self.speaker_print_transcript = threadFnc(sendSpeakerTranscript, end_fnc=endSpeakerTranscript)
self.speaker_print_transcript.daemon = True self.speaker_print_transcript.daemon = True
self.speaker_print_transcript.start() self.speaker_print_transcript.start()
# self.speaker_get_energy = threadFnc(sendSpeakerEnergy)
# self.speaker_get_energy.daemon = True
# self.speaker_get_energy.start()
def stopSpeakerTranscript(self): def stopSpeakerTranscript(self):
if isinstance(self.speaker_print_transcript, threadFnc): if isinstance(self.speaker_print_transcript, threadFnc):
self.speaker_print_transcript.stop() self.speaker_print_transcript.stop()
self.speaker_print_transcript = None self.speaker_print_transcript = None
if isinstance(self.speaker_audio_recorder, SelectedSpeakerRecorder): if isinstance(self.speaker_audio_recorder, SelectedSpeakerEnergyAndAudioRecorder):
self.speaker_audio_recorder.stop() self.speaker_audio_recorder.stop()
self.speaker_audio_recorder = None 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): def startCheckSpeakerEnergy(self, fnc, end_fnc, error_fnc=None):
speaker_device = getDefaultOutputDevice() speaker_device = getDefaultOutputDevice()
@@ -456,7 +518,7 @@ class Model:
sleep(0.01) sleep(0.01)
speaker_energy_queue = Queue() speaker_energy_queue = Queue()
self.speaker_energy_recorder = SelectedSpeakeEnergyRecorder(speaker_device) self.speaker_energy_recorder = SelectedSpeakerEnergyRecorder(speaker_device)
self.speaker_energy_recorder.recordIntoQueue(speaker_energy_queue) self.speaker_energy_recorder.recordIntoQueue(speaker_energy_queue)
self.speaker_energy_plot_progressbar = threadFnc(sendSpeakerEnergy, end_fnc=end_fnc) self.speaker_energy_plot_progressbar = threadFnc(sendSpeakerEnergy, end_fnc=end_fnc)
self.speaker_energy_plot_progressbar.daemon = True self.speaker_energy_plot_progressbar.daemon = True
@@ -466,7 +528,7 @@ class Model:
if isinstance(self.speaker_energy_plot_progressbar, threadFnc): if isinstance(self.speaker_energy_plot_progressbar, threadFnc):
self.speaker_energy_plot_progressbar.stop() self.speaker_energy_plot_progressbar.stop()
self.speaker_energy_plot_progressbar = None self.speaker_energy_plot_progressbar = None
if isinstance(self.speaker_energy_recorder, SelectedSpeakeEnergyRecorder): if isinstance(self.speaker_energy_recorder, SelectedSpeakerEnergyRecorder):
self.speaker_energy_recorder.stop() self.speaker_energy_recorder.stop()
self.speaker_energy_recorder = None self.speaker_energy_recorder = None

View File

@@ -1,8 +1,11 @@
from time import sleep from time import sleep
import threading
from pythonosc import osc_message_builder from pythonosc import osc_message_builder
from pythonosc import udp_client from pythonosc import udp_client
from pythonosc import dispatcher from pythonosc import dispatcher
from pythonosc import osc_server from pythonosc import osc_server
from tinyoscquery.queryservice import OSCQueryService
from tinyoscquery.utility import get_open_udp_port, get_open_tcp_port
# send OSC message typing # send OSC message typing
def sendTyping(flag=False, ip_address="127.0.0.1", port=9000): def sendTyping(flag=False, ip_address="127.0.0.1", port=9000):
@@ -45,12 +48,40 @@ def sendChangeVoice(ip_address="127.0.0.1", port=9000):
sendInputVoice(flag=0, ip_address=ip_address, port=port) sendInputVoice(flag=0, ip_address=ip_address, port=port)
sleep(0.05) sleep(0.05)
def receiveOscParameters(target, filter="/*", ip_address="127.0.0.1", port=9001):
_dispatcher = dispatcher.Dispatcher() def receiveOscParameters(dict_filter_and_target, ip_address="127.0.0.1", title="VRCT"):
_dispatcher.map(filter, target) osc_port = get_open_udp_port()
server = osc_server.ThreadingOSCUDPServer((ip_address, port), _dispatcher) http_port = get_open_tcp_port()
return server osc_dispatcher = dispatcher.Dispatcher()
for filter, target in dict_filter_and_target.items():
osc_dispatcher.map(filter, target)
osc_udp_server = osc_server.ThreadingOSCUDPServer((ip_address, osc_port), osc_dispatcher)
threading.Thread(target=osc_udp_server.serve_forever, daemon = True).start()
osc_client = OSCQueryService(title, http_port, osc_port)
for filter, target in dict_filter_and_target.items():
osc_client.advertise_endpoint(filter)
if __name__ == "__main__": if __name__ == "__main__":
sendChangeVoice() osc_parameter_prefix = "/avatar/parameters/"
sendChangeVoice() osc_avatar_change_path = "/avatar/change"
param_MuteSelf = "MuteSelf"
param_Voice = "Voice"
def print_handler_all(address, *args):
print(f"all {address}: {args}")
def print_handler_muteself(address, *args):
print(f"muteself {address}: {args}")
def print_handler_voice(address, *args):
print(f"voice {address}: {args}")
dict_filter_and_target = {
# osc_parameter_prefix + "*": print_handler_all,
osc_parameter_prefix + param_MuteSelf: print_handler_muteself,
osc_parameter_prefix + param_Voice: print_handler_voice,
}
receiveOscParameters(dict_filter_and_target)

View File

@@ -1,177 +1,438 @@
transcription_lang = { transcription_lang = {
"Afrikaans":{ "Afrikaans":{
"South Africa":"af-ZA", "South Africa":{
"Google": "af-ZA",
"Whisper": "af",
},
}, },
"Arabic":{ "Arabic":{
"Algeria":"ar-DZ", "Algeria":{
"Bahrain":"ar-BH", "Google": "ar-DZ",
"Egypt":"ar-EG", "Whisper": "ar",
"Israel":"ar-IL", },
"Iraq":"ar-IQ", "Bahrain":{
"Jordan":"ar-JO", "Google": "ar-BH",
"Kuwait":"ar-KW", "Whisper": "ar",
"Lebanon":"ar-LB", },
"Morocco":"ar-MA", "Egypt":{
"Oman":"ar-OM", "Google": "ar-EG",
"State of Palestine":"ar-PS", "Whisper": "ar",
"Qatar":"ar-QA", },
"Saudi Arabia":"ar-SA", "Israel":{
"Tunisia":"ar-TN", "Google": "ar-IL",
"United Arab Emirates":"ar-AE", "Whisper": "ar",
},
"Iraq":{
"Google": "ar-IQ",
"Whisper": "ar",
},
"Jordan":{
"Google": "ar-JO",
"Whisper": "ar",
},
"Kuwait":{
"Google": "ar-KW",
"Whisper": "ar",
},
"Lebanon":{
"Google": "ar-LB",
"Whisper": "ar",
},
"Morocco":{
"Google": "ar-MA",
"Whisper": "ar",
},
"Oman":{
"Google": "ar-OM",
"Whisper": "ar",
},
"State of Palestine":{
"Google": "ar-PS",
"Whisper": "ar",
},
"Qatar":{
"Google": "ar-QA",
"Whisper": "ar",
},
"Saudi Arabia":{
"Google": "ar-SA",
"Whisper": "ar",
},
"Tunisia":{
"Google": "ar-TN",
"Whisper": "ar",
},
"United Arab Emirates":{
"Google": "ar-AE",
"Whisper": "ar",
},
}, },
"Basque":{ "Basque":{
"Spain":"eu-ES", "Spain":{
"Google": "eu-ES",
"Whisper": "eu",
},
}, },
"Bulgarian":{ "Bulgarian":{
"Bulgaria":"bg-BG", "Bulgaria":{
"Google": "bg-BG",
"Whisper": "bg",
},
}, },
"Catalan":{ "Catalan":{
"Spain":"ca-ES", "Spain":{
"Google": "ca-ES",
"Whisper": "ca",
},
}, },
"Chinese":{ "Chinese":{
"Mandarin (Simplified, China)":"cmn-Hans-CN", "Mandarin (Simplified, China)":{
"Mandarin (Simplified, Hong Kong)":"cmn-Hans-HK", "Google": "cmn-Hans-CN",
"Mandarin (Traditional, Taiwan)":"cmn-Hant-TW", "Whisper": "zh",
"Cantonese (Traditional Hong Kong)":"yue-Hant-HK", },
"Mandarin (Simplified, Hong Kong)":{
"Google": "cmn-Hans-HK",
"Whisper": "zh",
},
"Mandarin (Traditional, Taiwan)":{
"Google": "cmn-Hant-TW",
"Whisper": "zh",
},
"Cantonese (Traditional Hong Kong)":{
"Google": "yue-Hant-HK",
"Whisper": "yue",
},
}, },
"Croatian":{ "Croatian":{
"Croatia":"hr-HR", "Croatia":{
"Google": "hr-HR",
"Whisper": "hr",
},
}, },
"Czech":{ "Czech":{
"Czech Republic":"cs-CZ", "Czech Republic":{
"Google": "cs-CZ",
"Whisper": "cs",
},
}, },
"Danish":{ "Danish":{
"Denmark":"da-DK", "Denmark":{
"Google": "da-DK",
"Whisper": "da",
},
}, },
"Dutch":{ "Dutch":{
"Netherlands":"nl-NL", "Netherlands":{
"Google": "nl-NL",
"Whisper": "nl",
},
}, },
"English": { "English": {
"United States":"en-US", "United States":{
"United Kingdom":"en-GB", "Google": "en-US",
"Australia":"en-AU", "Whisper": "en",
"Canada":"en-CA", },
"India":"en-IN", "United Kingdom":{
"Ireland":"en-IE", "Google": "en-GB",
"New Zealand":"en-NZ", "Whisper": "en",
"Philippines":"en-PH", },
"South Africa":"en-ZA", "Australia":{
"Google": "en-AU",
"Whisper": "en",
},
"Canada":{
"Google": "en-CA",
"Whisper": "en",
},
"India":{
"Google": "en-IN",
"Whisper": "en",
},
"Ireland":{
"Google": "en-IE",
"Whisper": "en",
},
"New Zealand":{
"Google": "en-NZ",
"Whisper": "en",
},
"Philippines":{
"Google": "en-PH",
"Whisper": "en",
},
"South Africa":{
"Google": "en-ZA",
"Whisper": "en",
},
}, },
"Filipino":{ "Filipino":{
"Philippines":"fil-PH", "Philippines":{
"Google": "fil-PH",
"Whisper": "tl",
},
}, },
"Finnish":{ "Finnish":{
"Finland":"fi-FI", "Finland":{
"Google": "fi-FI",
"Whisper": "fi",
},
}, },
"French":{ "French":{
"France":"fr-FR", "France":{
"Google": "fr-FR",
"Whisper": "fr",
},
}, },
"Galician":{ "Galician":{
"Spain":"gl-ES", "Spain":{
"Google": "gl-ES",
"Whisper": "gl",
},
}, },
"German":{ "German":{
"Germany":"de-DE", "Germany":{
"Google": "de-DE",
"Whisper": "de",
},
}, },
"Greek":{ "Greek":{
"Greece":"el-GR", "Greece":{
"Google": "el-GR",
"Whisper": "el",
},
}, },
"Hebrew":{ "Hebrew":{
"Israel":"he-IL", "Israel":{
"Google": "he-IL",
"Whisper": "he",
},
}, },
"Hindi": { "Hindi": {
"India":"hi-IN", "India":{
"Google": "hi-IN",
"Whisper": "hi",
},
}, },
"Hungarian":{ "Hungarian":{
"Hungary":"hu-HU", "Hungary":{
"Google": "hu-HU",
"Whisper": "hu",
},
}, },
"Indonesian":{ "Indonesian":{
"Indonesia":"id-ID", "Indonesia":{
"Google": "id-ID",
"Whisper": "id",
},
}, },
"Icelandic":{ "Icelandic":{
"Iceland":"is-IS", "Iceland":{
"Google": "is-IS",
"Whisper": "is",
},
}, },
"Italian":{ "Italian":{
"Italy":"it-IT", "Italy":{
"Switzerland":"it-CH", "Google": "it-IT",
"Whisper": "it",
},
"Switzerland":{
"Google": "it-CH",
"Whisper": "it",
},
}, },
"Japanese":{ "Japanese":{
"Japan":"ja-JP", "Japan":{
"Google": "ja-JP",
"Whisper": "ja",
},
}, },
"Korean":{ "Korean":{
"South Korea":"ko-KR", "South Korea":{
"Google": "ko-KR",
"Whisper": "ko",
},
}, },
"Lithuanian":{ "Lithuanian":{
"Lithuania":"lt-LT", "Lithuania":{
"Google": "lt-LT",
"Whisper": "lt",
},
}, },
"Malay":{ "Malay":{
"Malaysia":"ms-MY", "Malaysia":{
"Google": "ms-MY",
"Whisper": "ms",
},
}, },
"Norwegian":{ "Norwegian":{
"Norway":"nb-NO", "Norway":{
"Google": "nb-NO",
"Whisper": "no",
},
}, },
"Persian":{ "Persian":{
"Iran":"fa-IR", "Iran":{
"Google": "fa-IR",
"Whisper": "fa",
},
}, },
"Polish":{ "Polish":{
"Poland":"pl-PL", "Poland":{
"Google": "pl-PL",
"Whisper": "pl",
},
}, },
"Portuguese":{ "Portuguese":{
"Brazil":"pt-BR", "Brazil":{
"Portugal":"pt-PT", "Google": "pt-BR",
"Whisper": "pt",
},
"Portugal":{
"Google": "pt-PT",
"Whisper": "pt",
},
}, },
"Romanian":{ "Romanian":{
"Romania":"ro-RO", "Romania":{
"Google": "ro-RO",
"Whisper": "ro",
},
}, },
"Russian":{ "Russian":{
"Russia":"ru-RU", "Russia":{
"Google": "ru-RU",
"Whisper": "ru",
},
}, },
"Serbian":{ "Serbian":{
"Serbia":"sr-RS", "Serbia":{
"Google": "sr-RS",
"Whisper": "sr",
},
}, },
"Slovak":{ "Slovak":{
"Slovakia":"sk-SK", "Slovakia":{
"Google": "sk-SK",
"Whisper": "sk",
},
}, },
"Slovenian":{ "Slovenian":{
"Slovenia":"sl-SI", "Slovenia":{
"Google": "sl-SI",
"Whisper": "sl",
},
}, },
"Spanish":{ "Spanish":{
"Argentina":"es-AR", "Argentina":{
"Bolivia":"es-BO", "Google": "es-AR",
"Chile":"es-CL", "Whisper": "es",
"Colombia":"es-CO", },
"Costa Rica":"es-CR", "Bolivia":{
"Dominican Republic":"es-DO", "Google": "es-BO",
"Ecuador":"es-EC", "Whisper": "es",
"El Salvador":"es-SV", },
"Guatemala":"es-GT", "Chile":{
"Honduras":"es-HN", "Google": "es-CL",
"Mexico":"es-MX", "Whisper": "es",
"Nicaragua":"es-NI", },
"Panama":"es-PA", "Colombia":{
"Paraguay":"es-PY", "Google": "es-CO",
"Peru":"es-PE", "Whisper": "es",
"Puerto Rico":"es-PR", },
"Spain":"es-ES", "Costa Rica":{
"Uruguay":"es-UY", "Google": "es-CR",
"United States":"es-US", "Whisper": "es",
"Venezuela":"es-VE", },
"Dominican Republic":{
"Google": "es-DO",
"Whisper": "es",
},
"Ecuador":{
"Google": "es-EC",
"Whisper": "es",
},
"El Salvador":{
"Google": "es-SV",
"Whisper": "es",
},
"Guatemala":{
"Google": "es-GT",
"Whisper": "es",
},
"Honduras":{
"Google": "es-HN",
"Whisper": "es",
},
"Mexico":{
"Google": "es-MX",
"Whisper": "es",
},
"Nicaragua":{
"Google": "es-NI",
"Whisper": "es",
},
"Panama":{
"Google": "es-PA",
"Whisper": "es",
},
"Paraguay":{
"Google": "es-PY",
"Whisper": "es",
},
"Peru":{
"Google": "es-PE",
"Whisper": "es",
},
"Puerto Rico":{
"Google": "es-PR",
"Whisper": "es",
},
"Spain":{
"Google": "es-ES",
"Whisper": "es",
},
"Uruguay":{
"Google": "es-UY",
"Whisper": "es",
},
"United States":{
"Google": "es-US",
"Whisper": "es",
},
"Venezuela":{
"Google": "es-VE",
"Whisper": "es",
},
}, },
"Swedish":{ "Swedish":{
"Sweden":"sv-SE", "Sweden":{
"Google": "sv-SE",
"Whisper": "sv",
},
}, },
"Thai":{ "Thai":{
"Thailand":"th-TH", "Thailand":{
"Google": "th-TH",
"Whisper": "th",
},
}, },
"Turkish":{ "Turkish":{
"Turkey":"tr-TR", "Turkey":{
"Google": "tr-TR",
"Whisper": "tr",
},
}, },
"Ukrainian":{ "Ukrainian":{
"Ukraine":"uk-UA", "Ukraine":{
"Google": "uk-UA",
"Whisper": "uk",
},
}, },
"Vietnamese":{ "Vietnamese":{
"Vietnam":"vi-VN", "Vietnam":{
"Google": "vi-VN",
"Whisper": "vi",
}, },
"Zulu":{
"South Africa":"zu-ZA"
}, },
} }

View File

@@ -1,6 +1,7 @@
from speech_recognition import Recognizer, Microphone from speech_recognition import Recognizer, Microphone
from pyaudiowpatch import get_sample_size, paInt16 from pyaudiowpatch import get_sample_size, paInt16
from datetime import datetime from datetime import datetime
from queue import Queue
class BaseRecorder: class BaseRecorder:
def __init__(self, source, energy_threshold, dynamic_energy_threshold, record_timeout): def __init__(self, source, energy_threshold, dynamic_energy_threshold, record_timeout):
@@ -78,7 +79,7 @@ class SelectedMicEnergyRecorder(BaseEnergyRecorder):
super().__init__(source=source) super().__init__(source=source)
# self.adjustForNoise() # self.adjustForNoise()
class SelectedSpeakeEnergyRecorder(BaseEnergyRecorder): class SelectedSpeakerEnergyRecorder(BaseEnergyRecorder):
def __init__(self, device): def __init__(self, device):
source = Microphone(speaker=True, source = Microphone(speaker=True,
@@ -88,3 +89,53 @@ class SelectedSpeakeEnergyRecorder(BaseEnergyRecorder):
) )
super().__init__(source=source) super().__init__(source=source)
# self.adjustForNoise() # self.adjustForNoise()
class BaseEnergyAndAudioRecorder:
def __init__(self, source, energy_threshold, dynamic_energy_threshold, record_timeout):
self.recorder = Recognizer()
self.recorder.energy_threshold = energy_threshold
self.recorder.dynamic_energy_threshold = dynamic_energy_threshold
self.record_timeout = record_timeout
self.stop = None
if source is None:
raise ValueError("audio source can't be None")
self.source = source
def adjustForNoise(self):
with self.source:
self.recorder.adjust_for_ambient_noise(self.source)
def recordIntoQueue(self, audio_queue, energy_queue):
def audioRecordCallback(_, audio):
audio_queue.put((audio.get_raw_data(), datetime.now()))
def energyRecordCallback(energy):
energy_queue.put(energy)
if isinstance(energy_queue, Queue):
self.stop = self.recorder.listen_energy_and_audio_in_background(self.source, audioRecordCallback, phrase_time_limit=self.record_timeout, callback_energy=energyRecordCallback)
else:
self.stop = self.recorder.listen_energy_and_audio_in_background(self.source, audioRecordCallback, phrase_time_limit=self.record_timeout)
class SelectedMicEnergyAndAudioRecorder(BaseEnergyAndAudioRecorder):
def __init__(self, device, energy_threshold, dynamic_energy_threshold, record_timeout):
source=Microphone(
device_index=device['index'],
sample_rate=int(device["defaultSampleRate"]),
)
super().__init__(source=source, energy_threshold=energy_threshold, dynamic_energy_threshold=dynamic_energy_threshold, record_timeout=record_timeout)
# self.adjustForNoise()
class SelectedSpeakerEnergyAndAudioRecorder(BaseEnergyAndAudioRecorder):
def __init__(self, device, energy_threshold, dynamic_energy_threshold, record_timeout):
source = Microphone(speaker=True,
device_index= device["index"],
sample_rate=int(device["defaultSampleRate"]),
chunk_size=get_sample_size(paInt16),
channels=device["maxInputChannels"]
)
super().__init__(source=source, energy_threshold=energy_threshold, dynamic_energy_threshold=dynamic_energy_threshold, record_timeout=record_timeout)
# self.adjustForNoise()

View File

@@ -5,18 +5,24 @@ from speech_recognition import Recognizer, AudioData, AudioFile
from datetime import timedelta from datetime import timedelta
from pyaudiowpatch import get_sample_size, paInt16 from pyaudiowpatch import get_sample_size, paInt16
from .transcription_languages import transcription_lang from .transcription_languages import transcription_lang
from .transcription_whisper import getWhisperModel, checkWhisperWeight
import torch
import numpy as np
PHRASE_TIMEOUT = 3 PHRASE_TIMEOUT = 3
MAX_PHRASES = 10 MAX_PHRASES = 10
class AudioTranscriber: class AudioTranscriber:
def __init__(self, speaker, source, phrase_timeout, max_phrases): def __init__(self, speaker, source, phrase_timeout, max_phrases, transcription_engine, root=None, whisper_weight_type=None):
self.speaker = speaker self.speaker = speaker
self.phrase_timeout = phrase_timeout self.phrase_timeout = phrase_timeout
self.max_phrases = max_phrases self.max_phrases = max_phrases
self.transcript_data = [] self.transcript_data = []
self.transcript_changed_event = Event() self.transcript_changed_event = Event()
self.audio_recognizer = Recognizer() self.audio_recognizer = Recognizer()
self.transcription_engine = "Google"
self.whisper_model = None
self.audio_sources = { self.audio_sources = {
"sample_rate": source.SAMPLE_RATE, "sample_rate": source.SAMPLE_RATE,
"sample_width": source.SAMPLE_WIDTH, "sample_width": source.SAMPLE_WIDTH,
@@ -27,22 +33,45 @@ class AudioTranscriber:
"process_data_func": self.processSpeakerData if speaker else self.processSpeakerData "process_data_func": self.processSpeakerData if speaker else self.processSpeakerData
} }
if transcription_engine == "Whisper" and checkWhisperWeight(root, whisper_weight_type) is True:
self.whisper_model = getWhisperModel(root, whisper_weight_type)
self.transcription_engine = "Whisper"
def transcribeAudioQueue(self, audio_queue, language, country): def transcribeAudioQueue(self, audio_queue, language, country):
# while True:
audio, time_spoken = audio_queue.get() audio, time_spoken = audio_queue.get()
self.updateLastSampleAndPhraseStatus(audio, time_spoken) self.updateLastSampleAndPhraseStatus(audio, time_spoken)
text = '' text = ''
try: try:
# fd, path = tempfile.mkstemp(suffix=".wav")
# os.close(fd)
audio_data = self.audio_sources["process_data_func"]() audio_data = self.audio_sources["process_data_func"]()
text = self.audio_recognizer.recognize_google(audio_data, language=transcription_lang[language][country]) match self.transcription_engine:
case "Google":
text = self.audio_recognizer.recognize_google(audio_data, language=transcription_lang[language][country][self.transcription_engine])
case "Whisper":
audio_data = np.frombuffer(audio_data.get_raw_data(convert_rate=16000, convert_width=2), np.int16).flatten().astype(np.float32) / 32768.0
if isinstance(audio_data, torch.Tensor):
audio_data = audio_data.detach().numpy()
segments, _ = self.whisper_model.transcribe(
audio_data,
beam_size=5,
temperature=0.0,
log_prob_threshold=-0.8,
no_speech_threshold=0.6,
language=transcription_lang[language][country][self.transcription_engine],
word_timestamps=False,
without_timestamps=True,
task="transcribe",
vad_filter=False,
)
for s in segments:
if s.avg_logprob < -0.8 or s.no_speech_prob > 0.6:
continue
text += s.text
except Exception: except Exception:
pass pass
finally: finally:
pass pass
# os.unlink(path)
if text != '': if text != '':
self.updateTranscript(text) self.updateTranscript(text)

View File

@@ -0,0 +1,98 @@
from os import path as os_path, makedirs as os_makedirs
from requests import get as requests_get
from typing import Callable
import huggingface_hub
from faster_whisper import WhisperModel
import logging
logger = logging.getLogger('faster_whisper')
logger.setLevel(logging.CRITICAL)
_MODELS = {
"tiny": "Systran/faster-whisper-tiny",
"base": "Systran/faster-whisper-base",
"small": "Systran/faster-whisper-small",
"medium": "Systran/faster-whisper-medium",
"large-v1": "Systran/faster-whisper-large-v1",
"large-v2": "Systran/faster-whisper-large-v2",
"large-v3": "Systran/faster-whisper-large-v3",
}
_FILENAMES = [
"config.json",
"preprocessor_config.json",
"model.bin",
"tokenizer.json",
"vocabulary.txt",
"vocabulary.json",
]
def downloadFile(url, path, func=None):
try:
res = requests_get(url, stream=True)
res.raise_for_status()
file_size = int(res.headers.get('content-length', 0))
total_chunk = 0
with open(os_path.join(path), 'wb') as file:
for chunk in res.iter_content(chunk_size=1024*5):
file.write(chunk)
if isinstance(func, Callable):
total_chunk += len(chunk)
func(total_chunk/file_size)
except Exception as e:
print("error:downloadFile()", e)
def checkWhisperWeight(root, weight_type):
path = os_path.join(root, "weights", "whisper", weight_type)
result = False
try:
WhisperModel(
path,
device="cpu",
device_index=0,
compute_type="int8",
cpu_threads=4,
num_workers=1,
local_files_only=True,
)
result = True
except Exception:
pass
return result
def downloadWhisperWeight(root, weight_type, callbackFunc):
path = os_path.join(root, "weights", "whisper", weight_type)
os_makedirs(path, exist_ok=True)
if checkWhisperWeight(root, weight_type) is True:
return
for filename in _FILENAMES:
print("Downloading", filename, "...")
file_path = os_path.join(path, filename)
url = huggingface_hub.hf_hub_url(_MODELS[weight_type], filename)
downloadFile(url, file_path, func=callbackFunc)
def getWhisperModel(root, weight_type):
path = os_path.join(root, "weights", "whisper", weight_type)
return WhisperModel(
path,
device="cpu",
device_index=0,
compute_type="int8",
cpu_threads=4,
num_workers=1,
local_files_only=True,
)
if __name__ == "__main__":
def callback(value):
print(value)
pass
downloadWhisperWeight("./", "tiny", callback)
downloadWhisperWeight("./", "base", callback)
downloadWhisperWeight("./", "small", callback)
downloadWhisperWeight("./", "medium", callback)
downloadWhisperWeight("./", "large-v1", callback)
downloadWhisperWeight("./", "large-v2", callback)
downloadWhisperWeight("./", "large-v3", callback)

View File

@@ -1,8 +1,9 @@
import gc
import os import os
from deepl import Translator as deepl_Translator from deepl import Translator as deepl_Translator
from translators import translate_text as other_web_Translator from translators import translate_text as other_web_Translator
from .translation_languages import translation_lang from .translation_languages import translation_lang
from .utils import ctranslate2_weights from .translation_utils import ctranslate2_weights
import ctranslate2 import ctranslate2
import transformers import transformers
@@ -27,7 +28,8 @@ class Translator():
def changeCTranslate2Model(self, path, model_type): def changeCTranslate2Model(self, path, model_type):
directory_name = ctranslate2_weights[model_type]["directory_name"] directory_name = ctranslate2_weights[model_type]["directory_name"]
tokenizer = ctranslate2_weights[model_type]["tokenizer"] tokenizer = ctranslate2_weights[model_type]["tokenizer"]
weight_path = os.path.join(path, "weight", directory_name) weight_path = os.path.join(path, "weights", "ctranslate2", directory_name)
tokenizer_path = os.path.join(path, "weights", "ctranslate2", directory_name, "tokenizer")
self.ctranslate2_translator = ctranslate2.Translator( self.ctranslate2_translator = ctranslate2.Translator(
weight_path, weight_path,
device="cpu", device="cpu",
@@ -36,7 +38,19 @@ class Translator():
inter_threads=1, inter_threads=1,
intra_threads=4 intra_threads=4
) )
self.ctranslate2_tokenizer = transformers.AutoTokenizer.from_pretrained(tokenizer) try:
self.ctranslate2_tokenizer = transformers.AutoTokenizer.from_pretrained(tokenizer, cache_dir=tokenizer_path)
except Exception as e:
print("Error: changeCTranslate2Model()", e)
tokenizer_path = os.path.join("./weights", "ctranslate2", directory_name, "tokenizer")
self.ctranslate2_tokenizer = transformers.AutoTokenizer.from_pretrained(tokenizer, cache_dir=tokenizer_path)
def clearCTranslate2Model(self):
del self.ctranslate2_translator
del self.ctranslate2_tokenizer
gc.collect()
self.ctranslate2_translator = None
self.ctranslate2_tokenizer = None
@staticmethod @staticmethod
def getLanguageCode(translator_name, target_country, source_language, target_language): def getLanguageCode(translator_name, target_country, source_language, target_language):

View File

@@ -39,36 +39,36 @@ def calculate_file_hash(file_path, block_size=65536):
return hash_object.hexdigest() return hash_object.hexdigest()
def checkCTranslate2Weight(path, weight_type="Small"): def checkCTranslate2Weight(path, weight_type="Small"):
directory_name = 'weight'
current_directory = path
weight_directory_name = ctranslate2_weights[weight_type]["directory_name"] weight_directory_name = ctranslate2_weights[weight_type]["directory_name"]
hash_data = ctranslate2_weights[weight_type]["hash"] hash_data = ctranslate2_weights[weight_type]["hash"]
files = ["model.bin", "sentencepiece.model", "shared_vocabulary.txt"] files = [
"model.bin",
"sentencepiece.model",
"shared_vocabulary.txt"
]
# check already downloaded # check already downloaded
already_downloaded = False already_downloaded = False
if all(os_path.exists(os_path.join(current_directory, directory_name, weight_directory_name, file)) for file in files): if all(os_path.exists(os_path.join(path, weight_directory_name, file)) for file in files):
# check hash # check hash
for file in files: for file in files:
original_hash = hash_data[file] original_hash = hash_data[file]
current_hash = calculate_file_hash(os_path.join(current_directory, directory_name, weight_directory_name, file)) current_hash = calculate_file_hash(os_path.join(path, weight_directory_name, file))
if original_hash != current_hash: if original_hash != current_hash:
break break
already_downloaded = True already_downloaded = True
return already_downloaded return already_downloaded
def downloadCTranslate2Weight(path, weight_type="Small", func=None): def downloadCTranslate2Weight(root, weight_type="Small", func=None):
url = ctranslate2_weights[weight_type]["url"] url = ctranslate2_weights[weight_type]["url"]
filename = 'weight.zip' filename = "weight.zip"
directory_name = 'weight' path = os_path.join(root, "weights", "ctranslate2")
current_directory = path os_makedirs(path, exist_ok=True)
if checkCTranslate2Weight(path, weight_type): if checkCTranslate2Weight(path, weight_type):
return return
try: try:
os_makedirs(os_path.join(current_directory, directory_name), exist_ok=True)
print(os_path.join(current_directory, directory_name))
with tempfile.TemporaryDirectory() as tmp_path: with tempfile.TemporaryDirectory() as tmp_path:
res = requests_get(url, stream=True) res = requests_get(url, stream=True)
file_size = int(res.headers.get('content-length', 0)) file_size = int(res.headers.get('content-length', 0))
@@ -81,6 +81,6 @@ def downloadCTranslate2Weight(path, weight_type="Small", func=None):
func(total_chunk/file_size) func(total_chunk/file_size)
with ZipFile(os_path.join(tmp_path, filename)) as zf: with ZipFile(os_path.join(tmp_path, filename)) as zf:
zf.extractall(os_path.join(current_directory, directory_name)) zf.extractall(path)
except Exception as e: except Exception as e:
print("error:downloadCTranslate2Weight()", e) print("error:downloadCTranslate2Weight()", e)

View File

@@ -8,6 +8,10 @@ pyyaml == 6.0.1
python-i18n == 0.3.9 python-i18n == 0.3.9
CTkToolTip == 0.8 CTkToolTip == 0.8
pyinstaller==6.2.0 pyinstaller==6.2.0
transformers[torch] transformers[torch]==4.37.2
sentencepiece==0.1.99 sentencepiece==0.1.99
ctranslate2==3.21.0 ctranslate2==3.24.0
faster-whisper==0.10.0
translators @ git+https://github.com/misyaguziya/translators@5.8.9
SpeechRecognition @ git+https://github.com/misyaguziya/custom_speech_recognition@3.10.2
tinyoscquery @ git+https://github.com/cyberkitsune/tinyoscquery@0.1.2

View File

@@ -1,5 +1,5 @@
from typing import Union from typing import Union
from os import path as os_path from os import path as os_path, rename as os_rename
from PIL.Image import open as Image_open from PIL.Image import open as Image_open
def getImageFile(file_name): def getImageFile(file_name):
@@ -50,3 +50,9 @@ def isUniqueStrings(unique_strings:Union[str, list], input_string:str, require=F
else: else:
# If require is False, check if unique strings are used exactly once # If require is False, check if unique strings are used exactly once
return all(count == 1 for count in counts) return all(count == 1 for count in counts)
# path先のweightフォルダがある場合にはそのフォルダ名をweightsに変更する
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"))

90
view.py
View File

@@ -29,6 +29,7 @@ class View():
font_family=config.FONT_FAMILY, font_family=config.FONT_FAMILY,
ui_language=config.UI_LANGUAGE, ui_language=config.UI_LANGUAGE,
is_reset_button_displayed_for_translation=config.IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION, is_reset_button_displayed_for_translation=config.IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION,
is_reset_button_displayed_for_whisper=config.IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER,
) )
if config.ENABLE_SPEAKER2CHATBOX is False: if config.ENABLE_SPEAKER2CHATBOX is False:
@@ -102,6 +103,7 @@ class View():
CALLBACK_UPDATE_SOFTWARE=None, CALLBACK_UPDATE_SOFTWARE=None,
CALLBACK_OPEN_FILEPATH_LOGS=None, CALLBACK_OPEN_FILEPATH_LOGS=None,
CALLBACK_OPEN_FILEPATH_CONFIG_FILE=None, CALLBACK_OPEN_FILEPATH_CONFIG_FILE=None,
CALLBACK_OPEN_WEBPAGE_DEEPL_AUTH_KEY=self.openWebPage_DeepL_Auth_Key,
CALLBACK_DELETE_MAIN_WINDOW=self.quitVRCT, CALLBACK_DELETE_MAIN_WINDOW=self.quitVRCT,
CALLBACK_QUIT_VRCT=None, CALLBACK_QUIT_VRCT=None,
@@ -184,6 +186,8 @@ class View():
VAR_UPDATE_AVAILABLE=StringVar(value=i18n.t("main_window.update_available")), VAR_UPDATE_AVAILABLE=StringVar(value=i18n.t("main_window.update_available")),
CALLBACK_MESSAGE_BOX_BIND_KEYSYM__UP=None,
CALLBACK_MESSAGE_BOX_BIND_KEYSYM__DOWN=None,
# Main Window Cover # Main Window Cover
VAR_LABEL_MAIN_WINDOW_COVER_MESSAGE=StringVar(value=""), VAR_LABEL_MAIN_WINDOW_COVER_MESSAGE=StringVar(value=""),
@@ -211,6 +215,7 @@ class View():
VAR_SIDE_MENU_LABEL_TRANSCRIPTION=StringVar(value=i18n.t("config_window.side_menu_labels.transcription")), VAR_SIDE_MENU_LABEL_TRANSCRIPTION=StringVar(value=i18n.t("config_window.side_menu_labels.transcription")),
VAR_SECOND_TITLE_TRANSCRIPTION_MIC=StringVar(value=i18n.t("config_window.side_menu_labels.transcription_mic")), VAR_SECOND_TITLE_TRANSCRIPTION_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_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_OTHERS=StringVar(value=i18n.t("config_window.side_menu_labels.others")), 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_ADVANCED_SETTINGS=StringVar(value=i18n.t("config_window.side_menu_labels.advanced_settings")),
@@ -280,7 +285,7 @@ class View():
VAR_DESC_CTRANSLATE2_WEIGHT_TYPE=StringVar(value=i18n.t("config_window.ctranslate2_weight_type.desc")), VAR_DESC_CTRANSLATE2_WEIGHT_TYPE=StringVar(value=i18n.t("config_window.ctranslate2_weight_type.desc")),
DICT_CTRANSLATE2_WEIGHT_TYPE=self.getSelectableCtranslate2WeightTypeDict(), DICT_CTRANSLATE2_WEIGHT_TYPE=self.getSelectableCtranslate2WeightTypeDict(),
CALLBACK_SET_CTRANSLATE2_WEIGHT_TYPE=None, CALLBACK_SET_CTRANSLATE2_WEIGHT_TYPE=None,
VAR_CTRANSLATE2_WEIGHT_TYPE=StringVar(value=self.getSelectableCtranslate2WeightTypeDict()[config.WEIGHT_TYPE]), VAR_CTRANSLATE2_WEIGHT_TYPE=StringVar(value=self.getSelectableCtranslate2WeightTypeDict()[config.CTRANSLATE2_WEIGHT_TYPE]),
VAR_LABEL_DEEPL_AUTH_KEY=StringVar(value=i18n.t( "config_window.deepl_auth_key.label")), VAR_LABEL_DEEPL_AUTH_KEY=StringVar(value=i18n.t( "config_window.deepl_auth_key.label")),
VAR_DESC_DEEPL_AUTH_KEY=StringVar( VAR_DESC_DEEPL_AUTH_KEY=StringVar(
@@ -291,6 +296,7 @@ class View():
), ),
CALLBACK_SET_DEEPL_AUTH_KEY=None, CALLBACK_SET_DEEPL_AUTH_KEY=None,
VAR_DEEPL_AUTH_KEY=StringVar(value=config.AUTH_KEYS["DeepL_API"]), VAR_DEEPL_AUTH_KEY=StringVar(value=config.AUTH_KEYS["DeepL_API"]),
VAR_OPEN_DEEPL_WEB_PAGE=StringVar(value=i18n.t( "config_window.deepl_auth_key.open_auth_key_webpage")),
# Transcription Tab (Mic) # Transcription Tab (Mic)
@@ -381,6 +387,19 @@ class View():
CALLBACK_FOCUS_OUT_SPEAKER_MAX_PHRASES=self.callbackBindFocusOut_SpeakerMaxPhrases, CALLBACK_FOCUS_OUT_SPEAKER_MAX_PHRASES=self.callbackBindFocusOut_SpeakerMaxPhrases,
# Transcription Tab (Whisper Internal AI Model)
VAR_LABEL_USE_WHISPER_FEATURE=StringVar(value=i18n.t("config_window.use_whisper_feature.label")),
VAR_DESC_USE_WHISPER_FEATURE=StringVar(value=i18n.t("config_window.use_whisper_feature.desc")),
CALLBACK_SET_USE_WHISPER_FEATURE=None,
VAR_USE_WHISPER_FEATURE=BooleanVar(value=config.USE_WHISPER_FEATURE),
VAR_LABEL_WHISPER_WEIGHT_TYPE=StringVar(value=i18n.t("config_window.whisper_weight_type.label")),
VAR_DESC_WHISPER_WEIGHT_TYPE=StringVar(value=i18n.t("config_window.whisper_weight_type.desc")),
DICT_WHISPER_WEIGHT_TYPE=self.getSelectableWhisperWeightTypeDict(),
CALLBACK_SET_WHISPER_WEIGHT_TYPE=None,
VAR_WHISPER_WEIGHT_TYPE=StringVar(value=self.getSelectableWhisperWeightTypeDict()[config.WHISPER_WEIGHT_TYPE]),
# Others Tab # Others Tab
VAR_LABEL_ENABLE_AUTO_CLEAR_MESSAGE_BOX=StringVar(value=i18n.t("config_window.auto_clear_the_message_box.label")), 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, VAR_DESC_ENABLE_AUTO_CLEAR_MESSAGE_BOX=None,
@@ -562,6 +581,10 @@ class View():
self.view_variable.CALLBACK_CLICKED_SEND_MESSAGE_BUTTON=pressedSendMessageButtonFunction self.view_variable.CALLBACK_CLICKED_SEND_MESSAGE_BUTTON=pressedSendMessageButtonFunction
self.view_variable.CALLBACK_MESSAGE_BOX_BIND_KEYSYM__UP=main_window_registers.get("message_box_bind_Up_KeyPress")
self.view_variable.CALLBACK_MESSAGE_BOX_BIND_KEYSYM__DOWN=main_window_registers.get("message_box_bind_Down_KeyPress")
entry_message_box.bind("<FocusIn>", main_window_registers.get("message_box_bind_FocusIn")) entry_message_box.bind("<FocusIn>", main_window_registers.get("message_box_bind_FocusIn"))
entry_message_box.bind("<FocusOut>", main_window_registers.get("message_box_bind_FocusOut")) entry_message_box.bind("<FocusOut>", main_window_registers.get("message_box_bind_FocusOut"))
@@ -624,6 +647,11 @@ class View():
self.view_variable.CALLBACK_SET_SPEAKER_PHRASE_TIMEOUT=config_window_registers.get("callback_set_speaker_phrase_timeout", None) self.view_variable.CALLBACK_SET_SPEAKER_PHRASE_TIMEOUT=config_window_registers.get("callback_set_speaker_phrase_timeout", None)
self.view_variable.CALLBACK_SET_SPEAKER_MAX_PHRASES=config_window_registers.get("callback_set_speaker_max_phrases", None) self.view_variable.CALLBACK_SET_SPEAKER_MAX_PHRASES=config_window_registers.get("callback_set_speaker_max_phrases", None)
# Transcription Tab (Internal AI Model)
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)
# Others Tab # Others Tab
self.view_variable.CALLBACK_SET_ENABLE_AUTO_CLEAR_MESSAGE_BOX=config_window_registers.get("callback_set_enable_auto_clear_chatbox", None) self.view_variable.CALLBACK_SET_ENABLE_AUTO_CLEAR_MESSAGE_BOX=config_window_registers.get("callback_set_enable_auto_clear_chatbox", None)
self.view_variable.CALLBACK_SET_ENABLE_SEND_ONLY_TRANSLATED_MESSAGES=config_window_registers.get("callback_set_send_only_translated_messages", None) self.view_variable.CALLBACK_SET_ENABLE_SEND_ONLY_TRANSLATED_MESSAGES=config_window_registers.get("callback_set_send_only_translated_messages", None)
@@ -678,6 +706,11 @@ class View():
) )
self.replaceMicThresholdCheckButton_Disabled() self.replaceMicThresholdCheckButton_Disabled()
if config.USE_WHISPER_FEATURE is True:
self.openWhisperWeightTypeWidget()
else:
self.closeWhisperWeightTypeWidget()
if config.ENABLE_SPEAKER2CHATBOX is False: if config.ENABLE_SPEAKER2CHATBOX is False:
vrct_gui._changeConfigWindowWidgetsStatus( vrct_gui._changeConfigWindowWidgetsStatus(
status="disabled", status="disabled",
@@ -882,8 +915,8 @@ class View():
@staticmethod @staticmethod
def getSelectableCtranslate2WeightTypeDict(): def getSelectableCtranslate2WeightTypeDict():
return { return {
config._SELECTABLE_CTRANSLATE2_WEIGHT_TYPE_DICT["Small"]: i18n.t("config_window.ctranslate2_weight_type.small", capacity="418MB"), config.SELECTABLE_CTRANSLATE2_WEIGHT_TYPE_DICT["Small"]: i18n.t("config_window.ctranslate2_weight_type.small", capacity="418MB"),
config._SELECTABLE_CTRANSLATE2_WEIGHT_TYPE_DICT["Large"]: i18n.t("config_window.ctranslate2_weight_type.large", capacity="1.2GB"), config.SELECTABLE_CTRANSLATE2_WEIGHT_TYPE_DICT["Large"]: i18n.t("config_window.ctranslate2_weight_type.large", capacity="1.2GB"),
} }
def useTranslationFeatureProcess(self, state:str): def useTranslationFeatureProcess(self, state:str):
@@ -919,6 +952,24 @@ class View():
vrct_gui.update() vrct_gui.update()
vrct_gui.config_window.lift() vrct_gui.config_window.lift()
@staticmethod
def getSelectableWhisperWeightTypeDict():
def callI18n(model_name, capacity, is_recommended=False):
if is_recommended is True:
return i18n.t("config_window.whisper_weight_type.recommended_model_template", model_name=model_name, capacity=capacity)
else:
return i18n.t("config_window.whisper_weight_type.model_template", model_name=model_name, capacity=capacity)
DICT_DATA = config.SELECTABLE_WHISPER_WEIGHT_TYPE_DICT
return {
DICT_DATA["tiny"]: callI18n("tiny", "74.5MB"),
DICT_DATA["base"]: callI18n("base", "141MB", True),
DICT_DATA["small"]: callI18n("small", "463MB"),
DICT_DATA["medium"]: callI18n("medium", "1.42GB"),
DICT_DATA["large-v1"]: callI18n("large-v1", "2.87GB"),
DICT_DATA["large-v2"]: callI18n("large-v2", "2.87GB"),
DICT_DATA["large-v3"]: callI18n("large-v3", "2.87GB"),
}
# Open Webpage Functions # Open Webpage Functions
def openWebPage_Booth(self): def openWebPage_Booth(self):
@@ -929,6 +980,10 @@ class View():
self.openWebPage(config.DOCUMENTS_URL) self.openWebPage(config.DOCUMENTS_URL)
self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.opened_web_page_vrct_documents")) self._printToTextbox_Info(i18n.t("main_window.textbox_system_message.opened_web_page_vrct_documents"))
def openWebPage_DeepL_Auth_Key(self):
self.openWebPage(config.DEEPL_AUTH_KEY_PAGE_URL)
# Widget Control # Widget Control
# Common # Common
@@ -1014,7 +1069,8 @@ class View():
self.restart_required_configs_pre_data.ui_scaling == config.UI_SCALING and self.restart_required_configs_pre_data.ui_scaling == config.UI_SCALING and
self.restart_required_configs_pre_data.font_family == config.FONT_FAMILY and self.restart_required_configs_pre_data.font_family == config.FONT_FAMILY and
self.restart_required_configs_pre_data.ui_language == config.UI_LANGUAGE and self.restart_required_configs_pre_data.ui_language == config.UI_LANGUAGE and
self.restart_required_configs_pre_data.is_reset_button_displayed_for_translation == config.IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION self.restart_required_configs_pre_data.is_reset_button_displayed_for_translation == config.IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION and
self.restart_required_configs_pre_data.is_reset_button_displayed_for_whisper == config.IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER
) )
if locale is None: if locale is None:
@@ -1069,7 +1125,7 @@ class View():
self.view_variable.VAR_CTRANSLATE2_WEIGHT_TYPE.set(self.getSelectableCtranslate2WeightTypeDict()[selected_weight_type]) self.view_variable.VAR_CTRANSLATE2_WEIGHT_TYPE.set(self.getSelectableCtranslate2WeightTypeDict()[selected_weight_type])
def setLatestCTranslate2WeightType(self): def setLatestCTranslate2WeightType(self):
selected_weight_type = self.getSelectableCtranslate2WeightTypeDict()[config.WEIGHT_TYPE] selected_weight_type = self.getSelectableCtranslate2WeightTypeDict()[config.CTRANSLATE2_WEIGHT_TYPE]
self.view_variable.VAR_CTRANSLATE2_WEIGHT_TYPE.set(selected_weight_type) self.view_variable.VAR_CTRANSLATE2_WEIGHT_TYPE.set(selected_weight_type)
@@ -1082,6 +1138,23 @@ class View():
vrct_gui.config_window.sb__ctranslate2_weight_type.grid_remove() vrct_gui.config_window.sb__ctranslate2_weight_type.grid_remove()
def openWhisperWeightTypeWidget(self):
vrct_gui.config_window.sb__use_whisper_feature.grid()
vrct_gui.config_window.sb__whisper_weight_type.grid()
def closeWhisperWeightTypeWidget(self):
vrct_gui.config_window.sb__use_whisper_feature.grid()
vrct_gui.config_window.sb__whisper_weight_type.grid_remove()
def updateSelectedWhisperWeightType(self, selected_weight_type:str):
self.view_variable.VAR_WHISPER_WEIGHT_TYPE.set(self.getSelectableWhisperWeightTypeDict()[selected_weight_type])
def setLatestCTranslate2WeightType(self):
selected_weight_type = self.getSelectableWhisperWeightTypeDict()[config.WHISPER_WEIGHT_TYPE]
self.view_variable.VAR_WHISPER_WEIGHT_TYPE.set(selected_weight_type)
def openMicEnergyThresholdWidget(self): def openMicEnergyThresholdWidget(self):
self.view_variable.VAR_LABEL_MIC_DYNAMIC_ENERGY_THRESHOLD.set(i18n.t("config_window.mic_dynamic_energy_threshold.label_for_manual")) self.view_variable.VAR_LABEL_MIC_DYNAMIC_ENERGY_THRESHOLD.set(i18n.t("config_window.mic_dynamic_energy_threshold.label_for_manual"))
self.view_variable.VAR_DESC_MIC_DYNAMIC_ENERGY_THRESHOLD.set(i18n.t("config_window.mic_dynamic_energy_threshold.desc_for_manual")) self.view_variable.VAR_DESC_MIC_DYNAMIC_ENERGY_THRESHOLD.set(i18n.t("config_window.mic_dynamic_energy_threshold.desc_for_manual"))
@@ -1637,6 +1710,13 @@ class View():
def clearMessageBox(self): def clearMessageBox(self):
self._clearTextBox(vrct_gui.entry_message_box) self._clearTextBox(vrct_gui.entry_message_box)
@staticmethod
def insertMessageBox(text):
vrct_gui.entry_message_box.insert("end", text)
def replaceMessageBox(self, text):
self.clearMessageBox()
self.insertMessageBox(text)

View File

@@ -7,7 +7,7 @@ from ._createSettingBoxContainer import _createSettingBoxContainer
from .setting_box_containers.setting_box_appearance import createSettingBox_Appearance from .setting_box_containers.setting_box_appearance import createSettingBox_Appearance
from .setting_box_containers.setting_box_transcription import createSettingBox_Mic, createSettingBox_Speaker from .setting_box_containers.setting_box_transcription import createSettingBox_Mic, createSettingBox_Speaker, createSettingBox_InternalModel
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_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_advanced_settings import createSettingBox_AdvancedSettings
from .setting_box_containers.setting_box_translation import createSettingBox_Translation from .setting_box_containers.setting_box_translation import createSettingBox_Translation
@@ -94,6 +94,10 @@ def createSideMenuAndSettingsBoxContainers(config_window, settings, view_variabl
"var_section_title": view_variable.VAR_SECOND_TITLE_TRANSCRIPTION_SPEAKER, "var_section_title": view_variable.VAR_SECOND_TITLE_TRANSCRIPTION_SPEAKER,
"setting_box": createSettingBox_Speaker "setting_box": createSettingBox_Speaker
}, },
{
"var_section_title": view_variable.VAR_SECOND_TITLE_TRANSCRIPTION_INTERNAL_MODEL,
"setting_box": createSettingBox_InternalModel
},
] ]
}, },
}, },

View File

@@ -5,7 +5,7 @@ from typing import Union
from customtkinter import CTkFont, CTkFrame, CTkLabel, CTkEntry, CTkSlider, CTkSwitch, CTkCheckBox, CTkProgressBar, CTkImage, CTkRadioButton from customtkinter import CTkFont, CTkFrame, CTkLabel, CTkEntry, CTkSlider, CTkSwitch, CTkCheckBox, CTkProgressBar, CTkImage, CTkRadioButton
from CTkToolTip import * from CTkToolTip import *
from vrct_gui.ui_utils import createButtonWithImage, getLatestWidth, createOptionMenuBox, getLatestHeight, bindButtonFunctionAndColor, bindEnterAndLeaveFunction, bindButtonReleaseFunction, bindButtonPressFunction from vrct_gui.ui_utils import createButtonWithImage, getLatestWidth, createOptionMenuBox, getLatestHeight, bindButtonFunctionAndColor, bindEnterAndLeaveFunction, bindButtonReleaseFunction, bindButtonPressFunction, createLabelButton
from vrct_gui import vrct_gui from vrct_gui import vrct_gui
from utils import isEven, callFunctionIfCallable from utils import isEven, callFunctionIfCallable
@@ -615,6 +615,75 @@ class _SettingBoxGenerator():
return setting_box_frame return setting_box_frame
def createSettingBoxEntry_AuthKey(self,
for_var_label_text, for_var_desc_text,
entry_attr_name,
entry_width,
entry_textvariable,
entry_bind__Any_KeyRelease,
entry_bind__FocusOut=None,
open_authkey_page_command=None,
open_authkey_text_variable=None,
image_file=None,
):
(setting_box_frame, setting_box_item_frame) = self._createSettingBoxFrame(entry_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(0, weight=1)
def adjusted_command__for_entry_bind__Any_KeyRelease(e):
entry_bind__Any_KeyRelease(e.widget.get())
entry_widget = CTkEntry(
all_wrapper,
text_color=self.settings.ctm.SB__ENTRY_TEXT_COLOR,
fg_color=self.settings.ctm.SB__ENTRY_BG_COLOR,
border_color=self.settings.ctm.SB__ENTRY_BORDER_COLOR,
width=entry_width,
height=self.settings.uism.SB__PROGRESSBAR_X_SLIDER__ENTRY_HEIGHT,
textvariable=entry_textvariable,
font=CTkFont(family=self.settings.FONT_FAMILY, size=self.settings.uism.SB__ENTRY_FONT_SIZE, weight="normal"),
)
entry_widget.bind("<Any-KeyRelease>", adjusted_command__for_entry_bind__Any_KeyRelease)
setattr(self.config_window, entry_attr_name, entry_widget)
entry_widget.grid(row=0, column=SETTING_BOX_COLUMN, sticky="e")
if entry_bind__FocusOut is not None:
entry_widget.bind("<FocusOut>", entry_bind__FocusOut, "+")
(open_page_button, label_button_label_widget, label_button_img_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=open_authkey_text_variable,
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=open_authkey_page_command,
label_button_position="center",
image_file=image_file,
image_size=self.settings.uism.SB__AUTHKEY_WEBPAGE_BUTTON_IMG_SIZE,
label_button_padx_between_img=self.settings.uism.SB__AUTHKEY_WEBPAGE_PADX_BETWEEN_LABEL_AND_ICON,
)
open_page_button.grid(row=1, column=SETTING_BOX_COLUMN, pady=(self.settings.uism.SB__AUTHKEY_WEBPAGE_BUTTON_TOP_PADY,0))
return setting_box_frame

View File

@@ -1,2 +1,3 @@
from .createSettingBox_Mic import createSettingBox_Mic from .createSettingBox_Mic import createSettingBox_Mic
from .createSettingBox_Speaker import createSettingBox_Speaker from .createSettingBox_Speaker import createSettingBox_Speaker
from .createSettingBox_InternalModel import createSettingBox_InternalModel

View File

@@ -0,0 +1,37 @@
from utils import callFunctionIfCallable
from .._SettingBoxGenerator import _SettingBoxGenerator
def createSettingBox_InternalModel(setting_box_wrapper, config_window, settings, view_variable):
sbg = _SettingBoxGenerator(setting_box_wrapper, config_window, settings, view_variable)
createSettingBoxSwitch = sbg.createSettingBoxSwitch
createSettingBoxDropdownMenu = sbg.createSettingBoxDropdownMenu
def switchUseWhisperFeatureCallback(switch_widget):
callFunctionIfCallable(view_variable.CALLBACK_SET_USE_WHISPER_FEATURE, switch_widget.get())
def optionmenuWhisperWeightTypeCallback(value):
callFunctionIfCallable(view_variable.CALLBACK_SET_WHISPER_WEIGHT_TYPE, value)
row=0
config_window.sb__use_whisper_feature = createSettingBoxSwitch(
for_var_label_text=view_variable.VAR_LABEL_USE_WHISPER_FEATURE,
for_var_desc_text=view_variable.VAR_DESC_USE_WHISPER_FEATURE,
switch_attr_name="sb__switch_use_whisper_feature",
command=lambda: switchUseWhisperFeatureCallback(config_window.sb__switch_use_whisper_feature),
variable=view_variable.VAR_USE_WHISPER_FEATURE
)
config_window.sb__use_whisper_feature.grid(row=row, pady=0)
row+=1
config_window.sb__whisper_weight_type = createSettingBoxDropdownMenu(
for_var_label_text=view_variable.VAR_LABEL_WHISPER_WEIGHT_TYPE,
for_var_desc_text=view_variable.VAR_DESC_WHISPER_WEIGHT_TYPE,
optionmenu_attr_name="sb__optionmenu_whisper_weight_type",
dropdown_menu_values=view_variable.DICT_WHISPER_WEIGHT_TYPE,
command=lambda value: optionmenuWhisperWeightTypeCallback(value),
variable=view_variable.VAR_WHISPER_WEIGHT_TYPE,
)
config_window.sb__whisper_weight_type.grid(row=row, pady=0)
row+=1

View File

@@ -6,7 +6,7 @@ def createSettingBox_Translation(setting_box_wrapper, config_window, settings, v
sbg = _SettingBoxGenerator(setting_box_wrapper, config_window, settings, view_variable) sbg = _SettingBoxGenerator(setting_box_wrapper, config_window, settings, view_variable)
createSettingBoxSwitch = sbg.createSettingBoxSwitch createSettingBoxSwitch = sbg.createSettingBoxSwitch
createSettingBoxDropdownMenu = sbg.createSettingBoxDropdownMenu createSettingBoxDropdownMenu = sbg.createSettingBoxDropdownMenu
createSettingBoxEntry = sbg.createSettingBoxEntry createSettingBoxEntry_AuthKey = sbg.createSettingBoxEntry_AuthKey
def switchUseTranslationFeatureCallback(switch_widget): def switchUseTranslationFeatureCallback(switch_widget):
callFunctionIfCallable(view_variable.CALLBACK_SET_USE_TRANSLATION_FEATURE, switch_widget.get()) callFunctionIfCallable(view_variable.CALLBACK_SET_USE_TRANSLATION_FEATURE, switch_widget.get())
@@ -41,13 +41,16 @@ def createSettingBox_Translation(setting_box_wrapper, config_window, settings, v
row+=1 row+=1
config_window.sb__deepl_auth_key = createSettingBoxEntry( config_window.sb__deepl_auth_key = createSettingBoxEntry_AuthKey(
for_var_label_text=view_variable.VAR_LABEL_DEEPL_AUTH_KEY, for_var_label_text=view_variable.VAR_LABEL_DEEPL_AUTH_KEY,
for_var_desc_text=view_variable.VAR_DESC_DEEPL_AUTH_KEY, for_var_desc_text=view_variable.VAR_DESC_DEEPL_AUTH_KEY,
entry_attr_name="sb__entry_deepl_auth_key", entry_attr_name="sb__entry_deepl_auth_key",
entry_width=settings.uism.RESPONSIVE_UI_SIZE_INT_300, entry_width=settings.uism.RESPONSIVE_UI_SIZE_INT_300,
entry_bind__Any_KeyRelease=lambda value: deeplAuthKeyCallback(value), entry_bind__Any_KeyRelease=lambda value: deeplAuthKeyCallback(value),
entry_textvariable=view_variable.VAR_DEEPL_AUTH_KEY, entry_textvariable=view_variable.VAR_DEEPL_AUTH_KEY,
open_authkey_page_command=lambda _e: callFunctionIfCallable(view_variable.CALLBACK_OPEN_WEBPAGE_DEEPL_AUTH_KEY),
open_authkey_text_variable=view_variable.VAR_OPEN_DEEPL_WEB_PAGE,
image_file=settings.image_file.LINK_ICON
) )
config_window.sb__deepl_auth_key.grid(row=row, pady=0) config_window.sb__deepl_auth_key.grid(row=row, pady=0)
row+=1 row+=1

View File

@@ -19,6 +19,9 @@ def createEntryMessageBox(settings, main_window, view_variable):
border_width=settings.uism.TEXTBOX_ENTRY_BORDER_SIZE, border_width=settings.uism.TEXTBOX_ENTRY_BORDER_SIZE,
height=0, height=0,
font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TEXTBOX_ENTRY_FONT_SIZE, weight="normal"), font=CTkFont(family=settings.FONT_FAMILY, size=settings.uism.TEXTBOX_ENTRY_FONT_SIZE, weight="normal"),
undo=True,
autoseparators=True,
maxundo=64,
) )
main_window.entry_message_box.grid(row=0, column=0, padx=settings.uism.TEXTBOX_ENTRY_PADX, pady=settings.uism.TEXTBOX_ENTRY_PADY, sticky="nsew") main_window.entry_message_box.grid(row=0, column=0, padx=settings.uism.TEXTBOX_ENTRY_PADX, pady=settings.uism.TEXTBOX_ENTRY_PADY, sticky="nsew")
@@ -28,6 +31,14 @@ def createEntryMessageBox(settings, main_window, view_variable):
"Delete", "Select", "Up", "Down", "Next", "End", "Print", "Delete", "Select", "Up", "Down", "Next", "End", "Print",
"Prior","Insert","Home", "Left", "Clear", "Right", "Linefeed" "Prior","Insert","Home", "Left", "Clear", "Right", "Linefeed"
] ]
if e.keysym == "Up":
callFunctionIfCallable(view_variable.CALLBACK_MESSAGE_BOX_BIND_KEYSYM__UP)
return "break"
if e.keysym == "Down":
callFunctionIfCallable(view_variable.CALLBACK_MESSAGE_BOX_BIND_KEYSYM__DOWN)
return "break"
if e.keysym != "??": if e.keysym != "??":
if len(e.char) != 0 and e.keysym in BREAK_KEYSYM_LIST: if len(e.char) != 0 and e.keysym in BREAK_KEYSYM_LIST:
main_window.entry_message_box.insert("end", e.char) main_window.entry_message_box.insert("end", e.char)
@@ -35,6 +46,15 @@ def createEntryMessageBox(settings, main_window, view_variable):
main_window.entry_message_box.bind("<Any-KeyPress>", messageBoxAnyKeyPress) main_window.entry_message_box.bind("<Any-KeyPress>", messageBoxAnyKeyPress)
def messageBoxRedoFunction(_e):
try:
main_window.entry_message_box.edit_redo()
except:
pass
main_window.entry_message_box.bind("<Control-Shift-Z>", messageBoxRedoFunction, "+")
main_window.entry_message_box.bind("<Control-Y>", messageBoxRedoFunction, "+")
main_window.main_send_message_button_container = CTkFrame(main_window.main_entry_message_container, corner_radius=settings.uism.SEND_MESSAGE_BUTTON_CORNER_RADIUS, fg_color=settings.ctm.SEND_MESSAGE_BUTTON_BG_COLOR, width=0, height=0) main_window.main_send_message_button_container = CTkFrame(main_window.main_entry_message_container, corner_radius=settings.uism.SEND_MESSAGE_BUTTON_CORNER_RADIUS, fg_color=settings.ctm.SEND_MESSAGE_BUTTON_BG_COLOR, width=0, height=0)
main_window.main_send_message_button_container.grid(row=0, column=1, padx=(0, settings.uism.TEXTBOX_ENTRY_PADX), pady=settings.uism.TEXTBOX_ENTRY_PADY, sticky="nsew") main_window.main_send_message_button_container.grid(row=0, column=1, padx=(0, settings.uism.TEXTBOX_ENTRY_PADX), pady=settings.uism.TEXTBOX_ENTRY_PADY, sticky="nsew")

View File

@@ -330,6 +330,7 @@ def _darkTheme(base_color):
REDO_ICON = getImageFileFromUiUtils("redo_icon_white.png"), REDO_ICON = getImageFileFromUiUtils("redo_icon_white.png"),
SWAP_ICON = getImageFileFromUiUtils("swap_icon_white.png"), SWAP_ICON = getImageFileFromUiUtils("swap_icon_white.png"),
FOLDER_OPEN_ICON = getImageFileFromUiUtils("folder_open_icon_white.png"), FOLDER_OPEN_ICON = getImageFileFromUiUtils("folder_open_icon_white.png"),
LINK_ICON = getImageFileFromUiUtils("link_icon_white.png"),
), ),
) )

View File

@@ -324,6 +324,7 @@ def _lightTheme(base_color):
REDO_ICON = getImageFileFromUiUtils("redo_icon_black.png"), REDO_ICON = getImageFileFromUiUtils("redo_icon_black.png"),
SWAP_ICON = getImageFileFromUiUtils("swap_icon_black.png"), SWAP_ICON = getImageFileFromUiUtils("swap_icon_black.png"),
FOLDER_OPEN_ICON = getImageFileFromUiUtils("folder_open_icon_black.png"), FOLDER_OPEN_ICON = getImageFileFromUiUtils("folder_open_icon_black.png"),
LINK_ICON = getImageFileFromUiUtils("link_icon_black.png"),
), ),
) )

View File

@@ -349,6 +349,13 @@ class UiScalingManager():
self.config_window.SB__MESSAGE_FORMAT__ENTRIES_BOTTOM_PADY = (0, self._calculateUiSize(14)) self.config_window.SB__MESSAGE_FORMAT__ENTRIES_BOTTOM_PADY = (0, self._calculateUiSize(14))
self.config_window.SB__AUTHKEY_WEBPAGE_BUTTON_IPADX = self._calculateUiSize(12)
self.config_window.SB__AUTHKEY_WEBPAGE_BUTTON_IPADY = self._calculateUiSize(6)
self.config_window.SB__AUTHKEY_WEBPAGE_BUTTON_LABEL_FONT_SIZE = self._calculateUiSize(12)
self.config_window.SB__AUTHKEY_WEBPAGE_BUTTON_IMG_SIZE = self.dupTuple(self._calculateUiSize(12))
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__BUTTON_IPADXY = self._calculateUiSize(16) self.config_window.SB__BUTTON_IPADXY = self._calculateUiSize(16)
self.config_window.SB__BUTTON_ICON_SIZE = self._calculateUiSize(24) self.config_window.SB__BUTTON_ICON_SIZE = self._calculateUiSize(24)

View File

@@ -168,7 +168,27 @@ def createButtonWithImage(parent_widget, button_image_size, button_ipadxy, butto
return button_wrapper return button_wrapper
def createLabelButton(parent_widget, label_button_bg_color, label_button_hovered_bg_color, label_button_clicked_bg_color, label_button_ipadx, label_button_ipady, variable, font_family, font_size, text_color, label_button_clicked_command, label_button_position=None, label_button_padx_between_img=0, label_button_min_height=None, label_button_min_width=None): def createLabelButton(
parent_widget,
label_button_bg_color,
label_button_hovered_bg_color,
label_button_clicked_bg_color,
label_button_ipadx,
label_button_ipady,
variable,
font_family,
font_size,
text_color,
label_button_clicked_command,
label_button_position=None,
label_button_padx_between_img=0,
image_file=None,
image_size=None,
image_widget_attr_name=None,
label_button_min_height=None,
label_button_min_width=None,
setattr_widget=None,
):
label_button_box = CTkFrame(parent_widget, corner_radius=6, fg_color=label_button_bg_color, cursor="hand2") label_button_box = CTkFrame(parent_widget, corner_radius=6, fg_color=label_button_bg_color, cursor="hand2")
@@ -181,7 +201,7 @@ def createLabelButton(parent_widget, label_button_bg_color, label_button_hovered
label_button_box.grid_columnconfigure(0, minsize=label_button_min_width) label_button_box.grid_columnconfigure(0, minsize=label_button_min_width)
label_button_label_wrapper = CTkFrame(label_button_box, corner_radius=0, fg_color=label_button_bg_color) label_button_label_wrapper = CTkFrame(label_button_box, corner_radius=0, fg_color=label_button_bg_color)
label_button_label_wrapper.grid(row=0, column=0, padx=label_button_ipadx, pady=label_button_ipady, sticky="ew") label_button_label_wrapper.grid(row=0, column=0, padx=label_button_ipadx, pady=label_button_ipady)
LABEL_COLUMN=0 LABEL_COLUMN=0
if label_button_position == "center": if label_button_position == "center":
@@ -198,30 +218,51 @@ def createLabelButton(parent_widget, label_button_bg_color, label_button_hovered
label_button_label_widget.grid(row=0, column=LABEL_COLUMN, padx=(0, label_button_padx_between_img)) label_button_label_widget.grid(row=0, column=LABEL_COLUMN, padx=(0, label_button_padx_between_img))
bindEnterAndLeaveColor([label_button_label_wrapper, label_button_box, label_button_label_widget], label_button_hovered_bg_color, label_button_bg_color) register_widgets = [label_button_label_wrapper, label_button_box, label_button_label_widget]
bindButtonPressColor([label_button_label_wrapper, label_button_box, label_button_label_widget], label_button_clicked_bg_color, label_button_hovered_bg_color) if image_file is not None:
label_button_label_wrapper.grid_columnconfigure((0,3), weight=1)
label_button_img_widget = CTkLabel(
label_button_label_wrapper,
text=None,
corner_radius=0,
height=0,
image=CTkImage(image_file, size=image_size)
)
if image_widget_attr_name is not None:
setattr(setattr_widget, image_widget_attr_name, label_button_img_widget)
label_button_img_widget.grid(row=0, column=LABEL_COLUMN+1)
register_widgets.append(label_button_img_widget)
bindEnterAndLeaveColor(register_widgets, label_button_hovered_bg_color, label_button_bg_color)
bindButtonPressColor(register_widgets, label_button_clicked_bg_color, label_button_hovered_bg_color)
bindButtonReleaseFunction([label_button_label_wrapper, label_button_box, label_button_label_widget], label_button_clicked_command)
def bindEventFromWidgets(): def bindEventFromWidgets():
bindButtonReleaseFunction([label_button_label_wrapper, label_button_box, label_button_label_widget], label_button_clicked_command) bindButtonReleaseFunction(register_widgets, label_button_clicked_command)
bindEventFromWidgets()
def unbindEventFromWidgets(): def unbindEventFromWidgets():
unbindEnterLEaveButtonPressButtonReleaseFunction([label_button_label_wrapper, label_button_box, label_button_label_widget]) unbindEnterLEaveButtonPressButtonReleaseFunction(register_widgets)
bindEventFromWidgets()
label_button_box.unbindFunction = unbindEventFromWidgets label_button_box.unbindFunction = unbindEventFromWidgets
label_button_box.bindFunction = bindEventFromWidgets label_button_box.bindFunction = bindEventFromWidgets
if image_file is not None:
return (label_button_box, label_button_label_widget, label_button_img_widget)
else:
return (label_button_box, label_button_label_widget) return (label_button_box, label_button_label_widget)
def createOptionMenuBox(parent_widget, optionmenu_bg_color, optionmenu_hovered_bg_color, optionmenu_clicked_bg_color, optionmenu_ipadx, optionmenu_ipady, variable, font_family, font_size, text_color, image_file, image_size, optionmenu_clicked_command, optionmenu_position=None, optionmenu_padx_between_img=0, optionmenu_min_height=None, optionmenu_min_width=None, setattr_widget=None, image_widget_attr_name=None): def createOptionMenuBox(parent_widget, optionmenu_bg_color, optionmenu_hovered_bg_color, optionmenu_clicked_bg_color, optionmenu_ipadx, optionmenu_ipady, variable, font_family, font_size, text_color, image_file, image_size, optionmenu_clicked_command, optionmenu_position=None, optionmenu_padx_between_img=0, optionmenu_min_height=None, optionmenu_min_width=None, setattr_widget=None, image_widget_attr_name=None):
option_menu_box = CTkFrame(parent_widget, corner_radius=6, fg_color=optionmenu_bg_color, cursor="hand2") option_menu_box = CTkFrame(parent_widget, corner_radius=6, fg_color=optionmenu_bg_color, cursor="hand2")