标签归档:tkinter

如何在Tkinter中将参数传递给Button命令?

问题:如何在Tkinter中将参数传递给Button命令?

假设我Button在Python中使用Tkinter进行了以下操作:

import Tkinter as Tk
win = Tk.Toplevel()
frame = Tk.Frame(master=win).grid(row=1, column=1)
button = Tk.Button(master=frame, text='press', command=action)

action当我按下按钮时会调用该方法,但是如果我想向该方法传递一些参数action呢?

我尝试使用以下代码:

button = Tk.Button(master=frame, text='press', command=action(someNumber))

这只是立即调用该方法,而按该按钮则没有任何作用。

Suppose I have the following Button made with Tkinter in Python:

import Tkinter as Tk
win = Tk.Toplevel()
frame = Tk.Frame(master=win).grid(row=1, column=1)
button = Tk.Button(master=frame, text='press', command=action)

The method action is called when I press the button, but what if I wanted to pass some arguments to the method action?

I have tried with the following code:

button = Tk.Button(master=frame, text='press', command=action(someNumber))

This just invokes the method immediately, and pressing the button does nothing.


回答 0

我个人更喜欢lambdas在这种情况下使用,因为imo更加简单明了,并且如果您无法控制被调用的方法,也不会强迫您编写很多包装方法,但这当然是一个问题。

这就是使用lambda的方式(请注意,在功能模块中还存在一些currying的实现,因此您也可以使用它):

button = Tk.Button(master=frame, text='press', command= lambda: action(someNumber))

I personally prefer to use lambdas in such a scenario, because imo it’s clearer and simpler and also doesn’t force you to write lots of wrapper methods if you don’t have control over the called method, but that’s certainly a matter of taste.

That’s how you’d do it with a lambda (note there’s also some implementation of currying in the functional module, so you can use that too):

button = Tk.Button(master=frame, text='press', command= lambda: action(someNumber))

回答 1

这也可以通过使用partial标准库functools来完成,如下所示:

from functools import partial
#(...)
action_with_arg = partial(action, arg)
button = Tk.Button(master=frame, text='press', command=action_with_arg)

This can also be done by using partial from the standard library functools, like this:

from functools import partial
#(...)
action_with_arg = partial(action, arg)
button = Tk.Button(master=frame, text='press', command=action_with_arg)

回答 2

GUI示例:

假设我有GUI:

import tkinter as tk

root = tk.Tk()

btn = tk.Button(root, text="Press")
btn.pack()

root.mainloop()

按下按钮时会发生什么

看到btn按下时它会调用自己的函数,函数与button_press_handle以下示例非常相似:

def button_press_handle(callback=None):
    if callback:
        callback() # Where exactly the method assigned to btn['command'] is being callled

与:

button_press_handle(btn['command'])

您可以简单地认为command应该将option设置为对我们要调用的方法的引用,类似于callbackin button_press_handle


