更多小部件#

本章介绍了更多的小部件:列表框、滚动条、文本框、刻度条、微调框和进度条。其中一些比我们之前看到的基础小部件功能更强大。在这里,我们还将看到几个使用经典 Tk 小部件的实例,这些情况下没有(或没有必要)使用主题化的对应物。

Listbox#

列表框小部件显示单行文本项的列表,这些文本通常较长,它允许用户浏览列表,选择一个或多个项目。

列表框是经典 Tk 小部件的一部分;目前主题化的 Tk 小部件集中还没有列表框。

Tk 的 treeview 小部件(它是有主题的)也可以作为列表框使用(单层深度的树),这允许你使用图标和样式来装饰列表。此外,很有可能一个多列(表格)列表小部件将来会加入到 Tk 中,无论是基于 treeview 还是其中可用的扩展。

height 配置选项可以指定列表框在不滚动的情况下一次显示的行数:

l = Listbox(parent, height=10)

填充列表框项目#

填充和管理系统中的所有列表框项目有一个简单的方法,也有困难的方法。

这是简单的方式。每个列表框都有 listvariable 配置选项,允许您将变量(必须持有列表)链接到列表框。这个列表中的每个元素都是字符串,表示列表框中的项目。要添加、删除或重新排列列表框中的项目,只需像操作其他列表一样修改此变量即可。同样,要找出例如列表框第三行上的哪个项目,只需查看列表变量的第三个元素。

实际上,这并不那么简单。Tkinter 不允许您将常规 Python 列表链接到列表框。正如我们对 entry 小部件所见,我们需要使用 StringVar 作为中介。它提供了 Python 列表和底层 Tk 小部件可以使用的字符串表示之间的映射。这也意味着每当我们更改列表时,都需要更新 StringVar

choices = ["apple", "orange", "banana"]
choicesvar = StringVar(value=choices)
l = Listbox(parent, listvariable=choicesvar)
...
choices.append("peach")
choicesvar.set(choices)

较旧且更困难的方法涉及使用属于列表框小部件本身的一组方法,这些方法操作由小部件维护的内部项目列表:

  • insert idx item ?item...? 方法用于添加一个或多个项目;idx是一个基于 0 的索引,指示应在其之前添加项目的项目位置;指定 end 以将新项目放在列表末尾。

  • 使用 delete first ?last? 方法从列表中删除一个或多个项目;firstlast 是按 insert 方法指定的索引。

  • 使用 get first ?last? 方法返回给定位置的单个项目的内容,或返回 firstlast 之间项目的列表。

  • size 方法返回列表中的项目数。

之所以存在困难的方法,是因为 listvariable 选项是在Tk 8.3中引入的。在此之前,您只能使用困难的方法。因为使用 listvariable 可以让您使用所有标准的列表操作,所以它提供了一个更简单的 API。如果您的列表框以旧方式工作,那么这是一个值得考虑的升级。

选择项目#

您可以选择用户是否只能一次选择一个项目,或者是否可以同时选择多个项目。这是通过 selectmode 选项控制的:默认情况下,只能选择单个项目(browse),而 selectmodeextended 时,允许用户选择多个项目。

备注

名称 browseextended 出于向后兼容的原因确实很糟糕。更糟的是,还有两个其他模式 singlemultiple,您不应使用它们(它们使用了与现代用户界面和平台惯例不一致的旧交互样式)。

要找出当前在列表框中选择的项目或项目,请使用 curselection 方法。它返回当前选择的所有项目的索引列表;这可能是一个空列表。对于 selectmodebrowse 的列表,它永远不会超过一个项目。您还可以使用 selection_includes index 方法检查具有给定索引的项目是否当前被选择。

if lbox.selection_includes(2): ...

要编程方式更改选择,可以使用 selection_clear first ?last? 方法取消选择单个项目或指定索引范围内的任何项目。要选择项目或范围内的所有项目,请使用 selection_set first ?last? 方法。这两个都不会触及范围外的任何项目的选定状态。

