Skip to content

Commit 2c41e06

Browse files
committed
Added hotkeys, work setup
1 parent 0cd8959 commit 2c41e06

14 files changed

+258
-123
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.secrets
2+
.env

Makefile

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
.PHONY: run tunnel client help venv db db-migrate db-makemigrations format
22

3+
SHELL := /bin/bash
4+
35

46
# Help system from https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
57
.DEFAULT_GOAL := help
@@ -18,4 +20,5 @@ format: ## Format the code
1820
venv/bin/reorder-python-imports --py38-plus `find src -name "*.py"` || venv/bin/black src --target-version py38
1921

2022
run: ## Run the app
21-
cd src && ../venv/bin/python -m main
23+
source .secrets
24+
cd src && ../venv/bin/python -m sleuthdeck.cli work.py

requirements.in

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pydub
66
selenium
77
obs-websocket-py
88
watchdog
9+
pyautogui
910

1011
pytest
1112
black

requirements.txt

+20
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ idna==3.3
3737
# urllib3
3838
iniconfig==1.1.1
3939
# via pytest
40+
mouseinfo==0.1.3
41+
# via pyautogui
4042
mypy-extensions==0.4.3
4143
# via black
4244
numpy==1.22.1
@@ -61,16 +63,34 @@ psutil==5.9.0
6163
# via -r requirements.in
6264
py==1.11.0
6365
# via pytest
66+
pyautogui==0.9.53
67+
# via -r requirements.in
6468
pycparser==2.21
6569
# via cffi
6670
pydub==0.25.1
6771
# via -r requirements.in
72+
pygetwindow==0.0.9
73+
# via pyautogui
74+
pymsgbox==1.0.9
75+
# via pyautogui
6876
pyopenssl==21.0.0
6977
# via urllib3
7078
pyparsing==3.0.6
7179
# via packaging
80+
pyperclip==1.8.2
81+
# via mouseinfo
82+
pyrect==0.1.4
83+
# via pygetwindow
84+
pyscreeze==0.1.28
85+
# via pyautogui
7286
pytest==6.2.5
7387
# via -r requirements.in
88+
python3-xlib==0.15
89+
# via
90+
# mouseinfo
91+
# pyautogui
92+
pytweening==1.0.4
93+
# via pyautogui
7494
reorder-python-imports==2.6.0
7595
# via -r requirements.in
7696
selenium==4.1.0

src/main.py

-71
This file was deleted.

src/sleuthdeck/actions.py

+31-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import signal
22
import subprocess
3+
from time import sleep
4+
from typing import Optional, Union
5+
6+
from pyautogui import hotkey
37

48
from sleuthdeck.deck import Action
59
from sleuthdeck.deck import ClickType
610
from sleuthdeck.deck import Key
711
from sleuthdeck.deck import KeyScene
812
from sleuthdeck.deck import Scene
9-
from sleuthdeck.windows import get_window
13+
from sleuthdeck.windows import get_window, By
1014

1115

1216
class Command(Action):
@@ -39,7 +43,7 @@ def execute(self, scene: KeyScene, key: Key, click: ClickType):
3943

4044

4145
class MaximizeWindow(Action):
42-
def __init__(self, title: str):
46+
def __init__(self, title: Union[str, By]):
4347
self.title = title
4448

4549
def execute(self, scene: KeyScene, key: Key, click: ClickType):
@@ -50,20 +54,28 @@ def execute(self, scene: KeyScene, key: Key, click: ClickType):
5054
print("No window found")
5155

5256

57+
class Pause(Action):
58+
def __init__(self, seconds: Union[float, int]):
59+
self.seconds = seconds
60+
61+
def execute(self, scene: KeyScene, key: Key, click: ClickType):
62+
sleep(self.seconds)
63+
64+
5365
class CloseWindow(Action):
54-
def __init__(self, title: str):
66+
def __init__(self, title: Union[str, By]):
5567
self.title = title
5668

5769
def execute(self, scene: KeyScene, key: Key, click: ClickType):
5870
window = get_window(self.title, attempts=5 * 10)
5971
if window:
6072
window.close()
6173
else:
62-
print("No window found")
74+
print(f"No window found with {self.title}")
6375

