~chris-rogers/maus/1312

« back to all changes in this revision

Viewing changes to src/common_py/gui/window.py

  • Committer: Durga Rajaram
  • Date: 2013-11-23 10:23:56 UTC
  • mfrom: (1014 merge)
  • mto: (663.22.1 release)
  • mto: This revision was merged to the branch mainline in revision 1015.
  • Revision ID: durga@fnal.gov-20131123102356-zfvjoffpto9xceu7
candidate 0.7.5 - trunk r1014

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#  This file is part of MAUS: http://micewww.pp.rl.ac.uk/projects/maus
 
2
 
3
#  MAUS is free software: you can redistribute it and/or modify
 
4
#  it under the terms of the GNU General Public License as published by
 
5
#  the Free Software Foundation, either version 3 of the License, or
 
6
#  (at your option) any later version.
 
7
 
8
#  MAUS is distributed in the hope that it will be useful,
 
9
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
#  GNU General Public License for more details.
 
12
 
13
#  You should have received a copy of the GNU General Public License
 
14
#  along with MAUS.  If not, see <http://www.gnu.org/licenses/>.
 
15
 
 
16
# pylint does not import ROOT
 
17
# pylint: disable=E1101
 
18
 
 
19
"""
 
20
Module handles GUI elements for making python GUIs within MAUS. 
 
21
 
 
22
An interface is provided that enables layout to be defined using json
 
23
configuration files.
 
24
"""
 
25
 
 
26
import sys
 
27
import json
 
28
import ROOT
 
29
 
 
30
KNORMAL = ROOT.TGLayoutHints.kLHintsNormal
 
31
KEXPANDX = ROOT.TGLayoutHints.kLHintsExpandX
 
32
KCENTERX = ROOT.TGLayoutHints.kLHintsCenterX
 
33
DEFAULT_TEXT_LENGTH = 10
 
34
WINDOW_LIST = []
 
35
 
 
36
def layout(option):
 
37
    """
 
38
    Select a layout option for the element
 
39
      - option: string layout option - either normal (default), close (close 
 
40
                packed), close_v, close_h (close packed in only vertical or 
 
41
                horizontal direction)
 
42
    Returns an instance of ROOT.TGLayoutHints.
 
43
 
 
44
    Throws a ValueError if the option is not recognised.
 
45
    """
 
46
    close = 1
 
47
    normal = 5
 
48
    if option == "normal":
 
49
        return ROOT.TGLayoutHints(KNORMAL, normal, normal, normal, normal)
 
50
    if option == "close": # close_packed
 
51
        return ROOT.TGLayoutHints(KNORMAL, close, close, close, close)
 
52
    if option == "close_v": # close packed vertically
 
53
        return ROOT.TGLayoutHints(KNORMAL, normal, normal, close, close)
 
54
    if option == "close_h": # close_packed horizontally
 
55
        return ROOT.TGLayoutHints(KNORMAL, close, close, normal, normal)
 
56
    raise ValueError("Failed to recognise layout option "+str(option))
 
57
 
 
58
class GuiError(Exception):
 
59
    """
 
60
    GuiErrot can be thrown when user wants to catch and handle errors that
 
61
    are specifically related to gui errors (and e.g. let other errors pass
 
62
    through to caller)
 
63
    """
 
64
    def __init__(self, *args, **kwargs):
 
65
        """Initialise Exception base class"""
 
66
        Exception.__init__(self, *args, **kwargs)
 
67
 
 
68
 
 
69
class Label:
 
70
    """
 
71
    Wrapper for ROOT TGLabel
 
72
 
 
73
    I couldn't find an elegant way to build a ROOT TGLabel with a particular
 
74
    width; so I fill the label with default characters, and then update once
 
75
    the window is drawn with the actual text. Note that ROOT default font is not
 
76
    fixed width, so fill characters should have some width.
 
77
    """
 
78
    def __init__(self, parent, name, label_length, alignment, _layout):
 
79
        """
 
80
        Initialise the label
 
81
 
 
82
          - parent; parent ROOT frame
 
83
          - name; text that will fill the label
 
84
          - label_length; width of the label
 
85
          - alignment; integer index defining how the text is aligned within the
 
86
            label
 
87
        """
 
