forked from acidcoke/Grimassist
-
Notifications
You must be signed in to change notification settings - Fork 4
/
FaceCommander.py
142 lines (127 loc) · 4.58 KB
/
FaceCommander.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
"""\
Face Commander.
Control and move the pointer using head movements and facial gestures.
"""
# Standard library imports, in alphabetic order.
#
# Command line arguments module. Command line arguments are used for diagnostic
# options.
# https://docs.python.org/3/library/argparse.html
import argparse
#
# Logging module.
# https://docs.python.org/3/library/logging.html
import logging
#
# Object oriented path handling.
# https://docs.python.org/3/library/pathlib.html
from pathlib import Path
#
# Operating system module, used to get the command line and standard output.
# https://docs.python.org/3/library/sys.html
from sys import argv, stdout, executable as sys_executable
#
# Module for text dedentation. Only used for --help description.
# https://docs.python.org/3/library/textwrap.html
import textwrap
#
# PIP modules, in alphabetic order.
#
# https://customtkinter.tomschimansky.com/documentation/
import customtkinter
#
# Local imports.
#
from src.app import App
from src.gui import MainGui
from src.pipeline import Pipeline
from src.task_killer import TaskKiller
LOG_FORMAT = r"%(asctime)s %(levelname)s %(name)s: %(funcName)s: %(message)s"
LOG_LEVEL = logging.INFO
logger = logging.getLogger(__name__)
class MainApp(MainGui, Pipeline):
def __init__(self, tk_root):
super().__init__(tk_root)
# Wait for window drawing.
self.tk_root.wm_protocol("WM_DELETE_WINDOW", self.close_all)
self.is_active = True
# Enter loop
self.tk_root.after(50, self.anim_loop)
def anim_loop(self):
try:
if self.is_active:
self.poll_update_state()
# Run detectors and controllers.
self.pipeline_tick()
self.tk_root.after(50, self.anim_loop)
except Exception as e:
logging.critical(e, exc_info=e)
def close_all(self):
logging.info("Close all")
self.is_active = False
# Completely close this process
TaskKiller().exit()
def create_app_data_root():
try:
App().dataRoot.mkdir(parents=True, exist_ok=True)
except FileExistsError as exception:
# For details of why this exception is raised see the reference
# documentation here.
# https://docs.python.org/3/library/pathlib.html#pathlib.Path.mkdir
raise RuntimeError(
"Non-directory found where application data directory is expected."
f' {App().dataRoot}'
) from exception
def start_logging():
logging.basicConfig(
format=LOG_FORMAT, level=LOG_LEVEL, handlers=(
logging.FileHandler(App().logPath, mode="w"),
logging.StreamHandler(stdout)
)
)
logger.info(
f'Installation root "{App().installationRoot}".'
f' Application data root "{App().dataRoot}".')
if __name__ == "__main__":
cli={
'description': textwrap.dedent(__doc__),
'formatter_class': argparse.RawDescriptionHelpFormatter,
'allow_abbrev': False
}
progPath = Path(__file__)
for index, arg in enumerate(argv):
try:
if progPath.samefile(arg):
# TOTH https://stackoverflow.com/a/54443527/7657675
try:
executable = Path(sys_executable).relative_to(Path.cwd())
except ValueError:
executable = Path(sys_executable) # Use absolute path if not relative.
cli['prog'] = " ".join([str(executable),] + argv[0:index + 1])
break
except FileNotFoundError:
pass
argumentParser = argparse.ArgumentParser(**cli)
argumentParser.add_argument(
'--no-user-agent', dest='userAgentHeader', action='store_false', help=
"Don't send a user agent header when fetching release details, which"
" causes fetch to fail with HTTP 403.")
argumentParser.add_argument(
'--release-information-delay', dest='releaseInformationDelay'
, metavar="SECONDS", type=int, help=
"Wait for a number of seconds after retrieving each 1kb of release"
" information. This makes it easy to see retrieval progress on the"
" About page.")
argumentParser.add_argument(
'--include-prereleases', dest='includePrereleases'
, action='store_true', help=
"Include prereleases in the update check and installer download.")
argumentParser.parse_args(argv[1:], App())
create_app_data_root()
start_logging()
tk_root = customtkinter.CTk()
logger.info("Starting main app.")
TaskKiller().start()
main_app = MainApp(tk_root)
main_app.tk_root.mainloop()
main_app = None