import tkinter as tk from tkinter import * from tkinter import ttk from threading import Thread import time # For sleep, clock, time and perf_counter from datetime import datetime, timedelta import numpy as np import matplotlib.pyplot as plt from matplotlib.figure import Figure from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk) import ue9qol class SinGen: def __init__(self, ampl=1, offset=0, phase=0, sweeper=None): self.ampl = ampl self.phase = phase self.sweeper = sweeper self.offset = offset def getValue(self, sweeper=None): if sweeper is None and self.sweeper is None: print("Error: generator needs sweeper") return 0 if sweeper is None: sweeper = self.sweeper return self.ampl * np.sin( 2*np.pi*sweeper.getRelPos()) + self.offset class RampGen: # monotonically goes from start to stop, at final point move to start def __init__(self, start=0, stop=0, sweeper=None): self.start = start self.stop = stop self.sweeper = sweeper def getValue(self, sweeper=None): if sweeper is None and self.sweeper is None: print("Error: generator needs sweeper") return 0 if sweeper is None: sweeper = self.sweeper return self.start + sweeper.getRelPos()*(self.stop - self.start) class TriangleGen: # monotonically goes from start to stop, once reaches stop goes back to start def __init__(self, start=0, stop=0, sweeper=None): self.start = start self.stop = stop self.sweeper = sweeper def getValue(self, sweeper=None): if sweeper is None and self.sweeper is None: print("Error: generator needs sweeper") return 0 if sweeper is None: sweeper = self.sweeper if sweeper.getRelPos() < 0.5: return self.start + 2*sweeper.getRelPos()*(self.stop - self.start) return self.start + 2*(1-sweeper.getRelPos())*(self.stop - self.start) class Sweeper: def __init__(self, widget, Npoints, SweepTime, onTicCallbacks=[]): # walk from start to stop with Npoints # cnt = 1 corresponds to start # cnt = Npoints corresponds to stop # variables like relVar are relative to the start of the period self.cnt = 0 # onTic will increase it right away self.widget = widget self.Npoints = Npoints self.start = 1 self.stop = self.Npoints self.SweepTime = SweepTime self.onTicCallbacks = onTicCallbacks self.isOn = False self.isRestart = True self.isTicRunning = False self.span = self.stop - self.start self.center = (self.stop + self.start)/2 self.dPos = self.span/(self.Npoints-1) self.dT = self.SweepTime/(self.Npoints-1) self.dTmS = round(self.dT*1000) # dT in milliseconds def reset(self): self.cnt = 0 # onTic will increase it right away self.isRestart = False self.startTime = datetime.now() def onTic(self): start = datetime.now() deadline = start + timedelta(milliseconds=self.dTmS) if not self.isOn: self.isTicRunning = False return self.isTicRunning = True if self.isRestart: self.reset() self.isRestart = False self.incr() for cb in self.onTicCallbacks: cb(self) stop = datetime.now() self.isTicRunning = False if stop > deadline: runTime = (stop-start).seconds + float((stop-start).microseconds)/1000000 print("Overrun: Callbacks took %s seconds instead of %s" % (runTime, self.dTmS/1000) ) self.widget.after(0, self.onTic) idleTime_mS = round((deadline-stop).seconds * 1000 + (deadline-stop).microseconds/1000) # print("Will idle for %s" % (idleTime_mS) ) self.widget.after(idleTime_mS, self.onTic) def cmdRestart(self): self.cnt = 0 if self.isOn: self.isRestart = True return self.reset() self.isOn = True self.onTic() def cmdStart(self): self.isOn = True self.onTic() def cmdStop(self): self.isOn = False def incr(self): self.cnt += 1 self.updPos() def updPos(self): self.relCnt = 1 + ((self.cnt-1) % self.Npoints) self.pos = self.start + self.dPos * (self.relCnt - 1) self.relPos = (self.pos-self.start)/self.span def getCnt(self): return self.cnt def getRelCnt(self): return self.relCnt def getPos(self): return self.pos def getRelPos(self): return self.relPos class Experiment: def __init__(self, root): self.root = root self.tic = 0 self.clearData() self.guiSetup(root) self.guiSweeper = Sweeper(self.root, Npoints=2, SweepTime=1, onTicCallbacks=[self.updatePlot]) self.guiSweeper.cmdStart() self.hardware = {} self.hardwareSetup() self.sweeper = Sweeper(self.root, Npoints=100, SweepTime=1, onTicCallbacks=[self.onTic]) # self.funcGen = SinGen(2, 2, sweeper = self.sweeper) # self.funcGen = RampGen(0, 5, sweeper = self.sweeper) self.funcGen = TriangleGen(0, 5, sweeper = self.sweeper) def hardwareSetup(self): self.hardware['LabJack'] = ue9qol.UE9qol() def guiSetup(self, root): self.cntrlPannel=ttk.LabelFrame(root, text='controls') self.cntrlPannel.pack() self.bAutoZoom=Button(self.cntrlPannel,text="AutoZoom",command=self.autoZoom,font=('Arial','24')) self.bAutoZoom.pack(side='left') self.bRestart=Button(self.cntrlPannel,text="RESTART",command=self.restart,font=('Arial','24')) self.bRestart.pack(side='left') self.bStart=Button(self.cntrlPannel,text="START",command=self.start,font=('Arial','24')) self.bStart.pack(side='left') self.bStop=Button(self.cntrlPannel,text="STOP",command=self.stop,font=('Arial','24')) self.bStop.pack(side='left') self.bExit=Button(self.cntrlPannel,text="EXIT",command=exit,font=('Arial','24')) self.bExit.pack(side='left') self.dataDisplay=ttk.LabelFrame(root, text='data') self.dataDisplay.pack() self.fig=plt.figure(figsize=[32, 24]) self.ax = self.fig.add_subplot(1,1,1) # self.ax.set_xlim([0,20]) # self.ax.set_ylim([0,20]) # self.ax.plot([i for i in range(10)],[i for i in range(10)]) self.line, = self.ax.plot(self.data['tic'], self.data['ch1'], '.') self.canvas = FigureCanvasTkAgg(self.fig, master = self.dataDisplay) self.canvas.draw() # placing the canvas on the Tkinter window # self.canvas.get_tk_widget().pack() # creating the Matplotlib toolbar self.toolbar = NavigationToolbar2Tk(self.canvas, self.dataDisplay) self.toolbar.update() # placing the toolbar on the Tkinter window self.canvas.get_tk_widget().pack() def clearData(self): self.data = {} self.data['tic'] = [] self.data['ch1'] = [] def stop(self): self.sweeper.cmdStop() def start(self): self.sweeper.cmdStart() def restart(self): self.clearData() self.sweeper.cmdRestart() def onTic(self,swp=None): start = datetime.now() if swp is None: swp = self.sweeper # self.root.after(1000, self.hello ) # self.sweeper.incr() # tic = self.sweeper.getCnt() x = swp.getRelPos() y = self.funcGen.getValue(swp) # y = self.sweeper.getPos() # self.hardware['LabJack'].setOutputCh(0, x/2) self.hardware['LabJack'].setOutputCh(0, y) self.data['tic'].append(x) y= self.hardware['LabJack'].getInputCh(1) # self.data['ch1'].append( self.hardware['LabJack'].getInputCh(1) ) self.data['ch1'].append( y ) stop = datetime.now() runTime = (stop-start).seconds + float((stop-start).microseconds)/1000000 # print("onTic DAQ took %s seconds." % (runTime) ) def autoZoom(self): self.ax.cla() self.line, = self.ax.plot(self.data['tic'], self.data['ch1'], '.') self.canvas.draw() def updatePlot(self,swp=None): start = datetime.now() # self.ax.cla() # self.line, = self.ax.plot(self.data['tic'], self.data['ch1'], '.') # t = Thread(target=self.canvas.draw) # self.line.set_data([.1, .2, .3], [.1, .2, .3]) self.line.set_data(self.data['tic'], self.data['ch1']) self.ax.draw_artist(self.line) # self.canvas.update() # self.canvas.draw() self.fig.canvas.flush_events() stop = datetime.now() runTime = (stop-start).seconds + float((stop-start).microseconds)/1000000 print("Replot took %s seconds to plot %s points." % (runTime, len(self.data['ch1'])) ) if __name__ == '__main__': root=Tk() experiment=Experiment(root) root.mainloop()