按下按钮时调用方法(回调

没有参数

因此,如果要在print按下按钮时进行某些操作,则需要进行以下设置:

btn['command'] = print # default to print is new line

请密切注意缺少()print方法的不足,该方法的含义是:“这是我要在按下时调用的方法名称,不要立即调用。” 但是,我没有为传递任何参数,print因此在没有参数的情况下,它会打印任何内容。

论点

现在,如果我还希望将参数传递给要在按下按钮时调用的方法,则可以使用匿名函数,该函数可以通过lambda语句创建,在这种情况下,将使用print内置方法,如下所示:

btn['command'] = lambda arg1="Hello", arg2=" ", arg3="World!" : print(arg1 + arg2 + arg3)

按下按钮时调用多种方法

没有参数

您也可以使用using lambda语句实现该功能,但是这被认为是不好的做法,因此在此不再赘述。好的做法是定义一个单独的方法,multiple_methods该方法调用所需的方法,然后将其设置为按下按钮的回调:

def multiple_methods():
    print("Vicariously") # the first inner callback
    print("I") # another inner callback

论点

为了将参数传递给调用其他方法的方法,请再次使用lambda语句,但首先:

def multiple_methods(*args, **kwargs):
    print(args[0]) # the first inner callback
    print(kwargs['opt1']) # another inner callback

然后设置:

btn['command'] = lambda arg="live", kw="as the" : a_new_method(arg, opt1=kw)

从回调返回对象

还要进一步注意,这callback并不是真的,return因为它仅在button_press_handlewith 内调用,callback()而不是return callback()。确实return不在该功能之外的任何地方。因此,您应该修改当前作用域中可访问的对象。


具有全局对象修改的完整示例

下面的示例将调用一个方法,该方法btn每次按下按钮都会更改的文本:

import tkinter as tk

i = 0
def text_mod():
    global i, btn           # btn can be omitted but not sure if should be
    txt = ("Vicariously", "I", "live", "as", "the", "whole", "world", "dies")
    btn['text'] = txt[i]    # the global object that is modified
    i = (i + 1) % len(txt)  # another global object that gets modified

root = tk.Tk()

btn = tk.Button(root, text="My Button")
btn['command'] = text_mod

btn.pack(fill='both', expand=True)

root.mainloop()

镜子

Example GUI:

Let’s say I have the GUI:

import tkinter as tk

root = tk.Tk()

btn = tk.Button(root, text="Press")
btn.pack()

root.mainloop()

What Happens When a Button Is Pressed

See that when btn is pressed it calls its own function which is very similar to button_press_handle in the following example:

def button_press_handle(callback=None):
    if callback:
        callback() # Where exactly the method assigned to btn['command'] is being callled

with:

button_press_handle(btn['command'])

You can simply think that command option should be set as, the reference to the method we want to be called, similar to callback in button_press_handle.


Calling a Method(Callback) When the Button is Pressed

Without arguments

So if I wanted to print something when the button is pressed I would need to set:

btn['command'] = print # default to print is new line

Pay close attention to the lack of () with the print method which is omitted in the meaning that: “This is the method’s name which I want you to call when pressed but don’t call it just this very instant.” However, I didn’t pass any arguments for the print so it printed whatever it prints when called without arguments.

With Argument(s)

Now If I wanted to also pass arguments to the method I want to be called when the button is pressed I could make use of the anonymous functions, which can be created with lambda statement, in this case for print built-in method, like the following:

btn['command'] = lambda arg1="Hello", arg2=" ", arg3="World!" : print(arg1 + arg2 + arg3)

Calling Multiple Methods when the Button Is Pressed

Without Arguments

You can also achieve that using lambda statement but it is considered bad practice and thus I won’t include it here. The good practice is to define a separate method, multiple_methods, that calls the methods wanted and then set it as the callback to the button press:

def multiple_methods():
    print("Vicariously") # the first inner callback
    print("I") # another inner callback

With Argument(s)

In order to pass argument(s) to method that calls other methods, again make use of lambda statement, but first:

def multiple_methods(*args, **kwargs):
    print(args[0]) # the first inner callback
    print(kwargs['opt1']) # another inner callback

and then set:

btn['command'] = lambda arg="live", kw="as the" : a_new_method(arg, opt1=kw)

Returning Object(s) From the Callback

Also further note that callback can’t really return because it’s only called inside button_press_handle with callback() as opposed to return callback(). It does return but not anywhere outside that function. Thus you should rather modify object(s) that are accessible in the current scope.


Complete Example with global Object Modification(s)

Below example will call a method that changes btn‘s text each time the button is pressed:

import tkinter as tk

i = 0
def text_mod():
    global i, btn           # btn can be omitted but not sure if should be
    txt = ("Vicariously", "I", "live", "as", "the", "whole", "world", "dies")
    btn['text'] = txt[i]    # the global object that is modified
    i = (i + 1) % len(txt)  # another global object that gets modified

root = tk.Tk()

btn = tk.Button(root, text="My Button")
btn['command'] = text_mod

btn.pack(fill='both', expand=True)

root.mainloop()

Mirror


回答 3

Python提供函数参数默认值的能力为我们提供了一条出路。

def fce(x=myX, y=myY):
    myFunction(x,y)
button = Tk.Button(mainWin, text='press', command=fce)

请参阅:http : //infohost.nmt.edu/tcc/help/pubs/tkinter/web/extra-args.html

对于更多按钮,您可以创建一个返回函数的函数:

def fce(myX, myY):
    def wrapper(x=myX, y=myY):
        pass
        pass
        pass
        return x+y
    return wrapper

button1 = Tk.Button(mainWin, text='press 1', command=fce(1,2))
button2 = Tk.Button(mainWin, text='press 2', command=fce(3,4))
button3 = Tk.Button(mainWin, text='press 3', command=fce(9,8))

Python’s ability to provide default values for function arguments gives us a way out.

def fce(x=myX, y=myY):
    myFunction(x,y)
button = Tk.Button(mainWin, text='press', command=fce)

See: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/extra-args.html

For more buttons you can create a function which returns a function:

def fce(myX, myY):
    def wrapper(x=myX, y=myY):
        pass
        pass
        pass
        return x+y
    return wrapper

button1 = Tk.Button(mainWin, text='press 1', command=fce(1,2))
button2 = Tk.Button(mainWin, text='press 2', command=fce(3,4))
button3 = Tk.Button(mainWin, text='press 3', command=fce(9,8))

回答 4

建立在Matt Thompsons的答案上:可以将一个类设为可调用的,因此可以代替一个函数来使用它:

import tkinter as tk

class Callback:
    def __init__(self, func, *args, **kwargs):
        self.func = func
        self.args = args
        self.kwargs = kwargs
    def __call__(self):
        self.func(*self.args, **self.kwargs)

def default_callback(t):
    print("Button '{}' pressed.".format(t))

root = tk.Tk()

buttons = ["A", "B", "C"]

for i, b in enumerate(buttons):
    tk.Button(root, text=b, command=Callback(default_callback, b)).grid(row=i, column=0)

tk.mainloop()

Building on Matt Thompsons answer : a class can be made callable so it can be used instead of a function:

import tkinter as tk

class Callback:
    def __init__(self, func, *args, **kwargs):
        self.func = func
        self.args = args
        self.kwargs = kwargs
    def __call__(self):
        self.func(*self.args, **self.kwargs)

def default_callback(t):
    print("Button '{}' pressed.".format(t))

root = tk.Tk()

buttons = ["A", "B", "C"]

for i, b in enumerate(buttons):
    tk.Button(root, text=b, command=Callback(default_callback, b)).grid(row=i, column=0)

tk.mainloop()

回答 5

它立即调用该方法并且按下按钮没有执行任何操作的原因action(somenumber)是已评估并且其返回值归因于按钮的命令。因此,如果action打印出一些东西告诉您它已经运行并返回了None,那么您只需运行action以评估其返回值并给出None作为按钮的命令。

要使按钮具有不同的参数来调用函数,可以使用全局变量,尽管我不建议这样做:

import Tkinter as Tk

frame = Tk.Frame(width=5, height=2, bd=1, relief=Tk.SUNKEN)
frame.grid(row=2,column=2)
frame.pack(fill=Tk.X, padx=5, pady=5)
def action():
    global output
    global variable
    output.insert(Tk.END,variable.get())
button = Tk.Button(master=frame, text='press', command=action)
button.pack()
variable = Tk.Entry(master=frame)
variable.pack()
output = Tk.Text(master=frame)
output.pack()

if __name__ == '__main__':
    Tk.mainloop()

我要做的是制作一个class其对象包含所需的每个变量和根据需要更改它们的方法:

import Tkinter as Tk
class Window:
    def __init__(self):
        self.frame = Tk.Frame(width=5, height=2, bd=1, relief=Tk.SUNKEN)
        self.frame.grid(row=2,column=2)
        self.frame.pack(fill=Tk.X, padx=5, pady=5)

        self.button = Tk.Button(master=self.frame, text='press', command=self.action)
        self.button.pack()

        self.variable = Tk.Entry(master=self.frame)
        self.variable.pack()

        self.output = Tk.Text(master=self.frame)
        self.output.pack()

    def action(self):
        self.output.insert(Tk.END,self.variable.get())

if __name__ == '__main__':
    window = Window()
    Tk.mainloop()

The reason it invokes the method immediately and pressing the button does nothing is that action(somenumber) is evaluated and its return value is attributed as the command for the button. So if action prints something to tell you it has run and returns None, you just run action to evaluate its return value and given None as the command for the button.

To have buttons to call functions with different arguments you can use global variables, although I can’t recommend it:

import Tkinter as Tk

frame = Tk.Frame(width=5, height=2, bd=1, relief=Tk.SUNKEN)
frame.grid(row=2,column=2)
frame.pack(fill=Tk.X, padx=5, pady=5)
def action():
    global output
    global variable
    output.insert(Tk.END,variable.get())
button = Tk.Button(master=frame, text='press', command=action)
button.pack()
variable = Tk.Entry(master=frame)
variable.pack()
output = Tk.Text(master=frame)
output.pack()

if __name__ == '__main__':
    Tk.mainloop()

What I would do is make a class whose objects would contain every variable required and methods to change those as needed:

import Tkinter as Tk
class Window:
    def __init__(self):
        self.frame = Tk.Frame(width=5, height=2, bd=1, relief=Tk.SUNKEN)
        self.frame.grid(row=2,column=2)
        self.frame.pack(fill=Tk.X, padx=5, pady=5)

        self.button = Tk.Button(master=self.frame, text='press', command=self.action)
        self.button.pack()

        self.variable = Tk.Entry(master=self.frame)
        self.variable.pack()

        self.output = Tk.Text(master=self.frame)
        self.output.pack()

    def action(self):
        self.output.insert(Tk.END,self.variable.get())

if __name__ == '__main__':
    window = Window()
    Tk.mainloop()

回答 6

button = Tk.Button(master=frame, text='press', command=lambda: action(someNumber))

我相信应该解决这个问题

button = Tk.Button(master=frame, text='press', command=lambda: action(someNumber))

I believe should fix this


回答 7

最好的做法是使用lambda,如下所示:

button = Tk.Button(master=frame, text='press', command=lambda: action(someNumber))

The best thing to do is use lambda as follows:

button = Tk.Button(master=frame, text='press', command=lambda: action(someNumber))

回答 8

我来晚了,但是这是完成它的一种非常简单的方法。

import tkinter as tk
def function1(param1, param2):
    print(str(param1) + str(param2))

var1 = "Hello "
var2 = "World!"
def function2():
    function1(var1, var2)

root = tk.Tk()

myButton = tk.Button(root, text="Button", command=function2)
root.mainloop()

您只需将要使用的功能包装到另一个功能中,然后在按下按钮时调用第二个功能。

I am extremely late, but here is a very simple way of accomplishing it.

import tkinter as tk
def function1(param1, param2):
    print(str(param1) + str(param2))

var1 = "Hello "
var2 = "World!"
def function2():
    function1(var1, var2)

root = tk.Tk()

myButton = tk.Button(root, text="Button", command=function2)
root.mainloop()

You simply wrap the function you want to use in another function and call the second function on the button press.


回答 9

Lambda很不错,但是您也可以尝试一下(在for循环中顺便说一句):

root = Tk()

dct = {"1": [*args], "2": [*args]}
def keypress(event):
    *args = dct[event.char]
    for arg in args:
        pass
for i in range(10):
    root.bind(str(i), keypress)

之所以起作用,是因为设置了绑定后,按键将事件作为参数传递。然后,您可以取消事件的属性,例如event.char获得“ 1”或“ UP”。如果您需要一个或多个事件属性以外的参数。只需创建一个字典来存储它们。

Lambdas are all well and good, but you can also try this (which works in a for loop btw):

root = Tk()

dct = {"1": [*args], "2": [*args]}
def keypress(event):
    *args = dct[event.char]
    for arg in args:
        pass
for i in range(10):
    root.bind(str(i), keypress)

This works because when the binding is set, a key press passes the event as an argument. You can then call attributes off the event like event.char to get “1” or “UP” ect. If you need an argument or multiple arguments other than the event attributes. just create a dictionary to store them.


回答 10

我也曾经遇到过这个问题。您可以只使用lambda:

button = Tk.Button(master=frame, text='press',command=lambda: action(someNumber))

I have encountered this problem before, too. You can just use lambda:

button = Tk.Button(master=frame, text='press',command=lambda: action(someNumber))

回答 11

如果您要执行更多操作,请使用lambda将条目数据传递给命令函数,例如:

event1 = Entry(master)
button1 = Button(master, text="OK", command=lambda: test_event(event1.get()))

def test_event(event_text):
    if not event_text:
        print("Nothing entered")
    else:
        print(str(event_text))
        #  do stuff

这会将事件中的信息传递给按钮功能。可能有更多类似Python的方式编写此代码,但这对我有用。

Use a lambda to pass the entry data to the command function if you have more actions to carry out, like this (I’ve tried to make it generic, so just adapt):

event1 = Entry(master)
button1 = Button(master, text="OK", command=lambda: test_event(event1.get()))

def test_event(event_text):
    if not event_text:
        print("Nothing entered")
    else:
        print(str(event_text))
        #  do stuff

This will pass the information in the event to the button function. There may be more Pythonesque ways of writing this, but it works for me.


回答 12

JasonPy-一些事情…

如果您将一个按钮粘在一个循环中,它将一遍又一遍地创建…这可能不是您想要的。(也许是)…

它总是获得最后一个索引的原因是单击它们时运行的lambda事件-而不是程序启动时。我不确定100%在做什么,但也许尝试在完成后存储值,然后稍后使用lambda按钮调用它。

例如:(不使用此代码,仅作为示例)

for entry in stuff_that_is_happening:
    value_store[entry] = stuff_that_is_happening

那你可以说…

button... command: lambda: value_store[1]

希望这可以帮助!

JasonPy – a few things…

if you stick a button in a loop it will be created over and over and over again… which is probably not what you want. (maybe it is)…

The reason it always gets the last index is lambda events run when you click them – not when the program starts. I’m not sure 100% what you are doing but maybe try storing the value when it’s made then call it later with the lambda button.

eg: (don’t use this code, just an example)

for entry in stuff_that_is_happening:
    value_store[entry] = stuff_that_is_happening

then you can say….

button... command: lambda: value_store[1]

hope this helps!


回答 13

一种简单的方法是button使用lambda以下语法进行配置:

button['command'] = lambda arg1 = local_var1, arg2 = local_var2 : function(arg1, arg2)

One simple way would be to configure button with lambda like the following syntax:

button['command'] = lambda arg1 = local_var1, arg2 = local_var2 : function(arg1, arg2)

回答 14

为了后代:您也可以使用类来实现类似的目的。例如:

class Function_Wrapper():
    def __init__(self, x, y, z):
        self.x, self.y, self.z = x, y, z
    def func(self):
        return self.x + self.y + self.z # execute function

然后可以通过以下方式简单地创建按钮:

instance1 = Function_Wrapper(x, y, z)
button1  = Button(master, text = "press", command = instance1.func)

这种方法还允许您通过设置来更改函数参数instance1.x = 3

For posterity: you can also use classes to achieve something similar. For instance:

class Function_Wrapper():
    def __init__(self, x, y, z):
        self.x, self.y, self.z = x, y, z
    def func(self):
        return self.x + self.y + self.z # execute function

Button can then be simply created by:

instance1 = Function_Wrapper(x, y, z)
button1  = Button(master, text = "press", command = instance1.func)

This approach also allows you to change the function arguments by i.e. setting instance1.x = 3.


回答 15

您需要使用 lambda:

button = Tk.Button(master=frame, text='press', command=lambda: action(someNumber))

You need to use lambda:

button = Tk.Button(master=frame, text='press', command=lambda: action(someNumber))

回答 16

使用lambda

import tkinter as tk

root = tk.Tk()
def go(text):
    print(text)

b = tk.Button(root, text="Click", command=lambda: go("hello"))
b.pack()
root.mainloop()

输出:

hello

Use lambda

import tkinter as tk

root = tk.Tk()
def go(text):
    print(text)

b = tk.Button(root, text="Click", command=lambda: go("hello"))
b.pack()
root.mainloop()

output:

hello

构造tkinter应用程序的最佳方法?

问题:构造tkinter应用程序的最佳方法?

以下是我典型的python tkinter程序的整体结构。

def funA():
    def funA1():
        def funA12():
            # stuff

    def funA2():
        # stuff

def funB():
    def funB1():
        # stuff

    def funB2():
        # stuff

def funC():
    def funC1():
        # stuff

    def funC2():
        # stuff


root = tk.Tk()

button1 = tk.Button(root, command=funA)
button1.pack()
button2 = tk.Button(root, command=funB)
button2.pack()
button3 = tk.Button(root, command=funC)
button3.pack()

funA funB并在用户单击按钮1、2、3时funC打开另一个Toplevel带有窗口小部件的窗口。

我想知道这是否是编写python tkinter程序的正确方法吗?当然,即使我这样写也可以,但这是最好的方法吗?这听起来很愚蠢,但是当我看到其他人编写的代码时,他们的代码并没有弄乱一堆函数,而且大多数情况下它们都有类。

有没有作为良好实践应遵循的特定结构?开始编写python程序之前,我应该如何计划?

我知道编程中没有最佳实践之类的东西,我也不需要。在我自己学习Python时,我只想一些建议和解释就可以使我保持正确的方向。

The following is the overall structure of my typical python tkinter program.

def funA():
    def funA1():
        def funA12():
            # stuff

    def funA2():
        # stuff

def funB():
    def funB1():
        # stuff

    def funB2():
        # stuff

def funC():
    def funC1():
        # stuff

    def funC2():
        # stuff


root = tk.Tk()

button1 = tk.Button(root, command=funA)
button1.pack()
button2 = tk.Button(root, command=funB)
button2.pack()
button3 = tk.Button(root, command=funC)
button3.pack()

funA funB and funC will bring up another Toplevel windows with widgets when user click on button 1, 2, 3.

I am wondering if this is the right way to write a python tkinter program? Sure, it will work even if I write this way, but is it the best way? It sounds stupid but when I see the codes other people written, their code is not messed up with bunch of functions and mostly they have classes.

Is there any specific structure that we should follow as good practice? How should I plan before start writing a python program?

I know there is no such thing as best practice in programming and I am not asking for it either. I just want some advice and explanations to keep me on the right direction as I am learning Python by myself.


回答 0

我主张一种面向对象的方法。这是我开始的模板:

# Use Tkinter for python 2, tkinter for python 3
import tkinter as tk

class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent

        <create the rest of your GUI here>

if __name__ == "__main__":
    root = tk.Tk()
    MainApplication(root).pack(side="top", fill="both", expand=True)
    root.mainloop()

需要注意的重要事项是:

  • 我不使用通配符导入。我将软件包导入为“ tk”,这要求我在所有命令前加上tk.。这样可以防止全局命名空间污染,并且在使用Tkinter类,ttk类或您自己的某些类时,使代码完全显而易见。

  • 主要应用是一类。这为您的所有回调和私有函数提供了私有命名空间,并且通常使组织代码更容易。在过程样式中,您必须自上而下进行编码,在使用函数之前定义函数等。使用此方法,您无需真正地在最后一步之前创建主窗口。我更喜欢从中继承,tk.Frame因为我通常从创建框架开始,但这绝不是必需的。

如果您的应用程序具有其他顶级窗口,建议您将每个窗口都设为一个单独的类,并从继承tk.Toplevel。这为您提供了上述所有相同的优点-窗口是原子的,它们具有自己的命名空间,并且代码井井有条。另外,一旦代码开始变大,就可以很容易地将每个模块放入自己的模块中。

最后,您可能要考虑对接口的每个主要部分使用类。例如,如果您要创建一个带有工具栏,导航窗格,状态栏和主区域的应用程序,则可以使每个类成为一个类。这使您的主要代码非常小,易于理解:

class Navbar(tk.Frame): ...
class Toolbar(tk.Frame): ...
class Statusbar(tk.Frame): ...
class Main(tk.Frame): ...

class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.statusbar = Statusbar(self, ...)
        self.toolbar = Toolbar(self, ...)
        self.navbar = Navbar(self, ...)
        self.main = Main(self, ...)

        self.statusbar.pack(side="bottom", fill="x")
        self.toolbar.pack(side="top", fill="x")
        self.navbar.pack(side="left", fill="y")
        self.main.pack(side="right", fill="both", expand=True)

由于所有这些实例共享一个公共父对象,因此该父对象实际上成为了模型-视图-控制器体系结构的“控制器”部分。因此,例如,主窗口可以通过调用在状态栏上放置一些内容self.parent.statusbar.set("Hello, world")。这使您可以在组件之间定义一个简单的接口,从而有助于保持最小的耦合。

I advocate an object oriented approach. This is the template that I start out with:

# Use Tkinter for python 2, tkinter for python 3
import tkinter as tk

class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent

        <create the rest of your GUI here>

if __name__ == "__main__":
    root = tk.Tk()
    MainApplication(root).pack(side="top", fill="both", expand=True)
    root.mainloop()

The important things to notice are:

  • I don’t use a wildcard import. I import the package as “tk”, which requires that I prefix all commands with tk.. This prevents global namespace pollution, plus it makes the code completely obvious when you are using Tkinter classes, ttk classes, or some of your own.

  • The main application is a class. This gives you a private namespace for all of your callbacks and private functions, and just generally makes it easier to organize your code. In a procedural style you have to code top-down, defining functions before using them, etc. With this method you don’t since you don’t actually create the main window until the very last step. I prefer inheriting from tk.Frame just because I typically start by creating a frame, but it is by no means necessary.

If your app has additional toplevel windows, I recommend making each of those a separate class, inheriting from tk.Toplevel. This gives you all of the same advantages mentioned above — the windows are atomic, they have their own namespace, and the code is well organized. Plus, it makes it easy to put each into its own module once the code starts to get large.

Finally, you might want to consider using classes for every major portion of your interface. For example, if you’re creating an app with a toolbar, a navigation pane, a statusbar, and a main area, you could make each one of those classes. This makes your main code quite small and easy to understand:

class Navbar(tk.Frame): ...
class Toolbar(tk.Frame): ...
class Statusbar(tk.Frame): ...
class Main(tk.Frame): ...

class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.statusbar = Statusbar(self, ...)
        self.toolbar = Toolbar(self, ...)
        self.navbar = Navbar(self, ...)
        self.main = Main(self, ...)

        self.statusbar.pack(side="bottom", fill="x")
        self.toolbar.pack(side="top", fill="x")
        self.navbar.pack(side="left", fill="y")
        self.main.pack(side="right", fill="both", expand=True)

Since all of those instances share a common parent, the parent effectively becomes the “controller” part of a model-view-controller architecture. So, for example, the main window could place something on the statusbar by calling self.parent.statusbar.set("Hello, world"). This allows you to define a simple interface between the components, helping to keep coupling to a minimun.


回答 1

将每个顶级窗口放入自己的单独类中,可以使代码重用并更好地组织代码。窗口中存在的任何按钮和相关方法都应在此类内定义。这是一个示例(从此处获取):

import tkinter as tk

class Demo1:
    def __init__(self, master):
        self.master = master
        self.frame = tk.Frame(self.master)
        self.button1 = tk.Button(self.frame, text = 'New Window', width = 25, command = self.new_window)
        self.button1.pack()
        self.frame.pack()
    def new_window(self):
        self.newWindow = tk.Toplevel(self.master)
        self.app = Demo2(self.newWindow)

class Demo2:
    def __init__(self, master):
        self.master = master
        self.frame = tk.Frame(self.master)
        self.quitButton = tk.Button(self.frame, text = 'Quit', width = 25, command = self.close_windows)
        self.quitButton.pack()
        self.frame.pack()
    def close_windows(self):
        self.master.destroy()

def main(): 
    root = tk.Tk()
    app = Demo1(root)
    root.mainloop()

if __name__ == '__main__':
    main()

另请参阅:

希望有帮助。

Putting each of your top-level windows into it’s own separate class gives you code re-use and better code organization. Any buttons and relevant methods that are present in the window should be defined inside this class. Here’s an example (taken from here):

import tkinter as tk

class Demo1:
    def __init__(self, master):
        self.master = master
        self.frame = tk.Frame(self.master)
        self.button1 = tk.Button(self.frame, text = 'New Window', width = 25, command = self.new_window)
        self.button1.pack()
        self.frame.pack()
    def new_window(self):
        self.newWindow = tk.Toplevel(self.master)
        self.app = Demo2(self.newWindow)

class Demo2:
    def __init__(self, master):
        self.master = master
        self.frame = tk.Frame(self.master)
        self.quitButton = tk.Button(self.frame, text = 'Quit', width = 25, command = self.close_windows)
        self.quitButton.pack()
        self.frame.pack()
    def close_windows(self):
        self.master.destroy()

def main(): 
    root = tk.Tk()
    app = Demo1(root)
    root.mainloop()

if __name__ == '__main__':
    main()

Also see:

Hope that helps.


回答 2

这不是一个坏结构。它会很好地工作。但是,当某人单击按钮或其他内容时,您必须在函数中具有执行命令的功能

因此,您可以为这些编写类,然后在该类中具有处理按钮单击等命令的方法。

这是一个例子:

import tkinter as tk

class Window1:
    def __init__(self, master):
        pass
        # Create labels, entries,buttons
    def button_click(self):
        pass
        # If button is clicked, run this method and open window 2


class Window2:
    def __init__(self, master):
        #create buttons,entries,etc

    def button_method(self):
        #run this when button click to close window
        self.master.destroy()

def main(): #run mianloop 
    root = tk.Tk()
    app = Window1(root)
    root.mainloop()

if __name__ == '__main__':
    main()

通常带有多个窗口的tk程序是多个大类,并且在__init__所有条目中创建标签等,然后每种方法都将处理按钮单击事件

只要有可读性,实际上就没有正确的方法,只要它对您有用并且可以完成工作,并且您可以轻松地解释它,因为如果您不能轻松地解释您的程序,那么可能会有更好的方法。

看一看《Tkinter中的思考》

This isn’t a bad structure; it will work just fine. However, you do have to have functions in a function to do commands when someone clicks on a button or something

So what you could do is write classes for these then have methods in the class that handle commands for the button clicks and such.

Here’s an example:

import tkinter as tk

class Window1:
    def __init__(self, master):
        pass
        # Create labels, entries,buttons
    def button_click(self):
        pass
        # If button is clicked, run this method and open window 2


class Window2:
    def __init__(self, master):
        #create buttons,entries,etc

    def button_method(self):
        #run this when button click to close window
        self.master.destroy()

def main(): #run mianloop 
    root = tk.Tk()
    app = Window1(root)
    root.mainloop()

if __name__ == '__main__':
    main()

Usually tk programs with multiple windows are multiple big classes and in the __init__ all the entries, labels etc are created and then each method is to handle button click events

There isn’t really a right way to do it, whatever works for you and gets the job done as long as its readable and you can easily explain it because if you cant easily explain your program, there probably is a better way to do it.

Take a look at Thinking in Tkinter.


回答 3

OOP应该是方法,frame应该是类变量而不是实例变量

from Tkinter import *
class App:
  def __init__(self, master):
    frame = Frame(master)
    frame.pack()
    self.button = Button(frame, 
                         text="QUIT", fg="red",
                         command=frame.quit)
    self.button.pack(side=LEFT)
    self.slogan = Button(frame,
                         text="Hello",
                         command=self.write_slogan)
    self.slogan.pack(side=LEFT)
  def write_slogan(self):
    print "Tkinter is easy to use!"

root = Tk()
app = App(root)
root.mainloop()

在此处输入图片说明

参考:http : //www.python-course.eu/tkinter_buttons.php

OOP should be the approach and frame should be a class variable instead of instance variable.

from Tkinter import *
class App:
  def __init__(self, master):
    frame = Frame(master)
    frame.pack()
    self.button = Button(frame, 
                         text="QUIT", fg="red",
                         command=frame.quit)
    self.button.pack(side=LEFT)
    self.slogan = Button(frame,
                         text="Hello",
                         command=self.write_slogan)
    self.slogan.pack(side=LEFT)
  def write_slogan(self):
    print "Tkinter is easy to use!"

root = Tk()
app = App(root)
root.mainloop()

enter image description here

Reference: http://www.python-course.eu/tkinter_buttons.php


回答 4

使用类对应用程序进行组织可以使您和与您一起工作的其他人轻松调试问题并轻松改进应用程序。

您可以像这样轻松地组织您的应用程序:

class hello(Tk):
    def __init__(self):
        super(hello, self).__init__()
        self.btn = Button(text = "Click me", command=close)
        self.btn.pack()
    def close():
        self.destroy()

app = hello()
app.mainloop()

Organizing your application using class make it easy to you and others who work with you to debug problems and improve the app easily.

You can easily organize your application like this:

class hello(Tk):
    def __init__(self):
        super(hello, self).__init__()
        self.btn = Button(text = "Click me", command=close)
        self.btn.pack()
    def close():
        self.destroy()

app = hello()
app.mainloop()

回答 5

学习如何构建程序的最佳方法可能是阅读他人的代码,尤其是如果这是许多人都为之贡献的大型程序。在查看了许多项目的代码之后,您应该了解共识样式应该是什么。

作为一种语言,Python的特殊之处在于,在如何格式化代码方面存在一些严格的指导原则。第一个是所谓的“ Python禅”:

  • 美丽胜于丑陋。
  • 显式胜于隐式。
  • 简单胜于复杂。
  • 复杂胜于复杂。
  • 扁平比嵌套更好。
  • 稀疏胜于密集。
  • 可读性很重要。
  • 特殊情况还不足以打破规则。
  • 尽管实用性胜过纯度。
  • 错误绝不能默默传递。
  • 除非明确地保持沉默。
  • 面对模棱两可的想法,拒绝猜测的诱惑。
  • 应该有一种-最好只有一种-显而易见的方法。
  • 尽管除非您是荷兰人,否则一开始这种方式可能并不明显。
  • 现在总比没有好。
  • 虽然从未往往比了。
  • 如果实现难以解释,那是个坏主意。
  • 如果实现易于解释,则可能是个好主意。
  • 命名空间是一个很棒的主意-让我们做更多这些吧!

在更实际的水平上,有Python的样式指南PEP8

考虑到这些,我会说您的代码风格并不适合,特别是嵌套函数。通过使用类或将它们移到单独的模块中,找到一种解决方案。这将使程序的结构更容易理解。

Probably the best way to learn how to structure your program is by reading other people’s code, especially if it’s a large program to which many people have contributed. After looking at the code of many projects, you should get an idea of what the consensus style should be.

Python, as a language, is special in that there are some strong guidelines as to how you should format your code. The first is the so-called “Zen of Python”:

  • Beautiful is better than ugly.
  • Explicit is better than implicit.
  • Simple is better than complex.
  • Complex is better than complicated.
  • Flat is better than nested.
  • Sparse is better than dense.
  • Readability counts.
  • Special cases aren’t special enough to break the rules.
  • Although practicality beats purity.
  • Errors should never pass silently.
  • Unless explicitly silenced.
  • In the face of ambiguity, refuse the temptation to guess.
  • There should be one– and preferably only one –obvious way to do it.
  • Although that way may not be obvious at first unless you’re Dutch.
  • Now is better than never.
  • Although never is often better than right now.
  • If the implementation is hard to explain, it’s a bad idea.
  • If the implementation is easy to explain, it may be a good idea.
  • Namespaces are one honking great idea — let’s do more of those!

On a more practical level, there is PEP8, the style guide for Python.

With those in mind, I would say that your code style doesn’t really fit, particularly the nested functions. Find a way to flatten those out, either by using classes or moving them into separate modules. This will make the structure of your program much easier to understand.


回答 6

我个人不使用面向对象的方法,主要是因为a)只会妨碍;b)您永远不会将其作为模块重复使用。

但是这里没有讨论的是必须使用线程或多处理。总是。否则您的应用程序将很糟糕。

只需做一个简单的测试:启动一个窗口,然后获取一些URL或其他内容。所做的更改是在网络请求发生时不会更新您的用户界面。意思是,您的应用程序窗口将被破坏。取决于您所使用的操作系统,但是大多数情况下,它不会重绘,在窗口上拖动的任何内容都将粘贴在它上面,直到该过程返回到TK mainloop。

I personally do not use the objected oriented approach, mostly because it a) only get in the way; b) you will never reuse that as a module.

but something that is not discussed here, is that you must use threading or multiprocessing. Always. otherwise your application will be awful.

