Twilight GUI Documentation

Table of Contents

1. Importing the wrappers
2. Hello World
3. An IDE to run the examples
4. Callback mechanism
5. button widget
6. Widget Placement
7. checkbutton widget
8. label widget
9. Radio buttons
10. tbutton widget
11. textentry widget
12. editor widget
13. image widget
14. splitter views
15. listbox widget
16. streebox widget (static tree)
17. treebox widget
18. popup command
19. modal Dialog windows
20. Workspaces
21. Higher level widgets (openFile, saveFile)

At the moment, the Twilight GUI interface is minimal. Only the most basic widgets are implemented and standard signals are connected to them.

1. Importing the wrappers

In order to use TG we have to import one of: wrap_pygtk.py, wrap_pyqt.py, wrap_wxpython.py or wrap_tkinter.py. It's possible to import more than one and switch GUIs at runtime but often one toolkit may not allow the threads of another one to run and this should be used only for the development of TG itself.

	GUI=None
	try: # try to import PyQt first
 	    import wrap_pyqt
	    GUI = wrap_pyqt
	except ImportError:
	    try: # if it doesn't exist, go for PyGTK
	        import wrap_pygtk
	        GUI = wrap_pygtk
	    except ImportError:
	        try: # try for wxPython
	            import wrap_wxpython
	            GUI = wrap_wxpython
	        except ImportError:
		    try: # finally go for Tkinter
			import wrap_tkinter
			GUI = wrap_tkinter
		    except ImportError:
	                print "No toolkit"

In the future we may define a standard environemnt variable which would set the user's preferable toolkit search order. For example something like:

TWGUI_TOOLKITS = [ 'pygtk', 'pyqt', 'wxpython', 'tkinter' ]

2. Hello World

The basic element of a TG application is the Window class. There are many ways to use a window to put widgets in it, for example, inheritance, creating a window and calling its 'widget generation functions', passing the widget creation code as a string to the __init__ of the window, etc. In this tutorial we'll see the method of creating a Window and calling its 'widget creation functions':

Supposing you've imported the apropriate wrapper and set GUI=wrap_toolkit, the simple 'hello world' example is this:

	W=GUI.Window ()
	W.start_construction ()
	W.button (text='Click me', cmd='print "Hello world"')
	W.end_construction ()
	GUI.exec_loop ()

Widget creation code must always be included in start_construction() and end_construction(). The latter finalizes the creation and displays the window. The GUI.exec_loop() must be executed at least once to make everything working.

3. An IDE to run the examples

Throughout this short course we will see many small examples. It is good to have a gui to paste and run them. Here it is:


	W=GUI.Window ()
	if W.BaseGUI == 'pyqt': fnt = 'terminal'
	else: fnt = 'Courier'
	W.start_construction ()
	W.open_vbox ()
	W.editor (name='EDDIE', font=fnt, dims=(80,20))
	W.open_hbox ()
	W.button (text='Run It!', cmd='exec self.master.EDDIE.get_text () in globals (), globals ()')
	W.button (text='Clear', cmd='self.master.EDDIE.set_text ("")')
	W.close_box ()
	W.close_box ()
	W.end_construction ()
	GUI.exec_loop ()

Run it. You can paste examples from below and click on 'Run it' to run them and see what happens. In fact, for starters, paste the above editor text and run it if you want. It is too an example!

4. Callback mechanism

The button and several other widgets can execute callback code. The code can be a string or a function. In the editor example the code is a string and this string is exec'd. If we want to use functions we would also like to make them members of the Window instance. For that the famous transplant class is very useful: it takes a function and makes it an instance method. Here is the editor where this time the button's command is a function.


	class transplant: 
	   def __init__(self, method, host, method_name=None):
	      self.host = host
	      self.method = method
	      setattr(host, method_name or method.__name__, self)
	   def __call__(self, *args, **kwargs):
	      nargs = [self.host]
	      nargs.extend(args)
	      return apply(self.method, nargs, kwargs)

	def RUNCMD (self):
	    exec self.EDDIE.get_text () in globals (), globals ()

	W=GUI.Window ()
	# make RUNCMD a method of W
	transplant (RUNCMD, W)
	if W.BaseGUI == 'pyqt': fnt = 'terminal'
	else: fnt = 'Courier'
	W.start_construction ()
	W.open_vbox ()
	W.EDDIE = W.editor (font=fnt, dims=(80,20))
	W.open_hbox ()
	W.button (text='Run It!', cmd=W.RUNCMD)
	W.button (text='Clear', cmd='self.master.EDDIE.set_text ("")')
	W.close_box ()
	W.close_box ()
	W.end_construction ()
	GUI.exec_loop ()

