Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
willwade committed Oct 4, 2024
2 parents 028dc46 + dcb679a commit c1e3483
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 42 deletions.
4 changes: 2 additions & 2 deletions FaceCommander.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def __init__(self, tk_root):
self.is_active = True

# Enter loop
self.tk_root.after(1, self.anim_loop)
self.tk_root.after(50, self.anim_loop)

def anim_loop(self):
try:
Expand All @@ -60,7 +60,7 @@ def anim_loop(self):

# Run detectors and controllers.
self.pipeline_tick()
self.tk_root.after(1, self.anim_loop)
self.tk_root.after(50, self.anim_loop)
except Exception as e:
logging.critical(e, exc_info=e)

Expand Down
2 changes: 1 addition & 1 deletion assets/Version.ini
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# This file has the version number. For details of its usage, see the
# Developer/VersionNumberDeveloperGuide.md file.
[Release]
VersionNumber=0.9.1
VersionNumber=0.9.2
13 changes: 12 additions & 1 deletion build_executable.bat
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,23 @@ set site_packages=%venv_path%\Lib\site-packages
rem Convert the path to forward slashes for Nuitka compatibility
set site_packages=%site_packages:\=/%

rem Set the UAC flag conditionally based on --signed flag
rem Initialize SIGNED_FLAG to 0 (unsigned by default)
set SIGNED_FLAG=0

rem Loop through the arguments and check if --signed is present
for %%a in (%*) do (
if "%%a"=="--signed" (
set SIGNED_FLAG=1
)
)

rem Set the UAC_FLAG conditionally based on --signed flag
set UAC_FLAG=
if %SIGNED_FLAG%==1 (
set UAC_FLAG=--windows-uac-uiaccess
)


rem Capture the Python version from the Poetry environment
for /f "delims=" %%v in ('poetry run python -c "import sys; print(f'{sys.version_info.major}{sys.version_info.minor}')"') do set python_version=%%v

Expand Down
7 changes: 7 additions & 0 deletions src/config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def __init__(self):
self.unsave_configs = False
self.unsave_mouse_bindings = False
self.unsave_keyboard_bindings = False
self.throttle_time = 1.5
self.config = None

# Load config
Expand All @@ -61,6 +62,12 @@ def currentProfilePath(self):
App().profilesDirectory, CURRENT_PROFILE_FILENAME)
return self._currentProfilePath

def set_throttle_time(self, time):
self.throttle_time = time

def get_throttle_time(self):
return self.throttle_time

