WxPython PySerial和Wx.终端线程无法更新GUI

2024-06-02 07:25:40 发布

您现在位置:Python中文网/ 问答频道 /正文

以前的一些东西:

所有这些都是基于wxTerminal.pyLink

(Pyserial微型端口和wxpythongui的组合)

使用: Python:2.7.14。 WxPython:4.0.0b2

我的问题是我有一个线程从我的设备读取串行数据, 并尝试使用事件更新GUI:

class TerminalFrame(wx.Frame):
    ....
    ....
    def ComPortThread(self):
        """\
        Thread that handles the incoming traffic. Does the basic input
        transformation (newlines) and generates an SerialRxEvent
        """
        while self.alive.isSet():
            b = self.serial.read(self.serial.in_waiting or 1)
            if b:
                # newline transformation
                if self.settings.newline == NEWLINE_CR:
                    b = b.replace(b'\r', b'\n')
                elif self.settings.newline == NEWLINE_LF:
                    pass
                elif self.settings.newline == NEWLINE_CRLF:
                    b = b.replace(b'\r\n', b'\n')
                event = SerialRxEvent(self.GetId(), b)
 **ERROR!** >>> self.GetEventHandler().AddPendingEvent(event)

我得到一个错误:

^{pr2}$

少了什么?在

    SERIALRX = wx.NewEventType()
    # bind to serial data receive events
    EVT_SERIALRX = wx.PyEventBinder(SERIALRX, 0)


class SerialRxEvent(wx.PyCommandEvent):
    eventType = SERIALRX

    def __init__(self, windowID, data):
        wx.PyCommandEvent.__init__(self, self.eventType, windowID)
        self.data = data

    def Clone(self):
        self.__class__(self.GetId(), self.data)

Tags: theselfdataifsettingsdefserialnewline
3条回答

下面是一个线程事件的(Linux)示例,它可以与python2.7.12(wx3.0)和python3.5.2(wx4.0)一起工作。它使用的是Tcp套接字,而不是串行端口,但我相信您可以从中找出要点。
测试方法:

echo 'my data' | nc -q 1 127.0.0.1 5005
echo 'new data' | nc -q 1 127.0.0.1 5005
echo 'Quit' | nc -q 1 127.0.0.1 5005

import wx
import wx.lib.newevent
import socket
import threading
tcp_event, EVT_TCP_EVENT = wx.lib.newevent.NewEvent()

class MyMain(wx.Frame):
    def __init__(self, *args, **kwds):
        self.frame = wx.Frame.__init__(self, *args, **kwds)
        self.SetTitle("Threaded Port Listener")
        self.panel = wx.Panel(self)
        self.data = wx.StaticText(self.panel,-1,"Nothing yet",pos=(10,10))

    # Create a listening socket for external requests
        tcp_port = 5005
        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        except:
            print("Error on Socket")
        # force re-use of the socket if it is in time-out mode after being closed
        # other wise we can get bind errors after closing and attempting to start again
        # within a minute or so
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        try:
            self.sock.bind(("127.0.0.1", 5005))
        except IOError as msg:
            print("Error on Socket Bind 5005")
            pass
        try:
            self.sock.listen((1))
        except:
            print("Error on Socket listen")
        self.Bind(wx.EVT_CLOSE, self.OnExit)
        self.Bind(EVT_TCP_EVENT, self.OnTcpThreadEvent)

        #Start the thread
        self.tcp = TcpThread(self,self.sock)
        self.Show()

    def OnTcpThreadEvent(self, event):
        data = event.data.strip()
        print ("data received",data)
        self.data.SetLabel(data)
        if data == "Quit":
            self.OnExit(None)

    def OnExit(self,event):
        try:
            self.tcp.stop() # Shutdown the tcp listener
            self.tcp.join(0.1) # Wait 1/10 second for it to finish then give up
        except Exception as e:
            print (e)
            pass
        self.sock.close()
        self.Destroy()

# The tcp thread is started as a daemon because this allows us to make the socket a blocking socket
# The advantage is that it does nothing until a request comes in.
# The disadvantage is that the sock.accept cannot be interrupted which makes closing a problem as it will wait
# With the tcp thread as a daemon we can perform a self.tcp.join(timeout) which allows the program to close and leaves
# the still running thread to be cleaned up by the system garbage collecter
class TcpThread(threading.Thread):
    def __init__(self, tcp_target, sock):
        threading.Thread.__init__(self)
        self.sock = sock
        self.tcp_target = tcp_target
        self.stopthread = False
        self.setDaemon(True)
        self.start()    # start the thread

    def run(self):
        while self.stopthread == False:
            print ("listening")
            try:
                conn, addr = self.sock.accept()
            except socket.timeout:
                continue
            except socket.error as e:
                msg="tcp accept error",str(e)
                print (msg)
                break
            try:
                data = conn.recv(32).decode('UTF-8')
            except socket.timeout:
                continue
            except IOError as e:
                msg ="Error on socket receive "+str(e)
                print (msg)
                continue
            evt = tcp_event(data=data,target=conn)
            wx.PostEvent(self.tcp_target,evt)
        self.sock.close()

    def stop(self):
        self.stopthread = True


if __name__ == "__main__":
    myapp = wx.App()
    MyMain(None)
    myapp.MainLoop()

我只需安装wxpython3.0.2(insted 4.0.0b2)作为示例wxTerminal.py工作

https://sourceforge.net/projects/wxpython/files/wxPython/3.0.2.0/wxPython3.0-win32-3.0.2.0-py27.exe/download

这件事有时也让我感到困惑。在

由于某些原因,Clone方法永远不会在wxPython经典中被调用(尝试插入raise)。pyserial的作者似乎弄错了。根据the docsClone方法应返回一个新事件!在

当使用Phoenix(通过安装pip获得的)时,Clone方法将被调用。因为您返回NoneAddPendingEvent会抱怨。在

编辑事件类(wxTerminal中的SerialRxEvent)中的Clone方法,使其正确返回:

def Clone(self):
    # raise # uncomment this to show that this will not get called in classic
    # instead of this
    # self.__class__(self.GetId(), self.data)
    # do this
    return self.__class__(self.GetId(), self.data)

相关问题 更多 >