It is very important to understand that in the case the code is a string, it is executed in the namespace of the *module* and therefore it cannot access names from the global namespace -- unlike functions. The transplant class is part of the Window class (Dialog, Sheet), and can be invoked like this:

	W.transplant (RUNCMD)

5. button widget

 W.button (name=None, text='', cmd=None, rclick=None)
Attributes:

  .master
  .code
  .rclick
  .enable()
  .disable()

name: is the name of the button. We give a name to a button if we want to inspect or change its parameters from other widgets. Also, it is possible to generate the name manually. For instance:
		W.button (name='B1')
is the same as:
		W.B1 = W.button ()
text: is the label of the button. A button's label cannot be changed!
cmd: the code that will be executed when the button is clicked.
rclick: the code to be executed when a button is right-clicked.

cmd and rclick can be functions or strings. In the case they are strings, they will be executed in a method of the button instance. Therefore in this case in order to access things from the Window the member master has to be used.

An example with a button that increments a value and prints it and also prints its code:

	 CMD="""
	 self.master.counter += 1
	 print self.master.counter, 'My code is:', self.code
	 """

	 W=GUI.Window ()
	 W.counter = 0
	 W.start_construction ()
	 W.button (text='Inc Unlimited', cmd=CMD)
	 W.end_construction ()
	 GUI.exec_loop ()

6. Widget Placement

 W.open_vbox (text=None)
 W.open_hbox (text=None)
 W.close_box ()
 W.separator ()

vbox and hbox are vertical and horizontal placement containers. All widgets generated between an open_[vh]box() and a close_box() are placed inside this container. If text is passed then the container box will have a visible outline around it and a title which is text.
The separator is a vertical or horizontal line (depending on the current open container) which separates elements visually.
[ separators not implemented in wx ]

An example of placing three buttons :

	W=GUI.Window ()
	W.start_construction ()
	W.open_vbox ()
	if 1:
	 W.open_hbox ()
	 if 1:
	  W.button (name='B1', text='Button 1', cmd='print 1')
	  W.button (text='Button 2', cmd='print 2')
	 W.close_box ()
	 W.button (text='Button 1', cmd='print "The code of button 1 is", self.master.B1.code')
	W.close_box ()
	W.end_construction ()
	GUI.exec_loop ()

7. checkbutton widget

 W.checkbutton (name=None, text='', cmd='', active=False)
Attributes:

 .master
 .cmd
 .active
 .set_active (state)
 .enable()
 .disable()

The command cmd will be executed each time the check button's state is toggled. A checkbutton widget has the attributes active and set_active (value). For example, a check button which reports its state when it's toggled and a button which when clicked sets the checkbutton to 'ok':

	W=GUI.Window ()
	W.start_construction ()
	W.checkbutton (name='CB', text='Next time?', cmd='print "State:", self.active')
	W.button (text='I Agree', cmd='self.master.CB.activate (1)')
	W.end_construction ()
	GUI.exec_loop ()

8. label widget

 W.label (name=None, text='label')
Attributes:

 .set_text (text)
 .get_text ()

A label is a dummy area with a message.

	W=GUI.Window ()
	W.start_construction ()
	W.label (name='L', text='A Label')
	W.button (text='Do', cmd='self.master.L.set_text (self.master.L.get_text () + "*")')
	W.end_construction ()
	GUI.exec_loop ()

