问题
I have a script in Python 3 and I'm trying to make a GUI for it using tkinter.
Here is a complete example of working code:
#!/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()
You can run it and see the working window.
I'm quite new to tkinter, so I have several questions and will be gratefull for any help. Thanks.
The idea of this GUI - is that there are 3 buttons - start running, stop running, and clear text window. Text window - should be a substitute for a console - all messages should be printed in text window, instead of console.
For now I'm using queue to print messages. But I guess I'm using it in a wrong way - because I need manually to check the queue every time after I put something there.
So, QUESTIONS:
1) Is there a way to check queue autamatically ALL THE TIME - and instantly print to text window everything which gets to the queue, no matter from which thread it came there? (I can put up with checking queue every time after I put something there, but there will be couple of functions where it is impossible to predict how many times they will send something to queue - so I will not be able to check queue for unknown number of times.)
I will consider the question answered if you answer the FIRST question. Other questions are optional. Thank you.
2) Am I starting the GUI correctly? Should it be in the init() or somewhere else?
3) how to hide the console window? (tried renaming to .pyw - neither console, nor GUI showed up. Tried putting self.root.withdraw() after self.root = Tk() - the result: console showed up, GUI - not.)
4) Is there any clumsy or stupid places in this code (GUI, threading, queue)? As I told - I'm new to tkinter and wrote this code by several manuals - so I could misunderstand some or all of them and do it in a wrong way.
Thanks in advance for your help.
回答1:
It figured out to be quite simple: in this function
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)
should be added one more call to itself - even after successfull printing. After this - I can send text to queue from anywhere and it will be printed without calling this function right after sending text to queue.
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)
So now
self.thread_queue.put('Getting files\n')
can be used in any thread. Instead of
self.thread_queue.put('Getting files\n')
self.root.after(100, self.listen_for_result)
double lines like before.
来源:https://stackoverflow.com/questions/48862213/using-queue-with-tkinter-threading-python-3