首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Tkinter GUI在运行循环时冻结

Tkinter GUI在运行循环时冻结
EN

Stack Overflow用户
提问于 2019-05-31 05:07:50
回答 2查看 2K关注 0票数 -1

我是python编程的新手,我一直在做一个项目,它可以根据选择的颜色点击图像。我一直在使用一个程序,当我点击开始按钮时,它会循环搜索50次。然而,我一直在尝试实现一个停止按钮,但问题是我的代码在循环运行时冻结。有什么想法吗?

我听说要尝试线程化,但它似乎非常复杂,并且我无法正确地遵循与我的代码相关的任何教程。顺便说一下,搜索到的图像一直在测试我一直在使用的存储在程序文件中的图像。

代码语言:javascript
复制
from imagesearch import *
import pyautogui
import tkinter as tk
from tkinter import *
from tkinter.ttk import *
import time
import threading


# ---Defined Programs---
def run():
    global enterColor
    enterColor = str(enterColorField.get())
    program(enterColor)


def program(color):
    whitePos = imagesearch_numLoop(str(color) + ".PNG", 0, 50)
    pyautogui.moveTo(whitePos[0] + 20, whitePos[1] + 10)
    pyautogui.click()


def stop():
    print("Placeholder")


# ---Main Runner---
window = tk.Tk()
window.geometry("250x250")
window.configure(background="#181b54")

app = tk.Frame(window)
app.grid()

enterColorLabel = tk.Label(window, text="Enter Color:", bg="#181b54", fg="white")
enterColorLabel.place(x=10, y=50)

enterColorField = Combobox(window)
enterColorField['values'] = ("Black", "White")
enterColorField.current("0")  # set the selected item
enterColorField.place(x=10, y=70)

submitButton = tk.Button(window, text="Start", bg="#66ff00", command=run)
submitButton.place(x=10, y=130)

stopButton = tk.Button(window, text="Stop", bg="red", command=stop)
stopButton.place(x=50, y=130)

window.mainloop()


#---New Python Script---
import cv2
import numpy as np
import pyautogui
import random
import time

def imagesearch_numLoop(image, timesample, maxSamples, precision=0.8):
    pos = imagesearch(image, precision)
    count = 0
    while pos[0] == -1:
        print(image+" not found, waiting")
        count = count + 1
        if count>maxSamples:
            break
        pos = imagesearch(image, precision)
    return pos

每当单击start时,整个代码都会冻结。我连(x)都出不去。

EN

回答 2

Stack Overflow用户

发布于 2019-05-31 07:37:50

这里有一个很有希望的简单的多处理方法,它将对你起作用。我们将有三个主要函数。第一个将是一个示例循环,您将把您的处理放在其中。我在函数中包含了参数,向您展示了在使用多进程时传递args和kwargs是可能的。

代码语言:javascript
复制
def loop(a, b, c, d):
     # Will just sleep for 3 seconds.. simulates whatever processing you do.
    time.sleep(3)
    return

接下来是一个函数,我们将使用它来对多进程进行排队。

代码语言:javascript
复制
def queue_loop():
    p = multiprocessing.Process(target = loop, 
                                args = (1, 2),
                                kwargs = {"c": 3, "d": 4})
    # You can pass args and kwargs to the target function like that
    # Note that the process isn't started yet. You call p.start() to activate it.
    p.start()
    check_status(p) # This is the next function we'll define.
    return

然后,您可能有兴趣了解流程在整个执行过程中的状态。例如,有时需要在运行命令时禁用某些按钮。

代码语言:javascript
复制
def check_status(p):
    """ p is the multiprocessing.Process object """
    if p.is_alive(): # Then the process is still running
        label.config(text = "MP Running")
        mp_button.config(state = "disabled")
        not_mp_button.config(state = "disabled")
        root.after(200, lambda p=p: check_status(p)) # After 200 ms, it will check the status again.
    else:
        label.config(text = "MP Not Running")
        mp_button.config(state = "normal")
        not_mp_button.config(state = "normal")
    return

将所有这些放到一个代码片段中:

代码语言:javascript
复制
import tkinter as tk
import multiprocessing
import time

def loop(a, b, c, d):
     # Will just sleep for 3 seconds.. simulates whatever processing you do.
    time.sleep(3)
    return

def queue_loop():
    p = multiprocessing.Process(target = loop, 
                                args = (1, 2),
                                kwargs = {"c": 3, "d": 4})
    # You can pass args and kwargs to the target function like that
    # Note that the process isn't started yet. You call p.start() to activate it.
    p.start()
    check_status(p) # This is the next function we'll define.
    return

def check_status(p):
    """ p is the multiprocessing.Process object """
    if p.is_alive(): # Then the process is still running
        label.config(text = "MP Running")
        mp_button.config(state = "disabled")
        not_mp_button.config(state = "disabled")
        root.after(200, lambda p=p: check_status(p)) # After 200 ms, it will check the status again.
    else:
        label.config(text = "MP Not Running")
        mp_button.config(state = "normal")
        not_mp_button.config(state = "normal")
    return


if __name__ == "__main__":
    root = tk.Tk()
    mp_button = tk.Button(master = root, text = "Using MP", command = queue_loop)
    mp_button.pack()
    label = tk.Label(master = root, text = "MP Not Running")
    label.pack()
    not_mp_button = tk.Button(master = root, text = "Not MP", command = lambda: loop(1,2,3,4))
    not_mp_button.pack()
    root.mainloop()

结果是,当您单击“使用MP”按钮时,命令按钮将被禁用,并且该进程将在不冻结UI的情况下启动。单击"Not MP“按钮将启动类似于”normal“的功能,并将冻结您的UI,就像您在自己的代码中注意到的那样。

票数 1
EN

Stack Overflow用户

发布于 2019-05-31 06:45:29

一个简单的答案是你不能在GUI设计中使用while循环。

但是您可以改用.after(delay, callback=None)方法。

下面是一个示例:

代码语言:javascript
复制
from tkinter import *

root = Tk()

def loop():
    print("Hi!")
    root.after(1000, loop) # 1000 is equal to 1 second.

root.after(1000, loop) # This line is to call loop() in 1 second.
root.mainloop()
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56385659

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档