nion-software / nionui

Nion UI is a Python UI framework with a Qt backend.
Other
5 stars 15 forks source link

Add some simple operators for ``@binding``'s values #52

Closed Brow71189 closed 2 years ago

Brow71189 commented 2 years ago

I often find myself in a situation where I have a certain boolean flag available in the UI handler already, but some UI element needs the inverted flag or a combination of two (or more). In these situations it would be very useful to be able to use some (boolean) operators in the definition of a binding.

Here is an example with the current implementation:

# Each combination or negation of a flag needs its own full implementation:

class UIHandler:

    @property
    def flag_1(self): ...

    @property
    def flag_2(self): ...

    @property
    def not_flag_2(self): ...

    @property
    def flag_1_and_flag_2(self): ...

# UI implementation:

ui.create_push_button(enabled='@binding(flag_1)')
ui.create_push_button(enabled='@binding(flag_2)')
ui.create_push_button(enabled='@binding(not_flag_2)')
ui.create_push_button(enabled='@binding(flag_1_and_flag_2)')

If we could use some operators in the binding's definition, the handler implementation would get a lot shorter:

class UIHandler:

    @property
    def flag_1(self): ...

    @property
    def flag_2(self): ...

# UI implementation:

ui.create_push_button(enabled='@binding(flag_1)')
ui.create_push_button(enabled='@binding(flag_2)')
ui.create_push_button(enabled='@binding(not flag_2)')
ui.create_push_button(enabled='@binding(flag_1 and flag_2)')

I'm not sure if parsing the logical python operators (not, and, or) is feasible here, but I think also the bitwise operators would do for this purpose (~, &, |).

cmeyer commented 2 years ago

The bindings in nionui are based on WPF bindings:

https://docs.microsoft.com/en-us/dotnet/desktop/wpf/data https://docs.microsoft.com/en-us/dotnet/desktop/wpf/data/binding-declarations-overview

And the suggested WPF technique for inversions is to use a converter:

https://stackoverflow.com/questions/1039636/how-to-bind-inverse-boolean-properties-in-wpf https://stackoverflow.com/questions/534575/how-do-i-invert-booleantovisibilityconverter

Expressions involving multiple inputs (flag1 and flag2) would involve significantly more complex implementation. You're better off just creating new flags instead.

If you really want to go down the rabbit hole, you can auto-create some properties by converting properties to streams (Stream.PropertyChangedEventStream), doing operations on streams (e.g. Stream.MapStream), and then converting back to a property model (Model.StreamValueModel). Then you can bind to the property_model.value. An example of this is here:

https://github.com/nion-software/nionswift-instrumentation-kit/blob/cb7b2a71a613cc09ccd36506af76edae66647de6/nionswift_plugin/nion_instrumentation_ui/AcquisitionPanel.py#L2175

It's "bleeding edge" -- so use similar code with caution (or be prepared to maintain changes).

Note: low level objects from nionutils no longer require close to be called on them as long as you none-out references to instances of the object; however objects from nionui still typically require close. This is a work in progress. See work around August-September 2021 in nionutils.

Brow71189 commented 2 years ago

Ok I see. I'd still prefer to be able to use some operators directly since creating converters takes about as much code as implementing the properties. But I guess this can be closed then.

cmeyer commented 2 years ago

I'm not completely opposed to the idea - but I'd view it as a long term goal. It's not trivial since it typically changes a binding to be read only. In any case, here's another link with some other ideas for the future:

https://www.codeproject.com/Articles/1253153/Expression-bindings-in-XAML