12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028 |
- usefbs=0
- if usefbs:
- from fbs_runtime.application_context.PyQt5 import ApplicationContext
- from PyQt5.QtCore import QRunnable,pyqtSlot,QThreadPool,QMetaObject,Qt,Q_ARG,QTimer,QTime,QProcess,QItemSelection,QItemSelectionModel,QSignalBlocker,QRectF,pyqtSignal
- from PyQt5.QtWidgets import (QMainWindow, QApplication, QWidget, QPushButton, QStackedLayout,QItemDelegate,
- QSlider,
- QHBoxLayout, QVBoxLayout, QGridLayout, QApplication, QAction,QAbstractItemView,
- QTabWidget,QLineEdit,QLabel,QComboBox,QDialogButtonBox,QPlainTextEdit,QProgressBar,
- QSizePolicy,QMessageBox,QTableWidget,QTableWidgetItem,QTextEdit,QHeaderView,QMenu)
- from PyQt5.QtGui import QPalette,QColor,QTextCursor,QPainter,QFont
- import csv
- import numpy as np
- import traceback
- import sys,os,time,struct,math,itertools,json
- import pyqtgraph as pg
- from PC410Controller import PC410Controller
- """
- class PlotDataset(object):
- class PlotDataItem(GraphicsObject):
- """
- class QCycleThread(QRunnable):
- # Custom threader for running cycle() in pc410controller in a thread
- def __init__(self, name, pc410controller):
- QRunnable.__init__(self)
- self.name=name
- self.pc410controller=pc410controller
- def run(self):
- try:
- self.pc410controller.running=1
- print("Starting ct " + self.name)
- self.pc410controller.cycle(self.pc410controller.tstart0)
- print("Exiting ct " + self.name)
- except:
- print(traceback.format_exc())
- finally:
- self.pc410controller.running=0
- def start(self):
- QThreadPool.globalInstance().setObjectName(self.name)
- QThreadPool.globalInstance().start(self)
- def join(self):
- pass
- class QRunnableThread(QRunnable):
- def __init__(self, name, parent):
- QRunnable.__init__(self)
- self.name=name
- self.parent=parent
- def run(self):
- try:
- self.parent.running=1
- print("Starting ct " + self.name)
- self.parent.cycle(self.parent.tstart0)
- print("Exiting rt " + self.name)
- except:
- print(traceback.format_exc())
- finally:
- self.parent.running=0
- def start(self):
- QThreadPool.globalInstance().setObjectName(self.name)
- QThreadPool.globalInstance().start(self)
- def join(self):
- pass
-
- class ProcessRunnable(QRunnable):
- def __init__(self, target, args=None,start=True):
- QRunnable.__init__(self)
- self.name="ProcessRunnable"
- self.t = target
- if args==None: args=()
- self.args = args
- if start: self.start()
- def run(self):
- try:
- self.t(*self.args)
- except:
- print(traceback.format_exc())
- finally:
- print("exiting",self.t)
-
- def start(self):
- QThreadPool.globalInstance().setObjectName(self.name)
- QThreadPool.globalInstance().start(self)
- class TableView(QTableWidget):
- def __init__(self, headers, data, *args):
- QTableWidget.__init__(self, *args)
- self.data = data
- self.headers = headers
- self.setData()
- self.resizeColumnsToContents()
- self.resizeRowsToContents()
- self.cbSelectionChanged=None
- self.cbCellChanged=None
- self.cellChanged.connect(self.sCellChanged)
- def sCellChanged(self,row,column):
- if self.state()==QAbstractItemView.EditingState:
- txt=self.item(row,column).text()
- if self.cbCellChanged:
- self.cbCellChanged(row,column,txt)
- def selectionChanged(self,selected, deselected):
- super().selectionChanged(selected,deselected)
- indexes=selected.indexes()
- dindexes=deselected.indexes()
- rows=set([item.row() for item in indexes])
- drows=set([item.row() for item in dindexes])
- if rows:
- row0=min(rows)
- row1=max(rows)
- if self.cbSelectionChanged:
- self.cbSelectionChanged(row0,row1)
- def setData(self):
- horHeaders = []
- for n, key in enumerate(self.headers):
- horHeaders.append(key)
- for m, item in enumerate(self.data[key]):
- newitem = QTableWidgetItem(str(item))
- self.setItem(m, n, newitem)
- self.setHorizontalHeaderLabels(horHeaders)
- class ValueSelectDelegate(QItemDelegate):
- def __init__(self, parent=None,app=None):
- QItemDelegate.__init__(self, parent)
- self.app=app
- def createEditor(self, parent, option, index):
- model_value = index.model().data(index, Qt.DisplayRole)
- editor = QComboBox(parent)
- lines=[ x for x in model_value.split("\n") if x]
- editor.addItems(lines)
- return editor
- def setEditorData(self, editor, index):
- model_value = index.model().data(index, Qt.EditRole)
- # must get value from actual data and select value matching in list
- current_index = editor.findText(model_value)
- if current_index > 0:
- editor.setCurrentIndex(current_index)
- def setModelData(self, editor, model, index):
- editor_value = editor.currentText()
- parts=editor_value.split("=")
- if len(parts)>1:
- value=parts[0].strip()
- self.app.realTableCellChanged(index.row(),index.column(),value)
- #print("setModelData",index.row(),index.column(),editor_value,model)
- # model.setData(index, editor_value, QtCore.Qt.EditRole)
- class SimplifiedPlotItemMovement(pg.PlotItem):
- pointmoved = pyqtSignal([int,float,float],name="pointMoved")
- def __init__(self):
- pg.PlotItem.__init__(self)
- # Initialize the class instance variables.
- self.dragPoint = None
- self.dragIndex = -1
- self.dragOffset = 0
- self.plot_item_control = None
- def mouseDragEvent(self, ev):
- # Check to make sure the button is the left mouse button. If not, ignore it.
- # Would have to change this if porting to Mac I assume.
- if ev.button() != Qt.MouseButton.LeftButton:
- ev.ignore()
- return
- if ev.isStart():
- # We are already one step into the drag.
- # Find the point(s) at the mouse cursor when the button was first
- # pressed:
- # pos = ev.buttonDownPos()
- pos = ev.buttonDownScenePos()
- # Switch position into local coords using viewbox
- local_pos = self.vb.mapSceneToView(pos)
- for item in self.dataItems:
- if hasattr(item,"scatter"):
- new_pts = item.scatter.pointsAt(local_pos)
- if len(new_pts) == 1:
- # Store the drag point and the index of the point for future reference.
- self.dragPoint = new_pts[0]
- self.dragIndex = item.scatter.points().tolist().index(new_pts[0])
- # Find the initial offset of the drag operation from the current point.
- # This value should allow for the initial mismatch from cursor to point to be accounted for.
- self.dragOffset = new_pts[0].pos() - local_pos
- # If we get here, accept the event to prevent the screen from panning during the drag.
- ev.accept()
- elif ev.isFinish():
- # The drag is finished. Reset the drag point and index.
- self.dragPoint = None
- self.dragIndex = -1
- return
- else:
- # If we get here, this isn't the start or end. Somewhere in the middle.
- if self.dragPoint is None:
- # We aren't dragging a point so ignore the event and allow the panning to continue
- ev.ignore()
- return
- else:
- # We are dragging a point. Find the local position of the event.
- local_pos = self.vb.mapSceneToView(ev.scenePos())
- # Update the point in the PlotDataItem using get/set data.
- # If we had more than one plotdataitem we would need to search/store which item
- # is has a point being moved. For this example we know it is the plot_item_control object.
- x,y = self.plot_item_control.getData()
- # Be sure to add in the initial drag offset to each coordinate to account for the initial mismatch.
- x[self.dragIndex] = local_pos.x() + self.dragOffset.x()
- y[self.dragIndex] = local_pos.y() + self.dragOffset.y()
- # Update the PlotDataItem (this will automatically update the graphics when a change is detected)
- #self.plot_item_control.setData(x, y)
-
- pos=local_pos.x(),local_pos.y()
- self.pointmoved.emit(self.dragIndex,*pos)
-
-
- class MainWindow(QMainWindow):
- def __init__(self,ctx,argv):
- super(MainWindow, self).__init__()
- self.indicatorHeight=32
- self.resultHeight=36
- self.buttonHeight=43
- self.cursorMoved=0
- self.selectedRow=0
- self.fontsize=12
- self.infoColumns=2
- self.clockHeight=70 # 100
- self.progressCompact=1
- self.buttonRows=1
- self.withActionLabel=0
-
- self.running=0
- self.starttime=time.time()
- self.lasttime=self.starttime
- self.errorMessage=""
-
- self.tfontsize=10
- self.tfont = QFont('Courier', self.tfontsize) #, QFont.Bold)
-
- self.mintemp=20.0
- self.e={}
- home=os.environ["HOME"]
- if not home: raise Exeption("HOME environment variable not set!")
- self.home=home
- user=os.environ["USER"]
- if not home: raise Exeption("USER environment variable not set!")
- self.user=user
- self.loadSettings()
-
- self.build()
- self.pc410controller=PC410Controller(QCycleThread,self.cmdlog,self.infocallback,self.statecallback,self.recordcallback)
- self.pc410controller.run()
- def loadSettings(self):
- path=os.path.join(os.environ["HOME"],".pc410Settings")
- self.settings={}
- try:
- self.settings=json.loads(open(path).read())
- except:
- print("exception reading and decoding",path)
- fieldrows=self.settings.get("fieldrows",[])
- if not fieldrows:
- fieldrows=[["Sn63Pb37", 1,85,70, 1,150,35, 1,185,50, -0.01],
- ["Sn965.5Ag3Cu0.5", 1,85,60, 1,140,45, 1,170,25, 1,220,50, -0.01],
- ]
- self.settings["fieldrows"]=fieldrows
-
-
- def saveSettings(self):
- rowPosition = self.table.rowCount()
- fieldrows=[]
- for i in range(0,rowPosition):
- name,program=self.getProgramParameters(i)
- values=list(itertools.chain(*program))
- fieldrows+=[[name]+values]
- self.settings["fieldrows"]=fieldrows
- path=os.path.join(os.environ["HOME"],".pc410Settings")
- try:
- open(path,"w").write(json.dumps(self.settings))
- except:
- print("exception writing settings",path)
- raise
-
-
-
-
-
-
- # PTN is which program
- # STEP is which step of program...
-
- def addButton(self,layout,name,fn,key=None,colour="white"):
- button = QPushButton(self)
- button.setText(name)
- hmargin=(self.buttonHeight-self.fontsize)//2-5
- button.setSizePolicy(
- QSizePolicy.Preferred,
- QSizePolicy.Expanding)
- button.setContentsMargins(20,hmargin,20,hmargin)
- #button.setStyleSheet("background-color: %s;"%colour)
- #button.setStyleSheet("padding: 20px; background-color: %s;"%colour)
- button.setStyleSheet("padding: %dpx; background-color: %s;"%(hmargin,colour))
- #button.setGeometry(200, 150, 100, 40)
- #button.setFont(QFont('Times', 15))
-
- if not key: key=name
- self.e[key]=(name,key,fn,button)
- layout.addWidget(button)
- button.clicked.connect(fn)
- def saveAction(self):
- self.saveSettings()
-
- def stopAction(self):
- self.pc410controller.cmdStop()
- self.running=0
- def runAction(self):
- self.pc410controller.cmdRun()
- self.running=1
- self.clearRunRecord()
- self.starttime=time.time()
- def runningToStopped(self):
- self.running=0
- def stoppedToRunning(self):
- self.running=1
- self.clearRunRecord()
- self.starttime=time.time()
-
- def readAction(self):
- program=self.pc410controller.readProgram()
- print(program)
- if program:
- # update current row in table
- smodel=self.table.selectionModel()
- sindexes=smodel.selectedIndexes()
- if sindexes:
- rowPosition=max(set([s.row() for s in sindexes]))
- else:
- rowPosition = self.table.rowCount()
- self.table.insertRow(rowPosition)
- print(rowPosition)
- name="read"
- self.setProgramParameters(name,program,rowPosition)
-
- def writeAction(self):
- smodel=self.table.selectionModel()
- sindexes=smodel.selectedIndexes()
- if sindexes:
- rowSet=set([s.row() for s in sindexes])
- if len(rowSet)!=1:
- # fail
- return
- rowPosition=min(rowSet)
- name,program=self.getProgramParameters(rowPosition)
- if program:
- result=self.pc410controller.writeProgram(program)
- if result!=0:
- print("### failed to write program!!!")
-
- def clearRunRecord(self):
- self.runrecord=([],[])
-
- def addRowAction(self):
- self.addRow(-1)
-
- def addRow(self,rowPosition=-1):
- if rowPosition==-1:
- smodel=self.table.selectionModel()
- sindexes=smodel.selectedIndexes()
- if sindexes:
- rowPosition=max(set([s.row() for s in sindexes]))+1
- else:
- rowPosition = self.table.rowCount()
- self.table.insertRow(rowPosition)
- def removeRow(self,rowPosition):
- if self.table.rowCount()<=1:
- self.addRow(-1)
- self.table.removeRow(rowPosition)
-
- #@pyqtSlot(int,float,float)
- def tableContextMenu(self,point):
- index = self.table.indexAt(point)
- if index.isValid() :
- # show context menu
- self.contextMenu = QMenu(self)
- taddrowabove = self.contextMenu.addAction("Insert row above")
- taddrowbelow = self.contextMenu.addAction("Insert row below")
- self.contextMenu.addSeparator()
- tdeleterow = self.contextMenu.addAction("Delete row")
-
- # I want to perform actions only for a single column(E.g: Context menu only for column 4 of my table
- # Need help here....???
- action = self.contextMenu.exec_(self.table.mapToGlobal(point))
- row0=index.row()
- if action == taddrowabove:
- self.addRow(row0)
- elif action == taddrowbelow:
- self.addRow(row0+1)
- elif action == tdeleterow:
- self.removeRow(row0)
- def getTimeOfRun(self):
- dt=self.lasttime-self.starttime
- def showTime(self):
- if self.running:
- self.lasttime=time.time()
- dt=self.lasttime-self.starttime
- h=int((dt/3600)%100)
- m=int((dt/60)%60)
- s=int(dt%60)
- label_time="%02d:%02d:%02d"%(h,m,s)
- #current_time = QTime.currentTime()
- #label_time = current_time.toString('hh:mm:ss')
- self.clocklabel.setText(label_time)
-
-
- def build(self):
- if 1:
- font = QFont('Arial', self.fontsize) #, QFont.Bold)
- self.setFont(font)
- toplayout = QHBoxLayout()
-
-
-
- layout = QVBoxLayout()
- toplayout.setContentsMargins(0,0,0,0)
- layout.setContentsMargins(0,0,0,0)
- toplayout.addLayout(layout)
- layoutside = QVBoxLayout()
- toplayout.addLayout(layoutside)
- self.addButton(layoutside,"Save",self.saveAction,"action-save",colour="green")
- self.addButton(layoutside,"Add row",self.addRowAction,"action-addrow",colour="cyan")
- self.addButton(layoutside,"Run",self.runAction,"action-run",colour="yellow")
- self.addButton(layoutside,"Stop",self.stopAction,"action-stop",colour="red")
- self.addButton(layoutside,"Read program",self.readAction,"action-readprogram",colour="blue")
- self.addButton(layoutside,"Write program",self.writeAction,"action-writeprogram",colour="green")
-
- l=QLabel("Temperature over time")
- layout.addWidget(l)
-
- self.plot_graph = pg.GraphicsLayoutWidget()
- #self.plot_graph = pg.PlotWidget()
- self.plot_graph.setBackground("w")
- self.lineplot=SimplifiedPlotItemMovement()
- self.plot_graph.addItem(self.lineplot)
- self.lineplot.pointMoved.connect(self.graphPointMoved)
- self.plottexts=[]
- if 1:
- self.scatter = pg.ScatterPlotItem(size=2, brush=pg.mkBrush(255, 0, 0, 0))
- #plot_item = self.plot_graph.addPlot()
- #scatter_item = pg.ScatterPlotItem()
- self.lineplot.addItem(self.scatter)
- #self.plot_graph.addItem(self.scatter)
- else:
- self.recordplot = pg.PlotCurveItem(size=2, pen=pg.mkPen(color='#ff0000', width=1))
- self.plot_graph.addItem(self.recordplot)
-
- self.clearRunRecord()
-
- self.lineplot.setTitle('<span style="color: blue; font-size: 20pt">Temperature vs Time</span>')
- self.lineplot.setLabel("left", "Temperature (°C)")
- self.lineplot.setLabel("bottom", "Time (seconds)")
- self.timer = QTimer(self)
- self.timer.timeout.connect(self.showTime)
- self.timer.start(1000)
- hsize=self.clockHeight-5*2
- font = QFont('Arial', hsize, QFont.Bold)
- self.clocklabel = QLabel()
- self.clocklabel.setAlignment(Qt.AlignCenter)
- self.clocklabel.setFont(font)
- self.clocklabel.setContentsMargins(5,5,5,5)
- layoutside.addWidget(self.clocklabel)
-
- info=QGridLayout()
- infoslots={}
- def addInfoSlot(infoslots,name,label=None):
- if not label: label=name
- row=len(infoslots)
- l=QLabel(label)
- e=QLabel()
- infoslots[name]=(name,l,e)
- info.addWidget(l,row,0)
- info.addWidget(e,row,1)
- def addInfoSlot2D(infoslots,name,label=None):
- if not label: label=name
- count=len(infoslots)
- row=count//2
- col=count%2
-
- l=QLabel(label)
- e=QLabel()
- infoslots[name]=(name,l,e)
- info.addWidget(l,row,col*2+0)
- info.addWidget(e,row,col*2+1)
-
- faddinfoslot=addInfoSlot
- if self.infoColumns==2: faddinfoslot=addInfoSlot2D
- for param in ["PV","OP","SP","SL","HA","LA","DA","XP","TI","TD","HB","LB","CH","CC","RG","HS","LS","BP","HO","SR","Lc","SW","XS","OS"]:
- # "HB",
- faddinfoslot(infoslots,param)
- layoutside.addLayout(info)
- self.infoslots=infoslots
-
-
- layout.addWidget(self.plot_graph)
- if 1:
- l=QLabel("Settings")
- layout.addWidget(l)
- if 0:
- hbuttons = QHBoxLayout()
- self.addButton(hbuttons,"Add row",self.addRow,"action-addrow",colour="cyan")
- layout.addLayout(hbuttons)
-
- """
- Lead Sn63Pb37
- step r L d
- 1 1 85 70
- 2 1 150 35
- 3 1 185 50
- 4 END Hb=230
-
- Lead-Free Sn965.5Ag3Cu0.5
- step r L d
- 1 1 85 60
- 2 1 140 45
- 3 1 170 25
- 3 1 220 50
- 5 END hb=230
- """
-
- fieldrows=[["Sn63Pb37", 1,85,70, 1,150,35, 1,185,50, -0.01],
- ["Sn965.5Ag3Cu0.5", 1,85,60, 1,140,45, 1,170,25, 1,220,50, -0.01],
- ]
- fieldrows=self.settings.get("fieldrows",fieldrows)
- headers=["Name"]
- for i in range(1,9):
- headers+=["r%d"%i,"l%d"%i,"t%d"%i]
- data={}
- for i in range(0,len(headers)):
- data[headers[i]]=[]
- for row in fieldrows:
- fieldrow={}
- for ki in range(0,len(headers)):
- k=headers[ki]
- if ki>=len(row): v=""
- else: v=row[ki]
- data[k]+=[v]
- #data[self.decoded]+=[0]
-
- table = TableView(headers,data,len(data[headers[0]]),len(headers))
- self.table=table
- table.setContentsMargins(0,0,0,0)
- table.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
- table.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
- table.setSelectionBehavior(QAbstractItemView.SelectRows);
- table.cbSelectionChanged=self.tableSelectionChanged
- table.cbCellChanged=self.tableSelectionChanged #CellChanged
- def tableClicked(*args):
- item=args[0]
- row=item.row()
-
- table.clicked.connect(tableClicked)
- # setting context menu policy on my table, "self.table"
- self.table.setContextMenuPolicy(Qt.CustomContextMenu)
- # setting context menu request by calling a function,"self.tableContextMenu"
- self.table.customContextMenuRequested.connect(self.tableContextMenu)
-
- table.show()
- table.resize(250, 150)
- layout.addWidget(self.table)
- if layout:
- widget = QWidget()
- widget.setLayout(toplayout)
- self.setCentralWidget(widget)
- @pyqtSlot(int,float,float)
- def graphPointMoved(self,index,x,y):
- if index!=0:
- row0=self.selectedRow
- time,temp=self.buildCoordinates(row0)
- """
- moving time of point 1,3,5 etc means changing rate and adding diff to dwell time
- moving time of point 2,4,6 etc means changing dwell time, and changing rate of the one after
-
- """
- step=(index-1)//2
- stepi=1+step*3
- rgi0=stepi+0
- lgi0=stepi+1
- tgi0=stepi+2
- if (index%2)==0:
- # 0,2,4 etc
- # change rate of next to compensate for time change of this
- # should cap but that time is constructed from rate and delta so max x would then be
- # time[index]+(temp[index+1]-temp[index])/rate
- # cap to minx and mintemp
- rate0=float(self.table.item(row0,rgi0).text())
- dy0=(y-temp[index-2])
- minx=time[index-2]
- if rate0: minx+=abs(dy0)/rate0
- x=max(minx,x)
- y=max(self.mintemp,y)
- if index+1<len(temp):
- rgi1=rgi0+3
- rg=float(self.table.item(row0,rgi1).text())
- # cap to maxx and maxy
- dx0=0
- oldrate=rg
- if oldrate: dx0=abs((temp[index+1]-temp[index]))/oldrate
- maxx=time[index]+dx0
- x=min(x,maxx)
- # dwell time must not be negative so y might not change more than rate can fit until next level?
- #maxy=temp[index-2]+(temp[index
- if rate0:
- maxy=temp[index-2]+(maxx-time[index-2])*rate0
- y=min(maxy,y)
- # change rate
- dy1=temp[index+1]-y
- dx1=time[index+1]-x
- newrate=0.0
- if dx1: newrate=abs(dy1/dx1)
- else: newrate=9999.0
- rg=newrate
- newitem = QTableWidgetItem(str(rg))
- self.table.setItem(row0,rgi1,newitem)
-
- # compute dtime and dtemp with capped x and y
- dtime=x-time[index]
- dtemp=y-temp[index]
-
- # moving time means change dwell time
- dy0=y-temp[index-1]
- dt0=dy0/rate0
- tg=float(self.table.item(row0,tgi0).text())
- tg+=dtime-dt0
- newitem = QTableWidgetItem(str(tg))
- self.table.setItem(row0,tgi0,newitem)
- # change level
- lg=float(self.table.item(row0,lgi0).text())
- lg+=dtemp
- newitem = QTableWidgetItem(str(lg))
- self.table.setItem(row0,lgi0,newitem)
- else:
- # 1,3,5 etc
- # change level of edited point and change rate of rise to compensate
- # cap x and y
- x=max(x,time[index-1])
- x=min(x,time[index+1])
- y=max(self.mintemp,y)
- dtime=x-time[index]
- dtemp=y-temp[index]
-
- dy1=y-temp[index-1]
- dx1=x-time[index-1]
- newrate=0.0
- if dx1: newrate=abs(dy1/dx1)
- #newrate=max(0.01,newrate)
- # set new rate
- rg=float(self.table.item(row0,rgi0).text())
- rg=newrate
- newitem = QTableWidgetItem(str(rg))
- self.table.setItem(row0,rgi0,newitem)
- # set new level
- lg=float(self.table.item(row0,lgi0).text())
- lg+=dtemp
- newitem = QTableWidgetItem(str(lg))
- self.table.setItem(row0,lgi0,newitem)
-
- # change dwell time to compensate for move of editied point and also change next rate to compensate
- tg=float(self.table.item(row0,tgi0).text())
- tg-=dtime
- newitem = QTableWidgetItem(str(tg))
- self.table.setItem(row0,tgi0,newitem)
-
- if index+2<len(temp):
- dy1=temp[index+2]-y
- dx1=time[index+2]-time[index+1]
- newrate=0.0
- if dx1: newrate=abs(dy1/dx1)
- else: newrate=9999.0
- # set new next rate?
- rgi1=rgi0+3
- rg=float(self.table.item(row0,rgi1).text())
- rg=newrate
- newitem = QTableWidgetItem(str(rg))
- self.table.setItem(row0,rgi1,newitem)
-
- time,temp=self.buildCoordinates(row0)
- newdata=np.array([time,temp])
- self.lineplot.plot_item_control.setData(time, temp)
- self.updateLinePlotTexts(time,temp)
-
- def setProgramParameters(self,name,program,row0):
- for g in range(1,9):
- rn="r%d"%g
- ln="l%d"%g
- tn="t%d"%g
- rgi=1+(g-1)*3+0
- lgi=1+(g-1)*3+1
- tgi=1+(g-1)*3+2
-
- rg,lg,tg=-0.01,0.0,0.0
- newitem = QTableWidgetItem(name)
- self.table.setItem(row0,0,newitem)
-
- if g-1<len(program):
- rg,lg,tg=program[g-1]
- newitem = QTableWidgetItem(str(rg))
- self.table.setItem(row0,rgi,newitem)
- newitem = QTableWidgetItem(str(lg))
- self.table.setItem(row0,lgi,newitem)
- newitem = QTableWidgetItem(str(tg))
- self.table.setItem(row0,tgi,newitem)
-
- def getProgramParameters(self,row0):
- # r1,L1,d1
- # r2,L2,d2
- # ...
- program=[]
- name="program"
- try:
- name=self.table.item(row0,0).text()
- except:
- raise
-
- for g in range(1,9):
- rn="r%d"%g
- ln="l%d"%g
- tn="t%d"%g
- rgi=1+(g-1)*3+0
- lgi=1+(g-1)*3+1
- tgi=1+(g-1)*3+2
- rg,lg,tg=0.0,0.0,0.0
-
- try:
- rg=float(self.table.item(row0,rgi).text())
- except: pass
- try:
- lg=float(self.table.item(row0,lgi).text())
- except: pass
- try:
- tg=float(self.table.item(row0,tgi).text())
- except: pass
-
- program+=[(rg,lg,tg)]
-
- # -0.01 as end of program
- # 0.0 is step ie skip slope and go straight to dwell
- if rg<0: break
- return name,program
-
- def buildCoordinates(self,row0):
- time=[]
- temp=[]
- t0=0.0
- templ=self.mintemp
- temp0=self.mintemp
- time+=[t0]
- temp+=[temp0]
- for g in range(1,9):
- rn="r%d"%g
- ln="l%d"%g
- tn="t%d"%g
- rgi=1+(g-1)*3+0
- lgi=1+(g-1)*3+1
- tgi=1+(g-1)*3+2
- rg,lg,tg=0.0,0.0,0.0
-
- try:
- rg=float(self.table.item(row0,rgi).text())
- except: pass
- try:
- lg=float(self.table.item(row0,lgi).text())
- except: pass
- try:
- tg=float(self.table.item(row0,tgi).text())
- except: pass
- if rg<0: break
-
- dtemp=abs(lg-templ)
- if rg==0: dttemp=0
- else: dttemp=abs(dtemp/rg)
- #print(repr((rgi,lgi,tgi,rg,lg,tg,t0,dtemp,dttemp)))
-
- t0+=dttemp
- time+=[t0]
- temp+=[lg]
-
- t0+=tg
- time+=[t0]
- temp+=[lg]
- templ=lg
- return time,temp
- def updateLinePlotTexts(self,time,temp):
- if 1:
- for t in self.plottexts:
- self.lineplot.removeItem(t)
- del t
- self.plottexts=[]
- else:
- self.lineplot.clear()
-
-
- pen = pg.mkPen(color=(0, 0, 0))
- if 1:
- if self.lineplot.plot_item_control==None:
- self.lineplot.plot_item_control = self.lineplot.plot(time,temp, symbolBrush=(255,0,0), symbolPen='w')
- else:
- self.lineplot.plot_item_control.setData(time, temp)
- for i in range(1,len(time)):
- t0,t1=time[i-1],time[i]
- k0,k1=temp[i-1],temp[i]
- s=""
- rotateAxis=None
- if k0==k1:
- # dwell time at tempature
- s="%4.1fs at %4.1fC"%(t1-t0,k0)
- pos=(t0+t1)/2,k0
- angle=0
- elif t0!=t1:
- s="%.1fC/s"%((k1-k0)/(t1-t0))
- pos=(t0+t1)/2,(k0+k1)/2
- # angle
- # won't work since graph is not scale same in x and y
- angle=0 # math.atan2(pos[1],pos[0])*180/math.pi
- rotateAxis=(t1-t0,k1-k0)
- else:
- # same time so just skip
- pass
- if s:
- text = pg.TextItem(s,color=(0,0,0),anchor=(0.5,1),angle=angle,rotateAxis=rotateAxis)
- text.setFont(self.tfont)
- self.lineplot.addItem(text)
- text.setPos(*pos)
- self.plottexts+=[text]
-
- @pyqtSlot(int,int)
- def tableSelectionChanged(self,row0,row1,text=None):
- #print("selection changed",row0,row1,self.cursorMoved)
- if self.cursorMoved: return
- # time = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- # temperature = [30, 32, 34, 32, 33, 31, 29, 32, 35, 45]
- self.selectedRow=row0
- time,temp=self.buildCoordinates(row0)
- self.updateLinePlotTexts(time,temp)
-
-
-
- @pyqtSlot(int,int)
- def tableCellChanged(self,row,col,text):
- return self.realTableCellChanged(row,col,text)
- def realTableCellChanged(self,row,col,text):
- pass
- @pyqtSlot(str,int,int)
- def updateProgress(self,s,cmdp,p):
- # update action etc
-
- pass
- @pyqtSlot(dict)
- def updateState(self,state):
- # update enablement and such
- self.state=state
- for k,v in state.items():
- if k in self.infoslots:
- name,l,e=self.infoslots[k]
- # v needs to be processed and appenededlike C/s etc?
- e.setText(str(v))
- _os=state.get("OS","")
- if self.running and "program stopped" in _os:
- self.runningToStopped()
- if not self.running and "program running" in _os:
- self.stoppedToRunning()
-
- @pyqtSlot(dict)
- def updateRecord(self,state):
- # update enablement and such
- dt=state["dt"]
- value=state["value"]
- # feed graph and update it!!!
- tlist,vlist=self.runrecord
- tlist+=[dt]
- vlist+=[value]
- self.runrecord=(tlist,vlist)
- #print(self.runrecord)
-
- points=np.array([tlist,vlist])
- #print(points)
- self.scatter.setData(*points)
- self.scatter.updateSpots()
- self.scatter.invalidate()
-
-
- @pyqtSlot(str)
- def updateInfo(self,infopath):
- # update info about device
- pass
-
- def cmdlog(self,key,returncode,elements):
- print("key",repr(key),"returncode",returncode,"elem",elements)
-
- def statecallback(self,state):
- QMetaObject.invokeMethod(self,"updateState", Qt.QueuedConnection,Q_ARG(dict, state))
- def infocallback(self,infopath):
- QMetaObject.invokeMethod(self,"updateInfo", Qt.QueuedConnection,Q_ARG(str, infopath))
- def recordcallback(self,dt,value):
- QMetaObject.invokeMethod(self,"updateRecord", Qt.QueuedConnection,Q_ARG(dict, {"dt":dt,"value":value}))
-
- if __name__ == '__main__':
- if usefbs:
- # run with fbs ie fbs run etc
- appctxt = ApplicationContext() # 1. Instantiate ApplicationContext
- app=appctxt.app
- else:
- # run as plain python
- appctxt = QApplication(sys.argv)
- app=appctxt
-
- window = MainWindow(appctxt,sys.argv)
- window.resize(800, 600)
- window.show()
- exit_code = app.exec() # 2. Invoke appctxt.app.exec()
- sys.exit(exit_code)
-
|