pyloid 0.11.3__py3-none-any.whl → 0.11.5__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/pyloid.py
CHANGED
@@ -8,8 +8,17 @@ from PySide6.QtWidgets import (
|
|
8
8
|
)
|
9
9
|
from PySide6.QtWebEngineWidgets import QWebEngineView
|
10
10
|
from PySide6.QtWebChannel import QWebChannel
|
11
|
-
from PySide6.QtGui import
|
12
|
-
|
11
|
+
from PySide6.QtGui import (
|
12
|
+
QIcon,
|
13
|
+
QKeySequence,
|
14
|
+
QShortcut,
|
15
|
+
QClipboard,
|
16
|
+
QImage,
|
17
|
+
QAction,
|
18
|
+
QPalette,
|
19
|
+
QColor,
|
20
|
+
)
|
21
|
+
from PySide6.QtCore import Qt, Signal, QPoint, QUrl, QObject, QTimer, QSize
|
13
22
|
from PySide6.QtNetwork import QLocalServer, QLocalSocket
|
14
23
|
from PySide6.QtWebEngineCore import QWebEnginePage, QWebEngineSettings
|
15
24
|
from .api import PyloidAPI, Bridge
|
@@ -23,13 +32,16 @@ import json
|
|
23
32
|
from .autostart import AutoStart
|
24
33
|
from .filewatcher import FileWatcher
|
25
34
|
import logging
|
26
|
-
from PySide6.
|
35
|
+
from PySide6.QtGui import QPalette, QColor
|
36
|
+
from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QHBoxLayout, QSizePolicy
|
37
|
+
from PySide6.QtGui import QPixmap
|
27
38
|
|
28
39
|
# for linux debug
|
29
40
|
os.environ["QTWEBENGINE_DICTIONARIES_PATH"] = "/"
|
30
41
|
|
31
42
|
# for macos debug
|
32
|
-
logging.getLogger(
|
43
|
+
logging.getLogger("Qt").setLevel(logging.ERROR)
|
44
|
+
|
33
45
|
|
34
46
|
def custom_message_handler(mode, context, message):
|
35
47
|
if not hasattr(custom_message_handler, "vulkan_warning_shown") and (
|
@@ -42,7 +54,13 @@ def custom_message_handler(mode, context, message):
|
|
42
54
|
)
|
43
55
|
os.environ["QT_QUICK_BACKEND"] = "software"
|
44
56
|
custom_message_handler.vulkan_warning_shown = True
|
45
|
-
|
57
|
+
|
58
|
+
if "Autofill.enable failed" in message:
|
59
|
+
print(
|
60
|
+
"\033[93mPyloid Warning: Autofill is not enabled in developer tools.\033[0m"
|
61
|
+
)
|
62
|
+
|
63
|
+
if "vulkan" not in message.lower() and "Autofill.enable failed" not in message:
|
46
64
|
print(message)
|
47
65
|
|
48
66
|
|
@@ -60,12 +78,12 @@ class WindowAPI(PyloidAPI):
|
|
60
78
|
"""Returns the current window ID."""
|
61
79
|
return self.window_id
|
62
80
|
|
63
|
-
@Bridge(result=
|
81
|
+
@Bridge(result=dict)
|
64
82
|
def getWindowProperties(self):
|
65
83
|
"""Returns the properties of the window."""
|
66
84
|
window = self.app.get_window_by_id(self.window_id)
|
67
85
|
window_properties = window.get_window_properties()
|
68
|
-
return
|
86
|
+
return window_properties
|
69
87
|
|
70
88
|
@Bridge()
|
71
89
|
def close(self):
|
@@ -166,6 +184,44 @@ class WindowAPI(PyloidAPI):
|
|
166
184
|
return window.capture(save_path)
|
167
185
|
return None
|
168
186
|
|
187
|
+
@Bridge(result=bool)
|
188
|
+
def getFrame(self):
|
189
|
+
"""Returns whether the window has a frame."""
|
190
|
+
window = self.app.get_window_by_id(self.window_id)
|
191
|
+
return window.frame if window else False
|
192
|
+
|
193
|
+
@Bridge(result=bool)
|
194
|
+
def getContextMenu(self):
|
195
|
+
"""Returns whether the window has a context menu."""
|
196
|
+
window = self.app.get_window_by_id(self.window_id)
|
197
|
+
return window.context_menu if window else False
|
198
|
+
|
199
|
+
@Bridge(result=bool)
|
200
|
+
def getDevTools(self):
|
201
|
+
"""Returns whether the window has developer tools."""
|
202
|
+
window = self.app.get_window_by_id(self.window_id)
|
203
|
+
return window.dev_tools if window else False
|
204
|
+
|
205
|
+
@Bridge(result=str)
|
206
|
+
def getTitle(self):
|
207
|
+
"""Returns the title of the window."""
|
208
|
+
window = self.app.get_window_by_id(self.window_id)
|
209
|
+
return window.title if window else ""
|
210
|
+
|
211
|
+
@Bridge(result=dict)
|
212
|
+
def getSize(self):
|
213
|
+
"""Returns the size of the window."""
|
214
|
+
window = self.app.get_window_by_id(self.window_id)
|
215
|
+
return {"width": window.width, "height": window.height} if window else {"width": 0, "height": 0}
|
216
|
+
|
217
|
+
@Bridge(result=dict)
|
218
|
+
def getPosition(self):
|
219
|
+
"""Returns the position of the window."""
|
220
|
+
window = self.app.get_window_by_id(self.window_id)
|
221
|
+
return {"x": window.x, "y": window.y} if window else {"x": 0, "y": 0}
|
222
|
+
|
223
|
+
|
224
|
+
|
169
225
|
|
170
226
|
# class EventAPI(PylonAPI):
|
171
227
|
# def __init__(self, window_id: str, app):
|
@@ -189,6 +245,106 @@ class WindowAPI(PyloidAPI):
|
|
189
245
|
# callback(*args, **kwargs)
|
190
246
|
|
191
247
|
|
248
|
+
class CustomTitleBar(QWidget):
|
249
|
+
def __init__(self, parent=None):
|
250
|
+
super().__init__(parent)
|
251
|
+
self.layout = QHBoxLayout(self)
|
252
|
+
self.layout.setContentsMargins(5, 0, 5, 0)
|
253
|
+
self.layout.setSpacing(0)
|
254
|
+
|
255
|
+
self.icon_label = QLabel()
|
256
|
+
self.icon_label.setFixedSize(20, 20)
|
257
|
+
self.title = QLabel("Custom Title")
|
258
|
+
|
259
|
+
self.minimize_button = QPushButton("-")
|
260
|
+
self.maximize_button = QPushButton("❐")
|
261
|
+
self.close_button = QPushButton("×")
|
262
|
+
|
263
|
+
for button in (self.minimize_button, self.maximize_button, self.close_button):
|
264
|
+
button.setFixedSize(45, 30)
|
265
|
+
button.setFlat(True)
|
266
|
+
|
267
|
+
self.layout.addWidget(self.icon_label)
|
268
|
+
self.layout.addSpacing(5)
|
269
|
+
self.layout.addWidget(self.title)
|
270
|
+
self.layout.addStretch(1)
|
271
|
+
self.layout.addWidget(self.minimize_button)
|
272
|
+
self.layout.addWidget(self.maximize_button)
|
273
|
+
self.layout.addWidget(self.close_button)
|
274
|
+
|
275
|
+
self.minimize_button.clicked.connect(self.window().showMinimized)
|
276
|
+
self.maximize_button.clicked.connect(self.toggle_maximize)
|
277
|
+
self.close_button.clicked.connect(self.window().close)
|
278
|
+
|
279
|
+
self.setFixedHeight(30)
|
280
|
+
self.set_style("darkblue", "white")
|
281
|
+
|
282
|
+
def set_style(self, bg_color, text_color):
|
283
|
+
self.setAutoFillBackground(True)
|
284
|
+
palette = self.palette()
|
285
|
+
bg_qcolor = QColor(bg_color)
|
286
|
+
text_qcolor = QColor(text_color)
|
287
|
+
palette.setColor(QPalette.Window, bg_qcolor)
|
288
|
+
palette.setColor(QPalette.WindowText, text_qcolor)
|
289
|
+
self.setPalette(palette)
|
290
|
+
|
291
|
+
self.title.setStyleSheet(f"color: {text_color}; font-weight: bold;")
|
292
|
+
|
293
|
+
button_style = f"""
|
294
|
+
QPushButton {{
|
295
|
+
background-color: {bg_color};
|
296
|
+
color: {text_color};
|
297
|
+
border: none;
|
298
|
+
font-family: Arial;
|
299
|
+
font-size: 14px;
|
300
|
+
padding: 0px;
|
301
|
+
text-align: center;
|
302
|
+
}}
|
303
|
+
QPushButton:hover {{
|
304
|
+
background-color: {bg_qcolor.lighter(120).name()};
|
305
|
+
}}
|
306
|
+
QPushButton:pressed {{
|
307
|
+
background-color: {bg_qcolor.darker(110).name()};
|
308
|
+
}}
|
309
|
+
"""
|
310
|
+
for button in (self.minimize_button, self.maximize_button, self.close_button):
|
311
|
+
button.setStyleSheet(button_style)
|
312
|
+
|
313
|
+
self.close_button.setStyleSheet(button_style + f"""
|
314
|
+
QPushButton:hover {{
|
315
|
+
background-color: #e81123;
|
316
|
+
color: white;
|
317
|
+
}}
|
318
|
+
""")
|
319
|
+
|
320
|
+
def mousePressEvent(self, event):
|
321
|
+
if event.button() == Qt.LeftButton:
|
322
|
+
self.window().moving = True
|
323
|
+
self.window().offset = event.pos()
|
324
|
+
|
325
|
+
def mouseMoveEvent(self, event):
|
326
|
+
if self.window().moving:
|
327
|
+
self.window().move(event.globalPos() - self.window().offset)
|
328
|
+
|
329
|
+
def mouseReleaseEvent(self, event):
|
330
|
+
if event.button() == Qt.LeftButton:
|
331
|
+
self.window().moving = False
|
332
|
+
|
333
|
+
def toggle_maximize(self):
|
334
|
+
if self.window().isMaximized():
|
335
|
+
self.window().showNormal()
|
336
|
+
self.maximize_button.setText("❐")
|
337
|
+
else:
|
338
|
+
self.window().showMaximized()
|
339
|
+
self.maximize_button.setText("❐")
|
340
|
+
|
341
|
+
def set_icon(self, icon_path):
|
342
|
+
pixmap = QPixmap(icon_path)
|
343
|
+
self.icon_label.setPixmap(pixmap.scaled(20, 20, Qt.KeepAspectRatio, Qt.SmoothTransformation))
|
344
|
+
|
345
|
+
def set_title(self, title):
|
346
|
+
self.title.setText(title)
|
347
|
+
|
192
348
|
class BrowserWindow:
|
193
349
|
def __init__(
|
194
350
|
self,
|
@@ -226,6 +382,37 @@ class BrowserWindow:
|
|
226
382
|
self.shortcuts = {}
|
227
383
|
###########################################################################################
|
228
384
|
|
385
|
+
def set_custom_frame(self, use_custom: bool, title: str = "Custom Title", bg_color: str = "darkblue", text_color: str = "white", icon_path: str = None):
|
386
|
+
"""커스텀 프레임을 설정하거나 제거합니다."""
|
387
|
+
if use_custom:
|
388
|
+
self._window.setWindowFlags(Qt.FramelessWindowHint)
|
389
|
+
self.custom_title_bar = CustomTitleBar(self._window)
|
390
|
+
self.custom_title_bar.set_style(bg_color, text_color)
|
391
|
+
self.custom_title_bar.set_title(title)
|
392
|
+
|
393
|
+
if icon_path:
|
394
|
+
self.custom_title_bar.set_icon(icon_path)
|
395
|
+
|
396
|
+
layout = QVBoxLayout()
|
397
|
+
layout.setContentsMargins(0, 0, 0, 0)
|
398
|
+
layout.setSpacing(0)
|
399
|
+
layout.addWidget(self.custom_title_bar)
|
400
|
+
layout.addWidget(self.web_view)
|
401
|
+
|
402
|
+
central_widget = QWidget()
|
403
|
+
central_widget.setLayout(layout)
|
404
|
+
self._window.setCentralWidget(central_widget)
|
405
|
+
|
406
|
+
# 창 이동을 위한 속성 추가
|
407
|
+
self._window.moving = False
|
408
|
+
self._window.offset = QPoint()
|
409
|
+
else:
|
410
|
+
self._window.setWindowFlags(Qt.Window)
|
411
|
+
self._window.setCentralWidget(self.web_view)
|
412
|
+
self.custom_title_bar = None
|
413
|
+
|
414
|
+
self._window.show()
|
415
|
+
|
229
416
|
def _load(self):
|
230
417
|
self._window.setWindowTitle(self.title)
|
231
418
|
|
@@ -397,26 +584,21 @@ class BrowserWindow:
|
|
397
584
|
self.dev_tools_window.resize(800, 600)
|
398
585
|
self.dev_tools_window.show()
|
399
586
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
"title": self.title,
|
405
|
-
"width": self.width,
|
406
|
-
"height": self.height,
|
407
|
-
"x": self.x,
|
408
|
-
"y": self.y,
|
409
|
-
"frame": self.frame,
|
410
|
-
"context_menu": self.context_menu,
|
411
|
-
"dev_tools": self.dev_tools,
|
412
|
-
}
|
413
|
-
|
414
|
-
def get_id(self):
|
415
|
-
"""Returns the ID of the window."""
|
416
|
-
return self.id
|
587
|
+
# Add this line to handle dev tools window closure
|
588
|
+
self.dev_tools_window.closeEvent = lambda event: setattr(
|
589
|
+
self, "dev_tools_window", None
|
590
|
+
)
|
417
591
|
|
418
592
|
def closeEvent(self, event):
|
419
593
|
"""Handles the event when the window is closed."""
|
594
|
+
# Close developer tools if open
|
595
|
+
if hasattr(self, "dev_tools_window") and self.dev_tools_window:
|
596
|
+
self.dev_tools_window.close()
|
597
|
+
self.dev_tools_window = None
|
598
|
+
|
599
|
+
# Solve memory leak issue with web view engine
|
600
|
+
self.web_view.page().deleteLater()
|
601
|
+
self.web_view.deleteLater()
|
420
602
|
self._remove_from_app_windows()
|
421
603
|
event.accept() # Accept the event (allow the window to close)
|
422
604
|
|
@@ -556,15 +738,53 @@ class BrowserWindow:
|
|
556
738
|
###########################################################################################
|
557
739
|
# Get Properties
|
558
740
|
###########################################################################################
|
741
|
+
def get_window_properties(self):
|
742
|
+
"""Returns the properties of the window."""
|
743
|
+
return {
|
744
|
+
"id": self.id,
|
745
|
+
"title": self.title,
|
746
|
+
"width": self.width,
|
747
|
+
"height": self.height,
|
748
|
+
"x": self.x,
|
749
|
+
"y": self.y,
|
750
|
+
"frame": self.frame,
|
751
|
+
"context_menu": self.context_menu,
|
752
|
+
"dev_tools": self.dev_tools,
|
753
|
+
}
|
559
754
|
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
755
|
+
def get_id(self):
|
756
|
+
"""Returns the ID of the window."""
|
757
|
+
return self.id
|
758
|
+
|
759
|
+
def get_size(self) -> Dict[str, int]:
|
760
|
+
"""Returns the size of the window."""
|
761
|
+
return {"width": self.width, "height": self.height}
|
762
|
+
|
763
|
+
def get_position(self) -> Dict[str, int]:
|
764
|
+
"""Returns the position of the window."""
|
765
|
+
return {"x": self.x, "y": self.y}
|
766
|
+
|
767
|
+
def get_title(self) -> str:
|
768
|
+
"""Returns the title of the window."""
|
769
|
+
return self.title
|
770
|
+
|
771
|
+
def get_url(self) -> str:
|
772
|
+
"""Returns the URL of the window."""
|
773
|
+
return self.web_view.url().toString()
|
774
|
+
|
775
|
+
def get_visible(self) -> bool:
|
776
|
+
"""Returns the visibility of the window."""
|
777
|
+
return self._window.isVisible()
|
778
|
+
|
779
|
+
def set_resizable(self, resizable: bool):
|
780
|
+
"""창의 크기 조절 가능 여부를 설정합니다."""
|
781
|
+
self.resizable = resizable
|
782
|
+
if resizable:
|
783
|
+
self._window.setWindowFlags(self._window.windowFlags() & ~Qt.MSWindowsFixedSizeDialogHint)
|
784
|
+
else:
|
785
|
+
self._window.setWindowFlags(self._window.windowFlags() | Qt.MSWindowsFixedSizeDialogHint)
|
786
|
+
self._window.show() # 변경사항을 적용하기 위해 창을 다시 표시합니다.
|
787
|
+
|
568
788
|
|
569
789
|
class _WindowController(QObject):
|
570
790
|
create_window_signal = Signal(
|
@@ -602,7 +822,7 @@ class Pyloid(QApplication):
|
|
602
822
|
|
603
823
|
self.app_name = app_name
|
604
824
|
self.app_path = sys.executable
|
605
|
-
|
825
|
+
|
606
826
|
self.auto_start = AutoStart(self.app_name, self.app_path)
|
607
827
|
|
608
828
|
self.animation_timer = None
|
@@ -747,8 +967,11 @@ class Pyloid(QApplication):
|
|
747
967
|
window._window.close()
|
748
968
|
|
749
969
|
def quit(self):
|
750
|
-
"""
|
751
|
-
self.
|
970
|
+
"""애플리케이션을 종료합니다."""
|
971
|
+
for window in self.windows:
|
972
|
+
window._window.close()
|
973
|
+
window.web_page.deleteLater()
|
974
|
+
window.web_view.deleteLater()
|
752
975
|
QApplication.quit()
|
753
976
|
|
754
977
|
###########################################################################################
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pyloid
|
3
|
-
Version: 0.11.
|
3
|
+
Version: 0.11.5
|
4
4
|
Summary:
|
5
5
|
Author: aesthetics-of-record
|
6
6
|
Author-email: 111675679+aesthetics-of-record@users.noreply.github.com
|
@@ -48,7 +48,7 @@ With Pyloid, you can leverage the full power of Python in your desktop applicati
|
|
48
48
|
|
49
49
|
#### Creating a React + Vite + Pyloid Project ⚛️
|
50
50
|
|
51
|
-
[https://github.com/pylonic/pyloid_react_boilerplate](https://github.com/Pyloid/
|
51
|
+
[https://github.com/pylonic/pyloid_react_boilerplate](https://github.com/Pyloid/pyloid_react_boilerplate)
|
52
52
|
|
53
53
|
### Custom Your Boilerplate 🔨
|
54
54
|
|
@@ -3,11 +3,11 @@ pyloid/api.py,sha256=whgfvPr1A6iwZ1Ewo-0FnOUNnt1K58c-P7YjzuQHcUM,194
|
|
3
3
|
pyloid/autostart.py,sha256=K7DQYl4LHItvPp0bt1V9WwaaZmVSTeGvadkcwG-KKrI,3899
|
4
4
|
pyloid/filewatcher.py,sha256=n8N56D65le5TpsgxXb7z-FO_0lqv4UYD4yGq_UuMrAs,1285
|
5
5
|
pyloid/monitor.py,sha256=fqDnZ_7dpxVZLVJ5gCluDRY2USrQ5YL_fw1AnYivhsk,12741
|
6
|
-
pyloid/pyloid.py,sha256=
|
6
|
+
pyloid/pyloid.py,sha256=JMvWDd-3IA9wcpZSlzdMBIPK8F7v1O_1o8N58LkBlO4,49556
|
7
7
|
pyloid/timer.py,sha256=1bYhqte3rV77vaeMUkcTgmx2ux7FtCqLCx9lIC2-COg,4360
|
8
8
|
pyloid/tray.py,sha256=rXgdkvzGxtie_EIcTSA7fjuta4nJk5THhNkGFcfv5Ew,634
|
9
9
|
pyloid/utils.py,sha256=DQerZWU_0o8dHcJ5y3yXf9i5OXn7KQZqU-hVBq3uPUA,711
|
10
|
-
pyloid-0.11.
|
11
|
-
pyloid-0.11.
|
12
|
-
pyloid-0.11.
|
13
|
-
pyloid-0.11.
|
10
|
+
pyloid-0.11.5.dist-info/LICENSE,sha256=F96EzotgWhhpnQTW2TcdoqrMDir1jyEo6H915tGQ-QE,11524
|
11
|
+
pyloid-0.11.5.dist-info/METADATA,sha256=vp2aFxKcfVB42nBnkWlsFPNcb-xzHrigWIQD-TiHIXw,6069
|
12
|
+
pyloid-0.11.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
13
|
+
pyloid-0.11.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|