88
        self.name = name
 
89
        self.frame = ROOT.TGLabel(parent, "a"*label_length)
 
90
        self.frame.SetTextJustify(alignment)
 
91
        parent.AddFrame(self.frame, layout(_layout))
 
92
 
 
93
    def update(self):
 
94
        """Call to update the text once the window is built"""
 
95
        self.frame.SetText(self.name)
 
96
 
 
97
    @staticmethod
 
98
    def new_from_dict(my_dict, parent):
 
99
        """
 
100
        Build a Label based on a dictionary
 
101
            - my_dict; dictionary of string:value pairs defining the layout
 
102
                - type; should be "label"
 
103
                - name; text to fill the label
 
104
                - alignment; 'right', 'center' or 'left' to align the text.
 
105
                    Vertical alignment is always center
 
106
                - label_length; Integer defining the default width of the label. 
 
107
                - layout; see layout function...
 
108
              alignment, label_length, layout are optional
 
109
            - parent; reference to the parent frame
 
110
        Return value is the dictionary with additional entry "frame" appended
 
111
        containing a reference to the TGLabel object
 
112
        """
 
113
        if my_dict["type"] != "label":
 
114
            raise ValueError("Not a label")
 
115
        name = my_dict["name"]
 
116
        alignment = ROOT.TGLabel.kTextLeft
 
117
        if "alignment" in my_dict and my_dict["alignment"] == "right":
 
118
            alignment = ROOT.TGLabel.kTextRight
 
119
        elif "alignment" in my_dict and my_dict["alignment"] == "center":
 
120
            alignment = ROOT.TGLabel.kTextCenterX
 
121
        label_length = 5
 
122
        if "label_length" in my_dict:
 
123
            label_length = my_dict["label_length"]
 
124
        else:
 
125
            my_dict["label_length"] = label_length
 
126
        _layout = "normal"
 
127
        if "layout" in my_dict:
 
128
            _layout = my_dict["layout"]
 
129
        else:
 
130
            my_dict["layout"] = _layout
 
131
        my_dict["label"] = Label(parent, name, label_length, alignment, _layout)
 
132
        my_dict["frame"] = my_dict["label"].frame
 
133
        return my_dict
 
134
 
 
135
class NamedTextEntry: # pylint: disable=R0913
 
136
    """
 
137
    A named text entry is a label followed by a text entry box.
 
138
 
 
139
    These are used frequently enough that it is worthwhile to make a special
 
140
    entry for this.
 
141
    """
 
142
    def __init__(self, parent, name, default_text, entry_length=10, \
 
143
                 label_length=5, tooltip=""):
 
144
        """
 
145
        Initialise the text entry
 
146
          - parent; parent frame
 
147
          - name; string name that will appear in the label and is used to name
 
148
            the text entry
 
149
          - default_text; text that will be placed in the text entry by default
 
150
          - entry_length; width of the text entry field (number of characters)
 
151
          - label_length; width of the label (number of characters)
 
152
          - tooltip; text to place in a tool tip - if empty string, no tool tip
 
153
            will be made.
 
154
        """
 
155
        self.frame = ROOT.TGHorizontalFrame(parent)
 
156
        self.name = name
 
157
        self.label = ROOT.TGLabel(self.frame, "a"*label_length)
 
158
        self.label.SetTextJustify(ROOT.TGLabel.kTextLeft)
 
159
        self.text_entry = ROOT.TGTextEntry(self.frame, "a"*entry_length, 0)
 
160
        self.frame.AddFrame(self.label, layout("close_v"))
 
161
        self.frame.AddFrame(self.text_entry, layout("close_v"))
 
162
        self.text_entry.SetText(default_text)
 
163
        if tooltip != "":
 
164
            self.text_entry.SetToolTipText(tooltip)
 
165
 
 
166
    def update(self):
 
167
        """
 
168
        Update the label to contain actual text
 
169
        """
 
170
        self.label.SetText(self.name)
 
171
 
 
172
    @staticmethod
 
