Skip to content
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

Refactored the RangeEditor for Qt and Wx to be more consistent #1783

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 3 additions & 1 deletion traitsui/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

from .item import Item

UNITTESTING = False
# Reference to an EditorFactory object
factory_trait = Instance(EditorFactory)

Expand Down Expand Up @@ -164,7 +165,8 @@ def error(self, excp):
excp : Exception
The exception which occurred.
"""
pass
if UNITTESTING:
raise excp

def set_focus(self):
"""Assigns focus to the editor's underlying toolkit widget.
Expand Down
3 changes: 3 additions & 0 deletions traitsui/editor_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ class EditorFactory(HasPrivateTraits):
#: The editor class to use for 'readonly' style views.
readonly_editor_class = Property()

#: Show the error dialog when an error occur.
show_error_dialog = Bool(True)

def __init__(self, *args, **traits):
"""Initializes the factory object."""
HasPrivateTraits.__init__(self, **traits)
Expand Down
23 changes: 9 additions & 14 deletions traitsui/editors/range_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

""" Defines the range editor factory for all traits user interface toolkits.
"""
import ast
import warnings

from types import CodeType
Expand Down Expand Up @@ -130,12 +131,6 @@ def init(self, handler=None):
self.high = eval(handler._high)
else:
self.high = handler._high
else:
if (self.low is None) and (self.low_name == ""):
self.low = 0.0

if (self.high is None) and (self.high_name == ""):
self.high = 1.0

def _get_low(self):
return self._low
Expand Down Expand Up @@ -217,25 +212,25 @@ def _get_simple_editor_class(self):
The type of editor depends on the type and extent of the range being
edited:

* One end of range is unspecified: RangeTextEditor
* **mode** is specified and not 'auto': editor corresponding to
**mode**
* One end of range is unspecified: RangeTextEditor
* Floating point range with extent > 100: LargeRangeSliderEditor
* Integer range or floating point range with extent <= 100:
SimpleSliderEditor
* All other cases: SimpleSpinEditor
"""
low, high, is_float = self._low_value, self._high_value, self.is_float

if self.mode != "auto":
return toolkit_object("range_editor:SimpleEditorMap")[self.mode]

if (low is None) or (high is None):
return toolkit_object("range_editor:RangeTextEditor")

if (not is_float) and (abs(high - low) > 1000000000):
return toolkit_object("range_editor:RangeTextEditor")

if self.mode != "auto":
return toolkit_object("range_editor:SimpleEditorMap")[self.mode]

if is_float and (abs(high - low) > 100):
return toolkit_object("range_editor:LargeRangeSliderEditor")

Expand All @@ -250,21 +245,21 @@ def _get_custom_editor_class(self):
The type of editor depends on the type and extent of the range being
edited:

* One end of range is unspecified: RangeTextEditor
* **mode** is specified and not 'auto': editor corresponding to
**mode**
* One end of range is unspecified: RangeTextEditor
* Floating point range: Same as "simple" style
* Integer range with extent > 15: Same as "simple" style
* Integer range with extent <= 15: CustomEnumEditor

"""
low, high, is_float = self._low_value, self._high_value, self.is_float
if (low is None) or (high is None):
return toolkit_object("range_editor:RangeTextEditor")

if self.mode != "auto":
return toolkit_object("range_editor:CustomEditorMap")[self.mode]

if (low is None) or (high is None):
return toolkit_object("range_editor:RangeTextEditor")

if is_float or (abs(high - low) > 15):
return self.simple_editor_class

