Running a Python script in a thread and redirecting std.out / std.err to wx.TextCtrl in the GUI

I am trying to write a GUI that reads in the settings for a python script, then generates a script and runs it. The script can take several tens of minutes, so as not to block the graphical interface and not to compromise the user. I run it in a separate thread. Before I did this, I used a separate class to redirect std.out and std.err programs to TextCtrl. This worked fine, except that the GUI was locked at runtime.

Running a script from a thread with a redirect class still blocks the GUI. In order not to block the GUI, I need to disable redirection. All std.out / err from the script and gui then go into the console.

Here is a class that redirects and as I call it.

# For redirecting stdout/stderr to txtctrl.
class RedirectText(object):
    def __init__(self,aWxTextCtrl):
        self.out=aWxTextCtrl

    def write(self,string):
        self.out.WriteText(string) 

self.redir=RedirectText(self.bottom_text)
sys.stdout=self.redir
sys.stderr=self.redir
sys.stdin=self.redir

I tried using some kind of communication class from the stream to the GUI without success. That is, the GUI is still blocked.

Does anyone have any hints or a solution to this problem, i.e. get stdout / err from a script in a GUI without GUI locking?

+3
source share
2 answers

Yes. From a stream, use wx.CallAfter to send text to the GUI in a thread-safe manner. Then he can take the text and display it. Another way to do this is to use a subprocess and exchange data with it. Here is an example of this:

http://www.blog.pythonlibrary.org/2010/06/05/python-running-ping-traceroute-and-more/

, :

http://www.blog.pythonlibrary.org/2009/01/01/wxpython-redirecting-stdout-stderr/

, .

+2

, , python stdout/stderr. , logging.Handler, , wx.TextCtrl wx:

import logging
from logging import Handler

class WxHandler(Handler):
    def __init__(self, logCtrl):
        """
        Initialize the handler.
        logCtrl = an instance of wx.TextCtrl
        """
        self.logCtrl = logCtrl
        Handler.__init__(self)

    def flush(self):
        pass

    def emit(self, record):
        """
        Emit a record.

        If a formatter is specified, it is used to format the record.
        The record is then written to the stream with a trailing newline.  If
        exception information is present, it is formatted using
        traceback.print_exception and appended to the stream.  If the stream
        has an 'encoding' attribute, it is used to encode the message before
        output to the stream.
        """
        try:
            lastPos = self.logCtrl.GetLastPosition()
            msg = self.format(record)
            self.logCtrl.WriteText(msg)
            self.logCtrl.WriteText('\r\n')
            f = wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL, False, u'Arial', wx.FONTENCODING_ISO8859_1)
            if record.levelno == logging.INFO:
                textColour = wx.Colour(0, 0, 205)
            elif record.levelno == logging.WARN: 
                textColour = wx.Colour(250, 128, 114)
            elif record.levelno >= logging.ERROR:
                textColour = wx.Colour(220, 20, 60)
            else:
                textColour = wx.Colour(0, 0, 0)
            self.logCtrl.SetStyle(lastPos, lastPos + len(msg), wx.TextAttr(textColour, wx.NullColour, f))
        except:
            self.handleError(record)

:

def configureWxLogger(logCtrl, loggingLevel):
    """
        Wx Logger config
    """
    logger = logging.getLogger()
    logger.setLevel(loggingLevel)
    ch = WxHandler(logCtrl)
    formatter = logging.Formatter("%(asctime)-20s - %(levelname)-8s - %(message)s")
    formatter.datefmt = '%d/%m/%Y-%H:%M:%S'
    ch.setFormatter(formatter)
    logger.addHandler(ch)
    return logger

, , :

self.logCtrl = wx.TextCtrl(self, -1, "", size=(600, 200), style=wx.TE_MULTILINE|wx.TE_RICH2)
wxLoggingHelper.configureWxLogger(self.logCtrl, logging.DEBUG)
+2

Source: https://habr.com/ru/post/1761310/


All Articles