Skip to content

bpo-42560: rewrite of Tkinter docs "life preserver" #27842

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
321 changes: 176 additions & 145 deletions Doc/library/tkinter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -189,210 +189,241 @@ Other modules that provide Tk support include:
Tkinter Life Preserver
----------------------

.. sectionauthor:: Matt Conway


This section is not designed to be an exhaustive tutorial on either Tk or
Tkinter. Rather, it is intended as a stop gap, providing some introductory
orientation on the system.
Tkinter. For that, refer to one of the external resources noted earlier.
Instead, this section provides a very quick orientation to what a Tkinter
application looks like, identifies foundational Tk concepts, and
explains how the Tkinter wrapper is structured.

Credits:
The remainder of this section will help you to identify the classes,
methods, and options you'll need in your Tkinter application, and where to
find more detailed documentation on them, including in the official Tcl/Tk
reference manual.

* Tk was written by John Ousterhout while at Berkeley.

* Tkinter was written by Steen Lumholt and Guido van Rossum.
A Hello World Program
^^^^^^^^^^^^^^^^^^^^^

* This Life Preserver was written by Matt Conway at the University of Virginia.
We'll start by walking through a "Hello World" application in Tkinter. This
isn't the smallest one we could write, but has enough to illustrate some
key concepts you'll need to know.

* The HTML rendering, and some liberal editing, was produced from a FrameMaker
version by Ken Manheimer.
::

* Fredrik Lundh elaborated and revised the class interface descriptions, to get
them current with Tk 4.2.
from tkinter import *
from tkinter import ttk
root = Tk()
frm = ttk.Frame(root, padding=10)
frm.grid()
ttk.Label(frm, text="Hello World!").grid(column=0, row=0)
ttk.Button(frm, text="Quit", command=root.destroy).grid(column=1, row=0)
root.mainloop()

* Mike Clarkson converted the documentation to LaTeX, and compiled the User
Interface chapter of the reference manual.

After the imports, the next line creates an instance of the :class:`Tk` class,
which initializes Tk and creates its associated Tcl interpreter. It also
creates a toplevel window, known as the root window, which serves as the main
window of the application.

How To Use This Section
^^^^^^^^^^^^^^^^^^^^^^^
The following line creates a frame widget, which in this case will contain
a label and a button we'll create next. The frame is fit inside the root
window.

This section is designed in two parts: the first half (roughly) covers
background material, while the second half can be taken to the keyboard as a
handy reference.
The next line creates a label widget holding a static text string. The
:meth:`grid` method is used to specify the relative layout (position) of the
label within its containing frame widget, similar to how tables in HTML work.

When trying to answer questions of the form "how do I do blah", it is often best
to find out how to do "blah" in straight Tk, and then convert this back into the
corresponding :mod:`tkinter` call. Python programmers can often guess at the
correct Python command by looking at the Tk documentation. This means that in
order to use Tkinter, you will have to know a little bit about Tk. This document
can't fulfill that role, so the best we can do is point you to the best
documentation that exists. Here are some hints:
A button widget is then created, and placed to the right of the label. When
pressed, it will call the :meth:`destroy` method of the root window.

* The authors strongly suggest getting a copy of the Tk man pages.
Specifically, the man pages in the ``manN`` directory are most useful.
The ``man3`` man pages describe the C interface to the Tk library and thus
are not especially helpful for script writers.
Finally, the :meth:`mainloop` method puts everything on the display, and
responds to user input until the program terminates.

* Addison-Wesley publishes a book called Tcl and the Tk Toolkit by John
Ousterhout (ISBN 0-201-63337-X) which is a good introduction to Tcl and Tk for
the novice. The book is not exhaustive, and for many details it defers to the
man pages.

* :file:`tkinter/__init__.py` is a last resort for most, but can be a good
place to go when nothing else makes sense.

Important Tk Concepts
^^^^^^^^^^^^^^^^^^^^^

A Simple Hello World Program
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Even this simple program illustrates the following key Tk concepts:

