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

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.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 578a11d was f1cbae7, checked in by Piotr Rozyczko <rozyczko@…>, 8 years ago

Allow resize of the error panel. Fixed #688

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