enthought / traitsui
1
# ------------------------------------------------------------------------------
2
#
3
#  Copyright (c) 2005, Enthought, Inc.
4
#  All rights reserved.
5
#
6
#  This software is provided without warranty under the terms of the BSD
7
#  license included in LICENSE.txt and may be redistributed only
8
#  under the conditions described in the aforementioned license.  The license
9
#  is also available online at http://www.enthought.com/licenses/BSD.txt
10
#
11
#  Thanks for using Enthought open source!
12
#
13
#  Author: David C. Morrill
14
#  Date:   10/07/2004
15
#
16
# ------------------------------------------------------------------------------
17

18 4
""" Defines the View class used to represent the structural content of a
19
    Traits-based user interface.
20
"""
21

22

23

24 4
from pyface.ui_traits import Image
25 4
from traits.api import (
26
    Any,
27
    Bool,
28
    Callable,
29
    Enum,
30
    Event,
31
    Float,
32
    Instance,
33
    List,
34
    Str,
35
    Trait,
36
)
37

38 4
from .view_element import ViewElement, ViewSubElement
39

40 4
from .ui import UI
41

42 4
from .ui_traits import (
43
    AButton,
44
    AnObject,
45
    Buttons,
46
    DockStyle,
47
    EditorStyle,
48
    ExportType,
49
    HelpId,
50
    Image,
51
    SequenceTypes,
52
    ViewStatus,
53
)
54

55 4
from .handler import Handler, default_handler
56

57 4
from .group import Group
58

59 4
from .item import Item
60

61 4
from .include import Include
62

63 4
from .helper import PrefixList
64

65
# -------------------------------------------------------------------------
66
#  Trait definitions:
67
# -------------------------------------------------------------------------
68

69
# Name of the view trait:
70 4
AnId = Str(desc="the name of the view")
71

72
# Contents of the view trait (i.e., a single Group object):
73 4
Content = Instance(Group, desc="the content of the view")
74

75
# An optional model/view factory for converting the model into a viewable
76
# 'model_view' object
77 4
AModelView = Callable(
78
    desc="the factory function for converting a model "
79
    "into a model/view object"
80
)
81

82
# Reference to a Handler object trait:
83 4
AHandler = Any(desc="the handler for the view")
84

85
# Dialog window title trait:
86 4
ATitle = Str(desc="the window title for the view")
87

88
# User interface 'kind' trait. The values have the following meanings:
89
#
90
# * 'panel': An embeddable panel. This type of window is intended to be used as
91
#   part of a larger interface.
92
# * 'subpanel': An embeddable panel that does not display command buttons,
93
#   even if the View specifies them.
94
# * 'modal': A modal dialog box that operates on a clone of the object until
95
#   the user commits the change.
96
# * 'nonmodal':  A nonmodal dialog box that operates on a clone of the object
97
#   until the user commits the change
98
# * 'live': A nonmodal dialog box that immediately updates the object.
99
# * 'livemodal': A modal dialog box that immediately updates the object.
100
# * 'popup': A temporary, frameless popup dialog that immediately updates the
101
#   object and is active only while the mouse pointer is in the dialog.
102
# * 'info': A temporary, frameless popup dialog that immediately updates the
103
#   object and is active only while the dialog is still over the invoking
104
#   control.
105
# * 'wizard': A wizard modal dialog box. A wizard contains a sequence of
106
#   pages, which can be accessed by clicking **Next** and **Back** buttons.
107
#   Changes to attribute values are applied only when the user clicks the
108
#   **Finish** button on the last page.
109 4
AKind = PrefixList(
110
    (
111
        "panel",
112
        "subpanel",
113
        "modal",
114
        "nonmodal",
115
        "livemodal",
116
        "live",
117
        "popup",
118
        "popover",
119
        "info",
120
        "wizard",
121
    ),
122
    default_value='live',
123
    desc="the kind of view window to create",
124
    cols=4,
125
)
126

127
# Apply changes handler:
128 4
OnApply = Callable(
129
    desc="the routine to call when modal changes are applied " "or reverted"
130
)
131

132
# Is the dialog window resizable?
133 4
IsResizable = Bool(False, desc="whether dialog can be resized or not")
134

135
# Is the view scrollable?
136 4
IsScrollable = Bool(False, desc="whether view should be scrollable or not")
137

