pyglove 0.4.5.dev202412100810__py3-none-any.whl → 0.4.5.dev202412130809__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.
@@ -74,7 +74,7 @@ class Label(HtmlControl):
74
74
 
75
75
  def _to_html(self, **kwargs) -> Html:
76
76
  text_elem = Html.element(
77
- 'a',
77
+ 'a' if self.link is not None else 'span',
78
78
  [self.text],
79
79
  id=self.element_id(),
80
80
  href=self.link,
@@ -34,7 +34,7 @@ class LabelTest(TestCase):
34
34
  label = label_lib.Label('foo')
35
35
  self.assertIsNone(label.tooltip)
36
36
  self.assertIsNone(label.link)
37
- self.assert_html_content(label, '<a class="label">foo</a>')
37
+ self.assert_html_content(label, '<span class="label">foo</span>')
38
38
  with self.assertRaisesRegex(ValueError, 'Non-interactive .*'):
39
39
  label.update('bar')
40
40
 
@@ -54,7 +54,7 @@ class LabelTest(TestCase):
54
54
  self.assert_html_content(
55
55
  label,
56
56
  (
57
- '<div class="label-container"><a class="label">foo</a>'
57
+ '<div class="label-container"><span class="label">foo</span>'
58
58
  '<span class="tooltip">bar</span></div>'
59
59
  )
60
60
  )
@@ -130,7 +130,7 @@ class LabelTest(TestCase):
130
130
 
131
131
  def test_badge(self):
132
132
  badge = label_lib.Badge('foo')
133
- self.assert_html_content(badge, '<a class="label badge">foo</a>')
133
+ self.assert_html_content(badge, '<span class="label badge">foo</span>')
134
134
 
135
135
 
136
136
  class LabelGroupTest(TestCase):
@@ -140,9 +140,9 @@ class LabelGroupTest(TestCase):
140
140
  self.assert_html_content(
141
141
  group,
142
142
  (
143
- '<div class="label-group"><a class="label group-name">baz</a>'
144
- '<a class="label group-value">foo</a>'
145
- '<a class="label group-value">bar</a></div>'
143
+ '<div class="label-group"><span class="label group-name">baz</span>'
144
+ '<span class="label group-value">foo</span>'
145
+ '<span class="label group-value">bar</span></div>'
146
146
  )
147
147
  )
148
148
 
@@ -42,8 +42,8 @@ class ProgressBarTest(unittest.TestCase):
42
42
  f'<div class="sub-progress foo" id="{bar["foo"].element_id()}">'
43
43
  '</div><div class="sub-progress bar" '
44
44
  f'id="{bar["bar"].element_id()}"></div></div>'
45
- '<div class="label-container"><a class="label progress-label"'
46
- f' id="{bar._progress_label.element_id()}">n/a</a><span class='
45
+ '<div class="label-container"><span class="label progress-label"'
46
+ f' id="{bar._progress_label.element_id()}">n/a</span><span class='
47
47
  f'"tooltip" id="{bar._progress_label.tooltip.element_id()}">'
48
48
  'Not started</span></div></div>'
49
49
  )
@@ -13,7 +13,7 @@
13
13
  # limitations under the License.
14
14
  """Tab control."""
15
15
 
16
- from typing import Annotated, List, Literal, Union
16
+ from typing import Annotated, List, Literal, Optional, Union
17
17
 
18
18
  from pyglove.core.symbolic import flags as pg_flags
19
19
  from pyglove.core.symbolic import object as pg_object
@@ -44,6 +44,11 @@ class Tab(pg_object.Object):
44
44
  'The CSS classes of the tab.'
45
45
  ] = []
46
46
 
47
+ name: Annotated[
48
+ Optional[str],
49
+ 'An optional name that can be used to identify a tab under a tab control'
50
+ ] = None
51
+
47
52
 
48
53
  @pg_object.use_init_args(
49
54
  ['tabs', 'selected', 'tab_position', 'id', 'css_classes', 'styles']
@@ -87,10 +92,84 @@ class TabControl(HtmlControl):
87
92
  position='beforeend',
88
93
  )
89
94
 