173
    def new_from_dict(my_dict, parent):
 
174
        """
 
175
        Initialise a text entry from a dictionary
 
176
          - my_dict; dictionary containing configuration options
 
177
              - type; should be string like 'named_text_entry'
 
178
              - name; text to put in the label
 
179
              - default_text; default text that fills the text entry box
 
180
              - tool_tip; make a tool tip next to the text entry
 
181
              - entry_length (optional); width of the text entry box (number of
 
182
                characters)
 
183
              - label_length (optional); width of the label (number of
 
184
                characters)
 
185
          - parent; parent frame
 
186
        Return value is the dictionary with "text_entry" field and "frame" field
 
187
        appended containing references to the NamedTextEntry and the actual
 
188
        TGTextEntry box
 
189
        """
 
190
        if my_dict["type"] != "named_text_entry":
 
191
            raise ValueError("Not a text box")
 
192
        name = my_dict["name"]
 
193
        default = my_dict["default_text"]
 
194
        entry_length = DEFAULT_TEXT_LENGTH
 
195
        tool_tip = ""
 
196
        if "tool_tip" in my_dict:
 
197
            tool_tip = my_dict["tool_tip"]
 
198
        if "entry_length" in my_dict:
 
199
            entry_length = my_dict["entry_length"]
 
200
        else:
 
201
            my_dict["entry_length"] = entry_length
 
202
        label_length = 5
 
203
        if "label_length" in my_dict:
 
204
            label_length = my_dict["label_length"]
 
205
        else:
 
206
            my_dict["label_length"] = label_length
 
207
        my_dict["text_entry"] = NamedTextEntry(parent, name, default,
 
208
                                   entry_length, label_length, tooltip=tool_tip)
 
209
        my_dict["frame"] = my_dict["text_entry"].frame
 
210
        return my_dict
 
211
 
 
212
def function_wrapper(function):
 
213
    """
 
214
    Lightweight wrapper to intervene before TPyDispatcher handles python errors
 
215
    """
 
216
    return FunctionWrapper(function).execute
 
217
 
 
218
class FunctionWrapper: #pylint: disable=R0903
 
219
    """
 
220
    Function wrapper to intervene before TPyDispatcher handles python errors
 
221
    """
 
222
    def __init__(self, function):
 
223
        """
 
224
        Lightweight class that adds some error handling
 
225
        """
 
226
        self.function = function
 
227
 
 
228
    def execute(self, *args, **keywdargs):
 
229
        """
 
230
        Handle the errors from python function calls as I want
 
231
        """
 
232
        try:
 
233
            return self.function(*args, **keywdargs)
 
234
        except: # pylint: disable=W0702, W0142
 
235
            sys.excepthook(*sys.exc_info())
 
236
 
 
237
class Window(): # pylint: disable=R0201
 
