GameSentenceMiner 2.7.8__tar.gz → 2.7.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/ffmpeg.py +2 -2
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/gsm.py +1 -1
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/obs.py +1 -1
- gamesentenceminer-2.7.10/GameSentenceMiner/ocr/gsm_ocr_config.py +26 -0
- gamesentenceminer-2.7.10/GameSentenceMiner/ocr/owocr_area_selector.py +294 -0
- gamesentenceminer-2.7.10/GameSentenceMiner/ocr/owocr_helper.py +355 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/owocr/owocr/run.py +12 -5
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py +16 -8
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/utility_gui.py +53 -59
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner.egg-info/PKG-INFO +1 -1
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner.egg-info/SOURCES.txt +1 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/PKG-INFO +1 -1
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/pyproject.toml +1 -1
- gamesentenceminer-2.7.8/GameSentenceMiner/ocr/owocr_area_selector.py +0 -275
- gamesentenceminer-2.7.8/GameSentenceMiner/ocr/owocr_helper.py +0 -310
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/__init__.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/ai/__init__.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/ai/gemini.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/anki.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/communication/__init__.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/communication/send.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/communication/websocket.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/config_gui.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/configuration.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/downloader/Untitled_json.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/downloader/__init__.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/downloader/download_tools.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/electron_config.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/gametext.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/model.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/notification.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/ocr/__init__.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/ocr/ocrconfig.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/owocr/owocr/__init__.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/owocr/owocr/__main__.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/owocr/owocr/config.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/owocr/owocr/lens_betterproto.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/owocr/owocr/ocr.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/package.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/util.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/vad/__init__.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/vad/silero_trim.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/vad/vosk_helper.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner/vad/whisper_helper.py +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner.egg-info/dependency_links.txt +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner.egg-info/entry_points.txt +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner.egg-info/requires.txt +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/GameSentenceMiner.egg-info/top_level.txt +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/LICENSE +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/README.md +0 -0
- {gamesentenceminer-2.7.8 → gamesentenceminer-2.7.10}/setup.cfg +0 -0
@@ -52,7 +52,7 @@ def get_screenshot_for_line(video_file, game_line):
|
|
52
52
|
return get_screenshot(video_file, get_screenshot_time(video_file, game_line))
|
53
53
|
|
54
54
|
|
55
|
-
def get_screenshot_time(video_path, game_line, default_beginning=False, vad_beginning=None, vad_end=None):
|
55
|
+
def get_screenshot_time(video_path, game_line, default_beginning=False, vad_beginning=None, vad_end=None, doing_multi_line=False):
|
56
56
|
if game_line:
|
57
57
|
line_time = game_line.time
|
58
58
|
else:
|
@@ -68,7 +68,7 @@ def get_screenshot_time(video_path, game_line, default_beginning=False, vad_begi
|
|
68
68
|
screenshot_offset = get_config().screenshot.seconds_after_line
|
69
69
|
|
70
70
|
# Calculate screenshot time from the beginning by adding the offset
|
71
|
-
if vad_beginning and vad_end:
|
71
|
+
if vad_beginning and vad_end and not doing_multi_line:
|
72
72
|
logger.debug("Using VAD to determine screenshot time")
|
73
73
|
screenshot_time_from_beginning = line_timestamp_in_video + vad_end - screenshot_offset
|
74
74
|
elif get_config().screenshot.use_new_screenshot_logic:
|
@@ -141,7 +141,7 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
141
141
|
|
142
142
|
ss_timing = 1
|
143
143
|
if mined_line and line_cutoff or mined_line and get_config().screenshot.use_beginning_of_line_as_screenshot:
|
144
|
-
ss_timing = ffmpeg.get_screenshot_time(video_path, mined_line, vad_beginning, vad_end)
|
144
|
+
ss_timing = ffmpeg.get_screenshot_time(video_path, mined_line, vad_beginning, vad_end, bool(selected_lines))
|
145
145
|
|
146
146
|
if get_config().anki.update_anki and last_note:
|
147
147
|
anki.update_anki_card(last_note, note, audio_path=final_audio_output, video_path=video_path,
|
@@ -60,7 +60,7 @@ def get_obs_websocket_config_values():
|
|
60
60
|
|
61
61
|
# Extract values
|
62
62
|
server_enabled = config.get("server_enabled", False)
|
63
|
-
server_port = config.get("server_port",
|
63
|
+
server_port = config.get("server_port", 7274) # Default to 4455 if not set
|
64
64
|
server_password = config.get("server_password", None)
|
65
65
|
|
66
66
|
if not server_enabled:
|
@@ -0,0 +1,26 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
from dataclasses_json import dataclass_json
|
3
|
+
from typing import List, Optional
|
4
|
+
|
5
|
+
@dataclass_json
|
6
|
+
@dataclass
|
7
|
+
class Monitor:
|
8
|
+
left: int
|
9
|
+
top: int
|
10
|
+
width: int
|
11
|
+
height: int
|
12
|
+
index: int
|
13
|
+
|
14
|
+
@dataclass_json
|
15
|
+
@dataclass
|
16
|
+
class Rectangle:
|
17
|
+
monitor: Monitor
|
18
|
+
coordinates: List[int]
|
19
|
+
is_excluded: bool
|
20
|
+
|
21
|
+
@dataclass_json
|
22
|
+
@dataclass
|
23
|
+
class OCRConfig:
|
24
|
+
scene: str
|
25
|
+
rectangles: List[Rectangle]
|
26
|
+
window: Optional[str] = None
|
@@ -0,0 +1,294 @@
|
|
1
|
+
import sys
|
2
|
+
from multiprocessing import Process, Manager
|
3
|
+
import mss
|
4
|
+
from GameSentenceMiner import obs
|
5
|
+
|
6
|
+
from GameSentenceMiner.util import sanitize_filename
|
7
|
+
from PIL import Image, ImageTk
|
8
|
+
import tkinter as tk
|
9
|
+
import json
|
10
|
+
from pathlib import Path
|
11
|
+
import re
|
12
|
+
|
13
|
+
try:
|
14
|
+
import tkinter as tk
|
15
|
+
selector_available = True
|
16
|
+
except:
|
17
|
+
selector_available = False
|
18
|
+
|
19
|
+
MIN_RECT_WIDTH = 25 # Minimum width in pixels
|
20
|
+
MIN_RECT_HEIGHT = 25 # Minimum height in pixels
|
21
|
+
|
22
|
+
class ScreenSelector:
|
23
|
+
def __init__(self, result, window_name):
|
24
|
+
obs.connect_to_obs()
|
25
|
+
self.window_name = window_name
|
26
|
+
print(window_name)
|
27
|
+
self.sct = mss.mss()
|
28
|
+
self.monitors = self.sct.monitors[1:]
|
29
|
+
self.root = None
|
30
|
+
self.result = result
|
31
|
+
self.rectangles = [] # List to store (monitor, coordinates, is_excluded) tuples
|
32
|
+
self.drawn_rect_ids = [] # List to store canvas rectangle IDs
|
33
|
+
self.current_rect_id = None
|
34
|
+
self.start_x = None
|
35
|
+
self.start_y = None
|
36
|
+
self.canvas_windows = {} # Dictionary to store canvas per monitor
|
37
|
+
self.load_existing_rectangles()
|
38
|
+
self.image_mode = True
|
39
|
+
self.monitor_windows = {}
|
40
|
+
self.redo_stack = []
|
41
|
+
|
42
|
+
def get_scene_ocr_config(self):
|
43
|
+
"""Return the path to the OCR config file in GameSentenceMiner/ocr_config."""
|
44
|
+
app_dir = Path.home() / "AppData" / "Roaming" / "GameSentenceMiner"
|
45
|
+
ocr_config_dir = app_dir / "ocr_config"
|
46
|
+
ocr_config_dir.mkdir(parents=True, exist_ok=True)
|
47
|
+
scene = sanitize_filename(obs.get_current_scene())
|
48
|
+
config_path = ocr_config_dir / f"{scene}.json"
|
49
|
+
return config_path
|
50
|
+
|
51
|
+
def load_existing_rectangles(self):
|
52
|
+
config_path = self.get_scene_ocr_config()
|
53
|
+
try:
|
54
|
+
with open(config_path, 'r') as f:
|
55
|
+
config_data = json.load(f)
|
56
|
+
if "rectangles" in config_data:
|
57
|
+
self.rectangles = []
|
58
|
+
for rect_data in config_data["rectangles"]:
|
59
|
+
monitor_data = rect_data.get("monitor")
|
60
|
+
coords = rect_data.get("coordinates")
|
61
|
+
is_excluded = rect_data.get("is_excluded", False)
|
62
|
+
if monitor_data and isinstance(coords, list) and len(coords) == 4:
|
63
|
+
x, y, w, h = coords
|
64
|
+
if w >= MIN_RECT_WIDTH and h >= MIN_RECT_HEIGHT:
|
65
|
+
self.rectangles.append((monitor_data, tuple(coords), is_excluded))
|
66
|
+
else:
|
67
|
+
print(f"Skipping small rectangle from config: {coords}")
|
68
|
+
print(f"Loaded existing rectangles from {config_path}")
|
69
|
+
except FileNotFoundError:
|
70
|
+
print(f"No existing config found at {config_path}")
|
71
|
+
except json.JSONDecodeError:
|
72
|
+
print(f"Error decoding JSON from {config_path}")
|
73
|
+
except Exception as e:
|
74
|
+
print(f"An error occurred while loading rectangles: {e}")
|
75
|
+
|
76
|
+
def save_rects(self, event=None):
|
77
|
+
try:
|
78
|
+
print("Saving rectangles...")
|
79
|
+
config_path = self.get_scene_ocr_config()
|
80
|
+
print(config_path)
|
81
|
+
serializable_rects = []
|
82
|
+
for monitor, coords, is_excluded in self.rectangles:
|
83
|
+
rect_data = {
|
84
|
+
"monitor": monitor,
|
85
|
+
"coordinates": list(coords), # Convert tuple to list for JSON
|
86
|
+
"is_excluded": is_excluded
|
87
|
+
}
|
88
|
+
serializable_rects.append(rect_data)
|
89
|
+
|
90
|
+
print(serializable_rects)
|
91
|
+
with open(config_path, 'w', encoding="utf-8") as f:
|
92
|
+
json.dump({"scene": sanitize_filename(obs.get_current_scene()), "window": self.window_name, "rectangles": serializable_rects}, f, indent=4)
|
93
|
+
print("Rectangles saved.")
|
94
|
+
self.result['rectangles'] = self.rectangles.copy()
|
95
|
+
self.root.destroy()
|
96
|
+
except Exception as e:
|
97
|
+
print(f"Failed to save rectangles: {e}")
|
98
|
+
|
99
|
+
def undo_last_rect(self, event=None):
|
100
|
+
if self.rectangles and self.drawn_rect_ids:
|
101
|
+
last_rect = self.rectangles.pop()
|
102
|
+
last_rect_id = self.drawn_rect_ids.pop()
|
103
|
+
|
104
|
+
monitor, coords, is_excluded = last_rect
|
105
|
+
self.redo_stack.append((monitor, coords, is_excluded, last_rect_id))
|
106
|
+
|
107
|
+
for canvas in self.canvas_windows.values():
|
108
|
+
try:
|
109
|
+
canvas.delete(last_rect_id)
|
110
|
+
except tk.TclError:
|
111
|
+
pass
|
112
|
+
elif self.current_rect_id is not None:
|
113
|
+
for canvas in self.canvas_windows.values():
|
114
|
+
try:
|
115
|
+
canvas.delete(self.current_rect_id)
|
116
|
+
except tk.TclError:
|
117
|
+
pass
|
118
|
+
self.current_rect_id = None
|
119
|
+
self.start_x = None
|
120
|
+
self.start_y = None
|
121
|
+
|
122
|
+
|
123
|
+
def redo_last_rect(self, event=None):
|
124
|
+
if not self.redo_stack:
|
125
|
+
return
|
126
|
+
|
127
|
+
monitor, coords, is_excluded, rect_id = self.redo_stack.pop()
|
128
|
+
canvas = self.canvas_windows.get(monitor['index'])
|
129
|
+
if canvas:
|
130
|
+
x, y, w, h = coords
|
131
|
+
outline_color = 'green' if not is_excluded else 'orange'
|
132
|
+
new_rect_id = canvas.create_rectangle(x, y, x + w, y + h, outline=outline_color, width=2)
|
133
|
+
self.rectangles.append((monitor, coords, is_excluded))
|
134
|
+
self.drawn_rect_ids.append(new_rect_id)
|
135
|
+
|
136
|
+
|
137
|
+
def create_window(self, monitor):
|
138
|
+
screenshot = self.sct.grab(monitor)
|
139
|
+
img = Image.frombytes('RGB', screenshot.size, screenshot.rgb)
|
140
|
+
|
141
|
+
if img.width != monitor['width']:
|
142
|
+
img = img.resize((monitor['width'], monitor['height']), Image.Resampling.LANCZOS)
|
143
|
+
|
144
|
+
AscendingScale = 1.0 # For semi-transparent background
|
145
|
+
window = tk.Toplevel(self.root)
|
146
|
+
window.geometry(f"{monitor['width']}x{monitor['height']}+{monitor['left']}+{monitor['top']}")
|
147
|
+
window.overrideredirect(1)
|
148
|
+
window.attributes('-topmost', 1)
|
149
|
+
|
150
|
+
img_tk = ImageTk.PhotoImage(img)
|
151
|
+
|
152
|
+
canvas = tk.Canvas(window, cursor='cross', highlightthickness=0)
|
153
|
+
canvas.pack(fill=tk.BOTH, expand=True)
|
154
|
+
canvas.image = img_tk
|
155
|
+
canvas.bg_img_id = canvas.create_image(0, 0, image=img_tk, anchor=tk.NW) # Store image ID
|
156
|
+
self.canvas_windows[monitor['index']] = canvas
|
157
|
+
|
158
|
+
# Save monitor and window references for refreshing
|
159
|
+
self.monitor_windows[monitor['index']] = {
|
160
|
+
'window': window,
|
161
|
+
'canvas': canvas,
|
162
|
+
'bg_img_id': canvas.bg_img_id,
|
163
|
+
}
|
164
|
+
|
165
|
+
# Draw existing rectangles for this monitor
|
166
|
+
for mon, coords, is_excluded in self.rectangles:
|
167
|
+
if mon['index'] == monitor['index']:
|
168
|
+
x, y, w, h = coords
|
169
|
+
outline_color = 'green' if not is_excluded else 'orange'
|
170
|
+
rect_id = canvas.create_rectangle(x, y, x + w, y + h, outline=outline_color, width=2)
|
171
|
+
self.drawn_rect_ids.append(rect_id)
|
172
|
+
|
173
|
+
def on_click(event):
|
174
|
+
if self.current_rect_id is None:
|
175
|
+
self.start_x, self.start_y = event.x, event.y
|
176
|
+
outline_color = 'red' if not event.state & 0x0001 else 'purple' # Shift for exclusion
|
177
|
+
self.current_rect_id = canvas.create_rectangle(
|
178
|
+
self.start_x, self.start_y, self.start_x, self.start_y,
|
179
|
+
outline=outline_color, width=2
|
180
|
+
)
|
181
|
+
|
182
|
+
def on_drag(event):
|
183
|
+
if self.current_rect_id:
|
184
|
+
canvas.coords(self.current_rect_id, self.start_x, self.start_y, event.x, event.y)
|
185
|
+
|
186
|
+
def on_release(event):
|
187
|
+
if self.current_rect_id:
|
188
|
+
end_x, end_y = event.x, event.y
|
189
|
+
x1 = min(self.start_x, end_x)
|
190
|
+
y1 = min(self.start_y, end_y)
|
191
|
+
x2 = max(self.start_x, end_x)
|
192
|
+
y2 = max(self.start_y, end_y)
|
193
|
+
width = abs(x2 - x1)
|
194
|
+
height = abs(y2 - y1)
|
195
|
+
if width >= MIN_RECT_WIDTH and height >= MIN_RECT_HEIGHT:
|
196
|
+
is_excluded = bool(event.state & 0x0001) # Shift key for exclusion
|
197
|
+
canvas.itemconfig(self.current_rect_id, outline='green' if not is_excluded else 'orange')
|
198
|
+
self.rectangles.append((monitor, (x1, y1, width, height), is_excluded))
|
199
|
+
self.drawn_rect_ids.append(self.current_rect_id)
|
200
|
+
else:
|
201
|
+
canvas.delete(self.current_rect_id)
|
202
|
+
print(f"Skipping small rectangle: width={width}, height={height}")
|
203
|
+
self.current_rect_id = None
|
204
|
+
self.start_x = None
|
205
|
+
self.start_y = None
|
206
|
+
|
207
|
+
def on_right_click(event):
|
208
|
+
item = canvas.find_closest(event.x, event.y)
|
209
|
+
if item:
|
210
|
+
for idx, rect_id in enumerate(self.drawn_rect_ids):
|
211
|
+
if rect_id == item[0]:
|
212
|
+
canvas.delete(rect_id)
|
213
|
+
# Need to find the corresponding rectangle in self.rectangles and remove it
|
214
|
+
for i, (mon, coords, excluded) in enumerate(self.rectangles):
|
215
|
+
if mon['index'] == monitor['index']:
|
216
|
+
x_r, y_r, w_r, h_r = coords
|
217
|
+
item_coords = canvas.coords(item[0])
|
218
|
+
if x_r == item_coords[0] and y_r == item_coords[1] and x_r + w_r == item_coords[2] and y_r + h_r == item_coords[3]:
|
219
|
+
del self.rectangles[i]
|
220
|
+
break
|
221
|
+
del self.drawn_rect_ids[idx]
|
222
|
+
break
|
223
|
+
|
224
|
+
def toggle_image_mode(event=None):
|
225
|
+
self.image_mode = not self.image_mode
|
226
|
+
if self.image_mode:
|
227
|
+
window.attributes("-alpha", 1.0)
|
228
|
+
else:
|
229
|
+
window.attributes("-alpha", 0.20)
|
230
|
+
|
231
|
+
|
232
|
+
canvas.bind('<ButtonPress-1>', on_click)
|
233
|
+
canvas.bind('<B1-Motion>', on_drag)
|
234
|
+
canvas.bind('<ButtonRelease-1>', on_release)
|
235
|
+
canvas.bind('<Button-3>', on_right_click)
|
236
|
+
window.bind('<Control-s>', self.save_rects)
|
237
|
+
window.bind('<Control-z>', self.undo_last_rect)
|
238
|
+
window.bind('<s>', self.save_rects)
|
239
|
+
window.bind('<z>', self.undo_last_rect)
|
240
|
+
window.bind("<Escape>", self.quit_app)
|
241
|
+
window.bind("<m>", toggle_image_mode)
|
242
|
+
window.bind('<Control-y>', self.redo_last_rect)
|
243
|
+
window.bind('<y>', self.redo_last_rect)
|
244
|
+
|
245
|
+
def start(self):
|
246
|
+
self.root = tk.Tk()
|
247
|
+
self.root.withdraw()
|
248
|
+
|
249
|
+
for monitor in self.monitors:
|
250
|
+
monitor['index'] = self.monitors.index(monitor)
|
251
|
+
self.create_window(monitor)
|
252
|
+
|
253
|
+
self.root.mainloop()
|
254
|
+
|
255
|
+
def quit_app(self, event=None):
|
256
|
+
print("Escape pressed, closing application.")
|
257
|
+
self.on_close()
|
258
|
+
|
259
|
+
def on_close(self):
|
260
|
+
self.root.destroy()
|
261
|
+
|
262
|
+
|
263
|
+
def run_screen_selector(result, window_name):
|
264
|
+
selector = ScreenSelector(result, window_name)
|
265
|
+
selector.start()
|
266
|
+
|
267
|
+
|
268
|
+
def get_screen_selection(window_name):
|
269
|
+
if not selector_available:
|
270
|
+
raise ValueError('tkinter is not installed, unable to open picker')
|
271
|
+
|
272
|
+
with Manager() as manager:
|
273
|
+
res = manager.dict()
|
274
|
+
process = Process(target=run_screen_selector, args=(res,window_name))
|
275
|
+
|
276
|
+
process.start()
|
277
|
+
process.join()
|
278
|
+
|
279
|
+
if 'rectangles' in res:
|
280
|
+
return res.copy()
|
281
|
+
else:
|
282
|
+
return False
|
283
|
+
|
284
|
+
|
285
|
+
if __name__ == "__main__":
|
286
|
+
args = sys.argv[1:]
|
287
|
+
window_name = args[0] if args else None
|
288
|
+
selection = get_screen_selection(window_name)
|
289
|
+
if selection:
|
290
|
+
print("Selected rectangles:")
|
291
|
+
for monitor, coords, is_excluded in selection['rectangles']:
|
292
|
+
print(f"Monitor: {monitor}, Coordinates: {coords}, Excluded: {is_excluded}")
|
293
|
+
else:
|
294
|
+
print("No selection made or process was interrupted.")
|