pyloid 0.11.5__tar.gz → 0.12.1__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.5
3
+ Version: 0.12.1
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.5"
3
+ version = "0.12.1"
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,9 @@ from PySide6.QtGui import (
15
15
  QClipboard,
16
16
  QImage,
17
17
  QAction,
18
- QPalette,
19
- QColor,
18
+ QCursor,
20
19
  )
21
- from PySide6.QtCore import Qt, Signal, QPoint, QUrl, QObject, QTimer, QSize
20
+ from PySide6.QtCore import Qt, Signal, QPoint, QUrl, QObject, QTimer, QSize, QEvent
22
21
  from PySide6.QtNetwork import QLocalServer, QLocalSocket
23
22
  from PySide6.QtWebEngineCore import QWebEnginePage, QWebEngineSettings
24
23
  from .api import PyloidAPI, Bridge
@@ -32,9 +31,11 @@ import json
32
31
  from .autostart import AutoStart
33
32
  from .filewatcher import FileWatcher
34
33
  import logging
35
- from PySide6.QtGui import QPalette, QColor
36
- from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QHBoxLayout, QSizePolicy
37
- from PySide6.QtGui import QPixmap
34
+ from PySide6.QtWidgets import (
35
+ QWidget,
36
+ QVBoxLayout,
37
+ )
38
+ from .custom.titlebar import CustomTitleBar
38
39
 
39
40
  # for linux debug
40
41
  os.environ["QTWEBENGINE_DICTIONARIES_PATH"] = "/"
@@ -189,39 +190,48 @@ class WindowAPI(PyloidAPI):
189
190
  """Returns whether the window has a frame."""
190
191
  window = self.app.get_window_by_id(self.window_id)
191
192
  return window.frame if window else False
192
-
193
+
193
194
  @Bridge(result=bool)
194
195
  def getContextMenu(self):
195
196
  """Returns whether the window has a context menu."""
196
197
  window = self.app.get_window_by_id(self.window_id)
197
198
  return window.context_menu if window else False
198
-
199
+
199
200
  @Bridge(result=bool)
200
201
  def getDevTools(self):
201
202
  """Returns whether the window has developer tools."""
202
203
  window = self.app.get_window_by_id(self.window_id)
203
204
  return window.dev_tools if window else False
204
-
205
+
205
206
  @Bridge(result=str)
206
207
  def getTitle(self):
207
208
  """Returns the title of the window."""
208
209
  window = self.app.get_window_by_id(self.window_id)
209
210
  return window.title if window else ""
210
-
211
+
211
212
  @Bridge(result=dict)
212
213
  def getSize(self):
213
214
  """Returns the size of the window."""
214
215
  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
-
216
+ return (
217
+ {"width": window.width, "height": window.height}
218
+ if window
219
+ else {"width": 0, "height": 0}
220
+ )
221
+
217
222
  @Bridge(result=dict)
218
223
  def getPosition(self):
219
224
  """Returns the position of the window."""
220
225
  window = self.app.get_window_by_id(self.window_id)
221
226
  return {"x": window.x, "y": window.y} if window else {"x": 0, "y": 0}
222
-
223
-
224
-
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
+
225
235
 
226
236
  # class EventAPI(PylonAPI):
227
237
  # def __init__(self, window_id: str, app):
@@ -245,105 +255,62 @@ class WindowAPI(PyloidAPI):
245
255
  # callback(*args, **kwargs)
246
256
 
247
257
 
248
- class CustomTitleBar(QWidget):
258
+ # 어차피 load 부분에만 쓰이니까 나중에 분리해서 load 위에서 선언하자.
259
+ class CustomWebEngineView(QWebEngineView):
249
260
  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):
261
+ super().__init__(parent._window)
262
+ self.parent = parent
263
+ self.drag_relative_position = None
264
+ self.is_dragging = False
265
+ self.screen_geometry = self.screen().availableGeometry()
266
+
267
+ def mouse_press_event(self, event):
321
268
  if event.button() == Qt.LeftButton:
322
- self.window().moving = True
323
- self.window().offset = event.pos()
269
+ self.drag_relative_position = event.pos()
270
+
271
+ def start_system_drag(self):
272
+ self.is_dragging = True
273
+
274
+ def mouse_move_event(self, event):
275
+ if not self.parent.frame and self.is_dragging:
276
+ # 현재 마우스 위치를 전역 좌표로 가져옵니다
277
+ current_global_pos = event.globalPos()
278
+
279
+ # 화면 경계를 계산합니다
280
+ left_boundary = self.screen_geometry.left()
281
+ right_boundary = self.screen_geometry.right()
282
+ top_boundary = self.screen_geometry.top()
283
+ bottom_boundary = self.screen_geometry.bottom()
284
+
285
+ # 마우스 커서 위치를 제한합니다
286
+ new_cursor_pos = QPoint(
287
+ max(left_boundary, min(current_global_pos.x(), right_boundary)),
288
+ max(top_boundary, min(current_global_pos.y(), bottom_boundary)),
289
+ )
324
290
 
325
- def mouseMoveEvent(self, event):
326
- if self.window().moving:
327
- self.window().move(event.globalPos() - self.window().offset)
291
+ # 마우스 커서를 새 위치로 이동합니다
292
+ QCursor.setPos(new_cursor_pos)
328
293
 
329
- def mouseReleaseEvent(self, event):
330
- if event.button() == Qt.LeftButton:
331
- self.window().moving = False
294
+ # 창의 새 위치를 계산합니다
295
+ new_window_pos = new_cursor_pos - self.drag_relative_position
332
296
 
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("❐")
297
+ # 창을 새 위치로 이동합니다
298
+ self.parent._window.move(new_window_pos)
340
299
 
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))
300
+ def mouse_release_event(self, event):
301
+ if event.button() == Qt.LeftButton:
302
+ self.is_dragging = False
303
+
304
+ def eventFilter(self, source, event):
305
+ if self.focusProxy() is source:
306
+ if event.type() == QEvent.MouseButtonPress:
307
+ self.mouse_press_event(event)
308
+ elif event.type() == QEvent.MouseMove:
309
+ self.mouse_move_event(event)
310
+ elif event.type() == QEvent.MouseButtonRelease:
311
+ self.mouse_release_event(event)
312
+ return super().eventFilter(source, event)
344
313
 
345
- def set_title(self, title):
346
- self.title.setText(title)
347
314
 
348
315
  class BrowserWindow:
349
316
  def __init__(
@@ -361,9 +328,8 @@ class BrowserWindow:
361
328
  ):
362
329
  ###########################################################################################
363
330
  self.id = str(uuid.uuid4()) # Generate unique ID
364
-
365
331
  self._window = QMainWindow()
366
- self.web_view = QWebEngineView()
332
+ self.web_view = CustomWebEngineView(self)
367
333
 
368
334
  self._window.closeEvent = self.closeEvent # Override closeEvent method
369
335
  ###########################################################################################
@@ -382,28 +348,35 @@ class BrowserWindow:
382
348
  self.shortcuts = {}
383
349
  ###########################################################################################
384
350
 
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
- """커스텀 프레임을 설정하거나 제거합니다."""
351
+ def _set_custom_frame(
352
+ self,
353
+ use_custom: bool,
354
+ title: str = "Custom Title",
355
+ bg_color: str = "darkblue",
356
+ text_color: str = "white",
357
+ icon_path: str = None,
358
+ ):
359
+ """Sets or removes the custom frame."""
387
360
  if use_custom:
388
361
  self._window.setWindowFlags(Qt.FramelessWindowHint)
389
362
  self.custom_title_bar = CustomTitleBar(self._window)
390
363
  self.custom_title_bar.set_style(bg_color, text_color)
391
364
  self.custom_title_bar.set_title(title)
392
-
365
+
393
366
  if icon_path:
394
367
  self.custom_title_bar.set_icon(icon_path)
395
-
368
+
396
369
  layout = QVBoxLayout()
397
370
  layout.setContentsMargins(0, 0, 0, 0)
398
371
  layout.setSpacing(0)
399
372
  layout.addWidget(self.custom_title_bar)
400
373
  layout.addWidget(self.web_view)
401
-
374
+
402
375
  central_widget = QWidget()
403
376
  central_widget.setLayout(layout)
404
377
  self._window.setCentralWidget(central_widget)
405
378
 
406
- # 이동을 위한 속성 추가
379
+ # Add properties for window movement
407
380
  self._window.moving = False
408
381
  self._window.offset = QPoint()
409
382
  else:
@@ -414,9 +387,10 @@ class BrowserWindow:
414
387
  self._window.show()