238
    """
 
239
    This is the main class in this module. Initialise a window based on a json
 
240
    configuration file and layout the elements.
 
241
 
 
242
    Layout options
 
243
      Json configuration should be a dictionary. The top level object requires
 
244
      following fields
 
245
        - type; either main_frame (builds a TGMainFrame) or transient_frame 
 
246
          (builds a TGTransientFrmae
 
247
        - name; window name
 
248
        - children; list of child frames, each of which is a dict
 
249
 
 
250
      Window initialisation iterates over the child dicts and parses each one
 
251
      into a child TGFrame.  All frames support a "layout" entry, which 
 
252
      determines the amount of padding that will be placed around the entry.
 
253
        - "layout"; set the amount of padding in the frame. Can be either
 
254
          "normal" to make standard padding (5 units in each direction), "close"
 
255
          padding (1 unit in each direction), "close_v" (1 unit vertically, 5
 
256
          horizontally) or "close_h" (1 unit horizontally, 5 vertically) 
 
257
      The "type" entry of the dictionary determines the type of TGFrame that 
 
258
      will be contructed. Options are:
 
259
      - "horizontal_frame"; makes a TGHorizontalFrame, with a list of children.
 
260
          - "children": list of child frames to place in the horizontal frame
 
261
            Each sub-frame will be placed horizontally next to the previous one.
 
262
      - "vertical_frame"; makes a TGVerticalFrame, with a list of children.
 
263
          - "children": list of child frames to place in the horizontal frame.
 
264
            Each sub-frame will be placed vertically below the previous one.
 
265
      - "label"; makes a text TGLabel
 
266
          - "name": name of the label
 
267
          - "label_length": width of space allocated to the label in characters.
 
268
             (Default 5)
 
269
      - "text_entry"; makes a TGTextEntry
 
270
          - "name": name of the label
 
271
          - "label_length": width of space allocated to the label in characters.
 
272
             (Default 5)
 
273
      - "named_text_entry": makes a NamedTextEntry, which is a collection of
 
274
         TGHorizontalFrame, TGLabel and TGTextEntry. See NamedTextEntry 
 
275
         documentation for more details.
 
276
      - "button": makes a TGTextButton i.e. standard click button
 
277
          - "name": text to be used on the button; put an ampersand in to set a
 
278
            hot key
 
279
      - "drop_down": make a TGComboBox i.e. a drop down box.
 
280
          - "entries": list of strings, each of which becomes an entry in the
 
281
            drop down box, indexing from 0
 
282
          - "selected": integer determining the entry that will be selected
 
283
            initialliy (Default 0)
 
284
      - "check_button": make a TGCheckButton i.e. a true/false tick box
 
285
          - "text": text to place next to the check button
 
286
          - "default_state": set to 1 to make the box ticked by default, 0 to
 
287
            make it unticked
 
288
      - "special": user-defined GUI elements can be added at RunTime by making a
 
289
        special.
 
290
          - "manipulator": name of the manipulator to use for this special item.
 
291
            manipulators are set at the initialisation stage by passing a
 
292
            dictionary to manipulator_dict. Dictionary should contain a mapping
 
293
            that maps string "manipulator" to the function that will be called
 
294
            at run time to set up the "special" GUI element. The "manipulator"
 
295
            function should take the GUI element dict as an argument and return
 
296
            it at the end. The returned GUI element dict will be used to 
 
297
            construct the GUI element as per normal.
 
298
    """
 
299
 
 
300
    def __init__(self, parent, main, data_file=None, # pylint: disable = R0913
 
301
                 json_data=None, manipulator_dict=None):
 
302
        """
 
303
        Initialise the window
 
304
          - parent; parent ROOT frame (e.g. TGMainFrame/TGTransientFrame)
 
305
          - main; top level TGMainFrame for the whole GUI
 
306
          - data_file; string containing the file name for the layout data
 
307
          - json_data; json object containing the layout data
 
308
          - manipulator_dict; if there are any frames of type "special", this
 
309
            dictionary maps to a function which performs the layout for that
 
310
            frame.
 
311
        Throws an exception if both, or neither, of data_file and json_data are
 
312
        defined - caller must define the window layout in one and only one place
 
313
        """
 
314
        if data_file != None and json_data != None:
 
315
            raise ValueError("Define only one of json_data and data_file")
 
316
        if data_file == None and json_data == None:
 
317
            raise ValueError("Must define json_data or data_file")
 
318
        if data_file != None:
 
319
            self.data = open(data_file).read()
 
320
            self.data = json.loads(self.data)
 
321
        else:
 
322
            self.data = json_data
 
323
        if manipulator_dict == None:
 
324
            manipulator_dict = {}
 
325
        self.main_frame = None
 
326
        self.update_list = []
 
327
        self.socket_list = []
 
328
        self.tg_text_entries = {}
 
329
        self.manipulators = manipulator_dict
 
330
        self._setup_frame(parent, main)
 
331
        return None
 
332
 
 
333
    def close_window(self):
 
334
        """
 
335
        Close the window
 
336
        """
 
337
        # root v5.34 - makes segv if I don't disable the text entry before
 
338
        # closing
 
339
        for tg_text_entry in self.tg_text_entries.values():
 
340
            tg_text_entry.SetState(0)
 
341
        self.main_frame.CloseWindow()
 
342
        self.main_frame = None
 