9. Radio buttons

 W.open_radio (name=None, cmd=None)
 W.radio (name=None, text='', active=False)
 W.close_radio ()
Attributes:

 .master
 .cmd
 .current
 .activate (name)
 .enable(name)
 .disable(name)

Radio buttons are groupped very much like the container boxes. The W.radio() command must be executed within an W.open_radio() and a W.close_radio() segment. There is only one widget actually generated and that is with the open_radio command. name is the name of the widget and cmd the command that will be executed each time the active radio button is changed. The W.radio() command just adds a name-value pair to the radio group. The user sees the 'value' part and the progammer the 'name' part of a radio button.

	 W=GUI.Window ()
	 W.start_construction ()
	 W.open_radio (name='RBG', cmd='print "the active radio is", self.current')
	 if 1:
	  W.radio ('FM', "FM frequencies")
	  W.radio ('AM', "AM rocks", True)
	  W.radio ('OFF', "Turn it off")
	 W.close_radio ()
	 W.button (text="Turn Off Damn Thing", cmd="self.master.RBG.activate ('OFF')")
	 W.end_construction ()
	 GUI.exec_loop ()

As you will see if you execute the above example, the message "the active radio is:" is printed each time the active radio button is changed, even if it is changed indirectly from the button.

10. tbutton widget

 W.tbutton (name=Nome, text='', cmdact=None, cmddeact=None, active=0)
Attributes:

 .master
 .cmdact
 .cmddeact
 .toggle ()
 .isOn ()
 .enable()
 .disable()

The toggle button's cmdact will be executed each time the button is "pressed" and the cmddeact each time it's "unpressed".

11. textentry widget

 W.textentry (name=None, text='', length=0, cmd=None, label=None)
Attributes:

 .master
 .code
 .text ()
 .set_text (text)
 .enable()
 .disable()

This is a text entry. A goal in the next version is to create and advanced realine. Anyway, for now, cmd is executed each time enter is pressed. length specifies the visual length of the entry in characters. Because characters may be non fixed this is an approximation. A length zero means that the toolkit is free to size the widget as it wants.

Here is an example of a button, a text entry that changes the button's code to whatever is typed in it and another button which prints the current code of the first button:

	def ENTRY_CMD (self):
	    self.B1.code = self.ENT.text()
	    self.ENT.set_text ('')

	W=GUI.Window ()
	transplant (ENTRY_CMD, W)
	W.start_construction ()
	W.open_vbox ()
	if 1:
	 W.open_hbox ()
	 if 1:
	  W.button (name='B1', text='CLICK ME!', cmd='print "thanks"')
	  W.button (text='Code of button 1', cmd='print "it is:", self.master.B1.code')
	 W.close_box ()
	 W.separator ()
	 W.textentry (name='ENT', label='Enter new code for B1', cmd=W.ENTRY_CMD)
	W.close_box ()
	W.end_construction ()
	GUI.exec_loop ()

12. editor widget

 W.editor (name=None, dims=(0,0), font=None, wrap=0, editable=True)
Attributes:

 .master
 .get_text ()
 .set_text (text)
 .set_editable (value)
 .disable ()
 .enable ()

That is a simple text editor/viewer.

dims: is a tuple of the desired dimensions of the editor in characters. A value of zero means that the specific dimension will be what the toolkit wants.
font: is kinda complex. There is no real agreement between pygtk, pyqt and wxpython. Generally, something like 'Courier 10' should work here. Because pygtk and wxpython share pango they agree on most strings. pango on the other hand doesn't have terminal fonts, so 'terminal' is a good choice for editing with fixed size in pyqt (non-fixed fonts are for girl's diaries afterall).
wrap: is 0 for no wrap, 1 for character wrap and 2 for word wrap
editable: tells whether the text can be edited by the user. the text can always be altered by the code with set_text() though.

	W=GUI.Window ()
	if W.BaseGUI == 'pyqt': fnt = 'terminal'
	else: fnt = 'Courier'
	W.start_construction ()
	W.open_vbox ()
	W.editor (name='EDDIE', font=fnt, dims=(80,20))
	W.open_hbox ()
	W.button (text='Run It!', cmd='exec self.master.EDDIE.get_text () in globals (), globals ()')
	W.button (text='Clear', cmd='self.master.EDDIE.set_text ("")')
	W.close_box ()
	W.close_box ()
	W.end_construction ()
	GUI.exec_loop ()

