From 3aa39bd1ee9d8a9d52858ff3e9e4b894b3455997 Mon Sep 17 00:00:00 2001 From: misyaguziya Date: Wed, 1 May 2024 15:02:37 +0900 Subject: [PATCH 01/14] =?UTF-8?q?[Unmask]=20overlay=E3=81=AE=E5=87=A6?= =?UTF-8?q?=E7=90=86=E3=82=92=E6=88=BB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller.py | 104 +++++++++++++++++++++--------------------- model.py | 122 +++++++++++++++++++++++++------------------------- 2 files changed, 113 insertions(+), 113 deletions(-) diff --git a/controller.py b/controller.py index dbc88560..329f0fc3 100644 --- a/controller.py +++ b/controller.py @@ -27,10 +27,10 @@ def callbackFilepathConfigFile(): def callbackQuitVrct(): setMainWindowGeometry() -# def callbackEnableEasterEgg(): -# config.IS_EASTER_EGG_ENABLED = True -# config.OVERLAY_UI_TYPE = "sakura" -# view.printToTextbox_enableEasterEgg() +def callbackEnableEasterEgg(): + config.IS_EASTER_EGG_ENABLED = True + config.OVERLAY_UI_TYPE = "sakura" + view.printToTextbox_enableEasterEgg() def setMainWindowGeometry(): PRE_SCALING_INT = strPctToInt(view.getPreUiScaling()) @@ -161,14 +161,14 @@ def receiveSpeakerMessage(message): xsoverlay_message = messageFormatter("RECEIVED", translation, message) model.notificationXSOverlay(xsoverlay_message) - # if model.overlay.initialized is False: - # model.startOverlay() - # else: - # if config.ENABLE_OVERLAY_SMALL_LOG is True: - # overlay_image = model.createOverlayImageShort(message, translation) - # model.updateOverlay(overlay_image) - # # overlay_image = model.createOverlayImageLong("receive", message, translation) - # # model.updateOverlay(overlay_image) + if config.ENABLE_OVERLAY_SMALL_LOG is True: + if model.overlay.initialized is False: + model.startOverlay() + if model.overlay.initialized is True: + overlay_image = model.createOverlayImageShort(message, translation) + model.updateOverlay(overlay_image) + # overlay_image = model.createOverlayImageLong("receive", message, translation) + # model.updateOverlay(overlay_image) # ------------Speaker2Chatbox------------ if config.ENABLE_SPEAKER2CHATBOX is True: @@ -858,44 +858,44 @@ def callbackSetWhisperWeightType(value): config.SELECTED_TRANSCRIPTION_ENGINE = "Google" view.showRestartButtonIfRequired() -# # VR Tab -# def callbackSetOverlaySettings(value, set_type:str): -# print("callbackSetOverlaySettings", value, set_type) -# pre_settings = config.OVERLAY_SETTINGS -# pre_settings[set_type] = value -# config.OVERLAY_SETTINGS = pre_settings -# match (set_type): -# case "opacity": -# model.updateOverlayImageOpacity() -# case "ui_scaling": -# model.updateOverlayImageUiScaling() +# VR Tab +def callbackSetOverlaySettings(value, set_type:str): + print("callbackSetOverlaySettings", value, set_type) + pre_settings = config.OVERLAY_SETTINGS + pre_settings[set_type] = value + config.OVERLAY_SETTINGS = pre_settings + match (set_type): + case "opacity": + model.updateOverlayImageOpacity() + case "ui_scaling": + model.updateOverlayImageUiScaling() -# def callbackSetEnableOverlaySmallLog(value): -# print("callbackSetEnableOverlaySmallLog", value) -# config.ENABLE_OVERLAY_SMALL_LOG = value +def callbackSetEnableOverlaySmallLog(value): + print("callbackSetEnableOverlaySmallLog", value) + config.ENABLE_OVERLAY_SMALL_LOG = value -# if config.ENABLE_OVERLAY_SMALL_LOG is True: -# pass -# else: -# if model.overlay.initialized is True: -# model.clearOverlayImage() + if config.ENABLE_OVERLAY_SMALL_LOG is True: + pass + else: + if model.overlay.initialized is True: + model.clearOverlayImage() -# def callbackSetOverlaySmallLogSettings(value, set_type:str): -# print("callbackSetOverlaySmallLogSettings", value, set_type) -# pre_settings = config.OVERLAY_SMALL_LOG_SETTINGS -# pre_settings[set_type] = value -# config.OVERLAY_SMALL_LOG_SETTINGS = pre_settings -# match (set_type): -# case "x_pos": -# model.updateOverlayPosition() -# case "y_pos": -# model.updateOverlayPosition() -# case "depth": -# model.updateOverlayPosition() -# case "display_duration": -# model.updateOverlayTimes() -# case "fadeout_duration": -# model.updateOverlayTimes() +def callbackSetOverlaySmallLogSettings(value, set_type:str): + print("callbackSetOverlaySmallLogSettings", value, set_type) + pre_settings = config.OVERLAY_SMALL_LOG_SETTINGS + pre_settings[set_type] = value + config.OVERLAY_SMALL_LOG_SETTINGS = pre_settings + match (set_type): + case "x_pos": + model.updateOverlayPosition() + case "y_pos": + model.updateOverlayPosition() + case "depth": + model.updateOverlayPosition() + case "display_duration": + model.updateOverlayTimes() + case "fadeout_duration": + model.updateOverlayTimes() # Others Tab def callbackSetEnableAutoClearMessageBox(value): @@ -1049,7 +1049,7 @@ def createMainWindow(splash): # set UI and callback view.register( common_registers={ - # "callback_enable_easter_egg": callbackEnableEasterEgg, + "callback_enable_easter_egg": callbackEnableEasterEgg, "callback_update_software": callbackUpdateSoftware, "callback_restart_software": callbackRestartSoftware, @@ -1137,10 +1137,10 @@ def createMainWindow(splash): "callback_set_use_whisper_feature": callbackSetUserWhisperFeature, "callback_set_whisper_weight_type": callbackSetWhisperWeightType, - # # VR Tab - # "callback_set_overlay_settings": callbackSetOverlaySettings, - # "callback_set_enable_overlay_small_log": callbackSetEnableOverlaySmallLog, - # "callback_set_overlay_small_log_settings": callbackSetOverlaySmallLogSettings, + # VR Tab + "callback_set_overlay_settings": callbackSetOverlaySettings, + "callback_set_enable_overlay_small_log": callbackSetEnableOverlaySmallLog, + "callback_set_overlay_small_log_settings": callbackSetOverlaySmallLogSettings, # Others Tab "callback_set_enable_auto_clear_chatbox": callbackSetEnableAutoClearMessageBox, diff --git a/model.py b/model.py index ae0cc25d..8095bd94 100644 --- a/model.py +++ b/model.py @@ -26,8 +26,8 @@ from models.translation.translation_languages import translation_lang from models.transcription.transcription_languages import transcription_lang from models.translation.translation_utils import checkCTranslate2Weight from models.transcription.transcription_whisper import checkWhisperWeight -# from models.overlay.overlay import Overlay -# from models.overlay.overlay_image import OverlayImage +from models.overlay.overlay import Overlay +from models.overlay.overlay_image import OverlayImage from config import config @@ -72,18 +72,18 @@ class Model: self.previous_receive_message = "" self.translator = Translator() self.keyword_processor = KeywordProcessor() - # self.overlay = Overlay( - # config.OVERLAY_SMALL_LOG_SETTINGS["x_pos"], - # config.OVERLAY_SMALL_LOG_SETTINGS["y_pos"], - # config.OVERLAY_SMALL_LOG_SETTINGS["depth"], - # config.OVERLAY_SMALL_LOG_SETTINGS["display_duration"], - # config.OVERLAY_SMALL_LOG_SETTINGS["fadeout_duration"], - # config.OVERLAY_SETTINGS["opacity"], - # config.OVERLAY_SETTINGS["ui_scaling"], - # ) - # self.overlay_image = OverlayImage() - # self.pre_overlay_message = None - # self.th_overlay = None + self.overlay = Overlay( + config.OVERLAY_SMALL_LOG_SETTINGS["x_pos"], + config.OVERLAY_SMALL_LOG_SETTINGS["y_pos"], + config.OVERLAY_SMALL_LOG_SETTINGS["depth"], + config.OVERLAY_SMALL_LOG_SETTINGS["display_duration"], + config.OVERLAY_SMALL_LOG_SETTINGS["fadeout_duration"], + config.OVERLAY_SETTINGS["opacity"], + config.OVERLAY_SETTINGS["ui_scaling"], + ) + self.overlay_image = OverlayImage() + self.pre_overlay_message = None + self.th_overlay = None def checkCTranslatorCTranslate2ModelWeight(self): return checkCTranslate2Weight(config.PATH_LOCAL, config.CTRANSLATE2_WEIGHT_TYPE) @@ -584,65 +584,65 @@ class Model: def notificationXSOverlay(self, message): xsoverlayForVRCT(content=f"{message}") - # def createOverlayImageShort(self, message, translation): - # your_language = config.TARGET_LANGUAGE - # target_language = config.SOURCE_LANGUAGE - # ui_type = config.OVERLAY_UI_TYPE - # self.pre_overlay_message = { - # "message" : message, - # "your_language" : your_language, - # "translation" : translation, - # "target_language" : target_language, - # "ui_type" : ui_type, - # } - # return self.overlay_image.createOverlayImageShort(message, your_language, translation, target_language, ui_type) + def createOverlayImageShort(self, message, translation): + your_language = config.TARGET_LANGUAGE + target_language = config.SOURCE_LANGUAGE + ui_type = config.OVERLAY_UI_TYPE + self.pre_overlay_message = { + "message" : message, + "your_language" : your_language, + "translation" : translation, + "target_language" : target_language, + "ui_type" : ui_type, + } + return self.overlay_image.createOverlayImageShort(message, your_language, translation, target_language, ui_type) # def createOverlayImageLong(self, message_type, message, translation): # your_language = config.TARGET_LANGUAGE if message_type == "receive" else config.SOURCE_LANGUAGE # target_language = config.SOURCE_LANGUAGE if message_type == "receive" else config.TARGET_LANGUAGE # return self.overlay_image.create_overlay_image_long(message_type, message, your_language, translation, target_language) - # def clearOverlayImage(self): - # if self.overlay.initialized is True: - # self.overlay.uiManager.uiClear() + def clearOverlayImage(self): + if self.overlay.initialized is True: + self.overlay.uiManager.uiClear() - # def updateOverlay(self, img): - # if self.overlay.initialized is True: - # self.overlay.uiManager.uiUpdate(img) + def updateOverlay(self, img): + if self.overlay.initialized is True: + self.overlay.uiManager.uiUpdate(img) - # def startOverlay(self): - # if self.overlay.initialized is False: - # self.overlay.init() + def startOverlay(self): + if self.overlay.initialized is False: + self.overlay.init() - # if self.overlay.initialized is True and self.th_overlay is None: - # self.th_overlay = Thread(target=self.overlay.startOverlay) - # self.th_overlay.daemon = True - # self.th_overlay.start() + if self.overlay.initialized is True and self.th_overlay is None: + self.th_overlay = Thread(target=self.overlay.startOverlay) + self.th_overlay.daemon = True + self.th_overlay.start() - # def updateOverlayPosition(self): - # if self.overlay.initialized is True: - # pos = (config.OVERLAY_SMALL_LOG_SETTINGS["x_pos"], config.OVERLAY_SMALL_LOG_SETTINGS["y_pos"]) - # self.overlay.uiManager.setPosition(pos) - # depth = config.OVERLAY_SMALL_LOG_SETTINGS["depth"] - # self.overlay.uiManager.setDepth(depth) - # self.overlay.uiManager.posUpdate() + def updateOverlayPosition(self): + if self.overlay.initialized is True: + pos = (config.OVERLAY_SMALL_LOG_SETTINGS["x_pos"], config.OVERLAY_SMALL_LOG_SETTINGS["y_pos"]) + self.overlay.uiManager.setPosition(pos) + depth = config.OVERLAY_SMALL_LOG_SETTINGS["depth"] + self.overlay.uiManager.setDepth(depth) + self.overlay.uiManager.posUpdate() - # def updateOverlayTimes(self): - # if self.overlay.initialized is True: - # display_duration = config.OVERLAY_SMALL_LOG_SETTINGS["display_duration"] - # self.overlay.uiManager.setFadeTime(display_duration) - # fadeout_duration = config.OVERLAY_SMALL_LOG_SETTINGS["fadeout_duration"] - # self.overlay.uiManager.setFadeInterval(fadeout_duration) - # self.overlay.uiManager.update() + def updateOverlayTimes(self): + if self.overlay.initialized is True: + display_duration = config.OVERLAY_SMALL_LOG_SETTINGS["display_duration"] + self.overlay.uiManager.setFadeTime(display_duration) + fadeout_duration = config.OVERLAY_SMALL_LOG_SETTINGS["fadeout_duration"] + self.overlay.uiManager.setFadeInterval(fadeout_duration) + self.overlay.uiManager.update() - # def updateOverlayImageOpacity(self): - # if self.overlay.initialized is True: - # opacity = config.OVERLAY_SETTINGS["opacity"] - # self.overlay.uiManager.setTransparency(opacity) + def updateOverlayImageOpacity(self): + if self.overlay.initialized is True: + opacity = config.OVERLAY_SETTINGS["opacity"] + self.overlay.uiManager.setTransparency(opacity) - # def updateOverlayImageUiScaling(self): - # if self.overlay.initialized is True: - # ui_scaling = config.OVERLAY_SETTINGS["ui_scaling"] - # self.overlay.uiManager.setUiScaling(ui_scaling) + def updateOverlayImageUiScaling(self): + if self.overlay.initialized is True: + ui_scaling = config.OVERLAY_SETTINGS["ui_scaling"] + self.overlay.uiManager.setUiScaling(ui_scaling) model = Model() \ No newline at end of file From ee8c25dea9b489bddd8461d148708688bc0f079d Mon Sep 17 00:00:00 2001 From: misyaguziya Date: Thu, 2 May 2024 14:57:45 +0900 Subject: [PATCH 02/14] =?UTF-8?q?=F0=9F=9A=A7=20[WIP/TEST]=20Model=20:=20O?= =?UTF-8?q?verlay=E3=81=AE=E5=87=A6=E7=90=86=E3=82=92=E9=80=90=E6=AC=A1?= =?UTF-8?q?=E5=87=A6=E7=90=86=E3=81=8B=E3=82=89=E5=AE=9A=E6=9C=9F=E5=87=A6?= =?UTF-8?q?=E7=90=86=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller.py | 17 ++- model.py | 49 +++----- models/overlay/overlay.py | 2 +- models/overlay/overlay_2.py | 232 ++++++++++++++++++++++++++++++++++++ 4 files changed, 259 insertions(+), 41 deletions(-) create mode 100644 models/overlay/overlay_2.py diff --git a/controller.py b/controller.py index 329f0fc3..9447cadf 100644 --- a/controller.py +++ b/controller.py @@ -164,11 +164,10 @@ def receiveSpeakerMessage(message): if config.ENABLE_OVERLAY_SMALL_LOG is True: if model.overlay.initialized is False: model.startOverlay() - if model.overlay.initialized is True: - overlay_image = model.createOverlayImageShort(message, translation) - model.updateOverlay(overlay_image) - # overlay_image = model.createOverlayImageLong("receive", message, translation) - # model.updateOverlay(overlay_image) + overlay_image = model.createOverlayImageShort(message, translation) + model.updateOverlay(overlay_image) + # overlay_image = model.createOverlayImageLong("receive", message, translation) + # model.updateOverlay(overlay_image) # ------------Speaker2Chatbox------------ if config.ENABLE_SPEAKER2CHATBOX is True: @@ -191,6 +190,7 @@ def startTranscriptionReceiveMessage(): def stopTranscriptionReceiveMessage(): model.stopSpeakerTranscript() + model.shutdownOverlay() view.setMainWindowAllWidgetsStatusToNormal() def startThreadingTranscriptionReceiveMessage(): @@ -874,11 +874,8 @@ def callbackSetEnableOverlaySmallLog(value): print("callbackSetEnableOverlaySmallLog", value) config.ENABLE_OVERLAY_SMALL_LOG = value - if config.ENABLE_OVERLAY_SMALL_LOG is True: - pass - else: - if model.overlay.initialized is True: - model.clearOverlayImage() + if config.ENABLE_OVERLAY_SMALL_LOG is False: + model.clearOverlayImage() def callbackSetOverlaySmallLogSettings(value, set_type:str): print("callbackSetOverlaySmallLogSettings", value, set_type) diff --git a/model.py b/model.py index 8095bd94..e18a05eb 100644 --- a/model.py +++ b/model.py @@ -26,7 +26,7 @@ from models.translation.translation_languages import translation_lang from models.transcription.transcription_languages import transcription_lang from models.translation.translation_utils import checkCTranslate2Weight from models.transcription.transcription_whisper import checkWhisperWeight -from models.overlay.overlay import Overlay +from models.overlay.overlay_2 import Overlay from models.overlay.overlay_image import OverlayImage from config import config @@ -603,46 +603,35 @@ class Model: # return self.overlay_image.create_overlay_image_long(message_type, message, your_language, translation, target_language) def clearOverlayImage(self): - if self.overlay.initialized is True: - self.overlay.uiManager.uiClear() + self.overlay.clearImage() def updateOverlay(self, img): - if self.overlay.initialized is True: - self.overlay.uiManager.uiUpdate(img) + self.overlay.setImage(img) def startOverlay(self): - if self.overlay.initialized is False: - self.overlay.init() - - if self.overlay.initialized is True and self.th_overlay is None: - self.th_overlay = Thread(target=self.overlay.startOverlay) - self.th_overlay.daemon = True - self.th_overlay.start() + self.overlay.startOverlay() def updateOverlayPosition(self): - if self.overlay.initialized is True: - pos = (config.OVERLAY_SMALL_LOG_SETTINGS["x_pos"], config.OVERLAY_SMALL_LOG_SETTINGS["y_pos"]) - self.overlay.uiManager.setPosition(pos) - depth = config.OVERLAY_SMALL_LOG_SETTINGS["depth"] - self.overlay.uiManager.setDepth(depth) - self.overlay.uiManager.posUpdate() + pos = (config.OVERLAY_SMALL_LOG_SETTINGS["x_pos"], config.OVERLAY_SMALL_LOG_SETTINGS["y_pos"]) + self.overlay.setPosition(pos) + depth = config.OVERLAY_SMALL_LOG_SETTINGS["depth"] + self.overlay.setDepth(depth) def updateOverlayTimes(self): - if self.overlay.initialized is True: - display_duration = config.OVERLAY_SMALL_LOG_SETTINGS["display_duration"] - self.overlay.uiManager.setFadeTime(display_duration) - fadeout_duration = config.OVERLAY_SMALL_LOG_SETTINGS["fadeout_duration"] - self.overlay.uiManager.setFadeInterval(fadeout_duration) - self.overlay.uiManager.update() + display_duration = config.OVERLAY_SMALL_LOG_SETTINGS["display_duration"] + self.overlay.setFadeTime(display_duration) + fadeout_duration = config.OVERLAY_SMALL_LOG_SETTINGS["fadeout_duration"] + self.overlay.setFadeInterval(fadeout_duration) def updateOverlayImageOpacity(self): - if self.overlay.initialized is True: - opacity = config.OVERLAY_SETTINGS["opacity"] - self.overlay.uiManager.setTransparency(opacity) + opacity = config.OVERLAY_SETTINGS["opacity"] + self.overlay.setTransparency(opacity) def updateOverlayImageUiScaling(self): - if self.overlay.initialized is True: - ui_scaling = config.OVERLAY_SETTINGS["ui_scaling"] - self.overlay.uiManager.setUiScaling(ui_scaling) + ui_scaling = config.OVERLAY_SETTINGS["ui_scaling"] + self.overlay.setUiScaling(ui_scaling) + + def shutdownOverlay(self): + self.overlay.shutdown() model = Model() \ No newline at end of file diff --git a/models/overlay/overlay.py b/models/overlay/overlay.py index 87cd12b1..a2787151 100644 --- a/models/overlay/overlay.py +++ b/models/overlay/overlay.py @@ -229,7 +229,7 @@ if __name__ == '__main__': return self.fnc(*self._args, **self._kwargs) - overlay = Overlay() + overlay = Overlay(0, 0, 1, 1, 1, 1, 1) overlay_image = OverlayImage() if overlay.initialized is False: diff --git a/models/overlay/overlay_2.py b/models/overlay/overlay_2.py new file mode 100644 index 00000000..7462f365 --- /dev/null +++ b/models/overlay/overlay_2.py @@ -0,0 +1,232 @@ +import ctypes +import psutil +import asyncio +import ctypes +import time +import openvr +from PIL import Image +from queue import Queue +from threading import Thread + +def checkSteamvrRunning() -> bool: + for proc in psutil.process_iter(): + if "vrserver.exe" == proc.name().lower(): + return True + return False + +def mat34Id(): + arr = openvr.HmdMatrix34_t() + arr[0][0] = 1 + arr[1][1] = 1 + arr[2][2] = 1 + return arr + +class Overlay: + def __init__(self, x, y , depth, fade_time, fade_interval, transparency, ui_scaling): + self.initialized = False + settings = { + "Color": [1, 1, 1], + "Transparency": transparency, + "Normalized_icon_X_position": x, + "Normalized_icon_Y_position": y, + "Icon_plane_depth": depth, + "Fade_time": fade_time, + "Fade_interval": fade_interval, + "Ui_scaling": ui_scaling, + } + self.settings = settings + self.system = None + self.overlay = None + self.handle = None + self.image_queue = Queue() + self.lastUpdate = time.monotonic() + self.thread_overlay = None + + def init(self): + try: + if checkSteamvrRunning() is True: + self.system = openvr.init(openvr.VRApplication_Background) + self.overlay = openvr.IVROverlay() + self.handle = self.overlay.createOverlay("Overlay_Speaker2log", "SOverlay_Speaker2log_UI") + + self.setImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) + self.updateImage() + self.setColor(self.settings['Color']) + self.updateColor() + self.setTransparency(self.settings['Transparency']) + self.updateTransparency() + self.setUiScaling(self.settings['Ui_scaling']) + self.updateUiScaling() + self.setPosition((self.settings["Normalized_icon_X_position"], self.settings["Normalized_icon_Y_position"])) + self.updatePosition() + self.overlay.showOverlay(self.handle) + self.initialized = True + except Exception as e: + print("Could not initialise OpenVR", e) + + def setImage(self, img): + self.image_queue.put(img) + + def updateImage(self): + img = self.image_queue.get() + width, height = img.size + img = img.tobytes() + img = (ctypes.c_char * len(img)).from_buffer_copy(img) + self.overlay.setOverlayRaw(self.handle, img, width, height, 4) + + def clearImage(self): + while self.image_queue.empty() is False: + self.image_queue.get() + self.setImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) + + def setColor(self, col): + """ + col is a 3-tuple representing (r, g, b) + """ + self.settings["Color"] = col + + def updateColor(self): + r, g, b = self.settings["Color"] + self.overlay.setOverlayColor(self.handle, r, g, b) + + def setTransparency(self, transparency): + self.settings['Transparency'] = transparency + + def updateTransparency(self): + self.overlay.setOverlayAlpha(self.handle, self.settings['Transparency']) + + def setUiScaling(self, ui_scaling): + self.settings['Ui_scaling'] = ui_scaling + + def updateUiScaling(self): + self.overlay.setOverlayWidthInMeters(self.handle, self.settings['Ui_scaling']) + + def setPosition(self, pos): + """ + pos is a 2-tuple representing normalized (x, y) + """ + self.settings["Normalized_icon_X_position"] = pos[0] + self.settings["Normalized_icon_Y_position"] = pos[1] + + def setDepth(self, depth): + self.settings["Icon_plane_depth"] = depth + + def updatePosition(self): + self.transform = mat34Id() # no rotation required for HMD attachment + + # assign position + self.transform[0][3] = self.settings["Normalized_icon_X_position"] * self.settings['Icon_plane_depth'] + self.transform[1][3] = self.settings["Normalized_icon_Y_position"] * self.settings['Icon_plane_depth'] + self.transform[2][3] = - self.settings['Icon_plane_depth'] + + self.overlay.setOverlayTransformTrackedDeviceRelative( + self.handle, + openvr.k_unTrackedDeviceIndex_Hmd, + self.transform + ) + + def setFadeTime(self, fade_time): + self.settings['Fade_time'] = fade_time + + def setFadeInterval(self, fade_interval): + self.settings['Fade_interval'] = fade_interval + + def checkActive(self): + try: + if self.system is not None and self.initialized is True: + new_event = openvr.VREvent_t() + while self.system.pollNextEvent(new_event): + if new_event.eventType == openvr.VREvent_Quit: + return False + return True + except Exception as e: + print("Could not check SteamVR running") + print(e) + return False + + def evaluateTransparencyFade(self, lastUpdate, currentTime): + if (currentTime - lastUpdate) > self.settings['Fade_time']: + timeThroughInterval = currentTime - lastUpdate - self.settings['Fade_time'] + self.fadeRatio = 1 - timeThroughInterval / self.settings['Fade_interval'] + if self.fadeRatio < 0: + self.fadeRatio = 0 + self.overlay.setOverlayAlpha(self.handle, self.fadeRatio * self.settings['Transparency']) + + def update(self): + if self.image_queue.empty() is False: + self.updateImage() + self.updateTransparency() + self.lastUpdate = time.monotonic() + + self.updateUiScaling() + self.updatePosition() + + currTime = time.monotonic() + if self.settings['Fade_interval'] != 0: + self.evaluateTransparencyFade(self.lastUpdate, currTime) + else: + self.updateTransparency() + + async def mainloop(self): + while self.checkActive() is True: + startTime = time.monotonic() + self.update() + sleepTime = (1 / 60) - (time.monotonic() - startTime) + if sleepTime > 0: + await asyncio.sleep(sleepTime) + + async def initMain(self): + await self.mainloop() + + def main(self): + self.init() + if self.initialized is True: + asyncio.run(self.initMain()) + + def startOverlay(self): + self.thread_overlay = Thread(target=self.main) + self.thread_overlay.daemon = True + self.thread_overlay.start() + + def shutdown(self): + if self.thread_overlay is not None: + ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(self.thread_overlay.ident), ctypes.py_object(SystemExit)) + if self.overlay is not None: + self.overlay.destroyOverlay(self.handle) + if self.system is not None: + openvr.shutdown() + self.initialized = False + +if __name__ == '__main__': + import threading + from overlay_image import OverlayImage + + overlay_image = OverlayImage() + overlay = Overlay(0, 0, 1, 1, 1, 1, 1) + thread = threading.Thread(target=overlay.startOverlay) + thread.start() + + for i in range(10): + print(f"time sleep {i}s") + time.sleep(1) + + # Example usage + img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "Hello,World!Goodbye", "Japanese", ui_type="sakura") + overlay.setImage(img) + for i in range(10): + overlay.setPosition((i/10, i/10)) + time.sleep(0.1) + + img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "Hello,World!Goodbye", "Japanese") + overlay.setImage(img) + + for i in range(10): + overlay.setPosition((i/10, i/10)) + time.sleep(0.1) + + time.sleep(10) + + overlay.shutdown() + for i in range(10): + print(f"time sleep {i}s") + time.sleep(1) From 0eba5443b40ce32073513b6e5555d7488308f8d8 Mon Sep 17 00:00:00 2001 From: misyaguziya Date: Thu, 2 May 2024 18:24:09 +0900 Subject: [PATCH 03/14] =?UTF-8?q?=F0=9F=9A=A7=20[WIP/TEST]=20Model=20:=20O?= =?UTF-8?q?verlay=E3=81=AE=E5=87=A6=E7=90=86=E3=81=8B=E3=82=89async?= =?UTF-8?q?=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- models/overlay/overlay_2.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/models/overlay/overlay_2.py b/models/overlay/overlay_2.py index 7462f365..5238bde1 100644 --- a/models/overlay/overlay_2.py +++ b/models/overlay/overlay_2.py @@ -167,21 +167,18 @@ class Overlay: else: self.updateTransparency() - async def mainloop(self): + def mainloop(self): while self.checkActive() is True: startTime = time.monotonic() self.update() sleepTime = (1 / 60) - (time.monotonic() - startTime) if sleepTime > 0: - await asyncio.sleep(sleepTime) - - async def initMain(self): - await self.mainloop() + time.sleep(sleepTime) def main(self): self.init() if self.initialized is True: - asyncio.run(self.initMain()) + self.mainloop() def startOverlay(self): self.thread_overlay = Thread(target=self.main) @@ -191,10 +188,13 @@ class Overlay: def shutdown(self): if self.thread_overlay is not None: ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(self.thread_overlay.ident), ctypes.py_object(SystemExit)) + self.thread_overlay = None if self.overlay is not None: self.overlay.destroyOverlay(self.handle) + self.overlay = None if self.system is not None: openvr.shutdown() + self.system = None self.initialized = False if __name__ == '__main__': From b97273eace5b7b2b57b30c707c076d27c4e90799 Mon Sep 17 00:00:00 2001 From: misyaguziya Date: Fri, 3 May 2024 01:04:39 +0900 Subject: [PATCH 04/14] =?UTF-8?q?=F0=9F=9A=A7=20[WIP/TEST]=20Model=20:=20O?= =?UTF-8?q?verlay=E3=81=AE=E5=87=A6=E7=90=86=E3=81=A7=E7=94=BB=E5=83=8F?= =?UTF-8?q?=E3=82=92=E5=9B=BA=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller.py | 2 ++ img/test_chatbox.png | Bin 0 -> 33053 bytes models/overlay/overlay_2.py | 46 +++++++++++++----------------------- 3 files changed, 19 insertions(+), 29 deletions(-) create mode 100644 img/test_chatbox.png diff --git a/controller.py b/controller.py index 9447cadf..dfaf5506 100644 --- a/controller.py +++ b/controller.py @@ -164,8 +164,10 @@ def receiveSpeakerMessage(message): if config.ENABLE_OVERLAY_SMALL_LOG is True: if model.overlay.initialized is False: model.startOverlay() + print("model.startOverlay()") overlay_image = model.createOverlayImageShort(message, translation) model.updateOverlay(overlay_image) + print("model.updateOverlay(overlay_image)") # overlay_image = model.createOverlayImageLong("receive", message, translation) # model.updateOverlay(overlay_image) diff --git a/img/test_chatbox.png b/img/test_chatbox.png new file mode 100644 index 0000000000000000000000000000000000000000..9d96629967f0fae35ef0392ab6eb3b7dfaf43e09 GIT binary patch literal 33053 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%0AfkfgHl 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 literal 0 HcmV?d00001 diff --git a/models/overlay/overlay_2.py b/models/overlay/overlay_2.py index 5238bde1..42aa5097 100644 --- a/models/overlay/overlay_2.py +++ b/models/overlay/overlay_2.py @@ -1,6 +1,6 @@ import ctypes import psutil -import asyncio +from os import path as os_path import ctypes import time import openvr @@ -68,7 +68,8 @@ class Overlay: self.image_queue.put(img) def updateImage(self): - img = self.image_queue.get() + _ = self.image_queue.get() + img = Image.open(os_path.join(os_path.dirname(os_path.dirname(os_path.dirname(__file__))), "img", "test_chatbox.png")) width, height = img.size img = img.tobytes() img = (ctypes.c_char * len(img)).from_buffer_copy(img) @@ -171,7 +172,7 @@ class Overlay: while self.checkActive() is True: startTime = time.monotonic() self.update() - sleepTime = (1 / 60) - (time.monotonic() - startTime) + sleepTime = (1 / 16) - (time.monotonic() - startTime) if sleepTime > 0: time.sleep(sleepTime) @@ -198,35 +199,22 @@ class Overlay: self.initialized = False if __name__ == '__main__': - import threading from overlay_image import OverlayImage - overlay_image = OverlayImage() - overlay = Overlay(0, 0, 1, 1, 1, 1, 1) - thread = threading.Thread(target=overlay.startOverlay) - thread.start() - for i in range(10): - print(f"time sleep {i}s") - time.sleep(1) + for i in range(100): + print(i) + overlay = Overlay(0, 0, 1, 1, 1, 1, 1) + overlay.startOverlay() + # time.sleep(0.1) - # Example usage - img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "Hello,World!Goodbye", "Japanese", ui_type="sakura") - overlay.setImage(img) - for i in range(10): - overlay.setPosition((i/10, i/10)) - time.sleep(0.1) + # Example usage + img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "Hello,World!Goodbye", "Japanese", ui_type="sakura") + overlay.setImage(img) + time.sleep(0.5) - img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "Hello,World!Goodbye", "Japanese") - overlay.setImage(img) + img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "Hello,World!Goodbye", "Japanese") + overlay.setImage(img) + time.sleep(0.5) - for i in range(10): - overlay.setPosition((i/10, i/10)) - time.sleep(0.1) - - time.sleep(10) - - overlay.shutdown() - for i in range(10): - print(f"time sleep {i}s") - time.sleep(1) + overlay.shutdown() From 6f318a8b05edfaee0905c5b2705e23de380a0265 Mon Sep 17 00:00:00 2001 From: misyaguziya Date: Fri, 3 May 2024 17:14:17 +0900 Subject: [PATCH 05/14] =?UTF-8?q?=F0=9F=9A=A7=20[WIP/TEST]=20Model=20:=20O?= =?UTF-8?q?verlay=E3=81=AE=E5=87=A6=E7=90=86=E3=81=A7=E7=94=BB=E5=83=8F?= =?UTF-8?q?=E7=94=9F=E6=88=90=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/controller.py b/controller.py index dfaf5506..aa298552 100644 --- a/controller.py +++ b/controller.py @@ -165,8 +165,8 @@ def receiveSpeakerMessage(message): if model.overlay.initialized is False: model.startOverlay() print("model.startOverlay()") - overlay_image = model.createOverlayImageShort(message, translation) - model.updateOverlay(overlay_image) + # overlay_image = model.createOverlayImageShort(message, translation) + model.updateOverlay(1) print("model.updateOverlay(overlay_image)") # overlay_image = model.createOverlayImageLong("receive", message, translation) # model.updateOverlay(overlay_image) From 0577756dc10cbf87385a29279fb49cd1bece74bd Mon Sep 17 00:00:00 2001 From: misyaguziya Date: Fri, 3 May 2024 23:05:42 +0900 Subject: [PATCH 06/14] =?UTF-8?q?=F0=9F=9A=A7=20[WIP/TEST]=20Model=20:=20O?= =?UTF-8?q?verlay=E3=81=AE=E5=87=A6=E7=90=86=E3=82=92=E6=9C=80=E5=B0=8F?= =?UTF-8?q?=E9=99=90=E3=81=BE=E3=81=A7=E3=81=AB=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller.py | 25 +++++++++++++------- model.py | 2 +- models/overlay/overlay_2.py | 41 +++++++++++++++------------------ models/overlay/overlay_image.py | 6 ++--- 4 files changed, 40 insertions(+), 34 deletions(-) diff --git a/controller.py b/controller.py index aa298552..bdd640d7 100644 --- a/controller.py +++ b/controller.py @@ -162,11 +162,8 @@ def receiveSpeakerMessage(message): model.notificationXSOverlay(xsoverlay_message) if config.ENABLE_OVERLAY_SMALL_LOG is True: - if model.overlay.initialized is False: - model.startOverlay() - print("model.startOverlay()") - # overlay_image = model.createOverlayImageShort(message, translation) - model.updateOverlay(1) + overlay_image = model.createOverlayImageShort(message, translation) + model.updateOverlay(overlay_image) print("model.updateOverlay(overlay_image)") # overlay_image = model.createOverlayImageLong("receive", message, translation) # model.updateOverlay(overlay_image) @@ -192,7 +189,6 @@ def startTranscriptionReceiveMessage(): def stopTranscriptionReceiveMessage(): model.stopSpeakerTranscript() - model.shutdownOverlay() view.setMainWindowAllWidgetsStatusToNormal() def startThreadingTranscriptionReceiveMessage(): @@ -429,6 +425,14 @@ def callbackToggleTranscriptionReceive(is_turned_on): stopThreadingTranscriptionReceiveMessage() view.changeTranscriptionDisplayStatus("SPEAKER_OFF") + if config.ENABLE_TRANSCRIPTION_RECEIVE is True and config.ENABLE_OVERLAY_SMALL_LOG is True: + if model.overlay.initialized is False: + model.startOverlay() + print("model.startOverlay()") + elif config.ENABLE_TRANSCRIPTION_RECEIVE is False: + model.shutdownOverlay() + print("model.shutdownOverlay()") + def callbackToggleForeground(is_turned_on): config.ENABLE_FOREGROUND = is_turned_on if config.ENABLE_FOREGROUND is True: @@ -876,8 +880,13 @@ def callbackSetEnableOverlaySmallLog(value): print("callbackSetEnableOverlaySmallLog", value) config.ENABLE_OVERLAY_SMALL_LOG = value - if config.ENABLE_OVERLAY_SMALL_LOG is False: - model.clearOverlayImage() + if config.ENABLE_OVERLAY_SMALL_LOG is True and config.ENABLE_TRANSCRIPTION_RECEIVE is True: + if model.overlay.initialized is False: + model.startOverlay() + print("model.startOverlay()") + elif config.ENABLE_OVERLAY_SMALL_LOG is False: + model.shutdownOverlay() + print("model.shutdownOverlay()") def callbackSetOverlaySmallLogSettings(value, set_type:str): print("callbackSetOverlaySmallLogSettings", value, set_type) diff --git a/model.py b/model.py index e18a05eb..6c220c41 100644 --- a/model.py +++ b/model.py @@ -606,7 +606,7 @@ class Model: self.overlay.clearImage() def updateOverlay(self, img): - self.overlay.setImage(img) + self.overlay.updateImage(img) def startOverlay(self): self.overlay.startOverlay() diff --git a/models/overlay/overlay_2.py b/models/overlay/overlay_2.py index 42aa5097..24956f14 100644 --- a/models/overlay/overlay_2.py +++ b/models/overlay/overlay_2.py @@ -1,11 +1,11 @@ import ctypes import psutil -from os import path as os_path +# from os import path as os_path import ctypes import time import openvr from PIL import Image -from queue import Queue +# from queue import Queue from threading import Thread def checkSteamvrRunning() -> bool: @@ -38,7 +38,7 @@ class Overlay: self.system = None self.overlay = None self.handle = None - self.image_queue = Queue() + # self.image_queue = Queue() self.lastUpdate = time.monotonic() self.thread_overlay = None @@ -49,8 +49,7 @@ class Overlay: self.overlay = openvr.IVROverlay() self.handle = self.overlay.createOverlay("Overlay_Speaker2log", "SOverlay_Speaker2log_UI") - self.setImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) - self.updateImage() + self.updateImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) self.setColor(self.settings['Color']) self.updateColor() self.setTransparency(self.settings['Transparency']) @@ -64,21 +63,23 @@ class Overlay: except Exception as e: print("Could not initialise OpenVR", e) - def setImage(self, img): - self.image_queue.put(img) + # def setImage(self, img): + # self.image_queue.put(img) - def updateImage(self): - _ = self.image_queue.get() - img = Image.open(os_path.join(os_path.dirname(os_path.dirname(os_path.dirname(__file__))), "img", "test_chatbox.png")) + def updateImage(self, img): + # _ = self.image_queue.get() + # img = Image.open(os_path.join(os_path.dirname(os_path.dirname(os_path.dirname(__file__))), "img", "test_chatbox.png")) width, height = img.size img = img.tobytes() img = (ctypes.c_char * len(img)).from_buffer_copy(img) self.overlay.setOverlayRaw(self.handle, img, width, height, 4) + self.updateTransparency() + self.lastUpdate = time.monotonic() def clearImage(self): - while self.image_queue.empty() is False: - self.image_queue.get() - self.setImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) + # while self.image_queue.empty() is False: + # self.image_queue.get() + self.updateImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) def setColor(self, col): """ @@ -154,13 +155,8 @@ class Overlay: self.overlay.setOverlayAlpha(self.handle, self.fadeRatio * self.settings['Transparency']) def update(self): - if self.image_queue.empty() is False: - self.updateImage() - self.updateTransparency() - self.lastUpdate = time.monotonic() - - self.updateUiScaling() - self.updatePosition() + # self.updateUiScaling() + # self.updatePosition() currTime = time.monotonic() if self.settings['Fade_interval'] != 0: @@ -175,6 +171,7 @@ class Overlay: sleepTime = (1 / 16) - (time.monotonic() - startTime) if sleepTime > 0: time.sleep(sleepTime) + self.shutdown() def main(self): self.init() @@ -210,11 +207,11 @@ if __name__ == '__main__': # Example usage img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "Hello,World!Goodbye", "Japanese", ui_type="sakura") - overlay.setImage(img) + overlay.updateImage(img) time.sleep(0.5) img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "Hello,World!Goodbye", "Japanese") - overlay.setImage(img) + overlay.updateImage(img) time.sleep(0.5) overlay.shutdown() diff --git a/models/overlay/overlay_image.py b/models/overlay/overlay_image.py index d1607179..c52756bb 100644 --- a/models/overlay/overlay_image.py +++ b/models/overlay/overlay_image.py @@ -140,9 +140,9 @@ class OverlayImage: def getUiSize(self): return { - "width": int(960*4), - "height": int(23*4), - "font_size": int(23*4), + "width": int(960), + "height": int(23), + "font_size": int(23), } def getUiColors(self, ui_type): From a5962f699b70e58d356229705080725dca65f312 Mon Sep 17 00:00:00 2001 From: misyaguziya Date: Sat, 4 May 2024 16:31:36 +0900 Subject: [PATCH 07/14] =?UTF-8?q?=F0=9F=9A=A7=20[WIP/TEST]=20Model=20:=20O?= =?UTF-8?q?verlay=E3=81=AE=E5=87=A6=E7=90=86=E3=81=AB=E3=83=9E=E3=82=B9?= =?UTF-8?q?=E3=82=AF=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller.py | 7 ++++--- models/overlay/overlay_2.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/controller.py b/controller.py index bdd640d7..48eb42db 100644 --- a/controller.py +++ b/controller.py @@ -162,9 +162,10 @@ def receiveSpeakerMessage(message): model.notificationXSOverlay(xsoverlay_message) if config.ENABLE_OVERLAY_SMALL_LOG is True: - overlay_image = model.createOverlayImageShort(message, translation) - model.updateOverlay(overlay_image) - print("model.updateOverlay(overlay_image)") + if model.overlay.initialized is True: + overlay_image = model.createOverlayImageShort(message, translation) + model.updateOverlay(overlay_image) + print("model.updateOverlay(overlay_image)") # overlay_image = model.createOverlayImageLong("receive", message, translation) # model.updateOverlay(overlay_image) diff --git a/models/overlay/overlay_2.py b/models/overlay/overlay_2.py index 24956f14..9ea8e532 100644 --- a/models/overlay/overlay_2.py +++ b/models/overlay/overlay_2.py @@ -203,7 +203,7 @@ if __name__ == '__main__': print(i) overlay = Overlay(0, 0, 1, 1, 1, 1, 1) overlay.startOverlay() - # time.sleep(0.1) + time.sleep(1) # Example usage img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "Hello,World!Goodbye", "Japanese", ui_type="sakura") From 313682cbbdf3e2a88e36ccc3922db7da8e8799d4 Mon Sep 17 00:00:00 2001 From: misyaguziya Date: Sat, 4 May 2024 17:25:39 +0900 Subject: [PATCH 08/14] =?UTF-8?q?=F0=9F=9A=A7=20[WIP/TEST]=20Model=20:=20O?= =?UTF-8?q?verlay=E3=81=AE=E5=87=A6=E7=90=86=E3=82=92=E8=B5=B7=E5=8B=95?= =?UTF-8?q?=E3=81=97=E3=81=9F=E3=82=89=E3=81=97=E3=81=A3=E3=81=B1=E3=81=AA?= =?UTF-8?q?=E3=81=97=E3=81=AB=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller.py | 10 ++++----- models/overlay/overlay_2.py | 43 ++++++++++++++++++------------------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/controller.py b/controller.py index 48eb42db..fb505fdc 100644 --- a/controller.py +++ b/controller.py @@ -427,12 +427,11 @@ def callbackToggleTranscriptionReceive(is_turned_on): view.changeTranscriptionDisplayStatus("SPEAKER_OFF") if config.ENABLE_TRANSCRIPTION_RECEIVE is True and config.ENABLE_OVERLAY_SMALL_LOG is True: - if model.overlay.initialized is False: + if model.overlay.initialized is False and model.overlay.checkSteamvrRunning() is True: model.startOverlay() print("model.startOverlay()") elif config.ENABLE_TRANSCRIPTION_RECEIVE is False: - model.shutdownOverlay() - print("model.shutdownOverlay()") + pass def callbackToggleForeground(is_turned_on): config.ENABLE_FOREGROUND = is_turned_on @@ -882,12 +881,11 @@ def callbackSetEnableOverlaySmallLog(value): config.ENABLE_OVERLAY_SMALL_LOG = value if config.ENABLE_OVERLAY_SMALL_LOG is True and config.ENABLE_TRANSCRIPTION_RECEIVE is True: - if model.overlay.initialized is False: + if model.overlay.initialized is False and model.overlay.checkSteamvrRunning() is True: model.startOverlay() print("model.startOverlay()") elif config.ENABLE_OVERLAY_SMALL_LOG is False: - model.shutdownOverlay() - print("model.shutdownOverlay()") + pass def callbackSetOverlaySmallLogSettings(value, set_type:str): print("callbackSetOverlaySmallLogSettings", value, set_type) diff --git a/models/overlay/overlay_2.py b/models/overlay/overlay_2.py index 9ea8e532..929be276 100644 --- a/models/overlay/overlay_2.py +++ b/models/overlay/overlay_2.py @@ -1,5 +1,6 @@ +import os import ctypes -import psutil +from psutil import process_iter # from os import path as os_path import ctypes import time @@ -8,12 +9,6 @@ from PIL import Image # from queue import Queue from threading import Thread -def checkSteamvrRunning() -> bool: - for proc in psutil.process_iter(): - if "vrserver.exe" == proc.name().lower(): - return True - return False - def mat34Id(): arr = openvr.HmdMatrix34_t() arr[0][0] = 1 @@ -44,22 +39,21 @@ class Overlay: def init(self): try: - if checkSteamvrRunning() is True: - self.system = openvr.init(openvr.VRApplication_Background) - self.overlay = openvr.IVROverlay() - self.handle = self.overlay.createOverlay("Overlay_Speaker2log", "SOverlay_Speaker2log_UI") + self.system = openvr.init(openvr.VRApplication_Background) + self.overlay = openvr.IVROverlay() + self.handle = self.overlay.createOverlay("Overlay_Speaker2log", "SOverlay_Speaker2log_UI") - self.updateImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) - self.setColor(self.settings['Color']) - self.updateColor() - self.setTransparency(self.settings['Transparency']) - self.updateTransparency() - self.setUiScaling(self.settings['Ui_scaling']) - self.updateUiScaling() - self.setPosition((self.settings["Normalized_icon_X_position"], self.settings["Normalized_icon_Y_position"])) - self.updatePosition() - self.overlay.showOverlay(self.handle) - self.initialized = True + self.updateImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) + self.setColor(self.settings['Color']) + self.updateColor() + self.setTransparency(self.settings['Transparency']) + self.updateTransparency() + self.setUiScaling(self.settings['Ui_scaling']) + self.updateUiScaling() + self.setPosition((self.settings["Normalized_icon_X_position"], self.settings["Normalized_icon_Y_position"])) + self.updatePosition() + self.overlay.showOverlay(self.handle) + self.initialized = True except Exception as e: print("Could not initialise OpenVR", e) @@ -195,6 +189,11 @@ class Overlay: self.system = None self.initialized = False + @staticmethod + def checkSteamvrRunning() -> bool: + _proc_name = "vrmonitor.exe" if os.name == 'nt' else "vrmonitor" + return _proc_name in (p.name() for p in process_iter()) + if __name__ == '__main__': from overlay_image import OverlayImage overlay_image = OverlayImage() From 564167a9808dfd1048c885313b3d2f96f1896649 Mon Sep 17 00:00:00 2001 From: misyaguziya Date: Tue, 7 May 2024 16:39:00 +0900 Subject: [PATCH 09/14] =?UTF-8?q?=F0=9F=97=91=EF=B8=8F[Remove]=20Model=20:?= =?UTF-8?q?=20=E3=83=86=E3=82=B9=E3=83=88=E3=82=B3=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller.py | 1 - 1 file changed, 1 deletion(-) diff --git a/controller.py b/controller.py index fb505fdc..6a7ef5b1 100644 --- a/controller.py +++ b/controller.py @@ -883,7 +883,6 @@ def callbackSetEnableOverlaySmallLog(value): if config.ENABLE_OVERLAY_SMALL_LOG is True and config.ENABLE_TRANSCRIPTION_RECEIVE is True: if model.overlay.initialized is False and model.overlay.checkSteamvrRunning() is True: model.startOverlay() - print("model.startOverlay()") elif config.ENABLE_OVERLAY_SMALL_LOG is False: pass From 28073fbd81cfa68a6fa373770f0c79e015262927 Mon Sep 17 00:00:00 2001 From: misyaguziya Date: Tue, 7 May 2024 16:40:45 +0900 Subject: [PATCH 10/14] =?UTF-8?q?=F0=9F=9A=A7=20[WIP/TEST]=20Model=20:=20O?= =?UTF-8?q?verlay=20=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF=E3=82=BF=E3=83=AA?= =?UTF-8?q?=E3=83=B3=E3=82=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- img/test_chatbox.png | Bin 33053 -> 0 bytes model.py | 15 ++--- models/overlay/overlay_2.py | 129 +++++++++++++++++------------------- 3 files changed, 66 insertions(+), 78 deletions(-) delete mode 100644 img/test_chatbox.png diff --git a/img/test_chatbox.png b/img/test_chatbox.png deleted file mode 100644 index 9d96629967f0fae35ef0392ab6eb3b7dfaf43e09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33053 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%0AfkfgHl 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 diff --git a/model.py b/model.py index 6c220c41..251adf03 100644 --- a/model.py +++ b/model.py @@ -613,25 +613,24 @@ class Model: def updateOverlayPosition(self): pos = (config.OVERLAY_SMALL_LOG_SETTINGS["x_pos"], config.OVERLAY_SMALL_LOG_SETTINGS["y_pos"]) - self.overlay.setPosition(pos) depth = config.OVERLAY_SMALL_LOG_SETTINGS["depth"] - self.overlay.setDepth(depth) + self.overlay.updatePosition(pos, depth) def updateOverlayTimes(self): display_duration = config.OVERLAY_SMALL_LOG_SETTINGS["display_duration"] - self.overlay.setFadeTime(display_duration) + self.overlay.updateDisplayDuration(display_duration) fadeout_duration = config.OVERLAY_SMALL_LOG_SETTINGS["fadeout_duration"] - self.overlay.setFadeInterval(fadeout_duration) + self.overlay.updateFadeoutDuration(fadeout_duration) def updateOverlayImageOpacity(self): opacity = config.OVERLAY_SETTINGS["opacity"] - self.overlay.setTransparency(opacity) + self.overlay.updateOpacity(opacity, with_fade=True) def updateOverlayImageUiScaling(self): ui_scaling = config.OVERLAY_SETTINGS["ui_scaling"] - self.overlay.setUiScaling(ui_scaling) + self.overlay.updateUiScaling(ui_scaling) - def shutdownOverlay(self): - self.overlay.shutdown() + def stopOverlay(self): + self.overlay.setStopOverlay() model = Model() \ No newline at end of file diff --git a/models/overlay/overlay_2.py b/models/overlay/overlay_2.py index 929be276..68918baa 100644 --- a/models/overlay/overlay_2.py +++ b/models/overlay/overlay_2.py @@ -8,6 +8,7 @@ import openvr from PIL import Image # from queue import Queue from threading import Thread +import OverlayImage def mat34Id(): arr = openvr.HmdMatrix34_t() @@ -17,25 +18,26 @@ def mat34Id(): return arr class Overlay: - def __init__(self, x, y , depth, fade_time, fade_interval, transparency, ui_scaling): + def __init__(self, x, y , depth, fade_time, fade_interval, opacity, ui_scaling): self.initialized = False settings = { - "Color": [1, 1, 1], - "Transparency": transparency, - "Normalized_icon_X_position": x, - "Normalized_icon_Y_position": y, - "Icon_plane_depth": depth, - "Fade_time": fade_time, - "Fade_interval": fade_interval, - "Ui_scaling": ui_scaling, + "color": [1, 1, 1], + "opacity": opacity, + "x_pos": x, + "y_pos": y, + "depth": depth, + "fade_time": fade_time, + "fade_interval": fade_interval, + "ui_scaling": ui_scaling, } self.settings = settings self.system = None self.overlay = None self.handle = None - # self.image_queue = Queue() self.lastUpdate = time.monotonic() self.thread_overlay = None + self.fadeRatio = 1 + self.loop = True def init(self): try: @@ -44,76 +46,62 @@ class Overlay: self.handle = self.overlay.createOverlay("Overlay_Speaker2log", "SOverlay_Speaker2log_UI") self.updateImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) - self.setColor(self.settings['Color']) - self.updateColor() - self.setTransparency(self.settings['Transparency']) - self.updateTransparency() - self.setUiScaling(self.settings['Ui_scaling']) - self.updateUiScaling() - self.setPosition((self.settings["Normalized_icon_X_position"], self.settings["Normalized_icon_Y_position"])) - self.updatePosition() + self.updateColor(self.settings["color"]) + self.updateOpacity(self.settings["opacity"]) + self.updateUiScaling(self.settings["Ui_scaling"]) + self.updatePosition( + (self.settings["x_pos"], self.settings["y_pos"]), + self.settings["depth"] + ) self.overlay.showOverlay(self.handle) self.initialized = True except Exception as e: print("Could not initialise OpenVR", e) - # def setImage(self, img): - # self.image_queue.put(img) - def updateImage(self, img): - # _ = self.image_queue.get() - # img = Image.open(os_path.join(os_path.dirname(os_path.dirname(os_path.dirname(__file__))), "img", "test_chatbox.png")) width, height = img.size img = img.tobytes() img = (ctypes.c_char * len(img)).from_buffer_copy(img) self.overlay.setOverlayRaw(self.handle, img, width, height, 4) - self.updateTransparency() + self.updateOpacity(self.settings["opacity"]) self.lastUpdate = time.monotonic() def clearImage(self): - # while self.image_queue.empty() is False: - # self.image_queue.get() self.updateImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) - def setColor(self, col): + def updateColor(self, col): """ col is a 3-tuple representing (r, g, b) """ - self.settings["Color"] = col - - def updateColor(self): - r, g, b = self.settings["Color"] + self.settings["color"] = col + r, g, b = self.settings["color"] self.overlay.setOverlayColor(self.handle, r, g, b) - def setTransparency(self, transparency): - self.settings['Transparency'] = transparency + def updateOpacity(self, opacity, with_fade=False): + self.settings["opacity"] = opacity + self.overlay.setOverlayAlpha( + self.handle, + self.settings["opacity"] if with_fade is False else self.settings["opacity"] * self.fadeRatio) - def updateTransparency(self): - self.overlay.setOverlayAlpha(self.handle, self.settings['Transparency']) + def updateUiScaling(self, ui_scaling): + self.settings['ui_scaling'] = ui_scaling + self.overlay.setOverlayWidthInMeters(self.handle, self.settings['ui_scaling']) - def setUiScaling(self, ui_scaling): - self.settings['Ui_scaling'] = ui_scaling - - def updateUiScaling(self): - self.overlay.setOverlayWidthInMeters(self.handle, self.settings['Ui_scaling']) - - def setPosition(self, pos): + def updatePosition(self, pos, depth): """ pos is a 2-tuple representing normalized (x, y) + depth is a float representing the depth of the icon plane """ - self.settings["Normalized_icon_X_position"] = pos[0] - self.settings["Normalized_icon_Y_position"] = pos[1] + self.settings["x_pos"] = pos[0] + self.settings["y_pos"] = pos[1] + self.settings["depth"] = depth - def setDepth(self, depth): - self.settings["Icon_plane_depth"] = depth - - def updatePosition(self): self.transform = mat34Id() # no rotation required for HMD attachment # assign position - self.transform[0][3] = self.settings["Normalized_icon_X_position"] * self.settings['Icon_plane_depth'] - self.transform[1][3] = self.settings["Normalized_icon_Y_position"] * self.settings['Icon_plane_depth'] - self.transform[2][3] = - self.settings['Icon_plane_depth'] + self.transform[0][3] = self.settings["x_pos"] * self.settings['depth'] + self.transform[1][3] = self.settings["y_pos"] * self.settings['depth'] + self.transform[2][3] = - self.settings['depth'] self.overlay.setOverlayTransformTrackedDeviceRelative( self.handle, @@ -121,11 +109,11 @@ class Overlay: self.transform ) - def setFadeTime(self, fade_time): - self.settings['Fade_time'] = fade_time + def updateDisplayDuration(self, display_duration): + self.settings['display_duration'] = display_duration - def setFadeInterval(self, fade_interval): - self.settings['Fade_interval'] = fade_interval + def updateFadeoutDuration(self, fadeout_duration): + self.settings['fadeout_duration'] = fadeout_duration def checkActive(self): try: @@ -140,32 +128,30 @@ class Overlay: print(e) return False - def evaluateTransparencyFade(self, lastUpdate, currentTime): - if (currentTime - lastUpdate) > self.settings['Fade_time']: - timeThroughInterval = currentTime - lastUpdate - self.settings['Fade_time'] - self.fadeRatio = 1 - timeThroughInterval / self.settings['Fade_interval'] + def evaluateOpacityFade(self, lastUpdate, currentTime): + if (currentTime - lastUpdate) > self.settings['display_duration']: + timeThroughInterval = currentTime - lastUpdate - self.settings['display_duration'] + self.fadeRatio = 1 - timeThroughInterval / self.settings['fadeout_duration'] if self.fadeRatio < 0: self.fadeRatio = 0 - self.overlay.setOverlayAlpha(self.handle, self.fadeRatio * self.settings['Transparency']) + self.overlay.setOverlayAlpha(self.handle, self.fadeRatio * self.settings['opacity']) def update(self): - # self.updateUiScaling() - # self.updatePosition() - currTime = time.monotonic() - if self.settings['Fade_interval'] != 0: - self.evaluateTransparencyFade(self.lastUpdate, currTime) + if self.settings['fadeout_duration'] != 0: + self.evaluateOpacityFade(self.lastUpdate, currTime) else: - self.updateTransparency() + self.updateOpacity(self.settings["opacity"]) def mainloop(self): - while self.checkActive() is True: + self.loop = True + while self.checkActive() is True and self.loop is True: startTime = time.monotonic() self.update() sleepTime = (1 / 16) - (time.monotonic() - startTime) if sleepTime > 0: time.sleep(sleepTime) - self.shutdown() + self.shutdownOverlay() def main(self): self.init() @@ -177,9 +163,12 @@ class Overlay: self.thread_overlay.daemon = True self.thread_overlay.start() - def shutdown(self): + def setStopOverlay(self): + self.loop = False + + def shutdownOverlay(self): if self.thread_overlay is not None: - ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(self.thread_overlay.ident), ctypes.py_object(SystemExit)) + self.thread_overlay.join() self.thread_overlay = None if self.overlay is not None: self.overlay.destroyOverlay(self.handle) From db560197472070167707b8b522b1f9083682ec75 Mon Sep 17 00:00:00 2001 From: misyaguziya Date: Tue, 7 May 2024 16:43:05 +0900 Subject: [PATCH 11/14] =?UTF-8?q?=F0=9F=91=8D=EF=B8=8F[Update]=20Model=20:?= =?UTF-8?q?=20rename=20overlay=5F2.py=20->=20overlay.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model.py | 2 +- models/overlay/overlay.py | 345 +++++++++++++++--------------------- models/overlay/overlay_2.py | 205 --------------------- 3 files changed, 148 insertions(+), 404 deletions(-) delete mode 100644 models/overlay/overlay_2.py diff --git a/model.py b/model.py index 251adf03..39eacd56 100644 --- a/model.py +++ b/model.py @@ -26,7 +26,7 @@ from models.translation.translation_languages import translation_lang from models.transcription.transcription_languages import transcription_lang from models.translation.translation_utils import checkCTranslate2Weight from models.transcription.transcription_whisper import checkWhisperWeight -from models.overlay.overlay_2 import Overlay +from models.overlay.overlay import Overlay from models.overlay.overlay_image import OverlayImage from config import config diff --git a/models/overlay/overlay.py b/models/overlay/overlay.py index a2787151..68918baa 100644 --- a/models/overlay/overlay.py +++ b/models/overlay/overlay.py @@ -1,18 +1,15 @@ -import psutil +import os +import ctypes +from psutil import process_iter +# from os import path as os_path import ctypes import time -import asyncio import openvr from PIL import Image +# from queue import Queue +from threading import Thread +import OverlayImage -def checkSteamvrRunning(): - for proc in psutil.process_iter(): - if "vrserver.exe" == proc.name().lower(): - return True - return False - -# This code is based on the following source: -# [GOpy](https://github.com/MeroFune/GOpy) def mat34Id(): arr = openvr.HmdMatrix34_t() arr[0][0] = 1 @@ -20,51 +17,91 @@ def mat34Id(): arr[2][2] = 1 return arr -class UIElement: - def __init__(self, overlayRoot, key: str, name: str, settings: dict = None) -> None: - """ - pos is a 2-tuple representing (x, y) normalized position of the overlay on the screen - """ - self.overlay = overlayRoot - self.overlayKey = key - self.overlayName = name +class Overlay: + def __init__(self, x, y , depth, fade_time, fade_interval, opacity, ui_scaling): + self.initialized = False + settings = { + "color": [1, 1, 1], + "opacity": opacity, + "x_pos": x, + "y_pos": y, + "depth": depth, + "fade_time": fade_time, + "fade_interval": fade_interval, + "ui_scaling": ui_scaling, + } self.settings = settings - self.handle = self.overlay.createOverlay(self.overlayKey, self.overlayName) + self.system = None + self.overlay = None + self.handle = None + self.lastUpdate = time.monotonic() + self.thread_overlay = None + self.fadeRatio = 1 + self.loop = True - self.setImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) # blank image for default - self.setColor(self.settings['Color']) - self.setTransparency(self.settings['Transparency']) - self.overlay.setOverlayWidthInMeters( - self.handle, - self.settings['Ui_scaling'] - ) + def init(self): + try: + self.system = openvr.init(openvr.VRApplication_Background) + self.overlay = openvr.IVROverlay() + self.handle = self.overlay.createOverlay("Overlay_Speaker2log", "SOverlay_Speaker2log_UI") - self.updatePosition() - self.overlay.showOverlay(self.handle) + self.updateImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) + self.updateColor(self.settings["color"]) + self.updateOpacity(self.settings["opacity"]) + self.updateUiScaling(self.settings["Ui_scaling"]) + self.updatePosition( + (self.settings["x_pos"], self.settings["y_pos"]), + self.settings["depth"] + ) + self.overlay.showOverlay(self.handle) + self.initialized = True + except Exception as e: + print("Could not initialise OpenVR", e) - def setImage(self, img): - # configure overlay appearance + def updateImage(self, img): width, height = img.size img = img.tobytes() img = (ctypes.c_char * len(img)).from_buffer_copy(img) self.overlay.setOverlayRaw(self.handle, img, width, height, 4) + self.updateOpacity(self.settings["opacity"]) + self.lastUpdate = time.monotonic() - def setColor(self, col): + def clearImage(self): + self.updateImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) + + def updateColor(self, col): """ col is a 3-tuple representing (r, g, b) """ - self.overlay.setOverlayColor(self.handle, col[0], col[1], col[2]) + self.settings["color"] = col + r, g, b = self.settings["color"] + self.overlay.setOverlayColor(self.handle, r, g, b) - def setTransparency(self, a): - self.overlay.setOverlayAlpha(self.handle, a) + def updateOpacity(self, opacity, with_fade=False): + self.settings["opacity"] = opacity + self.overlay.setOverlayAlpha( + self.handle, + self.settings["opacity"] if with_fade is False else self.settings["opacity"] * self.fadeRatio) + + def updateUiScaling(self, ui_scaling): + self.settings['ui_scaling'] = ui_scaling + self.overlay.setOverlayWidthInMeters(self.handle, self.settings['ui_scaling']) + + def updatePosition(self, pos, depth): + """ + pos is a 2-tuple representing normalized (x, y) + depth is a float representing the depth of the icon plane + """ + self.settings["x_pos"] = pos[0] + self.settings["y_pos"] = pos[1] + self.settings["depth"] = depth - def updatePosition(self): self.transform = mat34Id() # no rotation required for HMD attachment # assign position - self.transform[0][3] = self.settings["Normalized_icon_X_position"] * self.settings['Icon_plane_depth'] - self.transform[1][3] = self.settings["Normalized_icon_Y_position"] * self.settings['Icon_plane_depth'] - self.transform[2][3] = - self.settings['Icon_plane_depth'] + self.transform[0][3] = self.settings["x_pos"] * self.settings['depth'] + self.transform[1][3] = self.settings["y_pos"] * self.settings['depth'] + self.transform[2][3] = - self.settings['depth'] self.overlay.setOverlayTransformTrackedDeviceRelative( self.handle, @@ -72,127 +109,11 @@ class UIElement: self.transform ) - def setPosition(self, pos): - """ - pos is a 2-tuple representing normalized (x, y) - """ - self.settings["Normalized_icon_X_position"] = pos[0] - self.settings["Normalized_icon_Y_position"] = pos[1] + def updateDisplayDuration(self, display_duration): + self.settings['display_duration'] = display_duration - def setDepth(self, depth): - self.settings["Icon_plane_depth"] = depth - - def setUiScaling(self, ui_scaling): - self.overlay.setOverlayWidthInMeters( - self.handle, - ui_scaling, - ) - -class UIManager: - def __init__(self, overlay_key, overlay_name, settings): - self.overlay = openvr.IVROverlay() - self.settings = settings - self.overlayUI = UIElement( - self.overlay, - overlay_key, - overlay_name, - self.settings, - ) - self.fadeRatio = 1 - self.lastUpdate = time.monotonic() - - def update(self): - currTime = time.monotonic() - if self.settings['Fade_interval'] != 0: - self.evaluateTransparencyFade(self.lastUpdate, currTime) - - def uiClear(self): - self.overlayUI.setImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) - self.overlayUI.setTransparency(self.settings['Transparency']) - self.lastUpdate = time.monotonic() - - def uiUpdate(self, img): - self.overlayUI.setImage(img) - self.overlayUI.setTransparency(self.settings['Transparency']) - self.lastUpdate = time.monotonic() - - def evaluateTransparencyFade(self, lastUpdate, currentTime): - if (currentTime - lastUpdate) > self.settings['Fade_time']: - timeThroughInterval = currentTime - lastUpdate - self.settings['Fade_time'] - self.fadeRatio = 1 - timeThroughInterval / self.settings['Fade_interval'] - if self.fadeRatio < 0: - self.fadeRatio = 0 - - self.overlayUI.setTransparency(self.fadeRatio * self.settings['Transparency']) - - def posUpdate(self): - self.overlayUI.updatePosition() - - def setPosition(self, pos): - self.overlayUI.setPosition(pos) - - def setDepth(self, depth): - self.overlayUI.setDepth(depth) - - def setFadeTime(self, fade_time): - self.settings["Fade_time"] = fade_time - - def setFadeInterval(self, fade_interval): - self.settings["Fade_interval"] = fade_interval - - def setUiScaling(self, ui_scaling): - self.overlayUI.setUiScaling(ui_scaling) - - def setTransparency(self, transparency): - self.settings["Transparency"] = transparency - self.overlayUI.setTransparency(self.fadeRatio * self.settings['Transparency']) - -class Overlay: - def __init__(self, x, y , depth, fade_time, fade_interval, transparency, ui_scaling): - self.initialized = False - settings = { - "Color": [1, 1, 1], - "Transparency": transparency, - "Normalized_icon_X_position": x, - "Normalized_icon_Y_position": y, - "Icon_plane_depth": depth, - "Fade_time": fade_time, - "Fade_interval": fade_interval, - "Ui_scaling": ui_scaling, - } - self.settings = settings - self.system = None - - def init(self): - try: - if checkSteamvrRunning() is True: - self.system = openvr.init(openvr.VRApplication_Background) - self.initialized = True - except Exception as e: - print("Could not initialise OpenVR") - print(e) - - async def mainLoop(self): - while self.checkActive() is True: - startTime = time.monotonic() - self.uiManager.update() - - sleepTime = (1 / 60) - (time.monotonic() - startTime) - if sleepTime > 0: - await asyncio.sleep(sleepTime) - - async def initMain(self): - if self.initialized is True: - self.uiManager = UIManager("Overlay_Speaker2log", "SOverlay_Speaker2log_UI", self.settings) - await self.mainLoop() - - def startOverlay(self): - asyncio.run(self.initMain()) - - def shutdown(self): - self.system = None - self.initialized = False - openvr.shutdown() + def updateFadeoutDuration(self, fadeout_duration): + self.settings['fadeout_duration'] = fadeout_duration def checkActive(self): try: @@ -200,7 +121,6 @@ class Overlay: new_event = openvr.VREvent_t() while self.system.pollNextEvent(new_event): if new_event.eventType == openvr.VREvent_Quit: - self.shutdown() return False return True except Exception as e: @@ -208,49 +128,78 @@ class Overlay: print(e) return False + def evaluateOpacityFade(self, lastUpdate, currentTime): + if (currentTime - lastUpdate) > self.settings['display_duration']: + timeThroughInterval = currentTime - lastUpdate - self.settings['display_duration'] + self.fadeRatio = 1 - timeThroughInterval / self.settings['fadeout_duration'] + if self.fadeRatio < 0: + self.fadeRatio = 0 + self.overlay.setOverlayAlpha(self.handle, self.fadeRatio * self.settings['opacity']) + + def update(self): + currTime = time.monotonic() + if self.settings['fadeout_duration'] != 0: + self.evaluateOpacityFade(self.lastUpdate, currTime) + else: + self.updateOpacity(self.settings["opacity"]) + + def mainloop(self): + self.loop = True + while self.checkActive() is True and self.loop is True: + startTime = time.monotonic() + self.update() + sleepTime = (1 / 16) - (time.monotonic() - startTime) + if sleepTime > 0: + time.sleep(sleepTime) + self.shutdownOverlay() + + def main(self): + self.init() + if self.initialized is True: + self.mainloop() + + def startOverlay(self): + self.thread_overlay = Thread(target=self.main) + self.thread_overlay.daemon = True + self.thread_overlay.start() + + def setStopOverlay(self): + self.loop = False + + def shutdownOverlay(self): + if self.thread_overlay is not None: + self.thread_overlay.join() + self.thread_overlay = None + if self.overlay is not None: + self.overlay.destroyOverlay(self.handle) + self.overlay = None + if self.system is not None: + openvr.shutdown() + self.system = None + self.initialized = False + + @staticmethod + def checkSteamvrRunning() -> bool: + _proc_name = "vrmonitor.exe" if os.name == 'nt' else "vrmonitor" + return _proc_name in (p.name() for p in process_iter()) + if __name__ == '__main__': from overlay_image import OverlayImage - from threading import Thread, Event - class threadFnc(Thread): - def __init__(self, fnc, end_fnc=None, daemon=True, *args, **kwargs): - super(threadFnc, self).__init__(daemon=daemon, *args, **kwargs) - self.fnc = fnc - self.end_fnc = end_fnc - self._stop = Event() - def stop(self): - self._stop.set() - def stopped(self): - return self._stop.is_set() - def run(self): - while True: - if self.stopped(): - if callable(self.end_fnc): - self.end_fnc() - return - self.fnc(*self._args, **self._kwargs) - - overlay = Overlay(0, 0, 1, 1, 1, 1, 1) overlay_image = OverlayImage() - if overlay.initialized is False: - overlay.init() - if overlay.initialized is True: - t = threadFnc(overlay.startOverlay) - t.start() + for i in range(100): + print(i) + overlay = Overlay(0, 0, 1, 1, 1, 1, 1) + overlay.startOverlay() + time.sleep(1) - time.sleep(1) - img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "Hello,World!Goodbye", "Japanese", ui_type="sakura") - # img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", ui_type="sakura") - if overlay.initialized is True: - overlay.uiManager.uiUpdate(img) - time.sleep(10) + # Example usage + img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "Hello,World!Goodbye", "Japanese", ui_type="sakura") + overlay.updateImage(img) + time.sleep(0.5) - img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "안녕하세요, 세계!안녕", "Korean") - if overlay.initialized is True: - overlay.uiManager.uiUpdate(img) - time.sleep(10) + img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "Hello,World!Goodbye", "Japanese") + overlay.updateImage(img) + time.sleep(0.5) - img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "你好世界!再见", "Chinese Simplified") - if overlay.initialized is True: - overlay.uiManager.uiUpdate(img) - time.sleep(10) \ No newline at end of file + overlay.shutdown() diff --git a/models/overlay/overlay_2.py b/models/overlay/overlay_2.py deleted file mode 100644 index 68918baa..00000000 --- a/models/overlay/overlay_2.py +++ /dev/null @@ -1,205 +0,0 @@ -import os -import ctypes -from psutil import process_iter -# from os import path as os_path -import ctypes -import time -import openvr -from PIL import Image -# from queue import Queue -from threading import Thread -import OverlayImage - -def mat34Id(): - arr = openvr.HmdMatrix34_t() - arr[0][0] = 1 - arr[1][1] = 1 - arr[2][2] = 1 - return arr - -class Overlay: - def __init__(self, x, y , depth, fade_time, fade_interval, opacity, ui_scaling): - self.initialized = False - settings = { - "color": [1, 1, 1], - "opacity": opacity, - "x_pos": x, - "y_pos": y, - "depth": depth, - "fade_time": fade_time, - "fade_interval": fade_interval, - "ui_scaling": ui_scaling, - } - self.settings = settings - self.system = None - self.overlay = None - self.handle = None - self.lastUpdate = time.monotonic() - self.thread_overlay = None - self.fadeRatio = 1 - self.loop = True - - def init(self): - try: - self.system = openvr.init(openvr.VRApplication_Background) - self.overlay = openvr.IVROverlay() - self.handle = self.overlay.createOverlay("Overlay_Speaker2log", "SOverlay_Speaker2log_UI") - - self.updateImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) - self.updateColor(self.settings["color"]) - self.updateOpacity(self.settings["opacity"]) - self.updateUiScaling(self.settings["Ui_scaling"]) - self.updatePosition( - (self.settings["x_pos"], self.settings["y_pos"]), - self.settings["depth"] - ) - self.overlay.showOverlay(self.handle) - self.initialized = True - except Exception as e: - print("Could not initialise OpenVR", e) - - def updateImage(self, img): - width, height = img.size - img = img.tobytes() - img = (ctypes.c_char * len(img)).from_buffer_copy(img) - self.overlay.setOverlayRaw(self.handle, img, width, height, 4) - self.updateOpacity(self.settings["opacity"]) - self.lastUpdate = time.monotonic() - - def clearImage(self): - self.updateImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) - - def updateColor(self, col): - """ - col is a 3-tuple representing (r, g, b) - """ - self.settings["color"] = col - r, g, b = self.settings["color"] - self.overlay.setOverlayColor(self.handle, r, g, b) - - def updateOpacity(self, opacity, with_fade=False): - self.settings["opacity"] = opacity - self.overlay.setOverlayAlpha( - self.handle, - self.settings["opacity"] if with_fade is False else self.settings["opacity"] * self.fadeRatio) - - def updateUiScaling(self, ui_scaling): - self.settings['ui_scaling'] = ui_scaling - self.overlay.setOverlayWidthInMeters(self.handle, self.settings['ui_scaling']) - - def updatePosition(self, pos, depth): - """ - pos is a 2-tuple representing normalized (x, y) - depth is a float representing the depth of the icon plane - """ - self.settings["x_pos"] = pos[0] - self.settings["y_pos"] = pos[1] - self.settings["depth"] = depth - - self.transform = mat34Id() # no rotation required for HMD attachment - - # assign position - self.transform[0][3] = self.settings["x_pos"] * self.settings['depth'] - self.transform[1][3] = self.settings["y_pos"] * self.settings['depth'] - self.transform[2][3] = - self.settings['depth'] - - self.overlay.setOverlayTransformTrackedDeviceRelative( - self.handle, - openvr.k_unTrackedDeviceIndex_Hmd, - self.transform - ) - - def updateDisplayDuration(self, display_duration): - self.settings['display_duration'] = display_duration - - def updateFadeoutDuration(self, fadeout_duration): - self.settings['fadeout_duration'] = fadeout_duration - - def checkActive(self): - try: - if self.system is not None and self.initialized is True: - new_event = openvr.VREvent_t() - while self.system.pollNextEvent(new_event): - if new_event.eventType == openvr.VREvent_Quit: - return False - return True - except Exception as e: - print("Could not check SteamVR running") - print(e) - return False - - def evaluateOpacityFade(self, lastUpdate, currentTime): - if (currentTime - lastUpdate) > self.settings['display_duration']: - timeThroughInterval = currentTime - lastUpdate - self.settings['display_duration'] - self.fadeRatio = 1 - timeThroughInterval / self.settings['fadeout_duration'] - if self.fadeRatio < 0: - self.fadeRatio = 0 - self.overlay.setOverlayAlpha(self.handle, self.fadeRatio * self.settings['opacity']) - - def update(self): - currTime = time.monotonic() - if self.settings['fadeout_duration'] != 0: - self.evaluateOpacityFade(self.lastUpdate, currTime) - else: - self.updateOpacity(self.settings["opacity"]) - - def mainloop(self): - self.loop = True - while self.checkActive() is True and self.loop is True: - startTime = time.monotonic() - self.update() - sleepTime = (1 / 16) - (time.monotonic() - startTime) - if sleepTime > 0: - time.sleep(sleepTime) - self.shutdownOverlay() - - def main(self): - self.init() - if self.initialized is True: - self.mainloop() - - def startOverlay(self): - self.thread_overlay = Thread(target=self.main) - self.thread_overlay.daemon = True - self.thread_overlay.start() - - def setStopOverlay(self): - self.loop = False - - def shutdownOverlay(self): - if self.thread_overlay is not None: - self.thread_overlay.join() - self.thread_overlay = None - if self.overlay is not None: - self.overlay.destroyOverlay(self.handle) - self.overlay = None - if self.system is not None: - openvr.shutdown() - self.system = None - self.initialized = False - - @staticmethod - def checkSteamvrRunning() -> bool: - _proc_name = "vrmonitor.exe" if os.name == 'nt' else "vrmonitor" - return _proc_name in (p.name() for p in process_iter()) - -if __name__ == '__main__': - from overlay_image import OverlayImage - overlay_image = OverlayImage() - - for i in range(100): - print(i) - overlay = Overlay(0, 0, 1, 1, 1, 1, 1) - overlay.startOverlay() - time.sleep(1) - - # Example usage - img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "Hello,World!Goodbye", "Japanese", ui_type="sakura") - overlay.updateImage(img) - time.sleep(0.5) - - img = overlay_image.createOverlayImageShort("こんにちは、世界!さようなら", "Japanese", "Hello,World!Goodbye", "Japanese") - overlay.updateImage(img) - time.sleep(0.5) - - overlay.shutdown() From ade7ae26495e5b28fe73b2f335c89fb802ce8c1b Mon Sep 17 00:00:00 2001 From: misyaguziya Date: Tue, 7 May 2024 18:05:06 +0900 Subject: [PATCH 12/14] =?UTF-8?q?=F0=9F=97=91=EF=B8=8F[Remove]=20Model=20:?= =?UTF-8?q?=20=E3=83=86=E3=82=B9=E3=83=88=E3=82=B3=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/controller.py b/controller.py index 6a7ef5b1..668c78d1 100644 --- a/controller.py +++ b/controller.py @@ -165,7 +165,6 @@ def receiveSpeakerMessage(message): if model.overlay.initialized is True: overlay_image = model.createOverlayImageShort(message, translation) model.updateOverlay(overlay_image) - print("model.updateOverlay(overlay_image)") # overlay_image = model.createOverlayImageLong("receive", message, translation) # model.updateOverlay(overlay_image) @@ -429,7 +428,6 @@ def callbackToggleTranscriptionReceive(is_turned_on): if config.ENABLE_TRANSCRIPTION_RECEIVE is True and config.ENABLE_OVERLAY_SMALL_LOG is True: if model.overlay.initialized is False and model.overlay.checkSteamvrRunning() is True: model.startOverlay() - print("model.startOverlay()") elif config.ENABLE_TRANSCRIPTION_RECEIVE is False: pass From 6ace1ed524472bcb2e5f153a497391c9a47bbfd1 Mon Sep 17 00:00:00 2001 From: misyaguziya Date: Tue, 7 May 2024 18:08:01 +0900 Subject: [PATCH 13/14] =?UTF-8?q?=F0=9F=90=9B[bugfix]=20Model=20:=20overla?= =?UTF-8?q?y=20=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF=E3=82=BF=E3=83=AA?= =?UTF-8?q?=E3=83=B3=E3=82=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - settingsのkey名を変更 - updateOpacityのちらつきを修正 --- models/overlay/overlay.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/models/overlay/overlay.py b/models/overlay/overlay.py index 68918baa..0f552308 100644 --- a/models/overlay/overlay.py +++ b/models/overlay/overlay.py @@ -8,7 +8,6 @@ import openvr from PIL import Image # from queue import Queue from threading import Thread -import OverlayImage def mat34Id(): arr = openvr.HmdMatrix34_t() @@ -18,7 +17,7 @@ def mat34Id(): return arr class Overlay: - def __init__(self, x, y , depth, fade_time, fade_interval, opacity, ui_scaling): + def __init__(self, x, y , depth, display_duration, fadeout_duration, opacity, ui_scaling): self.initialized = False settings = { "color": [1, 1, 1], @@ -26,8 +25,8 @@ class Overlay: "x_pos": x, "y_pos": y, "depth": depth, - "fade_time": fade_time, - "fade_interval": fade_interval, + "display_duration": display_duration, + "fadeout_duration": fadeout_duration, "ui_scaling": ui_scaling, } self.settings = settings @@ -48,7 +47,7 @@ class Overlay: self.updateImage(Image.new("RGBA", (1, 1), (0, 0, 0, 0))) self.updateColor(self.settings["color"]) self.updateOpacity(self.settings["opacity"]) - self.updateUiScaling(self.settings["Ui_scaling"]) + self.updateUiScaling(self.settings["ui_scaling"]) self.updatePosition( (self.settings["x_pos"], self.settings["y_pos"]), self.settings["depth"] @@ -79,9 +78,12 @@ class Overlay: def updateOpacity(self, opacity, with_fade=False): self.settings["opacity"] = opacity - self.overlay.setOverlayAlpha( - self.handle, - self.settings["opacity"] if with_fade is False else self.settings["opacity"] * self.fadeRatio) + + if with_fade is True: + if self.fadeRatio > 0: + self.overlay.setOverlayAlpha(self.handle, self.fadeRatio * self.settings["opacity"]) + else: + self.overlay.setOverlayAlpha(self.handle, self.settings["opacity"]) def updateUiScaling(self, ui_scaling): self.settings['ui_scaling'] = ui_scaling @@ -202,4 +204,4 @@ if __name__ == '__main__': overlay.updateImage(img) time.sleep(0.5) - overlay.shutdown() + overlay.setStopOverlay() From f449b4ebf1e5d1264e7fae17cb4e3695cc2e40e0 Mon Sep 17 00:00:00 2001 From: misyaguziya Date: Wed, 8 May 2024 11:01:30 +0900 Subject: [PATCH 14/14] =?UTF-8?q?=F0=9F=90=9B[bugfix]=20Model=20:=20overla?= =?UTF-8?q?y=5Fimage=20=E7=94=9F=E6=88=90=E3=81=99=E3=82=8B=E7=94=BB?= =?UTF-8?q?=E5=83=8F=E3=82=B5=E3=82=A4=E3=82=BA=E3=82=92=E6=88=BB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- models/overlay/overlay_image.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/models/overlay/overlay_image.py b/models/overlay/overlay_image.py index c52756bb..d1607179 100644 --- a/models/overlay/overlay_image.py +++ b/models/overlay/overlay_image.py @@ -140,9 +140,9 @@ class OverlayImage: def getUiSize(self): return { - "width": int(960), - "height": int(23), - "font_size": int(23), + "width": int(960*4), + "height": int(23*4), + "font_size": int(23*4), } def getUiColors(self, ui_type):