343
 
 
344
    def get_frame(self, frame_name, frame_type, frame_list=None):
 
345
        """
 
346
        Get the frame with given name and type from a frame list
 
347
          - frame_name; specify the "name" field in the frame's dictionary
 
348
          - frame_type; specify the "type" field in the frame's dictionary
 
349
          - frame_list; search within this list. If not defined, will use the
 
350
            top level frames list (self.data)
 
351
        Returns a ROOT TGFrame object. Raises ValueError if no frame was found
 
352
        """
 
353
        frame_dict = self.get_frame_dict(frame_name, frame_type, frame_list)
 
354
        return frame_dict["frame"]
 
355
 
 
356
    def get_frame_dict(self, frame_name, frame_type, frame_list=None):  
 
357
        """
 
358
        Get the frame dictionary with given name and type from a frame list
 
359
          - frame_name; specify the "name" field in the frame's dictionary
 
360
          - frame_type; specify the "type" field in the frame's dictionary
 
361
          - frame_list; search within this list. If not defined, will use the
 
362
            top level frames list (self.data)
 
363
        Returns a dictionary object containing the frame configuration. The ROOT
 
364
        TGFrame object is mapped to the "frame" key.
 
365
        """
 
366
        if frame_list == None:
 
367
            frame_list = self.data["children"]
 
368
        frame = self._get_frame_dict_recurse(frame_name, frame_type, frame_list)
 
369
        if frame == None:
 
370
            raise ValueError("Could not find frame of name "+str(frame_name)+\
 
371
                             " type "+str(frame_type))
 
372
        return frame
 
373
 
 
374
    def set_action(self, frame_name, frame_type, frame_socket, action):
 
375
        """
 
376
        Set an action that will occur on a given signal
 
377
          - frame_name; string containing the name of the frame that makes the
 
378
            signal
 
379
          - frame_type; string containing the type of the frame that makes the
 
380
            signal
 
381
          - frame_socket; string containing the name of the ROOT function that
 
382
            makes the signal. If the ROOT function takes arguments, those must
 
383
            be specified too
 
384
          - action; function that will be called when the specified signal is
 
385
            dispatched
 
386
        e.g.  my_window.set_action("select_box", "drop_down", "Selected(Int_t)",
 
387
                                   select_action)
 
388
        will call select_action() when the frame with "type":"drop_down" and
 
389
        "name":"select_box" makes a signal Selected(Int_t).
 
390
        """
 
391
        frame = self.get_frame(frame_name, frame_type)
 
392
        self.socket_list.append(ROOT.TPyDispatcher(function_wrapper(action)))
 
393
        frame.Connect(frame_socket, 'TPyDispatcher', self.socket_list[-1], 
 
394
                      'Dispatch()')
 
395
 
 
396
    def set_button_action(self, button_name, action):
 
397
        """
 
398
        Set an action that will occur when a button emits a  Clicked() signal
 
399
            - button_name; name of the button
 
400
            - action; function that will be called when the button is clicked
 
401
        """
 
402
        self.set_action(button_name, "button", "Clicked()", action)
 
403
 
 
404
    def get_text_entry(self, name, value_type):
 
405
        """
 
406
        Get the text stored in either a text_entry or a named_text_entry
 
407
            - name; name of the text entry
 
408
            - value_type; convert the value to the given type.
 
409
        Returns a value of type value_type.
 
410
        Raises a type error if the conversion fails (string conversion will 
 
411
        always succeed).
 
412
        """
 
413
        text_entry, text_length = self._find_text_entry(name)
 
414
        if text_length:
 
415
            pass
 
416
        try:
 
417
            value = value_type(text_entry.GetText())
 
418
            return value
 
419
        except ValueError:
 
420
            raise GuiError("Failed to parse text entry "+name+" as "+\
 
421
                           str(value_type))
 
422
 
 
423
    def set_text_entry(self, name, value):
 
424
        """
 
425
        Set the text stored in either a text_entry or a named_text_entry
 
426
            - name; name of the text entry
 
427
            - value; put the given value in the text_entry, converting to a
 
428
                     string using the value's __str__ method. The resultant 
 
429
                     string will be truncated to the width of the text entry.
 
430
        """
 
