pgwidgets-python 0.3.0__py3-none-any.whl → 0.3.1__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.
@@ -16,6 +16,7 @@ import logging
16
16
  import mimetypes
17
17
  import signal
18
18
  import secrets
19
+ import threading
19
20
  import traceback
20
21
  from http.server import SimpleHTTPRequestHandler
21
22
  from pathlib import Path
@@ -37,6 +38,15 @@ from pgwidgets.async_.widget import Widget, build_all_widget_classes
37
38
  _CONCURRENCY_MODES = ("serialized", "per_session", "concurrent")
38
39
 
39
40
 
41
+ # MIME types for the font formats register_font accepts.
42
+ _FONT_MIME = {
43
+ ".ttf": "font/ttf",
44
+ ".otf": "font/otf",
45
+ ".woff": "font/woff",
46
+ ".woff2": "font/woff2",
47
+ }
48
+
49
+
40
50
  class _Namespace:
41
51
  """Holds widget factory methods as attributes (W.Button, W.Label, etc.)."""
42
52
  pass
@@ -1428,6 +1438,13 @@ class Application:
1428
1438
  self._session_semaphore = None # initialized in start()
1429
1439
  self._cb_lock = None # for "serialized" mode
1430
1440
 
1441
+ # Custom-font registry — see sync.application for details.
1442
+ self._fonts = []
1443
+ self._fonts_by_id = {}
1444
+ self._next_font_id = 1
1445
+ self._default_font = None
1446
+ self._font_lock = threading.Lock()
1447
+
1431
1448
  self._run_future = None # set in run(), cancelled by close()
1432
1449
  self._httpd = None # HTTP server instance
1433
1450
 
@@ -1493,6 +1510,104 @@ class Application:
1493
1510
  self._widget_classes[name] = cls
1494
1511
  return cls
1495
1512
 
1513
+ # ----- Custom font registration ---------------------------
1514
+ #
1515
+ # API matches the sync backend; see ``sync.application`` for
1516
+ # full docstrings. The async variant schedules sends on the
1517
+ # event loop via ``asyncio.run_coroutine_threadsafe`` so the
1518
+ # method is safe to call from outside the loop (e.g. from the
1519
+ # ``on_connect`` callback running on a worker thread).
1520
+
1521
+ def register_font(self, family, source, *,
1522
+ weight="normal", style="normal"):
1523
+ if isinstance(source, (bytes, bytearray, memoryview)):
1524
+ data = bytes(source)
1525
+ mime = "font/ttf"
1526
+ else:
1527
+ p = Path(source)
1528
+ data = p.read_bytes()
1529
+ mime = _FONT_MIME.get(p.suffix.lower(), "font/ttf")
1530
+ with self._font_lock:
1531
+ font_id = self._next_font_id
1532
+ self._next_font_id += 1
1533
+ entry = {
1534
+ "id": font_id,
1535
+ "family": str(family),
1536
+ "weight": str(weight),
1537
+ "style": str(style),
1538
+ "bytes": data,
1539
+ "mime": mime,
1540
+ }
1541
+ self._fonts.append(entry)
1542
+ self._fonts_by_id[font_id] = entry
1543
+ msg = self._font_register_msg(entry)
1544
+ self._broadcast_font_msg(msg)
1545
+ return font_id
1546
+
1547
+ def set_default_font(self, family, *, size=None,
1548
+ weight=None, style=None):
1549
+ if family is None:
1550
+ self._default_font = None
1551
+ else:
1552
+ self._default_font = {
1553
+ "family": str(family),
1554
+ "size": None if size is None else float(size),
1555
+ "weight": None if weight is None else str(weight),
1556
+ "style": None if style is None else str(style),
1557
+ }
1558
+ self._broadcast_font_msg(self._font_default_msg())
1559
+
1560
+ def _font_register_msg(self, entry):
1561
+ return {
1562
+ "type": "register-font",
1563
+ "id": entry["id"],
1564
+ "family": entry["family"],
1565
+ "weight": entry["weight"],
1566
+ "style": entry["style"],
1567
+ "url": f"/_pgwidgets/font/{entry['id']}",
1568
+ }
1569
+
1570
+ def _font_default_msg(self):
1571
+ return {
1572
+ "type": "set-default-font",
1573
+ "font": self._default_font,
1574
+ }
1575
+
1576
+ def _broadcast_font_msg(self, msg):
1577
+ loop = getattr(self, "_loop", None) or asyncio.get_event_loop()
1578
+ for session in list(self._sessions.values()):
1579
+ try:
1580
+ asyncio.run_coroutine_threadsafe(
1581
+ session._send(dict(msg)), loop)
1582
+ except Exception:
1583
+ pass
1584
+
1585
+ async def _replay_fonts_to_session(self, session):
1586
+ """Push the registry + default font to a session before
1587
+ any user code runs. ``await``-ed from ``_on_session_open``
1588
+ so the JS side has loaded faces (or at least dispatched
1589
+ the load) before reconstruct / on_connect fires."""
1590
+ with self._font_lock:
1591
+ fonts = list(self._fonts)
1592
+ default = self._default_font
1593
+ for entry in fonts:
1594
+ try:
1595
+ await session._send(self._font_register_msg(entry))
1596
+ except Exception:
1597
+ pass
1598
+ if default is not None:
1599
+ try:
1600
+ await session._send(self._font_default_msg())
1601
+ except Exception:
1602
+ pass
1603
+
1604
+ def _get_font_bytes(self, font_id):
1605
+ with self._font_lock:
1606
+ entry = self._fonts_by_id.get(font_id)
1607
+ if entry is None:
1608
+ return None, None
1609
+ return entry["bytes"], entry["mime"]
1610
+
1496
1611
  @property
