与数字调整框控件变量一起使用时出现Python线程错误

2024-06-07 13:21:47 发布

您现在位置:Python中文网/ 问答频道 /正文

在反复阅读各种教程和在线参考资料之后,终于让我的第一个线程脚本“有点”运行了有点“因为我在启动过程中仍然遇到一些奇怪的错误。在

我的课程目标

  1. 使用tkinter构造的积分上/下计数器GUI。(Python 3.4.2)
  2. Spinbox作为用户入口点-用户可以输入“目标”整数 或者点击它的上/下箭头。在
  3. 当标签上的数字增加时,在标签上显示延迟更新/ 以满足(2)中提到的“目标”整数
  4. 当标签上显示的数字等于 目标价值
  5. 一个“Go”按钮,在用户输入目标值后启动斜坡上升/下降
  6. 停止更新的“停止”按钮可随时暂停斜坡上升/下降 在达到目标值之前

在这个用于调试的简化实现中,您将看到我似乎不必要地使用了spinbox控制变量。实际的(较长的程序)控制变量应用于触发用户输入到数字框的验证例程。实际上是控制变量在程序启动时给了我一些“奇怪的错误”

我的简化代码如下:

import tkinter as tk
import threading
import time

class controlPanel():
    def __init__(self, master):
        self.root = master
        self.buildWidgets()

    def buildWidgets(self):
        self.labelVar = 0
        self.ctrlVar  = tk.StringVar()
        self.ctrlVar.set(str(self.labelVar))
        self.delay = 0.5
        #+++++++++++++++++++++++++++++++++
        self.labelName  = tk.Label(self.root, text="Current Value: ", padx=3, pady=3)
        self.labelName.grid(row=0, column=0)
        self.labelValue = tk.Label(self.root, text=str(self.labelVar), padx=3, pady=3)
        self.labelValue.grid(row=0, column=1)
        #+++++++++++++++++++++++++++++++++
        self.spinboxName = tk.Label(self.root, text="Target: ", padx=3, pady=3)
        self.spinboxName.grid(row=1, column=0)
        self.spinBoxA = tk.Spinbox(self.root, from_=0, to=1000,
                                   textvariable=self.ctrlVar,
                                   width=10, justify=tk.CENTER)
        self.spinBoxA.grid(row=1, column=1)
        #+++++++++++++++++++++++++++++++++
        self.goButton   = tk.Button(self.root, text="Go", width=12,
                                    command=self.goButtonFunction,
                                    padx=3, pady=3)
        self.goButton.grid(row=2, column=1)
        #+++++++++++++++++++++++++++++++++
        self.stopButton = tk.Button(self.root, text="Stop", width=12,
                                    command=self.stopButtonFunction,
                                    padx=3, pady=3)
        self.stopButton.grid(row=2, column=0)
        #+++++++++++++++++++++++++++++++++
        #self.labelValue.update()
        #self.spinBoxA.update()
        self.root.update()

    def goButtonFunction(self):
        print('GO button clicked')
        self.flgRun = True

    def stopButtonFunction(self):
        print('STOP button clicked')
        self.flgRun = False



class controlThread(controlPanel):
    def __init__(self, master, name):
        self.root = master
        self.name = name
        controlPanel.__init__(self, self.root)

        self.flgRun     = False
        self.flgRunLock = threading.Lock()
        self.pollPeriod = 100 # polling period, in ms

        self.thread1 = threading.Thread(target = self.towardsTarget,
                                        name=self.name)
        self.thread1.daemon = False
        self.thread1.start()
        #time.sleep(5)
        self.pollFlgRun()
    #+++++++++++++++++++++++++++++++++
    def pollFlgRun(self): # polls self.flgRun every self.pollPeriod
        if self.flgRun:
            print('<< Entering pollFlgRun >>')
            time.sleep(0.01)
            self.towardsTarget()
            #self.flgRunLock.acquire() # lock thread to reset self.flgRun
            self.flgRun = False       # reset self.flgRun
            #self.flgRunLock.release() # release thread
        self.root.after(self.pollPeriod, self.pollFlgRun)
    #+++++++++++++++++++++++++++++++++    
    def towardsTarget(self):
        delay = 0.01  # delay in seconds
        time.sleep(delay)

        print('<< Entering towardsTarget >>')

        #self.flgRunLock.acquire()
        print('self.labelVar : ', str(self.labelVar))
            # Problem 1: the following reference to self.ctrlVar gave error everytime the
            # program starts. The error>> "RuntimeError: main thread is not in main loop"
            # subsequent click on controlPanel.goButton will still give the program behavior
            # specified above (i.e. no more error on subsequent clicks on the "Go" button)
            #
            # Problem 2: and when placed in a lock.acquire()/release() block, clicking the 
            # controlPanel.goButton will freeze up program 
        print('self.ctrlVar : ', self.ctrlVar.get()) 
        #self.flgRunLock.release()

        # test self.flgRun as well in case reset by controlPanel.stopButton
        while( self.flgRun and str(self.labelVar) != self.ctrlVar.get() ):
            print('WHILE loop')
            if( self.labelVar < int(self.ctrlVar.get()) ):
                self.labelVar +=1
                print('self.labelVar before sleep> ', self.labelVar)
                time.sleep(delay)
                print('self.labelVar AFTER sleep> ', self.labelVar)
                self.labelValue["text"] = str(self.labelVar)
                self.labelValue.update()
            else:
                self.labelVar -=1
                print('self.labelVar before sleep> ', self.labelVar)
                time.sleep(delay)
                print('self.labelVar AFTER sleep> ', self.labelVar)
                self.labelValue["text"] = str(self.labelVar)
                self.labelValue.update()



if __name__ == '__main__':
    root = tk.Tk()
    ctrl = controlThread(root, "X-thread")

问题1:

  • class controlThread.towardsTarget()内,print语句 每次程序运行时,引用self.ctrlVar都会导致错误 开始。错误是“RuntimeError:main thread is not in main 循环”。在
  • 随后点击“Go”按钮(controlPanel.goButton)仍将提供程序 上面指定的行为(即,在以后单击“Go”按钮时不再出错)

问题2:

  • 当问题1中提到的print语句放在 lock.acquire()/lock.release()块,单击“Go”按钮 (controlPanel.goButton)将冻结程序

我已经阅读了以下两页和相关的链接

但是上面两页提到的解决方案对我来说没有多大意义,因为如果完全删除引用printprint语句,则根本不会出现错误消息“RuntimeError:main thread is not in main loop”。在

我的问题是

  1. 问题1中出现此错误的原因是什么?在
  2. 我希望lock.acquire()/lock.release()块能够解决 但最终却冻结了程序。怎么可能 “print”语句引用可以避免此问题 self.ctrlVar到位了吗?在

Tags: textinselfmaindefsleeprootthread

热门问题