File: ui_wizard.py

package info (click to toggle)
python-traitsui 4.1.0-1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 13,292 kB
  • sloc: python: 39,867; makefile: 120; sh: 5
file content (286 lines) | stat: -rw-r--r-- 10,198 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
#------------------------------------------------------------------------------
#
#  Copyright (c) 2005, Enthought, Inc.
#  All rights reserved.
#
#  This software is provided without warranty under the terms of the BSD
#  license included in enthought/LICENSE.txt and may be redistributed only
#  under the conditions described in the aforementioned license.  The license
#  is also available online at http://www.enthought.com/licenses/BSD.txt
#
#  Thanks for using Enthought open source!
#
#  Author: David C. Morrill
#  Date:   11/01/2004
#
#------------------------------------------------------------------------------

""" Creates a wizard-based wxPython user interface for a specified UI object.

    A wizard is a dialog box that displays a series of pages, which the user
    can navigate with forward and back buttons.
"""

#-------------------------------------------------------------------------------
#  Imports:
#-------------------------------------------------------------------------------

import wx
import wx.wizard as wz

from constants \
    import DefaultTitle

from helper \
    import restore_window, save_window, GroupEditor

from ui_panel \
    import fill_panel_for_group

from traits.api \
    import Trait, Str

#-------------------------------------------------------------------------------
#  Trait definitions:
#-------------------------------------------------------------------------------

# Trait that allows only None or a string value
none_str_trait = Trait( '', None, str )

#-------------------------------------------------------------------------------
#  Creates a wizard-based wxPython user interface for a specified UI object:
#-------------------------------------------------------------------------------

def ui_wizard ( ui, parent ):
    """ Creates a wizard-based wxPython user interface for a specified UI
    object.
    """
    # Create the copy of the 'context' we will need while editing:
    context     = ui.context
    ui._context = context
    new_context = {}
    for name, value in context.items():
        if value is not None:
            new_context[ name ] = value.clone_traits()
        else:
            new_context[ name ] = None

    ui.context = new_context

    # Now bind the context values to the 'info' object:
    ui.info.bind_context()

    # Create the wxPython wizard window:
    title = ui.view.title
    if title == '':
        title = DefaultTitle
    ui.control = wizard = wz.Wizard( parent, -1, title )

    # Create all of the wizard pages:
    pages        = []
    editor_pages = []
    info         = ui.info
    shadow_group = ui.view.content.get_shadow( ui )
    min_dx = min_dy = 0
    # Create a dictionary mapping each contained group in shadow_group to
    # its id and enabled_when fields.
    group_fields_mapping = {}
    for group in shadow_group.get_content():
        # FIXME: When the group's id or enabled_when or visible_when is
        # set, the "fill_panel_for_group" will create a new Panel to hold the
        # contents of the group (instead of adding them to the page itself).
        # This leads to an incorrect sizing of the panel(not sure why
        # actually): example would be 'test_ui2.py' in
        # Traits/integrationtests/ui. In addition,
        # it leads to incorrect bindings (of the id) on the UIInfo object:
        # the id is bound to the GroupEditor created in "fill_panel.."
        # instead of the PageGroupEditor created here.
        # A simple solution is to clear out these fields before calling
        # "fill_panel_for_group", and then reset these traits.
        group_fields_mapping[group] = (group.id, group.enabled_when)
        (group.id, group.enabled_when) = ('', '')
        page = UIWizardPage( wizard, editor_pages )
        pages.append( page )
        fill_panel_for_group( page, group, ui )

        # Size the page correctly, then calculate cumulative minimum size:
        sizer = page.GetSizer()
        sizer.Fit( page )
        size   = sizer.CalcMin()
        min_dx = max( min_dx, size.GetWidth() )
        min_dy = max( min_dy, size.GetHeight() )

        # If necessary, create a PageGroupEditor and attach it to the right
        # places:
        (group.id, group.enabled_when) = group_fields_mapping[group]
        if group.id or group.enabled_when:
            page.editor = editor = PageGroupEditor( control = page )
            if group.id:
                page.id = group.id
                editor_pages.append( page )
                info.bind( page.id, editor )
            if group.enabled_when:
                ui.add_enabled( group.enabled_when, editor )

    # Size the wizard correctly:
    wizard.SetPageSize( wx.Size( min_dx, min_dy ) )

    # Set up the wizard 'page changing' event handler:
    wz.EVT_WIZARD_PAGE_CHANGING( wizard, wizard.GetId(), page_changing )

    # Size the wizard and the individual pages appropriately:
    prev_page = pages[0]
    wizard.FitToPage( prev_page )

    # Link the pages together:
    for page in pages[1:]:
        page.SetPrev( prev_page )
        prev_page.SetNext( page )
        prev_page = page

    # Finalize the display of the wizard:
    try:
        ui.prepare_ui()
    except:
        ui.control.Destroy()
        ui.control.ui = None
        ui.control    = None
        ui.result     = False
        raise

    # Position the wizard on the display:
    ui.handler.position( ui.info )

    # Restore the user_preference items for the user interface:
    restore_window( ui )

    # Run the wizard:
    if wizard.RunWizard( pages[0] ):
        # If successful, apply the modified context to the original context:
        original = ui._context
        for name, value in ui.context.items():
            if value is not None:
                original[ name ].copy_traits( value )
            else:
                original[ name ] = None
        ui.result = True
    else:
        ui.result = False

    # Clean up loose ends, like restoring the original context:
    save_window( ui )
    ui.finish()
    ui.context  = ui._context
    ui._context = {}