如果更改了选择,还应确保新选择的项目可见(即,它没有被滚动出视图)。为此,请使用 see index方法。

lbox.selection_set(idx)
lbox.see(idx)

当用户更改选择时,会生成 <<ListboxSelect>> 虚拟事件。您可以绑定到它以执行所需的任何操作。根据您的应用程序,您可能还想绑定到双击 <Double-1> 事件并使用它来调用当前选择项的操作。

lbox.bind("<<ListboxSelect>>", lambda e: updateDetails(lbox.curselection()))
lbox.bind("<Double-1>", lambda e: invokeAction(lbox.curselection()))

在大多数“经典”Tk小部件中,您可以通过修改外观来极大地灵活地调整列表框的显示。如参考手册所述,您可以修改列表框项显示的字体、正常状态下项的前景色和背景色、选中时的颜色以及当小部件被禁用时的颜色等。还有 itemconfigure 方法,允许您更改单个项的前景色和背景色。

通常,适度使用是有益的。一般来说,默认值将完全适合平台惯例。在我们即将展示的示例中,我们将展示如何将这些选项的适度使用付诸实践,在这种情况下,通过略微不同的颜色显示列表框中的交替行。

保持额外的项目数据#

listvariable (或内部列表,如果您以旧方式管理事物)保存将在列表框中显示的字符串。但是,通常每个您显示的字符串都与某个其他数据项相关联。这可能是您的程序有意义的内部对象,但不打算向用户显示。换句话说,您真正感兴趣的不是列表框中显示的字符串,而是相关的数据项。例如,一个列表框可能会向用户显示一个名字列表,但您的程序实际上对每个名字背后的用户对象(或ID号)感兴趣,而不是具体的名字。

我们如何将这个底层值与显示的名称关联起来?不幸的是,列表框小部件本身没有提供任何设施,因此我们必须单独管理它。有几种明显的途径。首先,如果显示的字符串是唯一的,可以使用哈希表将每个名称映射到其关联的底层对象。这在人名的情况下可能不太适用,因为可能存在重复,但在国家这样的唯一实体中可以工作。

第二种方法是保持一个与列表框中显示的字符串列表平行的第二个列表。这个第二个列表将保存与每个显示项关联的底层对象。因此,显示字符串列表中的第一项对应于底层对象列表中的第一项,第二项对应于第二项,依此类推。您在其中一个列表中所做的任何更改(插入、删除、重新排序),都必须在另一个列表中进行。然后,您可以轻松地基于它们在列表中的位置从显示的列表项映射到底层对象。

示例 这是一个愚蠢的示例,展示了这些列表框技术中的几个。我们将有一个国家列表显示出来。我们将能够一次选择一个国家。当我们这样做时,状态栏将显示该国的人口。您可以按下一个按钮,向所选国家的元首(嗯,不是真的,但请发挥你的想象力)发送一份礼物。发送礼物也可以通过双击列表或按回车键触发。

在幕后,我们维护两个平行的列表。第一个是两个字母的国家代码列表。另一个是我们将在列表框中显示的每个国家的名称。我们还拥有一个简单的哈希表,其中包含每个国家的人口,索引为两个字母的国家代码。

from tkinter import *
from tkinter import ttk
root = Tk()

# Initialize our country "databases":
#  - the list of country codes (a subset anyway)
#  - parallel list of country names, same order as the country codes
#  - a hash table mapping country code to population
countrycodes = ('ar', 'au', 'be', 'br', 'ca', 'cn', 'dk', 'fi', 'fr', 'gr', 'in', 'it', 'jp', 'mx', 'nl', 'no', 'es', 'se', 'ch')
countrynames = ('Argentina', 'Australia', 'Belgium', 'Brazil', 'Canada', 'China', 'Denmark', \
        'Finland', 'France', 'Greece', 'India', 'Italy', 'Japan', 'Mexico', 'Netherlands', 'Norway', 'Spain', \
        'Sweden', 'Switzerland')
