pyloid 0.16.12__py3-none-any.whl → 0.17.1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- pyloid/browser_window.py +79 -6
- pyloid/js_api/window_api.py +9 -0
- pyloid/pyloid.py +10 -1
- pyloid/thread_pool.py +501 -0
- pyloid/utils.py +24 -4
- {pyloid-0.16.12.dist-info → pyloid-0.17.1.dist-info}/METADATA +2 -3
- pyloid-0.17.1.dist-info/RECORD +18 -0
- pyloid-0.16.12.dist-info/RECORD +0 -17
- {pyloid-0.16.12.dist-info → pyloid-0.17.1.dist-info}/LICENSE +0 -0
- {pyloid-0.16.12.dist-info → pyloid-0.17.1.dist-info}/WHEEL +0 -0
pyloid/browser_window.py
CHANGED
@@ -36,26 +36,36 @@ if TYPE_CHECKING:
|
|
36
36
|
class CustomWebPage(QWebEnginePage):
|
37
37
|
def __init__(self, profile=None):
|
38
38
|
super().__init__(profile)
|
39
|
-
# 권한 요청 시그널 연결
|
40
39
|
self.featurePermissionRequested.connect(self._handlePermissionRequest)
|
40
|
+
self.desktopMediaRequested.connect(self._handleDesktopMediaRequest)
|
41
41
|
self._permission_handlers = {}
|
42
|
+
self._desktop_media_handler = None
|
42
43
|
|
43
44
|
def _handlePermissionRequest(self, origin: QUrl, feature: QWebEnginePage.Feature):
|
44
|
-
|
45
|
+
print(origin, feature)
|
46
|
+
|
47
|
+
"""Default permission request handler"""
|
45
48
|
if feature in self._permission_handlers:
|
46
|
-
#
|
49
|
+
# Execute if a handler is registered
|
47
50
|
handler = self._permission_handlers[feature]
|
48
51
|
handler(origin, feature)
|
49
52
|
else:
|
50
|
-
#
|
53
|
+
# Allow all permissions by default
|
51
54
|
self.setFeaturePermission(
|
52
55
|
origin, feature, QWebEnginePage.PermissionPolicy.PermissionGrantedByUser
|
53
56
|
)
|
54
57
|
|
55
58
|
def setPermissionHandler(self, feature: QWebEnginePage.Feature, handler):
|
56
|
-
"""
|
59
|
+
"""Register a handler for a specific permission"""
|
57
60
|
self._permission_handlers[feature] = handler
|
58
61
|
|
62
|
+
def _handleDesktopMediaRequest(self, *args, **kwargs):
|
63
|
+
print("Desktop media request received:", args, kwargs)
|
64
|
+
|
65
|
+
# def setDesktopMediaHandler(self, handler):
|
66
|
+
# """desktop media handler"""
|
67
|
+
# self._desktop_media_handler = handler
|
68
|
+
|
59
69
|
|
60
70
|
class CustomWebEngineView(QWebEngineView):
|
61
71
|
def __init__(self, parent: "BrowserWindow" = None):
|
@@ -291,6 +301,21 @@ class BrowserWindow:
|
|
291
301
|
self.web_view.settings().setUnknownUrlSchemePolicy(
|
292
302
|
QWebEngineSettings.UnknownUrlSchemePolicy.AllowAllUnknownUrlSchemes
|
293
303
|
)
|
304
|
+
self.web_view.settings().setAttribute(
|
305
|
+
QWebEngineSettings.WebAttribute.AllowRunningInsecureContent, True
|
306
|
+
)
|
307
|
+
self.web_view.settings().setAttribute(
|
308
|
+
QWebEngineSettings.WebAttribute.AllowGeolocationOnInsecureOrigins, True
|
309
|
+
)
|
310
|
+
self.web_view.settings().setAttribute(
|
311
|
+
QWebEngineSettings.WebAttribute.AllowWindowActivationFromJavaScript, True
|
312
|
+
)
|
313
|
+
self.web_view.settings().setAttribute(
|
314
|
+
QWebEngineSettings.WebAttribute.JavascriptCanPaste, True
|
315
|
+
)
|
316
|
+
self.web_view.settings().setAttribute(
|
317
|
+
QWebEngineSettings.WebAttribute.WebRTCPublicInterfacesOnly, False
|
318
|
+
)
|
294
319
|
|
295
320
|
# Set icon
|
296
321
|
if self.app.icon:
|
@@ -347,7 +372,17 @@ class BrowserWindow:
|
|
347
372
|
new QWebChannel(qt.webChannelTransport, function (channel) {
|
348
373
|
window.pyloid = {
|
349
374
|
EventAPI: {
|
375
|
+
_listeners: {}, // 콜백 함수들을 저장할 객체
|
376
|
+
|
350
377
|
listen: function(eventName, callback) {
|
378
|
+
// 이벤트에 대한 콜백 배열이 없다면 생성
|
379
|
+
if (!this._listeners[eventName]) {
|
380
|
+
this._listeners[eventName] = [];
|
381
|
+
}
|
382
|
+
|
383
|
+
// 콜백 함수 저장
|
384
|
+
this._listeners[eventName].push(callback);
|
385
|
+
|
351
386
|
document.addEventListener(eventName, function(event) {
|
352
387
|
let eventData;
|
353
388
|
try {
|
@@ -358,8 +393,16 @@ class BrowserWindow:
|
|
358
393
|
callback(eventData);
|
359
394
|
});
|
360
395
|
},
|
396
|
+
|
361
397
|
unlisten: function(eventName) {
|
362
|
-
|
398
|
+
// 해당 이벤트의 모든 리스너 제거
|
399
|
+
if (this._listeners[eventName]) {
|
400
|
+
this._listeners[eventName].forEach(callback => {
|
401
|
+
document.removeEventListener(eventName, callback);
|
402
|
+
});
|
403
|
+
// 저장된 콜백 제거
|
404
|
+
delete this._listeners[eventName];
|
405
|
+
}
|
363
406
|
}
|
364
407
|
}
|
365
408
|
};
|
@@ -1805,3 +1848,33 @@ class BrowserWindow:
|
|
1805
1848
|
)
|
1806
1849
|
|
1807
1850
|
self.set_permission_handler(feature, auto_deny)
|
1851
|
+
|
1852
|
+
def set_desktop_media_handler(self, handler):
|
1853
|
+
"""
|
1854
|
+
데스크톱 미디어(화면/윈도우) 선택 핸들러를 설정합니다.
|
1855
|
+
|
1856
|
+
Parameters
|
1857
|
+
----------
|
1858
|
+
handler : callable
|
1859
|
+
요청을 처리할 핸들러 함수. QWebEngineDesktopMediaRequest를 인자로 받습니다.
|
1860
|
+
|
1861
|
+
Examples
|
1862
|
+
--------
|
1863
|
+
```python
|
1864
|
+
def custom_media_handler(request):
|
1865
|
+
# 사용 가능한 화면 목록 출력
|
1866
|
+
for screen in request.screenList():
|
1867
|
+
print(f"Screen: {screen.name}")
|
1868
|
+
|
1869
|
+
# 사용 가능한 윈도우 목록 출력
|
1870
|
+
for window in request.windowList():
|
1871
|
+
print(f"Window: {window.name}")
|
1872
|
+
|
1873
|
+
# 첫 번째 화면 선택
|
1874
|
+
if request.screenList():
|
1875
|
+
request.selectScreen(request.screenList()[0])
|
1876
|
+
|
1877
|
+
window.set_desktop_media_handler(custom_media_handler)
|
1878
|
+
```
|
1879
|
+
"""
|
1880
|
+
self.web_view.custom_page.setDesktopMediaHandler(handler)
|
pyloid/js_api/window_api.py
CHANGED
@@ -2,6 +2,7 @@ from typing import TYPE_CHECKING, Optional
|
|
2
2
|
|
3
3
|
from ..api import PyloidAPI, Bridge
|
4
4
|
from PySide6.QtCore import QByteArray, QBuffer, QIODeviceBase
|
5
|
+
import base64
|
5
6
|
|
6
7
|
if TYPE_CHECKING:
|
7
8
|
from ..pyloid import Pyloid
|
@@ -244,3 +245,11 @@ class WindowAPI(PyloidAPI):
|
|
244
245
|
base64_data = byte_array.toBase64().data().decode()
|
245
246
|
return f"data:image/png;base64,{base64_data}"
|
246
247
|
return ""
|
248
|
+
|
249
|
+
###########################################################################################
|
250
|
+
# Quit
|
251
|
+
###########################################################################################
|
252
|
+
@Bridge()
|
253
|
+
def quit(self):
|
254
|
+
"""Quits the application."""
|
255
|
+
self.app.quit()
|
pyloid/pyloid.py
CHANGED
@@ -26,6 +26,9 @@ import logging
|
|
26
26
|
from .browser_window import BrowserWindow
|
27
27
|
from .tray import TrayEvent
|
28
28
|
from PySide6.QtCore import QCoreApplication
|
29
|
+
from PySide6.QtCore import QRunnable, QThreadPool, Signal, QObject
|
30
|
+
import time
|
31
|
+
from .thread_pool import PyloidThreadPool
|
29
32
|
|
30
33
|
# for linux debug
|
31
34
|
os.environ["QTWEBENGINE_DICTIONARIES_PATH"] = "/"
|
@@ -431,10 +434,16 @@ class Pyloid(QApplication):
|
|
431
434
|
app.quit()
|
432
435
|
```
|
433
436
|
"""
|
437
|
+
# 먼저 스레드 풀 정리
|
438
|
+
thread_pool = self.get_thread_pool()
|
439
|
+
thread_pool.clear() # 대기 중인 작업 제거
|
440
|
+
|
441
|
+
# 윈도우 정리
|
434
442
|
for window in self.windows:
|
435
443
|
window._window.close()
|
436
444
|
window.web_page.deleteLater()
|
437
445
|
window.web_view.deleteLater()
|
446
|
+
|
438
447
|
QApplication.quit()
|
439
448
|
|
440
449
|
###########################################################################################
|
@@ -1395,4 +1404,4 @@ class Pyloid(QApplication):
|
|
1395
1404
|
window.web_view.page().runJavaScript(js_code)
|
1396
1405
|
window.web_view.page().setBackgroundColor(
|
1397
1406
|
Qt.GlobalColor.black if self.theme == "dark" else Qt.GlobalColor.white
|
1398
|
-
)
|
1407
|
+
)
|
pyloid/thread_pool.py
ADDED
@@ -0,0 +1,501 @@
|
|
1
|
+
from PySide6.QtCore import QRunnable, QThreadPool, QDeadlineTimer, QObject, Signal
|
2
|
+
from typing import Callable, Optional, Union
|
3
|
+
|
4
|
+
class PyloidRunnable(QRunnable):
|
5
|
+
"""
|
6
|
+
A runnable task class that extends QRunnable from Qt.
|
7
|
+
|
8
|
+
Defines a unit of work that can be executed in a thread pool.
|
9
|
+
"""
|
10
|
+
|
11
|
+
def __init__(self):
|
12
|
+
"""
|
13
|
+
Initializes a PyloidRunnable instance.
|
14
|
+
|
15
|
+
By default, auto-delete is enabled.
|
16
|
+
"""
|
17
|
+
super().__init__()
|
18
|
+
self.setAutoDelete(True)
|
19
|
+
|
20
|
+
def get_auto_delete(self) -> bool:
|
21
|
+
"""
|
22
|
+
Returns whether the task is automatically deleted after completion in the thread pool.
|
23
|
+
|
24
|
+
The default value is True.
|
25
|
+
|
26
|
+
Returns
|
27
|
+
-------
|
28
|
+
bool
|
29
|
+
True if the task is automatically deleted after completion, False if manual deletion is required
|
30
|
+
|
31
|
+
Examples
|
32
|
+
--------
|
33
|
+
```python
|
34
|
+
from pyloid.thread_pool import PyloidRunnable
|
35
|
+
import time
|
36
|
+
|
37
|
+
class Worker(PyloidRunnable):
|
38
|
+
def run(self):
|
39
|
+
time.sleep(1)
|
40
|
+
print("Task executed")
|
41
|
+
|
42
|
+
worker = Worker()
|
43
|
+
print(worker.get_auto_delete())
|
44
|
+
```
|
45
|
+
"""
|
46
|
+
return self.autoDelete()
|
47
|
+
|
48
|
+
def set_auto_delete(self, value: bool) -> None:
|
49
|
+
"""
|
50
|
+
Sets whether the task is automatically deleted after completion in the thread pool.
|
51
|
+
|
52
|
+
Parameters
|
53
|
+
----------
|
54
|
+
value : bool
|
55
|
+
True to enable auto-delete after task completion,
|
56
|
+
False to require manual deletion
|
57
|
+
|
58
|
+
Examples
|
59
|
+
--------
|
60
|
+
```python
|
61
|
+
from pyloid.thread_pool import PyloidRunnable
|
62
|
+
import time
|
63
|
+
|
64
|
+
class Worker(PyloidRunnable):
|
65
|
+
def run(self):
|
66
|
+
time.sleep(1)
|
67
|
+
print("Task executed")
|
68
|
+
|
69
|
+
worker = Worker()
|
70
|
+
worker.set_auto_delete(False)
|
71
|
+
```
|
72
|
+
"""
|
73
|
+
self.setAutoDelete(value)
|
74
|
+
|
75
|
+
def run(self) -> None:
|
76
|
+
"""
|
77
|
+
Defines the actual work to be executed in the thread pool.
|
78
|
+
|
79
|
+
This method must be implemented in subclasses.
|
80
|
+
|
81
|
+
Examples
|
82
|
+
--------
|
83
|
+
```python
|
84
|
+
from pyloid.thread_pool import PyloidRunnable, PyloidThreadPool
|
85
|
+
import time
|
86
|
+
|
87
|
+
class Worker(PyloidRunnable):
|
88
|
+
def run(self):
|
89
|
+
time.sleep(1)
|
90
|
+
print("Task executed")
|
91
|
+
|
92
|
+
worker = Worker()
|
93
|
+
thread_pool = PyloidThreadPool()
|
94
|
+
thread_pool.start(worker)
|
95
|
+
```
|
96
|
+
"""
|
97
|
+
pass
|
98
|
+
|
99
|
+
class PyloidDefaultSignals(QObject):
|
100
|
+
"""
|
101
|
+
Default signal class.
|
102
|
+
|
103
|
+
Defines signals used for task start, completion, and error occurrence in the thread pool.
|
104
|
+
|
105
|
+
Attributes
|
106
|
+
----------
|
107
|
+
started : Signal
|
108
|
+
Signal emitted when a task starts
|
109
|
+
finished : Signal
|
110
|
+
Signal emitted when a task completes
|
111
|
+
error : Signal(str)
|
112
|
+
Signal emitted when an error occurs
|
113
|
+
progress : Signal(int)
|
114
|
+
Signal emitted when progress changes
|
115
|
+
|
116
|
+
Examples
|
117
|
+
--------
|
118
|
+
```python
|
119
|
+
from pyloid.thread_pool import PyloidRunnable, PyloidThreadPool, PyloidDefaultSignals
|
120
|
+
import time
|
121
|
+
|
122
|
+
class Worker(PyloidRunnable):
|
123
|
+
def __init__(self):
|
124
|
+
super().__init__()
|
125
|
+
self.signals = PyloidDefaultSignals()
|
126
|
+
|
127
|
+
def run(self):
|
128
|
+
for i in range(101):
|
129
|
+
self.signals.progress.emit(i)
|
130
|
+
time.sleep(0.1)
|
131
|
+
|
132
|
+
worker = Worker()
|
133
|
+
|
134
|
+
worker.signals.finished.connect(lambda: print("Task completed."))
|
135
|
+
worker.signals.error.connect(lambda error: print(f"Error occurred: {error}"))
|
136
|
+
worker.signals.progress.connect(lambda progress: print(f"Progress: {progress}%"))
|
137
|
+
|
138
|
+
thread_pool = PyloidThreadPool()
|
139
|
+
thread_pool.start(worker)
|
140
|
+
```
|
141
|
+
"""
|
142
|
+
started = Signal()
|
143
|
+
finished = Signal()
|
144
|
+
error = Signal(str)
|
145
|
+
progress = Signal(int)
|
146
|
+
|
147
|
+
|
148
|
+
class PyloidThreadPool:
|
149
|
+
def __init__(self):
|
150
|
+
self.thread_pool = QThreadPool.globalInstance()
|
151
|
+
|
152
|
+
def start(self, runnable: Union[PyloidRunnable, Callable], priority: int = ...) -> None:
|
153
|
+
"""
|
154
|
+
Adds a task to the thread pool and executes it.
|
155
|
+
|
156
|
+
Parameters
|
157
|
+
----------
|
158
|
+
runnable : Union[PyloidRunnable, Callable]
|
159
|
+
Task to be executed
|
160
|
+
priority : int
|
161
|
+
Task priority
|
162
|
+
|
163
|
+
Examples
|
164
|
+
--------
|
165
|
+
```python
|
166
|
+
from pyloid.thread_pool import PyloidRunnable, PyloidThreadPool
|
167
|
+
import time
|
168
|
+
|
169
|
+
class Worker(PyloidRunnable):
|
170
|
+
def run(self):
|
171
|
+
time.sleep(1)
|
172
|
+
print("Task executed")
|
173
|
+
|
174
|
+
worker = Worker()
|
175
|
+
thread_pool = PyloidThreadPool()
|
176
|
+
thread_pool.start(worker)
|
177
|
+
```
|
178
|
+
"""
|
179
|
+
self.thread_pool.start(runnable, priority)
|
180
|
+
|
181
|
+
def active_thread_count(self) -> int:
|
182
|
+
"""
|
183
|
+
Returns the number of currently active threads.
|
184
|
+
|
185
|
+
Returns
|
186
|
+
-------
|
187
|
+
int
|
188
|
+
Number of currently active threads
|
189
|
+
|
190
|
+
Examples
|
191
|
+
--------
|
192
|
+
```python
|
193
|
+
from pyloid.thread_pool import PyloidThreadPool
|
194
|
+
|
195
|
+
thread_pool = PyloidThreadPool()
|
196
|
+
print(thread_pool.active_thread_count())
|
197
|
+
```
|
198
|
+
"""
|
199
|
+
return self.thread_pool.activeThreadCount()
|
200
|
+
|
201
|
+
def max_thread_count(self) -> int:
|
202
|
+
"""
|
203
|
+
Returns the maximum number of threads that can run simultaneously in the thread pool.
|
204
|
+
|
205
|
+
Returns
|
206
|
+
-------
|
207
|
+
int
|
208
|
+
Maximum number of threads
|
209
|
+
|
210
|
+
Examples
|
211
|
+
--------
|
212
|
+
```python
|
213
|
+
from pyloid.thread_pool import PyloidThreadPool
|
214
|
+
|
215
|
+
thread_pool = PyloidThreadPool()
|
216
|
+
print(thread_pool.max_thread_count())
|
217
|
+
```
|
218
|
+
"""
|
219
|
+
return self.thread_pool.maxThreadCount()
|
220
|
+
|
221
|
+
def set_max_thread_count(self, max_thread_count: int) -> None:
|
222
|
+
"""
|
223
|
+
Sets the maximum number of threads that can run simultaneously in the thread pool.
|
224
|
+
|
225
|
+
Parameters
|
226
|
+
----------
|
227
|
+
max_thread_count : int
|
228
|
+
Maximum number of threads
|
229
|
+
|
230
|
+
Examples
|
231
|
+
--------
|
232
|
+
```python
|
233
|
+
from pyloid.thread_pool import PyloidThreadPool
|
234
|
+
|
235
|
+
thread_pool = PyloidThreadPool()
|
236
|
+
thread_pool.set_max_thread_count(10)
|
237
|
+
```
|
238
|
+
"""
|
239
|
+
self.thread_pool.setMaxThreadCount(max_thread_count)
|
240
|
+
|
241
|
+
def reserve_thread(self) -> None:
|
242
|
+
"""
|
243
|
+
Reserves a thread in the thread pool.
|
244
|
+
|
245
|
+
Examples
|
246
|
+
--------
|
247
|
+
```python
|
248
|
+
from pyloid.thread_pool import PyloidThreadPool, PyloidRunnable
|
249
|
+
import time
|
250
|
+
|
251
|
+
class Worker(PyloidRunnable):
|
252
|
+
def run(self):
|
253
|
+
time.sleep(1)
|
254
|
+
print("Task executed on reserved thread")
|
255
|
+
|
256
|
+
# Create thread pool
|
257
|
+
thread_pool = PyloidThreadPool()
|
258
|
+
|
259
|
+
# Reserve thread
|
260
|
+
thread_pool.reserve_thread()
|
261
|
+
|
262
|
+
try:
|
263
|
+
# Execute task on reserved thread
|
264
|
+
worker = Worker()
|
265
|
+
thread_pool.start_on_reserved_thread(worker)
|
266
|
+
|
267
|
+
# Wait for task completion
|
268
|
+
thread_pool.wait_for_done()
|
269
|
+
finally:
|
270
|
+
# Important: Reserved threads must be released
|
271
|
+
thread_pool.release_thread()
|
272
|
+
```
|
273
|
+
"""
|
274
|
+
self.thread_pool.reserveThread()
|
275
|
+
|
276
|
+
def release_thread(self) -> None:
|
277
|
+
"""
|
278
|
+
Releases a reserved thread in the thread pool.
|
279
|
+
|
280
|
+
Examples
|
281
|
+
--------
|
282
|
+
```python
|
283
|
+
from pyloid.thread_pool import PyloidThreadPool
|
284
|
+
|
285
|
+
thread_pool = PyloidThreadPool()
|
286
|
+
thread_pool.release_thread()
|
287
|
+
```
|
288
|
+
"""
|
289
|
+
self.thread_pool.releaseThread()
|
290
|
+
|
291
|
+
def clear(self) -> None:
|
292
|
+
"""
|
293
|
+
Removes all pending tasks from the thread pool.
|
294
|
+
|
295
|
+
Examples
|
296
|
+
--------
|
297
|
+
```python
|
298
|
+
from pyloid.thread_pool import PyloidThreadPool
|
299
|
+
|
300
|
+
thread_pool = PyloidThreadPool()
|
301
|
+
thread_pool.clear()
|
302
|
+
```
|
303
|
+
"""
|
304
|
+
self.thread_pool.clear()
|
305
|
+
|
306
|
+
# def contains(self, thread: QThread) -> bool:
|
307
|
+
# return self.thread_pool.contains(thread)
|
308
|
+
|
309
|
+
def get_expiry_timeout(self) -> int:
|
310
|
+
"""
|
311
|
+
Returns the thread expiry timeout in the thread pool.
|
312
|
+
|
313
|
+
Returns
|
314
|
+
-------
|
315
|
+
int
|
316
|
+
Thread expiry timeout
|
317
|
+
|
318
|
+
Examples
|
319
|
+
--------
|
320
|
+
```python
|
321
|
+
from pyloid.thread_pool import PyloidThreadPool
|
322
|
+
|
323
|
+
thread_pool = PyloidThreadPool()
|
324
|
+
print(thread_pool.get_expiry_timeout())
|
325
|
+
```
|
326
|
+
"""
|
327
|
+
return self.thread_pool.expiryTimeout()
|
328
|
+
|
329
|
+
def set_expiry_timeout(self, expiry_timeout: int) -> None:
|
330
|
+
"""
|
331
|
+
Sets the thread expiry timeout in the thread pool.
|
332
|
+
|
333
|
+
Parameters
|
334
|
+
----------
|
335
|
+
expiry_timeout : int
|
336
|
+
Thread expiry timeout
|
337
|
+
|
338
|
+
Examples
|
339
|
+
--------
|
340
|
+
```python
|
341
|
+
from pyloid.thread_pool import PyloidThreadPool
|
342
|
+
|
343
|
+
thread_pool = PyloidThreadPool()
|
344
|
+
thread_pool.set_expiry_timeout(1000)
|
345
|
+
```
|
346
|
+
"""
|
347
|
+
self.thread_pool.setExpiryTimeout(expiry_timeout)
|
348
|
+
|
349
|
+
# def set_stack_size(self, stack_size: int) -> None:
|
350
|
+
# self.thread_pool.setStackSize(stack_size)
|
351
|
+
|
352
|
+
# def stack_size(self) -> int:
|
353
|
+
# return self.thread_pool.stackSize()
|
354
|
+
|
355
|
+
# def set_thread_priority(self, priority: QThread.Priority) -> None:
|
356
|
+
# self.thread_pool.setThreadPriority(priority)
|
357
|
+
|
358
|
+
# def thread_priority(self) -> QThread.Priority:
|
359
|
+
# return self.thread_pool.threadPriority()
|
360
|
+
|
361
|
+
def start_on_reserved_thread(self, runnable: QRunnable) -> None:
|
362
|
+
"""
|
363
|
+
Executes a task on a reserved thread.
|
364
|
+
|
365
|
+
Parameters
|
366
|
+
----------
|
367
|
+
runnable : QRunnable
|
368
|
+
Task to be executed
|
369
|
+
|
370
|
+
Examples
|
371
|
+
--------
|
372
|
+
```python
|
373
|
+
from pyloid.thread_pool import PyloidThreadPool, PyloidRunnable
|
374
|
+
import time
|
375
|
+
|
376
|
+
class Worker(PyloidRunnable):
|
377
|
+
def run(self):
|
378
|
+
time.sleep(1)
|
379
|
+
print("Task executed on reserved thread")
|
380
|
+
|
381
|
+
worker = Worker()
|
382
|
+
thread_pool = PyloidThreadPool()
|
383
|
+
thread_pool.reserve_thread()
|
384
|
+
thread_pool.start_on_reserved_thread(worker)
|
385
|
+
thread_pool.wait_for_done()
|
386
|
+
thread_pool.release_thread()
|
387
|
+
```
|
388
|
+
"""
|
389
|
+
self.thread_pool.startOnReservedThread(runnable)
|
390
|
+
|
391
|
+
def try_start(self, runnable: Union[QRunnable, Callable]) -> bool:
|
392
|
+
"""
|
393
|
+
Adds a new task to the thread pool and attempts to execute it immediately.
|
394
|
+
|
395
|
+
Only executes the task if the thread pool has available capacity.
|
396
|
+
Operates in a non-blocking manner and does not start the task if the thread pool is full.
|
397
|
+
|
398
|
+
Parameters
|
399
|
+
----------
|
400
|
+
runnable : Union[QRunnable, Callable]
|
401
|
+
Task to be executed
|
402
|
+
|
403
|
+
Returns
|
404
|
+
-------
|
405
|
+
bool
|
406
|
+
True: Task successfully started
|
407
|
+
False: Task could not be started because the thread pool is full
|
408
|
+
|
409
|
+
Examples
|
410
|
+
--------
|
411
|
+
```python
|
412
|
+
from pyloid.thread_pool import PyloidThreadPool, PyloidRunnable
|
413
|
+
|
414
|
+
class Worker(PyloidRunnable):
|
415
|
+
def run(self):
|
416
|
+
print("Task executed")
|
417
|
+
|
418
|
+
thread_pool = PyloidThreadPool()
|
419
|
+
worker = Worker()
|
420
|
+
|
421
|
+
if thread_pool.try_start(worker):
|
422
|
+
print("Task started")
|
423
|
+
else:
|
424
|
+
print("Task could not be started because the thread pool is full")
|
425
|
+
```
|
426
|
+
"""
|
427
|
+
return self.thread_pool.tryStart(runnable)
|
428
|
+
|
429
|
+
def try_take(self, runnable: QRunnable) -> bool:
|
430
|
+
"""
|
431
|
+
Attempts to remove a specific task from the thread pool queue.
|
432
|
+
|
433
|
+
Only removes tasks that have not yet been executed. Tasks that are already running cannot be removed.
|
434
|
+
Used when task cancellation is needed.
|
435
|
+
|
436
|
+
Parameters
|
437
|
+
----------
|
438
|
+
runnable : QRunnable
|
439
|
+
Task to be removed from the queue
|
440
|
+
|
441
|
+
Returns
|
442
|
+
-------
|
443
|
+
bool
|
444
|
+
True: Task successfully removed from the queue
|
445
|
+
False: Task could not be removed (already running or not found)
|
446
|
+
|
447
|
+
Examples
|
448
|
+
--------
|
449
|
+
```python
|
450
|
+
from pyloid.thread_pool import PyloidThreadPool, PyloidRunnable
|
451
|
+
|
452
|
+
class Worker(PyloidRunnable):
|
453
|
+
def run(self):
|
454
|
+
print("Task executed")
|
455
|
+
|
456
|
+
thread_pool = PyloidThreadPool()
|
457
|
+
worker = Worker()
|
458
|
+
|
459
|
+
# Add task
|
460
|
+
thread_pool.start(worker)
|
461
|
+
|
462
|
+
# Attempt to remove task
|
463
|
+
if thread_pool.try_take(worker):
|
464
|
+
print("Task removed from queue")
|
465
|
+
else:
|
466
|
+
print("Task could not be removed (already running or not found)")
|
467
|
+
```
|
468
|
+
"""
|
469
|
+
return self.thread_pool.tryTake(runnable)
|
470
|
+
|
471
|
+
def wait_for_done(self, timeout: Optional[int] = None) -> bool:
|
472
|
+
"""
|
473
|
+
Waits for tasks to complete.
|
474
|
+
|
475
|
+
If no timeout is specified, waits indefinitely until completion.
|
476
|
+
|
477
|
+
Parameters
|
478
|
+
----------
|
479
|
+
timeout : Optional[int]
|
480
|
+
Wait time
|
481
|
+
|
482
|
+
Returns
|
483
|
+
-------
|
484
|
+
bool
|
485
|
+
True if tasks completed, False otherwise
|
486
|
+
|
487
|
+
Examples
|
488
|
+
--------
|
489
|
+
```python
|
490
|
+
from pyloid.thread_pool import PyloidThreadPool
|
491
|
+
|
492
|
+
thread_pool = PyloidThreadPool()
|
493
|
+
thread_pool.wait_for_done()
|
494
|
+
|
495
|
+
print("Tasks completed.")
|
496
|
+
```
|
497
|
+
"""
|
498
|
+
if timeout is None:
|
499
|
+
return self.thread_pool.waitForDone(-1)
|
500
|
+
else:
|
501
|
+
return self.thread_pool.waitForDone(timeout)
|
pyloid/utils.py
CHANGED
@@ -4,7 +4,7 @@ import platform
|
|
4
4
|
from typing import Optional
|
5
5
|
|
6
6
|
|
7
|
-
def get_production_path() -> Optional[str]:
|
7
|
+
def get_production_path(path: Optional[str] = None) -> Optional[str]:
|
8
8
|
"""
|
9
9
|
Returns the path to the resource files in a production environment.
|
10
10
|
If running as a regular Python script, returns None.
|
@@ -18,7 +18,7 @@ def get_production_path() -> Optional[str]:
|
|
18
18
|
Examples
|
19
19
|
--------
|
20
20
|
>>> from pyloid.utils import get_production_path
|
21
|
-
>>> path = get_production_path()
|
21
|
+
>>> path = get_production_path("assets/icon.ico")
|
22
22
|
>>> if path:
|
23
23
|
>>> print(f"Production path: {path}")
|
24
24
|
>>> else:
|
@@ -26,7 +26,7 @@ def get_production_path() -> Optional[str]:
|
|
26
26
|
"""
|
27
27
|
if getattr(sys, 'frozen', False):
|
28
28
|
# If built with PyInstaller
|
29
|
-
return sys._MEIPASS
|
29
|
+
return sys._MEIPASS + "/" + path if path else sys._MEIPASS
|
30
30
|
else:
|
31
31
|
# If running as a regular Python script
|
32
32
|
return None
|
@@ -73,6 +73,26 @@ def get_platform() -> str:
|
|
73
73
|
"""
|
74
74
|
return platform.system()
|
75
75
|
|
76
|
+
def get_absolute_path(path: str) -> str:
|
77
|
+
"""
|
78
|
+
Returns the absolute path of the given relative path.
|
79
|
+
|
80
|
+
Parameters
|
81
|
+
----------
|
82
|
+
path : str
|
83
|
+
The relative path to get the absolute path of.
|
76
84
|
|
77
|
-
|
85
|
+
Returns
|
86
|
+
-------
|
87
|
+
str
|
88
|
+
The absolute path of the given relative path.
|
89
|
+
|
90
|
+
Examples
|
91
|
+
--------
|
92
|
+
>>> from pyloid.utils import get_absolute_path
|
93
|
+
>>> absolute_path = get_absolute_path("assets/icon.ico")
|
94
|
+
>>> print(absolute_path)
|
95
|
+
C:/Users/aaaap/Documents/pyloid/pyloid/assets/icon.ico
|
96
|
+
"""
|
97
|
+
return os.path.normpath(os.path.abspath(path))
|
78
98
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pyloid
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.17.1
|
4
4
|
Summary:
|
5
5
|
Author: aesthetics-of-record
|
6
6
|
Author-email: 111675679+aesthetics-of-record@users.noreply.github.com
|
@@ -10,8 +10,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.10
|
11
11
|
Classifier: Programming Language :: Python :: 3.11
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
13
|
-
Requires-Dist:
|
14
|
-
Requires-Dist: pyside6 (>=6.7.3,<7.0.0)
|
13
|
+
Requires-Dist: pyside6 (>=6.8.0.2,<7.0.0.0)
|
15
14
|
Description-Content-Type: text/markdown
|
16
15
|
|
17
16
|
# Pyloid 👋
|
@@ -0,0 +1,18 @@
|
|
1
|
+
pyloid/__init__.py,sha256=OOPhOKNQVmAM8hnfTeE7lHzxb8LsFNcgegBAvDrA-vY,293
|
2
|
+
pyloid/api.py,sha256=np0pFVUlen_GpN0svY0A3awY_ZjVFk-RpHQZZKFUMuo,2157
|
3
|
+
pyloid/autostart.py,sha256=K7DQYl4LHItvPp0bt1V9WwaaZmVSTeGvadkcwG-KKrI,3899
|
4
|
+
pyloid/browser_window.py,sha256=BYjzcP6tPg-3Splai-v60-0LZ2gXeyk0QUrcy6rHydA,61946
|
5
|
+
pyloid/custom/titlebar.py,sha256=itzK9pJbZMQ7BKca9kdbuHMffurrw15UijR6OU03Xsk,3894
|
6
|
+
pyloid/filewatcher.py,sha256=3M5zWVUf1OhlkWJcDFC8ZA9agO4Q-U8WdgGpy6kaVz0,4601
|
7
|
+
pyloid/js_api/event_api.py,sha256=_52yyBonqecmMvJpFW7OMNi_jX8Nrteqw_kI6r-DGG0,951
|
8
|
+
pyloid/js_api/window_api.py,sha256=QutXXAOZkpYmytOYSenpzVSCl15-OFEjDDr75fHfLvk,8481
|
9
|
+
pyloid/monitor.py,sha256=1mXvHm5deohnNlTLcRx4sT4x-stnOIb0dUQnnxN50Uo,28295
|
10
|
+
pyloid/pyloid.py,sha256=klY6-O7rEZZYfhayq9RTwvO9f8KCXWxIo7G8BDFy2hg,44236
|
11
|
+
pyloid/thread_pool.py,sha256=fKOBb8jMfZn_7crA_fJCno8dObBRZE31EIWaNQ759aw,14616
|
12
|
+
pyloid/timer.py,sha256=RqMsChFUd93cxMVgkHWiIKrci0QDTBgJSTULnAtYT8M,8712
|
13
|
+
pyloid/tray.py,sha256=D12opVEc2wc2T4tK9epaN1oOdeziScsIVNM2uCN7C-A,1710
|
14
|
+
pyloid/utils.py,sha256=XPM2PxO0LQ_eAnba5pKJ3vYsCev91JMFmv13EFuAy5o,2620
|
15
|
+
pyloid-0.17.1.dist-info/LICENSE,sha256=F96EzotgWhhpnQTW2TcdoqrMDir1jyEo6H915tGQ-QE,11524
|
16
|
+
pyloid-0.17.1.dist-info/METADATA,sha256=zx0zcFBel8qVl0pzxeEey7KhwkzDbk6H1GrPj7uw-1Y,3054
|
17
|
+
pyloid-0.17.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
18
|
+
pyloid-0.17.1.dist-info/RECORD,,
|
pyloid-0.16.12.dist-info/RECORD
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
pyloid/__init__.py,sha256=OOPhOKNQVmAM8hnfTeE7lHzxb8LsFNcgegBAvDrA-vY,293
|
2
|
-
pyloid/api.py,sha256=np0pFVUlen_GpN0svY0A3awY_ZjVFk-RpHQZZKFUMuo,2157
|
3
|
-
pyloid/autostart.py,sha256=K7DQYl4LHItvPp0bt1V9WwaaZmVSTeGvadkcwG-KKrI,3899
|
4
|
-
pyloid/browser_window.py,sha256=xSf1tR1kazAIpqbrTEx0YgcMXqcHOzPpmdzU3agTqw0,58691
|
5
|
-
pyloid/custom/titlebar.py,sha256=itzK9pJbZMQ7BKca9kdbuHMffurrw15UijR6OU03Xsk,3894
|
6
|
-
pyloid/filewatcher.py,sha256=3M5zWVUf1OhlkWJcDFC8ZA9agO4Q-U8WdgGpy6kaVz0,4601
|
7
|
-
pyloid/js_api/event_api.py,sha256=_52yyBonqecmMvJpFW7OMNi_jX8Nrteqw_kI6r-DGG0,951
|
8
|
-
pyloid/js_api/window_api.py,sha256=_EAZ0GG0oa0EeIIyWnys03InLQuQfv4ZPezMouOJEGc,8155
|
9
|
-
pyloid/monitor.py,sha256=1mXvHm5deohnNlTLcRx4sT4x-stnOIb0dUQnnxN50Uo,28295
|
10
|
-
pyloid/pyloid.py,sha256=ar-3yP8IqB6HCWwuxJmjJZ9kekn7LhHqnNOTH-qhxpM,43925
|
11
|
-
pyloid/timer.py,sha256=RqMsChFUd93cxMVgkHWiIKrci0QDTBgJSTULnAtYT8M,8712
|
12
|
-
pyloid/tray.py,sha256=D12opVEc2wc2T4tK9epaN1oOdeziScsIVNM2uCN7C-A,1710
|
13
|
-
pyloid/utils.py,sha256=VGZE2liY8_AElEqxVe1YLbk3fWlcAevpRc6oOTTgi-U,1927
|
14
|
-
pyloid-0.16.12.dist-info/LICENSE,sha256=F96EzotgWhhpnQTW2TcdoqrMDir1jyEo6H915tGQ-QE,11524
|
15
|
-
pyloid-0.16.12.dist-info/METADATA,sha256=V0I3qXPqcx0dSLUOO0_03tgqO4wn6UBZwDl5VoqLxug,3088
|
16
|
-
pyloid-0.16.12.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
17
|
-
pyloid-0.16.12.dist-info/RECORD,,
|
File without changes
|
File without changes
|