::
widgets
A Tkinter user interface is made up of individual *widgets*. Each widget is
represented as a Python object, instantiated from classes like
:class:`ttk.Frame`, :class:`ttk.Label`, and :class:`ttk.Button`.

import tkinter as tk
widget hierarchy
Widgets are arranged in a *hierarchy*. The label and button were contained
within a frame, which in turn was contained within the root window. When
creating each *child* widget, its *parent* widget is passed as the first
argument to the widget constructor.

class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.pack()
self.create_widgets()
configuration options
Widgets have *configuration options*, which modify their appearance and
behavior, such as the text to display in a label or button. Different
classes of widgets will have different sets of options.

def create_widgets(self):
self.hi_there = tk.Button(self)
self.hi_there["text"] = "Hello World\n(click me)"
self.hi_there["command"] = self.say_hi
self.hi_there.pack(side="top")
geometry management
Widgets aren't automatically added to the user interface when they are
created. A *geometry manager* like ``grid`` controls where in the
user interface they are placed.

self.quit = tk.Button(self, text="QUIT", fg="red",
command=self.master.destroy)
self.quit.pack(side="bottom")
event loop
Tkinter reacts to user input, changes from your program, and even refreshes
the display only when actively running an *event loop*. If your program
isn't running the event loop, your user interface won't update.

def say_hi(self):
print("hi there, everyone!")

root = tk.Tk()
app = Application(master=root)
app.mainloop()
Understanding How Tkinter Wraps Tcl/Tk
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

When your application uses Tkinter's classes and methods, internally Tkinter
is assembling strings representing Tcl/Tk commands, and executing those
commands in the Tcl interpreter attached to your applicaton's :class:`Tk`
instance.

A (Very) Quick Look at Tcl/Tk
-----------------------------
Whether it's trying to navigate reference documentation, trying to find
the right method or option, adapting some existing code, or debugging your
Tkinter application, there are times that it will be useful to understand
what those underlying Tcl/Tk commands look like.

The class hierarchy looks complicated, but in actual practice, application
programmers almost always refer to the classes at the very bottom of the
hierarchy.
To illustrate, here is the Tcl/Tk equivalent of the main part of the Tkinter
script above.

Notes:
::

* These classes are provided for the purposes of organizing certain functions
under one namespace. They aren't meant to be instantiated independently.
ttk::frame .frm -padding 10
grid .frm
grid [ttk::label .frm.lbl -text "Hello World!"] -column 0 -row 0
grid [ttk::button .frm.btn -text "Quit" -command "destroy ."] -column 1 -row 0

* The :class:`Tk` class is meant to be instantiated only once in an application.
Application programmers need not instantiate one explicitly, the system creates
one whenever any of the other classes are instantiated.

