#1227 Maint: Refactor to make NestedUI redundant

Merged Pseudo commit used to compare (81902cc...53ad778)
Missing base report.

Unable to compare commits because the base of the pull request did not upload a coverage report.

Changes found in between 81902cc...53ad778 (pseudo...base) which prevent comparing this pull request.

Showing 35 of 53 files from the diff.
Newly tracked file
traitsui/__init__.py changed.
Newly tracked file
traitsui/wx/enum_editor.py changed.

@@ -40,7 +40,17 @@
Loading
40 40
        "pandas",
41 41
        "tables",
42 42
    ],
43 -
    "test": ["packaging"],
43 +
    "editors": [
44 +
        # Optional dependencies for certain editors which may not be needed by
45 +
        # projects. If they are absent, ``traitsui.api``` should still be
46 +
        # importable and the relevant tests should be skipped.
47 +
        "numpy",   # For ArrayEditor and DataFrameEditor
48 +
        "pandas",  # For DataFrameEditor
49 +
    ],
50 +
    "test": [
51 +
        # Dependencies for running test suites.
52 +
        "packaging",
53 +
    ],
44 54
}
45 55
46 56

@@ -14,9 +14,12 @@
Loading
14 14
from traitsui.testing.tester.wx.implementation import (
15 15
    button_editor,
16 16
    check_list_editor,
17 +
    enum_editor,
18 +
    instance_editor,
17 19
    list_editor,
18 20
    range_editor,
19 21
    text_editor,
22 +
    ui_base,
20 23
)
21 24
22 25
@@ -33,12 +36,17 @@
Loading
33 36
34 37
    common_ui_targets.LocatedTextbox.register(registry)
35 38
39 +
    common_ui_targets.LocatedSlider.register(registry)
40 +
36 41
    # ButtonEditor
37 42
    button_editor.register(registry)
38 43
39 44
    # CheckListEditor
40 45
    check_list_editor.register(registry)
41 46
47 +
    # EnumEditor
48 +
    enum_editor.register(registry)
49 +
42 50
    # TextEditor
43 51
    text_editor.register(registry)
44 52
@@ -48,4 +56,10 @@
Loading
48 56
    # RangeEditor
49 57
    range_editor.register(registry)
50 58
59 +
    # ui_base
60 +
    ui_base.register(registry)
61 +
62 +
    # InstanceEditor
63 +
    instance_editor.register(registry)
64 +
51 65
    return registry

@@ -8,7 +8,6 @@
Loading
8 8
#
9 9
#  Thanks for using Enthought open source!
10 10
#
11 -
12 11
from pyface.qt import QtCore, QtGui
13 12
from pyface.qt.QtTest import QTest
14 13
@@ -47,6 +46,28 @@
Loading
47 46
    )
48 47
49 48
49 +
def check_q_model_index_valid(index):
50 +
    """ Checks if a given QModelIndex is valid.
51 +
52 +
    Parameters
53 +
    ----------
54 +
    index : QModelIndex
55 +
56 +
    Raises
57 +
    ------
58 +
    LookupError
59 +
        If the index is not valid.
60 +
    """
61 +
    if not index.isValid():
62 +
        row = index.row()
63 +
        column = index.column()
64 +
        raise LookupError(
65 +
            "Unabled to locate item with row {!r} and column {!r}.".format(
66 +
                row, column,
67 +
            )
68 +
        )
69 +
70 +
50 71
# Generic Handlers ###########################################################
51 72
52 73
@@ -91,7 +112,32 @@
Loading
91 112
    )
92 113
93 114
94 -
def mouse_click_qlayout(layout, index, delay=0):
115 +
def mouse_click_tab_index(tab_widget, index, delay):
116 +
    """ Performs a mouse click on a tab at an index in a QtGui.QTabWidget.
117 +
118 +
    Parameters
119 +
    ----------
120 +
    tab_widget : QtGui.QTabWidget
121 +
        The tab widget containing the tab to be clicked.
122 +
    index : int
123 +
        The index of the tab to be clicked.
124 +
    delay : int
125 +
        Time delay (in ms) in which click will be performed.
126 +
    """
127 +
    if not 0 <= index < tab_widget.count():
128 +
        raise IndexError(index)
129 +
    tabbar = tab_widget.tabBar()
130 +
    rect = tabbar.tabRect(index)
131 +
    QTest.mouseClick(
132 +
        tabbar,
133 +
        QtCore.Qt.LeftButton,
134 +
        QtCore.Qt.NoModifier,
135 +
        rect.center(),
136 +
        delay=delay,
137 +
    )
