EyeTrackVR/EyeTrackApp/eyetrackapp.py
2024-07-19 13:14:42 -05:00

364 lines
14 KiB
Python

"""
------------------------------------------------------------------------------------------------------
,@@@@@@
@@@@@@@@@@@ @@@
@@@@@@@@@@@@ @@@@@@@@@@@
@@@@@@@@@@@@@ @@@@@@@@@@@@@@
@@@@@@@/ ,@@@@@@@@@@@@@
/@@@@@@@@@@@@@@@ @@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@
@@@@@@@@ @@@@@
,@@@ @@@@&
@@@@@@. @@@@
@@@ @@@@@@@@@/ @@@@@
,@@@. @@@@@@((@ @@@@(
//@@@ ,, @@@@ @@@@@
@@@( @@@@@@@
@@@ @ @@@@@@@@#
@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@(
Copyright (c) 2023 EyeTrackVR <3
LICENSE: GNU GPLv3
------------------------------------------------------------------------------------------------------
"""
import os
import PySimpleGUI as sg
import queue
import requests
import threading
from camera_widget import CameraWidget
from config import EyeTrackConfig
from eye import EyeId
from settings.VRCFTModuleSettings import VRCFTSettingsWidget
from settings.general_settings_widget import SettingsWidget
from settings.algo_settings_widget import AlgoSettingsWidget
from osc.osc import OSCManager
from osc.OSCMessage import OSCMessage
from utils.misc_utils import is_nt, resource_path
if is_nt:
from winotify import Notification
os.system("color") # init ANSI color
# Random environment variable to speed up webcam opening on the MSMF backend.
# https://github.com/opencv/opencv/issues/17687
os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0"
WINDOW_NAME = "EyeTrackApp"
RIGHT_EYE_NAME = "-RIGHTEYEWIDGET-"
LEFT_EYE_NAME = "-LEFTEYEWIDGET-"
SETTINGS_NAME = "-SETTINGSWIDGET-"
ALGO_SETTINGS_NAME = "-ALGOSETTINGSWIDGET-"
VRCFT_MODULE_SETTINGS_NAME = "-VRCFTSETTINGSWIDGET-"
LEFT_EYE_RADIO_NAME = "-LEFTEYERADIO-"
RIGHT_EYE_RADIO_NAME = "-RIGHTEYERADIO-"
BOTH_EYE_RADIO_NAME = "-BOTHEYERADIO-"
SETTINGS_RADIO_NAME = "-SETTINGSRADIO-"
ALGO_SETTINGS_RADIO_NAME = "-ALGOSETTINGSRADIO-"
VRCFT_MODULE_SETTINGS_RADIO_NAME = "-VRCFTSETTINGSRADIO-"
page_url = "https://github.com/RedHawk989/EyeTrackVR/releases/latest"
appversion = "EyeTrackApp 0.2.0 BETA 13"
def main():
# Get Configuration
config: EyeTrackConfig = EyeTrackConfig.load()
config.save()
cancellation_event = threading.Event()
# Check to see if we can connect to our video source first. If not, bring up camera finding
# dialog.
try:
if config.settings.gui_update_check:
response = requests.get("https://api.github.com/repos/EyeTrackVR/EyeTrackVR/releases/latest")
latestversion = response.json()["name"]
if (
appversion == latestversion
): # If what we scraped and hardcoded versions are same, assume we are up to date.
print(f"\033[92m[INFO] App is the latest version! [{latestversion}]\033[0m")
else:
print(
f"\033[93m[INFO] You have app version [{appversion}] installed. Please update to [{latestversion}] for the newest features.\033[0m"
)
try:
if is_nt:
cwd = os.getcwd()
# icon = cwd + "\Images\logo.ico"
icon = resource_path("Images/logo.ico")
toast = Notification(
app_id="EyeTrackApp",
title="New Update Available!",
msg=f"Please update to {latestversion}",
icon=r"{}".format(icon),
)
toast.add_actions(
label="Download Page",
launch="https://github.com/RedHawk989/EyeTrackVR/releases/latest",
)
toast.show()
except Exception as e:
print("[INFO] Toast notifications not supported")
except:
print("\033[91m[INFO] Could not check for updates. Please try again later.\033[0m")
osc_queue: queue.Queue[OSCMessage] = queue.Queue()
eyes = [
CameraWidget(EyeId.RIGHT, config, osc_queue),
CameraWidget(EyeId.LEFT, config, osc_queue),
]
settings = [
SettingsWidget(EyeId.SETTINGS, config),
AlgoSettingsWidget(EyeId.ALGOSETTINGS, config),
VRCFTSettingsWidget(EyeId.VRCFTMODULESETTINGS, config, osc_queue),
]
osc_manager = OSCManager(
osc_message_in_queue=osc_queue,
config=config,
)
config.register_listener_callback(osc_manager.update)
config.register_listener_callback(eyes[0].on_config_update)
config.register_listener_callback(eyes[1].on_config_update)
osc_manager.register_listeners(
config.settings.gui_osc_recenter_address,
[
eyes[0].recenter_eyes,
eyes[1].recenter_eyes,
],
)
osc_manager.register_listeners(
config.settings.gui_osc_recalibrate_address,
[
eyes[0].recalibrate_eyes,
eyes[1].recalibrate_eyes,
],
)
osc_manager.start()
layout = [
[
sg.Radio(
"Left Eye",
"EYESELECTRADIO",
background_color="#292929",
default=(config.eye_display_id == EyeId.LEFT),
key=LEFT_EYE_RADIO_NAME,
),
sg.Radio(
"Right Eye",
"EYESELECTRADIO",
background_color="#292929",
default=(config.eye_display_id == EyeId.RIGHT),
key=RIGHT_EYE_RADIO_NAME,
),
sg.Radio(
"Both Eyes",
"EYESELECTRADIO",
background_color="#292929",
default=(config.eye_display_id == EyeId.BOTH),
key=BOTH_EYE_RADIO_NAME,
),
sg.Radio(
"Settings",
"EYESELECTRADIO",
background_color="#292929",
default=(config.eye_display_id == EyeId.SETTINGS),
key=SETTINGS_RADIO_NAME,
),
sg.Radio(
"Algo Settings",
"EYESELECTRADIO",
background_color="#292929",
default=(config.eye_display_id == EyeId.ALGOSETTINGS),
key=ALGO_SETTINGS_RADIO_NAME,
),
sg.Radio(
"VRCFT Module Settings",
"EYESELECTRADIO",
background_color="#292929",
default=(config.eye_display_id == EyeId.VRCFTMODULESETTINGS),
key=VRCFT_MODULE_SETTINGS_RADIO_NAME,
),
],
[
sg.Column(
eyes[1].widget_layout,
vertical_alignment="top",
key=LEFT_EYE_NAME,
visible=(config.eye_display_id in [EyeId.LEFT, EyeId.BOTH]),
background_color="#424042",
),
sg.Column(
eyes[0].widget_layout,
vertical_alignment="top",
key=RIGHT_EYE_NAME,
visible=(config.eye_display_id in [EyeId.RIGHT, EyeId.BOTH]),
background_color="#424042",
),
sg.Column(
settings[0].get_layout(),
vertical_alignment="top",
key=SETTINGS_NAME,
visible=(config.eye_display_id in [EyeId.SETTINGS]),
background_color="#424042",
),
sg.Column(
settings[1].get_layout(),
vertical_alignment="top",
key=ALGO_SETTINGS_NAME,
visible=(config.eye_display_id in [EyeId.ALGOSETTINGS]),
background_color="#424042",
),
sg.Column(
settings[2].get_layout(),
vertical_alignment="top",
key=VRCFT_MODULE_SETTINGS_NAME,
visible=(config.eye_display_id in [EyeId.VRCFTMODULESETTINGS]),
background_color="#424042",
),
],
]
if config.eye_display_id in [EyeId.LEFT, EyeId.BOTH]:
eyes[1].start()
if config.eye_display_id in [EyeId.RIGHT, EyeId.BOTH]:
eyes[0].start()
if config.eye_display_id in [EyeId.SETTINGS]:
settings[0].start()
if config.eye_display_id in [EyeId.ALGOSETTINGS]:
settings[1].start()
if config.eye_display_id in [EyeId.VRCFTMODULESETTINGS]:
settings[2].start()
# the eye's needs to be running before it is passed to the OSC
# Create the window
window = sg.Window(
f"{appversion}",
layout,
icon=resource_path("Images/logo.ico"),
background_color="#292929",
)
# GUI Render loop
while True:
# First off, check for any events from the GUI
event, values = window.read(timeout=1) # this higher timeout saves some cpu usage
# If we're in either mode and someone hits q, quit immediately
if event == "Exit" or event == sg.WIN_CLOSED:
for eye in eyes:
eye.stop()
cancellation_event.set()
osc_manager.shutdown()
print("\033[94m[INFO] Exiting EyeTrackApp\033[0m")
os._exit(0) # I do not like this, but for now this fixes app hang on close
return
if values[RIGHT_EYE_RADIO_NAME] and config.eye_display_id != EyeId.RIGHT:
eyes[0].start()
eyes[1].stop()
settings[0].stop()
settings[1].stop()
settings[2].stop()
window[RIGHT_EYE_NAME].update(visible=True)
window[LEFT_EYE_NAME].update(visible=False)
window[SETTINGS_NAME].update(visible=False)
window[VRCFT_MODULE_SETTINGS_NAME].update(visible=False)
window[ALGO_SETTINGS_NAME].update(visible=False)
config.eye_display_id = EyeId.RIGHT
config.settings.tracker_single_eye = 2
config.save()
elif values[LEFT_EYE_RADIO_NAME] and config.eye_display_id != EyeId.LEFT:
settings[0].stop()
settings[1].stop()
settings[2].stop()
eyes[0].stop()
eyes[1].start()
window[RIGHT_EYE_NAME].update(visible=False)
window[LEFT_EYE_NAME].update(visible=True)
window[SETTINGS_NAME].update(visible=False)
window[VRCFT_MODULE_SETTINGS_NAME].update(visible=False)
window[ALGO_SETTINGS_NAME].update(visible=False)
config.eye_display_id = EyeId.LEFT
config.settings.tracker_single_eye = 1
config.save()
elif values[BOTH_EYE_RADIO_NAME] and config.eye_display_id != EyeId.BOTH:
settings[0].stop()
settings[1].stop()
settings[2].stop()
eyes[1].start()
eyes[0].start()
window[LEFT_EYE_NAME].update(visible=True)
window[RIGHT_EYE_NAME].update(visible=True)
window[SETTINGS_NAME].update(visible=False)
window[VRCFT_MODULE_SETTINGS_NAME].update(visible=False)
window[ALGO_SETTINGS_NAME].update(visible=False)
config.eye_display_id = EyeId.BOTH
config.settings.tracker_single_eye = 0
config.save()
elif values[SETTINGS_RADIO_NAME] and config.eye_display_id != EyeId.SETTINGS:
eyes[0].stop()
eyes[1].stop()
settings[1].stop()
settings[0].start()
settings[2].stop()
window[RIGHT_EYE_NAME].update(visible=False)
window[LEFT_EYE_NAME].update(visible=False)
window[SETTINGS_NAME].update(visible=True)
window[VRCFT_MODULE_SETTINGS_NAME].update(visible=False)
window[ALGO_SETTINGS_NAME].update(visible=False)
config.eye_display_id = EyeId.SETTINGS
config.save()
elif values[ALGO_SETTINGS_RADIO_NAME] and config.eye_display_id != EyeId.ALGOSETTINGS:
eyes[0].stop()
eyes[1].stop()
settings[0].stop()
settings[1].start()
settings[2].stop()
window[RIGHT_EYE_NAME].update(visible=False)
window[LEFT_EYE_NAME].update(visible=False)
window[SETTINGS_NAME].update(visible=False)
window[VRCFT_MODULE_SETTINGS_NAME].update(visible=False)
window[ALGO_SETTINGS_NAME].update(visible=True)
config.eye_display_id = EyeId.ALGOSETTINGS
config.save()
elif values[VRCFT_MODULE_SETTINGS_RADIO_NAME] and config.eye_display_id != EyeId.VRCFTMODULESETTINGS:
eyes[0].stop()
eyes[1].stop()
settings[0].stop()
settings[1].stop()
settings[2].start()
window[RIGHT_EYE_NAME].update(visible=False)
window[LEFT_EYE_NAME].update(visible=False)
window[SETTINGS_NAME].update(visible=False)
window[VRCFT_MODULE_SETTINGS_NAME].update(visible=True)
window[ALGO_SETTINGS_NAME].update(visible=False)
config.eye_display_id = EyeId.VRCFTMODULESETTINGS
config.save()
else:
# Otherwise, render all
for eye in eyes:
if eye.started():
eye.render(window, event, values)
for setting in settings:
if setting.started():
setting.render(window, event, values)
if __name__ == "__main__":
main()