除非使用休眠时间,否则Python线程tkinter GUI无响应。

时间:2018-01-16 14:40:51

标签: python multithreading tkinter

我正在使用python 2.7并使用Tkinter构建了一个UI。我正在使用线程和队列来在主脚本工作时保持UI响应。基本摘要是脚本读取文本文件,解析其中的一些信息并将该信息放入字典中的字典和列表中,然后使用该信息发送TCP modbus请求(使用pyModbus)。然后它将响应/结果写入文本文件。结果还会打印出UI中包含的Text小部件。 Text小部件的更新由mainloop处理。

对于线程和队列我还是比较新的,我在解决这个问题时遇到了麻烦。

我遇到的问题是我需要在列表中的每个项目循环后包含~10ms的睡眠,以便UI保持响应。如果我包含睡眠时间它按预期工作,如果没有它冻结,直到线程进程完成,然后立即更新UI(如果没有使用线程那样)。 10毫秒的睡眠时间可以略短一些。任何金额更长也有效。

以下是处理更新日志的代码:

textQueue = Queue.Queue()

def TextDisplay(message, disTime="false", myColor="black", bgColor="white"):
    textQueue.put([message, disTime, myColor, bgColor])

class LogUI:
    def __init__(self, master):
        self.master = master

        '''other ui elements, not relevent'''

        self.mainLogFrame = Frame(self.master)
        self.mainLogFrame.pack(side="top", fill="both", expand="yes", padx=5, pady=2)

        self.logText = Text(self.mainLogFrame, height=2)
        self.logText.pack(side="left", fill="both", expand="yes", padx=5, pady=2)

        self.ThreadSafeTextDisplay()

    def ThreadSafeTextDisplay(self):
        while not textQueue.empty():
            tempText = textQueue.get(0)
            message = tempText[0]
            disTime = tempText[1]
            myColor = tempText[2]
            bgColor =  tempText[3]

            message = str(message) + "\n"

            '''bunch of formating stuff'''

            logUI.logText.insert(END, message)
            print message
            #NOTE: tried to include a sleep time here, no effect

        self.logText.after(10, self.ThreadSafeTextDisplay)

这是用户单击按钮时调用的非线程函数。

def ParseInputFile():
    '''non-threaded function, called when user clicks button'''
    inputList = []

    inputFile = mainUI.fullInFileEntry.get()
    with open(inputFile, 'r') as myInput:
        '''open file and put contents in list'''
        for line in myInput:
            inputList.append(line.strip())

    outFile = mainUI.outFileEntry.get().strip() + '.txt'

    i = 1
    tableBol = False
    inputDict = {}
    inputKeys = []
    tableID = None
    for item in inputList:
        '''parses out inputKeys, inputDict using regular expressions'''

    runInputGetQueue.put([inputKeys, inputDict, outFile, inputFile])

这是接收解析信息并处理modbus请求的线程函数(注意:我试着注释掉实际的modbus请求,没有效果):

def RunInputThread():
    time.sleep(.1)
    while 1:
        while not runInputGetQueue.empty():
            tempGet = runInputGetQueue.get(0)
            inputKeys = tempGet[0]
            inputDict = tempGet[1]
            outFile = tempGet[2]
            inputFile = tempGet[3]

            outFile = open(outFile, 'w')

            TextDisplay('< Start of %s input file > ' % inputFile, True, 'blue')

            for key in inputKeys:
                '''loops through the keys in the dictionary'''
                TextDisplay(key) #just used as an example.
                for lineIndex in range(len(inputDict[key]['tableLines'])):
                    '''lots of code that loops thorugh the lines of input file, frequently calls the TextDisplay() function'''
                    TextDisplay(inputDict[key][lineIndex]) #just used as an example.

                    time.sleep(0.01) #UI will become unresponseive if not included. 



            outFile.close()
        time.sleep(0.001)

1 个答案:

答案 0 :(得分:0)

找到了一种让UI主要响应的方法。如上面的评论中所述,队列正在接收快速的功能,该功能将持续工作,导致UI锁定。我做了它所以它将打印最多5条消息,然后休息1分钟并回忆起允许UI“赶上”的功能。消息的打印速度几乎与它们一样快。

如果移动或调整大小,UI将略微无响应。在运行时,我没有与其他UI元素交互的问题。

如果你不介意它慢,你也可以将while循环更改为if语句。我运行的一个过程从14秒开始,使用if语句下降到大约5或6秒,使用下面的代码。如果您将pullCount break点更改为1而不是5,则会相同。

def ThreadSafeTextDisplay(self):
    pullCount = 0
    while not textQueue.empty():
        pullCount += 1
        tempText = textQueue.get(0)
        message = tempText[0]
        disTime = tempText[1]
        myColor = tempText[2]
        bgColor =  tempText[3]

        message = str(message) + "\n"

        '''bunch of formatting stuff'''

        logUI.logText.insert(END, message)
        print message
        if pullCount >= 5: break  #you can change the 5 value to whatever you want, the higher the number the faster stuff will print but the UI will start to become unresponsive. 
    self.logText.after(1, self.ThreadSafeTextDisplay)