Post by Vadim ZeitlinSC> Hi, i recently realized that logging to gui elements(like wxLogTextCtrl) is
SC> not thread safe.
No, only wxLogFrame is MT-safe among the GUI log targets currently.
SC> My first question: is it thread safe to log using wxLogStream?
If your streams are MT-safe (and they should be), it should be too.
SC> Secondly, i am planning to write a wrapper function for logging, so that
SC> when the function is called it will post an event to my wxFrame with the log
SC> msg attached. Then, main thread will handle logging. This is the first
SC> solution that came to my mind, what do the experts think about it?
This is one of the possible approaches and is the one used by wxLogFrame,
except that it doesn't even use a custom event but just reuses idle event
handler for this. Another approach is to make wxLog serialize the logging
itself. It obviously requires changing wxWidgets but this was what we
agreed to do (on 3.0 horizon) when we last discussed it, see
http://thread.gmane.org/gmane.comp.lib.wxwidgets.devel/106284/
If you can help us implement this, your contributions would be most
welcome. Please post to wx-dev if you'd like to discuss this further.
Regards,
VZ
I sidestepped this issue in wxpython by ignoring wxlogging and using
normal python logging instead.
The pattern used by wx to redirect stdio to a separate window in a
threadsafe way can be
extended for any textctrl:
class LoggerTextCntl(wx.TextCtrl):
def write(self, text):
"""
Write the text to the LoggerTextCntl instance
If not called in the context of the gui thread then uses
CallAfter to do the work there.
"""
if not wx.Thread_IsMain():
wx.CallAfter(self.__write, text)
else:
self.__write(text)
def __write(self, text):
# helper function for actually writing the text.
self.AppendText(text)
def flush(self):
pass
Now just redirect stdio or any logger stream to your LoggerTextCntls.
You can have as many as you want in any window you want.
aFrame.errtext = LoggerTextCntl(panel, 3, "",
style=wx.TE_MULTILINE|wx.TE_READONLY, pos = (151,401),size = (1049,200))
aFrame.loggertext = LoggerTextCntl(panel, 2, "",
style=wx.TE_MULTILINE|wx.TE_READONLY, pos = (151,0),size = (1049,400))
#[...]
self.savedstdio = sys.stdout = sys.stderr
sys.stdout = sys.stderr = aFrame.errtext
console1 = logging.StreamHandler(aFrame.msgtext)
# Now all print statements go to aFrame.errtext
# All output to console1 goes to aFrame.msgtext
# if you need to revert the behavior, just use self.savedstdio
Lawson