pyloid 0.11.3__tar.gz → 0.11.5__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyloid
3
- Version: 0.11.3
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/yloid_react_boilerplate)
51
+ [https://github.com/pylonic/pyloid_react_boilerplate](https://github.com/Pyloid/pyloid_react_boilerplate)
52
52
 
53
53
  ### Custom Your Boilerplate 🔨
54
54
 
@@ -33,7 +33,7 @@ With Pyloid, you can leverage the full power of Python in your desktop applicati
33
33
 
34
34
  #### Creating a React + Vite + Pyloid Project ⚛️
35
35
 
36
- [https://github.com/pylonic/pyloid_react_boilerplate](https://github.com/Pyloid/yloid_react_boilerplate)
36
+ [https://github.com/pylonic/pyloid_react_boilerplate](https://github.com/Pyloid/pyloid_react_boilerplate)
37
37
 
38
38
  ### Custom Your Boilerplate 🔨
39
39
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pyloid"
3
- version = "0.11.3"
3
+ version = "0.11.5"
4
4
  description = ""
5
5
  authors = ["aesthetics-of-record <111675679+aesthetics-of-record@users.noreply.github.com>"]
6
6
  readme = "README.md"
@@ -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 QIcon, QKeySequence, QShortcut, QClipboard, QImage, QAction, QPalette, QColor
12
- from PySide6.QtCore import Qt, Signal, QUrl, QObject, QTimer
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.QtCore import QCoreApplication
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('Qt').setLevel(logging.ERROR)
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
- if "vulkan" not in message.lower():
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=str)
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 json.dumps(window_properties)
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
- def get_window_properties(self):
401
- """Returns the properties of the window."""
402
- return {
403
- "id": self.id,
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
- """Quits the application."""
751
- self.close_all_windows()
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
  ###########################################################################################
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes