The Python code for this GUI console is:
class ConsoleUIError(Exception): def __init__(self, msg): self.value = msg def __repr__(self): return ("ConsoleUIError(%r)" % self.value) class ConsoleUI(object): class TkUI(object): def __init__(self, kill_event, stop_update_event, msg_queue, status_queue, title=None): self.kill_event = kill_event self.stop_update_event = stop_update_event self.msg_queue = msg_queue self.status_queue = status_queue import Tkinter as tk1 import ttk as ttk1 self.win = tk1.Toplevel() self.status_msg = tk1.StringVar() self.status_msg.set('') self.win.title(title if title else "execsql console") console_frame = ttk1.Frame(master=self.win, padding="2 2 2 2") console_frame.grid(column=0, row=0, sticky=tk1.NSEW) self.textarea = tk1.Text(console_frame, width=100, height=25, wrap='none') # Status bar statusframe = ttk1.Frame(master=self.win) statusbar = ttk1.Label(statusframe, text='', textvariable=self.status_msg, relief=tk1.RIDGE, anchor=tk1.W) statusbar.pack(side=tk1.BOTTOM, fill=tk1.X) statusframe.grid(column=0, row=1, sticky=tk1.EW) # Scrollbars vscroll = tk1.Scrollbar(console_frame, orient="vertical", command=self.textarea.yview) hscroll = tk1.Scrollbar(console_frame, orient="horizontal", command=self.textarea.xview) self.textarea.configure(yscrollcommand=vscroll.set) self.textarea.configure(xscrollcommand=hscroll.set) self.textarea.grid(column=0, row=0, sticky=tk1.NSEW) vscroll.grid(column=1, row=0, sticky=tk1.NS) hscroll.grid(column=0, row=2, sticky=tk1.EW) # Allow resizing self.win.columnconfigure(0, weight=1) self.win.rowconfigure(0, weight=1) console_frame.columnconfigure(0, weight=1) console_frame.rowconfigure(0, weight=1) # Kill on window close self.win.protocol("WM_DELETE_WINDOW", self.kill) # Display and center the window self.win.update_idletasks() m = re.match("(\d+)x(\d+)([-+]\d+)([-+]\d+)", self.win.geometry()) wwd = int(m.group(1)) wht = int(m.group(2)) swd = self.win.winfo_screenwidth() sht = self.win.winfo_screenheight() xpos = (swd/2) - (wwd/2) ypos = (sht/2) - (wht/2) self.win.geometry("%dx%d+%d+%d" % (wwd, wht, xpos, ypos)) self.win.grab_set() self.win._root().withdraw() self.update_id = self.win.after(200, self.update) self.win.wait_window(self.win) def kill(self): if self.update_id: self.win.after_cancel(self.update_id) self.update_id = None self.win.destroy() self.win.update_idletasks() def update(self): self.update_id = None while not self.msg_queue.empty(): msg = self.msg_queue.get(False) self.textarea.insert('end', msg) self.textarea.see('end') self.msg_queue.task_done() while not self.status_queue.empty(): msg = self.status_queue.get(False) self.status_msg.set(msg) if self.kill_event.is_set(): self.kill() else: if not self.stop_update_event.is_set(): self.update_id = self.win.after(200, self.update) def __init__(self, title=None): self.title = title self.msg_queue = Queue.Queue() self.status_queue = Queue.Queue() self.kill_event = threading.Event() self.stop_update_event = threading.Event() self.consolethread = None self.update_id = None # Start the local event loop in a thread. def openconsole(): self.active = True self.ui = self.TkUI(self.kill_event, self.stop_update_event, self.msg_queue, self.status_queue, self.title) # Deallocate the Tk object here to avoid the "main thread is not in main loop" error. self.ui = None self.consolethread = threading.Thread(target=openconsole) self.consolethread.start() def write(self, msg): self.active = self.consolethread and self.consolethread.is_alive() if not self.active: raise ConsoleUIError(msg) self.msg_queue.put(msg) def write_status(self, msg): self.active = self.consolethread and self.consolethread.is_alive() if not self.active: raise ConsoleUIError(msg) self.status_queue.put(msg) def deactivate(self): self.kill_event.set() if self.consolethread and self.consolethread.is_alive(): self.consolethread.join() self.active = False def wait_for_user_quit(self): self.stop_update_event.set() if self.consolethread and self.consolethread.is_alive(): self.consolethread.join() self.active = False
Because it runs in its own thread, this console can be used to display information produced by the main program or even several other separate processes.
The 'write()' method of the ConsoleUI object will write the given text at the end of the console display. A status message can also be written, separately from the stream of text that is written in the main part of the window. The console window can be closed directly from the program with the 'deactivate()' method, or the program can pause until the user closes the window by using the 'wait_for_user_quit()' method.
Other Tkinter widgets should not be activated in other threads while this GUI console window is open.
No comments:
Post a Comment