cnames = StringVar(value=countrynames)
populations = {'ar':41000000, 'au':21179211, 'be':10584534, 'br':185971537, \
        'ca':33148682, 'cn':1323128240, 'dk':5457415, 'fi':5302000, 'fr':64102140, 'gr':11147000, \
        'in':1131043000, 'it':59206382, 'jp':127718000, 'mx':106535000, 'nl':16402414, \
        'no':4738085, 'es':45116894, 'se':9174082, 'ch':7508700}

# Names of the gifts we can send
gifts = { 'card':'Greeting card', 'flowers':'Flowers', 'nastygram':'Nastygram'}

# State variables
gift = StringVar()
sentmsg = StringVar()
statusmsg = StringVar()

# Called when the selection in the listbox changes; figure out
# which country is currently selected, and then lookup its country
# code, and from that, its population.  Update the status message
# with the new population.  As well, clear the message about the
# gift being sent, so it doesn't stick around after we start doing
# other things.
def showPopulation(*args):
    idxs = lbox.curselection()
    if len(idxs)==1:
        idx = int(idxs[0])
        code = countrycodes[idx]
        name = countrynames[idx]
        popn = populations[code]
        statusmsg.set("The population of %s (%s) is %d" % (name, code, popn))
    sentmsg.set('')

# Called when the user double clicks an item in the listbox, presses
# the "Send Gift" button, or presses the Return key.  In case the selected
# item is scrolled out of view, make sure it is visible.
#
# Figure out which country is selected, which gift is selected with the 
# radiobuttons, "send the gift", and provide feedback that it was sent.
def sendGift(*args):
    idxs = lbox.curselection()
    if len(idxs)==1:
        idx = int(idxs[0])
        lbox.see(idx)
        name = countrynames[idx]
        # Gift sending left as an exercise to the reader
        sentmsg.set("Sent %s to leader of %s" % (gifts[gift.get()], name))

# Create and grid the outer content frame
c = ttk.Frame(root, padding=(5, 5, 12, 0))
c.grid(column=0, row=0, sticky=(N,W,E,S))
root.grid_columnconfigure(0, weight=1)
root.grid_rowconfigure(0,weight=1)

# Create the different widgets; note the variables that many
# of them are bound to, as well as the button callback.
# We're using the StringVar() 'cnames', constructed from 'countrynames'
lbox = Listbox(c, listvariable=cnames, height=5)
lbl = ttk.Label(c, text="Send to country's leader:")
g1 = ttk.Radiobutton(c, text=gifts['card'], variable=gift, value='card')
g2 = ttk.Radiobutton(c, text=gifts['flowers'], variable=gift, value='flowers')
g3 = ttk.Radiobutton(c, text=gifts['nastygram'], variable=gift, value='nastygram')
send = ttk.Button(c, text='Send Gift', command=sendGift, default='active')
sentlbl = ttk.Label(c, textvariable=sentmsg, anchor='center')
status = ttk.Label(c, textvariable=statusmsg, anchor=W)

# Grid all the widgets
lbox.grid(column=0, row=0, rowspan=6, sticky=(N,S,E,W))
lbl.grid(column=1, row=0, padx=10, pady=5)
g1.grid(column=1, row=1, sticky=W, padx=20)
g2.grid(column=1, row=2, sticky=W, padx=20)
g3.grid(column=1, row=3, sticky=W, padx=20)
send.grid(column=2, row=4, sticky=E)
sentlbl.grid(column=1, row=5, columnspan=2, sticky=N, pady=5, padx=5)
status.grid(column=0, row=6, columnspan=2, sticky=(W,E))
c.grid_columnconfigure(0, weight=1)
c.grid_rowconfigure(5, weight=1)

# Set event bindings for when the selection in the listbox changes,
# when the user double clicks the list, and when they hit the Return key
lbox.bind('<<ListboxSelect>>', showPopulation)
lbox.bind('<Double-1>', sendGift)
root.bind('<Return>', sendGift)

