Reputation: 11
I want to do a class that is able to "log" text in a Text Widget. This class could be used by other applications to send and display logs to the text widget.
class TraceConsole():
def __init__(self):
# Init the main GUI window
self._logFrame = Tk.Frame()
self._log = Tk.Text(self._logFrame, wrap=Tk.NONE, setgrid=True)
self._scrollb = Tk.Scrollbar(self._logFrame, orient=Tk.VERTICAL)
self._scrollb.config(command = self._log.yview)
self._log.config(yscrollcommand = self._scrollb.set)
# Grid & Pack
self._log.grid(column=0, row=0)
self._scrollb.grid(column=1, row=0, sticky=Tk.S+Tk.N)
self._logFrame.pack()
def log(self, msg, level=None):
# Write on GUI
self._log.insert('end', msg + '\n')
def exitWindow(self):
# Exit the GUI window and close log file
print('exit..')
Usage exemple:
t = TraceConsole()
t.log('hello world!')
My problem now is that I don't know where to put the mainloop. This logger must run "in background" and it shall be possible to write log at any time until the window is closed.
Upvotes: 1
Views: 6354
Reputation: 917
I struggled with this for a bit, but converged on the recommendations here:
And I have an example below that I created to elucidate the concept of logging to a GUI control using Tkinter. The example below logs to a text control as you ask, but you can send log messages to other GUI components by replacing/copying the class MyHandlerText
with other handler classes like MyHandlerLabel
, MyHandlerListbox
, etc. (choose your own names for the handler classes). Then you'd have a handler for a variety of GUI controls of interest. The big "a-ha" moment for me was the module-level getLogger
concept encouraged by python.org.
import Tkinter
import logging
import datetime
# this item "module_logger" is visible only in this module,
# (but you can also reference this getLogger instance from other modules and other threads by passing the same argument name...allowing you to share and isolate loggers as desired)
# ...so it is module-level logging and it takes the name of this module (by using __name__)
# recommended per https://docs.python.org/2/library/logging.html
module_logger = logging.getLogger(__name__)
class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.grid()
self.mybutton = Tkinter.Button(self, text="ClickMe")
self.mybutton.grid(column=0,row=0,sticky='EW')
self.mybutton.bind("<ButtonRelease-1>", self.button_callback)
self.mytext = Tkinter.Text(self, state="disabled")
self.mytext.grid(column=0, row=1)
def button_callback(self, event):
now = datetime.datetime.now()
module_logger.info(now)
class MyHandlerText(logging.StreamHandler):
def __init__(self, textctrl):
logging.StreamHandler.__init__(self) # initialize parent
self.textctrl = textctrl
def emit(self, record):
msg = self.format(record)
self.textctrl.config(state="normal")
self.textctrl.insert("end", msg + "\n")
self.flush()
self.textctrl.config(state="disabled")
if __name__ == "__main__":
# create Tk object instance
app = simpleapp_tk(None)
app.title('my application')
# setup logging handlers using the Tk instance created above
# the pattern below can be used in other threads...
# ...to allow other thread to send msgs to the gui
# in this example, we set up two handlers just for demonstration (you could add a fileHandler, etc)
stderrHandler = logging.StreamHandler() # no arguments => stderr
module_logger.addHandler(stderrHandler)
guiHandler = MyHandlerText(app.mytext)
module_logger.addHandler(guiHandler)
module_logger.setLevel(logging.INFO)
module_logger.info("from main")
# start Tk
app.mainloop()
Upvotes: 3
Reputation: 226
In this case you have created a component that would be used within an application. The mainloop would be called in that application and they would write to your log widget.
You could add some simple usage example (such as the one you gave) and/or tests in the same python file as TraceConsole using something like
if __name__ == '__main__':
m = tkinter.Tk()
t = TraceConsole()
t.log('hello world!')
m.mainloop()
I usually do something like this so I can test a tkinter component by itself before incorporating it into my application.
Upvotes: 0