#-------------------------------------------------------------------------------
#  Handles the user attempting to change the current wizard page:
#-------------------------------------------------------------------------------

def page_changing ( event ):
    """ Handles the user attempting to change the current wizard page.
    """
    # Get the page the user is trying to go to:
    page = event.GetPage()
    if event.GetDirection():
       new_page = page.GetNext()
    else:
       new_page = page.GetPrev()

    # If the page has a disabled PageGroupEditor object, veto the page change:
    if ((new_page is not None) and
        (new_page.editor is not None) and
        (not new_page.editor.enabled)):
        event.Veto()

        # If their is a message associated with the editor, display it:
        msg = new_page.editor.msg
        if msg != '':
            wx.MessageBox( msg )

#-------------------------------------------------------------------------------
#  'UIWizardPage' class:
#-------------------------------------------------------------------------------

class UIWizardPage ( wz.PyWizardPage ):
    """ A page within a wizard interface.
    """
    #---------------------------------------------------------------------------
    #  Initializes the object:
    #---------------------------------------------------------------------------

    def __init__ ( self, wizard, pages ):
        wz.PyWizardPage.__init__ ( self, wizard )
        self.next  = self.previous = self.editor = None
        self.pages = pages

    #---------------------------------------------------------------------------
    #  Sets the next page after this one:
    #---------------------------------------------------------------------------

    def SetNext ( self, page ):
        """ Sets the next page after this one.
        """
        self.next = page

    #---------------------------------------------------------------------------
    #  Sets the previous page before this one:
    #---------------------------------------------------------------------------

    def SetPrev ( self, page ):
        """ Sets the previous page to this one.
        """
        self.previous = page

    #---------------------------------------------------------------------------
    #  Returns the next page after this one:
    #---------------------------------------------------------------------------

    def GetNext ( self ):
        """ Returns the next page after this one.
        """
        editor = self.editor
        if (editor is not None) and (editor.next != ''):
            next = editor.next
            if next == None:
                return None
            for page in self.pages:
                if page.id == next:
                    return page
        return self.next

    #---------------------------------------------------------------------------
    #  Returns the previous page before this one:
    #---------------------------------------------------------------------------

    def GetPrev ( self ):
        """ Returns the previous page to this one.
        """
        editor = self.editor
        if (editor is not None) and (editor.previous != ''):
            previous = editor.previous
            if previous is None:
                return None
            for page in self.pages:
                if page.id == previous:
                    return page
        return self.previous

#-------------------------------------------------------------------------------
#  'PageGroupEditor' class:
#-------------------------------------------------------------------------------

class PageGroupEditor ( GroupEditor ):
    """ Editor for a group, which displays a page.
    """
    #---------------------------------------------------------------------------
    #  Trait definitions:
    #---------------------------------------------------------------------------

    # ID of next page to display
    next     = none_str_trait
    # ID of previous page to display
    previous = none_str_trait
    # Message to display if user can't link to page
    msg      = Str