1
1
import importlib .metadata
2
2
from pathlib import Path
3
- from typing import Any
3
+ from typing import Any , Literal , get_args
4
4
5
5
import anywidget
6
6
import ipywidgets
@@ -40,8 +40,12 @@ class GraphvizAnyWidget(anywidget.AnyWidget):
40
40
freeze_scroll = traitlets .Bool (False ).tag (sync = True ) # noqa: FBT003
41
41
42
42
43
+ Controls = Literal ["zoom" , "search" , "direction" ]
44
+
45
+
43
46
def graphviz_widget (
44
47
dot_source : str = "digraph { a -> b; b -> c; c -> a; }" ,
48
+ controls : bool | Controls | list [Controls ] = True ,
45
49
) -> ipywidgets .VBox :
46
50
"""Create a full-featured interactive Graphviz visualization widget.
47
51
@@ -50,6 +54,17 @@ def graphviz_widget(
50
54
dot_source
51
55
The DOT language string representing the graph.
52
56
Default is a simple cyclic graph: "digraph { a -> b; b -> c; c -> a; }"
57
+ controls
58
+ Controls to display above the graph. Can be:
59
+
60
+ - ``True``: show all controls
61
+ - ``False``: hide all controls
62
+ - ``"zoom"``: show only zoom-related controls (reset and freeze scroll)
63
+ - ``"search"``: show only search-related controls (search box, type selector, case toggle)
64
+ - ``"direction"``: show only direction selector
65
+ - list of the above strings to show multiple control groups
66
+
67
+ Default is True (show all controls).
53
68
54
69
Returns
55
70
-------
@@ -124,6 +139,15 @@ def graphviz_widget(
124
139
def reset_graph (_ : Any ) -> None :
125
140
widget .send ({"action" : "reset_zoom" })
126
141
142
+ def toggle_freeze_scroll (change : dict ) -> None :
143
+ widget .freeze_scroll = change ["new" ]
144
+ if widget .freeze_scroll :
145
+ freeze_toggle .description = "Unfreeze Scroll"
146
+ freeze_toggle .button_style = "danger"
147
+ else :
148
+ freeze_toggle .description = "Freeze Scroll"
149
+ freeze_toggle .button_style = "primary"
150
+
127
151
def update_direction (change : dict ) -> None :
128
152
widget .selected_direction = change ["new" ]
129
153
@@ -136,40 +160,49 @@ def update_search_type(change: dict) -> None:
136
160
def toggle_case_sensitive (change : dict ) -> None :
137
161
widget .case_sensitive = change ["new" ]
138
162
139
- def toggle_freeze_scroll (change : dict ) -> None :
140
- widget .freeze_scroll = change ["new" ]
141
- if widget .freeze_scroll :
142
- freeze_toggle .description = "Unfreeze Scroll"
143
- freeze_toggle .button_style = "danger"
144
- else :
145
- freeze_toggle .description = "Freeze Scroll"
146
- freeze_toggle .button_style = "primary"
147
-
148
163
reset_button .on_click (reset_graph )
164
+ freeze_toggle .observe (toggle_freeze_scroll , names = "value" )
149
165
direction_selector .observe (update_direction , names = "value" )
150
166
search_input .observe (perform_search , names = "value" )
151
167
search_type_selector .observe (update_search_type , names = "value" )
152
168
case_toggle .observe (toggle_case_sensitive , names = "value" )
153
- freeze_toggle .observe (toggle_freeze_scroll , names = "value" )
154
169
155
- # Display ipywidgets
156
- return ipywidgets .VBox (
157
- [
158
- ipywidgets .HBox (
159
- [
160
- reset_button ,
161
- freeze_toggle ,
162
- direction_selector ,
163
- search_input ,
164
- search_type_selector ,
165
- case_toggle ,
166
- ],
167
- layout = ipywidgets .Layout (gap = "8px" ),
168
- ),
169
- widget ,
170
- ],
170
+ zoom_widgets = [reset_button , freeze_toggle ]
171
+ search_widgets = [search_input , search_type_selector , case_toggle ]
172
+
173
+ controls_box = ipywidgets .HBox (
174
+ [* zoom_widgets , direction_selector , * search_widgets ],
175
+ layout = ipywidgets .Layout (gap = "8px" ),
171
176
)
172
177
178
+ # Set visibility of controls based on the `controls` parameter
179
+ if isinstance (controls , bool ):
180
+ if not controls :
181
+ controls_box .layout .visibility = "hidden"
182
+ else :
183
+ if isinstance (controls , str ):
184
+ controls = [controls ]
185
+ for w in controls_box .children :
186
+ w .layout .visibility = "hidden"
187
+ for control in controls :
188
+ if control == "search" :
189
+ for w in search_widgets :
190
+ w .layout .visibility = "visible"
191
+ elif control == "zoom" :
192
+ for w in zoom_widgets :
193
+ w .layout .visibility = "visible"
194
+ elif control == "direction" :
195
+ direction_selector .layout .visibility = "visible"
196
+ else :
197
+ options = get_args (Controls )
198
+ msg = (
199
+ f"Unknown control: `{ control } `."
200
+ f" Valid options are: { ', ' .join (options )} or lists of them."
201
+ )
202
+ raise ValueError (msg )
203
+
204
+ return ipywidgets .VBox ([controls_box , widget ])
205
+
173
206
174
207
def graphviz_widget_simple (
175
208
dot_source : str = "digraph { a -> b; b -> c; c -> a; }" ,
0 commit comments