13. image widget

 W.image (name=None, filename=None)
Attributes:

 .load (filename)

That is a primitive image displayer. The load method doesn't work in qt It's mainly used to display the Twilight GUI logo. This widget needs serious improvements.

14. splitter views

 self.hsplitter ()
 self.vsplitter ()

The splitter is a placement command which separates two widgets by a bar which the user can drag, consequently, making the one widget smaller and the other bigger, as he or she whishes at the current time they are working on their computer. The splitter applies for the next two widgets that will be created. The splitter DOESN'T work in wxpython yet and hbox, vbox will be used instead. An example with three editors in splitters:

	 W=GUI.Window ()
	 W.start_construction ()
	 W.hsplitter ()
	 if 1:
	  W.vsplitter ()
	  if 1:
	   W.editor ()
	   W.editor ()
	  W.editor ()
	 W.end_construction ()
	 GUI.exec_loop ()

15. listbox widget

 W.listbox (name=None, gcode=None, colnam=[''], scode=None, ecode=None, dims=(0,0), rclick=None)
Attributes:

 .master
 .row
 .reload (code=None)
 .enable ()
 .disable ()

Listbox Creation:

 .addrow (data)

The listbox is TG is very simple and it is used to reflect some internal python data. A list is loaded with the gcode argument which can be a string or a function. The code in gcode can call the member addrow to addrows to the list, and that's it. After that it is not possible to add extra rows or remove rows from the list once it has been created. The list can only be reload()ed in which case it will be emptied and the gcode will be called again to refill the list.

colnam: is a sequence of strings which gives the names of the columns of the list (and consequently their count)
scode: will be executed each time an element is selected.
ecode: each time an element is activated (double click or return)
rclick: each time an element is clicked with the right button
dims: specifies the dimensions of the listbox in characters.

Row data added with addrow: In lists and trees we want not only the visible column contents but often our own extra data accossiated with each row. For that the argument of addrow must be a sequence whos first argument is a sequence of the values. In the case a callback is invoked (scode, ecode or rclick), the member self.row will be set to the entire data sequence that was used in addrow. Ok. Example.

	 # make a listbox with the keys of globals() and a viewer to show their values

         def MAKELIST (self):
             lst = globals ().keys ()
             lst.sort ()
             for i in lst:
                self.addrow ([[i]])

         def SHOWKEY (self):
             self.E.set_text (repr (globals ()[self.L.row [0][0]]))

         W=GUI.Window ()
         transplant (SHOWKEY, W)
         W.start_construction ()
         W.hsplitter ()
         if 1:
          W.listbox (name='L', colnam=['keys'], gcode=MAKELIST, scode=W.SHOWKEY, dims=(20,10))
          W.editor (name='E', editable=False, dims = (40, 10))
         W.end_construction ()
         GUI.exec_loop ()

We should note two things: first, the list is not sorted. Actually in Qt it is and if we click on columns we select the sort column. In pygtk and wxpython the elements are not sorted, so we'd better sort them to be sure. Secondly, the gcode argument is special. If a plain function is passed to it (not a method function), it is automatically attached to the newly generated list instance.

16. streebox widget (static tree)

 W.streebox (name=None, gcode=None, colnam=[''], scode=None, ecode=None, dims=(0,0), rclick=None)
Attributes:

 .master
 .row
 .reload (code=None)
 .enable ()
 .disable ()

Listbox Creation:

 .sub ()
 .addrow (data)
 .unsub ()

