pyloid 0.22.0.dev6__py3-none-any.whl → 0.23.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.
pyloid/pyloid.py CHANGED
@@ -30,7 +30,12 @@ from PySide6.QtCore import Signal, QObject, Slot
30
30
  import uuid
31
31
  from PySide6.QtCore import QEventLoop
32
32
  import socket
33
- from typing import Any
33
+ from typing import Any, Set
34
+ from platformdirs import PlatformDirs
35
+ from .store import Store
36
+ from .rpc import PyloidRPC
37
+ import threading
38
+ import asyncio
34
39
 
35
40
  # software backend
36
41
  os.environ["QT_QUICK_BACKEND"] = "software"
@@ -74,11 +79,10 @@ qInstallMessageHandler(custom_message_handler)
74
79
 
75
80
  class _WindowController(QObject):
76
81
  create_window_signal = Signal(
77
- QApplication, str, int, int, int, int, bool, bool, bool, list
82
+ QApplication, str, int, int, int, int, bool, bool, bool, PyloidRPC
78
83
  )
79
84
 
80
85
 
81
-
82
86
  # Only Work in Main Thread
83
87
  class _Pyloid(QApplication):
84
88
  def __init__(
@@ -111,10 +115,10 @@ class _Pyloid(QApplication):
111
115
  ```
112
116
  """
113
117
  super().__init__(sys.argv)
114
-
118
+
115
119
  self.data = data
116
120
 
117
- self.windows_dict = {} # 윈도우 ID를 키로 사용하는 딕셔너리
121
+ self.windows_dict: Dict[str, BrowserWindow] = {} # 윈도우 ID를 키로 사용하는 딕셔너리
118
122
  self.server = None
119
123
 
120
124
  self.app_name = app_name
@@ -155,6 +159,8 @@ class _Pyloid(QApplication):
155
159
  # Add color scheme tracking
156
160
  self.styleHints().colorSchemeChanged.connect(self._handle_color_scheme_change)
157
161
 
162
+ self.dirs = PlatformDirs(self.app_name, appauthor=False)
163
+
158
164
  # def set_theme(self, theme: Literal["system", "dark", "light"]):
159
165
  # """
160
166
  # 시스템의 테마를 설정합니다.
@@ -224,7 +230,7 @@ class _Pyloid(QApplication):
224
230
  frame: bool = True,
225
231
  context_menu: bool = False,
226
232
  dev_tools: bool = False,
227
- js_apis: List[PyloidAPI] = [],
233
+ rpc: Optional[PyloidRPC] = None,
228
234
  ) -> BrowserWindow:
229
235
  """
230
236
  Creates a new browser window.
@@ -247,8 +253,8 @@ class _Pyloid(QApplication):
247
253
  Whether to use the context menu (default is False)
248
254
  dev_tools : bool, optional
249
255
  Whether to use developer tools (default is False)
250
- js_apis : list of PyloidAPI, optional
251
- List of JavaScript APIs to add to the window (default is an empty list)
256
+ rpc : PyloidRPC, optional
257
+ The RPC server instance to be used in the window
252
258
 
253
259
  Returns
254
260
  -------
@@ -271,7 +277,7 @@ class _Pyloid(QApplication):
271
277
  frame,
272
278
  context_menu,
273
279
  dev_tools,
274
- js_apis,
280
+ rpc,
275
281
  )
276
282
  latest_window_id = list(self.windows_dict.keys())[-1]
277
283
  return self.windows_dict[latest_window_id]
@@ -287,7 +293,8 @@ class _Pyloid(QApplication):
287
293
  frame: bool,
288
294
  context_menu: bool,
289
295
  dev_tools: bool,
290
- js_apis: List[PyloidAPI] = [],
296
+ # js_apis: List[PyloidAPI] = [],
297
+ rpc: Optional[PyloidRPC] = None,
291
298
  ) -> BrowserWindow:
292
299
  """Function to create a new browser window."""
293
300
  window = BrowserWindow(
@@ -300,7 +307,7 @@ class _Pyloid(QApplication):
300
307
  frame,
301
308
  context_menu,
302
309
  dev_tools,
303
- js_apis,
310
+ rpc,
304
311
  )
305
312
  self.windows_dict[window._window.id] = window
306
313
  return window
@@ -320,6 +327,19 @@ class _Pyloid(QApplication):
320
327
  app.run()
321
328
  ```
322
329
  """
330
+
331
+ # Collect and deduplicate RPC servers
332
+ rpc_servers: Set[PyloidRPC] = set()
333
+ for window in self.windows_dict.values():
334
+ if window._window.rpc is not None:
335
+ rpc_servers.add(window._window.rpc)
336
+
337
+ # 고유한 RPC 서버만 시작
338
+ for rpc in rpc_servers:
339
+ server_thread = threading.Thread(target=rpc.start, daemon=True)
340
+ server_thread.start()
341
+
342
+
323
343
  if is_production():
324
344
  sys.exit(self.exec())
325
345
  else:
@@ -348,20 +368,20 @@ class _Pyloid(QApplication):
348
368
  ###########################################################################################
349
369
  def get_windows(self) -> Dict[str, BrowserWindow]:
350
370
  """
351
- Returns a list of all browser windows.
371
+ Returns a dictionary of all browser windows.
352
372
 
353
373
  Returns
354
374
  -------
355
- List[BrowserWindow]
356
- List of all browser windows
375
+ Dict[str, BrowserWindow]
376
+ Dictionary with window IDs as keys and BrowserWindow objects as values
357
377
 
358
378
  Examples
359
379
  --------
360
380
  ```python
361
381
  app = Pyloid(app_name="Pyloid-App")
362
382
  windows = app.get_windows()
363
- for window in windows:
364
- print(window.get_id())
383
+ for window_id, window in windows.items():
384
+ print(f"Window ID: {window_id}")
365
385
  ```
366
386
  """
367
387
  return self.windows_dict
@@ -396,12 +416,18 @@ class _Pyloid(QApplication):
396
416
  if self.windows_dict:
397
417
  # 첫 번째 윈도우 가져오기
398
418
  main_window = next(iter(self.windows_dict.values()))
399
- main_window._window.activateWindow()
400
- main_window._window.raise_()
401
- main_window._window.setWindowState(
402
- main_window._window.windowState() & ~Qt.WindowMinimized
403
- | Qt.WindowActive
419
+ was_on_top = bool(
420
+ main_window._window._window.windowFlags() & Qt.WindowStaysOnTopHint
404
421
  )
422
+ if not was_on_top:
423
+ main_window._window._window.setWindowFlag(Qt.WindowStaysOnTopHint, True)
424
+ main_window._window._window.show()
425
+ main_window._window.activateWindow()
426
+ if not was_on_top:
427
+ main_window._window._window.setWindowFlag(
428
+ Qt.WindowStaysOnTopHint, False
429
+ )
430
+ main_window._window._window.show()
405
431
 
406
432
  def show_and_focus_main_window(self):
407
433
  """
@@ -417,12 +443,19 @@ class _Pyloid(QApplication):
417
443
  if self.windows_dict:
418
444
  main_window = next(iter(self.windows_dict.values()))
419
445
  main_window._window.show()
420
- main_window._window.activateWindow()
421
- main_window._window.raise_()
422
- main_window._window.setWindowState(
423
- main_window._window.windowState() & ~Qt.WindowMinimized
424
- | Qt.WindowActive
446
+
447
+ was_on_top = bool(
448
+ main_window._window._window.windowFlags() & Qt.WindowStaysOnTopHint
425
449
  )
450
+ if not was_on_top:
451
+ main_window._window._window.setWindowFlag(Qt.WindowStaysOnTopHint, True)
452
+ main_window._window._window.show()
453
+ main_window._window._window.activateWindow()
454
+ if not was_on_top:
455
+ main_window._window._window.setWindowFlag(
456
+ Qt.WindowStaysOnTopHint, False
457
+ )
458
+ main_window._window._window.show()
426
459
 
427
460
  def close_all_windows(self):
428
461
  """
@@ -449,7 +482,7 @@ class _Pyloid(QApplication):
449
482
  app.quit()
450
483
  ```
451
484
  """
452
-
485
+
453
486
  for window in self.windows_dict.values():
454
487
  window._window.close()
455
488
  window.web_page.deleteLater()
@@ -464,7 +497,7 @@ class _Pyloid(QApplication):
464
497
  def get_window_by_id(self, window_id: str) -> Optional[BrowserWindow]:
465
498
  """
466
499
  Returns the window with the given ID.
467
-
500
+
468
501
  Parameters
469
502
  ----------
470
503
  window_id : str
@@ -474,14 +507,14 @@ class _Pyloid(QApplication):
474
507
  -------
475
508
  Optional[BrowserWindow]
476
509
  The window object with the given ID. Returns None if the window is not found.
477
-
510
+
478
511
  Examples
479
512
  --------
480
513
  ```python
481
514
  app = Pyloid(app_name="Pyloid-App")
482
-
515
+
483
516
  window = app.get_window_by_id("123e4567-e89b-12d3-a456-426614174000")
484
-
517
+
485
518
  if window:
486
519
  print("Window found:", window)
487
520
  ```
@@ -1409,13 +1442,220 @@ class _Pyloid(QApplication):
1409
1442
  window.web_view.page().setBackgroundColor(
1410
1443
  Qt.GlobalColor.black if self.theme == "dark" else Qt.GlobalColor.white
1411
1444
  )
1412
-
1445
+
1446
+ ###########################################################################################
1447
+ # platformdirs
1448
+ ###########################################################################################
1449
+
1450
+ def user_data_dir(self) -> str:
1451
+ """
1452
+ Returns the user data directory path.
1453
+
1454
+ Returns
1455
+ -------
1456
+ str
1457
+ User data directory path
1458
+
1459
+ Examples
1460
+ --------
1461
+ >>> app = Pyloid(app_name="Pyloid-App")
1462
+ >>> data_dir = app.user_data_dir()
1463
+ >>> print(data_dir)
1464
+ '/Users/user/Library/Application Support/Pyloid-App' # Example for macOS
1465
+ """
1466
+ return self.dirs.user_data_dir
1467
+
1468
+ def site_data_dir(self) -> str:
1469
+ """
1470
+ Returns the site data directory path.
1471
+
1472
+ Returns
1473
+ -------
1474
+ str
1475
+ Site data directory path
1476
+
1477
+ Examples
1478
+ --------
1479
+ >>> app = Pyloid(app_name="Pyloid-App")
1480
+ >>> site_data_dir = app.site_data_dir()
1481
+ >>> print(site_data_dir)
1482
+ '/Library/Application Support/Pyloid-App' # Example for macOS
1483
+ """
1484
+ return self.dirs.site_data_dir
1485
+
1486
+ def user_cache_dir(self) -> str:
1487
+ """
1488
+ Returns the user cache directory path.
1489
+
1490
+ Returns
1491
+ -------
1492
+ str
1493
+ User cache directory path
1494
+
1495
+ Examples
1496
+ --------
1497
+ >>> app = Pyloid(app_name="Pyloid-App")
1498
+ >>> cache_dir = app.user_cache_dir()
1499
+ >>> print(cache_dir)
1500
+ '/Users/user/Library/Caches/Pyloid-App' # Example for macOS
1501
+ """
1502
+ return self.dirs.user_cache_dir
1503
+
1504
+ def user_log_dir(self) -> str:
1505
+ """
1506
+ Returns the user log directory path.
1507
+
1508
+ Returns
1509
+ -------
1510
+ str
1511
+ User log directory path
1512
+
1513
+ Examples
1514
+ --------
1515
+ >>> app = Pyloid(app_name="Pyloid-App")
1516
+ >>> log_dir = app.user_log_dir()
1517
+ >>> print(log_dir)
1518
+ '/Users/user/Library/Logs/Pyloid-App' # Example for macOS
1519
+ """
1520
+ return self.dirs.user_log_dir
1521
+
1522
+ def user_documents_dir(self) -> str:
1523
+ """
1524
+ Returns the user documents directory path.
1525
+
1526
+ Returns
1527
+ -------
1528
+ str
1529
+ User documents directory path
1530
+
1531
+ Examples
1532
+ --------
1533
+ >>> app = Pyloid(app_name="Pyloid-App")
1534
+ >>> documents_dir = app.user_documents_dir()
1535
+ >>> print(documents_dir)
1536
+ '/Users/user/Documents' # Example for macOS
1537
+ """
1538
+ return self.dirs.user_documents_dir
1539
+
1540
+ def user_downloads_dir(self) -> str:
1541
+ """
1542
+ Returns the user downloads directory path.
1543
+
1544
+ Returns
1545
+ -------
1546
+ str
1547
+ User downloads directory path
1548
+
1549
+ Examples
1550
+ --------
1551
+ >>> app = Pyloid(app_name="Pyloid-App")
1552
+ >>> downloads_dir = app.user_downloads_dir()
1553
+ >>> print(downloads_dir)
1554
+ '/Users/user/Downloads' # Example for macOS
1555
+ """
1556
+ return self.dirs.user_downloads_dir
1557
+
1558
+ def user_pictures_dir(self) -> str:
1559
+ """
1560
+ Returns the user pictures directory path.
1561
+
1562
+ Returns
1563
+ -------
1564
+ str
1565
+ User pictures directory path
1566
+
1567
+ Examples
1568
+ --------
1569
+ >>> app = Pyloid(app_name="Pyloid-App")
1570
+ >>> pictures_dir = app.user_pictures_dir()
1571
+ >>> print(pictures_dir)
1572
+ '/Users/user/Pictures' # Example for macOS
1573
+ """
1574
+ return self.dirs.user_pictures_dir
1575
+
1576
+ def user_videos_dir(self) -> str:
1577
+ """
1578
+ Returns the user videos directory path.
1579
+
1580
+ Returns
1581
+ -------
1582
+ str
1583
+ User videos directory path
1584
+
1585
+ Examples
1586
+ --------
1587
+ >>> app = Pyloid(app_name="Pyloid-App")
1588
+ >>> videos_dir = app.user_videos_dir()
1589
+ >>> print(videos_dir)
1590
+ '/Users/user/Movies' # Example for macOS
1591
+ """
1592
+ return self.dirs.user_videos_dir
1593
+
1594
+ def user_music_dir(self) -> str:
1595
+ """
1596
+ Returns the user music directory path.
1597
+
1598
+ Returns
1599
+ -------
1600
+ str
1601
+ User music directory path
1602
+
1603
+ Examples
1604
+ --------
1605
+ >>> app = Pyloid(app_name="Pyloid-App")
1606
+ >>> music_dir = app.user_music_dir()
1607
+ >>> print(music_dir)
1608
+ '/Users/user/Music' # Example for macOS
1609
+ """
1610
+ return self.dirs.user_music_dir
1611
+
1612
+ def user_desktop_dir(self) -> str:
1613
+ """
1614
+ Returns the user desktop directory path.
1615
+
1616
+ Returns
1617
+ -------
1618
+ str
1619
+ User desktop directory path
1620
+
1621
+ Examples
1622
+ --------
1623
+ >>> app = Pyloid(app_name="Pyloid-App")
1624
+ >>> desktop_dir = app.user_desktop_dir()
1625
+ >>> print(desktop_dir)
1626
+ '/Users/user/Desktop' # Example for macOS
1627
+ """
1628
+ return self.dirs.user_desktop_dir
1629
+
1630
+ def user_runtime_dir(self) -> str:
1631
+ """
1632
+ Returns the user runtime directory path.
1633
+
1634
+ Returns
1635
+ -------
1636
+ str
1637
+ User runtime directory path
1638
+
1639
+ Examples
1640
+ --------
1641
+ >>> app = Pyloid(app_name="Pyloid-App")
1642
+ >>> runtime_dir = app.user_runtime_dir()
1643
+ >>> print(runtime_dir)
1644
+ '/Users/user/Library/Caches/TemporaryItems/Pyloid-App' # Example for macOS
1645
+ """
1646
+ return self.dirs.user_runtime_dir
1647
+
1413
1648
 
1414
1649
  class Pyloid(QObject):
1415
1650
  command_signal = Signal(str, str, object)
1416
1651
  result_signal = Signal(str, object)
1417
-
1418
- def __init__(self, app_name: str, single_instance: bool = True, data: Optional[Dict[str, Any]] = None):
1652
+
1653
+ def __init__(
1654
+ self,
1655
+ app_name: str,
1656
+ single_instance: bool = True,
1657
+ # data: Optional[Dict[str, Any]] = None,
1658
+ ):
1419
1659
  """
1420
1660
  Initialize the Pyloid application.
1421
1661
 
@@ -1429,8 +1669,6 @@ class Pyloid(QObject):
1429
1669
  The name of the application.
1430
1670
  single_instance : bool, optional
1431
1671
  Determines whether to run as a single instance. (Default is True)
1432
- data : dict, optional
1433
- Data to be transmitted to the frontend web engine via IPC
1434
1672
 
1435
1673
  Notes
1436
1674
  -----
@@ -1438,13 +1676,13 @@ class Pyloid(QObject):
1438
1676
  and is used as an API key to connect to the integrated backend FastAPI server.
1439
1677
  """
1440
1678
  super().__init__()
1441
-
1442
- self.data = data
1679
+
1680
+ self.data = None # 나중에 데이터 필요 시 수정
1443
1681
 
1444
1682
  self.app = _Pyloid(app_name, single_instance, self.data)
1445
-
1683
+
1446
1684
  self.command_signal.connect(self._handle_command)
1447
-
1685
+
1448
1686
  @Slot(str, str, object)
1449
1687
  def _handle_command(self, command_id, command_type, params):
1450
1688
  result = None
@@ -1461,7 +1699,8 @@ class Pyloid(QObject):
1461
1699
  y=params.get("y", 200),
1462
1700
  frame=params.get("frame", True),
1463
1701
  context_menu=params.get("context_menu", False),
1464
- dev_tools=params.get("dev_tools", False)
1702
+ dev_tools=params.get("dev_tools", False),
1703
+ rpc=params.get("rpc", None),
1465
1704
  )
1466
1705
  result = window
1467
1706
 
@@ -1502,7 +1741,9 @@ class Pyloid(QObject):
1502
1741
  result = self.app.show_notification(params["title"], params["message"])
1503
1742
 
1504
1743
  elif command_type == "set_tray_icon_animation":
1505
- result = self.app.set_tray_icon_animation(params["icon_frames"], params.get("interval", 200))
1744
+ result = self.app.set_tray_icon_animation(
1745
+ params["icon_frames"], params.get("interval", 200)
1746
+ )
1506
1747
 
1507
1748
  elif command_type == "set_tray_tooltip":
1508
1749
  result = self.app.set_tray_tooltip(params["message"])
@@ -1574,39 +1815,40 @@ class Pyloid(QObject):
1574
1815
  return None
1575
1816
 
1576
1817
  self.result_signal.emit(command_id, result)
1577
-
1578
- def execute_command(self, command_type: str, params: object, timeout: Optional[int] = None):
1818
+
1819
+ def execute_command(
1820
+ self, command_type: str, params: object, timeout: Optional[int] = None
1821
+ ):
1579
1822
  command_id = str(uuid.uuid4())
1580
-
1823
+
1581
1824
  result_data = [None]
1582
1825
  loop = QEventLoop()
1583
-
1826
+
1584
1827
  if timeout:
1585
1828
  timer = QTimer()
1586
1829
  timer.setSingleShot(True)
1587
1830
  timer.timeout.connect(loop.quit)
1588
1831
  timer.start(timeout)
1589
-
1832
+
1590
1833
  def on_result(received_id, result):
1591
1834
  if received_id == command_id:
1592
- result_data[0] = result
1835
+ result_data[0] = result
1593
1836
  loop.quit()
1594
1837
 
1595
-
1596
1838
  self.result_signal.connect(on_result, Qt.QueuedConnection)
1597
-
1839
+
1598
1840
  self.command_signal.emit(command_id, command_type, params)
1599
-
1841
+
1600
1842
  loop.exec()
1601
-
1843
+
1602
1844
  self.result_signal.disconnect(on_result)
1603
-
1845
+
1604
1846
  return result_data[0]
1605
-
1847
+
1606
1848
  # -------------------------------------------------------------------
1607
1849
  # Execute_command 래퍼 (wrapper) 함수들
1608
1850
  # -------------------------------------------------------------------
1609
-
1851
+
1610
1852
  def set_icon(self, icon_path: str) -> bool:
1611
1853
  """
1612
1854
  Dynamically sets the application's icon.
@@ -1622,7 +1864,7 @@ class Pyloid(QObject):
1622
1864
  >>> app.set_icon("icons/icon.png")
1623
1865
  """
1624
1866
  return self.execute_command("set_icon", {"icon_path": icon_path})
1625
-
1867
+
1626
1868
  def create_window(
1627
1869
  self,
1628
1870
  title: str,
@@ -1633,6 +1875,7 @@ class Pyloid(QObject):
1633
1875
  frame: bool = True,
1634
1876
  context_menu: bool = False,
1635
1877
  dev_tools: bool = False,
1878
+ rpc: Optional[PyloidRPC] = None,
1636
1879
  ) -> BrowserWindow:
1637
1880
  """
1638
1881
  Creates a new browser window.
@@ -1655,6 +1898,8 @@ class Pyloid(QObject):
1655
1898
  Whether to use the context menu (default is False)
1656
1899
  dev_tools : bool, optional
1657
1900
  Whether to use developer tools (default is False)
1901
+ rpc : PyloidRPC, optional
1902
+ The RPC server instance to be used in the window
1658
1903
 
1659
1904
  Returns
1660
1905
  -------
@@ -1675,9 +1920,10 @@ class Pyloid(QObject):
1675
1920
  "frame": frame,
1676
1921
  "context_menu": context_menu,
1677
1922
  "dev_tools": dev_tools,
1923
+ "rpc": rpc,
1678
1924
  }
1679
1925
  return self.execute_command("create_window", params)
1680
-
1926
+
1681
1927
  def run(self) -> None:
1682
1928
  """
1683
1929
  Runs the application event loop.
@@ -1688,7 +1934,7 @@ class Pyloid(QObject):
1688
1934
  >>> app.run()
1689
1935
  """
1690
1936
  return self.app.run()
1691
-
1937
+
1692
1938
  def get_windows(self) -> Dict[str, BrowserWindow]:
1693
1939
  """
1694
1940
  Returns a list of all browser windows.
@@ -1704,7 +1950,7 @@ class Pyloid(QObject):
1704
1950
  >>> windows = app.get_windows()
1705
1951
  """
1706
1952
  return self.execute_command("get_windows", {})
1707
-
1953
+
1708
1954
  def show_main_window(self) -> None:
1709
1955
  """
1710
1956
  Shows and focuses the first window.
@@ -1715,7 +1961,7 @@ class Pyloid(QObject):
1715
1961
  >>> app.show_main_window()
1716
1962
  """
1717
1963
  return self.execute_command("show_main_window", {})
1718
-
1964
+
1719
1965
  def focus_main_window(self) -> None:
1720
1966
  """
1721
1967
  Focuses the first window.
@@ -1726,7 +1972,7 @@ class Pyloid(QObject):
1726
1972
  >>> app.focus_main_window()
1727
1973
  """
1728
1974
  return self.execute_command("focus_main_window", {})
1729
-
1975
+
1730
1976
  def show_and_focus_main_window(self) -> None:
1731
1977
  """
1732
1978
  Shows and focuses the first window.
@@ -1737,7 +1983,7 @@ class Pyloid(QObject):
1737
1983
  >>> app.show_and_focus_main_window()
1738
1984
  """
1739
1985
  return self.execute_command("show_and_focus_main_window", {})
1740
-
1986
+
1741
1987
  def close_all_windows(self) -> None:
1742
1988
  """
1743
1989
  Closes all windows.
@@ -1748,7 +1994,7 @@ class Pyloid(QObject):
1748
1994
  >>> app.close_all_windows()
1749
1995
  """
1750
1996
  return self.execute_command("close_all_windows", {})
1751
-
1997
+
1752
1998
  def quit(self) -> None:
1753
1999
  """
1754
2000
  Quits the application.
@@ -1759,7 +2005,7 @@ class Pyloid(QObject):
1759
2005
  >>> app.quit()
1760
2006
  """
1761
2007
  return self.execute_command("quit", {})
1762
-
2008
+
1763
2009
  def get_window_by_id(self, window_id: str) -> Optional[BrowserWindow]:
1764
2010
  """
1765
2011
  Returns the window with the given ID.
@@ -1780,7 +2026,7 @@ class Pyloid(QObject):
1780
2026
  >>> window = app.get_window_by_id("some-window-id")
1781
2027
  """
1782
2028
  return self.execute_command("get_window_by_id", {"window_id": window_id})
1783
-
2029
+
1784
2030
  def set_tray_icon(self, tray_icon_path: str) -> bool:
1785
2031
  """
1786
2032
  Dynamically sets the tray icon.
@@ -1796,8 +2042,10 @@ class Pyloid(QObject):
1796
2042
  >>> app.set_tray_icon("icons/icon.png")
1797
2043
  """
1798
2044
  return self.execute_command("set_tray_icon", {"tray_icon_path": tray_icon_path})
1799
-
1800
- def set_tray_menu_items(self, tray_menu_items: List[Dict[str, Union[str, Callable]]]) -> bool:
2045
+
2046
+ def set_tray_menu_items(
2047
+ self, tray_menu_items: List[Dict[str, Union[str, Callable]]]
2048
+ ) -> bool:
1801
2049
  """
1802
2050
  Dynamically sets the tray menu items.
1803
2051
 
@@ -1813,8 +2061,10 @@ class Pyloid(QObject):
1813
2061
  >>> {"label": "Exit", "callback": app.quit}]
1814
2062
  >>> app.set_tray_menu_items(menu_items)
1815
2063
  """
1816
- return self.execute_command("set_tray_menu_items", {"tray_menu_items": tray_menu_items})
1817
-
2064
+ return self.execute_command(
2065
+ "set_tray_menu_items", {"tray_menu_items": tray_menu_items}
2066
+ )
2067
+
1818
2068
  def set_tray_actions(self, actions: Dict[TrayEvent, Callable]) -> bool:
1819
2069
  """
1820
2070
  Dynamically sets the actions for tray icon activation.
@@ -1830,7 +2080,7 @@ class Pyloid(QObject):
1830
2080
  >>> app.set_tray_actions({TrayEvent.DoubleClick: lambda: print("Double-clicked")})
1831
2081
  """
1832
2082
  return self.execute_command("set_tray_actions", {"actions": actions})
1833
-
2083
+
1834
2084
  def show_notification(self, title: str, message: str) -> bool:
1835
2085
  """
1836
2086
  Displays a notification in the system tray.
@@ -1847,9 +2097,13 @@ class Pyloid(QObject):
1847
2097
  >>> app = Pyloid(app_name="Pyloid-App")
1848
2098
  >>> app.show_notification("Update Available", "A new update is available for download.")
1849
2099
  """
1850
- return self.execute_command("show_notification", {"title": title, "message": message})
1851
-
1852
- def set_tray_icon_animation(self, icon_frames: List[str], interval: int = 200) -> bool:
2100
+ return self.execute_command(
2101
+ "show_notification", {"title": title, "message": message}
2102
+ )
2103
+
2104
+ def set_tray_icon_animation(
2105
+ self, icon_frames: List[str], interval: int = 200
2106
+ ) -> bool:
1853
2107
  """
1854
2108
  Dynamically sets and starts the animation for the tray icon.
1855
2109
 
@@ -1865,8 +2119,11 @@ class Pyloid(QObject):
1865
2119
  >>> app = Pyloid(app_name="Pyloid-App")
1866
2120
  >>> app.set_tray_icon_animation(["frame1.png", "frame2.png", "frame3.png"], 100)
1867
2121
  """
1868
- return self.execute_command("set_tray_icon_animation", {"icon_frames": icon_frames, "interval": interval})
1869
-
2122
+ return self.execute_command(
2123
+ "set_tray_icon_animation",
2124
+ {"icon_frames": icon_frames, "interval": interval},
2125
+ )
2126
+
1870
2127
  def set_tray_tooltip(self, message: str) -> bool:
1871
2128
  """
1872
2129
  Dynamically sets the tooltip for the tray icon.
@@ -1882,7 +2139,7 @@ class Pyloid(QObject):
1882
2139
  >>> app.set_tray_tooltip("Pyloid is running")
1883
2140
  """
1884
2141
  return self.execute_command("set_tray_tooltip", {"message": message})
1885
-
2142
+
1886
2143
  def set_notification_callback(self, callback: Callable[[str], None]) -> bool:
1887
2144
  """
1888
2145
  Sets the callback function to be called when a notification is clicked.
@@ -1900,7 +2157,7 @@ class Pyloid(QObject):
1900
2157
  >>> app.set_notification_callback(on_notification_click)
1901
2158
  """
1902
2159
  return self.execute_command("set_notification_callback", {"callback": callback})
1903
-
2160
+
1904
2161
  def get_all_monitors(self) -> List[Monitor]:
1905
2162
  """
1906
2163
  Returns information about all connected monitors.
@@ -1918,7 +2175,7 @@ class Pyloid(QObject):
1918
2175
  >>> print(monitor.info())
1919
2176
  """
1920
2177
  return self.execute_command("get_all_monitors", {})
1921
-
2178
+
1922
2179
  def get_primary_monitor(self) -> Monitor:
1923
2180
  """
1924
2181
  Returns information about the primary monitor.
@@ -1935,7 +2192,7 @@ class Pyloid(QObject):
1935
2192
  >>> print(primary_monitor.info())
1936
2193
  """
1937
2194
  return self.execute_command("get_primary_monitor", {})
1938
-
2195
+
1939
2196
  def set_clipboard_text(self, text: str) -> None:
1940
2197
  """
1941
2198
  Copies text to the clipboard.
@@ -1951,7 +2208,7 @@ class Pyloid(QObject):
1951
2208
  >>> app.set_clipboard_text("Hello, World!")
1952
2209
  """
1953
2210
  return self.execute_command("set_clipboard_text", {"text": text})
1954
-
2211
+
1955
2212
  def get_clipboard_text(self) -> str:
1956
2213
  """
1957
2214
  Retrieves text from the clipboard.
@@ -1968,7 +2225,7 @@ class Pyloid(QObject):
1968
2225
  >>> print(text)
1969
2226
  """
1970
2227
  return self.execute_command("get_clipboard_text", {})
1971
-
2228
+
1972
2229
  def set_clipboard_image(self, image: Union[str, bytes, os.PathLike]) -> None:
1973
2230
  """
1974
2231
  Copies an image to the clipboard.
@@ -1984,7 +2241,7 @@ class Pyloid(QObject):
1984
2241
  >>> app.set_clipboard_image("/path/to/image.png")
1985
2242
  """
1986
2243
  return self.execute_command("set_clipboard_image", {"image": image})
1987
-
2244
+
1988
2245
  def get_clipboard_image(self) -> QImage:
1989
2246
  """
1990
2247
  Retrieves an image from the clipboard.
@@ -2002,7 +2259,7 @@ class Pyloid(QObject):
2002
2259
  >>> image.save("/path/to/save/image.png")
2003
2260
  """
2004
2261
  return self.execute_command("get_clipboard_image", {})
2005
-
2262
+
2006
2263
  def set_auto_start(self, enable: bool) -> Union[bool, None]:
2007
2264
  """
2008
2265
  Sets the application to start automatically at system startup.
@@ -2023,7 +2280,7 @@ class Pyloid(QObject):
2023
2280
  >>> app.set_auto_start(True)
2024
2281
  """
2025
2282
  return self.execute_command("set_auto_start", {"enable": enable})
2026
-
2283
+
2027
2284
  def is_auto_start(self) -> bool:
2028
2285
  """
2029
2286
  Checks if the application is set to start automatically at system startup.
@@ -2040,7 +2297,7 @@ class Pyloid(QObject):
2040
2297
  >>> print(auto_start_enabled)
2041
2298
  """
2042
2299
  return self.execute_command("is_auto_start", {})
2043
-
2300
+
2044
2301
  def watch_file(self, file_path: str) -> bool:
2045
2302
  """
2046
2303
  Adds a file to the watch list.
@@ -2061,7 +2318,7 @@ class Pyloid(QObject):
2061
2318
  >>> app.watch_file("/path/to/file.txt")
2062
2319
  """
2063
2320
  return self.execute_command("watch_file", {"file_path": file_path})
2064
-
2321
+
2065
2322
  def watch_directory(self, dir_path: str) -> bool:
2066
2323
  """
2067
2324
  Adds a directory to the watch list.
@@ -2082,7 +2339,7 @@ class Pyloid(QObject):
2082
2339
  >>> app.watch_directory("/path/to/directory")
2083
2340
  """
2084
2341
  return self.execute_command("watch_directory", {"dir_path": dir_path})
2085
-
2342
+
2086
2343
  def stop_watching(self, path: str) -> bool:
2087
2344
  """
2088
2345
  Removes a file or directory from the watch list.
@@ -2103,7 +2360,7 @@ class Pyloid(QObject):
2103
2360
  >>> app.stop_watching("/path/to/file_or_directory")
2104
2361
  """
2105
2362
  return self.execute_command("stop_watching", {"path": path})
2106
-
2363
+
2107
2364
  def get_watched_paths(self) -> List[str]:
2108
2365
  """
2109
2366
  Returns all currently watched paths.
@@ -2120,7 +2377,7 @@ class Pyloid(QObject):
2120
2377
  ['/path/to/file1.txt', '/path/to/directory']
2121
2378
  """
2122
2379
  return self.execute_command("get_watched_paths", {})
2123
-
2380
+
2124
2381
  def get_watched_files(self) -> List[str]:
2125
2382
  """
2126
2383
  Returns all currently watched files.
@@ -2137,7 +2394,7 @@ class Pyloid(QObject):
2137
2394
  ['/path/to/file1.txt', '/path/to/file2.txt']
2138
2395
  """
2139
2396
  return self.execute_command("get_watched_files", {})
2140
-
2397
+
2141
2398
  def get_watched_directories(self) -> List[str]:
2142
2399
  """
2143
2400
  Returns all currently watched directories.
@@ -2154,7 +2411,7 @@ class Pyloid(QObject):
2154
2411
  ['/path/to/directory1', '/path/to/directory2']
2155
2412
  """
2156
2413
  return self.execute_command("get_watched_directories", {})
2157
-
2414
+
2158
2415
  def remove_all_watched_paths(self) -> None:
2159
2416
  """
2160
2417
  Removes all paths from the watch list.
@@ -2165,7 +2422,7 @@ class Pyloid(QObject):
2165
2422
  >>> app.remove_all_watched_paths()
2166
2423
  """
2167
2424
  return self.execute_command("remove_all_watched_paths", {})
2168
-
2425
+
2169
2426
  def set_file_change_callback(self, callback: Callable[[str], None]) -> None:
2170
2427
  """
2171
2428
  Sets the callback function to be called when a file is changed.
@@ -2179,11 +2436,12 @@ class Pyloid(QObject):
2179
2436
  --------
2180
2437
  >>> def on_file_change(file_path):
2181
2438
  >>> print(f"File changed: {file_path}")
2439
+ >>>
2182
2440
  >>> app = Pyloid(app_name="Pyloid-App")
2183
2441
  >>> app.set_file_change_callback(on_file_change)
2184
2442
  """
2185
2443
  return self.execute_command("set_file_change_callback", {"callback": callback})
2186
-
2444
+
2187
2445
  def set_directory_change_callback(self, callback: Callable[[str], None]) -> None:
2188
2446
  """
2189
2447
  Sets the callback function to be called when a directory is changed.
@@ -2197,12 +2455,17 @@ class Pyloid(QObject):
2197
2455
  --------
2198
2456
  >>> def on_directory_change(dir_path):
2199
2457
  >>> print(f"Directory changed: {dir_path}")
2458
+ >>>
2200
2459
  >>> app = Pyloid(app_name="Pyloid-App")
2201
2460
  >>> app.set_directory_change_callback(on_directory_change)
2202
2461
  """
2203
- return self.execute_command("set_directory_change_callback", {"callback": callback})
2204
-
2205
- def open_file_dialog(self, dir: Optional[str] = None, filter: Optional[str] = None) -> Optional[str]:
2462
+ return self.execute_command(
2463
+ "set_directory_change_callback", {"callback": callback}
2464
+ )
2465
+
2466
+ def open_file_dialog(
2467
+ self, dir: Optional[str] = None, filter: Optional[str] = None
2468
+ ) -> Optional[str]:
2206
2469
  """
2207
2470
  Opens a file dialog to select a file to open.
2208
2471
 
@@ -2224,8 +2487,10 @@ class Pyloid(QObject):
2224
2487
  >>> file_path = app.open_file_dialog(dir="/home/user", filter="Text Files (*.txt)")
2225
2488
  """
2226
2489
  return self.execute_command("open_file_dialog", {"dir": dir, "filter": filter})
2227
-
2228
- def save_file_dialog(self, dir: Optional[str] = None, filter: Optional[str] = None) -> Optional[str]:
2490
+
2491
+ def save_file_dialog(
2492
+ self, dir: Optional[str] = None, filter: Optional[str] = None
2493
+ ) -> Optional[str]:
2229
2494
  """
2230
2495
  Opens a file dialog to select a file to save.
2231
2496
 
@@ -2247,7 +2512,7 @@ class Pyloid(QObject):
2247
2512
  >>> file_path = app.save_file_dialog(dir="/home/user", filter="Text Files (*.txt)")
2248
2513
  """
2249
2514
  return self.execute_command("save_file_dialog", {"dir": dir, "filter": filter})
2250
-
2515
+
2251
2516
  def select_directory_dialog(self, dir: Optional[str] = None) -> Optional[str]:
2252
2517
  """
2253
2518
  Opens a dialog to select a directory.
@@ -2267,4 +2532,235 @@ class Pyloid(QObject):
2267
2532
  >>> app = Pyloid(app_name="Pyloid-App")
2268
2533
  >>> directory_path = app.select_directory_dialog(dir="/home/user")
2269
2534
  """
2270
- return self.execute_command("select_directory_dialog", {"dir": dir})
2535
+ return self.execute_command("select_directory_dialog", {"dir": dir})
2536
+
2537
+ # --- Add platformdirs wrapper functions ---
2538
+
2539
+ def user_data_dir(self) -> str:
2540
+ """
2541
+ Returns the user data directory path.
2542
+
2543
+ Returns
2544
+ -------
2545
+ str
2546
+ User data directory path
2547
+
2548
+ Examples
2549
+ --------
2550
+ >>> app = Pyloid(app_name="Pyloid-App")
2551
+ >>> data_dir = app.user_data_dir()
2552
+ >>> print(data_dir)
2553
+ '/Users/user/Library/Application Support/Pyloid-App' # Example for macOS
2554
+ """
2555
+ return self.app.user_data_dir()
2556
+
2557
+ def site_data_dir(self) -> str:
2558
+ """
2559
+ Returns the site data directory path.
2560
+
2561
+ Returns
2562
+ -------
2563
+ str
2564
+ Site data directory path
2565
+
2566
+ Examples
2567
+ --------
2568
+ >>> app = Pyloid(app_name="Pyloid-App")
2569
+ >>> site_data_dir = app.site_data_dir()
2570
+ >>> print(site_data_dir)
2571
+ '/Library/Application Support/Pyloid-App' # Example for macOS
2572
+ """
2573
+ return self.app.site_data_dir()
2574
+
2575
+ def user_cache_dir(self) -> str:
2576
+ """
2577
+ Returns the user cache directory path.
2578
+
2579
+ Returns
2580
+ -------
2581
+ str
2582
+ User cache directory path
2583
+
2584
+ Examples
2585
+ --------
2586
+ >>> app = Pyloid(app_name="Pyloid-App")
2587
+ >>> cache_dir = app.user_cache_dir()
2588
+ >>> print(cache_dir)
2589
+ '/Users/user/Library/Caches/Pyloid-App' # Example for macOS
2590
+ """
2591
+ return self.app.user_cache_dir()
2592
+
2593
+ def user_log_dir(self) -> str:
2594
+ """
2595
+ Returns the user log directory path.
2596
+
2597
+ Returns
2598
+ -------
2599
+ str
2600
+ User log directory path
2601
+
2602
+ Examples
2603
+ --------
2604
+ >>> app = Pyloid(app_name="Pyloid-App")
2605
+ >>> log_dir = app.user_log_dir()
2606
+ >>> print(log_dir)
2607
+ '/Users/user/Library/Logs/Pyloid-App' # Example for macOS
2608
+ """
2609
+ return self.app.user_log_dir()
2610
+
2611
+ def user_documents_dir(self) -> str:
2612
+ """
2613
+ Returns the user documents directory path.
2614
+
2615
+ Returns
2616
+ -------
2617
+ str
2618
+ User documents directory path
2619
+
2620
+ Examples
2621
+ --------
2622
+ >>> app = Pyloid(app_name="Pyloid-App")
2623
+ >>> documents_dir = app.user_documents_dir()
2624
+ >>> print(documents_dir)
2625
+ '/Users/user/Documents' # Example for macOS
2626
+ """
2627
+ return self.app.user_documents_dir()
2628
+
2629
+ def user_downloads_dir(self) -> str:
2630
+ """
2631
+ Returns the user downloads directory path.
2632
+
2633
+ Returns
2634
+ -------
2635
+ str
2636
+ User downloads directory path
2637
+
2638
+ Examples
2639
+ --------
2640
+ >>> app = Pyloid(app_name="Pyloid-App")
2641
+ >>> downloads_dir = app.user_downloads_dir()
2642
+ >>> print(downloads_dir)
2643
+ '/Users/user/Downloads' # Example for macOS
2644
+ """
2645
+ return self.app.user_downloads_dir()
2646
+
2647
+ def user_pictures_dir(self) -> str:
2648
+ """
2649
+ Returns the user pictures directory path.
2650
+
2651
+ Returns
2652
+ -------
2653
+ str
2654
+ User pictures directory path
2655
+
2656
+ Examples
2657
+ --------
2658
+ >>> app = Pyloid(app_name="Pyloid-App")
2659
+ >>> pictures_dir = app.user_pictures_dir()
2660
+ >>> print(pictures_dir)
2661
+ '/Users/user/Pictures' # Example for macOS
2662
+ """
2663
+ return self.app.user_pictures_dir()
2664
+
2665
+ def user_videos_dir(self) -> str:
2666
+ """
2667
+ Returns the user videos directory path.
2668
+
2669
+ Returns
2670
+ -------
2671
+ str
2672
+ User videos directory path
2673
+
2674
+ Examples
2675
+ --------
2676
+ >>> app = Pyloid(app_name="Pyloid-App")
2677
+ >>> videos_dir = app.user_videos_dir()
2678
+ >>> print(videos_dir)
2679
+ '/Users/user/Movies' # Example for macOS
2680
+ """
2681
+ return self.app.user_videos_dir()
2682
+
2683
+ def user_music_dir(self) -> str:
2684
+ """
2685
+ Returns the user music directory path.
2686
+
2687
+ Returns
2688
+ -------
2689
+ str
2690
+ User music directory path
2691
+
2692
+ Examples
2693
+ --------
2694
+ >>> app = Pyloid(app_name="Pyloid-App")
2695
+ >>> music_dir = app.user_music_dir()
2696
+ >>> print(music_dir)
2697
+ '/Users/user/Music' # Example for macOS
2698
+ """
2699
+ return self.app.user_music_dir()
2700
+
2701
+ def user_desktop_dir(self) -> str:
2702
+ """
2703
+ Returns the user desktop directory path.
2704
+
2705
+ Returns
2706
+ -------
2707
+ str
2708
+ User desktop directory path
2709
+
2710
+ Examples
2711
+ --------
2712
+ >>> app = Pyloid(app_name="Pyloid-App")
2713
+ >>> desktop_dir = app.user_desktop_dir()
2714
+ >>> print(desktop_dir)
2715
+ '/Users/user/Desktop' # Example for macOS
2716
+ """
2717
+ return self.app.user_desktop_dir()
2718
+
2719
+ def user_runtime_dir(self) -> str:
2720
+ """
2721
+ Returns the user runtime directory path.
2722
+
2723
+ Returns
2724
+ -------
2725
+ str
2726
+ User runtime directory path
2727
+
2728
+ Examples
2729
+ --------
2730
+ >>> app = Pyloid(app_name="Pyloid-App")
2731
+ >>> runtime_dir = app.user_runtime_dir()
2732
+ >>> print(runtime_dir)
2733
+ '/Users/user/Library/Caches/TemporaryItems/Pyloid-App' # Example for macOS
2734
+ """
2735
+ return self.app.user_runtime_dir()
2736
+
2737
+ # --- Add store wrapper functions ---
2738
+
2739
+ def store(self, path: str, user_data_dir: bool = True) -> Store:
2740
+ """
2741
+ Returns a Store instance for the given path.
2742
+
2743
+ Parameters
2744
+ ----------
2745
+ path : str
2746
+ The path to the store file
2747
+ user_data_dir : bool, optional
2748
+ If True, the store will be created in the user data directory
2749
+
2750
+ Returns
2751
+ -------
2752
+ Store
2753
+ A Store instance for the given path
2754
+
2755
+ Examples
2756
+ --------
2757
+ >>> app = Pyloid(app_name="Pyloid-App")
2758
+ >>> store = app.store("store.json")
2759
+ >>> store.set("key", "value")
2760
+ True
2761
+ >>> print(store.get("key"))
2762
+ 'value'
2763
+ """
2764
+ if user_data_dir:
2765
+ path = os.path.join(self.app.user_data_dir(), path)
2766
+ return Store(path)