@@ -387,8 +387,88 @@ def add_projection_toggle_button(
387387 self .add_button (
388388 label = label ,
389389 method = "relayout" ,
390- args = [orthographic_projection ],
391- args2 = [perspective_projection ],
390+ args = [perspective_projection ],
391+ args2 = [orthographic_projection ],
392392 x = x ,
393393 y = y
394394 )
395+
396+ def add_theme_toggle_button (
397+ self ,
398+ label : str = "Toggle Theme" ,
399+ x : float = 0.2175 ,
400+ y : float = 1.02
401+ ) -> None :
402+ """Add a button to toggle between light and dark themes.
403+
404+ Parameters
405+ ----------
406+ label : str, optional
407+ The text to display on the button, by default "Toggle Theme".
408+ x : float, optional
409+ X position of the button (0-1), by default 0.22.
410+ y : float, optional
411+ Y position of the button (0-1), by default 1.02.
412+ """
413+ # Define light theme properties manually to avoid JSON serialization issues
414+ # Use dot notation to target specific properties without overriding the entire scene
415+ # Colors extracted from official plotly template
416+ light_theme = {
417+ "paper_bgcolor" : "white" ,
418+ "plot_bgcolor" : "#E5ECF6" ,
419+ "font.color" : "#2a3f5f" ,
420+ "scene.xaxis.backgroundcolor" : "#E5ECF6" ,
421+ "scene.xaxis.gridcolor" : "white" ,
422+ "scene.xaxis.linecolor" : "white" ,
423+ "scene.xaxis.zerolinecolor" : "white" ,
424+ "scene.yaxis.backgroundcolor" : "#E5ECF6" ,
425+ "scene.yaxis.gridcolor" : "white" ,
426+ "scene.yaxis.linecolor" : "white" ,
427+ "scene.yaxis.zerolinecolor" : "white" ,
428+ "scene.zaxis.backgroundcolor" : "#E5ECF6" ,
429+ "scene.zaxis.gridcolor" : "white" ,
430+ "scene.zaxis.linecolor" : "white" ,
431+ "scene.zaxis.zerolinecolor" : "white"
432+ }
433+
434+ # Define dark theme properties manually
435+ # Colors extracted from official plotly_dark template
436+ dark_theme = {
437+ "paper_bgcolor" : "rgb(17,17,17)" ,
438+ "plot_bgcolor" : "rgb(17,17,17)" ,
439+ "font.color" : "#f2f5fa" ,
440+ "scene.xaxis.backgroundcolor" : "rgb(17,17,17)" ,
441+ "scene.xaxis.gridcolor" : "#506784" ,
442+ "scene.xaxis.linecolor" : "#506784" ,
443+ "scene.xaxis.zerolinecolor" : "#C8D4E3" ,
444+ "scene.yaxis.backgroundcolor" : "rgb(17,17,17)" ,
445+ "scene.yaxis.gridcolor" : "#506784" ,
446+ "scene.yaxis.linecolor" : "#506784" ,
447+ "scene.yaxis.zerolinecolor" : "#C8D4E3" ,
448+ "scene.zaxis.backgroundcolor" : "rgb(17,17,17)" ,
449+ "scene.zaxis.gridcolor" : "#506784" ,
450+ "scene.zaxis.linecolor" : "#506784" ,
451+ "scene.zaxis.zerolinecolor" : "#C8D4E3"
452+ }
453+
454+ # Add styling updates for all existing updatemenus + the theme button we're about to add
455+ # Get the actual number of updatemenus in the figure
456+ current_updatemenus = self ._fig .layout .updatemenus or []
457+ # Add 1 to include the theme button we're about to create
458+ for i in range (len (current_updatemenus ) + 1 ):
459+ light_theme [f"updatemenus[{ i } ].bgcolor" ] = "rgba(255,255,255,0.95)"
460+ light_theme [f"updatemenus[{ i } ].bordercolor" ] = "rgba(0,0,0,0.3)"
461+ light_theme [f"updatemenus[{ i } ].font.color" ] = "black"
462+
463+ dark_theme [f"updatemenus[{ i } ].bgcolor" ] = "rgba(50,50,50,0.95)"
464+ dark_theme [f"updatemenus[{ i } ].bordercolor" ] = "rgba(255,255,255,0.3)"
465+ dark_theme [f"updatemenus[{ i } ].font.color" ] = "grey"
466+
467+ self .add_button (
468+ label = label ,
469+ method = "relayout" ,
470+ args = [light_theme ],
471+ args2 = [dark_theme ],
472+ x = x ,
473+ y = y
474+ )
0 commit comments