Expand Down
40 changes: 35 additions & 5 deletions traitsui/examples/demo/Standard_Editors/RangeEditor_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,48 @@
.. _RangeEditor API docs: https://docs.enthought.com/traitsui/api/traitsui.editors.range_editor.html#traitsui.editors.range_editor.RangeEditor
"""

from traits.api import HasTraits, Range
from traits.api import HasTraits, Range as _Range
from traitsui.api import Item, Group, View


# TODO: Update traits.api.Range with the following in order to avoid the popup-error-dialog.
# TODO: Remove redefinition of Range here once the traits.api.Range is updated.
class Range(_Range):
def create_editor(self):
""" Returns the default UI editor for the trait.
"""
# fixme: Needs to support a dynamic range editor.

auto_set = self.auto_set
if auto_set is None:
auto_set = True
show_error_dialog = self.show_error_dialog
if show_error_dialog is None:
show_error_dialog = True

from traitsui.api import RangeEditor
return RangeEditor(
self,
mode=self.mode or "auto",
cols=self.cols or 3,
auto_set=auto_set,
enter_set=self.enter_set or False,
low_label=self.low or "",
high_label=self.high or "",
low_name=self._low_name,
high_name=self._high_name,
show_error_dialog=show_error_dialog
)


class RangeEditorDemo(HasTraits):
"""Defines the RangeEditor demo class."""

# Define a trait for each of four range variants:
small_int_range = Range(1, 16)
medium_int_range = Range(1, 25)
large_int_range = Range(1, 150)
float_range = Range(0.0, 150.0)
small_int_range = Range(1, 16, show_error_dialog=False)
medium_int_range = Range(1, 25, show_error_dialog=False)
large_int_range = Range(1, 150, show_error_dialog=False)
float_range = Range(0.0, 150.0, show_error_dialog=False)

# RangeEditor display for narrow integer Range traits (< 17 wide):
int_range_group1 = Group(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,18 +129,19 @@ def test_run_demo(self):
simple_float_slider.perform(KeyClick("Page Up"))
self.assertEqual(demo.float_range, 1.000)
simple_float_text = simple_float.locate(Textbox())
for _ in range(3):
displayed = simple_float_text.inspect(DisplayedText())
for _ in range(len(displayed) - 2):
simple_float_text.perform(KeyClick("Backspace"))
simple_float_text.perform(KeyClick("5"))
simple_float_text.perform(KeyClick("Enter"))
self.assertEqual(demo.float_range, 1.5)

custom_float_slider = custom_float.locate(Slider())
# after the trait is set to 1.5 above, the active range shown by
# the LargeRangeSliderEditor for the custom style is [0,11.500]
# so a page down is now a decrement of 1.15
# the LargeRangeSliderEditor for the custom style is [0,10.00]
# so a page down is now a decrement of 1.0
custom_float_slider.perform(KeyClick("Page Down"))
self.assertEqual(round(demo.float_range, 2), 0.35)
self.assertEqual(round(demo.float_range, 2), 0.5)
custom_float_text = custom_float.locate(Textbox())
for _ in range(5):
custom_float_text.perform(KeyClick("Backspace"))
Expand Down
34 changes: 18 additions & 16 deletions traitsui/qt4/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,24 @@ def update_editor(self):

def error(self, excp):
"""Handles an error that occurs while setting the object's trait value."""
# Make sure the control is a widget rather than a layout.
if isinstance(self.control, QtGui.QLayout):
control = self.control.parentWidget()
else:
control = self.control

message_box = QtGui.QMessageBox(
QtGui.QMessageBox.Information,
self.description + " value error",
str(excp),
buttons=QtGui.QMessageBox.Ok,
parent=control,
)
message_box.setTextFormat(QtCore.Qt.PlainText)
message_box.setEscapeButton(QtGui.QMessageBox.Ok)
message_box.exec_()
super().error(excp)
if self.factory.show_error_dialog:
# Make sure the control is a widget rather than a layout.
if isinstance(self.control, QtGui.QLayout):
control = self.control.parentWidget()
else:
control = self.control

message_box = QtGui.QMessageBox(
QtGui.QMessageBox.Information,
self.description + " value error",
str(excp),
buttons=QtGui.QMessageBox.Ok,
parent=control,
)
message_box.setTextFormat(QtCore.Qt.PlainText)
message_box.setEscapeButton(QtGui.QMessageBox.Ok)
message_box.exec_()

def set_tooltip_text(self, control, text):
"""Sets the tooltip for a specified control."""
Expand Down
Loading