What will we cover? |
---|
|
In this topic we look at how a GUI program is assembled in a general sense, then how this is done using Python's native GUI toolkit, Tkinter. This will not be a full blown Tkinter reference nor even a complete tutorial. There is already a very good and detailed tutor linked from the Python web site. This tutorial will instead try to lead you through the basics of GUI programming, introducing some of the basic GUI components and how to use them. We will also look at how Object Oriented programming can help organize a GUI application.
The first thing I want to say is that you won't learn anything new about programming here. Programming a GUI is exactly like any other kind of programming, you can use sequences, loops, branches and modules just as before. What is different is that in programming a GUI you usually use a Toolkit and must follow the pattern of program design laid down by the toolkit provider. Each new toolkit will have its own API and set of design rules and you as a programmer need to learn these. This is why most programmers try to standardize on only a few toolkits which are available across multiple languages - learning a new toolkit tends to be much harder than learning a new programming language!
Most windows programming languages come with a toolkit included (usually a thin veneer over the very primitive toolkit built into the windowing system itself). Visual Basic, Delphi and Visual C++/.NET are examples of this.
Java is different in that the language includes its own graphics toolkit (actually more than one!) which runs on any platform that Java runs on - which is almost any platform!
There are other toolkits that you can get separately which can be used on any OS (Unix, Mac, Windows...). These generally have adapters to allow them to be used from many different languages. Some of these are commercial but many are freeware. Examples are: GT/K, Qt, Tk
They all have web sites. For some examples try:
Qt and GT/k are what most Linux applications are written in and are both free for non commercial use (i.e. where you don't sell your programs for profit). Qt can provide a commercial license too if you want to use it for commercial purposes and GTk is licensed under the Gnu GPL which has its own special terms.
The standard Python graphics toolkit (i.e. it comes with the language) is Tkinter which is based on Tk, a fairly old multi-OS toolkit. This is the toolkit we will look at most closely, versions of it are available for Tcl, Haskell, Ruby and Perl as well as Python.
The principles in Tk are slightly different to other toolkits so I will conclude with a very brief look at another popular GUI toolkit for Python (and C/C++) which is more conventional in its approach. But first, some general principles:
As we have already stated several times GUI applications are nearly always event driven in nature. If you don't remember what that means go back and look at the event driven programming topic.
I will assume that you are already familiar with GUIs as a user and will focus on how GUI programs work from a programmer's perspective. I will not be going into details of how to write large complex GUIs with multiple windows, MDI interfaces etc. I will stick to the basics of creating a single window application with some labels, buttons, text boxes and message boxes.
First things first, we need to check our vocabulary. GUI programming has its own set of programming terms. The most common terms are described in the table below:
Term | Description |
---|---|
Window | An area of the screen controlled by an application. Windows are usually rectangular but some GUI environments permit other shapes. Windows can contain other windows and frequently every single GUI control is treated as a window in its own right. |
Control | A control is a GUI object used for controlling the application. Controls have properties and usually generate events. Normally controls correspond to application level objects and the events are coupled to methods of the corresponding object such that when an event occurs the object executes one of its methods. The GUI environment provides a mechanism for binding events to methods. |
Widget | A control, sometimes restricted to visible controls. Some
controls (such as timers) can be associated with a given
window but are not visible. Widgets are that subset of
controls which are visible and can be manipulated by the
user or programmer. The widgets that we shall cover are:
The ones we won't discuss in this topic but are used elsewhere in the tutor are:
|
Frame | A type of widget used to group other widgets together. Often a Frame is used to represent the complete window and further frames are embedded within it. |
Layout | Controls are laid out within a Frame according to a particular set of rules or guidelines. These rules form a Layout. The Layout may be specified in a number of ways, either using on-screen coordinates specified in pixels, using relative position to other components (left, top etc) or using a grid or table arrangement. A coordinate system is easy to understand but difficult to manage when a window is resized etc. Beginners are advised to use non-resizable windows if working with coordinate based layouts. |
Child | GUI applications tend to consist of a hierarchy of widgets/controls. The top level Frame comprising the application window will contain sub frames which in turn contain still more frames or controls. These controls can be visualized as a tree structure with each control having a single parent and a number of children. In fact it is normal for this structure to be stored explicitly by the widgets so that the programmer, or more commonly the GUI environment itself, can often perform some common action to a control and all of its children. For example, closing the topmost widget results in all of the child widgets being closed too. |
One very important principle to grasp in GUI programming is the idea of a containment hierarchy. That is, the widgets are contained in a tree like structure with a top level widget controlling the entire interface. It has various child widgets which in turn may have children of their own. Events arrive at a child widget and if it is unable to handle it it will pass the event to its parent and so on up to the top level. Similarly if a command is given to draw a widget it will send the command on down to its children, thus a draw command to the top level widget will redraw the entire application whereas one sent to a button will likely only redraw the button itself.
This concept of events percolating up the tree and commands
being pushed down is fundamental to understanding how GUIs
operate at the programmer level. It is also the reason that
you always need to specify a widget's parent when creating
it, so that it knows where it sits in the containment tree.
We can draw the containment tree for a simple application
that we will create later in this topic like this:
This illustrates the top level widget containing a single
Frame which represents the outermost window border.
This in turn contains two more Frames, the first of which
contains a Text Entry widget and the second contains
the two Buttons used to control the application.
We will refer back to this diagram later in the topic when
we come to build the GUI.
In this section we will use the Python interactive prompt to
create some simple windows and widgets. Note that because IDLE
is itself a Tkinter application you cannot reliably run Tkinter
applications within IDLE. You can of course create the files
using IDLE as an editor but you must run them from a OS command
prompt. Pythonwin users can run Tkinter applications since
Pythonwin is built using windows own GUI toolkit, MFC. However
even within Pythonwin there are certain unexpected behaviors
with Tkinter application. As a result I will use the raw Python
prompt from the Operating System.
This is the first requirement of any Tkinter program - import
the names of the widgets. You could of course just import the
module but it quickly gets tiring typing tkinter in front of
every component name.
This creates the top level widget in our widget hierarchy.
All other widgets will be created as children of this.
What happened at this point will depend on where you are typing
the program. If, like me, you are using Python from an OS prompt
you will have seen that a new blank window has appeared complete
with an empty title bar save for a Tk logo as icon and the usual
set of control buttons (iconify, maximize etc). If you are using
an IDE you may not see anything yet, it will onoly appear when
we complete the GUI and start the main event loop running.
We will now add components to this window as we build an
application.
The dir() function shows us what names are known to the
argument. You can use it on modules but in this case we are
looking at the internals of the top object, an instance
of the Tk class. These are the attributes of
top, and there are a lot of them! Take a look, in particular,
for the children and master attributes which are
the links to the widget containment tree. Note also the attribute
_tclCommands, this is because, as you might recall,
Tkinter is built on a Tcl toolkit called Tk.
Create a Frame widget which will in turn contain the child
controls/widgets that we use. Frame specifies
top as its first (and in this case only) parameter
thus signifying that F will be a child widget of
top.
Notice that the Tk window (if it's visible) has now shrunk
to the size of the added Frame widget - which is currently empty
so the window is now very small! The pack() method
invokes a Layout Manager known as the packer which
is very easy to use for simple layouts but becomes a little
clumsy as the layouts get more complex. We will stick with it
for now because it is easy to use. Note that widgets will not
be visible in our application until we pack them (or use
another Layout manager method). We will talk a lot more
about Layout Managers later on, after we complete this short
program.
Here we create a new object, lHello, an instance
of the Label class, with a parent widget F
and a text attribute of "Hello world". Notice that
because Tkinter object constructors tend to have many parameters
(each with default values) it is usual to use the named
parameter technique of passing arguments to Tkinter objects.
Also notice that the object is not yet visible because we
haven't packed it yet.
One final point to note is the use of a naming convention: I
put a lowercasel, for Label, in front of a name,
Hello, which reminds me of its purpose. Like most naming
conventions this is a matter of personal choice, but I find it
helps.
Now we can see it. Hopefully yours looks quite a lot like this: We can specify other properties of the Label such as the font
and color using parameters to the object constructor too. We can
also access the corresponding properties using the configure
method of Tkinter widgets, like so:
The message changed. That was easy, wasn't it?
configure is an especially good technique if you need to
change multiple properties at once because they can all be passed
as arguments. However if you only want to change a single
property at a time, as we did above you can treat the object like
a dictionary, thus:
which is shorter and arguably easier to understand.
Labels are pretty boring widgets, they can only
display read-only text, albeit in various colors, fonts and
sizes. (In fact they can be used to display simple graphics
too but we won't bother with that here).
Before we look at another object type there is one more thing
to do and that's to set the title of the window. We do that by
using a method of the top level widget top:
We could have used top directly but, as we'll see
later, access through the Frame's master property is a useful
technique.
Here we create a new widget - a button. The button has a label
"Quit" and is associated with the command F.quit. Note
that we pass the method name, we do not call the method
by adding parentheses after it. This means we must pass a
function object in Python terms, it can be a built-in method
provided by Tkinter, as here, or any other function that we define.
The function or method must take no arguments. The quit
method, like the pack method, is defined in a base
class and is inherited by all Tkinter widgets, but is usually
called at the top window level of the application.
Once again the pack method makes the button visible.
And finally we start the Tkinter event loop. Notice that the
Python >>> prompt has now disappeared. That tells us
that Tkinter now has control. If you press the Quit button
the prompt will return, proving that our command option worked.
Don't expect the window to close, the python interpreter is
still running and we only quit the mainloop function,
the various widgets will be destroyed when Python exits
- which in real programs is usually immediately after
the mainloop terminates! Note that if running this from Pythonwin or IDLE you may
not have seen anything until this point! And you may get a slightly
different result, if so try typing the commands so far into a
Python script and running them from an OS command prompt.
In fact it's probably a good time to try that anyhow, after
all, it's how most Tkinter programs will be run in practice. Use
the principle commands from those we've discussed so far as shown: The call to the top.mainloop method starts the
Tkinter event loop generating events. In this case the only event
that we catch will be the button press event which is connected to the
F.quit method. F.quit in turn will terminate
the application and this time the window will also close because
Python has also exited. Try it, it should look like this: Notice that I missed the line that changes the window title.
Try adding that line in by yourself and check that it works as
expected.
Note: from now on I'll provide examples as Python script
files rather than as commands at the >>> prompt. In most
cases I'll only be providing snippets of code so you will have to
put in the calls to Tk() and the mainloop() yourself,
use the previous program as a template.
In this section I want to look at how Tkinter positions widgets within
a window. We already have seen Frame, Label and Button widgets and those
are all we need for this section. In the previous example we used the
pack method of the widget to locate it within its parent widget.
Technically what we are doing is invoking Tk's packer Layout Manager.
(Another name for Layout Manager is Geometry Manager.) The Layout
Manager's job is to determine the best layout for the widgets based on
hints that the programmer provides, plus constraints such as the
size of the window as controlled by the user. Some Layout managers use
exact locations within the window, specified in pixels normally, and
this is very common in Microsoft Windows environments such as Visual
Basic. Tkinter includes a Placer Layout Manager which can do
this too via a place method. I won't look at that in this tutor because
usually one of the other, more intelligent, managers is a better choice,
since they take the need to worry about what happens when a window is
resized away from us as programmers.
The simplest Layout Manager in Tkinter is the packer which we've
been using. The packer, by default, just stacks widgets one on top
of the other. That is very rarely what we want for normal widgets,
but if we build our applications from Frames then stacking Frames
on top of each other is quite a reasonable approach. We can then put
our other widgets into the Frames using either the packer or other
Layout Manager within each Frame as appropriate. (Each Frame can
have its own Layout Manager, but you cannot mix managers within
a single frame.) You can see an example of this in action in the
Case Study topic.
Even the simple packer provides a multitude of options, however.
For example we can arrange our widgets horizontally instead of
vertically by providing a side argument, like so:
That will force the widgets to go to the left thus the first widget
(the label) will appear at the extreme left hand side, followed by the
next widget (the Button). If you modify the lines in the example above
it will look like this: And if you change the "left" to "right" then the
Label appears on the extreme right and the Button to the left of it,
like so: One thing you notice is that it doesn't look very nice because the
widgets are squashed together. The packer also provides us with some
parameters to deal with that. The easiest to use is Padding and
is specified in terms of horizontal padding (padx), and vertical
padding(pady). These values are specified in pixels. Let's try
adding some horizontal padding to our example: It should look like this: If you try resizing the window you'll see that the widgets retain
their positions relative to one another but stay centered in the window.
Why is that, if we packed them to the left? The answer is that we
packed them into a Frame but the Frame was packed without a side,
so it is positioned top, center - the packers default. If you want
the widgets to stay at the correct side of the window you will need
to pack the Frame to the appropriate side too:
Also note that the widgets stay centered if you resize the window
vertically - again that's the packers default behavior.
I'll leave you to play with padx and pady for
yourself to see the effect of different values and combinations etc.
Between them, side and padx/pady allow quite a lot of
flexibility in the positioning of widgets using the packer. There
are several other options, each adding another subtle form of control,
please check the
Tkinter reference pages for details.
There are a few other layout managers in Tkinter, known
as the grid, and the placer. (In addition the Tix
module which augments Tkinter provides a Form layout manager.
We do not cover Tix here.) To use the grid manager
you use grid() instead of pack() and for the
placer you call place() instead of pack(). Each
has its own set of options and since I'll only cover the packer
in this intro you'll need to look up the Tkinter tutorial and
reference for the details. The main points to note are that the
grid arranges components in a grid (surprise!) within the window
- this can often be useful for dialog boxes with lined up text
entry boxes, for example. Many Tkinter users prefer the grid
to the packer but for beginners it can take a little getting
used to. The placer uses either fixed coordinates in pixels
or relative coordinates within a window. The latter allow the
component to resize along with the window - always occupying
75% of the vertical space say. This can be useful for intricate
window designs but does require a lot of pre-planning - I strongly
recommend a pad of squared paper, a pencil and eraser!
The Frame widget actually has a few useful properties that we
can use. After all, it's very well having a logical frame around
components but sometimes we want something we can see too. This
is especially useful for grouped controls like radio buttons or
check boxes. The Frame solves this problem by providing, in
common with many other Tk widgets, a relief property.
Relief can have any one of several values: sunken, raised,
groove, ridge or flat. Let's use the
sunken value on our simple dialog box. Simply change
the Frame creation line to:
Note 1:You need to provide a border too. If you don't
the Frame will be sunken but with an invisible border - you
don't see any difference!
Note 2: that you don't put the border size in quotes.
This is one of the confusing aspects of Tk programming is knowing
when to use quotes around an option and when to leave them out.
In general if it's a numeric or single character value you can
leave the quotes off. If it's a mixture of digits and letters or
a string then you need the quotes. Likewise with which letter
case to use. Unfortunately there is no easy solution, you just
learn from experience - Python often gives a list of the valid
options in it's error messages!
One other thing to notice is that the Frame doesn't fill the
window. We can fix that with another packer option called,
unsurprisingly, fill. When you pack the frame do it
thusly:
This fills horizontally, if you want the frame to fill the
entire window just use fill='y' too. Because this is
quite a common requirement there is a special fill option
called BOTH so you could type:
The end result of running the script now looks like: Let's now look at a text Entry widget. This is the
familiar single line of text input box. It shares a lot of the
methods of the more sophisticated Text widget which we
used in the event handling topic and will also use in the
case study topic. Essentially we will simply use an Entry to
capture what the user types and to clear that text on demand.
Going back to our "Hello World" program we'll add a text Entry
widget inside a Frame of its own and then, in a second Frame,
put a button that can clear the text that we type into the Entry.
We will also add a button to quit the application. This will
demonstrate not only how to create and use the Entry widget
but also how to define our own event handling functions and
connect them to widgets.
Note again that we pass the name of the event handlers
(evClear and F.quit), without parentheses,
as the command argument to the buttons. Note also
the use of a naming convention, evXXX to link the event
handler with the corresponding widget.
Running the program yields this: And if you type something in the text entry box then hit the
"Clear Text" button it removes it again.
Of course there is not much point in having an Entry widget
unless we can get access to the text contained within it. We
do this using the get method of the widget. I'll
illustrate that by copying the text from the widget to a
label just before clearing it so that we can always see
the last text that the widget held. To do that we need
to add a Label widget just under the Entry widget and
extend the evClear event handler to copy the text. And just
for fun we will colour the label text a light blue.
The modified program is shown below with the modifications
in bold:
Notice that while we have here assigned the text directly to
the Label property but we could equally well have assigned it
to a normal Python variable for use later in our program.
You might recall that back in the
Talking to the User topic we discussed
the EasyGUI module and its basic data entry dialog box?
You can probably begin to see how such a dialog box can
be created. Unfortunately there are a couple of extra bits
of information we need before we are ready to do that.
Up till now we have used the command property of buttons to
associate Python functions with GUI events. Sometimes we want
more explicit control, for example to catch a particular key
combination. The way to do that is use the bind
function to explicitly tie together (or bind) an event and a
Python function.
We'll now define a hot key - let's say CTRL-c - to delete the
text in the above example. To do that we need to bind the
CTRL-C key combination to the same event handler as the
Clear button. Unfortunately there's an unexpected snag. When we
use the command option the function specified must take no
arguments. When we use the bind function to do the same job the
bound function must take one argument. Thus we need to create a
new function with a single parameter which calls
evClear. Add the following after the evClear
definition: And add the following line following the definition of the
eHello Entry widget: Run the program again and you can now clear the text by
either hitting the button or typing Ctrl-c. We could also use
bind to capture things like mouse clicks or capturing or losing
focus (that is, making the window active or inactive)
or even the window becoming visible (or hidden). See the Tkinter
documentation for more information on this. The hardest part is
usually figuring out the format of the event description!
You can report short messages to your users using a
MessageBox. This is very easy in Tk and is accomplished
using the Tkinter messagebox module functions as shown:
Here are some of the Tkinter message boxes: There are also standard dialog boxes that you can use to
get filenames or directory names from the user that look
just like the normal GUI "Open File" or "Save File" dialogs.
I won't describe them here but you will find examples in
the Tkinter reference pages under
Standard Dialogs. You will notice there that there are direct
equivalents to the EasyGui dialogs available within Tkinter, so
you only really need to use EasyGUI when working in a command
line environment and want to give a bit of GUI like polish to
your program.
It's common when programming GUI's to wrap the entire
application as a class. This begs the question, how do we fit
the widgets of a Tkinter application into this class structure?
There are two choices, we either decide to make the
application itself as a subclass of a Tkinter Frame or have a
member field store a reference to the top level window. The
latter approach is the one most commonly used in other toolkits so
that's the approach we'll use here. If you want to see the first
approach in action go back and look at the example in the
Event Driven Programming topic.
(That example also illustrates the basic use of the incredibly
versatile Tkinter Text widget as well as another example of
using bind) I will convert the example above, using an Entry field, a
Clear button and a Quit button, to an OO structure. First we
create an Application class and within the constructor assemble
the visual parts of the GUI.
We assign the resultant Frame to self.mainWindow,
thus allowing other methods of the class access to the top level
Frame. Other widgets that we may need to access (such as the
Entry field) are likewise assigned to member variables of the
application. Using this technique the event handlers become methods of
the application class and all have access to any other data
members of the application (although in this case there are
none) through the self reference. This provides seamless
integration of the GUI with the underlying application
objects: Here's the result: The result looks remarkably like the previous incarnation
although I have tweaked some of the configuration and pack
options to look more similar to the wxPython example below. Of course it's not just the main application that we can wrap
up as an object. We could create a class based around a Frame
containing a standard set of buttons and reuse that class in
building dialog windows say. We could even create whole dialogs
and use them across several projects. Or we can extend the
capabilities of the standard widgets by subclassing them - maybe
to create a button that changes colour depending on its state.
This is what has been done with the Tix module which is an
extension to Tkinter which is also part of the stand library.
Since version 3.1 Tkinter also includes some new features,
known as themed widgets which greatly improve the look of
Tkinter so that it is virtually indistinguishable from the
native OS widgets. I won't cover these here but you can read
about them on the Tcl/Tk web site.
There are many other GUI toolkits available but one of the
most popular is the wxPython toolkit which is, in turn, a wrapper
for the C++ toolkit wxWidgets. wxPython is much more typical
than Tkinter of GUI toolkits in general. It also provides more
standard functionality than Tk "out of the box" - things like
tooltips, status bars etc which have to be hand crafted in
Tkinter. We'll use wxPython to recreate the simple "Hello
World" Label and Button example above.
One major snag is that wxPython is not yet available
for Python v3! It is intended to port it but the maintainer
has not, at the time of writing, done so. That means you will
need to treat the v2 code below as a reading exercise only. I won't go through this in detail, if you do want to know
more about how wxPython works you will need to download the
package from the
wxPython website.
In general terms the toolkit defines a framework which allows
us to create windows and populate them with controls and to
bind methods to those controls. It is fully object oriented so
you should use methods rather than functions. The example looks
like this:
And it looks like this: Points to note are the use of a naming convention for the
methods that get called by the framework - OnXXXX. Also
note the EVT_XXX constants used to bind events to widgets -
there is a whole family of these. wxPython has a vast array of
widgets, far more than Tkinter, and with them you can build
quite sophisticated GUIs. Unfortunately they tend to use a
coordinate based placement scheme which becomes very tedious
after a while. It is possible to use a scheme very similar to
the Tkinter packer but its not so well documented.
Incidentally it might be of interest to note that this and the
very similar Tkinter example above have both got about the
same number of lines of executable code - Tkinter: 23, wxPython: 21.
In conclusion, if you just want a quick GUI front end to a
text based tool then Tkinter should meet your needs with minimal
effort. If you want to build full featured cross platform GUI
applications look more closely at wxPython.
Other toolkits include MFC and .NET and of course there is
the venerable curses which is a kind of text based GUI!
Many of the lessons we've learned with Tkinter apply to all of
these toolkits but each has its own characteristics and
foibles. Pick one, get to know it and enjoy the wacky world of
GUI design. Finally I should mention that many of the toolkits
do have graphical GUI builder tools, for example Qt has Blackadder
and GTK has Glade. wxPython has
Python Card
which tries to simplify the whole wxPython GUI building process.
There is also a free GUI builder,
Boa Constructor,
available although still only in Alpha release state.
There is even a GUI builder for Tkinter called
SpecTix , based on an earlier
Tcl tool for building
Tk interfaces, but capable of generating code in multiple
languages including Python. There is also an enhanced set of
widgets for Tkinter called Tix which has recently been added
to the standard Python library ( and another popular add-in
is the Python Mega-Widgets (PMW)) to fill the gap between the
basic Tkinter set and those provided by wxPython etc.
That's enough for now. This wasn't meant to be a Tkinter
reference page, just enough to get you started. See the
Tkinter section
of the Python web pages for links to other Tkinter resources.
There are also several books on using Tcl/Tk and several
Python books have chapters on Tkinter. I will however come back
to Tkinter in the case study, where I illustrate one way of
encapsulating a batch mode program in a GUI for improved usability.
If you have any questions or feedback on this page send me mail at:
alan.gauld@yahoo.co.uk
,
A Tour of Some Common Widgets
>>> from tkinter import *
>>> top = Tk()
>>> dir(top)
[....lots of stuff!...]
>>> F = Frame(top)
>>> F.pack()
>>> lHello = Label(F, text="Hello world")
>>> lHello.pack()
>>> lHello.configure(text="Goodbye")
>>> lHello['text'] = "Hello again"
>>> F.master.title("Hello")
>>> bQuit = Button(F, text="Quit", command=F.quit)
>>> bQuit.pack()
>>> top.mainloop()
from tkinter import *
# set up the window itself
top = Tk()
F = Frame(top)
F.pack()
# add the widgets
lHello = Label(F, text="Hello")
lHello.pack()
bQuit = Button(F, text="Quit", command=F.quit)
bQuit.pack()
# set the loop running
top.mainloop()
Exploring Layout
lHello.pack(side="left")
bQuit.pack(side="left")
lHello.pack(side="left", padx=10)
bQuit.pack(side='left', padx=10)
F.pack(side='left')
Controlling Appearance using Frames and the Packer
F = Frame(top, relief="sunken", border=1)
F.pack(fill="x")
F.pack(fill="both")
Adding more widgets
from tkinter import *
# create the event handler to clear the text
def evClear():
eHello.delete(0,END)
# create the top level window/frame
top = Tk()
F = Frame(top)
F.pack(fill="both")
# Now the frame with text entry
fEntry = Frame(F, border=1)
eHello = Entry(fEntry)
fEntry.pack(side="top")
eHello.pack(side="left")
# Finally the frame with the buttons.
# We'll sink this one for emphasis
fButtons = Frame(F, relief="sunken", border=1)
bClear = Button(fButtons, text="Clear Text", command=evClear)
bClear.pack(side="left", padx=5, pady=2)
bQuit = Button(fButtons, text="Quit", command=F.quit)
bQuit.pack(side="left", padx=5, pady=2)
fButtons.pack(side="top", fill="x")
# Now run the eventloop
F.mainloop()
from tkinter import *
# create the event handler to clear the text
def evClear():
lHistory['text'] = eHello.get()
eHello.delete(0,END)
# create the top level window/frame
top = Tk()
F = Frame(top)
F.pack(fill="both")
# Now the frame with text entry
fEntry = Frame(F, border=1)
eHello = Entry(fEntry)
eHello.pack(side="left")
lHistory = Label(fEntry, foreground="steelblue")
lHistory.pack(side="bottom", fill="x")
fEntry.pack(side="top")
# Finally the frame with the buttons.
# We'll sink this one for emphasis
fButtons = Frame(F, relief="sunken", border=1)
bClear = Button(fButtons, text="Clear Text", command=evClear)
bClear.pack(side="left", padx=5, pady=2)
bQuit = Button(fButtons, text="Quit", command=F.quit)
bQuit.pack(side="left", padx=5, pady=2)
fButtons.pack(side="top", fill="x")
# Now run the eventloop
F.mainloop()
Binding events - from widgets to code
def evHotKey(event):
evClear()
eHello.bind("<Control-c>",evHotKey) # the key definition is case sensitive
A Short Message
from tkinter import messagebox
messagebox.showinfo("Window Text", "A short message")
There are also error, warning, Yes/No and OK/Cancel boxes
available via different showXXX functions. They are
distinguished by different icons and buttons. The latter two
use askXXX instead of showXXX and return
a value to indicate which button the user pressed, like so:
res = messagebox.askokcancel("Which?", "Ready to stop?")
print res
Wrapping Applications as Objects
from tkinter import *
# create the event handler to clear the text
class ClearApp:
def __init__(self, parent=0):
# create the top level window/frame
self.mainWindow = Frame(parent)
self.eHello = Entry(self.mainWindow)
self.eHello.insert(0,"Hello world")
self.eHello.pack(fill="x", padx=5, pady=5)
self.eHello.bind("<Control-c>", self.evHotKey)
# Now the frame with the buttons.
fButtons = Frame(self.mainWindow, height=2)
self.bClear = Button(fButtons, text="Clear",
width=10, height=1,command=self.evClear)
self.bQuit = Button(fButtons, text="Quit",
width=10, height=1, command=self.mainWindow.quit)
self.bClear.pack(side="left", padx=15, pady=1)
self.bQuit.pack(side="right", padx=15, pady=1)
fButtons.pack(side="top", pady=2, fill="x")
self.mainWindow.pack()
self.mainWindow.master.title("Clear Application")
def evClear(self):
self.eHello.delete(0,END)
def evHotKey(self, event):
self.evClear()
# Now create the app and run the eventloop
top = Tk()
app = ClearApp(top)
top.mainloop()
An alternative - wxPython
import wx
# --- Define a custom Frame, this will become the main window ---
class HelloFrame(wx.Frame):
def __init__(self, parent, id, title, pos, size):
wx.Frame.__init__(self, parent, id, title, pos, size)
# we need a panel to get the right background
panel = wx.Panel(self)
# Now create the text and button widgets
self.tHello = wx.TextCtrl(panel, -1, "Hello world", pos=(3,3), size=(185,22))
bClear = wx.Button(panel, -1, "Clear", pos=(15, 32))
self.Bind(wx.EVT_BUTTON, self.OnClear, bClear)
bQuit = wx.Button(panel, -1, "Quit", pos=(100, 32))
self.Bind(wx.EVT_BUTTON, self.OnQuit, bQuit)
# these are our event handlers
def OnClear(self, event):
self.tHello.Clear()
def OnQuit(self, event):
self.Destroy()
# --- Define the Application Object ---
# Note that all wxPython programs MUST define an
# application class derived from wx.App
class HelloApp(wx.App):
def OnInit(self):
frame = HelloFrame(None, -1, "Hello", (200,50), (200,90) )
frame.Show(True)
self.SetTopWindow(frame)
return True
# create instance and start the event loop
HelloApp().MainLoop()
Things to remember
Previous
Next
Contents