431
        text_entry, text_length = self._find_text_entry(name)
 
432
        my_value = str(value)
 
433
        if len(my_value) > text_length-1:
 
434
            my_value = my_value[0:text_length-1]
 
435
        text_entry.SetText(my_value)
 
436
 
 
437
    def _get_frame_dict_recurse(self, frame_name, frame_type, frame_list=None):
 
438
        """
 
439
        (Private) recursively search for a frame of a given name and type.
 
440
        Called by get_frame_dict.
 
441
        """
 
442
        for item in frame_list:
 
443
            if "name" in item and "type" in item:
 
444
                if item["name"] == frame_name and item["type"] == frame_type:
 
445
                    return item
 
446
            # if we have not found the frame dict here, then recurse into 
 
447
            # children
 
448
            if "children" in item:
 
449
                frame = self._get_frame_dict_recurse(frame_name,
 
450
                                                      frame_type,
 
451
                                                      item["children"])
 
452
                if frame != None:
 
453
                    return frame
 
454
 
 
455
    def _find_text_entry(self, name):
 
456
        """
 
457
        (Private) find a text entry. Search first for named_text_entry, if that
 
458
        fails search for text_entry. Return the TGTextEntry and the length of
 
459
        the text entry (number of characters).
 
460
        """
 
461
        try:
 
462
            value_dict = self.get_frame_dict(name, 'named_text_entry')
 
463
        except ValueError:
 
464
            value_dict = None
 
465
        entry_length = DEFAULT_TEXT_LENGTH
 
466
        if value_dict != None:
 
467
            if "entry_length" in value_dict:
 
468
                entry_length = value_dict["entry_length"]
 
469
            return (value_dict['text_entry'].text_entry,
 
470
                    entry_length)
 
471
        value_dict = self.get_frame_dict(name, 'text_entry')
 
472
        if "entry_length" in value_dict:
 
473
            entry_length = value_dict["entry_length"]
 
474
        if value_dict == None:
 
475
            raise ValueError("Could not find text entry named "+str(name))
 
476
        return value_dict["frame"], entry_length
 
477
 
 
478
    def _setup_frame(self, parent, main):
 
479
        """
 
480
        (Private) set up the frame. Parse the json file and convert to ROOT 
 
481
        TGFrames, lay out the windows and so forth.
 
482
        """
 
483
        if self.data["type"] == "transient_frame":
 
484
            self.main_frame = ROOT.TGTransientFrame(parent, main)
 
485
        elif self.data["type"] == "main_frame":
 
486
            self.main_frame = ROOT.TGMainFrame(parent)
 
487
        else:
 
488
            raise ValueError("Failed to recognise frame type "+\
 
489
                             str(self.data["type"]))
 
490
        self._expand_frames(self.data["children"], self.main_frame)
 
491
        self.data["frame"] = self.main_frame
 
492
        self.main_frame.SetWindowName(self.data["name"])
 
493
        self.main_frame.MapSubwindows()
 
494
        self.main_frame.Resize(self.main_frame.GetDefaultSize())
 
495
        self.main_frame.MapWindow()
 
496
        self._label_update()
 
497
 
 
498
    def _label_update(self):
 
499
        """
 
500
        (Private) once the labels are laid out, with width enforced by fake
 
501
        string data, update the labels with the real text. A bit hacky.
 
502
        """
 
503
        for item in self.update_list:
 
504
            item.update()
 
505
 
 
506
    def _expand_frames(self, frames, parent):
 
507
        """
 
508
        Parse the json object, adding frames to all json items
 
509
        """
 
510
        for frames_index, item in enumerate(frames):
 
511
            if item["type"] == "special":
 
512
                manipulator_name = item["manipulator"]
 
513
                if manipulator_name not in self.manipulators:
 
514
                    raise ValueError("Manipulator "+manipulator_name+\
 
515
                                     " has not been defined")
 
516
                item = self.manipulators[manipulator_name](item)
 
517
                frames[frames_index] = item
 
518
            if item["type"] in self.parse_item_dict.keys():
 