138
# The valid categories of imported elements that can be dragged into the view:
139 4
ImportTypes = List(
140
    Str, desc="the categories of elements that can be " "dragged into the view"
141
)
142

143
# The view position and size traits:
144 4
Width = Float(-1e6, desc="the width of the view window")
145 4
Height = Float(-1e6, desc="the height of the view window")
146 4
XCoordinate = Float(-1e6, desc="the x coordinate of the view window")
147 4
YCoordinate = Float(-1e6, desc="the y coordinate of the view window")
148

149
# The result that should be returned if the user clicks the window or dialog
150
# close button or icon
151 4
CloseResult = Enum(
152
    None,
153
    True,
154
    False,
155
    desc="the result to return when the user clicks the "
156
    "window or dialog close button or icon",
157
)
158

159
# The KeyBindings trait:
160 4
AKeyBindings = Instance(
161
    "traitsui.key_bindings.KeyBindings",
162
    desc="the global key bindings for the view",
163
)
164

165

166 4
class View(ViewElement):
167
    """ A Traits-based user interface for one or more objects.
168

169
        The attributes of the View object determine the contents and layout of
170
        an attribute-editing window. A View object contains a set of Group,
171
        Item, and Include objects. A View object can be an attribute of an
172
        object derived from HasTraits, or it can be a standalone object.
173
    """
174

175
    # -------------------------------------------------------------------------
176
    #  Trait definitions:
177
    # -------------------------------------------------------------------------
178

179
    #: A unique identifier for the view:
180 4
    id = AnId
181

182
    #: The top-level Group object for the view:
183 4
    content = Content
184

185
    #: The menu bar for the view. Usually requires a custom **handler**:
186 4
    menubar = Any  # Instance( pyface.action.MenuBarManager )
187

188
    #: The toolbar for the view. Usually requires a custom **handler**:
189 4
    toolbar = Any  # Instance( pyface.action.ToolBarManager )
190

191
    #: Status bar items to add to the view's status bar. The value can be:
192
    #:
193
    #:   - **None**: No status bar for the view (the default).
194
    #:   - string: Same as [ StatusItem( name = string ) ].
195
    #:   - StatusItem: Same as [ StatusItem ].
196
    #:   - [ [StatusItem|string], ... ]: Create a status bar with one field for
197
    #:     each StatusItem in the list (or tuple). The status bar fields are
198
    #:     defined from left to right in the order specified. A string value is
199
    #:     converted to: StatusItem( name = string ):
200 4
    statusbar = ViewStatus
201

202
    #: List of button actions to add to the view. The **traitsui.menu**
203
    #: module defines standard buttons, such as **OKButton**, and standard sets
204
    #: of buttons, such as **ModalButtons**, which can be used to define a value
205
    #: for this attribute. This value can also be a list of button name strings,
206
    #: such as ``['OK', 'Cancel', 'Help']``. If set to the empty list, the
207
    #: view contains a default set of buttons (equivalent to **LiveButtons**:
208
    #: Undo/Redo, Revert, OK, Cancel, Help). To suppress buttons in the view,
209
    #: use the **NoButtons** variable, defined in **traitsui.menu**.
210 4
    buttons = Buttons
211

212
    #: The default button to activate when Enter is pressed. If not specified,
213
    #: pressing Enter will not activate any button.
214 4
    default_button = AButton
215

216
    #: The set of global key bindings for the view. Each time a key is pressed
217
    #: while the view has keyboard focus, the key is checked to see if it is one
218
    #: of the keys recognized by the KeyBindings object. If it is, the matching
219
    #: KeyBinding's method name is checked to see if it is defined on any of the
220
    #: object's in the view's context. If it is, the method is invoked. If the
221
    #: result of the method is **False**, then the search continues with the
222
    #: next object in the context. If any invoked method returns a non-False
223
    #: value, processing stops and the key is marked as having been handled. If
224
    #: all invoked methods return **False**, or no matching KeyBinding object is
225
    #: found, the key is processed normally. If the view has a non-empty *id*
226
    #: trait, the contents of the **KeyBindings** object will be saved as part
227
    #: of the view's persistent data:
228 4
    key_bindings = AKeyBindings
229

230
    #: The Handler object that provides GUI logic for handling events in the
231
    #: window. Set this attribute only if you are using a custom Handler. If
