将Queue与tkinter一起使用(线程化,Python 3)

时间:2018-02-19 08:40:32

标签: python multithreading python-3.x tkinter queue

我在Python 3中有一个脚本,我试图使用tkinter为它制作一个GUI。

以下是工作代码的完整示例:

#!/usr/bin/python
# coding: utf-8

import pickle
import openpyxl

from tkinter import *
import threading
import queue


class Worker():

    def __init__(self):
        self.one_name_list = []
        self.dic = {}

        self.root = Tk()
        self.root.title("GUI Python")
        self.root.geometry("820x350")

        self.thread_queue = queue.Queue()

        self.btn1 = Button(text="start counting", width = '20', height = '1', background = "#555", foreground = "#ccc", command=self.start_working)
        self.btn1.grid(row=0, column=0, columnspan=2, ipadx=10, ipady=6, padx=5, pady=5, sticky=N)
        self.btn2 = Button(text="stop counting", width = '20', height = '1', background = "#555", foreground = "#ccc", command=self.stop_running)
        self.btn2.grid(row=1, column=0, columnspan=2, ipadx=10, ipady=6, padx=5, pady=5, sticky=N)
        self.btn5 = Button(text="clear window", width = '10', height = '1', background = "#555", foreground = "#ccc", command=self.tex_clear)
        self.btn5.grid(row=3, column=0, columnspan=2, ipadx=10, ipady=6, padx=5, pady=5, sticky=N)

        self.tex = Text(self.root, width = 72, height = 20, font="Verdana 10", wrap=WORD)
        self.tex.grid(row=0, column=2, rowspan=4, ipadx=10, ipady=6, padx=5, pady=5)

        self.S = Scrollbar(self.root, orient="vertical", command=self.tex.yview)
        self.S.grid(row=0, column=4, rowspan=4,  ipady=143, pady=5, sticky=W)
        self.tex.config(yscrollcommand=self.S.set)

        self.root.after(100, self.listen_for_result)

        self.root.mainloop()


    def read_from_pickle_file(self, filename):
        """ Reads python object from pickle file. """

        # with open(filename, 'rb') as handle:
        #     obj = pickle.load(handle)

        self.thread_queue.put('Got list file.\n')
        return True

    def get_boxes(self, xlsx_filename, txt_filename=None):

        pass # does some job
        self.thread_queue.put('Got boxes list.\n')

    def tex_clear(self):

        self.tex.delete('1.0', END)
        self.tex.see("end")

    def stop_running(self):

        pass # stops somehow\

    def _print(self, text):
        self.tex.insert(END, text)
        self.tex.see("end")

    def start_working(self):

        t = threading.Thread(target=self.start_working_2)
        t.start()

    def start_working_2(self):

        self.one_name_list = self.read_from_pickle_file('1.pickle')
        self.root.after(100, self.listen_for_result)

        self.boxes_list = self.get_boxes('1.xlsx')
        self.root.after(100, self.listen_for_result)

        self.thread_queue.put('Getting files\n')
        self.root.after(100, self.listen_for_result)

    def listen_for_result(self):
        """ Check if there is something in the queue. """

        try:
            self.res = self.thread_queue.get(0)
            self._print(self.res)
        except queue.Empty:
            self.root.after(100, self.listen_for_result)


if __name__ == '__main__':

    se = Worker()

您可以运行它并查看工作窗口。

我对tkinter很新,所以我有几个问题,并会感激任何帮助。感谢。

这个GUI的想法 - 有3个按钮 - 开始运行,停止运行和清除文本窗口。文本窗口 - 应该是控制台的替代 - 所有消息都应该打印在文本窗口中,而不是控制台。

目前我正在使用队列打印消息。但我想我是以错误的方式使用它 - 因为我每次放东西后都需要手动检查队列。

所以,问题:

1)有没有办法自动检查队列的所有时间 - 并立即将所有进入队列的内容打印到文本窗口,无论它来自哪个线程? (每次我把东西放在那里之后我都可以忍受检查队列,但是会有几个函数无法预测它们会将某些东西发送到队列的次数 - 所以我将无法检查队列中的未知次数。)

如果你回答第一个问题,我会考虑回答这个问题。其他问题是可选的。谢谢。

2)我是否正确启动了GUI?它应该在 init ()还是在其他地方?

3)如何隐藏控制台窗口? (尝试重命名为.pyw - 既没有控制台,也没有GUI出现。试着在self.root = Tk()之后放置self.root.withdraw() - 结果:控制台出现了,GUI - 没有。)

4)此代码(GUI,线程,队列)中是否有任何笨拙或愚蠢的地方?正如我所说 - 我是tkinter的新手,并通过几本手册编写了这段代码 - 所以我可能会误解其中的部分或全部,并以错误的方式进行。

提前感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

它想通了很简单: 在这个功能

def listen_for_result(self):
        """ Check if there is something in the queue. """

        try:
            self.res = self.thread_queue.get(0)
            self._print(self.res)
        except queue.Empty:
            self.root.after(100, self.listen_for_result)

应该再添加一次自己的调用 - 即使在成功打印之后也是如此。在此之后 - 我可以从任何地方发送文本到队列,并且在将文本发送到队列后立即打印而不调用此函数。

def listen_for_result(self):
        """ Check if there is something in the queue. """

        try:
            self.res = self.thread_queue.get(0)
            self._print(self.res)
            self.root.after(100, self.listen_for_result)
        except queue.Empty:
            self.root.after(100, self.listen_for_result)

所以现在

self.thread_queue.put('Getting files\n')

可以在任何线程中使用。而不是

 self.thread_queue.put('Getting files\n')
 self.root.after(100, self.listen_for_result)

像以前一样的双线。