def _get_profiles_directory(self, *name):
path = App().profilesDirectory
if path.exists():
Expand Down
26 changes: 24 additions & 2 deletions src/controllers/keybinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class Keybinder(metaclass=Singleton):
def __init__(self) -> None:
self.delay_count = None
self.key_states = None
self.last_act_time = None
self.schedule_toggle_off = {}
self.schedule_toggle_on = {}
self.monitors = None
Expand Down Expand Up @@ -55,11 +56,13 @@ def init_states(self) -> None:
"""
# keep states for all registered keys.
self.key_states = {}
self.last_act_time = {}
self.start_hold_ts = {}
for _, v in (ConfigManager().mouse_bindings |
ConfigManager().keyboard_bindings).items():
state_name = v[0]+"_"+v[1]
self.key_states[state_name] = False
self.last_act_time[state_name] = int(time.time() * 1000)
self.schedule_toggle_off[state_name] = False
self.schedule_toggle_on[state_name] = True
self.start_hold_ts[state_name] = math.inf
Expand Down Expand Up @@ -98,6 +101,8 @@ def get_current_monitor(self) -> int:
def meta_action(self, val, action, threshold, is_active: bool) -> None:
state_name = "meta_" + action

if self.last_act_time[state_name] > int(time.time() * 1000) - ConfigManager().get_throttle_time() * 1000:
return
if action == "pause":

if (val > threshold) and (self.key_states[state_name] is False):
Expand All @@ -110,6 +115,7 @@ def meta_action(self, val, action, threshold, is_active: bool) -> None:
self.key_states[state_name] = True
elif (val < threshold) and (self.key_states[state_name] is True):
self.key_states[state_name] = False
self.last_act_time[state_name] = int(time.time() * 1000)

if is_active:

Expand All @@ -127,6 +133,7 @@ def meta_action(self, val, action, threshold, is_active: bool) -> None:
elif (val < threshold) and (self.key_states[state_name] is
True):
self.key_states[state_name] = False
self.last_act_time[state_name] = int(time.time() * 1000)

elif action == "cycle":
if (val > threshold) and (self.key_states[state_name] is
Expand All @@ -140,15 +147,19 @@ def meta_action(self, val, action, threshold, is_active: bool) -> None:
elif (val < threshold) and (self.key_states[state_name] is
True):
self.key_states[state_name] = False
self.last_act_time[state_name] = int(time.time() * 1000)

def mouse_action(self, val, action, threshold, mode) -> None:
state_name = "mouse_" + action

if self.last_act_time[state_name] > int(time.time() * 1000) - ConfigManager().get_throttle_time() * 1000:
return
if mode == Trigger.SINGLE:
if val > threshold:
if self.key_states[state_name] is False:
pydirectinput.click(button=action)
self.key_states[state_name] = True
self.last_act_time[state_name] = int(time.time() * 1000)
if val < threshold:
self.key_states[state_name] = False

Expand All @@ -160,6 +171,7 @@ def mouse_action(self, val, action, threshold, mode) -> None:
elif (val < threshold) and (self.key_states[state_name] is True):
pydirectinput.mouseUp(button=action)
self.key_states[state_name] = False
self.last_act_time[state_name] = int(time.time() * 1000)

elif mode == Trigger.DYNAMIC:
if val > threshold:
Expand All @@ -177,11 +189,12 @@ def mouse_action(self, val, action, threshold, mode) -> None:
elif (val < threshold) and (self.key_states[state_name] is True):

self.key_states[state_name] = False

self.last_act_time[state_name] = int(time.time() * 1000)
if self.holding[state_name]:
pydirectinput.mouseUp(button=action)
self.holding[state_name] = False
self.start_hold_ts[state_name] = math.inf
self.last_act_time[state_name] = int(time.time() * 1000)

elif mode == Trigger.TOGGLE:
if val > threshold:
Expand All @@ -194,6 +207,7 @@ def mouse_action(self, val, action, threshold, mode) -> None:
if self.schedule_toggle_off[state_name] is True:
pydirectinput.mouseUp(button=action)
self.key_states[state_name] = False
self.last_act_time[state_name] = int(time.time() * 1000)

if val < threshold:
if self.key_states[state_name] is True:
Expand Down Expand Up @@ -221,16 +235,20 @@ def mouse_action(self, val, action, threshold, mode) -> None:
if self.key_states[state_name] is True:
self.key_states[state_name] = False
self.start_hold_ts[state_name] = math.inf
self.last_act_time[state_name] = int(time.time() * 1000)

def keyboard_action(self, val, keysym, threshold, mode):

state_name = "keyboard_" + keysym

if self.last_act_time[state_name] > int(time.time() * 1000) - ConfigManager().get_throttle_time() * 1000:
return
if mode == Trigger.SINGLE:
if val > threshold:
if self.key_states[state_name] is False:
pydirectinput.press(keys=keysym)
self.key_states[state_name] = True
self.last_act_time[state_name] = int(time.time() * 1000)
if val < threshold:
self.key_states[state_name] = False

Expand All @@ -242,6 +260,7 @@ def keyboard_action(self, val, keysym, threshold, mode):
elif (val < threshold) and (self.key_states[state_name] is True):
pydirectinput.keyUp(key=keysym)
self.key_states[state_name] = False
self.last_act_time[state_name] = int(time.time() * 1000)

elif mode == Trigger.DYNAMIC:
if val > threshold:
Expand All @@ -263,7 +282,8 @@ def keyboard_action(self, val, keysym, threshold, mode):
if self.holding[state_name]:
pydirectinput.keyUp(key=keysym)
self.holding[state_name] = False
self.start_hold_ts[state_name] = math.inf
self.start_hold_ts[state_name] = math.inf
self.last_act_time[state_name] = int(time.time() * 1000)

elif mode == Trigger.TOGGLE:
if val > threshold:
Expand All @@ -276,6 +296,7 @@ def keyboard_action(self, val, keysym, threshold, mode):
if self.schedule_toggle_off[state_name] is True:
pydirectinput.keyUp(key=keysym)
self.key_states[state_name] = False
self.last_act_time[state_name] = int(time.time() * 1000)

if val < threshold:
if self.key_states[state_name] is True:
Expand Down Expand Up @@ -303,6 +324,7 @@ def keyboard_action(self, val, keysym, threshold, mode):
if self.key_states[state_name] is True:
self.key_states[state_name] = False
self.start_hold_ts[state_name] = math.inf
self.last_act_time[state_name] = int(time.time() * 1000)

def act(self, blendshape_values) -> None:
"""Trigger devices action base on blendshape values
Expand Down
46 changes: 30 additions & 16 deletions src/gui/frames/frame_cam_preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from PIL import Image, ImageDraw
import threading
import platform
import ctypes

CANVAS_WIDTH = 320
CANVAS_HEIGHT = 240
Expand Down Expand Up @@ -46,7 +47,7 @@ def __init__(self, master, master_callback: callable, **kwargs):
bd=0,
relief='ridge',
highlightthickness=0)
self.canvas.grid(row=0, column=0, padx=10, pady=10, sticky="sw")
self.canvas.grid(row=0, column=0, padx=10, pady=0, sticky="sw")

