Python 3.x 将Python记录器输出重定向到wxPython文本控件
我正在尝试将Python记录器的输出从外部脚本重定向到wxPython文本控件。GUI脚本在单独的线程中执行外部脚本。我曾尝试遵循本文中公认答案所述的方法,但未能使其按预期运行 以下是我迄今为止所做的尝试:Python 3.x 将Python记录器输出重定向到wxPython文本控件,python-3.x,logging,wxpython,Python 3.x,Logging,Wxpython,我正在尝试将Python记录器的输出从外部脚本重定向到wxPython文本控件。GUI脚本在单独的线程中执行外部脚本。我曾尝试遵循本文中公认答案所述的方法,但未能使其按预期运行 以下是我迄今为止所做的尝试: # -*- coding: UTF-8 -*- #-------------------------------------------------------------------------------------------------- import logging from th
# -*- coding: UTF-8 -*-
#--------------------------------------------------------------------------------------------------
import logging
from threading import Thread
import wx
import wx.lib.newevent as NE
#---------------------------------------------------------------------------------------------------
from module_sine import sin_fun
####################################################################################################
# Create event for the logger
wxLogEvent, EVT_WX_LOG_EVENT = NE.NewEvent()
#####################################################################################################
# Notification event for thread completion
UpdateEvent, EVT_UPDATE = NE.NewEvent()
#####################################################################################################
class wxLogHandler(logging.Handler):
"""
A handler class which sends log strings to a wx object
"""
def __init__(self, wxDest=None):
"""
Initialize the handler
@param wxDest: the destination object to post the event to
@type wxDest: wx.Window
"""
logging.Handler.__init__(self)
self.wxDest = wxDest
self.level = logging.DEBUG
def flush(self):
"""
does nothing for this handler
"""
pass
def emit(self, record):
"""
Emit a record.
"""
try:
msg = self.format(record)
event = wxLogEvent(message=msg, levelname=record.levelname)
wx.PostEvent(self.wxDest, event)
except (KeyboardInterrupt, SystemExit):
raise
except:
self.handleError(record)
#####################################################################################################
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE | wx.STAY_ON_TOP
wx.Frame.__init__(self, *args, **kwds)
self.SetSize((400, 300))
self.SetTitle("TEST")
self.panel_1 = wx.Panel(self, wx.ID_ANY)
sizer_1 = wx.BoxSizer(wx.VERTICAL)
self.label_3 = wx.StaticText(self.panel_1, wx.ID_ANY, label="", style=wx.ALIGN_CENTER_HORIZONTAL)
sizer_1.Add(self.label_3, 0, wx.ALL, 5)
grid_sizer_2 = wx.FlexGridSizer(2, 1, 1, 1)
sizer_1.Add(grid_sizer_2, 1, wx.EXPAND, 0)
self.text_ctrl_3 = wx.TextCtrl(self.panel_1, wx.ID_ANY, "", style=wx.HSCROLL | wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2)
grid_sizer_2.Add(self.text_ctrl_3, 1, wx.EXPAND, 0)
grid_sizer_2.Add((0, 0), 0, 0, 0)
grid_sizer_1 = wx.FlexGridSizer(5, 2, 1, 1)
sizer_1.Add(grid_sizer_1, 1, wx.EXPAND, 0)
label_1 = wx.StaticText(self.panel_1, wx.ID_ANY, "x")
grid_sizer_1.Add(label_1, 0, wx.ALL, 5)
self.text_ctrl_1 = wx.TextCtrl(self.panel_1, wx.ID_ANY, "")
grid_sizer_1.Add(self.text_ctrl_1, 0, 0, 0)
label_2 = wx.StaticText(self.panel_1, wx.ID_ANY, "y")
grid_sizer_1.Add(label_2, 0, wx.ALL, 5)
self.text_ctrl_2 = wx.TextCtrl(self.panel_1, wx.ID_ANY, "")
grid_sizer_1.Add(self.text_ctrl_2, 0, 0, 0)
grid_sizer_1.Add((0, 0), 0, 0, 0)
self.button_1 = wx.Button(self.panel_1, wx.ID_ANY, "Save")
self.button_1.SetFocus()
grid_sizer_1.Add(self.button_1, 0, 0, 0)
grid_sizer_1.Add((0, 0), 0, 0, 0)
self.button_2 = wx.Button(self.panel_1, wx.ID_ANY, "Start")
grid_sizer_1.Add(self.button_2, 0, 0, 0)
grid_sizer_1.Add((0, 0), 0, 0, 0)
self.button_3 = wx.Button(self.panel_1, wx.ID_ANY, "Stop")
grid_sizer_1.Add(self.button_3, 0, 0, 0)
grid_sizer_2.AddGrowableRow(0)
grid_sizer_2.AddGrowableCol(0)
self.panel_1.SetSizer(sizer_1)
self.Layout()
self.Bind(wx.EVT_BUTTON, self.OnClick_Save, self.button_1)
self.Bind(wx.EVT_BUTTON, self.OnClick_Start, self.button_2)
self.Bind(wx.EVT_BUTTON, self.OnClick_Stop, self.button_3)
self.Bind(wx.EVT_CLOSE, self.CloseWindow)
# Set up event handler for any worker thread updates
self.Bind(EVT_UPDATE, self.OnUpdate)
# Indicate there is not any worker thread yet
self.worker = None
# Set up event handler for the logger
self.Bind(EVT_WX_LOG_EVENT, self.onLogEvent)
#------------------------------------------------------------------------------------------------
# Logger
logging.basicConfig(level=logging.DEBUG, format='%(message)s')
logger = logging.getLogger('testing')
logger.setLevel(logging.DEBUG)
# Text handler
txthandler = wxLogHandler(self.text_ctrl_3)
logger.addHandler(txthandler)
txthandler.setLevel(logging.DEBUG)
FORMAT = "%(message)s"
txthandler.setFormatter(logging.Formatter(FORMAT))
#------------------------------------------------------------------------------------------------
self.button_2.Disable()
self.button_3.Disable()
#------------------------------------------------------------------------------------------------
self.my_data = []
#----------------------------------------------------------------------------------------------------
def OnClick_Save(self, event):
x = float(self.text_ctrl_1.GetValue())
self.my_data.append(x)
y = float(self.text_ctrl_2.GetValue())
self.my_data.append(y)
self.button_2.Enable()
self.button_3.Enable()
event.Skip()
#----------------------------------------------------------------------------------------------------
def OnClick_Start(self, event):
"""
Run calc. in the thread
"""
inp_val = self.my_data # entered values that will be passed to the thread
if not self.worker:
self.label_3.SetLabel('Run started')
self.worker = WorkerThread(self, inp_val)
self.button_2 = event.GetEventObject()
self.button_2.Disable()
self.label_3.SetLabel('Running ...')
event.Skip()
#------------------------------------------------------------------------------------
def OnClick_Stop(self, event):
"""
Abort calculation
"""
if self.worker:
self.label_3.SetLabel('Trying to abort run ...')
self.worker.abort()
event.Skip()
#-----------------------------------------------------------------------------------
def OnUpdate(self, event):
"""Show updates"""
if event.data is None:
# Thread aborted
self.label_3.SetLabel('Run aborted')
self.button_2.Enable()
self.text_ctrl_3.SetValue("") # Clear any text transmitted to the text control when the thread is aborted.
self.my_data.clear() # remove values from the list
else:
# Thread completed
self.label_3.SetLabel('%s' % event.data)
self.button_2.Enable()
self.my_data.clear() # remove values from the list
# In either event, the worker is done
self.worker = None
#------------------------------------------------------------------------------------------------
def onLogEvent(self, event):
'''
Add event.message to text window
'''
msg = event.message.strip("\r")+"\n"
self.text_ctrl_3.WriteText(msg)
event.Skip()
#------------------------------------------------------------------------------------------------
def CloseWindow(self, event): # Not sure if this is a good implementation for the close window method.
WorkerThread.abort(self)
self.Destroy()
####################################################################################################
class WorkerThread(Thread):
"""Worker Thread Class."""
#-----------------------------------------------------------------------------------------------
def __init__(self, notify_win, inp_val):
"""Init Worker Thread Class."""
Thread.__init__(self)
self._notify_win = notify_win
self.inp_val = inp_val
self.daemon = True
self._want_abort = False
self.start()
#-----------------------------------------------------------------------------------------------
def run(self):
"""Run Worker Thread."""
# This is the function executing in the new thread.
[x, y] = self.inp_val
sin_fun(x, y)
if self._want_abort:
wx.PostEvent(self._notify_win, UpdateEvent(data=None))
return
wx.PostEvent(self._notify_win, UpdateEvent(data='Run completed successfully!'))
#-----------------------------------------------------------------------------------------------
def abort(self):
"""Abort Worker Thread."""
self._want_abort = True
####################################################################################################
class MyApp(wx.App):
def OnInit(self):
self.frame = MyFrame(None, wx.ID_ANY, "")
self.SetTopWindow(self.frame)
self.frame.Show()
return True
if __name__ == "__main__":
Test = MyApp(False)
Test.MainLoop()
下面是我正在试验的外部脚本:
# -*- coding: UTF-8 -*-
import logging
import math
def sin_fun(x, y):
logger = logging.getLogger('testing')
for i in range(0, 2):
a = (math.sin(x) + math.cos(y))*(x/y + i)
logger.debug(a)
我使用的是Windows10、Python3.8.5和Wxpython4.1.0。
非常感谢您的帮助。
非常感谢