415
388
 
416
389
  def _load(self):
417
- self._window.setWindowTitle(self.title)
390
+ self.set_title(self.title)
418
391
 
419
- self._window.setGeometry(self.x, self.y, self.width, self.height)
392
+ self.set_size(self.width, self.height)
393
+ self.set_position(self.x, self.y)
420
394
 
421
395
  # allow local file access to remote urls
422
396
  self.web_view.settings().setAttribute(
@@ -490,6 +464,12 @@ class BrowserWindow:
490
464
  console.log('pyloid.EventAPI object initialized:', window.pyloid.EventAPI);
491
465
 
492
466
  %s
467
+
468
+ document.addEventListener('mousedown', function (e) {
469
+ if (e.target.hasAttribute('data-pyloid-drag-region')) {
470
+ window.pyloid.WindowAPI.startSystemDrag();
471
+ }
472
+ });
493
473
 
494
474
  // Dispatch a custom event to signal that the initialization is ready
495
475
  const event = new CustomEvent('pyloidReady');
@@ -518,11 +498,13 @@ class BrowserWindow:
518
498
  self._load()
519
499
  file_path = os.path.abspath(file_path) # absolute path
520
500
  self.web_view.setUrl(QUrl.fromLocalFile(file_path))
501
+ self.web_view.focusProxy().installEventFilter(self.web_view)
521
502
 
522
503
  def load_url(self, url):
523
504
  """Sets the URL of the window."""
524
505
  self._load()
525
506
  self.web_view.setUrl(QUrl(url))
507
+ self.web_view.focusProxy().installEventFilter(self.web_view)
526
508
 
527
509
  ###########################################################################################
528
510
  # Set Parameters
@@ -735,6 +717,7 @@ class BrowserWindow:
735
717
  }})();
736
718
  """
737
719
  self.web_view.page().runJavaScript(script)
720
+
738
721
  ###########################################################################################
739
722
  # Get Properties
740
723
  ###########################################################################################
@@ -755,7 +738,7 @@ class BrowserWindow:
755
738
  def get_id(self):
756
739
  """Returns the ID of the window."""
757
740
  return self.id
758
-
741
+
759
742
  def get_size(self) -> Dict[str, int]:
760
743
  """Returns the size of the window."""
761
744
  return {"width": self.width, "height": self.height}
@@ -763,7 +746,7 @@ class BrowserWindow:
763
746
  def get_position(self) -> Dict[str, int]:
764
747
  """Returns the position of the window."""
765
748
  return {"x": self.x, "y": self.y}
766
-
749
+
767
750
  def get_title(self) -> str:
768
751
  """Returns the title of the window."""
769
752
  return self.title
@@ -771,7 +754,7 @@ class BrowserWindow:
771
754
  def get_url(self) -> str:
772
755
  """Returns the URL of the window."""
773
756
  return self.web_view.url().toString()
774
-
757
+
775
758
  def get_visible(self) -> bool:
776
759
  """Returns the visibility of the window."""
777
760
  return self._window.isVisible()
@@ -780,11 +763,15 @@ class BrowserWindow:
780
763
  """창의 크기 조절 가능 여부를 설정합니다."""
781
764
  self.resizable = resizable
782
765
  if resizable:
783
- self._window.setWindowFlags(self._window.windowFlags() & ~Qt.MSWindowsFixedSizeDialogHint)
766
+ self._window.setWindowFlags(
767
+ self._window.windowFlags() & ~Qt.MSWindowsFixedSizeDialogHint
768
+ )
784
769
  else:
785
- self._window.setWindowFlags(self._window.windowFlags() | Qt.MSWindowsFixedSizeDialogHint)
770
+ self._window.setWindowFlags(
771
+ self._window.windowFlags() | Qt.MSWindowsFixedSizeDialogHint
772
+ )
786
773
  self._window.show() # 변경사항을 적용하기 위해 창을 다시 표시합니다.
787
-
774
+
788
775
 
789
776
  class _WindowController(QObject):
790
777
  create_window_signal = Signal(
@@ -822,7 +809,7 @@ class Pyloid(QApplication):
822
809
 
823
810
  self.app_name = app_name
824
811
  self.app_path = sys.executable
825
-
812
+
826
813
  self.auto_start = AutoStart(self.app_name, self.app_path)
827
814
 
828
815
  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