just do a simple test: start a window, and then fetch some URL or anything else. changes are your UI will not be updated while the network request is happening. Meaning, your application window will be broken. depend on the OS you are on, but most times, it will not redraw, anything you drag over the window will be plastered on it, until the process is back to the TK mainloop.


如何在Tkinter中处理窗口关闭事件?

问题:如何在Tkinter中处理窗口关闭事件?

如何在Python Tkinter程序中处理窗口关闭事件(用户单击“ X”按钮)?

How do I handle the window close event (user clicking the ‘X’ button) in a Python Tkinter program?


回答 0

Tkinter支持一种称为协议处理程序的机制。在这里,术语协议是指应用程序和窗口管理器之间的交互。最常用的协议称为WM_DELETE_WINDOW,用于定义当用户使用窗口管理器显式关闭窗口时发生的情况。

您可以使用该protocol方法为该协议安装处理程序(窗口小部件必须是TkToplevel窗口小部件):

这里有一个具体的例子:

import tkinter as tk
from tkinter import messagebox

root = tk.Tk()

def on_closing():
    if messagebox.askokcancel("Quit", "Do you want to quit?"):
        root.destroy()

root.protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()

Tkinter supports a mechanism called protocol handlers. Here, the term protocol refers to the interaction between the application and the window manager. The most commonly used protocol is called WM_DELETE_WINDOW, and is used to define what happens when the user explicitly closes a window using the window manager.

You can use the protocol method to install a handler for this protocol (the widget must be a Tk or Toplevel widget):

Here you have a concrete example:

import tkinter as tk
from tkinter import messagebox

root = tk.Tk()

def on_closing():
    if messagebox.askokcancel("Quit", "Do you want to quit?"):
        root.destroy()

root.protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()

回答 1

马特展示了关闭按钮的一种经典修改。
另一种是使关闭按钮最小化窗口。
您可以通过将iconify方法
作为协议方法的第二个参数来重现此行为。

这是一个在Windows 7和10上测试过的有效示例:

# Python 3
import tkinter
import tkinter.scrolledtext as scrolledtext

root = tkinter.Tk()
# make the top right close button minimize (iconify) the main window
root.protocol("WM_DELETE_WINDOW", root.iconify)
# make Esc exit the program
root.bind('<Escape>', lambda e: root.destroy())

# create a menu bar with an Exit command
menubar = tkinter.Menu(root)
filemenu = tkinter.Menu(menubar, tearoff=0)
filemenu.add_command(label="Exit", command=root.destroy)
menubar.add_cascade(label="File", menu=filemenu)
root.config(menu=menubar)

# create a Text widget with a Scrollbar attached
txt = scrolledtext.ScrolledText(root, undo=True)
txt['font'] = ('consolas', '12')
txt.pack(expand=True, fill='both')

root.mainloop()

在此示例中,我们为用户提供了两个新的退出选项:
经典的File→Exit,以及Esc按钮。

Matt has shown one classic modification of the close button.
The other is to have the close button minimize the window.
You can reproduced this behavior by having the iconify method
be the protocol method’s second argument.

Here’s a working example, tested on Windows 7 & 10:

# Python 3
import tkinter
import tkinter.scrolledtext as scrolledtext

root = tkinter.Tk()
# make the top right close button minimize (iconify) the main window
root.protocol("WM_DELETE_WINDOW", root.iconify)
# make Esc exit the program
root.bind('<Escape>', lambda e: root.destroy())

# create a menu bar with an Exit command
menubar = tkinter.Menu(root)
filemenu = tkinter.Menu(menubar, tearoff=0)
filemenu.add_command(label="Exit", command=root.destroy)
menubar.add_cascade(label="File", menu=filemenu)
root.config(menu=menubar)

# create a Text widget with a Scrollbar attached
txt = scrolledtext.ScrolledText(root, undo=True)
txt['font'] = ('consolas', '12')
txt.pack(expand=True, fill='both')

root.mainloop()

In this example we give the user two new exit options:
the classic File → Exit, and also the Esc button.


回答 2

取决于Tkinter活动,尤其是在使用Tkinter.after时,使用destroy()-即使通过使用protocol(),按钮等停止该活动也会干扰该活动(“执行时出错”),而不仅仅是终止它。在几乎每种情况下,最好的解决方案是使用标志。这是一个简单,愚蠢的用法示例(尽管我敢肯定,你们大多数人都不需要它!

from Tkinter import *

def close_window():
  global running
  running = False  # turn off while loop
  print( "Window closed")

root = Tk()
root.protocol("WM_DELETE_WINDOW", close_window)
cv = Canvas(root, width=200, height=200)
cv.pack()

running = True;
# This is an endless loop stopped only by setting 'running' to 'False'
while running: 
  for i in range(200): 
    if not running: 
        break
    cv.create_oval(i, i, i+1, i+1)
    root.update() 

这样可以很好地终止图形活动。您只需要running在正确的位置检查即可。

Depending on the Tkinter activity, and especially when using Tkinter.after, stopping this activity with destroy() — even by using protocol(), a button, etc. — will disturb this activity (“while executing” error) rather than just terminate it. The best solution in almost every case is to use a flag. Here is a simple, silly example of how to use it (although I am certain that most of you don’t need it! :)

from Tkinter import *

def close_window():
  global running
  running = False  # turn off while loop
  print( "Window closed")

root = Tk()
root.protocol("WM_DELETE_WINDOW", close_window)
cv = Canvas(root, width=200, height=200)
cv.pack()

running = True;
# This is an endless loop stopped only by setting 'running' to 'False'
while running: 
  for i in range(200): 
    if not running: 
        break
    cv.create_oval(i, i, i+1, i+1)
    root.update() 

This terminates graphics activity nicely. You only need to check running at the right place(s).


回答 3

我要感谢Apostolos的回答,这一点引起了我的注意。这是2019年Python 3的更加详细的示例,带有更清晰的描述和示例代码。


注意以下事实destroy():(或完全没有自定义窗口关闭处理程序)将在用户关闭窗口时立即销毁窗口及其所有正在运行的回调

这可能对您不利,具体取决于您当前的Tkinter活动,尤其是在使用tkinter.after(定期回调)时。您可能正在使用处理一些数据并将其写入磁盘的回调…在这种情况下,您显然希望数据写入完成而不会被突然终止。

最好的解决方案是使用标志。因此,当用户请求关闭窗口时,可以将其标记为一个标志,然后对其进行响应。

(注意:我通常将GUI设计为封装良好的类和单独的工作线程,并且我绝对不使用“全局”(我改用类实例变量),但这只是一个简单的示例,用于演示当用户关闭窗口时,Tk如何突然终止您的定期回调…)

from tkinter import *
import time

# Try setting this to False and look at the printed numbers (1 to 10)
# during the work-loop, if you close the window while the periodic_call
# worker is busy working (printing). It will abruptly end the numbers,
# and kill the periodic callback! That's why you should design most
# applications with a safe closing callback as described in this demo.
safe_closing = True

# ---------

busy_processing = False
close_requested = False

def close_window():
    global close_requested
    close_requested = True
    print("User requested close at:", time.time(), "Was busy processing:", busy_processing)

root = Tk()
if safe_closing:
    root.protocol("WM_DELETE_WINDOW", close_window)
lbl = Label(root)
lbl.pack()

def periodic_call():
    global busy_processing

    if not close_requested:
        busy_processing = True
        for i in range(10):
            print((i+1), "of 10")
            time.sleep(0.2)
            lbl["text"] = str(time.time()) # Will error if force-closed.
            root.update() # Force redrawing since we change label multiple times in a row.
        busy_processing = False
        root.after(500, periodic_call)
    else:
        print("Destroying GUI at:", time.time())
        try: # "destroy()" can throw, so you should wrap it like this.
            root.destroy()
        except:
            # NOTE: In most code, you'll wanna force a close here via
            # "exit" if the window failed to destroy. Just ensure that
            # you have no code after your `mainloop()` call (at the
            # bottom of this file), since the exit call will cause the
            # process to terminate immediately without running any more
            # code. Of course, you should NEVER have code after your
            # `mainloop()` call in well-designed code anyway...
            # exit(0)
            pass

root.after_idle(periodic_call)
root.mainloop()

此代码将向您显示WM_DELETE_WINDOW即使在periodic_call()工作/循环中间我们的自定义繁忙时,处理程序也会运行!

我们使用一些相当夸张的.after()值:500毫秒。这只是为了使它很容易让你看到关闭的区别,而周期性呼叫占线,或不…如果关闭,而数字更新,你会看到WM_DELETE_WINDOW发生了,而你的定期电话“是忙处理:是”。如果您在数字暂停时关闭(这意味着此时不处理定期回调),则会看到关闭发生在“不忙”期间。

在实际使用中,您.after()将使用30到100毫秒左右的时间来获得响应式GUI。这只是一个演示,可以帮助您了解如何保护自己免受Tk的默认“关闭时立即中断所有工作”的行为。

总结:让WM_DELETE_WINDOW处理程序设置一个标志,然后.destroy()在安全的情况下(当您的应用程序完成所有工作时)定期并手动检查该标志。

PS:您还可以使用WM_DELETE_WINDOW询问用户是否真的要关闭的窗口; 如果他们回答“否”,则无需设置标志。非常简单 您只需要在自己的信箱中显示一个消息框,WM_DELETE_WINDOW然后根据用户的答案设置标志即可。

I’d like to thank the answer by Apostolos for bringing this to my attention. Here’s a much more detailed example for Python 3 in the year 2019, with a clearer description and example code.


Beware of the fact that destroy() (or not having a custom window closing handler at all) will destroy the window and all of its running callbacks instantly when the user closes it.

This can be bad for you, depending on your current Tkinter activity, and especially when using tkinter.after (periodic callbacks). You might be using a callback which processes some data and writes to disk… in that case, you obviously want the data writing to finish without being abruptly killed.

The best solution for that is to use a flag. So when the user requests window closing, you mark that as a flag, and then react to it.

(Note: I normally design GUIs as nicely encapsulated classes and separate worker threads, and I definitely don’t use “global” (I use class instance variables instead), but this is meant to be a simple, stripped-down example to demonstrate how Tk abruptly kills your periodic callbacks when the user closes the window…)

from tkinter import *
import time

# Try setting this to False and look at the printed numbers (1 to 10)
# during the work-loop, if you close the window while the periodic_call
# worker is busy working (printing). It will abruptly end the numbers,
# and kill the periodic callback! That's why you should design most
# applications with a safe closing callback as described in this demo.
safe_closing = True

# ---------

busy_processing = False
close_requested = False

def close_window():
    global close_requested
    close_requested = True
    print("User requested close at:", time.time(), "Was busy processing:", busy_processing)

root = Tk()
if safe_closing:
    root.protocol("WM_DELETE_WINDOW", close_window)
lbl = Label(root)
lbl.pack()

def periodic_call():
    global busy_processing

    if not close_requested:
        busy_processing = True
        for i in range(10):
            print((i+1), "of 10")
            time.sleep(0.2)
            lbl["text"] = str(time.time()) # Will error if force-closed.
            root.update() # Force redrawing since we change label multiple times in a row.
        busy_processing = False
        root.after(500, periodic_call)
    else:
        print("Destroying GUI at:", time.time())
        try: # "destroy()" can throw, so you should wrap it like this.
            root.destroy()
        except:
            # NOTE: In most code, you'll wanna force a close here via
            # "exit" if the window failed to destroy. Just ensure that
            # you have no code after your `mainloop()` call (at the
            # bottom of this file), since the exit call will cause the
            # process to terminate immediately without running any more
            # code. Of course, you should NEVER have code after your
            # `mainloop()` call in well-designed code anyway...
            # exit(0)
            pass

root.after_idle(periodic_call)
root.mainloop()

This code will show you that the WM_DELETE_WINDOW handler runs even while our custom periodic_call() is busy in the middle of work/loops!

We use some pretty exaggerated .after() values: 500 milliseconds. This is just meant to make it very easy for you to see the difference between closing while the periodic call is busy, or not… If you close while the numbers are updating, you will see that the WM_DELETE_WINDOW happened while your periodic call “was busy processing: True”. If you close while the numbers are paused (meaning that the periodic callback isn’t processing at that moment), you see that the close happened while it’s “not busy”.

In real-world usage, your .after() would use something like 30-100 milliseconds, to have a responsive GUI. This is just a demonstration to help you understand how to protect yourself against Tk’s default “instantly interrupt all work when closing” behavior.

In summary: Make the WM_DELETE_WINDOW handler set a flag, and then check that flag periodically and manually .destroy() the window when it’s safe (when your app is done with all work).

PS: You can also use WM_DELETE_WINDOW to ask the user if they REALLY want to close the window; and if they answer no, you don’t set the flag. It’s very simple. You just show a messagebox in your WM_DELETE_WINDOW and set the flag based on the user’s answer.


回答 4

尝试简单版本:

import tkinter

window = Tk()

closebutton = Button(window, text='X', command=window.destroy)
closebutton.pack()

window.mainloop()

或者,如果您想添加更多命令:

import tkinter

window = Tk()


def close():
    window.destroy()
    #More Functions


closebutton = Button(window, text='X', command=close)
closebutton.pack()

window.mainloop()

Try The Simple Version:

import tkinter

window = Tk()

closebutton = Button(window, text='X', command=window.destroy)
closebutton.pack()

window.mainloop()

Or If You Want To Add More Commands:

import tkinter

window = Tk()


def close():
    window.destroy()
    #More Functions


closebutton = Button(window, text='X', command=close)
closebutton.pack()

window.mainloop()

回答 5

from tkinter import*
root=Tk()
exit_button=Button(root,text="X",command=root.quit)
root.mainloop()
from tkinter import*
root=Tk()
exit_button=Button(root,text="X",command=root.quit)
root.mainloop()

您如何在Tkinter的事件循环中运行自己的代码?

问题:您如何在Tkinter的事件循环中运行自己的代码?

我的弟弟刚刚开始​​编程,对于他的Science Fair项目,他正在模拟天空中一群鸟。他得到了大部分他写的代码,它工作得很好,但鸟儿需要移动的每一刻

但是,Tkinter浪费了自己的事件循环的时间,因此他的代码无法运行。这样root.mainloop()运行,运行,并保持运行,并且它运行的唯一事情是事件处理程序。

有没有一种方法可以让他的代码与mainloop一起运行(没有多线程,这很令人困惑,应该保持简单),如果这样,那是什么?

现在,他想出了一个丑陋的方法,将其move()功能绑定到<b1-motion>,这样,只要按住按钮并摇动鼠标,它就可以工作。但是必须有一个更好的方法。

My little brother is just getting into programming, and for his Science Fair project, he’s doing a simulation of a flock of birds in the sky. He’s gotten most of his code written, and it works nicely, but the birds need to move every moment.

Tkinter, however, hogs the time for its own event loop, and so his code won’t run. Doing root.mainloop() runs, runs, and keeps running, and the only thing it runs is the event handlers.

Is there a way to have his code run alongside the mainloop (without multithreading, it’s confusing and this should be kept simple), and if so, what is it?

Right now, he came up with an ugly hack, tying his move() function to <b1-motion>, so that as long as he holds the button down and wiggles the mouse, it works. But there’s got to be a better way.


回答 0

afterTk对象上使用方法:

from tkinter import *

root = Tk()

def task():
    print("hello")
    root.after(2000, task)  # reschedule event in 2 seconds

root.after(2000, task)
root.mainloop()

这是该after方法的声明和文档:

def after(self, ms, func=None, *args):
    """Call function once after given time.

    MS specifies the time in milliseconds. FUNC gives the
    function which shall be called. Additional parameters
    are given as parameters to the function call.  Return
    identifier to cancel scheduling with after_cancel."""

Use the after method on the Tk object:

from tkinter import *

root = Tk()

def task():
    print("hello")
    root.after(2000, task)  # reschedule event in 2 seconds

root.after(2000, task)
root.mainloop()

Here’s the declaration and documentation for the after method:

def after(self, ms, func=None, *args):
    """Call function once after given time.

    MS specifies the time in milliseconds. FUNC gives the
    function which shall be called. Additional parameters
    are given as parameters to the function call.  Return
    identifier to cancel scheduling with after_cancel."""

回答 1

Bjorn发布解决方案在我的计算机(RedHat Enterprise 5,python 2.6.1)上显示“ RuntimeError:从不同的公寓呼叫Tcl”消息。Bjorn可能没有得到此消息,因为据我检查过的一个地方,使用Tkinter处理线程是不可预测的且依赖于平台。

问题似乎是可以将其app.start()视为对Tk的引用,因为app包含Tk元素。我通过替换app.start()self.start()inside来解决此问题__init__。我也这样做了,以便所有Tk引用都在调用mainloop()函数内,或者在调用的函数所调用的函数内mainloop()(这对于避免“不同单元”错误很关键)。

最后,我添加了带有回调的协议处理程序,因为如果没有此程序,当用户关闭Tk窗口时,程序将退出并出现错误。

修改后的代码如下:

# Run tkinter code in another thread

import tkinter as tk
import threading

class App(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)
        self.start()

    def callback(self):
        self.root.quit()

    def run(self):
        self.root = tk.Tk()
        self.root.protocol("WM_DELETE_WINDOW", self.callback)

        label = tk.Label(self.root, text="Hello World")
        label.pack()

        self.root.mainloop()


app = App()
print('Now we can continue running code while mainloop runs!')

for i in range(100000):
    print(i)

The solution posted by Bjorn results in a “RuntimeError: Calling Tcl from different appartment” message on my computer (RedHat Enterprise 5, python 2.6.1). Bjorn might not have gotten this message, since, according to one place I checked, mishandling threading with Tkinter is unpredictable and platform-dependent.

The problem seems to be that app.start() counts as a reference to Tk, since app contains Tk elements. I fixed this by replacing app.start() with a self.start() inside __init__. I also made it so that all Tk references are either inside the function that calls mainloop() or are inside functions that are called by the function that calls mainloop() (this is apparently critical to avoid the “different apartment” error).

Finally, I added a protocol handler with a callback, since without this the program exits with an error when the Tk window is closed by the user.

The revised code is as follows:

# Run tkinter code in another thread

import tkinter as tk
import threading

class App(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)
        self.start()

    def callback(self):
        self.root.quit()

    def run(self):
        self.root = tk.Tk()
        self.root.protocol("WM_DELETE_WINDOW", self.callback)

        label = tk.Label(self.root, text="Hello World")
        label.pack()

        self.root.mainloop()


app = App()
print('Now we can continue running code while mainloop runs!')

for i in range(100000):
    print(i)

回答 2

在编写自己的循环时,就像在模拟中一样(我假设),您需要调用执行update功能的函数mainloop:使用所做的更改来更新窗口,但是您需要在循环中进行操作。

def task():
   # do something
   root.update()

while 1:
   task()  

When writing your own loop, as in the simulation (I assume), you need to call the update function which does what the mainloop does: updates the window with your changes, but you do it in your loop.

def task():
   # do something
   root.update()

while 1:
   task()  

回答 3

另一个选择是让tkinter在单独的线程上执行。一种方法是这样的:

import Tkinter
import threading

class MyTkApp(threading.Thread):
    def __init__(self):
        self.root=Tkinter.Tk()
        self.s = Tkinter.StringVar()
        self.s.set('Foo')
        l = Tkinter.Label(self.root,textvariable=self.s)
        l.pack()
        threading.Thread.__init__(self)

    def run(self):
        self.root.mainloop()


app = MyTkApp()
app.start()

# Now the app should be running and the value shown on the label
# can be changed by changing the member variable s.
# Like this:
# app.s.set('Bar')

但是要小心,多线程编程很难,而且很容易使自己陷入困境。例如,当您更改上面的示例类的成员变量时,请务必小心,以免打断Tkinter的事件循环。

Another option is to let tkinter execute on a separate thread. One way of doing it is like this:

import Tkinter
import threading

class MyTkApp(threading.Thread):
    def __init__(self):
        self.root=Tkinter.Tk()
        self.s = Tkinter.StringVar()
        self.s.set('Foo')
        l = Tkinter.Label(self.root,textvariable=self.s)
        l.pack()
        threading.Thread.__init__(self)

    def run(self):
        self.root.mainloop()


app = MyTkApp()
app.start()

# Now the app should be running and the value shown on the label
# can be changed by changing the member variable s.
# Like this:
# app.s.set('Bar')

Be careful though, multithreaded programming is hard and it is really easy to shoot your self in the foot. For example you have to be careful when you change member variables of the sample class above so you don’t interrupt with the event loop of Tkinter.


回答 4

这是GPS读取器和数据演示器的第一个工作版本。tkinter是一件非常脆弱的事情,错误消息很少。它不会塞满东西,也无法说明为什么要花费很多时间。一个好的WYSIWYG表单开发者非常困难。无论如何,这将运行一个小的例程,每秒10次,并在表格上显示信息。花了一段时间才能实现。当我尝试将计时器值设置为0时,表单就永远不会出现。我的头现在好痛!每秒10次或更多次对我来说足够了。我希望它可以帮助其他人。迈克·莫罗

import tkinter as tk
import time

def GetDateTime():
  # Get current date and time in ISO8601
  # https://en.wikipedia.org/wiki/ISO_8601 
  # https://xkcd.com/1179/
  return (time.strftime("%Y%m%d", time.gmtime()),
          time.strftime("%H%M%S", time.gmtime()),
          time.strftime("%Y%m%d", time.localtime()),
          time.strftime("%H%M%S", time.localtime()))

class Application(tk.Frame):

  def __init__(self, master):

    fontsize = 12
    textwidth = 9

    tk.Frame.__init__(self, master)
    self.pack()

    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             text='Local Time').grid(row=0, column=0)
    self.LocalDate = tk.StringVar()
    self.LocalDate.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             textvariable=self.LocalDate).grid(row=0, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             text='Local Date').grid(row=1, column=0)
    self.LocalTime = tk.StringVar()
    self.LocalTime.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             textvariable=self.LocalTime).grid(row=1, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             text='GMT Time').grid(row=2, column=0)
    self.nowGdate = tk.StringVar()
    self.nowGdate.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             textvariable=self.nowGdate).grid(row=2, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             text='GMT Date').grid(row=3, column=0)
    self.nowGtime = tk.StringVar()
    self.nowGtime.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             textvariable=self.nowGtime).grid(row=3, column=1)

    tk.Button(self, text='Exit', width = 10, bg = '#FF8080', command=root.destroy).grid(row=4, columnspan=2)

    self.gettime()
  pass

  def gettime(self):
    gdt, gtm, ldt, ltm = GetDateTime()
    gdt = gdt[0:4] + '/' + gdt[4:6] + '/' + gdt[6:8]
    gtm = gtm[0:2] + ':' + gtm[2:4] + ':' + gtm[4:6] + ' Z'  
    ldt = ldt[0:4] + '/' + ldt[4:6] + '/' + ldt[6:8]
    ltm = ltm[0:2] + ':' + ltm[2:4] + ':' + ltm[4:6]  
    self.nowGtime.set(gdt)
    self.nowGdate.set(gtm)
    self.LocalTime.set(ldt)
    self.LocalDate.set(ltm)

    self.after(100, self.gettime)
   #print (ltm)  # Prove it is running this and the external code, too.
  pass