232
    #: not set, the default Traits UI Handler is used.
233 4
    handler = AHandler
234

235
    #: The factory function for converting a model into a model/view object:
236 4
    model_view = AModelView
237

238
    #: Title for the view, displayed in the title bar when the view appears as a
239
    #: secondary window (i.e., dialog or wizard). If not specified, "Edit
240
    #: properties" is used as the title.
241 4
    title = ATitle
242

243
    #: The name of the icon to display in the dialog window title bar:
244 4
    icon = Image
245

246
    #: The kind of user interface to create:
247 4
    kind = AKind
248

249
    #: The default object being edited:
250 4
    object = AnObject
251

252
    #: The default editor style of elements in the view:
253 4
    style = EditorStyle
254

255
    #: The default docking style to use for sub-groups of the view. The following
256
    #: values are possible:
257
    #:
258
    #: * 'fixed': No rearrangement of sub-groups is allowed.
259
    #: * 'horizontal': Moveable elements have a visual "handle" to the left by
260
    #:   which the element can be dragged.
261
    #: * 'vertical': Moveable elements have a visual "handle" above them by
262
    #:   which the element can be dragged.
263
    #: * 'tabbed': Moveable elements appear as tabbed pages, which can be
264
    #:   arranged within the window or "stacked" so that only one appears at
265
    #:   at a time.
266 4
    dock = DockStyle
267

268
    #: The image to display on notebook tabs:
269 4
    image = Image
270

271
    #: Called when modal changes are applied or reverted:
272 4
    on_apply = OnApply
273

274
    #: Can the user resize the window?
275 4
    resizable = IsResizable
276

277
    #: Can the user scroll the view? If set to True, window-level scroll bars
278
    #: appear whenever the window is too small to show all of its contents at
279
    #: one time. If set to False, the window does not scroll, but individual
280
    #: widgets might still contain scroll bars.
281 4
    scrollable = IsScrollable
282

283
    #: The category of exported elements:
284 4
    export = ExportType
285

286
    #: The valid categories of imported elements:
287 4
    imports = ImportTypes
288

289
    #: External help context identifier, which can be used by a custom help
290
    #: handler. This attribute is ignored by the default help handler.
291 4
    help_id = HelpId
292

293
    #: Requested x-coordinate (horizontal position) for the view window. This
294
    #: attribute can be specified in the following ways:
295
    #:
296
    #: * A positive integer: indicates the number of pixels from the left edge
297
    #:   of the screen to the left edge of the window.
298
    #: * A negative integer: indicates the number of pixels from the right edge
299
    #:   of the screen to the right edge of the window.
300
    #: * A floating point value between 0 and 1: indicates the fraction of the
301
    #:   total screen width between the left edge of the screen and the left edge
302
    #:   of the window.
303
    #: * A floating point value between -1 and 0: indicates the fraction of the
304
    #:   total screen width between the right edge of the screen and the right
305
    #:   edge of the window.
306 4
    x = XCoordinate
307

308
    #: Requested y-coordinate (vertical position) for the view window. This
309
    #: attribute behaves exactly like the **x** attribute, except that its value
310
    #: indicates the position of the top or bottom of the view window relative
311
    #: to the top or bottom of the screen.
312 4
    y = YCoordinate
313

314
    #: Requested width for the view window, as an (integer) number of pixels, or
315
    #: as a (floating point) fraction of the screen width.
316 4
    width = Width
317

318
    #: Requested height for the view window, as an (integer) number of pixels, or
319
    #: as a (floating point) fraction of the screen height.
320 4
    height = Height
321

322
    #: Class of dropped objects that can be added:
323 4
    drop_class = Any()
324

325
    #: Event when the view has been updated:
326 4
    updated = Event()
327

328
    #: What result should be returned if the user clicks the window or dialog
329
    #: close button or icon?
330 4
    close_result = CloseResult
331

332
    #: Note: Group objects delegate their 'object' and 'style' traits to the
333
    #: View
334

335
    # -- Deprecated Traits (DO NOT USE) ---------------------------------------
336

337 4
    ok = Bool(False)
338 4
    cancel = Bool(False)
339 4
    undo = Bool(False)
340 4
    redo = Bool(False)
341 4
    apply = Bool(False)
342 4
    revert = Bool(False)
343 4
    help = Bool(False)
344

