GUI design with Python – examples from crystallography Bernhard Lohkamp Karolinska Institute Stockholm Sweden Coot - Who are we – why am I here? Paul Emsley (Oxford; everything) Bernhard Lohkamp (Stockholm; Python, GUI, Windows) Kevin Cowtan (York; crystallographic libraries & tools) Bernhard.Lohkamp@ki.se Eugene Krissinel, Stuart McNicholas, and others... 7/5/10 COOT Graphical (CrystallographicObject)-Oriented Toolkit Used in macromolecular (X-ray) crystallography Main aims: Model building Model completion Model validation Graphical, interactive, intuitive, … Bernhard.Lohkamp@ki.se 7/5/10 G(raphical) U(ser) I(nterface) GUI <-> UI Allow end users to interact with application Interface can be simple (e.g. command line) to complex (graphical) Good UI: Intuitive Easy to use/learn Note: applications with good UI are preferred to ones with inferior ones (independent of quality of application!?) Bernhard.Lohkamp@ki.se 7/5/10 What do you prefer? >>> delete_residue(10) Bernhard.Lohkamp@ki.se 7/5/10 GUIs Good things about GUI Intuitive (if designed properly) More efficient (e.g., putting the cursor at a particular location) Multiple activities simultaneously Bad things about GUI Inefficient (if mis-designed) Difficult to automate (e.g. difficult to write a program to push a button in another application) Speed Bernhard.Lohkamp@ki.se 7/5/10 The challenge of GUI programming programming model must address the following issues: user allowed to perform different things (e.g. type a character, type a hot-key, click on a button, resize the window, obscure the window with another application, minimize it, etc.) program needs to adapt to different window size (and with it all its content) Solution: event-driven model widget systems (controlled by a geometry manager) Use Toolkits == TK Bernhard.Lohkamp@ki.se 7/5/10 Windows manager/Desktop Forms of interaction events in context Human Interface Device (HID) control Bernhard.Lohkamp@ki.se 7/5/10 Windowing system/hierarchy Lots of layers Not very efficient Slow (not necessary to be fast?!) Our script (in Python): Real application TK (in Python): glue to TK TK Python plugin (usually in C): translate to TK calls TK widgets (usually in C): Widget implementation TK library (usually C): glue to windowing system Windows managing system (e.g. X) Bernhard.Lohkamp@ki.se 7/5/10 Connect GUI with Python Direct via script GUI design tools and IDEs (Integrated Development Environments) GUI builder GUI to build a GUI application Useful especially for larger projects Output usually xml Use directly Translate to Python script Bernhard.Lohkamp@ki.se 7/5/10 GUI modules for Python (I) (Toolkits, cross-platform) PyGTK (Gnome): Coot TK: GTK+ (GIMP Tool Kit) Well supported Builder (Glade) xml direct Not native on Mac (yet) PyQt (KDE): CCP4mg – molecular graphics TK: Qt (“cu-te”) Licence issue? Native Builder (Designer) xml->python Bernhard.Lohkamp@ki.se 7/5/10 GUI modules for Python (II) Tk(Inter): PyMOL TK: Tk Tkinter Python Tk interface Distributed with python Not OO Builder (GUI builder) wxPython: Phenix TK: wxWidgets Builder (wxGlade) Native ? Bernhard.Lohkamp@ki.se 7/5/10 Nomenclature/Widgets Widget: “windowing gadget”, which means “a useful building block (gadget) to make a windowing system”. Hierarchy (exemplified): Bernhard.Lohkamp@ki.se 7/5/10 Widget examples Top level (independent widgets): Main window Various (predefined) dialogs Bernhard.Lohkamp@ki.se 7/5/10 Dialogs (I) Info (Message) dialog Dialog box Bernhard.Lohkamp@ki.se 7/5/10 Dialogs (II) About dialog Assistant Bernhard.Lohkamp@ki.se 7/5/10 Dialogs (III) Selection dialogs Files Colours Fonts …. Bernhard.Lohkamp@ki.se 7/5/10 Containers/Layout Boxes Vertical Horizontal Combined (grid, table) Scrolled window Tabbed widgets/notebook Frames Panes Bernhard.Lohkamp@ki.se 7/5/10 Boxed layout Bernhard.Lohkamp@ki.se 7/5/10 Tabbed window/Notebook Bernhard.Lohkamp@ki.se tabs 7/5/10 Buttons Button Toggle button Check button Spin button Radio button Pre-defined buttons Files Colours Fonts … Bernhard.Lohkamp@ki.se 7/5/10 Input & Entries (I) Menu Toolbar Bernhard.Lohkamp@ki.se 7/5/10 Menus Submenus Icons Accelerators Bernhard.Lohkamp@ki.se 7/5/10 Context menu Shown upon event (usually mouse click) Bernhard.Lohkamp@ki.se 7/5/10 Toolbars Like menus but contain icons/buttons Short-cut for frequently used functions Bernhard.Lohkamp@ki.se 7/5/10 Toolbar Bernhard.Lohkamp@ki.se 7/5/10 Change Style Bernhard.Lohkamp@ki.se 7/5/10 Text only Bernhard.Lohkamp@ki.se 7/5/10 Text and Icons Bernhard.Lohkamp@ki.se 7/5/10 Toolbar Detouch/ Handle Bernhard.Lohkamp@ki.se 7/5/10 detouched Bernhard.Lohkamp@ki.se 7/5/10 Input & Entries (I) Combobox – spin button Simple Entry: Bernhard.Lohkamp@ki.se 7/5/10 Input & Entries (II) Entry: Auto-completion possible Sliders Bernhard.Lohkamp@ki.se 7/5/10 List/Tree structures Bernhard.Lohkamp@ki.se 7/5/10 Can include: icons, buttons, … Bernhard.Lohkamp@ki.se 7/5/10 Tree-> expansion Bernhard.Lohkamp@ki.se 7/5/10 Miscellaneous widgets Progress bar Status bar Bernhard.Lohkamp@ki.se 7/5/10 Canvas Drawing area Canvas/cairo drawing (e.g. PyGoocanvas) OpenGL drawing (e.g. PyOpenGL, PyGtkGLExt) Can be interactive Bernhard.Lohkamp@ki.se 7/5/10 Tooltips Pop ups with text (upon mouse over) Helps to explore the GUI without reading help files Use whenever you can (!?) Bernhard.Lohkamp@ki.se 7/5/10 GUI design What makes a good UI? Simple Intuitive Respects the commonly accepted conventions Visually organized Native look Bernhard.Lohkamp@ki.se 7/5/10 Designing UIs Two levels: Graphical: visual aspect Events: Functionality Basic blocks = widgets Making an application react to events = binding Bernhard.Lohkamp@ki.se 7/5/10 General concepts Main loop and events Packing Showing How to do. Bernhard.Lohkamp@ki.se 7/5/10 Main loop and events TK mainloop: Event loop Idle until an event happens (e.g. a button is pushed, a menu is pulled, etc.) Quit when program (GUI) finishes Can have multiple loops (count!!) PyGTK: TKinter: PyQT: Bernhard.Lohkamp@ki.se gtk.main() tk.mainloop() application.exec_() 7/5/10 Main loop and events Events (examples): Keyboard (key-pressed, key-released) Mouse (button-pressed, button-released, motion, wheel, …) Window/widget (resize, destroy, visibility, activate, deactivate, …) Etc. Event modifiers (Shift, Alt, …) Events emit signals Can be connected with callbacks (functions, methods) Bernhard.Lohkamp@ki.se 7/5/10 Callbacks and signals Callback functions/bindings: functions, methods do something when event happens (e.g. button pressed) PyGTK: object.connect(signal_name, callback_func, func_data) def callback_func(widget, callback_data): def callback_meth(self, widget, callback_data): Tkinter: Button(master, text="OK", command=callback) def callback_func(): PyQT: connect(widget, signal, callback_func) Bernhard.Lohkamp@ki.se 7/5/10 Synergy between objects and widgets Variables are passed automatically within class refer to them as self.whatever No need to pass variables Callback functions Easy to handle No need to define callbacks on the fly (lambda functions) Bernhard.Lohkamp@ki.se 7/5/10 Packing Add widget to container Pack widget in boxes PyGTK: container_widget.add(widget) box_object.pack_start(child, expand, fill, padding) Tkinter: object.pack(various_options) PyQT: Layout.addWidget(widget) Bernhard.Lohkamp@ki.se 7/5/10 Packing Homogeneous (equal space for everyone) HBox(True/False, 0) Expand, fill box.pack(expand=, fill=) Bernhard.Lohkamp@ki.se 7/5/10 Showing Widgets itself do not show (neither do they do anything else -> connect signals!) Need to show every (!) widget Can hide widgets Usually collective show for all PyGTK: widget.show() widget.hide() widget.show_all() Tkinter: Done via mainloop PyQT: widget.show() widget.hide() Bernhard.Lohkamp@ki.se 7/5/10 Simple example GUI command input Schematic in terms of widgets window VBox HBox Label ScrolledWindow Entry TextBuffer Button Bernhard.Lohkamp@ki.se 7/5/10 Simple example - make window # import gtk module import gtk # create a main window window = gtk.Window(gtk.WINDOW_TOPLEVEL) # set the title window.set_title("Coot Python Scripting") # set some more properties (if we wish) window.set_default_size(400,250) Bernhard.Lohkamp@ki.se 7/5/10 Simple example - create widgets # create other needed widgets: boxes, label, entry, … vbox = gtk.VBox(homogeneous=False, spacing=0) hbox = gtk.HBox(False, 0) label = gtk.Label("Command: ") entry = gtk.Entry() scrolled_win = gtk.ScrolledWindow() text = gtk.TextView() textbuffer = text.get_buffer() close_button = gtk.Button(" Bernhard.Lohkamp@ki.se Close ") 7/5/10 Simple example - pack it in window VBox HBox Label ScrolledWindow # add vbox in window Entry TextBuffer Button window.add(vbox) # pack the hbox and pack into the vbox hbox.pack_start(child=label, expand=False, fill=False, padding=0) hbox.pack_start(entry, True, True, 0) vbox.pack_start(hbox, False, False, 5) Bernhard.Lohkamp@ki.se 7/5/10 Simple example - pack it in window VBox HBox Label ScrolledWindow Entry TextBuffer Button # pack the scrolled text area scrolled_win.add(text) vbox.add(scrolled_win) # and finally the button vbox.pack_end(close_button, False, False, 5) Bernhard.Lohkamp@ki.se 7/5/10 Simple example - show it # show everything connected to the window window.show_all() # run the main loop gtk.main() Bernhard.Lohkamp@ki.se 7/5/10 Simple example - making it functional (close button) Does not perform any function (yet) Main loop never stops connect callbacks and signals # connect close_button (no further args) close_button.connect(“clicked”, close_callback) # define callback: destroy window, quit gtk loop def close_callback(widget): window.destroy() gtk.main_quit() Bernhard.Lohkamp@ki.se 7/5/10 Simple example - making it functional (destroy window) Closing window does not finish gtk loop Deal with delete event # connect delete_event (two args, but no extra) window.connect(“delete_event”, delete_event_cb) # define callback: quit gtk main loop def delete_event_cb(widget, event): gtk.main_quit() Bernhard.Lohkamp@ki.se 7/5/10 Simple example - making it functional (entry) # do something with entry when enter is pressed entry.connect(“activate”, do_callback) # what to do def do_callback(widget): entry_text = widget.get_text() print “input is”, entry_text # erase the entry widget.set_text(“”) # do something with the text, # e.g. put in text # scrolled window end = textbuffer.get_end_iter() textbuffer.insert(end, str(entry_text + "\n")) Bernhard.Lohkamp@ki.se 7/5/10 Glade (GUI builders) Inspector Palette Editor Properties Bernhard.Lohkamp@ki.se 7/5/10 Glade Output xml file Two different ‘formats’ (libglade, builder) ==> my_test3.libglade <== <?xml version="1.0"?> <glade-interface> <!-- interface-requires gtk+ 2.16 --> <!-- interface-naming-policy project-wide --> <widget class="GtkWindow" id="window1"> <child> <widget class="GtkVBox" id="vbox1"> <property name="visible">True</property> <property name="orientation">vertical</property> Bernhard.Lohkamp@ki.se ==> my_test3.glade <== <?xml version="1.0"?> <interface> <requires lib="gtk+" version="2.16"/> <!-- interface-naming-policy project-wide --> <object class="GtkWindow" id="window1"> <child> <object class="GtkVBox" id="vbox1"> <property name="visible">True</property> <property name="orientation">vertical</property> 7/5/10 Connect to python script Keep GUI/widgets and events separate Useful for larger projects (easy to add, remove, clean) import pygtk, gtk builder = gtk.Builder() builder.add_from_file(“my_file.glade”) # get the main window and connect event window = builder.get_object(“window1”) window.connect("destroy", gtk.main_quit) # show and run window.show_all() gtk.main() Bernhard.Lohkamp@ki.se 7/5/10 Glade – connect more signals Use automatic connection # dictionary of callbacks (name of signal, # callback function) dic = { "on_button1_clicked" : button1_cb, "on_button2_toggled" : button2_cb, "on_window1_destroy" : gtk.main_quit } # define some callbacks def button1_cb(widget): print “button 1 pressed” def button2_cb(widget): print “button 2 toggled” # connect the signals builder.connect_signals(dic) Bernhard.Lohkamp@ki.se 7/5/10 (lib)glade same principle (as for gtk.Builder) slightly different syntax Bernhard.Lohkamp@ki.se 7/5/10 The higher end implementations Combining C/C++/Python/GTK Python Bernhard.Lohkamp@ki.se 7/5/10 Coot and Python SWIG Python functions C(++)-functions Python interface Python scripting functions call return value Python functions (objects) Gtk+ objects (graphics) Bernhard.Lohkamp@ki.se 7/5/10 Main Menubar Main Menubar Bernhard.Lohkamp@ki.se 7/5/10 Accessing existing menus Addition of individual menu items Bernhard.Lohkamp@ki.se 7/5/10 Main Toolbar Main Toolbar Bernhard.Lohkamp@ki.se 7/5/10 Main Toolbar Pop-up menu to manage toolbuttons Extra toolbuttons Bernhard.Lohkamp@ki.se 7/5/10 Some hints for your own design: Organize Bernhard.Lohkamp@ki.se 7/5/10 Some hints for your own design: group related items Bernhard.Lohkamp@ki.se 7/5/10 Some hints for your own design: keep it simple Bernhard.Lohkamp@ki.se 7/5/10 Some hints for your own design: don’t reinvent the wheel – keep conventions Use standard menu items Use standard icons Carefully choose colours: colour blindness!! Bernhard.Lohkamp@ki.se 7/5/10 Further information on GUI/Python/TKs GUI PyGTK, Glade http://www.guidebookgallery.org/index http://www.asktog.com/basics/firstPrinciples.html http://web.cs.wpi.edu/~matt/courses/cs563/talks/smartin/int_design.html http://www-01.ibm.com/software/ucd/designconcepts.html http://library.gnome.org/devel/hig-book/stable/intro.html.en http://www.pygtk.org/ http://glade.gnome.org/ http://live.gnome.org/Glade/Tutorials http://www.micahcarrick.com/12-24-2007/gtk-glade-tutorial-part-1.html PyQT http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/pyqt4ref.html http://www.commandprompt.com/community/pyqt/ http://www.cs.usfca.edu/~afedosov/qttut/ Bernhard.Lohkamp@ki.se 7/5/10 Acknowledgements http://www.biop.ox.ac.uk/coot/ or Paul Emsley Kevin Cowtan Eleanor Dodson Keith Wilson BBSRC & CCP4 funding Libraries, dictionaries Alexei Vagin, Eugene Krissinel, Stuart McNicholas Dunbrack, Richardsons Coot Builders and Testers William Scott, Ezra Peisach York YSBL, Dundee, Glasgow (early adopters) Coot Mailing List subscribers Bernhard.Lohkamp@ki.se Google: Coot or http://www.ysbl.ac.uk/~lohkamp/coot 7/5/10 DEMO? Bernhard.Lohkamp@ki.se 7/5/10 Hierarchy Bernhard.Lohkamp@ki.se 7/5/10 Bernhard.Lohkamp@ki.se 7/5/10