Movatterモバイル変換


[0]ホーム

URL:


— FREE Email Series —

🐍 Python Tricks 💌

Python Tricks Dictionary Merge

🔒 No spam. Unsubscribe any time.

Browse TopicsGuided Learning Paths
Basics Intermediate Advanced
aialgorithmsapibest-practicescareercommunitydatabasesdata-sciencedata-structuresdata-vizdevopsdjangodockereditorsflaskfront-endgamedevguimachine-learningnewsnumpyprojectspythonstdlibtestingtoolsweb-devweb-scraping

Table of Contents

Recommended Course

Python GUI Programming With Tkinter

Building a Python GUI Application With Tkinter

1h 21m · 19 lessons

Python GUI Programming With Tkinter

Python GUI Programming: Your Tkinter Tutorial

byDavid AmosReading time estimate 1h 27mbasicsguistdlib

Table of Contents

Remove ads

Recommended Course

Building a Python GUI Application With Tkinter(1h 21m)

Tkinter is Python’s standard GUI framework, making it convenient for developing graphical user interfaces. As a cross-platform library, Tkinter ensures your applications appear native across Windows, macOS, and Linux. Despite criticisms of its outdated appearance, Tkinter remains a practical choice for quickly creating functional and cross-platform GUI applications.

By the end of this tutorial, you’ll understand that:

  • GUI refers to graphical user interfaces, andTkinter is Python’s built-in library for creating them.
  • Tkinter is included with most Python installations, so separate installation is often unnecessary.
  • Tkinter is still a relevant choice for building simple, cross-platform GUI applications.
  • Widgets in aTkinter application can be arranged using geometry managers like.pack(),.place(), and.grid().
  • Interactive GUI applications withTkinter are created by binding events, such as button clicks, to Python functions.

You’ll cover getting started with Tkinter, managing widgets, and creating interactive applications. Once you’ve mastered these skills by working through the exercises at the end of each section, you’ll tie everything together by building two applications. The first is atemperature converter, and the second is atext editor. It’s time to dive right in and learn how to build an application with Tkinter!

Note: This tutorial is adapted from the chapter “Graphical User Interfaces” ofPython Basics: A Practical Introduction to Python 3.

The book uses Python’s built-inIDLE editor to create and edit Python files and interact with the Python shell. In this tutorial, references to IDLE have been removed in favor of more general language.

The bulk of the material in this tutorial has been left unchanged, and you should have no problems running the example code from the editor and environment of your choice.

Free Bonus:5 Thoughts On Python Mastery, a free course for Python developers that shows you the roadmap and the mindset you’ll need to take your Python skills to the next level.

Take the Quiz: Test your knowledge with our interactive “Python GUI Programming With Tkinter” quiz. You’ll receive a score upon completion to help you track your learning progress:


Python GUI Programming With Tkinter

Interactive Quiz

Python GUI Programming With Tkinter

In this quiz, you'll test your understanding of Python GUI Programming With Tkinter, the de facto Python GUI framework. Check your knowledge of GUI programming concepts such as widgets, geometry managers, and event handlers.

Building Your First Python GUI Application With Tkinter

The foundational element of a Tkinter GUI is thewindow. Windows are the containers in which all other GUI elements live. These other GUI elements, such as text boxes, labels, and buttons, are known aswidgets. Widgets are contained inside of windows.

First, create a window that contains a single widget. Start up a newPython shell session and follow along!

Note: The code examples in this tutorial have all been tested on Windows, macOS, and Ubuntu Linux 20.04 with Python version 3.10.

If you’veinstalled Python with the official installers available forWindows andmacOS frompython.org, then you should have no problem running the sample code. You can safely skip the rest of this note and continue with the tutorial!

If you haven’t installed Python with the official installers, or there’s no official distribution for your system, then here are some tips for getting up and going.

Python on macOS with Homebrew:

The Python distribution for macOS available onHomebrew doesn’t come bundled with theTcl/Tk dependency required by Tkinter. The default system version is used instead. This version may be outdated and prevent you from importing the Tkinter module. To avoid this problem, use theofficial macOS installer.

Ubuntu Linux 20.04:

To conserve memory space, the default version of the Python interpreter that comes pre-installed on Ubuntu Linux 20.04 has no support for Tkinter. However, if you want to continue using the Python interpreter bundled with your operating system, then install the following package:

Shell
$sudoapt-getinstallpython3-tk

This installs the Python GUI Tkinter module.

Other Linux Flavors:

If you’re unable to get a working Python installation on your flavor of Linux, then you can build Python with the correct version of Tcl/Tk from the source code. For a step-by-step walk-through of this process, check out thePython 3 Installation & Setup Guide. You may also try usingpyenv to manage multiple Python versions.

With your Python shell open, the first thing you need to do is import the Python GUI Tkinter module:

Python
>>>importtkinterastk

Awindow is an instance of Tkinter’sTk class. Go ahead and create a new window and assign it to thevariablewindow:

Python
>>>window=tk.Tk()

When you execute the above code, a new window pops up on your screen. How it looks depends on your operating system:

A blank Tkinter application window on Windows 10, macOS, and Ubuntu Linux

Throughout the rest of this tutorial, you’ll see Windows screenshots.

Adding a Widget

Now that you have a window, you can add a widget. Use thetk.Label class to add some text to a window. Create aLabel widget with the text"Hello, Tkinter" and assign it to a variable calledgreeting:

Python
>>>greeting=tk.Label(text="Hello, Tkinter")

The window you created earlier doesn’t change. You just created aLabel widget, but you haven’t added it to the window yet. There are several ways to add widgets to a window. Right now, you can use theLabel widget’s.pack() method:

Python
>>>greeting.pack()

The window now looks like this:

Example "Hello, world" Tkinter application on Windows 10

When you pack a widget into a window, Tkinter sizes the window as small as it can be while still fully encompassing the widget. Now execute the following:

Python
>>>window.mainloop()

Nothing seems to happen, but notice that no new prompt appears in the shell.

window.mainloop() tells Python to run the Tkinterevent loop. This method listens for events, such as button clicks or keypresses, andblocks any code that comes after it from running until you close the window where you called the method. Go ahead and close the window you’ve created, and you’ll see a new prompt displayed in the shell.

Warning: When you work with Tkinter from aPython REPL, updates to windows are applied as each line is executed. This isnot the case when a Tkinter program is executed from a Python file!

If you don’t includewindow.mainloop() at the end of a program in a Python file, then the Tkinter application will never run, and nothing will be displayed. Alternatively, you can build your user interface incrementally in Python REPL by callingwindow.update() after each step to reflect the change.

Creating a window with Tkinter only takes a couple of lines of code. But blank windows aren’t very useful! In the next section, you’ll learn about some of the widgets available in Tkinter, and how you can customize them to meet your application’s needs.

Check Your Understanding

Expand the code blocks below to check your understanding:

Write a full Python script that creates a Tkinter window with the text"Python rocks!".

The window should look like this:

A Tkinter window containing the text "Python rocks!"

Try this exercise now.

You can expand the code block below to see a solution:

Here’s one possible solution:

Python
importtkinterastkwindow=tk.Tk()label=tk.Label(text="Python rocks!")label.pack()window.mainloop()

Keep in mind your code may look different.

When you’re ready, you can move on to the next section.

Working With Widgets

Widgets are the bread and butter of the Python GUI framework Tkinter. They’re the elements through which users interact with your program. Eachwidget in Tkinter is defined by a class. Here are some of the widgets available:

Widget ClassDescription
LabelA widget used to display text on the screen
ButtonA button that can contain text and can perform an action when clicked
EntryA text entry widget that allows only a single line of text
TextA text entry widget that allows multiline text entry
FrameA rectangular region used to group related widgets or provide padding between widgets

You’ll see how to work with each of these in the following sections, but keep in mind that Tkinter has many more widgets than those listed here. The widget’s choice gets even more complicated when you account for a whole new set ofthemed widgets. In the remaining part of this tutorial, you’re only going to use Tkinter’sclassic widgets, though.

If you’d like to learn more about the two widget types, then you can expand the collapsible section below:

It’s worth noting that there are currently two broad categories of widgets in Tkinter:

  1. Classic widgets: Available in thetkinter package, for exampletkinter.Label
  2. Themed widgets: Available in thettk submodule, for exampletkinter.ttk.Label

Tkinter’s classic widgets are highly customizable and straightforward, but they tend to appear dated or somewhat foreign on most platforms today. If you’d like to take advantage of widgets with a nativelook and feel familiar to users of a given operating system, then you might want to check out the themed widgets.

Most of the themed widgets are near drop-in replacements for their legacy counterparts, but with a more modern look. You can also use a few brand-new widgets, such as theprogress bar, which weren’t available in Tkinter before. At the same time, you’ll need to continue using some of the classic widgets that don’t have a themed alternative.

Note: Themed widgets in thetkinter.ttk module use the operating system’s native look and feel by default. However, you can change theirtheme for a customized visual appearance, such as light and dark modes. A theme is a collection of reusable style definitions, which you can think of as aCascading Style Sheet (CSS) for Tkinter.

Making the new widgets themable meant extracting most of their style information into separate objects. On the one hand, such aseparation of concerns is a desired property in the library’s design, but on the other hand, it introduces an additional abstraction layer, which makes themed widgets more difficult to style than the classic ones.

When working with regular and themed widgets in Tkinter, it’s customary to declare the following aliases for the Tkinter packages and modules:

Python
>>>importtkinterastk>>>importtkinter.ttkasttk

Aliases like this let you explicitly refer to eithertk.Label orttk.Label, for example, in one program depending on your needs:

Python
>>>tk.Label()<tkinter.Label object .!label>>>>ttk.Label()<tkinter.ttk.Label object .!label2>

However, you may sometimes find it more convenient to use a wildcard import (*) to automatically override all legacy widgets with the themed ones where possible, like so:

Python
>>>fromtkinterimport*>>>fromtkinter.ttkimport*>>>Label()<tkinter.ttk.Label object .!label>>>>Text()<tkinter.Text object .!text>

Now, you don’t have to prefix the widget’s class name with its corresponding Python module. You’ll always create a themed widget as long as it’s available, or you’ll fall back to the classic widget otherwise. The two import statements above must be placed in the specified order to have an effect. Because of that, wildcard imports are considered a bad practice, which should generally be avoided unless used consciously.

For a full list of Tkinter widgets, check outBasic Widgets andMore Widgets in theTkDocs tutorial. Even though it describes themed widgets introduced in Tcl/Tk 8.5, most of the information there should also apply to the classic widgets.

Fun Fact: Tkinter literally stands for “Tk interface” because it’s a Pythonbinding or a programming interface to theTk library in theTcl scripting language.

For now, take a closer look at theLabel widget.

Displaying Text and Images WithLabel Widgets

Label widgets are used to displaytext orimages. The text displayed by aLabel widget can’t be edited by the user. It’s for display purposes only. As you saw in the example at the beginning of this tutorial, you can create aLabel widget by instantiating theLabel class and passing astring to thetext parameter:

Python
label=tk.Label(text="Hello, Tkinter")

Label widgets display text with the default system text color and the default system text background color. These are typically black and white, respectively, but you may see different colors if you’ve changed these settings in your operating system.

You can controlLabel text and background colors using theforeground andbackground parameters:

Python
label=tk.Label(text="Hello, Tkinter",foreground="white",# Set the text color to whitebackground="black"# Set the background color to black)

There are numerous valid color names, including:

  • "red"
  • "orange"
  • "yellow"
  • "green"
  • "blue"
  • "purple"

Many of theHTML color names work with Tkinter. For a full reference, including macOS- and Windows-specific system colors that the current system theme controls, check out thecolors manual page.

You can also specify a color usinghexadecimal RGB values:

Python
label=tk.Label(text="Hello, Tkinter",background="#34A2FE")

This sets the label background to a nice, light blue color. Hexadecimal RGB values are more cryptic than named colors, but they’re also more flexible. Fortunately, there aretools available that make getting hexadecimal color codes relatively painless.

If you don’t feel like typing outforeground andbackground all the time, then you can use the shorthandfg andbg parameters to set the foreground and background colors:

Python
label=tk.Label(text="Hello, Tkinter",fg="white",bg="black")

You can also control the width and height of a label with thewidth andheight parameters:

Python
label=tk.Label(text="Hello, Tkinter",fg="white",bg="black",width=10,height=10)

Here’s what this label looks like in a window:

A Tkinter window containing a button with a black background and white text that reads "Hello, Tkinter"

It may seem strange that the label in the window isn’t square even though the width and height are both set to10. This is because the width and height are measured intext units. One horizontal text unit is determined by the width of the character0, or the number zero, in the default system font. Similarly, one vertical text unit is determined by the height of the character0.

Note: For width and height measurements, Tkinter uses text units, instead of something like inches, centimeters, or pixels, to ensure consistent behavior of the application across platforms.

Measuring units by the width of a character means that the size of a widget is relative to the default font on a user’s machine. This ensures the text fits properly in labels and buttons, no matter where the application is running.

Labels are great for displaying some text, but they don’t help you get input from a user. The next three widgets that you’ll learn about are all used to get user input.

Displaying Clickable Buttons WithButton Widgets

Button widgets are used to displayclickable buttons. You can configure them to call a function whenever they’re clicked. You’ll cover how to call functions from button clicks in the next section. For now, take a look at how to create and style a button.

There are many similarities betweenButton andLabel widgets. In many ways, a button is just a label that you can click! The same keyword arguments that you use to create and style aLabel will work withButton widgets. For example, the following code creates a button with a blue background and yellow text. It also sets the width and height to25 and5 text units, respectively:

Python
button=tk.Button(text="Click me!",width=25,height=5,bg="blue",fg="yellow",)

Here’s what the button looks like in a window:

A Tkinter window containing a button with a blue background and yellow text that reads "Click me!"

Pretty nifty! You can use the next two widgets to collect text input from a user.

Getting User Input WithEntry Widgets

When you need to get a little bit of text from a user, like a name or an email address, use anEntry widget. It’ll display asmall text box that the user can type some text into. Creating and styling anEntry widget works pretty much exactly like withLabel andButton widgets. For example, the following code creates a widget with a blue background, some yellow text, and a width of50 text units:

Python
entry=tk.Entry(fg="yellow",bg="blue",width=50)

The interesting bit aboutEntry widgets isn’t how to style them, though. It’s how to use them to getinput from a user. There are three main operations that you can perform withEntry widgets:

  1. Retrieving text with.get()
  2. Deleting text with.delete()
  3. Inserting text with.insert()

The best way to get an understanding ofEntry widgets is to create one and interact with it. Open up a Python shell and follow along with the examples in this section. First, importtkinter and create a new window:

Python
>>>importtkinterastk>>>window=tk.Tk()

Now create aLabel and anEntry widget:

Python
>>>label=tk.Label(text="Name")>>>entry=tk.Entry()

TheLabel describes what sort of text should go in theEntry widget. It doesn’t enforce any sort of requirements on theEntry, but it tells the user what your program expects them to put there. You need to.pack() the widgets into the window so that they’re visible:

Python
>>>label.pack()>>>entry.pack()

Here’s what that looks like:

A Tkinter window containing an Entry widget withe Label "Name"

Notice that Tkinter automatically centers the label above theEntry widget in the window. This is a feature of.pack(), which you’ll learn more about in later sections.

Click inside theEntry widget with your mouse and typeReal Python:

A Tkinter window containing an Entry widget with the text "Real Python"

Now you’ve got some text entered into theEntry widget, but that text hasn’t been sent to your program yet. You can use.get() to retrieve the text and assign it to a variable calledname:

Python
>>>name=entry.get()>>>name'Real Python'

You can delete text as well. This.delete() method takes an integer argument that tells Python which character to remove. For example, the code block below shows how.delete(0) deletes the first character fromEntry:

Python
>>>entry.delete(0)

The text remaining in the widget is noweal Python:

A Tkinter window containing an Entry widget with the text "eal Python"

Note that, just like Pythonstring objects, text in anEntry widget is indexed starting with0.

If you need to remove several characters from anEntry, then pass a second integer argument to.delete() indicating the index of the character where deletion should stop. For example, the following code deletes the first four letters inEntry:

Python
>>>entry.delete(0,4)

The remaining text now readsPython:

A Tkinter window containing an Entry widget with the text "Python"

Entry.delete() works just likestring slicing. The first argument determines the starting index, and the deletion continues up to butnot including the index passed as the second argument. Use the special constanttk.END for the second argument of.delete() to remove all text inEntry:

Python
>>>entry.delete(0,tk.END)

You’ll now see a blank text box:

A Tkinter window containing an Entry widget withe Label "Name"

On the opposite end of the spectrum, you can also insert text into anEntry widget:

Python
>>>entry.insert(0,"Python")

The window now looks like this:

A Tkinter window containing an Entry widget with the text "Python"

The first argument tells.insert() where to insert the text. If there’s no text inEntry, then the new text will always be inserted at the beginning of the widget, no matter what value you pass as the first argument. For example, calling.insert() with100 as the first argument instead of0, as you did above, would’ve generated the same output.

IfEntry already contains some text, then.insert() will insert the new text at the specified position and shift all existing text to the right:

Python
>>>entry.insert(0,"Real ")

The widget text now readsReal Python:

A Tkinter window containing an Entry widget with the text "Real Python"

Entry widgets are great for capturing small amounts of text from a user, but because they’re only displayed on a single line, they’re not ideal for gathering large amounts of text. That’s whereText widgets come in!

Getting Multiline User Input WithText Widgets

Text widgets are used for entering text, just likeEntry widgets. The difference is thatText widgets may containmultiple lines of text. With aText widget, a user can input a whole paragraph or even several pages of text! Just like withEntry widgets, you can perform three main operations withText widgets:

  1. Retrieve text with.get()
  2. Delete text with.delete()
  3. Insert text with.insert()

Although the method names are the same as theEntry methods, they work a bit differently. It’s time to get your hands dirty by creating aText widget and seeing what it can do.

Note: Do you still have the window from the previous section open?

If so, then you can close it by executing the following:

Python
>>>window.destroy()

You can also close it manually by clicking theClose button.

In your Python shell, create a new blank window and pack aText() widget into it:

Python
>>>window=tk.Tk()>>>text_box=tk.Text()>>>text_box.pack()

Text boxes are much larger thanEntry widgets by default. Here’s what the window created above looks like:

A Tkinter window containing a Text Box widget

Click anywhere inside the window to activate the text box. Type in the wordHello. Then pressEnter and typeWorld on the second line. The window should now look like this:

A Tkinter window containing a Text Box widget with the text "Hello World"

Just like withEntry widgets, you can retrieve the text from aText widget using.get(). However, calling.get() with no arguments doesn’t return the full text in the text box like it does forEntry widgets. It raises anexception:

Python
>>>text_box.get()Traceback (most recent call last):...TypeError:get() missing 1 required positional argument: 'index1'

Text.get() requires at least one argument. Calling.get() with a single index returns a single character. To retrieve several characters, you need to pass astart index and anend index. Indices inText widgets work differently than inEntry widgets. SinceText widgets can have several lines of text, an index must contain two pieces of information:

  1. The line number of a character
  2. The position of a character on that line

Line numbers start with1, and character positions start with0. To make an index, you create a string of the form"<line>.<char>", replacing<line> with the line number and<char> with the character number. For example,"1.0" represents the first character on the first line, and"2.3" represents the fourth character on the second line.

Use the index"1.0" to get the first letter from the text box that you created earlier:

Python
>>>text_box.get("1.0")'H'

There are five letters in the wordHello, and the character number ofo is4, since character numbers start from0, and the wordHello starts at the first position in the text box. Just like with Python string slices, in order to get the entire wordHello from the text box, the end index must be one more than the index of the last character to be read.

So, to get the wordHello from the text box, use"1.0" for the first index and"1.5" for the second index:

Python
>>>text_box.get("1.0","1.5")'Hello'

To get the wordWorld on the second line of the text box, change the line numbers in each index to2:

Python
>>>text_box.get("2.0","2.5")'World'

To get all of the text in a text box, set the starting index in"1.0" and use the specialtk.END constant for the second index:

Python
>>>text_box.get("1.0",tk.END)'Hello\nWorld\n'

Notice that text returned by.get() includes any newline characters. You can also see from this example that every line in aText widget has a newline character at the end, including the last line of text in the text box.

.delete() is used to delete characters from a text box. It works just like.delete() forEntry widgets. There are two ways to use.delete():

  1. With asingle argument
  2. Withtwo arguments

Using the single-argument version, you pass to.delete() the index of a single character to be deleted. For example, the following deletes the first character,H, from the text box:

Python
>>>text_box.delete("1.0")

The first line of text in the window now readsello:

A Tkinter window containing a Text Box widget with the text "ello World"

With the two-argument version, you pass two indices to delete a range of characters starting at the first index and up to, but not including, the second index.

For example, to delete the remainingello on the first line of the text box, use the indices"1.0" and"1.4":

Python
>>>text_box.delete("1.0","1.4")

Notice that the text is gone from the first line. This leaves a blank line followed the wordWorld on the second line:

A Tkinter window containing a Text Box widget with a blank first line and the text "World" on the second line

Even though you can’t see it, there’s still a character on the first line. It’s a newline character! You can verify this using.get():

Python
>>>text_box.get("1.0")'\n'

If you delete that character, then the rest of the contents of the text box will shift up a line:

Python
>>>text_box.delete("1.0")

Now,World is on the first line of the text box:

A Tkinter window containing a Text Box widget with the text "World"

Try to clear out the rest of the text in the text box. Set"1.0" as the start index and usetk.END for the second index:

Python
>>>text_box.delete("1.0",tk.END)

The text box is now empty:

A Tkinter window containing a Text Box widget

You can insert text into a text box using.insert():

Python
>>>text_box.insert("1.0","Hello")

This inserts the wordHello at the beginning of the text box, using the same"<line>.<column>" format used by.get() to specify the insertion position:

A Tkinter window containing a Text Box widget with the text "Hello"

Check out what happens if you try to insert the wordWorld on the second line:

Python
>>>text_box.insert("2.0","World")

Instead of inserting the text on the second line, the text is inserted at the end of the first line:

A Tkinter window containing a Text Box widget with the text "HelloWorld"

If you want to insert text onto a new line, then you need to insert a newline character manually into the string being inserted:

Python
>>>text_box.insert("2.0","\nWorld")

NowWorld is on the second line of the text box:

A Tkinter window containing a Text Box widget with the text "Hello World"

.insert() will do one of two things:

  1. Insert text at the specified position if there’s already text at or after that position.
  2. Append text to the specified line if the character number is greater than the index of the last character in the text box.

It’s usually impractical to try and keep track of what the index of the last character is. The best way to insert text at the end of aText widget is to passtk.END to the first parameter of.insert():

Python
>>>text_box.insert(tk.END,"Put me at the end!")

Don’t forget to include the newline character (\n) at the beginning of the text if you want to put it on a new line:

Python
>>>text_box.insert(tk.END,"\nPut me on a new line!")

Label,Button,Entry, andText widgets are just a few of the widgets available in Tkinter. There are several others, including widgets for checkboxes, radio buttons, scroll bars, and progress bars. For more information on all of the available widgets, see the Additional Widgets list in theAdditional Resources section.

Assigning Widgets to Frames WithFrame Widgets

In this tutorial, you’re going to work with only five widgets:

  1. Label
  2. Button
  3. Entry
  4. Text
  5. Frame

These are the four you’ve seen so far plus theFrame widget.Frame widgets are important for organizing thelayout of your widgets in an application.

Before you get into the details about laying out the visual presentation of your widgets, take a closer look at howFrame widgets work, and how you can assign other widgets to them. The following script creates a blankFrame widget and assigns it to the main application window:

Python
importtkinterastkwindow=tk.Tk()frame=tk.Frame()frame.pack()window.mainloop()

frame.pack() packs the frame into the window so that the window sizes itself as small as possible to encompass the frame. When you run the above script, you get some seriously uninteresting output:

A Tkinter window containing an empty Frame widget

An emptyFrame widget is practically invisible. Frames are best thought of ascontainers for other widgets. You can assign a widget to a frame by setting the widget’smaster attribute:

Python
frame=tk.Frame()label=tk.Label(master=frame)

To get a feel for how this works, write a script that creates twoFrame widgets calledframe_a andframe_b. In this script,frame_a contains a label with the text"I'm in Frame A", andframe_b contains the label"I'm in Frame B". Here’s one way to do this:

Python
importtkinterastkwindow=tk.Tk()frame_a=tk.Frame()frame_b=tk.Frame()label_a=tk.Label(master=frame_a,text="I'm in Frame A")label_a.pack()label_b=tk.Label(master=frame_b,text="I'm in Frame B")label_b.pack()frame_a.pack()frame_b.pack()window.mainloop()

Note thatframe_a is packed into the window beforeframe_b. The window that opens shows the label inframe_a above the label inframe_b:

A Tkinter window containg two Frame widgets stacked vertically, with the text "I'm in Frame A" in the top Frame, and "I'm in Frame B" in the bottom Frame

Now see what happens when you swap the order offrame_a.pack() andframe_b.pack():

Python
importtkinterastkwindow=tk.Tk()frame_a=tk.Frame()label_a=tk.Label(master=frame_a,text="I'm in Frame A")label_a.pack()frame_b=tk.Frame()label_b=tk.Label(master=frame_b,text="I'm in Frame B")label_b.pack()# Swap the order of `frame_a` and `frame_b`frame_b.pack()frame_a.pack()window.mainloop()

The output looks like this:

A Tkinter window containg two Frame widgets stacked vertically, with the text "I'm in Frame B" in the top Frame, and "I'm in Frame A" in the bottom Frame

Nowlabel_b is on top. Sincelabel_b is assigned toframe_b, it moves to whereverframe_b is positioned.

All four of the widget types that you’ve learned about—Label,Button,Entry, andText—have amaster attribute that’s set when you instantiate them. That way, you can control whichFrame a widget is assigned to.Frame widgets are great for organizing other widgets in a logical manner. Related widgets can be assigned to the same frame so that, if the frame is ever moved in the window, then the related widgets stay together.

Note: If you omit themaster argument when creating a new widget instance, then it’ll be placed inside of the top-level window by default.

In addition to grouping your widgets logically,Frame widgets can add a little flare to thevisual presentation of your application. Read on to see how to create various borders forFrame widgets.

Adjusting Frame Appearance With Reliefs

Frame widgets can be configured with arelief attribute that creates a border around the frame. You can setrelief to be any of the following values:

  • tk.FLAT: Has no border effect (the default value)
  • tk.SUNKEN: Creates a sunken effect
  • tk.RAISED: Creates a raised effect
  • tk.GROOVE: Creates a grooved border effect
  • tk.RIDGE: Creates a ridged effect

To apply the border effect, you must set theborderwidth attribute to a value greater than1. This attribute adjusts the width of the border in pixels. The best way to get a feel for what each effect looks like is to see them for yourself. Here’s a script that packs fiveFrame widgets into a window, each with a different value for therelief argument:

Python
 1importtkinterastk 2 3border_effects={ 4"flat":tk.FLAT, 5"sunken":tk.SUNKEN, 6"raised":tk.RAISED, 7"groove":tk.GROOVE, 8"ridge":tk.RIDGE, 9}1011window=tk.Tk()1213forrelief_name,reliefinborder_effects.items():14frame=tk.Frame(master=window,relief=relief,borderwidth=5)15frame.pack(side=tk.LEFT)16label=tk.Label(master=frame,text=relief_name)17label.pack()1819window.mainloop()

Here’s a breakdown of this script:

  • Lines 3 to 9 create adictionary whose keys are the names of the different relief effects available in Tkinter. The values are the corresponding Tkinter objects. This dictionary is assigned to theborder_effects variable.

  • Line 13 starts afor loop to loop over each item in theborder_effects dictionary.

  • Line 14 creates a newFrame widget and assigns it to thewindow object. Therelief attribute is set to the corresponding relief in theborder_effects dictionary, and theborder attribute is set to5 so that the effect is visible.

  • Line 15 packs theFrame into the window using.pack(). Theside keyword argument tells Tkinter in which direction to pack theframe objects. You’ll see more about how this works in the next section.

  • Lines 16 and 17 create aLabel widget to display the name of the relief and pack it into theframe object you just created.

The window produced by the above script looks like this:

A Tkinter window containing 5 Frame widgets, each with one of the five relief values: tk.FLAT, tk.SUNKET, tk.RAISED, tk.GROOVE, and tk.RIDGE

In this image, you can see the following effects:

  • tk.FLAT creates a frame that appears to be flat.
  • tk.SUNKEN adds a border that gives the frame the appearance of being sunken into the window.
  • tk.RAISED gives the frame a border that makes it appear to stick out from the screen.
  • tk.GROOVE adds a border that appears as a sunken groove around an otherwise flat frame.
  • tk.RIDGE gives the appearance of a raised lip around the edge of the frame.

These effects give your Python GUI Tkinter application a bit of visual appeal.

Understanding Widget Naming Conventions

When you create a widget, you can give it any name you like, as long as it’s avalid Python identifier. It’s usually a good idea to include the name of the widget class in the variable name that you assign to the widget instance. For example, if aLabel widget is used to display a user’s name, then you might name the widgetlabel_user_name. AnEntry widget used to collect a user’s age might be calledentry_age.

Note: Sometimes, you may define a new widget without assigning it to a variable. You’ll call its.pack() method directly on the same line of code:

Python
>>>tk.Label(text="Hello, Tkinter").pack()

This might be helpful when you don’t intend to refer to the widget’s instance later on. Due to automaticmemory management, Python would normallygarbage collect such unassigned objects, but Tkinter prevents that by registering every new widget internally.

When you include the widget class name in the variable name, you help yourself and anyone else who needs to read your code to understand what type of widget the variable name refers to. However, using the full name of the widget class can lead to long variable names, so you may want to adopt a shorthand for referring to each widget type. For the rest of this tutorial, you’ll use the following shorthand prefixes to name widgets:

Widget ClassVariable Name PrefixExample
Labellbllbl_name
Buttonbtnbtn_submit
Entryentent_age
Texttxttxt_notes
Framefrmfrm_address

In this section, you learned how to create a window, use widgets, and work with frames. At this point, you can make some plain windows that display messages, but you’ve yet to create a full-blown application. In the next section, you’ll learn how to control the layout of your applications using Tkinter’s powerful geometry managers.

Check Your Understanding

Expand the code block below for an exercise to check your understanding:

Write a complete script that displays anEntry widget that’s 40 text units wide and has a white background and black text. Use.insert() to display text in the widget that readsWhat is your name?.

The output window should look like this:

A Tkinter window containing an Entry widget with the text "What is your name?"

Try this exercise now.

You can expand the code block below to see a solution:

There are a couple of ways to solve this exercise. Here’s one solution that uses thebg andfg parameters to set theEntry widget’s background and foreground colors:

Python
importtkinterastkwindow=tk.Tk()entry=tk.Entry(width=40,bg="white",fg="black")entry.pack()entry.insert(0,"What is your name?")window.mainloop()

This solution is great because it explicitly sets the background and foreground colors for theEntry widget.

On most systems, the default background color for anEntry widget is white, and the default foreground color is black. So, you might be able to generate the same window with thebg andfg parameters left out:

Python
importtkinterastkwindow=tk.Tk()entry=tk.Entry(width=40)entry.pack()entry.insert(0,"What is your name?")window.mainloop()

Keep in mind your code may look different.

When you’re ready, you can move on to the next section.

Controlling Layout With Geometry Managers

Up until now, you’ve been adding widgets to windows andFrame widgets using.pack(), but you haven’t learned what exactly this method does. Let’s clear things up! Application layout in Tkinter is controlled withgeometry managers. While.pack() is an example of a geometry manager, it isn’t the only one. Tkinter has two others:

  • .place()
  • .grid()

Each window orFrame in your application can use only one geometry manager. However, different frames can use different geometry managers, even if they’re assigned to a frame or window using another geometry manager. Start by taking a closer look at.pack().

The.pack() Geometry Manager

The.pack() geometry manager uses apacking algorithm to place widgets in aFrame or window in a specified order. For a given widget, the packing algorithm has two primary steps:

  1. Compute a rectangular area called aparcel that’s just tall (or wide) enough to hold the widget and fills the remaining width (or height) in the window with blank space.
  2. Center the widget in the parcel unless a different location is specified.

.pack() is powerful, but it can be difficult to visualize. The best way to get a feel for.pack() is to look at some examples. See what happens when you.pack() threeLabel widgets into aFrame:

Python
importtkinterastkwindow=tk.Tk()frame1=tk.Frame(master=window,width=100,height=100,bg="red")frame1.pack()frame2=tk.Frame(master=window,width=50,height=50,bg="yellow")frame2.pack()frame3=tk.Frame(master=window,width=25,height=25,bg="blue")frame3.pack()window.mainloop()

.pack() places eachFrame below the previous one by default, in the order that they’re assigned to the window:

A Tkinter window with three colored squares packed vertically

EachFrame is placed at the topmost available position. Therefore, the redFrame is placed at the top of the window. Then the yellowFrame is placed just below the red one and the blueFrame just below the yellow one.

There are three invisible parcels, each containing one of the threeFrame widgets. Each parcel is as wide as the window and as tall as theFrame that it contains. Because noanchor point was specified when.pack() was called for eachFrame, they’re all centered inside of their parcels. That’s why eachFrame is centered in the window.

.pack() accepts some keyword arguments for more precisely configuring widget placement. For example, you can set thefill keyword argument to specify in whichdirection the frames should fill. The options aretk.X to fill in the horizontal direction,tk.Y to fill vertically, andtk.BOTH to fill in both directions. Here’s how you would stack the three frames so that each one fills the whole window horizontally:

Python
importtkinterastkwindow=tk.Tk()frame1=tk.Frame(master=window,height=100,bg="red")frame1.pack(fill=tk.X)frame2=tk.Frame(master=window,height=50,bg="yellow")frame2.pack(fill=tk.X)frame3=tk.Frame(master=window,height=25,bg="blue")frame3.pack(fill=tk.X)window.mainloop()

Notice that thewidth is not set on any of theFrame widgets.width is no longer necessary because each frame sets.pack() to fill horizontally, overriding any width you may set.

The window produced by this script looks like this:

A Tkinter window containing three colored frames packed vertically and expanded horizontally to fill the entire window

One of the nice things about filling the window with.pack() is that the fill isresponsive to window resizing. Try widening the window generated by the previous script to see how this works. As you widen the window, the width of the threeFrame widgets grow to fill the window:

A Tkinter window that expands horizontally with window resizing

Notice, though, that theFrame widgets don’t expand in the vertical direction.

Theside keyword argument of.pack() specifies on which side of the window the widget should be placed. These are the available options:

  • tk.TOP
  • tk.BOTTOM
  • tk.LEFT
  • tk.RIGHT

If you don’t setside, then.pack() will automatically usetk.TOP and place new widgets at the top of the window, or at the topmost portion of the window that isn’t already occupied by a widget. For example, the following script places three frames side by side from left to right and expands each frame to fill the window vertically:

Python
importtkinterastkwindow=tk.Tk()frame1=tk.Frame(master=window,width=200,height=100,bg="red")frame1.pack(fill=tk.Y,side=tk.LEFT)frame2=tk.Frame(master=window,width=100,bg="yellow")frame2.pack(fill=tk.Y,side=tk.LEFT)frame3=tk.Frame(master=window,width=50,bg="blue")frame3.pack(fill=tk.Y,side=tk.LEFT)window.mainloop()

This time, you have to specify theheight keyword argument on at least one of the frames to force the window to have some height.

The resulting window looks like this:

A Tkinter window containing three colored frames packed horizontally and expanded vertically to fill the entire window

Just like when you setfill=tk.X to make the frames responsive when you resized the window horizontally, you can setfill=tk.Y to make the frames responsive when you resize the window vertically:

A Tkinter window that expands vertically with window resizing

To make the layout truly responsive, you can set an initial size for your frames using thewidth andheight attributes. Then, set thefill keyword argument of.pack() totk.BOTH and set theexpand keyword argument toTrue:

Python
importtkinterastkwindow=tk.Tk()frame1=tk.Frame(master=window,width=200,height=100,bg="red")frame1.pack(fill=tk.BOTH,side=tk.LEFT,expand=True)frame2=tk.Frame(master=window,width=100,bg="yellow")frame2.pack(fill=tk.BOTH,side=tk.LEFT,expand=True)frame3=tk.Frame(master=window,width=50,bg="blue")frame3.pack(fill=tk.BOTH,side=tk.LEFT,expand=True)window.mainloop()

When you run the above script, you’ll see a window that initially looks the same as the one you generated in the previous example. The difference is that now you can resize the window however you want, and the frames will expand and fill the window responsively:

A Tkinter window that expands both horizontally and vertically with window resizing

Pretty cool!

The.place() Geometry Manager

You can use.place() to control theprecise location that a widget should occupy in a window orFrame. You must provide two keyword arguments,x andy, which specify the x- and y-coordinates for the top-left corner of the widget. Bothx andy are measured in pixels, not text units.

Keep in mind that theorigin, wherex andy are both0, is the top-left corner of theFrame or window. So, you can think of they argument of.place() as the number of pixels from the top of the window, and thex argument as the number of pixels from the left edge of the window.

Here’s an example of how the.place() geometry manager works:

Python
 1importtkinterastk 2 3window=tk.Tk() 4 5frame=tk.Frame(master=window,width=150,height=150) 6frame.pack() 7 8label1=tk.Label(master=frame,text="I'm at (0, 0)",bg="red") 9label1.place(x=0,y=0)1011label2=tk.Label(master=frame,text="I'm at (75, 75)",bg="yellow")12label2.place(x=75,y=75)1314window.mainloop()

Here’s how this code works:

  • Lines 5 and 6 create a newFrame widget calledframe, measuring150 pixels wide and150 pixels tall, and pack it into the window with.pack().
  • Lines 8 and 9 create a newLabel calledlabel1 with a red background and place it inframe1 at position (0, 0).
  • Lines 11 and 12 create a secondLabel calledlabel2 with a yellow background and place it inframe1 at position (75, 75).

Here’s the window that the code produces:

A Tkinter window containing two Label widgets laid out using the .place() geometry manager

Note that if you run this code on a different operating system that uses different font sizes and styles, then the second label might become partially obscured by the window’s edge. That’s why.place() isn’t used often. In addition to this, it has two main drawbacks:

  1. Layout can be difficult to manage with.place(). This is especially true if your application has lots of widgets.
  2. Layouts created with.place() aren’t responsive. They don’t change as the window is resized.

One of the main challenges of cross-platform GUI development is making layouts that look good no matter which platform they’re viewed on, and.place() is a poor choice for making responsive and cross-platform layouts.

That’s not to say you should never use.place()! In some cases, it might be just what you need. For example, if you’re creating a GUI interface for a map, then.place() might be the perfect choice to ensure widgets are placed at the correct distance from each other on the map.

.pack() is usually a better choice than.place(), but even.pack() has some downsides. The placement of widgets depends on the order in which.pack() is called, so it can be difficult to modify existing applications without fully understanding the code controlling the layout. The.grid() geometry manager solves a lot of these issues, as you’ll see in the next section.

The.grid() Geometry Manager

The geometry manager you’ll likely use most often is.grid(), which provides all the power of.pack() in a format that’s easier to understand and maintain.

.grid() works by splitting a window orFrame into rows and columns. You specify the location of a widget by calling.grid() and passing the row and column indices to therow andcolumn keyword arguments, respectively. Both row and column indices start at0, so a row index of1 and a column index of2 tells.grid() to place a widget in the third column of the second row.

The following script creates a 3 × 3 grid of frames withLabel widgets packed into them:

Python
importtkinterastkwindow=tk.Tk()foriinrange(3):forjinrange(3):frame=tk.Frame(master=window,relief=tk.RAISED,borderwidth=1)frame.grid(row=i,column=j)label=tk.Label(master=frame,text=f"Row{i}\nColumn{j}")label.pack()window.mainloop()

Here’s what the resulting window looks like:

A Tkinter window containing a 3 x 3 grid of Frame widgets with Label widgets packed into them

You’re using two geometry managers in this example. Each frame is attached towindow with the.grid() geometry manager:

Python
importtkinterastkwindow=tk.Tk()foriinrange(3):forjinrange(3):frame=tk.Frame(master=window,relief=tk.RAISED,borderwidth=1)frame.grid(row=i,column=j)label=tk.Label(master=frame,text=f"Row{i}\nColumn{j}")label.pack()window.mainloop()

Eachlabel is attached to its masterFrame with.pack():

Python
importtkinterastkwindow=tk.Tk()foriinrange(3):forjinrange(3):frame=tk.Frame(master=window,relief=tk.RAISED,borderwidth=1)frame.grid(row=i,column=j)label=tk.Label(master=frame,text=f"Row{i}\nColumn{j}")label.pack()window.mainloop()

The important thing to realize here is that even though.grid() is called on eachFrame object, the geometry manager applies to thewindow object. Similarly, the layout of eachframe is controlled with the.pack() geometry manager.

The frames in the previous example are placed tightly next to one another. To add some space around each frame, you can set the padding of each cell in the grid.Padding is just some blank space that surrounds a widget and visually sets its content apart.

The two types of padding areexternal andinternal padding. External padding adds some space around the outside of a grid cell. It’s controlled with two keyword arguments to.grid():

  1. padx adds padding in the horizontal direction.
  2. pady adds padding in the vertical direction.

Bothpadx andpady are measured in pixels, not text units, so setting both of them to the same value will create the same amount of padding in both directions. Try to add some padding around the outside of the frames from the previous example:

Python
importtkinterastkwindow=tk.Tk()foriinrange(3):forjinrange(3):frame=tk.Frame(master=window,relief=tk.RAISED,borderwidth=1)frame.grid(row=i,column=j,padx=5,pady=5)label=tk.Label(master=frame,text=f"Row{i}\nColumn{j}")label.pack()window.mainloop()

Here’s the resulting window:

A Tkinter window containing a 3 x 3 grid of Frame widgets with Label widgets packed into them. Each grid cell has 5 pixels of exterior padding.

.pack() also haspadx andpady parameters. The following code is nearly identical to the previous code, except that you add five pixels of additional padding around each label in both thex andy directions:

Python
importtkinterastkwindow=tk.Tk()foriinrange(3):forjinrange(3):frame=tk.Frame(master=window,relief=tk.RAISED,borderwidth=1)frame.grid(row=i,column=j,padx=5,pady=5)label=tk.Label(master=frame,text=f"Row{i}\nColumn{j}")label.pack(padx=5,pady=5)window.mainloop()

The extra padding around theLabel widgets gives each cell in the grid a little bit of breathing room between theFrame border and the text in the label:

A Tkinter window containing a 3 x 3 grid of Frame widgets with Label widgets packed into them. Each grid cell and Label widget has 5 pixels of exterior padding.

That looks pretty nice! But if you try and expand the window in any direction, then you’ll notice that the layout isn’t very responsive:

A Tkinter window containing a 3 x 3 grid that does not expand with window resizing

The whole grid stays at the top-left corner as the window expands.

By using.columnconfigure() and.rowconfigure() on thewindow object, you can adjust how the rows and columns of the grid grow as the window is resized. Remember, the grid is attached towindow, even though you’re calling.grid() on eachFrame widget. Both.columnconfigure() and.rowconfigure() take three essential arguments:

  1. Index: The index of the grid column or row that you want to configure or a list of indices to configure multiple rows or columns at the same time
  2. Weight: A keyword argument calledweight that determines how the column or row should respond to window resizing, relative to the other columns and rows
  3. Minimum Size: A keyword argument calledminsize that sets the minimum size of the row height or column width in pixels

weight is set to0 by default, which means that the column or row doesn’t expand as the window resizes. If every column or row is given a weight of1, then they all grow at the same rate. If one column has a weight of1 and another a weight of2, then the second column expands at twice the rate of the first. Adjust the previous script to better handle window resizing:

Python
importtkinterastkwindow=tk.Tk()foriinrange(3):window.columnconfigure(i,weight=1,minsize=75)window.rowconfigure(i,weight=1,minsize=50)forjinrange(0,3):frame=tk.Frame(master=window,relief=tk.RAISED,borderwidth=1)frame.grid(row=i,column=j,padx=5,pady=5)label=tk.Label(master=frame,text=f"Row{i}\nColumn{j}")label.pack(padx=5,pady=5)window.mainloop()

.columnconfigure() and.rowconfigure() are placed in the body of the outerfor loop. You could explicitly configure each column and row outside of thefor loop, but that would require writing an additional six lines of code.

On each iteration of the loop, thei-th column and row are configured to have a weight of1. This ensures that the row and column expand at the same rate whenever the window is resized. Theminsize argument is set to75 for each column and50 for each row. This ensures that theLabel widget always displays its text without chopping off any characters, even if the window size is extremely small.

The result is a grid layout that expands and contracts smoothly as the window is resized:

A Tkinter window containing a fully responsive 3 x 3 grid layout

Try it yourself to get a feel for how it works! Play around with theweight andminsize parameters to see how they affect the grid.

By default, widgets are centered in their grid cells. For example, the following code creates twoLabel widgets and places them in a grid with one column and two rows:

Python
importtkinterastkwindow=tk.Tk()window.columnconfigure(0,minsize=250)window.rowconfigure([0,1],minsize=100)label1=tk.Label(text="A")label1.grid(row=0,column=0)label2=tk.Label(text="B")label2.grid(row=1,column=0)window.mainloop()

Each grid cell is250 pixels wide and100 pixels tall. The labels are placed in the center of each cell, as you can see in the following figure:

A Tkinter window with grid geometry manager and custom row and column sizes

You can change the location of each label inside of the grid cell using thesticky parameter, which accepts a string containing one or more of the following letters:

  • "n" or"N" to align to the top-center part of the cell
  • "e" or"E" to align to the right-center side of the cell
  • "s" or"S" to align to the bottom-center part of the cell
  • "w" or"W" to align to the left-center side of the cell

The letters"n","s","e", and"w" come from the cardinal directions north, south, east, and west. Settingsticky to"n" on both labels in the previous code positions each label at the top-center of its grid cell:

Python
importtkinterastkwindow=tk.Tk()window.columnconfigure(0,minsize=250)window.rowconfigure([0,1],minsize=100)label1=tk.Label(text="A")label1.grid(row=0,column=0,sticky="n")label2=tk.Label(text="B")label2.grid(row=1,column=0,sticky="n")window.mainloop()

Here’s the output:

A Tkinter window with grid geometry manager and sticky set to "North"

You can combine multiple letters in a single string to position each label in the corner of its grid cell:

Python
importtkinterastkwindow=tk.Tk()window.columnconfigure(0,minsize=250)window.rowconfigure([0,1],minsize=100)label1=tk.Label(text="A")label1.grid(row=0,column=0,sticky="ne")label2=tk.Label(text="B")label2.grid(row=1,column=0,sticky="sw")window.mainloop()

In this example, thesticky parameter oflabel1 is set to"ne", which places the label at the top-right corner of its grid cell.label2 is positioned in the bottom-left corner by passing"sw" tosticky. Here’s what that looks like in the window:

A Tkinter window with grid geometry manager and sticky set to "Northeast" and "Southwest"

When a widget is positioned withsticky, the size of the widget itself is just big enough to contain any text and other contents inside of it. It won’t fill the entire grid cell. In order to fill the grid, you can specify"ns" to force the widget to fill the cell in the vertical direction, or"ew" to fill the cell in the horizontal direction. To fill the entire cell, setsticky to"nsew". The following example illustrates each of these options:

Python
importtkinterastkwindow=tk.Tk()window.rowconfigure(0,minsize=50)window.columnconfigure([0,1,2,3],minsize=50)label1=tk.Label(text="1",bg="black",fg="white")label2=tk.Label(text="2",bg="black",fg="white")label3=tk.Label(text="3",bg="black",fg="white")label4=tk.Label(text="4",bg="black",fg="white")label1.grid(row=0,column=0)label2.grid(row=0,column=1,sticky="ew")label3.grid(row=0,column=2,sticky="ns")label4.grid(row=0,column=3,sticky="nsew")window.mainloop()

Here’s what the output looks like:

A Tkinter window with grid geometry manager and sticky used to fill horizontally, vertically, and along both axes.

What the above example illustrates is that the.grid() geometry manager’ssticky parameter can be used to achieve the same effects as the.pack() geometry manager’sfill parameter. The correspondence between thesticky andfill parameters is summarized in the following table:

.grid().pack()
sticky="ns"fill=tk.Y
sticky="ew"fill=tk.X
sticky="nsew"fill=tk.BOTH

.grid() is a powerful geometry manager. It’s often easier to understand than.pack() and is much more flexible than.place(). When you’re creating new Tkinter applications, you should consider using.grid() as your primary geometry manager.

Note:.grid() offers much more flexibility than you’ve seen here. For example, you can configure cells to span multiple rows and columns. For more information, check out theGrid Geometry Manager section of theTkDocs tutorial.

Now that you’ve got the fundamentals of geometry managers down for the Python GUI framework Tkinter, the next step is to assign actions to buttons to bring your applications to life.

Check Your Understanding

Expand the code block below for an exercise to check your understanding:

Below is an image of an address entry form made with Tkinter:

An address entry form window built with Tkinter

Write a complete script that re-creates the window. You may use any geometry manager you like.

You can expand the code block below to see a solution:

There are many different ways to solve this exercise. If your solution generates a window identical to the one in the exercise statement, then congratulations! You’ve successfully solved the exercise! Below, you can look at two solutions that use the.grid() geometry manager.

One solution creates aLabel andEntry widget with the desired settings for each field:

Python
importtkinterastk# Create a new window with the title "Address Entry Form"window=tk.Tk()window.title("Address Entry Form")# Create a new frame `frm_form` to contain the Label# and Entry widgets for entering address informationfrm_form=tk.Frame(relief=tk.SUNKEN,borderwidth=3)# Pack the frame into the windowfrm_form.pack()# Create the Label and Entry widgets for "First Name"lbl_first_name=tk.Label(master=frm_form,text="First Name:")ent_first_name=tk.Entry(master=frm_form,width=50)# Use the grid geometry manager to place the Label and# Entry widgets in the first and second columns of the# first row of the gridlbl_first_name.grid(row=0,column=0,sticky="e")ent_first_name.grid(row=0,column=1)# Create the Label and Entry widgets for "Last Name"lbl_last_name=tk.Label(master=frm_form,text="Last Name:")ent_last_name=tk.Entry(master=frm_form,width=50)# Place the widgets in the second row of the gridlbl_last_name.grid(row=1,column=0,sticky="e")ent_last_name.grid(row=1,column=1)# Create the Label and Entry widgets for "Address Line 1"lbl_address1=tk.Label(master=frm_form,text="Address Line 1:")ent_address1=tk.Entry(master=frm_form,width=50)# Place the widgets in the third row of the gridlbl_address1.grid(row=2,column=0,sticky="e")ent_address1.grid(row=2,column=1)# Create the Label and Entry widgets for "Address Line 2"lbl_address2=tk.Label(master=frm_form,text="Address Line 2:")ent_address2=tk.Entry(master=frm_form,width=50)# Place the widgets in the fourth row of the gridlbl_address2.grid(row=3,column=0,sticky=tk.E)ent_address2.grid(row=3,column=1)# Create the Label and Entry widgets for "City"lbl_city=tk.Label(master=frm_form,text="City:")ent_city=tk.Entry(master=frm_form,width=50)# Place the widgets in the fifth row of the gridlbl_city.grid(row=4,column=0,sticky=tk.E)ent_city.grid(row=4,column=1)# Create the Label and Entry widgets for "State/Province"lbl_state=tk.Label(master=frm_form,text="State/Province:")ent_state=tk.Entry(master=frm_form,width=50)# Place the widgets in the sixth row of the gridlbl_state.grid(row=5,column=0,sticky=tk.E)ent_state.grid(row=5,column=1)# Create the Label and Entry widgets for "Postal Code"lbl_postal_code=tk.Label(master=frm_form,text="Postal Code:")ent_postal_code=tk.Entry(master=frm_form,width=50)# Place the widgets in the seventh row of the gridlbl_postal_code.grid(row=6,column=0,sticky=tk.E)ent_postal_code.grid(row=6,column=1)# Create the Label and Entry widgets for "Country"lbl_country=tk.Label(master=frm_form,text="Country:")ent_country=tk.Entry(master=frm_form,width=50)# Place the widgets in the eight row of the gridlbl_country.grid(row=7,column=0,sticky=tk.E)ent_country.grid(row=7,column=1)# Create a new frame `frm_buttons` to contain the# Submit and Clear buttons. This frame fills the# whole window in the horizontal direction and has# 5 pixels of horizontal and vertical padding.frm_buttons=tk.Frame()frm_buttons.pack(fill=tk.X,ipadx=5,ipady=5)# Create the "Submit" button and pack it to the# right side of `frm_buttons`btn_submit=tk.Button(master=frm_buttons,text="Submit")btn_submit.pack(side=tk.RIGHT,padx=10,ipadx=10)# Create the "Clear" button and pack it to the# right side of `frm_buttons`btn_clear=tk.Button(master=frm_buttons,text="Clear")btn_clear.pack(side=tk.RIGHT,ipadx=10)# Start the applicationwindow.mainloop()

There’s nothing wrong with this solution. It’s a bit long, but everything is very explicit. If you want to change something, then it’s clear to see exactly where to do so.

That said, the solution can be considerably shortened by recognizing that eachEntry has the same width, and that all you need for eachLabel is the text:

Python
importtkinterastk# Create a new window with the title "Address Entry Form"window=tk.Tk()window.title("Address Entry Form")# Create a new frame `frm_form` to contain the Label# and Entry widgets for entering address informationfrm_form=tk.Frame(relief=tk.SUNKEN,borderwidth=3)# Pack the frame into the windowfrm_form.pack()# List of field labelslabels=["First Name:","Last Name:","Address Line 1:","Address Line 2:","City:","State/Province:","Postal Code:","Country:",]# Loop over the list of field labelsforidx,textinenumerate(labels):# Create a Label widget with the text from the labels listlabel=tk.Label(master=frm_form,text=text)# Create an Entry widgetentry=tk.Entry(master=frm_form,width=50)# Use the grid geometry manager to place the Label and# Entry widgets in the row whose index is idxlabel.grid(row=idx,column=0,sticky="e")entry.grid(row=idx,column=1)# Create a new frame `frm_buttons` to contain the# Submit and Clear buttons. This frame fills the# whole window in the horizontal direction and has# 5 pixels of horizontal and vertical padding.frm_buttons=tk.Frame()frm_buttons.pack(fill=tk.X,ipadx=5,ipady=5)# Create the "Submit" button and pack it to the# right side of `frm_buttons`btn_submit=tk.Button(master=frm_buttons,text="Submit")btn_submit.pack(side=tk.RIGHT,padx=10,ipadx=10)# Create the "Clear" button and pack it to the# right side of `frm_buttons`btn_clear=tk.Button(master=frm_buttons,text="Clear")btn_clear.pack(side=tk.RIGHT,ipadx=10)# Start the applicationwindow.mainloop()

In this solution, alist is used to store the strings for each label in the form. They’re stored in the order that each form field should appear. Then,enumerate() gets both the index and string from each value in thelabels list.

When you’re ready, you can move on to the next section.

Making Your Applications Interactive

By now, you have a pretty good idea of how to create a window with Tkinter, add some widgets, and control the application layout. That’s great, but applications shouldn’t just look good—they actually need to do something! In this section, you’ll learn how to bring your applications to life by performing actions whenever certainevents occur.

Using Events and Event Handlers

When you create a Tkinter application, you must callwindow.mainloop() to start theevent loop. During the event loop, your application checks if an event has occurred. If so, then it’ll execute some code in response.

The event loop is provided for you with Tkinter, so you don’t have to write any code that checks for events yourself. However, you do have to write the code that will be executed in response to an event. In Tkinter, you write functions calledevent handlers for the events that you use in your application.

Note: Anevent is any action that occurs during the event loop that might trigger some behavior in the application, such as when a key or mouse button is pressed.

When an event occurs, anevent object is emitted, which means that an instance of a class representing the event is created. You don’t need to worry about instantiating these classes yourself. Tkinter will create instances of event classes for you automatically.

You’ll write your own event loop in order to better understand how Tkinter’s event loop works. That way, you can see how Tkinter’s event loop fits into your application, and which parts you need to write yourself.

Assume there’s a list calledevents that contains event objects. A new event object is automatically appended toevents every time an event occurs in your program. You don’t need to implement this updating mechanism. It just automatically happens for you in this conceptual example. Using an infinite loop, you can continually check if there are any event objects inevents:

Python
# Assume that this list gets updated automaticallyevents=[]# Run the event loopwhileTrue:# If the event list is empty, then no events have occurred# and you can skip to the next iteration of the loopifevents==[]:continue# If execution reaches this point, then there is at least one# event object in the event listevent=events[0]

Right now, the event loop that you’ve created doesn’t do anything withevent. Let’s change that. Suppose your application needs to respond to keypresses. You need to check thatevent was generated by a user pressing a key on their keyboard, and if so, passevent to an event handler function for keypresses.

Assume thatevent has a.type attribute set to the string"keypress" if the event is a keypress event object, and a.char attribute containing the character of the key that was pressed. Create a newhandle_keypress() function and update your event loop code:

Python
events=[]# Create an event handlerdefhandle_keypress(event):"""Print the character associated to the key pressed"""print(event.char)whileTrue:ifevents==[]:continueevent=events[0]# If event is a keypress event objectifevent.type=="keypress":# Call the keypress event handlerhandle_keypress(event)

When you callwindow.mainloop(), something like the above loop is run for you. This method takes care of two parts of the loop for you:

  1. It maintains alist of events that have occurred.
  2. It runs anevent handler any time a new event is added to that list.

Update your event loop to usewindow.mainloop() instead of your own event loop:

Python
importtkinterastk# Create a window objectwindow=tk.Tk()# Create an event handlerdefhandle_keypress(event):"""Print the character associated to the key pressed"""print(event.char)# Run the event loopwindow.mainloop()

.mainloop() takes care of a lot for you, but there’s something missing from the above code. How does Tkinter know when to usehandle_keypress()? Tkinter widgets have a method called.bind() for just this purpose.

Using.bind()

To call an event handler whenever an event occurs on a widget, use.bind(). The event handler is said to bebound to the event because it’s called every time the event occurs. You’ll continue with the keypress example from the previous section and use.bind() to bindhandle_keypress() to the keypress event:

Python
importtkinterastkwindow=tk.Tk()defhandle_keypress(event):"""Print the character associated to the key pressed"""print(event.char)# Bind keypress event to handle_keypress()window.bind("<Key>",handle_keypress)window.mainloop()

Here, thehandle_keypress() event handler is bound to a"<Key>" event usingwindow.bind(). Whenever a key is pressed while the application is running, your program will print the character of the key pressed.

Note: The output of the above program isnot printed in the Tkinter application window. It’s printed to thestandard output stream (stdout).

If you run the program in IDLE, then you’ll see the output in the interactive window. If you run the program from a terminal, then you should see the output in your terminal.

.bind() always takes at least two arguments:

  1. Anevent that’s represented by a string of the form"<event_name>", whereevent_name can be any of Tkinter’s events
  2. Anevent handler that’s the name of the function to be called whenever the event occurs

The event handler is bound to the widget on which.bind() is called. When the event handler is called, the event object is passed to the event handler function.

In the example above, the event handler is bound to the window itself, but you can bind an event handler to any widget in your application. For example, you can bind an event handler to aButton widget that will perform some action whenever the button is pressed:

Python
defhandle_click(event):print("The button was clicked!")button=tk.Button(text="Click me!")button.bind("<Button-1>",handle_click)

In this example, the"<Button-1>" event on thebutton widget is bound to thehandle_click event handler. The"<Button-1>" event occurs whenever the left mouse button is pressed while the mouse is over the widget. There are other events for mouse button clicks, including"<Button-2>" for the middle mouse button and"<Button-3>" for the right mouse button.

Note: For a list of commonly used events, see theEvent types section of theTkinter 8.5 reference.

You can bind any event handler to any kind of widget with.bind(), but there’s a more straightforward way to bind event handlers to button clicks using theButton widget’scommand attribute.

Usingcommand

EveryButton widget has acommand attribute that you can assign to a function. Whenever the button is pressed, the function is executed.

Take a look at an example. First, you’ll create a window with aLabel widget that holds a numeric value. You’ll put buttons on the left and right side of the label. The left button will be used to decrease the value in theLabel, and the right one will increase the value. Here’s the code for the window:

Python
 1importtkinterastk 2 3window=tk.Tk() 4 5window.rowconfigure(0,minsize=50,weight=1) 6window.columnconfigure([0,1,2],minsize=50,weight=1) 7 8btn_decrease=tk.Button(master=window,text="-") 9btn_decrease.grid(row=0,column=0,sticky="nsew")1011lbl_value=tk.Label(master=window,text="0")12lbl_value.grid(row=0,column=1)1314btn_increase=tk.Button(master=window,text="+")15btn_increase.grid(row=0,column=2,sticky="nsew")1617window.mainloop()

The window looks like this:

A Tkinter application with increase and decrease buttons that increase and decrease a counter

With the app layout defined, you can bring it to life by giving the buttons some commands. Start with the left button. When this button is pressed, it should decrease the value in the label by one. In order to do this, you first need to get answers to two questions:

  1. How do you get the text inLabel?
  2. How do you update the text inLabel?

Label widgets don’t have.get() likeEntry andText widgets do. However, you can retrieve the text from the label by accessing thetext attribute with a dictionary-style subscript notation:

Python
label=tk.Label(text="Hello")# Retrieve a label's texttext=label["text"]# Set new text for the labellabel["text"]="Good bye"

Now that you know how to get and set a label’s text, write anincrease() function that increases the value inlbl_value by one:

Python
 1importtkinterastk 2 3defincrease(): 4value=int(lbl_value["text"]) 5lbl_value["text"]=f"{value+1}" 6 7# ...

increase() gets the text fromlbl_value and converts it to an integer withint(). Then, it increases this value by one and sets the label’stext attribute to this new value.

You’ll also needdecrease() to decrease the value invalue_label by one:

Python
 5# ... 6 7defdecrease(): 8value=int(lbl_value["text"]) 9lbl_value["text"]=f"{value-1}"1011# ...

Putincrease() anddecrease() in your code just after theimport statement.

To connect the buttons to the functions, assign the function to the button’scommand attribute. You can do this when you instantiate the buttons. For example, update the two lines that instantiate the buttons to the following:

Python
14# ...1516btn_decrease=tk.Button(master=window,text="-",command=decrease)17btn_decrease.grid(row=0,column=0,sticky="nsew")1819lbl_value=tk.Label(master=window,text="0")20lbl_value.grid(row=0,column=1)2122btn_increase=tk.Button(master=window,text="+",command=increase)23btn_increase.grid(row=0,column=2,sticky="nsew")2425window.mainloop()

That’s all you need to do to bind the buttons toincrease() anddecrease() and make the program functional. Try saving your changes and running the application! Click the buttons to increase and decrease the value in the center of the window:

A counter app built with Tkinter

Here’s the full application code for your reference:

Python
importtkinterastkdefincrease():value=int(lbl_value["text"])lbl_value["text"]=f"{value+1}"defdecrease():value=int(lbl_value["text"])lbl_value["text"]=f"{value-1}"window=tk.Tk()window.rowconfigure(0,minsize=50,weight=1)window.columnconfigure([0,1,2],minsize=50,weight=1)btn_decrease=tk.Button(master=window,text="-",command=decrease)btn_decrease.grid(row=0,column=0,sticky="nsew")lbl_value=tk.Label(master=window,text="0")lbl_value.grid(row=0,column=1)btn_increase=tk.Button(master=window,text="+",command=increase)btn_increase.grid(row=0,column=2,sticky="nsew")window.mainloop()

This app isn’t particularly useful, but the skills you learned here apply to every app you’ll make:

  • Usewidgets to create the components of the user interface.
  • Usegeometry managers to control the layout of the application.
  • Writeevent handlers that interact with various components to capture and transform user input.

In the next two sections, you’ll build more useful apps. First, you’ll build a temperature converter that converts a temperature value from Fahrenheit to Celsius. After that, you’ll build a text editor that can open, edit, and save text files!

Check Your Understanding

Expand the code block below for an exercise to check your understanding:

Write a program that simulates rolling a six-sided die. There should be one button with the textRoll. When the user clicks the button, a random integer from1 to6 should be displayed.

Hint: You can generate a random number usingrandint() in therandom module. If you’re not familiar with therandom module, then check outGenerating Random Data in Python (Guide) for more information.

The application window should look something like this:

A Tkinter application with a "Roll" button that produces a random number between 1 and 6

Try this exercise now.

You can expand the code block below to see a solution:

Here’s one possible solution:

Python
importrandomimporttkinterastkdefroll():lbl_result["text"]=str(random.randint(1,6))window=tk.Tk()window.columnconfigure(0,minsize=150)window.rowconfigure([0,1],minsize=50)btn_roll=tk.Button(text="Roll",command=roll)lbl_result=tk.Label()btn_roll.grid(row=0,column=0,sticky="nsew")lbl_result.grid(row=1,column=0)window.mainloop()

Keep in mind that your code may look different.

When you’re ready, you can move on to the next section.

Building a Temperature Converter (Example App)

In this section, you’ll build atemperature converter application that allows the user to input temperature in degrees Fahrenheit and push a button to convert that temperature to degrees Celsius. You’ll walk through the code step by step. You can also find the full source code at the end of this section for your reference.

Note: To get the most out of this section, follow along in aPython shell.

Before you start coding, you’ll first design the app. You need three elements:

  1. Entry: A widget calledent_temperature for entering the Fahrenheit value
  2. Label: A widget calledlbl_result to display the Celsius result
  3. Button: A widget calledbtn_convert that reads the value from theEntry widget, converts it from Fahrenheit to Celsius, and sets the text of theLabel widget to the result when clicked

You can arrange these in a grid with a single row and one column for each widget. That gets you a minimally working application, but it isn’t very user-friendly. Everything needs to havelabels.

You’ll put a label directly to the right of theent_temperature widget containing the Fahrenheit symbol (℉) so that the user knows that the valueent_temperature should be in degrees Fahrenheit. To do this, set the label text to"\N{DEGREE FAHRENHEIT}", which uses Python’s namedUnicode character support to display the Fahrenheit symbol.

You can givebtn_convert a little flair by setting its text to the value"\N{RIGHTWARDS BLACK ARROW}", which displays a black arrow pointing to the right. You’ll also make sure thatlbl_result always has the Celsius symbol (℃) following the label text"\N{DEGREE CELSIUS}" to indicate that the result is in degrees Celsius. Here’s what the final window will look like:

A temperature conversion application built with Tkinter

Now that you know what widgets you need and what the window is going to look like, you can start coding it up! First, importtkinter and create a new window:

Python
 1importtkinterastk 2 3window=tk.Tk() 4window.title("Temperature Converter") 5window.resizable(width=False,height=False)

window.title() sets the title of an existing window, whilewindow.resizable() with both arguments set toFalse makes the window have a fixed size. When you finally run this application, the window will have the textTemperature Converter in its title bar. Next, create theent_temperature widget with a label calledlbl_temp and assign both to aFrame widget calledfrm_entry:

Python
 5# ... 6 7frm_entry=tk.Frame(master=window) 8ent_temperature=tk.Entry(master=frm_entry,width=10) 9lbl_temp=tk.Label(master=frm_entry,text="\N{DEGREE FAHRENHEIT}")

The user will enter the Fahrenheit value inent_temperature, andlbl_temp is used to labelent_temperature with the Fahrenheit symbol. Thefrm_entry container groupsent_temperature andlbl_temp together.

You wantlbl_temp to be placed directly to the right ofent_temperature. You can lay them out infrm_entry using the.grid() geometry manager with one row and two columns:

Python
 9# ...1011ent_temperature.grid(row=0,column=0,sticky="e")12lbl_temp.grid(row=0,column=1,sticky="w")

You’ve set thesticky parameter to"e" forent_temperature so that it always sticks to the rightmost edge of its grid cell. You also setsticky to"w" forlbl_temp to keep it stuck to the leftmost edge of its grid cell. This ensures thatlbl_temp is always located immediately to the right ofent_temperature.

Now, make thebtn_convert and thelbl_result for converting the temperature entered intoent_temperature and displaying the results:

Python
12# ...1314btn_convert=tk.Button(15master=window,16text="\N{RIGHTWARDS BLACK ARROW}"17)18lbl_result=tk.Label(master=window,text="\N{DEGREE CELSIUS}")

Likefrm_entry, bothbtn_convert andlbl_result are assigned towindow. Together, these three widgets make up the three cells in the main application grid. Use.grid() to go ahead and lay them out now:

Python
18# ...1920frm_entry.grid(row=0,column=0,padx=10)21btn_convert.grid(row=0,column=1,pady=10)22lbl_result.grid(row=0,column=2,padx=10)

Finally, run the application:

Python
22# ...2324window.mainloop()

That looks great! But the button doesn’t do anything just yet. At the top of your script file, just below theimport line, add a function calledfahrenheit_to_celsius():

Python
 1importtkinterastk 2 3deffahrenheit_to_celsius(): 4"""Convert the value for Fahrenheit to Celsius and insert the 5    result into lbl_result. 6    """ 7fahrenheit=ent_temperature.get() 8celsius=(5/9)*(float(fahrenheit)-32) 9lbl_result["text"]=f"{round(celsius,2)}\N{DEGREE CELSIUS}"1011# ...

This function reads the value froment_temperature, converts it from Fahrenheit to Celsius, and then displays the result inlbl_result.

Now go down to the line where you definebtn_convert and set itscommand parameter tofahrenheit_to_celsius:

Python
20# ...2122btn_convert=tk.Button(23master=window,24text="\N{RIGHTWARDS BLACK ARROW}",25command=fahrenheit_to_celsius# <--- Add this line26)2728# ...

That’s it! You’ve created a fully functional temperature converter app in just twenty-six lines of code! Pretty cool, right?

You can expand the code block below to see the full script:

Here’s the full script for your reference:

Python
importtkinterastkdeffahrenheit_to_celsius():"""Convert the value for Fahrenheit to Celsius and insert the    result into lbl_result.    """fahrenheit=ent_temperature.get()celsius=(5/9)*(float(fahrenheit)-32)lbl_result["text"]=f"{round(celsius,2)}\N{DEGREE CELSIUS}"# Set up the windowwindow=tk.Tk()window.title("Temperature Converter")window.resizable(width=False,height=False)# Create the Fahrenheit entry frame with an Entry# widget and label in itfrm_entry=tk.Frame(master=window)ent_temperature=tk.Entry(master=frm_entry,width=10)lbl_temp=tk.Label(master=frm_entry,text="\N{DEGREE FAHRENHEIT}")# Layout the temperature Entry and Label in frm_entry# using the .grid() geometry managerent_temperature.grid(row=0,column=0,sticky="e")lbl_temp.grid(row=0,column=1,sticky="w")# Create the conversion Button and result display Labelbtn_convert=tk.Button(master=window,text="\N{RIGHTWARDS BLACK ARROW}",command=fahrenheit_to_celsius)lbl_result=tk.Label(master=window,text="\N{DEGREE CELSIUS}")# Set up the layout using the .grid() geometry managerfrm_entry.grid(row=0,column=0,padx=10)btn_convert.grid(row=0,column=1,pady=10)lbl_result.grid(row=0,column=2,padx=10)# Run the applicationwindow.mainloop()

It’s time to kick things up a notch! Read on to learn how to build a text editor.

Building a Text Editor (Example App)

In this section, you’ll build atext editor application that can create, open, edit, and save text files. There are three essential elements in the application:

  1. AButton widget calledbtn_open for opening a file for editing
  2. AButton widget calledbtn_save for saving a file
  3. ATextBox widget calledtxt_edit for creating and editing the text file

The three widgets will be arranged so that the two buttons are on the left-hand side of the window, and the text box is on the right-hand side. The whole window should have a minimum height of 800 pixels, andtxt_edit should have a minimum width of 800 pixels. The whole layout should be responsive so that if the window is resized, thentxt_edit is resized as well. The width of the frame holding the buttons should not change, however.

Here’s a sketch of how the window will look:

A design sketch for a text editor application

You can achieve the desired layout using the.grid() geometry manager. The layout contains a single row and two columns:

  1. A narrow column on the left for the buttons
  2. A wider column on the right for the text box

To set the minimum sizes for the window andtxt_edit, you can set theminsize parameters of the window methods.rowconfigure() and.columnconfigure() to800. To handle resizing, you can set theweight parameters of these methods to1.

In order to get both buttons into the same column, you’ll need to create aFrame widget calledfrm_buttons. According to the sketch, the two buttons should be stacked vertically inside of this frame, withbtn_open on top. You can do that with either the.grid() or.pack() geometry manager. For now, you’ll stick with.grid() since it’s a little easier to work with.

Now that you have a plan, you can start coding the application. The first step is to create all of the widgets you need:

Python
 1importtkinterastk 2 3window=tk.Tk() 4window.title("Simple Text Editor") 5 6window.rowconfigure(0,minsize=800,weight=1) 7window.columnconfigure(1,minsize=800,weight=1) 8 9txt_edit=tk.Text(window)10frm_buttons=tk.Frame(window,relief=tk.RAISED,bd=2)11btn_open=tk.Button(frm_buttons,text="Open")12btn_save=tk.Button(frm_buttons,text="Save As...")

Here’s a breakdown of this code:

  • Line 1 importstkinter.
  • Lines 3 and 4 create a new window with the title"Simple Text Editor".
  • Lines 6 and 7 set the row and column configurations.
  • Lines 9 to 12 create the four widgets you’ll need for the text box, the frame, and the open and save buttons.

Take a look at line 6 more closely. Theminsize parameter of.rowconfigure() is set to800, andweight is set to1:

Python
window.rowconfigure(0,minsize=800,weight=1)

The first argument is0, which sets the height of the first row to800 pixels and makes sure that the height of the row grows proportionally to the height of the window. There’s only one row in the application layout, so these settings apply to the entire window.

Let’s also take a closer look at line 7. Here, you use.columnconfigure() to set thewidth andweight attributes of the column with index1 to800 and1, respectively:

Python
window.columnconfigure(1,minsize=800,weight=1)

Remember, row and column indices are zero-based, so these settings apply only to the second column. By configuring just the second column, the text box will expand and contract naturally when the window is resized, while the column containing the buttons will remain at a fixed width.

Now you can work on the application layout. First, assign the two buttons to thefrm_buttons frame using the.grid() geometry manager:

Python
12# ...1314btn_open.grid(row=0,column=0,sticky="ew",padx=5,pady=5)15btn_save.grid(row=1,column=0,sticky="ew",padx=5)

These two lines of codecreate a grid with two rows and one column in thefrm_buttons frame since bothbtn_open andbtn_save have theirmaster attribute set tofrm_buttons.btn_open is put in the first row andbtn_save in the second row so thatbtn_open appears abovebtn_save in the layout, just you planned in your sketch.

Bothbtn_open andbtn_save have theirsticky attributes set to"ew", which forces the buttons toexpand horizontally in both directions and fill the entire frame. This ensures that both buttons are the same size.

You place five pixels ofpadding around each button by setting thepadx andpady parameters to5. Onlybtn_open has vertical padding. Since it’s on top, the vertical padding offsets the button down from the top of the window a bit and makes sure that there’s a small gap between it andbtn_save.

Now thatfrm_buttons is laid out and ready to go, you can set up the grid layout for the rest of the window:

Python
15# ...1617frm_buttons.grid(row=0,column=0,sticky="ns")18txt_edit.grid(row=0,column=1,sticky="nsew")

These two lines of codecreate a grid with one row and two columns forwindow. You placefrm_buttons in the first column andtxt_edit in the second column so thatfrm_buttons appears to the left oftxt_edit in the window layout.

Thesticky parameter forfrm_buttons is set to"ns", which forces the whole frame toexpand vertically and fill the entire height of its column.txt_edit fills its entire grid cell because you set itssticky parameter to"nsew", which forces it toexpand in every direction.

Now that the application layout is complete, addwindow.mainloop() to the bottom of the program and save and run the file:

Python
18# ...1920window.mainloop()

The following window is displayed:

A text editor application made with Tkinter

That looks great! But it doesn’t do anything just yet, so you need to start writing the commands for the buttons.btn_open needs to show afile open dialog and allow the user to select a file. It then needs toopen that file and set the text oftxt_edit to the contents of the file. Here’s anopen_file() function that does just this:

Python
 1importtkinterastk 2 3defopen_file(): 4"""Open a file for editing.""" 5filepath=askopenfilename( 6filetypes=[("Text Files","*.txt"),("All Files","*.*")] 7) 8ifnotfilepath: 9return10txt_edit.delete("1.0",tk.END)11withopen(filepath,mode="r",encoding="utf-8")asinput_file:12text=input_file.read()13txt_edit.insert(tk.END,text)14window.title(f"Simple Text Editor -{filepath}")1516# ...

Here’s a breakdown of this function:

  • Lines 5 to 7 use theaskopenfilename() dialog from thetkinter.filedialog module to display a file open dialog and store the selected file path tofilepath.
  • Lines 8 and 9 check to see if the user closes the dialog box or clicks theCancel button. If so, thenfilepath will beNone, and the function willreturn without executing any of the code to read the file and set the text oftxt_edit.
  • Line 10 clears the current contents oftxt_edit using.delete().
  • Lines 11 and 12 open the selected file and.read() its contents before storing thetext as a string.
  • Line 13 assigns the stringtext totxt_edit using.insert().
  • Line 14 sets the title of the window so that it contains the path of the open file.

Now you can update the program so thatbtn_open callsopen_file() whenever it’s clicked. There are a few things that you need to do to update the program. First, importaskopenfilename() fromtkinter.filedialog by adding the following import to the top of your program:

Python
 1importtkinterastk 2fromtkinter.filedialogimportaskopenfilename 3 4# ...

Next, set thecommand attribute ofbtn_opn toopen_file:

Python
 1importtkinterastk 2fromtkinter.filedialogimportaskopenfilename 3 4defopen_file(): 5"""Open a file for editing.""" 6filepath=askopenfilename( 7filetypes=[("Text Files","*.txt"),("All Files","*.*")] 8) 9ifnotfilepath:10return11txt_edit.delete("1.0",tk.END)12withopen(filepath,mode="r",encoding="utf-8")asinput_file:13text=input_file.read()14txt_edit.insert(tk.END,text)15window.title(f"Simple Text Editor -{filepath}")1617window=tk.Tk()18window.title("Simple Text Editor")1920window.rowconfigure(0,minsize=800,weight=1)21window.columnconfigure(1,minsize=800,weight=1)2223txt_edit=tk.Text(window)24frm_buttons=tk.Frame(window,relief=tk.RAISED,bd=2)25btn_open=tk.Button(frm_buttons,text="Open",command=open_file)26btn_save=tk.Button(frm_buttons,text="Save As...")2728# ...

Save the file and run it to check that everything is working. Then try opening a text file!

Withbtn_open working, it’s time to work on the function forbtn_save. This needs to open asave file dialog box so that the user can choose where they would like to save the file. You’ll use theasksaveasfilename() dialog in thetkinter.filedialog module for this. This function also needs to extract the text currently intxt_edit and write this to a file at the selected location. Here’s a function that does just this:

Python
15# ...1617defsave_file():18"""Save the current file as a new file."""19filepath=asksaveasfilename(20defaultextension=".txt",21filetypes=[("Text Files","*.txt"),("All Files","*.*")],22)23ifnotfilepath:24return25withopen(filepath,mode="w",encoding="utf-8")asoutput_file:26text=txt_edit.get("1.0",tk.END)27output_file.write(text)28window.title(f"Simple Text Editor -{filepath}")2930# ...

Here’s how this code works:

  • Lines 19 to 22 use theasksaveasfilename() dialog box to get the desired save location from the user. The selected file path is stored in thefilepath variable.
  • Lines 23 and 24 check to see if the user closes the dialog box or clicks theCancel button. If so, thenfilepath will beNone, and the function will return without executing any of the code to save the text to a file.
  • Line 25 creates a new file at the selected file path.
  • Line 26 extracts the text fromtxt_edit with.get() method and assigns it to the variabletext.
  • Line 27 writestext to the output file.
  • Line 28 updates the title of the window so that the new file path is displayed in the window title.

Now you can update the program so thatbtn_save callssave_file() when it’s clicked. Again, there are a few things you need to do in order to update the program. First, importasksaveasfilename() fromtkinter.filedialog by updating the import at the top of your script, like so:

Python
 1importtkinterastk 2fromtkinter.filedialogimportaskopenfilename,asksaveasfilename 3 4# ...

Finally, set thecommand attribute ofbtn_save tosave_file:

Python
28# ...2930window=tk.Tk()31window.title("Simple Text Editor")3233window.rowconfigure(0,minsize=800,weight=1)34window.columnconfigure(1,minsize=800,weight=1)3536txt_edit=tk.Text(window)37frm_buttons=tk.Frame(window,relief=tk.RAISED,bd=2)38btn_open=tk.Button(frm_buttons,text="Open",command=open_file)39btn_save=tk.Button(frm_buttons,text="Save As...",command=save_file)4041btn_open.grid(row=0,column=0,sticky="ew",padx=5,pady=5)42btn_save.grid(row=1,column=0,sticky="ew",padx=5)4344frm_buttons.grid(row=0,column=0,sticky="ns")45txt_edit.grid(row=0,column=1,sticky="nsew")4647window.mainloop()

Save the file and run it. You’ve now got a minimal yet fully functional text editor!

You can expand the code block below to see the full script:

Here’s the full script for your reference:

Python
importtkinterastkfromtkinter.filedialogimportaskopenfilename,asksaveasfilenamedefopen_file():"""Open a file for editing."""filepath=askopenfilename(filetypes=[("Text Files","*.txt"),("All Files","*.*")])ifnotfilepath:returntxt_edit.delete("1.0",tk.END)withopen(filepath,mode="r",encoding="utf-8")asinput_file:text=input_file.read()txt_edit.insert(tk.END,text)window.title(f"Simple Text Editor -{filepath}")defsave_file():"""Save the current file as a new file."""filepath=asksaveasfilename(defaultextension=".txt",filetypes=[("Text Files","*.txt"),("All Files","*.*")],)ifnotfilepath:returnwithopen(filepath,mode="w",encoding="utf-8")asoutput_file:text=txt_edit.get("1.0",tk.END)output_file.write(text)window.title(f"Simple Text Editor -{filepath}")window=tk.Tk()window.title("Simple Text Editor")window.rowconfigure(0,minsize=800,weight=1)window.columnconfigure(1,minsize=800,weight=1)txt_edit=tk.Text(window)frm_buttons=tk.Frame(window,relief=tk.RAISED,bd=2)btn_open=tk.Button(frm_buttons,text="Open",command=open_file)btn_save=tk.Button(frm_buttons,text="Save As...",command=save_file)btn_open.grid(row=0,column=0,sticky="ew",padx=5,pady=5)btn_save.grid(row=1,column=0,sticky="ew",padx=5)frm_buttons.grid(row=0,column=0,sticky="ns")txt_edit.grid(row=0,column=1,sticky="nsew")window.mainloop()

You’ve now built two GUI applications in Python and applied many of the skills that you’ve learned throughout this tutorial. That’s no small achievement, so take some time to feel good about what you’ve done. You’re now ready to tackle some applications on your own!

Conclusion

In this tutorial, you learned how to get started with Python GUI programming.Tkinter is a compelling choice for a Python GUI framework because it’s built into the Python standard library, and it’s relatively painless to make applications with this framework.

Throughout this tutorial, you’ve learned several important Tkinter concepts:

  • How to work withwidgets
  • How to control your application layout withgeometry managers
  • How to make your applicationsinteractive
  • How to use five basic Tkinterwidgets:Label,Button,Entry,Text, andFrame

Now that you’ve mastered the foundations of Python GUI programming with Tkinter, the next step is to build some of your own applications. What will you create? Share your fun projects down in the comments below!

Additional Resources

In this tutorial, you touched on just the foundations of creating Python GUI applications with Tkinter. There are a number of additional topics that aren’t covered here. In this section, you’ll find some of the best resources available to help you continue on your journey.

Tkinter References

Here are some official resources to check out:

  • Theofficial Python Tkinter reference documentation covers Python’s Tkinter module in moderate depth. It’s written for more advanced Python developers and isn’t the best resource for beginners.
  • Tkinter 8.5 reference: a GUI for Python is an extensive reference covering the majority of the Tkinter module. It’s exhaustive, but it’s written in the reference style without commentary or examples.
  • TheTk Commands reference is the definitive guide to commands in the Tk library. It’s written for the Tcl language, but it answers a lot of questions about why things work the way they do in Tkinter.

Additional Widgets

In this tutorial, you learned about theLabel,Button,Entry,Text, andFrame widgets. There are several other widgets in Tkinter, all of which are essential for building real-world applications. Here are some resources to continue learning about widgets:

  • TheTkDocs Tkinter Tutorial is a fairly comprehensive guide for Tk, the underlying code library used by Tkinter. Examples are presented in Python, Ruby, Perl, and Tcl. You can find several examples of widgets beyond those covered here in two sections:
  • The official Python docs cover additional widgets:

Application Distribution

Once you’ve created an application with Tkinter, you probably want to distribute it to your colleagues and friends. Here are some tutorials to get you going with that process:

Other GUI Frameworks

Tkinter isn’t your only choice for a Python GUI framework. If Tkinter doesn’t meet the needs of your project, then here are some other frameworks to consider:

Frequently Asked Questions

Now that you have some experience with GUI programming using Tkinter, you can use the questions and answers below to check your understanding and recap what you’ve learned.

These FAQs are related to the most important concepts you’ve covered in this tutorial. Click theShow/Hide toggle beside each question to reveal the answer.

Tkinter is the standard GUI (Graphical User Interface) toolkit for Python included in Python’s standard library. Tkinter is used to create graphical applications that can run on various operating systems like Windows, macOS, and Linux. It allows developers to create applications with elements such as buttons, text boxes, and menus.

To create a basic window using Tkinter, you need to import thetkinter module, create aTk instance, and then call themainloop() method. Themainloop() method starts the event loop and keeps the window open.

Geometry managers in Tkinter control the placement of widgets in a window. The three primary geometry managers are.pack(),.grid(), and.place().

.pack() arranges widgets in blocks before placing them in the parent widget,.grid() places widgets in a table-like structure based on rows and columns, and.place() positions widgets at an exact location you specify.

To make a Tkinter application interactive, you bind events to widgets. This is often done using thecommand attribute for buttons or the.bind() method for other events like keypresses or mouse clicks.

Common widgets in Tkinter areLabel, used to display text or images,Button, a clickable button that can trigger a function,Entry, a single-line text entry widget for user input,Text, a multiline text entry widget, andFrame, a container for organizing other widgets.

Take the Quiz: Test your knowledge with our interactive “Python GUI Programming With Tkinter” quiz. You’ll receive a score upon completion to help you track your learning progress:


Python GUI Programming With Tkinter

Interactive Quiz

Python GUI Programming With Tkinter

In this quiz, you'll test your understanding of Python GUI Programming With Tkinter, the de facto Python GUI framework. Check your knowledge of GUI programming concepts such as widgets, geometry managers, and event handlers.

Recommended Course

Building a Python GUI Application With Tkinter(1h 21m)

🐍 Python Tricks 💌

Get a short & sweetPython Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

AboutDavid Amos

David is a writer, programmer, and mathematician passionate about exploring mathematics through code.

» More about David

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

MasterReal-World Python Skills With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

MasterReal-World Python Skills
With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Rate this article:

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students.Get tips for asking good questions andget answers to common questions in our support portal.


Looking for a real-time conversation? Visit theReal Python Community Chat or join the next“Office Hours” Live Q&A Session. Happy Pythoning!

Keep Learning

Related Topics:basicsguistdlib

Related Learning Paths:

Related Courses:

Related Tutorials:

Keep reading Real Python by creating a free account or signing in:

Already have an account?Sign-In

Almost there! Complete this form and click the button below to gain instant access:

Python Logo

5 Thoughts On Python Mastery

🔒 No spam. We take your privacy seriously.


[8]ページ先頭

©2009-2026 Movatter.jp