class PlotFigure(wx.Frame): global plt_data_a_x, plt_data_a_y, plt_data_b_x, plt_data_b_y, plt_data_c_x, plt_data_c_y def __init__(self): wx.Frame.__init__(self, None, wx.ID_ANY, title="!!!", size=(1000, 1000)) self.fig = Figure((10, 10), 100) self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) self.ax = self.fig.add_subplot(111) self.ax.set_ylim([-10, 10]) self.ax.set_xlim([-5, 15]) self.ax.set_autoscale_on(False) self.ax.grid(True) self.user, = self.ax.plot(plt_data_a_x, plt_data_a_y, 'o', label='map') self.ax.legend(loc='upper center') self.canvas.draw() self.bg = self.canvas.copy_from_bbox(self.ax.bbox) wx.EVT_TIMER(self, TIMER_ID, self.onTimer) def onTimer(self, evt): """callback function for timer events""" self.canvas.restore_region(self.bg) a, = self.ax.plot(plt_data_b_x, plt_data_b_y, 'or') self.ax.draw_artist(a) b, = self.ax.plot(plt_data_c_x, plt_data_c_y, 'og') self.ax.draw_artist(b) c, = self.ax.plot(plt_data_d_x, plt_data_d_y, 'ok') self.ax.draw_artist(c) self.canvas.blit(self.ax.bbox)
class MyFigure(): def __init__(self, panel, ipos, isize=(400, 230)): self.fig_container = wx.TextCtrl(panel, -1, "", pos=ipos, size=isize, style=wx.TE_MULTILINE | wx.TE_RICH2) self.fig = Figure((4, 2), 100) self.canvas = FigureCanvas(self.fig_container, wx.ID_ANY, self.fig) self.ax = self.fig.add_subplot(111) self.ax.set_autoscale_on(False) self.ax.set(xlim=[0, TOTAL_SEND], ylim=[0, MAX_DELTA]) self.ax.grid(True) self.data = [0] * TOTAL_SEND self.plt_data, = self.ax.plot(range(TOTAL_SEND), self.data) self.canvas.draw() self.bg = self.canvas.copy_from_bbox(self.ax.bbox) self.index = 0 def update(self, idata): self.data[self.index] = float(idata.microseconds) / 1000000 self.canvas.restore_region(self.bg) self.plt_data.set_ydata(self.data) self.ax.draw_artist(self.plt_data) self.canvas.blit(self.ax.bbox) self.index += 1
class AngleCircle(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, style = wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX, title='Neo LiDAR demo(python)', size=(800, 800)) self.fig = Figure((8, 8), 100) self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) self.ax = self.fig.add_subplot(111) self.ax.set_ylim([-10, 10]) self.ax.set_xlim([-10, 10]) self.ax.set_autoscale_on(False) self.ax.set_xticks(range(-10, 11, 2)) self.ax.set_yticks(range(-10, 11, 2)) self.ax.grid(True) # self.datax = [None] * 360 # self.datay = [None] * 360 #for i in range(360): #x[i] = np.random.randint(-40, 40) #y[i] = np.random.randint(-40, 40) #self.datax = np.random.randn(100) #self.datay = np.random.randn(100) self.draw_data, = self.ax.plot(x, y, '.', ms = 3.0, mec = 'RED') self.canvas.draw() self.bg = self.canvas.copy_from_bbox(self.ax.bbox) wx.EVT_TIMER(self, TIMER_ID, self.onTimer) th.start() def onTimer(self, evt): global x, y global flag self.canvas.restore_region(self.bg) #self.draw_data.set_data(x, y) #self.draw_data.set_ydata(y) self.ax.draw_artist(self.draw_data) self.canvas.blit(self.ax.bbox)
class PlotFigure(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, wx.ID_ANY, title="Sensor Monitor", size=(800, 600)) #set window size self.fig = Figure((8, 6), 100) self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) self.ax = self.fig.add_subplot(111) self.ax.set_ylim([0, 100]) self.ax.set_xlim([0, POINTS]) self.ax.set_autoscale_on(False) self.ax.set_xticks([]) self.ax.set_yticks(range(0, 101, 10)) self.ax.grid(True) self.user = [None] * POINTS self.l_user,=self.ax.plot(range(POINTS),self.user,label='Light Sensors') self.ax.legend(loc='upper center', ncol=4, prop=font_manager.FontProperties(size=10)) self.canvas.draw() self.bg = self.canvas.copy_from_bbox(self.ax.bbox) wx.EVT_TIMER(self, TIMER_ID, self.onTimer) def onTimer(self, evt): self.canvas.restore_region(self.bg) for i in range(0,240): index = int(i/40)*40 per = (index-i)+20.0 per =((math.sin((per/20.0)*math.pi/2))+1.0)/2.0 self.user[i+30] = 100-(float(arr[i/40])*per+float(arr[(i+40)/40])*(1-per))*1 self.l_user.set_ydata(self.user) self.ax.draw_artist(self.l_user) self.canvas.blit(self.ax.bbox)
class PlotFigure(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, wx.ID_ANY, title="CPU Usage Monitor", size=(600, 400)) # Matplotlib Figur self.fig = Figure((6, 4), 100) # bind the Figure to the backend specific canvas self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) # add a subplot self.ax = self.fig.add_subplot(111) # limit the X and Y axes dimensions self.ax.set_ylim([0, 100]) self.ax.set_xlim([0, POINTS]) self.ax.set_autoscale_on(False) self.ax.set_xticks([]) # we want a tick every 10 point on Y (101 is to have 10 self.ax.set_yticks(range(0, 101, 10)) # disable autoscale, since we don't want the Axes to ad # draw a grid (it will be only for Y) self.ax.grid(True) # generates first "empty" plots self.user = [None] * POINTS self.l_user,=self.ax.plot(range(POINTS),self.user,label='User %') # add the legend self.ax.legend(loc='upper center', ncol=4, prop=font_manager.FontProperties(size=10)) # force a draw on the canvas() # trick to show the grid and the legend self.canvas.draw() # save the clean background - everything but the line # is drawn and saved in the pixel buffer background self.bg = self.canvas.copy_from_bbox(self.ax.bbox) # bind events coming from timer with id = TIMER_ID # to the onTimer callback function wx.EVT_TIMER(self, TIMER_ID, self.onTimer) self.Bind(wx.EVT_CLOSE,self.frame_close,self) def onTimer(self, evt): self.canvas.restore_region(self.bg) # update the data temp =np.random.randint(60,80) self.user = self.user[1:] + [temp] # update the plot self.l_user.set_ydata(self.user) # just draw the "animated" objects self.ax.draw_artist(self.l_user)# It is used to efficiently update Axes data (axis ticks, labels, etc are not updated) self.canvas.blit(self.ax.bbox) print num def frame_close(self,event): self.Show(False) def __del__(self): exit()
class PlotFigure(wx.Frame): """Matplotlib wxFrame with animation effect""" def __init__(self): wx.Frame.__init__(self, None, wx.ID_ANY, title="CPU Usage Monitor", size=(600, 400)) # Matplotlib Figure self.fig = Figure((6, 4), 100) # bind the Figure to the backend specific canvas self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) # add a subplot self.ax = self.fig.add_subplot(111) # limit the X and Y axes dimensions self.ax.set_ylim([0, 100]) self.ax.set_xlim([0, POINTS]) self.ax.set_autoscale_on(False) self.ax.set_xticks([]) # we want a tick every 10 point on Y (101 is to have 10 self.ax.set_yticks(range(0, 101, 10)) # disable autoscale, since we don't want the Axes to ad # draw a grid (it will be only for Y) self.ax.grid(True) # generates first "empty" plots self.user = [None] * POINTS self.l_user, = self.ax.plot(range(POINTS), self.user, label='User %') # add the legend self.ax.legend(loc='upper center', ncol=4, prop=font_manager.FontProperties(size=10)) # force a draw on the canvas() # trick to show the grid and the legend self.canvas.draw() # save the clean background - everything but the line # is drawn and saved in the pixel buffer background self.bg = self.canvas.copy_from_bbox(self.ax.bbox) # bind events coming from timer with id = TIMER_ID # to the onTimer callback function wx.EVT_TIMER(self, TIMER_ID, self.onTimer) def onTimer(self, evt): """callback function for timer events""" # restore the clean background, saved at the beginning self.canvas.restore_region(self.bg) # update the data temp = np.random.randint(10, 80) self.user = self.user[1:] + [temp] # update the plot self.l_user.set_ydata(self.user) # just draw the "animated" objects self.ax.draw_artist( self.l_user ) # It is used to efficiently update Axes data (axis ticks, labels, etc are not updated) self.canvas.blit(self.ax.bbox)
class PlotFigure(wx.Frame): def __init__(self, groundTruth=None): wx.Frame.__init__(self, None, wx.ID_ANY, title="Trajectory") self.fig = Figure() self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) self.ax = self.fig.add_subplot(111) self.ax.set_xlim([-600, 1000]) self.ax.set_ylim([-1500, 1500]) self.ax.set_autoscale_on(False) self.orbPos1 = None self.orbPos2 = None self.ax.grid(True) if groundTruth != None: grnd = groundTruth.toArray(False) self.groundPlot, = self.ax.plot(grnd[:, 0], grnd[:, 1]) # This must be done after all initial drawing self.canvas.draw() self.bg = self.canvas.copy_from_bbox(self.ax.bbox) # Bind events to timer function wx.EVT_TIMER(self, TIMER_ID, self.onTimer) def onTimer(self, event): self.canvas.restore_region(self.bg) orbPosition1 = orbProc1.getPose() if orbPosition1 is not None: if self.orbPos1 is None: self.orbPos1 = self.ax.scatter(orbPosition1.x, orbPosition1.y, color=[[1, 0, 0, 0.5]], s=100, linewidths=0) else: self.orbPos1.set_offsets([orbPosition1.x, orbPosition1.y]) orbPosition2 = orbProc2.getPose() if orbPosition2 is not None: if self.orbPos2 is None: self.orbPos2 = self.ax.scatter(orbPosition2.x, orbPosition2.y, color=[[0, 1, 0, 0.5]], s=100, linewidths=0) else: self.orbPos2.set_offsets([orbPosition2.x, orbPosition2.y]) self.canvas.draw() self.canvas.blit(self.ax.bbox)
class PlotFigure(wx.Frame): def __init__(self,data): wx.Frame.__init__(self, None, wx.ID_ANY, title="CPU Usage Monitor", size=(600, 400)) # Matplotlib Figur self.fig = Figure((6, 4), 100) self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) # add a subplot self.ax = self.fig.add_subplot(111) self.data=data; self.ax.set_ylim([0,100]) self.ax.set_xlim([0, POINTS]) self.ax.set_autoscale_on(False) self.ax.set_xticks([]) self.ax.set_yticks(range(0,100, 5)) # disable autoscale, since we don't want the Axes to ad # draw a grid (it will be only for Y) self.ax.grid(True) # generates first "empty" plots self.user = [None] * POINTS self.l_user,=self.ax.plot(range(POINTS),self.user,label=u'CPU percentage') # add the legend self.ax.legend(loc='upper center', ncol=4, prop=font_manager.FontProperties(size=10)) self.canvas.draw() self.bg = self.canvas.copy_from_bbox(self.ax.bbox) wx.EVT_TIMER(self, TIMER_ID, self.onTimer) self.Bind(wx.EVT_CLOSE,self.frame_close,self) def onTimer(self, evt): self.canvas.restore_region(self.bg) temp = (math.log(self.data.recvive())/LOG_FREQUENCE)*100 #temp =np.random.randint(60,80) self.user = self.user[1:] + [temp] # update the plot self.l_user.set_ydata(self.user) # just draw the "animated" objects self.ax.draw_artist(self.l_user)# It is used to efficiently update Axes data (axis ticks, labels, etc are not updated) self.canvas.blit(self.ax.bbox) #print self.data.recvive() def frame_close(self,event): self.Show(False) def __del__(self): exit()
class PlotFigure(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "Test wxFigure") self.fig = p.figure(1) self.ax = p.subplot(111) self.canvas = FigureCanvasWxAgg(self, -1, self.fig) # self.canvas = self.ax.figure.canvas self.background = None self.cnt = 0 self.tstart = time.time() wx.EVT_TIMER(self, TIMER_ID, self.update_line) def init_plot(self): # create the initial line x = nx.arange(0, 2 * nx.pi, 0.01) self.l_pos_a = [((0, 0), (1, 1)), ((0, 1), (1, 0))] self.l_pos_b = [((0, 0), (0, 1)), ((1, 1), (1, 0))] self.line_c = LineCollection(self.l_pos_a, animated=True) line, = p.plot(x, nx.sin(x), animated=False) self.ax.add_collection(self.line_c) def update_line(self, evt): # save the clean slate background -- everything but the animated line # is drawn and saved in the pixel buffer background if self.background is None: self.background = self.canvas.copy_from_bbox(self.ax.bbox) # restore the clean slate background self.canvas.restore_region(self.background) # update the data # line.set_ydata(nx.sin(x+update_line.cnt/10.0)) if (self.cnt / 10) % 2 == 0: self.line_c.set_verts(self.l_pos_b) else: self.line_c.set_verts(self.l_pos_a) # just draw the animated artist self.ax.draw_artist(self.line_c) # just redraw the axes rectangle self.canvas.blit(self.ax.bbox) if self.cnt == 50: # print the timing info and quit print 'FPS:', self.cnt / (time.time() - self.tstart) sys.exit() self.cnt += 1 # wx.WakeUpIdle() return True
class PlotFigure(wx.Frame): """Matplotlib wxFrame with animation effect""" def __init__(self): wx.Frame.__init__(self, None, wx.ID_ANY, title="CPU Usage Monitor", size=(600, 400)) self.fig = Figure((6, 4), 100) self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) self.ax = self.fig.add_subplot(111) # limit the X and Y axes dimensions self.ax.set_ylim(2140, 2150) self.ax.set_xlim([0, POINTS]) self.ax.set_autoscale_on(False) self.ax.set_xticks([]) # we want a tick every 10 point on Y (101 is to have 10 #self.ax.set_yticks(range(0, 101, 10)) self.ax.grid(True) self.user = [None] * POINTS self.l_user, = self.ax.plot(range(POINTS), self.user, label=u'IF1406') self.md = MdThread("127.0.0.1", 12345) self.md.start() self.md.RegTick(self.OnTick) # add the legend self.ax.legend(loc='upper center', ncol=4, prop=font_manager.FontProperties(size=10)) self.canvas.draw() self.bg = self.canvas.copy_from_bbox(self.ax.bbox) #wx.EVT_TIMER(self, TIMER_ID, self.onTimer) def OnTick(self, tick): self.canvas.restore_region(self.bg) # update the data if tick.InstrumentID != 'IF1404': return print tick.LastPrice temp = np.random.randint(10, 80) self.user = self.user[1:] + [tick.LastPrice] # update the plot self.l_user.set_ydata(self.user) # just draw the "animated" objects self.ax.draw_artist( self.l_user ) # It is used to efficiently update Axes data (axis ticks, labels, etc are not updated) self.canvas.blit(self.ax.bbox)
class PlotFigure (wx.Frame): def __init__ (self, groundTruth=None): wx.Frame.__init__ (self, None, wx.ID_ANY, title="Trajectory") self.fig = Figure () self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) self.ax = self.fig.add_subplot (111) self.ax.set_xlim ([-600, 1000]) self.ax.set_ylim ([-1500, 1500]) self.ax.set_autoscale_on (False) self.orbPos1 = None self.orbPos2 = None self.ax.grid(True) if groundTruth != None: grnd = groundTruth.toArray(False) self.groundPlot, = self.ax.plot (grnd[:,0], grnd[:,1]) # This must be done after all initial drawing self.canvas.draw() self.bg = self.canvas.copy_from_bbox (self.ax.bbox) # Bind events to timer function wx.EVT_TIMER (self, TIMER_ID, self.onTimer) def onTimer (self, event): self.canvas.restore_region(self.bg) orbPosition1 = orbProc1.getPose() if orbPosition1 is not None: if self.orbPos1 is None: self.orbPos1 = self.ax.scatter (orbPosition1.x, orbPosition1.y, color=[[1,0,0,0.5]], s=100, linewidths=0) else : self.orbPos1.set_offsets([orbPosition1.x, orbPosition1.y]) orbPosition2 = orbProc2.getPose() if orbPosition2 is not None: if self.orbPos2 is None: self.orbPos2 = self.ax.scatter (orbPosition2.x, orbPosition2.y, color=[[0,1,0,0.5]], s=100, linewidths=0) else : self.orbPos2.set_offsets([orbPosition2.x, orbPosition2.y]) self.canvas.draw() self.canvas.blit(self.ax.bbox)
class PlotFigure(wx.Frame): """Matplotlib wxFrame with animation effect""" def __init__(self): wx.Frame.__init__(self, None, wx.ID_ANY, title="CPU Usage Monitor", size=(600, 400)) self.fig = Figure((6, 4), 100) self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) self.ax = self.fig.add_subplot(111) # limit the X and Y axes dimensions self.ax.set_ylim(2140, 2150) self.ax.set_xlim([0, POINTS]) self.ax.set_autoscale_on(False) self.ax.set_xticks([]) # we want a tick every 10 point on Y (101 is to have 10 #self.ax.set_yticks(range(0, 101, 10)) self.ax.grid(True) self.user = [None] * POINTS self.l_user,=self.ax.plot(range(POINTS),self.user,label=u'IF1406') self.md = MdThread("127.0.0.1",12345) self.md.start() self.md.RegTick(self.OnTick) # add the legend self.ax.legend(loc='upper center', ncol=4, prop=font_manager.FontProperties(size=10)) self.canvas.draw() self.bg = self.canvas.copy_from_bbox(self.ax.bbox) #wx.EVT_TIMER(self, TIMER_ID, self.onTimer) def OnTick(self,tick): self.canvas.restore_region(self.bg) # update the data if tick.InstrumentID != 'IF1404': return print tick.LastPrice temp =np.random.randint(10,80) self.user = self.user[1:] + [tick.LastPrice] # update the plot self.l_user.set_ydata(self.user) # just draw the "animated" objects self.ax.draw_artist(self.l_user)# It is used to efficiently update Axes data (axis ticks, labels, etc are not updated) self.canvas.blit(self.ax.bbox)
class PlotPanel(wx.Panel): def __init__(self,parent,data_window,yrange=(-3,3),**kwargs): from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg from matplotlib.figure import Figure self.dw = data_window # initialize Panel if 'style' not in kwargs.keys(): kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE wx.Panel.__init__( self, parent, **kwargs ) # initialize matplotlib stuff self.figure = Figure() self.canvas = FigureCanvasWxAgg(self, -1, self.figure ) self.subplot_x = self.figure.add_subplot(311) self.subplot_x.set_ylim(yrange) self.subplot_x.set_xticks([]) self.subplot_y = self.figure.add_subplot(312) self.subplot_y.set_ylim(yrange) self.subplot_y.set_xticks([]) self.subplot_z = self.figure.add_subplot(313) self.subplot_z.set_ylim(yrange) self.subplot_z.set_xticks([]) self.dw.winlock.acquire() self.line_x, = self.subplot_x.plot(self.dw.win[:,0],color='r',lw=2,animated=True) self.line_y, = self.subplot_y.plot(self.dw.win[:,1],color='g',lw=2,animated=True) self.line_z, = self.subplot_z.plot(self.dw.win[:,2],color='b',lw=2,animated=True) self.dw.winlock.release() self.canvas.draw() self.draw() self.dw.start() self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER,self.OnTimer,self.timer) self.timer.Start(1) def OnTimer(self,event): self.draw() def draw( self ): """Draw data.""" if not hasattr(self, 'background' ): self.background = self.canvas.copy_from_bbox(self.figure.bbox) self.canvas.restore_region(self.background) self.dw.winlock.acquire() self.line_x.set_ydata(self.dw.win[:,0]) self.line_y.set_ydata(self.dw.win[:,1]) self.line_z.set_ydata(self.dw.win[:,2]) self.dw.winlock.release() self.subplot_x.draw_artist(self.line_x) self.subplot_y.draw_artist(self.line_y) self.subplot_z.draw_artist(self.line_z) self.canvas.blit(self.subplot_x.bbox) self.canvas.blit(self.subplot_y.bbox) self.canvas.blit(self.subplot_z.bbox)
class Window(wx.Frame): global Monitor_Interval, y_Range def __init__(self, parent): super(Window, self).__init__(parent) self.InitUI() self.Centre() self.Show() self.SetIcon(wx.Icon('bitcoin_48px.ico', wx.BITMAP_TYPE_ICO)) self.taskBarIcon = TaskBarIcon(self) # create some sizers mainSizer = wx.BoxSizer(wx.VERTICAL) checkSizer = wx.BoxSizer(wx.HORIZONTAL) #事件绑定 self.Bind(wx.EVT_CLOSE, self.OnClose) self.Bind(wx.EVT_ICONIZE, self.OnIconfiy) # 最小化事件绑定 self.Bind(wx.EVT_PAINT, self.OnPaint) def InitUI(self): global initial_time, elapsed_time global Monitor_Interval, y_Range global Cur_Price, Max_Price, Min_Price, Price_Log, Counter #静态文本条 wx.StaticText(self, label='Top price:', pos=(30, 310 + 50)) wx.StaticText(self, label='Current price:', pos=(30, 330 + 50)) wx.StaticText(self, label='Floor price:', pos=(30, 350 + 50)) wx.StaticText(self, label='Current time:', pos=(210, 310 + 50)) wx.StaticText(self, label='Monitor interval:', pos=(210, 330 + 50)) wx.StaticText(self, label='Monitor time:', pos=(210, 350 + 50)) wx.StaticText(self, label='Network state:', pos=(30, 310)) wx.StaticText(self, label='Price trend:', pos=(210, 310)) #动态文本条 self.string_top_price = wx.StaticText(self, label='', pos=(130, 310 + 50)) self.string_price = wx.StaticText(self, label='', pos=(130, 330 + 50)) self.string_flr_price = wx.StaticText(self, label='', pos=(130, 350 + 50)) self.string_cur_time = wx.StaticText(self, label='', pos=(320, 310 + 50)) self.string_mon_gap = wx.StaticText(self, label='', pos=(320, 330 + 50)) self.string_elp_time = wx.StaticText(self, label='', pos=(320, 350 + 50)) self.string_network = wx.StaticText(self, label='', pos=(130, 310)) self.string_price_trend = wx.StaticText(self, label='', pos=(320, 310)) # 创建定时器 self.timer = wx.Timer(self) #创建定时器 self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer) #绑定一个定时器事件 self.SetSize((500, 480)) #窗口的颜色 self.SetBackgroundColour('#DCDCDC') #窗口的标题 self.SetTitle('Huobi Bitcoin price monitor Version 0.1') self.Centre() self.Show(True) #检查网络连接 if Check_Network() == 1: #查询价格,更新文本条 self.string_price.SetLabel(str(Get_Huobi_Price())) self.string_top_price.SetLabel(str(Max_Price)) self.string_flr_price.SetLabel(str(Min_Price)) #设置网络状态指示灯为绿色 self.string_network.SetLabel('ok') self.string_price_trend.SetLabel('unknow') else: self.string_price.SetLabel('0') self.string_top_price.SetLabel('0') self.string_flr_price.SetLabel('0') #设置网络状态指示灯为红色 self.string_network.SetLabel('error') self.string_price_trend.SetLabel('unknow') self.string_cur_time.SetLabel( time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))) self.string_mon_gap.SetLabel( str(int(Monitor_Interval / 1000.0)) + ' s') initial_time = time.time() self.string_elp_time.SetLabel('0.00 mins') #绘制曲线相关,先生成画板 self.fig = Figure(facecolor='#DCDCDC') #设置背景色 self.fig.set_figheight(3) #设置Figure高度 self.fig.set_figwidth(5) #设置Figure宽度 # bind the Figure to the backend specific canvas self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) # add a subplot self.ax = self.fig.add_subplot(111) # limit the X and Y axes dimensions,以当前价格为中心,y_Range之内 self.ax.set_xlim([0, Curce_Points]) if y_Range == 1000: self.ax.set_ylim([Cur_Price - 500, Cur_Price + 500]) elif y_Range == 500: self.ax.set_ylim([Cur_Price - 250, Cur_Price + 250]) elif y_Range == 100: self.ax.set_ylim([Cur_Price - 50, Cur_Price + 50]) self.ax.set_autoscale_on(False) if Monitor_Interval == 1000 * 30: self.ax.set_xticks(np.linspace(0, 600, 7)) self.ax.set_xticklabels( ('300', '250', '200', '150', '100', '50', '0'), fontdict=font1) elif Monitor_Interval == 1000 * 10: self.ax.set_xticks(np.linspace(0, 600, 5)) self.ax.set_xticklabels(('100', '75', '50', '25', '0'), fontdict=font1) elif Monitor_Interval == 1000 * 60: self.ax.set_xticks(np.linspace(0, 600, 7)) self.ax.set_xticklabels( ('600', '500', '400', '300', '200', '100', '0'), fontdict=font1) elif Monitor_Interval == 1000 * 60 * 5: self.ax.set_xticks(np.linspace(0, 600, 7)) self.ax.set_xticklabels( ('3000', '2500', '2000', '1500', '1000', '500', '0'), fontdict=font1) if y_Range == 1000: self.ax.set_yticks( range(Cur_Price - 500 - 1, Cur_Price + 500 + 1, 100)) tem_array = tuple( range(Cur_Price - 500 - 1, Cur_Price + 500 + 1, 100)) elif y_Range == 500: self.ax.set_yticks( range(Cur_Price - 250 - 1, Cur_Price + 250 + 1, 50)) tem_array = tuple( range(Cur_Price - 250 - 1, Cur_Price + 250 + 1, 50)) elif y_Range == 100: self.ax.set_yticks( range(Cur_Price - 50 - 1, Cur_Price + 50 + 1, 10)) tem_array = tuple(range(Cur_Price - 50 - 1, Cur_Price + 50 + 1, 10)) self.ax.set_yticklabels(tem_array, fontdict=font1) #曲线图边框的颜色,本程序选择橘黄色 self.ax.spines['left'].set_color('#FF9000') self.ax.spines['right'].set_color('#FF9000') self.ax.spines['top'].set_color('#FF9000') self.ax.spines['bottom'].set_color('#FF9000') #坐标轴刻度朝向,颜色,长度,以及宽度 self.ax.tick_params(axis='x', direction='in', colors='black', length=4, width=1) self.ax.tick_params(axis='y', direction='in', colors='black', length=5, width=1) #网格线 self.ax.grid(True) # generates first "empty" plots self.user = [None] * Curce_Points self.l_user, = self.ax.plot(range(Curce_Points)) #图例(此处已关闭) ###self.l_user,=self.ax.plot(range(Curce_Points),self.user,label='Price curve of Bitcoin') ##self.ax.legend(loc='upper center',ncol=4,prop=font_manager.FontProperties(size=9)) # force a draw on the canvas() trick to show the grid and the legend self.canvas.draw() # save the clean background - everything but the line is drawn and saved in the pixel buffer background self.bg = self.canvas.copy_from_bbox(self.ax.bbox) # bind events coming from timer with id = TIMER_ID to the onTimer callback function wx.EVT_TIMER(self, TIMER_ID, self.OnTimer) def __del__(self): pass def OnTimer(self, evt): #显示时间事件处理函数 global Cur_Price, Max_Price, Min_Price, Price_Log, Counter global initial_time, elapsed_time global Network_State #检查网络状态,只有网络连通了,才调用价格查询子程序 if Check_Network() == 1: Cur_Price = Get_Huobi_Price() #网络状态指示灯绿色 self.string_network.SetLabel('ok') else: print('no network') #网络状态指示灯红色 self.string_network.SetLabel('error') self.string_price.SetLabel(str(Cur_Price)) self.string_top_price.SetLabel(str(Max_Price)) self.string_flr_price.SetLabel(str(Min_Price)) self.string_cur_time.SetLabel( time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))) self.string_mon_gap.SetLabel( str(int(Monitor_Interval / 1000.0)) + ' s') elapsed_time = time.time() - initial_time self.string_elp_time.SetLabel( str(round(elapsed_time / 60, 2)) + ' mins') # 转换成分钟,保留2位有效数字 #价格趋势rise或者fall或者unchanged if Cur_Price > Price_Log[Counter - 1]: self.string_price_trend.SetLabel('rise') elif Cur_Price == Price_Log[Counter - 1]: self.string_price_trend.SetLabel('unchanged') else: self.string_price_trend.SetLabel('fall') #绘制曲线 self.canvas.restore_region(self.bg) #更新曲线数据 temp = Cur_Price self.user = self.user[1:] + [temp] # update the plot self.l_user.set_ydata(self.user) # just draw the "animated" objects self.ax.draw_artist( self.l_user ) # It is used to efficiently update Axes data (axis ticks, labels, etc are not updated) self.canvas.blit(self.ax.bbox) def OnPaint(self, event=None): dc = wx.PaintDC(self) dc.Clear() pen = wx.Pen('#808A87', 1, wx.SOLID) #1表示线宽 dc.SetPen(pen) dc.DrawLine(0, 300, 500, 300) dc.SetPen(pen) dc.DrawLine(0, 340, 500, 340) def OnHide(self, event): self.Hide() def OnIconfiy(self, event): event.Skip() self.Hide() def OnClose(self, event): self.taskBarIcon.Destroy() self.Destroy()
class Graph(wx.BoxSizer): """ This holds a curve plot with associated toolbar keyword arguments: parent -- reference to the panel or context the plot should be created in. orientation -- (optional) wx.BoxSizer style. This also sets the deafult direction to expand when adding sub-plots (default: wx.VERTICAL) title -- (optional) sets the title of the plot window (default: ''). dpi -- (optional) sets dots per inch of plot window. params -- (optional) set matplotlib rcParams, should be a dictionary. (default: sets font size of: ticks, legend, axes label, font) **kwargs -- any keyword argument to matplotlib.Figure """ def __init__(self, parent, orientation=wx.VERTICAL, title='', dpi=None, params=None, **kwargs): super(Graph, self).__init__(orientation) #initialize some font settings for matplotlib if params == None: params = { 'axes.labelsize': 16, 'font.size': 14, 'legend.fontsize': 14, 'xtick.labelsize': 12, 'ytick.labelsize': 12 } matplotlib.rcParams.update(params) self.figure = Figure(dpi=dpi, figsize=(2, 2), **kwargs) self.canvas = FigureCanvas(parent, wx.NewId(), self.figure) self.sub_plots = _plot_list(self.figure) self.sub_plots.append(self.figure.add_subplot(111), title) self.toolbar = NavigationToolbar2Wx(self.canvas) ###Create some extra controls for the toolbar### self.cb_grid = wx.CheckBox(self.toolbar, wx.NewId(), 'Show Grid') btn_mark = wx.Button(self.toolbar, wx.NewId(), 'Mark selection') #btn_rem = wx.Button(parent, wx.NewId(), 'Remove_graph') self.cursor_pos = wx.StaticText( self.toolbar, wx.NewId(), 'x=0.0000, y=0.0000 ', style=wx.ALIGN_RIGHT | wx.ST_ELLIPSIZE_END | wx.ST_NO_AUTORESIZE) ####add extra controls to toolbar#### self.toolbar.AddControl(self.cb_grid) self.toolbar.AddControl(btn_mark) self.toolbar.AddStretchableSpace() self.toolbar.AddControl(self.cursor_pos) #self.toolbar.AddStretchableSpace() #needed to update the layout self.toolbar.Realize() #######Main layout####### v_sizer = wx.BoxSizer(wx.VERTICAL) v_sizer.Add(self.canvas, 1, wx.EXPAND) v_sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) #Add to self self.Add(v_sizer, 1, wx.EXPAND) ###Set title and other things### self.orientation = orientation #layout of plots (height, width, count) self.layout = (1, 1, 1) self.cb_grid.SetValue(True) self.sub_plots.show_grid(True) #connect the buttons to event handlers self.cb_grid.Connect(-1, -1, wx.wxEVT_COMMAND_CHECKBOX_CLICKED, self._on_cb_grid) btn_mark.Connect(-1, -1, wx.wxEVT_COMMAND_BUTTON_CLICKED, self._on_mark) #btn_rem.Connect(-1, -1, wx.wxEVT_COMMAND_BUTTON_CLICKED, self.on_rem) self.canvas.mpl_connect('motion_notify_event', self._UpdateCursorInformation) ############Event handlers############ def _on_cb_grid(self, evt): """ Toggles if grid should be shown on sub-plots """ self.sub_plots.show_grid(self.cb_grid.IsChecked()) #redraw plots self.canvas.draw() def _on_mark(self, evt): """ Draws or removes a selection outline on current sub-plot selection """ mark_color = 'k' if self.sub_plots.color.lower() == 'black': mark_color = 'white' if self.sub_plots.has_selection: #delete markers for sub_plot in self.sub_plots.sub_plots: for line in sub_plot.selection: sub_plot.axes.lines.remove(line) self.canvas.draw() else: for i, sub_plot in enumerate(self.sub_plots.sub_plots): x1, x2, y1, y2 = sub_plot.axes.axis() x = [x1, x2, x2, x1, x1] y = [y1, y1, y2, y2, y1] sub_plot.selection = self.redraw(x, y, hold=True, limits=(x1, x2, y1, y2), index=i, color=mark_color, linewidth=2.0) self.sub_plots.has_selection = not self.sub_plots.has_selection def _UpdateCursorInformation(self, evt): if evt.inaxes: x, y = evt.xdata, evt.ydata txt = 'x={0:.4g}, y={1:.4g}'.format(x, y) self.cursor_pos.SetLabel(txt) ############Worker functions############ def add_secondary_y_axis(self, label='', index=None): """ Creates a secondary y_axis on the specified sub-plots keyword arguments: label -- (optional) axis label of the second y-axis (default: '') index -- (optional) a integer or list of integers with the index of sub-plots for which a secondary y-axis should be created. When 'None' the formatter is set for all sub-plots (default: None) """ if type(index) == list: for i in index: self.sub_plots(i).create_y2_axis(label) elif type(index) == int: self.sub_plots(index).create_y2_axis(label) else: #do all count = self.layout[-1] for sub_plot in self.sub_plots.sub_plots: sub_plot.create_y2_axis(label) def add_subplot(self, title='', orientation=None): """ Adds an additional subplot. If more than one row exists and it will not be filled, than the plots on the last row will be expanded to fill all horizontal space. All plots will be recreated keyword arguments: title -- (optional) title text of new subplot (default: '') orientation -- (optional) direction to add subplot, valid data is: wx.VERTICAL or wx.HORIZONTAL (default: Graph.orientation) return -- index of created plot """ orientation = self.orientation if orientation == None else orientation count = self.layout[-1] + 1 size = self.layout[0] * self.layout[1] #check if there is room for another subplot if size >= count: self.layout = self.layout[:2] + (count, ) else: #expand layout if orientation == wx.VERTICAL: self.layout = (self.layout[0] + 1, self.layout[1], count) else: self.layout = (self.layout[0], self.layout[1] + 1, count) #Clear away old axes self.sub_plots.clear_axes() #recreate axes and add new one size = self.layout[0] * self.layout[1] for i in range(1, count + 1): if count < size and i > self.layout[1] * (self.layout[0] - 1): #expand graphs on last row exp_layout = (self.layout[0], self.layout[1] - (size - count)) exp_i = exp_layout[0] * exp_layout[1] - (count - i) axes = self.figure.add_subplot(*exp_layout + (exp_i, )) #check if to rebind axes or append new object if i == count: self.sub_plots.append(axes, title) else: self.sub_plots.set_axes(axes, i - 1) else: axes = self.figure.add_subplot(*self.layout[:2] + (i, )) #check if to rebind axes or append new object if i == count: self.sub_plots.append(axes, title) else: self.sub_plots.set_axes(axes, i - 1) self.canvas.draw() #return sub-plot index return count - 1 def cleanse_fontcache(self): """ Shouldn't be used. Can fix bug when using frozen programs under windows. Better to modify the setup script so that font files are left out of the matplotlib data files. """ file_path = path.join(path.expanduser('~'), \ '.matplotlib', \ 'fontList.cache') if path.exists(file_path): remove(file_path) def clear_lines(self, index=0): """ Removes all lines from the selected sub-plot keyword arguments: index -- (optional) index of subplot, which should be cleared from all lines (default: 0) """ self.sub_plots(index).axes.cla() self.sub_plots(index).lines = [] def get_lines(self, index=0): """ Retrieves all the lines of the specified sub-plot keyword arguments: index -- (optional) index of subplot from, which the lines should be retrieved from (default: 0) """ return self.sub_plots(index).axes.get_lines() def redraw(self, x, y, index=0, hold=False, xmin=None, ymin=None, limits=None, limit_selector=None, alpha=1.0, draw=True, **kwarg): """ Updates plot with new vectors keyword arguments: x -- the x-axis values y -- the y-axis values index -- (optional) index of subplot to plot the vector in (default: 0) hold -- (optional) should old vectors be kept (default: False) xmin -- (optional) set minimum value of x-axis (default: None) ymin -- (optional) set minimum value of y-axis (default: None) limits -- (optional) list to set the limits of x and y-axis. overrides the xmin and ymin arguments (default: None) limit_selector -- (optional) a fucntion which given the arguments: *(x1, x2, y1, y2) will return a list with the axis limits overrides the xmin, ymin and the limits argument (default: None) e.g. sel = lambda x1, x2, y1, y2: [x1, x2, min(y1, 0), y2] alpha -- (optional) sets the alpha of the line (default: 1.0) draw -- (optional) should the canvas be updated to show the new lines **kwarg -- all extra keyword arguments are sent to the plot function """ #set plot to update try: plot = self.sub_plots(index) except IndexError: raise (IndexError, "The sub-plot of index:{0:d} doesn't exist".format(index)) plot.axes.hold(hold) lines = plot.axes.plot(x, y, alpha=alpha, **kwarg) #Create a legend if label was given if not lines[0].get_label()[0] == "_": plot.axes.legend() #label must be sent through kwarg #if not ymin == None: x1, x2, y1, y2 = plot.axes.axis() ymin = y1 if ymin == None else ymin xmin = x1 if xmin == None else xmin plot.axes.axis([xmin, x2, ymin, y2]) if not limits == None: plot.axes.axis(limits) if not limit_selector == None: plot.axes.axis(limit_selector(x1, x2, y1, y2)) #plot it #plot.axes.grid(self.sub_plots.grid) plot.reload() if draw: self.canvas.draw() #store lines in a list #return line object #plot.lines.extend(lines) return lines def redraw_secondary_y(self, x, y, index=0, **kwarg): """ Update secondary y-axis with a new vector keyword arguments: x -- the x-axis values y -- the y-axis values index -- (optional) index of subplot to plot the vector in (default: 0) style -- (optional) line style (default: 'r.') **kwarg -- all extra keyword arguments are sent to the plot function """ sub_plot = self.sub_plots(index) lines = sub_plot.y2_axis.plot(x, y, style, **kwarg) #show it self.canvas.draw() return lines def remove_lines(self, lines, index=0): """ Re keyword arguments: lines -- A list of matplotlib lines as given by the redraw function index -- (optional) index of subplot from, which the lines should be retrieved from (default: 0) """ for line in lines: self.sub_plots(index).remove_line(line) self.canvas.draw() def remove_subplot(self): """ Removes the last sub-plot. Note that plots in the last row aren't expanded to fill the entire row. """ count = self.layout[-1] - 1 if count < 0: raise ValueError, "There is no sub-plot to remove" self.layout = (self.layout[0], self.layout[1], count) if count > 0: layout_change = True #check if layout of plots can be decreased if self.layout[0] > 1 and self.layout[1] > 1: if self.layout[0] < self.layout[1]: lrg = 1 sml = 0 else: lrg = 0 sml = 1 #check if a decrease is possible on the major axis size = (self.layout[lrg] - 1) * (self.layout[sml]) if size >= count: if lrg == 0: self.layout = (self.layout[0] - 1, self.layout[1], count) else: self.layout = (self.layout[0], self.layout[1] - 1, count) else: #check the minor axis size = (self.layout[lrg]) * (self.layout[sml] - 1) if size >= count: if sml == 0: self.layout = (self.layout[0] - 1, self.layout[1], count) else: self.layout = (self.layout[0], self.layout[1] - 1, count) else: layout_change = False else: if self.layout[0] > self.layout[1]: self.layout = (self.layout[0] - 1, self.layout[1], count) else: self.layout = (self.layout[0], self.layout[1] - 1, count) else: layout_change = False #remove the last sub-plot self.sub_plots.remove() if layout_change: #clear figure and recreate plots self.sub_plots.clear_axes() for i in range(1, count + 1): plot_index = i - 1 self.sub_plots.set_axes( self.figure.add_subplot(*self.layout[:2] + (i, )), plot_index) #redraw screen self.canvas.draw() def set_formatter(self, frmt='sci', axes='all', useOffset=True, limits=(-3, 3), index=None): """ Sets the formatter of the axes. Default is to set scientific notation for all axes of all subplots. keyword arguments: frmt -- Sets the type of formatter used, valid values are: 'sci', 'log', 'plain' (default: 'sci') axes -- which axes should the formatter be used for, valid values are: 'all', 'x', 'y' (default: 'all') useOffset -- Should offset be used to make the tickers more meaningful (default: True) limits -- Limits for scientific notation as a tuple (default: (-3, 3)) index -- a integer or list of integers with the index of sub-plots for which the formatter should set. When 'None' the formatter is set for all sub-plots (default: None) """ frmt = frmt.lower() axes = axes.lower() if frmt == 'log': formatter = LogFormatter() else: sci = frmt == 'sci' formatter = ScalarFormatter(useOffset=useOffset) formatter.set_powerlimits(limits) formatter.set_scientific(sci) # format axes if type(index) == list: for i in index: self.sub_plots(i).set_formatter(formatter, axes) elif type(index) == int: self.sub_plots(index).set_formatter(formatter, axes) else: # do all for sub_plot in self.sub_plots.sub_plots: sub_plot.set_formatter(formatter, axes) #set default formatter self.sub_plots.set_default_formatter(formatter, axes) # redraw screen self.canvas.draw() def set_label(self, xlabel='', ylabel='', index=None): """ Set labels of the specified plot axes keyword arguments: xlabel -- (optional) sets the label of the x-axis (default: '') ylabel -- (optional) sets the label of the y-axis (default: '') index -- (optional) a integer or list of integers with the index of sub-plots for which a the labels should be set. When 'None' the labels is set for all sub-plots (default: None) """ if type(index) == list: for i in index: self.sub_plots.set_label(xlabel, ylabel, i) elif type(index) == int: self.sub_plots.set_label(xlabel, ylabel, index) else: # do all count = self.layout[-1] for i in range(count): self.sub_plots.set_label(xlabel, ylabel, i) # Redraw screen self.canvas.draw() def set_limits(self, limits, index=0): """ Sets the axis limits of the specified sub-plot keyword arguments: limits -- A list to set the limits of x and y-axis: [x1, x2, y1, y2] index -- (optional) index of subplot to set axis limits (default: 0) """ self.sub_plots(index).axes.axis(limits) self.canvas.draw() def set_title(self, titles='', index=0): """ Set titles of the sub-plots keyword arguments: titles -- should be a list of title strings or a single string, in which case an index should be supplied (default: '') index -- (optional) Is only needed when titles is a single string specifies for which sub-plot to set the title """ if type(titles) == list: for i, title in enumerate(titles): self.sub_plots(i).set_title(title) else: self.sub_plots(index).set_title(titles) self.canvas.draw() def update(self): """ Will send a draw command to the canvas uppdating the graphs """ self.canvas.draw() self.canvas.flush_events() def update_plot_only(self, lines, index=0): """ Will redraw the background and plot lines only keyword arguments: lines -- a list of line objects for the plot index -- (optional) index of subplot to set axis limits (default: 0) """ try: plot = self.sub_plots(index) except IndexError: raise (IndexError, "The sub-plot of index:{0:d} doesn't exist".format(index)) ax = plot.axes #draw the background ax.draw_artist(ax.patch) #draw the lines for line in lines: ax.draw_artist(line) #draw the x grid for line in ax.get_xgridlines(): ax.draw_artist(line) #draw the y grid for line in ax.get_ygridlines(): ax.draw_artist(line) #redraw display selectively self.canvas.blit(ax.bbox)
class PlotPanel(wx.Panel): def __init__(self, parent, statusbar): wx.Panel.__init__(self, parent, -1, size=(50, 50)) self.statusbar = statusbar self.plot_handl = None self.sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.sizer) self.figure = Figure(None, dpi=300) self.ax = self.figure.add_axes([0, 0, 1, 1], frameon=False, projection=ccrs.PlateCarree()) self.canvas = PlotFigureCanvas(self, -1, self.figure) self.toolbar = PlotToolbar(self.canvas, self) self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) self.sizer.Add(self.toolbar, 0, wx.EXPAND) # Clear artifacts self.clear_artifacts() # Bind events self.figure.canvas.mpl_connect('button_release_event', self.onclick) self.ax.callbacks.connect('xlim_changed', self.on_xlims_change) self.ax.callbacks.connect('ylim_changed', self.on_ylims_change) # setup empty list of meridians and parallels self.meridians_w = {} self.meridians_b = {} self.parallels_w = {} self.parallels_b = {} self.labels = [] self.nlons = NUM_GRID_LINES self.nlats = NUM_GRID_LINES self.lons = [] self.lats = [] self.latll = -90 self.latur = 90 self.lonll = -180 self.lonur = 180 self.motion_display_font = FontProperties() self.motion_display_font.set_size(MOTION_DISPLAY_FONT_SIZE) self.motion_display_font.set_family('monospace') self.grid_label_font = FontProperties() self.grid_label_font.set_size(GRID_LABEL_FONT_SIZE) self.grid_label_font.set_family('monospace') self.map = None self.motion_display = None self._rc_zoomed = False self.cursor_coordinates_enabled = True self.coordinate_grid_enabled = True self.grid_labels_enabled = True def set_cursor_coordinates_enabled(self, enabled): self.cursor_coordinates_enabled = enabled # apply immediately if not enabled: if self.motion_display: self.motion_display.remove() self.figure.canvas.draw() self.motion_display = None def set_coordinate_grid_enabled(self, enabled): self.coordinate_grid_enabled = enabled def set_grid_labels_enabled(self, enabled): self.grid_labels_enabled = enabled def on_xlims_change(axes): pass def on_ylims_change(axes): pass def on_mouse_move(self, event): if not self.map: return lon, lat = self.map(event.xdata, event.ydata, inverse=True) lonll = max(self.lonll, self.map.llcrnrlon) lonur = min(self.lonur, self.map.urcrnrlon) latll = max(self.latll, self.map.llcrnrlat) latur = min(self.latur, self.map.urcrnrlat) if lat >= latll and lat <= latur and \ lon >= lonll and lon <= lonur and \ self.map.projection == 'cyl' and \ self.cursor_coordinates_enabled: if self.motion_display is None: # setup coordinate display for mouse motion self.motion_display = self.ax.annotate( s="(NaN, NaN)", xy=(0.995, 0.995), xycoords='axes fraction', fontproperties=self.motion_display_font, verticalalignment='top', horizontalalignment='right', bbox=dict(facecolor='white', alpha=1.0, pad=0.2, lw=0.2)) display_lon = self.wrap_lon_at_day_boundary(lon) self.motion_display.set_text('(%+10.5f,%+10.5f)' % (display_lon, lat)) self.ax.draw_artist(self.motion_display) self.canvas.blit(self.motion_display.get_window_extent()) else: if self.motion_display: self.motion_display.remove() self.figure.canvas.draw() self.motion_display = None def wrap_lon_at_day_boundary(self, lon): wrapped_lon = lon while wrapped_lon < -180: wrapped_lon += 360.0 while wrapped_lon > 180: wrapped_lon -= 360.0 return wrapped_lon def do_dynamic_update(self): if not self.map: return # update meridians self._plot_meridians_and_parallels() self.figure.canvas.draw() def set_right_click_zoomed(self): self._rc_zoomed = True def onclick(self, event): if event is None or event.button != 3 or not event.inaxes: return if self._rc_zoomed: self._rc_zoomed = False return self.click_event = event menu = wx.Menu() menu.Append(wx.ID_ANY, "Add Point Here", "Adds a point where the mouse was clicked on the map", wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.callback, id=wx.ID_ANY) if self.canvas.HasCapture(): self.canvas.ReleaseMouse() wx.CallAfter(self.PopupMenu, menu) def callback(self, event): self.map.plot(self.click_event.xdata, self.click_event.ydata, 'r.', markersize=1.) self.figure.canvas.draw() def clear_artifacts(self): for artifact in ['artists', 'lines', 'patches']: while getattr(self.ax, 'artists') != []: getattr(self.ax, 'artists')[0].remove() self.artifacts = {'artists': [], 'lines': [], 'patches': []} def updatePlot(self, attrs): self.statusbar.SetStatusText('Plotting... (Please Be Patient)') self.ax.clear() self.plot(attrs) self.statusbar.SetStatusText('Ready') def plot(self, param): # Create Map if not param: self.ax.stock_img() self.figure.canvas.draw() # # Create a really simple plot to fill the void on app startup. # self.map = Basemap(ax=self.ax, resolution=RESOLUTION_MAP['Crude'], # ellps='WGS84', suppress_ticks=True) # self.map.bluemarble() # self._plot_meridians_and_parallels() # # add the motion display to the list of texts for the new axes # if self.motion_display: # self.ax.texts.append(self.motion_display) # self.figure.canvas.draw() return # projection_name = param['Projection'] # projection_key = revlookup(PROJECTIONS, 'name', projection_name) # resolution = RESOLUTION_MAP[param['Resolution']] # kwargs = {'projection': projection_key, # 'ellps': 'WGS84', # 'suppress_ticks': True, # 'ax': self.ax, # 'resolution': resolution} # for p in PROJECTION_PARAMS[projection_key].keys(): # if 'rspherex' == p: # kwargs['rsphere'] = (param[OPTIONS['rspherex']['name']], # param[OPTIONS['rspherey']['name']]) # elif 'rspherey' == p: # pass # else: # kwargs[p] = param[OPTIONS[p]['name']] # self.map = Basemap(**kwargs) # # Draw Circle # self.draw_range_circle(param["longitude"], # param["geodetic_latitude"], # param["range"], # color='r', # alpha=0.5) # # Draw Blue Marble Texture # if param.get('Blue Marble', False): # self.map.bluemarble() # else: # self.map.fillcontinents(color='coral', lake_color='aqua') # # Draw Coastlines, borders, lines # if param.get('Coastlines', False): # self.map.drawcoastlines() # if param.get('State Borders', False): # self.map.drawstates() # if param.get('Country Borders', False): # self.map.drawcountries() # self._plot_meridians_and_parallels() # # add the motion display to the list of texts for the new axes # if self.motion_display: # self.ax.texts.append(self.motion_display) # self.figure.canvas.draw() def draw_great_circle(self, x1, y1, x2, y2, linewidth=.5, color='r'): self.map.drawgreatcircle(x1, y1, x2, y2, linewidth, color) def draw_text(self, text, x, y, xycoords='data', color='white', size='xx-small'): self.ax.add_artist( AnnotationBbox(TextArea(text, minimumdescent=False, textprops={ 'color': color, 'size': size })(x, y), xycoords='data', frameon=False)) def draw_image(self, image, x, y, zoom=.1, alpha=.7, xycoords='data'): if isinstance(image, PyEmbeddedImage): png = pyembeddedimage_to_png(image) elif isinstance(image, basestring): png = read_png(image) else: png = image self.ax.add_artist( AnnotationBbox(OffsetImage(png, zoom=zoom, alpha=alpha), (x, y), xycoords=xycoords, frameon=False)) def draw_range_circle(self, lat, lon, radius, color='r', alpha=.5): # FIXME Use the real radius of the earth at this lat/lon self.map.tissot(lon, lat, 180. / pi * radius / 6370., 256, facecolor=color, alpha=alpha) def _plot_meridians_and_parallels(self): """ Plots meridians and parallels appropriate for the current zoom level """ # if grid is off and labels are off wipe lines if not self.coordinate_grid_enabled and not self.grid_labels_enabled: self._label_grid([], []) self._clear_meridians_and_parallels() return # FIXME: Get this working for other projections if self.map.projection != 'cyl': self._clear_meridians_and_parallels() # FIXME: Make it an option to turn on/off white or black lines if self.coordinate_grid_enabled: lats = np.linspace(-90, 90, 10) self.parallels_b = self.map.drawparallels( lats, labels=[True, False, False, False], fontsize=GRID_LABEL_FONT_SIZE, linewidth=0.2, dashes=[], color='black') self.parallels_w = self.map.drawparallels( lats, labels=[False, False, False, False], linewidth=0.2, dashes=[1, 1], color='white') lons = np.linspace(-180, 180, 10) self.meridians_b = self.map.drawmeridians( lons, labels=[False, False, False, True], fontsize=GRID_LABEL_FONT_SIZE, linewidth=0.2, dashes=[], color='black') self.meridians_w = self.map.drawmeridians( lons, labels=[False, False, False, False], linewidth=0.2, dashes=[1, 1], color='white') self._label_grid(lons, lats) return # get the corners of the viewport self.lonll, self.latll, delta_lon, delta_lat = self.ax.viewLim.bounds self.latur = self.latll + delta_lat self.lonur = self.lonll + delta_lon # translate to lat/lon (if needed) self.lonll, self.latll = self.map(self.lonll, self.latll, inverse=True) self.lonur, self.latur = self.map(self.lonur, self.latur, inverse=True) if not self._zoom_is_valid(): return # get the range of lat/lon of the current viewport lat_range = self.latur - self.latll lon_range = self.lonur - self.lonll # get the estimated number of parallels and meridians to plot nlons = round(NUM_GRID_LINES * 360.0 / lon_range) nlats = round(NUM_GRID_LINES * 180.0 / lat_range) found = False for kv in LAT_GRID_LOOKUP: if nlats <= kv[0]: self.nlats = kv[1] found = True break if not found: self.nlats = self._scale_grid_by_twos(nlats, self.nlats) found = False for kv in LON_GRID_LOOKUP: if nlons <= kv[0]: self.nlons = kv[1] found = True break if not found: self.nlons = self._scale_grid_by_twos(nlons, self.nlons) # return if we've got too few to plot if self.nlons <= 1 or self.nlats <= 1: return # calculate the actual meridian coordinates lons = np.linspace(-180, 180.0, self.nlons) lons = np.concatenate((lons - 360.0, lons, lons + 360.0)) lats = np.linspace(-90.0, 90.0, self.nlats) # filter them to speed plotting lon_condition = lons >= self.lonll lons = np.extract(lon_condition, lons) lon_condition = lons <= self.lonur lons = np.extract(lon_condition, lons) lat_condition = lats >= self.latll lats = np.extract(lat_condition, lats) lat_condition = lats <= self.latur lats = np.extract(lat_condition, lats) # plot labels for each one self._label_grid(lons, lats) self.lats = lats self.lons = lons # plot new parallels/meridians # these are made up of a solid black line, and a dashed white line self._clear_meridians_and_parallels() # FIXME: Make it an option to turn on/off white or black lines if self.coordinate_grid_enabled: self.parallels_b = self.map.drawparallels( lats, labels=[False, False, False, False], linewidth=0.2, dashes=[], color='black') self.parallels_w = self.map.drawparallels( lats, labels=[False, False, False, False], linewidth=0.2, dashes=[1, 1], color='white') self.meridians_b = self.map.drawmeridians( lons, labels=[False, False, False, False], linewidth=0.2, dashes=[], color='black') self.meridians_w = self.map.drawmeridians( lons, labels=[False, False, False, False], linewidth=0.2, dashes=[1, 1], color='white') def _label_grid(self, lons, lats): """ Plots labels on grid lines. """ degree_sign = u'\N{DEGREE SIGN}' for label in self.labels: if label in self.ax.texts: label.remove() self.labels = [] if self.grid_labels_enabled: # peg the corners on the projection boundaries lonll = max(self.lonll, self.map.llcrnrlon) lonur = min(self.lonur, self.map.urcrnrlon) latll = max(self.latll, self.map.llcrnrlat) latur = min(self.latur, self.map.urcrnrlat) for lat in lats: if lat > latur or lat < latll: continue if lat >= 0: NS = 'N' else: NS = 'S' (d, m, sd) = self._to_dms(lat) self.labels.append( self.ax.text(lonll, lat, "%02d%s%02d'%05.2f\"%s" % (abs(d), degree_sign, m, sd, NS), fontproperties=self.grid_label_font, verticalalignment='center', horizontalalignment='left', bbox=dict(facecolor='white', alpha=0.5, pad=0.2, lw=0.2))) for lon in lons: if lon > lonur or lon < lonll: continue # normalize display_lon = self.wrap_lon_at_day_boundary(lon) if display_lon >= 0: NS = 'E' else: NS = 'W' (d, m, sd) = self._to_dms(display_lon) self.labels.append( self.ax.text(lon, latll, "%03d%s%02d'%05.2f\"%s" % (abs(d), degree_sign, m, sd, NS), fontproperties=self.grid_label_font, rotation='vertical', verticalalignment='bottom', horizontalalignment='center', bbox=dict(facecolor='white', alpha=0.5, pad=0.2, lw=0.2))) def _to_dms(self, deg): """ Converts degrees to degrees minutes seconds """ d = int(deg) md = abs(deg - d) * 60.0 m = int(md) sd = (md - m) * 60.0 return [d, m, sd] def _scale_grid_by_twos(self, n_target, n_current): """ Determines number of grid lines to plot via hopping by powers of 2 """ half_n_current = int(n_current / 2.0) + 1 twice_n_current = round(n_current * 2) - 1 if n_target < n_current and n_target > 1: while n_target < n_current: n_current = half_n_current half_n_current = int(n_current / 2.0) + 1 if n_current < 3: break else: while n_target >= twice_n_current: n_current = twice_n_current twice_n_current = round(n_current * 2) - 1 # should be an odd number at this point, but make sure if n_current % 2 == 0: n_current += 1 return n_current def _zoom_is_valid(self): """ Checks if zoom level is valid """ # check if corners are valid try: delta_lon = self.lonur - self.lonll delta_lat = self.latur - self.latll except: return False return True def _clear_meridians_and_parallels(self): """ Clears currently plotted meridians and parallels """ # clear old meridians and parallels # these are made up of a solid black line, and a dashed white line for par in self.parallels_w: for item in self.parallels_w[par]: if len(item) > 0: if item[0] in self.ax.lines: item[0].remove() self.parallels_w.clear() for par in self.parallels_b: for item in self.parallels_b[par]: if len(item) > 0: if item[0] in self.ax.lines: item[0].remove() self.parallels_b.clear() for mer in self.meridians_w: for item in self.meridians_w[mer]: if len(item) > 0: if item[0] in self.ax.lines: item[0].remove() self.meridians_w.clear() for mer in self.meridians_b: for item in self.meridians_b[mer]: if len(item) > 0: if item[0] in self.ax.lines: item[0].remove() self.meridians_b.clear()
class MyPlot(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self,parent, -1) self.fig = None self.canvas = None self.ax = None self.background = None self.lines = [] self._doRePlot = True self.foo = 1 self.t = time.time() self.blit_time=0 self.y = numpy.cos(numpy.arange(0.0,1.0,0.1)) self.ylim = None self.autolim = None self.span = 500 self.begin = 0 self.channels = [] self._SetSize() self.Bind(wx.EVT_IDLE, self._onIdle) self.Bind(wx.EVT_SIZE, self._onSize) self._resizeFlag = True sizer=wx.BoxSizer(wx.VERTICAL) sizer.Add(self.canvas,1,wx.GROW) self.SetSizer(sizer) self.canvas.Show() def addChannel(self, channel): self.channels.append(channel) def setTimespan(self, span): self.span = span def setYlim(self, ymin, ymax): self.ylim = [ymin, ymax] def _resizeCreateContent(self): '''Resize graph according to user input and initialize plots''' self.lines=[] for c in self.channels: data=c.getNext() line, = self.ax.plot(data[0],data[1], animated = True) self.lines.append(line) gca = self.fig.gca() #TODO: add an auto mode here if self.ylim: gca.set_ylim(self.ylim) else: if self.autolim: diff = self.autolim[1] - self.autolim[0] gca.set_ylim([self.autolim[0] - 0.1*diff, self.autolim[1] + 0.1*diff]) else: gca.set_ylim([-1,1]) gca.set_xlim([self.begin, (self.begin+self.span)]) self.ax.grid() #self.fig.clear() self.canvas.draw() self.background = None print 'content' self._doRePlot = False def _createGraphics(self): """Reallocate new figure and take care of panel resizing issues""" self.fig=Figure() self.canvas=FigureCanvas(self,-1,self.fig) self.ax = self.fig.add_subplot(111) self.ax._cachedRenderer=self.canvas.get_renderer() def _onSize(self, evt): self._resizeFlag = True def _onIdle(self, event): event.RequestMore(True) if self._resizeFlag: self._resizeFlag = False self._SetSize() self.draw_plot() #if self.foo > 2000: #u=time.time() #print self.foo/(u-self.t), self.blit_time/(u-self.t) #exit(0) def _SetSize(self, pixels=None): if not pixels: pixels = self.GetClientSize() self._createGraphics() self.canvas.SetSize(pixels) self.fig.set_size_inches(pixels[0]/self.fig.get_dpi(), pixels[1]/self.fig.get_dpi(), forward=True) self._doRePlot = True def draw_plot(self): if self._doRePlot: self._resizeCreateContent() if self.background is None: self.background = self.canvas.copy_from_bbox(self.ax.bbox) self.foo += 1 #self.y = numpy.cos(numpy.arange(0.0,1.0,0.1)+self.foo*0.1) # Optimization on the blitting: we compute the box where the changes happen changes_box = None for i in range(len(self.lines)): data=self.channels[i].getNext() if len(data[1])>0: if self.autolim: print self.autolim[0], data[1], self.autolim[1] self.autolim = [ min(self.autolim[0], min(data[1])), \ max(self.autolim[1], max(data[1])) ] else: self.autolim = [ min(data[1]), min(data[1]) ] if changes_box is None: changes_box = Bbox.unit() print '>>>>>>>>' print data[0], data[1] changes_box.update_from_data(numpy.array(data[0]), \ numpy.array(data[1]), ignore=changes_box.is_unit()) if not self._doRePlot and len(data[0]) > 0 : end = data[0][-1] if end > self.begin+self.span: self.begin += self.span self._doRePlot = True print 'do replot' self.lines[i].set_data(data[0], data[1]) else: self.lines[i].set_data([], []) if not changes_box: return #self.canvas.restore_region(self.background) for line in self.lines: self.ax.draw_artist(line) #print line.get_transform() tr = line.get_transform() changes_box_inframe = changes_box.transformed(tr) box_padding = 5 (x,y,l,w) = changes_box_inframe.bounds changes_box_inframe = Bbox.from_bounds(x-box_padding, \ y-box_padding, l+2*box_padding, w+2*box_padding) #print t0 = time.time() self.canvas.blit(None) #self.canvas.blit(changes_box_inframe) self.blit_time += time.time() - t0
class wxpygui_frame(wx.Frame): """The main gui frame.""" def __init__(self, tb): wx.Frame.__init__(self, parent=None, id=-1, title="gr-analyzer") self.tb = tb self.min_power = -120 # dBm self.max_power = 0 # dBm self.init_mpl_canvas() self.x = None # set by configure_mpl_plot # Setup a threshold level at None self.threshold = threshold.threshold(self, None) # Init markers (visible=False) self.mkr1 = marker.marker(self, 1, '#00FF00', 'd') # thin green diamond self.mkr2 = marker.marker(self, 2, '#00FF00', 'd') # thin green diamond # init control boxes self.gain_ctrls = gain.ctrls(self) self.threshold_ctrls = threshold.ctrls(self) self.mkr1_ctrls = marker.mkr1_ctrls(self) self.mkr2_ctrls = marker.mkr2_ctrls(self) self.res_ctrls = resolution.ctrls(self) self.windowfn_ctrls = window.ctrls(self) self.lo_offset_ctrls = lotuning.ctrls(self) self.nframes_ctrls = nframes.ctrls(self) self.tune_delay_ctrls = tune_delay.ctrls(self) self.frequency_ctrls = frequency.ctrls(self) self.span_ctrls = span.ctrls(self) self.trigger_ctrls = trigger.ctrls(self) self.power_ctrls = power.ctrls(self) self.export_ctrls = export.ctrls(self) self.detector_ctrls = detector.ctrls(self) self.scale_ctrls = scale.ctrls(self) self.set_layout() self.logger = logging.getLogger('gr-analyzer.wxpygui_frame') # gui event handlers self.Bind(wx.EVT_CLOSE, self.close) self.Bind(wx.EVT_IDLE, self.idle_notifier) self.canvas.mpl_connect('button_press_event', self.on_mousedown) self.canvas.mpl_connect('button_release_event', self.on_mouseup) self.plot_background = None # Used to peak search within range self.span = None # the actual matplotlib patch self.span_left = None # left bound x coordinate self.span_right = None # right bound x coordinate self.last_click_evt = None self.closed = False # Used to increment file numbers self.fft_data_export_counter = 0 self.time_data_export_counter = 0 #################### # GUI Sizers/Layout #################### def set_layout(self): """Setup frame layout and sizers""" # front panel to hold plot and control stack side-by-side frontpanel = wx.BoxSizer(wx.HORIZONTAL) # control stack to hold control clusters vertically controlstack = wx.BoxSizer(wx.VERTICAL) # first cluster - usrp state usrpstate_outline = wx.StaticBox(self, wx.ID_ANY, "USRP State") usrpstate_cluster = wx.StaticBoxSizer(usrpstate_outline, wx.HORIZONTAL) usrpstate_row1 = wx.BoxSizer(wx.HORIZONTAL) usrpstate_row1.Add(self.trigger_ctrls.layout, flag=wx.ALL, border=5) usrpstate_row1.Add(self.detector_ctrls.layout, flag=wx.ALL, border=5) usrpstate_row1.Add(self.gain_ctrls.layout, flag=wx.ALL, border=5) usrpstate_row1.Add(self.lo_offset_ctrls.layout, flag=wx.ALL, border=5) usrpstate_row2 = wx.BoxSizer(wx.HORIZONTAL) usrpstate_row2.Add( self.frequency_ctrls.layout, proportion=1, flag=wx.ALL, #|wx.EXPAND, border=5) usrpstate_row2.Add( self.span_ctrls.layout, proportion=1, flag=wx.ALL, #|wx.EXPAND, border=5) usrpstate_row2.Add( self.scale_ctrls.layout, proportion=1, flag=wx.ALL, #|wx.EXPAND, border=5) usrpstate_col1 = wx.BoxSizer(wx.VERTICAL) usrpstate_col1.Add(usrpstate_row1) usrpstate_col1.Add(usrpstate_row2, flag=wx.EXPAND) usrpstate_col2 = wx.BoxSizer(wx.VERTICAL) # col 1 usrpstate_cluster.Add(usrpstate_col1) # col 2 usrpstate_cluster.Add(usrpstate_col2) # second cluster - display controls display_outline = wx.StaticBox(self, wx.ID_ANY, "Display") display_cluster = wx.StaticBoxSizer(display_outline, wx.HORIZONTAL) nframesbox = wx.BoxSizer(wx.HORIZONTAL) nframesbox.Add(self.nframes_ctrls.layout, proportion=1, flag=wx.ALL, border=5) nframesbox.Add(self.tune_delay_ctrls.layout, proportion=1, flag=wx.ALL, border=5) display_col1 = wx.BoxSizer(wx.VERTICAL) display_col1.Add(self.res_ctrls.layout, flag=wx.ALL, border=5) display_col1.Add(nframesbox, flag=wx.EXPAND) display_col2 = wx.BoxSizer(wx.VERTICAL) display_col2.Add(self.windowfn_ctrls.layout, flag=wx.ALL, border=5) display_col2.Add(self.power_ctrls.layout, flag=wx.ALL | wx.EXPAND, border=5) # col 1 display_cluster.Add(display_col1) # col 2 display_cluster.Add(display_col2) # third cluster - data controls data_outline = wx.StaticBox(self, wx.ID_ANY, "Data") data_cluster = wx.StaticBoxSizer(data_outline, wx.HORIZONTAL) data_col3 = wx.BoxSizer(wx.VERTICAL) data_col3.Add(self.threshold_ctrls.layout) data_col3.Add(self.export_ctrls.layout) # col 1 data_cluster.Add(self.mkr1_ctrls.layout, flag=wx.ALL, border=5) # col 2 data_cluster.Add(self.mkr2_ctrls.layout, flag=wx.ALL, border=5) # col 3 data_cluster.Add(data_col3, flag=wx.ALL, border=5) # put everything together # Add control clusters vertically to control stack controlstack.Add(usrpstate_cluster, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5) controlstack.Add(display_cluster, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5) controlstack.Add(data_cluster, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5) # Add plot and control stack side-by-side on the front panel frontpanel.Add(self.plot, flag=wx.ALIGN_CENTER_VERTICAL) frontpanel.Add(controlstack, flag=wx.ALIGN_CENTER_VERTICAL) self.SetSizer(frontpanel) self.Fit() #################### # GUI Initialization #################### def init_mpl_canvas(self): """Initialize a matplotlib plot.""" self.plot = wx.Panel(self, wx.ID_ANY, size=(700, 600)) self.figure = Figure(figsize=(7, 6), dpi=100) self.figure.subplots_adjust(right=.95) self.canvas = FigureCanvas(self.plot, -1, self.figure) def configure_mpl_plot(self, y, adjust_freq_range=True): """Configure or reconfigure the matplotlib plot""" maxbin = self.tb.cfg.max_plotted_bin self.x = self.tb.cfg.bin_freqs[:maxbin] # self.line in a numpy array in the form [[x-vals], [y-vals]], where # x-vals are bin center frequencies and y-vals are powers. So once we # initialize a power at each freq, just find the index of the # frequency that a measurement was taken at, and insert it into the # corresponding index in y-vals. if adjust_freq_range: len_x = len(self.x) len_y = len(y) if len_x != len_y: # There's a race condition when in continuous mode and # a frequency range-adjusting parameter (like span) is # changed, so we sometimes get updated x-values before # updated y-values. Since a) it only affects # continuous mode and b) the user has requested a # different view, there's no harm in simply dropping # the old data and re-calling configure_mpl_plot next frame. # Still - this is a workaround. # The most "correct" solution would be to have # controller_c tag the first sample propagated after # flowgraph starts, which plotter_f would look for and # use to trigger plot reconfig. self.logger.debug("data mismatch - frame dropped") return False if hasattr(self, 'mkr1'): self.mkr1.unplot() if hasattr(self, 'mkr2'): self.mkr2.unplot() if hasattr(self, 'line'): self.line.remove() # initialize a line self.line, = self.subplot.plot(self.x, y, animated=True, antialiased=True, linestyle='-', color='b') self.canvas.draw() self._update_background() return True def format_axis(self): """Set the formatting of the plot axes.""" if hasattr(self, "subplot"): ax = self.subplot else: ax = self.figure.add_subplot(111) xaxis_formatter = FuncFormatter(self.format_mhz) ax.xaxis.set_major_formatter(xaxis_formatter) ax.set_xlabel("Frequency (MHz)") ax.set_ylabel("Power (dBm)") cf = self.tb.cfg.center_freq lowest_xtick = cf - (self.tb.cfg.span / 2) highest_xtick = cf + (self.tb.cfg.span / 2) ax.set_xlim(lowest_xtick - 1e6, highest_xtick + 1e6) ax.set_ylim(self.min_power + 1, self.max_power - 1) xticks = np.linspace(lowest_xtick, highest_xtick, 5, endpoint=True) ax.set_xticks(xticks) ax.set_yticks(np.arange(self.min_power, self.max_power, 10)) ax.grid(color='.90', linestyle='-', linewidth=1) ax.set_title("Power Spectrum") self.subplot = ax self.canvas.draw() self._update_background() @staticmethod def format_mhz(x, pos): """Format x ticks (in Hz) to MHz with 0 decimal places.""" return "{:.1f}".format(x / float(1e6)) #################### # Plotting functions #################### def update_plot(self, y, redraw_plot, keep_alive): """Update the plot.""" if redraw_plot: #assert not keep_alive self.logger.debug("Reconfiguring matplotlib plot") self.format_axis() if not self.configure_mpl_plot(y): # Got bad data, try again next frame self.tb.plot_iface.redraw_plot.set() return # Required for plot blitting self.canvas.restore_region(self.plot_background) if keep_alive: # Just keep markers and span alive after single run y = self.line.get_ydata() self.subplot.draw_artist(self.line) else: self._draw_line(y) self._check_threshold(y) self._draw_span() self._draw_threshold() self._draw_markers(y) # blit canvas self.canvas.blit(self.subplot.bbox) def _update_background(self): """Force update of the plot background.""" self.plot_background = self.canvas.copy_from_bbox(self.subplot.bbox) def _draw_span(self): """Draw a span to bound the peak search functionality.""" if self.span is not None: self.subplot.draw_artist(self.span) def _draw_threshold(self): """Draw a span to bound the peak search functionality.""" if self.threshold.line is not None: self.subplot.draw_artist(self.threshold.line) def _draw_line(self, y): """Draw the latest chunk of line data.""" self.line.set_ydata(y) self.subplot.draw_artist(self.line) def _draw_markers(self, y): """Draw power markers at a specific frequency.""" # Update mkr1 if it's set if self.mkr1.freq is not None: m1bin = self.mkr1.bin_idx mkr1_power = y[m1bin] self.mkr1.point.set_ydata(mkr1_power) self.mkr1.point.set_visible(True) # make visible self.mkr1.text_label.set_visible(True) self.mkr1.text_power.set_text("{:.1f} dBm".format(mkr1_power)) self.mkr1.text_power.set_visible(True) # redraw self.subplot.draw_artist(self.mkr1.point) self.figure.draw_artist(self.mkr1.text_label) self.figure.draw_artist(self.mkr1.text_power) # Update mkr2 if it's set if self.mkr2.freq is not None: m2bin = self.mkr2.bin_idx mkr2_power = y[m2bin] self.mkr2.point.set_ydata(mkr2_power) self.mkr2.point.set_visible(True) # make visible self.mkr2.text_label.set_visible(True) self.mkr2.text_power.set_text("{:.2f} dBm".format(mkr2_power)) self.mkr2.text_power.set_visible(True) # Redraw self.subplot.draw_artist(self.mkr2.point) self.figure.draw_artist(self.mkr2.text_label) self.figure.draw_artist(self.mkr2.text_power) def _check_threshold(self, y): """Warn to stdout if the threshold level has been crossed.""" # Update threshold # indices of where the y-value is greater than self.threshold.level if self.threshold.level is not None: overloads, = np.where(y > self.threshold.level) if overloads.size: # is > 0 self.log_threshold_overloads(overloads, y) def log_threshold_overloads(self, overloads, y): """Outout threshold violations to the logging system.""" logheader = "============= Overload at {} =============" self.logger.warning(logheader.format(int(time.time()))) logmsg = "Exceeded threshold {0:.0f}dBm ({1:.2f}dBm) at {2:.2f}MHz" for i in overloads: self.logger.warning( logmsg.format(self.threshold.level, y[i], self.x[i] / 1e6)) ################ # Event handlers ################ def on_mousedown(self, event): """store event info for single click.""" self.last_click_evt = event def on_mouseup(self, event): """Determine if mouse event was single click or click-and-drag.""" if abs(self.last_click_evt.x - event.x) >= 5: # mouse was clicked and dragged more than 5 pxls, set a span self.span = self.subplot.axvspan( self.last_click_evt.xdata, event.xdata, color='red', alpha=0.2, # play nice with blitting: animated=True) xdata_points = [self.last_click_evt.xdata, event.xdata] # always set left bound as lower value self.span_left, self.span_right = sorted(xdata_points) else: # caught single click, clear span if self.subplot.patches: self.span.remove() self.subplot.patches = [] self.span = self.span_left = self.span_right = None def idle_notifier(self, event): self.tb.plot_iface.set_gui_idle() def set_continuous_run(self, event): self.tb.pending_cfg.export_raw_time_data = False self.tb.pending_cfg.export_raw_fft_data = False self.tb.pending_cfg.continuous_run = True self.tb.set_continuous_run() def set_single_run(self, event): self.tb.pending_cfg.continuous_run = False self.tb.set_single_run() @staticmethod def _verify_data_dir(dir): if not os.path.exists(dir): os.makedirs(dir) def export_time_data(self, event): if (self.tb.single_run.is_set() or self.tb.continuous_run.is_set()): msg = "Can't export data while the flowgraph is running." msg += " Use \"single\" run mode." self.logger.error(msg) return else: if not self.tb.timedata_sink.data(): self.logger.warn("No more time data to export") return # creates path string 'data/time_data_01_TIMESTAMP.dat' dirname = "data" self._verify_data_dir(dirname) fname = str.join( '', ('time_data_', str(self.time_data_export_counter).zfill(2), '_', str(int(time.time())), '.dat')) wildcard = "Data and Settings files (*.dat; *.mat)|*.dat;*.mat" style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT filepath_dialog = wx.FileDialog(self, message="Save As", defaultDir=dirname, defaultFile=fname, wildcard=wildcard, style=style) if filepath_dialog.ShowModal() == wx.ID_CANCEL: return self.time_data_export_counter += 1 filepath_dialog.Destroy() self.tb.save_time_data_to_file(filepath_dialog.GetPath()) def export_fft_data(self, event): if self.tb.single_run.is_set() or self.tb.continuous_run.is_set(): msg = "Can't export data while the flowgraph is running." msg += " Use \"single\" run mode." self.logger.error(msg) return else: if not self.tb.freqdata_sink.data(): self.logger.warn("No more FFT data to export") return False # creates path string 'data/fft_data_01_TIMESTAMP.dat' dirname = "data" self._verify_data_dir(dirname) fname = str.join( '', ('fft_data_', str(self.fft_data_export_counter).zfill(2), '_', str(int(time.time())), '.dat')) wildcard = "Data and Settings files (*.dat; *.mat)|*.dat;*.mat" style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT filepath_dialog = wx.FileDialog(self, message="Save As", defaultDir=dirname, defaultFile=fname, wildcard=wildcard, style=style) if filepath_dialog.ShowModal() == wx.ID_CANCEL: return self.fft_data_export_counter += 1 filepath_dialog.Destroy() self.tb.save_freq_data_to_file(filepath_dialog.GetPath()) def close(self, event): """Handle a closed gui window.""" self.closed = True self.tb.stop() self.tb.wait() self.Destroy() self.logger.debug("GUI closing.")
class Frame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1) self.SetTitle('Blitting Example') # Figure layout. self.fig = Figure((5,4), 100) self.canvas = FigureCanvas(self, -1, self.fig) # BEGIN: Widget layout. self.wx_widgets = [] sizer = wx.BoxSizer() sizer.Add(self.canvas, proportion=1, flag=wx.EXPAND) self.SetSizer(sizer) self.Fit() # END: Widget layout. # BEGIN: Plot initialization. self.axes = self.fig.add_subplot(111) self.line1_ydata = [numpy.random.normal()] self.line2_ydata = [numpy.random.normal()] self.line1, = self.axes.plot(self.line1_ydata, color='b', animated=BLIT) self.line2, = self.axes.plot(self.line2_ydata, color='r', animated=BLIT) self.fps_label = self.axes.annotate("FPS: 0", (0.01, 0.95), xycoords="axes fraction", animated=BLIT) self.xaxis = self.axes.get_xaxis() self.yaxis = self.axes.get_yaxis() # END: Plot initialization. # BEGIN: Timers # IMPORTANT: Use wx.EVT_TIMER for redraw to avoid resource conflicts/crashes. self.redraw_timer = wx.Timer(self, wx.NewId()) # Plot refresh timer. self.redraw_timer.Start(50) wx.EVT_TIMER(self, self.redraw_timer.GetId(), self.redraw) self.frames = 0 self.start = time.time() # END: Timers ############################### # BEGIN: Plot related methods # ############################### def redraw(self, evt): # update the ydata, normally this would be done by SyncDB y1 = self.line1_ydata[-1] + numpy.random.normal() y2 = self.line2_ydata[-1] + numpy.random.normal() self.line1_ydata.append(y1) self.line2_ydata.append(y2) self.line1.set_data(range(len(self.line1_ydata)), self.line1_ydata) self.line2.set_data(range(len(self.line2_ydata)), self.line2_ydata) self.axes.set_ylim(min(self.line1_ydata + self.line2_ydata), max(self.line1_ydata + self.line2_ydata)) self.axes.set_xlim(max(0, len(self.line1_ydata)-100), max(100, len(self.line1_ydata))) self.fps_label.set_text("FPS: %.2f" % (self.frames / float(time.time() - self.start))) if BLIT: self.fig.draw_artist(self.fig) self.axes.draw_artist(self.line1) self.axes.draw_artist(self.line2) self.axes.draw_artist(self.fps_label) self.fig.draw_artist(self.xaxis) self.fig.draw_artist(self.yaxis) self.canvas.blit(self.fig.bbox) else: self.canvas.draw() self.frames += 1
class PlotFigure(wx.Frame): """Matplotlib wxFrame with animation effect""" def __init__(self): # initialize the super class wx.Frame.__init__(self, None, wx.ID_ANY, title="CPU Usage Monitor", size=(600, 400)) # Matplotlib Figure self.fig = Figure((6, 4), 100) # bind the Figure to the backend specific canvas self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) # add a subplot self.ax = self.fig.add_subplot(111) # limit the X and Y axes dimensions # we prefer 2 separate functions for clarity self.ax.set_ylim([0, 100]) self.ax.set_xlim([0, POINTS]) # but we want a "frozen" window (defined by y/xlim functions) self.ax.set_autoscale_on(False) # we do not want ticks on X axis self.ax.set_xticks([]) # we want a tick every 10 point on Y (101 is to have 100 too) self.ax.set_yticks(range(0, 101, 10)) # disable autoscale, since we don't want the Axes to adapt # draw a grid (it will be only for Y) self.ax.grid(True) # generates first "empty" plots self.user = [None] * POINTS self.nice = [None] * POINTS self.sys = [None] * POINTS self.idle = [None] * POINTS self.l_user, = self.ax.plot(range(POINTS), self.user, label='User%') self.l_nice, = self.ax.plot(range(POINTS), self.nice, label='Nice%') self.l_sys, = self.ax.plot(range(POINTS), self.sys, label='Sys%') self.l_idle, = self.ax.plot(range(POINTS), self.idle, label='Idle%') # add the legend self.ax.legend(loc='upper center', ncol=4, prop=font_manager.FontProperties(size=10)) # force a draw on the canvas() # trick to show the grid and the legend self.canvas.draw() # save the clean background - everything but the line # is drawn and saved in the pixel buffer background self.bg = self.canvas.copy_from_bbox(self.ax.bbox) # take a snapshot of CPU usage, needed for the update algorithm self.before = self.prepare_cpu_usage() # bind events coming from timer with id = TIMER_ID # to the onTimer callback function wx.EVT_TIMER(self, TIMER_ID, self.onTimer) def prepare_cpu_usage(self): """helper function to return CPU usage info""" # get the CPU times using psutil module t = p.cpu_times() # return only the values we're interested in if hasattr(t, 'nice'): return [t.user, t.nice, t.system, t.idle] else: # special case for Windows, without 'nice' value return [t.user, 0, t.system, t.idle] def get_cpu_usage(self): """Compute CPU usage comparing previous and currentmeasurements""" # take the current CPU usage information now = self.prepare_cpu_usage() # compute deltas between current and previous measurements delta = [now[i] - self.before[i] for i in range(len(now))] # compute the total (needed for percentages calculation) total = sum(delta) # save the current measurement to before object self.before = now # return the percentage of CPU usage for our 4 categories return [(100.0 * dt) / total for dt in delta] def onTimer(self, evt): """callback function for timer events""" # get the CPU usage information tmp = self.get_cpu_usage() # restore the clean background, saved at the beginning self.canvas.restore_region(self.bg) # update the data self.user = self.user[1:] + [tmp[0]] self.nice = self.nice[1:] + [tmp[1]] self.sys = self.sys[1:] + [tmp[2]] self.idle = self.idle[1:] + [tmp[3]] # update the plot self.l_user.set_ydata(self.user) self.l_nice.set_ydata(self.nice) self.l_sys.set_ydata(self.sys) self.l_idle.set_ydata(self.idle) # just draw the "animated" objects self.ax.draw_artist(self.l_user) self.ax.draw_artist(self.l_nice) self.ax.draw_artist(self.l_sys) self.ax.draw_artist(self.l_idle) # "blit" the background with the animated lines self.canvas.blit(self.ax.bbox)
class WxLineScatterWidget(wx.Panel): axis_width = 20 axis_offset = 1.05 def __init__(self, *args, **kwargs): kwargs['style'] = kwargs.setdefault( 'style', wx.NO_FULL_REPAINT_ON_RESIZE) | wx.NO_FULL_REPAINT_ON_RESIZE wx.Panel.__init__(self, *args, **kwargs) self.id = wx.NewId() self.plot1 = 'none' self.plot2 = 'none' self.plot3 = 'none' self.lineprops1 = wxLineProps({'color': '#990000', 'fill': True}) self.lineprops2 = wxLineProps({'color': '#009900', 'fill': True}) self.lineprops3 = wxLineProps({'color': '#000099', 'fill': True}) self.autoy1 = True self.autoy2 = True self.autoy3 = True self.smooth1 = 1 self.smooth2 = 1 self.smooth3 = 1 self.xaxis = '' self.press = False self.cursor = None self.span = None self.selstart = 0 self.selstop = 0 self.enablecursor = True self.enablespan = True self.cursorcolor = '#FF0000' self.cursorwidth = 1 self.gpxfig = Figure() self.ax1 = self.gpxfig.add_subplot( 1, 1, 1 ) # create a grid of 1 row, 1 col and put a subplot in the first cell of this grid self.gpxfig.subplots_adjust(right=0.9, left=0.06) self.ax2 = self.ax1.twinx() #self.ax2.spines["left"].set_visible(False) self.ax3 = self.ax1.twinx() self.ax3.spines["right"].set_position(("axes", self.axis_offset)) #self.ax3.spines["left"].set_visible(False) # canvas and events self.gpxcanvas = FigureCanvas(self, -1, self.gpxfig) self.gpxcanvas.mpl_connect('scroll_event', self.OnMouseWheel) self.gpxcanvas.mpl_connect('button_press_event', self.OnLeftMouseDown) self.gpxcanvas.mpl_connect('button_release_event', self.OnLeftMouseUp) self.gpxcanvas.mpl_connect('motion_notify_event', self.OnMouseMotion) self.gpxcanvas.mpl_connect('resize_event', self.OnSize) self.gpxcanvas.mpl_connect('figure_enter_event', self.OnMouseEnter) self.gpxcanvas.mpl_connect('figure_leave_event', self.OnMouseLeave) self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightMouseDown) self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(self.gpxcanvas, 1, wx.LEFT | wx.TOP | wx.GROW | wx.EXPAND) self.SetSizer(self.sizer) #self.OnSize(None) msgwrap.register(self.OnSigCurChanged, signal="CurChanged") msgwrap.register(self.OnSigSelChanged, signal="SelChanged") msgwrap.register(self.OnSigValChanged, signal="ValChanged") #set background color to pure white #that code does not work on linux... #color = wx.SystemSettings.GetColour(wx.wx.SYS_COLOUR_BTNFACE) color = wx.Colour(255, 255, 255) self.gpxfig.set_facecolor( (color.red / 255.0, color.green / 255.0, color.blue / 255.0)) self.gpxfig.set_edgecolor( (color.red / 255.0, color.green / 255.0, color.blue / 255.0)) self.gpxfig.set_edgecolor((0.0, 0.0, 0.0)) self.gpxcanvas.SetBackgroundColour(color) # create right now the popup menu self.select_menu = wx.Menu() for text in ["Disable selected",\ "Enable selected",\ "Delete selected",\ "Disable non selected",\ "Enable non selected",\ "Delete non selected",\ "Toggle points"]: item = self.select_menu.Append(wx.NewId(), text) self.Bind(wx.EVT_MENU, self.OnPopup, item) def x_to_num(self, value, scaled=True): if self.xaxis == 'time': return dates.date2num(dateutil.parser.parse(value)) else: if scaled: #return float(value)/self.gpx.get_scale(self.xaxis) return float(value) * self.gpx.get_scale(self.xaxis) else: return float(value) def num_to_x(self, value, scaled=True): if self.xaxis == 'time': return dates.num2date(value) else: if scaled: #return value*self.gpx.get_scale(self.xaxis) return value / self.gpx.get_scale(self.xaxis) else: return value def x_max(self): if self.xaxis == 'time': return self.x_to_num( self.gpx[self.xaxis][self.gpx.get_row_count() - 1]) else: return self.x_to_num(np.nanmax(self.gpx[self.xaxis])) def x_min(self): if self.xaxis == 'time': return self.x_to_num(self.gpx[self.xaxis][0]) else: return self.x_to_num(np.nanmin(self.gpx[self.xaxis])) def format_x_axis(self): if self.xaxis == 'time': xlo = self.ax1.get_xlim()[0] xhi = self.ax1.get_xlim()[1] if (xhi - xlo) > 0.003: self.ax1.xaxis.set_major_formatter( dates.DateFormatter("%H:%M")) else: self.ax1.xaxis.set_major_formatter( dates.DateFormatter("%H:%M:%S")) self.ax1.set_xlabel('Time (HH:MM:SS)') else: #self.ax1.set_xlabel('Distance (m)') self.ax1.set_xlabel(self.xaxis + " (" + self.gpx.get_unit(self.xaxis)[0] + ")") #self.ax1.xaxis.set_major_formatter(mpl.ticker.FormatStrFormatter('%.0f') ) self.ax1.xaxis.set_major_formatter(mpl.ticker.ScalarFormatter()) pass def get_axis(self, event, tolerance): bbox = self.ax1.get_window_extent().transformed( self.gpxfig.dpi_scale_trans.inverted()) l = bbox.bounds[0] * self.gpxfig.dpi b = bbox.bounds[1] * self.gpxfig.dpi r = l + bbox.bounds[2] * self.gpxfig.dpi t = b + bbox.bounds[3] * self.gpxfig.dpi #convert screen coordinates to graph coordinates xlo = self.ax1.get_xlim()[0] xhi = self.ax1.get_xlim()[1] event.xdata = (event.x - l) / (r - l) * (xhi - xlo) + xlo if ptinrect(l - tolerance, t, l, b, event.x, event.y): ylo, yhi = self.ax1.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return 'left' if ptinrect(r, t, r + tolerance, b, event.x, event.y): ylo, yhi = self.ax2.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return 'right' if ptinrect(l, t, r, t + tolerance, event.x, event.y): ylo, yhi = self.ax1.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return 'top' if ptinrect(l, b - tolerance, r, b, event.x, event.y): ylo, yhi = self.ax1.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return 'bottom' #we need a small adjustment here, but this hack gives good results if ptinrect(r * self.axis_offset * 0.985, t, r * self.axis_offset * 0.985 + tolerance, b, event.x, event.y): ylo, yhi = self.ax3.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return "3rd" if ptinrect(l, t, r, b, event.x, event.y): ylo, yhi = self.ax1.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return 'main' def update_axis(self, ax, plot, ylo, yhi, yauto, lineprops, smooth): if plot != 'none': ## process data!! N = smooth #data=(1.0)*np.convolve(self.gpx[plot]*self.gpx.scale[plot], np.ones((N,))/N)[(N-1):] data = (1.0) * np.convolve(self.gpx[(plot, True)], np.ones((N, )) / N)[(N - 1):] data[self.gpx['ok'] == False] = np.NAN ##end of data processing #remove fill_between collection for coll in ax.collections: ax.collections.remove(coll) #need to rebuild dates array in case something was deleted self.xvalues = [] for x in self.gpx[self.xaxis]: self.xvalues.append(self.x_to_num(x)) ax.get_lines()[0].set_data(self.xvalues, data) self.format_x_axis() if lineprops['fill']: ax.fill_between(self.xvalues, 0, data, facecolor=lineprops['color'], alpha=0.2) ax.get_lines()[0].set_color(lineprops['color']) ax.get_lines()[0].set_linewidth(lineprops['linewidth']) ax.get_lines()[0].set_marker(lineprops['marker']) ax.get_lines()[0].set_markersize(lineprops['markersize']) ax.set_autoscaley_on(yauto) ##now using legends instead of labels #ax.set_ylabel(plot+" ("+str(self.gpx.get_unit(plot)[0])+")") #ax.yaxis.label.set_color(lineprops['color']) lines = self.line1 + self.line2 + self.line3 labs = [ p for p in [self.plot1, self.plot2, self.plot3] if p != 'none' ] self.ax1.legend( lines, labs, loc='best' ) #,bbox_to_anchor=(0.5, 1.3), ncol=3, fancybox=False, shadow=False) if not yauto: ax.set_ylim(ylo, yhi) else: ax.set_ylim(np.min(self.gpx[plot] * self.gpx.scale[plot]), np.max(self.gpx[plot] * self.gpx.scale[plot])) ax.set_visible(True) for tick in ax.get_yticklabels(): tick.set_color(lineprops['color']) ax.spines["right"].set_edgecolor(lineprops['color']) ax.tick_params(axis='y', colors=lineprops['color']) else: ax.get_lines()[0].set_data(self.xvalues, np.zeros(self.gpx.get_row_count())) ax.set_visible(False) self.cursor.set_color(self.cursorcolor) self.cursor.set_linewidth(self.cursorwidth) self.format_x_axis() self.Draw(False) self.OnSize(None) def AttachGpx(self, data): self.gpx = data self.xvalues = [] self.xaxis = self.gpx.get_header_names()[0] for x in self.gpx[self.xaxis]: self.xvalues.append(self.x_to_num(x)) self.ax1.set_xlabel('') self.line1 = self.ax1.plot(self.xvalues, np.zeros(self.gpx.get_row_count()), picker=5, label='ax1') self.line2 = self.ax2.plot(self.xvalues, np.zeros(self.gpx.get_row_count()), picker=5, label='ax2') self.line3 = self.ax3.plot(self.xvalues, np.zeros(self.gpx.get_row_count()), picker=5, label='ax3') xlo = self.x_to_num(self.gpx[self.xaxis][0]) xhi = self.x_to_num(self.gpx[self.xaxis][self.gpx.get_row_count() - 1]) if xlo != xhi: self.ax1.set_xlim([xlo, xhi]) if self.enablecursor == True: self.cursor = self.ax1.axvline(color='r', animated=True) mid = (self.ax1.get_xlim()[0] + self.ax1.get_xlim()[1]) / 2 self.cursor.set_xdata(mid) #self.cursor.set_color('k') #self.cursor.set_linewidth(4) if self.enablespan == True: self.span = patches.Rectangle( (self.ax1.get_xlim()[0], 0), (self.ax1.get_xlim()[1] - self.ax1.get_xlim()[0]) / 3, 200, color='k', alpha=0.3, animated=True) self.ax1.add_patch(self.span) self.span.set_visible(False) self.SetDefaultPlots() self.OnSize(None) def DetachGpx(self): self.gpx = None self.plot1 = 'none' self.plot2 = 'none' self.plot3 = 'none' self.autoy1 = True self.autoy2 = True self.autoy3 = True self.fill1 = True self.fill2 = True self.fill3 = True self.xaxis = '' self.press = False if self.cursor != None: self.cursor.remove() self.cursor = None if self.span != None: self.span.remove() self.span = None def OnSigSelChanged(self, arg1, arg2, arg3): if arg1 == self.id: return if self.span != None: xlo = self.x_to_num(self.gpx[self.xaxis][arg2]) xhi = self.x_to_num(self.gpx[self.xaxis][arg3]) self.span.set_bounds( xlo, self.ax1.get_ylim()[0], xhi - xlo, self.ax1.get_ylim()[1] - self.ax1.get_ylim()[0]) self.span.set_visible(True) def OnSigValChanged(self, arg1): if arg1 == self.id: return self.update_axis(self.ax1, self.plot1, self.ax1.get_xlim()[0], self.ax1.get_xlim()[1], self.autoy1, self.lineprops1, self.smooth1) self.update_axis(self.ax2, self.plot2, self.ax2.get_xlim()[0], self.ax2.get_xlim()[1], self.autoy2, self.lineprops2, self.smooth2) self.update_axis(self.ax3, self.plot3, self.ax2.get_xlim()[0], self.ax2.get_xlim()[1], self.autoy3, self.lineprops3, self.smooth3) def OnSigCurChanged(self, arg1, arg2): if arg1 == self.id: return if self.gpx != None: xval = self.gpx[self.xaxis][arg2] self.gpxcanvas.restore_region(self.background) if self.cursor != None: self.cursor.set_xdata(self.x_to_num(xval)) self.ax1.draw_artist(self.cursor) if self.span != None and self.span.get_visible(): self.ax1.draw_artist(self.span) self.gpxcanvas.blit() self.UpdateStatusBar(arg2) def SetDefaultPlots(self): self.xaxis = self.gpx.get_header_names()[0] self.plot1 = self.gpx.get_header_names()[1] self.plot2 = 'none' self.plot3 = 'none' self.update_axis(self.ax1, self.plot1, 0, 1, True, self.lineprops1, self.smooth1) self.update_axis(self.ax2, self.plot2, 0, 1, True, self.lineprops2, self.smooth2) self.update_axis(self.ax3, self.plot3, 0, 1, True, self.lineprops3, self.smooth3) def XAxisAllowed(self): l = '' for name in self.gpx.get_header_names(): l += '|' + name return l[1:] def YAxisAllowed(self): l = '' for name in self.gpx.get_header_names(): l += '|' + name return l[1:] def Draw(self, blit): if blit: self.gpxcanvas.restore_region(self.background) else: self.gpxcanvas.draw() self.background = self.gpxcanvas.copy_from_bbox(self.ax1.bbox) if self.span != None and self.span.get_visible(): self.ax1.draw_artist(self.span) if self.cursor != None: self.ax1.draw_artist(self.cursor) self.gpxcanvas.blit() def OnSize(self, event): pixels = self.GetClientSize() if pixels[0] < 20 or pixels[1] < 20: return self.SetSize(pixels) self.gpxcanvas.SetSize(pixels) self.gpxfig.set_size_inches( float(pixels[0]) / self.gpxfig.get_dpi(), float(pixels[1]) / self.gpxfig.get_dpi()) leg = self.ax1.xaxis.get_tightbbox(self.gpxcanvas.get_renderer()) leg1 = self.ax1.yaxis.get_tightbbox(self.gpxcanvas.get_renderer()) leg2 = self.ax2.yaxis.get_tightbbox(self.gpxcanvas.get_renderer()) leg3 = self.ax3.yaxis.get_tightbbox(self.gpxcanvas.get_renderer( )) #leg2 and leg3 are exactly the same!! bottomalign = (leg.height + 5) / pixels[1] leftalign = (leg1.width + 5) / pixels[0] if self.plot2 == 'none' and self.plot3 == 'none': rightalign = (1 - (5.0) / pixels[0]) / self.axis_offset else: rightalign = (1 - (leg2.width + 5) / pixels[0]) / self.axis_offset if pixels[1] > 32: self.gpxfig.subplots_adjust(bottom=bottomalign) if pixels[0] > 32: self.gpxfig.subplots_adjust(left=leftalign, right=rightalign) ##PYTHON3 self.gpxfig.subplots_adjust(right=0.9, left=0.06, bottom=0.2) self.Draw(False) def OnLeftMouseDblClick(self, event): #dble click. Let's get prepared xlo = self.num_to_x(self.ax1.get_xlim()[0], False) xhi = self.num_to_x(self.ax1.get_xlim()[1], False) y1lo = self.ax1.get_ylim()[0] y1hi = self.ax1.get_ylim()[1] y2lo = self.ax2.get_ylim()[0] y2hi = self.ax2.get_ylim()[1] y3lo = self.ax3.get_ylim()[0] y3hi = self.ax3.get_ylim()[1] (dummy,xaxis,xlo,xhi,self.cursorcolor,self.cursorwidth, dummy,self.plot1,y1lo,y1hi,self.autoy1,self.smooth1, self.lineprops1['color'],self.lineprops1['linewidth'],self.lineprops1['marker'],self.lineprops1['markersize'],self.lineprops1['fill'],\ dummy,self.plot2,y2lo,y2hi,self.autoy2,self.smooth2, self.lineprops2['color'],self.lineprops2['linewidth'],self.lineprops2['marker'],self.lineprops2['markersize'],self.lineprops2['fill'],\ dummy,self.plot3,y3lo,y3hi,self.autoy3,self.smooth3, self.lineprops3['color'],self.lineprops3['linewidth'],self.lineprops3['marker'],self.lineprops3['markersize'],self.lineprops3['fill'])=\ WxQuery("Graph Settings",\ [('wxnotebook','X Axis',None,None,None), ('wxcombo','X axis',self.XAxisAllowed(),self.xaxis,'str'), ("wxentry","Start",None,str(xlo),'str'), ("wxentry","End",None,str(xhi),'str'), ('wxcolor','Cursor color',None,self.cursorcolor,'str'), ('wxspin','Cursor width','0|6|1',self.cursorwidth,'int'), ('wxnotebook','Y1Axis',None,None,None), ('wxcombo','Channel 1',self.YAxisAllowed(),self.plot1,'str'), ('wxentry','Bottom',None,y1lo,'float'), ('wxentry','Top',None,y1hi,'float'), ('wxcheck','Auto Scale','-9|-8', self.autoy1,'bool'), #8 ('wxhscale','Smooth','1|12|1|1',self.smooth1,'int'), ('wxcolor','Color',None,self.lineprops1['color'],'str'), ('wxspin','Line width','0|12|1',self.lineprops1['linewidth'],'int'), ('wxcombo','Marker','.|o|+|x|^|4|s|*|D',self.lineprops1['marker'],'str'), ('wxspin','Marker size','0|12|1',self.lineprops1['markersize'],'int'), ('wxcheck','Fill area',None,self.lineprops1['fill'],'bool'), ('wxnotebook','Y2 Axis',None,None,None), ('wxcombo','Channel 2',self.YAxisAllowed(),self.plot2,'str'), ('wxentry','Bottom',None,y2lo,'float'), ('wxentry','Top',None,y2hi,'float'), ('wxcheck','Auto Scale','-20|-19', self.autoy2,'bool'), ('wxhscale','Smooth','1|12|1|1',self.smooth2,'int'), ('wxcolor','Color',None,self.lineprops2['color'],'str'), ('wxspin','Line width','0|12|1',self.lineprops2['linewidth'],'int'), ('wxcombo','Marker','.|o|+|x|^|4|s|*|D',self.lineprops2['marker'],'str'), ('wxspin','Marker size','0|12|1',self.lineprops2['markersize'],'int'), ('wxcheck','Fill area',None,self.lineprops2['fill'],'bool'), ('wxnotebook','Y3 Axis',None,None,None), ('wxcombo','Channel 3',self.YAxisAllowed(),self.plot3,'str'), ('wxentry','Bottom',None,y3lo,'float'), ('wxentry','Top',None,y3hi,'float'), ('wxcheck','Auto Scale','-31|-30', self.autoy3,'bool'), ('wxhscale','Smooth','1|12|1|1',self.smooth3,'int'), ('wxcolor','Color',None,self.lineprops3['color'],'str'), ('wxspin','Line width','0|12|1',self.lineprops3['linewidth'],'int'), ('wxcombo','Marker','.|o|+|x|^|4|s|*|D',self.lineprops3['marker'],'str'), ('wxspin','Marker size','0|12|1',self.lineprops3['markersize'],'int'), ('wxcheck','Fill area',None,self.lineprops3['fill'],'bool') ]) if self.xaxis == xaxis: xlo = max(self.x_to_num(xlo, False), self.x_min()) xhi = min(self.x_to_num(xhi, False), self.x_max()) self.ax1.set_xlim([xlo, xhi]) else: #time units have changed... don't bother and set to full x range self.xaxis = xaxis self.ax1.set_xlim([self.x_min(), self.x_max()]) self.update_axis(self.ax1, self.plot1, y1lo, y1hi, self.autoy1, self.lineprops1, self.smooth1) self.update_axis(self.ax2, self.plot2, y2lo, y2hi, self.autoy2, self.lineprops2, self.smooth2) self.update_axis(self.ax3, self.plot3, y3lo, y3hi, self.autoy3, self.lineprops3, self.smooth3) def OnLeftMouseDown(self, event): where = self.get_axis(event, self.axis_width) #if hasattr(event, 'guiEvent') and int(event.guiEvent.type)==5: #calling direcly the dialog may freeze on unix (linux-osX systems) under wx backend #workaround is to release mouse #see http://stackoverflow.com/questions/16815695/modal-dialog-freezes-the-whole-application #event.guiEvent.GetEventObject().ReleaseMouse() for pick_event if event.button == 1: if event.dblclick: try: event.guiEvent.GetEventObject().ReleaseMouse() except: pass self.OnLeftMouseDblClick(event) return if where == 'bottom': (self.x0, self.y0) = (event.xdata, event.ydata) self.press = True if where == 'main' and self.span != None: self.span.set_visible(True) (self.x0, self.y0) = (event.xdata, event.ydata) self.selstart = self.x0 self.selstop = self.x0 self.span.set_bounds( event.xdata, self.ax1.get_ylim()[0], 0, self.ax1.get_ylim()[1] - self.ax1.get_ylim()[0]) self.press = True elif event.button == 3: if where == 'main': self.OnRightMouseDown(event) def OnLeftMouseUp(self, event): where = self.get_axis(event, self.axis_width) self.press = False if event.button == 1 and self.span != None: if where == 'main': idx1 = np.searchsorted(self.ax1.get_lines()[0].get_data()[0], self.x0) idx2 = np.searchsorted(self.ax1.get_lines()[0].get_data()[0], event.xdata) self.selstart = min(idx1, idx2) self.selstop = max(idx1, idx2) if self.selstart == self.selstop: self.span.set_visible(False) msgwrap.message("SelChanged", arg1=self.id, arg2=self.selstart, arg3=self.selstop) self.press = False def OnRightMouseDown(self, event): #may be necessary in some OSes event.guiEvent.GetEventObject().ReleaseMouse() if self.selstart == self.selstop: self.select_menu.Enable( self.select_menu.FindItem("Disable selected"), False) self.select_menu.Enable( self.select_menu.FindItem("Enable selected"), False) self.select_menu.Enable( self.select_menu.FindItem("Delete selected"), False) else: self.select_menu.Enable( self.select_menu.FindItem("Disable selected"), True) self.select_menu.Enable( self.select_menu.FindItem("Enable selected"), True) self.select_menu.Enable( self.select_menu.FindItem("Delete selected"), True) self.select_menu.Enable(self.select_menu.FindItem("Toggle points"), True) # on some OS (and depending on wxPython/wxWidgets version, calling # wx.PopupMenu will fail unless it is called after matplotlib handler has returned # for some magic reason, we do not need to specify wx.Point(event.x, event.y) in parameterss #self.PopupMenu(self.select_menus) wx.CallAfter(self.PopupMenu, self.select_menu) def OnMouseMotion(self, event): where = self.get_axis(event, self.axis_width) if where == 'bottom' or where == 'right' or where == 'left' or where == '3rd': wx.SetCursor(wx.Cursor(wx.CURSOR_MAGNIFIER)) else: wx.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) if where == 'bottom' and self.press: dx = event.xdata - self.x0 dy = event.ydata - self.y0 self.ax1.set_xlim(self.ax1.get_xlim()[0] - dx, self.ax1.get_xlim()[1] - dx) self.Draw(False) if where == 'main' and self.press: self.span.set_bounds(self.x0,\ self.ax1.get_ylim()[0],\ event.xdata-self.x0,\ self.ax1.get_ylim()[1]-self.ax1.get_ylim()[0]) self.Draw(True) if where == 'main' and self.cursor != None: self.cursor.set_xdata(event.xdata) xval = event.xdata idx = np.searchsorted(self.ax1.get_lines()[0].get_data()[0], xval) while self.gpx['ok'][ idx] == False and idx >= 0: #look for nearest enabled point idx -= 1 idx = clamp(idx, 0, self.gpx.get_row_count() - 1) self.cursor.set_xdata(self.x_to_num(self.gpx[self.xaxis][idx])) msgwrap.message("CurChanged", arg1=self.id, arg2=idx) ##send a message for the status bar self.UpdateStatusBar(idx) self.Draw(True) def OnMouseWheel(self, event): where = self.get_axis(event, self.axis_width) if where == 'bottom': xmax = self.x_max() xmin = self.x_min() xlo, xhi = self.ax1.get_xlim() if event.button == 'down': scale_factor = 1.2 else: scale_factor = 1 / 1.2 nxhi = event.xdata + (scale_factor * (xhi - event.xdata)) nxlo = event.xdata - (scale_factor * (event.xdata - xlo)) nxhi = min(nxhi, xmax) nxlo = max(nxlo, xmin) self.ax1.set_xlim([nxlo, nxhi]) self.format_x_axis() elif where == 'left' or where == 'right' or where == '3rd': if where == 'left': ax = self.ax1 plot = self.plot1 elif where == 'right': ax = self.ax2 plot = self.plot2 elif where == '3rd': ax = self.ax3 plot = self.plot3 ymax = np.max(self.gpx[plot] * self.gpx.scale[plot]) ymin = np.min(self.gpx[plot] * self.gpx.scale[plot]) ylo, yhi = ax.get_ylim() if event.button == 'down': scale_factor = 1.2 else: scale_factor = 1 / 1.2 nyhi = event.ydata + (scale_factor * (yhi - event.ydata)) nylo = event.ydata - (scale_factor * (event.ydata - ylo)) nyhi = min(nyhi, ymax) nylo = max(nylo, ymin) ax.set_ylim([nylo, nyhi]) self.Draw(False) def OnMouseEnter(self, event): self.SetFocus( ) # stupid bug in wxSplitterWindow, mouse wheel is always send to the same panel in wxSplittedWIndow def OnMouseLeave(self, event): wx.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) pass def OnPopup(self, event): item = self.select_menu.FindItemById(event.GetId()) text = item.GetText() if text == "Disable selected": self.gpx['ok'][self.selstart:self.selstop] = False if text == "Enable selected": self.gpx['ok'][self.selstart:self.selstop] = True if text == "Disable non selected": self.gpx['ok'][:self.selstart] = False self.gpx['ok'][self.selstop:] = False if text == "Enable non selected": self.gpx['ok'][:self.selstart] = True self.gpx['ok'][self.selstop:] = True if text == "Delete selected": if wx.MessageDialog(None, "Delete Points...?",\ 'Are you sure you want to delete these points',\ wx.YES_NO | wx.ICON_QUESTION).ShowModal()==wx.ID_YES: for _ in range(self.selstart, self.selstop): self.gpx.drop_row( self.selstart ) #each time we delete, the rest of the array is shifted. so we have to delete always the same index if text == "Delete non selected": if wx.MessageDialog(None, "Delete Points...?",\ 'Are you sure you want to delete these points',\ wx.YES_NO | wx.ICON_QUESTION).ShowModal()==wx.ID_YES: for _ in range(self.selstop, self.gpx.get_row_count()): self.gpx.drop_row( self.selstop ) #delete first end of range, to avoid shifting selstop for _ in range(0, self.selstart): self.gpx.drop_row(0) if text == "Toggle points": self.gpx['ok'] = np.invert(self.gpx['ok']) msgwrap.message("ValChanged", arg1=self.id) self.update_axis(self.ax1, self.plot1, self.ax1.get_ylim()[0], self.ax1.get_ylim()[1], self.autoy1, self.lineprops1, self.smooth1) self.update_axis(self.ax2, self.plot2, self.ax2.get_ylim()[0], self.ax2.get_ylim()[1], self.autoy2, self.lineprops2, self.smooth2) self.update_axis(self.ax3, self.plot3, self.ax3.get_ylim()[0], self.ax3.get_ylim()[1], self.autoy3, self.lineprops3, self.smooth3) def UpdateStatusBar(self, idx): if self.plot1 != "none": msg1=self.plot1+\ " ("+str(self.gpx.get_unit(self.plot1)[0])+"): "\ +str(self.gpx[self.plot1][idx]*self.gpx.scale[self.plot1]) else: msg1 = "" if self.plot2 != "none": msg2=self.plot2+\ " ("+str(self.gpx.get_unit(self.plot2)[0])+"): "\ +str(self.gpx[self.plot2][idx]*self.gpx.scale[self.plot2]) else: msg2 = "" if self.plot3 != "none": msg3=self.plot3+\ " ("+str(self.gpx.get_unit(self.plot3)[0])+"): "\ +str(self.gpx[self.plot3][idx]*self.gpx.scale[self.plot3]) else: msg3 = "" msgwrap.message("StatusChanged",arg1=self.id,\ arg2=self.gpx['time'][idx],\ arg3=msg1,\ arg4=msg2,\ arg5=msg3 )
class Graph(wx.BoxSizer): """ This holds a curve plot with associated toolbar keyword arguments: parent -- reference to the panel or context the plot should be created in. orientation -- (optional) wx.BoxSizer style. This also sets the deafult direction to expand when adding sub-plots (default: wx.VERTICAL) title -- (optional) sets the title of the plot window (default: ''). dpi -- (optional) sets dots per inch of plot window. params -- (optional) set matplotlib rcParams, should be a dictionary. (default: sets font size of: ticks, legend, axes label, font) **kwargs -- any keyword argument to matplotlib.Figure """ def __init__(self, parent, orientation= wx.VERTICAL, title='', dpi=None, params = None, **kwargs): super(Graph, self).__init__(orientation) #initialize some font settings for matplotlib if params == None: params = {'axes.labelsize': 16, 'font.size': 14, 'legend.fontsize': 14, 'xtick.labelsize': 12, 'ytick.labelsize': 12} matplotlib.rcParams.update(params) self.figure = Figure(dpi=dpi, figsize=(2,2), **kwargs) self.canvas = FigureCanvas(parent, wx.NewId(), self.figure) self.sub_plots = _plot_list(self.figure) self.sub_plots.append(self.figure.add_subplot(111), title) self.toolbar = NavigationToolbar2Wx(self.canvas) ###Create some extra controls for the toolbar### self.cb_grid = wx.CheckBox(self.toolbar, wx.NewId(), 'Show Grid') btn_mark = wx.Button(self.toolbar, wx.NewId(), 'Mark selection') #btn_rem = wx.Button(parent, wx.NewId(), 'Remove_graph') self.cursor_pos = wx.StaticText(self.toolbar, wx.NewId(), 'x=0.0000, y=0.0000 ', style=wx.ALIGN_RIGHT | wx.ST_ELLIPSIZE_END | wx.ST_NO_AUTORESIZE) ####add extra controls to toolbar#### self.toolbar.AddControl(self.cb_grid) self.toolbar.AddControl(btn_mark) self.toolbar.AddStretchableSpace() self.toolbar.AddControl(self.cursor_pos) #self.toolbar.AddStretchableSpace() #needed to update the layout self.toolbar.Realize() #######Main layout####### v_sizer = wx.BoxSizer(wx.VERTICAL) v_sizer.Add(self.canvas, 1, wx.EXPAND) v_sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) #Add to self self.Add(v_sizer, 1, wx.EXPAND) ###Set title and other things### self.orientation = orientation #layout of plots (height, width, count) self.layout = (1,1,1) self.cb_grid.SetValue(True) self.sub_plots.show_grid(True) #connect the buttons to event handlers self.cb_grid.Connect(-1, -1, wx.wxEVT_COMMAND_CHECKBOX_CLICKED, self._on_cb_grid) btn_mark.Connect(-1, -1, wx.wxEVT_COMMAND_BUTTON_CLICKED, self._on_mark) #btn_rem.Connect(-1, -1, wx.wxEVT_COMMAND_BUTTON_CLICKED, self.on_rem) self.canvas.mpl_connect('motion_notify_event', self._UpdateCursorInformation) ############Event handlers############ def _on_cb_grid(self, evt): """ Toggles if grid should be shown on sub-plots """ self.sub_plots.show_grid(self.cb_grid.IsChecked()) #redraw plots self.canvas.draw() def _on_mark(self, evt): """ Draws or removes a selection outline on current sub-plot selection """ mark_color = 'k' if self.sub_plots.color.lower() == 'black': mark_color = 'white' if self.sub_plots.has_selection: #delete markers for sub_plot in self.sub_plots.sub_plots: for line in sub_plot.selection: sub_plot.axes.lines.remove(line) self.canvas.draw() else: for i, sub_plot in enumerate(self.sub_plots.sub_plots): x1, x2, y1, y2 = sub_plot.axes.axis() x = [x1, x2, x2, x1, x1] y = [y1, y1, y2, y2, y1] sub_plot.selection = self.redraw(x,y, hold = True, limits = (x1,x2,y1,y2), index = i, color = mark_color, linewidth = 2.0) self.sub_plots.has_selection = not self.sub_plots.has_selection def _UpdateCursorInformation(self, evt): if evt.inaxes: x, y = evt.xdata, evt.ydata txt = 'x={0:.4g}, y={1:.4g}'.format(x, y) self.cursor_pos.SetLabel(txt) ############Worker functions############ def add_secondary_y_axis(self, label = '', index = None): """ Creates a secondary y_axis on the specified sub-plots keyword arguments: label -- (optional) axis label of the second y-axis (default: '') index -- (optional) a integer or list of integers with the index of sub-plots for which a secondary y-axis should be created. When 'None' the formatter is set for all sub-plots (default: None) """ if type(index) == list: for i in index: self.sub_plots(i).create_y2_axis(label) elif type(index) == int: self.sub_plots(index).create_y2_axis(label) else: #do all count = self.layout[-1] for sub_plot in self.sub_plots.sub_plots: sub_plot.create_y2_axis(label) def add_subplot(self, title='', orientation=None): """ Adds an additional subplot. If more than one row exists and it will not be filled, than the plots on the last row will be expanded to fill all horizontal space. All plots will be recreated keyword arguments: title -- (optional) title text of new subplot (default: '') orientation -- (optional) direction to add subplot, valid data is: wx.VERTICAL or wx.HORIZONTAL (default: Graph.orientation) return -- index of created plot """ orientation = self.orientation if orientation == None else orientation count = self.layout[-1] + 1 size = self.layout[0] * self.layout[1] #check if there is room for another subplot if size >= count: self.layout = self.layout[:2] + (count, ) else: #expand layout if orientation == wx.VERTICAL: self.layout = (self.layout[0] + 1, self.layout[1], count) else: self.layout = (self.layout[0], self.layout[1] + 1, count) #Clear away old axes self.sub_plots.clear_axes() #recreate axes and add new one size = self.layout[0] * self.layout[1] for i in range(1,count+1): if count < size and i > self.layout[1] * (self.layout[0]-1): #expand graphs on last row exp_layout = (self.layout[0], self.layout[1] - (size-count)) exp_i = exp_layout[0] * exp_layout[1] - (count - i) axes = self.figure.add_subplot(*exp_layout + (exp_i,)) #check if to rebind axes or append new object if i == count: self.sub_plots.append(axes, title) else: self.sub_plots.set_axes(axes, i-1) else: axes = self.figure.add_subplot(*self.layout[:2] + (i, )) #check if to rebind axes or append new object if i == count: self.sub_plots.append(axes, title) else: self.sub_plots.set_axes(axes, i-1) self.canvas.draw() #return sub-plot index return count - 1 def cleanse_fontcache(self): """ Shouldn't be used. Can fix bug when using frozen programs under windows. Better to modify the setup script so that font files are left out of the matplotlib data files. """ file_path = path.join(path.expanduser('~'), \ '.matplotlib', \ 'fontList.cache') if path.exists(file_path): remove(file_path) def clear_lines(self, index = 0): """ Removes all lines from the selected sub-plot keyword arguments: index -- (optional) index of subplot, which should be cleared from all lines (default: 0) """ self.sub_plots(index).axes.cla() self.sub_plots(index).lines = [] def get_lines(self, index = 0): """ Retrieves all the lines of the specified sub-plot keyword arguments: index -- (optional) index of subplot from, which the lines should be retrieved from (default: 0) """ return self.sub_plots(index).axes.get_lines() def redraw(self, x, y, index = 0, hold=False, xmin = None, ymin = None, limits=None, limit_selector = None, alpha = 1.0, draw = True, **kwarg): """ Updates plot with new vectors keyword arguments: x -- the x-axis values y -- the y-axis values index -- (optional) index of subplot to plot the vector in (default: 0) hold -- (optional) should old vectors be kept (default: False) xmin -- (optional) set minimum value of x-axis (default: None) ymin -- (optional) set minimum value of y-axis (default: None) limits -- (optional) list to set the limits of x and y-axis. overrides the xmin and ymin arguments (default: None) limit_selector -- (optional) a fucntion which given the arguments: *(x1, x2, y1, y2) will return a list with the axis limits overrides the xmin, ymin and the limits argument (default: None) e.g. sel = lambda x1, x2, y1, y2: [x1, x2, min(y1, 0), y2] alpha -- (optional) sets the alpha of the line (default: 1.0) draw -- (optional) should the canvas be updated to show the new lines **kwarg -- all extra keyword arguments are sent to the plot function """ #set plot to update try: plot = self.sub_plots(index) except IndexError: raise(IndexError, "The sub-plot of index:{0:d} doesn't exist".format(index)) plot.axes.hold(hold) lines = plot.axes.plot(x,y, alpha = alpha, **kwarg) #Create a legend if label was given if not lines[0].get_label()[0] == "_": plot.axes.legend() #label must be sent through kwarg #if not ymin == None: x1, x2, y1, y2 = plot.axes.axis() ymin = y1 if ymin == None else ymin xmin = x1 if xmin == None else xmin plot.axes.axis([xmin, x2, ymin, y2]) if not limits == None: plot.axes.axis(limits) if not limit_selector == None: plot.axes.axis(limit_selector(x1, x2, y1, y2)) #plot it #plot.axes.grid(self.sub_plots.grid) plot.reload() if draw: self.canvas.draw() #store lines in a list #return line object #plot.lines.extend(lines) return lines def redraw_secondary_y(self, x, y, index = 0, **kwarg): """ Update secondary y-axis with a new vector keyword arguments: x -- the x-axis values y -- the y-axis values index -- (optional) index of subplot to plot the vector in (default: 0) style -- (optional) line style (default: 'r.') **kwarg -- all extra keyword arguments are sent to the plot function """ sub_plot = self.sub_plots(index) lines = sub_plot.y2_axis.plot(x, y, style, **kwarg) #show it self.canvas.draw() return lines def remove_lines(self, lines, index = 0): """ Re keyword arguments: lines -- A list of matplotlib lines as given by the redraw function index -- (optional) index of subplot from, which the lines should be retrieved from (default: 0) """ for line in lines: self.sub_plots(index).remove_line(line) self.canvas.draw() def remove_subplot(self): """ Removes the last sub-plot. Note that plots in the last row aren't expanded to fill the entire row. """ count = self.layout[-1] - 1 if count < 0: raise ValueError, "There is no sub-plot to remove" self.layout = (self.layout[0], self.layout[1], count) if count > 0: layout_change = True #check if layout of plots can be decreased if self.layout[0] > 1 and self.layout[1] > 1: if self.layout[0] < self.layout[1]: lrg = 1 sml = 0 else: lrg = 0 sml = 1 #check if a decrease is possible on the major axis size = (self.layout[lrg] - 1) * (self.layout[sml]) if size >= count: if lrg == 0: self.layout = (self.layout[0] - 1, self.layout[1], count) else: self.layout = (self.layout[0], self.layout[1] - 1, count) else: #check the minor axis size = (self.layout[lrg]) * (self.layout[sml] - 1) if size >= count: if sml == 0: self.layout = (self.layout[0] - 1, self.layout[1], count) else: self.layout = (self.layout[0], self.layout[1] - 1, count) else: layout_change = False else: if self.layout[0] > self.layout[1]: self.layout = (self.layout[0] - 1, self.layout[1], count) else: self.layout = (self.layout[0], self.layout[1] - 1, count) else: layout_change = False #remove the last sub-plot self.sub_plots.remove() if layout_change: #clear figure and recreate plots self.sub_plots.clear_axes() for i in range(1,count+1): plot_index = i - 1 self.sub_plots.set_axes(self.figure.add_subplot(*self.layout[:2] + (i, )), plot_index) #redraw screen self.canvas.draw() def set_formatter(self, frmt = 'sci', axes = 'all', useOffset = True, limits = (-3, 3), index=None): """ Sets the formatter of the axes. Default is to set scientific notation for all axes of all subplots. keyword arguments: frmt -- Sets the type of formatter used, valid values are: 'sci', 'log', 'plain' (default: 'sci') axes -- which axes should the formatter be used for, valid values are: 'all', 'x', 'y' (default: 'all') useOffset -- Should offset be used to make the tickers more meaningful (default: True) limits -- Limits for scientific notation as a tuple (default: (-3, 3)) index -- a integer or list of integers with the index of sub-plots for which the formatter should set. When 'None' the formatter is set for all sub-plots (default: None) """ frmt = frmt.lower() axes = axes.lower() if frmt == 'log': formatter = LogFormatter() else: sci = frmt == 'sci' formatter = ScalarFormatter(useOffset = useOffset) formatter.set_powerlimits(limits) formatter.set_scientific(sci) # format axes if type(index) == list: for i in index: self.sub_plots(i).set_formatter(formatter, axes) elif type(index) == int: self.sub_plots(index).set_formatter(formatter, axes) else: # do all for sub_plot in self.sub_plots.sub_plots: sub_plot.set_formatter(formatter, axes) #set default formatter self.sub_plots.set_default_formatter(formatter, axes) # redraw screen self.canvas.draw() def set_label(self, xlabel='', ylabel='', index = None): """ Set labels of the specified plot axes keyword arguments: xlabel -- (optional) sets the label of the x-axis (default: '') ylabel -- (optional) sets the label of the y-axis (default: '') index -- (optional) a integer or list of integers with the index of sub-plots for which a the labels should be set. When 'None' the labels is set for all sub-plots (default: None) """ if type(index) == list: for i in index: self.sub_plots.set_label(xlabel, ylabel, i) elif type(index) == int: self.sub_plots.set_label(xlabel, ylabel, index) else: # do all count = self.layout[-1] for i in range(count): self.sub_plots.set_label(xlabel, ylabel, i) # Redraw screen self.canvas.draw() def set_limits(self, limits, index = 0): """ Sets the axis limits of the specified sub-plot keyword arguments: limits -- A list to set the limits of x and y-axis: [x1, x2, y1, y2] index -- (optional) index of subplot to set axis limits (default: 0) """ self.sub_plots(index).axes.axis(limits) self.canvas.draw() def set_title(self, titles = '', index = 0): """ Set titles of the sub-plots keyword arguments: titles -- should be a list of title strings or a single string, in which case an index should be supplied (default: '') index -- (optional) Is only needed when titles is a single string specifies for which sub-plot to set the title """ if type(titles) == list: for i, title in enumerate(titles): self.sub_plots(i).set_title(title) else: self.sub_plots(index).set_title(titles) self.canvas.draw() def update(self): """ Will send a draw command to the canvas uppdating the graphs """ self.canvas.draw() self.canvas.flush_events() def update_plot_only(self, lines, index=0): """ Will redraw the background and plot lines only keyword arguments: lines -- a list of line objects for the plot index -- (optional) index of subplot to set axis limits (default: 0) """ try: plot = self.sub_plots(index) except IndexError: raise(IndexError, "The sub-plot of index:{0:d} doesn't exist".format(index)) ax = plot.axes #draw the background ax.draw_artist(ax.patch) #draw the lines for line in lines: ax.draw_artist(line) #draw the x grid for line in ax.get_xgridlines(): ax.draw_artist(line) #draw the y grid for line in ax.get_ygridlines(): ax.draw_artist(line) #redraw display selectively self.canvas.blit(ax.bbox) #should flush_events be used a bunch of erros seem to occur on this? #self.canvas.flush_events()
class PlotFigure(wx.Frame): """Matplotlib wxFrame with animation effect""" global t global static_map, fix_point, grid_point def __init__(self): wx.Frame.__init__(self, None, wx.ID_ANY, title="!!!", size=(1000, 1000)) # Matplotlib Figure self.fig = Figure((10, 10), 100) # bind the Figure to the backend specific canvas self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) # add a subplot self.ax = self.fig.add_subplot(111) # limit the X and Y axes dimensions self.ax.set_ylim([-10, 10]) self.ax.set_xlim([-5, 15]) self.ax.set_autoscale_on(False) # self.ax.set_xticks([]) # # we want a tick every 10 point on Y (101 is to have 10 # self.ax.set_yticks(range(0, 101, 10)) # # disable autoscale, since we don't want the Axes to ad # # draw a grid (it will be only for Y) self.ax.grid(True) # generates first "empty" plots # self.user = [None] * POINTS self.user = self.ax.plot(static_map.data[0, :], static_map.data[1, :], 'o', label='map') # add the legend self.ax.legend(loc='upper center', ncol=4, prop=font_manager.FontProperties(size=10)) # force a draw on the canvas() # trick to show the grid and the legend self.canvas.draw() # save the clean background - everything but the line # is drawn and saved in the pixel buffer background self.bg = self.canvas.copy_from_bbox(self.ax.bbox) # bind events coming from timer with id = TIMER_ID # to the onTimer callback function wx.EVT_TIMER(self, TIMER_ID, self.onTimer) def onTimer(self, evt): """callback function for timer events""" # restore the clean background, saved at the beginning self.canvas.restore_region(self.bg) # update the data # temp = np.random.randint(10, 80) # self.user = self.user[1:] + [temp] # update the plot # self.l_user.set_ydata(self.user) self.user, = self.ax.plot(fix_point.point[0, :], fix_point.point[1, :], 'or') # just draw the "animated" objects self.ax.draw_artist(self.user) ### global odom_msg laser_range = 10 xlim = [-0.2, laser_range] ylim = [-laser_range / 2.0, laser_range / 2.0] frame_ori = np.array([[xlim[0], ylim[0]], [xlim[0], ylim[1]], [xlim[1], ylim[1]], [xlim[1], ylim[0]], [xlim[0], ylim[0]]]).transpose() T = np.array([[odom_msg.pose.pose.position.x], [odom_msg.pose.pose.position.y]]) R = quaternion_matrix((odom_msg.pose.pose.orientation.x, odom_msg.pose.pose.orientation.y, odom_msg.pose.pose.orientation.z, odom_msg.pose.pose.orientation.w))[0:2, 0:2] frame = np.dot(R, frame_ori) + np.dot(T, np.ones((1, frame_ori.shape[1]))) # delta = 0.3 # xlim = [fix_point.point[0, :].min() - delta, fix_point.point[0, :].max() + delta] # ylim = [fix_point.point[1, :].min() - delta, fix_point.point[1, :].max() + delta] # frame = np.array([[xlim[0], ylim[0]],[xlim[0], ylim[1]],[xlim[1], ylim[1]],[xlim[1], ylim[0]],[xlim[0], ylim[0]]]).transpose() self.user, = self.ax.plot(frame[0, :], frame[1, :], 'k') # just draw the "animated" objects self.ax.draw_artist(self.user) ### # It is used to efficiently update Axes data (axis ticks, labels, etc are not updated) self.user, = self.ax.plot(grid_point.point[0, :], grid_point.point[1, :], 'og', label='2') # just draw the "animated" objects self.ax.draw_artist(self.user) # It is used to efficiently update Axes data (axis ticks, labels, etc are not updated) self.canvas.blit(self.ax.bbox) def __del__(self): t.Stop()
class NaoPanel(wx.Panel): def _init_ctrls(self, prnt): # generated method, don't edit wx.Panel.__init__(self, id=wxID_LEFTPANEL, name='NaoPanel', parent=prnt, pos=wx.Point(208, 8), size=wx.Size(800, 400), style=wx.NO_BORDER | wx.TAB_TRAVERSAL) self.SetClientSize(wx.Size(800, 400)) self.SetBackgroundColour(wx.Colour(0, 0, 255)) self.Bind(wx.EVT_PAINT, self.OnNaoPanelPaint) def __init__(self, parent, id, pos, size, style, name): self._init_ctrls(parent) ##Create a matplotlib figure/canvas in this panel ##the background colour will be the same as the panel ##the size will also be the same as the panel ##calculate size in inches pixels_width, pixels_height = self.GetSizeTuple() self.dpi = 96.0 inches_width = pixels_width / self.dpi inches_height = pixels_height / self.dpi ##calculate colour in RGB 0 to 1 colour = self.GetBackgroundColour() self.fig = Figure(figsize=(inches_width,inches_height), dpi = self.dpi\ ,facecolor=(colour.Red()/255.0, colour.Green()/255.0, colour.Blue()/255.0)\ ,edgecolor=(colour.Red()/255.0, colour.Green()/255.0, colour.Blue()/255.0)) ##left : the left side of the subplots of the figure ## | right : the right side of the subplots of the figure ## | bottom : the bottom of the subplots of the figure ## | top : the top of the subplots of the figure ## | wspace : the amount of width reserved for blank space between subplots ## | hspace : the amount of height reserved for white space between subplots ## | self.canvas = FigureCanvasWxAgg(self, -1, self.fig) ##now put everything in a sizer sizer = wx.BoxSizer(wx.VERTICAL) # This way of adding to sizer allows resizing sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) self.SetSizer(sizer) self.Fit() ##now finally create the actual plot ##self.axes = self.fig.add_subplot(111) self.axes = self.fig.add_axes( (0.08, 0.08, 0.90, 0.85)) ##left,bottom,width,height self.naohistoryplot = self.axes.plot([0, 0], [0, 0], 'r', animated=True) self.naohistoryx = list() self.naohistoryy = list() self.positionmeasurementplot = self.axes.plot([0, 0], [0, 0], 'blue', marker='o', markersize=5, linewidth=0, markeredgewidth=0, animated=True) self.orientationmeasurementplot = self.axes.plot([0, 0], [0, 0], 'blue', linewidth=2, animated=True) self.shapeplot = self.axes.plot([0, 0], [0, 0], 'blue', marker='o', markersize=2, linewidth=0, markeredgewidth=0, animated=True) self.estimateplot = self.axes.plot([0, 0], [0, 0], 'red', linewidth=2, animated=True) self.particleplot = self.axes.quiver([0, 0], [0, 0], [1, 1], [0.5, -0.5], [1, 1], cmap=pylab.gray(), animated=True) ##plot formatting self.axes.set_title('Nao Image', fontsize='10') self.axes.set_xlabel('y (cm)', fontsize='10') self.axes.set_ylabel('x (cm)', fontsize='10') ticks = numpy.arange(-25, 25 + 5, 5) labels = [str(tick) for tick in ticks] self.axes.set_yticks(ticks) self.axes.set_yticklabels(labels, fontsize=8) self.axes.set_ylim(ticks[0], ticks[-1]) ticks = -numpy.arange(-50, 50 + 5, 5) labels = [str(tick) for tick in ticks] self.axes.set_xticks(ticks) self.axes.set_xticklabels(labels, fontsize=8) self.axes.set_xlim(ticks[0], ticks[-1]) self.canvas.draw() self.canvas.gui_repaint() # save the clean slate background -- everything but the animated line # is drawn and saved in the pixel buffer background self.background = self.canvas.copy_from_bbox(self.axes.bbox) #self.leftedgeplot = self.axes.plot([0,0],[0,0], 'orange', marker='o', markersize=4, linewidth=0, animated=True) #self.rightedgeplot = self.axes.plot([0,0],[0,0], 'purple', marker='o', markersize=4, linewidth=0, animated=True) def setNaoFinder(self, finder): """ """ self.NAOFinder = finder def setLocalisation(self, localisation): """ """ self.Localisation = localisation def updateData(self, data): """updateData. Updates the data that this panel is displaying. """ # Note the x values are plotted on the y-axis, and the y values are plotted on the x-axis naox = self.Localisation.X naoy = self.Localisation.Y naoorientation = self.Localisation.Orientation measurednaox = self.NAOFinder.NaoX measurednaoy = self.NAOFinder.NaoY measurednaoorientation = self.NAOFinder.NaoOrientation self.positionmeasurementplot[0].set_data([measurednaoy, measurednaoy], [measurednaox, measurednaox]) self.orientationmeasurementplot[0].set_data([ measurednaoy + 10 * numpy.sin(measurednaoorientation - numpy.pi), measurednaoy + 10 * numpy.sin(measurednaoorientation) ], [ measurednaox + 10 * numpy.cos(measurednaoorientation - numpy.pi), measurednaox + 10 * numpy.cos(measurednaoorientation) ]) self.shapeplot[0].set_data(self.NAOFinder.ShapeY, self.NAOFinder.ShapeX) self.naohistoryx.append(naox) self.naohistoryy.append(naoy) if len(self.naohistoryx) > 20: del self.naohistoryx[0] del self.naohistoryy[0] self.naohistoryplot[0].set_data(self.naohistoryy, self.naohistoryx) self.estimateplot[0].set_data( [naoy, naoy + 10 * numpy.sin(self.Localisation.Orientation)], [naox, naox + 10 * numpy.cos(self.Localisation.Orientation)]) #self.particleplot = self.axes.quiver(numpy.array(self.Localisation.States[:,Localisation.Y]), numpy.array(self.Localisation.States[:,Localisation.X]), -numpy.sin(self.Localisation.States[:,Localisation.THETA]), numpy.cos(self.Localisation.States[:,Localisation.THETA]), 1.0 - self.Localisation.GUIWeights, headlength=10, headwidth=10, width=0.001, scale=50.0) self.axes.set_xlim(naoy + 50, naoy - 50) self.axes.set_ylim(naox - 25, naox + 25) # restore the clean slate background self.canvas.restore_region(self.background) # just draw the animated artist self.axes.draw_artist(self.shapeplot[0]) #self.axes.draw_artist(self.particleplot) self.axes.draw_artist(self.naohistoryplot[0]) self.axes.draw_artist(self.orientationmeasurementplot[0]) self.axes.draw_artist(self.positionmeasurementplot[0]) self.axes.draw_artist(self.estimateplot[0]) # just redraw the axes rectangle self.canvas.blit(self.axes.bbox) #leftx = list() #lefty = list() #for leftedge in self.NAOFinder.LeftEdges: # leftx.append(data[0][leftedge]) # lefty.append(data[1][leftedge]) #rightx = list() #righty = list() #for rightedge in self.NAOFinder.RightEdges: # rightx.append(data[0][rightedge]) # righty.append(data[1][rightedge]) #self.leftedgeplot[0].set_data(lefty, leftx) #self.rightedgeplot[0].set_data(righty, rightx) def OnNaoPanelPaint(self, event): pass
class CartesianPanel(wx.Panel): def _init_ctrls(self, prnt): # generated method, don't edit wx.Panel.__init__(self, id=wxID_LEFTPANEL, name='CartesianPanel', parent=prnt, pos=wx.Point(8, 8), size=wx.Size(200, 400), style=wx.NO_BORDER | wx.TAB_TRAVERSAL) self.SetClientSize(wx.Size(200, 400)) self.SetBackgroundColour(wx.Colour(0, 0, 255)) self.Bind(wx.EVT_PAINT, self.OnCartesianPanelPaint) def __init__(self, parent, id, pos, size, style, name): self._init_ctrls(parent) ##Create a matplotlib figure/canvas in this panel ##the background colour will be the same as the panel ##the size will also be the same as the panel ##calculate size in inches pixels_width,pixels_height = self.GetSizeTuple() self.dpi = 96.0 inches_width = pixels_width/self.dpi inches_height = pixels_height/self.dpi ##calculate colour in RGB 0 to 1 colour = self.GetBackgroundColour() self.fig = Figure(figsize=(inches_width,inches_height), dpi = self.dpi\ ,facecolor=(colour.Red()/255.0, colour.Green()/255.0, colour.Blue()/255.0)\ ,edgecolor=(colour.Red()/255.0, colour.Green()/255.0, colour.Blue()/255.0)) ##left : the left side of the subplots of the figure ## | right : the right side of the subplots of the figure ## | bottom : the bottom of the subplots of the figure ## | top : the top of the subplots of the figure ## | wspace : the amount of width reserved for blank space between subplots ## | hspace : the amount of height reserved for white space between subplots ## | self.canvas = FigureCanvasWxAgg(self, -1, self.fig) ##now put everything in a sizer sizer = wx.BoxSizer(wx.VERTICAL) # This way of adding to sizer allows resizing sizer.Add(self.canvas, 1, wx.LEFT|wx.TOP|wx.GROW) self.SetSizer(sizer) self.Fit() ##now finally create the actual plot ##self.axes = self.fig.add_subplot(111) self.axes = self.fig.add_axes((0.16,0.08,0.90,0.85)) ##left,bottom,width,height self.plot = self.axes.plot([0,0],[0,0], 'b', animated=True) self.naohistoryplot = self.axes.plot([0,0],[0,0], 'r', animated=True) self.naohistoryx = list() self.naohistoryy = list() self.naoplot = self.axes.plot([0,0],[0,0], 'r', marker='o', markersize=4, animated=True) self.leftedgeplot = self.axes.plot([0,0],[0,0], 'orange', marker='o', markersize=4, linewidth=0, animated=True) self.rightedgeplot = self.axes.plot([0,0],[0,0], 'purple', marker='o', markersize=4, linewidth=0, animated=True) ##plot formatting self.axes.set_title('Laser Image', fontsize='10') #self.axes.set_xlabel('y (cm)', fontsize='10') #self.axes.set_ylabel('x (cm)', fontsize='10') ticks = numpy.arange(-450, 450+100, 100) labels = [str(tick) for tick in ticks] self.axes.set_yticks(ticks) self.axes.set_yticklabels(labels, fontsize=8) self.axes.set_ylim(ticks[0], ticks[-1]) ticks = numpy.arange(0, 450+100, 100) labels = [str(tick) for tick in ticks] self.axes.set_xticks(ticks) self.axes.set_xticklabels(labels,fontsize=8) self.axes.set_xlim(ticks[0], ticks[-1]) self.canvas.draw() self.canvas.gui_repaint() # save the clean slate background -- everything but the animated line # is drawn and saved in the pixel buffer background self.background = self.canvas.copy_from_bbox(self.axes.bbox) def setNaoFinder(self, finder): """ """ self.NAOFinder = finder def updateData(self, data, naox, naoy): """updateData. Updates the data that this panel is displaying. """ self.x = data[0] self.y = data[1] self.plot[0].set_data(self.x, self.y) self.naoplot[0].set_data([naox,naox], [naoy, naoy]) self.naohistoryx.append(naox) self.naohistoryy.append(naoy) if len(self.naohistoryx) > 400: del self.naohistoryx[0] del self.naohistoryy[0] self.naohistoryplot[0].set_data(self.naohistoryx, self.naohistoryy) leftx = list() lefty = list() for leftedge in self.NAOFinder.LeftEdges: leftx.append(data[0][leftedge]) lefty.append(data[1][leftedge]) rightx = list() righty = list() for rightedge in self.NAOFinder.RightEdges: rightx.append(data[0][rightedge]) righty.append(data[1][rightedge]) self.leftedgeplot[0].set_data(leftx, lefty) self.rightedgeplot[0].set_data(rightx, righty) # restore the clean slate background self.canvas.restore_region(self.background) # just draw the animated artist self.axes.draw_artist(self.plot[0]) self.axes.draw_artist(self.naoplot[0]) self.axes.draw_artist(self.naohistoryplot[0]) self.axes.draw_artist(self.leftedgeplot[0]) self.axes.draw_artist(self.rightedgeplot[0]) # just redraw the axes rectangle self.canvas.blit(self.axes.bbox) def OnCartesianPanelPaint(self, event): pass
class MonitorFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, parent=None, id=-1, title=__app_name__, pos=(10, 10), size=(1200, 620)) self.LoadParam() self.BuildUI() self.InitUiParam() self.proc_name_value.SetFocus() self.t = wx.Timer(self, TIMER_ID) def LoadParam(self): self.settings = param.load_param('config.json') if 'xmin' not in self.settings: self.settings['xmin'] = 0 if 'xmax' not in self.settings: self.settings['xmax'] = self.settings['points'] def BuildUI(self): # ------- config box ------------ # process name self.proc_name_label = wx.StaticText(parent=self, label='Process Name: ', style=wx.ALIGN_CENTER) self.proc_name_value = wx.TextCtrl(parent=self, value='', style=wx.TE_PROCESS_ENTER) self.proc_name_box = wx.BoxSizer(wx.HORIZONTAL) self.proc_name_box.Add(self.proc_name_label, 1, wx.ALIGN_CENTER, 5, 0) self.proc_name_box.Add(self.proc_name_value, 2, wx.ALIGN_CENTER, 5, 0) # input response self.proc_msg = wx.StaticText(parent=self, label='', size=(800, 30), style=wx.ALIGN_LEFT|wx.ST_NO_AUTORESIZE) # add to config box self.configBox= wx.BoxSizer(wx.VERTICAL) self.configBox.Add(self.proc_name_box, 1, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 5, 0) self.configBox.Add(self.proc_msg, 1, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 5, 0) # ------- control box ------------ self.startBtn = wx.Button(parent=self, label="Start", size=(60, 60)) self.stopBtn = wx.Button(parent=self, label="Stop") self.showBtn = wx.Button(parent=self, label="Show") self.controlBox = wx.BoxSizer(wx.HORIZONTAL) self.controlBox.Add(self.startBtn, 1, wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 5, 0) self.controlBox.Add(self.showBtn, 1, wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 5, 0) self.controlBox.Add(self.stopBtn, 1, wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 5, 0) self.startBtn.Enable() self.stopBtn.Disable() # ------- tool box(config, control) ------- self.toolbox = wx.BoxSizer(wx.HORIZONTAL) self.toolbox.AddSpacer(20) self.toolbox.Add(self.configBox, 5, wx.ALL|wx.ALIGN_CENTER, 5, 0) self.toolbox.Add(self.controlBox, 2, wx.ALL|wx.ALIGN_CENTER, 5, 0) # ------- track log box ------------------- self.track_log = wx.TextCtrl(parent=self, style=wx.TE_AUTO_SCROLL | wx.TE_MULTILINE) self.track_log.SetEditable(False) self.fig = self.InitPlotUI() self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) self.canvas.draw() self.bg = self.canvas.copy_from_bbox(self.ax.bbox) self.dispbox = wx.BoxSizer(wx.HORIZONTAL) self.dispbox.Add(self.track_log, 1, wx.ALL|wx.EXPAND, 5, 5) self.dispbox.Add(self.canvas, 0, wx.ALL|wx.EXPAND, 5, 5) # ------- main box(tool, tracklog) -------- self.mainbox = wx.BoxSizer(wx.VERTICAL) self.mainbox.Add(self.toolbox, 1, wx.NORMAL, 0, 0) self.mainbox.Add(self.dispbox, 0, wx.EXPAND, 5, 5) self.SetSizer(self.mainbox) self.CenterOnScreen() self.startBtn.Bind(wx.EVT_BUTTON, self.OnStartTrack) self.stopBtn.Bind(wx.EVT_BUTTON, self.OnStopTrack) self.proc_name_value.Bind(wx.EVT_TEXT, self.OnProcInputChanged) self.proc_name_value.Bind(wx.EVT_TEXT_ENTER, self.OnStartTrack) self.Bind(wx.EVT_ACTIVATE, self.OnWindowActivate) wx.EVT_TIMER(self, TIMER_ID, self.onTimer) def InitUiParam(self): self.proc_name_value.SetValue(self.settings['process_name']) self.proc_tracking = None self.is_track_running = False def OnStartTrack(self, event): if self.is_track_running: return proc_name = self.proc_name_value.GetValue().strip() if 0 == len(proc_name): msg = 'Please input a process name!' dlg = wx.MessageDialog(None, msg, "%s Error" % __app_name__, wx.ICON_ERROR) dlg.ShowModal() return None if self.proc_tracking is None: self.MatchProcName(proc_name) if self.proc_tracking is None: msg = 'No such process!\nGo on to track %s?' % proc_name dlg = wx.MessageDialog(None, msg, "%s Error" % __app_name__, wx.YES_NO|wx.ICON_QUESTION) if dlg.ShowModal() != wx.ID_YES: return None # transfer button status self.startBtn.Disable() self.showBtn.Disable() self.stopBtn.Enable() self.proc_name_value.Disable() # clear log self.track_log.SetValue('') wx.CallAfter(self.StartTrack, self.proc_tracking, self.proc_name_value.GetValue()) def update_log(self, disp_data): global _log_cache _log_cache.append(disp_data) if len(_log_cache) >= (1000.0/self.settings['interval']): wx.CallAfter(self.track_log.AppendText, '%s | %.4f MB\n' % (timestamp(), avg(_log_cache))) _log_cache = [] def StartTrack(self, proc, proc_name): self.is_track_running = True self.t.Start(self.settings['interval']) def OnStopTrack(self, event): self.startBtn.Enable() self.showBtn.Enable() self.stopBtn.Disable() self.proc_name_value.Enable() # stop thread self.t.Stop() self.is_track_running = False def OnWindowActivate(self, event): if not self.is_track_running: self.MatchProcName(self.proc_name_value.GetValue().strip()) def OnProcInputChanged(self, event): self.MatchProcName(self.proc_name_value.GetValue().strip()) def MatchProcName(self, pname): self.proc_tracking = None if 0 == len(pname): self.proc_msg.SetLabel('Please input a process name') return None procs = monitor.get_procs(pname) if 0 == len(procs): self.proc_msg.SetLabel('Process not exists or AccessDenied') return None self.proc_tracking = procs[0] if len(procs) > 1: self.proc_msg.SetLabel('Warning! Multi Processes Match. use %s' % format_proc(self.proc_tracking)) else: self.proc_msg.SetLabel(format_proc(self.proc_tracking)) return self.proc_tracking def InitPlotUI(self): plot_points = self.settings['points'] fig = Figure(figsize=(9, 5), dpi=100) self.ax = fig.add_subplot(111) self.ax.set_ylim([self.settings['ymin'], self.settings['ymax']]) self.ax.set_xlim([self.settings['xmin'], self.settings['xmax']]) self.ax.set_autoscale_on(False) self.ax.set_xticks([]) self.ax.set_yticks(range(self.settings['ymin'], self.settings['ymax']+1, self.settings['ystep'])) self.ax.grid(True) self.mem_rss_data = [None] * plot_points self.l_mem_rss,=self.ax.plot(range(plot_points), self.mem_rss_data, label='Memory(RSS) %') # add the legend self.ax.legend(loc='upper center', ncol=4, prop=font_manager.FontProperties(size=10)) return fig def onTimer(self, evt): """callback function for timer events""" # restore the clean background, saved at the beginning self.canvas.restore_region(self.bg) # get new perf data if self.proc_tracking is None: proc_name = self.proc_name_value.GetValue().strip() self.proc_tracking = monitor.find_proc(proc_name) rss_mem = getSizeInMb(monitor.get_rss_mem(self.proc_tracking)) # update log wx.CallAfter(self.update_log, rss_mem) # plot self.mem_rss_data = self.mem_rss_data[1:] + [rss_mem] self.l_mem_rss.set_ydata(self.mem_rss_data) self.ax.draw_artist(self.l_mem_rss) self.canvas.blit(self.ax.bbox)
class PanelGraph(wx.Panel): def __init__(self, panel, notify, settings, status, remoteControl): self.panel = panel self.notify = notify self.plot = None self.settings = settings self.status = status self.remoteControl = remoteControl self.spectrum = None self.isLimited = None self.limit = None self.extent = None self.annotate = None self.isDrawing = False self.toolTip = wx.ToolTip('') self.mouseSelect = None self.mouseZoom = None self.measureTable = None self.background = None self.selectStart = None self.selectEnd = None self.menuClearSelect = [] self.measure = None self.show = None self.doDraw = False wx.Panel.__init__(self, panel) self.figure = matplotlib.figure.Figure(facecolor='white') self.figure.set_size_inches(0, 0) self.canvas = FigureCanvas(self, -1, self.figure) self.canvas.SetToolTip(self.toolTip) self.measureTable = PanelMeasure(self, settings) self.toolbar = NavigationToolbar(self.canvas, self, settings, self.__hide_overlay) self.toolbar.Realize() vbox = wx.BoxSizer(wx.VERTICAL) vbox.Add(self.canvas, 1, wx.EXPAND) vbox.Add(self.measureTable, 0, wx.EXPAND) vbox.Add(self.toolbar, 0, wx.EXPAND) self.SetSizer(vbox) vbox.Fit(self) self.create_plot() self.canvas.mpl_connect('button_press_event', self.__on_press) self.canvas.mpl_connect('figure_enter_event', self.__on_enter) self.canvas.mpl_connect('axes_leave_event', self.__on_leave) self.canvas.mpl_connect('motion_notify_event', self.__on_motion) self.canvas.mpl_connect('draw_event', self.__on_draw) self.canvas.mpl_connect('idle_event', self.__on_idle) self.Bind(wx.EVT_SIZE, self.__on_size) self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.__on_timer, self.timer) def __set_fonts(self): axes = self.plot.get_axes() if axes is not None: axes.xaxis.label.set_size('small') axes.yaxis.label.set_size('small') if self.settings.display == Display.SURFACE: axes.zaxis.label.set_size('small') axes.tick_params(axis='both', which='major', labelsize='small') axes = self.plot.get_axes_bar() if axes is not None: axes.tick_params(axis='both', which='major', labelsize='small') def __enable_menu(self, state): for menu in self.menuClearSelect: menu.Enable(state) def __on_press(self, event): if self.settings.clickTune and matplotlib.__version__ >= '1.2' and event.dblclick: frequency = int(event.xdata * 1e6) self.remoteControl.tune(frequency) elif isinstance(self.plot, PlotterPreview): self.plot.to_front() def __on_enter(self, _event): self.toolTip.Enable(False) def __on_leave(self, _event): self.toolTip.Enable(True) self.status.set_info('', level=None) def __on_motion(self, event): axes = self.plot.get_axes() axesBar = self.plot.get_axes_bar() xpos = event.xdata ypos = event.ydata text = "" if (xpos is None or ypos is None or self.spectrum is None or event.inaxes is None): spectrum = None elif event.inaxes == axesBar: spectrum = None level = self.plot.get_bar().norm.inverse(ypos) text = "{}".format(format_precision(self.settings, level=level)) elif self.settings.display == Display.PLOT: timeStamp = max(self.spectrum) spectrum = self.spectrum[timeStamp] elif self.settings.display == Display.SPECT: timeStamp = num2epoch(ypos) if timeStamp in self.spectrum: spectrum = self.spectrum[timeStamp] else: nearest = min(self.spectrum.keys(), key=lambda k: abs(k - timeStamp)) spectrum = self.spectrum[nearest] elif self.settings.display == Display.SURFACE: spectrum = None coords = axes.format_coord(event.xdata, event.ydata) match = re.match('x=([-|0-9|\.]+).*y=([0-9|\:]+).*z=([-|0-9|\.]+)', coords) if match is not None and match.lastindex == 3: freq = float(match.group(1)) level = float(match.group(3)) text = "{}, {}".format( *format_precision(self.settings, freq, level)) else: spectrum = None if spectrum is not None and len(spectrum) > 0: x = min(spectrum.keys(), key=lambda freq: abs(freq - xpos)) if min(spectrum.keys(), key=float) <= xpos <= max(spectrum.keys(), key=float): y = spectrum[x] text = "{}, {}".format(*format_precision(self.settings, x, y)) else: text = format_precision(self.settings, xpos) markers = find_artists(self.figure, 'peak') markers.extend(find_artists(self.figure, 'peakThres')) hit = False for marker in markers: if isinstance(marker, Line2D): location = marker.get_path().vertices[0] markX, markY = axes.transData.transform(location) dist = abs(math.hypot(event.x - markX, event.y - markY)) if dist <= 5: if self.settings.display == Display.PLOT: tip = "{}, {}".format(*format_precision( self.settings, location[0], location[1])) else: tip = "{}".format( format_precision(self.settings, location[0])) self.toolTip.SetTip(tip) hit = True break self.toolTip.Enable(hit) self.status.set_info(text, level=None) def __on_size(self, event): ppi = wx.ScreenDC().GetPPI() size = [float(v) for v in self.canvas.GetSize()] width = size[0] / ppi[0] height = size[1] / ppi[1] self.figure.set_figwidth(width) self.figure.set_figheight(height) self.figure.set_dpi(ppi[0]) event.Skip() def __on_draw(self, _event): axes = self.plot.get_axes() if axes is not None: self.background = self.canvas.copy_from_bbox(axes.bbox) self.__draw_overlay() def __on_idle(self, _event): if self.doDraw and self.plot.get_plot_thread() is None: self.__hide_overlay() self.doDraw = False if os.name == 'nt': threading.Thread(target=self.__draw_canvas, name='Draw').start() else: self.__draw_canvas() def __on_timer(self, _event): self.timer.Stop() self.set_plot(None, None, None, None, self.annotate) def __draw_canvas(self): try: self.isDrawing = True self.canvas.draw() except wx.PyDeadObjectError: pass self.isDrawing = False wx.CallAfter(self.status.set_busy, False) def __draw_overlay(self): if self.background is not None: self.canvas.restore_region(self.background) self.__draw_select() self.draw_measure() axes = self.plot.get_axes() if axes is not None: self.canvas.blit(axes.bbox) def __draw_select(self): if self.selectStart is not None and self.selectEnd is not None: self.mouseSelect.draw(self.selectStart, self.selectEnd) def __hide_overlay(self): if self.plot is not None: self.plot.hide_measure() self.__hide_select() def __hide_select(self): if self.mouseSelect is not None: self.mouseSelect.hide() def create_plot(self): if self.plot is not None: self.plot.close() self.toolbar.set_auto(True) if self.settings.display == Display.PLOT: self.plot = Plotter(self.notify, self.figure, self.settings) elif self.settings.display == Display.SPECT: self.plot = Spectrogram(self.notify, self.figure, self.settings) elif self.settings.display == Display.SURFACE: self.plot = Plotter3d(self.notify, self.figure, self.settings) elif self.settings.display == Display.STATUS: self.plot = PlotterStatus(self.notify, self.figure, self.settings) elif self.settings.display == Display.TIMELINE: self.plot = PlotterTime(self.notify, self.figure, self.settings) elif self.settings.display == Display.PREVIEW: self.plot = PlotterPreview(self.notify, self.figure, self.settings) self.plot.set_window(self) self.__set_fonts() self.toolbar.set_plot(self.plot) self.toolbar.set_type(self.settings.display) self.measureTable.set_type(self.settings.display) self.set_plot_title() self.figure.subplots_adjust(top=0.85) self.redraw_plot() self.plot.scale_plot(True) self.mouseZoom = MouseZoom(self.toolbar, plot=self.plot, callbackHide=self.__hide_overlay) self.mouseSelect = MouseSelect(self.plot, self.on_select, self.on_selected) self.measureTable.show(self.settings.showMeasure) self.panel.SetFocus() def on_select(self): self.hide_measure() def on_selected(self, start, end): self.__enable_menu(True) self.selectStart = start self.selectEnd = end self.measureTable.set_selected(self.spectrum, start, end) def add_menu_clear_select(self, menu): self.menuClearSelect.append(menu) menu.Enable(False) def draw(self): self.doDraw = True def show_measure_table(self, show): self.measureTable.show(show) self.Layout() def set_plot(self, spectrum, isLimited, limit, extent, annotate=False): if spectrum is not None and extent is not None: if isLimited is not None and limit is not None: self.spectrum = copy.copy(spectrum) self.extent = extent self.annotate = annotate self.isLimited = isLimited self.limit = limit if self.plot.get_plot_thread() is None and not self.isDrawing: self.timer.Stop() self.measureTable.set_selected(self.spectrum, self.selectStart, self.selectEnd) if isLimited: self.spectrum = reduce_points(spectrum, limit) self.status.set_busy(True) self.plot.set_plot(self.spectrum, self.extent, annotate) if self.settings.display == Display.PREVIEW: self.status.set_busy(False) else: self.timer.Start(200, oneShot=True) def set_plot_title(self): if len(self.settings.devicesRtl) > 0: gain = self.settings.devicesRtl[self.settings.indexRtl].gain else: gain = 0 self.plot.set_title("Frequency Spectrogram\n{} - {} MHz," " gain = {}dB".format(self.settings.start, self.settings.stop, gain)) def redraw_plot(self): if self.spectrum is not None: self.set_plot(self.spectrum, self.settings.pointsLimit, self.settings.pointsMax, self.extent, self.settings.annotate) def set_grid(self, on): self.plot.set_grid(on) def set_selected(self, start, end): self.selectStart = start self.selectEnd = end self.__draw_select() def hide_toolbar(self, hide): self.toolbar.Show(not hide) def hide_measure(self): if self.plot is not None: self.plot.hide_measure() def draw_measure(self): if self.measure is not None and self.measure.is_valid(): self.plot.draw_measure(self.measure, self.show) def update_measure(self, measure=None, show=None): if not measure and not show: self.measureTable.update_measure() else: self.measure = measure self.show = show self.__draw_overlay() def get_figure(self): return self.figure def get_axes(self): return self.plot.get_axes() def get_canvas(self): return self.canvas def get_toolbar(self): return self.toolbar def get_mouse_select(self): return self.mouseSelect def scale_plot(self, force=False): self.plot.scale_plot(force) def clear_plots(self): self.plot.clear_plots() self.spectrum = None self.doDraw = True def clear_selection(self): self.measure = None self.measureTable.clear_measurement() self.selectStart = None self.selectEnd = None self.mouseSelect.clear() self.__enable_menu(False) def close(self): self.plot.close() close_modeless()
class PlotFigure(wx.Frame): """Matplotlib wxFrame with animation effect""" def __init__(self): # initialize the super class wx.Frame.__init__(self, None, wx.ID_ANY, title="CPU Usage Monitor", size=(600, 400)) # Matplotlib Figure self.fig = Figure((6, 4), 100) # bind the Figure to the backend specific canvas self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) # add a subplot self.ax = self.fig.add_subplot(111) # limit the X and Y axes dimensions # we prefer 2 separate functions for clarity self.ax.set_ylim([0, 100]) self.ax.set_xlim([0, POINTS]) # but we want a "frozen" window (defined by y/xlim functions) self.ax.set_autoscale_on(False) # we do not want ticks on X axis self.ax.set_xticks([]) # we want a tick every 10 point on Y (101 is to have 100 too) self.ax.set_yticks(range(0, 101, 10)) # disable autoscale, since we don't want the Axes to adapt # draw a grid (it will be only for Y) self.ax.grid(True) # generates first "empty" plots self.user = [None] * POINTS self.nice = [None] * POINTS self.sys = [None] * POINTS self.idle = [None] * POINTS self.l_user, = self.ax.plot(range(POINTS), self.user, label='User %') self.l_nice, = self.ax.plot(range(POINTS), self.nice, label='Nice %') self.l_sys, = self.ax.plot(range(POINTS), self.sys, label='Sys %') self.l_idle, = self.ax.plot(range(POINTS), self.idle, label='Idle %') # add the legend self.ax.legend(loc='upper center', ncol=4, prop=font_manager.FontProperties(size=10)) # force a draw on the canvas() # trick to show the grid and the legend self.canvas.draw() # save the clean background - everything but the line # is drawn and saved in the pixel buffer background self.bg = self.canvas.copy_from_bbox(self.ax.bbox) # take a snapshot of CPU usage, needed for the update algorithm self.before = self.prepare_cpu_usage() # bind events coming from timer with id = TIMER_ID # to the onTimer callback function wx.EVT_TIMER(self, TIMER_ID, self.onTimer) def prepare_cpu_usage(self): """helper function to return CPU usage info""" # get the CPU times using psutil module t = p.cpu_times() # return only the values we're interested in if hasattr(t, 'nice'): return [t.user, t.nice, t.system, t.idle] else: # special case for Windows, without 'nice' value return [t.user, 0, t.system, t.idle] def get_cpu_usage(self): """Compute CPU usage comparing previous and current measurements""" # take the current CPU usage information now = self.prepare_cpu_usage() # compute deltas between current and previous measurements delta = [now[i]-self.before[i] for i in range(len(now))] # compute the total (needed for percentages calculation) total = sum(delta) # save the current measurement to before object self.before = now # return the percentage of CPU usage for our 4 categories return [(100.0*dt)/total for dt in delta] def onTimer(self, evt): """callback function for timer events""" # get the CPU usage information tmp = self.get_cpu_usage() # restore the clean background, saved at the beginning self.canvas.restore_region(self.bg) # update the data self.user = self.user[1:] + [tmp[0]] self.nice = self.nice[1:] + [tmp[1]] self.sys = self.sys[1:] + [tmp[2]] self.idle = self.idle[1:] + [tmp[3]] # update the plot self.l_user.set_ydata(self.user) self.l_nice.set_ydata(self.nice) self.l_sys.set_ydata( self.sys) self.l_idle.set_ydata(self.idle) # just draw the "animated" objects self.ax.draw_artist(self.l_user) self.ax.draw_artist(self.l_nice) self.ax.draw_artist(self.l_sys) self.ax.draw_artist(self.l_idle) # "blit" the background with the animated lines self.canvas.blit(self.ax.bbox)
class PlotFigure(wx.Frame): def __init__(self): self.screen_size = wx.DisplaySize() #print("the screen size is {0}".format(self.screen_size)) self.screen_dpi = 100 self.frame_length = int(self.screen_size[0]) self.frame_width = int(self.screen_size[1]) self.fig_length = self.frame_length // self.screen_dpi self.fig_width = self.frame_width // self.screen_dpi self.frame_length = self.fig_length * self.screen_dpi self.frame_width = self.fig_width * self.screen_dpi wx.Frame.__init__(self, None, wx.ID_ANY, title='CP1/X86 TPCC Performance Comparison', size=(self.frame_length, self.frame_width) #size=(1900, 1000) ) # Matplotlib Figure, x/y-size should size_in_Frame/dpi #eg: 1800 = 15 * 120, 600 = 5 * 120 #self.fig = Figure((19, 10), 100) self.fig = Figure((self.fig_length, self.fig_width), self.screen_dpi) #print(self.frame_length, self.frame_width) #print(self.fig_length, self.fig_width) self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) self.ax = self.fig.add_subplot(211) self.ax.set_ylim([Y_MIN_VALUE, Y_MAX_VALUE]) self.ax.set_xlim([X_MIN_VALUE, X_MAX_VALUE]) self.ax.set_autoscale_on(False) #self.ax.set_xticks([]) self.ax.set_xticks(range(0, 61, 10)) #self.ax.set_xticks(range(X_MIN_VALUE, X_MAX_VALUE + 1, X_STEP_VALUE)) self.ax.set_yticks(range(Y_MIN_VALUE, Y_MAX_VALUE + 1, Y_STEP_VALUE)) self.ax.set_xlabel("Time(second)") self.ax.set_ylabel("Transactions Per Minute(tpmC)") self.ax.grid(True) self.power8_current_all_values = [None] * EMPTY_NUMBER self.x86_current_all_values = [None] * EMPTY_NUMBER self.power8_plot, = self.ax.plot(range(EMPTY_NUMBER), #self.power8_current_all_values, label='CP1 Value', self.power8_current_all_values, label='CP1 TPC-C', #color='red', linestyle = ':', linewidth = 2, marker = 'o' color='red', marker = '.' ) self.x86_plot, = self.ax.plot(range(EMPTY_NUMBER), #self.x86_current_all_values, label='X86 Value', self.x86_current_all_values, label='X86 TPC-C', color='green', marker = '.' ) self.ax.legend(loc='upper center', ncol=4, prop=font_manager.FontProperties(size=16) #prop=font_manager.FontProperties(size=10) ) # for 2nd subplot self.average = self.fig.add_subplot(212) self.average.set_ylim(0, 6) #self.average.set_xlim(Y_MIN_VALUE, Y_MAX_VALUE) self.average.set_xlim(Y_MIN_VALUE, 300000) #self.average.set_ylabel("yHello world") self.average.set_xlabel("Transactions Per Minute(tpmC)") self.power8_accumulate_value = 0 self.x86_accumulate_value = 0 self.power8_previous_value = 0 self.x86_previous_value = 0 self.power8_ave_index = [3] self.x86_ave_index = [1] self.power8_ave_value = [0] self.x86_ave_value = [0] self.power8_barh, = self.average.barh(bottom=self.power8_ave_index, width=self.power8_ave_value, height=1.0, color='red', label='CP1 TPC-C (Average)') self.x86_barh, = self.average.barh(bottom=self.x86_ave_index, width=self.x86_ave_value, height=1.0, color='green', label="X86 TPC-C (Average)") self.average.grid(True) self.average.legend(loc='upper center', ncol=4, prop=font_manager.FontProperties(size=16) #prop=font_manager.FontProperties(size=10) ) self.average.set_yticks([]) self.fig.subplots_adjust(left=0.08, right=0.95, bottom=0.05, top=0.95) ########################################################################################## # TODO: resize the subplot in figure self.ax.set_position([0.08, 0.40, 0.85, 0.55]) self.average.set_position([0.08, 0.05, 0.85, 0.28]) self.canvas.draw() # save the clean background self.background_1st = self.canvas.copy_from_bbox(self.ax.bbox) self.background_2nd = self.canvas.copy_from_bbox(self.average.bbox) self.global_timer_index = 0 self.local_timer_index = 0 self.power8_current_all_values = [] self.x86_current_all_values = [] wx.EVT_TIMER(self, TIMER_ID, self.on_timer) def on_timer(self, event): # restore the clean background, saved at the beginning self.canvas.restore_region(self.background_1st) self.canvas.restore_region(self.background_2nd) #copyfile(power8_source_filename, power8_input_filename) #copyfile(x86_source_filename, x86_input_filename) #print(time.strftime("%s")) self.global_timer_index += 1 self.local_timer_index += 1 line_index = self.global_timer_index - 1 less_number = EMPTY_NUMBER - self.local_timer_index needed_number = self.local_timer_index - 1 # get the value of current index from file power8_current_value = self.read_from_file_by_index( power8_input_filename, line_index) x86_current_value = self.read_from_file_by_index( x86_input_filename, line_index) # normal return: accumulate the return value directly. # abnormal return: accumulate the previous one. if power8_current_value: self.power8_accumulate_value += power8_current_value self.power8_previous_value = power8_current_value else: # TODO: new add for error character power8_current_value = self.power8_previous_value self.power8_accumulate_value += self.power8_previous_value if x86_current_value: self.x86_accumulate_value += x86_current_value self.x86_previous_value = x86_current_value else: # TODO: new add for error character x86_current_value = self.x86_previous_value self.x86_accumulate_value += self.x86_previous_value #print("==> accumulate = {0} and previous = {1} and current =" # "{2}".format(self.power8_accumulate_value, # self.power8_previous_value, # power8_current_value)) # update the new data into 1st subplot self.power8_current_all_values = \ self.power8_current_all_values[:needed_number] + \ [power8_current_value] + [None] * less_number self.x86_current_all_values = \ self.x86_current_all_values[:needed_number] + \ [x86_current_value] + [None] * less_number self.power8_plot.set_ydata(self.power8_current_all_values) self.x86_plot.set_ydata(self.x86_current_all_values) # update the new data into 2nd subplot self.power8_ave_value = self.power8_accumulate_value / \ self.global_timer_index self.x86_ave_value = self.x86_accumulate_value / \ self.global_timer_index self.power8_barh.set_width(self.power8_ave_value) self.x86_barh.set_width(self.x86_ave_value) self.ax.draw_artist(self.power8_plot) self.ax.draw_artist(self.x86_plot) self.average.draw_artist(self.power8_barh) self.average.draw_artist(self.x86_barh) # clean the data on screen if self.local_timer_index == EMPTY_NUMBER: #print("local_timer_index is full") self.power8_current_all_values = [] self.x86_current_all_values = [] self.local_timer_index = 0 self.canvas.blit(self.ax.bbox) self.canvas.blit(self.average.bbox) def read_from_file_by_index(self, filename, line_number): try: with open(filename, 'r') as file_object: all_content = file_object.read().split('\n')[:-1] file_length = len(all_content) except IOError, e: print("Error->[read_from_file_by_index]: CAN NOT find the" "filename:[{0}]".format(filename)) except Exception as ex: print("Error->[read_from_file_by_index]: {0}".format(str(ex)))
class CanvasPanel(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) self.figure = Figure(figsize=(2, 1), dpi=256, facecolor='#000000') self.ax = self.figure.add_subplot(111, xmargin=0, ymargin=0, ylim=[1, 32767]) self.ax.set_axis_off() self.ax.set_yscale('log', nonposy='clip', basey=10.0) self.figure.set_tight_layout({'pad': 0}) defaultData = ([0, 1], [0, 1]) self.ctrl_lines = [ self.ax.plot(*defaultData, c=c, lw=0.5)[0] for c in ['#ff0000', '#ffffff', '#ffffff'] ] self.graph_lines = [ None, self.ax.step(*defaultData, c='#ffff00', lw=0.3)[0] ] self.background = None self.canvas = FigureCanvas(self, -1, self.figure) def __restore_if_possible(self): if not (self.background is None): self.canvas.restore_region(self.background) def __draw_lines(self): for line in self.ctrl_lines: self.ax.draw_artist(line) def set_lines_content(self, h=None, l=None, r=None): if not (h is None): self.ctrl_lines[0].set_ydata([h, h]) if not (l is None and r is None): ymin, ymax = self.ax.get_ylim() if not (l is None): self.ctrl_lines[1].set_data([l, l], [ymin, ymax]) if not (r is None): self.ctrl_lines[2].set_data([r, r], [ymin, ymax]) self.__restore_if_possible() self.__draw_lines() self.canvas.blit(self.ax.bbox) def set_graph_content(self, accurate_data=None, steps_data=None): if not (accurate_data is None): datalen = len(accurate_data) fact = 1.0 / datalen self.graph_lines[0] = self.ax.stackplot( [i * fact for i in range(datalen)], accurate_data, colors=['#00ff00'], lw=0)[0] if not (steps_data is None): self.graph_lines[1].set_data(steps_data) self.graph_lines[1].set_visible(True) else: self.graph_lines[1].set_visible(False) for line in self.ctrl_lines: line.set_visible(False) self.canvas.draw() self.background = self.canvas.copy_from_bbox(self.ax.bbox) for line in self.ctrl_lines: line.set_visible(True) self.set_lines_content() if not (accurate_data is None): self.graph_lines[0].remove() def mark_regions(self, data): self.__restore_if_possible() ymin, ymax = self.ax.get_ylim() for xpair in data: polygon, = self.ax.fill( [xpair[0], xpair[0], xpair[1], xpair[1]], [ymin, ymax, ymax, ymin], '#0000ff', alpha=0.2) self.ax.draw_artist(polygon) polygon.remove() self.__draw_lines() self.canvas.blit(self.ax.bbox)
class PlotPanel(wx.Panel): def __init__(self, *args, **kwds): # begin wxGlade: PlotPanel.__init__ uc = kwds.pop('uc') kwds["style"] = wx.DOUBLE_BORDER|wx.TAB_TRAVERSAL wx.Panel.__init__(self, *args, **kwds) self.__set_properties() self.__do_layout() # end wxGlade # ``PlotPanel`` size is (600, 400). self.figure = Figure(figsize=(6, 4), dpi=100) self.ax = self.figure.add_subplot(111) self.canvas = FigureCanvas(self, wx.ID_ANY, self.figure) #self.ax.set_ylim([0.0, 1023.0]) self.ax.set_ylim([0.0, 80.0]) self.ax.set_xlim([0.0, POINTS]) self.ax.set_autoscale_on(False) # Disable autoscale. self.ax.set_xticks([]) self.ax.grid(True, animated=True, linewidth=1, antialiased=True, fillstyle='full') self.ax.set_title(u'Distance vs time') self.ax.set_ylabel('distance (cm)') self.ax.set_xlabel('time') # Initial empty plot. self.distance = [None] * POINTS self.distance_plot, = self.ax.plot(range(POINTS), self.distance, label='Distance') self.canvas.draw() # Save the clean background - everything but the line is drawn and # saved in the pixel buffer background. self.bg = self.canvas.copy_from_bbox(self.ax.bbox) # Represents UC interfaced through the serial port. self.uc = uc assert self.uc.port.isOpen() assert self.uc.status == OK_STATUS self.uc.set_mode(SERIAL_MODE) # Take a snapshot of voltage, needed for the update algorithm. self.before = self.uc.distance wx.EVT_TIMER(self, TIMER_ID, self.onTimer) # Initialize the timer. self.t = wx.Timer(self, TIMER_ID) self.samples = 0 def __set_properties(self): # begin wxGlade: PlotPanel.__set_properties self.SetMinSize((600, 400)) self.SetToolTipString("Distance plot.") # end wxGlade def __do_layout(self): # begin wxGlade: PlotPanel.__do_layout pass # end wxGlade def onTimer(self, evt): # Get distance. distance = self.uc.distance if distance <= 80.0: distance_str = str(distance) else: distance = 80.0 distance_str = u"OUT OF RANGE" mainframe.distance_label.SetLabel(distance_str) self.samples += 1 # Restore the clean background, saved at the beginning. self.canvas.restore_region(self.bg) # Update data array. self.distance = self.distance[1:] + [distance] # Update plot. self.distance_plot.set_ydata(self.distance) # Just draw the "animated" objects. self.ax.draw_artist(self.distance_plot) # Blit the background with the animated lines. self.canvas.blit(self.ax.bbox) with open(LOG_FILE_PATH, 'a+') as f: f.write("{0},{1}\n".format(str(self.samples * TIME_DELTA), str(distance)))
class PlotFigure(wx.Frame): """Matplotlib wxFrame with animation effect""" def __init__(self): self.count=1 wx.Frame.__init__(self, None, wx.ID_ANY, title="CPU Usage Monitor", size=(1280, 720)) # Matplotlib Figure self.fig = Figure((16, 9), 80) # bind the Figure to the backend specific canvas self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) # add a subplot self.ax1 = self.fig.add_subplot(411) self.ax2 = self.fig.add_subplot(412) self.ax3 = self.fig.add_subplot(413) self.ax4 = self.fig.add_subplot(414) # limit the X and Y axes dimensions self.ax1.set_ylim([-0.05, 1.2]) self.ax1.set_xlim([0, POINTS]) self.ax2.set_ylim([-0.05, 1.2]) self.ax2.set_xlim([0, POINTS]) self.ax3.set_ylim([-0.05, 1.2]) self.ax3.set_xlim([0, POINTS]) self.ax4.set_ylim([-0.05, 1.2]) self.ax4.set_xlim([0, POINTS]) self.ax1.set_autoscale_on(False) self.ax1.set_xticks([]) self.ax2.set_autoscale_on(False) self.ax2.set_xticks([]) self.ax3.set_autoscale_on(False) self.ax3.set_xticks([]) self.ax4.set_autoscale_on(False) self.ax4.set_xticks([]) # we want a tick every 10 point on Y (101 is to have 10 self.ax1.set_yticks(range(0, 2, 10)) self.ax2.set_yticks(range(0, 2, 10)) self.ax3.set_yticks(range(0, 2, 10)) self.ax4.set_yticks(range(0, 2, 10)) # disable autoscale, since we don't want the Axes to ad # draw a grid (it will be only for Y) self.ax1.grid(True) self.ax2.grid(True) self.ax3.grid(True) self.ax4.grid(True) # generates first "empty" plots self.s1e = [None] * POINTS self.s1s = [None] * POINTS self.s1w = [None] * POINTS self.s2e = [None] * POINTS self.s2s = [None] * POINTS self.s2w = [None] * POINTS self.s3e = [None] * POINTS self.s3s = [None] * POINTS self.s3w = [None] * POINTS self.s4e = [None] * POINTS self.s4s = [None] * POINTS self.s4w = [None] * POINTS self.s1_e,=self.ax1.plot(range(POINTS),self.s1e,label='ECMP',linewidth=3) self.s1_s,=self.ax1.plot(range(POINTS),self.s1s,label='STAT',linewidth=3) self.s1_w,=self.ax1.plot(range(POINTS),self.s1w,label='LBAS',linewidth=3) self.s2_e,=self.ax2.plot(range(POINTS),self.s2e,label='ECMP %',linewidth=3) self.s2_s,=self.ax2.plot(range(POINTS),self.s2s,label='STAT %',linewidth=3) self.s2_w,=self.ax2.plot(range(POINTS),self.s2w,label='WEIG %',linewidth=3) self.s3_e,=self.ax3.plot(range(POINTS),self.s3e,label='ECMP %',linewidth=3) self.s3_s,=self.ax3.plot(range(POINTS),self.s3s,label='STAT %',linewidth=3) self.s3_w,=self.ax3.plot(range(POINTS),self.s3w,label='WEIG %',linewidth=3) self.s4_e,=self.ax4.plot(range(POINTS),self.s4e,label='ECMP %',linewidth=3) self.s4_s,=self.ax4.plot(range(POINTS),self.s4s,label='STAT %',linewidth=3) self.s4_w,=self.ax4.plot(range(POINTS),self.s4w,label='WEIG %',linewidth=3) # add the legend self.ax1.legend(loc='upper center', bbox_to_anchor=(0.5,1.5), ncol=4, prop=font_manager.FontProperties(size=10)) # force a draw on the canvas() # trick to show the grid and the legend self.canvas.draw() # save the clean background - everything but the line # is drawn and saved in the pixel buffer background self.bg1 = self.canvas.copy_from_bbox(self.ax1.bbox) self.bg2 = self.canvas.copy_from_bbox(self.ax2.bbox) self.bg3 = self.canvas.copy_from_bbox(self.ax3.bbox) self.bg4 = self.canvas.copy_from_bbox(self.ax4.bbox) # bind events coming from timer with id = TIMER_ID # to the onTimer callback function self.timer = wx.Timer(self) self.timer.Start(500) self.Bind(wx.EVT_TIMER, self.onTimer,self.timer) def onTimer(self, evt): """callback function for timer events""" # restore the clean background, saved at the beginning if self.count<=46: self.count+=1 else : self.timer.Stop() self.canvas.restore_region(self.bg1) self.canvas.restore_region(self.bg2) self.canvas.restore_region(self.bg3) self.canvas.restore_region(self.bg4) # update the data with open('cpu_record_ecmp','r') as fecmp: dataecmp=fecmp.read().split("\n")[:-1] ecmp1=[s.split(" ")[0] for s in dataecmp] ecmp2=[s.split(" ")[1] for s in dataecmp] ecmp3=[s.split(" ")[2] for s in dataecmp] ecmp4=[s.split(" ")[3] for s in dataecmp] with open('cpu_record_static','r') as fstatic: datastatic=fstatic.read().split("\n")[:-1] static1=[s.split(" ")[0] for s in datastatic] static2=[s.split(" ")[1] for s in datastatic] static3=[s.split(" ")[2] for s in datastatic] static4=[s.split(" ")[3] for s in datastatic] with open('cpu_record_weight','r') as fweight: dataweight=fweight.read().split("\n")[:-1] weight1=[s.split(" ")[0] for s in dataweight] weight2=[s.split(" ")[1] for s in dataweight] weight3=[s.split(" ")[2] for s in dataweight] weight4=[s.split(" ")[3] for s in dataweight] self.s1e = self.s1e[1:] + [ecmp1[self.count]] self.s1s = self.s1s[1:] + [static1[self.count]] self.s1w = self.s1w[1:] + [weight1[self.count]] self.s2e = self.s2e[1:] + [ecmp2[self.count]] self.s2s = self.s2s[1:] + [static2[self.count]] self.s2w = self.s2w[1:] + [weight2[self.count]] self.s3e = self.s3e[1:] + [ecmp3[self.count]] self.s3s = self.s3s[1:] + [static3[self.count]] self.s3w = self.s3w[1:] + [weight3[self.count]] self.s4e = self.s4e[1:] + [ecmp4[self.count]] self.s4s = self.s4s[1:] + [static4[self.count]] self.s4w = self.s4w[1:] + [weight4[self.count]] # update the plot self.s1_e.set_ydata(self.s1e) self.s1_s.set_ydata(self.s1s) self.s1_w.set_ydata(self.s1w) self.s2_e.set_ydata(self.s2e) self.s2_s.set_ydata(self.s2s) self.s2_w.set_ydata(self.s2w) self.s3_e.set_ydata(self.s3e) self.s3_s.set_ydata(self.s3s) self.s3_w.set_ydata(self.s3w) self.s4_e.set_ydata(self.s4e) self.s4_s.set_ydata(self.s4s) self.s4_w.set_ydata(self.s4w) # just draw the "animated" objects self.ax1.draw_artist(self.s1_e) self.ax1.draw_artist(self.s1_s) self.ax1.draw_artist(self.s1_w) self.ax2.draw_artist(self.s2_e) self.ax2.draw_artist(self.s2_s) self.ax2.draw_artist(self.s2_w) self.ax3.draw_artist(self.s3_e) self.ax3.draw_artist(self.s3_s) self.ax3.draw_artist(self.s3_w) self.ax4.draw_artist(self.s4_e) self.ax4.draw_artist(self.s4_s) self.ax4.draw_artist(self.s4_w) # It is used to efficiently update Axes data (axis ticks, labels, etc are not updated) self.canvas.blit(self.ax1.bbox) self.canvas.blit(self.ax2.bbox) self.canvas.blit(self.ax3.bbox) self.canvas.blit(self.ax4.bbox)
class Spectrogram(StandardMonitorPage): """Main class for a page that generates real-time spectrogram plots of EEG. """ def __init__(self, *args, **kwargs): """Construct a new Spectrogram page. Args: *args, **kwargs: Arguments to pass to the Page base class. """ self.initConfig() # initialize Page base class StandardMonitorPage.__init__(self, name='Spectrogram', configPanelClass=ConfigPanel, *args, **kwargs) self.initCanvas() self.initLayout() def initConfig(self): self.filter = True # use raw or filtered signal self.chanIndex = 0 # index of channel to show self.width = 5.0 # width of window to use for computing PSD self.decimationFactor = 1 # decimation factor, e.g., 2 will decimate to half sampRate self.interpolation = 'none' self.normScale = 'log' self.scale = -2 self.method = 'Wavelet' self.setRefreshDelay(200) self.waveletConfig = util.Holder(nFreq=100, span=10) self.fourierConfig = util.Holder() def initCanvas(self): """Initialize a new matplotlib canvas, figure and axis. """ self.plotPanel = wx.Panel(self) self.plotPanel.SetBackgroundColour('white') plotSizer = wx.BoxSizer(orient=wx.VERTICAL) self.plotPanel.SetSizer(plotSizer) self.fig = plt.Figure(facecolor='white') #self.canvas = FigureCanvas(parent=self, id=wx.ID_ANY, figure=self.fig) self.canvas = FigureCanvas(parent=self.plotPanel, id=wx.ID_ANY, figure=self.fig) self.ax = self.fig.add_subplot(1, 1, 1) self.ax.set_xlabel('Time (s)') self.ax.set_ylabel('Frequency (Hz)') self.cbAx = self.fig.add_axes([0.91, 0.05, 0.03, 0.93]) #self.fig.subplots_adjust(hspace=0.0, wspace=0.0, # left=0.035, right=0.92, top=0.98, bottom=0.05) self.adjustMargins() self.firstPlot() self.lastSize = (0, 0) self.needsResizePlot = True self.canvas.Bind(wx.EVT_SIZE, self.resizePlot) self.canvas.Bind(wx.EVT_IDLE, self.idleResizePlot) ##self.plotToolbar = widgets.PyPlotNavbar(self.canvas) ##plotSizer.Add(self.plotToolbar, proportion=0, flag=wx.EXPAND) plotSizer.Add(self.canvas, proportion=1, flag=wx.EXPAND) #self.plotToolbar.Hide() def initLayout(self): self.initStandardLayout() plotPaneAuiInfo = aui.AuiPaneInfo().Name('canvas').Caption( 'Spectrogram').CenterPane() #self.auiManager.AddPane(self.canvas, plotPaneAuiInfo) self.auiManager.AddPane(self.plotPanel, plotPaneAuiInfo) self.auiManager.Update() self.canvas.Hide() def afterUpdateSource(self): self.configPanel.updateChannels() def afterStart(self): # make sure canvas is visible self.canvas.Show() self.plotPanel.Layout() # trigger initial plot update self.needsFirstPlot = True def getCap(self): cap = self.src.getEEGSecs(self.width, filter=self.filter, copy=False) if self.decimationFactor > 1: cap.decimate(self.decimationFactor) return cap def getSpectrum(self, cap): # configurable XXX - idfah data = cap.data[:, self.chanIndex] * sig.windows.tukey( cap.data.shape[0]) # tukey or hann? XXX - idfah freqs, powers, phases = self.cwt.apply(data) # configurable XXX - idfah powers = np.clip(powers, 1.0e-10, np.inf) return freqs, powers def firstPlot(self, event=None): cap = self.getCap() self.cwt = sig.CWT(sampRate=cap.getSampRate(), freqs=self.waveletConfig.nFreq, span=self.waveletConfig.span) if self.isRunning(): freqs, powers = self.getSpectrum(cap) else: freqs = np.arange(1, self.src.getSampRate() // 2 + 1) powers = np.zeros((128, 10, 1)) powers[0, 0, 0] = 1.0 self.ax.cla() self.cbAx.cla() self.ax.set_xlabel('Time (s)') self.ax.set_ylabel('Frequency (Hz)') self.wimg = self.ax.imshow(powers[:, :, 0].T, interpolation=self.interpolation, origin='lower', aspect='auto', norm=self.getNorm(), extent=self.getExtent(cap, freqs), cmap=plt.cm.get_cmap('jet'), animated=True) self.cbar = self.fig.colorbar(self.wimg, cax=self.cbAx) self.cbar.set_label(r'Power Density ($V^2 / Hz$)') #self.updateNorm(powers) self.canvas.draw() #self.background = self.canvas.copy_from_bbox(self.fig.bbox) self.background = self.canvas.copy_from_bbox(self.ax.bbox) self.needsFirstPlot = False def adjustMargins(self): self.fig.subplots_adjust(hspace=0.0, wspace=0.0, left=0.045, right=0.90, top=0.98, bottom=0.07) def resizePlot(self, event): # prevents handling extra resize events, hack XXX - idfah size = self.canvas.GetSize() if self.lastSize == size: return else: self.lastSize = size # this is all a hack to do resizing on idle when page is not running # should this be a custom FigureCanvas derived widget? XXX - idfah if self.isRunning(): # when running, just do event.Skip() this will # call canvas._onSize since it is second handler self.needsResizePlot = False event.Skip() else: # flag to resize on next idle event self.needsResizePlot = True def idleResizePlot(self, event): # if not running and flagged for resize if not self.isRunning() and self.needsResizePlot: ##self.adjustMargins() self.needsResizePlot = False # call canvas resize method manually # hack alert, we just pass None as event # since it's not used anyway self.canvas._onSize(None) def getExtent(self, cap, freqs): return (0.0, cap.getNObs() / float(cap.getSampRate()), np.min(freqs), np.max(freqs)) def getNorm(self): mx = 10**self.scale if self.normScale == 'linear': mn = 0.0 norm = pltLinNorm(mn, mx) elif self.normScale == 'log': mn = 1e-10 norm = pltLogNorm(mn, mx) else: raise RuntimeError('Invalid norm %s.' % norm) return norm def updatePlot(self, event=None): """Draw the spectrogram plot. """ if self.needsFirstPlot: self.firstPlot() else: cap = self.getCap() freqs, powers = self.getSpectrum(cap) #self.updateNorm(powers) self.canvas.restore_region(self.background) self.wimg.set_array(powers[:, :, 0].T) self.wimg.set_extent(self.getExtent(cap, freqs)) self.ax.draw_artist(self.wimg) ##self.cbAx.draw_artist(self.cbar.patch) ##self.cbAx.draw_artist(self.cbar.solids) #self.cbar.draw_all() #self.canvas.blit(self.cbAx.bbox) #self.canvas.blit(self.fig.bbox) self.canvas.blit(self.ax.bbox) # for debugging, redraws everything ##self.canvas.draw() def captureImage(self, event=None): ## Parts borrowed from backends_wx.py from matplotlib # Fetch the required filename and file type. filetypes, exts, filter_index = self.canvas._get_imagesave_wildcards() default_file = self.canvas.get_default_filename() dlg = wx.FileDialog(self, "Save to file", "", default_file, filetypes, wx.SAVE | wx.OVERWRITE_PROMPT) dlg.SetFilterIndex(filter_index) if dlg.ShowModal() == wx.ID_OK: dirname = dlg.GetDirectory() filename = dlg.GetFilename() format = exts[dlg.GetFilterIndex()] basename, ext = os.path.splitext(filename) if ext.startswith('.'): ext = ext[1:] if ext in ('svg', 'pdf', 'ps', 'eps', 'png') and format != ext: #looks like they forgot to set the image type drop #down, going with the extension. format = ext self.canvas.print_figure(os.path.join(dirname, filename), format=format)
class PanelGraph(wx.Panel): def __init__(self, panel, notify, settings, callbackMotion, remoteControl): self.panel = panel self.notify = notify self.plot = None self.settings = settings self.remoteControl = remoteControl self.spectrum = None self.isLimited = None self.limit = None self.extent = None self.annotate = None self.mouseSelect = None self.mouseZoom = None self.measureTable = None self.background = None self.selectStart = None self.selectEnd = None self.menuClearSelect = [] self.measure = None self.show = None self.doDraw = False wx.Panel.__init__(self, panel) self.figure = matplotlib.figure.Figure(facecolor='white') self.canvas = FigureCanvas(self, -1, self.figure) self.measureTable = PanelMeasure(self, settings) self.toolbar = NavigationToolbar(self.canvas, self, settings, self.__hide_overlay) self.toolbar.Realize() vbox = wx.BoxSizer(wx.VERTICAL) vbox.Add(self.canvas, 1, wx.EXPAND) vbox.Add(self.measureTable, 0, wx.EXPAND) vbox.Add(self.toolbar, 0, wx.EXPAND) self.SetSizer(vbox) vbox.Fit(self) self.create_plot() self.canvas.mpl_connect('button_press_event', self.__on_press) self.canvas.mpl_connect('motion_notify_event', callbackMotion) self.canvas.mpl_connect('draw_event', self.__on_draw) self.canvas.mpl_connect('idle_event', self.__on_idle) self.Bind(wx.EVT_SIZE, self.__on_size) self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.__on_timer, self.timer) def __set_fonts(self): axes = self.plot.get_axes() if axes is not None: axes.xaxis.label.set_size('small') axes.yaxis.label.set_size('small') if self.settings.display == Display.SURFACE: axes.zaxis.label.set_size('small') axes.tick_params(axis='both', which='major', labelsize='small') axes = self.plot.get_axes_bar() if axes is not None: axes.tick_params(axis='both', which='major', labelsize='small') def __enable_menu(self, state): for menu in self.menuClearSelect: menu.Enable(state) def __on_press(self, event): if self.settings.clickTune and matplotlib.__version__ >= '1.2' and event.dblclick: frequency = int(event.xdata * 1e6) self.remoteControl.tune(frequency) def __on_size(self, event): ppi = wx.ScreenDC().GetPPI() size = [float(v) for v in self.canvas.GetSize()] width = size[0] / ppi[0] height = size[1] / ppi[1] self.figure.set_figwidth(width) self.figure.set_figheight(height) self.figure.set_dpi(ppi[0]) event.Skip() def __on_draw(self, _event): axes = self.plot.get_axes() if axes is not None: self.background = self.canvas.copy_from_bbox(axes.bbox) self.__draw_overlay() def __on_idle(self, _event): if self.doDraw and self.plot.get_plot_thread() is None: self.__hide_overlay() self.canvas.draw() self.doDraw = False def __on_timer(self, _event): self.timer.Stop() self.set_plot(None, None, None, None, self.annotate) def __draw_overlay(self): if self.background is not None: self.canvas.restore_region(self.background) self.__draw_select() self.draw_measure() axes = self.plot.get_axes() if axes is None: self.canvas.draw() else: self.canvas.blit(axes.bbox) def __draw_select(self): if self.selectStart is not None and self.selectEnd is not None: self.mouseSelect.draw(self.selectStart, self.selectEnd) def __hide_overlay(self): if self.plot is not None: self.plot.hide_measure() self.__hide_select() def __hide_select(self): if self.mouseSelect is not None: self.mouseSelect.hide() def create_plot(self): if self.plot is not None: self.plot.close() self.toolbar.set_auto(True) if self.settings.display == Display.PLOT: self.plot = Plotter(self.notify, self.figure, self.settings) elif self.settings.display == Display.SPECT: self.plot = Spectrogram(self.notify, self.figure, self.settings) elif self.settings.display == Display.SURFACE: self.plot = Plotter3d(self.notify, self.figure, self.settings) elif self.settings.display == Display.STATUS: self.plot = PlotterStatus(self.notify, self.figure, self.settings) else: self.plot = PlotterTime(self.notify, self.figure, self.settings) self.__set_fonts() self.toolbar.set_plot(self.plot) self.toolbar.set_type(self.settings.display) self.measureTable.set_type(self.settings.display) self.set_plot_title() self.figure.subplots_adjust(top=0.85) self.redraw_plot() self.plot.scale_plot(True) self.mouseZoom = MouseZoom(self.toolbar, plot=self.plot, callbackHide=self.__hide_overlay) self.mouseSelect = MouseSelect(self.plot, self.on_select, self.on_selected) self.measureTable.show(self.settings.showMeasure) self.panel.SetFocus() def on_select(self): self.hide_measure() def on_selected(self, start, end): self.__enable_menu(True) self.selectStart = start self.selectEnd = end self.measureTable.set_selected(self.spectrum, start, end) def add_menu_clear_select(self, menu): self.menuClearSelect.append(menu) menu.Enable(False) def draw(self): self.doDraw = True def show_measure_table(self, show): self.measureTable.show(show) self.Layout() def set_plot(self, spectrum, isLimited, limit, extent, annotate=False): if spectrum is not None and extent is not None: if isLimited is not None and limit is not None: self.spectrum = copy.copy(spectrum) self.extent = extent self.annotate = annotate self.isLimited = isLimited self.limit = limit if self.plot.get_plot_thread() is None: self.timer.Stop() self.measureTable.set_selected(self.spectrum, self.selectStart, self.selectEnd) if isLimited: spectrum = reduce_points(spectrum, limit) self.plot.set_plot(self.spectrum, self.extent, annotate) else: self.timer.Start(200, oneShot=True) def set_plot_title(self): if len(self.settings.devicesRtl) > 0: gain = self.settings.devicesRtl[self.settings.indexRtl].gain else: gain = 0 self.plot.set_title("Frequency Spectrogram\n{} - {} MHz," " gain = {}dB".format(self.settings.start, self.settings.stop, gain)) def redraw_plot(self): if self.spectrum is not None: self.set_plot(self.spectrum, self.settings.pointsLimit, self.settings.pointsMax, self.extent, self.settings.annotate) def set_grid(self, on): self.plot.set_grid(on) def hide_measure(self): if self.plot is not None: self.plot.hide_measure() def draw_measure(self): if self.measure is not None and self.measure.is_valid(): self.plot.draw_measure(self.measure, self.show) def update_measure(self, measure=None, show=None): if not measure and not show: self.measureTable.update_measure() else: self.measure = measure self.show = show self.__draw_overlay() def get_figure(self): return self.figure def get_axes(self): return self.plot.get_axes() def get_canvas(self): return self.canvas def get_toolbar(self): return self.toolbar def scale_plot(self, force=False): self.plot.scale_plot(force) def clear_plots(self): self.plot.clear_plots() self.spectrum = None self.doDraw = True def clear_selection(self): self.measure = None self.measureTable.clear_measurement() self.selectStart = None self.selectEnd = None self.mouseSelect.clear() self.__enable_menu(False) def close(self): close_modeless()
class PanelGraph(wx.Panel): def __init__(self, panel, notify, settings, callbackMotion): self.panel = panel self.notify = notify self.plot = None self.settings = settings self.spectrum = None self.isLimited = None self.limit = None self.extent = None self.mouseSelect = None self.mouseZoom = None self.measureTable = None self.background = None self.selectStart = None self.selectEnd = None self.menuClearSelect = [] self.measure = None self.show = None self.doDraw = False wx.Panel.__init__(self, panel) self.figure = matplotlib.figure.Figure(facecolor='white') self.canvas = FigureCanvas(self, -1, self.figure) self.measureTable = PanelMeasure(self) self.toolbar = NavigationToolbar(self.canvas, self, settings, self.hide_overlay) self.toolbar.Realize() vbox = wx.BoxSizer(wx.VERTICAL) vbox.Add(self.canvas, 1, wx.EXPAND) vbox.Add(self.measureTable, 0, wx.EXPAND) vbox.Add(self.toolbar, 0, wx.EXPAND) self.SetSizer(vbox) vbox.Fit(self) self.create_plot() self.canvas.mpl_connect('motion_notify_event', callbackMotion) self.canvas.mpl_connect('draw_event', self.on_draw) self.canvas.mpl_connect('idle_event', self.on_idle) self.Bind(wx.EVT_SIZE, self.on_size) self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.on_timer, self.timer) def create_plot(self): if self.plot is not None: self.plot.close() self.toolbar.set_auto(True) if self.settings.display == Display.PLOT: self.plot = Plotter(self.notify, self.figure, self.settings) elif self.settings.display == Display.SPECT: self.plot = Spectrogram(self.notify, self.figure, self.settings) else: self.plot = Plotter3d(self.notify, self.figure, self.settings) self.set_fonts() self.toolbar.set_plot(self.plot) self.toolbar.set_type(self.settings.display) self.measureTable.set_type(self.settings.display) self.set_plot_title() self.figure.tight_layout() self.figure.subplots_adjust(top=0.85) self.redraw_plot() self.plot.scale_plot(True) self.mouseZoom = MouseZoom(self.plot, self.toolbar, self.hide_overlay) self.mouseSelect = MouseSelect(self.plot, self.on_select, self.on_selected) self.measureTable.show(self.settings.showMeasure) self.panel.SetFocus() def set_fonts(self): axes = self.plot.get_axes() axes.xaxis.label.set_size('small') axes.yaxis.label.set_size('small') if self.settings.display == Display.SURFACE: axes.zaxis.label.set_size('small') axes.tick_params(axis='both', which='major', labelsize='small') axes = self.plot.get_axes_bar() axes.tick_params(axis='both', which='major', labelsize='small') def add_menu_clear_select(self, menu): self.menuClearSelect.append(menu) menu.Enable(False) def enable_menu(self, state): for menu in self.menuClearSelect: menu.Enable(state) def on_size(self, event): ppi = wx.ScreenDC().GetPPI() size = [float(v) for v in self.canvas.GetSize()] width = size[0] / ppi[0] height = size[1] / ppi[1] self.figure.set_figwidth(width) self.figure.set_figheight(height) self.figure.set_dpi(ppi[0]) event.Skip() def on_draw(self, _event): axes = self.plot.get_axes() self.background = self.canvas.copy_from_bbox(axes.bbox) self.draw_overlay() def on_select(self): self.hide_measure() def on_selected(self, start, end): self.enable_menu(True) self.selectStart = start self.selectEnd = end self.measureTable.set_selected(self.spectrum, start, end) def on_idle(self, _event): if self.doDraw and self.plot.get_plot_thread() is None: self.hide_overlay() self.canvas.draw() self.doDraw = False def on_timer(self, _event): self.timer.Stop() self.set_plot(None, None, None, None, self.annotate) def draw(self): self.doDraw = True def show_measureTable(self, show): self.measureTable.show(show) self.Layout() def set_plot(self, spectrum, isLimited, limit, extent, annotate=False): if spectrum is not None and extent is not None: if isLimited is not None and limit is not None: self.spectrum = copy.copy(spectrum) self.extent = extent self.annotate = annotate self.isLimited = isLimited self.limit = limit if self.plot.get_plot_thread() is None: self.timer.Stop() self.measureTable.set_selected(self.spectrum, self.selectStart, self.selectEnd) if isLimited: spectrum = reduce_points(spectrum, limit) self.plot.set_plot(self.spectrum, self.extent, annotate) else: self.timer.Start(200, oneShot=True) def set_plot_title(self): if len(self.settings.devices) > 0: gain = self.settings.devices[self.settings.index].gain else: gain = 0 self.figure.suptitle("Frequency Spectrogram\n{0} - {1} MHz," " gain = {2}dB".format(self.settings.start, self.settings.stop, gain)) def redraw_plot(self): if self.spectrum is not None: self.set_plot(self.spectrum, self.settings.pointsLimit, self.settings.pointsMax, self.extent, self.settings.annotate) def set_grid(self, on): self.plot.set_grid(on) def draw_overlay(self): if self.background is not None: self.canvas.restore_region(self.background) self.draw_select() self.draw_measure() self.canvas.blit(self.plot.get_axes().bbox) def draw_select(self): if self.selectStart is not None and self.selectEnd is not None: self.mouseSelect.draw(self.selectStart, self.selectEnd) def hide_overlay(self): if self.plot is not None: self.plot.hide_measure() self.hide_select() def hide_measure(self): if self.plot is not None: self.plot.hide_measure() def hide_select(self): if self.mouseSelect is not None: self.mouseSelect.hide() def draw_measure(self): if self.measure is not None and self.measure.is_valid(): self.plot.draw_measure(self.measure, self.show) def update_measure(self, measure, show): self.measure = measure self.show = show self.draw_overlay() def get_figure(self): return self.figure def get_axes(self): return self.plot.get_axes() def get_canvas(self): return self.canvas def get_toolbar(self): return self.toolbar def scale_plot(self, force=False): self.plot.scale_plot(force) def clear_plots(self): self.plot.clear_plots() def clear_selection(self): self.measure = None self.measureTable.clear_measurement() self.selectStart = None self.selectEnd = None self.mouseSelect.clear() self.enable_menu(False) def close(self): close_modeless()
class NaoPanel(wx.Panel): def _init_ctrls(self, prnt): # generated method, don't edit wx.Panel.__init__(self, id=wxID_LEFTPANEL, name='NaoPanel', parent=prnt, pos=wx.Point(208, 8), size=wx.Size(800, 400), style=wx.NO_BORDER | wx.TAB_TRAVERSAL) self.SetClientSize(wx.Size(800, 400)) self.SetBackgroundColour(wx.Colour(0, 0, 255)) self.Bind(wx.EVT_PAINT, self.OnNaoPanelPaint) def __init__(self, parent, id, pos, size, style, name): self._init_ctrls(parent) ##Create a matplotlib figure/canvas in this panel ##the background colour will be the same as the panel ##the size will also be the same as the panel ##calculate size in inches pixels_width,pixels_height = self.GetSizeTuple() self.dpi = 96.0 inches_width = pixels_width/self.dpi inches_height = pixels_height/self.dpi ##calculate colour in RGB 0 to 1 colour = self.GetBackgroundColour() self.fig = Figure(figsize=(inches_width,inches_height), dpi = self.dpi\ ,facecolor=(colour.Red()/255.0, colour.Green()/255.0, colour.Blue()/255.0)\ ,edgecolor=(colour.Red()/255.0, colour.Green()/255.0, colour.Blue()/255.0)) ##left : the left side of the subplots of the figure ## | right : the right side of the subplots of the figure ## | bottom : the bottom of the subplots of the figure ## | top : the top of the subplots of the figure ## | wspace : the amount of width reserved for blank space between subplots ## | hspace : the amount of height reserved for white space between subplots ## | self.canvas = FigureCanvasWxAgg(self, -1, self.fig) ##now put everything in a sizer sizer = wx.BoxSizer(wx.VERTICAL) # This way of adding to sizer allows resizing sizer.Add(self.canvas, 1, wx.LEFT|wx.TOP|wx.GROW) self.SetSizer(sizer) self.Fit() ##now finally create the actual plot ##self.axes = self.fig.add_subplot(111) self.axes = self.fig.add_axes((0.08,0.08,0.90,0.85)) ##left,bottom,width,height self.naohistoryplot = self.axes.plot([0,0],[0,0], 'r', animated=True) self.naohistoryx = list() self.naohistoryy = list() self.positionmeasurementplot = self.axes.plot([0,0],[0,0], 'blue', marker='o', markersize=5, linewidth=0, markeredgewidth=0, animated=True) self.orientationmeasurementplot = self.axes.plot([0,0],[0,0], 'blue', linewidth=2, animated=True) self.shapeplot = self.axes.plot([0,0],[0,0], 'blue', marker='o', markersize=2, linewidth=0, markeredgewidth=0, animated=True) self.estimateplot = self.axes.plot([0,0],[0,0], 'red', linewidth=2, animated=True) self.particleplot = self.axes.quiver([0,0],[0,0], [1,1], [0.5, -0.5], [1, 1], cmap=pylab.gray(), animated=True) ##plot formatting self.axes.set_title('Nao Image', fontsize='10') self.axes.set_xlabel('y (cm)', fontsize='10') self.axes.set_ylabel('x (cm)', fontsize='10') ticks = numpy.arange(-25, 25 + 5, 5) labels = [str(tick) for tick in ticks] self.axes.set_yticks(ticks) self.axes.set_yticklabels(labels, fontsize=8) self.axes.set_ylim(ticks[0], ticks[-1]) ticks = -numpy.arange(-50, 50+5, 5) labels = [str(tick) for tick in ticks] self.axes.set_xticks(ticks) self.axes.set_xticklabels(labels,fontsize=8) self.axes.set_xlim(ticks[0], ticks[-1]) self.canvas.draw() self.canvas.gui_repaint() # save the clean slate background -- everything but the animated line # is drawn and saved in the pixel buffer background self.background = self.canvas.copy_from_bbox(self.axes.bbox) #self.leftedgeplot = self.axes.plot([0,0],[0,0], 'orange', marker='o', markersize=4, linewidth=0, animated=True) #self.rightedgeplot = self.axes.plot([0,0],[0,0], 'purple', marker='o', markersize=4, linewidth=0, animated=True) def setNaoFinder(self, finder): """ """ self.NAOFinder = finder def setLocalisation(self, localisation): """ """ self.Localisation = localisation def updateData(self, data): """updateData. Updates the data that this panel is displaying. """ # Note the x values are plotted on the y-axis, and the y values are plotted on the x-axis naox = self.Localisation.X naoy = self.Localisation.Y naoorientation = self.Localisation.Orientation measurednaox = self.NAOFinder.NaoX measurednaoy = self.NAOFinder.NaoY measurednaoorientation = self.NAOFinder.NaoOrientation self.positionmeasurementplot[0].set_data([measurednaoy, measurednaoy], [measurednaox, measurednaox]) self.orientationmeasurementplot[0].set_data([measurednaoy + 10*numpy.sin(measurednaoorientation - numpy.pi), measurednaoy + 10*numpy.sin(measurednaoorientation)], [measurednaox + 10*numpy.cos(measurednaoorientation - numpy.pi), measurednaox + 10*numpy.cos(measurednaoorientation)]) self.shapeplot[0].set_data(self.NAOFinder.ShapeY, self.NAOFinder.ShapeX) self.naohistoryx.append(naox) self.naohistoryy.append(naoy) if len(self.naohistoryx) > 20: del self.naohistoryx[0] del self.naohistoryy[0] self.naohistoryplot[0].set_data(self.naohistoryy, self.naohistoryx) self.estimateplot[0].set_data([naoy, naoy + 10*numpy.sin(self.Localisation.Orientation)], [naox, naox + 10*numpy.cos(self.Localisation.Orientation)]) #self.particleplot = self.axes.quiver(numpy.array(self.Localisation.States[:,Localisation.Y]), numpy.array(self.Localisation.States[:,Localisation.X]), -numpy.sin(self.Localisation.States[:,Localisation.THETA]), numpy.cos(self.Localisation.States[:,Localisation.THETA]), 1.0 - self.Localisation.GUIWeights, headlength=10, headwidth=10, width=0.001, scale=50.0) self.axes.set_xlim(naoy + 50, naoy - 50) self.axes.set_ylim(naox - 25, naox + 25) # restore the clean slate background self.canvas.restore_region(self.background) # just draw the animated artist self.axes.draw_artist(self.shapeplot[0]) #self.axes.draw_artist(self.particleplot) self.axes.draw_artist(self.naohistoryplot[0]) self.axes.draw_artist(self.orientationmeasurementplot[0]) self.axes.draw_artist(self.positionmeasurementplot[0]) self.axes.draw_artist(self.estimateplot[0]) # just redraw the axes rectangle self.canvas.blit(self.axes.bbox) #leftx = list() #lefty = list() #for leftedge in self.NAOFinder.LeftEdges: # leftx.append(data[0][leftedge]) # lefty.append(data[1][leftedge]) #rightx = list() #righty = list() #for rightedge in self.NAOFinder.RightEdges: # rightx.append(data[0][rightedge]) # righty.append(data[1][rightedge]) #self.leftedgeplot[0].set_data(lefty, leftx) #self.rightedgeplot[0].set_data(righty, rightx) def OnNaoPanelPaint(self, event): pass
class BaseInteractorsPanel(wx.Panel): def __init__(self, parent=None): super().__init__(parent=parent) self.interactors: List = [] self.factor = None self.image_id = None self.background = None self.BuildUI() def BuildUI(self): self.figure: Figure = Figure() self.axes: Axes = self.figure.add_subplot(1, 1, 1) self.axes.set_aspect("equal") self.canvas = FigureCanvas(self, id=wx.ID_ANY, figure=self.figure) self.figure.tight_layout() sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.canvas, flag=wx.EXPAND, proportion=1) self.SetSizer(sizer) self.canvas.mpl_connect("draw_event", self.DrawCallback) self.canvas.mpl_connect("button_press_event", self.OnClick) self.canvas.mpl_connect("button_release_event", self.OnMouseButtonUp) self.canvas.mpl_connect("motion_notify_event", self.OnMouseMoved) self.canvas.mpl_connect("key_press_event", self.OnKeyPress) self.canvas.mpl_connect("key_release_event", self.OnKeyRelease) def DrawCallback(self, event: MouseEvent): self.background = self.canvas.copy_from_bbox(self.axes.bbox) for interactor in self.interactors: interactor.draw_callback(event) self.canvas.blit(self.axes.bbox) def OnClick(self, event: MouseEvent): if event.inaxes != self.axes: return if event.inaxes.get_navigate_mode() is not None: return for interactor in self.interactors: interactor.button_press_callback(event) def OnMouseButtonUp(self, event: MouseEvent): if event.inaxes is not None and event.inaxes.get_navigate_mode( ) is not None: return for interactor in self.interactors: interactor.button_release_callback(event) def OnMouseMoved(self, event: MouseEvent): if event.inaxes != self.axes: return self.UpdateInteractors(event) def OnKeyPress(self, event: MouseEvent): if event.inaxes != self.axes: return if event.inaxes.get_navigate_mode() is not None: return for interactor in self.interactors: interactor.key_press_event(event) self.UpdateInteractors(event) def OnKeyRelease(self, event: MouseEvent): if event.inaxes != self.axes: return if event.inaxes.get_navigate_mode() is not None: return for interactor in self.interactors: interactor.key_release_event(event) self.UpdateInteractors(event) def UpdateInteractors(self, event: MouseEvent): if self.background is not None: self.canvas.restore_region(self.background) else: self.background = self.canvas.copy_from_bbox(self.axes.bbox) for interactor in self.interactors: interactor.motion_notify_callback(event) interactor.draw_callback(event) self.canvas.blit(self.axes.bbox) def Render(self, image: Image): self.axes.clear() self.axes.imshow(image, interpolation="lanczos", vmin=0, vmax=255)
class PlotFrame(wx.Frame): """ PlotFrame is a custom wxPython frame to hold the panel with a Figure and WxAgg backend canvas for matplotlib plots or other figures. In this frame: self is an instance of a wxFrame; axes is an instance of MPL Axes; fig is an instance of MPL Figure; panel is an instance of wxPanel, used for the main panel, to hold canvas, an instance of MPL FigureCanvasWxAgg. """ # Main function to set everything up when the frame is created def __init__(self, title, pos, size): """ This will be executed when an instance of PlotFrame is created. It is the place to define any globals as "self.<name>". """ wx.Frame.__init__(self, None, wx.ID_ANY, title, pos, size) if len(sys.argv) < 2: self.filename = "" else: self.filename = sys.argv[1] # set some Boolean flags self.STOP = False self.data_loaded = False self.reverse_play = False self.step = 1 # Make the main Matplotlib panel for plots self.create_main_panel() # creates canvas and contents # Then add wxPython widgets below the MPL canvas # Layout with box sizers self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.EXPAND) self.sizer.AddSpacer(10) self.sizer.Add(self.toolbar, 0, wx.EXPAND) self.sizer.AddSpacer(10) # Make the control panel with a row of buttons self.create_button_bar() self.sizer.Add(self.button_bar_sizer, 0, flag = wx.ALIGN_CENTER | wx.TOP) # Make a Status Bar self.statusbar = self.CreateStatusBar() self.sizer.Add(self.statusbar, 0, wx.EXPAND) self.SetStatusText("Frame created ...") # ------------------------------------------------------- # set up the Menu Bar # ------------------------------------------------------- menuBar = wx.MenuBar() menuFile = wx.Menu() # File menu menuFile.Append(1, "&Open", "Filename(s) or wildcard list to plot") menuFile.Append(3, "Save", "Save plot as a PNG image") menuFile.AppendSeparator() menuFile.Append(10, "E&xit") menuBar.Append(menuFile, "&File") menuHelp = wx.Menu() # Help menu menuHelp.Append(11, "&About Netview") menuHelp.Append(12, "&Usage and Help") menuHelp.Append(13, "Program &Info") menuBar.Append(menuHelp, "&Help") self.SetMenuBar(menuBar) self.panel.SetSizer(self.sizer) self.sizer.Fit(self) # ------------------------------------------------------- # Bind the menu items to functions # ------------------------------------------------------- self.Bind(wx.EVT_MENU, self.OnOpen, id=1) self.Bind(wx.EVT_MENU, self.OnSave, id=3) self.Bind(wx.EVT_MENU, self.OnQuit, id=10) self.Bind(wx.EVT_MENU, self.OnAbout, id=11) self.Bind(wx.EVT_MENU, self.OnUsage, id=12) self.Bind(wx.EVT_MENU, self.OnInfo, id=13) # methods defined below to get and plot the data # Normally do the plot on request, and not here # self.get_data_params() # self.init_plot() # self.get_xyt_data() # plot_data() # ---------- end of __init__ ---------------------------- # ------------------------------------------------------- # Function to make the main Matplotlib panel for plots # ------------------------------------------------------- def create_main_panel(self): """ create_main_panel creates the main mpl panel with instances of: * mpl Canvas * mpl Figure * mpl Figure * mpl Axes with subplot * mpl Widget class Sliders and Button * mpl navigation toolbar self.axes is the instance of MPL Axes, and is where it all happens """ self.panel = wx.Panel(self) # Create the mpl Figure and FigCanvas objects. # 3.5 x 5 inches, 100 dots-per-inch # self.dpi = 100 self.fig = Figure((3.5, 5.0), dpi=self.dpi) self.canvas = FigCanvas(self.panel, wx.ID_ANY, self.fig) # Since we have only one plot, we could use add_axes # instead of add_subplot, but then the subplot # configuration tool in the navigation toolbar wouldn't work. self.axes = self.fig.add_subplot(111) # (111) == (1,1,1) --> row 1, col 1, Figure 1) # self.axes.set_title("View from: "+self.filename) # Now create some sliders below the plot after making room self.fig.subplots_adjust(left=0.1, bottom=0.20) self.axtmin = self.fig.add_axes([0.2, 0.10, 0.5, 0.03]) self.axtmax = self.fig.add_axes([0.2, 0.05, 0.5, 0.03]) self.stmin = Slider(self.axtmin, 't_min:', 0.0, 1.0, valinit = 0.0) self.stmax = Slider(self.axtmax, 't_max:', 0.0, 1.0, valinit = 1.0) self.stmin.on_changed(self.update_trange) self.stmax.on_changed(self.update_trange) self.axbutton = self.fig.add_axes([0.8, 0.07, 0.1, 0.07]) self.reset_button = Button(self.axbutton, 'Reset') self.reset_button.color = 'skyblue' self.reset_button.hovercolor = 'lightblue' self.reset_button.on_clicked(self.reset_trange) # Create the navigation toolbar, tied to the canvas self.toolbar = NavigationToolbar(self.canvas) def update_trange(self, event): self.t_min = self.stmin.val self.t_max = self.stmax.val # print(self.t_min, self.t_max) def reset_trange(self, event): self.stmin.reset() self.stmax.reset() def create_button_bar(self): """ create_button_bar makes a control panel bar with buttons and toggles for New Data - Play - STOP - Single Step - Forward/Back - Normal/Fast It does not create a Panel container, but simply creates Button objects with bindings, and adds them to a horizontal BoxSizer self.button_bar_sizer. This is added to the PlotFrame vertical BoxSizer, after the MPL canvas, during initialization of the frame. """ rewind_button = wx.Button(self.panel, -1, "New Data") self.Bind(wx.EVT_BUTTON, self.OnRewind, rewind_button) replot_button = wx.Button(self.panel, -1, "Play") self.Bind(wx.EVT_BUTTON, self.OnReplot, replot_button) sstep_button = wx.Button(self.panel, -1, "Single Step") self.Bind(wx.EVT_BUTTON, self.OnSstep, sstep_button) stop_button = wx.Button(self.panel, -1, "STOP") self.Bind(wx.EVT_BUTTON, self.OnStop, stop_button) # The toggle buttons need to be globally accessible self.forward_toggle = wx.ToggleButton(self.panel, -1, "Forward") self.forward_toggle.SetValue(True) self.forward_toggle.SetLabel("Forward") self.Bind(wx.EVT_TOGGLEBUTTON, self.OnForward, self.forward_toggle) self.fast_toggle = wx.ToggleButton(self.panel, -1, " Normal ") self.fast_toggle.SetValue(True) self.fast_toggle.SetLabel(" Normal ") self.Bind(wx.EVT_TOGGLEBUTTON, self.OnFast, self.fast_toggle) # Set button colors to some simple colors that are likely # to be independent on X11 color definitions. Some nice # bit maps (from a media player skin?) should be used # or the buttons and toggle state colors in OnFast() below rewind_button.SetBackgroundColour('skyblue') replot_button.SetBackgroundColour('skyblue') sstep_button.SetBackgroundColour('skyblue') stop_button.SetBackgroundColour('skyblue') self.forward_toggle.SetForegroundColour('black') self.forward_toggle.SetBackgroundColour('yellow') self.fast_toggle.SetForegroundColour('black') self.fast_toggle.SetBackgroundColour('yellow') self.button_bar_sizer = wx.BoxSizer(wx.HORIZONTAL) flags = wx.ALIGN_CENTER | wx.ALL self.button_bar_sizer.Add(rewind_button, 0, border=3, flag=flags) self.button_bar_sizer.Add(replot_button, 0, border=3, flag=flags) self.button_bar_sizer.Add(sstep_button, 0, border=3, flag=flags) self.button_bar_sizer.Add(stop_button, 0, border=3, flag=flags) self.button_bar_sizer.Add(self.forward_toggle, 0, border=3, flag=flags) self.button_bar_sizer.Add(self.fast_toggle, 0, border=3, flag=flags) # ------------------------------------------------------- # Functions to generate or read (x,y) data and plot it # ------------------------------------------------------- def get_data_params(self): # These parameters would normally be provided in a file header, # past as arguments in a function, or from other file information # Next version will bring up a dialog for dt NX NY if no file header # Here check to see if a filename should be entered from File/Open # self.filename = 'Ex_net_Vm_0001.txt' if len(self.filename) == 0: # fake a button press of File/Open self.OnOpen(wx.EVT_BUTTON) # should check here if file exists as specified [path]/filename # assume it is a bzip2 compressed file try: fp = bz2.BZ2File(self.filename) line = fp.readline() except IOError: # then assume plain text fp = open(self.filename) line = fp.readline() fp.close() # check if first line is a header line starting with '#' header = line.split() if header[0][0] == "#": self.Ntimes = int(header[1]) self.t_min = float(header[2]) self.dt = float(header[3]) self.NX = int(header[4]) self.NY = int(header[5]) else: pdentry = self.ParamEntryDialog() if pdentry.ShowModal() == wx.ID_OK: self.Ntimes = int(pdentry.Ntimes_dialog.entry.GetValue()) self.t_min = float(pdentry.tmin_dialog.entry.GetValue()) self.dt = float(pdentry.dt_dialog.entry.GetValue()) self.NX = int(pdentry.NX_dialog.entry.GetValue()) self.NY = int(pdentry.NY_dialog.entry.GetValue()) print 'Ntimes = ', self.Ntimes, ' t_min = ', self.t_min print 'NX = ', self.NX, ' NY = ', self.NY pdentry.Destroy() self.t_max = (self.Ntimes - 1)*self.dt # reset slider max and min self.stmin.valmax = self.t_max self.stmin.valinit = self.t_min self.stmax.valmax = self.t_max self.stmax.valinit = self.t_max self.stmax.set_val(self.t_max) self.stmin.reset() self.stmax.reset() fp.close() def init_plot(self): ''' init_plot creates the initial plot display. A normal MPL plot would be created here with a command "self.axes.plot(x, y)" in order to create a plot of points in the x and y arrays on the Axes subplot. Here, we create an AxesImage instance with imshow(), instead. The initial image is a blank one of the proper dimensions, filled with zeroes. ''' self.t_max = (self.Ntimes - 1)*self.dt self.axes.set_title("View of "+self.filename) # Note that NumPy array (row, col) = image (y, x) data0 = np.zeros((self.NY,self.NX)) # Define a 'cold' to 'hot' color scale based in GENESIS 2 'hot' hotcolors = ['#000032', '#00003c', '#000046', '#000050', '#00005a', '#000064', '#00006e', '#000078', '#000082', '#00008c', '#000096', '#0000a0', '#0000aa', '#0000b4', '#0000be', '#0000c8', '#0000d2', '#0000dc', '#0000e6', '#0000f0', '#0000fa', '#0000ff', '#000af6', '#0014ec', '#001ee2', '#0028d8', '#0032ce', '#003cc4', '#0046ba', '#0050b0', '#005aa6', '#00649c', '#006e92', '#007888', '#00827e', '#008c74', '#00966a', '#00a060', '#00aa56', '#00b44c', '#00be42', '#00c838', '#00d22e', '#00dc24', '#00e61a', '#00f010', '#00fa06', '#00ff00', '#0af600', '#14ec00', '#1ee200', '#28d800', '#32ce00', '#3cc400', '#46ba00', '#50b000', '#5aa600', '#649c00', '#6e9200', '#788800', '#827e00', '#8c7400', '#966a00', '#a06000', '#aa5600', '#b44c00', '#be4200', '#c83800', '#d22e00', '#dc2400', '#e61a00', '#f01000', '#fa0600', '#ff0000', '#ff0a00', '#ff1400', '#ff1e00', '#ff2800', '#ff3200', '#ff3c00', '#ff4600', '#ff5000', '#ff5a00', '#ff6400', '#ff6e00', '#ff7800', '#ff8200', '#ff8c00', '#ff9600', '#ffa000', '#ffaa00', '#ffb400', '#ffbe00', '#ffc800', '#ffd200', '#ffdc00', '#ffe600', '#fff000', '#fffa00', '#ffff00', '#ffff0a', '#ffff14', '#ffff1e', '#ffff28', '#ffff32', '#ffff3c', '#ffff46', '#ffff50', '#ffff5a', '#ffff64', '#ffff6e', '#ffff78', '#ffff82', '#ffff8c', '#ffff96', '#ffffa0', '#ffffaa', '#ffffb4', '#ffffbe', '#ffffc8', '#ffffd2', '#ffffdc', '#ffffe6', '#fffff0'] cmap = matplotlib.colors.ListedColormap(hotcolors) self.im = self.axes.imshow(data0, cmap=cmap, origin='lower') # http://matplotlib.sourceforge.net/examples/pylab_examples/show_colormaps.html # shows examples to use as a 'cold' to 'hot' mapping of value to color # cm.jet, cm.gnuplot and cm.afmhot are good choices, but are unlike G2 'hot' self.im.cmap=cmap # Not sure how to properly add a colorbar # self.cb = self.fig.colorbar(self.im, orientation='vertical') # refresh the canvas self.canvas.draw() def get_xyt_data(self): # Create scaled (0-1) luminance(x,y) array from ascii G-2 disk_out file # get the data to plot from the specified filename # Note that NumPy loadtxt transparently deals with bz2 compression self.SetStatusText('Data loading - please wait ....') rawdata = np.loadtxt(self.filename) # Note the difference between NumPy [row, col] order and network # x-y grid (x, y) = (col, row). We want a NumPy NY x NX, not # NX x NY, array to be used by the AxesImage object. xydata = np.resize(rawdata, (self.Ntimes, self.NY, self.NX)) # imshow expects the data to be scaled to range 0-1. Vmin = xydata.min() Vmax = xydata.max() self.ldata = (xydata - Vmin)/(Vmax - Vmin) self.data_loaded = True self.SetStatusText('Data has been loaded - click Play') def plot_data(self): ''' plot_data() shows successive frames of the data that was loaded into the ldata array. Creating a new self.im AxesImage instance for each frame is extremely slow, so the set_data method of AxesImage is used to load new data into the existing self.im for each frame. Normally 'self.canvas.draw()' would be used to display a frame, but redrawing the entire canvas, redraws the axes, labels, sliders, buttons, or anything else on the canvas. This uses a method taken from an example in Ch 7, p. 192 Matplotlib for Python developers, with draw_artist() and blit() redraw only the part that was changed. ''' if self.data_loaded == False: # bring up a warning dialog msg = """ Data for plotting has not been loaded! Please enter the file to plot with File/Open, unless it was already specified, and then click on 'New Data' to load the data to play back, before clicking 'Play'. """ wx.MessageBox(msg, "Plot Warning", wx.OK | wx.ICON_ERROR,self) return # set color limits self.im.set_clim(0.0, 1.0) self.im.set_interpolation('nearest') # 'None' is is slightly faster, but not implemented for MPL ver < 1.1 # self.im.set_interpolation('None') # do an initial draw, then save the empty figure axes self.canvas.draw() # self.bg = self.canvas.copy_from_bbox(self.axes.bbox) # However the save and restore is only needed if I change # axes legends, etc. The draw_artist(artist), and blit # are much faster than canvas.draw() and are sufficient. print 'system time (seconds) = ', time.time() # round frame_min down and frame_max up for the time window frame_min = int(self.t_min/self.dt) frame_max = min(int(self.t_max/self.dt) + 1, self.Ntimes) frame_step = self.step # Displaying simulation time to the status bar is much faster # than updating a slider progress bar, but location isn't optimum. # The check for the STOP button doesn't work because the button # click is not registered until this function exits. # check to see if self.reverse_play == True # then interchange frame_min, frame_max, and use negative step if self.reverse_play == True: frame_min = min(int(self.t_max/self.dt) + 1, self.Ntimes) - 1 frame_max = int(self.t_min/self.dt) - 1 frame_step = -self.step for frame_num in range(frame_min, frame_max, frame_step): self.SetStatusText('time: '+str(frame_num*self.dt)) if self.STOP == True: self.t_min = frame_num*self.dt # set t_min slider ? self.STOP = False break self.im.set_data(self.ldata[frame_num]) self.axes.draw_artist(self.im) self.canvas.blit(self.axes.bbox) print 'system time (seconds) = ', time.time() # ------------------------------------------------------------------ # Define the classes and functions for getting parameter values # -------------------------------------------------------------- class ParamEntryDialog(wx.Dialog): def __init__(self): wx.Dialog.__init__(self, None, wx.ID_ANY) self.SetSize((250, 200)) self.SetTitle('Enter Data File Parameters') vbox = wx.BoxSizer(wx.VERTICAL) self.Ntimes_dialog = XDialog(self) self.Ntimes_dialog.entry_label.SetLabel('Number of entries') self.Ntimes_dialog.entry.ChangeValue(str(2501)) self.tmin_dialog = XDialog(self) self.tmin_dialog.entry_label.SetLabel('Start time (sec)') self.tmin_dialog.entry.ChangeValue(str(0.0)) self.dt_dialog = XDialog(self) self.dt_dialog.entry_label.SetLabel('Output time step (sec)') self.dt_dialog.entry.ChangeValue(str(0.0002)) self.NX_dialog = XDialog(self) self.NX_dialog.entry_label.SetLabel('Number of cells on x-axis') self.NX_dialog.entry.ChangeValue(str(32)) self.NY_dialog = XDialog(self) self.NY_dialog.entry_label.SetLabel('Number of cells on y-axis') self.NY_dialog.entry.ChangeValue(str(32)) vbox.Add(self.Ntimes_dialog, 0, wx.EXPAND|wx.ALL, border=5) vbox.Add(self.tmin_dialog, 0, wx.EXPAND|wx.ALL, border=5) vbox.Add(self.dt_dialog, 0, wx.EXPAND|wx.ALL, border=5) vbox.Add(self.NX_dialog, 0, wx.EXPAND|wx.ALL, border=5) vbox.Add(self.NY_dialog, 0, wx.EXPAND|wx.ALL, border=5) okButton = wx.Button(self, wx.ID_OK,'Ok') # vbox.Add(okButton,flag=wx.ALIGN_CENTER|wx.TOP|wx.BOTTOM, border=10) vbox.Add(okButton,flag=wx.ALIGN_CENTER, border=10) self.SetSizer(vbox) self.SetSizerAndFit(vbox) # ------------------------------------------------------------------ # Define the functions executed on menu choices # --------------------------------------------------------------- def OnQuit(self, event): self.Close() def OnSave(self, event): file_choices = "PNG (*.png)|*.png" dlg = wx.FileDialog( self, message="Save plot as...", defaultDir=os.getcwd(), defaultFile="plot.png", wildcard=file_choices, style=wx.SAVE) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() self.canvas.print_figure(path, dpi=self.dpi) # self.flash_status_message("Saved to %s" % path) def OnAbout(self, event): msg = """ G-3 Netview ver. 1.7 Netview is a stand-alone Python application for viewing the output of GENESIS 2 and 3 network simulations. It is intended to replace GENESIS 2 SLI scripts that use the XODUS 'xview' widget. The design and operation is based on the G3Plot application for creating 2D plots of y(t) or y(x) from data files. Unlike G3Plot, the image created with Netview is an animated representation of a rectangular network with colored squares used to indicate the value of some variable at that position and time. Typically, this would be the membrane potenial of a cell soma, or a synaptic current in a dendrite segment. Help/Usage gives HTML help for using Netview. This is the main Help page. Help/Program Info provides some information about the objects and functions, and the wxPython and matplotlib classes used here. Dave Beeman, August 2012 """ dlg = wx.MessageDialog(self, msg, "About G-3 Netview", wx.OK | wx.ICON_QUESTION) dlg.ShowModal() dlg.Destroy() def OnOpen(self, event): dlg = wx.TextEntryDialog(self, "File with x,y data to plot", "File Open", self.filename, style=wx.OK|wx.CANCEL) if dlg.ShowModal() == wx.ID_OK: self.filename = dlg.GetValue() # A new filename has been entered, but the data has not been read self.data_loaded = False # print "You entered: %s" % self.filename dlg.Destroy() # This starts with the long string of HTML to display class UsageFrame(wx.Frame): text = """ <HTML> <HEAD></HEAD> <BODY BGCOLOR="#D6E7F7"> <CENTER><H1>Using G-3 Netview</H1></CENTER> <H2>Introduction and Quick Start</H2> <p>Netview is a stand-alone Python application for viewing the output of GENESIS 2 and 3 network simulations. It is intended to replace GENESIS 2 SLI scripts that use the XODUS 'xview' widget.</p> <p>The design and operation is based on the G3Plot application for creating 2D plots of y(t) or y(x) from data files. As with G3Plot, the main class PlotFrame uses a basic wxPython frame to embed a matplotlib figure for plotting. It defines some basic menu items and a control panel of buttons and toggles, each with bindings to a function to execute on a mouse click.</p> <p>Unlike G3Plot, the image created with Netview is an animated representation of a rectangular network with colored squares used to indicate the value of some variable at that position and time. Typically, this would be the membrane potenial of a cell soma, or a synaptic current in a dendrite segment.</p> <h2>Usage</h2> <p>The Menu Bar has <em>File/Open</em>, <em>File/Save</em>, and <em>File/Exit</em> choices. The Help Menu choices <em>About</em> and <em>Usage</em> give further information. The <em>Program Info</em> selection shows code documentation that is contained in some of the main function <em>docstrings</em>.</p> <p>After starting the <em>netview</em> program, enter a data file name in the dialog for File/Open, unless the filename was given as a command line argument. Then click on <strong>New Data</strong> to load the new data and initialize the plot. When the plot is cleared to black, press <strong>Play</strong>.</p> <p>The file types recognized are plain text or text files compressed with bzip2. The expected data format is one line for each output time step, with each line having the membrane potential value of each cell in the net. No time value should be given on the line. In order to properly display the data, netview needs some additional information about the network and the data. This can optionally be contained in a header line that precedes the data. If a header is not detected, a dialog will appear asking for the needed parameters.</p> <p>It is assumed that the cells are arranged on a NX x NY grid, numbered from 0 (bottom left corner) to NX*NY - 1 (upper right corner). In order to provide this information to netview, the data file should begin with a header line of the form:</p> <pre> #optional_RUNID_string Ntimes start_time dt NX NY SEP_X SEP_Y x0 y0 z0 </pre> <p>The line must start with "#" and can optionally be followed immediately by any string. Typically this is some identification string generated by the simulation run. The following parameters, separated by blanks or any whitespace, are:</p> <ul> <li>Ntimes - the number of lines in the file, exclusive of the header</li> <li>start_time - the simulation time for the first data line (default 0.0)</li> <li>dt - the time step used for output</li> <li>NX, NY - the integer dimensions of the network</li> <li>SEP_X, SEP_Y - the x,y distances between cells (optional)</li> <li>x0, y0, z0 - the location of the compartment (data source) relative to the cell origin</li> </ul> <p>The RUNID string and the last five parameters are not read or used by netview. These are available for other data analysis tools that need a RUNID and the location of each source.</p> <p>The slider bars can be used to set a time window for display, and the <strong>Reset</strong> button can set t_min and t_max back to the defaults. Use the <strong>Forward/Back</strong> toggle to reverse direction of <strong>Play</strong>, and the <strong>Normal/Fast</strong> toggle to show every tenth frame.</p> <p>The <strong>Single Step</strong> button can be used to advance a single step at a time (or 10, if in 'Fast' mode).</p> <p>The <strong>STOP</strong> button is currently not implemented</p> <p>To plot different data, enter a new filename with <strong>File/Open</strong> and repeat with <strong>New Data</strong> and <strong>Play</strong>.</p> <HR> </BODY> </HTML> """ def __init__(self, parent): wx.Frame.__init__(self, parent, -1, "Usage and Help", size=(640,600), pos=(400,100)) html = wx.html.HtmlWindow(self) html.SetPage(self.text) panel = wx.Panel(self, -1) button = wx.Button(panel, wx.ID_OK, "Close") self.Bind(wx.EVT_BUTTON, self.OnCloseMe, button) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(html, 1, wx.EXPAND|wx.ALL, 5) sizer.Add(panel, 0, wx.ALIGN_CENTER|wx.ALL, 5) self.SetSizer(sizer) self.Layout() def OnCloseMe(self, event): self.Close(True) # ----------- end of class UsageFrame --------------- def OnUsage(self,event): usagewin = self.UsageFrame(self) usagewin.Show(True) def OnInfo(self,event): msg = "Program information for PlotFrame obtained from docstrings:" msg += "\n" + self.__doc__ + "\n" + self.create_main_panel.__doc__ msg += self.create_button_bar.__doc__ msg += self.init_plot.__doc__ msg += self.plot_data.__doc__ dlg = wx.lib.dialogs.ScrolledMessageDialog(self, msg, "PlotFrame Documentation") dlg.ShowModal() # --------------------------------------------------------------- # Define the functions executed on control button click # --------------------------------------------------------------- def OnRewind(self,event): self.get_data_params() self.init_plot() self.get_xyt_data() def OnReplot(self,event): self.plot_data() self.canvas.draw() def OnSstep(self,event): if self.data_loaded == False: # bring up a warning dialog msg = """ Data for plotting has not been loaded! Please enter the file to plot with File/Open, unless it was already specified, and then click on 'New Data' to load the data to play back, before clicking 'Play'. """ wx.MessageBox(msg, "Plot Warning", wx.OK | wx.ICON_ERROR,self) return self.t_max = min(self.t_max + self.dt, (self.Ntimes - 1)*self.dt) self.stmax.set_val(self.t_max) frame_num = int(self.t_max/self.dt) self.SetStatusText('time: '+str(frame_num*self.dt)) self.im.set_data(self.ldata[frame_num]) self.axes.draw_artist(self.im) self.canvas.blit(self.axes.bbox) def OnStop(self,event): self.STOP = 'True' def OnForward(self,event): state = self.forward_toggle.GetValue() if state: self.reverse_play = False self.forward_toggle.SetLabel("Forward ") self.forward_toggle.SetForegroundColour('black') self.forward_toggle.SetBackgroundColour('yellow') else: self.reverse_play = True self.forward_toggle.SetLabel(" Back ") self.forward_toggle.SetForegroundColour('red') self.forward_toggle.SetBackgroundColour('green') def OnFast(self,event): state = self.fast_toggle.GetValue() if state: # print state self.fast_toggle.SetLabel(" Normal ") self.fast_toggle.SetForegroundColour('black') self.fast_toggle.SetBackgroundColour('yellow') self.step = 1 else: # print state self.fast_toggle.SetLabel(" Fast ") self.fast_toggle.SetForegroundColour('red') self.fast_toggle.SetBackgroundColour('green') self.step = 10
class PlotFigure(wx.Frame): def __init__(self): self.screen_size = wx.DisplaySize() #print("the screen size is {0}".format(self.screen_size)) self.screen_dpi = 100 self.frame_length = int(self.screen_size[0]) self.frame_width = int(self.screen_size[1]) self.fig_length = self.frame_length // self.screen_dpi self.fig_width = self.frame_width // self.screen_dpi self.frame_length = self.fig_length * self.screen_dpi self.frame_width = self.fig_width * self.screen_dpi wx.Frame.__init__(self, None, wx.ID_ANY, title='CP1/X86 TPCC Performance Comparison', size=(self.frame_length, self.frame_width) #size=(1900, 1000) ) # Matplotlib Figure, x/y-size should size_in_Frame/dpi #eg: 1800 = 15 * 120, 600 = 5 * 120 #self.fig = Figure((19, 10), 100) self.fig = Figure((self.fig_length, self.fig_width), self.screen_dpi) #print(self.frame_length, self.frame_width) #print(self.fig_length, self.fig_width) self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) self.ax = self.fig.add_subplot(211) self.ax.set_ylim([Y_MIN_VALUE, Y_MAX_VALUE]) self.ax.set_xlim([X_MIN_VALUE, X_MAX_VALUE]) self.ax.set_autoscale_on(False) #self.ax.set_xticks([]) self.ax.set_xticks(range(0, 61, 10)) #self.ax.set_xticks(range(X_MIN_VALUE, X_MAX_VALUE + 1, X_STEP_VALUE)) self.ax.set_yticks(range(Y_MIN_VALUE, Y_MAX_VALUE + 1, Y_STEP_VALUE)) self.ax.set_xlabel("Time(second)") self.ax.set_ylabel("Transactions Per Minute(tpmC)") self.ax.grid(True) self.power8_current_all_values = [None] * EMPTY_NUMBER self.x86_current_all_values = [None] * EMPTY_NUMBER self.power8_plot, = self.ax.plot( range(EMPTY_NUMBER), #self.power8_current_all_values, label='CP1 Value', self.power8_current_all_values, label='CP1 TPC-C', #color='red', linestyle = ':', linewidth = 2, marker = 'o' color='red', marker='.') self.x86_plot, = self.ax.plot( range(EMPTY_NUMBER), #self.x86_current_all_values, label='X86 Value', self.x86_current_all_values, label='X86 TPC-C', color='green', marker='.') self.ax.legend(loc='upper center', ncol=4, prop=font_manager.FontProperties(size=16) #prop=font_manager.FontProperties(size=10) ) # for 2nd subplot self.average = self.fig.add_subplot(212) self.average.set_ylim(0, 6) #self.average.set_xlim(Y_MIN_VALUE, Y_MAX_VALUE) self.average.set_xlim(Y_MIN_VALUE, 300000) #self.average.set_ylabel("yHello world") self.average.set_xlabel("Transactions Per Minute(tpmC)") self.power8_accumulate_value = 0 self.x86_accumulate_value = 0 self.power8_previous_value = 0 self.x86_previous_value = 0 self.power8_ave_index = [3] self.x86_ave_index = [1] self.power8_ave_value = [0] self.x86_ave_value = [0] self.power8_barh, = self.average.barh(bottom=self.power8_ave_index, width=self.power8_ave_value, height=1.0, color='red', label='CP1 TPC-C (Average)') self.x86_barh, = self.average.barh(bottom=self.x86_ave_index, width=self.x86_ave_value, height=1.0, color='green', label="X86 TPC-C (Average)") self.average.grid(True) self.average.legend(loc='upper center', ncol=4, prop=font_manager.FontProperties(size=16) #prop=font_manager.FontProperties(size=10) ) self.average.set_yticks([]) self.fig.subplots_adjust(left=0.08, right=0.95, bottom=0.05, top=0.95) ########################################################################################## # TODO: resize the subplot in figure self.ax.set_position([0.08, 0.40, 0.85, 0.55]) self.average.set_position([0.08, 0.05, 0.85, 0.28]) self.canvas.draw() # save the clean background self.background_1st = self.canvas.copy_from_bbox(self.ax.bbox) self.background_2nd = self.canvas.copy_from_bbox(self.average.bbox) self.global_timer_index = 0 self.local_timer_index = 0 self.power8_current_all_values = [] self.x86_current_all_values = [] wx.EVT_TIMER(self, TIMER_ID, self.on_timer) def on_timer(self, event): # restore the clean background, saved at the beginning self.canvas.restore_region(self.background_1st) self.canvas.restore_region(self.background_2nd) #copyfile(power8_source_filename, power8_input_filename) #copyfile(x86_source_filename, x86_input_filename) #print(time.strftime("%s")) self.global_timer_index += 1 self.local_timer_index += 1 line_index = self.global_timer_index - 1 less_number = EMPTY_NUMBER - self.local_timer_index needed_number = self.local_timer_index - 1 # get the value of current index from file power8_current_value = self.read_from_file_by_index( power8_input_filename, line_index) x86_current_value = self.read_from_file_by_index( x86_input_filename, line_index) # normal return: accumulate the return value directly. # abnormal return: accumulate the previous one. if power8_current_value: self.power8_accumulate_value += power8_current_value self.power8_previous_value = power8_current_value else: # TODO: new add for error character power8_current_value = self.power8_previous_value self.power8_accumulate_value += self.power8_previous_value if x86_current_value: self.x86_accumulate_value += x86_current_value self.x86_previous_value = x86_current_value else: # TODO: new add for error character x86_current_value = self.x86_previous_value self.x86_accumulate_value += self.x86_previous_value #print("==> accumulate = {0} and previous = {1} and current =" # "{2}".format(self.power8_accumulate_value, # self.power8_previous_value, # power8_current_value)) # update the new data into 1st subplot self.power8_current_all_values = \ self.power8_current_all_values[:needed_number] + \ [power8_current_value] + [None] * less_number self.x86_current_all_values = \ self.x86_current_all_values[:needed_number] + \ [x86_current_value] + [None] * less_number self.power8_plot.set_ydata(self.power8_current_all_values) self.x86_plot.set_ydata(self.x86_current_all_values) # update the new data into 2nd subplot self.power8_ave_value = self.power8_accumulate_value / \ self.global_timer_index self.x86_ave_value = self.x86_accumulate_value / \ self.global_timer_index self.power8_barh.set_width(self.power8_ave_value) self.x86_barh.set_width(self.x86_ave_value) self.ax.draw_artist(self.power8_plot) self.ax.draw_artist(self.x86_plot) self.average.draw_artist(self.power8_barh) self.average.draw_artist(self.x86_barh) # clean the data on screen if self.local_timer_index == EMPTY_NUMBER: #print("local_timer_index is full") self.power8_current_all_values = [] self.x86_current_all_values = [] self.local_timer_index = 0 self.canvas.blit(self.ax.bbox) self.canvas.blit(self.average.bbox) def read_from_file_by_index(self, filename, line_number): try: with open(filename, 'r') as file_object: all_content = file_object.read().split('\n')[:-1] file_length = len(all_content) except IOError, e: print("Error->[read_from_file_by_index]: CAN NOT find the" "filename:[{0}]".format(filename)) except Exception as ex: print("Error->[read_from_file_by_index]: {0}".format(str(ex)))
class Plot(wx.Panel): """ Class to display a plot in wx figure (mpl.figure.Figure): Figure that displays plots ax (mpl.axes.Axes): Axes inside figure canvas (FigureCanvasWxAgg): Canvas where the figure paints x_len (int): Maximum number of points to display at a time background (BufferRegion): Empty background to leverage blitting lines (list<mpl.lines.Line2D>): Each sensor of the same type has a line in this list. Used to update them later with different data """ def __init__(self, parent, x_len, id=-1, dpi=100, **kwargs): """ Create empty plot """ wx.Panel.__init__(self, parent, id=id, **kwargs) self.x_len = x_len self.figure = mpl.figure.Figure(dpi=dpi, figsize=(6.5, 6.5)) self.ax = self.figure.add_axes([0.1, 0.1, 0.7, 0.85]) self.ax.set_xlim(0, self.x_len) self.ax.autoscale(enable=True, axis="y", tight=True) self.canvas = FigureCanvas(self, -1, self.figure) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.canvas, 0, wx.ALL) self.SetSizer(sizer) self.Fit() self.canvas.draw() self.background = self.canvas.copy_from_bbox(self.ax.bbox) self.lines = [] def updateLines(self, plot_data): """ Redefine lines attribute as response to change in notebook """ self.clear() self.lines = [] for i in range(plot_data.data.shape[0]): self.lines.append( self.ax.plot( range(self.x_len), plot_data.data[i, :], marker="o", color="C" + str(i), markerfacecolor="C" + str(i), )[0] ) self.redoLegend(plot_data) def refresh(self, plot_data): """ Tell the Plot to actually implement the latest modifications Use canvas.blit() instead of canvas.draw_idle() to save time Documentation for this backend is kinda poor, but basically whenever something important needs to be done, it will only work if called from the FigureCanvas, not the Figure """ self.canvas.restore_region(self.background) for line, data in zip(self.lines, plot_data.data): line.set_ydata(data) self.ax.add_line(line) self.ax.draw_artist(line) self.canvas.blit(self.ax.bbox) def clear(self): """ Function to clear the Axes of the Figure """ self.ax.cla() self.canvas.draw_idle() def redoLegend(self, plot_data): """ Remove legend and create a new one Used when the number of sensors changes (from Ports dialog) """ legend = self.ax.get_legend() if legend is not None: legend.remove() self.addCustomLegend(plot_data) def addCustomLegend(self, plot_data): """ Create legend for each variable Legend elements are defined for as many sensors as there are. The colors are given with the C0, C1, etc format, which allows to cycle. I suspect the current colormap goes up to 10 before repeating itself. The label is in the form of mL1, gR, etc. The legend will appear to the right of the Axes, outside the box, aligned with its top. """ if plot_data.scaling: legend_elements_L = [ Line2D( [0], [0], marker="o", color="C" + str(i), label=plot_data.sensor_type + "L" + str(i + 1), markerfacecolor="C" + str(i), ) for i in range(plot_data.num_sensors) ] legend_elements_R = [ Line2D( [0], [0], marker="o", color="C" + str(plot_data.num_sensors + i), label=plot_data.sensor_type + "R" + str(i + 1), markerfacecolor="C" + str(plot_data.num_sensors + i), ) for i in range(plot_data.num_sensors) ] legend_elements = legend_elements_L + legend_elements_R else: legend_elements = [ Line2D( [0], [0], marker="o", color="C0", label=plot_data.sensor_type + "L", markerfacecolor="C0", ), Line2D( [0], [0], marker="o", color="C1", label=plot_data.sensor_type + "R", markerfacecolor="C1", ), ] self.ax.legend( handles=legend_elements, loc="upper left", bbox_to_anchor=(1.04, 1) )
class PanelGraph(wx.Panel): def __init__(self, panel, notify, settings, status, remoteControl): self.panel = panel self.notify = notify self.plot = None self.settings = settings self.status = status self.remoteControl = remoteControl self.spectrum = None self.isLimited = None self.limit = None self.extent = None self.annotate = None self.lockDraw = threading.Lock() self.toolTip = wx.ToolTip('') self.mouseSelect = None self.mouseZoom = None self.measureTable = None self.background = None self.selectStart = None self.selectEnd = None self.menuClearSelect = [] self.measure = None self.show = None self.doDraw = False wx.Panel.__init__(self, panel) self.figure = matplotlib.figure.Figure(facecolor='white') self.canvas = FigureCanvas(self, -1, self.figure) self.canvas.SetToolTip(self.toolTip) self.measureTable = PanelMeasure(self, settings) self.toolbar = NavigationToolbar(self.canvas, self, settings, self.__hide_overlay) self.toolbar.Realize() vbox = wx.BoxSizer(wx.VERTICAL) vbox.Add(self.canvas, 1, wx.EXPAND) vbox.Add(self.measureTable, 0, wx.EXPAND) vbox.Add(self.toolbar, 0, wx.EXPAND) self.SetSizer(vbox) vbox.Fit(self) self.create_plot() self.canvas.mpl_connect('button_press_event', self.__on_press) self.canvas.mpl_connect('figure_enter_event', self.__on_enter) self.canvas.mpl_connect('figure_leave_event', self.__on_leave) self.canvas.mpl_connect('motion_notify_event', self.__on_motion) self.canvas.mpl_connect('draw_event', self.__on_draw) self.canvas.mpl_connect('idle_event', self.__on_idle) self.Bind(wx.EVT_SIZE, self.__on_size) self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.__on_timer, self.timer) def __set_fonts(self): axes = self.plot.get_axes() if axes is not None: axes.xaxis.label.set_size('small') axes.yaxis.label.set_size('small') if self.settings.display == Display.SURFACE: axes.zaxis.label.set_size('small') axes.tick_params(axis='both', which='major', labelsize='small') axes = self.plot.get_axes_bar() if axes is not None: axes.tick_params(axis='both', which='major', labelsize='small') def __enable_menu(self, state): for menu in self.menuClearSelect: menu.Enable(state) def __on_press(self, event): if self.settings.clickTune and matplotlib.__version__ >= '1.2' and event.dblclick: frequency = int(event.xdata * 1e6) self.remoteControl.tune(frequency) def __on_enter(self, _event): self.toolTip.Enable(False) def __on_leave(self, _event): self.toolTip.Enable(True) def __on_motion(self, event): xpos = event.xdata ypos = event.ydata text = "" if xpos is None or ypos is None or self.spectrum is None: return if self.settings.display == Display.PLOT: timeStamp = max(self.spectrum) spectrum = self.spectrum[timeStamp] elif self.settings.display == Display.SPECT: timeStamp = num2epoch(ypos) if timeStamp in self.spectrum: spectrum = self.spectrum[timeStamp] else: nearest = min(self.spectrum.keys(), key=lambda k: abs(k - timeStamp)) spectrum = self.spectrum[nearest] elif self.settings.display == Display.SURFACE: spectrum = None coords = self.plot.get_axes().format_coord(event.xdata, event.ydata) match = re.match('x=([-|0-9|\.]+).*y=([0-9|\:]+).*z=([-|0-9|\.]+)', coords) if match is not None and match.lastindex == 3: freq = float(match.group(1)) level = float(match.group(3)) text = "{}, {}".format(*format_precision(self.settings, freq, level)) else: spectrum = None if spectrum is not None and len(spectrum) > 0: x = min(spectrum.keys(), key=lambda freq: abs(freq - xpos)) if min(spectrum.keys(), key=float) <= xpos <= max(spectrum.keys(), key=float): y = spectrum[x] text = "{}, {}".format(*format_precision(self.settings, x, y)) else: text = format_precision(self.settings, xpos) self.status.set_info(text, level=None) axes = self.figure.get_axes()[0] markers = find_artists(self.figure, 'peak') markers.extend(find_artists(self.figure, 'peakThres')) hit = False for marker in markers: if isinstance(marker, Line2D): location = marker.get_path().vertices[0] markX, markY = axes.transData.transform(location) dist = abs(math.hypot(event.x - markX, event.y - markY)) if dist <= 5: if self.settings.display == Display.PLOT: tip = "{}, {}".format(*format_precision(self.settings, location[0], location[1])) else: tip = "{}".format(format_precision(self.settings, location[0])) self.toolTip.SetTip(tip) hit = True break self.toolTip.Enable(hit) def __on_size(self, event): ppi = wx.ScreenDC().GetPPI() size = [float(v) for v in self.canvas.GetSize()] width = size[0] / ppi[0] height = size[1] / ppi[1] self.figure.set_figwidth(width) self.figure.set_figheight(height) self.figure.set_dpi(ppi[0]) event.Skip() def __on_draw(self, _event): axes = self.plot.get_axes() if axes is not None: self.background = self.canvas.copy_from_bbox(axes.bbox) self.__draw_overlay() def __on_idle(self, _event): if self.doDraw and self.plot.get_plot_thread() is None: self.__hide_overlay() self.doDraw = False if os.name == 'nt': threading.Thread(target=self.__draw_canvas, name='Draw').start() else: with self.lockDraw: self.canvas.draw() self.status.set_busy(False) def __on_timer(self, _event): self.timer.Stop() self.set_plot(None, None, None, None, self.annotate) def __draw_canvas(self): with self.lockDraw: try: self.canvas.draw() except wx.PyDeadObjectError: pass wx.CallAfter(self.status.set_busy, False) def __draw_overlay(self): if self.background is not None: self.canvas.restore_region(self.background) self.__draw_select() self.draw_measure() axes = self.plot.get_axes() if axes is None: self.canvas.draw() else: self.canvas.blit(axes.bbox) def __draw_select(self): if self.selectStart is not None and self.selectEnd is not None: self.mouseSelect.draw(self.selectStart, self.selectEnd) def __hide_overlay(self): if self.plot is not None: self.plot.hide_measure() self.__hide_select() def __hide_select(self): if self.mouseSelect is not None: self.mouseSelect.hide() def create_plot(self): if self.plot is not None: self.plot.close() self.toolbar.set_auto(True) if self.settings.display == Display.PLOT: self.plot = Plotter(self.notify, self.figure, self.settings) elif self.settings.display == Display.SPECT: self.plot = Spectrogram(self.notify, self.figure, self.settings) elif self.settings.display == Display.SURFACE: self.plot = Plotter3d(self.notify, self.figure, self.settings) elif self.settings.display == Display.STATUS: self.plot = PlotterStatus(self.notify, self.figure, self.settings) else: self.plot = PlotterTime(self.notify, self.figure, self.settings) self.__set_fonts() self.toolbar.set_plot(self.plot) self.toolbar.set_type(self.settings.display) self.measureTable.set_type(self.settings.display) self.set_plot_title() self.figure.subplots_adjust(top=0.85) self.redraw_plot() self.plot.scale_plot(True) self.mouseZoom = MouseZoom(self.toolbar, plot=self.plot, callbackHide=self.__hide_overlay) self.mouseSelect = MouseSelect(self.plot, self.on_select, self.on_selected) self.measureTable.show(self.settings.showMeasure) self.panel.SetFocus() def on_select(self): self.hide_measure() def on_selected(self, start, end): self.__enable_menu(True) self.selectStart = start self.selectEnd = end self.measureTable.set_selected(self.spectrum, start, end) def add_menu_clear_select(self, menu): self.menuClearSelect.append(menu) menu.Enable(False) def draw(self): self.doDraw = True def show_measure_table(self, show): self.measureTable.show(show) self.Layout() def set_plot(self, spectrum, isLimited, limit, extent, annotate=False): if spectrum is not None and extent is not None: if isLimited is not None and limit is not None: self.spectrum = copy.copy(spectrum) self.extent = extent self.annotate = annotate self.isLimited = isLimited self.limit = limit if self.plot.get_plot_thread() is None: self.timer.Stop() self.measureTable.set_selected(self.spectrum, self.selectStart, self.selectEnd) if isLimited: self.spectrum = reduce_points(spectrum, limit) self.status.set_busy(True) self.plot.set_plot(self.spectrum, self.extent, annotate) else: self.timer.Start(200, oneShot=True) def set_plot_title(self): if len(self.settings.devicesRtl) > 0: gain = self.settings.devicesRtl[self.settings.indexRtl].gain else: gain = 0 self.plot.set_title("Frequency Spectrogram\n{} - {} MHz," " gain = {}dB".format(self.settings.start, self.settings.stop, gain)) def redraw_plot(self): if self.spectrum is not None: self.set_plot(self.spectrum, self.settings.pointsLimit, self.settings.pointsMax, self.extent, self.settings.annotate) def set_grid(self, on): self.plot.set_grid(on) def hide_measure(self): if self.plot is not None: self.plot.hide_measure() def draw_measure(self): if self.measure is not None and self.measure.is_valid(): self.plot.draw_measure(self.measure, self.show) def update_measure(self, measure=None, show=None): if not measure and not show: self.measureTable.update_measure() else: self.measure = measure self.show = show with self.lockDraw: self.__draw_overlay() def get_figure(self): return self.figure def get_axes(self): return self.plot.get_axes() def get_canvas(self): return self.canvas def get_toolbar(self): return self.toolbar def scale_plot(self, force=False): self.plot.scale_plot(force) def clear_plots(self): self.plot.clear_plots() self.spectrum = None self.doDraw = True def clear_selection(self): self.measure = None self.measureTable.clear_measurement() self.selectStart = None self.selectEnd = None self.mouseSelect.clear() self.__enable_menu(False) def close(self): close_modeless()
class AnalyzeORBResult (wx.Panel) : def __init__ (self, parent): wx.Panel.__init__(self, parent=parent) splitter = wx.SplitterWindow (self) # XXX: we may need to place the canvas in its own Panel self.figure = Figure() self.canvas = FigureCanvas(splitter, wx.ID_ANY, self.figure) self.vehiclePointer = None self.imageTopic = None rightPanel = wx.Panel(splitter) cPanel = wx.BoxSizer (wx.VERTICAL) rightPanel.SetSizer(cPanel) self.orbResultChooser = FilePrompt(rightPanel, 'ORB-SLAM Result Bag', self.onClickOrbChoose) cPanel.Add(self.orbResultChooser, flag=wx.EXPAND) self.groundTruthChooser = FilePrompt(rightPanel, 'GroundTruth', self.onClickGtChoose) cPanel.Add(self.groundTruthChooser, flag=wx.EXPAND) timePosBox = wx.BoxSizer(wx.HORIZONTAL) timePosBox.Add(wx.StaticText(rightPanel, label='Time Position'), flag=wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL, proportion=0) self.timeDisplay = wx.TextCtrl(rightPanel, style=wx.TE_READONLY) timePosBox.Add(self.timeDisplay, flag=wx.EXPAND|wx.ALIGN_CENTER, proportion=1) cPanel.Add(timePosBox, flag=wx.EXPAND) orbStatusBox = wx.BoxSizer(wx.HORIZONTAL) orbStatusBox.Add(wx.StaticText(rightPanel, label="ORB Status"), flag=wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL, proportion=0) self.orbStatus = wx.StaticText(rightPanel, label="Init") orbStatusBox.Add(self.orbStatus, flag=wx.ALIGN_CENTER|wx.EXPAND, proportion=1) cPanel.Add(orbStatusBox, flag=wx.EXPAND) bagGroup = wx.StaticBoxSizer(wx.StaticBox(rightPanel, label='Image Source Bag'), wx.VERTICAL) askBag = FilePrompt(rightPanel, "Image Bag File", self.onBagChange) bagGroup.Add(askBag, flag=wx.EXPAND) self.imageTopicChooser = wx.ComboBox(rightPanel) bagGroup.Add(self.imageTopicChooser, flag=wx.EXPAND) self.imageTopicChooser.Bind(wx.EVT_COMBOBOX, self.onImageTopicChange) self.bagImageView = BagImagePreview (rightPanel) bagGroup.Add (self.bagImageView, flag=wx.EXPAND) cPanel.Add(bagGroup, flag=wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL) splitter.SplitVertically(self.canvas, rightPanel) splitter.SetMinimumPaneSize(30) mainLayout = wx.BoxSizer (wx.VERTICAL) self.SetSizer(mainLayout) mainLayout.Add(splitter, proportion=1, flag=wx.EXPAND|wx.ALIGN_TOP) self.timeChooser = wx.Slider(self) self.timeChooser.Bind(wx.EVT_SCROLL, self.onChangeTimePosition) mainLayout.Add(self.timeChooser, flag=wx.EXPAND|wx.ALIGN_BOTTOM) self.Fit() def onClickOrbChoose (self, bagPath): self.loadData() def onClickGtChoose (self, gtPath): self.loadData() def onChangeTimePosition (self, e): if self.currentORBTimestamp == None: return # print (self.timeChooser.GetValue()) self.currentORBTimestamp = int(self.timeChooser.GetValue()) self.redrawPosition() def onBagChange (self, bagPath): self.bagImageView.setBagPath (bagPath) self.imageTopicChooser.AppendItems(self.bagImageView.getAllTopics()) def onImageTopicChange (self, e): self.imageTopic = (self.imageTopicChooser.GetValue()) self.bagImageView.setTopic (self.imageTopic) self.redrawPosition() def loadData (self): if (self.orbResultChooser.GetValue()=='' or self.groundTruthChooser.GetValue()==''): print ("Select filenames first") return None print ("Wait...") # Load ground truth and plot it self.ax = self.figure.add_subplot(111) self.ax.set_autoscale_on(True) self.ax.grid(True) self.groundTruth = PoseTable.loadFromBagFile(self.groundTruthChooser.GetValue(), 'world', 'ndt_frame') gtTbl = self.groundTruth.toArray() self.groundTruthPlot, = self.ax.plot(gtTbl[:,0], gtTbl[:,1]) # self.groundTruthPlot, = self.ax.plot([0,1,2], [4,5,6]) self.orbResult = PoseTable.loadFromBagFile(self.orbResultChooser.GetValue(), '/ORB_SLAM/World', '/ORB_SLAM/ExtCamera') orbTbl = self.orbResult.toArray() self.orbResultPlot, = self.ax.plot(orbTbl[:,0], orbTbl[:,1]) self.canvas.draw() self.background = self.canvas.copy_from_bbox(self.ax.bbox) # self.helpText.Show(True) self.timeChooser.SetRange(self.orbResult[0].timestamp, self.orbResult.last().timestamp) self.currentORBTimestamp = self.orbResult[0].timestamp self.redrawPosition() @staticmethod def readMessage (bag, topic, timestamp): tm = rospy.Time.from_sec(timestamp) for topic, msg, time in bag.read_messages(topics=topic, start_time=tm): return msg def redrawPosition (self): if self.currentORBTimestamp == None: return dp = datetime.datetime.fromtimestamp(self.currentORBTimestamp) self.timeDisplay.SetValue(str(dp)) orbPose = self.orbResult.findNearestInTime (self.currentORBTimestamp, 0.1) self.canvas.restore_region(self.background) if (orbPose!=None): if (self.vehiclePointer==None): self.vehiclePointer = self.ax.scatter(orbPose.x, orbPose.y, s=100, c=[1,0,0,0.5], linewidths=0) else: self.vehiclePointer.set_offsets([orbPose.x, orbPose.y]) self.orbStatus.SetLabel("OK") else: self.orbStatus.SetLabel("Lost") self.canvas.draw() self.canvas.blit(self.ax.bbox) if self.imageTopic != None: self.bagImageView.showTime (self.currentORBTimestamp)
class CirclePanel(wx.Panel): def __init__(self, parent, **kwargs): wx.Panel.__init__(self, parent, **kwargs) self.SetBackgroundColour(wx.Colour(255, 255, 255)) #set up widgets self.figure = Figure((5, 5), 80) self.figure.set_facecolor("#FFFFFF") self.canvas = FigureCanvas(self, -1, self.figure) self.canvas.SetBackgroundColour(wx.Colour(255,255,255)) self.subplot = self.figure.add_subplot(111) self.subplot.set_ylim([-5, 5]) self.subplot.set_xlim([-5, 5]) self.toolbar = NavigationToolbar(self.canvas) self.toolbar.SetBackgroundColour(wx.Colour(255, 255, 255)) #set up boxes self.box = wx.BoxSizer(wx.VERTICAL) self.box.Add(self.canvas, 1, wx.LEFT | wx.GROW) #the sizer automatically resizes the canvas self.box.Add(self.toolbar, 0, wx.CENTER) self.SetSizer(self.box) #set up the point dragging feature; #user can drag center and move circle self.canvas.mpl_connect("pick_event", self.OnPick) self.canvas.mpl_connect("motion_notify_event", self.OnMotion) self.canvas.mpl_connect("button_release_event", self.OnMouseUp) self.dragging = False #circle parameters self.h = 0.0 self.k = 0.0 self.r = 1.0 #plot objects self.background = None self.circle = None self.points = None self.DrawFigure() #user picked a point/line in the plot def OnPick(self, event): #the user picked more than one point because #there is a point on top of another; #just pick the first point if not isinstance(event.ind, type(1)): event.ind = event.ind[0] #if the user picked the center point, enable translation #the center point is the first point of the point list (ind=0) if event.ind == 0: self.dragging = 1 self.SaveBackground() #if the user picked the diameter point, enable dilation #the diameter point is the second point of the point list (ind=1) elif event.ind == 1: self.dragging = 2 self.SaveBackground() #user moved the mouse in the plot def OnMotion(self, event): #make sure that the mouse is IN the plot! if not event.xdata == None: #if user is dragging the center point, #move the center point to mouse coordinates if self.dragging == 1: evt = FigureTransformEvent(typeEVT_FIGURE_TRANSLATE, self.GetId()) evt.oldH = self.h evt.oldK = self.k evt.oldR = self.r evt.deltaH = self.h - event.xdata evt.deltaK = self.k - event.ydata self.h = event.xdata self.k = event.ydata evt.newH = self.h evt.newK = self.k self.UpdateFigure() self.GetEventHandler().ProcessEvent(evt) #if user is dragging the diameter point, #change the radius to the distance between the diameter and center point if self.dragging == 2: evt = FigureTransformEvent(typeEVT_FIGURE_DILATE, self.GetId()) #make sure r is greater than 0 if event.xdata - self.h > 0.0: evt.oldH = self.h evt.oldK = self.k evt.oldR = self.r self.r = event.xdata - self.h evt.deltaR = evt.oldR - self.r evt.newR = self.r self.UpdateFigure() self.GetEventHandler().ProcessEvent(evt) #user released button def OnMouseUp(self, event): #if the user is dragging the center point #and releases the button, stop dragging self.dragging = 0 self.circle.set_animated(False) self.points.set_animated(False) #update the title, which shows the equation of the circle def UpdateTitle(self): titleText = "$(x{h})^2 + (y{k})^2 = {r}^2$" titleH, titleK, titleR = "-0.0", "-0.0", round(self.r, 2) #format signs correctly if self.h < 0.0: titleH = "+{val}".format(val=abs(round(self.h, 2))) elif self.h > 0.0: titleH = "-{val}".format(val=abs(round(self.h, 2))) if self.k < 0.0: titleK = "+{val}".format(val=abs(round(self.k, 2))) elif self.k > 0.0: titleK = "-{val}".format(val=abs(round(self.k, 2))) #show the students that they can omit h or k in the equation if it equals 0.0 if self.h == 0.0 and not self.k == 0.0: titleText = titleText + " OR $x^2 + (y{k})^2 = {r}^2$" elif not self.h == 0.0 and self.k == 0.0: titleText = titleText + " OR $(x{h})^2 + y^2 = {r}^2$" elif self.h == 0.0 and self.k == 0.0: titleText = titleText + " OR $x^2 + y^2 = {r}^2$" self.subplot.set_title(titleText.format(h=titleH, k=titleK, r=titleR), fontproperties=mpl.font_manager.FontProperties(size="x-large")) #draw/redraw the canvas def DrawFigure(self): self.subplot.clear() #set the "window" of the plot self.subplot.set_ylim([-5, 5]) self.subplot.set_xlim([-5, 5]) #draw grid and axes lines self.subplot.grid(True) self.subplot.axhspan(0, 0) self.subplot.axvspan(0, 0) self.UpdateTitle() #draw the circles circleColor = (0, 0, 1, 1) #must multiply r by 2 b/c Arc takes the length (diameter) of the axes, not the radius #circle1 is the reference circle (red) """ circle1 = patches.Arc((0, 0), 2, 2, edgecolor="#FF0000", alpha=0.8) self.subplot.plot([0.0, 1.0], [0.0, 0.0], marker="o", color="#FF0000", mec="#FF0000", mfc="#FF0000") self.subplot.add_patch(circle1) """ #circle2 is the user-manipulated circle (blue) self.circle = patches.Arc((self.h, self.k), self.r*2, self.r*2, edgecolor=circleColor, alpha=0.8) self.points = self.subplot.plot([self.h, self.h+self.r], [self.k, self.k], marker="o", picker=5, color=circleColor, mec=circleColor, mfc=circleColor) #get the first (and only) line, not the list self.points = self.points[0] self.subplot.add_patch(self.circle) self.canvas.draw() def UpdateFigure(self): #update data self.circle.center = (self.h, self.k) self.circle.width = 2*self.r self.circle.height = 2*self.r self.points.set_xdata([self.h, self.h+self.r]) self.points.set_ydata([self.k, self.k]) self.UpdateTitle() #draw self.canvas.restore_region(self.background) self.subplot.draw_artist(self.subplot.title) self.subplot.draw_artist(self.circle) self.subplot.draw_artist(self.points) self.canvas.blit(self.figure.bbox) def SaveBackground(self): self.circle.set_animated(True) self.points.set_animated(True) #clear plot self.subplot.set_title(" ") self.canvas.draw() #save figure self.background = self.canvas.copy_from_bbox(self.figure.bbox) self.UpdateTitle() #blit figures back onto the plot self.subplot.draw_artist(self.circle) self.subplot.draw_artist(self.points) self.subplot.draw_artist(self.subplot.title) self.canvas.blit(self.figure.bbox) def SetParameters(self, h, k, r): self.h = h self.k = k self.r = r
class _MonitorPlot(wx.Frame): """Class to provide live updated plotting to monitor the convergence of FlowVB """ def __init__(self, data, scale=1): """Setup plotting environment """ self.scale = scale self.data = data # Setup plotting window wx.Frame.__init__(self, None, wx.ID_ANY, title="FlowVB Progress Monitor", size=(800, 600)) self.fig = Figure((8, 6), 100) self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) self.ax = self.fig.add_subplot(111) # Set axis limits x_lims = [data[:, 0].min(), data[:, 0].max()] y_lims = [data[:, 1].min(), data[:, 1].max()] self.ax.set_xlim(x_lims) self.ax.set_ylim(y_lims) self.ax.set_autoscale_on(False) # Draw the data self.l_data = self.ax.plot(self.data[:, 0], self.data[:, 1], color='blue', linestyle='', marker='o') # Draw to screen self.canvas.draw() # Save background self.bg = self.canvas.copy_from_bbox(self.ax.bbox) def update_plot(self, pos, cov): """Update the plot """ # Restore empty plot (but doesn't seem to do anything) self.canvas.restore_region(self.bg) # Remove previous elements from plot self.ax.lines = [self.ax.lines[0]] # Delete everything except # scatterplot self.ax.patches = [] K = pos.shape[0] for k in range(K): # Draw centers l_center, = self.ax.plot(pos[k, 0], pos[k, 1], color='red', marker='+') # Compute and draw error ellipses U, s, Vh = np.linalg.svd(cov[k, :, :]) orient = math.atan2(U[1, 0], U[0, 0]) * 180 / pi ellipsePlot = Ellipse(xy=pos[k, :], width=2.0 * math.sqrt(s[0]), height=2.0 * math.sqrt(s[1]), angle=orient, facecolor='none', edgecolor='red', zorder=100) self.ax.add_patch(ellipsePlot) # Draw to screen self.canvas.draw() self.canvas.blit(self.ax.bbox) def end_of_iteration(self): self.button = wx.Button(self.canvas, label="Close Window", pos=(680, 10)) self.Bind(wx.EVT_BUTTON, self.OnButtonClick, self.button) at = AnchoredText("Iteration finished.", prop=dict(size=8), frameon=True, loc=2) at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") self.ax.add_artist(at) self.canvas.draw() def OnButtonClick(self, event): self.Close(True)
class LeftGraphTop(wx.Panel): def __init__(self, parent, statusbar): wx.Panel.__init__(self, parent) self.statusbar = statusbar """ An polygon editor. Key-bindings 't' toggle vertex markers on and off. When vertex markers are on, you can move them, delete them 'd' delete the vertex under point 'i' insert a vertex at point. You must be within epsilon of the line connecting two existing vertices """ self.fig = Figure((4.0, 3.0)) self.canvas = FigCanvas(self, -1, self.fig) self.ax = self.fig.add_subplot(111) self.ax.set_ylabel("Strain", fontdict=font) self.ax.set_xlabel("Depth ($\AA$)", fontdict=font) self.toolbar = NavigationToolbar(self.canvas) self.toolbar.Hide() self.fig.patch.set_facecolor(colorBackgroundGraph) self._ind = None # the active vert self.poly = [] self.line = [] self.showverts = True self.epsilon = 5 # max pixel distance to count as a vertex hit self.new_coord = {'indice': 0, 'x': 0, 'y': 0} self.modelpv = False xs = [-1] ys = [-1] poly = Polygon(list(zip(xs, ys)), fill=False, closed=False, animated=True) self.ax.set_xlim([0, 1]) self.ax.set_ylim([0, 1]) self.c_strain = "" self.l_strain = "" self.canvas.mpl_connect('draw_event', self.draw_callback) self.canvas.mpl_connect('button_press_event', self.button_press_callback) self.canvas.mpl_connect('button_release_event', self.button_release_callback) self.canvas.mpl_connect('scroll_event', self.scroll_callback) self.canvas.mpl_connect('motion_notify_event', self.motion_notify_callback) self.canvas.mpl_connect('motion_notify_event', self.on_update_coordinate) mastersizer = wx.BoxSizer(wx.VERTICAL) mastersizer.Add(self.canvas, 1, wx.ALL|wx.EXPAND) mastersizer.Add(self.toolbar, 0, wx.ALL) pub.subscribe(self.OnDrawGraph, pubsub_Draw_Strain) pub.subscribe(self.scale_manual, pubsub_Update_Scale_Strain) pub.subscribe(self.on_color, pubsub_Graph_change_color_style) self.on_color() self.draw_c(poly, xs, ys) self.SetSizer(mastersizer) self.Fit() def on_color(self): a = P4Rm() self.c_strain = a.DefaultDict['c_strain'] self.l_strain = a.DefaultDict['l_strain'] self.c_bkg = a.DefaultDict['c_graph_background'] def OnDrawGraph(self, b=None): a = P4Rm() self.modelpv = a.modelPv self.ax.clear() if a.AllDataDict['damaged_depth'] == 0: self.ax.text(0.5, 0.5, "No Damage", size=30, rotation=0., ha="center", va="center", bbox=dict(boxstyle="round", ec='red', fc=self.c_strain,)) xs = [-1] ys = [-1] x_sp = [-1] y_sp = [-1] self.ax.set_xticklabels([]) self.ax.set_yticklabels([]) self.ax.set_xlim([0, 1]) self.ax.set_ylim([0, 1]) else: if b != 2: x_sp = a.ParamDict['x_sp'] y_sp = a.ParamDict['strain_shifted'] xs = deepcopy(a.ParamDict['depth']) ys = deepcopy(a.ParamDict['strain_i']*100) P4Rm.DragDrop_Strain_x = x_sp P4Rm.DragDrop_Strain_y = y_sp ymin = min(ys) - min(ys)*10/100 ymax = max(ys) + max(ys)*10/100 self.ax.set_ylim([ymin, ymax]) if a.ParamDict['x_sp'] is not "": self.ax.set_xlim([a.ParamDict['depth'][-1], a.ParamDict['depth'][0]]) elif b == 2: x_sp = [-1] y_sp = [-1] xs = [-1] ys = [-1] self.ax.set_xlim([0, 1]) self.ax.set_ylim([-1, 1]) poly = Polygon(list(zip(x_sp, y_sp)), lw=0, ls='dashdot', color=self.c_strain, fill=False, closed=False, animated=True) if self.modelpv is True: P4Rm.ParamDict['sp_pv_backup'] = a.ParamDict['sp'] self.draw_c(poly, xs, ys) def draw_c(self, data, x, y): self.ax.plot(x[1:], y[1:], color=self.c_strain, lw=2., ls=self.l_strain) self.ax.set_ylabel("Strain", fontdict=font) self.ax.set_xticklabels([]) self.ax.set_axis_bgcolor(self.c_bkg) self.poly = data xs, ys = zip(*self.poly.xy) self.line = Line2D(xs, ys, lw=0, ls='-.', color=self.c_strain, marker='.', ms=32, markerfacecolor=self.c_strain, markeredgecolor='k', mew=1.0) self.ax.add_line(self.line) self.ax.add_patch(self.poly) self.canvas.draw() self.Update() def draw_callback(self, event): self.background = self.canvas.copy_from_bbox(self.ax.bbox) self.ax.draw_artist(self.poly) self.ax.draw_artist(self.line) self.canvas.blit(self.ax.bbox) def get_ind_under_point(self, event): 'get the index of the vertex under point if within epsilon tolerance' # display coords xy = np.asarray(self.poly.xy) xyt = self.poly.get_transform().transform(xy) xt, yt = xyt[:, 0], xyt[:, 1] d = np.sqrt((xt-event.x)**2 + (yt-event.y)**2) indseq = np.nonzero(np.equal(d, np.amin(d)))[0] ind = indseq[0] if d[ind] >= self.epsilon: ind = None return ind def button_press_callback(self, event): 'whenever a mouse button is pressed' a = P4Rm() val = a.xrd_graph_loaded if self.canvas.HasCapture(): self.canvas.ReleaseMouse() if not self.showverts: return if event.inaxes is None: return if event.button != 1: return if val == 1: self._ind = self.get_ind_under_point(event) self.new_coord['indice'] = self._ind def button_release_callback(self, event): 'whenever a mouse button is released' a = P4Rm() val = a.xrd_graph_loaded if self.canvas.HasCapture(): self.canvas.ReleaseMouse() else: if not self.showverts: return if event.button != 1: return if self.new_coord['indice'] is not None and val == 1: temp_1 = self.new_coord['y'] temp_2 = self.new_coord['x'] P4Rm.DragDrop_Strain_y[self.new_coord['indice']] = temp_1 P4Rm.DragDrop_Strain_x[self.new_coord['indice']] = temp_2 if a.AllDataDict['model'] == 0: temp = self.new_coord['y'] P4Rm.DragDrop_Strain_y[self.new_coord['indice']] = temp temp = [strain*scale/100 for strain, scale in zip(a.DragDrop_Strain_y, a.ParamDict['scale_strain'])] temp = [float(format(value, '.8f')) for value in temp] temp2 = np.concatenate([temp, [a.ParamDict['stain_out']]]) P4Rm.ParamDict['sp'] = deepcopy(temp2) P4Rm.ParamDictbackup['sp'] = deepcopy(temp2) elif a.AllDataDict['model'] == 1: temp = self.new_coord['y'] P4Rm.DragDrop_Strain_y[self.new_coord['indice']] = temp temp = [strain*scale/100 for strain, scale in zip(a.DragDrop_Strain_y, a.ParamDict['scale_strain'])] temp = [float(format(value, '.8f')) for value in temp] temp2 = np.concatenate([[a.ParamDict['stain_out'][0]], temp, [a.ParamDict['stain_out'][1]]]) P4Rm.ParamDict['sp'] = deepcopy(temp2) P4Rm.ParamDictbackup['sp'] = deepcopy(temp2) elif a.AllDataDict['model'] == 2: t_temp = a.ParamDict['depth'] + a.ParamDict['z'] t = t_temp[0] sp_temp = range(7) sp_temp[0] = a.DragDrop_Strain_y[0] sp_temp[1] = 1 - a.DragDrop_Strain_x[0]/t sp_temp[2] = 2*(-1 + a.ParamDict['sp'][1] + a.DragDrop_Strain_x[1]/t) sp_temp[3] = 2*(1 - a.ParamDict['sp'][1] - 1*a.DragDrop_Strain_x[2]/t) sp_temp[4] = a.ParamDict['sp'][4] sp_temp[5] = a.ParamDict['sp'][5] sp_temp[6] = a.DragDrop_Strain_y[3] P4Rm.ParamDict['sp'] = deepcopy(sp_temp) P4Rm.ParamDictbackup['sp'] = deepcopy(sp_temp) P4Rm.ParamDict['sp_pv'] = deepcopy(sp_temp) pub.sendMessage(pubsub_Update_Fit_Live) self._ind = None def scroll_callback(self, event): if not event.inaxes: return a = P4Rm() if event.key == 'u' and event.button == 'up': temp = a.ParamDict['strain_multiplication'] + 0.01 P4Rm.ParamDict['strain_multiplication'] = temp elif event.key == 'u' and event.button == 'down': temp = a.ParamDict['strain_multiplication'] - 0.01 P4Rm.ParamDict['strain_multiplication'] = temp temp_1 = a.ParamDictbackup['sp'] temp_2 = a.ParamDict['strain_multiplication'] P4Rm.ParamDict['sp'] = multiply(temp_1, temp_2) pub.sendMessage(pubsub_Re_Read_field_paramters_panel, event=event) def scale_manual(self, event, val=None): a = P4Rm() if val is not None: P4Rm.ParamDict['strain_multiplication'] = val temp_1 = a.ParamDict['sp'] temp_2 = a.ParamDict['strain_multiplication'] P4Rm.ParamDict['sp'] = multiply(temp_1, temp_2) pub.sendMessage(pubsub_Re_Read_field_paramters_panel, event=event) def motion_notify_callback(self, event): 'on mouse movement' a = P4Rm() if a.AllDataDict['damaged_depth'] == 0: return if not self.showverts: return if self._ind is None: return if event.inaxes is None: return if event.button != 1: return if self.modelpv is True: if self._ind == 0: y = event.ydata x = event.xdata elif self._ind == 1 or self._ind == 2: y = a.DragDrop_Strain_y[self.new_coord['indice']] x = event.xdata else: x = a.DragDrop_Strain_x[self.new_coord['indice']] y = event.ydata else: y = event.ydata x = a.DragDrop_Strain_x[self.new_coord['indice']] self.new_coord['x'] = x self.new_coord['y'] = y self.poly.xy[self._ind] = x, y self.line.set_data(zip(*self.poly.xy)) self.canvas.restore_region(self.background) self.ax.draw_artist(self.poly) self.ax.draw_artist(self.line) self.canvas.blit(self.ax.bbox) def on_update_coordinate(self, event): if event.inaxes is None: self.statusbar.SetStatusText(u"", 1) self.statusbar.SetStatusText(u"", 2) else: a = P4Rm() if not a.AllDataDict['damaged_depth'] == 0: x, y = event.xdata, event.ydata xfloat = round(float(x), 2) yfloat = round(float(y), 2) self.statusbar.SetStatusText(u"x = " + str(xfloat), 1) self.statusbar.SetStatusText(u"y = " + str(yfloat), 2) xy = np.asarray(self.poly.xy) xyt = self.poly.get_transform().transform(xy) xt, yt = xyt[:, 0], xyt[:, 1] d = np.sqrt((xt-event.x)**2 + (yt-event.y)**2) indseq = np.nonzero(np.equal(d, np.amin(d)))[0] ind = indseq[0] if d[ind] >= self.epsilon: self.canvas.SetCursor(Cursor(wx.CURSOR_ARROW)) elif d[ind] <= self.epsilon: self.canvas.SetCursor(Cursor(wx.CURSOR_HAND))
class AnalyzeORBResult(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent=parent) splitter = wx.SplitterWindow(self) # XXX: we may need to place the canvas in its own Panel self.figure = Figure() self.canvas = FigureCanvas(splitter, wx.ID_ANY, self.figure) self.vehiclePointer = None self.imageTopic = None rightPanel = wx.Panel(splitter) cPanel = wx.BoxSizer(wx.VERTICAL) rightPanel.SetSizer(cPanel) self.orbResultChooser = FilePrompt(rightPanel, 'ORB-SLAM Result Bag', self.onClickOrbChoose) cPanel.Add(self.orbResultChooser, flag=wx.EXPAND) self.groundTruthChooser = FilePrompt(rightPanel, 'GroundTruth', self.onClickGtChoose) cPanel.Add(self.groundTruthChooser, flag=wx.EXPAND) timePosBox = wx.BoxSizer(wx.HORIZONTAL) timePosBox.Add(wx.StaticText(rightPanel, label='Time Position'), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, proportion=0) self.timeDisplay = wx.TextCtrl(rightPanel, style=wx.TE_READONLY) timePosBox.Add(self.timeDisplay, flag=wx.EXPAND | wx.ALIGN_CENTER, proportion=1) cPanel.Add(timePosBox, flag=wx.EXPAND) orbStatusBox = wx.BoxSizer(wx.HORIZONTAL) orbStatusBox.Add(wx.StaticText(rightPanel, label="ORB Status"), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, proportion=0) self.orbStatus = wx.StaticText(rightPanel, label="Init") orbStatusBox.Add(self.orbStatus, flag=wx.ALIGN_CENTER | wx.EXPAND, proportion=1) cPanel.Add(orbStatusBox, flag=wx.EXPAND) bagGroup = wx.StaticBoxSizer( wx.StaticBox(rightPanel, label='Image Source Bag'), wx.VERTICAL) askBag = FilePrompt(rightPanel, "Image Bag File", self.onBagChange) bagGroup.Add(askBag, flag=wx.EXPAND) self.imageTopicChooser = wx.ComboBox(rightPanel) bagGroup.Add(self.imageTopicChooser, flag=wx.EXPAND) self.imageTopicChooser.Bind(wx.EVT_COMBOBOX, self.onImageTopicChange) self.bagImageView = BagImagePreview(rightPanel) bagGroup.Add(self.bagImageView, flag=wx.EXPAND) cPanel.Add(bagGroup, flag=wx.EXPAND | wx.ALIGN_CENTER_HORIZONTAL) splitter.SplitVertically(self.canvas, rightPanel) splitter.SetMinimumPaneSize(30) mainLayout = wx.BoxSizer(wx.VERTICAL) self.SetSizer(mainLayout) mainLayout.Add(splitter, proportion=1, flag=wx.EXPAND | wx.ALIGN_TOP) self.timeChooser = wx.Slider(self) self.timeChooser.Bind(wx.EVT_SCROLL, self.onChangeTimePosition) mainLayout.Add(self.timeChooser, flag=wx.EXPAND | wx.ALIGN_BOTTOM) self.Fit() def onClickOrbChoose(self, bagPath): self.loadData() def onClickGtChoose(self, gtPath): self.loadData() def onChangeTimePosition(self, e): if self.currentORBTimestamp == None: return # print (self.timeChooser.GetValue()) self.currentORBTimestamp = int(self.timeChooser.GetValue()) self.redrawPosition() def onBagChange(self, bagPath): self.bagImageView.setBagPath(bagPath) self.imageTopicChooser.AppendItems(self.bagImageView.getAllTopics()) def onImageTopicChange(self, e): self.imageTopic = (self.imageTopicChooser.GetValue()) self.bagImageView.setTopic(self.imageTopic) self.redrawPosition() def loadData(self): if (self.orbResultChooser.GetValue() == '' or self.groundTruthChooser.GetValue() == ''): print("Select filenames first") return None print("Wait...") # Load ground truth and plot it self.ax = self.figure.add_subplot(111) self.ax.set_autoscale_on(True) self.ax.grid(True) self.groundTruth = PoseTable.loadFromBagFile( self.groundTruthChooser.GetValue(), 'world', 'ndt_frame') gtTbl = self.groundTruth.toArray() self.groundTruthPlot, = self.ax.plot(gtTbl[:, 0], gtTbl[:, 1]) # self.groundTruthPlot, = self.ax.plot([0,1,2], [4,5,6]) self.orbResult = PoseTable.loadFromBagFile( self.orbResultChooser.GetValue(), '/ORB_SLAM/World', '/ORB_SLAM/ExtCamera') orbTbl = self.orbResult.toArray() self.orbResultPlot, = self.ax.plot(orbTbl[:, 0], orbTbl[:, 1]) self.canvas.draw() self.background = self.canvas.copy_from_bbox(self.ax.bbox) # self.helpText.Show(True) self.timeChooser.SetRange(self.orbResult[0].timestamp, self.orbResult.last().timestamp) self.currentORBTimestamp = self.orbResult[0].timestamp self.redrawPosition() @staticmethod def readMessage(bag, topic, timestamp): tm = rospy.Time.from_sec(timestamp) for topic, msg, time in bag.read_messages(topics=topic, start_time=tm): return msg def redrawPosition(self): if self.currentORBTimestamp == None: return dp = datetime.datetime.fromtimestamp(self.currentORBTimestamp) self.timeDisplay.SetValue(str(dp)) orbPose = self.orbResult.findNearestInTime(self.currentORBTimestamp, 0.1) self.canvas.restore_region(self.background) if (orbPose != None): if (self.vehiclePointer == None): self.vehiclePointer = self.ax.scatter(orbPose.x, orbPose.y, s=100, c=[1, 0, 0, 0.5], linewidths=0) else: self.vehiclePointer.set_offsets([orbPose.x, orbPose.y]) self.orbStatus.SetLabel("OK") else: self.orbStatus.SetLabel("Lost") self.canvas.draw() self.canvas.blit(self.ax.bbox) if self.imageTopic != None: self.bagImageView.showTime(self.currentORBTimestamp)
class CartesianPanel(wx.Panel): def _init_ctrls(self, prnt): # generated method, don't edit wx.Panel.__init__(self, id=wxID_LEFTPANEL, name='CartesianPanel', parent=prnt, pos=wx.Point(8, 8), size=wx.Size(200, 400), style=wx.NO_BORDER | wx.TAB_TRAVERSAL) self.SetClientSize(wx.Size(200, 400)) self.SetBackgroundColour(wx.Colour(0, 0, 255)) self.Bind(wx.EVT_PAINT, self.OnCartesianPanelPaint) def __init__(self, parent, id, pos, size, style, name): self._init_ctrls(parent) ##Create a matplotlib figure/canvas in this panel ##the background colour will be the same as the panel ##the size will also be the same as the panel ##calculate size in inches pixels_width, pixels_height = self.GetSizeTuple() self.dpi = 96.0 inches_width = pixels_width / self.dpi inches_height = pixels_height / self.dpi ##calculate colour in RGB 0 to 1 colour = self.GetBackgroundColour() self.fig = Figure(figsize=(inches_width,inches_height), dpi = self.dpi\ ,facecolor=(colour.Red()/255.0, colour.Green()/255.0, colour.Blue()/255.0)\ ,edgecolor=(colour.Red()/255.0, colour.Green()/255.0, colour.Blue()/255.0)) ##left : the left side of the subplots of the figure ## | right : the right side of the subplots of the figure ## | bottom : the bottom of the subplots of the figure ## | top : the top of the subplots of the figure ## | wspace : the amount of width reserved for blank space between subplots ## | hspace : the amount of height reserved for white space between subplots ## | self.canvas = FigureCanvasWxAgg(self, -1, self.fig) ##now put everything in a sizer sizer = wx.BoxSizer(wx.VERTICAL) # This way of adding to sizer allows resizing sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) self.SetSizer(sizer) self.Fit() ##now finally create the actual plot ##self.axes = self.fig.add_subplot(111) self.axes = self.fig.add_axes( (0.16, 0.08, 0.90, 0.85)) ##left,bottom,width,height self.plot = self.axes.plot([0, 0], [0, 0], 'b', animated=True) self.naohistoryplot = self.axes.plot([0, 0], [0, 0], 'r', animated=True) self.naohistoryx = list() self.naohistoryy = list() self.naoplot = self.axes.plot([0, 0], [0, 0], 'r', marker='o', markersize=4, animated=True) self.leftedgeplot = self.axes.plot([0, 0], [0, 0], 'orange', marker='o', markersize=4, linewidth=0, animated=True) self.rightedgeplot = self.axes.plot([0, 0], [0, 0], 'purple', marker='o', markersize=4, linewidth=0, animated=True) ##plot formatting self.axes.set_title('Laser Image', fontsize='10') #self.axes.set_xlabel('y (cm)', fontsize='10') #self.axes.set_ylabel('x (cm)', fontsize='10') ticks = numpy.arange(-450, 450 + 100, 100) labels = [str(tick) for tick in ticks] self.axes.set_yticks(ticks) self.axes.set_yticklabels(labels, fontsize=8) self.axes.set_ylim(ticks[0], ticks[-1]) ticks = numpy.arange(0, 450 + 100, 100) labels = [str(tick) for tick in ticks] self.axes.set_xticks(ticks) self.axes.set_xticklabels(labels, fontsize=8) self.axes.set_xlim(ticks[0], ticks[-1]) self.canvas.draw() self.canvas.gui_repaint() # save the clean slate background -- everything but the animated line # is drawn and saved in the pixel buffer background self.background = self.canvas.copy_from_bbox(self.axes.bbox) def setNaoFinder(self, finder): """ """ self.NAOFinder = finder def updateData(self, data, naox, naoy): """updateData. Updates the data that this panel is displaying. """ self.x = data[0] self.y = data[1] self.plot[0].set_data(self.x, self.y) self.naoplot[0].set_data([naox, naox], [naoy, naoy]) self.naohistoryx.append(naox) self.naohistoryy.append(naoy) if len(self.naohistoryx) > 400: del self.naohistoryx[0] del self.naohistoryy[0] self.naohistoryplot[0].set_data(self.naohistoryx, self.naohistoryy) leftx = list() lefty = list() for leftedge in self.NAOFinder.LeftEdges: leftx.append(data[0][leftedge]) lefty.append(data[1][leftedge]) rightx = list() righty = list() for rightedge in self.NAOFinder.RightEdges: rightx.append(data[0][rightedge]) righty.append(data[1][rightedge]) self.leftedgeplot[0].set_data(leftx, lefty) self.rightedgeplot[0].set_data(rightx, righty) # restore the clean slate background self.canvas.restore_region(self.background) # just draw the animated artist self.axes.draw_artist(self.plot[0]) self.axes.draw_artist(self.naoplot[0]) self.axes.draw_artist(self.naohistoryplot[0]) self.axes.draw_artist(self.leftedgeplot[0]) self.axes.draw_artist(self.rightedgeplot[0]) # just redraw the axes rectangle self.canvas.blit(self.axes.bbox) def OnCartesianPanelPaint(self, event): pass
class MyFrame(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self,parent, id, 'Biometrics Scanner', style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER, size=(800, 600)) self.panel = wx.Panel(self, -1) self.fig = Figure((5, 4), 75) self.canvas = FigureCanvasWxAgg(self.panel, -1, self.fig) self.init_plot() self.start_stop_button = wx.Button(self.panel, -1, "Start"); self.Bind(wx.EVT_BUTTON, self.start_stop_action, self.start_stop_button) self.mark_time_button = wx.Button(self.panel, -1, "Mark"); self.Bind(wx.EVT_BUTTON, self.mark_time_action, self.mark_time_button) self.subject_name_label = wx.StaticText(self.panel, label="", style=wx.ALIGN_CENTER) topBar = wx.BoxSizer(wx.HORIZONTAL) topBar.Add(self.start_stop_button, 1, wx.EXPAND) topBar.Add(self.subject_name_label, 2, wx.CENTER) topBar.Add(self.mark_time_button, 1, wx.EXPAND) font = wx.Font(22, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, True) aggregates_bar = wx.BoxSizer(wx.VERTICAL) bpmPanel = wx.Panel(self.panel, -1) self.currentBPM = wx.StaticText(bpmPanel, label='##', style=wx.ALIGN_RIGHT, pos=(10, 10)) self.currentBPM.SetFont(font) wx.StaticText(bpmPanel, label='BPM', style=wx.ALIGN_RIGHT, pos=(150, 10)) aggregates_bar.Add(bpmPanel, 1, wx.EXPAND) edResponsePanel = wx.Panel(self.panel,-1) self.currentEDR = wx.StaticText(edResponsePanel, label='##', style=wx.ALIGN_RIGHT, pos=(10,10)) self.currentEDR.SetFont(font) wx.StaticText(edResponsePanel, label='kOhms', style=wx.ALIGN_RIGHT, pos=(150,10)) aggregates_bar.Add(edResponsePanel, 1, wx.EXPAND) mainBar = wx.BoxSizer(wx.HORIZONTAL) mainBar.Add(self.canvas, 5, wx.EXPAND) mainBar.Add(aggregates_bar, 2, wx.EXPAND) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(topBar, 0, wx.EXPAND) sizer.Add(mainBar, 5, wx.EXPAND) self.panel.SetSizer(sizer) self.panel.Fit() # Setting up the menu. filemenu = wx.Menu() # wx.ID_ABOUT and wx.ID_EXIT are standard IDs provided by wxWidgets. about_button = filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program") self.Bind(wx.EVT_MENU, self.OnAbout, about_button) filemenu.AppendSeparator() exit_button = filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program") self.Bind(wx.EVT_MENU, self.OnExit, exit_button) # Creating the menubar. menuBar = wx.MenuBar() menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar self.SetMenuBar(menuBar) # Adding the MenuBar to the Frame content. self.Bind(EVT_REDRAW, self.onRedraw) self.Bind(EVT_RESTART, self.onRestart) self.mark_times = list() self.daqThread = None self.running = False self.beats_drawn = 0 self.marks_drawn = 0 def init_plot(self): self.t_window = 15 self.t_undrawn = 2 self.data_sets = ['ecg', 'bpm2', 'edr'] self.data_limits = [(-512, 512), (50, 150), (80, 300)] # self.data_limits = [(-10, 10), (50, 55), (150, 200)] self.lines = list() self.data_labels = ['ECG', 'Pulse Rate (BPM)', 'EDR (kOhms)'] self.show_beats = [True, False, False] self.show_marks = [True, True, True] self.axes = [self.fig.add_subplot(len(self.data_sets), 1, x) for x in range(1, len(self.data_sets) + 1)] self.reset_plot() def reset_plot(self): self.beats_drawn = 0 self.marks_drawn = 0 for ax, data_label, data_limit, show_beat, show_mark in zip( self.axes, self.data_labels, self.data_limits, self.show_beats, self.show_marks): ax.cla() ax.set_ylabel(data_label) data_set_line = ax.plot(0,0)[0] data_point_line = ax.plot(0,-600,'go')[0] beat_line = None mark_line = None if show_beat: beat_line = ax.plot(0,-600, 'ro')[0] if show_mark: mark_line = ax.plot(0,-600, 'ko')[0] ax.set_xlim(self.t_undrawn - self.t_window, self.t_undrawn) ax.set_ylim(*data_limit) self.lines.append((data_set_line, data_point_line, beat_line, mark_line)) self.canvas.draw() self.backgrounds = [self.canvas.copy_from_bbox(ax.bbox) for ax in self.axes] def reset_data_limits(self, ax, background, limits): ax.set_ylim(*limits) self.canvas.draw() background = self.canvas.copy_from_bbox(ax.bbox) def onRedraw(self, event): # last_drawable = self.daqThread.last_drawable # first_drawable = self.daqThread.first_drawable self.daqThread.redraw_lock() self.currentBPM.SetLabel('%0.3f' % self.daqThread.get_last('bpm2')) if self.daqThread.pulse_regular: self.currentBPM.SetForegroundColour((0,255,0)) else: self.currentBPM.SetForegroundColour((255,0,0)) self.currentEDR.SetLabel('%0.3f' % self.daqThread.get_last('edr')) t_max = self.daqThread.get_last('time') drawable_time = [x - t_max for x in self.daqThread.get_drawable('time')] t_cutoff = self.t_undrawn - self.t_window + t_max beats_list_x = None beats_list_y = None try: beats_list_x, beats_list_y = zip( *[(x - t_max, 1) for x in self.daqThread.beats if x > t_cutoff]) except ValueError: '''''' # print "beats_list unpopulated" marks_list_x = None marks_list_y = None try: marks_list_x, marks_list_y = zip( *[(x - t_max, 1) for x in self.daqThread.marks if x > t_cutoff]) except ValueError: '''''' for ax, background, data_set_name, data_label, line_tuple, data_limit in zip( self.axes, self.backgrounds, self.data_sets, self.data_labels, self.lines, self.data_limits): self.canvas.restore_region(background) data_set_line, data_point_line, beat_line, mark_line = line_tuple data_set_line.set_xdata(drawable_time) data_set_line.set_ydata(self.daqThread.get_drawable(data_set_name)) # new_data_limit = self.daqThread.get_y_limits(data_set_name) # if new_data_limit[0] < data_limit[0] or new_data_limit[1] > data_limit[1]: # self.reset_data_limits(ax, background, new_data_limit) ax.draw_artist(data_set_line) data_point_line.set_ydata(self.daqThread.get_last(data_set_name)) ax.draw_artist(data_point_line) if beat_line is not None and beats_list_x is not None: beat_line.set_xdata(beats_list_x); beat_line.set_ydata([y * float(data_limit[1] - (data_limit[1] - data_limit[0]) / 8.0) for y in beats_list_y]); ax.draw_artist(beat_line) if mark_line is not None and marks_list_x is not None: mark_line.set_xdata(marks_list_x) mark_line.set_ydata([y * float(data_limit[0] + (data_limit[1] - data_limit[0]) / 8.0) for y in marks_list_y]); ax.draw_artist(mark_line) self.canvas.blit(ax.bbox) self.daqThread.redraw_lock_release() def OnAbout(self, event): """""" def onRestart(self, event): if self.running: redrawThread = RedrawThread(self) redrawThread.start() def start_stop_action(self, event): if self.running: self.start_stop_button.SetLabel("Processing...") self.running = False self.daqThread.stop() while self.daqThread.is_alive(): sleep(0.1) self.daqThread = None self.start_stop_button.SetLabel("Start") else: self.reset_plot() self.running = True self.daqThread = DAQThread() self.daqThread.t_drawable = self.t_window - self.t_undrawn self.daqThread.start() redrawThread = RedrawThread(self) redrawThread.start() self.start_stop_button.SetLabel("Stop") def OnExit(self, event): exit() def mark_time_action(self, event): if self.daqThread.is_alive(): self.daqThread.add_mark()
class PlotFigure(wx.Frame): def __init__(self, portname, baudrate):### wx.Frame.__init__(self, None, wx.ID_ANY, title="Arduino Monitor", size=(800,600)) self.fig = Figure((8,6), 100) self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) self.ax = self.fig.add_subplot(1,1,1) self.ax.set_ylim([-0.1, 1.15]) self.ax.set_xlim([0,300]) self.ax.set_autoscale_on(False) self.xleft = 0 self.xright = 300 self.ax.set_xticks([]) self.ax.set_yticks([0.0,0.5,1.0]) self.ax.grid(True) self.data = [None] * 300 self.l_data,=self.ax.plot(range(300), self.data, label='Arduino Output') #',' means iteration self.l_x1 = self.ax.text(0,-0.05,'') self.l_x5 = self.ax.text(290,-0.05,'') self.ax.legend(loc='upper center', ncol=1) self.canvas.draw() self.bg = self.canvas.copy_from_bbox(self.ax.bbox) self.ser = serial.Serial(portname, baudrate)###open serial port and assign a baudrate time.sleep(5)#this command is very important since the arduino board needs a short while to settle. #without this settling time, the programme would got stuck! self.ser.flushInput() self.ser.flushOutput() self.counter = 0.0 wx.EVT_TIMER(self, TIMER_ID, self.onTimer)#binding def onTimer(self, evt): self.ser.write("?")#the py program might need to wait a bit till data arrives in buffer tmp = self.ser.read()#tmp is a string tmp = int(tmp)#tmp is an integer now self.canvas.restore_region(self.bg) self.data = self.data[1:] + [tmp]#keep self.data 300 elements long while forwarding the sequence self.xleft = self.xleft + 1 self.xright = self.xright + 1 #print self.xleft, self.xright self.l_data.set_ydata(self.data) self.counter = self.counter + 0.05 tmp1 = str(int(self.counter + 0.5)) tmp2 = str(int(self.counter + 0.5) - 15)# 15 = 300 pts / (1 sec / 50 msec) self.l_x1.set_text(tmp2) self.l_x5.set_text(tmp1) self.ax.draw_artist(self.l_data) self.ax.draw_artist(self.l_x1)## self.ax.draw_artist(self.l_x5)## self.canvas.blit(self.ax.bbox)
class MainFrame(wx.Frame): def __init__(self, parent=None, id=-1, title="Main Frame"): wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(900, 700), style=wx.DEFAULT_FRAME_STYLE) self.create_menu() self.statusbar = self.CreateStatusBar() self.create_main_panel() self.Centre() # self.beatingimage = BeatingImage(path="dati/generated.dat", repetitions=15) # self.beatingdata = BeatingImageRow(data=self.beatingimage.data[24,:,:], pixel_frequency=100.0, shutter_frequency=5.856 / 2) self.beatingimage = BeatingImage(path="dati/samp6.dat") self.beatingdata = BeatingImageRow(data=self.beatingimage.data[1,:,:], pixel_frequency=100.0, shutter_frequency=5.865 / 2) self.drawingdata = self.beatingdata.data self.line_det_h, = self.axes_det1.plot( arange(self.beatingdata.image_width), zeros_like(arange(self.beatingdata.image_width)), animated=True) self.axes_det1.set_ylim(self.beatingdata.data.min(), self.beatingdata.data.max()) self.line_det_v, = self.axes_det2.plot( arange(self.beatingdata.image_height), zeros_like(arange(self.beatingdata.image_height)), animated=True) self.axes_det2.set_ylim(self.beatingdata.data.min(), self.beatingdata.data.max()) self.crosshair_lock = False self.draw_figure() def create_menu(self): self.menubar = wx.MenuBar() file_menu = wx.Menu() close_window_menu = wx.MenuItem(file_menu, 105, 'Close &Window\tCtrl+W', 'Close the Window') file_menu.AppendItem(close_window_menu) self.Bind(wx.EVT_MENU, self.OnCloseMe, close_window_menu) self.menubar.Append(file_menu, '&File') self.SetMenuBar(self.menubar) def create_main_panel(self): """ Creates the main panel with all the controls on it: * mpl canvas * mpl navigation toolbar * Control panel for interaction """ self.panel = wx.Panel(self) # Create the mpl Figure and FigCanvas objects. # 5x4 inches, 100 dots-per-inch self.dpi = 100 self.fig = Figure((9.0, 7.0), dpi=self.dpi) self.canvas = FigCanvas(self.panel, -1, self.fig) self.onclick_cid = self.canvas.mpl_connect('button_press_event', self.on_mouseclick) self.detailfig = Figure((1.0, 7.0), dpi=self.dpi) self.detailcanvas = FigCanvas(self.panel, -1, self.detailfig) # Since we have only one plot, we can use add_axes # instead of add_subplot, but then the subplot # configuration tool in the navigation toolbar wouldn't # work. self.axes = self.fig.add_subplot(111) self.axes_det1 = self.detailfig.add_subplot(211) self.axes_det2 = self.detailfig.add_subplot(212) self.in_axes = False self.canvas.mpl_connect('axes_enter_event', self.enter_axes) self.canvas.mpl_connect('axes_leave_event', self.leave_axes) self.cb_grid = wx.CheckBox(self.panel, -1, "Show Grid", style=wx.ALIGN_RIGHT) self.Bind(wx.EVT_CHECKBOX, self.on_cb_grid, self.cb_grid) self.cb_unbleach = wx.CheckBox(self.panel, -1, "Correct for bleaching", style=wx.ALIGN_RIGHT) self.Bind(wx.EVT_CHECKBOX, self.on_cb_unbleach, self.cb_unbleach) self.cb_ratiograph = wx.CheckBox(self.panel, -1, "Show enhancement ratio data") self.cb_ratiograph.Enable(False) self.Bind(wx.EVT_CHECKBOX, self.on_cb_ratiograph, self.cb_ratiograph) self.slider_label = wx.StaticText(self.panel, -1, "Crosshair opacity (%): ") self.slider_alpha = wx.Slider(self.panel, -1, value=30, minValue=1, maxValue=100, style=wx.SL_AUTOTICKS | wx.SL_LABELS) self.alpha = 0.3 self.slider_alpha.SetTickFreq(5, 1) self.Bind(wx.EVT_COMMAND_SCROLL_THUMBTRACK, self.on_slider_alpha, self.slider_alpha) # Create the navigation toolbar, tied to the canvas self.toolbar = NavigationToolbar(self.canvas) # # Layout with box sizers # self.vbox = wx.BoxSizer(wx.VERTICAL) self.graphbox = wx.BoxSizer(wx.HORIZONTAL) flags = wx.ALIGN_LEFT | wx.ALL | wx.ALIGN_CENTER_VERTICAL self.graphbox.Add(self.canvas, 2, flag=flags| wx.GROW) self.graphbox.Add(self.detailcanvas, 1, flag=flags | wx.GROW) self.vbox.Add(self.graphbox, 1, wx.LEFT | wx.TOP | wx.GROW) self.vbox.Add(self.toolbar, 0, wx.EXPAND) self.vbox.AddSpacer(10) self.hbox = wx.BoxSizer(wx.HORIZONTAL) flags = wx.ALIGN_LEFT | wx.ALL | wx.ALIGN_CENTER_VERTICAL self.hbox.Add(self.cb_unbleach, 0, border=3, flag=flags) self.hbox.Add(self.cb_ratiograph, 0, border=3, flag=flags) self.hbox.Add(self.cb_grid, 0, border=3, flag=flags) self.hbox.AddSpacer(30) self.hbox.Add(self.slider_label, 0, flag=flags) self.hbox.Add(self.slider_alpha, 0, border=3, flag=flags) self.vbox.Add(self.hbox, 0, flag = wx.ALIGN_CENTER | wx.TOP) self.panel.SetSizer(self.vbox) self.vbox.Fit(self) self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.callback, self.timer) self.prevx, self.prevy = -1, -1 def draw_figure(self): """ Redraws the figure """ self.axes.clear() if self.cb_grid.IsChecked(): self.axes.grid(b=True, color="#ffffff", alpha=0.8) if self.cb_unbleach.IsChecked(): self.drawingdata = self.beatingdata.unbleached_data else: self.drawingdata = self.beatingdata.data self.beating_image = self.axes.imshow(self.drawingdata, cmap=my_color_map) self.beating_image.set_interpolation('nearest') self.canvas.draw() self.detailcanvas.draw() if not self.cb_ratiograph.IsChecked(): self.background_h = self.detailcanvas.copy_from_bbox(self.axes_det1.bbox) self.background_v = self.detailcanvas.copy_from_bbox(self.axes_det2.bbox) def on_cb_grid(self, event): self.draw_figure() def on_cb_unbleach(self, event): if self.cb_unbleach.IsChecked(): self.cb_ratiograph.Enable(True) else: self.cb_ratiograph.Enable(False) self.draw_figure() def on_cb_ratiograph(self,event): if self.cb_ratiograph.IsChecked(): self.axes_det1.clear() width = self.drawingdata.shape[1] self.er_graph, = self.axes_det1.plot( arange(width), self.beatingdata.enhancement_ratios) self.axes_det2.clear() self.min_graph, = self.axes_det2.plot( arange(width), self.beatingdata.reconstructed_off) self.max_graph, = self.axes_det2.plot( arange(width), self.beatingdata.reconstructed_on) self.detailcanvas.draw() self.axes_det1.autoscale() self.axes_det2.autoscale() else: # Riattivare il vecchio grafico! self.axes_det1.cla() self.axes_det2.cla() self.line_det_h, = self.axes_det1.plot( arange(self.beatingdata.image_width), zeros_like(arange(self.beatingdata.image_width)), animated=True) self.axes_det1.set_ylim(self.beatingdata.data.min(), self.beatingdata.data.max()) self.line_det_v, = self.axes_det2.plot( arange(self.beatingdata.image_height), zeros_like(arange(self.beatingdata.image_height)), animated=True) self.axes_det2.set_ylim(self.beatingdata.data.min(), self.beatingdata.data.max()) self.detailcanvas.draw() self.background_h = self.detailcanvas.copy_from_bbox(self.axes_det1.bbox) self.background_v = self.detailcanvas.copy_from_bbox(self.axes_det2.bbox) def OnCloseMe(self, event): self.Close(True) def on_mouseover(self, event): if event.inaxes == self.axes: x, y = int(floor(event.xdata)), int(floor(event.ydata)) self.x, self.y = x, y def on_mouseclick(self, event): if event.inaxes == self.axes: if not self.crosshair_lock: self.crosshair_lock = True self.deactivate_mouseover() x, y = int(floor(event.xdata)), int(floor(event.ydata)) self.x, self.y = x, y else: self.crosshair_lock = False x, y = int(floor(event.xdata)), int(floor(event.ydata)) self.x, self.y = x, y self.activate_mouseover() def activate_mouseover(self): self.cid = self.canvas.mpl_connect('motion_notify_event', self.on_mouseover) self.timer.Start(80) def deactivate_mouseover(self): self.canvas.mpl_disconnect(self.cid) self.timer.Stop() def enter_axes(self, event): self.in_axes = True if not self.crosshair_lock: self.activate_mouseover() def leave_axes(self, event): self.in_axes = False if not self.crosshair_lock: self.deactivate_mouseover() self.statusbar.SetStatusText(" ") self.beating_image.set_array(self.drawingdata) self.canvas.draw() if not self.cb_ratiograph.IsChecked(): self.axes_det1.clear() self.axes_det2.clear() self.detailcanvas.draw() def callback(self, event): if self.in_axes and (self.x != self.prevx or self.y != self.prevy): x, y = self.x, self.y value = self.drawingdata[y, x] msg = "Coordinate: {0}, {1} Valore: {2}".format(x, y, value) self.statusbar.SetStatusText(msg) highlight_data = copy(self.drawingdata) highlight_data[:, x] = highlight_data[:, x] * (1.0 - self.alpha) + highlight_data.max() * self.alpha highlight_data[y, :] = highlight_data[y, :] * (1.0 - self.alpha) + highlight_data.max() * self.alpha highlight_data[y, x] = value self.beating_image.set_array(highlight_data) self.canvas.draw() # Aggiorno i dettagli if not self.cb_ratiograph.IsChecked(): self.detailcanvas.restore_region(self.background_h) self.detailcanvas.restore_region(self.background_v) self.line_det_h.set_ydata(self.drawingdata[y, :]) self.line_det_v.set_ydata(self.drawingdata[:, x]) self.axes_det1.draw_artist(self.line_det_h) self.axes_det2.draw_artist(self.line_det_v) self.detailcanvas.blit(self.axes_det1.bbox) self.detailcanvas.blit(self.axes_det2.bbox) self.prevx, self.prevy = x, y def on_slider_alpha(self, event): self.alpha = self.slider_alpha.GetValue() / 100.0
class PlotFrame(wx.Frame): """ PlotFrame is a custom wxPython frame to hold the panel with a Figure and WxAgg backend canvas for matplotlib plots or other figures. In this frame: self is an instance of a wxFrame; axes is an instance of MPL Axes; fig is an instance of MPL Figure; panel is an instance of wxPanel, used for the main panel, to hold canvas, an instance of MPL FigureCanvasWxAgg. """ # Main function to set everything up when the frame is created def __init__(self, title, pos, size): """ This will be executed when an instance of PlotFrame is created. It is the place to define any globals as "self.<name>". """ wx.Frame.__init__(self, None, wx.ID_ANY, title, pos, size) if len(sys.argv) < 2: self.filename = "" else: self.filename = sys.argv[1] # set some Boolean flags self.STOP = False self.data_loaded = False self.reverse_play = False self.step = 1 # Make the main Matplotlib panel for plots self.create_main_panel() # creates canvas and contents # Then add wxPython widgets below the MPL canvas # Layout with box sizers self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.EXPAND) self.sizer.AddSpacer(10) self.sizer.Add(self.toolbar, 0, wx.EXPAND) self.sizer.AddSpacer(10) # Make the control panel with a row of buttons self.create_button_bar() self.sizer.Add(self.button_bar_sizer, 0, flag=wx.ALIGN_CENTER | wx.TOP) # Make a Status Bar self.statusbar = self.CreateStatusBar() self.sizer.Add(self.statusbar, 0, wx.EXPAND) self.SetStatusText("Frame created ...") # ------------------------------------------------------- # set up the Menu Bar # ------------------------------------------------------- menuBar = wx.MenuBar() menuFile = wx.Menu() # File menu menuFile.Append(1, "&Open", "Filename(s) or wildcard list to plot") menuFile.Append(3, "Save", "Save plot as a PNG image") menuFile.AppendSeparator() menuFile.Append(10, "E&xit") menuBar.Append(menuFile, "&File") menuHelp = wx.Menu() # Help menu menuHelp.Append(11, "&About Netview") menuHelp.Append(12, "&Usage and Help") menuHelp.Append(13, "Program &Info") menuBar.Append(menuHelp, "&Help") self.SetMenuBar(menuBar) self.panel.SetSizer(self.sizer) self.sizer.Fit(self) # ------------------------------------------------------- # Bind the menu items to functions # ------------------------------------------------------- self.Bind(wx.EVT_MENU, self.OnOpen, id=1) self.Bind(wx.EVT_MENU, self.OnSave, id=3) self.Bind(wx.EVT_MENU, self.OnQuit, id=10) self.Bind(wx.EVT_MENU, self.OnAbout, id=11) self.Bind(wx.EVT_MENU, self.OnUsage, id=12) self.Bind(wx.EVT_MENU, self.OnInfo, id=13) # methods defined below to get and plot the data # Normally do the plot on request, and not here # self.get_data_params() # self.init_plot() # self.get_xyt_data() # plot_data() # ---------- end of __init__ ---------------------------- # ------------------------------------------------------- # Function to make the main Matplotlib panel for plots # ------------------------------------------------------- def create_main_panel(self): """ create_main_panel creates the main mpl panel with instances of: * mpl Canvas * mpl Figure * mpl Figure * mpl Axes with subplot * mpl Widget class Sliders and Button * mpl navigation toolbar self.axes is the instance of MPL Axes, and is where it all happens """ self.panel = wx.Panel(self) # Create the mpl Figure and FigCanvas objects. # 3.5 x 5 inches, 100 dots-per-inch # self.dpi = 100 self.fig = Figure((3.5, 5.0), dpi=self.dpi) self.canvas = FigCanvas(self.panel, wx.ID_ANY, self.fig) # Since we have only one plot, we could use add_axes # instead of add_subplot, but then the subplot # configuration tool in the navigation toolbar wouldn't work. self.axes = self.fig.add_subplot(111) # (111) == (1,1,1) --> row 1, col 1, Figure 1) # self.axes.set_title("View from: "+self.filename) # Now create some sliders below the plot after making room self.fig.subplots_adjust(left=0.1, bottom=0.20) self.axtmin = self.fig.add_axes([0.2, 0.10, 0.5, 0.03]) self.axtmax = self.fig.add_axes([0.2, 0.05, 0.5, 0.03]) self.stmin = Slider(self.axtmin, 't_min:', 0.0, 1.0, valinit=0.0) self.stmax = Slider(self.axtmax, 't_max:', 0.0, 1.0, valinit=1.0) self.stmin.on_changed(self.update_trange) self.stmax.on_changed(self.update_trange) self.axbutton = self.fig.add_axes([0.8, 0.07, 0.1, 0.07]) self.reset_button = Button(self.axbutton, 'Reset') self.reset_button.color = 'skyblue' self.reset_button.hovercolor = 'lightblue' self.reset_button.on_clicked(self.reset_trange) # Create the navigation toolbar, tied to the canvas self.toolbar = NavigationToolbar(self.canvas) def update_trange(self, event): self.t_min = self.stmin.val self.t_max = self.stmax.val # print(self.t_min, self.t_max) def reset_trange(self, event): self.stmin.reset() self.stmax.reset() def create_button_bar(self): """ create_button_bar makes a control panel bar with buttons and toggles for New Data - Play - STOP - Single Step - Forward/Back - Normal/Fast It does not create a Panel container, but simply creates Button objects with bindings, and adds them to a horizontal BoxSizer self.button_bar_sizer. This is added to the PlotFrame vertical BoxSizer, after the MPL canvas, during initialization of the frame. """ rewind_button = wx.Button(self.panel, -1, "New Data") self.Bind(wx.EVT_BUTTON, self.OnRewind, rewind_button) replot_button = wx.Button(self.panel, -1, "Play") self.Bind(wx.EVT_BUTTON, self.OnReplot, replot_button) sstep_button = wx.Button(self.panel, -1, "Single Step") self.Bind(wx.EVT_BUTTON, self.OnSstep, sstep_button) stop_button = wx.Button(self.panel, -1, "STOP") self.Bind(wx.EVT_BUTTON, self.OnStop, stop_button) # The toggle buttons need to be globally accessible self.forward_toggle = wx.ToggleButton(self.panel, -1, "Forward") self.forward_toggle.SetValue(True) self.forward_toggle.SetLabel("Forward") self.Bind(wx.EVT_TOGGLEBUTTON, self.OnForward, self.forward_toggle) self.fast_toggle = wx.ToggleButton(self.panel, -1, " Normal ") self.fast_toggle.SetValue(True) self.fast_toggle.SetLabel(" Normal ") self.Bind(wx.EVT_TOGGLEBUTTON, self.OnFast, self.fast_toggle) # Set button colors to some simple colors that are likely # to be independent on X11 color definitions. Some nice # bit maps (from a media player skin?) should be used # or the buttons and toggle state colors in OnFast() below rewind_button.SetBackgroundColour('skyblue') replot_button.SetBackgroundColour('skyblue') sstep_button.SetBackgroundColour('skyblue') stop_button.SetBackgroundColour('skyblue') self.forward_toggle.SetForegroundColour('black') self.forward_toggle.SetBackgroundColour('yellow') self.fast_toggle.SetForegroundColour('black') self.fast_toggle.SetBackgroundColour('yellow') self.button_bar_sizer = wx.BoxSizer(wx.HORIZONTAL) flags = wx.ALIGN_CENTER | wx.ALL self.button_bar_sizer.Add(rewind_button, 0, border=3, flag=flags) self.button_bar_sizer.Add(replot_button, 0, border=3, flag=flags) self.button_bar_sizer.Add(sstep_button, 0, border=3, flag=flags) self.button_bar_sizer.Add(stop_button, 0, border=3, flag=flags) self.button_bar_sizer.Add(self.forward_toggle, 0, border=3, flag=flags) self.button_bar_sizer.Add(self.fast_toggle, 0, border=3, flag=flags) # ------------------------------------------------------- # Functions to generate or read (x,y) data and plot it # ------------------------------------------------------- def get_data_params(self): # These parameters would normally be provided in a file header, # past as arguments in a function, or from other file information # Next version will bring up a dialog for dt NX NY if no file header # Here check to see if a filename should be entered from File/Open # self.filename = 'Ex_net_Vm_0001.txt' if len(self.filename) == 0: # fake a button press of File/Open self.OnOpen(wx.EVT_BUTTON) # should check here if file exists as specified [path]/filename # assume it is a bzip2 compressed file try: fp = bz2.BZ2File(self.filename) line = fp.readline() except IOError: # then assume plain text fp = open(self.filename) line = fp.readline() fp.close() # check if first line is a header line starting with '#' header = line.split() if header[0][0] == "#": self.Ntimes = int(header[1]) self.t_min = float(header[2]) self.dt = float(header[3]) self.NX = int(header[4]) self.NY = int(header[5]) else: pdentry = self.ParamEntryDialog() if pdentry.ShowModal() == wx.ID_OK: self.Ntimes = int(pdentry.Ntimes_dialog.entry.GetValue()) self.t_min = float(pdentry.tmin_dialog.entry.GetValue()) self.dt = float(pdentry.dt_dialog.entry.GetValue()) self.NX = int(pdentry.NX_dialog.entry.GetValue()) self.NY = int(pdentry.NY_dialog.entry.GetValue()) print 'Ntimes = ', self.Ntimes, ' t_min = ', self.t_min print 'NX = ', self.NX, ' NY = ', self.NY pdentry.Destroy() self.t_max = (self.Ntimes - 1) * self.dt # reset slider max and min self.stmin.valmax = self.t_max self.stmin.valinit = self.t_min self.stmax.valmax = self.t_max self.stmax.valinit = self.t_max self.stmax.set_val(self.t_max) self.stmin.reset() self.stmax.reset() fp.close() def init_plot(self): ''' init_plot creates the initial plot display. A normal MPL plot would be created here with a command "self.axes.plot(x, y)" in order to create a plot of points in the x and y arrays on the Axes subplot. Here, we create an AxesImage instance with imshow(), instead. The initial image is a blank one of the proper dimensions, filled with zeroes. ''' self.t_max = (self.Ntimes - 1) * self.dt self.axes.set_title("View of " + self.filename) # Note that NumPy array (row, col) = image (y, x) data0 = np.zeros((self.NY, self.NX)) # Define a 'cold' to 'hot' color scale based in GENESIS 2 'hot' hotcolors = [ '#000032', '#00003c', '#000046', '#000050', '#00005a', '#000064', '#00006e', '#000078', '#000082', '#00008c', '#000096', '#0000a0', '#0000aa', '#0000b4', '#0000be', '#0000c8', '#0000d2', '#0000dc', '#0000e6', '#0000f0', '#0000fa', '#0000ff', '#000af6', '#0014ec', '#001ee2', '#0028d8', '#0032ce', '#003cc4', '#0046ba', '#0050b0', '#005aa6', '#00649c', '#006e92', '#007888', '#00827e', '#008c74', '#00966a', '#00a060', '#00aa56', '#00b44c', '#00be42', '#00c838', '#00d22e', '#00dc24', '#00e61a', '#00f010', '#00fa06', '#00ff00', '#0af600', '#14ec00', '#1ee200', '#28d800', '#32ce00', '#3cc400', '#46ba00', '#50b000', '#5aa600', '#649c00', '#6e9200', '#788800', '#827e00', '#8c7400', '#966a00', '#a06000', '#aa5600', '#b44c00', '#be4200', '#c83800', '#d22e00', '#dc2400', '#e61a00', '#f01000', '#fa0600', '#ff0000', '#ff0a00', '#ff1400', '#ff1e00', '#ff2800', '#ff3200', '#ff3c00', '#ff4600', '#ff5000', '#ff5a00', '#ff6400', '#ff6e00', '#ff7800', '#ff8200', '#ff8c00', '#ff9600', '#ffa000', '#ffaa00', '#ffb400', '#ffbe00', '#ffc800', '#ffd200', '#ffdc00', '#ffe600', '#fff000', '#fffa00', '#ffff00', '#ffff0a', '#ffff14', '#ffff1e', '#ffff28', '#ffff32', '#ffff3c', '#ffff46', '#ffff50', '#ffff5a', '#ffff64', '#ffff6e', '#ffff78', '#ffff82', '#ffff8c', '#ffff96', '#ffffa0', '#ffffaa', '#ffffb4', '#ffffbe', '#ffffc8', '#ffffd2', '#ffffdc', '#ffffe6', '#fffff0' ] cmap = matplotlib.colors.ListedColormap(hotcolors) self.im = self.axes.imshow(data0, cmap=cmap, origin='lower') # http://matplotlib.sourceforge.net/examples/pylab_examples/show_colormaps.html # shows examples to use as a 'cold' to 'hot' mapping of value to color # cm.jet, cm.gnuplot and cm.afmhot are good choices, but are unlike G2 'hot' self.im.cmap = cmap # Not sure how to properly add a colorbar # self.cb = self.fig.colorbar(self.im, orientation='vertical') # refresh the canvas self.canvas.draw() def get_xyt_data(self): # Create scaled (0-1) luminance(x,y) array from ascii G-2 disk_out file # get the data to plot from the specified filename # Note that NumPy loadtxt transparently deals with bz2 compression self.SetStatusText('Data loading - please wait ....') rawdata = np.loadtxt(self.filename) # Note the difference between NumPy [row, col] order and network # x-y grid (x, y) = (col, row). We want a NumPy NY x NX, not # NX x NY, array to be used by the AxesImage object. xydata = np.resize(rawdata, (self.Ntimes, self.NY, self.NX)) # imshow expects the data to be scaled to range 0-1. Vmin = xydata.min() Vmax = xydata.max() self.ldata = (xydata - Vmin) / (Vmax - Vmin) self.data_loaded = True self.SetStatusText('Data has been loaded - click Play') def plot_data(self): ''' plot_data() shows successive frames of the data that was loaded into the ldata array. Creating a new self.im AxesImage instance for each frame is extremely slow, so the set_data method of AxesImage is used to load new data into the existing self.im for each frame. Normally 'self.canvas.draw()' would be used to display a frame, but redrawing the entire canvas, redraws the axes, labels, sliders, buttons, or anything else on the canvas. This uses a method taken from an example in Ch 7, p. 192 Matplotlib for Python developers, with draw_artist() and blit() redraw only the part that was changed. ''' if self.data_loaded == False: # bring up a warning dialog msg = """ Data for plotting has not been loaded! Please enter the file to plot with File/Open, unless it was already specified, and then click on 'New Data' to load the data to play back, before clicking 'Play'. """ wx.MessageBox(msg, "Plot Warning", wx.OK | wx.ICON_ERROR, self) return # set color limits self.im.set_clim(0.0, 1.0) self.im.set_interpolation('nearest') # 'None' is is slightly faster, but not implemented for MPL ver < 1.1 # self.im.set_interpolation('None') # do an initial draw, then save the empty figure axes self.canvas.draw() # self.bg = self.canvas.copy_from_bbox(self.axes.bbox) # However the save and restore is only needed if I change # axes legends, etc. The draw_artist(artist), and blit # are much faster than canvas.draw() and are sufficient. print 'system time (seconds) = ', time.time() # round frame_min down and frame_max up for the time window frame_min = int(self.t_min / self.dt) frame_max = min(int(self.t_max / self.dt) + 1, self.Ntimes) frame_step = self.step # Displaying simulation time to the status bar is much faster # than updating a slider progress bar, but location isn't optimum. # The check for the STOP button doesn't work because the button # click is not registered until this function exits. # check to see if self.reverse_play == True # then interchange frame_min, frame_max, and use negative step if self.reverse_play == True: frame_min = min(int(self.t_max / self.dt) + 1, self.Ntimes) - 1 frame_max = int(self.t_min / self.dt) - 1 frame_step = -self.step for frame_num in range(frame_min, frame_max, frame_step): self.SetStatusText('time: ' + str(frame_num * self.dt)) if self.STOP == True: self.t_min = frame_num * self.dt # set t_min slider ? self.STOP = False break self.im.set_data(self.ldata[frame_num]) self.axes.draw_artist(self.im) self.canvas.blit(self.axes.bbox) print 'system time (seconds) = ', time.time() # ------------------------------------------------------------------ # Define the classes and functions for getting parameter values # -------------------------------------------------------------- class ParamEntryDialog(wx.Dialog): def __init__(self): wx.Dialog.__init__(self, None, wx.ID_ANY) self.SetSize((250, 200)) self.SetTitle('Enter Data File Parameters') vbox = wx.BoxSizer(wx.VERTICAL) self.Ntimes_dialog = XDialog(self) self.Ntimes_dialog.entry_label.SetLabel('Number of entries') self.Ntimes_dialog.entry.ChangeValue(str(2501)) self.tmin_dialog = XDialog(self) self.tmin_dialog.entry_label.SetLabel('Start time (sec)') self.tmin_dialog.entry.ChangeValue(str(0.0)) self.dt_dialog = XDialog(self) self.dt_dialog.entry_label.SetLabel('Output time step (sec)') self.dt_dialog.entry.ChangeValue(str(0.0002)) self.NX_dialog = XDialog(self) self.NX_dialog.entry_label.SetLabel('Number of cells on x-axis') self.NX_dialog.entry.ChangeValue(str(32)) self.NY_dialog = XDialog(self) self.NY_dialog.entry_label.SetLabel('Number of cells on y-axis') self.NY_dialog.entry.ChangeValue(str(32)) vbox.Add(self.Ntimes_dialog, 0, wx.EXPAND | wx.ALL, border=5) vbox.Add(self.tmin_dialog, 0, wx.EXPAND | wx.ALL, border=5) vbox.Add(self.dt_dialog, 0, wx.EXPAND | wx.ALL, border=5) vbox.Add(self.NX_dialog, 0, wx.EXPAND | wx.ALL, border=5) vbox.Add(self.NY_dialog, 0, wx.EXPAND | wx.ALL, border=5) okButton = wx.Button(self, wx.ID_OK, 'Ok') # vbox.Add(okButton,flag=wx.ALIGN_CENTER|wx.TOP|wx.BOTTOM, border=10) vbox.Add(okButton, flag=wx.ALIGN_CENTER, border=10) self.SetSizer(vbox) self.SetSizerAndFit(vbox) # ------------------------------------------------------------------ # Define the functions executed on menu choices # --------------------------------------------------------------- def OnQuit(self, event): self.Close() def OnSave(self, event): file_choices = "PNG (*.png)|*.png" dlg = wx.FileDialog(self, message="Save plot as...", defaultDir=os.getcwd(), defaultFile="plot.png", wildcard=file_choices, style=wx.SAVE) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() self.canvas.print_figure(path, dpi=self.dpi) # self.flash_status_message("Saved to %s" % path) def OnAbout(self, event): msg = """ G-3 Netview ver. 1.7 Netview is a stand-alone Python application for viewing the output of GENESIS 2 and 3 network simulations. It is intended to replace GENESIS 2 SLI scripts that use the XODUS 'xview' widget. The design and operation is based on the G3Plot application for creating 2D plots of y(t) or y(x) from data files. Unlike G3Plot, the image created with Netview is an animated representation of a rectangular network with colored squares used to indicate the value of some variable at that position and time. Typically, this would be the membrane potenial of a cell soma, or a synaptic current in a dendrite segment. Help/Usage gives HTML help for using Netview. This is the main Help page. Help/Program Info provides some information about the objects and functions, and the wxPython and matplotlib classes used here. Dave Beeman, August 2012 """ dlg = wx.MessageDialog(self, msg, "About G-3 Netview", wx.OK | wx.ICON_QUESTION) dlg.ShowModal() dlg.Destroy() def OnOpen(self, event): dlg = wx.TextEntryDialog(self, "File with x,y data to plot", "File Open", self.filename, style=wx.OK | wx.CANCEL) if dlg.ShowModal() == wx.ID_OK: self.filename = dlg.GetValue() # A new filename has been entered, but the data has not been read self.data_loaded = False # print "You entered: %s" % self.filename dlg.Destroy() # This starts with the long string of HTML to display class UsageFrame(wx.Frame): text = """ <HTML> <HEAD></HEAD> <BODY BGCOLOR="#D6E7F7"> <CENTER><H1>Using G-3 Netview</H1></CENTER> <H2>Introduction and Quick Start</H2> <p>Netview is a stand-alone Python application for viewing the output of GENESIS 2 and 3 network simulations. It is intended to replace GENESIS 2 SLI scripts that use the XODUS 'xview' widget.</p> <p>The design and operation is based on the G3Plot application for creating 2D plots of y(t) or y(x) from data files. As with G3Plot, the main class PlotFrame uses a basic wxPython frame to embed a matplotlib figure for plotting. It defines some basic menu items and a control panel of buttons and toggles, each with bindings to a function to execute on a mouse click.</p> <p>Unlike G3Plot, the image created with Netview is an animated representation of a rectangular network with colored squares used to indicate the value of some variable at that position and time. Typically, this would be the membrane potenial of a cell soma, or a synaptic current in a dendrite segment.</p> <h2>Usage</h2> <p>The Menu Bar has <em>File/Open</em>, <em>File/Save</em>, and <em>File/Exit</em> choices. The Help Menu choices <em>About</em> and <em>Usage</em> give further information. The <em>Program Info</em> selection shows code documentation that is contained in some of the main function <em>docstrings</em>.</p> <p>After starting the <em>netview</em> program, enter a data file name in the dialog for File/Open, unless the filename was given as a command line argument. Then click on <strong>New Data</strong> to load the new data and initialize the plot. When the plot is cleared to black, press <strong>Play</strong>.</p> <p>The file types recognized are plain text or text files compressed with bzip2. The expected data format is one line for each output time step, with each line having the membrane potential value of each cell in the net. No time value should be given on the line. In order to properly display the data, netview needs some additional information about the network and the data. This can optionally be contained in a header line that precedes the data. If a header is not detected, a dialog will appear asking for the needed parameters.</p> <p>It is assumed that the cells are arranged on a NX x NY grid, numbered from 0 (bottom left corner) to NX*NY - 1 (upper right corner). In order to provide this information to netview, the data file should begin with a header line of the form:</p> <pre> #optional_RUNID_string Ntimes start_time dt NX NY SEP_X SEP_Y x0 y0 z0 </pre> <p>The line must start with "#" and can optionally be followed immediately by any string. Typically this is some identification string generated by the simulation run. The following parameters, separated by blanks or any whitespace, are:</p> <ul> <li>Ntimes - the number of lines in the file, exclusive of the header</li> <li>start_time - the simulation time for the first data line (default 0.0)</li> <li>dt - the time step used for output</li> <li>NX, NY - the integer dimensions of the network</li> <li>SEP_X, SEP_Y - the x,y distances between cells (optional)</li> <li>x0, y0, z0 - the location of the compartment (data source) relative to the cell origin</li> </ul> <p>The RUNID string and the last five parameters are not read or used by netview. These are available for other data analysis tools that need a RUNID and the location of each source.</p> <p>The slider bars can be used to set a time window for display, and the <strong>Reset</strong> button can set t_min and t_max back to the defaults. Use the <strong>Forward/Back</strong> toggle to reverse direction of <strong>Play</strong>, and the <strong>Normal/Fast</strong> toggle to show every tenth frame.</p> <p>The <strong>Single Step</strong> button can be used to advance a single step at a time (or 10, if in 'Fast' mode).</p> <p>The <strong>STOP</strong> button is currently not implemented</p> <p>To plot different data, enter a new filename with <strong>File/Open</strong> and repeat with <strong>New Data</strong> and <strong>Play</strong>.</p> <HR> </BODY> </HTML> """ def __init__(self, parent): wx.Frame.__init__(self, parent, -1, "Usage and Help", size=(640, 600), pos=(400, 100)) html = wx.html.HtmlWindow(self) html.SetPage(self.text) panel = wx.Panel(self, -1) button = wx.Button(panel, wx.ID_OK, "Close") self.Bind(wx.EVT_BUTTON, self.OnCloseMe, button) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(html, 1, wx.EXPAND | wx.ALL, 5) sizer.Add(panel, 0, wx.ALIGN_CENTER | wx.ALL, 5) self.SetSizer(sizer) self.Layout() def OnCloseMe(self, event): self.Close(True) # ----------- end of class UsageFrame --------------- def OnUsage(self, event): usagewin = self.UsageFrame(self) usagewin.Show(True) def OnInfo(self, event): msg = "Program information for PlotFrame obtained from docstrings:" msg += "\n" + self.__doc__ + "\n" + self.create_main_panel.__doc__ msg += self.create_button_bar.__doc__ msg += self.init_plot.__doc__ msg += self.plot_data.__doc__ dlg = wx.lib.dialogs.ScrolledMessageDialog(self, msg, "PlotFrame Documentation") dlg.ShowModal() # --------------------------------------------------------------- # Define the functions executed on control button click # --------------------------------------------------------------- def OnRewind(self, event): self.get_data_params() self.init_plot() self.get_xyt_data() def OnReplot(self, event): self.plot_data() self.canvas.draw() def OnSstep(self, event): if self.data_loaded == False: # bring up a warning dialog msg = """ Data for plotting has not been loaded! Please enter the file to plot with File/Open, unless it was already specified, and then click on 'New Data' to load the data to play back, before clicking 'Play'. """ wx.MessageBox(msg, "Plot Warning", wx.OK | wx.ICON_ERROR, self) return self.t_max = min(self.t_max + self.dt, (self.Ntimes - 1) * self.dt) self.stmax.set_val(self.t_max) frame_num = int(self.t_max / self.dt) self.SetStatusText('time: ' + str(frame_num * self.dt)) self.im.set_data(self.ldata[frame_num]) self.axes.draw_artist(self.im) self.canvas.blit(self.axes.bbox) def OnStop(self, event): self.STOP = 'True' def OnForward(self, event): state = self.forward_toggle.GetValue() if state: self.reverse_play = False self.forward_toggle.SetLabel("Forward ") self.forward_toggle.SetForegroundColour('black') self.forward_toggle.SetBackgroundColour('yellow') else: self.reverse_play = True self.forward_toggle.SetLabel(" Back ") self.forward_toggle.SetForegroundColour('red') self.forward_toggle.SetBackgroundColour('green') def OnFast(self, event): state = self.fast_toggle.GetValue() if state: # print state self.fast_toggle.SetLabel(" Normal ") self.fast_toggle.SetForegroundColour('black') self.fast_toggle.SetBackgroundColour('yellow') self.step = 1 else: # print state self.fast_toggle.SetLabel(" Fast ") self.fast_toggle.SetForegroundColour('red') self.fast_toggle.SetBackgroundColour('green') self.step = 10
class VelocityPanel(wx.Panel): def _init_ctrls(self, prnt): # generated method, don't edit wx.Panel.__init__(self, id=wxID_RIGHTPANEL, name='VelocityPanel', parent=prnt, pos=wx.Point(8, 416), size=wx.Size(1000, 250), style=wx.TAB_TRAVERSAL) self.SetClientSize(wx.Size(1000, 250)) self.SetBackgroundColour(wx.Colour(0, 0, 255)) self.Bind(wx.EVT_PAINT, self.OnPolarPanelPaint) def __init__(self, parent, id, pos, size, style, name): self._init_ctrls(parent) self.Times = list() self.VelocitiesX = list() self.VelocitiesY = list() self.Velocities = list() self._initFigure() ##now finally create the actual plot self.x_axes = self.fig.add_subplot(131) ## the x velocity self.y_axes = self.fig.add_subplot(132) ## the y velocity self.m_axes = self.fig.add_subplot( 133) ## the magnitude of the velocity #self.axes = self.fig.add_axes((0.1,0.1,0.85,0.8)) ##left,bottom,width,height self.x_plot = self.x_axes.plot([0, 1], [0, 1], 'b', animated=True) self.y_plot = self.y_axes.plot([0, 1], [0, 1], 'b', animated=True) self.m_plot = self.m_axes.plot([0, 1], [0, 1], 'r', animated=True) self.x_axes.set_title('X', fontsize='10') self.y_axes.set_title('Y', fontsize='10') self.m_axes.set_title('M', fontsize='10') self.x_axes.set_ylabel('Velocity (cm/s)', fontsize='10') #self.axes.set_ylabel('x (cm)', fontsize='10') ##plot formatting self._formatAxes(self.x_axes) self._formatAxes(self.y_axes) self._formatAxes(self.m_axes) #self.axes.set_title('Velocity', fontsize='10') self.canvas.draw() self.canvas.gui_repaint() # save the clean slate background -- everything but the animated line # is drawn and saved in the pixel buffer background self.background = self.canvas.copy_from_bbox(self.fig.bbox) def _initFigure(self): ##Create a matplotlib figure/canvas in this panel ##the background colour will be the same as the panel ##the size will also be the same as the panel ##calculate size in inches pixels_width, pixels_height = self.GetSizeTuple() self.dpi = 96.0 inches_width = pixels_width / self.dpi inches_height = pixels_height / self.dpi ##calculate colour in RGB 0 to 1 colour = self.GetBackgroundColour() self.fig = Figure(figsize=(inches_width,inches_height), dpi = self.dpi\ ,facecolor=(colour.Red()/255.0, colour.Green()/255.0, colour.Blue()/255.0)\ ,edgecolor=(colour.Red()/255.0, colour.Green()/255.0, colour.Blue()/255.0)) self.canvas = FigureCanvasWxAgg(self, -1, self.fig) self.fig.subplots_adjust(left=0.05, right=0.95, wspace=0.08) ##now put everything in a sizer sizer = wx.BoxSizer(wx.VERTICAL) # This way of adding to sizer allows resizing sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) self.SetSizer(sizer) self.Fit() def _formatAxes(self, axes): """ """ ticks = numpy.arange(-25, 25 + 5, 5) labels = [str(tick) for tick in ticks] # velocity axes.set_yticks(ticks) axes.set_yticklabels(labels, fontsize=8) ticks = numpy.arange(0, 10 + 1.0, 1.0) # time labels = [str(tick) for tick in ticks] axes.set_xticks(ticks) axes.set_xticklabels(labels, fontsize=8) #if axes == self.m_axes: # self.axes.set_xlabel('time (s)', fontsize='10') #self.axes.set_ylabel(' (mm)', fontsize='10') def updateData(self, velx, vely, vel): """updateData. Updates the data that this panel is displaying. """ self.VelocitiesX.append(velx) self.VelocitiesY.append(vely) self.Velocities.append(vel) timenow = time.time() self.Times.append(timenow) if timenow - self.Times[0] > 10: del self.Times[0] del self.VelocitiesX[0] del self.VelocitiesY[0] del self.Velocities[0] self.x_plot[0].set_data( numpy.array(self.Times) - self.Times[0], self.VelocitiesX) self.y_plot[0].set_data( numpy.array(self.Times) - self.Times[0], self.VelocitiesY) self.m_plot[0].set_data( numpy.array(self.Times) - self.Times[0], self.Velocities) # restore the clean slate background self.canvas.restore_region(self.background) # just draw the animated artist self.fig.draw_artist(self.x_plot[0]) self.fig.draw_artist(self.y_plot[0]) self.fig.draw_artist(self.m_plot[0]) # just redraw the axes rectangle self.canvas.blit(self.fig.bbox) def OnPolarPanelPaint(self, event): pass
class MonitorFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, parent=None, id=-1, title=__app_name__, pos=(10, 10), size=(1200, 620)) self.LoadParam() self.BuildUI() self.InitUiParam() self.proc_name_value.SetFocus() self.t = wx.Timer(self, TIMER_ID) def LoadParam(self): self.settings = param.load_param('config.json') if 'xmin' not in self.settings: self.settings['xmin'] = 0 if 'xmax' not in self.settings: self.settings['xmax'] = self.settings['points'] def BuildUI(self): # ------- config box ------------ # process name self.proc_name_label = wx.StaticText(parent=self, label='Process Name: ', style=wx.ALIGN_CENTER) self.proc_name_value = wx.TextCtrl(parent=self, value='', style=wx.TE_PROCESS_ENTER) self.proc_name_box = wx.BoxSizer(wx.HORIZONTAL) self.proc_name_box.Add(self.proc_name_label, 1, wx.ALIGN_CENTER, 5, 0) self.proc_name_box.Add(self.proc_name_value, 2, wx.ALIGN_CENTER, 5, 0) # input response self.proc_msg = wx.StaticText(parent=self, label='', size=(800, 30), style=wx.ALIGN_LEFT | wx.ST_NO_AUTORESIZE) # add to config box self.configBox = wx.BoxSizer(wx.VERTICAL) self.configBox.Add(self.proc_name_box, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5, 0) self.configBox.Add(self.proc_msg, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5, 0) # ------- control box ------------ self.startBtn = wx.Button(parent=self, label="Start", size=(60, 60)) self.stopBtn = wx.Button(parent=self, label="Stop") self.showBtn = wx.Button(parent=self, label="Show") self.controlBox = wx.BoxSizer(wx.HORIZONTAL) self.controlBox.Add(self.startBtn, 1, wx.ALL | wx.EXPAND | wx.ALIGN_CENTER_VERTICAL, 5, 0) self.controlBox.Add(self.showBtn, 1, wx.ALL | wx.EXPAND | wx.ALIGN_CENTER_VERTICAL, 5, 0) self.controlBox.Add(self.stopBtn, 1, wx.ALL | wx.EXPAND | wx.ALIGN_CENTER_VERTICAL, 5, 0) self.startBtn.Enable() self.stopBtn.Disable() # ------- tool box(config, control) ------- self.toolbox = wx.BoxSizer(wx.HORIZONTAL) self.toolbox.AddSpacer(20) self.toolbox.Add(self.configBox, 5, wx.ALL | wx.ALIGN_CENTER, 5, 0) self.toolbox.Add(self.controlBox, 2, wx.ALL | wx.ALIGN_CENTER, 5, 0) # ------- track log box ------------------- self.track_log = wx.TextCtrl(parent=self, style=wx.TE_AUTO_SCROLL | wx.TE_MULTILINE) self.track_log.SetEditable(False) self.fig = self.InitPlotUI() self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) self.canvas.draw() self.bg = self.canvas.copy_from_bbox(self.ax.bbox) self.dispbox = wx.BoxSizer(wx.HORIZONTAL) self.dispbox.Add(self.track_log, 1, wx.ALL | wx.EXPAND, 5, 5) self.dispbox.Add(self.canvas, 0, wx.ALL | wx.EXPAND, 5, 5) # ------- main box(tool, tracklog) -------- self.mainbox = wx.BoxSizer(wx.VERTICAL) self.mainbox.Add(self.toolbox, 1, wx.NORMAL, 0, 0) self.mainbox.Add(self.dispbox, 0, wx.EXPAND, 5, 5) self.SetSizer(self.mainbox) self.CenterOnScreen() self.startBtn.Bind(wx.EVT_BUTTON, self.OnStartTrack) self.stopBtn.Bind(wx.EVT_BUTTON, self.OnStopTrack) self.proc_name_value.Bind(wx.EVT_TEXT, self.OnProcInputChanged) self.proc_name_value.Bind(wx.EVT_TEXT_ENTER, self.OnStartTrack) self.Bind(wx.EVT_ACTIVATE, self.OnWindowActivate) wx.EVT_TIMER(self, TIMER_ID, self.onTimer) def InitUiParam(self): self.proc_name_value.SetValue(self.settings['process_name']) self.proc_tracking = None self.is_track_running = False def OnStartTrack(self, event): if self.is_track_running: return proc_name = self.proc_name_value.GetValue().strip() if 0 == len(proc_name): msg = 'Please input a process name!' dlg = wx.MessageDialog(None, msg, "%s Error" % __app_name__, wx.ICON_ERROR) dlg.ShowModal() return None if self.proc_tracking is None: self.MatchProcName(proc_name) if self.proc_tracking is None: msg = 'No such process!\nGo on to track %s?' % proc_name dlg = wx.MessageDialog(None, msg, "%s Error" % __app_name__, wx.YES_NO | wx.ICON_QUESTION) if dlg.ShowModal() != wx.ID_YES: return None # transfer button status self.startBtn.Disable() self.showBtn.Disable() self.stopBtn.Enable() self.proc_name_value.Disable() # clear log self.track_log.SetValue('') wx.CallAfter(self.StartTrack, self.proc_tracking, self.proc_name_value.GetValue()) def update_log(self, disp_data): global _log_cache _log_cache.append(disp_data) if len(_log_cache) >= (1000.0 / self.settings['interval']): wx.CallAfter(self.track_log.AppendText, '%s | %.4f MB\n' % (timestamp(), avg(_log_cache))) _log_cache = [] def StartTrack(self, proc, proc_name): self.is_track_running = True self.t.Start(self.settings['interval']) def OnStopTrack(self, event): self.startBtn.Enable() self.showBtn.Enable() self.stopBtn.Disable() self.proc_name_value.Enable() # stop thread self.t.Stop() self.is_track_running = False def OnWindowActivate(self, event): if not self.is_track_running: self.MatchProcName(self.proc_name_value.GetValue().strip()) def OnProcInputChanged(self, event): self.MatchProcName(self.proc_name_value.GetValue().strip()) def MatchProcName(self, pname): self.proc_tracking = None if 0 == len(pname): self.proc_msg.SetLabel('Please input a process name') return None procs = monitor.get_procs(pname) if 0 == len(procs): self.proc_msg.SetLabel('Process not exists or AccessDenied') return None self.proc_tracking = procs[0] if len(procs) > 1: self.proc_msg.SetLabel('Warning! Multi Processes Match. use %s' % format_proc(self.proc_tracking)) else: self.proc_msg.SetLabel(format_proc(self.proc_tracking)) return self.proc_tracking def InitPlotUI(self): plot_points = self.settings['points'] fig = Figure(figsize=(9, 5), dpi=100) self.ax = fig.add_subplot(111) self.ax.set_ylim([self.settings['ymin'], self.settings['ymax']]) self.ax.set_xlim([self.settings['xmin'], self.settings['xmax']]) self.ax.set_autoscale_on(False) self.ax.set_xticks([]) self.ax.set_yticks( range(self.settings['ymin'], self.settings['ymax'] + 1, self.settings['ystep'])) self.ax.grid(True) self.mem_rss_data = [None] * plot_points self.l_mem_rss, = self.ax.plot(range(plot_points), self.mem_rss_data, label='Memory(RSS) %') # add the legend self.ax.legend(loc='upper center', ncol=4, prop=font_manager.FontProperties(size=10)) return fig def onTimer(self, evt): """callback function for timer events""" # restore the clean background, saved at the beginning self.canvas.restore_region(self.bg) # get new perf data if self.proc_tracking is None: proc_name = self.proc_name_value.GetValue().strip() self.proc_tracking = monitor.find_proc(proc_name) rss_mem = getSizeInMb(monitor.get_rss_mem(self.proc_tracking)) # update log wx.CallAfter(self.update_log, rss_mem) # plot self.mem_rss_data = self.mem_rss_data[1:] + [rss_mem] self.l_mem_rss.set_ydata(self.mem_rss_data) self.ax.draw_artist(self.l_mem_rss) self.canvas.blit(self.ax.bbox)
class VelocityPanel(wx.Panel): def _init_ctrls(self, prnt): # generated method, don't edit wx.Panel.__init__(self, id=wxID_RIGHTPANEL, name='VelocityPanel', parent=prnt, pos=wx.Point(8, 416), size=wx.Size(1000, 250), style=wx.TAB_TRAVERSAL) self.SetClientSize(wx.Size(1000, 250)) self.SetBackgroundColour(wx.Colour(0, 0, 255)) self.Bind(wx.EVT_PAINT, self.OnPolarPanelPaint) def __init__(self, parent, id, pos, size, style, name): self._init_ctrls(parent) self.Times = list() self.VelocitiesX = list() self.VelocitiesY = list() self.Velocities = list() self._initFigure() ##now finally create the actual plot self.x_axes = self.fig.add_subplot(131) ## the x velocity self.y_axes = self.fig.add_subplot(132) ## the y velocity self.m_axes = self.fig.add_subplot(133) ## the magnitude of the velocity #self.axes = self.fig.add_axes((0.1,0.1,0.85,0.8)) ##left,bottom,width,height self.x_plot = self.x_axes.plot([0,1],[0,1],'b', animated=True) self.y_plot = self.y_axes.plot([0,1],[0,1],'b', animated=True) self.m_plot = self.m_axes.plot([0,1],[0,1],'r', animated=True) self.x_axes.set_title('X', fontsize='10') self.y_axes.set_title('Y', fontsize='10') self.m_axes.set_title('M', fontsize='10') self.x_axes.set_ylabel('Velocity (cm/s)', fontsize='10') #self.axes.set_ylabel('x (cm)', fontsize='10') ##plot formatting self._formatAxes(self.x_axes) self._formatAxes(self.y_axes) self._formatAxes(self.m_axes) #self.axes.set_title('Velocity', fontsize='10') self.canvas.draw() self.canvas.gui_repaint() # save the clean slate background -- everything but the animated line # is drawn and saved in the pixel buffer background self.background = self.canvas.copy_from_bbox(self.fig.bbox) def _initFigure(self): ##Create a matplotlib figure/canvas in this panel ##the background colour will be the same as the panel ##the size will also be the same as the panel ##calculate size in inches pixels_width,pixels_height = self.GetSizeTuple() self.dpi = 96.0 inches_width = pixels_width/self.dpi inches_height = pixels_height/self.dpi ##calculate colour in RGB 0 to 1 colour = self.GetBackgroundColour() self.fig = Figure(figsize=(inches_width,inches_height), dpi = self.dpi\ ,facecolor=(colour.Red()/255.0, colour.Green()/255.0, colour.Blue()/255.0)\ ,edgecolor=(colour.Red()/255.0, colour.Green()/255.0, colour.Blue()/255.0)) self.canvas = FigureCanvasWxAgg(self, -1, self.fig) self.fig.subplots_adjust(left=0.05,right=0.95,wspace=0.08) ##now put everything in a sizer sizer = wx.BoxSizer(wx.VERTICAL) # This way of adding to sizer allows resizing sizer.Add(self.canvas, 1, wx.LEFT|wx.TOP|wx.GROW) self.SetSizer(sizer) self.Fit() def _formatAxes(self, axes): """ """ ticks = numpy.arange(-25, 25+5, 5) labels = [str(tick) for tick in ticks] # velocity axes.set_yticks(ticks) axes.set_yticklabels(labels, fontsize=8) ticks = numpy.arange(0, 10+1.0, 1.0) # time labels = [str(tick) for tick in ticks] axes.set_xticks(ticks) axes.set_xticklabels(labels,fontsize=8) #if axes == self.m_axes: # self.axes.set_xlabel('time (s)', fontsize='10') #self.axes.set_ylabel(' (mm)', fontsize='10') def updateData(self, velx, vely, vel): """updateData. Updates the data that this panel is displaying. """ self.VelocitiesX.append(velx) self.VelocitiesY.append(vely) self.Velocities.append(vel) timenow = time.time() self.Times.append(timenow) if timenow - self.Times[0] > 10: del self.Times[0] del self.VelocitiesX[0] del self.VelocitiesY[0] del self.Velocities[0] self.x_plot[0].set_data(numpy.array(self.Times) - self.Times[0], self.VelocitiesX) self.y_plot[0].set_data(numpy.array(self.Times) - self.Times[0], self.VelocitiesY) self.m_plot[0].set_data(numpy.array(self.Times) - self.Times[0], self.Velocities) # restore the clean slate background self.canvas.restore_region(self.background) # just draw the animated artist self.fig.draw_artist(self.x_plot[0]) self.fig.draw_artist(self.y_plot[0]) self.fig.draw_artist(self.m_plot[0]) # just redraw the axes rectangle self.canvas.blit(self.fig.bbox) def OnPolarPanelPaint(self, event): pass
class LeftGraphBottom(wx.Panel): def __init__(self, parent, statusbar): wx.Panel.__init__(self, parent) self.statusbar = statusbar """ An polygon editor. Key-bindings 't' toggle vertex markers on and off. When vertex markers are on, you can move them, delete them 'd' delete the vertex under point 'i' insert a vertex at point. You must be within epsilon of the line connecting two existing vertices """ self.fig = Figure((4.0, 3.0)) self.canvas = FigCanvas(self, -1, self.fig) self.ax = self.fig.add_subplot(111) """ subplots_adjust(bottom=0.14): permet d'ajuster la taille du canevas en prenant en compte la legende sinon la legende est rognee""" self.fig.subplots_adjust(bottom=0.20) self.ax.set_ylabel("DW", fontdict=font) self.ax.set_xlabel("Depth ($\AA$)", fontdict=font) self.toolbar = NavigationToolbar(self.canvas) self.toolbar.Hide() self.fig.patch.set_facecolor(colorBackgroundGraph) self._ind = None # the active vert self.poly = [] self.line = [] self.showverts = True self.epsilon = 5 # max pixel distance to count as a vertex hit self.new_coord = {'indice': 0, 'x': 0, 'y': 0} self.modelpv = False xs = [-1] ys = [-1] poly = Polygon(list(zip(xs, ys)), ls='solid', fill=False, closed=False, animated=True) self.ax.set_xlim([0, 1]) self.ax.set_ylim([0, 1]) self.c_dw = "" self.l_dw = "" self.canvas.mpl_connect('draw_event', self.draw_callback) self.canvas.mpl_connect('button_press_event', self.button_press_callback) self.canvas.mpl_connect('button_release_event', self.button_release_callback) self.canvas.mpl_connect('motion_notify_event', self.motion_notify_callback) self.canvas.mpl_connect('scroll_event', self.scroll_callback) self.canvas.mpl_connect('motion_notify_event', self.on_update_coordinate) mastersizer = wx.BoxSizer(wx.VERTICAL) mastersizer.Add(self.canvas, 1, wx.ALL | wx.EXPAND) mastersizer.Add(self.toolbar, 0, wx.ALL) pub.subscribe(self.draw_c, pubsub_draw_graph) pub.subscribe(self.OnDrawGraph, pubsub_Draw_DW) pub.subscribe(self.scale_manual, pubsub_Update_Scale_DW) pub.subscribe(self.on_color, pubsub_Graph_change_color_style) self.on_color() self.draw_c(poly, xs, ys) self.SetSizer(mastersizer) self.Fit() def on_color(self): a = P4Rm() self.c_dw = a.DefaultDict['c_dw'] self.l_dw = a.DefaultDict['l_dw'] self.c_bkg = a.DefaultDict['c_graph_background'] def OnDrawGraph(self, b=None): a = P4Rm() self.modelpv = a.modelPv self.ax.clear() if a.AllDataDict['damaged_depth'] == 0: self.ax.text(0.5, 0.5, "No Damage", size=30, rotation=0., ha="center", va="center", bbox=dict( boxstyle="round", ec='red', fc=self.c_dw, )) x_dwp = [-1] y_dwp = [-1] xs = [-1] ys = [-1] self.ax.set_xticklabels([]) self.ax.set_yticklabels([]) self.ax.set_xlim([0, 1]) self.ax.set_ylim([0, 1]) else: if b != 2: x_dwp = a.ParamDict['x_dwp'] y_dwp = a.ParamDict['DW_shifted'] xs = deepcopy(a.ParamDict['depth']) ys = deepcopy(a.ParamDict['DW_i']) P4Rm.DragDrop_DW_x = x_dwp P4Rm.DragDrop_DW_y = y_dwp ymin = min(ys) - min(ys) * 10 / 100 ymax = max(ys) + max(ys) * 10 / 100 self.ax.set_ylim([ymin, ymax]) if a.ParamDict['x_dwp'] != "": self.ax.set_xlim( [a.ParamDict['depth'][-1], a.ParamDict['depth'][0]]) elif b == 2: x_dwp = [-1] y_dwp = [-1] xs = [-1] ys = [-1] self.ax.set_xlim([0, 1]) self.ax.set_ylim([0, 1]) poly = Polygon(list(zip(x_dwp, y_dwp)), lw=0, ls='solid', color=self.c_dw, fill=False, closed=False, animated=True) if self.modelpv is True: P4Rm.ParamDict['dwp_pv_backup'] = a.ParamDict['dwp'] self.draw_c(poly, xs, ys) def draw_c(self, data, x, y): self.ax.plot(x, y, color=self.c_dw, lw=2., ls='solid') self.ax.set_ylabel("DW", fontdict=font) self.ax.set_xlabel("Depth ($\AA$)", fontdict=font) if LooseVersion(matplotlib_vers) < LooseVersion("2.0.0"): self.ax.set_axis_bgcolor(self.c_bkg) else: self.ax.set_facecolor(self.c_bkg) self.poly = data xs, ys = zip(*self.poly.xy) self.line = Line2D(xs, ys, lw=0, ls='solid', color=self.c_dw, marker='.', ms=32, markerfacecolor=self.c_dw, markeredgecolor='k', mew=1.0) self.ax.add_line(self.line) self.ax.add_patch(self.poly) self.canvas.SetCursor(Cursor(wx.CURSOR_HAND)) self.canvas.draw() def draw_callback(self, event): self.background = self.canvas.copy_from_bbox(self.ax.bbox) self.ax.draw_artist(self.poly) self.ax.draw_artist(self.line) self.canvas.blit(self.ax.bbox) def get_ind_under_point(self, event): 'get the index of the vertex under point if within epsilon tolerance' # display coords xy = np.asarray(self.poly.xy) xyt = self.poly.get_transform().transform(xy) xt, yt = xyt[:, 0], xyt[:, 1] d = np.sqrt((xt - event.x)**2 + (yt - event.y)**2) indseq = np.nonzero(np.equal(d, np.amin(d)))[0] ind = indseq[0] if d[ind] >= self.epsilon: ind = None return ind def button_press_callback(self, event): 'whenever a mouse button is pressed' a = P4Rm() val = a.xrd_graph_loaded if self.canvas.HasCapture(): self.canvas.ReleaseMouse() if not self.showverts: return if event.inaxes is None: return if event.button != 1: return if val == 1: self._ind = self.get_ind_under_point(event) self.new_coord['indice'] = self._ind def button_release_callback(self, event): 'whenever a mouse button is released' a = P4Rm() val = a.xrd_graph_loaded if self.canvas.HasCapture(): self.canvas.ReleaseMouse() else: if not self.showverts: return if event.button != 1: return if self.new_coord['indice'] is not None and val == 1: a = P4Rm() temp_1 = self.new_coord['y'] temp_2 = self.new_coord['x'] P4Rm.DragDrop_DW_y[self.new_coord['indice']] = temp_1 P4Rm.DragDrop_DW_x[self.new_coord['indice']] = temp_2 if a.AllDataDict['model'] == 0: temp = self.new_coord['y'] P4Rm.DragDrop_DW_y[self.new_coord['indice']] = temp temp = [ dw * scale for dw, scale in zip( a.DragDrop_DW_y, a.ParamDict['scale_dw']) ] temp = [float(format(value, '.8f')) for value in temp] temp2 = np.concatenate([temp, [a.ParamDict['dw_out']]]) P4Rm.ParamDict['dwp'] = deepcopy(temp2) P4Rm.ParamDictbackup['dwp'] = deepcopy(temp2) elif a.AllDataDict['model'] == 1: temp = self.new_coord['y'] P4Rm.DragDrop_DW_y[self.new_coord['indice']] = temp temp = [ dw * scale for dw, scale in zip( a.DragDrop_DW_y, a.ParamDict['scale_dw']) ] temp = [float(format(value, '.8f')) for value in temp] temp2 = np.concatenate([[a.ParamDict['dw_out'][0]], temp, [a.ParamDict['dw_out'][1]]]) P4Rm.ParamDict['dwp'] = deepcopy(temp2) P4Rm.ParamDictbackup['dwp'] = deepcopy(temp2) elif a.AllDataDict['model'] == 2: t_temp = a.ParamDict['depth'] + a.ParamDict['z'] t = t_temp[0] dwp_temp = range(7) dwp_temp[0] = a.DragDrop_DW_y[0] dwp_temp[1] = 1 - a.DragDrop_DW_x[0] / t dwp_temp[2] = 2 * (-1 + a.ParamDict['dwp'][1] + a.DragDrop_DW_x[1] / t) dwp_temp[3] = 2 * (1 - a.ParamDict['dwp'][1] - 1 * a.DragDrop_DW_x[2] / t) dwp_temp[4] = a.ParamDict['dwp'][4] dwp_temp[5] = a.ParamDict['dwp'][5] dwp_temp[6] = a.DragDrop_DW_y[3] P4Rm.ParamDict['dwp'] = deepcopy(dwp_temp) P4Rm.ParamDictbackup['dwp'] = deepcopy(dwp_temp) P4Rm.ParamDict['dwp_pv'] = deepcopy(dwp_temp) pub.sendMessage(pubsub_Update_Fit_Live) self._ind = None def scroll_callback(self, event): if not event.inaxes: return a = P4Rm() if event.key == 'u' and event.button == 'up': temp = a.ParamDict['DW_multiplication'] + 0.01 P4Rm.ParamDict['DW_multiplication'] = temp elif event.key == 'u' and event.button == 'down': temp = a.ParamDict['DW_multiplication'] - 0.01 P4Rm.ParamDict['DW_multiplication'] = temp P4Rm.ParamDict['dwp'] = multiply(a.ParamDictbackup['dwp'], a.ParamDict['DW_multiplication']) pub.sendMessage(pubsub_Re_Read_field_paramters_panel, event=event) def scale_manual(self, event, val=None): a = P4Rm() if val is not None: P4Rm.ParamDict['DW_multiplication'] = val P4Rm.ParamDict['dwp'] = multiply(a.ParamDict['dwp'], a.ParamDict['DW_multiplication']) pub.sendMessage(pubsub_Re_Read_field_paramters_panel, event=event) def motion_notify_callback(self, event): 'on mouse movement' a = P4Rm() if a.AllDataDict['damaged_depth'] == 0: return if not self.showverts: return if self._ind is None: return if event.inaxes is None: return if event.button != 1: return if self.modelpv is True: if self._ind == 0: y = event.ydata x = event.xdata elif self._ind == 1 or self._ind == 2: y = a.DragDrop_DW_y[self.new_coord['indice']] x = event.xdata else: x = a.DragDrop_DW_x[self.new_coord['indice']] y = event.ydata else: y = event.ydata x = a.DragDrop_DW_x[self.new_coord['indice']] self.new_coord['x'] = x self.new_coord['y'] = y self.poly.xy[self._ind] = x, y self.line.set_data(zip(*self.poly.xy)) self.canvas.restore_region(self.background) self.ax.draw_artist(self.poly) self.ax.draw_artist(self.line) self.canvas.blit(self.ax.bbox) def on_update_coordinate(self, event): if event.inaxes is None: self.statusbar.SetStatusText(u"", 1) self.statusbar.SetStatusText(u"", 2) else: a = P4Rm() if not a.AllDataDict['damaged_depth'] == 0: x, y = event.xdata, event.ydata xfloat = round(float(x), 2) yfloat = round(float(y), 2) self.statusbar.SetStatusText(u"x = " + str(xfloat), 1) self.statusbar.SetStatusText(u"y = " + str(yfloat), 2) xy = np.asarray(self.poly.xy) xyt = self.poly.get_transform().transform(xy) xt, yt = xyt[:, 0], xyt[:, 1] d = np.sqrt((xt - event.x)**2 + (yt - event.y)**2) indseq = np.nonzero(np.equal(d, np.amin(d)))[0] ind = indseq[0] if d[ind] >= self.epsilon: self.canvas.SetCursor(Cursor(wx.CURSOR_ARROW)) elif d[ind] <= self.epsilon: self.canvas.SetCursor(Cursor(wx.CURSOR_HAND))
class MyPlot(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent, -1) self.fig = None self.canvas = None self.ax = None self.background = None self.lines = [] self._doRePlot = True self.foo = 1 self.t = time.time() self.blit_time = 0 self.y = numpy.cos(numpy.arange(0.0, 1.0, 0.1)) self.ylim = None self.autolim = None self.span = 500 self.begin = 0 self.channels = [] self._SetSize() self.Bind(wx.EVT_IDLE, self._onIdle) self.Bind(wx.EVT_SIZE, self._onSize) self._resizeFlag = True sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.canvas, 1, wx.GROW) self.SetSizer(sizer) self.canvas.Show() def addChannel(self, channel): self.channels.append(channel) def setTimespan(self, span): self.span = span def setYlim(self, ymin, ymax): self.ylim = [ymin, ymax] def _resizeCreateContent(self): '''Resize graph according to user input and initialize plots''' self.lines = [] for c in self.channels: data = c.getNext() line, = self.ax.plot(data[0], data[1], animated=True) self.lines.append(line) gca = self.fig.gca() #TODO: add an auto mode here if self.ylim: gca.set_ylim(self.ylim) else: if self.autolim: diff = self.autolim[1] - self.autolim[0] gca.set_ylim([ self.autolim[0] - 0.1 * diff, self.autolim[1] + 0.1 * diff ]) else: gca.set_ylim([-1, 1]) gca.set_xlim([self.begin, (self.begin + self.span)]) self.ax.grid() #self.fig.clear() self.canvas.draw() self.background = None print 'content' self._doRePlot = False def _createGraphics(self): """Reallocate new figure and take care of panel resizing issues""" self.fig = Figure() self.canvas = FigureCanvas(self, -1, self.fig) self.ax = self.fig.add_subplot(111) self.ax._cachedRenderer = self.canvas.get_renderer() def _onSize(self, evt): self._resizeFlag = True def _onIdle(self, event): event.RequestMore(True) if self._resizeFlag: self._resizeFlag = False self._SetSize() self.draw_plot() #if self.foo > 2000: #u=time.time() #print self.foo/(u-self.t), self.blit_time/(u-self.t) #exit(0) def _SetSize(self, pixels=None): if not pixels: pixels = self.GetClientSize() self._createGraphics() self.canvas.SetSize(pixels) self.fig.set_size_inches(pixels[0] / self.fig.get_dpi(), pixels[1] / self.fig.get_dpi(), forward=True) self._doRePlot = True def draw_plot(self): if self._doRePlot: self._resizeCreateContent() if self.background is None: self.background = self.canvas.copy_from_bbox(self.ax.bbox) self.foo += 1 #self.y = numpy.cos(numpy.arange(0.0,1.0,0.1)+self.foo*0.1) # Optimization on the blitting: we compute the box where the changes happen changes_box = None for i in range(len(self.lines)): data = self.channels[i].getNext() if len(data[1]) > 0: if self.autolim: print self.autolim[0], data[1], self.autolim[1] self.autolim = [ min(self.autolim[0], min(data[1])), \ max(self.autolim[1], max(data[1])) ] else: self.autolim = [min(data[1]), min(data[1])] if changes_box is None: changes_box = Bbox.unit() print '>>>>>>>>' print data[0], data[1] changes_box.update_from_data(numpy.array(data[0]), \ numpy.array(data[1]), ignore=changes_box.is_unit()) if not self._doRePlot and len(data[0]) > 0: end = data[0][-1] if end > self.begin + self.span: self.begin += self.span self._doRePlot = True print 'do replot' self.lines[i].set_data(data[0], data[1]) else: self.lines[i].set_data([], []) if not changes_box: return #self.canvas.restore_region(self.background) for line in self.lines: self.ax.draw_artist(line) #print line.get_transform() tr = line.get_transform() changes_box_inframe = changes_box.transformed(tr) box_padding = 5 (x, y, l, w) = changes_box_inframe.bounds changes_box_inframe = Bbox.from_bounds(x-box_padding, \ y-box_padding, l+2*box_padding, w+2*box_padding) #print t0 = time.time() self.canvas.blit(None) #self.canvas.blit(changes_box_inframe) self.blit_time += time.time() - t0