1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
import subprocess
from pathlib import Path
import mkdocs_gen_files
import psygnal
from magicgui import widgets
from magicgui.backends import _ipynb, _qtpy
MAKE_IMAGES = True
BACKENDS = {"qt": _qtpy.widgets, "ipynb": _ipynb.widgets}
WIDGETS_PATH = Path("api/widgets")
IMAGES_PATH = Path("images")
WIDGET_PAGE = """
# {name}
{img_link}
Available in backends: {backends}
## Signals
{signals}
::: magicgui.widgets.{name}
handler: widget_handler
options:
docstring_options:
warn_unknown_params: false
"""
def _set_dark_mode(dark_mode: bool):
"""Set the system to dark mode or not."""
cmd = ["osascript", "-l", "JavaScript", "-e"]
if dark_mode:
cmd += ["Application('System Events').appearancePreferences.darkMode = true"]
else:
cmd += ["Application('System Events').appearancePreferences.darkMode = false"]
subprocess.run(cmd)
# values just for the sake of making the images for docs
VALS = {
widgets.Password: "password",
widgets.FileEdit: "/Users/user/Desktop",
widgets.FloatRangeSlider: (200, 800),
widgets.RangeSlider: (200, 800),
widgets.CheckBox: True,
widgets.LineEdit: "Some value",
widgets.TextEdit: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
widgets.Label: "Some label",
widgets.LiteralEvalLineEdit: "{'a': 1, 'b': 2}",
widgets.ProgressBar: 420,
widgets.QuantityEdit: "1 meter",
widgets.Slider: 42,
widgets.Table: [{"a": 1, "b": 2}, {"a": 3, "b": 4}],
}
def _add_value(obj: widgets.bases.ValueWidget) -> None:
if isinstance(obj, widgets.bases.ButtonWidget):
obj.text = "Click me!"
if obj.__class__ in VALS:
obj.value = VALS[obj.__class__]
return
if isinstance(obj, widgets.bases.CategoricalWidget):
obj.choices = ["Choice A", "Choice B", "Choice C"]
obj.value = "Choice A"
if isinstance(obj, widgets.Dialog):
obj.append(widgets.Label(value="Are you sure?"))
def _snap_image(_obj: type, _name: str) -> str:
from qtpy.QtWidgets import QVBoxLayout, QWidget
outer = QWidget()
if _obj is widgets.Container:
return ""
if issubclass(_obj, widgets.FunctionGui):
return ""
if issubclass(_obj, (widgets.TupleEdit, widgets.ListEdit)):
wdg = _obj(value=(1, 2, 3))
else:
wdg = _obj()
_add_value(wdg)
outer.setLayout(QVBoxLayout())
outer.layout().addWidget(wdg.native)
pth = IMAGES_PATH / f"{_name}.png"
with mkdocs_gen_files.open(pth, "wb") as f:
outer.setMinimumWidth(300) # turns out this is very important for grab
outer.grab().save(f.name)
return f'{{ loading=lazy, class="widget-image" }}'
def _get_signals(cls: type[widgets.Widget]) -> list[psygnal.Signal]:
signals = []
for name in dir(cls):
if name.startswith("_"):
continue
attr = getattr(cls, name)
if isinstance(attr, psygnal.Signal):
signals.append(attr)
return signals
def main() -> None:
for name in dir(widgets):
if name.startswith("_"):
continue
cls = getattr(widgets, name)
if (
isinstance(cls, type)
and issubclass(cls, widgets.Widget)
and cls is not widgets.Widget
and cls.__name__ == name
):
backends = [
f"`{key}`" for key, module in BACKENDS.items() if hasattr(module, name)
]
img_link = _snap_image(cls, name) if MAKE_IMAGES else ""
_signals = _get_signals(cls)
siglines = []
for sig in _signals:
param = sig.signature.parameters
sigstr = ", ".join([str(p.annotation.__name__) for p in param.values()])
siglines.append(f"* **`{sig._name}({sigstr})`** - {sig.description}")
with mkdocs_gen_files.open(WIDGETS_PATH / f"{name}.md", "w") as f:
md = WIDGET_PAGE.format(
name=name,
backends=", ".join(backends),
img_link=img_link,
signals="\n".join(siglines),
)
f.write(md)
main()
|