138 +
139 +
140 +
def mouse_click_qlayout(layout, index, delay):
95 141
    """ Performs a mouse click on a widget at an index in a QLayout.
96 142
97 143
    Parameters
@@ -111,6 +157,63 @@
Loading
111 157
    )
112 158
113 159
160 +
def mouse_click_item_view(model, view, index, delay):
161 +
    """ Perform mouse click on the given QAbstractItemModel (model) and
162 +
    QAbstractItemView (view) with the given row and column.
163 +
164 +
    Parameters
165 +
    ----------
166 +
    model : QAbstractItemModel
167 +
        Model from which QModelIndex will be obtained
168 +
    view : QAbstractItemView
169 +
        View from which the widget identified by the index will be
170 +
        found and key sequence be performed.
171 +
    index : QModelIndex
172 +
173 +
    Raises
174 +
    ------
175 +
    LookupError
176 +
        If the index cannot be located.
177 +
        Note that the index error provides more
178 +
    """
179 +
    check_q_model_index_valid(index)
180 +
    rect = view.visualRect(index)
181 +
    QTest.mouseClick(
182 +
        view.viewport(),
183 +
        QtCore.Qt.LeftButton,
184 +
        QtCore.Qt.NoModifier,
185 +
        rect.center(),
186 +
        delay=delay,
187 +
    )
188 +
189 +
190 +
def mouse_click_combobox(combobox, index, delay):
191 +
    """ Perform a mouse click on a QComboBox at a given index.
192 +
193 +
    Paramters
194 +
    ---------
195 +
    combobox : QtGui.ComboBox
196 +
        The combobox to be clicked.
197 +
    index : int
198 +
        The index of the item in the combobox to be clicked.
199 +
    delay : int
200 +
        Time delay (in ms) in which each key click in the sequence will be
201 +
        performed.
202 +
    """
203 +
    q_model_index = combobox.model().index(index, 0)
204 +
    check_q_model_index_valid(q_model_index)
205 +
    mouse_click_item_view(
206 +
        model=combobox.model(),
207 +
        view=combobox.view(),
208 +
        index=q_model_index,
209 +
        delay=delay,
210 +
    )
211 +
    # Otherwise the click won't get registered.
212 +
    key_click(
213 +
        combobox.view().viewport(), key="Enter",
214 +
    )
215 +
216 +
114 217
def key_sequence_qwidget(control, interaction, delay):
115 218
    """ Performs simulated typing of a sequence of keys on the given widget
116 219
    after a delay.
@@ -171,3 +274,29 @@
Loading
171 274
    if not control.isEnabled():
172 275
        raise Disabled("{!r} is disabled.".format(control))
173 276
    key_click(control, interaction.key, delay=delay)
277 +
278 +
279 +
def key_click_qslider(control, interaction, delay):
280 +
    """ Performs simulated typing of a key on the given slider after a delay.
281 +
    Only allowed keys are:
282 +
    "Left", "Right", "Up", "Down", "Page Up", "Page Down"
283 +
    Also, note that up related keys correspond to an increment on the slider,
284 +
    and down a decrement.
285 +
286 +
    Parameters
287 +
    ----------
288 +
    control : QSlider
289 +
        The Qt slider to be acted on.
290 +
    interaction : instance of command.KeyClick
291 +
        The interaction object holding the key input
292 +
        to be simulated being typed
293 +
    delay : int
294 +
        Time delay (in ms) in which the key click will be performed.
295 +
    """
296 +
    valid_keys = {"Left", "Right", "Up", "Down", "Page Up", "Page Down"}
297 +
    if interaction.key not in valid_keys:
298 +
        raise ValueError(
299 +
            "Unexpected Key. Supported keys are: {}".format(sorted(valid_keys))
300 +
        )
301 +
    else:
302 +
        key_click(control, interaction.key, delay)

