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

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 68669da was ec72ceb, checked in by smk78, 8 years ago

Changed various occurrences of 'custom models' in window titles, etc, to
'plugin models'. Under Fitting menu also changed 'Edit Custom Model' to
'Plugin Model Operations' and changed menu options below this.

  • Property mode set to 100644
File size: 10.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
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        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
234        # See if there is a corresponding C file
235        if result.path != None:
236            c_filename = os.path.splitext(result.path)[0] + ".c"
237            if os.path.isfile(c_filename):
238                self.bufferCreate(c_filename)
239
240        cancel = False
241        return cancel
242
243    def bufferSaveAs(self):
244        """
245        Save buffer to a new filename: Bypassing editor bufferSaveAs
246        """
247        filedir = ''
248        if self.buffer and self.buffer.doc.filedir:
249            filedir = self.buffer.doc.filedir
250        if not filedir:
251            filedir = self.dataDir
252        result = editor.saveSingle(directory=filedir,
253                                   filename='untitled.py',
254                                   wildcard='Python Files (*.py)|*.py')
255        if result.path:
256            self.buffer.confirmed = True
257            self.buffer.saveAs(result.path)
258            cancel = False
259        else:
260            cancel = True
261        return cancel
262
263    def OnRun(self, event):
264        """
265        Run
266        """
267        if not self._check_saved():
268            return True
269        if self.buffer and self.buffer.doc.filepath:
270            self.editor.setFocus()
271            # Why we have to do this (Otherwise problems on Windows)?
272            forward_path = self.buffer.doc.filepath.replace('\\', '/')
273            self.shell.Execute("execfile('%s')" % forward_path)
274            self.shell.Hide()
275            self.shell.Show(True)
276            return self.shell.GetText().split(">>>")[-2]
277        else:
278            mssg = "\n This is not a python file."
279            title = 'Error'
280            icon = wx.ICON_ERROR
281            wx.MessageBox(str(mssg), title, style=icon)
282            return False
283
284    def OnCheckModel(self, event):
285        """
286        Compile
287        """
288        if not self._check_saved():
289            return True
290        fname = self.editor.getStatus()[0]
291        success = show_model_output(self, fname)
292
293        # Update custom model list in fitpage combobox
294        if success and self._manager != None and self.panel != None:
295            self._manager.set_edit_menu_helper(self.parent)
296            wx.CallAfter(self._manager.update_custom_combo)
297
298    def _check_saved(self):
299        """
300        If content was changed, suggest to save it first
301        """
302        if self.bufferHasChanged() and self.buffer.doc.filepath:
303            cancel = self.bufferSuggestSave()
304            return not cancel
305        return True
306
307    def OnUpdateCompileMenu(self, event):
308        """
309        Update Compile menu items based on current tap.
310        """
311        win = wx.Window.FindFocus()
312        id = event.GetId()
313        event.Enable(True)
314        try:
315            if id == ID_CHECK_MODEL or id == ID_RUN:
316                menu_on = False
317                if self.buffer and self.buffer.doc.filepath:
318                    menu_on = True
319                event.Enable(menu_on)
320        except AttributeError:
321            # This menu option is not supported in the current context.
322            event.Enable(False)
323
324    def on_close(self, event):
325        """
326        Close event
327        """
328        if self.base != None:
329            self.base.py_frame = None
330        self.Destroy()
331
332ABOUT = "Welcome to Python %s! \n\n" % sys.version.split()[0]
333ABOUT += "This uses Py Shell/Editor in wx (developed by Patrick K. O'Brien).\n"
334ABOUT += "If this is your first time using Python, \n"
335ABOUT += "you should definitely check out the tutorial "
336ABOUT += "on the Internet at http://www.python.org/doc/tut/."
337
338
339if __name__ == "__main__":
340
341    app = wx.App()
342    dlg = PyConsole()
343    dlg.Show()
344    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.