A tree is just like a list in almost everything, except that the elements of the first column can be in a tree representation. The static tree is the one that is entirely created with gcode, and so it's not suitable for thousands of nodes (for example a filesystem navigator).

	 def MAKETREE (self):
	    self.addrow ([['tmp']])
	    self.addrow ([['home']])
	    self.sub ()
	    if 1:
	     self.addrow ([['johnny']])
	     self.addrow ([['jack']])
	     self.sub ()
	     if 1:
	      self.addrow ([['src']])
	     self.unsub ()
	    self.unsub ()

         W=GUI.Window ()
         W.start_construction ()
         W.streebox (colnam=['dirs'], gcode=MAKETREE)
         W.end_construction ()
         GUI.exec_loop ()

[ in wx, the tree's GOT to have a root, (TreeListCtrl) and for that there is an ugly '.' root for now ]

17. treebox widget

 W.treebox (name=None, gcode=None, colnam=[''], scodeecode=None, dims=(0,0), rclick=None)
Attributes:

 .master
 .row
 .reload (code=None)
 .enable ()
 .disable ()

Listbox Creation:

 .addrow (data, expander=None)

This is the real tree. Just like the listbox, elements are inserted with addrow from gcode. Additionally, an element that passes code (string or function) to the expander argument of addrow, is a parent node and the expander code is invoked to generate the children (with addrow) each time the user clicks to expand the node. Ok, this is fun.

A filesystem navigator:

	import os
	import os.path

	def EXPAND_DIR (self):
	 self = self.L
	 # the hidden data is the full pathname
	 basedir = self.row [1]
	 dirs=os.listdir (basedir)
	 dirs.sort ()
	 for i in dirs:
	     fullname = basedir + '/' + i
	     if os.path.isdir (fullname): 
		self.addrow (((i,),fullname),self.master.EXPAND_DIR)
	     else:
		self.addrow (((i,),))

	CODE="self.addrow ((('/',),'/'), self.master.EXPAND_DIR)"

	W = GUI.Window ()
	transplant (EXPAND_DIR, W)
	W.start_construction ()
	W.treebox (name='L', colnam = ['dir'], gcode = CODE, dims=(20,10))
	W.end_construction ()
	GUI.exec_loop ()

18. popup command

 W.popup (code, *args, **kwargs)
Menu Creation:

 .item (name, code)
 .sub (name)
 .unsub ()
 .sep ()

The popup menu is normally not created during the window construction, but it 'pops up' when some event happens. So we excpect the popup creation to happen in the code of a button or something like that. The popup command is a method of the window instance nontheless.

The code argument which can be a string or function (in case it's a plain function it will be attached to the popup), can invoke the members item(), sub(), unsub() and sep() to generate menu items, submenus and separators.

A menubar can be done with buttons that make popups on click:

	def FILEMENU (self):
	    self.item ('New', 'print "New"')
	    self.item ('Open', 'print "Open"')
	    self.sep ()
	    self.sub ('Send to')
	    if 1:
	     self.item ('Floppy disk', 'print "Floppy"')
	    self.unsub ()
	    self.sep ()
	    self.item ('Exit', 'self.master.quit ()')

	def POPUPCODE (self):
	    self.popup (FILEMENU)

	W = GUI.Window ()
	transplant (POPUPCODE, W)
	W.start_construction ()
	W.open_vbox ()
	W.open_hbox ()
	W.button (text='File', cmd=W.POPUPCODE)
	W.close_box ()
	W.separator ()
	W.editor (dims = (80,25))
	W.close_box ()
	W.end_construction ()
	GUI.exec_loop ()

Just like the tree/list generation code, the code argument of the popup, if a global function shall be attached to the currently constucted popup instance.

Popup menus, usually operate on some selected data. For example, in the dir viewer example we would like to make a popup menu that does something with the selected file. For that we need to pass values to the popup. Any keyword arguments to popup() will be attached as values to the instance. The dirviewer with a popup that prints the fullname of the selected file:

	import os
	import os.path

	def EXPAND_DIR (self):
	 self = self.L
	 # the hidden data is the full pathname
	 basedir = self.row [1]
	 dirs=os.listdir (basedir)
	 dirs.sort ()
	 for i in dirs:
	     fullname = basedir + '/' + i
	     if os.path.isdir (fullname): 
		self.addrow (((i,),fullname),self.master.EXPAND_DIR)
	     else:
		self.addrow (((i,),fullname))

	def FILEMENU (self):
	    self.item ('Print fullname', 'print "Fullname:", self.fullname')

	def POPUPCODE (self):
	    self.popup (FILEMENU, fullname=self.L.row [1])

	CODE="self.addrow ((('/',),'/'), self.master.EXPAND_DIR)"

	W = GUI.Window ()
	transplant (EXPAND_DIR, W)
	transplant (POPUPCODE, W)
	W.start_construction ()
	W.treebox (name='L', colnam = ['dir'], gcode = CODE, dims=(20,10), rclick=W.POPUPCODE)
	W.end_construction ()
	GUI.exec_loop ()

(in PyGTK you have to keep the right button pressed otherwise the menu closes. I don't know how to fix this)

19. modal Dialog windows

A dialog is a modal popup window that can be a message or a confirmation or whatever that freezes its background window until it gets a reply. The Dialog is a class just like Window. Here is a confirmation dialog that appears each time we click on the 'Dangerous action button'.

	def CONFIRM(self):
	    D = GUI.Dialog (self)
	    D.ok = False
	    D.start_construction ()
	    D.open_vbox ()
	    D.label (text='Are you 100% sure?')
	    D.open_hbox ()
 	    D.button (text='ok', cmd="self.master.ok=True; self.master.close()")
	    D.button (text='Cancel', cmd='self.master.close()')
	    D.close_box ()
	    D.close_box ()
	    D.end_construction ()	# this fires up the dialog box
	    if D.ok:
		print "Ok with me"
	    else:
		print "Canceled"

	W = GUI.Window ()
	transplant (CONFIRM, W)
	W.start_construction ()
	W.button (text='Dangerous action button', cmd=W.CONFIRM)
	W.end_construction ()
	GUI.exec_loop ()

20. Workspaces

 W.tabworkspace (name=None)
 W.MDIworkspace (name=None)

The notebook and MDI workspaces are very experimental and still do not work very well. Anyway they are workspaces inside which we can add pages (GUI pages) at runtime. Like a small window manager inside our window. The notebook is known as "tabs". The MDI is only supported in QT. wxPython is supposed to support MDI but it just opens tabs in another window here. Maybe I missed --enable-mdi or something. PyGTK doesn't have MDI and a notebook will be used instead. Anyway.

Then we have the class Sheet which is just like Window and Dialog. In its construction we must pass the name of the workspace where the sheet will be added. Here is an example. *Workspaces need serious fixing*

	NT="""
	self.settitle ('A page')
	self.button (text='butto', cmd='self.master.RR.disable ("On")')
	self.button (name='B', text='boggler', cmd='self.master.disable()')
	self.editor (name='E',dims=(30,10))
	self.open_radio (name='RR')
	self.radio ('This', "This")
	self.radio ('On', "Ons", active=1)
	self.close_radio ()
	self.E.set_text (self.workspace.master.NT)
	"""

	def NEWSHEET (self):
	    GUI.Sheet (self.WS, NT)
	    return	

	W = GUI.Window ()
	W.NT=NT
	transplant (NEWSHEET, W)
	W.start_construction ()
	W.button (text='Make new sheet', cmd=W.NEWSHEET)
	W.MDIworkspace (name='WS')
	GUI.Sheet (W.WS, NT)
	W.NEWSHEET ()
	W.end_construction ()
	GUI.exec_loop ()

It just works.

21. Higher level widgets

 W.openFileDialog (name='')
 W.saveFileDialog (name='')

These can be invoked from callbacks. They open custom pygtk/pyqt/wxpython advanced dialog widgets to read or save a file. A string of the selected filename or None is returned when these are called. PyGTK 2.4.0 has a neat advanced fileDialog which isn't used yet. We should add wildcards and directory here in the future...

Here is the code of the editor IDE that appears in the screenshot of the Twilight GUI homepage. It uses openFileDialog and saveFileDialog too:

class transplant: 
   def __init__(self, method, host, method_name=None):
      self.host = host
      self.method = method
      setattr(host, method_name or method.__name__, self)
   def __call__(self, *args, **kwargs):
      nargs = [self.host]
      nargs.extend(args)
      return apply(self.method, nargs, kwargs)

def readfile (fnm):
    f=open (fnm, 'r')
    txt = f.read()
    f.close ()
    return txt

def writefile (fnm, txt):
    f=open (fnm, 'w')
    f.write (txt)
    f.close ()

def app ():
 def NEWCMD(self):
   self.ED.set_text('')
   self.CURRENT_FILE='NewFile.py'
   self.FILENAME.set_text ('NewFile.py')

 def RELOADCMD(self):
   self.ED.set_text (readfile (self.CURRENT_FILE))

 def LOADCMD (self):
    l = self.openFileDialog (name=self.CURRENT_FILE)
    if l:
        try:
	    self.ED.set_text (readfile (l))
	    self.FILENAME.set_text (l)
	    self.CURRENT_FILE = l
        except IOError:
	    print "No such file"

 def SAVEASCMD (self):
    l = self.saveFileDialog (name=self.CURRENT_FILE)
    if l:
        try:
	    writefile (l, self.ED.get_text ())
	    self.FILENAME.set_text (l)
	    self.CURRENT_FILE = l
        except IOError:
	    print "Failed save"

 def SAVECMD(self):
    D=self.MyGUI.Dialog (self)
    D.ok = False
    D.start_construction ()
    D.open_vbox ()
    D.label (text='Save file '+self.CURRENT_FILE+' ?')
    D.open_hbox ()
    D.button (text='ok', cmd="self.master.ok=True; self.master.close()")
    D.button (text='Cancel', cmd='self.master.close()')
    D.close_box ()
    D.close_box ()
    D.end_construction ()
    if D.ok:
        writefile (self.CURRENT_FILE, self.ED.get_text())

 def RUNCMD (self):
    x = self.LIBRADIO.current
    print 'Running with ['+x+']'
    for i in self.GUIList:
	if i[0] == x:
	    break
    xx=self.ED.get_text ()
    global GUI
    TMP=GUI
    GUI = i[1]
    exec xx in globals (), globals ()
    GUI.exec_loop ()
    GUI=TMP

 W = GUI.Window ()
 W.MyGUI = GUI
 W.CURRENT_FILE = 'Editor.py'
 W.GUIList = GUIList
 transplant (RUNCMD, W)
 transplant (NEWCMD, W)
 transplant (RELOADCMD, W)
 transplant (SAVECMD, W)
 transplant (SAVEASCMD, W)
 transplant (LOADCMD, W)
 if W.BaseGUI == 'pyqt': fnt = 'terminal'
 else: fnt = 'Courier'
 W.start_construction ()
 W.settitle ('Editor IDE in '+W.BaseGUI)
 W.open_vbox ()
 if 1:
  W.label (name='FILENAME', text=W.CURRENT_FILE)
  W.open_hbox ()
  if 1:
   W.editor (name='ED', dims=(78,23), font = fnt)
   W.open_vbox ('Run it with')
   if 1:
    W.open_radio (name='LIBRADIO')
    if 1:
     for i in GUIList:
         W.radio (name=i[0], text=i[0], active=(GUI==i[1]))
    W.close_radio ()
    W.button (text="Run it!", cmd=W.RUNCMD)
    W.image (name='IMG', filename='tw-logo.png')
   W.close_box ()
  W.close_box ()
  W.open_hbox ()
  if 1:
   W.button (text='New', cmd=W.NEWCMD)
   W.button (text='Reload', cmd=W.RELOADCMD)
   W.button (text='Load', cmd=W.LOADCMD)
   W.button (text='Save', cmd=W.SAVECMD)
   W.button (text='Save As...', cmd=W.SAVEASCMD)
   W.button (text='Quit', cmd='self.master.quit ()')
  W.close_box ()
 W.close_box ()
 W.end_construction ()
 W.ED.set_text (readfile (W.CURRENT_FILE))
 GUI.exec_loop ()

app ()