Search code examples
pythonuser-interfacewxpythonpyserial

Start / Stop buttons for reading from serial with wxpython


I created a simple GUI with wxpython to read data from a serial port. Based on several posts I was able to connect to the serial port when I press the Connect button and print the data when i press Start button but I press the Stop button I can´t stop printing data.

Here's is my code (inside are the links to the posts previously refered):

# -*- coding: utf-8 -*-

import wx
import wx.xrc
import serial
import time
import threading

class MyFrame ( wx.Frame ):

    def __init__( self, parent ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )

        self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )

        bSizer1 = wx.BoxSizer( wx.HORIZONTAL )

        bSizer2 = wx.BoxSizer( wx.VERTICAL )

        self.connectBtn = wx.Button( self, wx.ID_ANY, u"Connect", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer2.Add( self.connectBtn, 0, wx.ALL, 5 )

        self.startBtn = wx.Button( self, wx.ID_ANY, u"Start", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer2.Add( self.startBtn, 0, wx.ALL, 5 )

        self.stopBtn = wx.Button( self, wx.ID_ANY, u"Stop", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer2.Add( self.stopBtn, 0, wx.ALL, 5 )


        bSizer1.Add( bSizer2, 0, wx.EXPAND, 5 )

        bSizer3 = wx.BoxSizer( wx.VERTICAL )

        self.m_textCtrl1 = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer3.Add( self.m_textCtrl1, 1, wx.ALL|wx.EXPAND, 5 )

        self.ser = None
        self.settings = {'PORT':'COM3' , 'BAUDRATE':9600}
        self.connected = False
        bSizer1.Add( bSizer3, 1, wx.EXPAND, 5 )

        self.SetSizer( bSizer1 )
        self.Layout()

        self.Centre( wx.BOTH )

        # Connect Events
        self.connectBtn.Bind( wx.EVT_BUTTON, self.connectBtnOnButtonClick )
        self.startBtn.Bind( wx.EVT_BUTTON, self.startBtnOnButtonClick )
        self.stopBtn.Bind( wx.EVT_BUTTON, self.stopBtnOnButtonClick )

    def __del__( self ):
        pass

    # Virtual event handlers, overide them in your derived class
    def connectBtnOnButtonClick( self, event ):
        # http://stackoverflow.com/questions/11092417/reconnecting-to-device-with-pyserial
        try:
            if self.ser == None:
                self.ser = serial.Serial(self.settings['PORT'],
                                         self.settings['BAUDRATE'],timeout=10)
                # print "Successfully connected to port %r." % self.ser.port
                self.connectBtn.SetLabel('Disconnect')
                self.connected = True
                return True
            else:
                if self.ser.isOpen():
                    self.ser.close()
                    self.connected = False
                    self.connectBtn.SetLabel('Connect')
                    # print "Disconnected."
                    return False
                else:
                    self.ser.open()
                    self.connected = True
                    self.connectBtn.SetLabel('Disconnect')
                    # print "Connected."
                    return True
        except serial.SerialException, e:
            return False

    def startBtnOnButtonClick( self, event ):
        while self.connected:
            self.connected = True
            while True:
                if (self.ser.inWaiting() > 0):
                    data_str = self.ser.read(self.ser.inWaiting())
                    print(data_str.strip())
                    time.sleep(0.1)

    def stopBtnOnButtonClick( self, event ):
        self.connected = False
        # self.connected = False
        # http://stackoverflow.com/questions/17553543/pyserial-non-blocking-read-loop

if __name__ == "__main__":
    app = wx.App(redirect=False)
    frame = MyFrame(None)
    #app.SetTopWindow(frame)
    frame.Show(True)
    app.MainLoop()

Thank you. Ivo


Solution

  • Essentially, I think that your startBtnOnButtonClick is a "long running process" and you will need to call wx.Yield() before or after your sleep.
    This will allow your program is check back with the main loop to see if anything else has occurred i.e. you pressed the stop button.
    See: https://wiki.wxpython.org/LongRunningTasks
    Change startBtnOnButtonClick to remove the double while loop

    def startBtnOnButtonClick( self, event ):
        while self.connected:
            if (self.ser.inWaiting() > 0):
                data_str = self.ser.read(self.ser.inWaiting())
                print(data_str.strip())
                time.sleep(0.1)
                wx.Yield()