6476

6577
class MoveWindow(Action):
66-
def __init__(self, title: str, x: int, y: int, width: int, height: int):
78+
def __init__(self, title: Union[str, By], x: int, y: int, width: int, height: int):
6779
self.x = x
6880
self.y = y
6981
self.width = width
@@ -76,3 +88,17 @@ def execute(self, scene: KeyScene, key: Key, click: ClickType):
7688
window.move(self.x, self.y, self.width, self.height)
7789
else:
7890
print("No window found")
91+
92+
93+
class SendHotkey(Action):
94+
def __init__(self, title: Union[str, By], *hotkey: str):
95+
self.title = title
96+
self.hotkey = hotkey
97+
98+
def execute(self, scene: KeyScene, key: Key, click: ClickType):
99+
print("sending key")
100+
window = get_window(self.title, attempts=5 * 10)
101+
print("got window")
102+
window.focus()
103+
hotkey(*self.hotkey)
104+
print("sent")
File renamed without changes.

src/sleuthdeck/cli.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import sys
55
import threading
66
import time
7+
import traceback
78
from datetime import datetime
89
from datetime import timedelta
910
from multiprocessing import Queue
@@ -39,7 +40,13 @@ def run(self):
3940

4041
try:
4142
while self._reloading_queue.get():
42-
mod = runpy.run_module(mod_name)
43+
try:
44+
mod = runpy.run_module(mod_name)
45+
except:
46+
print(f"Error loading script")
47+
traceback.print_exc()
48+
continue
49+
4350
if "run" not in mod:
4451
raise ValueError(f"Script {self.script} missing 'run' function")
4552

@@ -50,7 +57,12 @@ def run(self):
5057
print(f"Loaded new deck from {self.script}")
5158
self.deck = deck
5259
with deck:
53-
mod["run"](deck)
60+
try:
61+
mod["run"](deck)
62+
except Exception as e:
63+
print(f"Error: {e}")
64+
traceback.print_exc()
65+
continue
5466
except EOFError:
5567
if self.deck and self.deck.stream_deck.is_open():
5668
self.deck.close()

src/sleuthdeck/deck.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import asyncio
44
import threading
55
import time
6+
import traceback
67
from asyncio import Future
78
from asyncio import Task
89
from dataclasses import dataclass
@@ -53,8 +54,11 @@ def connect(self, scene: KeyScene):
5354

5455
class Deck:
5556
def __init__(self):
57+
print("Scanning for stream decks")
5658
streamdecks = DeviceManager().enumerate()
5759
self.stream_deck: StreamDeck = streamdecks[0]
60+
print(f"Found stream deck: {self.stream_deck.id()}")
61+
5862
self._animation = Animations(self.stream_deck)
5963
self._scene = Scene()
6064
self._last_scene: Scene = self._scene
@@ -191,12 +195,14 @@ def _check_long_click(self):
191195

192196
def _run_actions(self, click: ClickType, key: Key):
193197
key.clicked_on = None
194-
actions = self._actions.get(key, key.actions)
198+
actions = list(self._actions.get(key, key.actions))
195199
for action in actions:
196200
try:
201+
print(f"Running {action.__class__.__name__}")
197202
action.execute(self, key, click)
198203
except Exception as e:
199204
print(f"Error running action {action}: {e}")
205+
traceback.print_exc()
200206

201207
def add(
202208
self,

src/sleuthdeck/keys.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import asyncio
44
from os import path
5+
from os.path import dirname
56
from typing import Callable
67
from typing import List
78
from typing import Optional
@@ -56,7 +57,7 @@ def load_image(
5657
# Load a custom TrueType font and use it to overlay the key index, draw key
5758
# label onto the image a few pixels from the bottom of the key.
5859
draw = ImageDraw.Draw(image)
59-
font = ImageFont.truetype("Roboto-Regular.ttf", 14)
60+
font = ImageFont.truetype(path.join(dirname(__file__), "assets", "Roboto-Regular.ttf"), 14)
6061
draw.text(
6162
(image.width / 2, image.height - 5),
6263
text=text,

0 commit comments

Comments
 (0)