# Colorize alternating lines of the listbox
for i in range(0,len(countrynames),2):
    lbox.itemconfigure(i, background='#f0f0ff')

# Set the starting state of the interface, including selecting the
# default gift to send, and clearing the messages.  Select the first
# country in the list; because the <<ListboxSelect>> event is only
# fired when users makes a change, we explicitly call showPopulation.
gift.set('card')
sentmsg.set('')
statusmsg.set('')
lbox.selection_set(0)
showPopulation()

root.mainloop()

在这个例子中,一个明显的遗漏是,虽然国家列表可能相当长,但屏幕上一次只能显示其中的一部分。要查看列表中更下方的国家,您必须使用鼠标拖动或按下箭头键。如果能有一个滚动条就好了。让我们来解决这个问题。

示例#

class Window(Tk):
    def __init__(self):
        super().__init__()
        self.title('my window')
        self.geometry('200x200')
        self.var1 = StringVar()
        self.var2 = StringVar()
        self.init_list = [11, 22, 33, 44] # 初始列表
        self.create_widgets()
        self.layout()

    def create_widgets(self):
        self.label = ttk.Label(background='yellow',
                               width=25, textvariable=self.var1)
        self.button = ttk.Button(
            text='打印选项', width=15, command=self.print_selection)
        self.var2.set(self.init_list)
        # 可多选
        self.list_box = Listbox(listvariable=self.var2, selectmode="extended")

    def layout(self):
        self.label.grid()
        self.button.grid()
        self.list_box.grid()

    def print_selection(self):
        curs = self.list_box.curselection()
        if curs:
            if len(curs) > 1:
                mul = [str(self.list_box.get(cur)) for cur in curs]
                value = ','.join(mul)
            else:
                value = self.list_box.get(curs)
            self.var1.set(value)


window = Window()

要以编程方式更改选择(即传统的方式),可以使用 "selection clear first ?last?" 方法取消选择单个项目或取消指定索引范围内的任何项目。要选择一个项目或一个范围内的所有项目,请使用 "selection set first ?last?" 方法。这两项都不会触及超出指定范围的任何项目的选择。比如:

list_items = [1, 2, 3, 4]
for item in list_items:
    # 插入到末尾
    window.list_box.insert('end', item)
window.list_box.insert(1, 'first')  # 插入到开头
window.list_box.insert(2, 'second')  # 插入到第3个位置
window.list_box.delete(2)  # 删除第3个位置的元素

但是由于 window.init_list 列表是一个 list, 因此,要添加,删除或重新排列列表框中的项目,您可以像处理任何其他列表(list)一样简单地操作此变量。 同样,要找出哪个项目位于列表框的第三行,只需以索引的方式查看 list 变量的第三个元素即可。这样便有:

list_items = [1, 2, 3, 4]
# 依次添加 list_items 的元素
window.init_list.extend(list_items)
window.init_list.insert(1, 'first')  # 插入到开头
window.init_list.insert(2, 'second')  # 插入到第3个位置
window.init_list.pop(2)  # 删除第3个位置的元素
window.var2.set(window.init_list)

最后,展示效果图如下:

图3 Listbox 示例

Scrollbar#

滚动条控件帮助用户查看另一个控件的所有部分,后者的内容通常远大于可用屏幕空间所能显示的内容。

s = ttk.Scrollbar( parent, orient=VERTICAL, command=listbox.yview)
listbox.configure(yscrollcommand=s.set)

与某些用户界面工具包不同,Tk 的滚动条并不是另一个部件(例如列表框)的一部分,而是一个独立的部件。相反,滚动条通过调用被滚动部件的方法与之通信;实际上,被滚动的部件也需要调用滚动条上的方法。

备注

如果你使用的是近期的 Linux 发行版,可能会注意到许多应用程序中的滚动条已经变得更像在macOS上看到的那样。这种较新的外观在Linux上不被Tk附带的任何默认主题支持。然而,一些第三方主题确实支持它。