@@ -10,9 +10,9 @@
Loading
10 10
#
11 11
12 12
from traitsui.ui import UI
13 -
from traitsui.testing.tester import locator
14 13
from traitsui.testing.tester.default_registry import get_default_registry
15 14
from traitsui.testing.tester.registry import TargetRegistry
15 +
from traitsui.testing.tester.registry_helper import register_nested_ui_solvers
16 16
from traitsui.testing.tester.ui_wrapper import UIWrapper
17 17
from traitsui.tests._tools import (
18 18
    create_ui as _create_ui,
@@ -125,21 +125,27 @@
Loading
125 125
126 126
    See documentation on ``TargetRegistry`` for details.
127 127
128 -
    Attributes
128 +
    Parameters
129 129
    ----------
130 130
    registries : list of TargetRegistry, optional
131 -
        Registries of interaction for different target, in the order
132 -
        of decreasing priority. A shallow copy will be made.
131 +
        Registries of interaction for different targets, in the order
132 +
        of decreasing priority. If provided, a shallow copy will be made.
133 +
        Default registries are always appended to the list to provide
134 +
        builtin support for TraitsUI UI and editors.
133 135
    delay : int, optional
134 136
        Time delay (in ms) in which actions by the tester are performed. Note
135 -
        it is propogated through to created child wrappers. The delay allows
137 +
        it is propagated through to created child wrappers. The delay allows
136 138
        visual confirmation test code is working as desired. Defaults to 0.
139 +
140 +
    Attributes
141 +
    ----------
142 +
    delay : int
143 +
        Time delay (in ms) in which actions by the tester are performed. Note
144 +
        it is propagated through to created child wrappers. The delay allows
145 +
        visual confirmation test code is working as desired.
137 146
    """
138 147
139 148
    def __init__(self, registries=None, delay=0):
140 -
        """ Instantiate the UI tester.
141 -
        """
142 -
143 149
        if registries is None:
144 150
            self._registries = []
145 151
        else:
@@ -211,83 +217,18 @@
Loading
211 217
        ).find_by_id(id=id)
212 218
213 219
214 -
def _get_editor_by_name(ui, name):
215 -
    """ Return a single Editor from an instance of traitsui.ui.UI with
216 -
    a given extended name. Raise if zero or many editors are found.
217 -
218 -
    Parameters
219 -
    ----------
220 -
    ui : traitsui.ui.UI
221 -
        The UI from which an editor will be retrieved.
222 -
    name : str
223 -
        A single name for retrieving an editor on a UI.
224 -
225 -
    Returns
226 -
    -------
227 -
    editor : Editor
228 -
        The single editor found.
229 -
    """
230 -
    editors = ui.get_editors(name)
231 -
232 -
    all_names = [editor.name for editor in ui._editors]
233 -
    if not editors:
234 -
        raise ValueError(
235 -
            "No editors can be found with name {!r}. "
236 -
            "Found these: {!r}".format(name, all_names)
237 -
        )
238 -
    if len(editors) > 1:
239 -
        raise ValueError(
240 -
            "Found multiple editors with name {!r}.".format(name))
241 -
    editor, = editors
242 -
    return editor
243 -
244 -
245 -
def _get_editor_by_id(ui, id):
246 -
    """ Return single Editor from an instance of traitsui.ui.UI with
247 -
    the given identifier.
248 -
249 -
    Parameters
250 -
    ----------
251 -
    ui : traitsui.ui.UI
252 -
        The UI from which an editor will be retrieved.
253 -
    id : str
254 -
        Id for finding an item in the UI.
255 -
256 -
    Returns
257 -
    -------
258 -
    editor : Editor
259 -
        The single editor found.
260 -
    """
261 -
    try:
262 -
        editor = getattr(ui.info, id)
263 -
    except AttributeError:
264 -
        raise ValueError(
265 -
            "No editors found with id {!r}. Got these: {!r}".format(
266 -
                id, ui._names)
267 -
            )
268 -
    return editor
269 -
270 -
271 220
def _get_ui_registry():
272 221
    """ Return a TargetRegistry with traitsui.ui.UI as the target.
273 222
274 223
    Parameters
275 224
    ----------
276 225
    registry : TargetRegistry
277 226
    """
227 +
278 228
    registry = TargetRegistry()
279 -
    registry.register_solver(
280 -
        target_class=UI,
281 -
        locator_class=locator.TargetByName,
282 -
        solver=lambda wrapper, location: (
283 -
            _get_editor_by_name(wrapper.target, location.name)
284 -
        ),
285 -
    )
286 -
    registry.register_solver(
229 +
    register_nested_ui_solvers(
230 +
        registry=registry,
287 231
        target_class=UI,
288 -
        locator_class=locator.TargetById,
289 -
        solver=lambda wrapper, location: (
290 -
            _get_editor_by_id(wrapper.target, location.id)
291 -
        ),
232 +
        nested_ui_getter=lambda target: target,
292 233
    )
293 234
    return registry

@@ -259,6 +259,7 @@
Loading
259 259
260 260
        if action.on_perform is not None:
261 261
            action.on_perform(selection)
262 +
            return
262 263
263 264
        action.perform(selection)
264 265

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Unable to process changes.

No base report to compare against.

21 Commits

Hiding 2 contexual commits
+2 Files
-39
+62
-1
-100
Hiding 17 contexual commits
+8 Files
+370
+290
+13
+67
Pull Request Base Commit
Files Coverage
traitsui 34.4%
Project Totals (293 files) 34.4%
Loading