# Toggle label
self.toggle_label = customtkinter.CTkLabel(master=self,
Expand Down Expand Up @@ -96,7 +97,7 @@ def __init__(self, master, master_callback: callable, **kwargs):
self.minimize_label.grid(row=1,
column=0,
padx=(10, 0),
pady=30,
pady=(30, 0),
sticky="nw")

# Toggle switch
Expand All @@ -119,22 +120,35 @@ def __init__(self, master, master_callback: callable, **kwargs):
self.minimize_switch.grid(row=1,
column=0,
padx=(100, 0),
pady=30,
pady=(30, 0),
sticky="nw")

# Toggle description label
self.toggle_label = customtkinter.CTkLabel(
master=self,
compound='right',
text="Allow facial gestures to control\nyour actions. ",
text_color="#444746",
justify=tkinter.LEFT)
self.toggle_label.cget("font").configure(size=12)
self.toggle_label.grid(row=2,
column=0,
padx=(10, 0),
pady=5,
sticky="nw")

hdc = ctypes.windll.user32.GetDC(0)

# Get the screen DPI
dpi_x = ctypes.windll.gdi32.GetDeviceCaps(hdc, 88) # 88 is the LOGPIXELSX value for horizontal DPI

# Release the device context (DC)
ctypes.windll.user32.ReleaseDC(0, hdc)

# 96 DPI is the default for 100% scaling, so scaling is DPI / 96
scaling_percentage = dpi_x / 96 * 100

if int(self.tk_root.winfo_screenwidth() * 100 / scaling_percentage) > 1280:
# Toggle description label
self.toggle_label = customtkinter.CTkLabel(
master=self,
compound='right',
text="Allow facial gestures to control\nyour actions. ",
text_color="#444746",
justify=tkinter.LEFT)
self.toggle_label.cget("font").configure(size=12)
self.toggle_label.grid(row=2,
column=0,
padx=(10, 0),
pady=5,
sticky="nw")

# Set first image.
self.canvas_image = self.canvas.create_image(0,
Expand Down
19 changes: 15 additions & 4 deletions src/gui/main_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from tkinter import IntVar, StringVar

import customtkinter

import ctypes
from src.app import App
from src.update_manager import UpdateManager
from src.gui import frames
Expand All @@ -28,16 +28,27 @@ def __init__(self, tk_root):
self.tk_root = tk_root

self.tk_root.bind('<Control-x>', self.switch_cursor)

hdc = ctypes.windll.user32.GetDC(0)

# Get the screen DPI
dpi_x = ctypes.windll.gdi32.GetDeviceCaps(hdc, 88) # 88 is the LOGPIXELSX value for horizontal DPI

# Release the device context (DC)
ctypes.windll.user32.ReleaseDC(0, hdc)

# 96 DPI is the default for 100% scaling, so scaling is DPI / 96
scaling_percentage = dpi_x / 96 * 100
# Get screen width and height for dynamic scaling
screen_width = self.tk_root.winfo_screenwidth()
screen_height = self.tk_root.winfo_screenheight()
screen_width = int(self.tk_root.winfo_screenwidth() * 100 / scaling_percentage)
screen_height = int(self.tk_root.winfo_screenheight() * 100 / scaling_percentage)
self.bounday_width = 800
self.sidebar_big_size = 260
self.sidebar_small_size = 60

# Set window size based on screen dimensions for tablets
if screen_width <= 1280:
self.tk_root.geometry(f"{int(screen_width * 0.9)}x{int(screen_height * 0.9)}")
self.tk_root.geometry(f"{int(screen_width * 0.9)}x{int(screen_height * 0.9)}+0+0")
else:
self.tk_root.geometry("1024x800")

Expand Down
2 changes: 1 addition & 1 deletion src/gui/pages/page_keyboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def __init__(
self.shared_info_balloon = Balloon(
self, image_path="assets/images/balloon.png")

self.shared_dialog = Select_Facial_Gesture(self, shape_list.available_gestures, width=750, callback=self.dialog_callback)
self.shared_dialog = Select_Facial_Gesture(self, shape_list.available_gestures, width=650, callback=self.dialog_callback)

self.help_icon = customtkinter.CTkImage(
Image.open("assets/images/help.png").resize(HELP_ICON_SIZE),
Expand Down
7 changes: 4 additions & 3 deletions src/gui/pages/page_select_gestures.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def __init__(
Image.open("assets/images/help.png").resize(HELP_ICON_SIZE),
size=HELP_ICON_SIZE)

self.shared_dialog = Select_Facial_Gesture(self, shape_list.available_gestures, width=750, callback=self.dialog_callback)
self.shared_dialog = Select_Facial_Gesture(self, shape_list.available_gestures, width=650, callback=self.dialog_callback)
# Divs
self.divs = self.create_divs(shape_list.available_actions_keys,
shape_list.available_gestures_keys)
Expand Down Expand Up @@ -99,8 +99,9 @@ def dialog_callback(self, caller_name, target_gesture):
div["volume_bar"].grid()
div["tips_label"].grid()
div["subtle_label"].grid()
div["timer_slider"].grid()
div["timer_label"].grid()
if 'blink' in target_gesture:
div["timer_slider"].grid()
div["timer_label"].grid()
div["trigger_dropdown"].grid()
thres_value = div["slider"].get() / 100
trigger = Trigger(div["trigger_dropdown"].get())
Expand Down
Loading

0 comments on commit c1e3483

Please sign in to comment.