[Python-checkins] bpo-42560: rewrite of Tkinter docs "life preserver" (GH-27842)

ambv webhook-mailer at python.org
Mon Aug 23 14:31:01 EDT 2021

commit: 08830a4f75553682e8d1ea6cdabd4c3472c59e28
branch: main
author: Mark Roseman <mark at markroseman.com>
committer: ambv <lukasz at langa.pl>
date: 2021-08-23T20:30:53+02:00

bpo-42560: rewrite of Tkinter docs "life preserver" (GH-27842)

Co-authored-by: Łukasz Langa <lukasz at langa.pl>

M Doc/library/tkinter.rst
M Doc/tools/susp-ignored.csv

diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst
index 34238f4cdd0bbe..425f030dcc983f 100644
--- a/Doc/library/tkinter.rst
+++ b/Doc/library/tkinter.rst
@@ -197,210 +197,241 @@ Additional modules:
 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.
+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
-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:
+  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`
-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
+To illustrate, here is the Tcl/Tk equivalent of the main part of the Tkinter
+script above.
-* 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.
-   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
-   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...?
-   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
diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv
index 1fde253feac2fa..211b49a5449f48 100644
--- a/Doc/tools/susp-ignored.csv
+++ b/Doc/tools/susp-ignored.csv
@@ -233,6 +233,12 @@ library/tarfile,,:xz,'r:xz'
+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/tracemalloc,,:limit,"for index, stat in enumerate(top_stats[:limit], 1):"
 library/unittest,,:foo,"self.assertEqual(cm.output, ['INFO:foo:first message',"

More information about the Python-checkins mailing list