使用tkinter按钮

时间:2015-07-21 01:18:05

标签: python multithreading tkinter multiprocessing

我正在构建一个应用程序,根据用户输入的内容进行数千次(可能是数百万次)的计算。因为这些计算可能需要很长时间,所以我想使用Python的多处理模块。这是我的困境;我想设置一个带有取消按钮的tkinter窗口,以停止在我使用Pool设置的处理器中运行的计算。我尝试使用线程来允许弹出窗口运行,但是当我运行代码时会发生一些有趣的事情。

当我按下'开始'按钮,池按预期开始运行。但是,即使它在自己的线程上,我的弹出窗口也不显示。甚至更奇怪,当我告诉我的IDE(PyCharm)停止程序时,弹出窗口显示并且计算仍在运行,直到我按下退出'弹出窗口中的按钮,或者完全杀死程序。

所以我的问题是:为什么我的弹出窗口显示,即使它在自己的线程上?我正确使用多处理模块吗?或问题是完全不同的?

import tkinter as tk
from tkinter import ttk
import random
import threading
import time
from multiprocessing import Pool

def f(x):
    print(x*x)
    time.sleep(random.randrange(1,5))  # simulates long calculations
    # Extra math stuff here...

def processor():
    global calc
    calc = Pool(4)
    calc.map(f, [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30])
    print("Done")

def calculation_start():
    p = threading.Thread(target=progress_window)  # Thread to open a new window for the user to cancel a calculation
    p.start()
    processor()  # Calls the function to run the calculations

def progress_window():
    # This is the window that pops up to let a user cancel a calculation.
    popup = tk.Toplevel()
    endButton = ttk.Button(popup, text="End", command=lambda :(calc.terminate()))
    endButton.pack()


master = tk.Tk()

startButton = ttk.Button(master, text="Start", command=calculation_start)
startButton.pack()

tk.mainloop()

编辑: 我尝试将processor函数切换到线程而不是progress_window函数。

def calculation_start():
    p = threading.Thread(target=processor)  # Thread for the function to run the calculations
    p.start()
    progress_window()  # Open a new window for the user to cancel a calculation

出现我的弹出窗口,结束'按钮看起来像按下时停止处理器,但它永远不会继续超过该点。它就像calc.map(f, [1,2,3,...]函数中的processor()一样停滞不前。

1 个答案:

答案 0 :(得分:0)

来自Pool.map文档:

  

它会阻塞,直到结果准备就绪。

这意味着,是的,工作正在其他线程上完成,但主线程(调用map的线程)在其他线程完成之前不会执行任何其他操作。您希望使用map_async和(对于长时间运行的交互式进程),提供一个回调,以便在池完成时执行操作(可能隐藏弹出窗口)。

如果您希望程序只显示弹出窗口然后退出,则需要使用map_async返回的AsyncResult。使用after_idle设置空闲处理程序(请参阅tkinter universal widget methods并记住您需要在每次调用后手动重新添加处理程序)。在每次空闲呼叫之后,您可以呼叫AsyncResult.ready()AsyncResult.get(<timeout>)以查看该池是否已完成。一旦完成,您就可以安全地处理结果并退出。