pyloid 0.11.5__py3-none-any.whl → 0.12.0__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.
@@ -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)
pyloid/pyloid.py CHANGED
@@ -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, QPoint, QUrl, QObject, QTimer, QSize
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,9 +30,11 @@ import json
32
30
  from .autostart import AutoStart
33
31
  from .filewatcher import FileWatcher
34
32
  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
33
+ from PySide6.QtWidgets import (
34
+ QWidget,
35
+ QVBoxLayout,
36
+ )
37
+ from .custom.titlebar import CustomTitleBar
38
38
 
39
39
  # for linux debug
40
40
  os.environ["QTWEBENGINE_DICTIONARIES_PATH"] = "/"
@@ -189,39 +189,48 @@ class WindowAPI(PyloidAPI):
189
189
  """Returns whether the window has a frame."""
190
190
  window = self.app.get_window_by_id(self.window_id)
191
191
  return window.frame if window else False
192
-
192
+
193
193
  @Bridge(result=bool)
194
194
  def getContextMenu(self):
195
195
  """Returns whether the window has a context menu."""
196
196
  window = self.app.get_window_by_id(self.window_id)
197
197
  return window.context_menu if window else False
198
-
198
+
199
199
  @Bridge(result=bool)
200
200
  def getDevTools(self):
201
201
  """Returns whether the window has developer tools."""
202
202
  window = self.app.get_window_by_id(self.window_id)
203
203
  return window.dev_tools if window else False
204
-
204
+
205
205
  @Bridge(result=str)
206
206
  def getTitle(self):
207
207
  """Returns the title of the window."""
208
208
  window = self.app.get_window_by_id(self.window_id)
209
209
  return window.title if window else ""
210
-
210
+
211
211
  @Bridge(result=dict)
212
212
  def getSize(self):
213
213
  """Returns the size of the window."""
214
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
-
215
+ return (
216
+ {"width": window.width, "height": window.height}
217
+ if window
218
+ else {"width": 0, "height": 0}
219
+ )
220
+
217
221
  @Bridge(result=dict)
218
222
  def getPosition(self):
219
223
  """Returns the position of the window."""
220
224
  window = self.app.get_window_by_id(self.window_id)
221
225
  return {"x": window.x, "y": window.y} if window else {"x": 0, "y": 0}
222
-
223
-
224
-
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
+
225
234
 
226
235
  # class EventAPI(PylonAPI):
227
236
  # def __init__(self, window_id: str, app):
@@ -245,105 +254,43 @@ class WindowAPI(PyloidAPI):
245
254
  # callback(*args, **kwargs)
246
255
 
247
256
 
248
- class CustomTitleBar(QWidget):
257
+ # 어차피 load 부분에만 쓰이니까 나중에 분리해서 load 위에서 선언하자.
258
+ class CustomWebEngineView(QWebEngineView):
249
259
  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):
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):
321
266
  if event.button() == Qt.LeftButton:
322
- self.window().moving = True
323
- self.window().offset = event.pos()
267
+ self.drag_relative_position = event.pos()
324
268
 
325
- def mouseMoveEvent(self, event):
326
- if self.window().moving:
327
- self.window().move(event.globalPos() - self.window().offset)
269
+ def start_system_drag(self):
270
+ self.is_dragging = True
328
271
 
329
- def mouseReleaseEvent(self, event):
330
- if event.button() == Qt.LeftButton:
331
- self.window().moving = False
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)
332
280
 
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("❐")
281
+ def mouse_release_event(self, event):
282
+ if event.button() == Qt.LeftButton:
283
+ self.is_dragging = False
340
284
 
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))
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)
344
293
 
345
- def set_title(self, title):
346
- self.title.setText(title)
347
294
 
348
295
  class BrowserWindow:
349
296
  def __init__(
@@ -361,9 +308,8 @@ class BrowserWindow:
361
308
  ):
362
309
  ###########################################################################################
363
310
  self.id = str(uuid.uuid4()) # Generate unique ID
364
-
365
311
  self._window = QMainWindow()
