pyloid 0.11.4__tar.gz → 0.12.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -198,4 +198,4 @@ Apache License
198
198
  distributed under the License is distributed on an "AS IS" BASIS,
199
199
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
200
  See the License for the specific language governing permissions and
201
- limitations under the License.
201
+ limitations under the License.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyloid
3
- Version: 0.11.4
3
+ Version: 0.12.0
4
4
  Summary:
5
5
  Author: aesthetics-of-record
6
6
  Author-email: 111675679+aesthetics-of-record@users.noreply.github.com
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pyloid"
3
- version = "0.11.4"
3
+ version = "0.12.0"
4
4
  description = ""
5
5
  authors = ["aesthetics-of-record <111675679+aesthetics-of-record@users.noreply.github.com>"]
6
6
  readme = "README.md"
@@ -0,0 +1,116 @@
1
+ from PySide6.QtWidgets import (
2
+ QWidget,
3
+ QHBoxLayout,
4
+ QLabel,
5
+ QPushButton,
6
+ QVBoxLayout,
7
+ QApplication,
8
+ )
9
+ from PySide6.QtGui import QColor, QPalette, QPixmap
10
+ from PySide6.QtCore import Qt
11
+
12
+
13
+ class CustomTitleBar(QWidget):
14
+ def __init__(self, parent=None):
15
+ super().__init__(parent)
16
+ self.layout = QHBoxLayout(self)
17
+ self.layout.setContentsMargins(5, 0, 5, 0)
18
+ self.layout.setSpacing(0)
19
+
20
+ self.icon_label = QLabel()
21
+ self.icon_label.setFixedSize(20, 20)
22
+ self.title = QLabel("Custom Title")
23
+
24
+ self.minimize_button = QPushButton("-")
25
+ self.maximize_button = QPushButton("❐")
26
+ self.close_button = QPushButton("×")
27
+
28
+ for button in (self.minimize_button, self.maximize_button, self.close_button):
29
+ button.setFixedSize(45, 30)
30
+ button.setFlat(True)
31
+
32
+ self.layout.addWidget(self.icon_label)
33
+ self.layout.addSpacing(5)
34
+ self.layout.addWidget(self.title)
35
+ self.layout.addStretch(1)
36
+ self.layout.addWidget(self.minimize_button)
37
+ self.layout.addWidget(self.maximize_button)
38
+ self.layout.addWidget(self.close_button)
39
+
40
+ self.minimize_button.clicked.connect(self.window().showMinimized)
41
+ self.maximize_button.clicked.connect(self.toggle_maximize)
42
+ self.close_button.clicked.connect(self.window().close)
43
+
44
+ self.setFixedHeight(30)
45
+ self.set_style("darkblue", "white")
46
+
47
+ def set_style(self, bg_color, text_color):
48
+ self.setAutoFillBackground(True)
49
+ palette = self.palette()
50
+ bg_qcolor = QColor(bg_color)
51
+ text_qcolor = QColor(text_color)
52
+ palette.setColor(QPalette.Window, bg_qcolor)
53
+ palette.setColor(QPalette.WindowText, text_qcolor)
54
+ self.setPalette(palette)
55
+
56
+ self.title.setStyleSheet(f"color: {text_color}; font-weight: bold;")
57
+
58
+ button_style = f"""
59
+ QPushButton {{
60
+ background-color: {bg_color};
61
+ color: {text_color};
62
+ border: none;
63
+ font-family: Arial;
64
+ font-size: 14px;
65
+ padding: 0px;
66
+ text-align: center;
67
+ }}
68
+ QPushButton:hover {{
69
+ background-color: {bg_qcolor.lighter(120).name()};
70
+ }}
71
+ QPushButton:pressed {{
72
+ background-color: {bg_qcolor.darker(110).name()};
73
+ }}
74
+ """
75
+ for button in (self.minimize_button, self.maximize_button, self.close_button):
76
+ button.setStyleSheet(button_style)
77
+
78
+ self.close_button.setStyleSheet(
79
+ button_style
80
+ + f"""
81
+ QPushButton:hover {{
82
+ background-color: #e81123;
83
+ color: white;
84
+ }}
85
+ """
86
+ )
87
+
88
+ def mousePressEvent(self, event):
89
+ if event.button() == Qt.LeftButton:
90
+ self.window().moving = True
91
+ self.window().offset = event.pos()
92
+
93
+ def mouseMoveEvent(self, event):
94
+ if self.window().moving:
95
+ self.window().move(event.globalPos() - self.window().offset)
96
+
97
+ def mouseReleaseEvent(self, event):
98
+ if event.button() == Qt.LeftButton:
99
+ self.window().moving = False
100
+
101
+ def toggle_maximize(self):
102
+ if self.window().isMaximized():
103
+ self.window().showNormal()
104
+ self.maximize_button.setText("❐")
105
+ else:
106
+ self.window().showMaximized()
107
+ self.maximize_button.setText("❐")
108
+
109
+ def set_icon(self, icon_path):
110
+ pixmap = QPixmap(icon_path)
111
+ self.icon_label.setPixmap(
112
+ pixmap.scaled(20, 20, Qt.KeepAspectRatio, Qt.SmoothTransformation)
113
+ )
114
+
115
+ def set_title(self, title):
116
+ self.title.setText(title)
@@ -15,10 +15,8 @@ from PySide6.QtGui import (
15
15
  QClipboard,
16
16
  QImage,
17
17
  QAction,
18
- QPalette,
19
- QColor,
20
18
  )