1497
1612
  def sessions(self):
1498
1613
  """Dict of active sessions (session_id -> Session)."""
@@ -1603,6 +1718,10 @@ class Application:
1603
1718
 
1604
1719
  if is_reconnect:
1605
1720
  async def do_reconstruct():
1721
+ # Replay the font registry before reconstruct() so
1722
+ # any widget reconstructed with ``set_font(...)``
1723
+ # finds the face already declared.
1724
+ await self._replay_fonts_to_session(session)
1606
1725
  self._logger.info(
1607
1726
  f"Session {session.id}: reconstructing UI.")
1608
1727
  session._reconstructing = True
@@ -1615,10 +1734,16 @@ class Application:
1615
1734
  asyncio.ensure_future(do_reconstruct())
1616
1735
  else:
1617
1736
  self._logger.info(f"Session {session.id} connected.")
1618
- if self._on_connect:
1619
- result = self._on_connect(session)
1620
- if hasattr(result, "__await__"):
1621
- asyncio.ensure_future(result)
1737
+ async def do_connect():
1738
+ # Replay fonts before the user callback so any
1739
+ # widget the user builds with ``set_font(family,
1740
+ # ...)`` sees the face already declared.
1741
+ await self._replay_fonts_to_session(session)
1742
+ if self._on_connect:
1743
+ result = self._on_connect(session)
1744
+ if hasattr(result, "__await__"):
1745
+ await result
1746
+ asyncio.ensure_future(do_connect())
1622
1747
 
1623
1748
  try:
1624
1749
  async for message in ws:
@@ -1699,6 +1824,7 @@ class Application:
1699
1824
  favicon_path = self._favicon_path
1700
1825
  ws_host = self._host
1701
1826
  ws_port = self._ws_port
1827
+ app = self
1702
1828
 
1703
1829
  class Handler(SimpleHTTPRequestHandler):
1704
1830
  def __init__(self, *a, **kw):
@@ -1707,6 +1833,27 @@ class Application:
1707
1833
  def do_GET(self):
1708
1834
  # Strip query string for path matching (e.g. /?session=1)
1709
1835
  path = self.path.split("?")[0]
