- Timestamp:
- Apr 6, 2015 1:14:09 AM (10 years ago)
- Branches:
- master, ESS_GUI, ESS_GUI_Docs, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_iss959, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc, costrafo411, magnetic_scatt, release-4.1.1, release-4.1.2, release-4.2.2, release_4.0.1, ticket-1009, ticket-1094-headless, ticket-1242-2d-resolution, ticket-1243, ticket-1249, ticket885, unittest-saveload
- Children:
- 2d50115
- Parents:
- 439420f9
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sas/perspectives/calculator/model_editor.py
r439420f9 r18ac46b 7 7 complete customizatin is provided. 8 8 9 :TODOthe writiong of the file and name checking (and maybe some other9 ..TODO:: the writiong of the file and name checking (and maybe some other 10 10 funtions?) should be moved to a computational module which could be called 11 11 fropm a python script. Basically one just needs to pass the name, … … 17 17 #This software was developed by the University of Tennessee as part of the 18 18 #Distributed Data Analysis of Neutron Scattering Experiments (DANSE) 19 #project funded by the US National Science Foundation. 19 #project funded by the US National Science Foundation. 20 20 # 21 21 #See the license text in license.txt … … 33 33 FONT_VARIANT = 0 34 34 PNL_WIDTH = 450 35 PNL_H ITE= 32035 PNL_HEIGHT = 320 36 36 else: 37 37 FONT_VARIANT = 1 38 38 PNL_WIDTH = 590 39 PNL_H ITE= 35039 PNL_HEIGHT = 350 40 40 M_NAME = 'Model' 41 41 EDITOR_WIDTH = 800 … … 45 45 46 46 47 def _compile File(path):47 def _compile_file(path): 48 48 """ 49 49 Compile the file in the path … … 57 57 return value 58 58 59 def _delete File(path):59 def _delete_file(path): 60 60 """ 61 61 Delete file in the path … … 71 71 Dialog for easy custom composite models. Provides a wx.Dialog panel 72 72 to choose two existing models (including pre-existing custom models which 73 may themselves be sum models) as well as an operation on those models (add74 or multiply) the resulting model will add a scale parameter for summed73 may themselves be composite models) as well as an operation on those models 74 (add or multiply) the resulting model will add a scale parameter for summed 75 75 models and a background parameter for a multiplied model. 76 76 … … 83 83 a Modal Dialog. 84 84 85 :TODOthe build in compiler currently balks at when it tries to import85 ..TODO:: the build in compiler currently balks at when it tries to import 86 86 a model whose name contains spaces or symbols (such as + ... underscore 87 should be fine) need to either alter how the model file is written on add 88 checking method to look for non-approved characters. - PDB:April 5, 2015 87 should be fine). Have fixed so the editor cannot save such a file name 88 but if a file is dropped in the plugin directory from outside this class 89 will create a file that cannot be compiled. Should add the check to 90 the write method or to the on_modelx method. 91 - PDB:April 5, 2015 89 92 """ 90 93 def __init__(self, parent=None, base=None, id=None, title='', 91 94 model_list=[], plugin_dir=None): 92 95 """ 93 This class is run when instatiated. The __init__ in tializes and96 This class is run when instatiated. The __init__ initializes and 94 97 calls the internal methods necessary. On exiting the wx.Dialog 95 98 window should be destroyed. 96 97 99 """ 98 100 wx.Dialog.__init__(self, parent=parent, id=id, 99 title=title, size=(PNL_WIDTH, PNL_H ITE))101 title=title, size=(PNL_WIDTH, PNL_HEIGHT)) 100 102 self.parent = base 101 103 #Font … … 123 125 self.model2 = None 124 126 self.static_line_1 = None 125 self.ok Button = None126 self.close Button = None127 self.ok_button = None 128 self.close_button = None 127 129 self._msg_box = None 128 130 self.msg_sizer = None … … 152 154 153 155 self.name_sizer.AddMany([(self.name_txt, 0, wx.LEFT | wx.TOP, 10), 154 (self.name_tcl, -1,155 wx.EXPAND | wx.RIGHT | wx.TOP | wx.BOTTOM, 10)])156 (self.name_tcl, -1, 157 wx.EXPAND | wx.RIGHT | wx.TOP | wx.BOTTOM, 10)]) 156 158 157 159 … … 170 172 171 173 self.desc_sizer.AddMany([(desc_txt, 0, wx.LEFT | wx.TOP, 10), 172 (self.desc_tcl, -1, 173 wx.EXPAND | wx.RIGHT | wx.TOP | wx.BOTTOM, 10)]) 174 (self.desc_tcl, -1, 175 wx.EXPAND | wx.RIGHT | wx.TOP | wx.BOTTOM, 176 10)]) 174 177 175 178 … … 183 186 selection_box_title = wx.StaticBox(self, -1, 'Select', 184 187 size=(PNL_WIDTH - 30, 70)) 185 self._selection_box = wx.StaticBoxSizer(selection_box_title, wx.VERTICAL) 188 self._selection_box = wx.StaticBoxSizer(selection_box_title, 189 wx.VERTICAL) 186 190 187 191 #Next create the help labels for the model selection 188 192 select_help_box = wx.BoxSizer(wx.HORIZONTAL) 189 193 model_string = " Model%s (p%s):" 190 select_help_box.Add(wx.StaticText(self, -1, model_string % (1, 1)), 0, 0) 194 select_help_box.Add(wx.StaticText(self, -1, model_string % (1, 1)), 195 0, 0) 191 196 select_help_box.Add((box_width - 25, 10),0,0) 192 select_help_box.Add(wx.StaticText(self, -1, model_string % (2, 2)), 0, 0) 197 select_help_box.Add(wx.StaticText(self, -1, model_string % (2, 2)), 198 0, 0) 193 199 self._selection_box.Add(select_help_box, 0, 0) 194 200 … … 201 207 self.model1.SetToolTipString("model1") 202 208 203 self._operator_choice = wx.ComboBox(self, -1, size=(50, -1), style=wx.CB_READONLY) 209 self._operator_choice = wx.ComboBox(self, -1, size=(50, -1), 210 style=wx.CB_READONLY) 204 211 wx.EVT_COMBOBOX(self._operator_choice, -1, self.on_select_operator) 205 212 operation_tip = "Add: +, Multiply: * " … … 217 224 selection_box_choose.Add((15, 10)) 218 225 selection_box_choose.Add(self.model2, 0, 0) 219 self._selection_box.Add((20,5), 0, 0) # add some space between labels and selection 226 # add some space between labels and selection 227 self._selection_box.Add((20,5), 0, 0) 220 228 self._selection_box.Add(selection_box_choose, 0, 0) 221 229 … … 260 268 # Finally add the buttons (apply and close) on the bottom 261 269 # TODO: need help added here 262 self.ok Button = wx.Button(self, wx.ID_OK, 'Apply')263 self.ok Button.Bind(wx.EVT_BUTTON, self.check_name)264 self.close Button = wx.Button(self, wx.ID_CANCEL, 'Close')270 self.ok_button = wx.Button(self, wx.ID_OK, 'Apply') 271 self.ok_button.Bind(wx.EVT_BUTTON, self.check_name) 272 self.close_button = wx.Button(self, wx.ID_CANCEL, 'Close') 265 273 sizer_button = wx.BoxSizer(wx.HORIZONTAL) 266 274 sizer_button.AddMany([((20, 20), 1, 0), 267 (self.ok Button, 0, 0),268 (self.close Button, 0, wx.LEFT | wx.RIGHT, 10)])275 (self.ok_button, 0, 0), 276 (self.close_button, 0, wx.LEFT | wx.RIGHT, 10)]) 269 277 mainsizer.Add(sizer_button, 0, wx.EXPAND | wx.BOTTOM | wx.TOP, 10) 270 278 … … 287 295 pink background in text box else call on_apply 288 296 289 :TODOthis should be separated out from the GUI code. For that we297 ..TODO:: this should be separated out from the GUI code. For that we 290 298 need to pass it the name (or if we want to keep the default name 291 299 option also need to pass the self._operator attribute) We just need … … 317 325 self.good_name = False 318 326 msg = ("%s is not a valid Python name. Only alphanumeric \n" \ 319 "and underscore allowed" % self.name)327 "and underscore allowed" % self.name) 320 328 321 329 #Now check if the name already exists … … 350 358 def on_apply(self, path): 351 359 """ 352 On Apply 360 This method is a misnomer - it is not bound to the apply button 361 event. Instead the apply button event goes to check_name which 362 then calls this method if the name of the new file is acceptable. 363 364 ..todo:: this should be bound to the apply button. The first line 365 should call the check_name method which itself should be in another 366 module separated from the the GUI modules. 353 367 """ 354 368 self.name_tcl.SetBackgroundColour('white') 355 369 try: 356 label = self.get Text()370 label = self.get_textnames() 357 371 fname = path 358 372 name1 = label[0] … … 373 387 from sas.guiframe.events import StatusEvent 374 388 wx.PostEvent(self.parent.parent, StatusEvent(status=msg, 375 info=info))389 info=info)) 376 390 else: 377 391 raise … … 382 396 """ 383 397 # list of model names 384 cm_list = [] 385 # models 386 list = self.model_list 387 # custom models 388 al_list = os.listdir(self.plugin_dir) 389 for c_name in al_list: 390 if c_name.split('.')[-1] == 'py' and \ 391 c_name.split('.')[0] != '__init__': 392 name = str(c_name.split('.')[0]) 393 cm_list.append(name) 394 if name not in list: 395 list.append(name) 396 self.cm_list = cm_list 397 if len(list) > 1: 398 list.sort() 399 for idx in range(len(list)): 400 self.model1.Append(str(list[idx]), idx) 401 self.model2.Append(str(list[idx]), idx) 398 # get regular models 399 main_list = self.model_list 400 # get custom models 401 self.update_cm_list() 402 # add custom models to model list 403 for name in self.cm_list: 404 if name not in main_list: 405 main_list.append(name) 406 407 if len(main_list) > 1: 408 main_list.sort() 409 for idx in range(len(main_list)): 410 self.model1.Append(str(main_list[idx]), idx) 411 self.model2.Append(str(main_list[idx]), idx) 402 412 self.model1.SetStringSelection(self.model1_string) 403 413 self.model2.SetStringSelection(self.model2_string) … … 473 483 self._operator = type 474 484 self.explanation = " Custom Model = %s %s (model1 %s model2)\n" % \ 475 (self.factor, f_oper, self._operator)485 (self.factor, f_oper, self._operator) 476 486 self.explanationctr.SetLabel(self.explanation) 477 487 self.name = name + M_NAME … … 488 498 self._operator_choice.SetSelection(0) 489 499 490 def get Text(self):500 def get_textnames(self): 491 501 """ 492 502 Returns model name string as list … … 502 512 if description == '': 503 513 description = name1 + self._operator + name2 504 name = self.name_tcl.GetValue().lstrip().rstrip()505 514 text = self._operator_choice.GetValue() 506 515 if text.count('+') > 0: … … 573 582 """ 574 583 path = self.fname 575 _compile File(path)584 _compile_file(path) 576 585 577 586 def delete_file(self, path): … … 579 588 Delete file in the path 580 589 """ 581 _delete File(path)590 _delete_file(path) 582 591 583 592 … … 588 597 def __init__(self, parent, base, path, title, *args, **kwds): 589 598 kwds['name'] = title 590 kwds["size"] = (EDITOR_WIDTH, EDITOR_HEIGTH)599 # kwds["size"] = (EDITOR_WIDTH, EDITOR_HEIGTH) 591 600 kwds["style"] = wx.FULL_REPAINT_ON_RESIZE 592 601 wx.ScrolledWindow.__init__(self, parent, *args, **kwds) 593 #self.SetupScrolling()594 602 self.parent = parent 595 603 self.base = base … … 605 613 self.name_sizer = None 606 614 self.name_hsizer = None 615 self.name_tcl = None 607 616 self.desc_sizer = None 617 self.desc_tcl = None 608 618 self.param_sizer = None 619 self.param_tcl = None 609 620 self.function_sizer = None 610 621 self.func_horizon_sizer = None … … 618 629 self._description = "New Custom Model" 619 630 self.function_tcl = None 631 self.math_combo = None 632 self.bt_apply = None 633 self.bt_close = None 620 634 #self._default_save_location = os.getcwd() 621 635 self._do_layout() 622 #self.bt_apply.Disable() 636 623 637 624 638 … … 656 670 self.name_tcl.SetToolTipString(hint_name) 657 671 self.name_hsizer.AddMany([(self.name_tcl, 0, wx.LEFT | wx.TOP, 0), 658 672 (overwrite_cb, 0, wx.LEFT, 20)]) 659 673 self.name_sizer.AddMany([(name_txt, 0, wx.LEFT | wx.TOP, 10), 660 661 674 (self.name_hsizer, 0, 675 wx.LEFT | wx.TOP | wx.BOTTOM, 10)]) 662 676 663 677 … … 670 684 self.desc_tcl = wx.TextCtrl(self, -1, size=(PANEL_WIDTH * 3 / 5, -1)) 671 685 self.desc_tcl.SetValue('') 672 #self.name_tcl.SetFont(self.font)673 686 hint_desc = "Write a short description of the model function." 674 687 self.desc_tcl.SetToolTipString(hint_desc) 675 688 self.desc_sizer.AddMany([(desc_txt, 0, wx.LEFT | wx.TOP, 10), 676 677 689 (self.desc_tcl, 0, 690 wx.LEFT | wx.TOP | wx.BOTTOM, 10)]) 678 691 def _layout_param(self): 679 692 """ … … 686 699 param_tip += "A = 1\nB = 1" 687 700 #param_txt.SetToolTipString(param_tip) 688 id = wx.NewId() 689 self.param_tcl = EditWindow(self, id, wx.DefaultPosition, 690 wx.DefaultSize, wx.CLIP_CHILDREN | wx.SUNKEN_BORDER) 701 newid = wx.NewId() 702 self.param_tcl = EditWindow(self, newid, wx.DefaultPosition, 703 wx.DefaultSize, 704 wx.CLIP_CHILDREN | wx.SUNKEN_BORDER) 691 705 self.param_tcl.setDisplayLineNumbers(True) 692 706 self.param_tcl.SetToolTipString(param_tip) 693 707 694 708 self.param_sizer.AddMany([(param_txt, 0, wx.LEFT, 10), 695 (self.param_tcl, 1, wx.EXPAND | wx.ALL, 10)])709 (self.param_tcl, 1, wx.EXPAND | wx.ALL, 10)]) 696 710 697 711 def _layout_function(self): … … 709 723 math_combo = self._fill_math_combo() 710 724 711 id = wx.NewId() 712 self.function_tcl = EditWindow(self, id, wx.DefaultPosition, 713 wx.DefaultSize, wx.CLIP_CHILDREN | wx.SUNKEN_BORDER) 725 newid = wx.NewId() 726 self.function_tcl = EditWindow(self, newid, wx.DefaultPosition, 727 wx.DefaultSize, 728 wx.CLIP_CHILDREN | wx.SUNKEN_BORDER) 714 729 self.function_tcl.setDisplayLineNumbers(True) 715 730 self.function_tcl.SetToolTipString(hint_function) … … 758 773 self._layout_msg() 759 774 self._layout_button() 760 self.main_sizer.AddMany([(self.name_sizer, 0, 761 wx.EXPAND | wx.ALL, 5), 775 self.main_sizer.AddMany([(self.name_sizer, 0, wx.EXPAND | wx.ALL, 5), 762 776 (wx.StaticLine(self), 0, 763 wx.ALL | wx.EXPAND, 5), 764 (self.desc_sizer, 0, 765 wx.EXPAND | wx.ALL, 5), 777 wx.ALL | wx.EXPAND, 5), 778 (self.desc_sizer, 0, wx.EXPAND | wx.ALL, 5), 766 779 (wx.StaticLine(self), 0, 767 wx.ALL | wx.EXPAND, 5), 768 (self.param_sizer, 1, 769 wx.EXPAND | wx.ALL, 5), 780 wx.ALL | wx.EXPAND, 5), 781 (self.param_sizer, 1, wx.EXPAND | wx.ALL, 5), 770 782 (wx.StaticLine(self), 0, 771 772 (self.function_sizer, 2,773 783 wx.ALL | wx.EXPAND, 5), 784 (self.function_sizer, 2, 785 wx.EXPAND | wx.ALL, 5), 774 786 (wx.StaticLine(self), 0, 775 wx.ALL | wx.EXPAND, 5), 776 (self.msg_sizer, 0, 777 wx.EXPAND | wx.ALL, 5), 778 (self.button_sizer, 0, 779 wx.EXPAND | wx.ALL, 5)]) 787 wx.ALL | wx.EXPAND, 5), 788 (self.msg_sizer, 0, wx.EXPAND | wx.ALL, 5), 789 (self.button_sizer, 0, wx.EXPAND | wx.ALL, 5)]) 780 790 self.SetSizer(self.main_sizer) 781 791 self.SetAutoLayout(True) … … 860 870 if event is not None: 861 871 event.Skip() 862 cb = event.GetEventObject()863 self.overwrite_name = cb .GetValue()872 cb_value = event.GetEventObject() 873 self.overwrite_name = cb_value.GetValue() 864 874 865 875 def on_click_apply(self, event): 866 876 """ 867 Changes are saved in data object imported to edit 877 Changes are saved in data object imported to edit. 878 879 checks firs for valid name, then if it already exists then checks 880 that a function was entered and finally that if entered it contains at 881 least a return statement. If all passes writes file then tries to 882 compile. If compile fails or import module fails or run method fails 883 tries to remove any .py and pyc files that may have been created and 884 sets error message. 885 886 ..todo:: this code still could do with a careful going over to clean 887 up and simplify. the non GUI methods such as this one should be removed 888 to computational code of SasView. Most of those computational methods 889 would be the same for both the simple editors. 868 890 """ 869 891 #must post event here 870 892 event.Skip() 893 name = self.name_tcl.GetValue().lstrip().rstrip() 871 894 info = 'Info' 872 895 msg = '' 873 896 # Sort out the errors if occur 874 if self.check_name(): 875 name = self.name_tcl.GetValue().lstrip().rstrip() 897 # First check for valid python name then if the name already exists 898 if not re.match('^[A-Za-z0-9_]*$',name): 899 msg = "not a valid python name. Name must include only alpha \n" 900 msg += "numeric or underline characters and no spaces" 901 elif self.check_name(): 876 902 description = self.desc_tcl.GetValue() 877 903 param_str = self.param_tcl.GetText() … … 881 907 if func_str.count('return') > 0: 882 908 self.write_file(self.fname, description, param_str, 883 884 tr_msg = _compile File(self.fname)909 func_str) 910 tr_msg = _compile_file(self.fname) 885 911 msg = str(tr_msg.__str__()) 886 912 # Compile error … … 895 921 else: 896 922 msg = "Name exists already." 897 # Prepare forthe messagebox923 # Prepare the messagebox 898 924 if self.base != None and not msg: 899 925 self.base.update_custom_combo() 900 926 Model = None 901 exec "from %s import Model" % name 927 try: 928 exec "from %s import Model" % name 929 except: 930 msg = 'new model fails to import in python' 931 if self.base != None and not msg: 902 932 try: 903 933 Model().run(0.01) 904 934 except: 905 msg = " Error"935 msg = "new model fails on run method:" 906 936 _, value, _ = sys.exc_info() 907 937 msg += "in %s:\n%s\n" % (name, value) 938 # Prepare the messagebox 908 939 if msg: 909 940 info = 'Error' … … 911 942 try: 912 943 # try to remove pyc file if exists 913 _delete File(self.fname)914 _delete File(self.fname + "c")944 _delete_file(self.fname) 945 _delete_file(self.fname + "c") 915 946 except: 916 947 pass … … 921 952 info = 'Info' 922 953 color = 'blue' 923 # Not to display long error msg 924 if info == 'Error': 925 mss = info 926 else: 927 mss = msg 928 self._msg_box.SetLabel(mss) 954 self._msg_box.SetLabel(msg) 929 955 self._msg_box.SetForegroundColour(color) 930 # Send msg to the top window 956 # Send msg to the top window 931 957 if self.base != None: 932 from sas.guiframe.events import StatusEvent 933 wx.PostEvent(self.base.parent, StatusEvent(status=msg, 934 info=info)) 958 from sas.guiframe.events import StatusEvent 959 wx.PostEvent(self.base.parent, StatusEvent(status=msg, info=info)) 935 960 self.warning = msg 936 961 … … 947 972 try: 948 973 out_f = open(fname, 'w') 949 except 974 except: 950 975 raise 951 976 # Prepare the content of the function … … 962 987 # write function here 963 988 for line in lines: 964 # The location where to put the strings is 989 # The location where to put the strings is 965 990 # hard-coded in the template as shown below. 966 991 if line.count("#self.params here"): … … 1017 1042 :param line: one line of string got from the param_str 1018 1043 """ 1019 flag = True1020 1044 params_str = '' 1021 1045 spaces = ' '#8spaces … … 1038 1062 :param line: one line of string got from the param_str 1039 1063 """ 1040 flag = True1041 1064 params_str = '' 1042 1065 spaces = ' '#8spaces … … 1085 1108 #if self.parent != None: 1086 1109 # self.parent.new_model_frame = None 1087 #self.Destroy() 1110 #self.Destroy() 1088 1111 1089 1112 ## Templates for custom models … … 1489 1512 """ 1490 1513 1491 if __name__ == "__main__": 1514 if __name__ == "__main__": 1492 1515 # app = wx.PySimpleApp() 1493 1516 app = wx.App() 1494 frame = TextDialog(id=1, model_list=["SphereModel", "CylinderModel"], plugin_dir='../fitting/plugin_models')1495 # frame = wx.Dialog()1517 frame = TextDialog(id=1, model_list=["SphereModel", "CylinderModel"], 1518 plugin_dir='../fitting/plugin_models') 1496 1519 frame.ShowModal() 1497 # frame.Show(True) 1498 app.MainLoop() 1520 app.MainLoop() 1499 1521 1500 1522 #if __name__ == "__main__":
Note: See TracChangeset
for help on using the changeset viewer.