* The :class:`Widget` class is not meant to be instantiated, it is meant only
for subclassing to make "real" widgets (in C++, this is called an 'abstract
class').
Tcl's syntax is similar to many shell languages, where the first word is the
command to be executed, with arguments to that command following it, separated
by spaces. Without getting into too many details, notice the following:

To make use of this reference material, there will be times when you will need
to know how to read short passages of Tk and how to identify the various parts
of a Tk command. (See section :ref:`tkinter-basic-mapping` for the
:mod:`tkinter` equivalents of what's below.)
* The commands used to create widgets (like ``ttk::frame``) correspond to
widget classes in Tkinter.

Tk scripts are Tcl programs. Like all Tcl programs, Tk scripts are just lists
of tokens separated by spaces. A Tk widget is just its *class*, the *options*
that help configure it, and the *actions* that make it do useful things.
* Tcl widget options (like ``-text``) correspond to keyword arguments in
Tkinter.

To make a widget in Tk, the command is always of the form::
* Widgets are referred to by a *pathname* in Tcl (like ``.frm.btn``),
whereas Tkinter doesn't use names but object references.

classCommand newPathname options
* A widget's place in the widget hierarchy is encoded in its (hierarchical)
pathname, which uses a ``.`` (dot) as a path separator. The pathname for
the root window is just ``.`` (dot). In Tkinter, the hierarchy is defined
not by pathname but by specifying the parent widget when creating each
child widget.

*classCommand*
denotes which kind of widget to make (a button, a label, a menu...)
* Operations which are implemented as separate *commands* in Tcl (like
``grid`` or ``destroy``) are represented as *methods* on Tkinter widget
objects. As you'll see shortly, at other times Tcl uses what appear to be
method calls on widget objects, which more closely mirror what would is
used in Tkinter.

.. index:: single: . (dot); in Tkinter

*newPathname*
is the new name for this widget. All names in Tk must be unique. To help
enforce this, widgets in Tk are named with *pathnames*, just like files in a
file system. The top level widget, the *root*, is called ``.`` (period) and
children are delimited by more periods. For example,
``.myApp.controlPanel.okButton`` might be the name of a widget.
How do I...? What option does...?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

*options*
configure the widget's appearance and in some cases, its behavior. The options
come in the form of a list of flags and values. Flags are preceded by a '-',
like Unix shell command flags, and values are put in quotes if they are more
than one word.
If you're not sure how to do something in Tkinter, and you can't immediately
find it in the tutorial or reference documentation you're using, there are a
few strategies that can be helpful.

For example::
First, remember that the details of how individual widgets work may vary
across different versions of both Tkinter and Tcl/Tk. If you're searching
documentation, make sure it corresponds to the Python and Tcl/Tk versions
installed on your system.

button .fred -fg red -text "hi there"
^ ^ \______________________/
| | |
class new options
command widget (-opt val -opt val ...)
When searching for how to use an API, it helps to know the exact name of the
class, option, or method that you're using. Introspection, either in an
interactive Python shell or with :func:`print`, can help you identify what
you need.

Once created, the pathname to the widget becomes a new command. This new
*widget command* is the programmer's handle for getting the new widget to
perform some *action*. In C, you'd express this as someAction(fred,
someOptions), in C++, you would express this as fred.someAction(someOptions),
and in Tk, you say::
To find out what configuration options are available on any widget, call its
:meth:`configure` method, which returns a dictionary containing a variety of
information about each object, including its default and current values. Use
:meth:`keys` to get just the names of each option.

.fred someAction someOptions
::

Note that the object name, ``.fred``, starts with a dot.
btn = ttk.Button(frm, ...)
print(btn.configure().keys())

As you'd expect, the legal values for *someAction* will depend on the widget's
class: ``.fred disable`` works if fred is a button (fred gets greyed out), but
does not work if fred is a label (disabling of labels is not supported in Tk).
As most widgets have many configuration options in common, it can be useful
to find out which are specific to a particular widget class. Comparing the
list of options to that of a simpler widget, like a frame, is one way to
do that.

The legal values of *someOptions* is action dependent. Some actions, like
``disable``, require no arguments, others, like a text-entry box's ``delete``
command, would need arguments to specify what range of text to delete.
::

print(set(btn.configure().keys()) - set(frm.configure().keys()))

.. _tkinter-basic-mapping:
Similarly, you can find the available methods for a widget object using the
standard :func:`dir` function. If you try it, you'll see there are over 200
common widget methods, so again identifying those specific to a widget class
is helpful.

Mapping Basic Tk into Tkinter
-----------------------------
::

Class commands in Tk correspond to class constructors in Tkinter. ::
print(dir(btn))
print(set(dir(btn)) - set(dir(frm)))

button .fred =====> fred = Button()

The master of an object is implicit in the new name given to it at creation
time. In Tkinter, masters are specified explicitly. ::
Navigating the Tcl/Tk Reference Manual
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

button .panel.fred =====> fred = Button(panel)
As noted, the official `Tk commands <https://www.tcl.tk/man/tcl8.6/TkCmd/contents.htm>`_
reference manual (man pages) is often the most accurate description of what
specific operations on widgets do. Even when you know the name of the option
or method that you need, you may still have a few places to look.

The configuration options in Tk are given in lists of hyphened tags followed by
values. In Tkinter, options are specified as keyword-arguments in the instance
constructor, and keyword-args for configure calls or as instance indices, in
dictionary style, for established instances. See section
:ref:`tkinter-setting-options` on setting options. ::
While all operations in Tkinter are implemented as method calls on widget
objects, you've seen that many Tcl/Tk operations appear as commands that
take a widget pathname as its first parameter, followed by optional
parameters, e.g.

button .fred -fg red =====> fred = Button(panel, fg="red")
.fred configure -fg red =====> fred["fg"] = red
OR ==> fred.config(fg="red")
::

In Tk, to perform an action on a widget, use the widget name as a command, and
follow it with an action name, possibly with arguments (options). In Tkinter,
you call methods on the class instance to invoke actions on the widget. The
actions (methods) that a given widget can perform are listed in
:file:`tkinter/__init__.py`. ::
destroy .
grid .frm.btn -column 0 -row 0

.fred invoke =====> fred.invoke()
Others, however, look more like methods called on a widget object (in fact,
when you create a widget in Tcl/Tk, it creates a Tcl command with the name
of the widget pathname, with the first parameter to that command being the
name of a method to call).

To give a widget to the packer (geometry manager), you call pack with optional
arguments. In Tkinter, the Pack class holds all this functionality, and the
various forms of the pack command are implemented as methods. All widgets in
:mod:`tkinter` are subclassed from the Packer, and so inherit all the packing
methods. See the :mod:`tkinter.tix` module documentation for additional
information on the Form geometry manager. ::
::

pack .fred -side left =====> fred.pack(side="left")
.frm.btn invoke
.frm.lbl configure -text "Goodbye"


In the official Tcl/Tk reference documentation, you'll find most operations
that look like method calls on the man page for a specific widget (e.g.,
you'll find the :meth:`invoke` method on the
`ttk::button <https://www.tcl.tk/man/tcl8.6/TkCmd/ttk_button.htm>`_
man page), while functions that take a widget as a parameter often have
their own man page (e.g.,
`grid <https://www.tcl.tk/man/tcl8.6/TkCmd/grid.htm>`_).

You'll find many common options and methods in the
`options <https://www.tcl.tk/man/tcl8.6/TkCmd/options.htm>`_ or
`ttk::widget <https://www.tcl.tk/man/tcl8.6/TkCmd/ttk_widget.htm>`_ man
pages, while others are found in the man page for a specific widget class.

You'll also find that many Tkinter methods have compound names, e.g.,
:func:`winfo_x`, :func:`winfo_height`, :func:`winfo_viewable`. You'd find
documentation for all of these in the
`winfo <https://www.tcl.tk/man/tcl8.6/TkCmd/winfo.htm>`_ man page.

.. note::
Somewhat confusingly, there are also methods on all Tkinter widgets
that don't actually operate on the widget, but operate at a global
scope, independent of any widget. Examples are methods for accessing
the clipboard or the system bell. (They happen to be implemented as
methods in the base :class:`Widget` class that all Tkinter widgets
inherit from).


How Tk and Tkinter are Related
Expand Down
6 changes: 6 additions & 0 deletions Doc/tools/susp-ignored.csv
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,12 @@ library/tarfile,,:xz,'r:xz'
library/tarfile,,:xz,'w:xz'
library/time,,:mm,
library/time,,:ss,
library/tkinter,294,::,ttk::frame .frm -padding 10
library/tkinter,294,::,"grid [ttk::label .frm.lbl -text ""Hello World!""] -column 0 -row 0"
library/tkinter,294,::,"grid [ttk::button .frm.btn -text ""Quit"" -command ""destroy .""] -column 1 -row 0"
library/tkinter,304,::,ttk::frame
library/tkinter,402,::,ttk::button
library/tkinter,410,::,ttk::widget
library/tracemalloc,,:limit,"for index, stat in enumerate(top_stats[:limit], 1):"
library/turtle,,::,Example::
library/unittest,,:foo,"self.assertEqual(cm.output, ['INFO:foo:first message',"
Expand Down