orient 配置选项决定了滚动条是在 horizontal 还是 vertical 方向上滚动被滚动的部件。然后你需要使用 command 配置选项来指定如何与被滚动的部件进行通信。这是在滚动条移动时调用被滚动部件的方法。

每个可以垂直滚动的部件都包含一个名为 yview 的方法,而那些可以水平滚动的则有 xview 方法。只要这个方法存在,滚动条就不需要了解被滚动部件的其他任何信息。当操作滚动条时,它会向方法调用中添加几个参数,指示如何滚动、滚动到什么位置等。

被滚动的部件还需要反馈给滚动条,告诉它现在整个内容区域有多少百分比是可见的。除了 yview 和/或 xview 方法外,每个可滚动的部件还有一个 yscrollcommand 和/或 xscrollcommand 配置选项。这用于指定一个方法调用,必须是滚动条的 set 方法。同样,额外的参数将自动附加到方法调用中。

备注

如果你想从程序内部将滚动条移动到特定位置,可以自行调用 set first last 方法。传递两个浮点数值(介于0和1之间),表示可见内容区域的开始和结束百分比。

示例#

列表框是可以滚动的几种类型的部件之一。在这里,我们将构建一个非常简单的用户界面,包括一个占据整个窗口的垂直可滚动列表框,底部仅有一个状态行。

from tkinter import *
from tkinter import ttk

root = Tk()
l = Listbox(root, height=5)
l.grid(column=0, row=0, sticky=(N,W,E,S))
s = ttk.Scrollbar(root, orient=VERTICAL, command=l.yview)
s.grid(column=1, row=0, sticky=(N,S))
l['yscrollcommand'] = s.set
ttk.Label(root, text="Status message here", anchor=(W)).grid(column=0, columnspan=2, row=1, sticky=(W,E))
root.grid_columnconfigure(0, weight=1)
root.grid_rowconfigure(0, weight=1)
for i in range(1,101):
    l.insert('end', 'Line %d of 100' % i)
root.mainloop()

备注

如果您曾经看过这个教程的早期版本,您可能还记得,在这个阶段,引入了 sizegrip 小部件。它在窗口的右下角放置了一个小手柄,允许用户通过拖动手柄来调整窗口的大小。这在一些平台上很常见,包括旧版本的 macOS。一些旧版本的Tk甚至会自动为您的窗口添加这个手柄。

平台惯例往往比长期存在的开源GUI工具包发展得更快。Mac OS X 10.7取消了角落的 size grip,改为允许从任何窗口边缘调整大小,终于赶上了世界的其他部分。除非有迫切需要与10年以上历史的操作系统视觉上兼容,否则如果您的应用程序中有 sizegrip,最好将其移除。

Text#

文本小部件为用户提供了一个区域,使他们能够输入多行文本。文本小部件是经典Tk小部件的一部分,而不是主题化Tk小部件的一部分。

备注

Tk 的文本组件与画布组件一样,是两个超级强大的组件之一,它们提供了深度惊人但易于编程的特性。文本组件构成了完整的文字处理器、大纲编辑器、网页浏览器等的基础。我们将在后续章节中探讨一些高级内容。在这里,我们将向您展示如何使用文本组件捕获相对简单的多行文本输入。

t = Text(parent, width=40, height=10)

文本小部件的宽度和高度选项分别以字符数和行数指定请求的屏幕尺寸。文本内容可以任意大。你可以使用 wrap 配置选项来控制换行的处理方式:值可以是 none(不换行,文本可能会水平滚动)、char(在任何字符处换行)以及 word (只在单词边界换行)。

文本小部件可以被禁用,从而无法进行编辑。因为文本不是一个主题化的小部件,所以通常的 stateinstate 方法都不可用。相反,应使用 state 配置选项,将其设置为 disablednormal

txt['state'] = 'disabled'

