Initial support for pydantic models
A start on #98: support for pydantic models.
I say a "start" because I'm certain there are going to be types that are rather easy to declare in pydantic, for which generating a widget is not straightforward.
@aeisenbarth, I'd be curious to hear your experience if you pull this and try it with your models. See examples/pydantic_model ... (basic idea is that magicui() now also takes a BaseModel as an argument
Codecov Report
Merging #318 (4eafd75) into main (b30588d) will decrease coverage by
1.69%. The diff coverage is21.42%.
@@ Coverage Diff @@
## main #318 +/- ##
==========================================
- Coverage 90.33% 88.64% -1.70%
==========================================
Files 29 30 +1
Lines 3364 3442 +78
==========================================
+ Hits 3039 3051 +12
- Misses 325 391 +66
| Impacted Files | Coverage Δ | |
|---|---|---|
| magicgui/_pydantic.py | 0.00% <0.00%> (ø) |
|
| magicgui/type_map.py | 90.42% <38.88%> (-5.49%) |
:arrow_down: |
| magicgui/_magicgui.py | 91.37% <40.00%> (-4.92%) |
:arrow_down: |
| magicgui/types.py | 97.61% <75.00%> (-2.39%) |
:arrow_down: |
| magicgui/__init__.py | 62.50% <100.00%> (+2.50%) |
:arrow_up: |
| magicgui/backends/_qtpy/widgets.py | 86.26% <100.00%> (+0.03%) |
:arrow_up: |
| magicgui/widgets/_bases/container_widget.py | 92.34% <100.00%> (ø) |
|
| magicgui/widgets/_bases/create_widget.py | 97.22% <100.00%> (ø) |
|
| magicgui/widgets/_concrete.py | 84.63% <100.00%> (ø) |
Continue to review full report at Codecov.
Legend - Click here to learn more
Δ = absolute <relative> (impact),ø = not affected,? = missing dataPowered by Codecov. Last update b30588d...4eafd75. Read the comment docs.
Thanks, this looks good! In my GUI, I wasn't annotating a function signature but I subclassed Container and added widgets using the direct widget API. This GUI class is then registered as a dock, and it has a model attribute which is the Pydantic model (the GUI class itself was not a Pydantic class). So it took me a while to see how I best integrate this.
I tried two approaches:
-
Keep my Pydantic model under the
modelattribute, convert it to a widget and add it to the containerclass MyDock(Container): … inputs = model_widget(self.model.__class__) # Important: I used to instantiate my model, but here the class is needed. self.append(inputs)- Pro: It is easy to add more widgets (buttons, layer selector) and you are not required to have them in the model.
- Issues: I tried the
Tuple[int, int]annotation and it can only have a label/tooltip on container of the tuple, not different texts individually on the inputs for each int. Alternatively I used a nested pydantic model (class Shape(BaseModel) … grid_shape: Shape = Field(title="Shape", ui_layout="horizontal")) with two int attributes, but the container created from this submodel would not take up the submodel's properties ("grid_shape" instead of "Shape", and vertical layout instead of horizontal)
-
Turn the dock into a Pydantic model (
class MyDock(BaseModel)- In my UI init function added to this model class, it is not clear how to access the input widgets to connect them to further functions because accessing an attribute gives its value, not the created widget.
- Also I could not append extra widgets that are not represented in the model. Underscored attributes (
_my_private_attribute) would cause errors in Pydantic.
pydantic -> widgets? If so I think that would make a great example/ call out because I see that as a major usecase of this too. Maybe once this is merged we could then drop our vendored qtjsonschema and use this instead (cc @ppwadhwa @goanpeca).
Thanks for the ping @sofroniewn. We could do pydantic -> widgets directly, but need to make sure to add the ability to provide custom widgets that follow an interface, we could actually reuse some of the qtjsonschema code.
closing this in favor of #446