root = tk.Tk()
root.wm_title('Temp Converter')
app = Application(master=root)

w = 200 # width for the Tk root
h = 125 # height for the Tk root

# get display screen width and height
ws = root.winfo_screenwidth()  # width of the screen
hs = root.winfo_screenheight() # height of the screen

# calculate x and y coordinates for positioning the Tk root window

#centered
#x = (ws/2) - (w/2)
#y = (hs/2) - (h/2)

#right bottom corner (misfires in Win10 putting it too low. OK in Ubuntu)
x = ws - w
y = hs - h - 35  # -35 fixes it, more or less, for Win10

#set the dimensions of the screen and where it is placed
root.geometry('%dx%d+%d+%d' % (w, h, x, y))

root.mainloop()

This is the first working version of what will be a GPS reader and data presenter. tkinter is a very fragile thing with way too few error messages. It does not put stuff up and does not tell why much of the time. Very difficult coming from a good WYSIWYG form developer. Anyway, this runs a small routine 10 times a second and presents the information on a form. Took a while to make it happen. When I tried a timer value of 0, the form never came up. My head now hurts! 10 or more times per second is good enough for me. I hope it helps someone else. Mike Morrow

import tkinter as tk
import time

def GetDateTime():
  # Get current date and time in ISO8601
  # https://en.wikipedia.org/wiki/ISO_8601 
  # https://xkcd.com/1179/
  return (time.strftime("%Y%m%d", time.gmtime()),
          time.strftime("%H%M%S", time.gmtime()),
          time.strftime("%Y%m%d", time.localtime()),
          time.strftime("%H%M%S", time.localtime()))

class Application(tk.Frame):

  def __init__(self, master):

    fontsize = 12
    textwidth = 9

    tk.Frame.__init__(self, master)
    self.pack()

    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             text='Local Time').grid(row=0, column=0)
    self.LocalDate = tk.StringVar()
    self.LocalDate.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             textvariable=self.LocalDate).grid(row=0, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             text='Local Date').grid(row=1, column=0)
    self.LocalTime = tk.StringVar()
    self.LocalTime.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             textvariable=self.LocalTime).grid(row=1, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             text='GMT Time').grid(row=2, column=0)
    self.nowGdate = tk.StringVar()
    self.nowGdate.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             textvariable=self.nowGdate).grid(row=2, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             text='GMT Date').grid(row=3, column=0)
    self.nowGtime = tk.StringVar()
    self.nowGtime.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             textvariable=self.nowGtime).grid(row=3, column=1)

    tk.Button(self, text='Exit', width = 10, bg = '#FF8080', command=root.destroy).grid(row=4, columnspan=2)

    self.gettime()
  pass

  def gettime(self):
    gdt, gtm, ldt, ltm = GetDateTime()
    gdt = gdt[0:4] + '/' + gdt[4:6] + '/' + gdt[6:8]
    gtm = gtm[0:2] + ':' + gtm[2:4] + ':' + gtm[4:6] + ' Z'  
    ldt = ldt[0:4] + '/' + ldt[4:6] + '/' + ldt[6:8]
    ltm = ltm[0:2] + ':' + ltm[2:4] + ':' + ltm[4:6]  
    self.nowGtime.set(gdt)
    self.nowGdate.set(gtm)
    self.LocalTime.set(ldt)
    self.LocalDate.set(ltm)

    self.after(100, self.gettime)
   #print (ltm)  # Prove it is running this and the external code, too.
  pass

root = tk.Tk()
root.wm_title('Temp Converter')
app = Application(master=root)

w = 200 # width for the Tk root
h = 125 # height for the Tk root

# get display screen width and height
ws = root.winfo_screenwidth()  # width of the screen
hs = root.winfo_screenheight() # height of the screen

# calculate x and y coordinates for positioning the Tk root window

#centered
#x = (ws/2) - (w/2)
#y = (hs/2) - (h/2)

#right bottom corner (misfires in Win10 putting it too low. OK in Ubuntu)
x = ws - w
y = hs - h - 35  # -35 fixes it, more or less, for Win10

#set the dimensions of the screen and where it is placed
root.geometry('%dx%d+%d+%d' % (w, h, x, y))

root.mainloop()

如何在Python中创建一个简单的消息框?

问题:如何在Python中创建一个简单的消息框?

我正在寻找与alert()JavaScript 相同的效果。

我今天下午使用Twisted.web编写了一个基于Web的简单解释器。您基本上是通过表单提交Python代码块的,客户端来抓取并执行它。我希望能够做出一个简单的弹出消息,而不必每次都重写一堆样板的wxPython或TkInter代码(因为该代码是通过表单提交的,然后消失了)。

我尝试过tkMessageBox:

import tkMessageBox
tkMessageBox.showinfo(title="Greetings", message="Hello World!")

但这会在后台用tk图标打开另一个窗口。我不要这个 我一直在寻找一些简单的wxPython代码,但是它总是需要设置一个类并进入应用程序循环等。在Python中没有简单易行的方法来制作消息框吗?

I’m looking for the same effect as alert() in JavaScript.

I wrote a simple web-based interpreter this afternoon using Twisted.web. You basically submit a block of Python code through a form, and the client comes and grabs it and executes it. I want to be able to make a simple popup message, without having to re-write a whole bunch of boilerplate wxPython or TkInter code every time (since the code gets submitted through a form and then disappears).

I’ve tried tkMessageBox:

import tkMessageBox
tkMessageBox.showinfo(title="Greetings", message="Hello World!")

but this opens another window in the background with a tk icon. I don’t want this. I was looking for some simple wxPython code but it always required setting up a class and entering an app loop etc. Is there no simple, catch-free way of making a message box in Python?


回答 0

您可以使用导入和单行代码,如下所示:

import ctypes  # An included library with Python install.   
ctypes.windll.user32.MessageBoxW(0, "Your text", "Your title", 1)

或定义一个函数(Mbox),如下所示:

import ctypes  # An included library with Python install.
def Mbox(title, text, style):
    return ctypes.windll.user32.MessageBoxW(0, text, title, style)
Mbox('Your title', 'Your text', 1)

注意样式如下:

##  Styles:
##  0 : OK
##  1 : OK | Cancel
##  2 : Abort | Retry | Ignore
##  3 : Yes | No | Cancel
##  4 : Yes | No
##  5 : Retry | No 
##  6 : Cancel | Try Again | Continue

玩得开心!

注意:已编辑以MessageBoxW代替MessageBoxA

You could use an import and single line code like this:

import ctypes  # An included library with Python install.   
ctypes.windll.user32.MessageBoxW(0, "Your text", "Your title", 1)

Or define a function (Mbox) like so:

import ctypes  # An included library with Python install.
def Mbox(title, text, style):
    return ctypes.windll.user32.MessageBoxW(0, text, title, style)
Mbox('Your title', 'Your text', 1)

Note the styles are as follows:

##  Styles:
##  0 : OK
##  1 : OK | Cancel
##  2 : Abort | Retry | Ignore
##  3 : Yes | No | Cancel
##  4 : Yes | No
##  5 : Retry | Cancel 
##  6 : Cancel | Try Again | Continue

Have fun!

Note: edited to use MessageBoxW instead of MessageBoxA


回答 1

你看过easygui吗?

import easygui

easygui.msgbox("This is a message!", title="simple gui")

Have you looked at easygui?

import easygui

easygui.msgbox("This is a message!", title="simple gui")

回答 2

另外,您可以在撤消另一个窗口之前先放置它,以便放置消息

#!/usr/bin/env python

from Tkinter import *
import tkMessageBox

window = Tk()
window.wm_withdraw()

#message at x:200,y:200
window.geometry("1x1+200+200")#remember its .geometry("WidthxHeight(+or-)X(+or-)Y")
tkMessageBox.showerror(title="error",message="Error Message",parent=window)

#centre screen message
window.geometry("1x1+"+str(window.winfo_screenwidth()/2)+"+"+str(window.winfo_screenheight()/2))
tkMessageBox.showinfo(title="Greetings", message="Hello World!")

Also you can position the other window before withdrawing it so that you position your message

#!/usr/bin/env python

from Tkinter import *
import tkMessageBox

window = Tk()
window.wm_withdraw()

#message at x:200,y:200
window.geometry("1x1+200+200")#remember its .geometry("WidthxHeight(+or-)X(+or-)Y")
tkMessageBox.showerror(title="error",message="Error Message",parent=window)

#centre screen message
window.geometry("1x1+"+str(window.winfo_screenwidth()/2)+"+"+str(window.winfo_screenheight()/2))
tkMessageBox.showinfo(title="Greetings", message="Hello World!")

回答 3

您提供的代码很好!您只需要使用以下代码显式创建“背景中的其他窗口”并将其隐藏:

import Tkinter
window = Tkinter.Tk()
window.wm_withdraw()

就在您的消息框之前。

The code you presented is fine! You just need to explicitly create the “other window in the background” and hide it, with this code:

import Tkinter
window = Tkinter.Tk()
window.wm_withdraw()

Right before your messagebox.


回答 4

PyMsgBox模块正是这样做的。它具有遵循JavaScript命名约定的消息框功能:alert(),confirm(),prompt()和password()(后者是hint(),但键入时使用*)。这些函数调用将阻塞,直到用户单击“确定” /“取消”按钮为止。这是一个无依赖的跨平台纯Python模块。

安装方式: pip install PyMsgBox

用法示例:

import pymsgbox
pymsgbox.alert('This is an alert!', 'Title')
response = pymsgbox.prompt('What is your name?')

完整文档位于http://pymsgbox.readthedocs.org/en/latest/

The PyMsgBox module does exactly this. It has message box functions that follow the naming conventions of JavaScript: alert(), confirm(), prompt() and password() (which is prompt() but uses * when you type). These function calls block until the user clicks an OK/Cancel button. It’s a cross-platform, pure Python module with no dependencies.

Install with: pip install PyMsgBox

Sample usage:

import pymsgbox
pymsgbox.alert('This is an alert!', 'Title')
response = pymsgbox.prompt('What is your name?')

Full documentation at http://pymsgbox.readthedocs.org/en/latest/


回答 5

在Windows中,可以将ctypes与user32库一起使用:

from ctypes import c_int, WINFUNCTYPE, windll
from ctypes.wintypes import HWND, LPCSTR, UINT
prototype = WINFUNCTYPE(c_int, HWND, LPCSTR, LPCSTR, UINT)
paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", None), (1, "flags", 0)
MessageBox = prototype(("MessageBoxA", windll.user32), paramflags)

MessageBox()
MessageBox(text="Spam, spam, spam")
MessageBox(flags=2, text="foo bar")

In Windows, you can use ctypes with user32 library:

from ctypes import c_int, WINFUNCTYPE, windll
from ctypes.wintypes import HWND, LPCSTR, UINT
prototype = WINFUNCTYPE(c_int, HWND, LPCSTR, LPCSTR, UINT)
paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", None), (1, "flags", 0)
MessageBox = prototype(("MessageBoxA", windll.user32), paramflags)

MessageBox()
MessageBox(text="Spam, spam, spam")
MessageBox(flags=2, text="foo bar")

回答 6

在Mac上,python标准库有一个名为的模块EasyDialogs。在http://www.averdevelopment.com/python/EasyDialogs.html上还有一个(基于ctypes)Windows版本

如果对您来说很重要:它使用本机对话框并且不像已经提到的那样依赖Tkinter easygui,但是它可能没有那么多功能。

On Mac, the python standard library has a module called EasyDialogs. There is also a (ctypes based) windows version at http://www.averdevelopment.com/python/EasyDialogs.html

If it matters to you: it uses native dialogs and doesn’t depend on Tkinter like the already mentioned easygui, but it might not have as much features.


回答 7

import ctypes
ctypes.windll.user32.MessageBoxW(0, "Your text", "Your title", 1)

可以更改最后一个数字(此处为1)以更改窗口样式(不仅是按钮!):

## Button styles:
# 0 : OK
# 1 : OK | Cancel
# 2 : Abort | Retry | Ignore
# 3 : Yes | No | Cancel
# 4 : Yes | No
# 5 : Retry | No 
# 6 : Cancel | Try Again | Continue

## To also change icon, add these values to previous number
# 16 Stop-sign icon
# 32 Question-mark icon
# 48 Exclamation-point icon
# 64 Information-sign icon consisting of an 'i' in a circle

例如,

ctypes.windll.user32.MessageBoxW(0, "That's an error", "Warning!", 16)

会给这个

在此处输入图片说明

import ctypes
ctypes.windll.user32.MessageBoxW(0, "Your text", "Your title", 1)

The last number (here 1) can be change to change window style (not only buttons!):

## Button styles:
# 0 : OK
# 1 : OK | Cancel
# 2 : Abort | Retry | Ignore
# 3 : Yes | No | Cancel
# 4 : Yes | No
# 5 : Retry | No 
# 6 : Cancel | Try Again | Continue

## To also change icon, add these values to previous number
# 16 Stop-sign icon
# 32 Question-mark icon
# 48 Exclamation-point icon
# 64 Information-sign icon consisting of an 'i' in a circle

For example,

ctypes.windll.user32.MessageBoxW(0, "That's an error", "Warning!", 16)

will give this:

enter image description here


回答 8

from tkinter.messagebox import *
Message([master], title="[title]", message="[message]")

