it is a medical plotter/display program with wxagg backend. it reads 3 kinds of data from a text file: ecg, heart rate, and oxygen saturation. ecg data is plotted by embedding matplotlib inside wx, while the other two is wxStaticText. to differentiate them, a buffer character was introduced before each reading and parsed accordingly.
my PROBLEM is i want to make the program work without using wxYield. i've read somewhere that as much as possible it should be avoided. im thinking maybe use threading but i cant make it work like it should be. if i comment out the wxYield line the plot is updated, but the other two readings won't.
here is my code and the input textfile(truncated version of 'data.txt').
it requires wxpython,pyserial, and matplotlib modules.
Expand|Select|Wrap|Line Numbers
- #!/usr/bin/env python
- # -*- coding: UTF-8 -*-
- # generated by wxGlade 0.4.1 on Thu Aug 16 11:20:21 2007
- from matplotlib.numerix import arange
- import matplotlib
- # uncomment the following to use wx rather than wxagg
- #matplotlib.use('WX')
- #from matplotlib.backends.backend_wx import FigureCanvasWx as FigureCanvas
- # comment out the following to use wx rather than wxagg
- matplotlib.use('WXAgg')
- from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
- from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg,Toolbar
- from matplotlib.figure import Figure
- import wx
- import serial
- class MyFrame(wx.Frame):
- count = 1000 # this is the number of samples to display at a time
- t = arange(0.0,count,1)
- data = [0.3 for i in range(count)] # storage for ECG graph data
- #ser = serial.Serial(port = '/dev/ttyUSB0', baudrate = 57600) # for COM 1: port = 0, etc
- #ser = serial.Serial(port = 'COM2', baudrate = 57600) # for COM 1: port = 0, etc
- txtfile = open('data.txt','r')
- def __init__(self, *args, **kwds):
- # begin wxGlade: MyFrame.__init__
- kwds["style"] = wx.DEFAULT_FRAME_STYLE
- wx.Frame.__init__(self, *args, **kwds)
- self.sizer_8_staticbox = wx.StaticBox(self, -1, "")
- self.sizer_7_staticbox = wx.StaticBox(self, -1, "")
- self.spo2_display = wx.StaticText(self, -1, "0")
- self.spo2_statictxt = wx.StaticText(self, -1, "% Sp02")
- self.spo2_header = wx.StaticText(self, -1, "blood-oxygen saturation")
- self.bpm_display = wx.StaticText(self, -1, "0")
- self.bpm_statictxt = wx.StaticText(self, -1, "bpm")
- self.bpm_header = wx.StaticText(self, -1, "heart rate")
- # Menu Bar
- # File menu
- self.Main_Menu = wx.MenuBar()
- self.SetMenuBar(self.Main_Menu)
- self.File = wx.Menu()
- self.Start_Display = wx.MenuItem(self.File, wx.NewId(), "Start Display", "", wx.ITEM_NORMAL)
- self.File.AppendItem(self.Start_Display)
- self.Exit = wx.MenuItem(self.File, wx.NewId(), "Exit", "", wx.ITEM_NORMAL)
- self.File.AppendItem(self.Exit)
- self.Main_Menu.Append(self.File, "File")
- # Edit menu
- self.Edit = wx.Menu()
- self.Options = wx.MenuItem(self.Edit,wx.NewId(),"Options","",wx.ITEM_NORMAL)
- self.Edit.AppendItem(self.Options)
- self.Main_Menu.Append(self.Edit,"Edit")
- # Help menu
- self.Help = wx.Menu()
- self.About = wx.MenuItem(self.Help,wx.NewId(),"About","",wx.ITEM_NORMAL)
- self.Help.AppendItem(self.About)
- self.Main_Menu.Append(self.Help,"Help")
- # Menu Bar end
- # Status bar
- self.Telemed_statusbar = self.CreateStatusBar(1, 0)
- # embed matplotlib
- self.figure = Figure()
- self.axes = self.figure.add_subplot(111) # 111 means 1 row, 1 column on subplot #1
- #self.line, = self.axes.plot(self.t, self.data, 'r', linewidth = 3.0,color='#FF523A')
- self.line, = self.axes.plot(self.t, self.data, 'r', linewidth = 3.0,color='#FF351A')
- self.canvas = FigureCanvas(self, -1, self.figure)
- self.axes.set_title('ECG Waveform')
- #self.axes.set_ylim(0,3.3)
- self.axes.set_ylim(0.4,2.2)
- #self.axes.set_ylim(0,2.5)
- self.axes.grid(True)
- #self.sizer_1.Add(self.canvas, 0, wx.LEFT | wx.TOP | wx.GROW)
- # Toolbar for matplotlib canvas
- #self.toolbar = NavigationToolbar2WxAgg(self.canvas)
- self.toolbar = Toolbar(self.canvas)
- self.toolbar.Realize()
- if wx.Platform == '__WXMAC__':
- # Mac platform (OSX 10.3, MacPython) does not seem to cope with
- # having a toolbar in a sizer. This work-around gets the buttons
- # back, but at the expense of having the toolbar at the top
- self.SetToolBar(self.toolbar)
- else:
- # On Windows platform, default window size is incorrect, so set
- # toolbar width to figure width.
- tw, th = self.toolbar.GetSizeTuple()
- fw, fh = self.canvas.GetSizeTuple()
- # By adding toolbar in sizer, we are able to put it at the bottom
- # of the frame - so appearance is closer to GTK version.
- # As noted above, doesn't work for Mac.
- #self.toolbar.SetSize(wx.Size(fw, th))
- self.toolbar.SetSize(wx.Size(1000, th))
- self.toolbar.update()
- self.__set_properties()
- self.__do_layout()
- self.__bind_events()
- # end wxGlade
- def __set_properties(self):
- # begin wxGlade: MyFrame.__set_properties
- self.SetTitle("Telemed")
- self.SetSize((1280, 700))
- self.canvas.SetMinSize((1000, 501))
- self.canvas.SetPosition((40,40))
- self.spo2_header.SetFont(wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, "Sans"))
- self.spo2_header.SetForegroundColour(wx.Colour(35, 142, 35))
- self.spo2_display.SetMinSize((130, 78))
- self.spo2_display.SetBackgroundColour(wx.Colour(255, 255, 255))
- self.spo2_display.SetFont(wx.Font(45, wx.MODERN, wx.NORMAL, wx.BOLD, 0, "Sans"))
- self.spo2_display.SetForegroundColour(wx.Colour(35, 142, 35))
- self.spo2_statictxt.SetMinSize((103, 60))
- self.spo2_statictxt.SetFont(wx.Font(20, wx.MODERN, wx.ITALIC, wx.NORMAL, 0, "Sans"))
- self.spo2_statictxt.SetForegroundColour(wx.Colour(35, 142, 35))
- self.bpm_header.SetFont(wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, "Sans"))
- self.bpm_header.SetForegroundColour(wx.Colour(50, 50, 204))
- self.bpm_display.SetMinSize((130, 78))
- self.bpm_display.SetFont(wx.Font(45, wx.MODERN, wx.NORMAL, wx.BOLD, 0, "Sans"))
- self.bpm_display.SetForegroundColour(wx.Colour(50, 50, 204))
- self.bpm_statictxt.SetMinSize((60, 60))
- self.bpm_statictxt.SetFont(wx.Font(20, wx.MODERN, wx.ITALIC, wx.NORMAL, 0, "Sans"))
- self.bpm_statictxt.SetForegroundColour(wx.Colour(50, 50, 204))
- #self.bpm_statictxt.SetBackgroundColour(wx.Colour(50, 50, 204))
- #self.canvas.SetBackgroundColour(wx.Colour(239, 235, 231))
- self.Telemed_statusbar.SetStatusWidths([-1])
- # statusbar fields
- Telemed_statusbar_fields = ["Ready"]
- for i in range(len(Telemed_statusbar_fields)):
- self.Telemed_statusbar.SetStatusText(Telemed_statusbar_fields[i], i)
- # end wxGlade
- def __do_layout(self):
- # begin wxGlade: MyFrame.__do_layout
- sizer_1 = wx.BoxSizer(wx.VERTICAL)
- sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
- sizer_6 = wx.BoxSizer(wx.VERTICAL)
- sizer_8 = wx.StaticBoxSizer(self.sizer_8_staticbox, wx.HORIZONTAL)
- sizer_7 = wx.StaticBoxSizer(self.sizer_7_staticbox, wx.HORIZONTAL)
- #sizer_2.Add(self.panel_7, 1, wx.EXPAND, 0)
- #sizer_2.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
- sizer_2.Add(self.canvas, 0, wx.LEFT | wx.TOP | wx.GROW | wx.EXPAND)
- sizer_6.Add(self.spo2_header, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ADJUST_MINSIZE, 0)
- sizer_7.Add(self.spo2_display, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 1)
- sizer_7.Add(self.spo2_statictxt, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 2)
- sizer_6.Add(sizer_7, 1, wx.EXPAND, 0)
- sizer_6.Add(self.bpm_header, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ADJUST_MINSIZE, 0)
- sizer_8.Add(self.bpm_display, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 1)
- sizer_8.Add(self.bpm_statictxt, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 2)
- sizer_6.Add(sizer_8, 1, wx.EXPAND, 0)
- sizer_6.Add((20, 80), 0, wx.ADJUST_MINSIZE, 0)
- sizer_6.Add((20, 80), 0, wx.ADJUST_MINSIZE, 0)
- sizer_6.Add((20, 80), 0, wx.ADJUST_MINSIZE, 0)
- sizer_2.Add(sizer_6, 1, wx.EXPAND, 0)
- sizer_1.Add(sizer_2, 1, wx.EXPAND, 0)
- self.SetAutoLayout(True)
- self.SetSizer(sizer_1)
- self.Layout()
- # end wxGlade
- def __bind_events(self):
- self.Bind(wx.EVT_MENU, self.startPlot, self.Start_Display)
- self.Bind(wx.EVT_MENU, self.onExit, self.Exit)
- #self.Bind(wx.EVT_MENU, self.onAbout, self.About)
- def onExit(self, event): # wxGlade: MyFrame.<event_handler>
- #self.ser.close()
- self.txtfile.close()
- self.Close()
- def startPlot(self, event): # wxGlade: MyFrame.<event_handler>
- #self.ser.flushInput()
- print 'ok here'
- buf = 30 #number of samples to take before redrawing ecg graph
- while 1:
- #input = self.ser.readline().strip('\r\n')
- input = self.txtfile.readline().strip('\r\n')
- #self.txtfile.write(input + '\n')
- #print input
- # check if input is from pulse ox or from ECG
- if input[0] == '-':
- if input.count('.') != 0:
- continue
- if input.count('-') != 1:
- continue
- if input.count('+') != 0:
- continue
- self.spo2_display.SetLabel(input.strip('-'))
- #wx.Yield()
- elif input[0] == '+':
- if input.count('.') != 0:
- continue
- if input.count('-') != 0:
- continue
- if input.count('+') != 1:
- continue
- self.bpm_display.SetLabel(input.strip('+'))
- #wx.Yield()
- elif input[0] == '.':
- if input.count('.') != 2:
- continue
- if input.count('-') != 0:
- continue
- if input.count('+') != 0:
- continue
- del self.data[0]
- self.data.append(input.strip('.'))
- buf-= 1
- if buf <= 0:
- buf = 30
- self.line.set_data(self.t, self.data)
- self.canvas.draw()
- #wx.Yield()
- def OnPaint(self, event):
- self.canvas.draw()
- # end of class MyFrame
- class MyApp(wx.PySimpleApp):
- """Application class."""
- def OnInit(self):
- wx.InitAllImageHandlers()
- self.frame=MyFrame(None,-1,"")
- self.frame.Show()
- self.SetTopWindow(self.frame)
- return True
- def main():
- app = MyApp()
- app.MainLoop()
- if __name__ == "__main__":
- main()
-97
+83
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
-97
+83
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
-97
+83
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
-98
+83
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
-98
+83
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.1.851
.2.000
.2.000
.2.000
.1.639
.2.000
.2.000
.2.000
-98
+83
.1.373
.1.859
.2.000
.2.000
.1.482
.0.996
.2.000
.2.000
.1.529
.0.572
.2.000
.2.000
.1.780
-98
+83
.0.580
.1.812
.2.000
.2.000
.0.580
.0.745
.2.000
.2.000
.0.580
.0.564
.2.000
.2.000
.0.580
.0.572
-99
+83
.2.000
.2.000
.0.572
.0.580
.1.733
.2.000
.0.603
.0.580
.1.082
.2.000
.1.208
.0.588
.0.564
-99
+83
.2.000
.1.467
.0.588
.0.572
.2.000
.1.835
.0.588
.0.572
.1.733
.1.898
.0.580
.0.580
.1.161
.2.000
-97
+83
.0.588
.0.580
.0.564
.1.922
.0.580
.0.588
.0.572
.1.906
.0.572
.0.580
.0.572
.1.475
.0.564
-97
+83
.0.588
.0.580
.1.098
.0.713
.0.588
.0.580
.0.564
.0.933
.0.588
.0.580
.0.572
.1.169
.0.588
.0.580
-97
+83
.0.572
.1.302
.0.588
.0.588
.0.580
.1.098
.0.580
.0.588
.0.580
.0.721
.0.572
.0.588
.0.580
-97
+83
.0.698
.0.564
.0.588
.0.580
.0.572
.0.556
.0.588
.0.580
.0.580
.0.564
.0.588
.0.580
.0.580
.0.572
-97
+83
.0.588
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.580
.0.580
-99
+83
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.580
.0.580
.0.580
.0.588
-97
+83
.0.580
.0.580
.0.580
.0.580
.0.580
.0.580
.0.580
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.580
-97
+83
.0.572
.0.588
.0.580
.0.580
.0.572
.0.588
.0.580
.0.580
.0.572
.0.580
.0.588
.0.580
.0.580
-97
+153
.0.588
.0.588
.0.580
.0.580
.0.572
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.580
.0.580
.0.588
-97
+153
.0.580
.0.580
.0.580
.0.588
.0.580
.0.580
.0.580
.0.580
.0.588
.0.588
.0.580
.0.580
.0.588
-97
+153
.0.580
.0.580
.0.580
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.580
-97
+153
.0.580
.0.588
.0.580
.0.580
.0.580
.0.580
.0.588
.0.588
.0.580
.0.580
.0.588
.0.588
.0.580
.0.580
-97
+199
.0.588
.0.580
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.588
.0.580
.2.000
.2.000
.2.000
-97
+199
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
-97
+199
.1.145
.0.580
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.588
-97
+199
.0.580
.0.580
.0.580
.0.588
.0.588
.0.588
.0.580
.0.580
.0.580
.0.580
.0.588
.0.580
.0.588
.0.580
-97
+199
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.588
-94
+199
.0.580
.0.588
.0.588
.0.588
.0.588
.0.588
.0.588
.0.588
.0.588
.0.588
.0.580
.0.580
.0.588
.0.580
-94
+199
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.705
.2.000
.2.000
.2.000
-94
+199
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
-94
+127
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
-94
+127
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.0.588
.0.580
-94
+127
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
-94
+127
.0.580
.0.588
.0.588
.0.588
.0.580
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
-94
+127
.0.580
.0.580
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.588
.0.588
-94
+127
.0.588
.0.588
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
-94
+127
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.588
.0.588
.0.588
.0.588
-94
+127
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
-94
+127
.0.572
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
-93
+127
.2.000
.2.000
.2.000
.2.000
.0.572
.0.580
.0.580
.2.000
.0.588
.0.580
.0.588
.1.773
.0.580
.0.580
-93
+127
.0.588
.1.059
.2.000
.0.580
.0.588
.1.443
.2.000
.0.588
.0.588
.0.745
.2.000
.0.580
.0.580
-93
+153
.0.564
.2.000
.0.572
.0.580
.0.580
.2.000
.1.529
.0.580
.0.580
.2.000
.2.000
.0.580
.0.588
.2.000
-93
+153
.2.000
.0.580
.0.588
.1.122
.2.000
.0.580
.0.580
.0.572
.2.000
.0.580
.0.580
.0.580
.2.000
-93
+153
.0.580
.0.580
.0.580
.2.000
.2.000
.0.580
.0.588
.2.000
.2.000
.0.580
.0.588
.1.027
.2.000
.0.580
-93
+187
.0.588
.0.572
.2.000
.0.588
.0.588
.0.588
.0.588
.0.580
.2.000
.2.000
.1.490
.0.588
.0.580
-93
+187
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.588
.0.580
.0.580
.0.580
-93
+187
.0.588
.0.580
.0.588
.0.588
.0.588
.0.588
.0.588
.0.588
.0.588
.0.588
.0.580
.0.588
.0.580
-93
+187
.0.580
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
-93
+187
.0.588
.0.588
.0.580
.0.588
.0.588
.0.588
.0.588
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
-93
+187
.0.588
.0.580
.0.588
.0.580
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.580
.0.580
.0.588
.0.588
-93
+187
.0.580
.0.588
.0.588
.0.588
.0.580
.0.580
.0.580
.0.580
.0.580
.0.580
.0.580
.0.588
.0.580
-97
+187
.0.588
.0.580
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.580
.0.580
.0.588
.0.580
.0.580
.0.580
-97
+187
.0.588
.0.588
.0.564
.2.000
.0.588
.0.588
.0.564
.2.000
.0.580
.0.580
.0.572
.2.000
.0.556
.0.580
-97
+187
.0.580
.2.000
.1.851
.0.588
.0.580
.2.000
.2.000
.0.588
.0.580
.1.757
.2.000
.0.588
.0.588
-97
+187
.2.000
.2.000
.0.580
.0.588
.0.572
.2.000
.0.556
.0.588
.0.580
.0.588
.0.580
.2.000
.2.000
.2.000
-97
+187
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
.2.000
-97
+187
.2.000
.2.000
.2.000
[/end of data.txt]