366
- self.web_view = QWebEngineView()
312
+ self.web_view = CustomWebEngineView(self)
367
313
 
368
314
  self._window.closeEvent = self.closeEvent # Override closeEvent method
369
315
  ###########################################################################################
@@ -382,28 +328,35 @@ class BrowserWindow:
382
328
  self.shortcuts = {}
383
329
  ###########################################################################################
384
330
 
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
- """커스텀 프레임을 설정하거나 제거합니다."""
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."""
387
340
  if use_custom:
388
341
  self._window.setWindowFlags(Qt.FramelessWindowHint)
389
342
  self.custom_title_bar = CustomTitleBar(self._window)
390
343
  self.custom_title_bar.set_style(bg_color, text_color)
391
344
  self.custom_title_bar.set_title(title)
392
-
345
+
393
346
  if icon_path:
394
347
  self.custom_title_bar.set_icon(icon_path)
395
-
348
+
396
349
  layout = QVBoxLayout()
397
350
  layout.setContentsMargins(0, 0, 0, 0)
398
351
  layout.setSpacing(0)
399
352
  layout.addWidget(self.custom_title_bar)
400
353
  layout.addWidget(self.web_view)
401
-
354
+
402
355
  central_widget = QWidget()
403
356
  central_widget.setLayout(layout)
404
357
  self._window.setCentralWidget(central_widget)
405
358
 
406
- # 이동을 위한 속성 추가
359
+ # Add properties for window movement
407
360
  self._window.moving = False
408
361
  self._window.offset = QPoint()
409
362
  else:
@@ -414,9 +367,10 @@ class BrowserWindow:
414
367
  self._window.show()
415
368
 
416
369
  def _load(self):
417
- self._window.setWindowTitle(self.title)
370
+ self.set_title(self.title)
418
371
 
419
- 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)
420
374
 
421
375
  # allow local file access to remote urls
422
376
  self.web_view.settings().setAttribute(
@@ -490,6 +444,12 @@ class BrowserWindow:
490
444
  console.log('pyloid.EventAPI object initialized:', window.pyloid.EventAPI);
491
445
 
492
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
+ });
493
453
 
494
454
  // Dispatch a custom event to signal that the initialization is ready
495
455
  const event = new CustomEvent('pyloidReady');
@@ -518,11 +478,13 @@ class BrowserWindow:
518
478
  self._load()
519
479
  file_path = os.path.abspath(file_path) # absolute path
520
480
  self.web_view.setUrl(QUrl.fromLocalFile(file_path))
481
+ self.web_view.focusProxy().installEventFilter(self.web_view)
521
482
 
522
483
  def load_url(self, url):
523
484
  """Sets the URL of the window."""
524
485
  self._load()
525
486
  self.web_view.setUrl(QUrl(url))
487
+ self.web_view.focusProxy().installEventFilter(self.web_view)
526
488
 
527
489
  ###########################################################################################
528
490
  # Set Parameters
@@ -735,6 +697,7 @@ class BrowserWindow:
735
697
  }})();
736
698
  """
737
699
  self.web_view.page().runJavaScript(script)
700
+
738
701
  ###########################################################################################
739
702
  # Get Properties
740
703
  ###########################################################################################
@@ -755,7 +718,7 @@ class BrowserWindow:
755
718
  def get_id(self):
756
719
  """Returns the ID of the window."""
757
720
  return self.id
758
-
721
+
759
722
  def get_size(self) -> Dict[str, int]:
760
723
  """Returns the size of the window."""
761
724
  return {"width": self.width, "height": self.height}
@@ -763,7 +726,7 @@ class BrowserWindow:
763
726
  def get_position(self) -> Dict[str, int]:
764
727
  """Returns the position of the window."""
765
728
  return {"x": self.x, "y": self.y}
766
-
729
+
767
730
  def get_title(self) -> str:
768
731
  """Returns the title of the window."""
769
732
  return self.title
@@ -771,7 +734,7 @@ class BrowserWindow:
771
734
  def get_url(self) -> str:
772
735
  """Returns the URL of the window."""
