source: sasview/src/sas/sasgui/perspectives/calculator/pyconsole.py @ 934ce649

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 934ce649 was 26d6e045, checked in by Paul Kienzle <pkienzle@…>, 9 years ago

make sure compiler/test errors on custom models are reported to the user

  • Property mode set to 100644
File size: 9.9 KB
RevLine 
[ec6c520]1"""
2Console Module display Python console
3"""
4import sys
[5d1c1f4]5import os
[26d6e045]6
7import numpy as np
8
[fb58234]9import wx
[26d6e045]10from wx.lib.dialogs import ScrolledMessageDialog
[fb58234]11import wx.py.editor as editor
[5d1c1f4]12
[49ab5d7]13if sys.platform.count("win32") > 0:
[ec6c520]14    PANEL_WIDTH = 800
[7c8d3093]15    PANEL_HEIGHT = 700
[ec6c520]16    FONT_VARIANT = 0
17else:
18    PANEL_WIDTH = 830
[7c8d3093]19    PANEL_HEIGHT = 730
[ec6c520]20    FONT_VARIANT = 1
[26d6e045]21ID_CHECK_MODEL = wx.NewId()
[49ab5d7]22ID_RUN = wx.NewId()
[5d1c1f4]23
[26d6e045]24def check_model(path):
[5d1c1f4]25    """
[26d6e045]26    Check that the model on the path can run.
[5d1c1f4]27    """
[26d6e045]28    # try running the model
29    from sasmodels.core import load_model, call_kernel
30    model = load_model(path)
31
32    q =  np.array([0.01, 0.1])
33    kernel = model.make_kernel([q])
34    Iq = call_kernel(kernel, {})
35
36    qx, qy =  np.array([0.01, 0.01]), np.array([0.1, 0.1])
37    kernel = model.make_kernel([qx, qy])
38    Iqxy = call_kernel(kernel, {})
39
40    result = """
41    Iq(%s) = %s
42    Iqxy(%s, %s) = %s
43    """%(q, Iq, qx, qy, Iqxy)
44
45    return result
46
47def show_model_output(parent, fname):
48    # Make sure we have a python file; not sure why we care though...
49    if not (fname and os.path.exists(fname) and fname.endswith('.py')):
50        mssg = "\n This is not a python file."
51        wx.MessageBox(str(mssg), 'Error', style=wx.ICON_ERROR)
52        return False
53
[5d1c1f4]54    try:
[26d6e045]55        result, errmsg = check_model(fname), None
56    except Exception:
57        import traceback
58        result, errmsg = None, traceback.format_exc(limit=2)
59
60    parts = ["Running model '%s'..." % os.path.basename(fname)]
61    if errmsg is not None:
62        parts.extend(["", "Error occurred:", errmsg, ""])
63        title, icon = "Error", wx.ICON_ERROR
64    else:
65        parts.extend(["", "Success:", result, ""])
66        title, icon = "Info", wx.ICON_INFORMATION
67    text = "\n".join(parts)
68    dlg = ScrolledMessageDialog(parent, text, title, size=((550, 250)))
69    fnt = wx.Font(10, wx.TELETYPE, wx.NORMAL, wx.NORMAL)
70    dlg.GetChildren()[0].SetFont(fnt)
71    dlg.GetChildren()[0].SetInsertionPoint(0)
72    dlg.ShowModal()
73    dlg.Destroy()
74    return errmsg is None
[5d1c1f4]75
[fb58234]76class PyConsole(editor.EditorNotebookFrame):
[ec6c520]77    ## Internal nickname for the window, used by the AUI manager
[5d1c1f4]78    window_name = "Custom Model Editor"
[ec6c520]79    ## Name to appear on the window title bar
[5d1c1f4]80    window_caption = "Custom Model Editor"
[ec6c520]81    ## Flag to tell the AUI manager to put this panel in the center pane
82    CENTER_PANE = False
[ae84427]83    def __init__(self, parent=None, base=None, manager=None, panel=None,
[fb58234]84                    title='Python Shell/Editor', filename=None,
[ec6c520]85                    size=(PANEL_WIDTH, PANEL_HEIGHT)):
[fb58234]86        self.config = None
[49ab5d7]87        editor.EditorNotebookFrame.__init__(self, parent=parent,
[ec6c520]88                                        title=title, size=size,
[fb58234]89                                        filename=filename)
[ec6c520]90        self.parent = parent
91        self._manager = manager
[ae84427]92        self.base = base
[5d1c1f4]93        self.panel = panel
94        self._add_menu()
95        if filename != None:
96            dataDir = os.path.dirname(filename)
97        elif self.parent != None:
98            dataDir = self.parent._default_save_location
99        else:
100             dataDir = None
101        self.dataDir = dataDir
[ec6c520]102        self.Centre()
[49ab5d7]103
[7c8d3093]104        self.Bind(wx.EVT_MENU, self.OnNewFile, id=wx.ID_NEW)
[4c5448c]105        self.Bind(wx.EVT_MENU, self.OnOpenFile, id=wx.ID_OPEN)
[5d1c1f4]106        self.Bind(wx.EVT_MENU, self.OnSaveFile, id=wx.ID_SAVE)
107        self.Bind(wx.EVT_MENU, self.OnSaveAsFile, id=wx.ID_SAVEAS)
[26d6e045]108        self.Bind(wx.EVT_MENU, self.OnCheckModel, id=ID_CHECK_MODEL)
[5d1c1f4]109        self.Bind(wx.EVT_MENU, self.OnRun, id=ID_RUN)
[26d6e045]110        self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateCompileMenu, id=ID_CHECK_MODEL)
[5d1c1f4]111        self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateCompileMenu, id=ID_RUN)
[ae84427]112        self.Bind(wx.EVT_CLOSE, self.on_close)
[7c8d3093]113        if not title.count('Python Shell'):
114            # Delete menu item (open and new) if not python shell
[86be650]115            #self.fileMenu.Delete(wx.ID_NEW)
[7c8d3093]116            self.fileMenu.Delete(wx.ID_OPEN)
[49ab5d7]117
118
[5d1c1f4]119    def _add_menu(self):
[861f8317]120        """
[5d1c1f4]121        Add menu
[861f8317]122        """
[5d1c1f4]123        self.compileMenu = wx.Menu()
[26d6e045]124        self.compileMenu.Append(ID_CHECK_MODEL, 'Check model',
125                 'Loading and run the model')
[5d1c1f4]126        self.compileMenu.AppendSeparator()
[7c8d3093]127        self.compileMenu.Append(ID_RUN, 'Run in Shell',
[5d1c1f4]128                 'Run the file in the Python Shell')
[7c8d3093]129        self.MenuBar.Insert(3, self.compileMenu, '&Run')
[49ab5d7]130
[5d1c1f4]131    def OnHelp(self, event):
132        """
133        Show a help dialog.
134        """
135        import  wx.lib.dialogs
136        title = 'Help on key bindings'
137        text = wx.py.shell.HELP_TEXT
138        dlg = wx.lib.dialogs.ScrolledMessageDialog(self, text, title,
[49ab5d7]139                                                   size=((700, 540)))
[5d1c1f4]140        fnt = wx.Font(10, wx.TELETYPE, wx.NORMAL, wx.NORMAL)
141        dlg.GetChildren()[0].SetFont(fnt)
142        dlg.GetChildren()[0].SetInsertionPoint(0)
143        dlg.ShowModal()
144        dlg.Destroy()
145
[ec6c520]146    def set_manager(self, manager):
147        """
148        Set the manager of this window
149        """
150        self._manager = manager
[49ab5d7]151
[861f8317]152    def OnAbout(self, event):
153        """
154        On About
155        """
156        message = ABOUT
[b1bda35]157        dial = wx.MessageDialog(self, message, 'About',
[49ab5d7]158                           wx.OK | wx.ICON_INFORMATION)
[861f8317]159        dial.ShowModal()
[49ab5d7]160
[7c8d3093]161    def OnNewFile(self, event):
162        """
[49ab5d7]163        OnFileOpen
[7c8d3093]164        """
165        self.OnFileNew(event)
[4c5448c]166
167    def OnOpenFile(self, event):
168        """
[49ab5d7]169        OnFileOpen
[4c5448c]170        """
171        self.OnFileOpen(event)
172        self.Show(False)
173        self.Show(True)
[49ab5d7]174
[5d1c1f4]175    def OnSaveFile(self, event):
176        """
[49ab5d7]177        OnFileSave overwrite
[5d1c1f4]178        """
179        self.OnFileSave(event)
180        self.Show(False)
181        self.Show(True)
[49ab5d7]182
[5d1c1f4]183    def OnSaveAsFile(self, event):
184        """
[49ab5d7]185        OnFileSaveAs overwrite
[5d1c1f4]186        """
187        self.OnFileSaveAs(event)
188        self.Show(False)
189        self.Show(True)
190
[7c8d3093]191    def bufferOpen(self):
192        """
193        Open file in buffer, bypassing editor bufferOpen
194        """
195        if self.bufferHasChanged():
196            cancel = self.bufferSuggestSave()
197            if cancel:
198                return cancel
199        filedir = ''
200        if self.buffer and self.buffer.doc.filedir:
201            filedir = self.buffer.doc.filedir
[86be650]202        if not filedir:
203            filedir = self.dataDir
[49ab5d7]204        result = editor.openSingle(directory=filedir,
[7c8d3093]205                            wildcard='Python Files (*.py)|*.py')
206        if result.path:
207            self.bufferCreate(result.path)
208        cancel = False
209        return cancel
[49ab5d7]210
[5d1c1f4]211    def bufferSaveAs(self):
212        """
[7c8d3093]213        Save buffer to a new filename: Bypassing editor bufferSaveAs
[5d1c1f4]214        """
215        filedir = ''
216        if self.buffer and self.buffer.doc.filedir:
217            filedir = self.buffer.doc.filedir
[86be650]218        if not filedir:
219            filedir = self.dataDir
[49ab5d7]220        result = editor.saveSingle(directory=filedir,
[7c8d3093]221                                   filename='untitled.py',
222                                   wildcard='Python Files (*.py)|*.py')
[5d1c1f4]223        if result.path:
[f706e09c]224            self.buffer.confirmed = True
[5d1c1f4]225            self.buffer.saveAs(result.path)
226            cancel = False
227        else:
228            cancel = True
229        return cancel
[49ab5d7]230
[5d1c1f4]231    def OnRun(self, event):
232        """
233        Run
234        """
[26d6e045]235        if not self._check_saved():
[5d1c1f4]236            return True
237        if self.buffer and self.buffer.doc.filepath:
238            self.editor.setFocus()
239            # Why we have to do this (Otherwise problems on Windows)?
240            forward_path = self.buffer.doc.filepath.replace('\\', '/')
[49ab5d7]241            self.shell.Execute("execfile('%s')" % forward_path)
[5d1c1f4]242            self.shell.Hide()
243            self.shell.Show(True)
[7c8d3093]244            return self.shell.GetText().split(">>>")[-2]
[5d1c1f4]245        else:
246            mssg = "\n This is not a python file."
247            title = 'Error'
248            icon = wx.ICON_ERROR
249            wx.MessageBox(str(mssg), title, style=icon)
[26d6e045]250            return False
[49ab5d7]251
[26d6e045]252    def OnCheckModel(self, event):
[5d1c1f4]253        """
254        Compile
255        """
[26d6e045]256        if not self._check_saved():
[5d1c1f4]257            return True
[26d6e045]258        fname = self.editor.getStatus()[0]
259        success = show_model_output(self, fname)
260
261        # Update custom model list in fitpage combobox
262        if success and self._manager != None and self.panel != None:
263            self._manager.set_edit_menu_helper(self.parent)
264            wx.CallAfter(self._manager.update_custom_combo)
265
266    def _check_saved(self):
[5d1c1f4]267        """
268        If content was changed, suggest to save it first
269        """
270        if self.bufferHasChanged() and self.buffer.doc.filepath:
271            cancel = self.bufferSuggestSave()
[26d6e045]272            return not cancel
[5d1c1f4]273        return True
[49ab5d7]274
[5d1c1f4]275    def OnUpdateCompileMenu(self, event):
276        """
277        Update Compile menu items based on current tap.
278        """
279        win = wx.Window.FindFocus()
280        id = event.GetId()
281        event.Enable(True)
282        try:
[26d6e045]283            if id == ID_CHECK_MODEL or id == ID_RUN:
[5d1c1f4]284                menu_on = False
285                if self.buffer and self.buffer.doc.filepath:
286                    menu_on = True
287                event.Enable(menu_on)
288        except AttributeError:
289            # This menu option is not supported in the current context.
290            event.Enable(False)
[49ab5d7]291
[ae84427]292    def on_close(self, event):
293        """
294        Close event
295        """
296        if self.base != None:
297            self.base.py_frame = None
298        self.Destroy()
[49ab5d7]299
300ABOUT = "Welcome to Python %s! \n\n" % sys.version.split()[0]
[5d1c1f4]301ABOUT += "This uses Py Shell/Editor in wx (developed by Patrick K. O'Brien).\n"
[861f8317]302ABOUT += "If this is your first time using Python, \n"
303ABOUT += "you should definitely check out the tutorial "
304ABOUT += "on the Internet at http://www.python.org/doc/tut/."
[49ab5d7]305
306
[ec6c520]307if __name__ == "__main__":
[49ab5d7]308
309    app = wx.App()
[ec6c520]310    dlg = PyConsole()
311    dlg.Show()
[49ab5d7]312    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.