1836
+ # Custom-font registry: see sync.application for
1837
+ # the matching implementation.
1838
+ if path.startswith("/_pgwidgets/font/"):
1839
+ try:
1840
+ font_id = int(path.rsplit("/", 1)[-1])
1841
+ except ValueError:
1842
+ self.send_error(404)
1843
+ return
1844
+ data, mime = app._get_font_bytes(font_id)
1845
+ if data is None:
1846
+ self.send_error(404)
1847
+ return
1848
+ self.send_response(200)
1849
+ self.send_header("Content-Type", mime)
1850
+ self.send_header("Content-Length", str(len(data)))
1851
+ self.send_header(
1852
+ "Cache-Control", "public, max-age=31536000, immutable")
1853
+ self.send_header("Access-Control-Allow-Origin", "*")
1854
+ self.end_headers()
1855
+ self.wfile.write(data)
1856
+ return
1710
1857
  if path == "/" or path == "/index.html":
1711
1858
  html = remote_html.read_text(encoding="utf-8")
1712
1859
  inject = (
@@ -446,6 +446,11 @@ def _resolve_kwargs(method_name, param_names, args, kwargs):
446
446
  are bundled into a dict for that parameter (e.g.
447
447
  ``add_widget(child, title="Tab 1")`` becomes
448
448
  ``add_widget(child, {"title": "Tab 1"})``).
449
+
450
+ Skipped-positional kwargs are supported: a call like
451
+ ``set_color(fg='red')`` against ``param_names = ['bg', 'fg']``
452
+ fills the omitted ``bg`` slot with ``None`` (the JS-side
453
+ default) instead of erroring out.
449
454
  """
450
455
  if not kwargs:
451
456
  return args
@@ -456,7 +461,10 @@ def _resolve_kwargs(method_name, param_names, args, kwargs):
456
461
  if name in kwargs:
457
462
  merged.append(kwargs.pop(name))
458
463
  else:
459
- break
464
+ # Leave a placeholder so subsequent kwargs can land in
465
+ # later positions. The JS side reads omitted args as
466
+ # null / default, which matches ``None`` here.
467
+ merged.append(None)
460
468
  if kwargs and param_names and param_names[-1] == "options":
461
469
  # Bundle remaining kwargs into the options dict
462
470
  opts_idx = len(param_names) - 1
@@ -352,8 +352,13 @@ class FileBrowser(Callbacks):
352
352
  self._navigate_to(d)
353
353
  self._name_entry.set_text(os.path.basename(path))
354
354
 
355
- def _on_row_activated(self, values, path):
356
- """Double-click on a row."""
355
+ def _on_row_activated(self, values, path, col_key=None):
356
+ """Double-click on a row.
357
+
358
+ ``col_key`` (TableView ≥ this rev) reports which cell was
359
+ clicked; unused here since the browser only cares about
360
+ the row's filename, but the arg has to be in the signature
361
+ or the new 3-arg dispatch raises TypeError."""
357
362
  name = values.get("name", "")
358
363
  if name == "..":
359
364
  self._go_up()
pgwidgets/method_types.py CHANGED
@@ -82,6 +82,17 @@ ACTION_METHODS = {
82
82
  "expand_all", "collapse_all", "expand_item", "collapse_item",
83
83
  "sort_by_column", "set_optimal_column_widths",
84
84
  "select_path", "select_paths", "select_all",
85
+ "select_cell", "select_cells", "clear_cell_selection",
86
+ # Per-cell / row / column / table colour overrides — the
87
+ # ``set_*`` naming would otherwise classify them as SETTERs,
88
+ # whose single-state-slot semantics can't represent the
89
+ # accumulated dict of overrides we actually keep. ACTION
90
+ # dispatch sends each call straight through to the JS side,
91
+ # which holds the canonical state in its own maps.
92
+ "set_cell_color", "set_row_color", "set_column_color",
93
+ "set_table_color",
94
+ "clear_cell_color", "clear_row_color", "clear_column_color",
95
+ "clear_all_colors",
85
96
  # TextSource editing
86
97
  "insert_text", "delete_range", "create_tag", "remove_tag_def",
87
98
  "apply_tag", "remove_tag", "create_ref", "remove_ref",
@@ -130,6 +130,17 @@ def _run_queue_loop(cb_queue, stop_event, logger=None):
130
130
  result_slot['event'].set()
131
131
 
132
132
 
133
+ # MIME types for the font formats register_font accepts. Used to
134
+ # set the ``Content-Type`` header when the HTTP server delivers a
135
+ # registered font to the browser.
136
+ _FONT_MIME = {
137
+ ".ttf": "font/ttf",
138
+ ".otf": "font/otf",
139
+ ".woff": "font/woff",
140
+ ".woff2": "font/woff2",
141
+ }
142
+
143
+
133
144
  class Session:
134
145
  """
135
146
  A session that owns a widget tree and its associated state.
@@ -1642,6 +1653,17 @@ class Application:
1642
1653
  self._on_disconnect = None # user callback: fn(session)
1643
1654
  self._cb_queue = queue.Queue() # for "serialized" mode
1644
1655
 
1656
+ # Custom-font registry. Each entry is
1657
+ # ``{id, family, weight, style, bytes, mime}``; ``id`` is a
1658
+ # monotonic int used as the path component of the HTTP URL
1659
+ # the browser fetches the font from. ``_default_font`` is
1660
+ # ``{family, size, weight, style}`` or ``None``.
1661
+ self._fonts = []
1662
+ self._fonts_by_id = {}
1663
+ self._next_font_id = 1
1664
+ self._default_font = None
1665
+ self._font_lock = threading.Lock()
1666
+
1645
1667
  self._loop = None
1646
1668
  self._shutdown = threading.Event()
1647
1669
  self._thread = None
@@ -1714,6 +1736,141 @@ class Application:
1714
1736
  self._widget_classes[name] = cls
1715
1737
  return cls
1716
1738
 
1739
+ # ----- Custom font registration ---------------------------
1740
+
1741
+ def register_font(self, family, source, *,
1742
+ weight="normal", style="normal"):
1743
+ """Register a custom font with the application.
1744
+
1745
+ Browsers receive a ``@font-face``-equivalent declaration
1746
+ through the JS ``FontFace`` API, so any widget that does
1747
+ ``set_font(family, ...)`` thereafter renders with this
1748
+ face. Multiple registrations for the same ``family`` but
1749
+ different ``weight`` / ``style`` combine into a single
1750
+ font family with multiple faces.
1751
+
1752
+ Parameters
1753
+ ----------
1754
+ family : str
1755
+ CSS font-family name to expose to widgets.
1756
+ source : str | os.PathLike | bytes | bytearray | memoryview
1757
+ Path to a font file (``.ttf`` / ``.otf`` / ``.woff`` /
1758
+ ``.woff2``) or raw font bytes.
1759
+ weight : str
1760
+ CSS ``font-weight`` -- ``'normal'``, ``'bold'``, or a
1761
+ numeric string (``'100'`` ... ``'900'``).
1762
+ style : str
1763
+ CSS ``font-style`` -- ``'normal'``, ``'italic'``, or
1764
+ ``'oblique'``.
1765
+
1766
+ Returns
1767
+ -------
1768
+ int
1769
+ The registration id (rarely needed; callers usually
1770
+ just refer to the font by ``family``).
1771
+ """
1772
+ if isinstance(source, (bytes, bytearray, memoryview)):
1773
+ data = bytes(source)
1774
+ mime = "font/ttf"
1775
+ else:
1776
+ p = Path(source)
1777
+ data = p.read_bytes()
1778
+ ext = p.suffix.lower()
1779
+ mime = _FONT_MIME.get(ext, "font/ttf")
1780
+ with self._font_lock:
1781
+ font_id = self._next_font_id
1782
+ self._next_font_id += 1
1783
+ entry = {
1784
+ "id": font_id,
1785
+ "family": str(family),
1786
+ "weight": str(weight),
1787
+ "style": str(style),
1788
+ "bytes": data,
1789
+ "mime": mime,
1790
+ }
1791
+ self._fonts.append(entry)
1792
+ self._fonts_by_id[font_id] = entry
1793
+ # Push to any already-connected sessions so live UIs pick
1794
+ # the font up without a reconnect.
1795
+ msg = self._font_register_msg(entry)
1796
+ for session in list(self._sessions.values()):
1797
+ try:
1798
+ session._send(msg)
1799
+ except Exception:
1800
+ pass
1801
+ return font_id
1802
+
1803
+ def set_default_font(self, family, *, size=None,
1804
+ weight=None, style=None):
1805
+ """Set the document-level default font.
1806
+
1807
+ Writes ``--pg-default-font-family`` / ``--pg-default-font-size``
1808
+ / ``--pg-default-font-weight`` / ``--pg-default-font-style``
1809
+ CSS variables on ``:root``; the base pgwidgets stylesheet
1810
+ consumes these so any widget that hasn't been given an
1811
+ explicit ``set_font(...)`` follows the default.
1812
+
1813
+ Pass ``family=None`` to clear the default and fall back to
1814
+ the built-in stylesheet."""
1815
+ if family is None:
1816
+ self._default_font = None
1817
+ else:
1818
+ self._default_font = {
1819
+ "family": str(family),
1820
+ "size": None if size is None else float(size),
1821
+ "weight": None if weight is None else str(weight),
1822
+ "style": None if style is None else str(style),
1823
+ }
1824
+ msg = self._font_default_msg()
1825
+ for session in list(self._sessions.values()):
1826
+ try:
1827
+ session._send(msg)
1828
+ except Exception:
1829
+ pass
1830
+
1831
+ def _font_register_msg(self, entry):
1832
+ return {
1833
+ "type": "register-font",
1834
+ "id": entry["id"],
1835
+ "family": entry["family"],
1836
+ "weight": entry["weight"],
1837
+ "style": entry["style"],
1838
+ "url": f"/_pgwidgets/font/{entry['id']}",
1839
+ }
1840
+
1841
+ def _font_default_msg(self):
1842
+ return {
1843
+ "type": "set-default-font",
1844
+ "font": self._default_font,
1845
+ }
1846
+
1847
+ def _replay_fonts_to_session(self, session):
1848
+ """Push the full font registry + default font to a freshly
1849
+ connected (or reconnecting) session before any user code
1850
+ creates widgets. Called from ``_on_session_open``."""
1851
+ with self._font_lock:
1852
+ fonts = list(self._fonts)
1853
+ default = self._default_font
1854
+ for entry in fonts:
1855
+ try:
1856
+ session._send(self._font_register_msg(entry))
1857
+ except Exception:
1858
+ pass
1859
+ if default is not None:
1860
+ try:
1861
+ session._send(self._font_default_msg())
1862
+ except Exception:
1863
+ pass
1864
+
1865
+ def _get_font_bytes(self, font_id):
1866
+ """Return ``(bytes, mime)`` for a registered font, or
1867
+ ``(None, None)`` if unknown. Called by the HTTP handler."""
1868
+ with self._font_lock:
1869
+ entry = self._fonts_by_id.get(font_id)
1870
+ if entry is None:
1871
+ return None, None
1872
+ return entry["bytes"], entry["mime"]
1873
+
1717
1874
  def start(self):
1718
1875
  """Start the WebSocket server (and HTTP server if enabled).
1719
1876
 
@@ -1836,6 +1993,10 @@ class Application:
1836
1993
  # because _send() blocks with event.wait() while the event
1837
1994
  # loop that must process ws.send() is also blocked.
1838
1995
  def do_reconstruct():
1996
+ # Replay the font registry before reconstruct() so any
1997
+ # widget reconstructed with ``set_font(...)`` finds the
1998
+ # face already declared.
1999
+ self._replay_fonts_to_session(session)
1839
2000
  self._logger.info(
1840
2001
  f"Session {session.id}: reconstructing UI.")
1841
2002
  session._reconstructing = True
@@ -1848,8 +2009,14 @@ class Application:
1848
2009
  self._dispatch(session, do_reconstruct, ())
1849
2010
  else:
1850
2011
  self._logger.info(f"Session {session.id} connected.")
1851
- if self._on_connect:
1852
- self._dispatch(session, self._on_connect, (session,))
2012
+ def do_connect():
2013
+ # Replay fonts before the user callback so any widget
2014
+ # the user builds with ``set_font(family, ...)`` sees
2015
+ # the face already declared.
2016
+ self._replay_fonts_to_session(session)
2017
+ if self._on_connect:
2018
+ self._on_connect(session)
2019
+ self._dispatch(session, do_connect, ())
1853
2020
 
1854
2021
  try:
1855
2022
  async for message in ws:
@@ -1942,6 +2109,7 @@ class Application:
1942
2109
  favicon_path = self._favicon_path
1943
2110
  ws_host = self._host
1944
2111
  ws_port = self._ws_port
2112
+ app = self
1945
2113
 
1946
2114
  class Handler(http.server.SimpleHTTPRequestHandler):
1947
2115
  def __init__(self, *args, **kwargs):
@@ -1951,6 +2119,31 @@ class Application:
1951
2119
  # serve remote.html at the root, with WS URL injected
1952
2120
  # Strip query string for path matching (e.g. /?session=1)
1953
2121
  path = self.path.split("?")[0]
2122
+ # Custom-font registry: /_pgwidgets/font/<id> serves
2123
+ # the bytes the app registered via ``register_font``.
2124
+ # ``Cache-Control: immutable`` is safe because the
2125
+ # registry assigns a fresh id on every call -- the
2126
+ # URL is content-stable for the lifetime of the
2127
+ # registration.
2128
+ if path.startswith("/_pgwidgets/font/"):
2129
+ try:
2130
+ font_id = int(path.rsplit("/", 1)[-1])
2131
+ except ValueError:
2132
+ self.send_error(404)
2133
+ return
2134
+ data, mime = app._get_font_bytes(font_id)
2135
+ if data is None:
2136
+ self.send_error(404)
2137
+ return
2138
+ self.send_response(200)
2139
+ self.send_header("Content-Type", mime)
2140
+ self.send_header("Content-Length", str(len(data)))
2141
+ self.send_header(
2142
+ "Cache-Control", "public, max-age=31536000, immutable")
2143
+ self.send_header("Access-Control-Allow-Origin", "*")
2144
+ self.end_headers()
2145
+ self.wfile.write(data)
2146
+ return
1954
2147
  if path == "/" or path == "/index.html":
1955
2148
  html = remote_html.read_text(encoding="utf-8")
1956
2149
  inject = (
pgwidgets/sync/widget.py CHANGED
@@ -420,6 +420,11 @@ def _resolve_kwargs(method_name, param_names, args, kwargs):
420
420
  are bundled into a dict for that parameter (e.g.
421
421
  ``add_widget(child, title="Tab 1")`` becomes
422
422
  ``add_widget(child, {"title": "Tab 1"})``).
423
+
424
+ Skipped-positional kwargs are supported: a call like
425
+ ``set_color(fg='red')`` against ``param_names = ['bg', 'fg']``
426
+ fills the omitted ``bg`` slot with ``None`` (the JS-side
427
+ default) instead of erroring out.
423
428
  """
424
429
  if not kwargs:
425
430
  return args
@@ -430,7 +435,10 @@ def _resolve_kwargs(method_name, param_names, args, kwargs):
430
435
  if name in kwargs:
431
436
  merged.append(kwargs.pop(name))
432
437
  else:
433
- break
438
+ # Leave a placeholder so subsequent kwargs can land in
439
+ # later positions. The JS side reads omitted args as
440
+ # null / default, which matches ``None`` here.
441
+ merged.append(None)
434
442
  if kwargs and param_names and param_names[-1] == "options":
435
443
  # Bundle remaining kwargs into the options dict
436
444
  opts_idx = len(param_names) - 1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pgwidgets-python
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Python bindings for the pgwidgets JavaScript widget library
5
5
  Author: PGWidgets Developers
6
6
  License: BSD-3-Clause
@@ -3,19 +3,19 @@ pgwidgets/_json.py,sha256=o21qywJ6yAldbqxTq3nLwgK9O67r3a8JxhvI_WAJnMY,2184
3
3
  pgwidgets/buffer.py,sha256=BYj_nb6fuyGsBqUp3_Z1YrzUm4h5VmoWPGKvpTjWrYk,4048
4
4
  pgwidgets/callbacks.py,sha256=gA2FnX0N5BmbmOMyEV35yHySgAfVjfAZcZhZbo7j4-c,3314
5
5
  pgwidgets/defs.py,sha256=Q8qhvTeansFU1Av6j5ncdwF2xSNHrpXuKErdHWQ3HiA,436
6
- pgwidgets/method_types.py,sha256=E_f0CQtDwWKgiPmIPGEftm3OXvntErAY4JZH0C7Dwuo,17699
6
+ pgwidgets/method_types.py,sha256=1gOTR8pdX_t7E_UEyUy4rnWiFoh10tJSUAINA1mE-SU,18298
7
7
  pgwidgets/async_/Widgets.py,sha256=vld2xkvusBBEVbhbezaJsjBRRj3L7c17Up0pOVs8PGo,723
8
8
  pgwidgets/async_/__init__.py,sha256=rXB-v9XRrYt1imYuYikhkzIyiRqaYhlTpQei2LHaQ18,564
9
- pgwidgets/async_/application.py,sha256=xLt7ZF7jgRYKT5-L95sahohT4Omq2TfW7y8V1f6GM9g,74443
10
- pgwidgets/async_/widget.py,sha256=H27v7AzHNg5obzr2e8UyOyj2lbGfWhEhZUrIDqrZNbg,37612
9
+ pgwidgets/async_/application.py,sha256=5tUlfyE9ie0aFPh2WAs2VRvgyuqQmdaGTX0d1IkSB7o,80077
10
+ pgwidgets/async_/widget.py,sha256=gO7eJh9LV3UY7yRLdeml_0gqmHnFB5VU3NWlDURLtfc,38042
11
11
  pgwidgets/extras/__init__.py,sha256=AXUmFtnn4RSlp6pJX6z55j-m_29VmfzpYIjQvAy7pO0,325
12
- pgwidgets/extras/file_browser.py,sha256=PWzJltjQZbmsD1ykbbC2xJ6ErEkJj7KafhmVkd9q9Ac,16963
12
+ pgwidgets/extras/file_browser.py,sha256=CXQSfJx1VQ-fJfhKNrVyqrkKTJEQngmiP954y70Fjo0,17229
13
13
  pgwidgets/sync/Widgets.py,sha256=7SaocMVMzFHhi51pjuEAr47Wez90b_a61kDbiv0jOfM,706
14
14
  pgwidgets/sync/__init__.py,sha256=SF5RTAvtu8BbYBWzpiCPipy6DJzNXf6nqk9xdvwxUhQ,542
15
- pgwidgets/sync/application.py,sha256=0xZDEywx7L7vUe1QAs29iufIqqvmSzOLeu9QQIGiSj0,87160
16
- pgwidgets/sync/widget.py,sha256=tQDkCOkcevDrGnG4LhBf69law82-UB1fF-r08GUZPSE,37913
17
- pgwidgets_python-0.3.0.dist-info/licenses/LICENSE.md,sha256=LoM3fMTiMnQuHRCJghdjOtjnCrL8soBpu2PFk24Xvyg,1528
18
- pgwidgets_python-0.3.0.dist-info/METADATA,sha256=LtcjQx8O8cbpQ_1HcXVm8Jpo7UyFLfVkPCEe8fEmJIY,4568
19
- pgwidgets_python-0.3.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
20
- pgwidgets_python-0.3.0.dist-info/top_level.txt,sha256=wwL6fBq0gU-JwzlM6TdduY1qYpu39ysqnnbQT-1bqAs,10
21
- pgwidgets_python-0.3.0.dist-info/RECORD,,
15
+ pgwidgets/sync/application.py,sha256=Gfft77F66PfRnr_8_FBqU0v_gWe4YC9M-q5rww1N-2U,94741
16
+ pgwidgets/sync/widget.py,sha256=yrScNkZB34u0JXn3-kCB-1mPLxLysVbuaPsCjC8kq0E,38343
17
+ pgwidgets_python-0.3.1.dist-info/licenses/LICENSE.md,sha256=LoM3fMTiMnQuHRCJghdjOtjnCrL8soBpu2PFk24Xvyg,1528
18
+ pgwidgets_python-0.3.1.dist-info/METADATA,sha256=QKWuFclVrksOLKxHPqSM-6CoNVskbt_bw_rqaUa3WpI,4568
19
+ pgwidgets_python-0.3.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
20
+ pgwidgets_python-0.3.1.dist-info/top_level.txt,sha256=wwL6fBq0gU-JwzlM6TdduY1qYpu39ysqnnbQT-1bqAs,10
21
+ pgwidgets_python-0.3.1.dist-info/RECORD,,