source: sasview/src/sas/sasgui/perspectives/simulation/ShapeParameters.py @ 3ec78a1

Last change on this file since 3ec78a1 was 5251ec6, checked in by Paul Kienzle <pkienzle@…>, 6 years ago

improved support for py37 in sasgui

  • Property mode set to 100644
File size: 20.9 KB
Line 
1"""
2This software was developed by the University of Tennessee as part of the
3Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
4project funded by the US National Science Foundation.
5
6See the license text in license.txt
7
8copyright 2009, University of Tennessee
9"""
10from __future__ import print_function
11
12import sys
13from copy import deepcopy
14
15import wx
16import wx.lib.newevent
17
18from . import SimCanvas
19
20(CreateShapeEvent, EVT_ADD_SHAPE) = wx.lib.newevent.NewEvent()
21(EditShapeEvent, EVT_EDIT_SHAPE)  = wx.lib.newevent.NewEvent()
22(DelShapeEvent, EVT_DEL_SHAPE)    = wx.lib.newevent.NewEvent()
23(QRangeEvent, EVT_Q_RANGE)        = wx.lib.newevent.NewEvent() 
24(PtDensityEvent, EVT_PT_DENSITY)  = wx.lib.newevent.NewEvent() 
25
26class ShapeParameterPanel(wx.Panel):
27    #TODO: show units
28    #TODO: order parameters properly
29    CENTER_PANE = True
30   
31    def __init__(self, parent, q_min=0.001, q_max=0.5, q_npts=10, pt_density=0.1, *args, **kwargs):
32        wx.Panel.__init__(self, parent, *args, **kwargs)
33       
34        self.window_name = "ShapeParams"
35        self.window_caption = "Shape parameter"
36       
37     
38        self.params = {}
39        self.parent = parent
40        self.type = None
41        self.listeners = []
42        self.parameters = []
43        self.bck = wx.GridBagSizer(5,5)
44        self.SetSizer(self.bck)
45       
46        # position and orientation ctrl
47        self.xctrl = None
48        self.yctrl = None
49        self.zctrl = None
50        self.actrl = None
51        self.bctrl = None
52        self.cctrl = None
53               
54        # Sizer to hold the shape parameters
55        self.shape_sizer = wx.GridBagSizer(5,5)
56       
57        ny = 0       
58        title = wx.StaticText(self, -1, "[Temporary form]", style=wx.ALIGN_LEFT)
59        self.bck.Add(title, (ny,0), (1,2), flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=15)
60       
61        # Shape list
62        shape_text = wx.StaticText(self, -1, "Geometric shape", style=wx.ALIGN_LEFT)
63        self.shape_list = SimCanvas.getShapes()
64        value_list = []
65        for item in self.shape_list:
66            value_list.append(item['name'])
67           
68        self.model_combo = wx.ComboBox(self, -1, value=value_list[0], choices=value_list, style=wx.CB_READONLY)
69        self.model_combo.SetToolTip(wx.ToolTip("Select a geometric shape from the drop-down list"))
70       
71        ny+=1
72        self.bck.Add(shape_text, (ny,0), flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=15)
73        self.bck.Add(self.model_combo, (ny,1), flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=15)
74        wx.EVT_COMBOBOX(self.model_combo,-1, self._on_select_shape)
75   
76        # Placeholder for parameter form
77        ny+=1
78        self.bck.Add(self.shape_sizer, (ny,0), (1,2), flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=0)   
79       
80        # Point density control
81        point_density_text = wx.StaticText(self, -1, "Point density", style=wx.ALIGN_LEFT)
82        ny+=1
83        self.bck.Add(point_density_text, (ny,0), flag = wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border = 15)
84        self.point_density_ctrl = wx.TextCtrl(self, -1, size=(40,20), style=wx.TE_PROCESS_ENTER)
85        self.point_density_ctrl.SetValue(str(pt_density))
86        self.point_density_ctrl.SetToolTip(wx.ToolTip("Enter the number of real-space points per Angstrom cube"))
87        self.point_density_ctrl.Bind(wx.EVT_TEXT_ENTER, self._on_density_changed)
88        self.point_density_ctrl.Bind(wx.EVT_KILL_FOCUS, self._on_density_changed)
89        self.bck.Add(self.point_density_ctrl, (ny,1), flag = wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border = 15)
90       
91        # Q range
92        q_min_text = wx.StaticText(self, -1, "Q min", style=wx.ALIGN_LEFT)
93        ny+=1
94        self.bck.Add(q_min_text, (ny,0), flag = wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border = 15)
95        self.q_min_ctrl = wx.TextCtrl(self, -1, size=(40,20), style=wx.TE_PROCESS_ENTER)
96        self.q_min_ctrl.SetValue(str(q_min))
97        self.q_min_ctrl.SetToolTip(wx.ToolTip("Enter the minimum Q value to be simulated"))
98        self.q_min_ctrl.Bind(wx.EVT_TEXT_ENTER, self._on_q_range_changed)
99        self.q_min_ctrl.Bind(wx.EVT_KILL_FOCUS, self._on_q_range_changed)
100        self.bck.Add(self.q_min_ctrl, (ny,1), flag = wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border = 15)
101       
102        q_max_text = wx.StaticText(self, -1, "Q max", style=wx.ALIGN_LEFT)
103        self.bck.Add(q_max_text, (ny,2), flag = wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border = 15)
104        self.q_max_ctrl = wx.TextCtrl(self, -1, size=(40,20), style=wx.TE_PROCESS_ENTER)
105        self.q_max_ctrl.SetValue(str(q_max))
106        self.q_min_ctrl.SetToolTip(wx.ToolTip("Enter the maximum Q value to be simulated"))
107        self.q_max_ctrl.Bind(wx.EVT_TEXT_ENTER, self._on_q_range_changed)
108        self.q_max_ctrl.Bind(wx.EVT_KILL_FOCUS, self._on_q_range_changed)
109        self.bck.Add(self.q_max_ctrl, (ny,3), flag = wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border = 15)
110       
111        q_npts_text = wx.StaticText(self, -1, "No. of Q pts", style=wx.ALIGN_LEFT)
112        self.bck.Add(q_npts_text, (ny,4), flag = wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border = 15)
113        self.q_npts_ctrl = wx.TextCtrl(self, -1, size=(40,20), style=wx.TE_PROCESS_ENTER)
114        self.q_npts_ctrl.SetValue(str(q_npts))
115        self.q_min_ctrl.SetToolTip(wx.ToolTip("Enter the number of Q points to be simulated"))
116        self.q_npts_ctrl.Bind(wx.EVT_TEXT_ENTER, self._on_q_range_changed)
117        self.q_npts_ctrl.Bind(wx.EVT_KILL_FOCUS, self._on_q_range_changed)
118        self.bck.Add(self.q_npts_ctrl, (ny,5), flag = wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border = 15)
119       
120        # Shape List
121        # Internal counter of shapes in the listbox
122        self.counter = 0
123        # Buffer filled flag
124        self.buffer_filled = False
125        # Save current flag
126        self.current_saved = False       
127         
128        id = wx.NewId()
129        shape_listbox_text = wx.StaticText(self, -1, "List of shapes on 3D canvas", style=wx.ALIGN_LEFT)
130        self.shape_listbox = wx.ListBox(self, id, wx.DefaultPosition, (295, 200), 
131                                   [], wx.LB_SINGLE | wx.LB_HSCROLL)
132       
133        # Listen to history events
134        self.parent.Bind(EVT_ADD_SHAPE, self._on_add_shape_to_listbox)
135        self.shape_listbox.Bind(wx.EVT_LISTBOX, self._on_select_from_listbox, id=id)
136        self.shape_listbox.Bind(wx.EVT_CONTEXT_MENU, self._on_listbox_context_menu, id=id)
137       
138        ny+=1
139        self.bck.Add(shape_listbox_text, (ny,0), (1,2), flag = wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border = 15)
140        ny+=1
141        self.bck.Add(self.shape_listbox, (ny,0), (1,5), flag = wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border = 15)
142       
143        self.current_shape = None
144       
145        # Default shape
146        if type(self.shape_list)==list and len(self.shape_list)>0:
147            shape = SimCanvas.getShapeClassByName(self.shape_list[0]['name'])()
148            self.editShape(shape)                       
149
150    def _on_select_shape(self, event):
151        shape = SimCanvas.getShapeClassByName(self.shape_list[event.GetEventObject().GetSelection()]['name'])()
152        self.editShape(shape)   
153
154    def _on_density_changed(self, event):
155        """
156            Process event that might mean a change of the simulation point density
157        """
158        npts  = self._readCtrlFloat(self.point_density_ctrl)
159        if npts is not None:
160            event = PtDensityEvent(npts=npts)
161            wx.PostEvent(self.parent, event)
162           
163    def _on_q_range_changed(self, event):
164        """
165            Process event that might mean a change in Q range
166            @param event: EVT_Q_RANGE event
167        """
168        q_min = self._readCtrlFloat(self.q_min_ctrl)
169        q_max = self._readCtrlFloat(self.q_max_ctrl)
170        npts  = self._readCtrlInt(self.q_npts_ctrl)
171        if q_min is not None or q_max is not None or npts is not None:
172            event = QRangeEvent(q_min=q_min, q_max=q_max, npts=npts)
173            wx.PostEvent(self.parent, event)
174           
175    def _onEditShape(self, evt):
176        """
177            Process an EVT_EDIT_SHAPE event
178            @param evt: EVT_EDIT_SHAPE object
179        """
180        evt.Skip()
181        self.editShape(evt.shape, False)
182       
183    def editShape(self, shape=None, new=True):
184        """
185            Rebuild the panel
186        """
187        self.current_shape = shape
188        self.shape_sizer.Clear(True) 
189        #self.type = type 
190       
191        if shape==None:
192            title = wx.StaticText(self, -1, "Use menu to add a shapes", style=wx.ALIGN_LEFT)
193            self.shape_sizer.Add(title, (0,0), (1,2), flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=15)
194
195        else:
196            if new==False:
197                title = wx.StaticText(self, -1, "Edit shape parameters", style=wx.ALIGN_LEFT)
198            else:
199                title = wx.StaticText(self, -1, "Create shape from parameters", style=wx.ALIGN_LEFT)
200               
201            self.shape_sizer.Add(title, (0,0), (1,2), flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=15)
202           
203            n = 1
204            self.parameters = []
205           
206            for item in sorted(shape.params.keys()):
207                if item in ['contrast', 'order']:
208                    continue
209                n += 1
210                text = wx.StaticText(self, -1, item, style=wx.ALIGN_LEFT)
211                self.shape_sizer.Add(text, (n-1,0), flag = wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border = 15)
212                ctl = wx.TextCtrl(self, -1, size=(40,20), style=wx.TE_PROCESS_ENTER)
213               
214               
215                ctl.SetValue(str(shape.params[item]))
216                self.Bind(wx.EVT_TEXT_ENTER, self.onTextEnter)
217                ctl.Bind(wx.EVT_KILL_FOCUS, self.onTextEnter)
218                self.parameters.append([item, ctl])
219                self.shape_sizer.Add(ctl, (n-1,1), flag=wx.TOP|wx.BOTTOM, border = 0)
220               
221                # Units
222                units = wx.StaticText(self, -1, shape.details[item], style=wx.ALIGN_LEFT)
223                self.shape_sizer.Add(units, (n-1,2), flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border = 15)
224
225            for item in ['contrast', 'order']:
226                n += 1
227                text = wx.StaticText(self, -1, item, style=wx.ALIGN_LEFT)
228                self.shape_sizer.Add(text, (n-1,0), flag = wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border = 15)
229                ctl = wx.TextCtrl(self, -1, size=(40,20), style=wx.TE_PROCESS_ENTER)
230               
231               
232                ctl.SetValue(str(shape.params[item]))
233                self.Bind(wx.EVT_TEXT_ENTER, self.onTextEnter)
234                ctl.Bind(wx.EVT_KILL_FOCUS, self.onTextEnter)
235                self.parameters.append([item, ctl])
236                self.shape_sizer.Add(ctl, (n-1,1), flag=wx.TOP|wx.BOTTOM, border = 0)
237               
238                # Units
239                units = wx.StaticText(self, -1, shape.details[item], style=wx.ALIGN_LEFT)
240                self.shape_sizer.Add(units, (n-1,2), flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border = 15)
241               
242               
243            # Add position
244            n += 1
245           
246            pos_sizer = wx.GridBagSizer(0,0)
247           
248            text = wx.StaticText(self, -1, 'x', style=wx.ALIGN_LEFT)
249            pos_sizer.Add(text, (0,0), (1,1), flag = wx.LEFT|wx.ALIGN_CENTER, border = 15)
250            text = wx.StaticText(self, -1, 'y', style=wx.ALIGN_LEFT)
251            pos_sizer.Add(text, (0,1), (1,1), flag = wx.LEFT|wx.ALIGN_CENTER, border = 15)
252            text = wx.StaticText(self, -1, 'z', style=wx.ALIGN_LEFT)
253            pos_sizer.Add(text, (0,2), (1,1), flag = wx.LEFT|wx.ALIGN_CENTER, border = 15)
254           
255            self.xctrl = wx.TextCtrl(self, -1, size=(40,20), style=wx.TE_PROCESS_ENTER)
256            self.xctrl.SetValue(str(shape.x))
257            self.xctrl.Bind(wx.EVT_KILL_FOCUS, self.onTextEnter)
258            pos_sizer.Add(self.xctrl, (1,0), (1,1), flag = wx.LEFT|wx.ALIGN_CENTER, border = 15)
259            self.yctrl = wx.TextCtrl(self, -1, size=(40,20), style=wx.TE_PROCESS_ENTER)
260            self.yctrl.SetValue(str(shape.y))
261            self.yctrl.Bind(wx.EVT_KILL_FOCUS, self.onTextEnter)
262            pos_sizer.Add(self.yctrl, (1,1), (1,1), flag = wx.LEFT|wx.ALIGN_CENTER, border = 15)
263            self.zctrl = wx.TextCtrl(self, -1, size=(40,20), style=wx.TE_PROCESS_ENTER)
264            self.zctrl.SetValue(str(shape.z))
265            self.zctrl.Bind(wx.EVT_KILL_FOCUS, self.onTextEnter)
266            pos_sizer.Add(self.zctrl, (1,2), (1,1), flag = wx.LEFT|wx.ALIGN_CENTER, border = 15)
267           
268            self.shape_sizer.Add(pos_sizer, (n-1, 0), (1,3), flag=wx.LEFT|wx.ALIGN_CENTER)
269           
270            # Add orientation
271            n += 1
272           
273            pos_sizer = wx.GridBagSizer(0,0)
274           
275            text = wx.StaticText(self, -1, 'theta_x', style=wx.ALIGN_LEFT)
276            pos_sizer.Add(text, (0,0), (1,1), flag = wx.LEFT|wx.ALIGN_CENTER, border = 15)
277            text = wx.StaticText(self, -1, 'theta_y', style=wx.ALIGN_LEFT)
278            pos_sizer.Add(text, (0,1), (1,1), flag = wx.LEFT|wx.ALIGN_CENTER, border = 15)
279            text = wx.StaticText(self, -1, 'theta_z', style=wx.ALIGN_LEFT)
280            pos_sizer.Add(text, (0,2), (1,1), flag = wx.LEFT|wx.ALIGN_CENTER, border = 15)
281           
282            self.actrl = wx.TextCtrl(self, -1, size=(40,20), style=wx.TE_PROCESS_ENTER)
283            self.actrl.SetValue(str(shape.theta_x))
284            self.actrl.Bind(wx.EVT_KILL_FOCUS, self.onTextEnter)
285            pos_sizer.Add(self.actrl, (1,0), (1,1), flag = wx.LEFT|wx.ALIGN_CENTER, border = 15)
286            self.bctrl = wx.TextCtrl(self, -1, size=(40,20), style=wx.TE_PROCESS_ENTER)
287            self.bctrl.SetValue(str(shape.theta_y))
288            self.bctrl.Bind(wx.EVT_KILL_FOCUS, self.onTextEnter)
289            pos_sizer.Add(self.bctrl, (1,1), (1,1), flag = wx.LEFT|wx.ALIGN_CENTER, border = 15)
290            self.cctrl = wx.TextCtrl(self, -1, size=(40,20), style=wx.TE_PROCESS_ENTER)
291            self.cctrl.SetValue(str(shape.theta_z))
292            self.cctrl.Bind(wx.EVT_KILL_FOCUS, self.onTextEnter)
293            pos_sizer.Add(self.cctrl, (1,2), (1,1), flag = wx.LEFT|wx.ALIGN_CENTER, border = 15)
294           
295            self.shape_sizer.Add(pos_sizer, (n-1, 0), (1,3), flag=wx.LEFT|wx.ALIGN_CENTER)
296           
297           
298            # Add a button to create the new shape on the canvas
299            n += 1
300            if new==True:
301                create = wx.Button(self, 1,"Create")
302                self.shape_sizer.Add(create, (n-1,1), (1,2), flag=wx.ALIGN_RIGHT|wx.ALL, border = 15)
303                self.Bind(wx.EVT_BUTTON, self._onCreate, id = 1)       
304            else:
305                create = wx.Button(self, 1,"Apply")
306                self.shape_sizer.Add(create, (n-1,1), (1,2), flag=wx.ALIGN_RIGHT|wx.ALL, border = 15)
307                self.Bind(wx.EVT_BUTTON, self._onEdited, id = 1)       
308               
309
310
311        self.shape_sizer.Layout()
312        self.shape_sizer.Fit(self)
313        try:
314            self.parent.GetSizer().Layout()
315        except:
316            print("TODO: move the Layout call of editShape up to the caller")
317
318    def _readCtrlFloat(self, ctrl):
319        """
320            Parses a TextCtrl for a float value.
321            Returns None is the value hasn't changed or the value is not a float.
322            The control background turns pink if the value is not a float.
323           
324            @param ctrl: TextCtrl object
325        """
326        if ctrl.IsModified():
327            ctrl.SetModified(False)
328            str_value = ctrl.GetValue().lstrip().rstrip()
329            try:
330                flt_value = float(str_value)
331                ctrl.SetBackgroundColour(
332                        wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
333                ctrl.Refresh()
334                return flt_value
335            except:
336                ctrl.SetBackgroundColour("pink")
337                ctrl.Refresh()
338        return None
339
340    def _readCtrlInt(self, ctrl):
341        """
342            Parses a TextCtrl for a float value.
343            Returns None is the value hasn't changed or the value is not a float.
344            The control background turns pink if the value is not a float.
345           
346            @param ctrl: TextCtrl object
347        """
348        if ctrl.IsModified():
349            ctrl.SetModified(False)
350            str_value = ctrl.GetValue().lstrip().rstrip()
351            try:
352                int_value = int(str_value)
353                ctrl.SetBackgroundColour(
354                        wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
355                ctrl.Refresh()
356                return int_value
357            except:
358                ctrl.SetBackgroundColour("pink")
359                ctrl.Refresh()
360        return None
361
362    def _parseCtrl(self):
363        try:
364            # Position
365            tmp = self._readCtrlFloat(self.xctrl)
366            if not tmp==None:
367                self.current_shape.x = tmp
368               
369            tmp = self._readCtrlFloat(self.yctrl)
370            if not tmp==None:
371                self.current_shape.y = tmp
372               
373            tmp = self._readCtrlFloat(self.zctrl)
374            if not tmp==None:
375                self.current_shape.z = tmp
376               
377            # Orientation
378            tmp = self._readCtrlFloat(self.actrl)
379            if not tmp==None:
380                self.current_shape.theta_x = tmp
381               
382            tmp = self._readCtrlFloat(self.bctrl)
383            if not tmp==None:
384                self.current_shape.theta_y = tmp
385               
386            tmp = self._readCtrlFloat(self.cctrl)
387            if not tmp==None:
388                self.current_shape.theta_z = tmp
389               
390            # Parameters
391            for item in self.parameters:
392                tmp = self._readCtrlFloat(item[1])
393                if not tmp==None:
394                    self.current_shape.params[item[0]] = tmp
395        except Exception as exc:
396            print("Could not create")
397            print(exc)
398               
399    def _onCreate(self, evt):
400        """
401            Create a new shape with the parameters given by the user
402        """
403        self._parseCtrl()
404        event = CreateShapeEvent(shape=self.current_shape, new=True)
405        wx.PostEvent(self.parent, event)
406        self.editShape(self.current_shape, False)
407           
408       
409    def _onEdited(self, evt):
410        self._parseCtrl()
411        event = CreateShapeEvent(shape=self.current_shape, new=False)
412        wx.PostEvent(self.parent, event)
413        self.editShape(self.current_shape, False)
414
415    def onTextEnter(self, evt): 
416        """
417            Read text field to check values
418        """ 
419        self._readCtrlFloat(self.xctrl)
420        self._readCtrlFloat(self.yctrl)
421        self._readCtrlFloat(self.zctrl)
422        self._readCtrlFloat(self.actrl)
423        self._readCtrlFloat(self.bctrl)
424        self._readCtrlFloat(self.cctrl)
425        for item in self.parameters:
426            self._readCtrlFloat(item[1])
427 
428    #-- Methods to support list of shapes --
429    def _on_add_shape_to_listbox(self, event):
430        """
431            Process a new shape           
432            @param event: EVT_ADD_SHAPE event
433        """
434        event.Skip()
435        if event.new:
436            self.counter += 1
437            self.shape_listbox.Insert("%d: %s" % (self.counter, event.shape.name), 
438                                 self.shape_listbox.GetCount(), event.shape)
439
440    def _on_select_from_listbox(self, event):
441        """
442            Process item selection events
443        """
444        index = event.GetSelection()
445        view_name = self.shape_listbox.GetString(index)
446        self.editShape(shape=event.GetClientData(), new=False)
447        # The following event is bound to the SimPanel to highlight the shape in blue
448        #TODO: how come it doesn't work?
449        wx.PostEvent(self.parent, EditShapeEvent(shape=event.GetClientData()))
450        #TODO: select the shape from the drop down
451       
452    def _on_listbox_context_menu(self, event):
453        """
454            Popup context menu event
455        """
456        # Create context menu
457        popupmenu = wx.Menu()
458       
459        # Create an item to delete the selected shape from the canvas
460        id = wx.NewId()
461        popupmenu.Append(id, "&Remove Selected")
462        wx.EVT_MENU(self, id, self._on_remove_shape_from_listbox)
463       
464        # Create an item to rename a shape to a more user friendly name
465        #id = wx.NewId()
466        #popupmenu.Append(102, "&Rename Selected")
467        #wx.EVT_MENU(self, 102, self.onRename)
468
469        pos = event.GetPosition()
470        pos = self.ScreenToClient(pos)
471        self.PopupMenu(popupmenu, pos)       
472       
473    def _on_remove_shape_from_listbox(self, ev):
474        """
475            Remove an item
476        """
477        indices = self.shape_listbox.GetSelections()
478        if len(indices)>0:
479            name =  self.shape_listbox.GetClientData(indices[0]).name
480            self.shape_listbox.Delete(indices[0])
481            wx.PostEvent(self.parent, DelShapeEvent(id = name))
482       
483    def _on_rename_shape_from_listbox(self, ev):
484        """
485            Rename an item
486        """
487        indices = self.shape_listbox.GetSelections()
488        if len(indices)>0:
489            print("NOT YET IMPLMENTED")
490            print("renaming", self.shape_listbox.GetString(indices[0]))
Note: See TracBrowser for help on using the repository browser.