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

Last change on this file since d619341 was d472e86, checked in by Piotr Rozyczko <rozyczko@…>, 8 years ago

C models also available through Edit Custom Model/Advanced?

  • Property mode set to 100644
File size: 11.2 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 = "Plugin 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
113        editor.EditorNotebookFrame.__init__(self, parent=parent,
114                                        title=title, size=size)
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        # See if there is a corresponding C file
130        if filename != None:
131            c_filename = os.path.splitext(filename)[0] + ".c"
132            if os.path.isfile(c_filename):
133                self.bufferCreate(c_filename)
134
135            # If not, just open the requested .py, if any.
136            # Needs to be after the C file so the tab focus is correct.
137            if os.path.isfile(filename):
138                    self.bufferCreate(filename)
139
140        self.Bind(wx.EVT_MENU, self.OnNewFile, id=wx.ID_NEW)
141        self.Bind(wx.EVT_MENU, self.OnOpenFile, id=wx.ID_OPEN)
142        self.Bind(wx.EVT_MENU, self.OnSaveFile, id=wx.ID_SAVE)
143        self.Bind(wx.EVT_MENU, self.OnSaveAsFile, id=wx.ID_SAVEAS)
144        self.Bind(wx.EVT_MENU, self.OnCheckModel, id=ID_CHECK_MODEL)
145        self.Bind(wx.EVT_MENU, self.OnRun, id=ID_RUN)
146        self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateCompileMenu, id=ID_CHECK_MODEL)
147        self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateCompileMenu, id=ID_RUN)
148        self.Bind(wx.EVT_CLOSE, self.on_close)
149        if not title.count('Python Shell'):
150            # Delete menu item (open and new) if not python shell
151            #self.fileMenu.Delete(wx.ID_NEW)
152            self.fileMenu.Delete(wx.ID_OPEN)
153
154
155    def _add_menu(self):
156        """
157        Add menu
158        """
159        self.compileMenu = wx.Menu()
160        self.compileMenu.Append(ID_CHECK_MODEL, 'Check model',
161                 'Loading and run the model')
162        self.compileMenu.AppendSeparator()
163        self.compileMenu.Append(ID_RUN, 'Run in Shell',
164                 'Run the file in the Python Shell')
165        self.MenuBar.Insert(3, self.compileMenu, '&Run')
166
167    def OnHelp(self, event):
168        """
169        Show a help dialog.
170        """
171        import  wx.lib.dialogs
172        title = 'Help on key bindings'
173        text = wx.py.shell.HELP_TEXT
174        dlg = wx.lib.dialogs.ScrolledMessageDialog(self, text, title,
175                                                   size=((700, 540)))
176        fnt = wx.Font(10, wx.TELETYPE, wx.NORMAL, wx.NORMAL)
177        dlg.GetChildren()[0].SetFont(fnt)
178        dlg.GetChildren()[0].SetInsertionPoint(0)
179        dlg.ShowModal()
180        dlg.Destroy()
181
182    def set_manager(self, manager):
183        """
184        Set the manager of this window
185        """
186        self._manager = manager
187
188    def OnAbout(self, event):
189        """
190        On About
191        """
192        message = ABOUT
193        dial = wx.MessageDialog(self, message, 'About',
194                           wx.OK | wx.ICON_INFORMATION)
195        dial.ShowModal()
196
197    def OnNewFile(self, event):
198        """
199        OnFileOpen
200        """
201        self.OnFileNew(event)
202
203    def OnOpenFile(self, event):
204        """
205        OnFileOpen
206        """
207        self.OnFileOpen(event)
208        self.Show(False)
209        self.Show(True)
210
211    def OnSaveFile(self, event):
212        """
213        OnFileSave overwrite
214        """
215        self.OnFileSave(event)
216        self.Show(False)
217        self.Show(True)
218
219    def OnSaveAsFile(self, event):
220        """
221        OnFileSaveAs overwrite
222        """
223        self.OnFileSaveAs(event)
224        self.Show(False)
225        self.Show(True)
226
227    def bufferOpen(self):
228        """
229        Open file in buffer, bypassing editor bufferOpen
230        """
231        if self.bufferHasChanged():
232            cancel = self.bufferSuggestSave()
233            if cancel:
234                return cancel
235        filedir = ''
236        if self.buffer and self.buffer.doc.filedir:
237            filedir = self.buffer.doc.filedir
238        if not filedir:
239            filedir = self.dataDir
240        result = editor.openSingle(directory=filedir,
241                            wildcard='Python Files (*.py)|*.py')
242        if result.path:
243            self.bufferCreate(result.path)
244
245        # See if there is a corresponding C file
246        if result.path != None:
247            c_filename = os.path.splitext(result.path)[0] + ".c"
248            if os.path.isfile(c_filename):
249                self.bufferCreate(c_filename)
250
251        cancel = False
252        return cancel
253
254    def bufferSaveAs(self):
255        """
256        Save buffer to a new filename: Bypassing editor bufferSaveAs
257        """
258        filedir = ''
259        if self.buffer and self.buffer.doc.filedir:
260            filedir = self.buffer.doc.filedir
261        if not filedir:
262            filedir = self.dataDir
263        result = editor.saveSingle(directory=filedir,
264                                   filename='untitled.py',
265                                   wildcard='Python Files (*.py)|*.py')
266        if result.path:
267            self.buffer.confirmed = True
268            self.buffer.saveAs(result.path)
269            cancel = False
270        else:
271            cancel = True
272        return cancel
273
274    def OnRun(self, event):
275        """
276        Run
277        """
278        if not self._check_saved():
279            return True
280        if self.buffer and self.buffer.doc.filepath:
281            self.editor.setFocus()
282            # Why we have to do this (Otherwise problems on Windows)?
283            forward_path = self.buffer.doc.filepath.replace('\\', '/')
284            self.shell.Execute("execfile('%s')" % forward_path)
285            self.shell.Hide()
286            self.shell.Show(True)
287            return self.shell.GetText().split(">>>")[-2]
288        else:
289            mssg = "\n This is not a python file."
290            title = 'Error'
291            icon = wx.ICON_ERROR
292            wx.MessageBox(str(mssg), title, style=icon)
293            return False
294
295    def OnCheckModel(self, event):
296        """
297        Compile
298        """
299        if not self._check_saved():
300            return True
301        fname = self.editor.getStatus()[0]
302        success = show_model_output(self, fname)
303
304        # Update custom model list in fitpage combobox
305        if success and self._manager != None and self.panel != None:
306            self._manager.set_edit_menu_helper(self.parent)
307            wx.CallAfter(self._manager.update_custom_combo)
308
309    def _check_saved(self):
310        """
311        If content was changed, suggest to save it first
312        """
313        if self.bufferHasChanged() and self.buffer.doc.filepath:
314            cancel = self.bufferSuggestSave()
315            return not cancel
316        return True
317
318    def OnUpdateCompileMenu(self, event):
319        """
320        Update Compile menu items based on current tap.
321        """
322        win = wx.Window.FindFocus()
323        id = event.GetId()
324        event.Enable(True)
325        try:
326            if id == ID_CHECK_MODEL or id == ID_RUN:
327                menu_on = False
328                if self.buffer and self.buffer.doc.filepath:
329                    menu_on = True
330                event.Enable(menu_on)
331        except AttributeError:
332            # This menu option is not supported in the current context.
333            event.Enable(False)
334
335    def on_close(self, event):
336        """
337        Close event
338        """
339        if self.base != None:
340            self.base.py_frame = None
341        self.Destroy()
342
343ABOUT = "Welcome to Python %s! \n\n" % sys.version.split()[0]
344ABOUT += "This uses Py Shell/Editor in wx (developed by Patrick K. O'Brien).\n"
345ABOUT += "If this is your first time using Python, \n"
346ABOUT += "you should definitely check out the tutorial "
347ABOUT += "on the Internet at http://www.python.org/doc/tut/."
348
349
350if __name__ == "__main__":
351
352    app = wx.App()
353    dlg = PyConsole()
354    dlg.Show()
355    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.