-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcode_active.py
More file actions
233 lines (207 loc) · 8.05 KB
/
Copy pathcode_active.py
File metadata and controls
233 lines (207 loc) · 8.05 KB
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# requirements:
# pip install pygame pyttsx3
import os
import subprocess
# os.environ.setdefault("SDL_AUDIODRIVER", "dummy") # keep pygame off the audio device
import sys
import threading
import queue
import time
import pygame
import pyttsx3
from typing import Optional
# -----------------------------
# Config (tweak as you like)
# -----------------------------
WINDOWED_SIZE = (800, 600)
DOT_RADIUS = 30
DOT_THICKNESS = 0
BG_COLOR = (0, 0, 0)
DOT_COLOR = (255, 255, 255)
TARGET_FPS = 120
FULLSCREEN = False
MONITOR_INDEX = 0
TTS_RATE = 170
TTS_VOLUME = 1.0
INPUT_HOTKEY = pygame.K_1 # '1' enters typing mode
EXIT_TYPING_KEY = pygame.K_2 # '2' exits typing mode (cancel)
QUIT_KEY = pygame.K_q # 'q' quits the app
FONT_SIZE = 28
OVERLAY_BG = (0, 0, 0)
OVERLAY_FG = (255, 255, 255)
OVERLAY_ALPHA = 200
PADDING = 16
TYPING_TEXT_COLOR = (20, 20, 20) # <-- NEW: almost black
# -----------------------------
def create_display(fullscreen: bool, monitor_index: int):
pygame.display.init()
if fullscreen:
flags = pygame.FULLSCREEN
try:
return pygame.display.set_mode((0, 0), flags, display=monitor_index)
except TypeError:
sizes = pygame.display.get_desktop_sizes()
if monitor_index < 0 or monitor_index >= len(sizes):
monitor_index = 0
x_offset = sum(w for (w, h) in sizes[:monitor_index])
os.environ["SDL_VIDEO_WINDOW_POS"] = f"{x_offset},0"
pygame.display.quit(); pygame.display.init()
return pygame.display.set_mode(sizes[monitor_index], pygame.NOFRAME)
else:
return pygame.display.set_mode(WINDOWED_SIZE)
def humanize_key_name(name: str) -> str:
"""Make pygame key names sound nicer when spoken."""
mapping = {
"left": "Make sure to put the right sides together",
"right": "The feet need to face inward",
"g": "The feet should be placed between the fabric",
"down": "Make sure not to add pins all the way",
"return": "Do not cut the thread too short",
"backspace": "Rest assured, I am here to help you",
"tab": "Are you okay?",
"space": "Do you need help?",
"s": "Should I call a human for assistance?",
"w": "Try to sew in a straight line",
"e": "The washer for the eye should be on the inside",
"r": "Hello! I am your smart sewing machine.",
"t": "What are we making today?",
"y": "This is my working area.",
"u": "Goodbye!",
"i": "That is right.",
"o": "Uh-oh.",
"p": "I believe in you!",
"a": "Yes.",
"z": "No.",
"x": "You seem to have a sheet with instructions.",
"caps lock": "Make sure to push the filling inward before pinning the final edge",
"left shift": "You are forgetting a step",
"right shift": "You are doing it correctly",
}
# Function keys like f1..f24 already okay, keep as-is
return mapping.get(name, name)
def tts_worker(q: "queue.Queue[str]"):
try:
while True:
text = q.get()
if text is None:
break
try:
# macOS 'say' fallback; replace with pyttsx3 if preferred
subprocess.run(['say', '-r', str(TTS_RATE), text], check=True)
except Exception as e:
print(f"TTS error: {e}")
except Exception:
pass
def draw_input_overlay(screen: pygame.Surface, font: pygame.font.Font, buffer: str, prompt: Optional[str] = None):
w, h = screen.get_size()
panel_w = min(int(w * 0.9), 1000)
panel_h = 130
panel_x = (w - panel_w) // 2
panel_y = h - panel_h - 40
overlay = pygame.Surface((panel_w, panel_h), pygame.SRCALPHA)
overlay.fill((*OVERLAY_BG, OVERLAY_ALPHA))
lines = []
if prompt:
lines.append(prompt)
lines.append(buffer if buffer else "")
y = PADDING
for line in lines:
surf = font.render(line, True, TYPING_TEXT_COLOR)
overlay.blit(surf, (PADDING, y))
y += surf.get_height() + 8
screen.blit(overlay, (panel_x, panel_y))
def main():
screen = create_display(FULLSCREEN, MONITOR_INDEX)
pygame.font.init()
font = pygame.font.Font(None, FONT_SIZE)
pygame.mouse.set_visible(False)
# Optional: ensure key repeat is off (it is by default, but harmless)
try:
pygame.key.set_repeat(0)
except Exception:
pass
tts_queue: "queue.Queue[str]" = queue.Queue()
tts_thread = threading.Thread(target=tts_worker, args=(tts_queue,), daemon=True)
tts_thread.start()
clock = pygame.time.Clock()
running = True
input_mode = False
input_buffer = ""
last_spoken: Optional[str] = None # remembers last thing that was spoken
def start_text_input():
try:
pygame.key.start_text_input()
except Exception:
pass
def stop_text_input():
try:
pygame.key.stop_text_input()
except Exception:
pass
try:
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
# Global quit with 'q'
if event.key == QUIT_KEY:
running = False
break
if input_mode:
# CONTROL KEYS ONLY while typing
if event.key in (EXIT_TYPING_KEY, pygame.K_ESCAPE):
input_buffer = ""
input_mode = False
stop_text_input()
elif event.key == pygame.K_RETURN:
text = input_buffer.strip()
if text:
print(f"TTS (typed): {text}")
tts_queue.put(text)
last_spoken = text # remember typed message
input_buffer = ""
input_mode = False
stop_text_input()
elif event.key == pygame.K_BACKSPACE:
input_buffer = input_buffer[:-1]
# IMPORTANT: do NOT append event.unicode here.
else:
# Up arrow repeats the last spoken phrase
if event.key == pygame.K_UP:
if last_spoken:
print(f"Repeat: {last_spoken}")
tts_queue.put(last_spoken)
# do not fall through; Up only repeats
# Enter typing mode with '1'
elif event.key == INPUT_HOTKEY:
input_mode = True
input_buffer = ""
start_text_input()
else:
key_name = pygame.key.name(event.key)
readable = humanize_key_name(key_name)
print(f"User pressed {readable}")
tts_queue.put(readable)
last_spoken = readable # remember mapped speech
# Use TEXTINPUT exclusively for characters
elif event.type == pygame.TEXTINPUT and input_mode:
if event.text:
input_buffer += event.text
screen.fill(BG_COLOR)
mx, my = pygame.mouse.get_pos()
pygame.draw.circle(screen, DOT_COLOR, (mx, my), DOT_RADIUS, DOT_THICKNESS)
if input_mode:
draw_input_overlay(
screen, font, input_buffer,
prompt="Typing mode (started with '1'). Enter: speak · '2' or Esc: exit typing · 'q': quit app"
)
pygame.display.flip()
clock.tick(TARGET_FPS)
finally:
tts_queue.put(None)
tts_thread.join(timeout=2.0)
pygame.quit()
sys.exit()
if __name__ == "__main__":
main()