I find that I often want to test some small code samples, too long to type at the interpreter (and retype, with some small changes), but not worth saving. The following wxPython based mini-app implements the basic for a small Python editor with a side output window. It also handles input() and raw_input().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | import wx # requires wxPython
import sys # required to redirect the output
class EditorSashWindow(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
winids = []
# Left window has fixed size and contains control buttons
self.controls = wx.SashLayoutWindow(self, -1)
winids.append(self.controls.GetId())
self.controls.SetDefaultSize((80, 600))
self.controls.SetOrientation(wx.LAYOUT_VERTICAL)
self.controls.SetAlignment(wx.LAYOUT_LEFT)
b = wx.Button(self.controls, -1, "Open", (3, 20))
self.Bind(wx.EVT_BUTTON, self.openFile, b)
b.SetDefault()
b2 = wx.Button(self.controls, -1, "Save", (3, 60))
self.Bind(wx.EVT_BUTTON, self.saveFile, b2)
b2.SetDefault()
b3 = wx.Button(self.controls, -1, "Run", (3, 100))
self.Bind(wx.EVT_BUTTON, self.run, b3)
b3.SetDefault()
b4 = wx.Button(self.controls, -1, "Clear", (3, 140))
self.Bind(wx.EVT_BUTTON, self.clear, b4)
b4.SetDefault()
# This will occupy the space not used by the Layout Algorithm
self.remainingSpace = wx.SashLayoutWindow(
self, -1, style=wx.NO_BORDER|wx.SW_3D)
self.python_editor = wx.TextCtrl(self.remainingSpace,
-1, "", wx.DefaultPosition, wx.DefaultSize,
wx.TE_MULTILINE|wx.SUNKEN_BORDER
)
self.python_editor.SetValue("#Editor window")
# The output window is at the extreme right
win = wx.SashLayoutWindow(
self, -1, wx.DefaultPosition, (200, 30),
wx.NO_BORDER|wx.SW_3D
)
winids.append(win.GetId())
win.SetDefaultSize((300, 600))
win.SetOrientation(wx.LAYOUT_VERTICAL)
win.SetAlignment(wx.LAYOUT_RIGHT)
win.SetSashVisible(wx.SASH_LEFT, True)
win.SetExtraBorderSize(10)
self.rightWindow = win
self.output_window = wx.TextCtrl(win, -1, "", wx.DefaultPosition,
wx.DefaultSize, wx.TE_MULTILINE|wx.SUNKEN_BORDER)
self.output_window.SetValue("Output Window\n")
#redirecting output
sys.stdout = self.output_window
sys.stderr = self.output_window
self.Bind(wx.EVT_SASH_DRAGGED_RANGE, self.OnSashDrag, id=min(winids),
id2=max(winids))
self.Bind(wx.EVT_SIZE, self.OnSize)
def OnSashDrag(self, event):
eobj = event.GetEventObject()
if eobj is self.rightWindow:
self.rightWindow.SetDefaultSize((event.GetDragRect().width, 600))
wx.LayoutAlgorithm().LayoutWindow(self, self.remainingSpace)
self.remainingSpace.Refresh()
def OnSize(self, event):
wx.LayoutAlgorithm().LayoutWindow(self, self.remainingSpace)
def saveFile(self, event):
self.output_window.SetValue("Save File not implemented")
def openFile(self, event):
self.output_window.SetValue("Open File not implemented")
def run(self, event):
'''Runs the user code; input() and raw_input() are implemented
with dialogs.'''
user_code = self.python_editor.GetValue()
myGlobals = globals()
myGlobals['raw_input'] = self.myRawInput
myGlobals['input'] = self.myInput
exec user_code in myGlobals
def clear(self, event):
'''Clears the output window'''
self.output_window.SetValue("")
def myRawInput(self, text):
dlg = wx.TextEntryDialog(self, text, 'raw_input() request', '')
if dlg.ShowModal() == wx.ID_OK:
user_response = dlg.GetValue()
dlg.Destroy()
return user_response
def myInput(self, text):
dlg = wx.TextEntryDialog(self, text, 'input() request', '')
if dlg.ShowModal() == wx.ID_OK:
user_response = dlg.GetValue()
dlg.Destroy()
return eval(user_response)
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self,parent, -1, title, size=(800, 600),
style=wx.DEFAULT_FRAME_STYLE|wx.NO_FULL_REPAINT_ON_RESIZE)
self.app = EditorSashWindow(self)
self.Show(True)
app = wx.PySimpleApp()
frame=MainWindow(None, 'Lightning Compiler')
app.MainLoop()
|
As a teaching tool, I wanted to implement something a bit more user friendly than the standard interpreter. This version runs the user code (in the "editor" window) and shows the output in a secondary window. The code is executed by the exec statement, which, on its own, does not allow to use input() or raw_input(). After looking in various books and websites, I couldn't find any hints as to how to solve this problem and I came up with the version shown above, which simply rebinds input() and raw_input() to wxPython dialogs, something which should be obvious to the experts but not necessarily so to others.
In the interest of keeping the code relatively short, I based the editor used here on a simple TextCtrl class, instead of a scintilla based editor which allows proper syntax colouring. While I used wxPython for the GUI, the principle is independent of the chosen GUI.
Since I wrote this, I almost never use the standard Python interpreter to test code snippets!
Very good tool. Thx for sharing.
Version with colorized source. I should probably add that a version with the scintilla editor embedded for colorizing the source is available at http://sourceforge.net/project/showfiles.php?group_id=125834 under "lightning".
Thank you, I like it.
May I suggest:
1) # -- encoding: latin-1 --
at the beginning (so no encode error appear).
For even faster typing and operating:
2) even faster to start would be to assign a hotkey.
I think, F5 is commonly used to run something.
class EditorSashWindow(wx.Panel):
3) I would add a LF at the end to be able to type immediatly:
self.python_editor.AddText("#Editor window\n")
4) SetFocus to the edit window immediatly:
Suggestions implemented! Thanks for the suggestions. I implemented them, and made a few minor changes. New version on sourceforge
Great, thank you (for your changes and mentioning me! ;))
May I suggest one small thing.
(No need to update the program again, only of my suggestion)
the scrolling of the output window isn't working;
so if the output exceeds line 28 (on my pc) the next outputs
are not seen automatically.
So I added:
class LogWindow(python_editor):
...
=> self.EnsureCaretVisible()