95
+ def insert(self, index_or_name: Union[int, str], tab: Tab) -> None:
96
+ """Inserts a tab before a tab identified by index or name."""
97
+ index = self.indexof(index_or_name)
98
+ if index == -1:
99
+ raise ValueError(f'Tab not found: {index_or_name!r}')
100
+ with pg_flags.notify_on_change(False):
101
+ self.tabs.insert(index, tab)
102
+
103
+ self._insert_adjacent_html(
104
+ f"""
105
+ const elem = document.querySelectorAll('#{self.element_id()}-button-group > .tab-button')[{index}];
106
+ """,
107
+ self._tab_button(tab, len(self.tabs) - 1),
108
+ position='beforebegin',
109
+ )
110
+ self._insert_adjacent_html(
111
+ f"""
112
+ const elem = document.querySelectorAll('#{self.element_id()}-content-group > .tab-content')[{index}];
113
+ """,
114
+ self._tab_content(tab, len(self.tabs) - 1),
115
+ position='beforebegin',
116
+ )
117
+
118
+ def indexof(self, index_or_name: Union[int, str]) -> int:
119
+ if isinstance(index_or_name, int):
120
+ index = index_or_name
121
+ if index >= len(self.tabs):
122
+ return len(self.tabs) - 1
123
+ elif index < -len(self.tabs):
124
+ return -1
125
+ elif index < 0:
126
+ index = index + len(self.tabs)
127
+ return index
128
+ else:
129
+ name = index_or_name
130
+ assert isinstance(name, str), name
131
+ for i, tab in enumerate(self.tabs):
132
+ if tab.name == name:
133
+ return i
134
+ return -1
135
+
90
136
  def extend(self, tabs: List[Tab]) -> None:
91
137
  for tab in tabs:
92
138
  self.append(tab)
93
139
 
140
+ def select(
141
+ self,
142
+ index_or_name: Union[int, str, List[str]]) -> Union[int, str]:
143
+ """Selects a tab identified by an index or name.
144
+
145
+ Args:
146
+ index_or_name: The index or name of the tab to select. If a list of names
147
+ is provided, the first name in the list that is found will be selected.
148
+
149
+ Returns:
150
+ The index (if the index was provided) or name of the selected tab.
151
+ """
152
+ selected_name = index_or_name if isinstance(index_or_name, str) else None
153
+ index = -1
154
+ if isinstance(index_or_name, list):
155
+ for name in index_or_name:
156
+ index = self.indexof(name)
157
+ if index != -1:
158
+ selected_name = name
159
+ break
160
+ else:
161
+ index = self.indexof(index_or_name)
162
+ if index == -1:
163
+ raise ValueError(f'Tab not found: {index_or_name!r}')
164
+ self._sync_members(selected=index)
165
+ self._run_javascript(
166
+ f"""
167
+ const tabButtons = document.querySelectorAll('#{self.element_id()}-button-group > .tab-button');
168
+ tabButtons[{index}].click();
169
+ """
170
+ )
171
+ return selected_name or index
172
+
94
173
  def _to_html(self, **kwargs):