773
736
  return self.web_view.url().toString()
774
-
737
+
775
738
  def get_visible(self) -> bool:
776
739
  """Returns the visibility of the window."""
777
740
  return self._window.isVisible()
@@ -780,11 +743,15 @@ class BrowserWindow:
780
743
  """창의 크기 조절 가능 여부를 설정합니다."""
781
744
  self.resizable = resizable
782
745
  if resizable:
783
- self._window.setWindowFlags(self._window.windowFlags() & ~Qt.MSWindowsFixedSizeDialogHint)
746
+ self._window.setWindowFlags(
747
+ self._window.windowFlags() & ~Qt.MSWindowsFixedSizeDialogHint
748
+ )
784
749
  else:
785
- self._window.setWindowFlags(self._window.windowFlags() | Qt.MSWindowsFixedSizeDialogHint)
750
+ self._window.setWindowFlags(
751
+ self._window.windowFlags() | Qt.MSWindowsFixedSizeDialogHint
752
+ )
786
753
  self._window.show() # 변경사항을 적용하기 위해 창을 다시 표시합니다.
787
-
754
+
788
755
 
789
756
  class _WindowController(QObject):
790
757
  create_window_signal = Signal(
@@ -822,7 +789,7 @@ class Pyloid(QApplication):
822
789
 
823
790
  self.app_name = app_name
824
791
  self.app_path = sys.executable
825
-
792
+
826
793
  self.auto_start = AutoStart(self.app_name, self.app_path)
827
794
 
828
795
  self.animation_timer = None
@@ -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.0
4
4
  Summary:
5
5
  Author: aesthetics-of-record
6
6
  Author-email: 111675679+aesthetics-of-record@users.noreply.github.com
@@ -1,13 +1,14 @@
1
1
  pyloid/__init__.py,sha256=OOPhOKNQVmAM8hnfTeE7lHzxb8LsFNcgegBAvDrA-vY,293
2
2
  pyloid/api.py,sha256=whgfvPr1A6iwZ1Ewo-0FnOUNnt1K58c-P7YjzuQHcUM,194
3
3
  pyloid/autostart.py,sha256=K7DQYl4LHItvPp0bt1V9WwaaZmVSTeGvadkcwG-KKrI,3899
4
+ pyloid/custom/titlebar.py,sha256=itzK9pJbZMQ7BKca9kdbuHMffurrw15UijR6OU03Xsk,3894
4
5
  pyloid/filewatcher.py,sha256=n8N56D65le5TpsgxXb7z-FO_0lqv4UYD4yGq_UuMrAs,1285
5
6
  pyloid/monitor.py,sha256=fqDnZ_7dpxVZLVJ5gCluDRY2USrQ5YL_fw1AnYivhsk,12741
6
- pyloid/pyloid.py,sha256=JMvWDd-3IA9wcpZSlzdMBIPK8F7v1O_1o8N58LkBlO4,49556
7
+ pyloid/pyloid.py,sha256=MGfBrB6uIrFpKWkoznu52i7sd4WDRTSorqak3zzzsus,48199
7
8
  pyloid/timer.py,sha256=1bYhqte3rV77vaeMUkcTgmx2ux7FtCqLCx9lIC2-COg,4360
8
9
  pyloid/tray.py,sha256=rXgdkvzGxtie_EIcTSA7fjuta4nJk5THhNkGFcfv5Ew,634
9
10
  pyloid/utils.py,sha256=DQerZWU_0o8dHcJ5y3yXf9i5OXn7KQZqU-hVBq3uPUA,711
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,,
11
+ pyloid-0.12.0.dist-info/LICENSE,sha256=MTYF-6xpRekyTUglRweWtbfbwBL1I_3Bgfbm_SNOuI8,11525
12
+ pyloid-0.12.0.dist-info/METADATA,sha256=3nNffa8kFieZFpiJjE5nXHq9XA27JtvpSbBAWKBRndE,6069
13
+ pyloid-0.12.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
14
+ pyloid-0.12.0.dist-info/RECORD,,