必须先创建主窗口。这是针对Python 3的。这不是wxPython,而是tkinter的。

Use

from tkinter.messagebox import *
Message([master], title="[title]", message="[message]")

The master window has to be created before. This is for Python 3. This is not fot wxPython, but for tkinter.


回答 9

import sys
from tkinter import *
def mhello():
    pass
    return

mGui = Tk()
ment = StringVar()

mGui.geometry('450x450+500+300')
mGui.title('My youtube Tkinter')

mlabel = Label(mGui,text ='my label').pack()

mbutton = Button(mGui,text ='ok',command = mhello,fg = 'red',bg='blue').pack()

mEntry = entry().pack 
import sys
from tkinter import *
def mhello():
    pass
    return

mGui = Tk()
ment = StringVar()

mGui.geometry('450x450+500+300')
mGui.title('My youtube Tkinter')

mlabel = Label(mGui,text ='my label').pack()

mbutton = Button(mGui,text ='ok',command = mhello,fg = 'red',bg='blue').pack()

mEntry = entry().pack 

回答 10

另外,您可以在撤消另一个窗口之前先放置它,以便放置消息

from tkinter import *
import tkinter.messagebox

window = Tk()
window.wm_withdraw()

# message at x:200,y:200
window.geometry("1x1+200+200")  # remember its.geometry("WidthxHeight(+or-)X(+or-)Y")
tkinter.messagebox.showerror(title="error", message="Error Message", parent=window)

# center screen message
window.geometry(f"1x1+{round(window.winfo_screenwidth() / 2)}+{round(window.winfo_screenheight() / 2)}")
tkinter.messagebox.showinfo(title="Greetings", message="Hello World!")

请注意:这是Lewis Cowles的答案,只是Python 3ified,因为tkinter自python 2起就发生了变化。

try:
    import tkinter
    import tkinter.messagebox
except ModuleNotFoundError:
    import Tkinter as tkinter
    import tkMessageBox as tkinter.messagebox

Also you can position the other window before withdrawing it so that you position your message

from tkinter import *
import tkinter.messagebox

window = Tk()
window.wm_withdraw()

# message at x:200,y:200
window.geometry("1x1+200+200")  # remember its.geometry("WidthxHeight(+or-)X(+or-)Y")
tkinter.messagebox.showerror(title="error", message="Error Message", parent=window)

# center screen message
window.geometry(f"1x1+{round(window.winfo_screenwidth() / 2)}+{round(window.winfo_screenheight() / 2)}")
tkinter.messagebox.showinfo(title="Greetings", message="Hello World!")

Please Note: This is Lewis Cowles’ answer just Python 3ified, since tkinter has changed since python 2. If you want your code to be backwords compadible do something like this:

try:
    import tkinter
    import tkinter.messagebox
except ModuleNotFoundError:
    import Tkinter as tkinter
    import tkMessageBox as tkinter.messagebox

回答 11

并不是最好的,这是我仅使用tkinter的基本消息框。

#Python 3.4
from    tkinter import  messagebox  as  msg;
import  tkinter as      tk;

def MsgBox(title, text, style):
    box = [
        msg.showinfo,       msg.showwarning,    msg.showerror,
        msg.askquestion,    msg.askyesno,       msg.askokcancel,        msg.askretrycancel,
];

tk.Tk().withdraw(); #Hide Main Window.

if style in range(7):
    return box[style](title, text);

if __name__ == '__main__':

Return = MsgBox(#Use Like This.
    'Basic Error Exemple',

    ''.join( [
        'The Basic Error Exemple a problem with test',                      '\n',
        'and is unable to continue. The application must close.',           '\n\n',
        'Error code Test',                                                  '\n',
        'Would you like visit http://wwww.basic-error-exemple.com/ for',    '\n',
        'help?',
    ] ),

    2,
);

print( Return );

"""
Style   |   Type        |   Button      |   Return
------------------------------------------------------
0           Info            Ok              'ok'
1           Warning         Ok              'ok'
2           Error           Ok              'ok'
3           Question        Yes/No          'yes'/'no'
4           YesNo           Yes/No          True/False
5           OkCancel        Ok/Cancel       True/False
6           RetryCancal     Retry/Cancel    True/False
"""

Not the best, here is my basic Message box using only tkinter.

#Python 3.4
from    tkinter import  messagebox  as  msg;
import  tkinter as      tk;

def MsgBox(title, text, style):
    box = [
        msg.showinfo,       msg.showwarning,    msg.showerror,
        msg.askquestion,    msg.askyesno,       msg.askokcancel,        msg.askretrycancel,
];

tk.Tk().withdraw(); #Hide Main Window.

if style in range(7):
    return box[style](title, text);

if __name__ == '__main__':

Return = MsgBox(#Use Like This.
    'Basic Error Exemple',

    ''.join( [
        'The Basic Error Exemple a problem with test',                      '\n',
        'and is unable to continue. The application must close.',           '\n\n',
        'Error code Test',                                                  '\n',
        'Would you like visit http://wwww.basic-error-exemple.com/ for',    '\n',
        'help?',
    ] ),

    2,
);

print( Return );

"""
Style   |   Type        |   Button      |   Return
------------------------------------------------------
0           Info            Ok              'ok'
1           Warning         Ok              'ok'
2           Error           Ok              'ok'
3           Question        Yes/No          'yes'/'no'
4           YesNo           Yes/No          True/False
5           OkCancel        Ok/Cancel       True/False
6           RetryCancal     Retry/Cancel    True/False
"""

回答 12

签出我的python模块:pip install quickgui(需要wxPython,但不需要wxPython知识) https://pypi.python.org/pypi/quickgui

