【问题标题】:Python GUI ThreadingPython GUI 线程
【发布时间】:2016-02-08 17:10:38
【问题描述】:

我想让一个函数在 tkinter GUI 中连续运行。我附上了一些shell代码:

#!/usr/bin/env python3

import tkinter as tk
from time import sleep
import os
import sys

class Application(Frame):

    def __init__(self, master):
        super(Application, self).__init__(master)
        self.grid()
        self.create_widgets()

        def create_widgets(self):
        ......

root = Tk()  

def run_continously:
    ... -- calls sleep -- ...
    root.after(3000, run_continuously)


root.geometry('%dx%d+%d+%d' % (w, h, x, y))
app = Application(root)
root.after(3000, run_continuously)          
root.mainloop()

在运行 GUI 时,它往往会运行一次“run_continuously”函数,然后 GUI 就会死机。我怀疑这是由于 sleep 函数(我在 run_continuously 函数中调用的)

我将如何在一个非常简单的线程中实现“run_continuously”函数来解决这个问题?在线程中运行该函数甚至可以解决问题吗? “run_continuously”函数根本不需要与 Application 类交互。我希望它只是在后台运行并在主循环完成时停止。

代码到此结束:

def run_continuously(quit_flag):
    print("in it")
    if not quit_flag:
        GPIO.output(DIR_PIN, True)
        for i in range(steps):
            print("in loop")
            GPIO.output(STEP_PIN, True)
            sleep(sDelay)
            GPIO.output(STEP_PIN, False)
            sleep(sDelay)
        sleep(wait_time)
        GPIO.output(DIR_PIN, False)    
        for i in range(steps):
            GPIO.output(STEP_PIN, True)
            sleep(sDelay)
            GPIO.output(STEP_PIN, False)
            sleep(sDelay)
            print("run motor")
        root.after(1000, run_continuously(quit_flag,))


#=================================================================
# main
#=================================================================

root = Tk()                            # Create the GUI root object
press1 = StringVar()
press2 = StringVar()

x = 275
y = 50
w = 580
h = 250


root.geometry('%dx%d+%d+%d' % (w, h, x, y))
app = Application(root)          # Create the root application window
quit_flag = False                
root.after(0, app.read_pressure)
motor_thread = threading.Thread(target=run_continuously, args=(quit_flag,)).start()
root.mainloop()
quit_flag=True
motor_thread.join()

【问题讨论】:

  • 你为什么在run_continuously中调用睡眠?这有什么用?
  • 为什么不试试import threading 并创建一个threading.Thread 来运行run_continuously
  • 连续运行功能输出值来控制步进电机。它使用睡眠功能,但不是唯一的。
  • David - 我试过了,run_continuously 函数只执行了一次:
  • root.aftertime.sleep 的传统知识等效项。在 tk 线程中,不要使用sleep。由于您的代码不是 MCVE,google.com/search?q=mcve&ie=utf-8&oe=utf-8,因此不可能对您的代码进行试验以确定哪里出了问题。如果您需要在 run_continuously 内暂停,那么您可能需要将其分解为子函数以使用 root.after 进行暂停。

标签: python multithreading user-interface tkinter


【解决方案1】:

这是一个minimal, complete, and verifiable example,如果按下 'QUIT' 按钮或按下 Ctrl-C,则会干净地退出:

from Tkinter import *
import multiprocessing
import threading
import time
import logging


class Application(Frame):
    def create_widgets(self):
        self.quit_button = Button(self)
        self.quit_button['text'] = 'QUIT'
        self.quit_button['fg'] = 'red'
        self.quit_button['command'] = self.quit
        self.quit_button.pack({'side': 'left'})

    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.quit_button = None
        self.pack()
        self.create_widgets()
        self.poll()

    def poll(self):
        """
        This method is required to allow the mainloop to receive keyboard
        interrupts when the frame does not have the focus
        """
        self.master.after(250, self.poll)


def worker_function(quit_flag):
    counter = 0
    while not quit_flag.value:
        counter += 1
        logging.info("Work # %d" % counter)
        time.sleep(1.0)


format = '%(levelname)s: %(filename)s: %(lineno)d: %(message)s'
logging.basicConfig(level=logging.DEBUG, format=format)
root = Tk()
app = Application(master=root)
quit_flag = multiprocessing.Value('i', int(False))
worker_thread = threading.Thread(target=worker_function, args=(quit_flag,))
worker_thread.start()
logging.info("quit_flag.value = %s" % bool(quit_flag.value))
try:
    app.mainloop()
except KeyboardInterrupt:
    logging.info("Keyboard interrupt")
quit_flag.value = True
logging.info("quit_flag.value = %s" % bool(quit_flag.value))
worker_thread.join()

【讨论】:

  • 嗨大卫,这在大多数情况下都很好用。我在 motor_thread 声明之后添加了 .start() 。运行程序它工作正常,但退出后我收到以下错误: Traceback (last recent call last): File "mDS_v14.py", line 509, in motor_thread.join() AttributeError: 'NoneType' object has no attribute 'join' -----参见上面的更新代码。
  • @RickySpanish:查看我的答案中的更改。当您在链中调用start() 时,它会返回None,因此您会丢失线程句柄。如上所示分离调用以保留线程句柄。在run_continuously 中使用while 循环并使用time.sleep 而不是root.after
  • 好的,这很好,但是现在我退出 GUI 后 motor_thread 不会停止。用户按下退出按钮后,我调用 root.destroy() 但电机继续运行。退出主循环后,quit_flag 保持为假
  • @RickySpanish:我想你想根据这个StackOverflow question 打电话给root.quit()。我的猜测是root.destroy() 会阻止root.mainloop() 之后的任何代码运行。
  • 是的,我也试过了:在我调用 root.quit() 之后,GUI 冻结(不退出)并且电机继续运行 我可以让电机停止运行的唯一方法是做 GPIO 清理(控制步进电机的 GPIO 引脚)
猜你喜欢
  • 2018-10-10
  • 2014-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多