21
- from PySide6.QtCore import Qt, Signal, QUrl, QObject, QTimer
19
+ from PySide6.QtCore import Qt, Signal, QPoint, QUrl, QObject, QTimer, QSize, QEvent
22
20
  from PySide6.QtNetwork import QLocalServer, QLocalSocket
23
21
  from PySide6.QtWebEngineCore import QWebEnginePage, QWebEngineSettings
24
22
  from .api import PyloidAPI, Bridge
@@ -32,7 +30,11 @@ import json
32
30
  from .autostart import AutoStart
33
31
  from .filewatcher import FileWatcher
34
32
  import logging
35
- from PySide6.QtCore import QCoreApplication, QtMsgType
33
+ from PySide6.QtWidgets import (
34
+ QWidget,
35
+ QVBoxLayout,
36
+ )
37
+ from .custom.titlebar import CustomTitleBar
36
38
 
37
39
  # for linux debug
38
40
  os.environ["QTWEBENGINE_DICTIONARIES_PATH"] = "/"
@@ -187,39 +189,48 @@ class WindowAPI(PyloidAPI):
187
189
  """Returns whether the window has a frame."""
188
190
  window = self.app.get_window_by_id(self.window_id)
189
191
  return window.frame if window else False
190
-
192
+
191
193
  @Bridge(result=bool)
192
194
  def getContextMenu(self):
193
195
  """Returns whether the window has a context menu."""
194
196
  window = self.app.get_window_by_id(self.window_id)
195
197
  return window.context_menu if window else False
196
-
198
+
197
199
  @Bridge(result=bool)
198
200
  def getDevTools(self):
199
201
  """Returns whether the window has developer tools."""
200
202
  window = self.app.get_window_by_id(self.window_id)
201
203
  return window.dev_tools if window else False
202
-
204
+
203
205
  @Bridge(result=str)
204
206
  def getTitle(self):
205
207
  """Returns the title of the window."""
206
208
  window = self.app.get_window_by_id(self.window_id)
207
209
  return window.title if window else ""
208
-
210
+
209
211
  @Bridge(result=dict)
210
212
  def getSize(self):
211
213
  """Returns the size of the window."""
212
214
  window = self.app.get_window_by_id(self.window_id)
213
- return {"width": window.width, "height": window.height} if window else {"width": 0, "height": 0}
214
-
215
+ return (
216
+ {"width": window.width, "height": window.height}
217
+ if window
218
+ else {"width": 0, "height": 0}
219
+ )
220
+
215
221
  @Bridge(result=dict)
216
222
  def getPosition(self):
217
223
  """Returns the position of the window."""
218
224
  window = self.app.get_window_by_id(self.window_id)
219
225
  return {"x": window.x, "y": window.y} if window else {"x": 0, "y": 0}
