-
-
Notifications
You must be signed in to change notification settings - Fork 156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
System tray state machine #290
base: main
Are you sure you want to change the base?
Changes from 13 commits
011d8de
dcd56b6
d8c465d
4ca59a8
dbb9979
cf04900
323e829
81f6eca
2f5ecd0
238ce1d
ad2c104
b071ec3
f765d48
6096a8f
ecf0e65
aa5d1bc
8c99bf3
59fd3e9
5e28b26
6abdee7
e89ee68
ca130bc
2d51ff9
23fb1ad
d74385d
17c545c
1de4930
b6d7bc1
895d738
ddead1e
ec3f672
74254f3
8d6f8c2
f962f73
de071c1
2da733b
a2c2d27
0c6283b
1027546
49d3d32
bb45d40
19afebd
3a66d88
038470e
e746bc6
dbed721
e3afd5a
443150a
7a5e401
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import kivy | ||
from subprocess import Popen | ||
import signal | ||
from kivy.app import App | ||
from kivy.uix.button import Button | ||
from kivy.uix.floatlayout import FloatLayout | ||
from kivy.core.window import Window | ||
import pygetwindow as gw | ||
from loguru import logger | ||
import ctypes | ||
from ctypes import wintypes | ||
from kivy.clock import Clock | ||
import sys | ||
import pywinauto | ||
import Quartz | ||
|
||
|
||
class OpenAdaptWidget(FloatLayout): | ||
def __init__(self, **kwargs): | ||
super(OpenAdaptWidget, self).__init__(**kwargs) | ||
self.window = Window | ||
self.window.borderless = True | ||
self.window.size = (50, 50) | ||
self.window.clearcolor = (255, 255, 255) | ||
self.window.always_on_top = True | ||
self.PROC = None | ||
self.button = Button(background_normal="assets/logo.png") | ||
self.button.bind(on_press=self.callback) | ||
self.current_state = "default" | ||
self.add_widget(self.button) | ||
# Check for active window changes every 0.5 seconds | ||
self.active_window = None | ||
self._active_window_checker = Clock.schedule_interval( | ||
self.position_above_active_window, 0.5 | ||
) | ||
|
||
def position_above_active_window(self, *args): | ||
if sys.platform == "win32": | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks @jesicasusanto ! What do you think about re-using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
try: | ||
app = pywinauto.application.Application(backend="win32").connect( | ||
active_only=True | ||
) | ||
window = app.active() | ||
active_window_properties = window.get_properties() | ||
self.window.top, self.window.right = ( | ||
active_window_properties["rectangle"].top, | ||
active_window_properties["rectangle"].right, | ||
) | ||
except: | ||
pass | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned elsewhere I think it would be preferable to re-use In general, however, swallowing exceptions like this is not recommended -- logging is preferable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. resolved in 6096a8f |
||
elif sys.platform == "darwin": | ||
windows = Quartz.CGWindowListCopyWindowInfo( | ||
( | ||
Quartz.kCGWindowListExcludeDesktopElements | ||
| Quartz.kCGWindowListOptionOnScreenOnly | ||
), | ||
Quartz.kCGNullWindowID, | ||
) | ||
active_windows_info = [win for win in windows if win["kCGWindowLayer"] == 0] | ||
meta = active_windows_info[0] | ||
bounds = meta["kCGWindowBounds"] | ||
left = bounds["X"] | ||
self.window.top = bounds["Y"] + 10 | ||
width = bounds["Width"] | ||
self.window.left = left | ||
if (self.window.top, self.window.right) != self.prev_active_window_position: | ||
self.prev_active_window_position = (self.window.top, self.window.right) | ||
if self.current_state == "replay_in_progress": | ||
self.current_state == "replay_paused" | ||
|
||
def callback(self, instance): | ||
if self.current_state == "default": | ||
self.button.background_normal = "assets/recording_inprogress.png" | ||
self.current_state = "recording_in_progress" | ||
self.start_recording() | ||
elif self.current_state == "recording_in_progress": | ||
self.button.background_normal = "assets/replay_available.png" | ||
self.current_state = "replay_available" | ||
self.stop_recording() | ||
elif self.current_state == "replay_available": | ||
self.button.background_normal = "assets/replay_inprogress.png" | ||
self.current_state = "replaying_in_progress" | ||
self.replay_recording() | ||
elif self.current_state == "replaying_in_progress": | ||
self.button.background_normal = "assets/replay_paused" | ||
self.current_state = "replaying_paused" | ||
self.pause_replay() | ||
elif self.current_state == "replaying_paused": | ||
self.button.background_normal = "assets/replay_inprogress.png" | ||
self.current_state = "replaying_in_progress" | ||
self.resume_replay() | ||
|
||
def start_recording(self): | ||
self.PROC = Popen( | ||
"python -m openadapt.record " + "test", | ||
shell=True, | ||
) | ||
|
||
def stop_recording(self): | ||
try: | ||
self.PROC.send_signal(signal.CTRL_C_EVENT) | ||
# Wait for process to terminate | ||
self.PROC.wait() | ||
except KeyboardInterrupt: | ||
# Catch the KeyboardInterrupt exception to prevent termination | ||
pass | ||
self.PROC = None | ||
|
||
def replay_recording(self): | ||
self.PROC = Popen( | ||
"python -m openadapt.replay " + "NaiveReplayStrategy", | ||
shell=True, | ||
) | ||
|
||
def pause_replay(self): | ||
self.PROC.send_signal(signal.SIGSTOP) | ||
|
||
def resume_replay(self): | ||
self.PROC.send_signal(signal.SIGCONT) | ||
|
||
|
||
class OpenAdapt(App): | ||
def build(self): | ||
return OpenAdaptWidget() | ||
|
||
|
||
if __name__ == "__main__": | ||
OpenAdapt().run() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this isn't a constant, what do you think about naming it e.g.
process
? Even better, how about splitting it intoreplay_process
andrecord_process
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved in ecf0e65