可以创建任意数量的输入((比率,复选框,输入框),自动将它们排列在一个GUI上。

check out my python module: pip install quickgui (Requires wxPython, but requires no knowledge of wxPython) https://pypi.python.org/pypi/quickgui

Can create any numbers of inputs,(ratio, checkbox, inputbox), auto arrange them on a single gui.


回答 13

最新的消息框版本是hint_box模块。它有两个软件包:警报和消息。Message使您可以更好地控制该框,但键入时间会更长。

警报代码示例:

import prompt_box

prompt_box.alert('Hello') #This will output a dialog box with title Neutrino and the 
#text you inputted. The buttons will be Yes, No and Cancel

消息代码示例:

import prompt_box

prompt_box.message('Hello', 'Neutrino', 'You pressed yes', 'You pressed no', 'You 
pressed cancel') #The first two are text and title, and the other three are what is 
#printed when you press a certain button

A recent message box version is the prompt_box module. It has two packages: alert and message. Message gives you greater control over the box, but takes longer to type up.

Example Alert code:

import prompt_box

prompt_box.alert('Hello') #This will output a dialog box with title Neutrino and the 
#text you inputted. The buttons will be Yes, No and Cancel

Example Message code:

import prompt_box

prompt_box.message('Hello', 'Neutrino', 'You pressed yes', 'You pressed no', 'You 
pressed cancel') #The first two are text and title, and the other three are what is 
#printed when you press a certain button

回答 14

带螺纹的ctype模块

我正在使用tkinter消息框,但它会使我的代码崩溃。我不想找出原因,所以我改用ctypes模块。

例如:

import ctypes
ctypes.windll.user32.MessageBoxW(0, "Your text", "Your title", 1)

我从Arkelis那里得到了那个代码


我喜欢它不会使代码崩溃,因此我对其进行了工作并添加了线程,以便随后的代码可以运行。

我的代码示例

import ctypes
import threading


def MessageboxThread(buttonstyle, title, text, icon):
    threading.Thread(
        target=lambda: ctypes.windll.user32.MessageBoxW(buttonstyle, text, title, icon)
    ).start()

messagebox(0, "Your title", "Your text", 1)

对于按钮样式和图标编号:

## Button styles:
# 0 : OK
# 1 : OK | Cancel
# 2 : Abort | Retry | Ignore
# 3 : Yes | No | Cancel
# 4 : Yes | No
# 5 : Retry | No
# 6 : Cancel | Try Again | Continue

## To also change icon, add these values to previous number
# 16 Stop-sign icon
# 32 Question-mark icon
# 48 Exclamation-point icon
# 64 Information-sign icon consisting of an 'i' in a circle

ctype module with threading

i was using the tkinter messagebox but it would crash my code. i didn’t want to find out why so i used the ctypes module instead.

for example:

import ctypes
ctypes.windll.user32.MessageBoxW(0, "Your text", "Your title", 1)

i got that code from Arkelis


i liked that it didn’t crash the code so i worked on it and added a threading so the code after would run.

example for my code

import ctypes
import threading


def MessageboxThread(buttonstyle, title, text, icon):
    threading.Thread(
        target=lambda: ctypes.windll.user32.MessageBoxW(buttonstyle, text, title, icon)
    ).start()

messagebox(0, "Your title", "Your text", 1)

for button styles and icon numbers:

## Button styles:
# 0 : OK
# 1 : OK | Cancel
# 2 : Abort | Retry | Ignore
# 3 : Yes | No | Cancel
# 4 : Yes | No
# 5 : Retry | No
# 6 : Cancel | Try Again | Continue

## To also change icon, add these values to previous number
# 16 Stop-sign icon
# 32 Question-mark icon
# 48 Exclamation-point icon
# 64 Information-sign icon consisting of an 'i' in a circle

回答 15

您可以使用pyautoguipymsgbox

import pyautogui
pyautogui.alert("This is a message box",title="Hello World")

使用pymsgbox与使用相同pyautogui

import pymsgbox
pymsgbox.alert("This is a message box",title="Hello World")

You can use pyautogui or pymsgbox:

import pyautogui
pyautogui.alert("This is a message box",title="Hello World")

Using pymsgbox is the same as using pyautogui:

import pymsgbox
pymsgbox.alert("This is a message box",title="Hello World")

在matplotlib中动态更新图

问题:在matplotlib中动态更新图

我正在用Python开发一个应用程序,该应用程序从串行端口收集数据并针对到达时间绘制收集的数据图。数据的到达时间不确定。我希望在收到数据时更新绘图。我搜索了如何执行此操作,发现了两种方法:

  1. 清除绘图,然后重新绘制所有点的绘图。
  2. 通过在特定间隔后对其进行更改来对图进行动画处理。

我不喜欢第一个程序,因为该程序会运行很长时间(例如一天),并且会收集数据,因此重绘该图将非常慢。第二种方法也不可取,因为数据的到达时间不确定,我希望仅在接收到数据时更新绘图。

有没有一种方法可以仅在收到数据后才通过添加更多点来更新图?

I am making an application in Python which collects data from a serial port and plots a graph of the collected data against arrival time. The time of arrival for the data is uncertain. I want the plot to be updated when data is received. I searched on how to do this and found two methods:

  1. Clear the plot and re-draw the plot with all the points again.
  2. Animate the plot by changing it after a particular interval.

I do not prefer the first one as the program runs and collects data for a long time (a day for example), and redrawing the plot will be pretty slow. The second one is also not preferable as time of arrival of data is uncertain and I want the plot to update only when the data is received.

Is there a way in which I can update the plot just by adding more points to it only when the data is received?


回答 0

有没有一种方法可以通过添加更多点来更新图…

matplotlib中有多种动画数据的方式,具体取决于您使用的版本。您看过matplotlib食谱示例吗?另外,请在matplotlib文档中查看更现代的动画示例。最后,动画API定义了一个函数FuncAnimation,该函数可以对一个函数进行实时动画处理。该功能可能只是您用来获取数据的功能。

每种方法基本上都设置了data要绘制的对象的属性,因此不需要清除屏幕或图形。data可以简单地扩展该属性,因此您可以保留先前的点,而只需继续添加到线(或图像或所绘制的任何东西)中。

假设您说数据到达时间不确定,那么最好的选择就是执行以下操作:

import matplotlib.pyplot as plt
import numpy

hl, = plt.plot([], [])

def update_line(hl, new_data):
    hl.set_xdata(numpy.append(hl.get_xdata(), new_data))
    hl.set_ydata(numpy.append(hl.get_ydata(), new_data))
    plt.draw()

然后,当您从串行端口接收数据时,只需调用update_line

Is there a way in which I can update the plot just by adding more point[s] to it…

There are a number of ways of animating data in matplotlib, depending on the version you have. Have you seen the matplotlib cookbook examples? Also, check out the more modern animation examples in the matplotlib documentation. Finally, the animation API defines a function FuncAnimation which animates a function in time. This function could just be the function you use to acquire your data.

Each method basically sets the data property of the object being drawn, so doesn’t require clearing the screen or figure. The data property can simply be extended, so you can keep the previous points and just keep adding to your line (or image or whatever you are drawing).

Given that you say that your data arrival time is uncertain your best bet is probably just to do something like:

import matplotlib.pyplot as plt
import numpy

hl, = plt.plot([], [])

def update_line(hl, new_data):
    hl.set_xdata(numpy.append(hl.get_xdata(), new_data))
    hl.set_ydata(numpy.append(hl.get_ydata(), new_data))
    plt.draw()

Then when you receive data from the serial port just call update_line.


回答 1

为了在不使用FuncAnimation的情况下执行此操作(例如,要在生成图时执行代码的其他部分,或者要同时更新多个图),draw单独调用不会生成图(至少使用qt后端)。

以下对我有用:

import matplotlib.pyplot as plt
plt.ion()
class DynamicUpdate():
    #Suppose we know the x range
    min_x = 0
    max_x = 10

    def on_launch(self):
        #Set up plot
        self.figure, self.ax = plt.subplots()
        self.lines, = self.ax.plot([],[], 'o')
        #Autoscale on unknown axis and known lims on the other
        self.ax.set_autoscaley_on(True)
        self.ax.set_xlim(self.min_x, self.max_x)
        #Other stuff
        self.ax.grid()
        ...

    def on_running(self, xdata, ydata):
        #Update data (with the new _and_ the old points)
        self.lines.set_xdata(xdata)
        self.lines.set_ydata(ydata)
        #Need both of these in order to rescale
        self.ax.relim()
        self.ax.autoscale_view()
        #We need to draw *and* flush
        self.figure.canvas.draw()
        self.figure.canvas.flush_events()

    #Example
    def __call__(self):
        import numpy as np
        import time
        self.on_launch()
        xdata = []
        ydata = []
        for x in np.arange(0,10,0.5):
            xdata.append(x)
            ydata.append(np.exp(-x**2)+10*np.exp(-(x-7)**2))
            self.on_running(xdata, ydata)
            time.sleep(1)
        return xdata, ydata

d = DynamicUpdate()
d()

In order to do this without FuncAnimation (eg you want to execute other parts of the code while the plot is being produced or you want to be updating several plots at the same time), calling draw alone does not produce the plot (at least with the qt backend).

The following works for me:

import matplotlib.pyplot as plt
plt.ion()
class DynamicUpdate():
    #Suppose we know the x range
    min_x = 0
    max_x = 10

    def on_launch(self):
        #Set up plot
        self.figure, self.ax = plt.subplots()
        self.lines, = self.ax.plot([],[], 'o')
        #Autoscale on unknown axis and known lims on the other
        self.ax.set_autoscaley_on(True)
        self.ax.set_xlim(self.min_x, self.max_x)
        #Other stuff
        self.ax.grid()
        ...

    def on_running(self, xdata, ydata):
        #Update data (with the new _and_ the old points)
        self.lines.set_xdata(xdata)
        self.lines.set_ydata(ydata)
        #Need both of these in order to rescale
        self.ax.relim()
        self.ax.autoscale_view()
        #We need to draw *and* flush
        self.figure.canvas.draw()
        self.figure.canvas.flush_events()

    #Example
    def __call__(self):
        import numpy as np
        import time
        self.on_launch()
        xdata = []
        ydata = []
        for x in np.arange(0,10,0.5):
            xdata.append(x)
            ydata.append(np.exp(-x**2)+10*np.exp(-(x-7)**2))
            self.on_running(xdata, ydata)
            time.sleep(1)
        return xdata, ydata

d = DynamicUpdate()
d()

回答 2

这是一种允许在绘制一定数量的点之后删除点的方法:

import matplotlib.pyplot as plt
# generate axes object
ax = plt.axes()

# set limits
plt.xlim(0,10) 
plt.ylim(0,10)

for i in range(10):        
     # add something to axes    
     ax.scatter([i], [i]) 
     ax.plot([i], [i+1], 'rx')

     # draw the plot
     plt.draw() 
     plt.pause(0.01) #is necessary for the plot to update for some reason

     # start removing points if you don't want all shown
     if i>2:
         ax.lines[0].remove()
         ax.collections[0].remove()

Here is a way which allows to remove points after a certain number of points plotted:

import matplotlib.pyplot as plt
# generate axes object
ax = plt.axes()

# set limits
plt.xlim(0,10) 
plt.ylim(0,10)

for i in range(10):        
     # add something to axes    
     ax.scatter([i], [i]) 
     ax.plot([i], [i+1], 'rx')

     # draw the plot
     plt.draw() 
     plt.pause(0.01) #is necessary for the plot to update for some reason

     # start removing points if you don't want all shown
     if i>2:
         ax.lines[0].remove()
         ax.collections[0].remove()

回答 3

我知道我来不及回答这个问题,但是对于您的问题,您可以考虑使用“操纵杆”包装。我设计它用于从串行端口绘制数据流,但它适用于任何流。它还允许交互式文本记录或图像绘图(除了图形绘图外)。无需在单独的线程中执行自己的循环,程序包会照顾它,只需提供您希望的更新频率即可。另外,终端在绘制时仍可用于监视命令。参见http://www.github.com/ceyzeriat/joystick/https://pypi.python.org/pypi/joystick(使用pip install游戏杆进行安装)

只需将np.random.random()替换为您从以下代码中从串行端口读取的实际数据点:

import joystick as jk
import numpy as np
import time

class test(jk.Joystick):
    # initialize the infinite loop decorator
    _infinite_loop = jk.deco_infinite_loop()

    def _init(self, *args, **kwargs):
        """
        Function called at initialization, see the doc
        """
        self._t0 = time.time()  # initialize time
        self.xdata = np.array([self._t0])  # time x-axis
        self.ydata = np.array([0.0])  # fake data y-axis
        # create a graph frame
        self.mygraph = self.add_frame(jk.Graph(name="test", size=(500, 500), pos=(50, 50), fmt="go-", xnpts=10000, xnptsmax=10000, xylim=(None, None, 0, 1)))

    @_infinite_loop(wait_time=0.2)
    def _generate_data(self):  # function looped every 0.2 second to read or produce data
        """
        Loop starting with the simulation start, getting data and
    pushing it to the graph every 0.2 seconds
        """
        # concatenate data on the time x-axis
        self.xdata = jk.core.add_datapoint(self.xdata, time.time(), xnptsmax=self.mygraph.xnptsmax)
        # concatenate data on the fake data y-axis
        self.ydata = jk.core.add_datapoint(self.ydata, np.random.random(), xnptsmax=self.mygraph.xnptsmax)
        self.mygraph.set_xydata(t, self.ydata)

t = test()
t.start()
t.stop()

I know I’m late to answer this question, but for your issue you could look into the “joystick” package. I designed it for plotting a stream of data from the serial port, but it works for any stream. It also allows for interactive text logging or image plotting (in addition to graph plotting). No need to do your own loops in a separate thread, the package takes care of it, just give the update frequency you wish. Plus the terminal remains available for monitoring commands while plotting. See http://www.github.com/ceyzeriat/joystick/ or https://pypi.python.org/pypi/joystick (use pip install joystick to install)

Just replace np.random.random() by your real data point read from the serial port in the code below:

import joystick as jk
import numpy as np
import time

class test(jk.Joystick):
    # initialize the infinite loop decorator
    _infinite_loop = jk.deco_infinite_loop()

    def _init(self, *args, **kwargs):
        """
        Function called at initialization, see the doc
        """
        self._t0 = time.time()  # initialize time
        self.xdata = np.array([self._t0])  # time x-axis
        self.ydata = np.array([0.0])  # fake data y-axis
        # create a graph frame
        self.mygraph = self.add_frame(jk.Graph(name="test", size=(500, 500), pos=(50, 50), fmt="go-", xnpts=10000, xnptsmax=10000, xylim=(None, None, 0, 1)))

    @_infinite_loop(wait_time=0.2)
    def _generate_data(self):  # function looped every 0.2 second to read or produce data
        """
        Loop starting with the simulation start, getting data and
    pushing it to the graph every 0.2 seconds
        """
        # concatenate data on the time x-axis
        self.xdata = jk.core.add_datapoint(self.xdata, time.time(), xnptsmax=self.mygraph.xnptsmax)
        # concatenate data on the fake data y-axis
        self.ydata = jk.core.add_datapoint(self.ydata, np.random.random(), xnptsmax=self.mygraph.xnptsmax)
        self.mygraph.set_xydata(t, self.ydata)

t = test()
t.start()
t.stop()

如何从Tkinter文本小部件获取输入?

问题:如何从Tkinter文本小部件获取输入?

如何从小Text部件获取Tkinter输入?

编辑

我问这个问题是为了帮助其他有同样问题的人- 就是为什么没有示例代码的原因。这个问题困扰了我好几个小时,我用这个问题来教别人。不要把它当作一个真正的问题来评分-答案很重要。

How to get Tkinter input from the Text widget?

EDIT

I asked this question to help others with the same problem – that is the reason why there is no example code. This issue had been troubling me for hours and I used this question to teach others. Please do not rate it as if it was a real question – the answer is the thing that matters.


回答 0

要从文本框中获取Tkinter输入,必须向常规.get()函数添加更多属性。如果我们有一个文本框myText_Box,则这是检索其输入的方法。

def retrieve_input():
    input = self.myText_Box.get("1.0",END)

第一部分"1.0"意味着应该从第一行字符零(即:第一个字符)读取输入。END是设置为字符串的导入常量"end"。该END部分意味着阅读直到到达文本框的末尾。唯一的问题是它实际上在我们的输入中添加了换行符。因此,为了修复它,我们应该更改ENDend-1c(Thanks Bryan Oakley-1c删除1个字符,而-2c意味着删除两个字符,依此类推。

def retrieve_input():
    input = self.myText_Box.get("1.0",'end-1c')

To get Tkinter input from the text box, you must add a few more attributes to the normal .get() function. If we have a text box myText_Box, then this is the method for retrieving its input.

def retrieve_input():
    input = self.myText_Box.get("1.0",END)

The first part, "1.0" means that the input should be read from line one, character zero (ie: the very first character). END is an imported constant which is set to the string "end". The END part means to read until the end of the text box is reached. The only issue with this is that it actually adds a newline to our input. So, in order to fix it we should change END to end-1c(Thanks Bryan Oakley) The -1c deletes 1 character, while -2c would mean delete two characters, and so on.

def retrieve_input():
    input = self.myText_Box.get("1.0",'end-1c')

回答 1

这是我用python 3.5.2做的:

from tkinter import *
root=Tk()
def retrieve_input():
    inputValue=textBox.get("1.0","end-1c")
    print(inputValue)

textBox=Text(root, height=2, width=10)
textBox.pack()
buttonCommit=Button(root, height=1, width=10, text="Commit", 
                    command=lambda: retrieve_input())
#command=lambda: retrieve_input() >>> just means do this when i press the button
buttonCommit.pack()

mainloop()

这样,当我在文本小部件中键入“ blah blah”并按下按钮时,我键入的任何内容都会打印出来。所以我认为这是将用户输入从“文本”小部件存储到变量的答案。

Here is how I did it with python 3.5.2:

from tkinter import *
root=Tk()
def retrieve_input():
    inputValue=textBox.get("1.0","end-1c")
    print(inputValue)

textBox=Text(root, height=2, width=10)
textBox.pack()
buttonCommit=Button(root, height=1, width=10, text="Commit", 
                    command=lambda: retrieve_input())
#command=lambda: retrieve_input() >>> just means do this when i press the button
buttonCommit.pack()

mainloop()

with that, when i typed “blah blah” in the text widget and pressed the button, whatever i typed got printed out. So i think that is the answer for storing user input from Text widget to variable.


回答 2

为了从python 3的文本框中获取Tkinter输入,我使用的完整的学生级程序如下:

#Imports all (*) classes,
#atributes, and methods of tkinter into the
#current workspace

from tkinter import *

#***********************************
#Creates an instance of the class tkinter.Tk.
#This creates what is called the "root" window. By conventon,
#the root window in Tkinter is usually called "root",
#but you are free to call it by any other name.

root = Tk()
root.title('how to get text from textbox')


#**********************************
mystring = StringVar()

####define the function that the signup button will do
def getvalue():
##    print(mystring.get())
#*************************************

Label(root, text="Text to get").grid(row=0, sticky=W)  #label
Entry(root, textvariable = mystring).grid(row=0, column=1, sticky=E) #entry textbox

WSignUp = Button(root, text="print text", command=getvalue).grid(row=3, column=0, sticky=W) #button


############################################
# executes the mainloop (that is, the event loop) method of the root
# object. The mainloop method is what keeps the root window visible.
# If you remove the line, the window created will disappear
# immediately as the script stops running. This will happen so fast
# that you will not even see the window appearing on your screen.
# Keeping the mainloop running also lets you keep the
# program running until you press the close buton
root.mainloop()

To get Tkinter input from the text box in python 3 the complete student level program used by me is as under:

#Imports all (*) classes,
#atributes, and methods of tkinter into the
#current workspace

from tkinter import *

#***********************************
#Creates an instance of the class tkinter.Tk.
#This creates what is called the "root" window. By conventon,
#the root window in Tkinter is usually called "root",
#but you are free to call it by any other name.

root = Tk()
root.title('how to get text from textbox')


#**********************************
mystring = StringVar()

####define the function that the signup button will do
def getvalue():
##    print(mystring.get())
#*************************************

Label(root, text="Text to get").grid(row=0, sticky=W)  #label
Entry(root, textvariable = mystring).grid(row=0, column=1, sticky=E) #entry textbox

WSignUp = Button(root, text="print text", command=getvalue).grid(row=3, column=0, sticky=W) #button


############################################
# executes the mainloop (that is, the event loop) method of the root
# object. The mainloop method is what keeps the root window visible.
# If you remove the line, the window created will disappear
# immediately as the script stops running. This will happen so fast
# that you will not even see the window appearing on your screen.
# Keeping the mainloop running also lets you keep the
# program running until you press the close buton
root.mainloop()

回答 3

为了在Text小部件中获取字符串,可以简单地使用get定义的方法,Text该方法接受1到2个参数作为startend字符位置text_widget_object.get(start, end=None)。如果仅start通过并end没有传递它只返回位于单个字符start如果end 还有通过,返回位置之间的所有字符start,并end作为字符串。

还有一些特殊的字符串,它们是基础Tk的变量。其中一个将是,"end"tk.END表示Text小部件中最后一个char的可变位置。一个例子是在控件返回所有文字,用text_widget_object.get('1.0', 'end')或者text_widget_object.get('1.0', 'end-1c')如果你不想最后一个换行符。

演示版

请参见下面的演示,该演示使用滑块在给定位置之间选择字符:

try:
    import tkinter as tk
except:
    import Tkinter as tk


class Demo(tk.LabelFrame):
    """
    A LabeFrame that in order to demonstrate the string returned by the
    get method of Text widget, selects the characters in between the
    given arguments that are set with Scales.
    """

    def __init__(self, master, *args, **kwargs):
        tk.LabelFrame.__init__(self, master, *args, **kwargs)
        self.start_arg = ''
        self.end_arg = None
        self.position_frames = dict()
        self._create_widgets()
        self._layout()
        self.update()


    def _create_widgets(self):
        self._is_two_args = tk.Checkbutton(self,
                                    text="Use 2 positional arguments...")
        self.position_frames['start'] = PositionFrame(self,
                                    text="start='{}.{}'.format(line, column)")
        self.position_frames['end'] = PositionFrame(   self,
                                    text="end='{}.{}'.format(line, column)")
        self.text = TextWithStats(self, wrap='none')
        self._widget_configs()


    def _widget_configs(self):
        self.text.update_callback = self.update
        self._is_two_args.var = tk.BooleanVar(self, value=False)
        self._is_two_args.config(variable=self._is_two_args.var,
                                    onvalue=True, offvalue=False)
        self._is_two_args['command'] = self._is_two_args_handle
        for _key in self.position_frames:
            self.position_frames[_key].line.slider['command'] = self.update
            self.position_frames[_key].column.slider['command'] = self.update


    def _layout(self):
        self._is_two_args.grid(sticky='nsw', row=0, column=1)
        self.position_frames['start'].grid(sticky='nsew', row=1, column=0)
        #self.position_frames['end'].grid(sticky='nsew', row=1, column=1)
        self.text.grid(sticky='nsew', row=2, column=0,
                                                    rowspan=2, columnspan=2)
        _grid_size = self.grid_size()
        for _col in range(_grid_size[0]):
            self.grid_columnconfigure(_col, weight=1)
        for _row in range(_grid_size[1] - 1):
            self.grid_rowconfigure(_row + 1, weight=1)


    def _is_two_args_handle(self):
        self.update_arguments()
        if self._is_two_args.var.get():
            self.position_frames['end'].grid(sticky='nsew', row=1, column=1)
        else:
            self.position_frames['end'].grid_remove()


    def update(self, event=None):
        """
        Updates slider limits, argument values, labels representing the
        get method call.
        """

        self.update_sliders()
        self.update_arguments()


    def update_sliders(self):
        """
        Updates slider limits based on what's written in the text and
        which line is selected.
        """

        self._update_line_sliders()
        self._update_column_sliders()


    def _update_line_sliders(self):
        if self.text.lines_length:
            for _key in self.position_frames:
                self.position_frames[_key].line.slider['state'] = 'normal'
                self.position_frames[_key].line.slider['from_'] = 1
                _no_of_lines = self.text.line_count
                self.position_frames[_key].line.slider['to'] = _no_of_lines
        else:
            for _key in self.position_frames:
                self.position_frames[_key].line.slider['state'] = 'disabled'


    def _update_column_sliders(self):
        if self.text.lines_length:
            for _key in self.position_frames:
                self.position_frames[_key].column.slider['state'] = 'normal'
                self.position_frames[_key].column.slider['from_'] = 0
                _line_no = int(self.position_frames[_key].line.slider.get())-1
                _max_line_len = self.text.lines_length[_line_no]
                self.position_frames[_key].column.slider['to'] = _max_line_len
        else:
            for _key in self.position_frames:
                self.position_frames[_key].column.slider['state'] = 'disabled'


    def update_arguments(self):
        """
        Updates the values representing the arguments passed to the get
        method, based on whether or not the 2nd positional argument is
        active and the slider positions.
        """

        _start_line_no = self.position_frames['start'].line.slider.get()
        _start_col_no = self.position_frames['start'].column.slider.get()
        self.start_arg = "{}.{}".format(_start_line_no, _start_col_no)
        if self._is_two_args.var.get():
            _end_line_no = self.position_frames['end'].line.slider.get()
            _end_col_no = self.position_frames['end'].column.slider.get()
            self.end_arg = "{}.{}".format(_end_line_no, _end_col_no)
        else:
            self.end_arg = None
        self._update_method_labels()
        self._select()


    def _update_method_labels(self):
        if self.end_arg:
            for _key in self.position_frames:
                _string = "text.get('{}', '{}')".format(
                                                self.start_arg, self.end_arg)
                self.position_frames[_key].label['text'] = _string
        else:
            _string = "text.get('{}')".format(self.start_arg)
            self.position_frames['start'].label['text'] = _string


    def _select(self):
        self.text.focus_set()
        self.text.tag_remove('sel', '1.0', 'end')
        self.text.tag_add('sel', self.start_arg, self.end_arg)
        if self.end_arg:
            self.text.mark_set('insert', self.end_arg)
        else:
            self.text.mark_set('insert', self.start_arg)


class TextWithStats(tk.Text):
    """
    Text widget that stores stats of its content:
    self.line_count:        the total number of lines
    self.lines_length:      the total number of characters per line
    self.update_callback:   can be set as the reference to the callback
                            to be called with each update
    """

    def __init__(self, master, update_callback=None, *args, **kwargs):
        tk.Text.__init__(self, master, *args, **kwargs)
        self._events = ('<KeyPress>',
                        '<KeyRelease>',
                        '<ButtonRelease-1>',
                        '<ButtonRelease-2>',
                        '<ButtonRelease-3>',
                        '<Delete>',
                        '<<Cut>>',
                        '<<Paste>>',
                        '<<Undo>>',
                        '<<Redo>>')
        self.line_count = None
        self.lines_length = list()
        self.update_callback = update_callback
        self.update_stats()
        self.bind_events_on_widget_to_callback( self._events,
                                                self,
                                                self.update_stats)


    @staticmethod
    def bind_events_on_widget_to_callback(events, widget, callback):
        """
        Bind events on widget to callback.
        """

        for _event in events:
            widget.bind(_event, callback)


    def update_stats(self, event=None):
        """
        Update self.line_count, self.lines_length stats and call
        self.update_callback.
        """

        _string = self.get('1.0', 'end-1c')
        _string_lines = _string.splitlines()
        self.line_count = len(_string_lines)
        del self.lines_length[:]
        for _line in _string_lines:
            self.lines_length.append(len(_line))
        if self.update_callback:
            self.update_callback()


class PositionFrame(tk.LabelFrame):
    """
    A LabelFrame that has two LabelFrames which has Scales.
    """

    def __init__(self, master, *args, **kwargs):
        tk.LabelFrame.__init__(self, master, *args, **kwargs)
        self._create_widgets()
        self._layout()


    def _create_widgets(self):
        self.line = SliderFrame(self, orient='vertical', text="line=")
        self.column = SliderFrame(self, orient='horizontal', text="column=")
        self.label = tk.Label(self, text="Label")


    def _layout(self):
        self.line.grid(sticky='ns', row=0, column=0, rowspan=2)
        self.column.grid(sticky='ew', row=0, column=1, columnspan=2)
        self.label.grid(sticky='nsew', row=1, column=1)
        self.grid_rowconfigure(1, weight=1)
        self.grid_columnconfigure(1, weight=1)


class SliderFrame(tk.LabelFrame):
    """
    A LabelFrame that encapsulates a Scale.
    """

    def __init__(self, master, orient, *args, **kwargs):
        tk.LabelFrame.__init__(self, master, *args, **kwargs)

        self.slider = tk.Scale(self, orient=orient)
        self.slider.pack(fill='both', expand=True)


if __name__ == '__main__':
    root = tk.Tk()
    demo = Demo(root, text="text.get(start, end=None)")

    with open(__file__) as f:
        demo.text.insert('1.0', f.read())
    demo.text.update_stats()
    demo.pack(fill='both', expand=True)
    root.mainloop()

In order to obtain the string in a Text widget one can simply use get method defined for Text which accepts 1 to 2 arguments as start and end positions of characters, text_widget_object.get(start, end=None). If only start is passed and end isn’t passed it returns only the single character positioned at start, if end is passed as well, it returns all characters in between positions start and end as string.

There are also special strings, that are variables to the underlying Tk. One of them would be "end" or tk.END which represents the variable position of the very last char in the Text widget. An example would be to returning all text in the widget, with text_widget_object.get('1.0', 'end') or text_widget_object.get('1.0', 'end-1c') if you don’t want the last newline character.

Demo

See below demonstration that selects the characters in between the given positions with sliders:

try:
    import tkinter as tk
except:
    import Tkinter as tk


class Demo(tk.LabelFrame):
    """
    A LabeFrame that in order to demonstrate the string returned by the
    get method of Text widget, selects the characters in between the
    given arguments that are set with Scales.
    """

    def __init__(self, master, *args, **kwargs):
        tk.LabelFrame.__init__(self, master, *args, **kwargs)
        self.start_arg = ''
        self.end_arg = None
        self.position_frames = dict()
        self._create_widgets()
        self._layout()
        self.update()


    def _create_widgets(self):
        self._is_two_args = tk.Checkbutton(self,
                                    text="Use 2 positional arguments...")
        self.position_frames['start'] = PositionFrame(self,
                                    text="start='{}.{}'.format(line, column)")
        self.position_frames['end'] = PositionFrame(   self,
                                    text="end='{}.{}'.format(line, column)")
        self.text = TextWithStats(self, wrap='none')
        self._widget_configs()


    def _widget_configs(self):
        self.text.update_callback = self.update
        self._is_two_args.var = tk.BooleanVar(self, value=False)
        self._is_two_args.config(variable=self._is_two_args.var,
                                    onvalue=True, offvalue=False)
        self._is_two_args['command'] = self._is_two_args_handle
        for _key in self.position_frames:
            self.position_frames[_key].line.slider['command'] = self.update
            self.position_frames[_key].column.slider['command'] = self.update


    def _layout(self):
        self._is_two_args.grid(sticky='nsw', row=0, column=1)
        self.position_frames['start'].grid(sticky='nsew', row=1, column=0)
        #self.position_frames['end'].grid(sticky='nsew', row=1, column=1)
        self.text.grid(sticky='nsew', row=2, column=0,
                                                    rowspan=2, columnspan=2)
        _grid_size = self.grid_size()
        for _col in range(_grid_size[0]):
            self.grid_columnconfigure(_col, weight=1)
        for _row in range(_grid_size[1] - 1):
            self.grid_rowconfigure(_row + 1, weight=1)


    def _is_two_args_handle(self):
        self.update_arguments()
        if self._is_two_args.var.get():
            self.position_frames['end'].grid(sticky='nsew', row=1, column=1)
        else:
            self.position_frames['end'].grid_remove()


    def update(self, event=None):
        """
        Updates slider limits, argument values, labels representing the
        get method call.
        """

        self.update_sliders()
        self.update_arguments()


    def update_sliders(self):
        """
        Updates slider limits based on what's written in the text and
        which line is selected.
        """

        self._update_line_sliders()
        self._update_column_sliders()


    def _update_line_sliders(self):
        if self.text.lines_length:
            for _key in self.position_frames:
                self.position_frames[_key].line.slider['state'] = 'normal'
                self.position_frames[_key].line.slider['from_'] = 1
                _no_of_lines = self.text.line_count
                self.position_frames[_key].line.slider['to'] = _no_of_lines
        else:
            for _key in self.position_frames:
                self.position_frames[_key].line.slider['state'] = 'disabled'


    def _update_column_sliders(self):
        if self.text.lines_length:
            for _key in self.position_frames:
                self.position_frames[_key].column.slider['state'] = 'normal'
                self.position_frames[_key].column.slider['from_'] = 0
                _line_no = int(self.position_frames[_key].line.slider.get())-1
                _max_line_len = self.text.lines_length[_line_no]
                self.position_frames[_key].column.slider['to'] = _max_line_len
        else:
            for _key in self.position_frames:
                self.position_frames[_key].column.slider['state'] = 'disabled'


    def update_arguments(self):
        """
        Updates the values representing the arguments passed to the get
        method, based on whether or not the 2nd positional argument is
        active and the slider positions.
        """

        _start_line_no = self.position_frames['start'].line.slider.get()
        _start_col_no = self.position_frames['start'].column.slider.get()
        self.start_arg = "{}.{}".format(_start_line_no, _start_col_no)
        if self._is_two_args.var.get():
            _end_line_no = self.position_frames['end'].line.slider.get()
            _end_col_no = self.position_frames['end'].column.slider.get()
            self.end_arg = "{}.{}".format(_end_line_no, _end_col_no)
        else:
            self.end_arg = None
        self._update_method_labels()
        self._select()


    def _update_method_labels(self):
        if self.end_arg:
            for _key in self.position_frames:
                _string = "text.get('{}', '{}')".format(
                                                self.start_arg, self.end_arg)
                self.position_frames[_key].label['text'] = _string
        else:
            _string = "text.get('{}')".format(self.start_arg)
            self.position_frames['start'].label['text'] = _string


    def _select(self):
        self.text.focus_set()
        self.text.tag_remove('sel', '1.0', 'end')
        self.text.tag_add('sel', self.start_arg, self.end_arg)
        if self.end_arg:
            self.text.mark_set('insert', self.end_arg)
        else:
            self.text.mark_set('insert', self.start_arg)


class TextWithStats(tk.Text):
    """
    Text widget that stores stats of its content:
    self.line_count:        the total number of lines
    self.lines_length:      the total number of characters per line
    self.update_callback:   can be set as the reference to the callback
                            to be called with each update
    """

    def __init__(self, master, update_callback=None, *args, **kwargs):
        tk.Text.__init__(self, master, *args, **kwargs)
        self._events = ('<KeyPress>',
                        '<KeyRelease>',
                        '<ButtonRelease-1>',
                        '<ButtonRelease-2>',
                        '<ButtonRelease-3>',
                        '<Delete>',
                        '<<Cut>>',
                        '<<Paste>>',
                        '<<Undo>>',
                        '<<Redo>>')
        self.line_count = None
        self.lines_length = list()
        self.update_callback = update_callback
        self.update_stats()
        self.bind_events_on_widget_to_callback( self._events,
                                                self,
                                                self.update_stats)


    @staticmethod
    def bind_events_on_widget_to_callback(events, widget, callback):
        """
        Bind events on widget to callback.
        """

        for _event in events:
            widget.bind(_event, callback)


    def update_stats(self, event=None):
        """
        Update self.line_count, self.lines_length stats and call
        self.update_callback.
        """

        _string = self.get('1.0', 'end-1c')
        _string_lines = _string.splitlines()
        self.line_count = len(_string_lines)
        del self.lines_length[:]
        for _line in _string_lines:
            self.lines_length.append(len(_line))
        if self.update_callback:
            self.update_callback()


class PositionFrame(tk.LabelFrame):
    """
    A LabelFrame that has two LabelFrames which has Scales.
    """

    def __init__(self, master, *args, **kwargs):
        tk.LabelFrame.__init__(self, master, *args, **kwargs)
        self._create_widgets()
        self._layout()


    def _create_widgets(self):
        self.line = SliderFrame(self, orient='vertical', text="line=")
        self.column = SliderFrame(self, orient='horizontal', text="column=")
        self.label = tk.Label(self, text="Label")


    def _layout(self):
        self.line.grid(sticky='ns', row=0, column=0, rowspan=2)
        self.column.grid(sticky='ew', row=0, column=1, columnspan=2)
        self.label.grid(sticky='nsew', row=1, column=1)
        self.grid_rowconfigure(1, weight=1)
        self.grid_columnconfigure(1, weight=1)


class SliderFrame(tk.LabelFrame):
    """
    A LabelFrame that encapsulates a Scale.
    """

    def __init__(self, master, orient, *args, **kwargs):
        tk.LabelFrame.__init__(self, master, *args, **kwargs)

        self.slider = tk.Scale(self, orient=orient)
        self.slider.pack(fill='both', expand=True)


if __name__ == '__main__':
    root = tk.Tk()
    demo = Demo(root, text="text.get(start, end=None)")

    with open(__file__) as f:
        demo.text.insert('1.0', f.read())
    demo.text.update_stats()
    demo.pack(fill='both', expand=True)
    root.mainloop()

回答 4

我认为这是更好的方法-

variable1=StringVar() # Value saved here

def search():
  print(variable1.get())
  return ''

ttk.Entry(mainframe, width=7, textvariable=variable1).grid(column=2, row=1)

ttk.Label(mainframe, text="label").grid(column=1, row=1)

ttk.Button(mainframe, text="Search", command=search).grid(column=2, row=13)

按下按钮后,文本字段中的值将被打印。但是请确保您分别导入ttk。

完整的代码基础的应用程序是-

from tkinter import *
from tkinter import ttk

root=Tk()
mainframe = ttk.Frame(root, padding="10 10 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)


variable1=StringVar() # Value saved here

def search():
  print(variable1.get())
  return ''

ttk.Entry(mainframe, width=7, textvariable=variable1).grid(column=2, row=1)

ttk.Label(mainframe, text="label").grid(column=1, row=1)

ttk.Button(mainframe, text="Search", command=search).grid(column=2, row=13)

root.mainloop()

I think this is a better way-

variable1=StringVar() # Value saved here

def search():
  print(variable1.get())
  return ''

ttk.Entry(mainframe, width=7, textvariable=variable1).grid(column=2, row=1)

ttk.Label(mainframe, text="label").grid(column=1, row=1)

ttk.Button(mainframe, text="Search", command=search).grid(column=2, row=13)

On pressing the button, the value in the text field would get printed. But make sure You import the ttk separately.

The full code for a basic application is-

from tkinter import *
from tkinter import ttk

root=Tk()
mainframe = ttk.Frame(root, padding="10 10 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)


variable1=StringVar() # Value saved here

def search():
  print(variable1.get())
  return ''

ttk.Entry(mainframe, width=7, textvariable=variable1).grid(column=2, row=1)

ttk.Label(mainframe, text="label").grid(column=1, row=1)

ttk.Button(mainframe, text="Search", command=search).grid(column=2, row=13)

root.mainloop()

回答 5

我遇到了从文本小部件获取整个文本的问题,以下解决方案对我有用:

txt.get(1.0,END)

其中1.0表示第一行,第零个字符(即在第一行之前!)是开始位置,而END是结束位置。

感谢Alan Gauld在此链接中

I faced the problem of gettng entire text from Text widget and following solution worked for me :

txt.get(1.0,END)

Where 1.0 means first line, zeroth character (ie before the first!) is the starting position and END is the ending position.

Thanks to Alan Gauld in this link


回答 6

假设您有一个Text名为的小部件my_text_widget

要从中获取输入,my_text_widget您可以使用get函数。

假设您已经导入tkintermy_text_widget首先定义,让它成为一个简单的文本小部件。

my_text_widget = Text(self)

要从小部件获取输入,text您需要使用该get功能,text并且entry小部件都具有此功能。

input = my_text_widget.get()

之所以将其保存到变量中,是为了在进一步的过程中使用它,例如,测试输入的内容。

Lets say that you have a Text widget called my_text_widget.

To get input from the my_text_widget you can use the get function.

Let’s assume that you have imported tkinter. Lets define my_text_widget first, lets make it just a simple text widget.

my_text_widget = Text(self)

To get input from a text widget you need to use the get function, both, text and entry widgets have this.

input = my_text_widget.get()

The reason we save it to a variable is to use it in the further process, for example, testing for what’s the input.


如何在Windows上点子或easy_install tkinter

问题:如何在Windows上点子或easy_install tkinter

我的空闲状态抛出错误,并指出tkinter无法导入。

有没有一种简单的方法可以tkinter通过pip或安装easy_install

似乎有很多软件包名称在出现……

此版本以及其他各种各样的版本tkinter-pypy均无效。

pip install python-tk

我在Windows上使用python 2.7,不能apt-get

谢谢。

My Idle is throwing errors that and says tkinter can’t be imported.

Is there a simple way to install tkinter via pip or easy_install?

There seem to be a lot of package names flying around for this…

This and other assorted variations with tkinter-pypy aren’t working.

pip install python-tk

I’m on Windows with Python 2.7 and I don’t have apt-get or other system package managers.


回答 0

好吧,我可以在这里看到两个解决方案:

1)按照适用于PythonDocs-Tkinter安装适用于Windows):

所有标准的Python发行版都包含Tkinter(以及从Python 3.1开始的ttk)。使用支持Tk 8.5或更高版本以及ttk的Python版本很重要。我们建议从ActiveState安装“ ActivePython”发行版,其中包括您需要的所有内容。

在您的Web浏览器中,转到Activestate.com,然后按照链接下载Windows版ActivePython社区版。确保您下载的是3.1或更高版本,而不是2.x版本。

运行安装程序,然后继续。最后,您将获得一个全新的ActivePython安装,例如位于C:\python32。在Windows命令提示符或“开始”菜单的“运行…”命令中,您应该能够通过以下方式运行Python Shell:

% C:\python32\python

这应该给您Python命令提示符。在提示符下,输入以下两个命令:

>>> import tkinter
>>> tkinter._test()

这应该会弹出一个小窗口。窗口顶部的第一行应显示“这是Tcl / Tk版本8.5”;确保它不是8.4!

2)卸载64位Python,然后安装32位Python。

Well I can see two solutions here:

1) Follow the Docs-Tkinter install for Python (for Windows):

Tkinter (and, since Python 3.1, ttk) are included with all standard Python distributions. It is important that you use a version of Python supporting Tk 8.5 or greater, and ttk. We recommend installing the “ActivePython” distribution from ActiveState, which includes everything you’ll need.

In your web browser, go to Activestate.com, and follow along the links to download the Community Edition of ActivePython for Windows. Make sure you’re downloading a 3.1 or newer version, not a 2.x version.

Run the installer, and follow along. You’ll end up with a fresh install of ActivePython, located in, e.g. C:\python32. From a Windows command prompt, or the Start Menu’s “Run…” command, you should then be able to run a Python shell via:

% C:\python32\python

This should give you the Python command prompt. From the prompt, enter these two commands:

>>> import tkinter
>>> tkinter._test()

This should pop up a small window; the first line at the top of the window should say “This is Tcl/Tk version 8.5”; make sure it is not 8.4!

2) Uninstall 64-bit Python and install 32 bit Python.


回答 1

Tkinter库在每个Python安装中都是内置的。而且由于您使用的是Windows,我相信您是通过Python的网站上的二进制文件安装的吗?

如果是这样,那么很可能您输入的命令是错误的。它应该是:

import Tkinter as tk

注意Tkinter开头的大写字母T。

对于Python 3,

import tkinter as tk

The Tkinter library is built-in with every Python installation. And since you are on Windows, I believe you installed Python through the binaries on their website?

If so, Then most probably you are typing the command wrong. It should be:

import Tkinter as tk

Note the capital T at the beginning of Tkinter.

For Python 3,

import tkinter as tk

回答 2

如果您使用virtualenv,则可以使用sudo apt-get install python-tk(python2),sudo apt-get install python3-tk(python3)安装tkinter,并且在虚拟环境中也可以正常工作

If you are using virtualenv, it is fine to install tkinter using sudo apt-get install python-tk(python2), sudo apt-get install python3-tk(python3), and and it will work fine in the virtual environment


回答 3

在安装时,请确保Tcl/Tk选择下Will be installed on hard drive。如果安装时带有左侧的叉号,则不会安装Tkinter。

在此处输入图片说明

Python 3也是如此:

在此处输入图片说明

When installing make sure that under Tcl/Tk you select Will be installed on hard drive. If it is installing with a cross at the left then Tkinter will not be installed.

enter image description here

The same goes for Python 3:

enter image description here


回答 4

当您为Windows安装python时,请使用标准选项或安装它要求的所有内容。我收到错误消息是因为我取消选择了tcl。

When you install python for Windows, use the standard option or install everything it asks. I got the error because I deselected tcl.


回答 5

在Linux中也有同样的问题。这解决了。(我正在使用Debian 9衍生的本生氦气)

$ sudo apt-get install python3-tk

Had the same problem in Linux. This solved it. (I’m on Debian 9 derived Bunsen Helium)

$ sudo apt-get install python3-tk


回答 6

我发布的是最佳答案,重新引用了我认为没有用的文档。

如果您在安装窗口中选择了tkinter,则会在Windows IFF上将python install打包在一起。

解决方法是修复安装(通过卸载GUI即可),然后选择安装tk。在此过程中,您可能需要指向或重新下载二进制文件。直接从activestate下载对我不起作用。

这是人们在Windows上经常遇到的问题,因为如果您不知道TCL / TK是什么,很容易不想安装它,但是Matplotlib等需要它。

I’m posting as the top answer requotes the documentation which I didn’t find useful.

tkinter comes packaged with python install on windows IFF you select it during the install window.

The solution is to repair the installation (via uninstall GUI is fine), and select to install tk this time. You may need to point at or redownload the binary in this process. Downloading directly from activestate did not work for me.

This is a common problem people have on windows as it’s easy to not want to install TCL/TK if you don’t know what it is, but Matplotlib etc require it.


回答 7

在python中,Tkinter是默认软件包,您可以修复安装并选择Tcl / Tk。修理运行此命令时,应按以下方式安装DDL:在此处输入图片说明

In python, Tkinter was a default package, you can repair the installation and select Tcl/Tk. repair When you run this, DDL should be installed like so: enter image description here


回答 8

我在Win-8和python-3.4 32位上也遇到了类似的问题,可以通过从python.org下载相同版本来解决。

下一步将是点击修复按钮并安装Tk / tkinter软件包,或者只是修复。现在应该获得Python34 / Lib / tkinter模块。导入tkinter应该工作..

I had the similar problem with Win-8 and python-3.4 32 bit , I got it resolved by downloading same version from python.org .

Next step will be to hit the repair button and Install the Tk/tkinter Package or Just hit the repair. Now should get Python34/Lib/tkinter Module present. The import tkinter should work ..


回答 9

在内部cmd,运行命令pip install tk和Tkinter应该安装。

Inside cmd, run command pip install tk and Tkinter should install.


回答 10

最简单的方法:

cd C:\Users\%User%\AppData\Local\Programs\Python\Python37\Scripts> 
pip install pythonds 

安装屏幕截图

Easiest way to do this:

cd C:\Users\%User%\AppData\Local\Programs\Python\Python37\Scripts> 
pip install pythonds 

Screenshot of installing


回答 11

如果您使用的是python 3.4.1,则只需编写此行,from tkinter import *这会将模块中的所有内容放入程序的默认命名空间。实际上,不是像tkinter.Button您说的那样说一个按钮Button

if your using python 3.4.1 just write this line from tkinter import * this will put everything in the module into the default namespace of your program. in fact instead of referring to say a button like tkinter.Button you just type Button


如何在matplotlib中更新图?

问题:如何在matplotlib中更新图?

我在这里重新绘制图形时遇到问题。我允许用户在时间刻度(x轴)中指定单位,然后重新计算并调用此函数plots()。我希望该图可以简单地更新,而不是将另一个图添加到该图上。

def plots():
    global vlgaBuffSorted
    cntr()

    result = collections.defaultdict(list)
    for d in vlgaBuffSorted:
        result[d['event']].append(d)

    result_list = result.values()

    f = Figure()
    graph1 = f.add_subplot(211)
    graph2 = f.add_subplot(212,sharex=graph1)

    for item in result_list:
        tL = []
        vgsL = []
        vdsL = []
        isubL = []
        for dict in item:
            tL.append(dict['time'])
            vgsL.append(dict['vgs'])
            vdsL.append(dict['vds'])
            isubL.append(dict['isub'])
        graph1.plot(tL,vdsL,'bo',label='a')
        graph1.plot(tL,vgsL,'rp',label='b')
        graph2.plot(tL,isubL,'b-',label='c')

    plotCanvas = FigureCanvasTkAgg(f, pltFrame)
    toolbar = NavigationToolbar2TkAgg(plotCanvas, pltFrame)
    toolbar.pack(side=BOTTOM)
    plotCanvas.get_tk_widget().pack(side=TOP)

I’m having issues with redrawing the figure here. I allow the user to specify the units in the time scale (x-axis) and then I recalculate and call this function plots(). I want the plot to simply update, not append another plot to the figure.

def plots():
    global vlgaBuffSorted
    cntr()

    result = collections.defaultdict(list)
    for d in vlgaBuffSorted:
        result[d['event']].append(d)

    result_list = result.values()

    f = Figure()
    graph1 = f.add_subplot(211)
    graph2 = f.add_subplot(212,sharex=graph1)

    for item in result_list:
        tL = []
        vgsL = []
        vdsL = []
        isubL = []
        for dict in item:
            tL.append(dict['time'])
            vgsL.append(dict['vgs'])
            vdsL.append(dict['vds'])
            isubL.append(dict['isub'])
        graph1.plot(tL,vdsL,'bo',label='a')
        graph1.plot(tL,vgsL,'rp',label='b')
        graph2.plot(tL,isubL,'b-',label='c')

    plotCanvas = FigureCanvasTkAgg(f, pltFrame)
    toolbar = NavigationToolbar2TkAgg(plotCanvas, pltFrame)
    toolbar.pack(side=BOTTOM)
    plotCanvas.get_tk_widget().pack(side=TOP)

回答 0

您基本上有两个选择:

  1. 完全执行当前操作,但是在重新配置数据之前先调用graph1.clear()graph2.clear()。这是最慢,但最简单和最可靠的选择。

  2. 除了重新绘制外,您还可以更新绘图对象的数据。您需要在代码中进行一些更改,但这应该比每次重新绘制都快得多。但是,您要绘制的数据的形状无法更改,并且如果数据范围正在更改,则需要手动重置x和y轴限制。

举一个第二种选择的例子:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 6*np.pi, 100)
y = np.sin(x)