滚动的工作方式与列表框相同。xscrollcommandyscrollcommand 配置选项将文本小部件连接到水平和/或垂直滚动条上,并且通过滚动条调用 xviewyview 方法。为了确保特定行可见(即,不被滚动出视图),你可以使用 see index 方法,其中 index 的形式为 linenum.charnum,例如,5.0 表示第5行(从1开始计数)的第一个字符(从0开始计数)。

内容#

文本组件与例如输入组件不同,它们没有关联的链接变量。要检索整个文本组件的内容,请调用方法 get 1.0 end; 这里的 1.0 是文本中的索引,表示第一行的首字符,而 end 是最后一行最后一个字符的快捷索引。如果需要,可以提供其他索引来检索更小范围的文本。

可以使用 insert index string 方法向组件添加文本;同样,index 采用 line.char 的形式,并标记在哪个字符前插入文本;使用 end 将文本添加到组件的末尾。你可以使用 delete start end 方法删除一段文本,其中 startend 都是前面描述的文本索引。

我们将在后面的章节中详细介绍文本组件的许多附加高级功能。

Scale#

一个刻度小部件允许用户通过直接操作选择一个数值。

s = ttk.Scale(parent, orient=HORIZONTAL, length=200, from_=1.0, to=100.0)

备注

由于“from”是Python中的保留关键字,因此在使用它作为配置选项时需要在后面添加一个下划线。

orient 选项可以是 horizontal 也可以是 verticallength 选项,代表水平或垂直尺度中的较长轴,是以屏幕单位(例如像素)来指定的。你还需要定义尺度允许用户选择的数字范围;为此,为 fromto 配置选项分别设置一个浮点数。

你可以用几种不同的方法来设置尺度的当前值(必须是 fromto 值之间的一个浮点值)。你可以设置(或读取以获取当前值)尺度的 value 配置选项。你可以使用 variable 选项将尺度与一个变量链接起来。或者,你可以调用尺度的 set value 方法来更改值,或调用 get 方法来读取当前值。

command 配置选项允许你指定一个脚本,每当尺度改变时都调用这个脚本。Tk每次调用此脚本时都会将尺度的当前值作为参数附加(我们在滚动条回调中添加额外参数时看到了类似的情况)。

# 标签与尺度绑定到相同的变量上,因此自动更新
num = StringVar()
ttk.Label(root, textvariable=num).grid(column=0, row=0, sticky='we')

# 我们将通过尺度的命令回调手动更新的标签
manual = ttk.Label(root)
manual.grid(column=0, row=1, sticky='we')

def update_lbl(val):
   manual['text'] = "Scale at " + val

scale = ttk.Scale(root, orient='horizontal', length=200, from_=1.0, to=100.0, variable=num, command=update_lbl)
scale.grid(column=0, row=2, sticky='we')
scale.set(20)

与其他主题小部件一样,你可以使用 state disabledstate !disabledinstate disabled 方法来阻止用户修改尺度。

备注

由于尺度小部件不显示实际值,你可能想要另外添加这些值,例如,使用标签小部件。

Spinbox#

Spinbox 小部件允许用户选择数字(或事实上,从任意列表中选择项目)。它通过结合一个显示当前值的输入式小部件和一对小型上下箭头来实现这一点,这对箭头可以用来逐步浏览可能的选择范围。

备注

主题化的 spinbox 小部件在Tk 8.5.9版本中被添加(发布于2010年)。如果您必须运行较旧的版本,经典Tk小部件中也有一个spinbox,尽管其API略有不同。

spinval = StringVar()
s = ttk.Spinbox(parent, from_=1.0, to=100.0, textvariable=spinval)

像刻度控件一样,微调框允许用户在特定范围内选择一个数字(通过 fromto 配置选项指定),尽管其用户界面大不相同。您还可以指定一个增量,该增量控制每次点击向上或向下按钮时值的变化量。

与列表框或组合框类似,微调框也可用于让用户从任意字符串列表中选择项目;这些可以通过 values 配置选项来指定。其工作原理与组合框相同;指定一组值将覆盖 fromto 设置。

