-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optional widget wrapper #73
Changes from all commits
ebe8975
674adad
5d5c63f
0fe8fe8
057b8ce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# SPDX-License-Identifier: BSD-3-Clause | ||
# Copyright (c) 2024 Scipp contributors (https://github.com/scipp) | ||
from typing import Any | ||
|
||
from ipywidgets import HTML, HBox, Layout, RadioButtons, Widget | ||
|
||
from ._config import default_style | ||
|
||
|
||
class OptionalWidget(HBox): | ||
"""Wrapper widget to handle optional widgets. | ||
|
||
When you retrieve the value of this widget, | ||
it will return the value of the wrapped widget. | ||
The parameter should be set as ``None`` or the actual value. | ||
""" | ||
|
||
def __init__(self, wrapped: Widget, name: str = '', **kwargs) -> None: | ||
self.name = name | ||
self.wrapped = wrapped | ||
self._option_box = RadioButtons( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it's simpler to use a single checkbox here? That would let us remove the layout code, since we would not have to manually place the two radio elements on the same line. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or, same but using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But it doesn't matter much. If you think it's better like it is just go with that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I didn't want to use checkbox because we are using it for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I made this note in the #74 |
||
description="", | ||
style=default_style, | ||
layout=Layout(width="auto", min_width="80px"), | ||
options={str(None): None, "": self.name}, | ||
) | ||
self._option_box.value = None | ||
if hasattr(wrapped, "disabled"): | ||
# Disable the wrapped widget by default if possible | ||
# since the option box is set to None by default | ||
wrapped.disabled = True | ||
|
||
# Make the wrapped radio box horizontal | ||
self.add_class("widget-optional") | ||
_style_html = HTML( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see comment above about checkbox, I think that would let us remove this |
||
"<style>.widget-optional .widget-radio-box " | ||
"{flex-direction: row !important;} </style>", | ||
layout=Layout(display="none"), | ||
) | ||
|
||
def disable_wrapped(change) -> None: | ||
if change["new"] is None: | ||
if hasattr(wrapped, "disabled"): | ||
wrapped.disabled = True | ||
else: | ||
if hasattr(wrapped, "disabled"): | ||
wrapped.disabled = False | ||
|
||
self._option_box.observe(disable_wrapped, names="value") | ||
|
||
super().__init__([self._option_box, wrapped, _style_html], **kwargs) | ||
|
||
@property | ||
def value(self) -> Any: | ||
if self._option_box.value is None: | ||
self._option_box.value = None | ||
return None | ||
return self.wrapped.value | ||
|
||
@value.setter | ||
def value(self, value: Any) -> None: | ||
if value is None: | ||
self._option_box.value = None | ||
else: | ||
self._option_box.value = self.name | ||
self.wrapped.value = value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this decorator meant to be used elsewhere? Right now it's only used in one place as far as I can tell.
Maybe we can just put the logic inside that function for now and make it a decorator later if the logic is reused elsewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't put it inside because of the singledispatch decorator.
You'll have to do it in every dispatch registered functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aha I see