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

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.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since b768e92 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
Line 
1"""
2Console Module display Python console
3"""
4import sys
5import os
6
7import numpy as np
8
9import wx
10from wx.lib.dialogs import ScrolledMessageDialog
11import wx.py.editor as editor
12
13if sys.platform.count("win32") > 0:
14    PANEL_WIDTH = 800
15    PANEL_HEIGHT = 700
16    FONT_VARIANT = 0
17else:
18    PANEL_WIDTH = 830
19    PANEL_HEIGHT = 730
20    FONT_VARIANT = 1
21ID_CHECK_MODEL = wx.NewId()
22ID_RUN = wx.NewId()
23
24def check_model(path):
25    """
26    Check that the model on the path can run.
27    """
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
54    try:
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
75
76class PyConsole(editor.EditorNotebookFrame):
77    ## Internal nickname for the window, used by the AUI manager
78    window_name = "Custom Model Editor"
79    ## Name to appear on the window title bar
80    window_caption = "Custom Model Editor"
81    ## Flag to tell the AUI manager to put this panel in the center pane
82    CENTER_PANE = False
83    def __init__(self, parent=None, base=None, manager=None, panel=None,
84                    title='Python Shell/Editor', filename=None,
85                    size=(PANEL_WIDTH, PANEL_HEIGHT)):
86        self.config = None
87        editor.EditorNotebookFrame.__init__(self, parent=parent,
88                                        title=title, size=size,
89                                        filename=filename)
90        self.parent = parent
91        self._manager = manager
92        self.base = base
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
102        self.Centre()
103
104        self.Bind(wx.EVT_MENU, self.OnNewFile, id=wx.ID_NEW)
105        self.Bind(wx.EVT_MENU, self.OnOpenFile, id=wx.ID_OPEN)
106        self.Bind(wx.EVT_MENU, self.OnSaveFile, id=wx.ID_SAVE)
107        self.Bind(wx.EVT_MENU, self.OnSaveAsFile, id=wx.ID_SAVEAS)
108        self.Bind(wx.EVT_MENU, self.OnCheckModel, id=ID_CHECK_MODEL)
109        self.Bind(wx.EVT_MENU, self.OnRun, id=ID_RUN)
110        self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateCompileMenu, id=ID_CHECK_MODEL)
111        self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateCompileMenu, id=ID_RUN)
112        self.Bind(wx.EVT_CLOSE, self.on_close)
113        if not title.count('Python Shell'):
114            # Delete menu item (open and new) if not python shell
115            #self.fileMenu.Delete(wx.ID_NEW)
116            self.fileMenu.Delete(wx.ID_OPEN)
117
118
119    def _add_menu(self):
120        """
121        Add menu
122        """
123        self.compileMenu = wx.Menu()
124        self.compileMenu.Append(ID_CHECK_MODEL, 'Check model',
125                 'Loading and run the model')
126        self.compileMenu.AppendSeparator()
127        self.compileMenu.Append(ID_RUN, 'Run in Shell',
128                 'Run the file in the Python Shell')
129        self.MenuBar.Insert(3, self.compileMenu, '&Run')
130
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,
139                                                   size=((700, 540)))
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
146    def set_manager(self, manager):
147        """
148        Set the manager of this window
149        """
150        self._manager = manager
151
152    def OnAbout(self, event):
153        """
154        On About
155        """
156        message = ABOUT
157        dial = wx.MessageDialog(self, message, 'About',
158                           wx.OK | wx.ICON_INFORMATION)
159        dial.ShowModal()
160
161    def OnNewFile(self, event):
162        """
163        OnFileOpen
164        """
165        self.OnFileNew(event)
166
167    def OnOpenFile(self, event):
168        """
169        OnFileOpen
170        """
171        self.OnFileOpen(event)
172        self.Show(False)
173        self.Show(True)
174
175    def OnSaveFile(self, event):
176        """
177        OnFileSave overwrite
178        """
179        self.OnFileSave(event)
180        self.Show(False)
181        self.Show(True)
182
183    def OnSaveAsFile(self, event):
184        """
185        OnFileSaveAs overwrite
186        """
187        self.OnFileSaveAs(event)
188        self.Show(False)
189        self.Show(True)
190
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
202        if not filedir:
203            filedir = self.dataDir
204        result = editor.openSingle(directory=filedir,
205                            wildcard='Python Files (*.py)|*.py')
206        if result.path:
207            self.bufferCreate(result.path)
208        cancel = False
209        return cancel
210
211    def bufferSaveAs(self):
212        """
213        Save buffer to a new filename: Bypassing editor bufferSaveAs
214        """
215        filedir = ''
216        if self.buffer and self.buffer.doc.filedir:
217            filedir = self.buffer.doc.filedir
218        if not filedir:
219            filedir = self.dataDir
220        result = editor.saveSingle(directory=filedir,
221                                   filename='untitled.py',
222                                   wildcard='Python Files (*.py)|*.py')
223        if result.path:
224            self.buffer.confirmed = True
225            self.buffer.saveAs(result.path)
226            cancel = False
227        else:
228            cancel = True
229        return cancel
230
231    def OnRun(self, event):
232        """
233        Run
234        """
235        if not self._check_saved():
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('\\', '/')
241            self.shell.Execute("execfile('%s')" % forward_path)
242            self.shell.Hide()
243            self.shell.Show(True)
244            return self.shell.GetText().split(">>>")[-2]
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)
250            return False
251
252    def OnCheckModel(self, event):
253        """
254        Compile
255        """
256        if not self._check_saved():
257            return True
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):
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()
272            return not cancel
273        return True
274
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:
283            if id == ID_CHECK_MODEL or id == ID_RUN:
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)
291
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()
299
300ABOUT = "Welcome to Python %s! \n\n" % sys.version.split()[0]
301ABOUT += "This uses Py Shell/Editor in wx (developed by Patrick K. O'Brien).\n"
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/."
305
306
307if __name__ == "__main__":
308
309    app = wx.App()
310    dlg = PyConsole()
311    dlg.Show()
312    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.