在默认状态下,微调框允许用户通过向上和向下按钮或直接在显示当前值的输入区域输入值来选择值。如果您希望禁用后者功能,以便只使用上下按钮,可以设置 readonly 状态标志。

s.state(['readonly'])

与其他主题控件一样,您也可以通过 disabled 状态标志禁用微调框,或者通过 instate 方法检查状态。微调框还支持验证,方式与输入控件相同,使用 validatevalidatecommand 配置选项。

备注

您可能会对何时选择刻度、列表框、组合框、输入框或微调框感到困惑。通常,这些都可以用于处理相同类型的数据。答案实际上取决于您希望用户选择什么,平台用户界面惯例以及该值在用户界面中的角色。

例如,与列表框相比,组合框和微调框(spinbox)占用的空间相对较小。它们可能适用于更外围的设置。在用户界面中更为主要和突出的选择可能需要列表框所占据的额外空间。当项目没有自然且明显的排序时,微调框不太有意义。注意不要在组合框和微调框中放置太多项目。这可能会使选择项目更加耗时。

布尔选项 wrap 决定数值超出起始或结束值时是否循环。您也可以为微调框当前值的输入指定宽度。

同样,如何设置或获取微调框中的当前值也有选择。通常,您会使用 textvariable 配置选项指定一个链接变量。如往常一样,任何对变量的更改都会反映在微调框中,而微调框中的任何更改也会反映在链接变量中。此外,set valueget 方法允许您直接设置或获取值。

当用户按下向上(<<Increment>>)或向下(<<Decrement>>)按钮时,微调框会生成虚拟事件。command 配置选项允许您提供一个回调函数,该函数在任何更改时被调用。

Progressbar#

进度条小部件为用户提供了关于长时间操作进度的反馈。

在你可以估算操作完成所需时间的情况下,可以显示已经完成的比例。否则,你可以指示操作正在进行,但不暗示还需要多长时间。

p = ttk.Progressbar(parent, orient=HORIZONTAL, length=200, mode='determinate')

与刻度小部件类似,进度条也应通过 orient 配置选项指定方向(horizontalvertical),并且可以设置可选的 lengthmode 配置选项可以设置为 determinate,此时进度条将显示完成进度的相对比例;或者设置为 indeterminate,此时它表明操作仍在继续,但不显示相对进度。

确定性进度#

要使用确定性模式,首先估算操作完成所需的总“步数”。这可能是一个时间量,但不一定非得如此。通过 maximum 配置选项将此值提供给进度条。它应是浮点数,默认值为 100.0(即每一步是 1%)。

在操作进行过程中,通过 value 配置选项告诉进度条你的进度。这将从0开始,然后递增到你设定的最大值。

备注

这里有两种微小的变化。首先,你可以通过进度条的 variable 配置选项,仅将当前值存储在一个与进度条链接的变量中;这样,当你更改该变量时,进度条将更新。另一种选择是调用进度条的 step? amount? 方法。这将给定的值 amount(默认为 1.0)。

非确定性进度#

当你无法轻易估计长时间运行任务的进度时,使用非确定性模式。然而,你仍然希望提供反馈,表明操作正在进行(以及你的程序没有崩溃)。在操作开始时,调用进度条的 start 方法。在操作结束时,调用其 stop 方法。进度条将处理其余部分。

不幸的是,“进度条将处理其余部分”并不那么简单。事实上,如果你 start 了进度条,调用一个需要几分钟来完成的函数,然后 stop 进度条,你的程序在整个时间内看起来都会冻结,进度条也不会更新。实际上,它甚至可能根本不会出现在屏幕上。

函数

描述

start(interval=None)

自动调整进度条的位置。通过启动一个循环定时事件,按照定义的步长调整进度条位置。定时器的间隔由interval参数来设定。间隔单位是毫秒,默认间隔是 50 毫秒。

step(amount=None)

每次调整进度条的步长,默认是 1.0

stop()

停止定时器,停止进度条的自动调整

为了了解为什么会这样以及如何解决它,下一章将深入探讨Tk的事件循环。