220
-
221
-
222
-
226
+
227
+ @Bridge()
228
+ def startSystemDrag(self):
229
+ """Starts the system drag."""
230
+ window = self.app.get_window_by_id(self.window_id)
231
+ if window:
232
+ window.web_view.start_system_drag()
233
+
223
234
 
224
235
  # class EventAPI(PylonAPI):
225
236
  # def __init__(self, window_id: str, app):
@@ -243,6 +254,44 @@ class WindowAPI(PyloidAPI):
243
254
  # callback(*args, **kwargs)
244
255
 
245
256
 
257
+ # 어차피 load 부분에만 쓰이니까 나중에 분리해서 load 위에서 선언하자.
258
+ class CustomWebEngineView(QWebEngineView):
259
+ def __init__(self, parent=None):
260
+ super().__init__(parent._window)
261
+ self.parent = parent
262
+ self.drag_relative_position = None
263
+ self.is_dragging = False
264
+
265
+ def mouse_press_event(self, event):
266
+ if event.button() == Qt.LeftButton:
267
+ self.drag_relative_position = event.pos()
268
+
269
+ def start_system_drag(self):
270
+ self.is_dragging = True
271
+
272
+ def mouse_move_event(self, event):
273
+ if not self.parent.frame and self.is_dragging:
274
+ # 현재 마우스 위치를 전역 좌표로 가져옵니다
275
+ current_global_pos = event.globalPos()
276
+ # 새로운 창 위치를 계산합니다
277
+ new_window_pos = current_global_pos - self.drag_relative_position
278
+ # 창을 새 위치로 이동합니다
279
+ self.parent._window.move(new_window_pos)
280
+
281
+ def mouse_release_event(self, event):
282
+ if event.button() == Qt.LeftButton:
283
+ self.is_dragging = False
284
+
285
+ def eventFilter(self, source, event):
286
+ if self.focusProxy() is source and event.type() == QEvent.MouseButtonPress:
287
+ self.mouse_press_event(event)
288
+ if self.focusProxy() is source and event.type() == QEvent.MouseMove:
289
+ self.mouse_move_event(event)
290
+ elif self.focusProxy() is source and event.type() == QEvent.MouseButtonRelease:
291
+ self.mouse_release_event(event)
292
+ return super().eventFilter(source, event)
293
+
294
+
246
295
  class BrowserWindow:
247
296
  def __init__(
248
297
  self,
@@ -259,9 +308,8 @@ class BrowserWindow:
259
308
  ):
260
309
  ###########################################################################################
261
310
  self.id = str(uuid.uuid4()) # Generate unique ID
262
-
263
311
  self._window = QMainWindow()
264
- self.web_view = QWebEngineView()
312
+ self.web_view = CustomWebEngineView(self)
265
313
 
266
314
  self._window.closeEvent = self.closeEvent # Override closeEvent method
267
315
  ###########################################################################################
@@ -280,10 +328,49 @@ class BrowserWindow:
280
328
  self.shortcuts = {}
281
329
  ###########################################################################################
282
330
 
331
+ def _set_custom_frame(
332
+ self,
333
+ use_custom: bool,
334
+ title: str = "Custom Title",
335
+ bg_color: str = "darkblue",
336
+ text_color: str = "white",
337
+ icon_path: str = None,
338
+ ):
339
+ """Sets or removes the custom frame."""
340
+ if use_custom:
341
+ self._window.setWindowFlags(Qt.FramelessWindowHint)
342
+ self.custom_title_bar = CustomTitleBar(self._window)
343
+ self.custom_title_bar.set_style(bg_color, text_color)
344
+ self.custom_title_bar.set_title(title)
345
+
346
+ if icon_path:
347
+ self.custom_title_bar.set_icon(icon_path)
348
+
349
+ layout = QVBoxLayout()
350
+ layout.setContentsMargins(0, 0, 0, 0)
351
+ layout.setSpacing(0)
352
+ layout.addWidget(self.custom_title_bar)
353
+ layout.addWidget(self.web_view)
354
+
355
+ central_widget = QWidget()
356
+ central_widget.setLayout(layout)
357
+ self._window.setCentralWidget(central_widget)
358
+
359
+ # Add properties for window movement
360
+ self._window.moving = False
361
+ self._window.offset = QPoint()
362
+ else:
363
+ self._window.setWindowFlags(Qt.Window)
364
+ self._window.setCentralWidget(self.web_view)
365
+ self.custom_title_bar = None
366
+
367
+ self._window.show()
368
+
283
369
  def _load(self):
