pyloid 0.13.0__py3-none-any.whl → 0.14.0__py3-none-any.whl
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.
- pyloid/api.py +85 -2
- pyloid/browser_window.py +1190 -0
- pyloid/filewatcher.py +137 -9
- pyloid/js_api/event_api.py +25 -0
- pyloid/js_api/window_api.py +166 -0
- pyloid/monitor.py +602 -77
- pyloid/pyloid.py +661 -892
- pyloid/timer.py +222 -38
- pyloid/tray.py +30 -0
- pyloid/utils.py +55 -6
- {pyloid-0.13.0.dist-info → pyloid-0.14.0.dist-info}/METADATA +1 -1
- pyloid-0.14.0.dist-info/RECORD +17 -0
- pyloid-0.13.0.dist-info/RECORD +0 -14
- {pyloid-0.13.0.dist-info → pyloid-0.14.0.dist-info}/LICENSE +0 -0
- {pyloid-0.13.0.dist-info → pyloid-0.14.0.dist-info}/WHEEL +0 -0
pyloid/pyloid.py
CHANGED
@@ -2,40 +2,28 @@ import sys
|
|
2
2
|
import os
|
3
3
|
from PySide6.QtWidgets import (
|
4
4
|
QApplication,
|
5
|
-
QMainWindow,
|
6
5
|
QSystemTrayIcon,
|
7
6
|
QMenu,
|
8
7
|
)
|
9
|
-
from PySide6.QtWebEngineWidgets import QWebEngineView
|
10
|
-
from PySide6.QtWebChannel import QWebChannel
|
11
8
|
from PySide6.QtGui import (
|
12
9
|
QIcon,
|
13
|
-
QKeySequence,
|
14
|
-
QShortcut,
|
15
10
|
QClipboard,
|
16
11
|
QImage,
|
17
12
|
QAction,
|
18
|
-
QCursor,
|
19
13
|
)
|
20
|
-
from PySide6.QtCore import Qt, Signal,
|
14
|
+
from PySide6.QtCore import Qt, Signal, QObject, QTimer
|
21
15
|
from PySide6.QtNetwork import QLocalServer, QLocalSocket
|
22
|
-
from
|
23
|
-
from
|
24
|
-
import uuid
|
25
|
-
from typing import List, Optional, Dict, Callable, Union, Any
|
16
|
+
from .api import PyloidAPI
|
17
|
+
from typing import List, Optional, Dict, Callable, Union
|
26
18
|
from PySide6.QtCore import qInstallMessageHandler
|
27
19
|
import signal
|
28
20
|
from .utils import is_production
|
29
21
|
from .monitor import Monitor
|
30
|
-
import json
|
31
22
|
from .autostart import AutoStart
|
32
23
|
from .filewatcher import FileWatcher
|
33
24
|
import logging
|
34
|
-
from
|
35
|
-
|
36
|
-
QVBoxLayout,
|
37
|
-
)
|
38
|
-
from .custom.titlebar import CustomTitleBar
|
25
|
+
from .browser_window import BrowserWindow
|
26
|
+
from .tray import TrayEvent
|
39
27
|
|
40
28
|
# for linux debug
|
41
29
|
os.environ["QTWEBENGINE_DICTIONARIES_PATH"] = "/"
|
@@ -67,809 +55,6 @@ def custom_message_handler(mode, context, message):
|
|
67
55
|
|
68
56
|
qInstallMessageHandler(custom_message_handler)
|
69
57
|
|
70
|
-
|
71
|
-
class WindowAPI(PyloidAPI):
|
72
|
-
def __init__(self, window_id: str, app):
|
73
|
-
super().__init__()
|
74
|
-
self.window_id: str = window_id
|
75
|
-
self.app: Pyloid = app
|
76
|
-
|
77
|
-
@Bridge(result=str)
|
78
|
-
def getWindowId(self):
|
79
|
-
"""Returns the current window ID."""
|
80
|
-
return self.window_id
|
81
|
-
|
82
|
-
@Bridge(result=dict)
|
83
|
-
def getWindowProperties(self):
|
84
|
-
"""Returns the properties of the window."""
|
85
|
-
window = self.app.get_window_by_id(self.window_id)
|
86
|
-
window_properties = window.get_window_properties()
|
87
|
-
return window_properties
|
88
|
-
|
89
|
-
@Bridge()
|
90
|
-
def close(self):
|
91
|
-
"""Closes the window."""
|
92
|
-
window = self.app.get_window_by_id(self.window_id)
|
93
|
-
if window:
|
94
|
-
window.close()
|
95
|
-
|
96
|
-
@Bridge()
|
97
|
-
def hide(self):
|
98
|
-
"""Hides the window."""
|
99
|
-
window = self.app.get_window_by_id(self.window_id)
|
100
|
-
if window:
|
101
|
-
window.hide()
|
102
|
-
|
103
|
-
@Bridge()
|
104
|
-
def show(self):
|
105
|
-
"""Shows and focuses the window."""
|
106
|
-
window = self.app.get_window_by_id(self.window_id)
|
107
|
-
if window:
|
108
|
-
window.show()
|
109
|
-
|
110
|
-
@Bridge()
|
111
|
-
def toggleFullscreen(self):
|
112
|
-
"""Toggles fullscreen mode for the window."""
|
113
|
-
window = self.app.get_window_by_id(self.window_id)
|
114
|
-
if window:
|
115
|
-
window.toggle_fullscreen()
|
116
|
-
|
117
|
-
@Bridge()
|
118
|
-
def minimize(self):
|
119
|
-
"""Minimizes the window."""
|
120
|
-
window = self.app.get_window_by_id(self.window_id)
|
121
|
-
if window:
|
122
|
-
window.minimize()
|
123
|
-
|
124
|
-
@Bridge()
|
125
|
-
def maximize(self):
|
126
|
-
"""Maximizes the window."""
|
127
|
-
window = self.app.get_window_by_id(self.window_id)
|
128
|
-
if window:
|
129
|
-
window.maximize()
|
130
|
-
|
131
|
-
@Bridge()
|
132
|
-
def unmaximize(self):
|
133
|
-
"""Restores the window to its normal state."""
|
134
|
-
window = self.app.get_window_by_id(self.window_id)
|
135
|
-
if window:
|
136
|
-
window.unmaximize()
|
137
|
-
|
138
|
-
@Bridge(str)
|
139
|
-
def setTitle(self, title: str):
|
140
|
-
"""Sets the title of the window."""
|
141
|
-
window = self.app.get_window_by_id(self.window_id)
|
142
|
-
if window:
|
143
|
-
window.set_title(title)
|
144
|
-
|
145
|
-
@Bridge(int, int)
|
146
|
-
def setSize(self, width: int, height: int):
|
147
|
-
"""Sets the size of the window."""
|
148
|
-
window = self.app.get_window_by_id(self.window_id)
|
149
|
-
if window:
|
150
|
-
window.set_size(width, height)
|
151
|
-
|
152
|
-
@Bridge(int, int)
|
153
|
-
def setPosition(self, x: int, y: int):
|
154
|
-
"""Sets the position of the window."""
|
155
|
-
window = self.app.get_window_by_id(self.window_id)
|
156
|
-
if window:
|
157
|
-
window.set_position(x, y)
|
158
|
-
|
159
|
-
@Bridge(bool)
|
160
|
-
def setFrame(self, frame: bool):
|
161
|
-
"""Sets the frame of the window."""
|
162
|
-
window = self.app.get_window_by_id(self.window_id)
|
163
|
-
if window:
|
164
|
-
window.set_frame(frame)
|
165
|
-
|
166
|
-
@Bridge(bool)
|
167
|
-
def setContextMenu(self, context_menu: bool):
|
168
|
-
"""Sets the context menu of the window."""
|
169
|
-
window = self.app.get_window_by_id(self.window_id)
|
170
|
-
if window:
|
171
|
-
window.set_context_menu(context_menu)
|
172
|
-
|
173
|
-
@Bridge(bool)
|
174
|
-
def setDevTools(self, enable: bool):
|
175
|
-
"""Sets the developer tools of the window."""
|
176
|
-
window = self.app.get_window_by_id(self.window_id)
|
177
|
-
if window:
|
178
|
-
window.set_dev_tools(enable)
|
179
|
-
|
180
|
-
@Bridge(str, result=Optional[str])
|
181
|
-
def capture(self, save_path: str) -> Optional[str]:
|
182
|
-
"""Captures the current window."""
|
183
|
-
window = self.app.get_window_by_id(self.window_id)
|
184
|
-
if window:
|
185
|
-
return window.capture(save_path)
|
186
|
-
return None
|
187
|
-
|
188
|
-
@Bridge(result=bool)
|
189
|
-
def getFrame(self):
|
190
|
-
"""Returns whether the window has a frame."""
|
191
|
-
window = self.app.get_window_by_id(self.window_id)
|
192
|
-
return window.frame if window else False
|
193
|
-
|
194
|
-
@Bridge(result=bool)
|
195
|
-
def getContextMenu(self):
|
196
|
-
"""Returns whether the window has a context menu."""
|
197
|
-
window = self.app.get_window_by_id(self.window_id)
|
198
|
-
return window.context_menu if window else False
|
199
|
-
|
200
|
-
@Bridge(result=bool)
|
201
|
-
def getDevTools(self):
|
202
|
-
"""Returns whether the window has developer tools."""
|
203
|
-
window = self.app.get_window_by_id(self.window_id)
|
204
|
-
return window.dev_tools if window else False
|
205
|
-
|
206
|
-
@Bridge(result=str)
|
207
|
-
def getTitle(self):
|
208
|
-
"""Returns the title of the window."""
|
209
|
-
window = self.app.get_window_by_id(self.window_id)
|
210
|
-
return window.title if window else ""
|
211
|
-
|
212
|
-
@Bridge(result=dict)
|
213
|
-
def getSize(self):
|
214
|
-
"""Returns the size of the window."""
|
215
|
-
window = self.app.get_window_by_id(self.window_id)
|
216
|
-
return (
|
217
|
-
{"width": window.width, "height": window.height}
|
218
|
-
if window
|
219
|
-
else {"width": 0, "height": 0}
|
220
|
-
)
|
221
|
-
|
222
|
-
@Bridge(result=dict)
|
223
|
-
def getPosition(self):
|
224
|
-
"""Returns the position of the window."""
|
225
|
-
window = self.app.get_window_by_id(self.window_id)
|
226
|
-
return {"x": window.x, "y": window.y} if window else {"x": 0, "y": 0}
|
227
|
-
|
228
|
-
@Bridge()
|
229
|
-
def startSystemDrag(self):
|
230
|
-
"""Starts the system drag."""
|
231
|
-
window = self.app.get_window_by_id(self.window_id)
|
232
|
-
if window:
|
233
|
-
window.web_view.start_system_drag()
|
234
|
-
|
235
|
-
|
236
|
-
# class EventAPI(PylonAPI):
|
237
|
-
# def __init__(self, window_id: str, app):
|
238
|
-
# super().__init__()
|
239
|
-
# self.window_id: str = window_id
|
240
|
-
# self.app: PylonApp = app
|
241
|
-
# self.subscribers = {}
|
242
|
-
|
243
|
-
# @Bridge(str, Callable)
|
244
|
-
# def on(self, event_name: str, callback: Callable):
|
245
|
-
# """특정 이벤트를 구독합니다."""
|
246
|
-
# if event_name not in self.subscribers:
|
247
|
-
# self.subscribers[event_name] = []
|
248
|
-
# self.subscribers[event_name].append(callback)
|
249
|
-
|
250
|
-
# @Bridge(str, result=Optional[str])
|
251
|
-
# def emit(self, event_name: str, *args, **kwargs):
|
252
|
-
# """다른 윈도우로 특정 이벤트를 보냅니다."""
|
253
|
-
# if event_name in self.subscribers:
|
254
|
-
# for callback in self.subscribers[event_name]:
|
255
|
-
# callback(*args, **kwargs)
|
256
|
-
|
257
|
-
|
258
|
-
# 어차피 load 부분에만 쓰이니까 나중에 분리해서 load 위에서 선언하자.
|
259
|
-
class CustomWebEngineView(QWebEngineView):
|
260
|
-
def __init__(self, parent=None):
|
261
|
-
super().__init__(parent._window)
|
262
|
-
self.parent = parent
|
263
|
-
self.drag_relative_position = None
|
264
|
-
self.is_dragging = False
|
265
|
-
self.is_resizing = False
|
266
|
-
self.resize_start_pos = None
|
267
|
-
self.resize_direction = None
|
268
|
-
self.screen_geometry = self.screen().availableGeometry()
|
269
|
-
self.is_resizing_enabled = True
|
270
|
-
|
271
|
-
def mouse_press_event(self, event):
|
272
|
-
if event.button() == Qt.LeftButton:
|
273
|
-
self.drag_relative_position = event.pos()
|
274
|
-
if not self.parent.frame and self.is_resizing_enabled:
|
275
|
-
self.resize_direction = self.get_resize_direction(event.pos())
|
276
|
-
if self.resize_direction:
|
277
|
-
self.is_resizing = True
|
278
|
-
self.resize_start_pos = event.globalPos()
|
279
|
-
|
280
|
-
def start_system_drag(self):
|
281
|
-
self.is_dragging = True
|
282
|
-
|
283
|
-
def mouse_move_event(self, event):
|
284
|
-
if self.is_resizing and self.is_resizing_enabled:
|
285
|
-
self.resize_window(event.globalPos())
|
286
|
-
elif not self.parent.frame and self.is_dragging:
|
287
|
-
# 현재 마우스 위치를 전역 좌표로 가져옵니다
|
288
|
-
current_global_pos = event.globalPos()
|
289
|
-
|
290
|
-
# 화면 경계를 계산합니다
|
291
|
-
left_boundary = self.screen_geometry.left()
|
292
|
-
right_boundary = self.screen_geometry.right()
|
293
|
-
top_boundary = self.screen_geometry.top()
|
294
|
-
bottom_boundary = self.screen_geometry.bottom()
|
295
|
-
|
296
|
-
# 마우스 커서 위치를 제한합니다
|
297
|
-
new_cursor_pos = QPoint(
|
298
|
-
max(left_boundary, min(current_global_pos.x(), right_boundary)),
|
299
|
-
max(top_boundary, min(current_global_pos.y(), bottom_boundary)),
|
300
|
-
)
|
301
|
-
|
302
|
-
# 마우스 커서를 새 위치로 이동합니다
|
303
|
-
QCursor.setPos(new_cursor_pos)
|
304
|
-
|
305
|
-
# 창의 새 위치를 계산합니다
|
306
|
-
new_window_pos = new_cursor_pos - self.drag_relative_position
|
307
|
-
|
308
|
-
# 창을 새 위치로 이동합니다
|
309
|
-
self.parent._window.move(new_window_pos)
|
310
|
-
else:
|
311
|
-
# Change cursor based on resize direction
|
312
|
-
resize_direction = self.get_resize_direction(event.pos())
|
313
|
-
if resize_direction and self.is_resizing_enabled:
|
314
|
-
self.set_cursor_for_resize_direction(resize_direction)
|
315
|
-
else:
|
316
|
-
self.unsetCursor()
|
317
|
-
|
318
|
-
def mouse_release_event(self, event):
|
319
|
-
if event.button() == Qt.LeftButton:
|
320
|
-
self.is_dragging = False
|
321
|
-
self.is_resizing = False
|
322
|
-
self.resize_direction = None
|
323
|
-
self.unsetCursor()
|
324
|
-
|
325
|
-
def eventFilter(self, source, event):
|
326
|
-
if self.focusProxy() is source:
|
327
|
-
if event.type() == QEvent.MouseButtonPress:
|
328
|
-
self.mouse_press_event(event)
|
329
|
-
elif event.type() == QEvent.MouseMove:
|
330
|
-
self.mouse_move_event(event)
|
331
|
-
elif event.type() == QEvent.MouseButtonRelease:
|
332
|
-
self.mouse_release_event(event)
|
333
|
-
return super().eventFilter(source, event)
|
334
|
-
|
335
|
-
def get_resize_direction(self, pos):
|
336
|
-
if not self.parent.frame and self.is_resizing_enabled: # Check if frame is not present and resizing is enabled
|
337
|
-
margin = 5 # Margin in pixels to detect edge
|
338
|
-
rect = self.rect()
|
339
|
-
direction = None
|
340
|
-
|
341
|
-
if pos.x() <= margin:
|
342
|
-
direction = 'left'
|
343
|
-
elif pos.x() >= rect.width() - margin:
|
344
|
-
direction = 'right'
|
345
|
-
|
346
|
-
if pos.y() <= margin:
|
347
|
-
direction = 'top' if direction is None else direction + '-top'
|
348
|
-
elif pos.y() >= rect.height() - margin:
|
349
|
-
direction = 'bottom' if direction is None else direction + '-bottom'
|
350
|
-
|
351
|
-
return direction
|
352
|
-
return None
|
353
|
-
|
354
|
-
def set_cursor_for_resize_direction(self, direction):
|
355
|
-
if not self.parent.frame and direction and self.is_resizing_enabled: # Check if frame is not present and resizing is enabled
|
356
|
-
if direction in ['left', 'right']:
|
357
|
-
self.setCursor(Qt.SizeHorCursor)
|
358
|
-
elif direction in ['top', 'bottom']:
|
359
|
-
self.setCursor(Qt.SizeVerCursor)
|
360
|
-
elif direction in ['left-top', 'right-bottom']:
|
361
|
-
self.setCursor(Qt.SizeFDiagCursor)
|
362
|
-
elif direction in ['right-top', 'left-bottom']:
|
363
|
-
self.setCursor(Qt.SizeBDiagCursor)
|
364
|
-
|
365
|
-
def resize_window(self, global_pos):
|
366
|
-
if not self.parent.frame and self.resize_start_pos and self.resize_direction and self.is_resizing_enabled: # Check if frame is not present and resizing is enabled
|
367
|
-
delta = global_pos - self.resize_start_pos
|
368
|
-
new_geometry = self.parent._window.geometry()
|
369
|
-
|
370
|
-
if 'left' in self.resize_direction:
|
371
|
-
new_geometry.setLeft(new_geometry.left() + delta.x())
|
372
|
-
if 'right' in self.resize_direction:
|
373
|
-
new_geometry.setRight(new_geometry.right() + delta.x())
|
374
|
-
if 'top' in self.resize_direction:
|
375
|
-
new_geometry.setTop(new_geometry.top() + delta.y())
|
376
|
-
if 'bottom' in self.resize_direction:
|
377
|
-
new_geometry.setBottom(new_geometry.bottom() + delta.y())
|
378
|
-
|
379
|
-
self.parent._window.setGeometry(new_geometry)
|
380
|
-
self.resize_start_pos = global_pos
|
381
|
-
|
382
|
-
|
383
|
-
class BrowserWindow:
|
384
|
-
def __init__(
|
385
|
-
self,
|
386
|
-
app,
|
387
|
-
title: str = "pyloid app",
|
388
|
-
width: int = 800,
|
389
|
-
height: int = 600,
|
390
|
-
x: int = 200,
|
391
|
-
y: int = 200,
|
392
|
-
frame: bool = True,
|
393
|
-
context_menu: bool = False,
|
394
|
-
dev_tools: bool = False,
|
395
|
-
js_apis: List[PyloidAPI] = [],
|
396
|
-
):
|
397
|
-
###########################################################################################
|
398
|
-
self.id = str(uuid.uuid4()) # Generate unique ID
|
399
|
-
self._window = QMainWindow()
|
400
|
-
self.web_view = CustomWebEngineView(self)
|
401
|
-
|
402
|
-
self._window.closeEvent = self.closeEvent # Override closeEvent method
|
403
|
-
###########################################################################################
|
404
|
-
self.app = app
|
405
|
-
self.title = title
|
406
|
-
self.width = width
|
407
|
-
self.height = height
|
408
|
-
self.x = x
|
409
|
-
self.y = y
|
410
|
-
self.frame = frame
|
411
|
-
self.context_menu = context_menu
|
412
|
-
self.dev_tools = dev_tools
|
413
|
-
self.js_apis = [WindowAPI(self.id, self.app)]
|
414
|
-
for js_api in js_apis:
|
415
|
-
self.js_apis.append(js_api)
|
416
|
-
self.shortcuts = {}
|
417
|
-
###########################################################################################
|
418
|
-
|
419
|
-
def _set_custom_frame(
|
420
|
-
self,
|
421
|
-
use_custom: bool,
|
422
|
-
title: str = "Custom Title",
|
423
|
-
bg_color: str = "darkblue",
|
424
|
-
text_color: str = "white",
|
425
|
-
icon_path: str = None,
|
426
|
-
):
|
427
|
-
"""Sets or removes the custom frame."""
|
428
|
-
if use_custom:
|
429
|
-
self._window.setWindowFlags(Qt.FramelessWindowHint)
|
430
|
-
self.custom_title_bar = CustomTitleBar(self._window)
|
431
|
-
self.custom_title_bar.set_style(bg_color, text_color)
|
432
|
-
self.custom_title_bar.set_title(title)
|
433
|
-
|
434
|
-
if icon_path:
|
435
|
-
self.custom_title_bar.set_icon(icon_path)
|
436
|
-
|
437
|
-
layout = QVBoxLayout()
|
438
|
-
layout.setContentsMargins(0, 0, 0, 0)
|
439
|
-
layout.setSpacing(0)
|
440
|
-
layout.addWidget(self.custom_title_bar)
|
441
|
-
layout.addWidget(self.web_view)
|
442
|
-
|
443
|
-
central_widget = QWidget()
|
444
|
-
central_widget.setLayout(layout)
|
445
|
-
self._window.setCentralWidget(central_widget)
|
446
|
-
|
447
|
-
# Add properties for window movement
|
448
|
-
self._window.moving = False
|
449
|
-
self._window.offset = QPoint()
|
450
|
-
else:
|
451
|
-
self._window.setWindowFlags(Qt.Window)
|
452
|
-
self._window.setCentralWidget(self.web_view)
|
453
|
-
self.custom_title_bar = None
|
454
|
-
|
455
|
-
self._window.show()
|
456
|
-
|
457
|
-
def _load(self):
|
458
|
-
self.set_title(self.title)
|
459
|
-
|
460
|
-
self.set_size(self.width, self.height)
|
461
|
-
self.set_position(self.x, self.y)
|
462
|
-
|
463
|
-
# allow local file access to remote urls
|
464
|
-
self.web_view.settings().setAttribute(
|
465
|
-
QWebEngineSettings.LocalContentCanAccessRemoteUrls, True
|
466
|
-
)
|
467
|
-
|
468
|
-
# Set icon
|
469
|
-
if self.app.icon:
|
470
|
-
self._window.setWindowIcon(self.app.icon)
|
471
|
-
else:
|
472
|
-
print("Icon is not set.")
|
473
|
-
|
474
|
-
# Set Windows taskbar icon
|
475
|
-
if sys.platform == "win32":
|
476
|
-
import ctypes
|
477
|
-
|
478
|
-
myappid = "mycompany.myproduct.subproduct.version"
|
479
|
-
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
|
480
|
-
|
481
|
-
# Remove title bar and borders (if needed)
|
482
|
-
if not self.frame:
|
483
|
-
self._window.setWindowFlags(Qt.FramelessWindowHint)
|
484
|
-
|
485
|
-
# Disable default context menu
|
486
|
-
if not self.context_menu:
|
487
|
-
self.web_view.setContextMenuPolicy(Qt.NoContextMenu)
|
488
|
-
|
489
|
-
# Set up QWebChannel
|
490
|
-
self.channel = QWebChannel()
|
491
|
-
|
492
|
-
# Register additional JS APIs
|
493
|
-
if self.js_apis:
|
494
|
-
for js_api in self.js_apis:
|
495
|
-
self.channel.registerObject(js_api.__class__.__name__, js_api)
|
496
|
-
|
497
|
-
self.web_view.page().setWebChannel(self.channel)
|
498
|
-
|
499
|
-
# Connect pylonjs bridge
|
500
|
-
self.web_view.loadFinished.connect(self._on_load_finished)
|
501
|
-
|
502
|
-
# Add QWebEngineView to main window
|
503
|
-
self._window.setCentralWidget(self.web_view)
|
504
|
-
|
505
|
-
# Set F12 shortcut
|
506
|
-
self.set_dev_tools(self.dev_tools)
|
507
|
-
|
508
|
-
def _on_load_finished(self, ok):
|
509
|
-
"""Handles the event when the web page finishes loading."""
|
510
|
-
if ok and self.js_apis:
|
511
|
-
js_code = """
|
512
|
-
if (typeof QWebChannel !== 'undefined') {
|
513
|
-
new QWebChannel(qt.webChannelTransport, function (channel) {
|
514
|
-
window.pyloid = {
|
515
|
-
EventAPI: {
|
516
|
-
listen: function(eventName, callback) {
|
517
|
-
document.addEventListener(eventName, function(event) {
|
518
|
-
let eventData;
|
519
|
-
try {
|
520
|
-
eventData = JSON.parse(event.detail);
|
521
|
-
} catch (e) {
|
522
|
-
eventData = event.detail;
|
523
|
-
}
|
524
|
-
callback(eventData);
|
525
|
-
});
|
526
|
-
},
|
527
|
-
unlisten: function(eventName) {
|
528
|
-
document.removeEventListener(eventName);
|
529
|
-
}
|
530
|
-
}
|
531
|
-
};
|
532
|
-
console.log('pyloid.EventAPI object initialized:', window.pyloid.EventAPI);
|
533
|
-
|
534
|
-
%s
|
535
|
-
|
536
|
-
document.addEventListener('mousedown', function (e) {
|
537
|
-
if (e.target.hasAttribute('data-pyloid-drag-region')) {
|
538
|
-
window.pyloid.WindowAPI.startSystemDrag();
|
539
|
-
}
|
540
|
-
});
|
541
|
-
|
542
|
-
// Dispatch a custom event to signal that the initialization is ready
|
543
|
-
const event = new CustomEvent('pyloidReady');
|
544
|
-
document.dispatchEvent(event);
|
545
|
-
});
|
546
|
-
} else {
|
547
|
-
console.error('QWebChannel is not defined.');
|
548
|
-
}
|
549
|
-
"""
|
550
|
-
js_api_init = "\n".join(
|
551
|
-
[
|
552
|
-
f"window.pyloid['{js_api.__class__.__name__}'] = channel.objects['{js_api.__class__.__name__}'];\n"
|
553
|
-
f"console.log('pyloid.{js_api.__class__.__name__} object initialized:', window.pyloid['{js_api.__class__.__name__}']);"
|
554
|
-
for js_api in self.js_apis
|
555
|
-
]
|
556
|
-
)
|
557
|
-
self.web_view.page().runJavaScript(js_code % js_api_init)
|
558
|
-
else:
|
559
|
-
pass
|
560
|
-
|
561
|
-
###########################################################################################
|
562
|
-
# Load
|
563
|
-
###########################################################################################
|
564
|
-
def load_file(self, file_path):
|
565
|
-
"""Loads a local HTML file into the web view."""
|
566
|
-
self._load()
|
567
|
-
file_path = os.path.abspath(file_path) # absolute path
|
568
|
-
self.web_view.setUrl(QUrl.fromLocalFile(file_path))
|
569
|
-
self.web_view.focusProxy().installEventFilter(self.web_view)
|
570
|
-
|
571
|
-
def load_url(self, url):
|
572
|
-
"""Sets the URL of the window."""
|
573
|
-
self._load()
|
574
|
-
self.web_view.setUrl(QUrl(url))
|
575
|
-
self.web_view.focusProxy().installEventFilter(self.web_view)
|
576
|
-
|
577
|
-
###########################################################################################
|
578
|
-
# Set Parameters
|
579
|
-
###########################################################################################
|
580
|
-
def set_title(self, title: str):
|
581
|
-
"""Sets the title of the window."""
|
582
|
-
self.title = title
|
583
|
-
self._window.setWindowTitle(self.title)
|
584
|
-
|
585
|
-
def set_size(self, width: int, height: int):
|
586
|
-
"""Sets the size of the window."""
|
587
|
-
self.width = width
|
588
|
-
self.height = height
|
589
|
-
self._window.setGeometry(self.x, self.y, self.width, self.height)
|
590
|
-
|
591
|
-
def set_position(self, x: int, y: int):
|
592
|
-
"""Sets the position of the window."""
|
593
|
-
self.x = x
|
594
|
-
self.y = y
|
595
|
-
self._window.setGeometry(self.x, self.y, self.width, self.height)
|
596
|
-
|
597
|
-
def set_frame(self, frame: bool):
|
598
|
-
"""Sets the frame of the window."""
|
599
|
-
self.frame = frame
|
600
|
-
was_visible = self._window.isVisible()
|
601
|
-
if self.frame:
|
602
|
-
self._window.setWindowFlags(Qt.Window)
|
603
|
-
else:
|
604
|
-
self._window.setWindowFlags(Qt.FramelessWindowHint)
|
605
|
-
if was_visible:
|
606
|
-
self._window.show()
|
607
|
-
|
608
|
-
def set_context_menu(self, context_menu: bool):
|
609
|
-
"""Sets the context menu of the window."""
|
610
|
-
self.context_menu = context_menu
|
611
|
-
if self.context_menu:
|
612
|
-
self.web_view.setContextMenuPolicy(Qt.NoContextMenu)
|
613
|
-
else:
|
614
|
-
self.web_view.setContextMenuPolicy(Qt.DefaultContextMenu)
|
615
|
-
|
616
|
-
def set_dev_tools(self, enable: bool):
|
617
|
-
"""Sets the developer tools of the window.
|
618
|
-
|
619
|
-
If enabled, the developer tools can be opened using the F12 key.
|
620
|
-
"""
|
621
|
-
self.dev_tools = enable
|
622
|
-
if self.dev_tools:
|
623
|
-
self.add_shortcut("F12", self.open_dev_tools)
|
624
|
-
else:
|
625
|
-
self.remove_shortcut("F12")
|
626
|
-
|
627
|
-
def open_dev_tools(self):
|
628
|
-
"""Opens the developer tools window."""
|
629
|
-
self.web_view.page().setDevToolsPage(QWebEnginePage(self.web_view.page()))
|
630
|
-
self.dev_tools_window = QMainWindow(self._window)
|
631
|
-
dev_tools_view = QWebEngineView(self.dev_tools_window)
|
632
|
-
dev_tools_view.setPage(self.web_view.page().devToolsPage())
|
633
|
-
self.dev_tools_window.setCentralWidget(dev_tools_view)
|
634
|
-
self.dev_tools_window.resize(800, 600)
|
635
|
-
self.dev_tools_window.show()
|
636
|
-
|
637
|
-
# Add this line to handle dev tools window closure
|
638
|
-
self.dev_tools_window.closeEvent = lambda event: setattr(
|
639
|
-
self, "dev_tools_window", None
|
640
|
-
)
|
641
|
-
|
642
|
-
def closeEvent(self, event):
|
643
|
-
"""Handles the event when the window is closed."""
|
644
|
-
# Close developer tools if open
|
645
|
-
if hasattr(self, "dev_tools_window") and self.dev_tools_window:
|
646
|
-
self.dev_tools_window.close()
|
647
|
-
self.dev_tools_window = None
|
648
|
-
|
649
|
-
# Solve memory leak issue with web view engine
|
650
|
-
self.web_view.page().deleteLater()
|
651
|
-
self.web_view.deleteLater()
|
652
|
-
self._remove_from_app_windows()
|
653
|
-
event.accept() # Accept the event (allow the window to close)
|
654
|
-
|
655
|
-
def _remove_from_app_windows(self):
|
656
|
-
"""Removes the window from the app's window list."""
|
657
|
-
if self in self.app.windows:
|
658
|
-
self.app.windows.remove(self)
|
659
|
-
if not self.app.windows:
|
660
|
-
self.app.quit() # Quit the app if all windows are closed
|
661
|
-
|
662
|
-
###########################################################################################
|
663
|
-
# Window management (no ID required)
|
664
|
-
###########################################################################################
|
665
|
-
def hide(self):
|
666
|
-
"""Hides the window."""
|
667
|
-
self._window.hide()
|
668
|
-
|
669
|
-
def show(self):
|
670
|
-
"""Shows the window."""
|
671
|
-
self._window.show()
|
672
|
-
|
673
|
-
def focus(self):
|
674
|
-
"""Focuses the window."""
|
675
|
-
self._window.activateWindow()
|
676
|
-
self._window.raise_()
|
677
|
-
self._window.setWindowState(
|
678
|
-
self._window.windowState() & ~Qt.WindowMinimized | Qt.WindowActive
|
679
|
-
)
|
680
|
-
|
681
|
-
def show_and_focus(self):
|
682
|
-
"""Shows and focuses the window."""
|
683
|
-
self._window.show()
|
684
|
-
self._window.activateWindow()
|
685
|
-
self._window.raise_()
|
686
|
-
self._window.setWindowState(
|
687
|
-
self._window.windowState() & ~Qt.WindowMinimized | Qt.WindowActive
|
688
|
-
)
|
689
|
-
|
690
|
-
def close(self):
|
691
|
-
"""Closes the window."""
|
692
|
-
self._window.close()
|
693
|
-
|
694
|
-
def toggle_fullscreen(self):
|
695
|
-
"""Toggles fullscreen mode for the window."""
|
696
|
-
if self._window.isFullScreen():
|
697
|
-
self._window.showNormal()
|
698
|
-
else:
|
699
|
-
self._window.showFullScreen()
|
700
|
-
|
701
|
-
def minimize(self):
|
702
|
-
"""Minimizes the window."""
|
703
|
-
self._window.showMinimized()
|
704
|
-
|
705
|
-
def maximize(self):
|
706
|
-
"""Maximizes the window."""
|
707
|
-
self._window.showMaximized()
|
708
|
-
|
709
|
-
def unmaximize(self):
|
710
|
-
"""Unmaximizes the window."""
|
711
|
-
self._window.showNormal()
|
712
|
-
|
713
|
-
def capture(self, save_path: str) -> Optional[str]:
|
714
|
-
"""
|
715
|
-
Captures the current window.
|
716
|
-
|
717
|
-
:param save_path: Path to save the captured image. If not specified, it will be saved in the current directory.
|
718
|
-
:return: Path of the saved image
|
719
|
-
"""
|
720
|
-
try:
|
721
|
-
# Capture window
|
722
|
-
screenshot = self._window.grab()
|
723
|
-
|
724
|
-
# Save image
|
725
|
-
screenshot.save(save_path)
|
726
|
-
return save_path
|
727
|
-
except Exception as e:
|
728
|
-
print(f"Error occurred while capturing the window: {e}")
|
729
|
-
return None
|
730
|
-
|
731
|
-
###########################################################################################
|
732
|
-
# Shortcut
|
733
|
-
###########################################################################################
|
734
|
-
def add_shortcut(self, key_sequence: str, callback: Callable):
|
735
|
-
"""
|
736
|
-
Adds a keyboard shortcut to the window if it does not already exist.
|
737
|
-
|
738
|
-
:param key_sequence: Shortcut sequence (e.g., "Ctrl+C")
|
739
|
-
:param callback: Function to be executed when the shortcut is pressed
|
740
|
-
:return: Created QShortcut object or None if the shortcut already exists
|
741
|
-
"""
|
742
|
-
if key_sequence in self.shortcuts:
|
743
|
-
# print(f"Shortcut {key_sequence} already exists.")
|
744
|
-
return None
|
745
|
-
|
746
|
-
shortcut = QShortcut(QKeySequence(key_sequence), self._window)
|
747
|
-
shortcut.activated.connect(callback)
|
748
|
-
self.shortcuts[key_sequence] = shortcut
|
749
|
-
return shortcut
|
750
|
-
|
751
|
-
def remove_shortcut(self, key_sequence: str):
|
752
|
-
"""
|
753
|
-
Removes a keyboard shortcut from the window.
|
754
|
-
|
755
|
-
:param key_sequence: Shortcut sequence to be removed
|
756
|
-
"""
|
757
|
-
if key_sequence in self.shortcuts:
|
758
|
-
shortcut = self.shortcuts.pop(key_sequence)
|
759
|
-
shortcut.setEnabled(False)
|
760
|
-
shortcut.deleteLater()
|
761
|
-
|
762
|
-
def get_all_shortcuts(self):
|
763
|
-
"""
|
764
|
-
Returns all registered shortcuts in the window.
|
765
|
-
|
766
|
-
:return: Dictionary of shortcut sequences and QShortcut objects
|
767
|
-
"""
|
768
|
-
return self.shortcuts
|
769
|
-
|
770
|
-
###########################################################################################
|
771
|
-
# Event (Calling the JS from Python)
|
772
|
-
###########################################################################################
|
773
|
-
def emit(self, event_name, data: Optional[Dict] = None):
|
774
|
-
"""
|
775
|
-
Emits an event to the JavaScript side.
|
776
|
-
|
777
|
-
:param event_name: Name of the event
|
778
|
-
:param data: Data to be sent with the event (optional)
|
779
|
-
"""
|
780
|
-
script = f"""
|
781
|
-
(function() {{
|
782
|
-
const eventData = {json.dumps(data)};
|
783
|
-
const customEvent = new CustomEvent('{event_name}', {{ detail: eventData }});
|
784
|
-
document.dispatchEvent(customEvent);
|
785
|
-
}})();
|
786
|
-
"""
|
787
|
-
self.web_view.page().runJavaScript(script)
|
788
|
-
|
789
|
-
###########################################################################################
|
790
|
-
# Get Properties
|
791
|
-
###########################################################################################
|
792
|
-
def get_window_properties(self):
|
793
|
-
"""Returns the properties of the window."""
|
794
|
-
return {
|
795
|
-
"id": self.id,
|
796
|
-
"title": self.title,
|
797
|
-
"width": self.width,
|
798
|
-
"height": self.height,
|
799
|
-
"x": self.x,
|
800
|
-
"y": self.y,
|
801
|
-
"frame": self.frame,
|
802
|
-
"context_menu": self.context_menu,
|
803
|
-
"dev_tools": self.dev_tools,
|
804
|
-
}
|
805
|
-
|
806
|
-
def get_id(self):
|
807
|
-
"""Returns the ID of the window."""
|
808
|
-
return self.id
|
809
|
-
|
810
|
-
def get_size(self) -> Dict[str, int]:
|
811
|
-
"""Returns the size of the window."""
|
812
|
-
return {"width": self.width, "height": self.height}
|
813
|
-
|
814
|
-
def get_position(self) -> Dict[str, int]:
|
815
|
-
"""Returns the position of the window."""
|
816
|
-
return {"x": self.x, "y": self.y}
|
817
|
-
|
818
|
-
def get_title(self) -> str:
|
819
|
-
"""Returns the title of the window."""
|
820
|
-
return self.title
|
821
|
-
|
822
|
-
def get_url(self) -> str:
|
823
|
-
"""Returns the URL of the window."""
|
824
|
-
return self.web_view.url().toString()
|
825
|
-
|
826
|
-
def get_visible(self) -> bool:
|
827
|
-
"""Returns the visibility of the window."""
|
828
|
-
return self._window.isVisible()
|
829
|
-
|
830
|
-
def get_frame(self) -> bool:
|
831
|
-
"""Returns the frame enabled state of the window."""
|
832
|
-
return self.frame
|
833
|
-
|
834
|
-
###########################################################################################
|
835
|
-
# Resize
|
836
|
-
###########################################################################################
|
837
|
-
def set_resizable(self, resizable: bool):
|
838
|
-
"""Sets the resizability of the window."""
|
839
|
-
self.resizable = resizable
|
840
|
-
if self.frame:
|
841
|
-
flags = self._window.windowFlags() | Qt.WindowCloseButtonHint
|
842
|
-
if resizable:
|
843
|
-
pass
|
844
|
-
else:
|
845
|
-
flags |= Qt.MSWindowsFixedSizeDialogHint
|
846
|
-
self._window.setWindowFlags(flags)
|
847
|
-
else:
|
848
|
-
# 프레임이 없는 경우 커스텀 리사이징 로직을 설정합니다.
|
849
|
-
self.web_view.is_resizing_enabled = resizable
|
850
|
-
|
851
|
-
self._window.show() # 변경사항을 적용하기 위해 창을 다시 표시합니다.
|
852
|
-
|
853
|
-
def set_minimum_size(self, min_width: int, min_height: int):
|
854
|
-
"""Sets the minimum size of the window."""
|
855
|
-
self._window.setMinimumSize(min_width, min_height)
|
856
|
-
|
857
|
-
def set_maximum_size(self, max_width: int, max_height: int):
|
858
|
-
"""Sets the maximum size of the window."""
|
859
|
-
self._window.setMaximumSize(max_width, max_height)
|
860
|
-
|
861
|
-
def get_minimum_size(self):
|
862
|
-
"""Returns the minimum size of the window."""
|
863
|
-
return {'width': self._window.minimumWidth(), 'height': self._window.minimumHeight()}
|
864
|
-
|
865
|
-
def get_maximum_size(self):
|
866
|
-
"""Returns the maximum size of the window."""
|
867
|
-
return {'width': self._window.maximumWidth(), 'height': self._window.maximumHeight()}
|
868
|
-
|
869
|
-
def get_resizable(self):
|
870
|
-
"""Returns the resizability of the window."""
|
871
|
-
return self.resizable
|
872
|
-
|
873
58
|
class _WindowController(QObject):
|
874
59
|
create_window_signal = Signal(
|
875
60
|
QApplication, str, int, int, int, int, bool, bool, bool, list
|
@@ -882,6 +67,27 @@ class Pyloid(QApplication):
|
|
882
67
|
app_name,
|
883
68
|
single_instance=True,
|
884
69
|
):
|
70
|
+
"""
|
71
|
+
Initializes the Pyloid application.
|
72
|
+
|
73
|
+
Parameters
|
74
|
+
----------
|
75
|
+
app_name : str, required
|
76
|
+
The name of the application
|
77
|
+
single_instance : bool, optional
|
78
|
+
Whether to run the application as a single instance (default is True)
|
79
|
+
|
80
|
+
Examples
|
81
|
+
--------
|
82
|
+
```python
|
83
|
+
app = Pyloid(app_name="Pyloid-App")
|
84
|
+
|
85
|
+
window = app.create_window(title="New Window", width=1024, height=768)
|
86
|
+
window.show()
|
87
|
+
|
88
|
+
app.run()
|
89
|
+
```
|
90
|
+
"""
|
885
91
|
super().__init__(sys.argv)
|
886
92
|
|
887
93
|
self.windows = []
|
@@ -920,20 +126,28 @@ class Pyloid(QApplication):
|
|
920
126
|
"""
|
921
127
|
Dynamically sets the application's icon.
|
922
128
|
|
923
|
-
:param icon_path: Path to the new icon file
|
924
|
-
|
925
129
|
This method can be called while the application is running.
|
926
|
-
The icon can be changed at any time and
|
130
|
+
The icon can be changed at any time and will be applied immediately.
|
131
|
+
|
132
|
+
Parameters
|
133
|
+
----------
|
134
|
+
icon_path : str
|
135
|
+
Path to the new icon file
|
136
|
+
|
137
|
+
Examples
|
138
|
+
--------
|
139
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
140
|
+
>>> app.set_icon("icons/icon.png")
|
927
141
|
"""
|
928
142
|
self.icon = QIcon(icon_path)
|
929
143
|
|
930
|
-
# Immediately update the icon for all open windows
|
144
|
+
# Immediately update the icon for all open windows.
|
931
145
|
for window in self.windows:
|
932
146
|
window._window.setWindowIcon(self.icon)
|
933
147
|
|
934
148
|
def create_window(
|
935
149
|
self,
|
936
|
-
title: str
|
150
|
+
title: str,
|
937
151
|
width: int = 800,
|
938
152
|
height: int = 600,
|
939
153
|
x: int = 200,
|
@@ -943,7 +157,41 @@ class Pyloid(QApplication):
|
|
943
157
|
dev_tools: bool = False,
|
944
158
|
js_apis: List[PyloidAPI] = [],
|
945
159
|
) -> BrowserWindow:
|
946
|
-
"""
|
160
|
+
"""
|
161
|
+
Creates a new browser window.
|
162
|
+
|
163
|
+
Parameters
|
164
|
+
----------
|
165
|
+
title : str, required
|
166
|
+
Title of the window
|
167
|
+
width : int, optional
|
168
|
+
Width of the window (default is 800)
|
169
|
+
height : int, optional
|
170
|
+
Height of the window (default is 600)
|
171
|
+
x : int, optional
|
172
|
+
X coordinate of the window (default is 200)
|
173
|
+
y : int, optional
|
174
|
+
Y coordinate of the window (default is 200)
|
175
|
+
frame : bool, optional
|
176
|
+
Whether the window has a frame (default is True)
|
177
|
+
context_menu : bool, optional
|
178
|
+
Whether to use the context menu (default is False)
|
179
|
+
dev_tools : bool, optional
|
180
|
+
Whether to use developer tools (default is False)
|
181
|
+
js_apis : list of PyloidAPI, optional
|
182
|
+
List of JavaScript APIs to add to the window (default is an empty list)
|
183
|
+
|
184
|
+
Returns
|
185
|
+
-------
|
186
|
+
BrowserWindow
|
187
|
+
The created browser window object
|
188
|
+
|
189
|
+
Examples
|
190
|
+
--------
|
191
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
192
|
+
>>> window = app.create_window(title="New Window", width=1024, height=768)
|
193
|
+
>>> window.show()
|
194
|
+
"""
|
947
195
|
self.controller.create_window_signal.emit(
|
948
196
|
self,
|
949
197
|
title,
|
@@ -988,7 +236,20 @@ class Pyloid(QApplication):
|
|
988
236
|
return window
|
989
237
|
|
990
238
|
def run(self):
|
991
|
-
"""
|
239
|
+
"""
|
240
|
+
Runs the application event loop.
|
241
|
+
|
242
|
+
This method starts the application's event loop, allowing the application to run.
|
243
|
+
|
244
|
+
This code should be written at the very end of the file.
|
245
|
+
|
246
|
+
Examples
|
247
|
+
--------
|
248
|
+
```python
|
249
|
+
app = Pyloid(app_name="Pyloid-App")
|
250
|
+
app.run()
|
251
|
+
```
|
252
|
+
"""
|
992
253
|
if is_production():
|
993
254
|
sys.exit(self.exec())
|
994
255
|
else:
|
@@ -1016,17 +277,51 @@ class Pyloid(QApplication):
|
|
1016
277
|
# App window
|
1017
278
|
###########################################################################################
|
1018
279
|
def get_windows(self) -> List[BrowserWindow]:
|
1019
|
-
"""
|
280
|
+
"""
|
281
|
+
Returns a list of all browser windows.
|
282
|
+
|
283
|
+
Returns
|
284
|
+
-------
|
285
|
+
List[BrowserWindow]
|
286
|
+
List of all browser windows
|
287
|
+
|
288
|
+
Examples
|
289
|
+
--------
|
290
|
+
```python
|
291
|
+
app = Pyloid(app_name="Pyloid-App")
|
292
|
+
windows = app.get_windows()
|
293
|
+
for window in windows:
|
294
|
+
print(window.get_id())
|
295
|
+
```
|
296
|
+
"""
|
1020
297
|
return self.windows
|
1021
298
|
|
1022
299
|
def show_main_window(self):
|
1023
|
-
"""
|
300
|
+
"""
|
301
|
+
Shows and focuses the first window.
|
302
|
+
|
303
|
+
Examples
|
304
|
+
--------
|
305
|
+
```python
|
306
|
+
app = Pyloid(app_name="Pyloid-App")
|
307
|
+
app.show_main_window()
|
308
|
+
```
|
309
|
+
"""
|
1024
310
|
if self.windows:
|
1025
311
|
main_window = self.windows[0]
|
1026
312
|
main_window._window.show()
|
1027
313
|
|
1028
314
|
def focus_main_window(self):
|
1029
|
-
"""
|
315
|
+
"""
|
316
|
+
Focuses the first window.
|
317
|
+
|
318
|
+
Examples
|
319
|
+
--------
|
320
|
+
```python
|
321
|
+
app = Pyloid(app_name="Pyloid-App")
|
322
|
+
app.focus_main_window()
|
323
|
+
```
|
324
|
+
"""
|
1030
325
|
if self.windows:
|
1031
326
|
main_window = self.windows[0]
|
1032
327
|
main_window._window.activateWindow()
|
@@ -1037,7 +332,16 @@ class Pyloid(QApplication):
|
|
1037
332
|
)
|
1038
333
|
|
1039
334
|
def show_and_focus_main_window(self):
|
1040
|
-
"""
|
335
|
+
"""
|
336
|
+
Shows and focuses the first window.
|
337
|
+
|
338
|
+
Examples
|
339
|
+
--------
|
340
|
+
```python
|
341
|
+
app = Pyloid(app_name="Pyloid-App")
|
342
|
+
app.show_and_focus_main_window()
|
343
|
+
```
|
344
|
+
"""
|
1041
345
|
if self.windows:
|
1042
346
|
main_window = self.windows[0]
|
1043
347
|
main_window._window.show()
|
@@ -1049,12 +353,30 @@ class Pyloid(QApplication):
|
|
1049
353
|
)
|
1050
354
|
|
1051
355
|
def close_all_windows(self):
|
1052
|
-
"""
|
356
|
+
"""
|
357
|
+
Closes all windows.
|
358
|
+
|
359
|
+
Examples
|
360
|
+
--------
|
361
|
+
```python
|
362
|
+
app = Pyloid(app_name="Pyloid-App")
|
363
|
+
app.close_all_windows()
|
364
|
+
```
|
365
|
+
"""
|
1053
366
|
for window in self.windows:
|
1054
367
|
window._window.close()
|
1055
368
|
|
1056
369
|
def quit(self):
|
1057
|
-
"""
|
370
|
+
"""
|
371
|
+
Quits the application.
|
372
|
+
|
373
|
+
Examples
|
374
|
+
--------
|
375
|
+
```python
|
376
|
+
app = Pyloid(app_name="Pyloid-App")
|
377
|
+
app.quit()
|
378
|
+
```
|
379
|
+
"""
|
1058
380
|
for window in self.windows:
|
1059
381
|
window._window.close()
|
1060
382
|
window.web_page.deleteLater()
|
@@ -1065,20 +387,77 @@ class Pyloid(QApplication):
|
|
1065
387
|
# Window management in the app (ID required)
|
1066
388
|
###########################################################################################
|
1067
389
|
def get_window_by_id(self, window_id: str) -> Optional[BrowserWindow]:
|
1068
|
-
"""
|
390
|
+
"""
|
391
|
+
Returns the window with the given ID.
|
392
|
+
|
393
|
+
Parameters
|
394
|
+
----------
|
395
|
+
window_id : str
|
396
|
+
The ID of the window to find
|
397
|
+
|
398
|
+
Returns
|
399
|
+
-------
|
400
|
+
Optional[BrowserWindow]
|
401
|
+
The window object with the given ID. Returns None if the window is not found.
|
402
|
+
|
403
|
+
Examples
|
404
|
+
--------
|
405
|
+
```python
|
406
|
+
app = Pyloid(app_name="Pyloid-App")
|
407
|
+
|
408
|
+
window = app.get_window_by_id("123e4567-e89b-12d3-a456-426614174000")
|
409
|
+
|
410
|
+
if window:
|
411
|
+
print("Window found:", window)
|
412
|
+
```
|
413
|
+
"""
|
1069
414
|
for window in self.windows:
|
1070
415
|
if window.id == window_id:
|
1071
416
|
return window
|
1072
417
|
return None
|
1073
418
|
|
1074
419
|
def hide_window_by_id(self, window_id: str):
|
1075
|
-
"""
|
420
|
+
"""
|
421
|
+
Hides the window with the given ID.
|
422
|
+
|
423
|
+
Parameters
|
424
|
+
----------
|
425
|
+
window_id : str
|
426
|
+
The ID of the window to hide
|
427
|
+
|
428
|
+
Examples
|
429
|
+
--------
|
430
|
+
```python
|
431
|
+
app = Pyloid(app_name="Pyloid-App")
|
432
|
+
|
433
|
+
window = app.create_window(title="pyloid-window")
|
434
|
+
|
435
|
+
app.hide_window_by_id(window.id)
|
436
|
+
```
|
437
|
+
"""
|
1076
438
|
window = self.get_window_by_id(window_id)
|
1077
439
|
if window:
|
1078
440
|
window.hide()
|
1079
441
|
|
1080
442
|
def show_window_by_id(self, window_id: str):
|
1081
|
-
"""
|
443
|
+
"""
|
444
|
+
Shows and focuses the window with the given ID.
|
445
|
+
|
446
|
+
Parameters
|
447
|
+
----------
|
448
|
+
window_id : str
|
449
|
+
The ID of the window to show
|
450
|
+
|
451
|
+
Examples
|
452
|
+
--------
|
453
|
+
```python
|
454
|
+
app = Pyloid(app_name="Pyloid-App")
|
455
|
+
|
456
|
+
window = app.create_window(title="pyloid-window")
|
457
|
+
|
458
|
+
app.show_window_by_id(window.id)
|
459
|
+
```
|
460
|
+
"""
|
1082
461
|
window = self.get_window_by_id(window_id)
|
1083
462
|
if window:
|
1084
463
|
window._window.show()
|
@@ -1089,41 +468,147 @@ class Pyloid(QApplication):
|
|
1089
468
|
)
|
1090
469
|
|
1091
470
|
def close_window_by_id(self, window_id: str):
|
1092
|
-
"""
|
471
|
+
"""
|
472
|
+
Closes the window with the given ID.
|
473
|
+
|
474
|
+
Parameters
|
475
|
+
----------
|
476
|
+
window_id : str
|
477
|
+
The ID of the window to close
|
478
|
+
|
479
|
+
Examples
|
480
|
+
--------
|
481
|
+
```python
|
482
|
+
app = Pyloid(app_name="Pyloid-App")
|
483
|
+
|
484
|
+
window = app.create_window(title="pyloid-window")
|
485
|
+
|
486
|
+
app.close_window_by_id(window.id)
|
487
|
+
```
|
488
|
+
"""
|
1093
489
|
window = self.get_window_by_id(window_id)
|
1094
490
|
if window:
|
1095
491
|
window._window.close()
|
1096
492
|
|
1097
493
|
def toggle_fullscreen_by_id(self, window_id: str):
|
1098
|
-
"""
|
494
|
+
"""
|
495
|
+
Toggles fullscreen mode for the window with the given ID.
|
496
|
+
|
497
|
+
Parameters
|
498
|
+
----------
|
499
|
+
window_id : str
|
500
|
+
The ID of the window to toggle fullscreen mode
|
501
|
+
|
502
|
+
Examples
|
503
|
+
--------
|
504
|
+
```python
|
505
|
+
app = Pyloid(app_name="Pyloid-App")
|
506
|
+
|
507
|
+
window = app.create_window(title="pyloid-window")
|
508
|
+
|
509
|
+
app.toggle_fullscreen_by_id(window.id)
|
510
|
+
```
|
511
|
+
"""
|
1099
512
|
window = self.get_window_by_id(window_id)
|
1100
513
|
window.toggle_fullscreen()
|
1101
514
|
|
1102
515
|
def minimize_window_by_id(self, window_id: str):
|
1103
|
-
"""
|
516
|
+
"""
|
517
|
+
Minimizes the window with the given ID.
|
518
|
+
|
519
|
+
Parameters
|
520
|
+
----------
|
521
|
+
window_id : str
|
522
|
+
The ID of the window to minimize
|
523
|
+
|
524
|
+
Examples
|
525
|
+
--------
|
526
|
+
```python
|
527
|
+
app = Pyloid(app_name="Pyloid-App")
|
528
|
+
|
529
|
+
window = app.create_window(title="pyloid-window")
|
530
|
+
|
531
|
+
app.minimize_window_by_id(window.id)
|
532
|
+
```
|
533
|
+
"""
|
1104
534
|
window = self.get_window_by_id(window_id)
|
1105
535
|
if window:
|
1106
536
|
window.minimize()
|
1107
537
|
|
1108
538
|
def maximize_window_by_id(self, window_id: str):
|
1109
|
-
"""
|
539
|
+
"""
|
540
|
+
Maximizes the window with the given ID.
|
541
|
+
|
542
|
+
Parameters
|
543
|
+
----------
|
544
|
+
window_id : str
|
545
|
+
The ID of the window to maximize
|
546
|
+
|
547
|
+
Examples
|
548
|
+
--------
|
549
|
+
```python
|
550
|
+
app = Pyloid(app_name="Pyloid-App")
|
551
|
+
|
552
|
+
window = app.create_window(title="pyloid-window")
|
553
|
+
|
554
|
+
app.maximize_window_by_id(window.id)
|
555
|
+
```
|
556
|
+
"""
|
1110
557
|
window = self.get_window_by_id(window_id)
|
1111
558
|
if window:
|
1112
559
|
window.maximize()
|
1113
560
|
|
1114
561
|
def unmaximize_window_by_id(self, window_id: str):
|
1115
|
-
"""
|
562
|
+
"""
|
563
|
+
Unmaximizes the window with the given ID.
|
564
|
+
|
565
|
+
Parameters
|
566
|
+
----------
|
567
|
+
window_id : str
|
568
|
+
The ID of the window to unmaximize
|
569
|
+
|
570
|
+
Examples
|
571
|
+
--------
|
572
|
+
```python
|
573
|
+
app = Pyloid(app_name="Pyloid-App")
|
574
|
+
|
575
|
+
window = app.create_window(title="pyloid-window")
|
576
|
+
|
577
|
+
app.unmaximize_window_by_id(window.id)
|
578
|
+
```
|
579
|
+
"""
|
1116
580
|
window = self.get_window_by_id(window_id)
|
1117
581
|
if window:
|
1118
582
|
window.unmaximize()
|
1119
583
|
|
1120
584
|
def capture_window_by_id(self, window_id: str, save_path: str) -> Optional[str]:
|
1121
585
|
"""
|
1122
|
-
Captures
|
586
|
+
Captures the specified window.
|
1123
587
|
|
1124
|
-
|
1125
|
-
|
1126
|
-
:
|
588
|
+
Parameters
|
589
|
+
----------
|
590
|
+
window_id : str
|
591
|
+
The ID of the window to capture
|
592
|
+
save_path : str
|
593
|
+
The path to save the captured image. If not specified, it will be saved in the current directory.
|
594
|
+
|
595
|
+
Returns
|
596
|
+
-------
|
597
|
+
Optional[str]
|
598
|
+
The path of the saved image. Returns None if the window is not found or an error occurs.
|
599
|
+
|
600
|
+
Examples
|
601
|
+
--------
|
602
|
+
```python
|
603
|
+
app = Pyloid(app_name="Pyloid-App")
|
604
|
+
|
605
|
+
window = app.create_window(title="pyloid-window")
|
606
|
+
|
607
|
+
image_path = app.capture_window_by_id(window.id, "save/image.png")
|
608
|
+
|
609
|
+
if image_path:
|
610
|
+
print("Image saved at:", image_path)
|
611
|
+
```
|
1127
612
|
"""
|
1128
613
|
try:
|
1129
614
|
window = self.get_window_by_id(window_id)
|
@@ -1149,7 +634,15 @@ class Pyloid(QApplication):
|
|
1149
634
|
Dynamically sets the tray icon.
|
1150
635
|
Can be called while the application is running, and changes are applied immediately.
|
1151
636
|
|
1152
|
-
|
637
|
+
Parameters
|
638
|
+
----------
|
639
|
+
tray_icon_path : str
|
640
|
+
The path of the new tray icon file
|
641
|
+
|
642
|
+
Examples
|
643
|
+
--------
|
644
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
645
|
+
>>> app.set_tray_icon("icons/icon.png")
|
1153
646
|
"""
|
1154
647
|
# Stop and remove existing animation timer if present
|
1155
648
|
if hasattr(self, "animation_timer") and self.animation_timer is not None:
|
@@ -1176,7 +669,19 @@ class Pyloid(QApplication):
|
|
1176
669
|
Dynamically sets the tray menu items.
|
1177
670
|
Can be called while the application is running, and changes are applied immediately.
|
1178
671
|
|
1179
|
-
|
672
|
+
Parameters
|
673
|
+
----------
|
674
|
+
tray_menu_items : List[Dict[str, Union[str, Callable]]]
|
675
|
+
The list of new tray menu items
|
676
|
+
|
677
|
+
Examples
|
678
|
+
--------
|
679
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
680
|
+
>>> menu_items = [
|
681
|
+
>>> {"label": "Open", "callback": lambda: print("Open clicked")},
|
682
|
+
>>> {"label": "Exit", "callback": app.quit}
|
683
|
+
>>> ]
|
684
|
+
>>> app.set_tray_menu_items(menu_items)
|
1180
685
|
"""
|
1181
686
|
self.tray_menu_items = tray_menu_items
|
1182
687
|
if not hasattr(self, "tray"):
|
@@ -1212,12 +717,27 @@ class Pyloid(QApplication):
|
|
1212
717
|
if reason_enum in self.tray_actions:
|
1213
718
|
self.tray_actions[reason_enum]()
|
1214
719
|
|
1215
|
-
def set_tray_actions(self, actions):
|
720
|
+
def set_tray_actions(self, actions: Dict[TrayEvent, Callable]):
|
1216
721
|
"""
|
1217
|
-
Dynamically sets actions for tray icon activation.
|
722
|
+
Dynamically sets the actions for tray icon activation.
|
1218
723
|
Can be called while the application is running, and changes are applied immediately.
|
1219
724
|
|
1220
|
-
|
725
|
+
Parameters
|
726
|
+
----------
|
727
|
+
actions: Dict[TrayEvent, Callable]
|
728
|
+
Dictionary with TrayEvent enum values as keys and corresponding callback functions as values
|
729
|
+
|
730
|
+
Examples
|
731
|
+
--------
|
732
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
733
|
+
>>> app.set_tray_actions(
|
734
|
+
>>> {
|
735
|
+
>>> TrayEvent.DoubleClick: lambda: print("Tray icon was double-clicked."),
|
736
|
+
>>> TrayEvent.MiddleClick: lambda: print("Tray icon was middle-clicked."),
|
737
|
+
>>> TrayEvent.RightClick: lambda: print("Tray icon was right-clicked."),
|
738
|
+
>>> TrayEvent.LeftClick: lambda: print("Tray icon was left-clicked."),
|
739
|
+
>>> }
|
740
|
+
>>> )
|
1221
741
|
"""
|
1222
742
|
if self.tray_actions:
|
1223
743
|
self.tray.activated.disconnect() # Disconnect existing connections
|
@@ -1233,8 +753,17 @@ class Pyloid(QApplication):
|
|
1233
753
|
Displays a notification in the system tray.
|
1234
754
|
Can be called while the application is running, and the notification is displayed immediately.
|
1235
755
|
|
1236
|
-
|
1237
|
-
|
756
|
+
Parameters
|
757
|
+
----------
|
758
|
+
title : str
|
759
|
+
Notification title
|
760
|
+
message : str
|
761
|
+
Notification message
|
762
|
+
|
763
|
+
Examples
|
764
|
+
--------
|
765
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
766
|
+
>>> app.show_notification("Update Available", "A new update is available for download.")
|
1238
767
|
"""
|
1239
768
|
if not hasattr(self, "tray"):
|
1240
769
|
self._init_tray() # Ensure the tray is initialized
|
@@ -1242,18 +771,30 @@ class Pyloid(QApplication):
|
|
1242
771
|
self.tray.showMessage(title, message, QIcon(self.icon), 5000)
|
1243
772
|
|
1244
773
|
def _update_tray_icon(self):
|
1245
|
-
"""
|
774
|
+
"""
|
775
|
+
Updates the animation frame.
|
776
|
+
"""
|
1246
777
|
if hasattr(self, "tray") and self.icon_frames:
|
1247
778
|
self.tray.setIcon(self.icon_frames[self.current_frame])
|
1248
779
|
self.current_frame = (self.current_frame + 1) % len(self.icon_frames)
|
1249
780
|
|
1250
781
|
def set_tray_icon_animation(self, icon_frames: List[str], interval: int = 200):
|
1251
782
|
"""
|
1252
|
-
Dynamically sets and starts
|
783
|
+
Dynamically sets and starts the animation for the tray icon.
|
1253
784
|
Can be called while the application is running, and changes are applied immediately.
|
1254
785
|
|
1255
|
-
|
1256
|
-
|
786
|
+
Parameters
|
787
|
+
----------
|
788
|
+
icon_frames : list of str
|
789
|
+
List of animation frame image paths
|
790
|
+
interval : int, optional
|
791
|
+
Frame interval in milliseconds, default is 200
|
792
|
+
|
793
|
+
Examples
|
794
|
+
--------
|
795
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
796
|
+
>>> icon_frames = ["frame1.png", "frame2.png", "frame3.png"]
|
797
|
+
>>> app.set_tray_icon_animation(icon_frames, 100)
|
1257
798
|
"""
|
1258
799
|
if not hasattr(self, "tray"):
|
1259
800
|
self._init_tray()
|
@@ -1270,10 +811,12 @@ class Pyloid(QApplication):
|
|
1270
811
|
|
1271
812
|
self.icon_frames = [QIcon(frame) for frame in icon_frames]
|
1272
813
|
self.animation_interval = interval
|
1273
|
-
self.
|
814
|
+
self._start_tray_icon_animation()
|
1274
815
|
|
1275
|
-
def
|
1276
|
-
"""
|
816
|
+
def _start_tray_icon_animation(self):
|
817
|
+
"""
|
818
|
+
Starts the tray icon animation.
|
819
|
+
"""
|
1277
820
|
if self.icon_frames:
|
1278
821
|
if self.animation_timer is None:
|
1279
822
|
self.animation_timer = QTimer(self)
|
@@ -1286,7 +829,15 @@ class Pyloid(QApplication):
|
|
1286
829
|
Dynamically sets the tooltip for the tray icon.
|
1287
830
|
Can be called while the application is running, and changes are applied immediately.
|
1288
831
|
|
1289
|
-
|
832
|
+
Parameters
|
833
|
+
----------
|
834
|
+
message : str
|
835
|
+
New tooltip message
|
836
|
+
|
837
|
+
Examples
|
838
|
+
--------
|
839
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
840
|
+
>>> app.set_tray_tooltip("Pyloid is running")
|
1290
841
|
"""
|
1291
842
|
if not hasattr(self, "tray"):
|
1292
843
|
self._init_tray()
|
@@ -1296,7 +847,17 @@ class Pyloid(QApplication):
|
|
1296
847
|
"""
|
1297
848
|
Sets the callback function to be called when a notification is clicked.
|
1298
849
|
|
1299
|
-
|
850
|
+
Parameters
|
851
|
+
----------
|
852
|
+
callback : function
|
853
|
+
Callback function to be called when a notification is clicked
|
854
|
+
|
855
|
+
Examples
|
856
|
+
--------
|
857
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
858
|
+
>>> def on_notification_click():
|
859
|
+
>>> print("Notification clicked")
|
860
|
+
>>> app.set_notification_callback(on_notification_click)
|
1300
861
|
"""
|
1301
862
|
if not hasattr(self, "tray"):
|
1302
863
|
self._init_tray()
|
@@ -1307,9 +868,19 @@ class Pyloid(QApplication):
|
|
1307
868
|
###########################################################################################
|
1308
869
|
def get_all_monitors(self) -> List[Monitor]:
|
1309
870
|
"""
|
1310
|
-
Returns
|
871
|
+
Returns information about all connected monitors.
|
872
|
+
|
873
|
+
Returns
|
874
|
+
-------
|
875
|
+
list of Monitor
|
876
|
+
List containing monitor information
|
1311
877
|
|
1312
|
-
|
878
|
+
Examples
|
879
|
+
--------
|
880
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
881
|
+
>>> monitors = app.get_all_monitors()
|
882
|
+
>>> for monitor in monitors:
|
883
|
+
>>> print(monitor.info())
|
1313
884
|
"""
|
1314
885
|
monitors = [
|
1315
886
|
Monitor(index, screen) for index, screen in enumerate(self.screens())
|
@@ -1318,9 +889,18 @@ class Pyloid(QApplication):
|
|
1318
889
|
|
1319
890
|
def get_primary_monitor(self) -> Monitor:
|
1320
891
|
"""
|
1321
|
-
Returns information
|
892
|
+
Returns information about the primary monitor.
|
1322
893
|
|
1323
|
-
|
894
|
+
Returns
|
895
|
+
-------
|
896
|
+
Monitor
|
897
|
+
Primary monitor information
|
898
|
+
|
899
|
+
Examples
|
900
|
+
--------
|
901
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
902
|
+
>>> primary_monitor = app.get_primary_monitor()
|
903
|
+
>>> print(primary_monitor.info())
|
1324
904
|
"""
|
1325
905
|
primary_monitor = self.screens()[0]
|
1326
906
|
return Monitor(0, primary_monitor)
|
@@ -1328,11 +908,21 @@ class Pyloid(QApplication):
|
|
1328
908
|
###########################################################################################
|
1329
909
|
# Clipboard
|
1330
910
|
###########################################################################################
|
1331
|
-
def
|
911
|
+
def set_clipboard_text(self, text):
|
1332
912
|
"""
|
1333
913
|
Copies text to the clipboard.
|
1334
914
|
|
1335
|
-
|
915
|
+
This function copies the given text to the clipboard. The text copied to the clipboard can be pasted into other applications.
|
916
|
+
|
917
|
+
Parameters
|
918
|
+
----------
|
919
|
+
text : str
|
920
|
+
Text to copy to the clipboard
|
921
|
+
|
922
|
+
Examples
|
923
|
+
--------
|
924
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
925
|
+
>>> app.set_clipboard_text("Hello, World!")
|
1336
926
|
"""
|
1337
927
|
self.clipboard_class.setText(text, QClipboard.Clipboard)
|
1338
928
|
|
@@ -1340,7 +930,19 @@ class Pyloid(QApplication):
|
|
1340
930
|
"""
|
1341
931
|
Retrieves text from the clipboard.
|
1342
932
|
|
1343
|
-
|
933
|
+
This function returns the text stored in the clipboard. If there is no text in the clipboard, it may return an empty string.
|
934
|
+
|
935
|
+
Returns
|
936
|
+
-------
|
937
|
+
str
|
938
|
+
Text stored in the clipboard
|
939
|
+
|
940
|
+
Examples
|
941
|
+
--------
|
942
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
943
|
+
>>> text = app.get_clipboard_text()
|
944
|
+
>>> print(text)
|
945
|
+
Hello, World!
|
1344
946
|
"""
|
1345
947
|
return self.clipboard_class.text()
|
1346
948
|
|
@@ -1348,7 +950,17 @@ class Pyloid(QApplication):
|
|
1348
950
|
"""
|
1349
951
|
Copies an image to the clipboard.
|
1350
952
|
|
1351
|
-
|
953
|
+
This function copies the given image file to the clipboard. The image copied to the clipboard can be pasted into other applications.
|
954
|
+
|
955
|
+
Parameters
|
956
|
+
----------
|
957
|
+
image : Union[str, bytes, os.PathLike]
|
958
|
+
Path to the image file to copy to the clipboard
|
959
|
+
|
960
|
+
Examples
|
961
|
+
--------
|
962
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
963
|
+
>>> app.set_clipboard_image("/path/to/image.png")
|
1352
964
|
"""
|
1353
965
|
self.clipboard_class.setImage(QImage(image), QClipboard.Clipboard)
|
1354
966
|
|
@@ -1356,20 +968,46 @@ class Pyloid(QApplication):
|
|
1356
968
|
"""
|
1357
969
|
Retrieves an image from the clipboard.
|
1358
970
|
|
1359
|
-
|
971
|
+
This function returns the image stored in the clipboard. If there is no image in the clipboard, it may return None.
|
972
|
+
|
973
|
+
Returns
|
974
|
+
-------
|
975
|
+
QImage
|
976
|
+
QImage object stored in the clipboard (None if no image)
|
977
|
+
|
978
|
+
Examples
|
979
|
+
--------
|
980
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
981
|
+
>>> image = app.get_clipboard_image()
|
982
|
+
>>> if image is not None:
|
983
|
+
>>> image.save("/path/to/save/image.png")
|
1360
984
|
"""
|
1361
985
|
return self.clipboard_class.image()
|
1362
986
|
|
1363
987
|
###########################################################################################
|
1364
|
-
#
|
988
|
+
# Autostart
|
1365
989
|
###########################################################################################
|
1366
990
|
def set_auto_start(self, enable: bool):
|
1367
991
|
"""
|
1368
|
-
Sets the application to start automatically
|
1369
|
-
True only works in production.
|
1370
|
-
False works in
|
992
|
+
Sets the application to start automatically at system startup. (set_auto_start(True) only works in production environment)
|
993
|
+
True only works in production environment.
|
994
|
+
False works in all environments.
|
995
|
+
|
996
|
+
Parameters
|
997
|
+
----------
|
998
|
+
enable : bool
|
999
|
+
True to enable auto start, False to disable
|
1000
|
+
|
1001
|
+
Returns
|
1002
|
+
-------
|
1003
|
+
bool or None
|
1004
|
+
True if auto start is successfully set, False if disabled, None if trying to enable in non-production environment
|
1371
1005
|
|
1372
|
-
|
1006
|
+
Examples
|
1007
|
+
--------
|
1008
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
1009
|
+
>>> app.set_auto_start(True)
|
1010
|
+
True
|
1373
1011
|
"""
|
1374
1012
|
if not enable:
|
1375
1013
|
self.auto_start.set_auto_start(False)
|
@@ -1387,11 +1025,20 @@ class Pyloid(QApplication):
|
|
1387
1025
|
|
1388
1026
|
def is_auto_start(self):
|
1389
1027
|
"""
|
1390
|
-
Checks if the application is set to start automatically
|
1028
|
+
Checks if the application is set to start automatically at system startup.
|
1391
1029
|
|
1392
|
-
|
1393
|
-
|
1030
|
+
Returns
|
1031
|
+
-------
|
1032
|
+
bool
|
1033
|
+
True if auto start is enabled, False otherwise
|
1394
1034
|
|
1035
|
+
Examples
|
1036
|
+
--------
|
1037
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
1038
|
+
>>> auto_start_enabled = app.is_auto_start()
|
1039
|
+
>>> print(auto_start_enabled)
|
1040
|
+
True
|
1041
|
+
"""
|
1395
1042
|
return self.auto_start.is_auto_start()
|
1396
1043
|
|
1397
1044
|
###########################################################################################
|
@@ -1401,8 +1048,23 @@ class Pyloid(QApplication):
|
|
1401
1048
|
"""
|
1402
1049
|
Adds a file to the watch list.
|
1403
1050
|
|
1404
|
-
|
1405
|
-
|
1051
|
+
This function adds the specified file to the watch list. When the file is changed, the set callback function is called.
|
1052
|
+
|
1053
|
+
Parameters
|
1054
|
+
----------
|
1055
|
+
file_path : str
|
1056
|
+
Path to the file to watch
|
1057
|
+
|
1058
|
+
Returns
|
1059
|
+
-------
|
1060
|
+
bool
|
1061
|
+
True if the file is successfully added to the watch list, False otherwise
|
1062
|
+
|
1063
|
+
Examples
|
1064
|
+
--------
|
1065
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
1066
|
+
>>> app.watch_file("/path/to/file.txt")
|
1067
|
+
True
|
1406
1068
|
"""
|
1407
1069
|
return self.file_watcher.add_path(file_path)
|
1408
1070
|
|
@@ -1410,8 +1072,23 @@ class Pyloid(QApplication):
|
|
1410
1072
|
"""
|
1411
1073
|
Adds a directory to the watch list.
|
1412
1074
|
|
1413
|
-
|
1414
|
-
|
1075
|
+
This function adds the specified directory to the watch list. When a file in the directory is changed, the set callback function is called.
|
1076
|
+
|
1077
|
+
Parameters
|
1078
|
+
----------
|
1079
|
+
dir_path : str
|
1080
|
+
Path to the directory to watch
|
1081
|
+
|
1082
|
+
Returns
|
1083
|
+
-------
|
1084
|
+
bool
|
1085
|
+
True if the directory is successfully added to the watch list, False otherwise
|
1086
|
+
|
1087
|
+
Examples
|
1088
|
+
--------
|
1089
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
1090
|
+
>>> app.watch_directory("/path/to/directory")
|
1091
|
+
True
|
1415
1092
|
"""
|
1416
1093
|
return self.file_watcher.add_path(dir_path)
|
1417
1094
|
|
@@ -1419,8 +1096,23 @@ class Pyloid(QApplication):
|
|
1419
1096
|
"""
|
1420
1097
|
Removes a file or directory from the watch list.
|
1421
1098
|
|
1422
|
-
|
1423
|
-
|
1099
|
+
This function removes the specified file or directory from the watch list.
|
1100
|
+
|
1101
|
+
Parameters
|
1102
|
+
----------
|
1103
|
+
path : str
|
1104
|
+
Path to the file or directory to stop watching
|
1105
|
+
|
1106
|
+
Returns
|
1107
|
+
-------
|
1108
|
+
bool
|
1109
|
+
True if the path is successfully removed from the watch list, False otherwise
|
1110
|
+
|
1111
|
+
Examples
|
1112
|
+
--------
|
1113
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
1114
|
+
>>> app.stop_watching("/path/to/file_or_directory")
|
1115
|
+
True
|
1424
1116
|
"""
|
1425
1117
|
return self.file_watcher.remove_path(path)
|
1426
1118
|
|
@@ -1428,7 +1120,18 @@ class Pyloid(QApplication):
|
|
1428
1120
|
"""
|
1429
1121
|
Returns all currently watched paths.
|
1430
1122
|
|
1431
|
-
|
1123
|
+
This function returns the paths of all files and directories currently being watched.
|
1124
|
+
|
1125
|
+
Returns
|
1126
|
+
-------
|
1127
|
+
List[str]
|
1128
|
+
List of all watched paths
|
1129
|
+
|
1130
|
+
Examples
|
1131
|
+
--------
|
1132
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
1133
|
+
>>> app.get_watched_paths()
|
1134
|
+
['/path/to/file1.txt', '/path/to/directory']
|
1432
1135
|
"""
|
1433
1136
|
return self.file_watcher.get_watched_paths()
|
1434
1137
|
|
@@ -1436,7 +1139,18 @@ class Pyloid(QApplication):
|
|
1436
1139
|
"""
|
1437
1140
|
Returns all currently watched files.
|
1438
1141
|
|
1439
|
-
|
1142
|
+
This function returns the paths of all files currently being watched.
|
1143
|
+
|
1144
|
+
Returns
|
1145
|
+
-------
|
1146
|
+
List[str]
|
1147
|
+
List of all watched files
|
1148
|
+
|
1149
|
+
Examples
|
1150
|
+
--------
|
1151
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
1152
|
+
>>> app.get_watched_files()
|
1153
|
+
['/path/to/file1.txt', '/path/to/file2.txt']
|
1440
1154
|
"""
|
1441
1155
|
return self.file_watcher.get_watched_files()
|
1442
1156
|
|
@@ -1444,7 +1158,18 @@ class Pyloid(QApplication):
|
|
1444
1158
|
"""
|
1445
1159
|
Returns all currently watched directories.
|
1446
1160
|
|
1447
|
-
|
1161
|
+
This function returns the paths of all directories currently being watched.
|
1162
|
+
|
1163
|
+
Returns
|
1164
|
+
-------
|
1165
|
+
List[str]
|
1166
|
+
List of all watched directories
|
1167
|
+
|
1168
|
+
Examples
|
1169
|
+
--------
|
1170
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
1171
|
+
>>> app.get_watched_directories()
|
1172
|
+
['/path/to/directory1', '/path/to/directory2']
|
1448
1173
|
"""
|
1449
1174
|
return self.file_watcher.get_watched_directories()
|
1450
1175
|
|
@@ -1452,22 +1177,66 @@ class Pyloid(QApplication):
|
|
1452
1177
|
"""
|
1453
1178
|
Removes all paths from the watch list.
|
1454
1179
|
|
1455
|
-
|
1180
|
+
This function removes the paths of all files and directories from the watch list.
|
1181
|
+
|
1182
|
+
Returns
|
1183
|
+
-------
|
1184
|
+
None
|
1185
|
+
|
1186
|
+
Examples
|
1187
|
+
--------
|
1188
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
1189
|
+
>>> app.remove_all_watched_paths()
|
1456
1190
|
"""
|
1457
1191
|
self.file_watcher.remove_all_paths()
|
1458
1192
|
|
1459
1193
|
def set_file_change_callback(self, callback: Callable[[str], None]) -> None:
|
1460
1194
|
"""
|
1461
|
-
Sets the callback function to be called when a file
|
1195
|
+
Sets the callback function to be called when a file is changed.
|
1196
|
+
|
1197
|
+
This function sets the callback function to be called when a file is changed.
|
1198
|
+
|
1199
|
+
Parameters
|
1200
|
+
----------
|
1201
|
+
callback : Callable[[str], None]
|
1202
|
+
Function to be called when a file is changed
|
1203
|
+
|
1204
|
+
Returns
|
1205
|
+
-------
|
1206
|
+
None
|
1462
1207
|
|
1463
|
-
|
1208
|
+
Examples
|
1209
|
+
--------
|
1210
|
+
>>> def on_file_change(file_path):
|
1211
|
+
>>> print(f"File changed: {file_path}")
|
1212
|
+
>>>
|
1213
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
1214
|
+
>>> app.set_file_change_callback(on_file_change)
|
1464
1215
|
"""
|
1465
1216
|
self.file_watcher.file_changed.connect(callback)
|
1466
1217
|
|
1467
1218
|
def set_directory_change_callback(self, callback: Callable[[str], None]) -> None:
|
1468
1219
|
"""
|
1469
|
-
Sets the callback function to be called when a directory
|
1220
|
+
Sets the callback function to be called when a directory is changed.
|
1470
1221
|
|
1471
|
-
|
1222
|
+
This function sets the callback function to be called when a directory is changed.
|
1223
|
+
|
1224
|
+
Parameters
|
1225
|
+
----------
|
1226
|
+
callback : Callable[[str], None]
|
1227
|
+
Function to be called when a directory is changed
|
1228
|
+
|
1229
|
+
Returns
|
1230
|
+
-------
|
1231
|
+
None
|
1232
|
+
|
1233
|
+
Examples
|
1234
|
+
--------
|
1235
|
+
>>> def on_directory_change(dir_path):
|
1236
|
+
>>> print(f"Directory changed: {dir_path}")
|
1237
|
+
>>>
|
1238
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
1239
|
+
>>> app.set_directory_change_callback(on_directory_change)
|
1472
1240
|
"""
|
1473
1241
|
self.file_watcher.directory_changed.connect(callback)
|
1242
|
+
|