Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/releases/upcoming/1684.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add RangeEditor support for format_func (#1684)
24 changes: 24 additions & 0 deletions traitsui/editors/range_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,30 @@ def custom_editor(self, ui, object, name, description, parent):
)
return super().custom_editor(ui, object, name, description, parent)

def string_value(self, value, format_func=None):
""" Returns the text representation of a specified object trait value.

If the **format_func** attribute is set on the editor factory, then
this method calls that function to do the formatting. If the
**format** attribute is set on the editor factory, then this
method uses that string for formatting. If neither attribute is
set, then this method just calls the appropriate text type to format.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no mention of the format_func kwarg to the method in the docstring here.

Tangentially, the priority here seems a little wonky to me - shouldn't format_func passed to the method take priority over the format_func/format set on the factory? I think it should because format_func is being explicitly passed when calling string_value - and format_func/format might have been set on the factory at any earlier point after instantiation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, this is quite strange and unexpected behavior...
I will open an issue pointing to the origin of this method (i.e. EditorFactory.string_value)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've opened #1695


This is slightly modified for the base EditorFactory inplementation to
use this class' ``format`` trait, as opposed to the ``format_str``
trait defined on the base class.
"""
if self.format_func is not None:
return self.format_func(value)

if self.format != "":
return self.format % value

if format_func is not None:
return format_func(value)

return str(value)


# This alias is deprecated and will be removed in TraitsUI 8.
ToolkitEditorFactory = RangeEditor
14 changes: 7 additions & 7 deletions traitsui/qt4/range_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def init(self, parent):
try:
if not (self.low <= fvalue <= self.high):
fvalue = self.low
fvalue_text = self.format % fvalue
fvalue_text = self.factory.string_value(fvalue)
except:
fvalue_text = ""
fvalue = self.low
Expand Down Expand Up @@ -153,11 +153,11 @@ def init(self, parent):

low_label = factory.low_label
if factory.low_name != "":
low_label = self.format % self.low
low_label = self.factory.string_value(self.low)

high_label = factory.high_label
if factory.high_name != "":
high_label = self.format % self.high
high_label = self.factory.string_value(self.high)

self._label_lo.setText(low_label)
self._label_hi.setText(high_label)
Expand All @@ -171,7 +171,7 @@ def update_object_on_scroll(self, pos):
""" Handles the user changing the current slider value.
"""
value = self._convert_from_slider(pos)
self.control.text.setText(self.format % value)
self.control.text.setText(self.factory.string_value(value))
try:
self.value = value
except Exception as exc:
Expand Down Expand Up @@ -221,7 +221,7 @@ def update_editor(self):
low = self.low
high = self.high
try:
text = self.format % value
text = self.factory.string_value(value)
1 / (low <= value <= high)
except:
text = ""
Expand Down Expand Up @@ -250,7 +250,7 @@ def _low_changed(self, low):
self.value = int(low)

if self._label_lo is not None:
self._label_lo.setText(self.format % low)
self._label_lo.setText(self.factory.string_value(low))
self.update_editor()

def _high_changed(self, high):
Expand All @@ -261,7 +261,7 @@ def _high_changed(self, high):
self.value = int(high)

if self._label_hi is not None:
self._label_hi.setText(self.format % high)
self._label_hi.setText(self.factory.string_value(high))
self.update_editor()

def _convert_to_slider(self, value):
Expand Down
46 changes: 46 additions & 0 deletions traitsui/tests/editors/test_range_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,3 +320,49 @@ def test_modify_slider_log_range_slider(self):
displayed = text.inspect(DisplayedText())
self.assertEqual(model.float_value, 10.0)
self.assertEqual(displayed, str(model.float_value))

def test_format_func(self):

def num_to_time(num):
minutes = int(num / 60)
if minutes < 10:
minutes_str = '0' + str(minutes)
else:
minutes_str = str(minutes)
seconds = num % 60
if seconds < 10:
seconds_str = '0' + str(seconds)
else:
seconds_str = str(seconds)
return minutes_str + ':' + seconds_str

model = RangeModel()
view = View(
Item(
"float_value",
editor=RangeEditor(format_func=num_to_time)
)
)
tester = UITester()
with tester.create_ui(model, dict(view=view)) as ui:
float_value_field = tester.find_by_name(ui, "float_value")
float_value_text = float_value_field.locate(Textbox())
self.assertEqual(
float_value_text.inspect(DisplayedText()), "00:00.1"
)

def test_format(self):
model = RangeModel()
view = View(
Item(
"float_value",
editor=RangeEditor(format="%s ...")
)
)
tester = UITester()
with tester.create_ui(model, dict(view=view)) as ui:
float_value_field = tester.find_by_name(ui, "float_value")
float_value_text = float_value_field.locate(Textbox())
self.assertEqual(
float_value_text.inspect(DisplayedText()), "0.1 ..."
)
14 changes: 7 additions & 7 deletions traitsui/wx/range_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def init(self, parent):
fvalue = self.low
else:
try:
fvalue_text = self.format % fvalue
fvalue_text = self.factory.string_value(fvalue)
except (ValueError, TypeError) as e:
fvalue_text = ""

Expand Down Expand Up @@ -157,11 +157,11 @@ def init(self, parent):

low_label = factory.low_label
if factory.low_name != "":
low_label = self.format % self.low
low_label = self.factory.string_value(self.low)

high_label = factory.high_label
if factory.high_name != "":
high_label = self.format % self.high
high_label = self.factory.string_value(self.high)

self._label_lo.SetLabel(low_label)
self._label_hi.SetLabel(high_label)
Expand Down Expand Up @@ -191,7 +191,7 @@ def update_object_on_scroll(self, event):
):
try:
self.ui_changing = True
self.control.text.SetValue(self.format % value)
self.control.text.SetValue(self.factory.string_value(value))
self.value = value
except TraitError:
pass
Expand Down Expand Up @@ -253,7 +253,7 @@ def update_editor(self):
"""
value = self.value
try:
text = self.format % value
text = self.factory.string_value(value)
1 // (self.low <= value <= self.high)
except:
text = ""
Expand Down Expand Up @@ -296,7 +296,7 @@ def _low_changed(self, low):
self.value = int(low)

if self._label_lo is not None:
self._label_lo.SetLabel(self.format % low)
self._label_lo.SetLabel(self.factory.string_value(low))
self.update_editor()

def _high_changed(self, high):
Expand All @@ -307,7 +307,7 @@ def _high_changed(self, high):
self.value = int(high)

if self._label_hi is not None:
self._label_hi.SetLabel(self.format % high)
self._label_hi.SetLabel(self.factory.string_value(high))
self.update_editor()


Expand Down