284
- self._window.setWindowTitle(self.title)
370
+ self.set_title(self.title)
285
371
 
286
- self._window.setGeometry(self.x, self.y, self.width, self.height)
372
+ self.set_size(self.width, self.height)
373
+ self.set_position(self.x, self.y)
287
374
 
288
375
  # allow local file access to remote urls
289
376
  self.web_view.settings().setAttribute(
@@ -357,6 +444,12 @@ class BrowserWindow:
357
444
  console.log('pyloid.EventAPI object initialized:', window.pyloid.EventAPI);
358
445
 
359
446
  %s
447
+
448
+ document.addEventListener('mousedown', function (e) {
449
+ if (e.target.hasAttribute('data-pyloid-drag-region')) {
450
+ window.pyloid.WindowAPI.startSystemDrag();
451
+ }
452
+ });
360
453
 
361
454
  // Dispatch a custom event to signal that the initialization is ready
362
455
  const event = new CustomEvent('pyloidReady');
@@ -385,11 +478,13 @@ class BrowserWindow:
385
478
  self._load()
386
479
  file_path = os.path.abspath(file_path) # absolute path
387
480
  self.web_view.setUrl(QUrl.fromLocalFile(file_path))
481
+ self.web_view.focusProxy().installEventFilter(self.web_view)
388
482
 
389
483
  def load_url(self, url):
390
484
  """Sets the URL of the window."""
391
485
  self._load()
392
486
  self.web_view.setUrl(QUrl(url))
487
+ self.web_view.focusProxy().installEventFilter(self.web_view)
393
488
 
394
489
  ###########################################################################################
395
490
  # Set Parameters
@@ -602,6 +697,7 @@ class BrowserWindow:
602
697
  }})();
603
698
  """
604
699
  self.web_view.page().runJavaScript(script)
700
+
605
701
  ###########################################################################################
606
702
  # Get Properties
607
703
  ###########################################################################################
@@ -622,7 +718,7 @@ class BrowserWindow:
622
718
  def get_id(self):
623
719
  """Returns the ID of the window."""
624
720
  return self.id
625
-
721
+
626
722
  def get_size(self) -> Dict[str, int]:
627
723
  """Returns the size of the window."""
628
724
  return {"width": self.width, "height": self.height}
@@ -630,7 +726,7 @@ class BrowserWindow:
630
726
  def get_position(self) -> Dict[str, int]:
631
727
  """Returns the position of the window."""
632
728
  return {"x": self.x, "y": self.y}
633
-
729
+
634
730
  def get_title(self) -> str:
635
731
  """Returns the title of the window."""
636
732
  return self.title
@@ -638,13 +734,23 @@ class BrowserWindow:
638
734
  def get_url(self) -> str:
639
735
  """Returns the URL of the window."""
640
736
  return self.web_view.url().toString()
641
-
737
+
642
738
  def get_visible(self) -> bool:
643
739
  """Returns the visibility of the window."""
644
740
  return self._window.isVisible()
645
741
 
646
-
647
-
742
+ def set_resizable(self, resizable: bool):
743
+ """창의 크기 조절 가능 여부를 설정합니다."""
744
+ self.resizable = resizable
745
+ if resizable:
746
+ self._window.setWindowFlags(
747
+ self._window.windowFlags() & ~Qt.MSWindowsFixedSizeDialogHint
748
+ )
749
+ else:
750
+ self._window.setWindowFlags(
751
+ self._window.windowFlags() | Qt.MSWindowsFixedSizeDialogHint
752
+ )
753
+ self._window.show() # 변경사항을 적용하기 위해 창을 다시 표시합니다.
648
754
 
649
755
 
650
756
  class _WindowController(QObject):
@@ -683,7 +789,7 @@ class Pyloid(QApplication):
683
789
 
684
790
  self.app_name = app_name
685
791
  self.app_path = sys.executable
686
-
792
+
687
793
  self.auto_start = AutoStart(self.app_name, self.app_path)
688
794
 
689
795
  self.animation_timer = None
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes