In general, choosing a GUI Toolbox for your application can be tricky. Programmers with Python (as in many languages) can choose a wide variety of GUI toolkits, each with its own pros and cons. Some are faster than others, some are smaller, some are easy to install, some are better for cross-platform use (and, for that, some of the specific features that you need to meet). Of course, all kinds of libraries are appropriately licensed.
For Python programmers, the default GUI selection is Tk (via Tkinter binding)-the reason is obvious. Tkinter and the unused IDE are written by the Python founders, and they appear as the default choices for most Python distributions. The standard Python document discusses Tkinter, but does not involve any other GUI bindings. It was intentional! At the very least, if Tk and tkinter are not so bad, programmers have no reason to look for alternatives. To induce Python programmers to discard the default selections, the toolbox must provide something extra. PyQt is such a toolbox.
PyQt has far more advantages than tkinter (It also has several drawbacks). Qt and PyQt are very fast; Qt and PyQt are all object-oriented; QT provides a well-designed set of widgets that is much larger than what Tk provides. For its part, QT licenses are more restrictive than many toolbox (at least not Linux platforms), and it is often complicated to install Qt and PyQt correctly, and Qt is a fairly large library. Users of the PYQT application will need to try to complete the installation of Qt and PyQt, which makes distribution difficult. (Please read the Qt bindings for other languages later in this article.) )
PyQt strictly follows Qt's licensing. In particular, it is available on the UNIX/X11 platform's GPL and can be used for the Zaurus QT palmtop environment environment, as well as for free (free-as-in-free-beer) Windows packages for older versions of QT. PyQt's commercial license is available for Windows.
For this article, PyQt has one aspect that is superior to many other toolkits, and it deserves our special attention. Qt uses a mechanism called signal/slot (signals/slots) to pass events and messages between window artifacts (and other objects). This mechanism is completely different from the callback (callback) mechanism used by most toolbox, including Tkinter. Using signals/slots to control communication between objects in a flexible and maintainable manner is much easier than using a fragile callback style. The larger the application, the more important this advantage of Qt is.
One of the authors of this article, Boudewijn Rempt, has published a book on application development using PYQT. The GUI programming with PYTHON:QT Edition (see Resources) shows how to design and develop a complete GUI application, including the entire process from initial conception to distribution.
Sample Application
To show the contrast between the signal/slot and callback, we provide an application that is written to play, using Tkinter and PyQt. Although the PyQt version is actually not simpler for this basic program, it has demonstrated better modularity and maintainability of the PyQt application.
An application consists of four window artifacts:
- "Quit" button (used to communicate with the entire application)
- "Log Timestamp" button (for messages between widget)
- Text area, displaying a scrollable list of recorded log time stamps
- message window widget, displaying the number of time stamps logged
In Tkinter, we can implement the application in this way:
Listing 1. logger.py tkinter Application
#!/usr/bin/python import sys, time from tkinter import * class Logger (Frame): Def __in It__ (self): frame.__init__ (self) self.pack (Expand=yes, Fill=both) self.master.title ("Timestamp Logging Applica tion ") self.tslist = [] Self.tsdisp = Text (height=6, width=25) Self.count = Stringvar () Self.cntdisp = Mes Sage (' Sans ',), textvariable=self.count) Self.log = button (text= "Log Timestamp", C Ommand=self.log_timestamp) Self.quit = button (text= "Quit", Command=sys.exit) Self.tsdisp.pack (side=left) self. Cntdisp.pack () self.log.pack (Side=top, Expand=yes, Fill=both) self.quit.pack (Side=bottom, Fill=both) def Log_tim Estamp (self): stamp = Time.ctime () Self.tsdisp.insert (end, stamp+ "\ n") self.tsdisp.see (end) Self.tslist.ap Pend (stamp) self.count.set ("% 3d"% len (self.tslist)) if __name__== ' __main__ ': Logger (). Mainloop ()
This Tk version uses the Log_timestamp () method as the command= parameter of the button. This method needs to individually manipulate all the window artifacts it will affect. This style is very fragile if we want to change the effect of the button's press (for example, to record the time stamp). You can accomplish this by inheriting:
Listing 2. stdoutlogger.py Tkinter Enhanced
Class StdoutLogger (Logger):
def log_timestamp (self):
logger.log_timestamp (self)
print self.tslist[-1]
But the author of this subclass needs to understand quite precisely what logger.log_timestamp () has done, and there is no way to drop the message unless you completely override the. Log_timestamp () method in the subclass and do not invoke the parent method.
A very basic PyQt application always has some sample code, where the code is the same, and so is the Tkinter code. However, when we look further at the code needed to set up the application and the code that displays the widget, the difference becomes apparent.
Listing 3. logger-qt.py PyQt Application
#!/usr/bin/env python import sys, time from QT import * # generally advertised as safe class Logger (qwidget): Def __i Nit__ (self, *args): qwidget.__init__ (self, *args) self.setcaption ("Timestamp Logging Application") self.layout = Qgridlayout (self, 3, 2, 5,) Self.tsdisp = Qtextedit (self) self.tsdisp.setMinimumSize (by) SELF.TSDI Sp.settextformat (qt.plaintext) Self.tscount = Qlabel ("", self) Self.tscount.setFont (Qfont ("Sans",) Self.lo g = Qpushbutton ("&log Timestamp", self) self.quit = Qpushbutton ("&quit", self) self.layout.addMultiCellWid Get (Self.tsdisp, 0, 2, 0, 0) self.layout.addWidget (self.tscount, 0, 1) self.layout.addWidget (Self.log, 1, 1) s Elf.layout.addWidget (Self.quit, 2, 1) self.connect (Self.log, SIGNAL ("clicked ()"), Self.log_timestamp) s
Elf.connect (Self.quit, SIGNAL ("clicked ()"), Self.close) def log_timestamp (self): stamp = Time.ctime () Self.tsdisp.append (Stamp) self.tscount.setText (str (self.tsdisp.lines ())) if __name__ = = "__main__": App = Qapplication (SYS.ARGV) app.conn ECT (app, SIGNAL (' lastwindowclosed () '), app, SLOT (' Quit () ') logger = logger () logger.show () APP.SETMAINWIDG
ET (logger) app.exec_loop ()
By creating a layout manager, the Logger class begins to work. The layout manager is a complex subject in any GUI system, but the implementation of QT makes it simple. In most cases, you will use Qt Designer to create a generic GUI design that can then be used to generate Python or C + + code. You can then make the generated code generate subclasses to add functionality.
But in this example, we chose to create the layout manager manually. The widget is placed in each cell of the grid, or it can be placed across multiple units. In places where Tkinter require named parameters, PyQt does not allow them. This is a very important difference, and it often makes people who work in both environments feel overwhelmed.
All Qt window artifacts can work naturally with QString objects, not with Python strings or Unicode objects. Fortunately, the conversion is automatic. If you use a string or Unicode parameter in the Qt method, it is automatically converted to QString. Cannot reverse convert: If you call a method that returns QString, then you get a QString.
The most interesting part of the application is where we connect the clicked signal to the feature. One button is connected to the Log_timestamp method, and the other is connected to the Close method of the Qwidget class.
Figure 1. Screenshot of LOGGER-QT
Screenshot of LOGGER-QT
Now we want to add the log records to the standard output of this application. It's very easy. We can make subclasses of the Logger class, or create simple stand-alone functions for demonstration purposes:
Listing 4. logger-qt.py PyQt Enhanced
Def logwrite ():
print (Time.ctime ())
if __name__ = = "__main__":
app = Qapplication (SYS.ARGV)
App.connect (App, SIGNAL (' lastwindowclosed () '), app,
SLOT (' Quit () '))
logger = logger () Qobject.connect
( Logger.log, SIGNAL ("clicked ()"), logwrite) logger.show (
)
app.setmainwidget (logger)
App.exec_loop ()
As we can see from the above code, this is the thing that connects the log Qpushbutton clicked () signal to the new function. Note: The signal can also transfer any data to the slot they are connected to, although we do not show such an example here.
If you do not want to invoke the original method, you can disconnect the signal from the slot, for example by adding the following line before the Logger.show () line:
Listing 5. logger-qt.py PyQt Enhanced
Qobject.disconnect (Logger.log, SIGNAL ("clicked ()"),
Logger.log_timestamp)
The GUI will now no longer be updated.
Other GUI bindings for Python
PyQt may not be useful in a given instance, may be a license state issue, or it may be a platform availability issue (or it may be difficult to redistribute, such as large size). For this reason (and also for comparison), we would like to point out some of the other popular GUI toolkits for Python.
Anygui
Anygui is not actually a GUI toolbox, but an abstract wrapper that works on a large number of toolkits (and even amazing toolkits like curses and Java/jython Swing). In terms of programming style, using Anygui is similar to using Tkinter, but to select the underlying toolbox, either automate it or make a configuration call. Anygui is useful because it allows applications to run on very different platforms without changing it (but it supports the "lowest common feature" of the supported toolbox).
PyGTK
The PyGTK binding wraps the GTK Toolbox used under the GPL, which is the foundation of the popular Gnome environment. GTK is essentially the X Window Toolbox, but it also has Win32 beta-level support and BeOS alpha-level support. In the general paradigm, PyGTK uses callbacks against the widget. Bindings exist between GTK and a large number of programming languages, not just Qt, or even Tk.
Fxpy
The Python binding Fxpy wraps the FOX Toolbox. The FOX Toolbox has been ported to most UNIX-like platforms, as well as Win32. Like most toolkits, both FOX and Fxpy use callback paradigms. FOX is licensed by LGPL.
WxPython
This binding wraps the Wxwindows Toolbox. Like FOX or GTK, Wxwindows is ported to Win32 and Unix-like platforms (but not to MacOS, OS/2, BeOS, or other "secondary" platforms-although its support for MacOSX is alpha-level). In the case of example, WxPython is close to the callback style. WxPython is more concerned with the inheritance structure than most other toolkits, and it uses "events" rather than callbacks. In essence, however, events are still connected to a single method and may then need to be used for various window artifacts.
Win32ui
Win32ui belongs to the Win32all software package, which wraps the MFC classes. Obviously, this toolbox is a Win32-specific library. MFC is not really just a GUI toolbox, it also uses a mixture of various examples. For readers who want to create a Windows application, Win32ui will make you "closer to the essentials" than the other toolbox.
Using Qt from other languages
Like Python, it is possible to use the Qt Toolbox from a large number of other programming languages. If we are free to choose, we will prefer Python to other languages. External constraints such as corporate policy and connections to other code libraries can determine the choice of programming languages. Qt's original language is C + +, but it also has bindings for C, Java, Perl, and Ruby. For comparison with the Python example, let's talk about applications that are written in Ruby and Java.
RUBY/QT is very similar in usage to PyQt. The two languages have similar dynamics and simplicity, so the code is similar in addition to the spelling differences:
Listing 6. HELLOWORLD.RB QT2 Application
#!/usr/local/bin/ruby
require ' qt2 '
include Qt2
a = Qapplication.new ([$] + ARGV)
Hello = Qpushbutton.new (' Hello world! ')
Hello.resize (
a.connect) (Hello, Qsignal (' clicked () '), A, Qslot (' Quit () '))
a.setmainwidget (hello)
hello.show
a.exec
Java is always a bit more verbose than a scripting language, but the basic parts are the same. An equally functional minimum Qtjava application is similar to the following:
Listing 7. Helloworld.java QT2 Application
Import org.kde.qt.*;
public class HelloWorld {public
static void Main (string[] args)
{
qapplication MyApp = new Qapplication (args );
Qpushbutton Hello = new Qpushbutton ("Hello World", null);
Hello.resize (100,30);
Myapp.connect (Hello, SIGNAL ("clicked"), this
, SLOT ("Quit ()"));
Myapp.setmainwidget (hello);
Hello.show ();
Myapp.exec ();
return;
}
static {
system.loadlibrary ("Qtjava");
try {
Class c = class.forname ("Org.kde.qt.qtjava");
} catch (Exception e) {
System.out.println ("Can" t Load Qtjava class ");}}
PyQt is an attractive and fast interface that integrates the Qt Toolbox with the Python programming language. In addition to the wide variety of widgets provided by the Toolbox, the signal/slot programming style used in Qt is superior to the callback style used in most other GUI toolkits in terms of productivity and maintainability.