95
174
  return Html.element(
96
175
  'table',
@@ -141,7 +220,6 @@ class TabControl(HtmlControl):
141
220
  .tab-control {
142
221
  border-spacing: 0px;
143
222
  border-collapse: collapse;
144
- height: 100%;
145
223
  margin-top: 10px;
146
224
  }
147
225
  .tab-control td {
@@ -36,7 +36,7 @@ class TabControlTest(unittest.TestCase):
36
36
  elem_id = tab.element_id()
37
37
  self.assert_html_content(
38
38
  tab,
39
- f"""<table class="tab-control"><tr><td><div class="tab-button-group top" id="{elem_id}-button-group"><button class="tab-button selected foo" onclick="openTab(event, '{elem_id}', '{elem_id}-0')"><a class="label">foo</a></button><button class="tab-button" onclick="openTab(event, '{elem_id}', '{elem_id}-1')"><a class="label">bar</a></button></div></td></tr><tr><td><div class="tab-content-group top" id="{elem_id}-content-group"><div class="tab-content selected foo" id="{elem_id}-0"><h1>foo</h1></div><div class="tab-content" id="{elem_id}-1"><h1>bar</h1></div></div></td></tr></table>"""
39
+ f"""<table class="tab-control"><tr><td><div class="tab-button-group top" id="{elem_id}-button-group"><button class="tab-button selected foo" onclick="openTab(event, '{elem_id}', '{elem_id}-0')"><span class="label">foo</span></button><button class="tab-button" onclick="openTab(event, '{elem_id}', '{elem_id}-1')"><span class="label">bar</span></button></div></td></tr><tr><td><div class="tab-content-group top" id="{elem_id}-content-group"><div class="tab-content selected foo" id="{elem_id}-0"><h1>foo</h1></div><div class="tab-content" id="{elem_id}-1"><h1>bar</h1></div></div></td></tr></table>"""
40
40
  )
41
41
  with tab.track_scripts() as scripts:
42
42
  tab.extend([
@@ -48,6 +48,39 @@ class TabControlTest(unittest.TestCase):
48
48
  tab_lib.Tab('qux', base.Html('<h1>qux</h1>')),
49
49
  ])
50
50
  self.assertEqual(len(scripts), 6)
51
+ with tab.track_scripts() as scripts:
52
+ tab.insert(0, tab_lib.Tab('x', 'foo', name='x'))
53
+ self.assertEqual(len(scripts), 2)
54
+ self.assertEqual(len(tab.tabs), 5)
55
+ self.assertEqual(tab.indexof(-1), 4)
56
+ self.assertEqual(tab.indexof(3), 3)
57
+ self.assertEqual(tab.indexof(10), 4)
58
+ self.assertEqual(tab.indexof(-10), -1)
59
+ self.assertEqual(tab.indexof('x'), 0)
60
+ self.assertEqual(tab.indexof('y'), -1)
61
+ self.assertEqual(tab.select(0), 0)
62
+ self.assertEqual(tab.select('x'), 'x')
63
+ self.assertEqual(tab.select(['y', 'x']), 'x')
64
+
65
+ with self.assertRaisesRegex(ValueError, 'Tab not found'):
66
+ tab.select('y')
67
+ with self.assertRaisesRegex(ValueError, 'Tab not found'):
68
+ tab.insert('y', tab_lib.Tab('z', 'bar'))
69
+
70
+ with tab.track_scripts() as scripts:
71
+ tab.insert('x', tab_lib.Tab('y', 'bar'))
72
+ self.assertEqual(len(scripts), 2)
73
+ self.assertEqual(len(tab.tabs), 6)
74
+
75
+ with tab.track_scripts() as scripts:
76
+ tab.select(3)
77
+ self.assertEqual(len(scripts), 1)
78
+ self.assertEqual(tab.selected, 3)
79
+
80
+ with tab.track_scripts() as scripts:
81
+ tab.select('x')
82
+ self.assertEqual(len(scripts), 1)
83
+ self.assertEqual(tab.selected, 1)
51
84
 
52
85
 
53
86
  if __name__ == '__main__':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyglove
3
- Version: 0.4.5.dev202412100810
3
+ Version: 0.4.5.dev202412130809
4
4
  Summary: PyGlove: A library for manipulating Python objects.
5
5
  Home-page: https://github.com/google/pyglove
6
6
  Author: PyGlove Authors
@@ -72,7 +72,7 @@ It's commonly used in:
72
72
  PyGlove has been [published](https://proceedings.neurips.cc/paper/2020/file/012a91467f210472fab4e11359bbfef6-Paper.pdf)
73
73
  at NeurIPS 2020. It is widely used within [Alphabet](https://abc.xyz/), including Google Research, Google Cloud, Youtube and Waymo.
74
74
 
75
- PyGlove is developed by Daiyi Peng and colleagues in [Google Brain Team](https://research.google/teams/brain/).
75
+ PyGlove is developed by Daiyi Peng and colleagues at [Google Brain](https://research.google/teams/brain/).
76
76
 
77
77
 
78
78
  ## Hello PyGlove
@@ -145,6 +145,9 @@ pip install pyglove --pre
145
145
  * [Interactive SVG: Components for Direct Manipulation](https://github.com/google/pyglove/blob/main/docs/notebooks/python/interactive_svg.ipynb)
146
146
  * [Where is the Duck: Developing Context-aware Component](https://github.com/google/pyglove/blob/main/docs/notebooks/python/where_is_the_duck.ipynb)
147
147
 
148
+ * Interactive Programming
149
+ * [Viewing PyGlove objects in HTML](https://colab.research.google.com/github/google/pyglove/blob/main/docs/notebooks/gui/html_view.ipynb)
150
+
148
151
  ## Citing PyGlove
149
152
 
150
153
  ```
@@ -149,12 +149,12 @@ pyglove/core/views/html/tree_view.py,sha256=3Db0aOke-A7Inp-vgqebsDMwJ0Wvaznithkw
149
149
  pyglove/core/views/html/tree_view_test.py,sha256=whUorrw0eiDaZsEzGB2B3EN3wx0vLFuNEe2RBU03GeU,74707
150
150
  pyglove/core/views/html/controls/__init__.py,sha256=61qs5pnJPCTECCGBtkbNfIV3KcCu7cxfVNBEtIg1lMo,1318
151
151
  pyglove/core/views/html/controls/base.py,sha256=VV-dDc_myKYlZeiJUXubIJXYKnm1ncEqjlqibN3d4tM,7710
152
- pyglove/core/views/html/controls/label.py,sha256=JVLUByEHZ5uL8Mu65Y4UV33JIBo85Z7pE6Zcug6pOts,5644
153
- pyglove/core/views/html/controls/label_test.py,sha256=4h6j4tZjAqhazp4AGnW1d_ohaeihofpggJ3UkK02Oho,5126
152
+ pyglove/core/views/html/controls/label.py,sha256=dXcYId7ASuNqkzKsWMjJ0iQtecoSsUlUgynNNQZOFAM,5681
153
+ pyglove/core/views/html/controls/label_test.py,sha256=uUYqBSZ0XLOuv4qG20gmoZzA3RxYxpfqT63K0azHEfU,5162
154
154
  pyglove/core/views/html/controls/progress_bar.py,sha256=kLdY3JQLCOv00ShldnQg3Qs8l1j_pDk148FoKfrZB64,5275
155
- pyglove/core/views/html/controls/progress_bar_test.py,sha256=6u1A6kAV8GY_vUMD6PSl7HuuOkVMELYqU32YEVx6qz8,3500
156
- pyglove/core/views/html/controls/tab.py,sha256=HpHKprScIPtAgC4WbSk_ComU_TayC8q_aj54zZUc0ik,6858
157
- pyglove/core/views/html/controls/tab_test.py,sha256=4tf8kiECcuY6vEcdgJWCY09UAfqMSwA2eJ-b-hxvLJw,2310
155
+ pyglove/core/views/html/controls/progress_bar_test.py,sha256=kKOJDZQtBPkmNcgIBrRQkNNzcTm51ojuFBTRUEDSsp0,3506
156
+ pyglove/core/views/html/controls/tab.py,sha256=ELyroRb3qKQ8ULM_BMq2j-tqV_mnHOvrYv2kbuIe65o,9430
157
+ pyglove/core/views/html/controls/tab_test.py,sha256=wr1msQjZHxp1TFKQ5LxtppY2Dys736JCmmEJ8WBC3Fo,3531
158
158
  pyglove/core/views/html/controls/tooltip.py,sha256=01BbpuM1twf3FYMUT09_Ck5JSSONe8QE9RmyA9nhCnU,3092
159
159
  pyglove/core/views/html/controls/tooltip_test.py,sha256=17BY-WmZKpz9tCbySPcwG6KJyfeE_MeMyKxtfxorBQ0,3194
160
160
  pyglove/ext/__init__.py,sha256=3jp8cJvKW6PENOZlmVAbT0w-GBRn_kjhc0wDX3XjpOE,755
@@ -196,8 +196,8 @@ pyglove/ext/scalars/randoms.py,sha256=LkMIIx7lOq_lvJvVS3BrgWGuWl7Pi91-lA-O8x_gZs
196
196
  pyglove/ext/scalars/randoms_test.py,sha256=nEhiqarg8l_5EOucp59CYrpO2uKxS1pe0hmBdZUzRNM,2000
197
197
  pyglove/ext/scalars/step_wise.py,sha256=IDw3tuTpv0KVh7AN44W43zqm1-E0HWPUlytWOQC9w3Y,3789
198
198
  pyglove/ext/scalars/step_wise_test.py,sha256=TL1vJ19xVx2t5HKuyIzGoogF7N3Rm8YhLE6JF7i0iy8,2540
199
- pyglove-0.4.5.dev202412100810.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
200
- pyglove-0.4.5.dev202412100810.dist-info/METADATA,sha256=DqoXAklEqKdWrQYxgzBOkjT_wpmSGfKzZse1unmazpk,6666
201
- pyglove-0.4.5.dev202412100810.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
202
- pyglove-0.4.5.dev202412100810.dist-info/top_level.txt,sha256=wITzJSKcj8GZUkbq-MvUQnFadkiuAv_qv5qQMw0fIow,8
203
- pyglove-0.4.5.dev202412100810.dist-info/RECORD,,
199
+ pyglove-0.4.5.dev202412130809.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
200
+ pyglove-0.4.5.dev202412130809.dist-info/METADATA,sha256=nDfx3t7PuUS5y9KUG0VnnEaDmtF90a_8a9r16-1Q1m0,6828
201
+ pyglove-0.4.5.dev202412130809.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
202
+ pyglove-0.4.5.dev202412130809.dist-info/top_level.txt,sha256=wITzJSKcj8GZUkbq-MvUQnFadkiuAv_qv5qQMw0fIow,8
203
+ pyglove-0.4.5.dev202412130809.dist-info/RECORD,,