"Visual Python" =============== (Guido van Rossum, 4-10 Nov. 1994) Here are my current thoughts about a GUI building environment for Python, whose code name will be "Visual Python", short "VP". Obviously this will have to change before we go public with this, as Microsoft probably owns the trade marks for Visual Anything. Note that I'm thinking as I'm writing so this may seem chaotic at times -- also, near the end I may be drawing a different picture as sketched initially. Remember, my only exposure to Visual Basic was a ten-minute demonstration by Jon Bently at the VHLL conference. Another exposure (maybe twice as long) was to XF, the Tcl/Tk GUI builder. The latter's user interface struck me as particularly unintuitive, although I've heard some good reports about it as well. (My problems may be related to its rather uninuitive use of the middle mouse button for all sorts of essential functionality, as well as the lack of visual distinction between buttons and labels in its user interface, which made me waste a lot of time trying to click on labels...) This draft will not use spam apart from this sentence (nudge nudge, wink wink). A word on easy of use --------------------- My ideal application's user interface teaches the user what its functionality is simply by its visual lay-out, augmented by well-chosen button labels, menu names and error messages, rather than relying on lots of context-sensitive help. Not that the latter is bad -- it just shouldn't be necessary to consult it for your first steps. Part of ideal can be asserted by having a relatively simple, consistent internal structure that reveals itself through its behavior and "affordances". Another part is accomplished by conforming to conventions that are used outside the application -- whether in the "real world" or in other computer applications. I believe that some of the attractiveness of Python as a language comes from using both rules -- it would seem that Scheme and Tcl exaggerate the importance of the former guideline, while Perl seems to apply the latter exclusively (maybe because Larry is a linguist?). Non-goals --------- Let's first list some non-goals here. - I'm not aiming for the world record in performance - I'm not planning any great breakthroughs in human factors - I'm not going to bend over to support extension writers - I'm not defining the abstract GUI layer here -- I'm using it - I don't want it to become as big as Emacs, and - It needn't have an integrated mail handler But maybe more interesting are som positive goals and requirements. Meta-goals ---------- First a number of meta-requirements: (Some may be in direct contradiction with the Emacs non-goal -- I realize I am asking a lot here.) - VP must appeal to the casual user who doesn't know a lot about programming (in Python nor otherwise) as well as to the experienced Python hacks - It must be possible to run the programs created with VP in a non-VP environment (provided it has some implementation of the abstract GUI layer) - VP must assist in programming any kind of Python application, not just highly visial or interactive ones - It must be possible to use VP to develop only part of an application - It must be possible to begin using VP after part of an application has been written - It must be possible to alternate development of an application using VP and other development environments - VP should provide a broad range of support tools for programming - VP must support the use of any Python extensions such as may be available on the platform of choice, including dynamically loadable libraries - VP must support the look and feel of the platform it is running on while encouraging (at least allowing) the writing of portable programs (obviously the abstract GUI layer will help here) - VP should support using external tools that may be available on a platform, whether written in Python or not - It must be possible for third parties to extend VP with new functionality and to sell the result as an improved version, but without locking users into that version (i.e. it must be possible to go back to using the free version) - VP itself must be implemented (mostly) using the abstract GUI it supports for the programs written in it - It must be possible for (power) users to extend VP by writing Python code - Even though VP may support running test code inside itself (i.e. without forking a subprocess etc.), it must be sufficiently difficult (not necessarily impossible) to screw up VP by mistakes there (without taking extreme measures like Safe-Python) - VP should use graphical metaphors whereever this makes sense, but it should also support a mostly-text version, e.g. for the Visually challenged Specific features ----------------- Here are a number of specific features that I think should be present in Visual Python (of course fully integrated): - text editor - customization - debugger - class browser - multi-file search facility - cross-reference tools - static checks - interactive execution window (much like current interactive Python) - interactive building of simple applications using callbacks using a mostly graphical metaphor - (multiple) application templates - context-sensitive help (if you insist) Text editor ----------- Some requirements for the VP text editor: - multiple levels of undo - aware of Python syntax - (optional?) power keys / shortcuts to make Emacs users feel at home - support using external text editors - support insertion of statement templates - completion based on knowledge of Python syntax as well as analysis of current file / program Customization ------------- Some requirements for VP's customization mechanism: - it should be possible to do all customization of VP interactively, without leaving VP - the customization method should be extensible (usable by extensions to VP) - it should also be possible to manually edit configuration files (and it would be nice if VP could pick up those changes without restarting as well, either automatically or at least after a command "reread startup files") - customization files should be portable between platforms (unfortunately (?) this rules out using X resources as the main customization mechanism) - don't overdo customization. I realize that by mentioning it in this early stage I may actually over-emphasize it. In fact I'm a fan of systems that do the right thing without customization (I prefer one exquisite salad dressing over a choice of a dozen ordinary ones) - it wouldn't hurt if the customization method would be usable to customize other programs (including the bigger ones that the user writes using VP) Debugger -------- Some meta-requirements: - It should not require a major context change to use the debugger. Rather, it should always be available (when an application is being run as part of VP) and offer help whenever an unhandled exception occurs. - It would be nice (but I don't know how doable this is without static checking) if, when an unhandled exception occurs, the debugger is entered at the moment the exception occurs rather than after it has reached the outermost level of the program. This would potentially make it possible to fix the problem and continue execution of the program instead of having to restart it. The problem is that if there are any active "try" statements further down on the stack that might catch the exception, the debugger can't know if an exception will be handled or not until it is too late. Fortunately, if most code in written in the form of relatively small callback functions, most of it won't be using exceptions heavily. Also, we can cut off the call chain at the callback level, i.e. we can consider each callback as a separate program -- if a callback raises an exception that it doesn't handle we (generally) only need to debug that particular callback, not the whole program. - Multiple debugger instances should be allowable - Rather than "wdb", the debugger should use a small number of windows structured into panels - Post-mortem as well as running program debugging - Quickly run a program with as well as without the debugger without having to clear all breakpoint Some practical features: - breakpoints on: - lines - functions - modules - classes - objects - tracing (same as breakpoints / stepping but print message and continue) - once-only breakpoints - conditional breakpoints (too fancy?) - various step modes: - one atomic Python instruction? (currently not possible) - until next statement in current function - until next call - until next return - stack traces - detailed stack frame display - evaluation of expressions and execution of statements in any stack frame - execution animation (a form of tracing where the current line is highlighted) - integrated with text editor: - can set/clear breakpoints - show presence of breakpoins - show current execution pointer - would it be possible to open as well as close source windows automatically (or on request) as we traverse the stack (rather than only opening them, leaving lots of windows around once we've been debugging)? Class browser ------------- I know very little about class browsers. I presume a class browser ought to present you with a hierarchical view on your program(s), with directories or packages at the root, then files, then top-level entities inside files (such as classes, but also variables and top-level functions), then methods inside classes. It should probably support different sorting methods, such as by order of occurrence in the text and alphabetically. I suppose that when you ask to actually see a particular function or class, the browser will drop you into the text editor at the indicated line (attempting to show as much as possible of the indicated entity). A Python class browser has to parse Python source files in order to be useful. I think it would be wise to do this parsing rather superficially, say using a number of regular expressions, rather than using the Python parser (even though the latter is available). The andvantage of this approach is that it will allow browsing of files that temporarily have invalid syntax (e.g. due to frantic editing). Searching --------- A search / replace dialog with options like most PC and Mac editors would be nice: - case dependent / case independent - whole word / part word - regular expression / plain string - forward / backward / from beginning of file - replace / replace and search next / replace all (with undo!) - show one / all occurrences - search current file / current program / all files - shortcut for searching whatever text is in the current focus (without bringing up the dialog box) Cross-reference tools --------------------- I would like to see two kinds of cross-reference tools: interactive ones (e.g. "find the definition of this identifier") as well as batch-like ones (e.g. "make a listing of all variables in this module and their uses). The interactive cross-reference tools can possibly be integrated with the searching interface (the user can think of it as a "more intelligent search mode"). These tools could use some knowledge about Python, e.g. parse the current function to see if an identifier is local or not and restrict the search accordingly. For the batch cross reference tools the user may have to wait a while while they are grinding away. It would be nice if this could be done in such a way that the application is still responsive ("background grinding"). The interactive xref tools may be able to use information gathered by batch xref tools (this could replace tags files). Some xref tools are actually part of the static check facility, see the following section. Static checks ------------- This is currently mostly a dream. I can envisage a "Python Lint" integrated with VP. It could either run automatically (in the background, or -- if it's fast enough -- while you are typing) or only at request (and this could be configurable :-). Interactive execution window ---------------------------- This would feel very similar to running an interactive Python interpreter in an xterm window on Unix, except that the VP Python-aware text editor would be used to edit multi-line input. Output should always be appended at the end of the window. I have done some experimentation with a text editor that allows free editing (with syntax support) of the current command (the one that is not yet executed) only -- program output and previously executed commands are read-only. There should probably be a way to turn on editing those parts as well (especially to throw away unwanted output). An alternative interface would separate the command history from the output, thus making it possible to scroll through previous commands independently from the output. I don't know how pleasant this would be in actual usage -- we may have to implement it as an option to see how it feels. The system should support multiple independent execution windows (except that I think if two windows import the same module they should probably see the same module and not independent copies). The system should allow inspection of an execution window's name space using the standard debugger (this would actually be a nice way to introduce the debugger to the first-time user). Interactive application building -------------------------------- The only thing that I've seen someone do using Visual Basic was building a simple interactive application: something like selecting a "create new window" command from a menu, which immediately pops a fresh window on the screen, followed by "create new button" which pops a button up in the window with a default label; you can drag the button and resize it or click on its text to edit it. When you have selected the button you can select "edit script" which pops up a small text window in which you can edit the command callback for the button. The first time you do this for a particular button, the window is not empty but contains a template for a function, with the cursor conveniently placed at a position were the first statement can be entered. When you select the "run" command (even without "saving" the callback function) the program is actually run and you see its real behavior. There are also commands for editing a large variety of attributes for each visible object, such as font, color and placement. Colors (or other cues) are used to indicate which attributes have their default value and which ones you have given an explicit value. All this seems relatively straightforward to translate into Python. It may be possible to generate the application as some kind of class, but this may not be necessary: a module containing a bunch of functions may work as well. This has to be investigated. I propose that somehow if you edit a particular callback, you only see that callback in your editing window, but that actually all callbacks are placed together in a module, with a prolog containing some imports at the front and an epilog containing some initialization code at the end, so the file is actually a working program. (The init code should of course create the widgets and start the main loop.) Special commands allow you to edit (or at least see) the prolog and epilog as well. Small manual edits to the resulting file outside VP shouldn't break VP's ability to recognize the file the next time (as long as you maintain VP's style). Application templates --------------------- This is just a wild idea -- maybe we could provide several different "empty applications" that can be filled up with callbacks as described above. It should be possible to combine or nest templates as well as remove parts from them after they have been copied into the application. (XF has something like "compound widget templates" -- will this help?) Context-sensitive on-line help ------------------------------ Someone else will have to design this. It should build on existing conventions for providing help (e.g. Motif, MS Help) as well as on "doc strings" that should be generously sprinkled through Python source. Spam, spam, spam, eggs and spam ------------------------------- Sorry, couldn't help it after keeping quiet for such a long time :-) Thoughts about the abstract GUI interface ========================================= The Tk text widget has many features not found in Motif: it is possible to change font, style and size of selected character ranges, to insert bitmaps (soon: pixmaps) in the text stream, to highlight ranges of characters with arbitrary colors, and to assign arbitrary 'attributes' to ranges of characters. As far as I know the Motif text widget only supports one font/style/size per widget and two selections, which must be contiguous (the X11 primary and secondary selection). A lowest common denominator approach will either define an interface with the same restrictions as Motif, or has to provide its own reimplementation of the Tk text widget as a new Motif widget. Neither package, by the way, supports a feature that would be really handy for a debugger: the ability to attach little icons or markers to certain lines of text which are displayed in the left (or right) margin. This could be used to indicate the presence/absence of a breakpoint on a line of code, or the fact that (somewhere on the call chain) the program counter is on a line of code. I suppose that it is possible to mimic this by placing a second tall text widget, whose width would be one or two characters, to the left of the main text widget, and inserting spaces or special characters in it (or using a canvas widget to draw little icons), but this has the big disadvantage that it needs to be modified each time the line structure of the main widget changes. Alternatively, we could use underlining or highlighting with special colors to indicate breakpoints, but this would seem less clear from a human factors point of view. (Note that the possibility to display markers in the margin is also useful to display change bars as commonly used in computer documentation.) Other ideas =========== There's something to say for the notion that beginning users will find it easier to be able to place new widgets manually in a canvas-like frame than to work with powerful automatic geometry managers like Tk's packer. Even for experienced users it may be simpler to be able to first design an approximate lay-out and only later worry about packer options. I suggest we can do the following: Each frame can be either a packer or a placer frame, and the default is placer. This can be changed at any time. It is also possible to sweep a rectangle in a frame, selecting all widgets contained in (or touched by) it, and turn this into a new frame, thus adding more tree structure. It should also be possible to remove a frame from the tree structure without removing its children (the frame's parent will inherit them), thus flattening the tree structure. When a placer frame is turned into a packer frame, some heuristics can be applied to guess appropriate packer options for its children depending on their lay-out in the placer: e.g. if the frame contains a number of buttons one above the other, there's an obvious way to pack them. Vice versa, the placer could use the last geometry computer by the packer's as the initial geometry for its children after a change from packer to placer. I believe I remember XF (the current Tk GUI builder) has the feature that the widgets in the application being constructed are always active. As a consequence it requires the use of the middle mouse button to select a widget for editing of its attributes. I don't like this. Visual Basic has an explicit "Run" command which seems to work just as well. (In other GUI builders this is often done by an Edit/Test mode switch -- the difference being that test mode generally doesn't run the entire application but only exercises the user interface part. VB and VP can actually run the application without having to go through an expensive and slow compilation phase, so a Run command is appropriate.) A Teaser ======== Here's one way how this stuff could work. I apologize for the lack of structure in this chapter (it's getting kind of late). Let's for a moment assume that we want to write a medium-sized application. Its implementation will consist of a GUI module and an application module. (The idea is that the application module can be written in such a way that it can be tested independently from the GUI module, and may be used by other applications as well.) The support you get for writing the application module is restricted to editing and debugging (and perhaps a class browser). However, for your GUI module you get lots of support -- in fact you won't notice that you are creating a module in the normal sense. Jon Bently mentioned a nice property of Visual Basic: if you created a smallish VB program and then look at the generated BASIC code, it actually looks fairly reasonable (e.g. a small amount of fixed boilerplate, plus a few statements per created widget, a small function per callback, one line per option whose value differs from the default). This seems a useful characteristic to aim for. The GUI module is (normally) the main program of your application. Its structure is roughly as follows: - Boilerplate: import statements and perhaps some other stuff. - At least one class definition (maybe more if there are multiple independent toplevel windows?) for the GUI. - A small main program and a statement to call it. The support that you get differs per section: - Boilerplate support could consist of a dialog that letss you select the modules you want to import and a tick box to indicate whether you want to use the form import or from import * The GUI builder could suggest a default set of modules and tick mark values (including Tkinter!). It could even go as far as automatically adding import statements if you reference a module in the code -- e.g. if it sees "math." anywhere in the code it could infer that you want to use the math module. of course you can turn this off if you don't want it. The idea is that for simple applications, the defaults would make it unnecessary to even know that the boilerplate exists in your program -- what's added by default does what you want. - The class definitions are constructed according to the windows and widgets you define. We'll see how this could work soon enough. - The main program can be generated completely automatic -- all it needs to do is instantiate the classes and start the Tk mainloop. It could also handle command line argument handling -- again, for advanced usage there could be a dialog where you define the command line options and their meaning (e.g. binding each option to a global variable, specifying defaults and text for the usage message, etc.) The mainloop could actually be a special version of the Tk mainloop, prepared for use with Visual Python: e.g. when a callback raises an exception, the VP debugger should automatically be invoked (of course it should be possible to run the program outside of VP as well, without first editing it). Let's concentrate now on the support for creating the GUI classes. Disregarding some more esoteric features, we can distinguish the following high-level components that will be used in most applications (I purposely add some things that are actually implemented as "mere" widgets or occurrences of other component types, since they are actually somewhat special from a human factors or implementation point of view): - the application's root window (in Tk, if you kill this the whole application dies, as all other windows are (grand)children of it) - normal top-level windows (your garden variety window) - modal dialogs (these don't go away until you tell them to, and as long as they're there, you can't interact with any other window of the same application) - file selection dialogs (a special case of modal dialogs because they are often rather complicated and you may want to add extra options to them -- also, on the Mac they are implemented by a system package) - modeless dialogs (really the same as normal top-level windows but usually shorter-lived) - modeless status dialogs (output-mostly dialogs; may be updated even while the application is busy working and not listening for events, and then there may be a "stop" button that cancels the current activity in a planned way) - menu bars and their menus (lots of special code is needed to make these work properly in Tk; also on the Mac they are of course implemented differently) The distinction between the root window and other (non-dialog) top-level windows should probably disappear -- this is mostly an artefact of the Tk implementation. (We may have to have an invisible dummy root window to make this possible.) Let's assume for a moment that the Visual Python menu contains these commands: Create Window; Create Dialog. If you choose Create Window, it pops up a new window which you can drag around or resize, and into which you can place new widgets. If you choose Create Dialog, you are first presented with a small options dialog where you choose the type of dialog you want (including a menu of some standard warning, error and alert dialogs). An option for dialogs could determine whether they belong to a particular main window or are global to the application. Note that in all case, when you create a window or dialog, you are really creating a window/dialog *class*: most simple applications will instantiate only one top-level instance, but it is simple to create more than one. Each window-representing GUI class has a standard structure: instance variables representing application-specific state, instance variables representing the subwidgets, methods representing user-defined callbacks, and a few standard callbacks for widget management like open / close / clone / activate (pop to top of window stack) / deactivate (push to bottom of window stack) / iconify / deiconify and maybe even move / resize. The user-defined callbacks may call these methods, and some will also be called automatically by the framework. The collection of widgets in any particular window seems fixed, but we may introduce a mechanism whereby certain sections of a window (say a particular subframe) can be populated with a number of (mutually exclusive) alternative widget collections, under program control. In Tk this can be supported by placing each alternative in a frame widget of its own, making all these frame widgets children of the frame that represents the location of the alternatives, and using the packer options "pack" and "forget" to make exactly one of the children visible at any given time. Tk uses a naming scheme whereby the nesting structure of widgets in frames is represented by the names for the widgets (the number of dots in the name represents a widget's depth). I propose not to copy this for VP: instead, we make all widgets instance variables (suggesting names for them like text1, text2, frame1, frame2, button1 that the user can change if so desired). This has the advantage that it will be possible to regroup widgets without affecting their name. It also makes it easier to start out using the MS Windows style of widget lay-out (you place it and that's that) and letting the user choose to use packer- style lay-out for selected groups of widgets afterwards. A disadvantage of this "flatter" approach may be that if a window contains multiple groups of widgets with the same structure (e.g. three groups of three check boxes representing UNIX r/w/x permissions) the user is forced to invent lots of names for these widgets. I suppose we could allow a logical grouping, or do something clever when a group of widgets is duplicated using cut/paste. Tk's widgets are often more primitive than you would like them to be. E.g. there are no "scrollable text" or "scrollable list" widgets -- there are text and list widgets and there are scroll bars and there are some options and commands designed to bind them together. I propose that (at least for these and a few other common cases) we handle the silightly higher-level abstractions as primitives in the GUI builder -- e.g. when creating a text widget the user only has to select a check box to add a scroll bar to it. A particular kind of dialog often seen on the Mac also seems worth to provide some specific support for: these are "option setting" dialogs that contain a number of subdialogs and have a standard interface for selecting one of the subdialogs. There are actually a number of different styles: either there's a list of labeled icons on the left, each representing one subdialog (with a scroll bar if there are more icons than fit in the space available, and highlighting the icon representing the current subdialog), or there's a pop-up menu from which the subdialog can be chosen (the current selection is shown in the button that pops up the menu), or (if there is only a small number of subdialogs) there are some radiobuttons representing the subdialogs. Note that the implementation for this feature could use the mechanism described above for creating alternative dialog parts. Earlier I mentioned that a class representing a window has three components: application-specific data, widgets, and callbacks. I'll continue by sketching the support for each of these in the GUI builder. For application-specific data I don't have many ideas. I suppose we could create a window-specific dialog where you can fill in each instance variable's name, its type and its initial value (which may be an expression that calls the application-specific module to create a value, and which may use other instance variables as input -- it would be nice if dependencies here would be recognized automatically). Widgets get a lot of support. There is a "Create Widget" menu with items for each widget type (these are the high-level possibly compound widgets I mentioned above -- inapplicable widget types are greyed ouy, and there may be a "More..." submenu or dialog to select less popular widgets if there are lots of them). This menu is only active when there's a top-level window open for editing (created with the Create Window command). When you select an item from it, a widget of the appropriate type and of a default size is placed in the window and selected as the current widget. (If we have groups I suppose it automatically becomes a member of the current group if there is one.) You can move the item with the mouse, resize it (there's probably a snap grid), type to rename it, and there's a menu that opens up a dialog with widget options. Some options apply to all widgets, others only to certain ones. Since (at least in Tk) there are lots of widget types and lots of options, these dialogs in the GUI generator should probably be generated somehow. I seem to remember that in Visual basic, they looked like a large list of option names XXX Q. how does one window reference another window's data??? A. each window is a global variable.