# You probably won't need this if you're embedding things in a tkinter plot...
plt.ion()

fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma

for phase in np.linspace(0, 10*np.pi, 500):
    line1.set_ydata(np.sin(x + phase))
    fig.canvas.draw()
    fig.canvas.flush_events()

You essentially have two options:

  1. Do exactly what you’re currently doing, but call graph1.clear() and graph2.clear() before replotting the data. This is the slowest, but most simplest and most robust option.

  2. Instead of replotting, you can just update the data of the plot objects. You’ll need to make some changes in your code, but this should be much, much faster than replotting things every time. However, the shape of the data that you’re plotting can’t change, and if the range of your data is changing, you’ll need to manually reset the x and y axis limits.

To give an example of the second option:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 6*np.pi, 100)
y = np.sin(x)

# You probably won't need this if you're embedding things in a tkinter plot...
plt.ion()

fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma

for phase in np.linspace(0, 10*np.pi, 500):
    line1.set_ydata(np.sin(x + phase))
    fig.canvas.draw()
    fig.canvas.flush_events()

回答 1

您还可以执行以下操作:这将在for循环的50个循环的绘图上绘制10×1随机矩阵数据。

import matplotlib.pyplot as plt
import numpy as np

plt.ion()
for i in range(50):
    y = np.random.random([10,1])
    plt.plot(y)
    plt.draw()
    plt.pause(0.0001)
    plt.clf()