519
                parser = self.parse_item_dict[item["type"]]
 
520
                parser(self, parent, item)
 
521
            else:
 
522
                raise ValueError("Did not recognise item type "+item["type"])
 
523
            layout_option = "normal"
 
524
            if "layout" in item.keys():
 
525
                layout_option = item["layout"]
 
526
            parent.AddFrame(item["frame"], layout(layout_option))
 
527
            if "children" in item:
 
528
                self._expand_frames(item["children"], item["frame"])
 
529
 
 
530
    def _parse_horizontal_frame(self, parent, item):
 
531
        """parse a horizontal_frame into a TGHorizontalFrame"""
 
532
        item["frame"] = ROOT.TGHorizontalFrame(parent)
 
533
 
 
534
    def _parse_vertical_frame(self, parent, item):
 
535
        """parse a vertical_frame into a TGVerticalFrame"""
 
536
        item["frame"] = ROOT.TGVerticalFrame(parent)
 
537
 
 
538
    def _parse_named_text_entry(self, parent, item):
 
539
        """parse a name_text_entry into a NamedEntry"""
 
540
        item = NamedTextEntry.new_from_dict(item, parent)
 
541
        self.tg_text_entries[item["name"]] = item["text_entry"].text_entry
 
542
        self.update_list.append(item["text_entry"])
 
543
 
 
544
    def _parse_canvas(self, parent, item):
 
545
        """parse a canvas into a TRootEmbeddedCanvas"""
 
546
        item["frame"] = ROOT.TRootEmbeddedCanvas('canvas',
 
547
                                         parent, item["width"], item["height"])
 
548
        item["frame"].Draw()
 
549
 
 
550
    def _parse_label(self, parent, item):
 
551
        """parse a label into a TGLabel"""
 
552
        item = Label.new_from_dict(item, parent)
 
553
        self.update_list.append(item["label"])
 
554
 
 
555
    def _parse_button(self, parent, item):
 
556
        """parse a text_button into a TGTextButton"""
 
557
        name = item["name"]
 
558
        item["frame"] = ROOT.TGTextButton(parent, name, 50)
 
559
 
 
560
    def _parse_text_entry(self, parent, item):
 
561
        """parse a text_entry into a TGTextEntry"""
 
562
        entry_length = DEFAULT_TEXT_LENGTH
 
563
        if "entry_length" in item:
 
564
            entry_length = item["entry_length"]
 
565
        item["frame"] = ROOT.TGTextEntry(parent, "a"*entry_length, 0)
 
566
        # use default, then change to get width right?
 
567
        default_text = ""
 
568
        if "default_text" in item:
 
569
            default_text = item["default_text"]
 
570
        item["frame"].SetText(default_text)
 
571
        self.tg_text_entries[item["name"]] = item["frame"]
 
572
 
 
573
    def _parse_drop_down(self, parent, item):
 
574
        """parse a drop_down into a TGComboBox"""
 
575
        item["frame"] = ROOT.TGComboBox(parent)
 
576
        for i, entry in enumerate(item["entries"]):
 
577
            item["frame"].AddEntry(entry, i)
 
578
        item["frame"].Resize(150, 20)
 
579
        if "selected" in item:
 
580
            item["frame"].Select(item["selected"])
 
581
 
 
582
    def _parse_check_button(self, parent, item):
 
583
        """parse a check_button into a TGCheckButton"""
 
584
        item["frame"] = ROOT.TGCheckButton(parent, item["text"])
 
585
        item["frame"].SetState(item["default_state"])
 
586
 
 
587
    parse_item_dict = {
 
588
         "horizontal_frame":_parse_horizontal_frame,
 
589
         "vertical_frame":_parse_vertical_frame,
 
590
         "named_text_entry":_parse_named_text_entry,
 
591
         "canvas":_parse_canvas,
 
592
         "label":_parse_label,
 
593
         "button":_parse_button,
 
594
         "text_entry":_parse_text_entry,
 
595
         "drop_down":_parse_drop_down,
 
596
         "check_button":_parse_check_button,
 
597
    }
 
598