345 4
    def __init__(self, *values, **traits):
346
        """ Initializes the object.
347
        """
348 4
        ViewElement.__init__(self, **traits)
349 4
        self.set_content(*values)
350

351 4
    def set_content(self, *values):
352
        """ Sets the content of a view.
353
        """
354 4
        content = []
355 4
        accum = []
356 4
        for value in values:
357 4
            if isinstance(value, ViewSubElement):
358 4
                content.append(value)
359 4
            elif type(value) in SequenceTypes:
360 4
                content.append(Group(*value))
361 4
            elif (
362
                isinstance(value, str)
363
                and (value[:1] == "<")
364
                and (value[-1:] == ">")
365
            ):
366
                # Convert string to an Include value:
367 0
                content.append(Include(value[1:-1].strip()))
368
            else:
369 4
                content.append(Item(value))
370

371
        # If there are any 'Item' objects in the content, wrap the content in a
372
        # Group:
373 4
        for item in content:
374 4
            if isinstance(item, Item):
375 4
                content = [Group(*content)]
376 4
                break
377

378
        # Wrap all of the content up into a Group and save it as our content:
379 4
        self.content = Group(container=self, *content)
380

381 4
    def ui(
382
        self,
383
        context,
384
        parent=None,
385
        kind=None,
386
        view_elements=None,
387
        handler=None,
388
        id="",
389
        scrollable=None,
390
        args=None,
391
    ):
392
        """ Creates a **UI** object, which generates the actual GUI window or
393
        panel from a set of view elements.
394

395
        Parameters
396
        ----------
397
        context : object or dictionary
398
            A single object or a dictionary of string/object pairs, whose trait
399
            attributes are to be edited. If not specified, the current object is
400
            used.
401
        parent : window component
402
            The window parent of the View object's window
403
        kind : string
404
            The kind of window to create. See the **AKind** trait for details.
405
            If *kind* is unspecified or None, the **kind** attribute of the
406
            View object is used.
407
        view_elements : ViewElements object
408
            The set of Group, Item, and Include objects contained in the view.
409
            Do not use this parameter when calling this method directly.
410
        handler : Handler object
411
            A handler object used for event handling in the dialog box. If
412
            None, the default handler for Traits UI is used.
413
        id : string
414
            A unique ID for persisting preferences about this user interface,
415
            such as size and position. If not specified, no user preferences
416
            are saved.
417
        scrollable : Boolean
418
            Indicates whether the dialog box should be scrollable. When set to
419
            True, scroll bars appear on the dialog box if it is not large enough
420
            to display all of the items in the view at one time.
421

422
        """
423 4
        handler = handler or self.handler or default_handler()
424 4
        if not isinstance(handler, Handler):
425 0
            handler = handler()
426

427 4
        if args is not None:
428 4
            handler.trait_set(**args)
429

430 4
        if not isinstance(context, dict):
431 4
            context = context.trait_context()
432

433 4
        context.setdefault("handler", handler)
434 4
        handler = context["handler"]
435

436 4
        if self.model_view is not None:
437 0
            context["object"] = self.model_view(context["object"])
438

439 4
        self_id = self.id
440 4
        if self_id != "":
441 4
            if id != "":
442 0
                id = "%s:%s" % (self_id, id)
443
            else:
444 4
                id = self_id
445

446 4
        if scrollable is None:
447 4
            scrollable = self.scrollable
448

449 4
        ui = UI(
450
            view=self,
451
            context=context,
452
            handler=handler,
453
            view_elements=view_elements,
454
            title=self.title,
455
            id=id,
456
            scrollable=scrollable,
457
        )
458

459 4
        if kind is None:
460 4
            kind = self.kind
461

462 4
        ui.ui(parent, kind)
463

464 4
        return ui
465

466 4
    def replace_include(self, view_elements):
467
        """ Replaces any items that have an ID with an Include object with
468
            the same ID, and puts the object with the ID into the specified
469
            ViewElements object.
470
        """
471 4
        if self.content is not None:
472 4
            self.content.replace_include(view_elements)
473

474
    def __repr__(self):
475
        """ Returns a "pretty print" version of the View.
476
        """
477
        if self.content is None:
478
            return "()"
479
        return "( %s )" % ", ".join(
480
            [item.__repr__() for item in self.content.content]
481
        )

Read our documentation on viewing source code .

Loading