You can also do like the following: This will draw a 10×1 random matrix data on the plot for 50 cycles of the for loop.

import matplotlib.pyplot as plt
import numpy as np

plt.ion()
for i in range(50):
    y = np.random.random([10,1])
    plt.plot(y)
    plt.draw()
    plt.pause(0.0001)
    plt.clf()

回答 2

这对我有用。每次重复调用更新图形的函数。

import matplotlib.pyplot as plt
import matplotlib.animation as anim

def plot_cont(fun, xmax):
    y = []
    fig = plt.figure()
    ax = fig.add_subplot(1,1,1)

    def update(i):
        yi = fun()
        y.append(yi)
        x = range(len(y))
        ax.clear()
        ax.plot(x, y)
        print i, ': ', yi

    a = anim.FuncAnimation(fig, update, frames=xmax, repeat=False)
    plt.show()

“ fun”是一个返回整数的函数。FuncAnimation将反复调用“更新”,它将执行“ xmax”次。

This worked for me. Repeatedly calls a function updating the graph every time.

import matplotlib.pyplot as plt
import matplotlib.animation as anim

def plot_cont(fun, xmax):
    y = []
    fig = plt.figure()
    ax = fig.add_subplot(1,1,1)

    def update(i):
        yi = fun()
        y.append(yi)
        x = range(len(y))
        ax.clear()
        ax.plot(x, y)
        print i, ': ', yi

    a = anim.FuncAnimation(fig, update, frames=xmax, repeat=False)
    plt.show()

“fun” is a function that returns an integer. FuncAnimation will repeatedly call “update”, it will do that “xmax” times.


回答 3

如果有人碰到本文来寻找我想要的东西,我在

如何使用Matplotlib可视化标量2D数据?

http://mri.brechmos.org/2009/07/automatically-update-a-figure-in-a-loop(在web.archive.org上)

然后对其进行修改,以将imshow与输入的帧堆栈一起使用,而不是动态生成和使用轮廓。


从3D形状的图像阵列(nBins,nBins,nBins)开始,称为frames

def animate_frames(frames):
    nBins   = frames.shape[0]
    frame   = frames[0]
    tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
    for k in range(nBins):
        frame   = frames[k]
        tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
        del tempCS1
        fig.canvas.draw()
        #time.sleep(1e-2) #unnecessary, but useful
        fig.clf()

fig = plt.figure()
ax  = fig.add_subplot(111)

win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate_frames, frames)

我还发现了进行此整个过程的简单得多的方法,尽管不够健壮:

fig = plt.figure()

for k in range(nBins):
    plt.clf()
    plt.imshow(frames[k],cmap=plt.cm.gray)
    fig.canvas.draw()
    time.sleep(1e-6) #unnecessary, but useful

请注意,这两个似乎只适用于ipython --pylab=tkbackend = TkAgg

感谢您提供的所有帮助。

In case anyone comes across this article looking for what I was looking for, I found examples at

How to visualize scalar 2D data with Matplotlib?

and

http://mri.brechmos.org/2009/07/automatically-update-a-figure-in-a-loop (on web.archive.org)

then modified them to use imshow with an input stack of frames, instead of generating and using contours on the fly.


Starting with a 3D array of images of shape (nBins, nBins, nBins), called frames.

def animate_frames(frames):
    nBins   = frames.shape[0]
    frame   = frames[0]
    tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
    for k in range(nBins):
        frame   = frames[k]
        tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
        del tempCS1
        fig.canvas.draw()
        #time.sleep(1e-2) #unnecessary, but useful
        fig.clf()

fig = plt.figure()
ax  = fig.add_subplot(111)

win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate_frames, frames)

I also found a much simpler way to go about this whole process, albeit less robust:

fig = plt.figure()

for k in range(nBins):
    plt.clf()
    plt.imshow(frames[k],cmap=plt.cm.gray)
    fig.canvas.draw()
    time.sleep(1e-6) #unnecessary, but useful

Note that both of these only seem to work with ipython --pylab=tk, a.k.a.backend = TkAgg

Thank you for the help with everything.


回答 4

我发布了一个名为python-drawnow的软件包,该软件包提供了使图形更新的功能(通常在for循环内调用),类似于Matlab的drawnow

用法示例:

from pylab import figure, plot, ion, linspace, arange, sin, pi
def draw_fig():
    # can be arbitrarily complex; just to draw a figure
    #figure() # don't call!
    plot(t, x)
    #show() # don't call!

N = 1e3
figure() # call here instead!
ion()    # enable interactivity
t = linspace(0, 2*pi, num=N)
for i in arange(100):
    x = sin(2 * pi * i**2 * t / 100.0)
    drawnow(draw_fig)

该软件包适用于任何matplotlib图形,并提供了在每个图形更新或放入调试器后等待的选项。

I have released a package called python-drawnow that provides functionality to let a figure update, typically called within a for loop, similar to Matlab’s drawnow.

An example usage:

from pylab import figure, plot, ion, linspace, arange, sin, pi
def draw_fig():
    # can be arbitrarily complex; just to draw a figure
    #figure() # don't call!
    plot(t, x)
    #show() # don't call!

N = 1e3
figure() # call here instead!
ion()    # enable interactivity
t = linspace(0, 2*pi, num=N)
for i in arange(100):
    x = sin(2 * pi * i**2 * t / 100.0)
    drawnow(draw_fig)

This package works with any matplotlib figure and provides options to wait after each figure update or drop into the debugger.


回答 5

以上所有内容可能都是正确的,但是对我来说,“在线更新”数字仅适用于某些后端wx。您可能只是想尝试更改为该值,例如通过启动ipython / pylab ipython --pylab=wx!祝好运!

All of the above might be true, however for me “online-updating” of figures only works with some backends, specifically wx. You just might try to change to this, e.g. by starting ipython/pylab by ipython --pylab=wx! Good luck!


回答 6

这对我有用:

from matplotlib import pyplot as plt
from IPython.display import clear_output
import numpy as np
for i in range(50):
    clear_output(wait=True)
    y = np.random.random([10,1])
    plt.plot(y)
    plt.show()

This worked for me:

from matplotlib import pyplot as plt
from IPython.display import clear_output
import numpy as np
for i in range(50):
    clear_output(wait=True)
    y = np.random.random([10,1])
    plt.plot(y)
    plt.show()

ImportError:没有名为“ Tkinter”的模块

问题:ImportError:没有名为“ Tkinter”的模块

由于某些原因,我无法使用Tkinteror tkinter模块。在python shell中运行以下命令后

import Tkinter

要么

import tkinter

我得到这个错误

ModuleNotFoundError:没有名为“ Tkinter”的模块

要么

ModuleNotFoundError:没有名为“ tkinter”的模块

可能是什么原因,我们如何解决呢?

For some reason, I can’t use the Tkinter or tkinter module. After running the following command in the python shell

import Tkinter

or

import tkinter

I got this error

ModuleNotFoundError: No module named ‘Tkinter’

or

ModuleNotFoundError: No module named ‘tkinter’

What could be the reason for and how can we solve it?


回答 0

您可能需要使用以下一种(或类似的方式)安装它:

sudo apt-get install python3-tk 

sudo dnf install python3-tkinter

我已经使用Tkinter一段时间了。您为什么不尝试一下,让我知道它是否有效?

try:
    # for Python2
    from Tkinter import *   ## notice capitalized T in Tkinter 
except ImportError:
    # for Python3
    from tkinter import *   ## notice lowercase 't' in tkinter here

这是参考链接,这是文档

最好按照这里的建议检查版本:

if sys.version_info[0] == 3:
    # for Python3
    from tkinter import *   ## notice lowercase 't' in tkinter here
else:
    # for Python2
    from Tkinter import *   ## notice capitalized T in Tkinter

否则你会得到错误 ImportError: No module named tkinter

编辑1:为了使这个答案更通用,我从评论中借用了以下内容。在Fedora上,请使用以下任一命令

sudo dnf install python3-tkinter-3.6.6-1.fc28.x86_64

要么

sudo dnf install python3-tkinter

You probably need to install it using one of (or something similar to) the following:

sudo apt-get install python3-tk 

You can also mention version number like this sudo apt-get install python3.7-tk for python 3.7.

sudo dnf install python3-tkinter

Why don’t you try this and let me know if it worked:

try:
    # for Python2
    from Tkinter import *   ## notice capitalized T in Tkinter 
except ImportError:
    # for Python3
    from tkinter import *   ## notice lowercase 't' in tkinter here

Here is the reference link and here are the docs

Better to check versions as suggested here:

if sys.version_info[0] == 3:
    # for Python3
    from tkinter import *   ## notice lowercase 't' in tkinter here
else:
    # for Python2
    from Tkinter import *   ## notice capitalized T in Tkinter

Or you will get an error ImportError: No module named tkinter


Just to make this answer more generic I borrowed the following from Devendra Bhat‘s comment:

On Fedora please use either of the following commands

sudo dnf install python3-tkinter-3.6.6-1.fc28.x86_64

or

sudo dnf install python3-tkinter

回答 1

在使用Python 3时,该模块已重命名为tkinter,如文档中所述:

注意Tkinter在Python 3中已重命名为tkinter。将源转换为Python 3时,2to3工具将自动适应导入。

As you are using Python 3, the module has been renamed to tkinter, as stated in the documentation:

Note Tkinter has been renamed to tkinter in Python 3. The 2to3 tool will automatically adapt imports when converting your sources to Python 3.


回答 2

对于Windows 10,重要的是要在Python中签入安装可选功能“ tcl / tk和IDLE”。否则,您将收到ModuleNotFoundError:没有名为’tkinter’的模块。就我而言,在Python安装后无法通过“ pip install tkinter”之类的方式安装tkinter

For windows 10, it is important to check in the Python install the optional feature “tcl/tk and IDLE”. Otherwise you get a ModuleNotFoundError: No module named ‘tkinter’. In my case, it was not possible to install tkinter after the Python install with something like “pip install tkinter”


回答 3

对于 使用VSCode或PyCharm和Python 3.7.4的Windows 10-确保在安装中勾选了Tk。我尝试import tkinter as xyz使用较高/较低的tk以及所有没有运气的变体

起作用的是:

import tkinter
import _tkinter
tkinter._test()

一个实际的例子:

import tkinter
import _tkinter

HEIGHT = 700
WIDTH = 800

root = tkinter.Tk()

canvas = tkinter.Canvas(root, height = HEIGHT, width=WIDTH)
canvas.pack()

frame = tkinter.Frame(root, bg='red')
frame.pack()

root.mainloop()

For Windows 10 using either VSCode or PyCharm with Python 3.7.4 – make sure Tk is ticked in the install. I tried import tkinter as xyz with upper/lower t and k‘s and all variants without luck.

What works is:

import tkinter
import _tkinter
tkinter._test()

An example in action:

import tkinter
import _tkinter

HEIGHT = 700
WIDTH = 800

root = tkinter.Tk()

canvas = tkinter.Canvas(root, height = HEIGHT, width=WIDTH)
canvas.pack()

frame = tkinter.Frame(root, bg='red')
frame.pack()

root.mainloop()

回答 4

您可能需要为您的特定版本安装,当我使用许多版本的python,而在virtualenv中使用例如python 3.7的一个版本未导入tkinter时,我已经知道需要这样做,因此我必须为该版本安装它特别。

例如

sudo apt-get install python3.7-tk 

不知道为什么-但这已经发生了。

You might need to install for your specific version, I have known cases where this was needed when I was using many versions of python and one version in a virtualenv using for example python 3.7 was not importing tkinter I would have to install it for that version specifically.

For example

sudo apt-get install python3.7-tk 

No idea why – but this has occured.


回答 5

确保在运行python代码时,它位于python3上下文中。我遇到了同样的问题,我要做的就是输入以下命令:

sudo python3 REPLACE.py

sudo python REPLACE.py

后一个代码不正确,因为tkinter在python1或python2中显然不可用。

Make sure that when you are running your python code that it is in the python3 context. I had the same issue and all I had to do was input the command as:

sudo python3 REPLACE.py

versus

sudo python REPLACE.py

the latter code is incorrect because tkinter is apparently unnavailable in python1 or python2.


回答 6

使用命令检查您已安装的python版本 python --version

通过以下代码检查Tk模块是否正确安装

sudo apt-get install python3-tk 

然后检查您是否正在使用开源操作系统

检查以下路径中的tkinter模块 / home / python / site-packages / tkinter相应地 更改路径

check the python version you have installed by using command python --version

check for the Tk module installed correctly from following code

sudo apt-get install python3-tk 

Check if you are using open-source OS then

check the tkinter module in the following path /home/python/site-packages/tkinter change the path accordingly your system


回答 7

您只需要安装它,然后将其导入项目,如下所示:

此代码导入命令行:

sudo apt-get install python3-tk 

导入tkinter后,您的项目:

from tkinter import *

You just need to install it and import them your project like that :

this code import to command line :

sudo apt-get install python3-tk 

after import tkinter your project :

from tkinter import *

回答 8

在CentOS7上,要使其与Python2一起使用,我必须做:

yum -y install tkinter

在这里指出这一点是因为我认为会有一个pip包,但是实际上需要安装一个rpm。

On CentOS7, to get this working with Python2, I had to do:

yum -y install tkinter

Noting this here because I thought that there would be a pip package, but instead, one needs to actually install an rpm.


回答 9

tkinter是python附带的…卸载python,重新安装,就完成了

tkinter comes with python… uninstall python, reinstall it, you’re done


回答 10

检查易于执行的任务,可能会将其标记为已删除

sudo apt autoremove

然后检查并安装所需

Check apt for tasks, it may be marked for removed

sudo apt autoremove

Then check and install needed


回答 11

———在PYTHON 2.7上工作————

安装以下所有软件包

sudo apt-get install git
sudo apt-get install python-tk
sudo apt-get install python-pip
sudo apt install picolisp
sudo -H pip2 install --upgrade pip
sudo pip install -I pillow
sudo apt-get install python-imaging-tk
sudo apt-get install python-tk

——— WORKED ON PYTHON 2.7————

Install all below packages

sudo apt-get install git
sudo apt-get install python-tk
sudo apt-get install python-pip
sudo apt install picolisp
sudo -H pip2 install --upgrade pip
sudo pip install -I pillow
sudo apt-get install python-imaging-tk
sudo apt-get install python-tk

回答 12

tkinter应该带有最新的python,我不认为它是python 2附带的。我有同样的问题,但是一旦我升级到python 3.8,就安装了tkinter

Tkinter should come with the latest Python, I don’t think it comes with Python2. I had the same problem but once. I upgraded to Python 3.8 Tkinter was installed.


回答 13

在下面使用。

from tkinter import *
root=Tk()
.....
root.mainloop()

use below.

from tkinter import *
root=Tk()
.....
root.mainloop()