source: sasview/sansguiframe/src/sans/guiframe/CategoryManager.py @ ae4c139

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since ae4c139 was ea5fa58, checked in by Jae Cho <jhjcho@…>, 12 years ago

category stuffs start working in interp. environment

  • Property mode set to 100755
File size: 16.4 KB
RevLine 
[df7a7e3]1#!/usr/bin/python
2
3"""
4
5/**
6        This software was developed by Institut Laue-Langevin as part of
7        Distributed Data Analysis of Neutron Scattering Experiments (DANSE).
8
9        Copyright 2012 Institut Laue-Langevin
10
11**/
12
13"""
14
15
16import wx
17import sys
[ea5fa58]18import os
[df7a7e3]19from wx.lib.mixins.listctrl import CheckListCtrlMixin, ListCtrlAutoWidthMixin
20from collections import defaultdict
21import cPickle as pickle
22from sans.guiframe.events import ChangeCategoryEvent
23from sans.guiframe.CategoryInstaller import CategoryInstaller
24
25""" Notes
26The category manager mechanism works from 3 data structures used:
27- self.master_category_dict: keys are the names of categories,
28the values are lists of tuples,
29the first being the model names (the models belonging to that
30category), the second a boolean
31of whether or not the model is enabled
32- self.by_model_dict: keys are model names, values are a list
33of categories belonging to that model
34- self.model_enabled_dict: keys are model names, values are
35bools of whether the model is enabled
36use self._regenerate_model_dict() to create the latter two
37structures from the former
38use self._regenerate_master_dict() to create the first
39structure from the latter two
40
41The need for so many data structures comes from the fact
42sometimes we need fast access
43to all the models in a category (eg user selection from the gui)
44and sometimes we need access to all the categories
45corresponding to a model (eg user modification of model categories)
46
47"""
48
49
50
51class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin, 
52                    ListCtrlAutoWidthMixin):
53    """
54    Taken from
55    http://zetcode.com/wxpython/advanced/
56    """
57
58    def __init__(self, parent, callback_func):
59        """
60        Initialization
61        :param parent: Parent window
62        :param callback_func: A function to be called when
63        an element is clicked
64        """
65        wx.ListCtrl.__init__(self, parent, -1, style=wx.LC_REPORT \
66                                 | wx.SUNKEN_BORDER)
67        CheckListCtrlMixin.__init__(self)
68        ListCtrlAutoWidthMixin.__init__(self)
69
70        self.callback_func = callback_func
71       
72    def OnCheckItem(self, index, flag):
73        """
74        When the user checks the item we need to save that state
75        """
76        self.callback_func(index, flag)
77   
78
79class CategoryManager(wx.Frame):
80    """
81    A class for managing categories
82    """
83    def __init__(self, parent, win_id, title):
84        """
85        Category Manager Dialog class
86        :param win_id: A new wx ID
87        :param title: Title for the window
88        """
89       
90        # make sure the category file is where it should be
91        self.performance_blocking = False
92
93        self.master_category_dict = defaultdict(list)
94        self.by_model_dict = defaultdict(list)
95        self.model_enabled_dict = defaultdict(bool)
96
97        wx.Frame.__init__(self, parent, win_id, title, size=(650, 400))
98
99        panel = wx.Panel(self, -1)
100        self.parent = parent
101
102        self._read_category_info()
103
104
105        vbox = wx.BoxSizer(wx.VERTICAL)
106        hbox = wx.BoxSizer(wx.HORIZONTAL)
107
108        left_panel = wx.Panel(panel, -1)
109        right_panel = wx.Panel(panel, -1)
110
111        self.cat_list = CheckListCtrl(right_panel, self._on_check)
112        self.cat_list.InsertColumn(0, 'Model', width = 280)
113        self.cat_list.InsertColumn(1, 'Category', width = 240)
114
115        self._fill_lists() 
116        self._regenerate_model_dict()
117        self._set_enabled()     
118
119        vbox2 = wx.BoxSizer(wx.VERTICAL)
120
121        sel = wx.Button(left_panel, -1, 'Enable All', size=(100, -1))
122        des = wx.Button(left_panel, -1, 'Disable All', size=(100, -1))
123        modify_button = wx.Button(left_panel, -1, 'Modify', 
124                                  size=(100, -1))
125        ok_button = wx.Button(left_panel, -1, 'OK', size=(100, -1))
126        cancel_button = wx.Button(left_panel, -1, 'Cancel', 
127                                  size=(100, -1))       
128
129       
130
131        self.Bind(wx.EVT_BUTTON, self._on_selectall, 
132                  id=sel.GetId())
133        self.Bind(wx.EVT_BUTTON, self._on_deselectall, 
134                  id=des.GetId())
135        self.Bind(wx.EVT_BUTTON, self._on_apply, 
136                  id = modify_button.GetId())
137        self.Bind(wx.EVT_BUTTON, self._on_ok, 
138                  id = ok_button.GetId())
139        self.Bind(wx.EVT_BUTTON, self._on_cancel, 
140                  id = cancel_button.GetId())
141
142        vbox2.Add(modify_button, 0, wx.TOP, 10)
143        vbox2.Add((-1, 20))
144        vbox2.Add(sel)
145        vbox2.Add(des)
146        vbox2.Add((-1, 20))
147        vbox2.Add(ok_button)
148        vbox2.Add(cancel_button)
149
150        left_panel.SetSizer(vbox2)
151
152        vbox.Add(self.cat_list, 1, wx.EXPAND | wx.TOP, 3)
153        vbox.Add((-1, 10))
154
155
156        right_panel.SetSizer(vbox)
157
158        hbox.Add(left_panel, 0, wx.EXPAND | wx.RIGHT, 5)
159        hbox.Add(right_panel, 1, wx.EXPAND)
160        hbox.Add((3, -1))
161
162        panel.SetSizer(hbox)
163        self.performance_blocking = True
164
165
166        self.Centre()
167        self.Show(True)
168
169        # gui stuff finished
170
171    def _on_check(self, index, flag):
172        """
173        When the user checks an item we need to immediately save that state.
174        :param index: The index of the checked item
175        :param flag: True or False whether the item was checked
176        """
177        if self.performance_blocking:
178            # for computational reasons we don't want to
179            # call this function every time the gui is set up
180            model_name = self.cat_list.GetItem(index, 0).GetText()
181            self.model_enabled_dict[model_name] = flag
182            self._regenerate_master_dict()
183
184
185    def _fill_lists(self):
186        """
187        Expands lists on the GUI
188        """
189        self.cat_list.DeleteAllItems()
190        model_name_list = [model for model in self.by_model_dict]
191        model_name_list.sort()
192
193        for model in model_name_list:
194            index = self.cat_list.InsertStringItem(sys.maxint, model)
195            self.cat_list.SetStringItem(index, 1, \
196                                            str(self.by_model_dict[model]).\
197                                            replace("'","").\
198                                            replace("[","").\
199                                            replace("]",""))
200
201
202           
203    def _set_enabled(self):
204        """
205        Updates enabled models from self.model_enabled_dict
206        """
207        num = self.cat_list.GetItemCount()
208        for i in range(num):
209            model_name = self.cat_list.GetItem(i, 0).GetText()
210            self.cat_list.CheckItem(i, 
211                                    self.model_enabled_dict[model_name] )
212                                   
213
214
215    def _on_selectall(self, event):
216        """
217        Callback for 'enable all'
218        """
219        self.performance_blocking = False
220        num = self.cat_list.GetItemCount()
221        for i in range(num):
222            self.cat_list.CheckItem(i)
223        for model in self.model_enabled_dict:
224            self.model_enabled_dict[model] = True
225        self._regenerate_master_dict()
226        self.performance_blocking = True
227
228    def _on_deselectall(self, event):
229        """
230        Callback for 'disable all'
231        """
232        self.performance_blocking = False
233        num = self.cat_list.GetItemCount()
234        for i in range(num):
235            self.cat_list.CheckItem(i, False)
236        for model in self.model_enabled_dict:
237            self.model_enabled_dict[model] = False
238        self._regenerate_master_dict()
239        self.performance_blocking = True
240
241    def _on_apply(self, event):
242        """
243        Call up the 'ChangeCat' dialog for category editing
244        """
245
246        if self.cat_list.GetSelectedItemCount() == 0:
247            wx.MessageBox('Please select a model', 'Error',
248                          wx.OK | wx.ICON_EXCLAMATION )
249
250        else:
251            selected_model = \
252                self.cat_list.GetItem(\
253                self.cat_list.GetFirstSelected(), 0).GetText()
254
255
256            modify_dialog = ChangeCat(self, 'Change Category: ' + \
257                                          selected_model, 
258                                      self._get_cat_list(),
259                                      self.by_model_dict[selected_model])
260
261            if modify_dialog.ShowModal() == wx.ID_OK:
262                self.by_model_dict[selected_model] = \
263                    modify_dialog.get_category()
264                self._regenerate_master_dict()
265                self._fill_lists()
266                self._set_enabled()
267
268    def _on_ok(self, event):
269        """
270        Close the manager
271        """
272        self._save_state()
273        evt = ChangeCategoryEvent()
274        wx.PostEvent(self.parent, evt)
275
276        self.Destroy()
277
278    def _on_cancel(self, event):
279        """
280        On cancel
281        """
282        self.Destroy()
283
284    def _save_state(self):
285        """
286        Serializes categorization info to file
287        """
288
289        self._regenerate_master_dict()
290
291        cat_file = open(CategoryInstaller.get_user_file(), 'wb')
292
[ea5fa58]293        pickle.dump( self.master_category_dict, cat_file )
[df7a7e3]294
295   
296    def _read_category_info(self):
297        """
298        Read in categorization info from file
299        """
300        try:
[ea5fa58]301                file = CategoryInstaller.get_user_file()
302                if os.path.isfile(file):
303                    cat_file = open(file, 'rb')
304                    self.master_category_dict = pickle.load(cat_file)
305                else:
306                        cat_file = open(CategoryInstaller.get_default_file(), 'rb')
307                        self.master_category_dict = pickle.load(cat_file)
[df7a7e3]308        except IOError:
309            print 'Problem reading in category file. Please review'
310
311
312        self._regenerate_model_dict()
313
314    def _get_cat_list(self):
315        """
316        Returns a simple list of categories
317        """
318        cat_list = list()
319        for category in self.master_category_dict.iterkeys():
320            if not category == 'Uncategorized':
321                cat_list.append(category)
322   
323        return cat_list
324
325    def _regenerate_model_dict(self):
326        """
327        regenerates self.by_model_dict which has each model
328        name as the key
329        and the list of categories belonging to that model
330        along with the enabled mapping
331        """
332        self.by_model_dict = defaultdict(list)
333        for category in self.master_category_dict:
334            for (model, enabled) in self.master_category_dict[category]:
335                self.by_model_dict[model].append(category)
336                self.model_enabled_dict[model] = enabled
337
338    def _regenerate_master_dict(self):
339        """
340        regenerates self.master_category_dict from
341        self.by_model_dict and self.model_enabled_dict
342        """
343        self.master_category_dict = defaultdict(list)
344        for model in self.by_model_dict:
345            for category in self.by_model_dict[model]:
346                self.master_category_dict[category].append\
347                    ((model, self.model_enabled_dict[model]))
348   
349
350
351class ChangeCat(wx.Dialog):
352    """
353    dialog for changing the categories of a model
354    """
355
356    def __init__(self, parent, title, cat_list, current_cats):
357        """
358        Actual editor for a certain category
359        :param parent: Window parent
360        :param title: Window title
361        :param cat_list: List of all categories
362        :param current_cats: List of categories applied to current model
363        """
364        wx.Dialog.__init__(self, parent, title = title, size=(480, 420))
365
366        self.current_cats = current_cats
367        if str(self.current_cats[0]) == 'Uncategorized':
368            self.current_cats = []
369
370        self.cat_list = cat_list
371       
372        self.cat_text = wx.StaticText(self, label = "Current categories: ")
373        self.current_categories = wx.ListBox(self, 
374                                             choices = self.current_cats
375                                             , size=(300, 100))
376        self.existing_check = wx.RadioButton(self, 
377                                             label = 'Choose Existing')
378        self.new_check = wx.RadioButton(self, label = 'Create new')
379        self.exist_combo = wx.ComboBox(self, style = wx.CB_READONLY, 
380                                       size=(220,-1), choices = cat_list)
381        self.exist_combo.SetSelection(0)
382
383        self.add_sb = wx.StaticBox(self, label = "Add Category")
384        self.add_sb_sizer = wx.StaticBoxSizer(self.add_sb, wx.VERTICAL)
385        self.remove_sb = wx.StaticBox(self, label = "Remove Category")
386        self.remove_sb_sizer = wx.StaticBoxSizer(self.remove_sb, 
387                                                 wx.VERTICAL)
388
389        self.new_text = wx.TextCtrl(self, size=(220, -1))
390        self.ok_button = wx.Button(self, wx.ID_OK, "Done")
391        self.add_button = wx.Button(self, label = "Add")
392        self.add_button.Bind(wx.EVT_BUTTON, self.on_add)
393        self.remove_button = wx.Button(self, label = "Remove Selected")
394        self.remove_button.Bind(wx.EVT_BUTTON, self.on_remove)
395
396        self.existing_check.Bind(wx.EVT_RADIOBUTTON, self.on_existing)
397        self.new_check.Bind(wx.EVT_RADIOBUTTON, self.on_newcat)
398
399
400
401        vbox = wx.BoxSizer(wx.VERTICAL)
402        vbox.Add(self.cat_text, flag = wx.LEFT | wx.TOP | wx.ALIGN_LEFT, 
403                 border = 10)
404        vbox.Add(self.current_categories, flag = wx.ALL | wx.EXPAND, 
405                 border = 10  )
406
407        gs = wx.GridSizer(3, 2, 5, 5)
408
409        gs.AddMany( [ (self.existing_check, 5, wx.ALL),
410                      (self.exist_combo, 5, wx.ALL),
411                      (self.new_check, 5, wx.ALL),
412                      (self.new_text, 5, wx.ALL ),
413                      ((-1,-1)),
414                      (self.add_button, 5, wx.ALL | wx.ALIGN_RIGHT) ] )
415
416        self.add_sb_sizer.Add(gs, proportion = 1, flag = wx.ALL, border = 5)
417        vbox.Add(self.add_sb_sizer, flag = wx.ALL | wx.EXPAND, border = 10)
418
419        self.remove_sb_sizer.Add(self.remove_button, border = 5, 
420                                 flag = wx.ALL | wx.ALIGN_RIGHT)
421        vbox.Add(self.remove_sb_sizer, 
422                 flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 
423                 border = 10)
424        vbox.Add(self.ok_button, flag = wx.ALL | wx.ALIGN_RIGHT, 
425                 border = 10)
[ea5fa58]426       
427        if self.current_categories.GetCount() > 0:
428                self.current_categories.SetSelection(0)
[df7a7e3]429        self.new_text.Disable()
430        self.SetSizer(vbox)
431        self.Centre()
432        self.Show(True)
433
434    def on_add(self, event):
435        """
436        Callback for new category added
437        """
438        new_cat = ''
439        if self.existing_check.GetValue():
440            new_cat = str(self.exist_combo.GetValue())
441        else:
442            new_cat = str(self.new_text.GetValue())
443            if new_cat in self.cat_list:
444                wx.MessageBox('%s is already a model' % new_cat, 'Error',
445                              wx.OK | wx.ICON_EXCLAMATION )
446                return
447
448        if new_cat in self.current_cats:
449            wx.MessageBox('%s is already included in this model' \
450                              % new_cat, 'Error',
451                          wx.OK | wx.ICON_EXCLAMATION )
452            return
453
454        self.current_cats.append(new_cat)
455        self.current_categories.SetItems(self.current_cats)
456           
457       
458    def on_remove(self, event):
459        """
460        Callback for a category removed
461        """
462        if self.current_categories.GetSelection() == wx.NOT_FOUND:
463            wx.MessageBox('Please select a category to remove', 'Error',
464                          wx.OK | wx.ICON_EXCLAMATION )
465        else:
466            self.current_categories.Delete( \
467                self.current_categories.GetSelection())
468            self.current_cats = self.current_categories.GetItems()
469
470       
471
472    def on_newcat(self, event):
473        """
474        Callback for new category added
475        """
476        self.new_text.Enable()
477        self.exist_combo.Disable()
478
479
480    def on_existing(self, event):   
481        """
482        Callback for existing category selected
483        """
484        self.new_text.Disable()
485        self.exist_combo.Enable()
486
487    def get_category(self):
488        """
489        Returns a list of categories applying to this model
490        """
491        if not self.current_cats:
492            self.current_cats.append("Uncategorized")
493
494        ret = list()
495        for cat in self.current_cats:
496            ret.append(str(cat))
497        return ret
498
499if __name__ == '__main__':
500       
501   
502    if(len(sys.argv) > 1):
503        app = wx.App()
504        CategoryManager(None, -1, 'Category Manager', sys.argv[1])
505        app.MainLoop()
506    else:
507        app = wx.App()
508        CategoryManager(None, -1, 'Category Manager', sys.argv[1])
509        app.MainLoop()
510
Note: See TracBrowser for help on using the repository browser.