def show_overshoot(self): """ """ dialog = QtWidgets.QDialog() vlayout = QtWidgets.QVBoxLayout() tree = ParameterTree() tree.setMinimumWidth(400) tree.setMinimumHeight(500) tree.setParameters(self.overshoot_params, showTop=False) vlayout.addWidget(tree) dialog.setLayout(vlayout) buttonBox = QtWidgets.QDialogButtonBox(parent=dialog) buttonBox.addButton('Save', buttonBox.AcceptRole) buttonBox.accepted.connect(dialog.accept) buttonBox.addButton('Cancel', buttonBox.RejectRole) buttonBox.rejected.connect(dialog.reject) vlayout.addWidget(buttonBox) dialog.setWindowTitle('Fill in information about this managers') res = dialog.exec() if res == dialog.Accepted: # save managers parameters in a xml file # start = os.path.split(os.path.split(os.path.realpath(__file__))[0])[0] # start = os.path.join("..",'daq_scan') ioxml.parameter_to_xml_file( self.overshoot_params, os.path.join(overshoot_path, self.overshoot_params.child('filename').value()))
def show_file_attributes(self, type_info='dataset'): """ """ dialog = QtWidgets.QDialog() vlayout = QtWidgets.QVBoxLayout() tree = ParameterTree() tree.setMinimumWidth(400) tree.setMinimumHeight(500) if type_info == 'scan': tree.setParameters(self.scan_attributes, showTop=False) elif type_info == 'dataset': tree.setParameters(self.dataset_attributes, showTop=False) vlayout.addWidget(tree) dialog.setLayout(vlayout) buttonBox = QtWidgets.QDialogButtonBox(parent=dialog) buttonBox.addButton('Cancel', buttonBox.RejectRole) buttonBox.addButton('Apply', buttonBox.AcceptRole) buttonBox.rejected.connect(dialog.reject) buttonBox.accepted.connect(dialog.accept) vlayout.addWidget(buttonBox) dialog.setWindowTitle( 'Fill in information about this {}'.format(type_info)) res = dialog.exec() return res
def show_preset(self): """ """ dialog = QtWidgets.QDialog() vlayout = QtWidgets.QVBoxLayout() tree = ParameterTree() tree.setMinimumWidth(400) tree.setMinimumHeight(500) tree.setParameters(self.preset_params, showTop=False) vlayout.addWidget(tree) dialog.setLayout(vlayout) buttonBox = QtWidgets.QDialogButtonBox(parent=dialog) buttonBox.addButton('Save', buttonBox.AcceptRole) buttonBox.accepted.connect(dialog.accept) buttonBox.addButton('Cancel', buttonBox.RejectRole) buttonBox.rejected.connect(dialog.reject) vlayout.addWidget(buttonBox) dialog.setWindowTitle('Fill in information about this preset') res = dialog.exec() if self.pid_type: path = pid_path else: path = preset_path if res == dialog.Accepted: # save preset parameters in a xml file #start = os.path.split(os.path.split(os.path.realpath(__file__))[0])[0] #start = os.path.join("..",'daq_scan') custom_tree.parameter_to_xml_file( self.preset_params, os.path.join(path, self.preset_params.child(('filename')).value())) if not self.pid_type: #check if overshoot configuration and layout configuration with same name exists => delete them if yes overshoot_path = os.path.join(local_path, 'overshoot_configurations') file = os.path.splitext( self.preset_params.child(('filename')).value())[0] file = os.path.join(overshoot_path, file + '.xml') if os.path.isfile(file): os.remove(file) layout_path = os.path.join(local_path, 'layout') file = os.path.splitext( self.preset_params.child(('filename')).value())[0] file = os.path.join(layout_path, file + '.dock') if os.path.isfile(file): os.remove(file)
def show_file_attributes(self, type_info='dataset'): """ Switch the type_info value. In case of : * *scan* : Set parameters showing top false * *dataset* : Set parameters showing top false * *managers* : Set parameters showing top false. Add the save/cancel buttons to the accept/reject dialog (to save managers parameters in a xml file). Finally, in case of accepted managers type info, save the managers parameters in a xml file. =============== =========== ==================================== **Parameters** **Type** **Description** *type_info* string The file type information between * scan * dataset * managers =============== =========== ==================================== See Also -------- custom_tree.parameter_to_xml_file, create_menu """ dialog = QtWidgets.QDialog() vlayout = QtWidgets.QVBoxLayout() tree = ParameterTree() tree.setMinimumWidth(400) tree.setMinimumHeight(500) if type_info == 'scan': tree.setParameters(self.scan_attributes, showTop=False) elif type_info == 'dataset': tree.setParameters(self.dataset_attributes, showTop=False) vlayout.addWidget(tree) dialog.setLayout(vlayout) buttonBox = QtWidgets.QDialogButtonBox(parent=dialog) buttonBox.addButton('Cancel', buttonBox.RejectRole) buttonBox.addButton('Apply', buttonBox.AcceptRole) buttonBox.rejected.connect(dialog.reject) buttonBox.accepted.connect(dialog.accept) vlayout.addWidget(buttonBox) dialog.setWindowTitle( 'Fill in information about this {}'.format(type_info)) res = dialog.exec() return res
class ScanSelector(QObject): scan_select_signal = pyqtSignal(ROI) params = [ { 'title': 'Scan options', 'name': 'scan_options', 'type': 'group', 'children': [ { 'title': 'Sources:', 'name': 'sources', 'type': 'list', }, { 'title': 'Viewers:', 'name': 'viewers', 'type': 'list', }, { 'title': 'Scan type:', 'name': 'scan_type', 'type': 'list', 'values': ['Scan1D', 'Scan2D'], 'value': 'Scan2D' }, ] }, { 'title': 'Scan Area', 'name': 'scan_area', 'type': 'group', 'children': [ { 'title': 'ROI select:', 'name': 'ROIselect', 'type': 'group', 'visible': True, 'children': [ { 'title': 'x0:', 'name': 'x0', 'type': 'int', 'value': 0, 'min': 0 }, { 'title': 'y0:', 'name': 'y0', 'type': 'int', 'value': 0, 'min': 0 }, { 'title': 'width:', 'name': 'width', 'type': 'int', 'value': 10, 'min': 1 }, { 'title': 'height:', 'name': 'height', 'type': 'int', 'value': 10, 'min': 1 }, ] }, { 'title': 'Coordinates:', 'name': 'coordinates', 'type': 'itemselect', 'visible': True }, ] }, ] def __init__(self, viewer_items=[], scan_type='Scan2D', positions=[]): """ Parameters ---------- viewer_items: dict where the keys are the titles of the sources while the values are dict with keys viewers: list of plotitems names: list of viewer titles scan_type: (str) either 'Scan1D' correspondign to a polyline ROI or 'Scan2D' for a rect Roi positions: list in case of 'Scan1D', should be a sequence of 2 floats sequence [(x1,y1),(x2,y2),(x3,y3),...] in case of 'Scan2D', should be a sequence of 4 floats (x, y , w, h) """ super(ScanSelector, self).__init__() self._viewers_items = viewer_items self.sources_names = list(viewer_items.keys()) if len(viewer_items) != 0: self.scan_selector_source = viewer_items[ self.sources_names[0]]['viewers'][0] else: self.scan_selector_source = None self.scan_selector = None self.setupUI() self.settings.child('scan_options', 'scan_type').setValue(scan_type) self.remove_scan_selector() if self.scan_selector_source is not None: if len(viewer_items[self.sources_names[0]]['viewers']) == 1: self.settings.child('scan_options', 'viewers').hide() else: self.settings.child('scan_options', 'viewers').hide() self.update_scan_area_type() if scan_type == "Scan1D" and positions != []: self.scan_selector.setPoints(positions) elif scan_type == 'Scan2D' and positions != []: self.scan_selector.setPos(positions[:2]) self.scan_selector.setSize(positions[3:]) @property def viewers_items(self): return self._viewers_items @viewers_items.setter def viewers_items(self, items): self._viewers_items = items self.sources_names = list(items.keys()) self.scan_selector_source = items[self.sources_names[0]]['viewers'][0] self.settings.child('scan_options', 'sources').setOpts(limits=self.sources_names) viewer_names = self._viewers_items[self.sources_names[0]]['names'] self.settings.child('scan_options', 'viewers').setOpts(limits=viewer_names) def show(self, visible=True): self.show_scan_selector(visible) if visible: self.widget.show() else: self.widget.hide() def hide(self): self.show(False) def setupUI(self): self.widget = QtWidgets.QWidget() layout = QtWidgets.QVBoxLayout() self.settings_tree = ParameterTree() layout.addWidget(self.settings_tree, 10) self.settings_tree.setMinimumWidth(300) self.settings = Parameter.create(name='Settings', type='group', children=self.params) self.settings_tree.setParameters(self.settings, showTop=False) self.settings.child('scan_options', 'sources').setOpts(limits=self.sources_names) if len(self.viewers_items): viewer_names = self._viewers_items[self.sources_names[0]]['names'] else: viewer_names = [] self.settings.child('scan_options', 'viewers').setOpts(limits=viewer_names) self.settings.sigTreeStateChanged.connect(self.source_changed) self.widget.setLayout(layout) #self.widget.show() self.widget.setWindowTitle('Scan Selector') def source_changed(self, param, changes): for param, change, data in changes: path = self.settings.childPath(param) if path is not None: childName = '.'.join(path) else: childName = param.name() if change == 'childAdded': pass elif change == 'value': if param.name() == 'sources' and param.value() is not None: viewer_names = self._viewers_items[param.value()]['names'] self.settings.child('scan_options', 'viewers').setOpts(limits=viewer_names) if len(viewer_names) == 1: self.settings.child('scan_options', 'viewers').hide() self.remove_scan_selector() self.scan_selector_source = self._viewers_items[ param.value()]['viewers'][0] self.update_scan_area_type() if param.name() == 'scan_type': if param.value() == 'Scan1D': self.settings.child('scan_area', 'ROIselect').hide() self.settings.child('scan_area', 'coordinates').show() self.remove_scan_selector() self.update_scan_area_type() else: self.settings.child('scan_area', 'ROIselect').show() self.settings.child('scan_area', 'coordinates').hide() self.remove_scan_selector() self.update_scan_area_type() self.scan_selector.sigRegionChangeFinished.emit( self.scan_selector) elif change == 'parent': pass def remove_scan_selector(self): if self.scan_selector_source is not None: try: self.scan_selector_source.image_widget.plotitem.removeItem( self.scan_selector) except: pass pyqtSlot(str) def update_scan_area_type(self): if self.settings.child('scan_options', 'scan_type').value() == 'Scan1D': scan_area_type = 'PolyLines' else: scan_area_type = 'Rect' self.remove_scan_selector() if scan_area_type == 'Rect': self.scan_selector = RectROI([0, 0], [10, 10]) elif scan_area_type == 'PolyLines': self.scan_selector = PolyLineROI_custom([(0, 0), [10, 10]]) if self.scan_selector_source is not None: self.scan_selector.sigRegionChangeFinished.connect( self.update_scan) self.scan_selector_source.image_widget.plotitem.addItem( self.scan_selector) self.show_scan_selector() self.scan_selector.sigRegionChangeFinished.emit(self.scan_selector) def show_scan_selector(self, visible=True): self.scan_selector.setVisible(visible) def update_scan(self, roi): if self.scan_selector_source is not None: if isinstance(roi, RectROI): self.settings.child('scan_area', 'ROIselect', 'x0').setValue(roi.pos().x()) self.settings.child('scan_area', 'ROIselect', 'y0').setValue(roi.pos().y()) self.settings.child('scan_area', 'ROIselect', 'width').setValue(roi.size().x()) self.settings.child('scan_area', 'ROIselect', 'height').setValue(roi.size().y()) elif isinstance(roi, PolyLineROI_custom): self.settings.child('scan_area', 'coordinates').setValue( dict(all_items=[ '({:.03f} , {:.03f})'.format(pt.x(), pt.y()) for pt in roi.get_vertex() ], selected=[])) self.scan_select_signal.emit(roi)
class GuiManager(QtGui.QMainWindow): def __init__(self): super(GuiManager, self).__init__() self.createLayout() def closeEvent(self, event): print 'User asked to close the app' self.stopAcquisition() Parameters.isAppKilled = True #time.sleep(0.1) #Wait for the worker death event.accept() # let the window close def startAcquisition(self): if Parameters.isConnected == False: return Parameters.isSaving = Parameters.paramObject.child("Saving parameters", "saveResults").value() if Parameters.isSaving == True: theDate = datetime.datetime.today() .strftime('%Y%m%d') theTime = datetime.datetime.today() .strftime('%H%M%S') theName=Parameters.paramObject.child("Saving parameters", "Experiment name").value() #Check counter increment folderParent = 'C:/InterferometryResults/' + theDate counter = 1 folderFound = False theFolder = folderParent + '/' + theName + '_' + str(counter).zfill(3) if not os.path.exists(folderParent): #Make sure parent exist os.makedirs(folderParent) while folderFound == False and counter < 1000: #Loop until folder is found if not os.path.exists(theFolder): #Make sure parent exist os.makedirs(theFolder) folderFound = True else: counter = counter + 1 theFolder = folderParent + '/' + theName + '_' + str(counter).zfill(3) if counter < 1000: Parameters.savingFolder = theFolder if not os.path.exists(theFolder): os.makedirs(theFolder) else: Parameters.isSaving = False; #Should not happen. self.btStart.setEnabled(False) self.btStop.setEnabled(True) self.btOpen.setEnabled(False) #Get the parameter values Parameters.nSamples=Parameters.paramObject.child("Acquisition Parameters", "Number of samples to acquire").value() acqMode=Parameters.paramObject.child("Trigger Options", "Acquisition mode").value() triggerAmplitude=Parameters.paramObject.child("Trigger Options", "Trigger amplitude").value() motorSpeed=Parameters.paramObject.child("Acquisition Parameters", "Rotation speed (%)").value()+28 #28 is calibration nBlocks=int(math.ceil(float(Parameters.nSamples)/512)) #Must be a multiple of 512 Parameters.nSamples = nBlocks*512 Parameters.paramObject.child("Acquisition Parameters", "Number of samples to acquire").setValue(Parameters.nSamples) #Set frequency in free mode. These settings could be optimized depending on the computer. freq=2500000 #10im/s if Parameters.nSamples > 100352: freq=5000000 #5im/s if Parameters.nSamples > 300032: freq=12500000 #2im/s if Parameters.nSamples > 1000000: freq=25000000 #1im/s if Parameters.nSamples > 2000000: freq=50000000 #0.5im/s #Start the acquisition on the FPGA data = [] data = array('i') data.append(1) data.append(freq) data.append(2) data.append(acqMode) data.append(3) data.append(nBlocks) data.append(4) data.append(motorSpeed) data.append(5) data.append(triggerAmplitude) data.append(6) data.append(0) theBytes = struct.pack('i' * len(data), *data) buf = bytearray(theBytes) Parameters.dev.WriteToPipeIn(128, buf) Parameters.dev.SetWireInValue(0, 1+2+8, 1+2+8);Parameters.dev.UpdateWireIns() #Enable DDR2 reading and writing and activate memory. time.sleep(0.1); #Wait to make sure everything is ready. TODO: Reduce this. Parameters.dev.SetWireInValue(0, 4, 4);Parameters.dev.UpdateWireIns() #Start acquisition clock. #self.plotTr.enableAutoRange('xy', True) ## stop auto-scaling after the first data set is plotted Parameters.theQueue = Queue.Queue() #Create the queue #self.DisplayUpdater(0) self.workThread.start(); #Start the display worker def stopAcquisition(self): print 'Stopping...' if hasattr(self,'tDisplay'): self.tDisplay.cancel() self.workThread.exitWorker(); while self.workThread.isRunning == True: time.sleep(0.01) #Wait for the worker death, before resetting the board Parameters.dev.SetWireInValue(0, 0, 1+2+4+8);Parameters.dev.UpdateWireIns() #Reset board. #Save format: Results / YYMMDD / ExperimentName_Counter. Could also use the time. if Parameters.isSaving == True: #global state state = Parameters.paramObject.saveState() file = open(Parameters.savingFolder + '\Parameters.txt', 'w') pickle.dump(state, file) file.close() print 'Stopped.' self.btStart.setEnabled(True) self.btStop.setEnabled(False) self.btOpen.setEnabled(True) ''' def DisplayUpdater(self,dummy): if Parameters.isAppKilled == True: return; if Parameters.theQueue.empty(): print 'no data...' while not Parameters.theQueue.empty(): #Empty the display queue data = Parameters.theQueue.get() #print 'data available!' if 'data' in locals(): #print 'display data' + str(data[2]) self.setDataCurveTr(data) tDisplay = Timer(2, self.DisplayUpdater, [0],{}) tDisplay.start() ''' def motorSpeedChanged(self,value): newSpeed=Parameters.paramObject.child("Acquisition Parameters", "Rotation speed (%)").value() newSpeed=newSpeed+28 #Calibration #Debug #Parameters.paramObject.child("Acquisition Parameters", "Rotation speed (%)").setOpts(enabled=False) #Parameters.paramObject.child("Acquisition Parameters", "Rotation speed (%)").setOpts(visible=False) #Parameters.paramObject.child("Acquisition Parameters", "Rotation speed (%)").setOpts(value=200) data = [] data = array('i') data.append(4) data.append(newSpeed) data.append(0) #Minimum size is 8 'int' data.append(0) data.append(0) data.append(0) data.append(0) data.append(0) theBytes = struct.pack('i' * len(data), *data) Parameters.bufferToDev = bytearray(theBytes) Parameters.bufferToDevReady = True def triggerChanged(self,value): acqMode=Parameters.paramObject.child("Trigger Options", "Acquisition mode").value() triggerAmplitude=Parameters.paramObject.child("Trigger Options", "Trigger amplitude").value() data = [] data = array('i') data.append(2) data.append(acqMode) data.append(5) data.append(triggerAmplitude) data.append(0) data.append(0) data.append(0) data.append(0) theBytes = struct.pack('i' * len(data), *data) print str(theBytes) Parameters.bufferToDev = bytearray(theBytes) Parameters.bufferToDevReady = True def triggerHalfSwitch(self): Parameters.triggerToDev = True def saveParam(self): #global state state = Parameters.paramObject.saveState() file = open('dump.txt', 'w') pickle.dump(state, file) file.close() def restoreParam(self): #lobal state file = open('dump.txt', 'r') state = pickle.load(file) #add = Parameters.paramObject['Save/Restore functionality', 'Restore State', 'Add missing items'] #rem = Parameters.paramObject['Save/Restore functionality', 'Restore State', 'Remove extra items'] Parameters.paramObject.restoreState(state, addChildren=False, removeChildren=False) #Set the status text (system connected or not) def setStatus(self, isConnected, isError = False): def dotChange(item, nDots): #Timer function to display a varying number of dots after "retrying" if Parameters.isConnected == True: return; if Parameters.isAppKilled == True: return; textNotConnected = "System not connected, retrying" item.setText(textNotConnected+".") #Number of dots varies from 1 to 5 if nDots == 1: textDots = "." nDots = 2 elif nDots == 2: textDots = ".." nDots = 3 elif nDots == 3: textDots = "..." nDots = 4 elif nDots == 4: textDots = "...." nDots = 5 else: textDots = "....." nDots = 1 item.setForeground(QtGui.QColor("red")) item.setText(textNotConnected+textDots) self.timerDotChange = Timer(0.25, dotChange, [self.itemStatus, nDots]) self.timerDotChange.start() if (isError == True): #System not connected print "Error" if hasattr(self,'timerDotChange'): self.timerDotChange.cancel() time.sleep(0.5) self.itemStatus.setForeground(QtGui.QColor("red")) self.itemStatus.setText("Error with system. Please restart the application and the system.") elif (isConnected == False): #System not connected nDots = 1 if hasattr(self,'timerDotChange'): self.timerDotChange.cancel() self.timerDotChange = Timer(0.25, dotChange, [self.itemStatus, nDots]) self.timerDotChange.start() else: print "Connected" if hasattr(self,'timerDotChange'): self.timerDotChange.cancel() time.sleep(0.1) self.itemStatus.setForeground(QtGui.QColor("green")) self.itemStatus.setText("System connected.") print "Connected2" #Set the status text (system connected or not) def setCameraStatus(self, isConnected): def dotChange(item, nDots): #Timer function to display a varying number of dots after "retrying" if Parameters.isCameraConnected == True: return; if Parameters.isAppKilled == True: return; textNotConnected = "Camera not connected, retrying" item.setText(textNotConnected+".") #Number of dots varies from 1 to 5 if nDots == 1: textDots = "." nDots = 2 elif nDots == 2: textDots = ".." nDots = 3 elif nDots == 3: textDots = "..." nDots = 4 elif nDots == 4: textDots = "...." nDots = 5 else: textDots = "....." nDots = 1 item.setForeground(QtGui.QColor("red")) item.setText(textNotConnected+textDots) self.timerDotChangeCamera = Timer(0.25, dotChange, [self.itemCameraStatus, nDots]) self.timerDotChangeCamera.start() if (isConnected == False): #System not connected print 'cam oups' nDots = 1 if hasattr(self,'timerDotChangeCamera'): self.timerDotChangeCamera.cancel() self.timerDotChangeCamera = Timer(0.25, dotChange, [self.itemCameraStatus, nDots]) self.timerDotChangeCamera.start() else: print "Camera connected" if hasattr(self,'timerDotChangeCamera'): self.timerDotChangeCamera.cancel() time.sleep(0.1) self.itemCameraStatus.setForeground(QtGui.QColor("green")) self.itemCameraStatus.setText("Camera connected.") print "Camera connected2" #Set the status text (system connected or not) def setInfo(self, text): self.tbInfo.append(text) #Set the status text (system connected or not) def setWire(self, mem1, mem2, mem3, mem4, maxValPos): self.itemMemory.setText("Acquisition board memory usage:\nDDR2: " + str(mem1) + " bytes.\nFIFO in: " + str(mem2) + " bytes.\nFIFO out: " + str(mem3) + " bytes.\nFIFO out (minimum): " + str(mem4) + " bytes.") self.itemMaxValPos.setText("Maximum RF value position: " + str(maxValPos)) def createLayout(self): print 'Creating layout...' self.setWindowTitle('Interferometry Acquisition GUI') #self.widget = QtGui.QWidget() #self.setCentralWidget(self.widget) self.layout = pg.LayoutWidget() #self.widget.setLayout(self.layout) self.setCentralWidget(self.layout) #Create GUI sizePolicyBt = QtGui.QSizePolicy(1, 1) sizePolicyBt.setHorizontalStretch(0) sizePolicyBt.setVerticalStretch(0) self.btOpen = QtGui.QPushButton("Open\nprevious results") sizePolicyBt.setHeightForWidth(self.btOpen.sizePolicy().hasHeightForWidth()) self.btOpen.setSizePolicy(sizePolicyBt); self.btOpen.setStyleSheet("background-color: yellow; font-size: 16px; font: bold") self.btStart = QtGui.QPushButton("Start\nacquisition") sizePolicyBt.setHeightForWidth(self.btStart.sizePolicy().hasHeightForWidth()) self.btStart.setSizePolicy(sizePolicyBt); self.btStart.setStyleSheet("background-color: green; font-size: 16px; font: bold") self.btStart.clicked.connect(self.startAcquisition) self.btStop = QtGui.QPushButton("Stop\nacquisition") sizePolicyBt.setHeightForWidth(self.btStop.sizePolicy().hasHeightForWidth()) self.btStop.setSizePolicy(sizePolicyBt); self.btStop.setStyleSheet("background-color: red; font-size: 16px; font: bold") self.btStop.clicked.connect(self.stopAcquisition) self.btStop.setEnabled(False) self.paramTree = ParameterTree() self.paramTree.setParameters(Parameters.paramObject, showTop=False) self.paramTree.setWindowTitle('pyqtgraph example: Parameter Tree') self.paramTree.setMinimumWidth(350) self.paramTree.setMaximumWidth(350) sizePolicyPt = QtGui.QSizePolicy(1,1) sizePolicyPt.setHorizontalStretch(QtGui.QSizePolicy.Fixed) sizePolicyPt.setVerticalStretch(1) self.paramTree.setSizePolicy(sizePolicyPt); ## Create random 2D data data = np.random.normal(size=(512, 512)) + pg.gaussianFilter(np.random.normal(size=(512, 512)), (5, 5)) * 20 + 100 data = data[:,:,np.newaxis] data = data.repeat(3,2) self.plotTl = pg.GraphicsView() self.plotTlImage = pg.ImageItem(data[:,:,:]) #parent=self.plotTl self.plotTlViewBox = pg.ViewBox() self.plotTl.setCentralWidget(self.plotTlViewBox) self.plotTlViewBox.addItem(self.plotTlImage) self.plotTr = pg.PlotWidget(title="Interferometry", labels={'left': 'Signal amplitude (A.U.)', 'bottom': 'Distance (mm)'}) #self.plotTlViewBox2.addItem(self.plotTr) self.plotTrCurve = self.plotTr.plot(pen=(255,0,0),name='C1') #Red self.plotTrCurve2 = self.plotTr.plot(pen=(0,255,0),name='C2') #Green #self.plotTlViewBox2.enableAutoRange('xy', True) ## stop auto-scaling after the first data set is plotted #self.plotTr.addLegend('Test') self.plotTr.setYRange(-1000, 1000) self.plotBl = pg.PlotWidget(title="Distance", labels={'left': 'Distance (mm)', 'bottom': 'Number of acquisitions'}) self.plotBlCurve = self.plotBl.plot(pen=(255,0,0),name='C1') self.plotBl.enableAutoRange('xy', True) ## stop auto-scaling after the first data set is plotted self.plotBl.setMaximumWidth(3500) self.tbInfo = QtGui.QTextEdit() self.tbInfo.setEnabled(False) palette = self.tbInfo.palette() palette.setColor(QtGui.QPalette.Base, QtGui.QColor("white")) #White background self.tbInfo.setPalette(palette) self.tbInfo.setTextColor(QtGui.QColor("black")) self.tbInfo.insertPlainText("Useful information will appear here.") #Create list view of multiple items self.tbStatus = QtGui.QListView() self.tbStatus.setEnabled(False) palette = self.tbStatus.palette() palette.setColor(QtGui.QPalette.Base, QtGui.QColor("white")) #White background self.tbStatus.setPalette(palette) itemModelStatus = QtGui.QStandardItemModel(self.tbStatus) self.tbStatus.setModel(itemModelStatus) #Add system status self.itemStatus = QtGui.QStandardItem() self.setStatus(False) itemModelStatus.appendRow(self.itemStatus) #Add camera status self.itemCameraStatus = QtGui.QStandardItem() self.setCameraStatus(False) itemModelStatus.appendRow(self.itemCameraStatus) #Add memory usage self.itemMemory = QtGui.QStandardItem("Acquisition board memory usage: N/A") self.itemMemory.setForeground(QtGui.QColor("black")) itemModelStatus.appendRow(self.itemMemory) #Add max value position self.itemMaxValPos = QtGui.QStandardItem("Maximum RF value position: N/A") self.itemMaxValPos.setForeground(QtGui.QColor("black")) itemModelStatus.appendRow(self.itemMaxValPos) #layout.addWidget(QtGui.QLabel("These are two views of the same data. They should always display the same values."), 0, 0, 1, 2) self.layout.addWidget(self.btOpen, 9, 6, 1, 1) self.layout.addWidget(self.btStart, 9, 7, 1, 1) self.layout.addWidget(self.btStop, 9, 8, 1, 1) self.layout.addWidget(self.paramTree, 0, 0, 10, 3) self.layout.addWidget(self.plotTl, 0, 3, 5, 3) self.layout.addWidget(self.plotTr, 0, 6, 5, 3) self.layout.addWidget(self.plotBl, 5, 3, 5, 3) self.layout.addWidget(self.tbInfo, 5, 6, 2, 3) self.layout.addWidget(self.tbStatus, 7, 6, 2, 3) self.layout.layout.setColumnStretch(3,1) self.layout.layout.setColumnStretch(4,1) self.layout.layout.setColumnStretch(5,1) self.layout.layout.setColumnStretch(6,1) self.layout.layout.setColumnStretch(7,1) self.layout.layout.setColumnStretch(8,1) self.show() self.resize(1500,800) self.move(100,100) Parameters.paramObject.param('Save/Restore functionality', 'Save State').sigActivated.connect(self.saveParam) Parameters.paramObject.param('Save/Restore functionality', 'Restore State').sigActivated.connect(self.restoreParam) Parameters.paramObject.child("Acquisition Parameters", "Rotation speed (%)").sigValueChanged.connect(self.motorSpeedChanged) Parameters.paramObject.child("Trigger Options", "Acquisition mode").sigValueChanged.connect(self.triggerChanged) Parameters.paramObject.child("Trigger Options", "Trigger amplitude").sigValueChanged.connect(self.triggerChanged) Parameters.paramObject.child("Trigger Options", "Trigger switch 1/2").sigActivated.connect(self.triggerHalfSwitch) # adding by emitting signal in different thread self.workThread = AcquisitionWorker() self.workThread.updateDataCamera.connect(self.setDataCurveTl) self.workThread.updateDataInterf.connect(self.setDataCurveTr) self.workThread.updateDataDistance.connect(self.setDataCurveBl) self.workThread.updateDataDistance2.connect(self.setDataCurveBl2) self.workThread.updateWire.connect(self.setWire) self.workThread.setStatus.connect(self.setStatus) self.workThread.setInfo.connect(self.setInfo) self.testCount = 0; #Fill the plots with dummy data self.data = np.random.normal(size=(10,1000)) self.plotTrXAxis = np.arange(1000) * (0.01) self.plotBlXAxis = np.arange(1000) * (1) self.plotTrCurve.setData(x=self.plotTrXAxis,y=self.data[2%10],name='C1') self.plotTrCurve2.setData(x=self.plotTrXAxis,y=self.data[3%10],name='C2') self.plotBlCurve.setData(x=self.plotBlXAxis,y=self.data[4%10],name='C1') self.valueTest = 1 def setDataCurveTl(self, data): self.dataImage1 = data self.plotTlImage.setImage(data) self.testCount = self.testCount + 1; def setDataCurveTr(self, dataIn): self.plotTrCurve.setData(dataIn[0:len(dataIn):2],name='C1') self.plotTrCurve2.setData(dataIn[1:len(dataIn):2],name='C2') def setDataCurveBl(self, dataIn): self.plotBlCurve.setData(dataIn,name='C1') def setDataCurveBl2(self, xIn, dataIn): self.plotBlCurve.setData(x=xIn,y=dataIn,name='C1')
class BeeActions(QObject): log_signal = pyqtSignal(str) def __init__(self, dockarea): super().__init__() self.dockarea = dockarea self.dockarea.dock_signal.connect(self.save_layout_state_auto) self.mainwindow = dockarea.parent() self.chrono = ChronoTimer(dockarea) self.author = 'Aurore Avargues' self.h5saver = H5Saver() self.h5saver.new_file_sig.connect(self.create_new_file) self.settings = None self.shortcut_file = None self.shortcuts = [] self.shortcut_manager = ShortCutManager(list_actions) self.timestamp_array = None self.action_array = None self.bee_array = None self.setup_ui() def setup_ui(self): #creating the menubar self.menubar = self.mainwindow.menuBar() self.create_menu(self.menubar) #disconnect normal chrono/timer behaviour with the start button self.chrono.start_pb.disconnect() self.chrono.start_pb.clicked.connect(self.set_scan) self.chrono.reset_pb.clicked.connect(self.stop_daq) self.settings_dock = Dock('Settings') self.dockarea.addDock(self.settings_dock, 'bottom', self.chrono.dock_controls) self.dock_daq = Dock('Data Acquisition') self.dockarea.addDock(self.dock_daq, 'right') self.logger_list = QtWidgets.QListWidget() self.logger_list.setMinimumWidth(300) self.dock_daq.addWidget(self.logger_list) self.init_tree = ParameterTree() self.init_tree.setMinimumWidth(300) self.init_tree.setMinimumHeight(150) self.settings_dock.addWidget(self.init_tree) self.settings_dock.addWidget(self.h5saver.settings_tree) self.h5saver.settings.child(('save_type')).hide() self.h5saver.settings.child(('save_2D')).hide() self.h5saver.settings.child(('save_raw_only')).hide() self.h5saver.settings.child(('do_save')).hide() self.h5saver.settings.child(('custom_name')).hide() self.settings = Parameter.create(name='init_settings', type='group', children=[ { 'title': 'Loaded presets', 'name': 'loaded_files', 'type': 'group', 'children': [ { 'title': 'Shortcut file', 'name': 'shortcut_file', 'type': 'str', 'value': '', 'readonly': True }, { 'title': 'Layout file', 'name': 'layout_file', 'type': 'str', 'value': '', 'readonly': True }, ] }, { 'title': 'Settings', 'name': 'settings', 'type': 'group', 'children': [ { 'title': 'Save Bee number', 'name': 'save_bee_number', 'type': 'bool', 'value': True }, ] }, { 'title': 'Shortcuts', 'name': 'shortcuts', 'type': 'group', 'children': [] }, ]) self.init_tree.setParameters(self.settings, showTop=False) self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed) #params about dataset attributes and scan attibutes date = QDateTime(QDate.currentDate(), QTime.currentTime()) params_dataset = [{ 'title': 'Dataset information', 'name': 'dataset_info', 'type': 'group', 'children': [{ 'title': 'Author:', 'name': 'author', 'type': 'str', 'value': self.author }, { 'title': 'Date/time:', 'name': 'date_time', 'type': 'date_time', 'value': date }, { 'title': 'Sample:', 'name': 'sample', 'type': 'str', 'value': '' }, { 'title': 'Experiment type:', 'name': 'experiment_type', 'type': 'str', 'value': '' }, { 'title': 'Description:', 'name': 'description', 'type': 'text', 'value': '' }] }] params_scan = [{ 'title': 'Scan information', 'name': 'scan_info', 'type': 'group', 'children': [ { 'title': 'Author:', 'name': 'author', 'type': 'str', 'value': self.author }, { 'title': 'Date/time:', 'name': 'date_time', 'type': 'date_time', 'value': date }, { 'title': 'Scan type:', 'name': 'scan_type', 'type': 'list', 'value': 'Scan1D', 'values': ['Scan1D', 'Scan2D'] }, { 'title': 'Scan name:', 'name': 'scan_name', 'type': 'str', 'value': '', 'readonly': True }, { 'title': 'Description:', 'name': 'description', 'type': 'text', 'value': '' }, ] }] self.dataset_attributes = Parameter.create(name='Attributes', type='group', children=params_dataset) self.scan_attributes = Parameter.create(name='Attributes', type='group', children=params_scan) def parameter_tree_changed(self, param, changes): """ | Check eventual changes in the changes list parameter. | | In case of changed values, emit the signal containing the current path and parameter via update_settings_signal to the connected hardware. =============== ==================================== ================================================== **Parameters** **Type** **Description** *param* instance of pyqtgraph parameter The parameter to be checked *changes* (parameter,change,infos) tuple list The (parameter,change,infos) list to be treated =============== ==================================== ================================================== """ for param, change, data in changes: path = self.settings.childPath(param) if path is not None: childName = '.'.join(path) else: childName = param.name() if change == 'childAdded': pass elif change == 'value': if param.name() in custom_tree.iter_children( self.settings.child(('shortcuts')), []): if param.parent().name() == 'shortcuts': param_index = custom_tree.iter_children( self.settings.child(('shortcuts')), []).index(param.name()) action = self.shortcut_manager.shortcut_params.child( ('actions')).children()[param_index].child( ('action')).value() self.activate_shortcut(self.shortcuts[param_index], action, activate=param.value()) elif change == 'parent': pass def activate_shortcut(self, shortcut, action='', activate=True): if activate: shortcut.activated.connect(self.create_activated_slot(action)) else: try: shortcut.activated.disconnect() except: pass def create_activated_slot(self, action): return lambda: self.log_data(action) def log_data(self, action=''): now = self.chrono.get_elapsed_time() if self.settings.child('settings', 'save_bee_number').value(): widget = QtWidgets.QWidget() index, res = QtWidgets.QInputDialog.getInt( widget, 'Bee number', 'Pick a number for this bee!') if res: new_item = QtWidgets.QListWidgetItem( f'Elapsed time: {int(now)} s, Bee {index} did :{action}') self.logger_list.insertItem(0, new_item) self.h5saver.append(self.action_array, action) self.h5saver.append(self.timestamp_array, np.array([now])) self.h5saver.append(self.bee_array, np.array([index])) else: new_item = QtWidgets.QListWidgetItem( f'Elapsed time: {int(now)} s:{action}') self.logger_list.insertItem(0, new_item) self.h5saver.append(self.action_array, action) self.h5saver.append(self.timestamp_array, np.array([now])) self.h5saver.flush() def create_shortcuts(self): pass def create_new_file(self, new_file): self.h5saver.init_file(update_h5=new_file) res = self.update_file_settings(new_file) return res def set_scan(self): """ Sets the current scan given the selected settings. Makes some checks, increments the h5 file scans. In case the dialog is cancelled, return False and aborts the scan """ try: if self.shortcut_file is not None: # set the filename and path res = self.create_new_file(False) if not res: return #create the arrays within the current scan group self.timestamp_array = self.h5saver.add_array( self.h5saver.current_scan_group, 'time_axis', 'data', scan_type='scan1D', enlargeable=True, array_to_save=np.array([ 0., ]), data_shape=(1, ), title='Timestamps', metadata=dict(units='seconds')) self.action_array = self.h5saver.add_string_array( self.h5saver.current_scan_group, 'actions', title='Actions', metadata=dict([])) if self.settings.child('settings', 'save_bee_number').value(): self.bee_array = self.h5saver.add_array( self.h5saver.current_scan_group, 'bees', 'data', scan_type='scan1D', enlargeable=True, array_to_save=np.array([ 0, ]), title='Bees', data_shape=(1, )) current_filename = self.h5saver.settings.child( ('current_scan_name')).value() self.init_tree.setEnabled(False) self.h5saver.settings_tree.setEnabled(False) self.logger_list.clear() self.h5saver.current_scan_group._v_attrs['scan_done'] = False # if all metadat steps have been validated, start the chrono self.chrono.start() return True else: mssg = QtWidgets.QMessageBox() mssg.setText( 'You have to load a shortcut file configuration before starting' ) mssg.exec() return False except Exception as e: self.update_status(getLineInfo() + str(e)) def stop_daq(self): self.h5saver.current_scan_group._v_attrs['scan_done'] = True self.init_tree.setEnabled(True) self.h5saver.settings_tree.setEnabled(True) self.h5saver.flush() def update_file_settings(self, new_file=False): try: if self.h5saver.current_scan_group is None: new_file = True if new_file: self.set_metadata_about_dataset() self.save_metadata(self.h5saver.raw_group, 'dataset_info') if self.h5saver.current_scan_name is None: self.h5saver.add_scan_group() elif not self.h5saver.is_node_in_group( self.h5saver.raw_group, self.h5saver.current_scan_name): self.h5saver.add_scan_group() #set attributes to the current group, such as scan_type.... self.scan_attributes.child('scan_info', 'scan_type').setValue('Scan1D') self.scan_attributes.child('scan_info', 'scan_name').setValue( self.h5saver.current_scan_group._v_name) self.scan_attributes.child('scan_info', 'description').setValue( self.h5saver.current_scan_group._v_attrs['description']) res = self.set_metadata_about_current_scan() self.save_metadata(self.h5saver.current_scan_group, 'scan_info') return res except Exception as e: self.update_status(getLineInfo() + str(e)) def set_metadata_about_current_scan(self): """ Set the date/time and author values of the scan_info child of the scan_attributes tree. Show the 'scan' file attributes. See Also -------- show_file_attributes """ date = QDateTime(QDate.currentDate(), QTime.currentTime()) self.scan_attributes.child('scan_info', 'date_time').setValue(date) self.scan_attributes.child('scan_info', 'author').setValue( self.dataset_attributes.child('dataset_info', 'author').value()) res = self.show_file_attributes('scan') return res def set_metadata_about_dataset(self): """ Set the date value of the data_set_info-date_time child of the data_set_attributes tree. Show the 'dataset' file attributes. See Also -------- show_file_attributes """ date = QDateTime(QDate.currentDate(), QTime.currentTime()) self.dataset_attributes.child('dataset_info', 'date_time').setValue(date) res = self.show_file_attributes('dataset') return res def show_file_attributes(self, type_info='dataset'): """ """ dialog = QtWidgets.QDialog() vlayout = QtWidgets.QVBoxLayout() tree = ParameterTree() tree.setMinimumWidth(400) tree.setMinimumHeight(500) if type_info == 'scan': tree.setParameters(self.scan_attributes, showTop=False) elif type_info == 'dataset': tree.setParameters(self.dataset_attributes, showTop=False) vlayout.addWidget(tree) dialog.setLayout(vlayout) buttonBox = QtWidgets.QDialogButtonBox(parent=dialog) buttonBox.addButton('Cancel', buttonBox.RejectRole) buttonBox.addButton('Apply', buttonBox.AcceptRole) buttonBox.rejected.connect(dialog.reject) buttonBox.accepted.connect(dialog.accept) vlayout.addWidget(buttonBox) dialog.setWindowTitle( 'Fill in information about this {}'.format(type_info)) res = dialog.exec() return res def save_metadata(self, node, type_info='dataset_info'): """ """ attr = node._v_attrs if type_info == 'dataset_info': attr['type'] = 'dataset' params = self.dataset_attributes else: attr['type'] = 'scan' params = self.scan_attributes for child in params.child((type_info)).children(): if isinstance(child.value(), QDateTime): attr[child.name()] = child.value().toString( 'dd/mm/yyyy HH:MM:ss') else: attr[child.name()] = child.value() if type_info == 'dataset_info': #save contents of given parameter object into an xml string under the attribute settings settings_str = b'<All_settings title="All Settings" type="group">' settings_str += custom_tree.parameter_to_xml_string(params) settings_str += custom_tree.parameter_to_xml_string(self.settings) if hasattr(self.shortcut_manager, 'shortcut_params'): settings_str += custom_tree.parameter_to_xml_string( self.shortcut_manager.shortcut_params) settings_str += b'</All_settings>' attr.settings = settings_str elif type_info == 'scan_info': settings_str = b'<All_settings title="All Settings" type="group">' + \ custom_tree.parameter_to_xml_string(params) + \ custom_tree.parameter_to_xml_string(self.settings) + \ custom_tree.parameter_to_xml_string(self.h5saver.settings) + b'</All_settings>' attr.settings = settings_str def show_log(self): import webbrowser webbrowser.open(logging.getLoggerClass().root.handlers[0].baseFilename) def show_file(self): self.h5saver.flush() self.h5saver.show_file_content() def create_menu(self, menubar): menubar.clear() # %% create Settings menu self.file_menu = menubar.addMenu('File') self.file_menu.addAction('Show log file', self.show_log) self.file_menu.addAction('Show data file', self.show_file) self.file_menu.addSeparator() quit_action = self.file_menu.addAction('Quit') quit_action.triggered.connect(self.quit_fun) self.settings_menu = menubar.addMenu('Settings') docked_menu = self.settings_menu.addMenu('Docked windows') action_load = docked_menu.addAction('Load Layout') action_save = docked_menu.addAction('Save Layout') action_load.triggered.connect(self.load_layout_state) action_save.triggered.connect(self.save_layout_state) docked_menu.addSeparator() self.preset_menu = menubar.addMenu('Preset Shortcuts') action_new_preset = self.preset_menu.addAction('New preset') # action.triggered.connect(lambda: self.show_file_attributes(type_info='preset')) action_new_preset.triggered.connect(self.create_preset) action_modify_preset = self.preset_menu.addAction('Modify preset') action_modify_preset.triggered.connect(self.modify_shortcuts) self.preset_menu.addSeparator() load_preset = self.preset_menu.addMenu('Load presets') slots = dict([]) for ind_file, file in enumerate(os.listdir(shortcut_path)): if file.endswith(".xml"): (filesplited, ext) = os.path.splitext(file) slots[filesplited] = load_preset.addAction(filesplited) slots[filesplited].triggered.connect( self.create_menu_slot(os.path.join(shortcut_path, file))) def modify_shortcuts(self): try: path = select_file(start_path=shortcut_path, save=False, ext='xml') if path != '': self.shortcut_manager.set_file_preset(str(path)) mssg = QtWidgets.QMessageBox() mssg.setText( f'You have to restart the application to take the modifications into account! ' f'Quitting the application...') mssg.exec() self.quit_fun() else: # cancel pass except Exception as e: self.update_status(getLineInfo() + str(e)) def create_menu_slot(self, filename): return lambda: self.set_shortcut_mode(filename) def set_shortcut_mode(self, filename): #TODO: apply shortcuts to this widget tail, fileext = os.path.split(filename) file, ext = os.path.splitext(fileext) if ext == '.xml': self.shortcut_file = filename self.shortcut_manager.set_file_preset(filename, show=False) self.settings.child('loaded_files', 'shortcut_file').setValue(filename) self.author = self.shortcut_manager.shortcut_params.child( ('author')).value() self.dataset_attributes.child('dataset_info', 'author').setValue(self.author) self.scan_attributes.child('scan_info', 'author').setValue(self.author) path = os.path.join(layout_path, file + '.dock') if os.path.isfile(path): self.load_layout_state(path) #remove existing shorcuts while len(self.shortcuts): self.shortcuts.pop(0) for ind, shortcut in enumerate( self.shortcut_manager.shortcut_params.child( ('actions')).children()): stc = QtWidgets.QShortcut( QtGui.QKeySequence(shortcut.child(('shortcut')).value()), self.dockarea) self.settings.child(('shortcuts')).addChild({ 'title': f"Shortcut{ind:02d}: {shortcut.child(('action')).value()} {shortcut.child(('shortcut')).value()}:", 'name': f'shortcut{ind:02d}', 'type': 'led_push', 'value': True }) self.shortcuts.append(stc) self.activate_shortcut(stc, shortcut.child(('action')).value(), activate=True) def create_preset(self): try: self.shortcut_manager.set_new_preset() self.create_menu(self.menubar) except Exception as e: self.update_status(getLineInfo() + str(e)) def save_layout_state(self, file=None): """ Save the current layout state in the select_file obtained pathname file. Once done dump the pickle. See Also -------- utils.select_file """ try: dockstate = self.dockarea.saveState() if file is None: file = select_file(start_path=None, save=True, ext='dock') if file is not None: with open(str(file), 'wb') as f: pickle.dump(dockstate, f, pickle.HIGHEST_PROTOCOL) except: pass def save_layout_state_auto(self): if self.shortcut_file is not None: file = os.path.split(self.shortcut_file)[1] file = os.path.splitext(file)[0] path = os.path.join(layout_path, file + '.dock') self.save_layout_state(path) def load_layout_state(self, file=None): """ Load and restore a layout state from the select_file obtained pathname file. See Also -------- utils.select_file """ try: if file is None: file = select_file(save=False, ext='dock') if file is not None: with open(str(file), 'rb') as f: dockstate = pickle.load(f) self.dockarea.restoreState(dockstate) file = os.path.split(file)[1] self.settings.child('loaded_files', 'layout_file').setValue(file) except: pass def quit_fun(self): """ Quit the current instance of DAQ_scan and close on cascade move and detector modules. See Also -------- quit_fun """ try: areas = self.dockarea.tempAreas[:] for area in areas: area.win.close() QtWidgets.QApplication.processEvents() QThread.msleep(1000) QtWidgets.QApplication.processEvents() if hasattr(self, 'mainwindow'): self.mainwindow.close() except Exception as e: pass def update_status(self, txt): """ Show the txt message in the status bar with a delay of wait_time ms. =============== =========== ======================= **Parameters** **Type** **Description** *txt* string The message to show *wait_time* int the delay of showing *log_type* string the type of the log =============== =========== ======================= """ try: self.log_signal.emit(txt) logging.info(txt) except Exception as e: pass
class BatchManager: params = [{'title': 'Filename:', 'name': 'filename', 'type': 'str', 'value': 'batch_default'}, {'title': 'Scans', 'name': 'scans', 'type': 'group', 'children': []}] def __init__(self, msgbox=False, actuators=[], detectors=[], path=None): self.actuators = actuators self.detectors = detectors self.scans = OrderedDict([]) self.tree = ParameterTree() self.tree.setMinimumWidth(400) self.tree.setMaximumWidth(500) self.tree.setMinimumHeight(500) if path is None: path = batch_path else: assert isinstance(path, Path) self.batch_path = path self.settings = None if msgbox: msgBox = QtWidgets.QMessageBox() msgBox.setText("Scan Batch Manager?") msgBox.setInformativeText("What do you want to do?") cancel_button = msgBox.addButton(QtWidgets.QMessageBox.Cancel) new_button = msgBox.addButton("New", QtWidgets.QMessageBox.ActionRole) modify_button = msgBox.addButton('Modify', QtWidgets.QMessageBox.AcceptRole) msgBox.setDefaultButton(QtWidgets.QMessageBox.Cancel) ret = msgBox.exec() if msgBox.clickedButton() == new_button: self.set_new_batch() elif msgBox.clickedButton() == modify_button: self.set_file_batch() else: # cancel pass def get_act_dets(self): acts = dict([]) dets = dict([]) for name in self.scans: acts[name] = self.settings.child('scans', name, 'modules', 'actuators').value()['selected'] dets[name] = self.settings.child('scans', name, 'modules', 'detectors').value()['selected'] return acts, dets def set_file_batch(self, filename=None, show=True): """ """ if filename is None or filename == False: filename = pymodaq.daq_utils.gui_utils.file_io.select_file(start_path=self.batch_path, save=False, ext='xml') if filename == '': return status = False settings_tmp = Parameter.create(title='Batch', name='settings_tmp', type='group', children=ioxml.XML_file_to_parameter(str(filename))) children = settings_tmp.child('scans').children() self.settings = Parameter.create(title='Batch', name='settings', type='group', children=self.params) actuators = children[0].child('modules', 'actuators').value()['all_items'] if actuators != self.actuators: pymodaq.daq_utils.messenger.show_message('The loaded actuators from the batch file do not corresponds to the' ' dashboard actuators') return else: self.actuators = actuators detectors = children[0].child('modules', 'detectors').value()['all_items'] if detectors != self.detectors: pymodaq.daq_utils.messenger.show_message('The loaded detectors from the batch file do not corresponds to the' ' dashboard detectors') return else: self.detectors = detectors for child in children: self.add_scan(name=child.name(), title=child.opts['title']) self.settings.child('scans', child.name()).restoreState(child.saveState()) if show: status = self.show_tree() else: self.tree.setParameters(self.settings, showTop=False) return status def set_scans(self): infos = [] acts, dets = self.get_act_dets() for scan in self.scans: infos.append(f'{scan}: {acts[scan]} / {dets[scan]}') infos.append(f'{scan}: {self.scans[scan].set_scan()}') return infos def set_new_batch(self): self.settings = Parameter.create(title='Batch', name='settings', type='group', children=self.params) self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed) status = self.show_tree() return status def parameter_tree_changed(self, param, changes): """ Check for changes in the given (parameter,change,information) tuple list. In case of value changed, update the DAQscan_settings tree consequently. =============== ============================================ ============================== **Parameters** **Type** **Description** *param* instance of pyqtgraph parameter the parameter to be checked *changes* (parameter,change,information) tuple list the current changes state =============== ============================================ ============================== """ for param, change, data in changes: path = self.settings.childPath(param) if change == 'childAdded': pass elif change == 'value': pass elif change == 'parent': pass def show_tree(self): dialog = QtWidgets.QDialog() vlayout = QtWidgets.QVBoxLayout() add_scan = QtWidgets.QPushButton('Add Scan') add_scan.clicked.connect(self.add_scan) self.tree.setParameters(self.settings, showTop=False) vlayout.addWidget(add_scan) vlayout.addWidget(self.tree) dialog.setLayout(vlayout) buttonBox = QtWidgets.QDialogButtonBox(parent=dialog) buttonBox.addButton('Save', buttonBox.AcceptRole) buttonBox.accepted.connect(dialog.accept) buttonBox.addButton('Cancel', buttonBox.RejectRole) buttonBox.rejected.connect(dialog.reject) vlayout.addWidget(buttonBox) dialog.setWindowTitle('Fill in information about this Scan batch') res = dialog.exec() if res == dialog.Accepted: # save managers parameters in a xml file # start = os.path.split(os.path.split(os.path.realpath(__file__))[0])[0] # start = os.path.join("..",'daq_scan') ioxml.parameter_to_xml_file( self.settings, os.path.join(self.batch_path, self.settings.child('filename').value())) return res == dialog.Accepted def add_scan(self, name=None, title=None): if name is None or name is False: name_prefix = 'scan' child_indexes = [int(par.name()[len(name_prefix) + 1:]) for par in self.settings.child('scans').children()] if child_indexes == []: newindex = 0 else: newindex = max(child_indexes) + 1 name = f'{name_prefix}{newindex:02.0f}' title = f'Scan {newindex:02.0f}' child = {'title': title, 'name': name, 'type': 'group', 'removable': True, 'children': params} self.scans[name] = Scanner(actuators=[self.actuators[0]], adaptive_losses=adaptive_losses) self.settings.child('scans').addChild(child) self.settings.child('scans', name, 'modules', 'actuators').setValue(dict(all_items=self.actuators, selected=[self.actuators[0]])) self.settings.child('scans', name, 'modules', 'detectors').setValue(dict(all_items=self.detectors, selected=[self.detectors[0]])) self.settings.child('scans', name).addChild( self.scans[name].settings)
class ModulesManager(QObject): detectors_changed = Signal(list) actuators_changed = Signal(list) det_done_signal = Signal(OrderedDict) move_done_signal = Signal(OrderedDict) timeout_signal = Signal(bool) params = [ { 'title': 'Actuators/Detectors Selection', 'name': 'modules', 'type': 'group', 'children': [ { 'title': 'detectors', 'name': 'detectors', 'type': 'itemselect' }, { 'title': 'Actuators', 'name': 'actuators', 'type': 'itemselect' }, ] }, { 'title': "Moves done?", 'name': 'move_done', 'type': 'led', 'value': False }, { 'title': "Detections done?", 'name': 'det_done', 'type': 'led', 'value': False }, { 'title': 'Data dimensions', 'name': 'data_dimensions', 'type': 'group', 'children': [ { 'title': "Probe detector's data", 'name': 'probe_data', 'type': 'action' }, { 'title': 'Data0D list:', 'name': 'det_data_list0D', 'type': 'itemselect' }, { 'title': 'Data1D list:', 'name': 'det_data_list1D', 'type': 'itemselect' }, { 'title': 'Data2D list:', 'name': 'det_data_list2D', 'type': 'itemselect' }, { 'title': 'DataND list:', 'name': 'det_data_listND', 'type': 'itemselect' }, ] }, { 'title': 'Actuators positions', 'name': 'actuators_positions', 'type': 'group', 'children': [ { 'title': "Test actuators", 'name': 'test_actuator', 'type': 'action' }, { 'title': 'Positions:', 'name': 'positions_list', 'type': 'itemselect' }, ] }, ] def __init__(self, detectors=[], actuators=[], selected_detectors=[], selected_actuators=[], timeout=10000): super().__init__() for mod in selected_actuators: assert mod in actuators for mod in selected_detectors: assert mod in detectors self.timeout = timeout # in ms self.det_done_datas = OrderedDict() self.det_done_flag = False self.move_done_positions = OrderedDict() self.move_done_flag = False self.settings = Parameter.create(name='Settings', type='group', children=self.params) self.settings_tree = ParameterTree() self.settings_tree.setMinimumWidth(300) self.settings_tree.setParameters(self.settings, showTop=False) self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed) self.settings.child('data_dimensions', 'probe_data').sigActivated.connect( self.get_det_data_list) self.settings.child('actuators_positions', 'test_actuator').sigActivated.connect( self.test_move_actuators) self._detectors = [] self._actuators = [] self.grab_done_signals = [] self.det_commands_signal = [] self.actuators_connected = False self.detectors_connected = False self.set_actuators(actuators, selected_actuators) self.set_detectors(detectors, selected_detectors) @classmethod def get_names(cls, modules): if not hasattr(modules, '__iter__'): modules = [modules] return [mod.title for mod in modules] def get_mods_from_names(self, names, mod='det'): mods = [] for name in names: d = self.get_mod_from_name(name, mod) if d is not None: mods.append(d) return mods def get_mod_from_name(self, name, mod='det'): if mod == 'det': modules = self._detectors else: modules = self._actuators if name in self.get_names(modules): return modules[self.get_names(modules).index(name)] else: logger.warning(f'No detector with this name: {name}') return None def set_actuators(self, actuators, selected_actuators): self._actuators = actuators self.settings.child('modules', 'actuators').setValue( dict(all_items=self.get_names(actuators), selected=self.get_names(selected_actuators))) def set_detectors(self, detectors, selected_detectors): self._detectors = detectors self.settings.child('modules', 'detectors').setValue( dict(all_items=self.get_names(detectors), selected=self.get_names(selected_detectors))) @property def detectors(self): return self.get_mods_from_names(self.selected_detectors_name) @property def detectors_all(self): return self._detectors @property def actuators(self): return self.get_mods_from_names(self.selected_actuators_name, mod='act') @property def actuators_all(self): return self._actuators @property def Ndetectors(self): return len(self.detectors) @property def Nactuators(self): return len(self.actuators) @property def detectors_name(self): return self.settings.child('modules', 'detectors').value()['all_items'] @property def selected_detectors_name(self): return self.settings.child('modules', 'detectors').value()['selected'] @selected_detectors_name.setter def selected_detectors_name(self, detectors): if set(detectors).issubset(self.detectors_name): self.settings.child('modules', 'detectors').setValue( dict(all_items=self.detectors_name, selected=detectors)) @property def actuators_name(self): return self.settings.child('modules', 'actuators').value()['all_items'] @property def selected_actuators_name(self): return self.settings.child('modules', 'actuators').value()['selected'] @selected_actuators_name.setter def selected_actuators_name(self, actuators): if set(actuators).issubset(self.actuators_name): self.settings.child('modules', 'actuators').setValue( dict(all_items=self.actuators_name, selected=actuators)) def parameter_tree_changed(self, param, changes): """ Check for changes in the given (parameter,change,information) tuple list. In case of value changed, update the DAQscan_settings tree consequently. =============== ============================================ ============================== **Parameters** **Type** **Description** *param* instance of pyqtgraph parameter the parameter to be checked *changes* (parameter,change,information) tuple list the current changes state =============== ============================================ ============================== """ for param, change, data in changes: path = self.settings.childPath(param) if change == 'childAdded': pass elif change == 'value': if param.name() == 'detectors': self.detectors_changed.emit(data['selected']) elif param.name() == 'actuators': self.actuators_changed.emit(data['selected']) elif change == 'parent': pass def get_det_data_list(self): self.connect_detectors() datas = self.grab_datas() data_list0D = [] data_list1D = [] data_list2D = [] data_listND = [] for k in datas.keys(): if 'data0D' in datas[k].keys(): data_list0D.extend( [f'{k}/{name}' for name in datas[k]['data0D'].keys()]) if 'data1D' in datas[k].keys(): data_list1D.extend( [f'{k}/{name}' for name in datas[k]['data1D'].keys()]) if 'data2D' in datas[k].keys(): data_list2D.extend( [f'{k}/{name}' for name in datas[k]['data2D'].keys()]) if 'dataND' in datas[k].keys(): data_listND.extend( [f'{k}/{name}' for name in datas[k]['dataND'].keys()]) self.settings.child('data_dimensions', 'det_data_list0D').setValue( dict(all_items=data_list0D, selected=[])) self.settings.child('data_dimensions', 'det_data_list1D').setValue( dict(all_items=data_list1D, selected=[])) self.settings.child('data_dimensions', 'det_data_list2D').setValue( dict(all_items=data_list2D, selected=[])) self.settings.child('data_dimensions', 'det_data_listND').setValue( dict(all_items=data_listND, selected=[])) self.connect_detectors(False) def get_selected_probed_data(self, dim='0D'): return self.settings.child( 'data_dimensions', f'det_data_list{dim.upper()}').value()['selected'] def grab_datas(self, **kwargs): self.det_done_datas = OrderedDict() self.det_done_flag = False self.settings.child(('det_done')).setValue(self.det_done_flag) tzero = time.perf_counter() for sig in [mod.command_detector for mod in self.detectors]: sig.emit(utils.ThreadCommand("single", [1, kwargs])) while not self.det_done_flag: # wait for grab done signals to end QtWidgets.QApplication.processEvents() if time.perf_counter() - tzero > self.timeout: self.timeout_signal.emit(True) logger.error( 'Timeout Fired during waiting for data to be acquired') break self.det_done_signal.emit(self.det_done_datas) return self.det_done_datas def connect_actuators(self, connect=True, slot=None): if slot is None: slot = self.move_done if connect: for sig in [mod.move_done_signal for mod in self.actuators]: sig.connect(slot) else: try: for sig in [mod.move_done_signal for mod in self.actuators]: sig.disconnect(slot) except Exception as e: logger.error(str(e)) self.actuators_connected = connect def connect_detectors(self, connect=True, slot=None): """ Connect selected DAQ_Viewers's grab_done_signal to the given slot Parameters ---------- connect: (bool) if True, connect to the given slot (or default slot) if False, disconnect all detectors (not only the currently selected ones. This is made because when selected detectors changed if you only disconnect those ones, the previously connected ones will stay connected) slot: (method) A method that should be connected """ if slot is None: slot = self.det_done if connect: for sig in [mod.grab_done_signal for mod in self.detectors]: sig.connect(slot) else: for sig in [mod.grab_done_signal for mod in self.detectors_all]: try: sig.disconnect(slot) except TypeError as e: # means the slot was not previously connected logger.info(str(e)) self.detectors_connected = connect def test_move_actuators(self): positions = dict() for act in self.get_names(self.actuators): pos, done = QtWidgets.QInputDialog.getDouble( None, f'Enter a target position for actuator {act}', 'Position:') if not done: pos = 0. positions[act] = pos self.connect_actuators() positions = self.move_actuators(positions) self.settings.child('actuators_positions', 'positions_list').setValue( dict(all_items=[f'{k}: {positions[k]}' for k in positions], selected=[])) self.connect_actuators(False) def move_actuators(self, positions, mode='abs', polling=True): """will apply positions to each currently selected actuators. By Default the mode is absolute but can be Parameters ---------- positions: (list) the list of position to apply. Its length must be equal to the number of selected actutors mode: (str) either 'abs' for absolute positionning or 'rel' for relative poll: (bool) if True will wait for the selected actuators to reach their target positions (they have to be connected to a method checking for the position and letting the programm know the move is done (default connection is this object `move_done` method) Returns ------- (OrderedDict) with the selected actuators's name as key and current actuators's value as value See Also -------- move_done """ self.move_done_positions = OrderedDict() self.move_done_flag = False self.settings.child(('move_done')).setValue(self.move_done_flag) if mode == 'abs': command = 'move_Abs' elif mode == 'rel': command = 'move_Rel' else: logger.error(f'Invalid positioning mode: {mode}') return self.move_done_positions if not hasattr(positions, '__iter__'): positions = [positions] if len(positions) == self.Nactuators: if isinstance(positions, dict): for k in positions: act = self.get_mod_from_name(k, 'act') if act is not None: act.command_stage.emit( utils.ThreadCommand( command=command, attributes=[positions[k], polling])) else: for ind, act in enumerate(self.actuators): act.command_stage.emit( utils.ThreadCommand( command=command, attributes=[positions[ind], polling])) else: logger.error( 'Invalid number of positions compared to selected actuators') return self.move_done_positions tzero = time.perf_counter() if polling: while not self.move_done_flag: # polling move done QtWidgets.QApplication.processEvents() if time.perf_counter() - tzero > self.timeout: self.timeout_signal.emit(True) logger.error( 'Timeout Fired during waiting for data to be acquired') break QThread.msleep(20) self.move_done_signal.emit(self.move_done_positions) return self.move_done_positions def order_positions(self, positions_as_dict): actuators = self.selected_actuators_name pos = [] for act in actuators: pos.append(positions_as_dict[act]) return pos Slot(str, float) def move_done(self, name, position): """ """ try: if name not in list(self.move_done_positions.keys()): self.move_done_positions[name] = position if len(self.move_done_positions.items()) == len(self.actuators): self.move_done_flag = True self.settings.child('move_done').setValue(self.move_done_flag) except Exception as e: logger.exception(str(e)) @Slot(OrderedDict) def det_done(self, data): try: if data['name'] not in list(self.det_done_datas.keys()): self.det_done_datas[data['name']] = data if len(self.det_done_datas.items()) == len(self.detectors): self.det_done_flag = True self.settings.child(('det_done')).setValue(self.det_done_flag) except Exception as e: logger.exception(str(e))
class Spectrometer(QObject): """ Defines a Spectrometer object, unified interface for many spectrometers Parameters that could be set in the selected detector plugin (should be defined there): 'laser_wl' : value of the configured laser (could eventually be changed, case of Xplora, Labram...) 'spectro_center_freq': value of the configured grating center wavelength (could eventually be changed, case of Shamrock, Xplora...) """ #custom signal that will be fired sometimes. Could be connected to an external object method or an internal method log_signal = Signal(str) #list of dicts enabling the settings tree on the user interface params = [ { 'title': 'Configuration settings:', 'name': 'config_settings', 'type': 'group', 'children': [ { 'title': 'Laser wavelength (nm):', 'name': 'laser_wl', 'type': 'float', 'value': 515. }, { 'title': 'Laser wavelength (nm):', 'name': 'laser_wl_list', 'type': 'list', 'limits': [''] }, { 'title': 'Current Detector:', 'name': 'curr_det', 'type': 'str', 'value': '' }, { 'title': 'Show detector:', 'name': 'show_det', 'type': 'bool', 'value': False }, ], }, { 'title': 'Calibration settings:', 'name': 'calib_settings', 'type': 'group', 'children': [ { 'title': 'Use calibration:', 'name': 'use_calib', 'type': 'bool', 'value': False }, { 'title': 'Save calibration', 'name': 'save_calib', 'type': 'bool_push', 'value': False }, { 'title': 'Load calibration', 'name': 'load_calib', 'type': 'bool_push', 'value': False }, { 'title': 'Calibration coeffs:', 'name': 'calib_coeffs', 'type': 'group', 'children': [ { 'title': 'Center wavelength (nm):', 'name': 'center_calib', 'type': 'float', 'value': 515. }, { 'title': 'Slope (nm/pxl):', 'name': 'slope_calib', 'type': 'float', 'value': 1. }, { 'title': 'Second order :', 'name': 'second_calib', 'type': 'float', 'value': 0 }, { 'title': 'third:', 'name': 'third_calib', 'type': 'float', 'value': 0 }, ] }, { 'title': 'Perform calibration:', 'name': 'do_calib', 'type': 'bool', 'value': False }, ] }, { 'title': 'Acquisition settings:', 'name': 'acq_settings', 'type': 'group', 'children': [ { 'title': 'Spectro. Center:', 'name': 'spectro_center_freq', 'type': 'float', 'value': 800, }, { 'title': 'Spectro. Center:', 'name': 'spectro_center_freq_txt', 'type': 'str', 'value': '????', 'readonly': True }, { 'title': 'Units:', 'name': 'units', 'type': 'list', 'value': 'nm', 'limits': ['nm', 'cm-1', 'eV'] }, { 'title': 'Exposure (ms):', 'name': 'exposure_ms', 'type': 'float', 'value': 100, }, ] }, ] def __init__(self, parent): QLocale.setDefault(QLocale(QLocale.English, QLocale.UnitedStates)) super().__init__() if not isinstance(parent, DockArea): raise Exception('no valid parent container, expected a DockArea') self.wait_time = 2000 #ms self.offline = True self.dockarea = parent self.mainwindow = parent.parent() self.spectro_widget = QtWidgets.QWidget() self.data_dict = None """ List of the possible plugins that could be used with Spectrometer module type : dimensionality of the detector name: name of the plugin calib = True means there is a builtin calibration of the frequency axis movable : tells if the dispersion can be set (for instance by moving a grating) unit: valid only if calib is True. Unit of the calibration axis (x_axis of the detector), most often in nanometers. Possible values are 'nm', 'radfs' (rad/femtosecond), 'eV' laser: if False, laser cannot be changed by the program, do it manually laser_list: if laser is True, laser_list gives a list of selectable lasers """ self.current_det = None # will be after initialization self.laser_set_manual = True #init the object parameters self.detector = None self.save_file_pathname = None self._spectro_wl = 550 # center wavelngth of the spectrum self.viewer_freq_axis = utils.Axis(data=None, label='Photon energy', units='') self.raw_data = [] #init the user interface self.dashboard = self.set_dashboard() self.dashboard.preset_loaded_signal.connect( lambda: self.show_detector(False)) self.dashboard.preset_loaded_signal.connect(self.set_detector) self.dashboard.preset_loaded_signal.connect(self.initialized) self.set_GUI() self.dashboard.new_preset_created.connect( lambda: self.create_menu(self.menubar)) self.show_detector(False) self.dockarea.setEnabled(False) def set_dashboard(self): params = [ { 'title': 'Spectro Settings:', 'name': 'spectro_settings', 'type': 'group', 'children': [ { 'title': 'Is calibrated?', 'name': 'iscalibrated', 'type': 'bool', 'value': False, 'tooltip': 'Whether the selected plugin has internal frequency calibration or not.' }, { 'title': 'Movable?', 'name': 'ismovable', 'type': 'bool', 'value': False, 'tooltip': 'Whether the selected plugin has a functionality to change its central frequency: as a movable grating' ' for instance.' }, { 'title': 'Laser selectable?', 'name': 'laser_selectable', 'type': 'bool', 'value': False, 'tooltip': 'Whether the selected plugin has a functionality to change its excitation ray' }, { 'title': 'Laser ray:', 'name': 'laser_ray', 'type': 'list', 'value': '', 'show_pb': True, 'tooltip': 'List of settable laser rays (not manual ones)' }, ] }, ] dashboard = DashBoard(self.dockarea.addTempArea()) dashboard.set_preset_path(spectro_path) options = [ dict(path='saving_options', options_dict=dict(visible=False)), dict(path='use_pid', options_dict=dict(visible=False)), dict(path='Moves', options_dict=dict(visible=False)) ] dashboard.set_extra_preset_params(params, options) dashboard.dockarea.window().setVisible(False) return dashboard def set_GUI(self): ########################################### ########################################### #init the docks containing the main widgets ####################################################################################################################### #create a dock containing a viewer object, displaying the data for the spectrometer self.dock_viewer = Dock('Viewer dock', size=(350, 350)) self.dockarea.addDock(self.dock_viewer, 'left') target_widget = QtWidgets.QWidget() self.viewer = Viewer1D(target_widget) self.dock_viewer.addWidget(target_widget) ################################################################ #create a logger dock where to store info senf from the programm self.dock_logger = Dock("Logger") self.logger_list = QtWidgets.QListWidget() self.logger_list.setMinimumWidth(300) self.dock_logger.addWidget(self.logger_list) self.dockarea.addDock(self.dock_logger, 'right') self.log_signal[str].connect(self.add_log) ############################################ # creating a menubar self.menubar = self.mainwindow.menuBar() self.create_menu(self.menubar) #creating a toolbar self.toolbar = QtWidgets.QToolBar() self.create_toolbar() self.mainwindow.addToolBar(self.toolbar) #creating a status bar self.statusbar = QtWidgets.QStatusBar() self.statusbar.setMaximumHeight(25) self.status_laser = QtWidgets.QLabel('????') self.status_laser.setAlignment(Qt.AlignCenter) #self.status_laser.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) #self.status_laser.setReadOnly(True) self.status_laser.setMaximumWidth(80) self.status_laser.setMinimumWidth(80) self.status_laser.setToolTip('Current laser wavelength') self.status_laser.setStyleSheet("background-color: red") self.status_center = QtWidgets.QLabel('????') self.status_center.setAlignment(Qt.AlignCenter) #self.status_center.setReadOnly(True) #self.status_center.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) self.status_center.setMaximumWidth(80) self.status_center.setMinimumWidth(80) self.status_center.setToolTip( 'center frequency of the spectrum, either in nm or cm-1') self.status_center.setStyleSheet("background-color: red") self.status_init = QLED() self.status_init.setToolTip('Initialization state of the detector') self.status_init.set_as_false() self.status_init.clickable = False self.statusbar.addPermanentWidget(self.status_laser) self.statusbar.addPermanentWidget(self.status_center) self.statusbar.addPermanentWidget(self.status_init) self.dockarea.window().setStatusBar(self.statusbar) ############################################# self.settings = Parameter.create(name='settings', type='group', children=self.params) self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed) dock_config_settings = Dock('Configuration', size=(300, 350)) self.dockarea.addDock(dock_config_settings, 'above', self.dock_logger) # create main parameter tree self.config_settings_tree = ParameterTree() dock_config_settings.addWidget(self.config_settings_tree, 10) self.config_settings_tree.setMinimumWidth(300) self.config_settings_tree.setParameters(self.settings.child( ('config_settings')), showTop=False) #any change to the tree on the user interface will call the parameter_tree_changed method where all actions will be applied dock_calib_settings = Dock('Calibration', size=(300, 350)) self.dockarea.addDock(dock_calib_settings, 'above', self.dock_logger) # create main parameter tree self.calib_settings_tree = ParameterTree() dock_calib_settings.addWidget(self.calib_settings_tree, 10) self.calib_settings_tree.setMinimumWidth(300) self.calib_settings_tree.setParameters(self.settings.child( ('calib_settings')), showTop=False) #any change to the tree on the user interface will call the parameter_tree_changed method where all actions will be applied #this one for the custom application settings dock_acq_settings = Dock('Acquisition', size=(300, 350)) self.dockarea.addDock(dock_acq_settings, 'above', dock_config_settings) # create main parameter tree self.acq_settings_tree = ParameterTree() dock_acq_settings.addWidget(self.acq_settings_tree, 10) self.acq_settings_tree.setMinimumWidth(300) self.acq_settings_tree.setParameters(self.settings.child( ('acq_settings')), showTop=False) @Slot(ThreadCommand) def cmd_from_det(self, status): try: if status.command == 'spectro_wl': self.status_center.setStyleSheet("background-color: green") self.spectro_wl_is(status.attributes[0]) elif status.command == 'laser_wl': #self.laser_set_manual = False self.settings.child('config_settings', 'laser_wl_list').setValue( status.attributes[0]) self.status_laser.setText('{:}nm'.format(status.attributes[0])) self.status_laser.setStyleSheet("background-color: green") self.update_center_frequency(self.spectro_wl) elif status.command == 'exposure_ms': self.settings.child('acq_settings', 'exposure_ms').setValue( status.attributes[0]) elif status.command == "x_axis": x_axis = status.attributes[0] if np.any(x_axis['data'] != self.viewer_freq_axis['data'] ) and self.current_det['calib']: self.viewer_freq_axis.update(x_axis) self.update_axis() except Exception as e: logger.exception(str(e)) def update_status(self, txt, wait_time=1000, log_type=None): """ """ self.statusbar.showMessage(txt, wait_time) if log_type is not None: self.log_signal.emit(txt) def set_detector(self): self.detector = self.dashboard.detector_modules[0] self.settings.child('config_settings', 'curr_det').setValue( f"{self.detector.settings.child('main_settings','DAQ_type').value()} / " f"{self.detector.settings.child('main_settings','detector_type').value()} / {self.detector.title}" ) self.detector.custom_sig[ThreadCommand].connect(self.cmd_from_det) self.current_det = \ dict(laser=self.dashboard.preset_manager.preset_params.child('spectro_settings', 'laser_selectable').value(), laser_list=self.dashboard.preset_manager.preset_params.child('spectro_settings', 'laser_ray').opts['limits'], movable=self.dashboard.preset_manager.preset_params.child('spectro_settings', 'ismovable').value(), calib=self.dashboard.preset_manager.preset_params.child('spectro_settings', 'iscalibrated').value(), ) self.detector.grab_done_signal.connect(self.show_data) self.settings.sigTreeStateChanged.disconnect( self.parameter_tree_changed) if self.current_det['laser']: self.settings.child('config_settings', 'laser_wl_list').show() self.settings.child('config_settings', 'laser_wl').hide() self.settings.child( 'config_settings', 'laser_wl_list').setOpts(limits=self.current_det['laser_list']) else: self.settings.child('config_settings', 'laser_wl').show() self.settings.child('config_settings', 'laser_wl_list').hide() self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed) #apply current detector particularities #self.settings.child('acq_settings', 'spectro_center_freq').setOpts(readonly=not self.current_det['movable']) self.get_spectro_wl() QtWidgets.QApplication.processEvents() self.get_laser_wl() QtWidgets.QApplication.processEvents() self.get_exposure_ms() QtWidgets.QApplication.processEvents() def get_exposure_ms(self): self.detector.command_detector.emit(ThreadCommand('get_exposure_ms')) def set_exposure_ms(self, data): self.detector.command_detector.emit( ThreadCommand('set_exposure_ms', [data])) @Slot(bool) def initialized(self, state, offline=False): self.offline = offline self.grab_action.setEnabled(state) self.snap_action.setEnabled(state) if state or offline: self.status_init.set_as_true() self.dockarea.setEnabled(True) else: self.status_init.set_as_false() def update_center_frequency(self, spectro_wl): self._spectro_wl = spectro_wl if self.settings.child('acq_settings', 'units').value() == 'nm': self.settings.child('acq_settings', 'spectro_center_freq').setValue(spectro_wl) elif self.settings.child('acq_settings', 'units').value() == 'cm-1': self.settings.child('acq_settings', 'spectro_center_freq').setValue( Enm2cmrel( spectro_wl, self.settings.child( 'config_settings', 'laser_wl').value())) elif self.settings.child('acq_settings', 'units').value() == 'eV': self.settings.child('acq_settings', 'spectro_center_freq').setValue( nm2eV(spectro_wl)) self.set_status_center( self.settings.child('acq_settings', 'spectro_center_freq').value(), self.settings.child('acq_settings', 'units').value()) def set_status_center(self, val, unit, precision=3): self.status_center.setText(f'{val:.{precision}f} {unit}') def spectro_wl_is(self, spectro_wl): """ this slot receives a signal from the detector telling it what's the current spectro_wl Parameters ---------- spectro_wl """ self._spectro_wl = spectro_wl self.update_center_frequency(spectro_wl) def set_spectro_wl(self, spectro_wl): try: if self.current_det['movable']: self.detector.command_detector.emit( ThreadCommand('set_spectro_wl', [spectro_wl])) except Exception as e: logger.exception(str(e)) def get_spectro_wl(self): if self.current_det['calib']: self.settings.child('acq_settings', 'spectro_center_freq').show() self.settings.child('acq_settings', 'spectro_center_freq_txt').hide() self.detector.command_detector.emit( ThreadCommand('get_spectro_wl')) self.detector.command_detector.emit(ThreadCommand('get_axis')) else: self.settings.child('acq_settings', 'spectro_center_freq').hide() self.settings.child('acq_settings', 'spectro_center_freq_txt').show() self.viewer_freq_axis['units'] = 'Pxls' def get_laser_wl(self): if self.current_det['laser']: self.detector.command_detector.emit(ThreadCommand('get_laser_wl')) else: self.settings.child('config_settings', 'laser_wl').setValue(0) @property def spectro_wl(self): # try to get the param value from detector (if it has been added in the plugin) return self._spectro_wl @spectro_wl.setter def spectro_wl(self, spec_wl): # try to get the param value from detector (if it has been added in the plugin) self.set_spectro_wl(spec_wl) def show_detector(self, show=True): self.dashboard.mainwindow.setVisible(show) for area in self.dashboard.dockarea.tempAreas: area.window().setVisible(show) def parameter_tree_changed(self, param, changes): for param, change, data in changes: path = self.settings.childPath(param) if path is not None: childName = '.'.join(path) else: childName = param.name() if change == 'childAdded': pass elif change == 'value': if param.name() == 'show_det': self.show_detector(data) elif param.name() == 'spectro_center_freq': unit = self.settings.child('acq_settings', 'units').value() if unit == 'nm': center_wavelength = data elif unit == 'cm-1': center_wavelength = Ecmrel2Enm( data, self.settings.child('config_settings', 'laser_wl').value()) elif unit == 'eV': center_wavelength = eV2nm(data) if int(self.spectro_wl * 100) != int( 100 * center_wavelength): #comprison at 1e-2 self.spectro_wl = center_wavelength self.update_axis() elif param.name() == 'units': if self.settings.child( 'acq_settings', 'spectro_center_freq').value() > 0.000000001: if data == 'nm': self.settings.child( 'acq_settings', 'spectro_center_freq').setValue( self._spectro_wl) elif data == 'cm-1': self.settings.child( 'acq_settings', 'spectro_center_freq').setValue( Enm2cmrel( self._spectro_wl, self.settings.child( 'config_settings', 'laser_wl').value())) elif data == 'eV': self.settings.child( 'acq_settings', 'spectro_center_freq').setValue( nm2eV(self._spectro_wl)) self.set_status_center( self.settings.child('acq_settings', 'spectro_center_freq').value(), self.settings.child('acq_settings', 'units').value()) elif param.name() == 'laser_wl_list': if data is not None: self.move_laser_wavelength(data) elif param.name() == 'laser_wl': if data is not None: self.move_laser_wavelength(data) if int(data) == 0: self.settings.child('acq_settings', 'units').setValue('nm') self.settings.child('acq_settings', 'units').setOpts(readonly=True) else: self.settings.child( 'acq_settings', 'units').setOpts(readonly=False) if data != 0: self.set_manual_laser_wl(data) elif param.name() == 'exposure_ms': self.set_exposure_ms(data) elif param.name() == 'do_calib': if len(self.raw_data) != 0: if data: self.calib_dock = Dock('Calibration module') self.dockarea.addDock(self.calib_dock) self.calibration = Calibration(self.dockarea) self.calib_dock.addWidget(self.calibration) self.calibration.coeffs_calib.connect( self.update_calibration) else: self.calib_dock.close() elif param.name() == 'save_calib': filename = select_file(start_path=self.save_file_pathname, save=True, ext='xml') if filename != '': custom_tree.parameter_to_xml_file( self.settings.child('calib_settings', 'calib_coeffs'), filename) elif param.name() == 'load_calib': filename = select_file(start_path=self.save_file_pathname, save=False, ext='xml') if filename != '': children = custom_tree.XML_file_to_parameter(filename) self.settings.child( 'calib_settings', 'calib_coeffs').restoreState( Parameter.create( title='Calibration coeffs:', name='calib_coeffs', type='group', children=children).saveState()) elif param.name() in custom_tree.iter_children(self.settings.child('calib_settings', 'calib_coeffs')) \ or param.name() == 'use_calib': if self.settings.child('calib_settings', 'use_calib').value(): calib_coeffs = [ self.settings.child('calib_settings', 'calib_coeffs', 'third_calib').value(), self.settings.child('calib_settings', 'calib_coeffs', 'second_calib').value(), self.settings.child('calib_settings', 'calib_coeffs', 'slope_calib').value(), self.settings.child('calib_settings', 'calib_coeffs', 'center_calib').value() ] self.update_center_frequency( self.settings.child('calib_settings', 'calib_coeffs', 'center_calib').value()) self.settings.child('acq_settings', 'spectro_center_freq').show() self.settings.child( 'acq_settings', 'spectro_center_freq').setOpts(readonly=True) self.status_center.setStyleSheet( "background-color: green") self.settings.child('acq_settings', 'spectro_center_freq_txt').hide() x_axis_pxls = np.linspace(0, self.raw_data[0].size - 1, self.raw_data[0].size) self.viewer_freq_axis['data'] = np.polyval( calib_coeffs, x_axis_pxls - np.max(x_axis_pxls) / 2) self.update_axis() else: self.settings.child('acq_settings', 'spectro_center_freq').hide() self.settings.child('acq_settings', 'spectro_center_freq_txt').show() self.status_center.setStyleSheet( "background-color: red") elif change == 'parent': pass @Slot(list) def update_calibration(self, coeffs): self.settings.child('calib_settings', 'calib_coeffs', 'center_calib').setValue(coeffs[0]) self.settings.child('calib_settings', 'calib_coeffs', 'slope_calib').setValue(coeffs[1]) if len(coeffs) > 2: self.settings.child('calib_settings', 'calib_coeffs', 'second_calib').setValue(coeffs[2]) else: self.settings.child('calib_settings', 'calib_coeffs', 'second_calib').setValue(0) if len(coeffs) > 3: self.settings.child('calib_settings', 'calib_coeffs', 'third_calib').setValue(coeffs[3]) else: self.settings.child('calib_settings', 'calib_coeffs', 'third_calib').setValue(0) def set_manual_laser_wl(self, laser_wl): messg = QtWidgets.QMessageBox() messg.setText( 'You manually changed the laser wavelength to {:}nm!'.format( laser_wl)) messg.setInformativeText("Is that correct?") messg.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) ret = messg.exec() if ret == QtWidgets.QMessageBox.Yes: self.status_laser.setText('{:}nm'.format(laser_wl)) self.status_laser.setStyleSheet("background-color: green") self.settings.child('acq_settings', 'units').setOpts(readonly=False) def move_laser_wavelength(self, laser_wavelength): #do hardware stuff if possible (Mock, labspec...) try: if self.current_det['laser']: self.detector.command_detector.emit( ThreadCommand('set_laser_wl', [laser_wavelength])) except Exception as e: logger.exception(str(e)) @Slot(OrderedDict) def show_data(self, data): """ do stuff with data from the detector if its grab_done_signal has been connected Parameters ---------- data: (OrderedDict) #OrderedDict(name=self.title,x_axis=None,y_axis=None,z_axis=None,data0D=None,data1D=None,data2D=None) """ self.data_dict = data if 'data1D' in data: self.raw_data = [] for key in data['data1D']: self.raw_data.append(data['data1D'][key]['data']) if 'x_axis' in data['data1D'][key]: x_axis = data['data1D'][key]['x_axis'] else: x_axis = utils.Axis(data=np.linspace( 0, len(data['data1D'][key]['data']) - 1, len(data['data1D'][key]['data'])), units='pxls', label='') if self.viewer_freq_axis['data'] is None: self.viewer_freq_axis.update(x_axis) elif np.any(x_axis['data'] != self.viewer_freq_axis['data'] ) and self.current_det['calib']: self.viewer_freq_axis.update(x_axis) self.viewer.show_data(self.raw_data) self.update_axis() def update_axis(self): axis = utils.Axis() unit = self.settings.child('acq_settings', 'units').value() if unit == 'nm': axis['data'] = self.viewer_freq_axis['data'] elif unit == 'cm-1': axis['data'] = Enm2cmrel( self.viewer_freq_axis['data'], self.settings.child('config_settings', 'laser_wl').value()) elif unit == 'eV': axis['data'] = nm2eV(self.viewer_freq_axis['data']) axis['units'] = unit axis['label'] = 'Photon energy' self.viewer.x_axis = axis def create_menu(self, menubar): """ """ menubar.clear() # %% create file menu file_menu = menubar.addMenu('File') load_action = file_menu.addAction('Load file') load_action.triggered.connect(self.load_file) save_action = file_menu.addAction('Save file') save_action.triggered.connect(self.save_data) export_action = file_menu.addAction('Export as ascii') export_action.triggered.connect(lambda: self.save_data(export=True)) file_menu.addSeparator() file_menu.addAction('Show log file', self.show_log) file_menu.addSeparator() quit_action = file_menu.addAction('Quit') quit_action.triggered.connect(self.quit_function) settings_menu = menubar.addMenu('Settings') settings_menu.addAction('Show Units Converter', self.show_units_converter) docked_menu = settings_menu.addMenu('Docked windows') docked_menu.addAction('Load Layout', self.load_layout_state) docked_menu.addAction('Save Layout', self.save_layout_state) self.preset_menu = menubar.addMenu(self.dashboard.preset_menu) self.preset_menu.menu().addSeparator() self.preset_menu.menu().addAction( 'Offline Mode', lambda: self.initialized(state=False, offline=True)) def load_layout_state(self, file=None): """ Load and restore a layout state from the select_file obtained pathname file. See Also -------- utils.select_file """ try: if file is None: file = select_file(save=False, ext='dock') if file is not None: with open(str(file), 'rb') as f: dockstate = pickle.load(f) self.dockarea.restoreState(dockstate) file = file.name self.settings.child('loaded_files', 'layout_file').setValue(file) except Exception as e: logger.exception(str(e)) def save_layout_state(self, file=None): """ Save the current layout state in the select_file obtained pathname file. Once done dump the pickle. See Also -------- utils.select_file """ try: dockstate = self.dockarea.saveState() if 'float' in dockstate: dockstate['float'] = [] if file is None: file = select_file(start_path=None, save=True, ext='dock') if file is not None: with open(str(file), 'wb') as f: pickle.dump(dockstate, f, pickle.HIGHEST_PROTOCOL) except Exception as e: logger.exception(str(e)) def show_log(self): import webbrowser webbrowser.open(logging.getLogger('pymodaq').handlers[0].baseFilename) def show_units_converter(self): self.units_converter = UnitsConverter() dock_converter = Dock('Units Converter', size=(300, 350)) self.dockarea.addDock(dock_converter, 'bottom', self.dock_logger) dock_converter.addWidget(self.units_converter.parent) def load_file(self): data, fname, node_path = browse_data(ret_all=True) if data is not None: h5utils = H5BrowserUtil() h5utils.open_file(fname) data, axes, nav_axes, is_spread = h5utils.get_h5_data(node_path) data_node = h5utils.get_node(node_path) if data_node.attrs['type'] == 'data': if data_node.attrs['data_dimension'] == '1D': data_dict = OrderedDict(data1D=dict( raw=dict(data=data, x_axis=axes['x_axis']))) self.show_data(data_dict) h5utils.close_file() def quit_function(self): #close all stuff that need to be if self.detector is not None: self.detector.quit_fun() QtWidgets.QApplication.processEvents() self.mainwindow.close() def create_toolbar(self): self.toolbar.addWidget(QtWidgets.QLabel('Acquisition:')) iconquit = QtGui.QIcon() iconquit.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/close2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.quit_action = QtWidgets.QAction(iconquit, "Quit program", None) self.toolbar.addAction(self.quit_action) self.quit_action.triggered.connect(self.quit_function) iconload = QtGui.QIcon() iconload.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/Open.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.loadaction = QtWidgets.QAction( iconload, "Load target file (.h5, .png, .jpg) or data from camera", None) self.toolbar.addAction(self.loadaction) self.loadaction.triggered.connect(self.load_file) iconsave = QtGui.QIcon() iconsave.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/SaveAs.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.saveaction = QtWidgets.QAction(iconsave, "Save current data", None) self.toolbar.addAction(self.saveaction) self.saveaction.triggered.connect(self.save_data) iconrun = QtGui.QIcon() iconrun.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/run2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.grab_action = QtWidgets.QAction(iconrun, 'Grab', None) self.grab_action.setCheckable(True) self.toolbar.addAction(self.grab_action) self.grab_action.triggered.connect(self.grab_detector) iconsnap = QtGui.QIcon() iconsnap.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/snap.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.snap_action = QtWidgets.QAction(iconsnap, 'Snap', None) self.snap_action.triggered.connect(self.snap_detector) self.toolbar.addAction(self.snap_action) self.grab_action.setEnabled(False) self.snap_action.setEnabled(False) def grab_detector(self): self.detector.ui.grab_pb.click() def snap_detector(self): self.detector.ui.single_pb.click() def save_data(self, export=False): try: if export: ext = 'dat' else: ext = 'h5' path = select_file(start_path=self.save_file_pathname, save=True, ext=ext) if not (not (path)): if not export: h5saver = H5Saver(save_type='detector') h5saver.init_file(update_h5=True, custom_naming=False, addhoc_file_path=path) settings_str = b'<All_settings>' + custom_tree.parameter_to_xml_string( self.settings) if self.detector is not None: settings_str += custom_tree.parameter_to_xml_string( self.detector.settings) if hasattr(self.detector.ui.viewers[0], 'roi_manager'): settings_str += custom_tree.parameter_to_xml_string( self.detector.ui.viewers[0].roi_manager. settings) settings_str += custom_tree.parameter_to_xml_string( h5saver.settings) settings_str += b'</All_settings>' det_group = h5saver.add_det_group(h5saver.raw_group, "Data", settings_str) try: self.channel_arrays = OrderedDict([]) data_dim = 'data1D' if not h5saver.is_node_in_group(det_group, data_dim): self.channel_arrays['data1D'] = OrderedDict([]) data_group = h5saver.add_data_group( det_group, data_dim) for ind_channel, data in enumerate( self.raw_data): # list of numpy arrays channel = f'CH{ind_channel:03d}' channel_group = h5saver.add_CH_group( data_group, title=channel) self.channel_arrays[data_dim][ 'parent'] = channel_group self.channel_arrays[data_dim][ channel] = h5saver.add_data( channel_group, dict(data=data, x_axis=self.viewer_freq_axis), scan_type='', enlargeable=False) h5saver.close_file() except Exception as e: logger.exception(str(e)) else: data_to_save = [self.viewer_freq_axis['data']] data_to_save.extend([dat for dat in self.raw_data]) np.savetxt(path, data_to_save, delimiter='\t') except Exception as e: logger.exception(str(e)) @Slot(str) def add_log(self, txt): """ Add a log to the logger list from the given text log and the current time ================ ========= ====================== **Parameters** **Type** **Description** *txt* string the log to be added ================ ========= ====================== """ now = datetime.datetime.now() new_item = QtWidgets.QListWidgetItem(str(now) + ": " + txt) self.logger_list.addItem(new_item) ##to do ##self.save_parameters.logger_array.append(str(now)+": "+txt) @Slot(str) def emit_log(self, txt): """ Emit a log-signal from the given log index =============== ======== ======================= **Parameters** **Type** **Description** *txt* string the log to be emitted =============== ======== ======================= """ self.log_signal.emit(txt)
class DAQ_Logger(QObject): """ Main class initializing a DAQ_Scan module with its dashboard and scanning control panel """ command_DAQ_signal = pyqtSignal(list) status_signal = pyqtSignal(str) params = [ {'title': 'Log Type:', 'name': 'log_type', 'type': 'str', 'value': '', 'readonly': True}, ] def __init__(self, dockarea=None, dashboard=None): """ Parameters ---------- dockarea: (dockarea) instance of the modified pyqtgraph Dockarea (see daq_utils) dashboard: (DashBoard) instance of the pymodaq dashboard """ QLocale.setDefault(QLocale(QLocale.English, QLocale.UnitedStates)) super().__init__() self.dockarea = dockarea self.dashboard = dashboard if dashboard is None: raise Exception('No valid dashboard initialized') self.mainwindow = self.dockarea.parent() self.wait_time = 1000 self.logger_thread = None self.detector_modules = self.dashboard.detector_modules self.det_modules_log = [] self.log_types = ['None', 'H5 File'] if is_sql: self.log_types.append('SQL DataBase') self.logger = None # should be a reference either to self.h5saver or self.dblogger depending the choice of the user self.h5saver = H5Saver(save_type='logger') if is_sql: self.dblogger = DbLoggerGUI(self.dashboard.preset_file.stem) else: self.dblogger = None self.modules_manager = ModulesManager() self.setupUI() self.setup_modules(self.dashboard.title) self.h5saver.settings_tree.setVisible(False) if is_sql: self.dblogger.settings_tree.setVisible(False) def create_menu(self): """ """ # %% create Settings menu menubar = QtWidgets.QMenuBar() menubar.setMaximumHeight(30) self.ui.layout.insertWidget(0, menubar) self.file_menu = menubar.addMenu('File') def quit_fun(self): """ Quit the current instance of DAQ_scan and close on cascade move and detector modules. See Also -------- quit_fun """ try: self.h5saver.close_file() except Exception as e: logger.exception(str(e)) try: self.dblogger.close() except Exception as e: logger.exception(str(e)) self.ui.logger_dock.close() def parameter_tree_changed(self, param, changes): """ Check for changes in the given (parameter,change,information) tuple list. In case of value changed, update the DAQscan_settings tree consequently. =============== ============================================ ============================== **Parameters** **Type** **Description** *param* instance of pyqtgraph parameter the parameter to be checked *changes* (parameter,change,information) tuple list the current changes state =============== ============================================ ============================== """ for param, change, data in changes: path = self.settings.childPath(param) if path is not None: childName = '.'.join(path) else: childName = param.name() if change == 'childAdded': pass elif change == 'value': if param.name() == 'log_type': if param.value() == 'H5 File': self.logger = self.h5saver elif param.value() == 'SQL DataBase': self.logger = self.dblogger self.h5saver.settings_tree.setVisible(param.value() == 'H5 File') if self.dblogger is not None: self.dblogger.settings_tree.setVisible(param.value() == 'SQL DataBase') elif change == 'parent': pass def setup_modules(self, filename): """ """ try: ###################################################################### # setting moves and det in tree preset_items_det = [] items_det = [module.title for module in self.detector_modules] if items_det != []: preset_items_det = items_det self.modules_manager.set_detectors(self.detector_modules, preset_items_det) except Exception as e: logger.exception(str(e)) def set_continuous_save(self): """ Set a continous save file using the base path located file with a header-name containing date as a string. See Also -------- daq_utils.set_current_scan_path """ self.do_continuous_save = True self.logger.settings.child(('N_saved')).show() self.logger.settings.child(('N_saved')).setValue(0) settings_str = b'<All_settings>' settings_str += pymodaq.daq_utils.parameter.ioxml.parameter_to_xml_string(self.dashboard.settings) settings_str += pymodaq.daq_utils.parameter.ioxml.parameter_to_xml_string( self.dashboard.preset_manager.preset_params) if self.dashboard.settings.child('loaded_files', 'overshoot_file').value() != '': settings_str += pymodaq.daq_utils.parameter.ioxml.parameter_to_xml_string( self.dashboard.overshoot_manager.overshoot_params) if self.dashboard.settings.child('loaded_files', 'roi_file').value() != '': settings_str += pymodaq.daq_utils.parameter.ioxml.parameter_to_xml_string( self.dashboard.roi_saver.roi_presets) settings_str += pymodaq.daq_utils.parameter.ioxml.parameter_to_xml_string(self.settings) settings_str += pymodaq.daq_utils.parameter.ioxml.parameter_to_xml_string(self.logger.settings) settings_str += b'</All_settings>' if self.settings.child(('log_type')).value() == 'H5 File': self.logger.settings.child(('base_name')).setValue('DataLogging') self.h5saver.init_file(update_h5=True, metadata=dict(settings=settings_str)) logger.addHandler(H5LogHandler(self.h5saver)) self.h5saver.h5_file.flush() elif self.settings.child(('log_type')).value() == 'SQL DataBase': if not self.logger.settings.child('connected_db').value(): status = self.logger.connect_db() if not status: logger.critical('the Database is not and cannot be connnect') self.update_status('the Database is not and cannot be connnect') return False self.logger.add_config(settings_str) logger.addHandler(DBLogHandler(self.logger)) return True def set_logging(self): """ """ status = self.set_continuous_save() if status: det_modules_log = self.modules_manager.detectors if det_modules_log != []: # check if the modules are initialized for module in det_modules_log: if not module.initialized_state: logger.error(f'module {module.title} is not initialized') return False # create the detectors in the chosen logger for det in det_modules_log: settings_str = b'<All_settings>' settings_str += pymodaq.daq_utils.parameter.ioxml.parameter_to_xml_string(det.settings) for viewer in det.ui.viewers: if hasattr(viewer, 'roi_manager'): settings_str += pymodaq.daq_utils.parameter.ioxml.parameter_to_xml_string( viewer.roi_manager.settings) settings_str += b'</All_settings>' if self.settings.child(('log_type')).value() == 'H5 File': if det.title not in self.h5saver.raw_group.children_name(): det_group = self.h5saver.add_det_group(self.h5saver.raw_group, det.title, settings_str) self.h5saver.add_navigation_axis(np.array([0.0, ]), det_group, 'time_axis', enlargeable=True, title='Time axis', metadata=dict(label='Time axis', units='timestamp', nav_index=0)) elif self.settings.child(('log_type')).value() == 'SQL DataBase': self.logger.add_detectors([dict(name=det.title, xml_settings=settings_str)]) self.ui.start_button.setEnabled(True) self.ui.stop_button.setEnabled(True) return True else: self.update_status('Cannot start logging... No detectors selected') self.ui.start_button.setEnabled(False) self.ui.stop_button.setEnabled(True) return False else: self.update_status('Cannot start logging... check connections') self.ui.start_button.setEnabled(False) self.ui.stop_button.setEnabled(True) return False def show_log(self): import webbrowser webbrowser.open(logger.handlers[0].baseFilename) def setupUI(self): self.ui = QObject() widget_settings = QtWidgets.QWidget() self.ui.layout = QtWidgets.QVBoxLayout() widget_settings.setLayout(self.ui.layout) widget_buttons = QtWidgets.QWidget() layout_buttons = QtWidgets.QHBoxLayout() widget_buttons.setLayout(layout_buttons) self.ui.layout.addWidget(widget_buttons) iconquit = QtGui.QIcon() iconquit.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/close2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.ui.quit_button = QtWidgets.QPushButton(iconquit, 'Quit') iconstart = QtGui.QIcon() iconstart.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/run2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.ui.start_button = QtWidgets.QPushButton(iconstart, '') self.ui.start_button.setToolTip('Start logging into h5file or database') iconstop = QtGui.QIcon() iconstop.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/stop.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.ui.stop_button = QtWidgets.QPushButton(iconstop, '') self.ui.stop_button.setToolTip('Stop/pause logging') log_type_combo = QtWidgets.QComboBox() log_type_combo.addItems(self.log_types) log_type_combo.currentTextChanged.connect(self.set_log_type) iconstartall = QtGui.QIcon() iconstartall.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/run_all.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.ui.startall_button = QtWidgets.QPushButton(iconstartall, '') self.ui.startall_button.setToolTip('Grab all selected detectors') layout_buttons.addWidget(self.ui.quit_button) layout_buttons.addStretch() layout_buttons.addWidget(self.ui.startall_button) layout_buttons.addWidget(log_type_combo) layout_buttons.addWidget(self.ui.start_button) layout_buttons.addWidget(self.ui.stop_button) # %% create logger dock and make it a floating window self.ui.logger_dock = Dock("Logger", size=(1, 1), autoOrientation=False) # give this dock the minimum possible size self.ui.logger_dock.setOrientation('vertical') self.ui.logger_dock.addWidget(widget_settings) self.dockarea.addDock(self.ui.logger_dock, 'left') self.ui.logger_dock.float() widget_hor = QtWidgets.QWidget() layout_hor = QtWidgets.QHBoxLayout() main_sett_widget = QtWidgets.QWidget() main_sett_widget.setLayout(QtWidgets.QVBoxLayout()) widget_hor.setLayout(layout_hor) self.ui.layout.addWidget(widget_hor) self.settings_tree = ParameterTree() self.settings_tree.setMinimumWidth(300) layout_hor.addWidget(main_sett_widget) main_sett_widget.layout().addWidget(self.settings_tree) main_sett_widget.layout().addWidget(self.modules_manager.settings_tree) self.modules_manager.settings.child('modules', 'actuators').hide() self.modules_manager.settings.child(('data_dimensions')).hide() self.modules_manager.settings.child(('actuators_positions')).hide() self.h5saver.settings_tree.setMinimumWidth(300) layout_hor.addWidget(self.h5saver.settings_tree) if is_sql: self.dblogger.settings_tree.setMinimumWidth(300) layout_hor.addWidget(self.dblogger.settings_tree) self.settings = Parameter.create(name='Settings', type='group', children=self.params) self.settings_tree.setParameters(self.settings, showTop=False) self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed) # %% init and set the status bar self.ui.statusbar = QtWidgets.QStatusBar(self.dockarea) self.ui.statusbar.setMaximumHeight(25) self.ui.layout.addWidget(self.ui.statusbar) self.ui.log_message = QtWidgets.QLabel('Initializing') self.ui.statusbar.addPermanentWidget(self.ui.log_message) self.ui.start_log_time = QtWidgets.QDateTimeEdit() self.ui.start_log_time.setReadOnly(True) self.ui.start_log_time.setToolTip('Logging started at:') self.ui.logging_state = QLED() self.ui.logging_state.setToolTip('logging status: green (running), red (idle)') self.ui.logging_state.clickable = False self.ui.statusbar.addPermanentWidget(self.ui.start_log_time) self.ui.statusbar.addPermanentWidget(self.ui.logging_state) self.ui.layout.addWidget(self.ui.statusbar) # connecting self.status_signal[str].connect(self.dashboard.add_status) self.ui.quit_button.clicked.connect(self.quit_fun) self.ui.start_button.clicked.connect(self.start_logging) self.ui.stop_button.clicked.connect(self.stop_logging) self.ui.startall_button.clicked.connect(self.start_all) self.create_menu() def start_all(self): preset_items_det = self.modules_manager.detectors for det in preset_items_det: det.ui.grab_pb.click() def set_log_type(self, log_type): self.settings.child(('log_type')).setValue(log_type) def show_file_attributes(self, type_info='dataset'): """ Switch the type_info value. In case of : * *scan* : Set parameters showing top false * *dataset* : Set parameters showing top false * *managers* : Set parameters showing top false. Add the save/cancel buttons to the accept/reject dialog (to save managers parameters in a xml file). Finally, in case of accepted managers type info, save the managers parameters in a xml file. =============== =========== ==================================== **Parameters** **Type** **Description** *type_info* string The file type information between * scan * dataset * managers =============== =========== ==================================== See Also -------- custom_tree.parameter_to_xml_file, create_menu """ dialog = QtWidgets.QDialog() vlayout = QtWidgets.QVBoxLayout() tree = ParameterTree() tree.setMinimumWidth(400) tree.setMinimumHeight(500) if type_info == 'scan': tree.setParameters(self.scan_attributes, showTop=False) elif type_info == 'dataset': tree.setParameters(self.dataset_attributes, showTop=False) vlayout.addWidget(tree) dialog.setLayout(vlayout) buttonBox = QtWidgets.QDialogButtonBox(parent=dialog) buttonBox.addButton('Cancel', buttonBox.RejectRole) buttonBox.addButton('Apply', buttonBox.AcceptRole) buttonBox.rejected.connect(dialog.reject) buttonBox.accepted.connect(dialog.accept) vlayout.addWidget(buttonBox) dialog.setWindowTitle('Fill in information about this {}'.format(type_info)) res = dialog.exec() return res def show_file_content(self): try: self.h5saver.init_file(addhoc_file_path=self.h5saver.settings.child(('current_h5_file')).value()) self.h5saver.show_file_content() except Exception as e: logger.exception(str(e)) def start_logging(self): """ Start a logging. """ self.ui.log_message.setText('Starting logging') self.overshoot = False res = self.set_logging() # mandatory to deal with multithreads if self.logger_thread is not None: self.command_DAQ_signal.disconnect() if self.logger_thread.isRunning(): self.logger_thread.exit() while not self.logger_thread.isFinished(): QThread.msleep(100) self.logger_thread = None self.logger_thread = QThread() log_acquisition = DAQ_Logging(self.settings, self.logger, self.modules_manager) log_acquisition.moveToThread(self.logger_thread) self.command_DAQ_signal[list].connect(log_acquisition.queue_command) log_acquisition.status_sig[list].connect(self.thread_status) self.logger_thread.log_acquisition = log_acquisition self.logger_thread.start() self.ui.start_button.setEnabled(False) QtWidgets.QApplication.processEvents() self.ui.logging_state.set_as_false() self.command_DAQ_signal.emit(["start_logging"]) self.ui.log_message.setText('Running acquisition') def stop_logging(self): """ Emit the command_DAQ signal "stop_acquisiion". See Also -------- set_ini_positions """ preset_items_det = self.modules_manager.detectors for det in preset_items_det: det.ui.stop_pb.click() self.command_DAQ_signal.emit(["stop_acquisition"]) if not self.dashboard.overshoot: status = 'Data Logging has been stopped by user' else: status = 'Data Logging has been stopped due to overshoot' self.update_status(status) self.ui.start_button.setEnabled(True) @pyqtSlot(list) def thread_status(self, status): # general function to get datas/infos from all threads back to the main """ | General function to get datas/infos from all threads back to the main. | Switch the status with : * *"Update status"* : Update the status bar with the status attribute txt message * *"Update_scan_index"* : Set the value of the User Interface - indice_scan_sb attribute. * *"Scan_done"* : Save the scan and init the positions * *"Timeout"* : Set the "Timeout occured" in the User Interface-log message See Also -------- update_status, save_scan, set_ini_positions """ if status[0] == "Update_Status": self.update_status(status[1], wait_time=self.wait_time) elif status[0] == "Timeout": self.ui.log_message.setText('Timeout occurred') def update_status(self, txt, wait_time=0): """ Show the txt message in the status bar with a delay of wait_time ms. =============== =========== ======================= **Parameters** **Type** **Description** *txt* string The message to show *wait_time* int the delay of showing *log_type* string the type of the log =============== =========== ======================= """ try: self.ui.statusbar.showMessage(txt, wait_time) logging.info(txt) except Exception as e: logger.exception(str(e))
class Calibration(QtWidgets.QWidget): log_signal = Signal(str) coeffs_calib = Signal(list) params = [ { 'title': 'Laser wavelength (nm):', 'name': 'laser_wl', 'type': 'float', 'value': 515. }, { 'title': 'Fit options:', 'name': 'fit_options', 'type': 'group', 'children': [ { 'title': 'Fit in?:', 'name': 'fit_units', 'type': 'list', 'value': 'nm', 'limits': ['nm', 'cm-1', 'eV'] }, { 'title': 'Polynomial Fit order:', 'name': 'fit_order', 'type': 'int', 'value': 1, 'min': 1, 'max': 3 }, { 'title': 'Do calib:', 'name': 'do_calib', 'type': 'bool', 'value': False }, ] }, { 'title': 'Peaks', 'name': 'peaks_table', 'type': 'table_view' }, PeakGroup(title='Peak options:', name="peak_options", channels=[]), ] def __init__(self, parent): QLocale.setDefault(QLocale(QLocale.English, QLocale.UnitedStates)) super().__init__() if not isinstance(parent, DockArea): raise Exception('no valid parent container, expected a DockArea') self.dockarea = parent self.window = self.dockarea.parent() self.setupUI() self.raw_datas = dict([]) self.raw_axis = None self.text_peak_items = [] self.arrow_peak_items = [] self.table_model = None self.calib_plot = None self.filenames = [] def create_toolbar(self): self.toolbar.addWidget(QtWidgets.QLabel('Calibration:')) iconadd = QtGui.QIcon() iconadd.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/Add2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.addh5_action = QtWidgets.QAction(iconadd, "Add spectrum", None) self.toolbar.addAction(self.addh5_action) self.addh5_action.triggered.connect(self.add_spectrum_h5) iconreset = QtGui.QIcon() iconreset.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/Refresh2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.reset_action = QtWidgets.QAction(iconreset, "Remove plots", None) self.toolbar.addAction(self.reset_action) self.reset_action.triggered.connect(self.reset) def add_spectrum_h5(self): data, fname, node_path = browse_data(ret_all=True) if data is not None: file = Path(fname).parts[-1] self.filenames.append(file) self.raw_datas[file] = data self.raw_axis = np.linspace(0, len(data) - 1, len(data)) # with tables.open_file(fname) as h5file: # data_node = h5file.get_node(node_path) # # # if 'X_axis' in list(data_node._v_parent._v_children): # self.raw_axis = data_node._v_parent._f_get_child('X_axis').read() self.viewer_data.show_data(self.raw_datas.values(), x_axis=self.raw_axis, labels=self.filenames) def update_peak_source(self): for child in self.settings.child(('peak_options')).children(): child.child(('channel')).setOpts(limits=self.filenames) def reset(self): self.raw_datas = dict([]) self.raw_axis = None self.viewer_data.remove_plots() def setupUI(self): horlayout = QtWidgets.QHBoxLayout() splitter = QtWidgets.QSplitter(Qt.Horizontal) self.setLayout(horlayout) horlayout.addWidget(splitter) tab = QtWidgets.QTabWidget() form = QtWidgets.QWidget() self.viewer_data = Viewer1D(form) self.plot_peak_item = self.viewer_data.viewer.plotwidget.plot() form1 = QtWidgets.QWidget() self.viewer_calib = Viewer1D(form1) self.viewer_calib.set_axis_label(axis_settings=dict( orientation='left', label='Photon wavelength', units='nm')) tab.addTab(form, 'Data Viewer') tab.addTab(form1, 'Calibration') splitter.addWidget(tab) self.settings = Parameter.create(name='settings', type='group', children=self.params) self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed) self.settings_tree = ParameterTree() self.settings_tree.setMinimumWidth(300) self.settings_tree.setParameters(self.settings, showTop=False) splitter.addWidget(self.settings_tree) # creating a toolbar self.toolbar = QtWidgets.QToolBar() self.create_toolbar() self.window.addToolBar(self.toolbar) def parameter_tree_changed(self, param, changes): for param, change, data in changes: path = self.settings.childPath(param) if path is not None: childName = '.'.join(path) else: childName = param.name() if change == 'childAdded': self.update_peak_source() if param.name() == 'peak_options': QtWidgets.QApplication.processEvents() #self.update_peak_finding() elif change == 'value': if param.name() in custom_tree.iter_children( self.settings.child(('peak_options')), []): self.update_peak_finding() elif param.name() == 'fit_units': if self.table_model is not None: self.table_model.setHeaderData(2, Qt.Horizontal, data) if self.settings.child('fit_options', 'do_calib').value(): self.calculate_calibration( self.settings.child(('peaks_table')).value()._data) elif change == 'parent': pass def update_peak_finding(self): try: if len(self.raw_datas) != 0: peak_options = [] for channel in self.filenames: opts = dict([]) for child in self.settings.child(('peak_options')): if child.child(('channel')).value() == channel: children = [ ch.name() for ch in child.children() if not (ch.name() == 'use_opts' or ch.name() == 'channel') ] if child.child(('use_opts')).value(): param_opt = child.child((children[0])) opts[param_opt.name()] = param_opt.value() if len(opts) != 0: peak_options.append(dict(channel=channel, opts=opts)) self.peak_indexes = [] self.peak_amplitudes = [] if len(peak_options) != 0: for option in peak_options: peak_indexes, properties = find_peaks( self.raw_datas[option['channel']], **option['opts']) self.peak_indexes.extend(list(peak_indexes)) self.peak_amplitudes.extend( list(self.raw_datas[option['channel']] [peak_indexes])) self.peak_indexes = np.array(self.peak_indexes) self.peak_amplitudes = np.array(self.peak_amplitudes) arg_sorted_indexes = np.argsort(self.peak_indexes) self.peak_indexes = self.peak_indexes[arg_sorted_indexes] self.peak_amplitudes = self.peak_amplitudes[ arg_sorted_indexes] if len(self.peak_indexes) != 0: self.viewer_data.viewer.plotwidget.plotItem.removeItem( self.plot_peak_item) while len(self.text_peak_items) != 0: self.viewer_data.viewer.plotwidget.plotItem.removeItem( self.text_peak_items.pop(0)) self.viewer_data.viewer.plotwidget.plotItem.removeItem( self.arrow_peak_items.pop(0)) self.plot_peak_item = self.viewer_data.viewer.plotwidget.plot( self.raw_axis[self.peak_indexes], self.peak_amplitudes, pen=None, symbol='+') for ind, peak_index in enumerate(self.peak_indexes): item = TextItem('({:.00f},{:.02f})'.format( self.raw_axis[peak_index], self.peak_amplitudes[ind]), angle=45, color='w', anchor=(0, 1)) size = self.viewer_data.viewer.plotwidget.plotItem.vb.itemBoundingRect( item) item.setPos( self.raw_axis[peak_index], self.peak_amplitudes[ind] + size.height()) self.text_peak_items.append(item) item_ar = ArrowItem( pos=(self.raw_axis[peak_index], self.peak_amplitudes[ind] + size.height() / 5), angle=-90, tipAngle=30, baseAngle=20, headLen=10, tailLen=20, tailWidth=1, pen=None, brush='w') self.arrow_peak_items.append(item_ar) self.viewer_data.viewer.plotwidget.plotItem.addItem( item) self.viewer_data.viewer.plotwidget.plotItem.addItem( item_ar) self.table_model = PandasModel( pd.DataFrame([[False, ind, 0] for ind in self.peak_indexes], columns=[ 'Use', 'Pxl', self.settings.child( 'fit_options', 'fit_units').value() ])) self.settings.child( ('peaks_table')).setValue(self.table_model) except Exception as e: logger.exception(str(e)) def update_status(self, txt, log_type=None): """ """ print(txt) if log_type is not None: self.log_signal.emit(txt) def calculate_calibration(self, dataframe=pd.DataFrame()): try: data_to_use = dataframe.query('Use == True') data = data_to_use[dataframe.columns[2]].to_numpy() indexes = data_to_use['Pxl'].to_numpy() unit = self.settings.child('fit_options', 'fit_units').value() if unit == 'nm': pass elif unit == 'cm-1': data = Ecmrel2Enm(data, self.settings.child(('laser_wl')).value()) elif unit == 'eV': data = eV2nm(data) if data.size != 0: if self.calib_plot is not None: self.viewer_calib.viewer.plotwidget.plotItem.removeItem( self.calib_plot) self.calib_plot = self.viewer_calib.viewer.plotwidget.plot( indexes, data, pen=None, symbol='+') calib_coeffs = np.polyfit( indexes - np.max(self.raw_axis) / 2, data, self.settings.child('fit_options', 'fit_order').value()) calib_data = np.polyval( calib_coeffs, self.raw_axis - np.max(self.raw_axis) / 2) self.viewer_calib.show_data([calib_data], labels=[ 'Fit of order {:d}'.format( self.settings.child( 'fit_options', 'fit_order').value()) ]) self.coeffs_calib.emit(list(calib_coeffs)[::-1]) except Exception as e: self.update_status(e, 'log')
class CustomApp(QtWidgets.QWidget, QObject): # custom signal that will be fired sometimes. Could be connected to an external object method or an internal method log_signal = pyqtSignal(str) # list of dicts enabling the settings tree on the user interface params = [ { 'title': 'Main settings:', 'name': 'main_settings', 'type': 'group', 'children': [{ 'title': 'Save base path:', 'name': 'base_path', 'type': 'browsepath', 'value': config['data_saving']['h5file']['save_path'] }, { 'title': 'File name:', 'name': 'target_filename', 'type': 'str', 'value': "", 'readonly': True }, { 'title': 'Date:', 'name': 'date', 'type': 'date', 'value': QDate.currentDate() }, { 'title': 'Do something, such as showing data:', 'name': 'do_something', 'type': 'bool', 'value': False }, { 'title': 'Something done:', 'name': 'something_done', 'type': 'led', 'value': False, 'readonly': True }, { 'title': 'Infos:', 'name': 'info', 'type': 'text', 'value': "" }, { 'title': 'push:', 'name': 'push', 'type': 'bool_push', 'value': False }] }, { 'title': 'Other settings:', 'name': 'other_settings', 'type': 'group', 'children': [ { 'title': 'List of stuffs:', 'name': 'list_stuff', 'type': 'list', 'value': 'first', 'values': ['first', 'second', 'third'], 'tip': 'choose a stuff from the list' }, { 'title': 'List of integers:', 'name': 'list_int', 'type': 'list', 'value': 0, 'values': [0, 256, 512], 'tip': 'choose a stuff from this int list' }, { 'title': 'one integer:', 'name': 'an_integer', 'type': 'int', 'value': 500, }, { 'title': 'one float:', 'name': 'a_float', 'type': 'float', 'value': 2.7, }, ] }, ] def __init__(self, dockarea): QLocale.setDefault(QLocale(QLocale.English, QLocale.UnitedStates)) super(CustomApp, self).__init__() if not isinstance(dockarea, DockArea): raise Exception('no valid parent container, expected a DockArea') self.dockarea = dockarea self.mainwindow = dockarea.parent() # init the object parameters self.detector = None self.raw_data = [] # init the user interface self.setup_UI() def setup_UI(self): ########################################### ########################################### # init the docks containing the main widgets ############################################# # this one for the custom application settings dock_settings = Dock('Settings', size=(350, 350)) self.dockarea.addDock(dock_settings, 'left') # create main parameter tree self.settings_tree = ParameterTree() dock_settings.addWidget(self.settings_tree, 10) self.settings_tree.setMinimumWidth(300) # create a Parameter object containing the settings self.settings = Parameter.create(name='Settings', type='group', children=self.params) # load the tree with this parameter object self.settings_tree.setParameters(self.settings, showTop=False) # any change to the tree on the user interface will call the parameter_tree_changed method where all actions will be applied self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed) ################################################################ # create a logger dock where to store info senf from the programm self.dock_logger = Dock("Logger") self.logger_list = QtWidgets.QListWidget() self.logger_list.setMinimumWidth(300) self.dock_logger.addWidget(self.logger_list) self.dockarea.addDock(self.dock_logger, 'bottom', dock_settings) # dock_logger.setVisible(False) # connect together this custom signal with the add_log method self.log_signal[str].connect(self.add_log) ####################################################################################################################### # create a dock containing a viewer object, could be 0D, 1D or 2D depending what kind of data one want to plot here a 0D dock_Viewer0D = Dock('Viewer dock', size=(350, 350)) self.dockarea.addDock(dock_Viewer0D, 'right', self.dock_logger) target_widget = QtWidgets.QWidget() self.target_viewer = Viewer0D(target_widget) dock_Viewer0D.addWidget(target_widget) ################################################################################### # create 2 docks to display the DAQ_Viewer (one for its settings, one for its viewer) dock_detector_settings = Dock("Detector Settings", size=(350, 350)) self.dockarea.addDock(dock_detector_settings, 'right', dock_settings) dock_detector = Dock("Detector Viewer", size=(350, 350)) self.dockarea.addDock(dock_detector, 'right', dock_detector_settings) # init one daq_viewer object named detector self.detector = DAQ_Viewer(self.dockarea, dock_settings=dock_detector_settings, dock_viewer=dock_detector, title="A detector", DAQ_type='DAQ0D') # set its type to 'Mock' control_type = 'Mock' self.detector.ui.Detector_type_combo.setCurrentText(control_type) # init the detector and wait 1000ms for the completion self.detector.ui.IniDet_pb.click() self.detector.settings.child('main_settings', 'wait_time').setValue(100) QtWidgets.QApplication.processEvents() QThread.msleep(1000) self.detector.grab_done_signal.connect(self.data_done) ############################# # create a dock for a DAQ_Move dock_move = Dock("Move module", size=(350, 350)) self.dockarea.addDock(dock_move, 'right', self.dock_logger) move_widget = QtWidgets.QWidget() self.move = DAQ_Move(move_widget) dock_move.addWidget(move_widget) self.move.ui.IniStage_pb.click() QtWidgets.QApplication.processEvents() QThread.msleep(1000) ############################################ # creating a menubar self.menubar = self.mainwindow.menuBar() self.create_menu(self.menubar) # creating a toolbar self.toolbar = QtWidgets.QToolBar() self.create_toolbar() self.mainwindow.addToolBar(self.toolbar) @pyqtSlot(OrderedDict) def data_done(self, data): # print(data) pass @pyqtSlot(QRectF) def update_weighted_settings(self, rect): self.settings.child('weighting_settings', 'x0').setValue(int(rect.x())) self.settings.child('weighting_settings', 'y0').setValue(int(rect.y())) self.settings.child('weighting_settings', 'width').setValue(max([1, int(rect.width())])) self.settings.child('weighting_settings', 'height').setValue(max([1, int(rect.height())])) def parameter_tree_changed(self, param, changes): for param, change, data in changes: path = self.settings.childPath(param) if path is not None: childName = '.'.join(path) else: childName = param.name() if change == 'childAdded': pass elif change == 'value': if param.name() == 'do_something': if param.value(): self.log_signal.emit('Do something') self.detector.grab_done_signal.connect(self.show_data) self.raw_data = [] # init the data to be finally saved self.settings.child('main_settings', 'something_done').setValue(True) else: self.log_signal.emit('Stop Doing something') self.detector.grab_done_signal.disconnect() self.settings.child('main_settings', 'something_done').setValue(False) elif change == 'parent': pass @pyqtSlot(OrderedDict) def show_data(self, data): """ do stuff with data from the detector if its grab_done_signal has been connected Parameters ---------- data: (OrderedDict) #OrderedDict(name=self.title,x_axis=None,y_axis=None,z_axis=None,data0D=None,data1D=None,data2D=None) """ data0D = [[data['data0D'][key]['data']] for key in data['data0D']] if self.raw_data == []: self.raw_data = data0D else: if len(self.raw_data) != len(data0D): self.raw_data = data0D else: for ind in range(len(data0D)): self.raw_data[ind].append(data0D[ind][0]) self.target_viewer.show_data(data0D) def create_menu(self, menubar): """ """ menubar.clear() # %% create file menu file_menu = menubar.addMenu('File') load_action = file_menu.addAction('Load file') load_action.triggered.connect(self.load_file) save_action = file_menu.addAction('Save file') save_action.triggered.connect(self.save_data) file_menu.addSeparator() quit_action = file_menu.addAction('Quit') quit_action.triggered.connect(self.quit_function) settings_menu = menubar.addMenu('Settings') docked_menu = settings_menu.addMenu('Docked windows') action_load = docked_menu.addAction('Load Layout') action_save = docked_menu.addAction('Save Layout') def load_file(self): # init the data browser module widg = QtWidgets.QWidget() self.data_browser = H5Browser(widg) widg.show() def quit_function(self): # close all stuff that need to be self.detector.quit_fun() QtWidgets.QApplication.processEvents() self.mainwindow.close() def create_toolbar(self): iconquit = QtGui.QIcon() iconquit.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/close2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.quit_action = QtWidgets.QAction(iconquit, "Quit program", None) self.toolbar.addAction(self.quit_action) self.quit_action.triggered.connect(self.quit_function) icon_detector = QtGui.QIcon() icon_detector.addPixmap( QtGui.QPixmap(":/icons/Icon_Library/camera.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.detector_action = QtWidgets.QAction(icon_detector, "Grab from camera", None) self.detector_action.setCheckable(True) self.toolbar.addAction(self.detector_action) self.detector_action.triggered.connect(lambda: self.run_detector()) iconload = QtGui.QIcon() iconload.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/Open.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.loadaction = QtWidgets.QAction( iconload, "Load target file (.h5, .png, .jpg) or data from camera", None) self.toolbar.addAction(self.loadaction) self.loadaction.triggered.connect(self.load_file) iconsave = QtGui.QIcon() iconsave.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/SaveAs.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.saveaction = QtWidgets.QAction(iconsave, "Save current data", None) self.toolbar.addAction(self.saveaction) self.saveaction.triggered.connect(self.save_data) def run_detector(self): self.detector.ui.grab_pb.click() def save_data(self): try: path = gutils.select_file(start_path=self.settings.child( 'main_settings', 'base_path').value(), save=True, ext='h5') if path is not None: # init the file object with an addhoc name given by the user h5saver = H5Saver(save_type='custom') h5saver.init_file(update_h5=True, addhoc_file_path=path) # save all metadata settings_str = ioxml.parameter_to_xml_string(self.settings) settings_str = b'<All_settings>' + settings_str settings_str += ioxml.parameter_to_xml_string( self.detector.settings) + ioxml.parameter_to_xml_string( h5saver.settings) + b'</All_settings>' data_group = h5saver.add_data_group( h5saver.raw_group, group_data_type='data0D', title='data from custom app', settings_as_xml=settings_str) for dat in self.raw_data: channel = h5saver.add_CH_group(data_group) data_dict = dict(data=np.array(dat), x_axis=dict(data=np.linspace( 0, len(dat) - 1, len(dat)), units='pxl')) h5saver.add_data(channel, data_dict=data_dict, scan_type='') st = 'file {:s} has been saved'.format(str(path)) self.add_log(st) self.settings.child('main_settings', 'info').setValue(st) h5saver.close_file() except Exception as e: self.add_log(getLineInfo() + str(e)) @pyqtSlot(str) def add_log(self, txt): """ Add a log to the logger list from the given text log and the current time ================ ========= ====================== **Parameters** **Type** **Description** *txt* string the log to be added ================ ========= ====================== """ now = datetime.datetime.now() new_item = QtWidgets.QListWidgetItem(str(now) + ": " + txt) self.logger_list.addItem(new_item) # #to do # #self.save_parameters.logger_array.append(str(now)+": "+txt) @pyqtSlot(str) def emit_log(self, txt): """ Emit a log-signal from the given log index =============== ======== ======================= **Parameters** **Type** **Description** *txt* string the log to be emitted =============== ======== ======================= """ self.log_signal.emit(txt)
def _ui_config_init(self): self._ui_init_buttons() self.ui_output_dir_widget = iowidgetqt.SetDirWidget( self.teigen.config["filepattern"], "output directory") self.ui_output_dir_widget.setToolTip( "Data are stored in defined directory.\nOutput format is based on file extension.\n\ For saving into image stack use 'filename{:06d}.jpg'") self.mainLayout.addWidget(self.ui_output_dir_widget, 1, 1, 1, 2) # , (gd_max_i / 2), text_col) postprocessing_params = self.teigen.config["postprocessing"] hide_keys = [ "build", "gtree", "voxelsize_mm", "areasize_px", "resolution", "n_slice", "dims", "tube_shape", "radius_distribution_normal", "radius_distribution_uniform", "radius_distribution_fixed", "allow_overlap" ] self._ui_generators_tab_wg = QTabWidget() self._ui_generators_tab_wg.setMinimumWidth(400) self.mainLayout.addWidget(self._ui_generators_tab_wg, 0, 1, 1, 2) # l = QVBoxLayout(self) rename_captions_dict = { "voxelsize_mm": "voxel size [mm]", } dropdownoboxes = { "radius_distribution": ["normal", "fixed", "uniform"] } # list is pointer. It causes problems with temporary reconstruction information # copy fix this issue teigen_config = copy.deepcopy(self.teigen.config) self._ui_generator_widgets = [] for generator_name in teigen_config["generators"]: wg = dictwidgetqt.DictWidget( teigen_config["generators"][generator_name], hide_keys=hide_keys, captions=rename_captions_dict, ncols=1, dropdownboxes=dropdownoboxes, ) self._ui_generator_widgets.append(wg) self._ui_generators_tab_wg.addTab( wg, generator_name.replace(" ", "\n")) # self._ui_generators_tab_wg. # l.addWidget(self._ui_generators_tab_wg) # wgn = QtGui.QWidget() # layout = QtGui.QFormLayout() # layout.addRow("Name", QtGui.QLineEdit()) # layout.addRow("Address",QtGui.QLineEdit()) # wgn.setLayout(layout) # self._ui_generators_tab_wg.addTab(wgn, "ahoj") id = self.teigen.get_generator_id_by_name_or_number( teigen_config["generator_id"]) self._ui_generators_tab_wg.setCurrentIndex(id) # self.mainLayout.setColumnMinimumWidth(text_col, 500) # pyqtgraph experiments input_params = { "Area Sampling": dictwidgetpg.AreaSamplingParameter( name='Area Sampling', **self.teigen.config["areasampling"]), "Postprocessing": postprocessing_params, "Batch processing": dictwidgetpg.BatchFileProcessingParameter(name="Batch processing", children=[ { 'name': 'Run batch', 'type': 'action' }, ]), "Appearance": self.teigen.config["appearance"], "Output": self.teigen.config["output"], "Measurement": self.teigen.config["measurement"] # 'name': {'type': 'action'}, # "dur": i5, # TODO add more lines here # "Intensity Profile": dictwidgetpyqtgraph.ScalableFloatGroup( # name="Intensity Profile", children=[ # {'name': '0.2', 'type': 'float', 'value': "100"}, # {'name': '0.4', 'type': 'float', 'value': "115"}, # ]) } gr_struct = dictwidgetpg.to_pyqtgraph_struct('params', input_params, opts={}) dictwidgetpg.add_tip(gr_struct, "noise_preview", "this is noise") dictwidgetpg.add_tips(gr_struct, teigen_keysdoc) gr_struct["children"][1]['tip'] = "Apperance tip" gr_struct["children"][2]['tip'] = "output tip" gr_struct["children"][3]['tip'] = "post processing tip" gr_struct["children"][4]['tip'] = "measurement" p = Parameter.create(**gr_struct) t = ParameterTree() t.setParameters(p, showTop=False) t.setMinimumWidth(380) # t.setColumnCount(3) t.show() p.sigTreeStateChanged.connect(self._parameters_changed) p.param('Batch processing', 'Run batch').sigActivated.connect(self.run_batch) # how to add button # i5 = pg.TreeWidgetItem(["Item 5"]) # b5 = QtGui.QPushButton('Button') # i5.setWidget(1, b5) # t.addTopLevelItem(i5) self.mainLayout.addWidget(t, 0, 0, 6, 1) self.config_wg = t # self.config_wg.setToolTip(teigendoc) self.area_sampling_params = p self.teigen.progress_callback = self._progressbar_update self._ui_btn_step2.setEnabled(False)
class ModulesManager(QObject): detectors_changed = pyqtSignal(list) actuators_changed = pyqtSignal(list) det_done_signal = pyqtSignal(OrderedDict) move_done_signal = pyqtSignal(OrderedDict) timeout_signal = pyqtSignal(bool) params = [ { 'title': 'Actuators/Detectors Selection', 'name': 'modules', 'type': 'group', 'children': [ { 'title': 'detectors', 'name': 'detectors', 'type': 'itemselect' }, { 'title': 'Actuators', 'name': 'actuators', 'type': 'itemselect' }, ] }, { 'title': "Moves done?", 'name': 'move_done', 'type': 'led', 'value': False }, { 'title': "Detections done?", 'name': 'det_done', 'type': 'led', 'value': False }, { 'title': 'Data dimensions', 'name': 'data_dimensions', 'type': 'group', 'children': [ { 'title': "Probe detector's data", 'name': 'probe_data', 'type': 'action' }, { 'title': 'Data0D list:', 'name': 'det_data_list0D', 'type': 'itemselect' }, { 'title': 'Data1D list:', 'name': 'det_data_list1D', 'type': 'itemselect' }, { 'title': 'Data2D list:', 'name': 'det_data_list2D', 'type': 'itemselect' }, { 'title': 'DataND list:', 'name': 'det_data_listND', 'type': 'itemselect' }, ] }, { 'title': 'Actuators positions', 'name': 'actuators_positions', 'type': 'group', 'children': [ { 'title': "Test actuators", 'name': 'test_actuator', 'type': 'action' }, { 'title': 'Positions:', 'name': 'positions_list', 'type': 'itemselect' }, ] }, ] def __init__(self, detectors=[], actuators=[], selected_detectors=[], selected_actuators=[], timeout=10000): super().__init__() for mod in selected_actuators: assert mod in actuators for mod in selected_detectors: assert mod in detectors self.timeout = timeout #in ms self.det_done_datas = OrderedDict() self.det_done_flag = False self.move_done_positions = OrderedDict() self.move_done_flag = False self.settings = Parameter.create(name='Settings', type='group', children=self.params) self.settings_tree = ParameterTree() self.settings_tree.setMinimumWidth(300) self.settings_tree.setParameters(self.settings, showTop=False) self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed) self.settings.child('data_dimensions', 'probe_data').sigActivated.connect( self.get_det_data_list) self.settings.child('actuators_positions', 'test_actuator').sigActivated.connect( self.test_move_actuators) self._detectors = [] self._actuators = [] self.grab_done_signals = [] self.det_commands_signal = [] self.set_actuators(actuators, selected_actuators) self.set_detectors(detectors, selected_detectors) @classmethod def get_names(cls, modules): if not hasattr(modules, '__iter__'): modules = [modules] return [mod.title for mod in modules] def get_mods_from_names(self, names, mod='det'): mods = [] for name in names: d = self.get_mod_from_name(name, mod) if d is not None: mods.append(d) return mods def get_mod_from_name(self, name, mod='det'): if mod == 'det': modules = self._detectors else: modules = self._actuators if name in self.get_names(modules): return modules[self.get_names(modules).index(name)] else: logger.warning(f'No detector with this name: {name}') return None def set_actuators(self, actuators, selected_actuators): self._actuators = actuators self.settings.child('modules', 'actuators').setValue( dict(all_items=self.get_names(actuators), selected=self.get_names(selected_actuators))) def set_detectors(self, detectors, selected_detectors): self._detectors = detectors self.settings.child('modules', 'detectors').setValue( dict(all_items=self.get_names(detectors), selected=self.get_names(selected_detectors))) @property def detectors(self): return self.get_mods_from_names(self.selected_detectors_name) @property def actuators(self): return self.get_mods_from_names(self.selected_actuators_name, mod='act') @property def Ndetectors(self): return len(self.detectors) @property def Nactuators(self): return len(self.actuators) @property def detectors_name(self): return self.settings.child('modules', 'detectors').value()['all_items'] @property def selected_detectors_name(self): return self.settings.child('modules', 'detectors').value()['selected'] @property def actuators_name(self): return self.settings.child('modules', 'actuators').value()['all_items'] @property def selected_actuators_name(self): return self.settings.child('modules', 'actuators').value()['selected'] def parameter_tree_changed(self, param, changes): """ Check for changes in the given (parameter,change,information) tuple list. In case of value changed, update the DAQscan_settings tree consequently. =============== ============================================ ============================== **Parameters** **Type** **Description** *param* instance of pyqtgraph parameter the parameter to be checked *changes* (parameter,change,information) tuple list the current changes state =============== ============================================ ============================== """ for param, change, data in changes: path = self.settings.childPath(param) if change == 'childAdded': pass elif change == 'value': if param.name() == 'detectors': self.detectors_changed.emit(data['selected']) elif param.name() == 'actuators': self.actuators_changed.emit(data['selected']) elif change == 'parent': pass def get_det_data_list(self): self.connect_detectors() datas = self.grab_datas() data_list0D = [] data_list1D = [] data_list2D = [] data_listND = [] for k in datas.keys(): if 'data0D' in datas[k].keys(): data_list0D.extend( [f'{k}/{name}' for name in datas[k]['data0D'].keys()]) if 'data1D' in datas[k].keys(): data_list1D.extend( [f'{k}/{name}' for name in datas[k]['data1D'].keys()]) if 'data2D' in datas[k].keys(): data_list2D.extend( [f'{k}/{name}' for name in datas[k]['data2D'].keys()]) if 'dataND' in datas[k].keys(): data_listND.extend( [f'{k}/{name}' for name in datas[k]['dataND'].keys()]) self.settings.child('data_dimensions', 'det_data_list0D').setValue( dict(all_items=data_list0D, selected=[])) self.settings.child('data_dimensions', 'det_data_list1D').setValue( dict(all_items=data_list1D, selected=[])) self.settings.child('data_dimensions', 'det_data_list2D').setValue( dict(all_items=data_list2D, selected=[])) self.settings.child('data_dimensions', 'det_data_listND').setValue( dict(all_items=data_listND, selected=[])) self.connect_detectors(False) def get_selected_probed_data(self, dim='0D'): return self.settings.child( 'data_dimensions', f'det_data_list{dim.upper()}').value()['selected'] def grab_datas(self, **kwargs): self.det_done_datas = OrderedDict() self.det_done_flag = False self.settings.child(('det_done')).setValue(self.det_done_flag) tzero = time.perf_counter() for sig in [mod.command_detector for mod in self.detectors]: sig.emit(utils.ThreadCommand("single", [1, kwargs])) while not self.det_done_flag: #wait for grab done signals to end QtWidgets.QApplication.processEvents() if time.perf_counter() - tzero > self.timeout: self.timeout_signal.emit(True) logger.error( 'Timeout Fired during waiting for data to be acquired') break self.det_done_signal.emit(self.det_done_datas) return self.det_done_datas def connect_actuators(self, connect=True, slot=None): if slot is None: slot = self.move_done if connect: for sig in [mod.move_done_signal for mod in self.actuators]: sig.connect(slot) else: try: for sig in [mod.move_done_signal for mod in self.actuators]: sig.disconnect(slot) except Exception as e: logger.error(str(e)) def connect_detectors(self, connect=True, slot=None): if slot is None: slot = self.det_done if connect: for sig in [mod.grab_done_signal for mod in self.detectors]: sig.connect(slot) else: try: for sig in [mod.grab_done_signal for mod in self.detectors]: sig.disconnect(slot) except Exception as e: logger.error(str(e)) def test_move_actuators(self): positions = dict() for act in self.get_names(self.actuators): pos, done = QtWidgets.QInputDialog.getDouble( None, f'Enter a target position for actuator {act}', 'Position:') if not done: pos = 0. positions[act] = pos self.connect_actuators() positions = self.move_actuators(positions) self.settings.child('actuators_positions', 'positions_list').setValue( dict(all_items=[f'{k}: {positions[k]}' for k in positions], selected=[])) self.connect_actuators(False) def move_actuators(self, positions): self.move_done_positions = OrderedDict() self.move_done_flag = False self.settings.child(('move_done')).setValue(self.move_done_flag) if not hasattr(positions, '__iter__'): positions = [positions] if len(positions) == self.Nactuators: if isinstance(positions, dict): for k in positions: act = self.get_mod_from_name(k, 'act') if act is not None: act.command_stage.emit( utils.ThreadCommand(command="move_Abs", attributes=[positions[k]])) else: for ind, act in enumerate(self.actuators): act.command_stage.emit( utils.ThreadCommand(command="move_Abs", attributes=[positions[ind]])) else: logger.error( 'Invalid number of positions compared to selected actuators') return self.move_done_positions tzero = time.perf_counter() while not self.move_done_flag: QtWidgets.QApplication.processEvents() if time.perf_counter() - tzero > self.timeout: self.timeout_signal.emit(True) logger.error( 'Timeout Fired during waiting for data to be acquired') break self.move_done_signal.emit(self.move_done_positions) return self.move_done_positions def order_positions(self, positions_as_dict): actuators = self.selected_actuators_name pos = [] for act in actuators: pos.append(positions_as_dict[act]) return pos pyqtSlot(str, float) def move_done(self, name, position): """ """ try: if name not in list(self.move_done_positions.keys()): self.move_done_positions[name] = position if len(self.move_done_positions.items()) == len(self.actuators): self.move_done_flag = True self.settings.child( ('move_done')).setValue(self.move_done_flag) except Exception as e: logger.exception(str(e)) @pyqtSlot(OrderedDict) def det_done(self, data): try: if data['name'] not in list(self.det_done_datas.keys()): self.det_done_datas[data['name']] = data if len(self.det_done_datas.items()) == len(self.detectors): self.det_done_flag = True self.settings.child(('det_done')).setValue(self.det_done_flag) except Exception as e: logger.exception(str(e))
class Navigator(QObject): log_signal = pyqtSignal(str) sig_double_clicked = pyqtSignal(float, float) def __init__(self, parent=None, h5file_path=None): super(Navigator, self).__init__(parent) if parent is None: parent = QtWidgets.QWidget() self.parent = parent self.title = 'Navigator' self.status_time = 1000 self.x_range = [] self.y_range = [] self.filters = tables.Filters(complevel=5) self.next_scan_index = 0 self.viewer = None self.overlays = [] # %list of imageItem items displaying 2D scans info self.h5module_path = h5file_path self.h5module = H5BrowserUtil() if h5file_path is not None: self.h5module.open_file(h5file_path, 'a') self.settings.child('settings', 'filepath').setValue(h5file_path) self.settings.child('settings', 'Load h5').hide() self.show_overlay() self.setupUI() def create_toolbar(self): iconload = QtGui.QIcon() iconload.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/NewLayer.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.loadaction = QtWidgets.QAction(iconload, "Load scan file (.h5)", None) self.toolbar.addAction(self.loadaction) self.loadaction.triggered.connect(self.load_data) iconloadim = QtGui.QIcon() iconloadim.addPixmap( QtGui.QPixmap(":/icons/Icon_Library/Open_File_32.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.loadactionim = QtWidgets.QAction(iconloadim, "Load image file (.h5)", None) self.toolbar.addAction(self.loadactionim) self.loadactionim.triggered.connect(self.load_image) icon_ratio = QtGui.QIcon() icon_ratio.addPixmap( QtGui.QPixmap(":/icons/Icon_Library/Zoom_1_1.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.action_ratio = QtWidgets.QAction(icon_ratio, "Set viewbox aspect ratio to 1", None) self.action_ratio.setCheckable(True) self.toolbar.addAction(self.action_ratio) self.action_ratio.triggered.connect(self.set_aspect_ratio) icon_moveat = QtGui.QIcon() icon_moveat.addPixmap( QtGui.QPixmap(":/icons/Icon_Library/move_contour.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.moveat_action = QtWidgets.QAction( icon_moveat, "When selected, double clicking on viewbox will move DAQ_Move modules", None) self.moveat_action.setCheckable(True) self.toolbar.addAction(self.moveat_action) icon_sel_all = QtGui.QIcon() icon_sel_all.addPixmap( QtGui.QPixmap(":/icons/Icon_Library/select_all2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.sel_all_action = QtWidgets.QAction( icon_sel_all, "Select (show) all 2D scans on the viewer", None) self.toolbar.addAction(self.sel_all_action) self.sel_all_action.triggered.connect(self.show_all) icon_sel_none = QtGui.QIcon() icon_sel_none.addPixmap( QtGui.QPixmap(":/icons/Icon_Library/select_none.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.sel_none_action = QtWidgets.QAction( icon_sel_none, "Unselect (hide) all 2D scans on the viewer", None) self.toolbar.addAction(self.sel_none_action) self.sel_none_action.triggered.connect(self.show_none) icon_histo = QtGui.QIcon() icon_histo.addPixmap( QtGui.QPixmap(":/icons/Icon_Library/Histogram.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.histo_action = QtWidgets.QAction(icon_histo, "Show (hide) histograms", None) self.toolbar.addAction(self.histo_action) self.histo_action.setCheckable(True) self.histo_action.triggered.connect(self.show_histo) @pyqtSlot(float, float) def move_at(self, posx, posy): if self.moveat_action.isChecked(): self.sig_double_clicked.emit(posx, posy) def show_histo(self): show_state = self.histo_action.isChecked() self.viewer.histo_widget.setVisible(show_state) def show_all(self): self.show_scans() def show_none(self): self.show_scans(False) def show_scans(self, show=True): for child in self.settings.child('scans'): val = child.value() val['checked'] = show child.setValue(val) child.sigValueChanged.emit(child, val) def list_2Dscans(self): try: scans = self.h5module.get_h5file_scans() # settings=[dict(scan_name=node._v_name,path=node._v_pathname, pixmap=nparray2Qpixmap(node.read()))),...] params = [] for child in self.settings.child(('scans')).children(): if 'Scan' in child.name(): self.settings.child(('scans')).removeChild(child) for scan in scans: params.append({ 'name': scan['scan_name'], 'type': 'pixmap_check', 'value': dict(data=scan['data'], checked=False, path=scan['path']) }) self.settings.child(('scans')).addChildren(params) for child in self.settings.child(('scans')).children(): val = child.value() val.update(dict(checked=True)) child.setValue(val) child.sigValueChanged.emit(child, child.value()) except Exception as e: logger.exception(str(e)) def load_image(self): #image_filepath = str(utils.select_file(start_path=None, save=False, ext='h5')) data, fname, node_path = browse_data(ret_all=True) if data is not None and fname != '': self.h5module_image = H5BrowserUtil() self.h5module_image.open_file(fname, 'a') node = self.h5module_image.get_node(node_path) pixmaps = self.h5module_image.get_h5file_scans(node.parent_node) self.settings.child('settings', 'imagepath').setValue(fname) other_child = [ child for child in self.settings.child(('scans')).children() if 'Scan' not in child.name() ] if len(other_child) >= 1: for child in other_child: self.settings.child(('scans')).removeChild(child) params = [] for pixmap in pixmaps: params.append({ 'name': pixmap['scan_name'], 'type': 'pixmap_check', 'value': dict(data=pixmap['data'], checked=False, path=pixmap['path']) }) self.settings.child(('scans')).addChildren(params) val = self.settings.child('scans', pixmaps[0]['scan_name']).value() val.update(dict(checked=True)) self.settings.child('scans', pixmaps[0]['scan_name']).setValue(val) self.settings.child( 'scans', pixmaps[0]['scan_name']).sigValueChanged.emit( self.settings.child('scans', pixmaps[0]['scan_name']), self.settings.child('scans', pixmaps[0]['scan_name']).value()) def load_data(self): self.h5module_path = str( gutils.select_file(start_path=None, save=False, ext='h5')) if self.h5module_path != '': self.settings.child('settings', 'filepath').setValue(self.h5module_path) if self.h5module is not None: if self.h5module.isopen(): self.h5module.close_file() self.h5module.open_file(self.h5module_path, 'a') self.list_2Dscans() def set_aspect_ratio(self): if self.action_ratio.isChecked(): self.viewer.image_widget.plotitem.vb.setAspectLocked(lock=True, ratio=1) else: self.viewer.image_widget.plotitem.vb.setAspectLocked(lock=False, ratio=1) def settings_changed(self, param, changes): """ Check for changes in the given (parameter,change,information) tuple list. In case of value changed, update the DAQscan_settings tree consequently. =============== ============================================ ============================== **Parameters** **Type** **Description** *param* instance of pyqtgraph parameter the parameter to be checked *changes* (parameter,change,information) tuple list the current changes state =============== ============================================ ============================== """ for param, change, data in changes: path = self.settings.childPath(param) if path is not None: childName = '.'.join(path) else: childName = param.name() if change == 'childAdded': pass elif change == 'value': if param.parent().name() == 'scans': if data['checked']: try: if 'Scan' in param.name(): h5module = self.h5module nodes = [ node for node in h5module.walk_nodes( data['path']) ] else: h5module = self.h5module_image nodes = [h5module.get_node(data['path'])] ind = 0 for node in nodes: flag = False if 'type' in node.attrs.attrs_name and 'data_dimension' in node.attrs.attrs_name: if 'scan_type' in node.attrs.attrs_name: if node.attrs[ 'scan_type'] == 'scan2D' and node.attrs[ 'data_dimension'] == '0D': #2d scan of 0D data flag = True elif node.attrs[ 'scan_type'] == '' and node.attrs[ 'data_dimension'] == '2D': #image data (2D) with no scan flag = True if flag: isadaptive = 'adaptive' in node.attrs[ 'scan_subtype'].lower() if isadaptive: im = TriangulationItem() else: im = ImageItem() im.setOpacity(1) # im.setOpts(axisOrder='row-major') self.viewer.image_widget.plotitem.addItem( im) im.setCompositionMode( QtGui.QPainter.CompositionMode_Plus) if 'Scan' in param.name(): if isadaptive: x_axis = h5module.get_node( h5module.get_node( data['path']).parent_node, utils.capitalize( 'scan_x_axis')).read() y_axis = h5module.get_node( h5module.get_node( data['path']).parent_node, utils.capitalize( 'scan_y_axis')).read() else: x_axis = np.unique( h5module.get_node( h5module.get_node( data['path']). parent_node, utils.capitalize( 'scan_x_axis')).read()) y_axis = np.unique( h5module.get_node( h5module.get_node( data['path']). parent_node, utils.capitalize( 'scan_y_axis')).read()) else: x_axis = np.unique( h5module.get_node( h5module.get_node( data['path']).parent_node, utils.capitalize( 'x_axis')).read()) y_axis = np.unique( h5module.get_node( h5module.get_node( data['path']).parent_node, utils.capitalize( 'y_axis')).read()) if not isadaptive: rect = QtCore.QRectF( np.min(x_axis), np.min(y_axis), (np.max(x_axis) - np.min(x_axis)), (np.max(y_axis) - np.min(y_axis))) im.setOpts(rescale=rect) im.setImage(node.read()) else: im.setImage( np.vstack((x_axis, y_axis, node.read())).T) if ind == 0: #im.setLookupTable(colors_red) self.viewer.histogram_red.setImageItem( im) if not self.viewer.histogram_red.isVisible( ): self.viewer.histogram_red.setVisible( True) elif ind == 1: #im.setLookupTable(colors_green) self.viewer.histogram_green.setImageItem( im) if not self.viewer.histogram_green.isVisible( ): self.viewer.histogram_green.setVisible( True) else: #im.setLookupTable(colors_blue) self.viewer.histogram_blue.setImageItem( im) if not self.viewer.histogram_blue.isVisible( ): self.viewer.histogram_blue.setVisible( True) self.overlays.append( dict(name='{:s}_{:03d}'.format( param.name(), ind), image=im)) ind += 1 #self.viewer.image_widget.view.autoRange() except Exception as e: logger.exception(str(e)) else: for overlay in self.overlays[:]: if param.name() in overlay['name']: ind = self.overlays.index(overlay) self.viewer.image_widget.plotitem.removeItem( overlay['image']) self.overlays.pop(ind) elif change == 'parent': for overlay in self.overlays[:]: if param.name() in overlay['name']: ind = self.overlays.index(overlay) self.viewer.image_widget.plotitem.removeItem( overlay['image']) self.overlays.pop(ind) def setupUI(self): self.ui = QObject() layout = QtWidgets.QVBoxLayout() self.parent.setLayout(layout) sett_widget = QtWidgets.QWidget() self.sett_layout = QtWidgets.QHBoxLayout() sett_widget.setLayout(self.sett_layout) # creating a toolbar self.toolbar = QtWidgets.QToolBar() self.create_toolbar() layout.addWidget(self.toolbar) splitter = QtWidgets.QSplitter(Qt.Horizontal) layout.addWidget(splitter) #set viewer area widg = QtWidgets.QWidget() #self.viewer = Viewer2D(widg) self.viewer = Viewer2DBasic(widg) self.viewer.histogram_red.setVisible(False) self.viewer.histogram_green.setVisible(False) self.viewer.histogram_blue.setVisible(False) self.viewer.sig_double_clicked.connect(self.move_at) #displaying the scan list tree self.settings_tree = ParameterTree() #self.settings_tree.setMaximumWidth(300) self.settings_tree.setMinimumWidth(300) #self.settings_tree.setVisible(False) params_scan = [ { 'title': 'Settings', 'name': 'settings', 'type': 'group', 'children': [ { 'title': 'Load h5:', 'name': 'Load h5', 'type': 'action' }, { 'title': 'h5 path:', 'name': 'filepath', 'type': 'str', 'value': '', 'readonly': True }, { 'title': 'Load Image:', 'name': 'Load Image', 'type': 'action' }, { 'title': 'Image path:', 'name': 'imagepath', 'type': 'str', 'value': '', 'readonly': True }, ] }, { 'title': 'Scans', 'name': 'scans', 'type': 'group', 'children': [] }, ] self.settings = Parameter.create(name='settings', type='group', children=params_scan) self.settings_tree.setParameters(self.settings, showTop=False) self.settings.sigTreeStateChanged.connect(self.settings_changed) self.settings.child('settings', 'Load h5').sigActivated.connect(self.load_data) self.settings.child('settings', 'Load Image').sigActivated.connect(self.load_image) self.ui.statusbar = QtWidgets.QStatusBar() self.ui.statusbar.setMaximumHeight(25) layout.addWidget(self.ui.statusbar) self.ui.log_message = QtWidgets.QLabel('Initializing') self.ui.statusbar.addPermanentWidget(self.ui.log_message) self.sett_layout.addWidget(self.settings_tree) splitter.addWidget(sett_widget) splitter.addWidget(self.viewer.parent) def show_image(self, data): """ Parameters ---------- data: (dict) with keys 'names', 'data', 'x_axis', 'y_axis', 'pixmap2D' """ if self.h5module is None: scan_path, current_filename, dataset_path = H5Saver.set_current_scan_path( navigator_path, base_name='Scan', update_h5=True, next_scan_index=self.next_scan_index, create_scan_folder=False) self.h5module = H5BrowserUtil() self.h5module.open_file( str(dataset_path.joinpath(dataset_path.name + ".h5")), 'w') else: scan_path, current_filename, dataset_path = H5Saver.set_current_scan_path( navigator_path, base_name='Scan', update_h5=False, next_scan_index=self.next_scan_index, create_scan_folder=False) if not self.h5module.isopen(): self.h5module.open_file( str(dataset_path.joinpath(dataset_path.name + ".h5")), 'a') h5group = self.h5module.root() data2D_group = self.h5module.get_set_group(h5group, 'Data2D') data2D_group.attrs.type = 'data2D' self.next_scan_index += 1 curr_group = self.h5module.get_set_group('/Data2D', current_filename) live_group = self.h5module.get_set_group(curr_group, 'Live_scan_2D') live_group.attrs['pixmap2D'] = data['pixmap2D'] xdata = data['x_axis'] if isinstance(xdata, dict): xdata = xdata['data'] xarray = self.h5module.create_carray(curr_group, "Scan_x_axis", obj=xdata, title=current_filename) xarray.attrs['type'] = 'navigation_axis' xarray.attrs['data_dimension'] = '1D' xarray.attrs['nav_index'] = 0 ydata = data['y_axis'] if isinstance(ydata, dict): ydata = ydata['data'] yarray = self.h5module.create_carray(curr_group, "Scan_y_axis", obj=ydata, title=current_filename) yarray.attrs['type'] = 'navigation_axis' yarray.attrs['data_dimension'] = '1D' yarray.attrs['nav_index'] = 1 for ind_channel, name in enumerate(data['names']): try: channel_group = self.h5module.get_set_group(live_group, name) channel_group.attrs.Channel_name = name array = self.h5module.create_carray( channel_group, current_filename + '_' + name, obj=data['data'][ind_channel], title='data', ) array.attrs['type'] = 'data' array.attrs['data_dimension'] = '0D' array.attrs['data_name'] = name array.attrs['scan_type'] = 'scan2D' array.attrs['scan_subtype'] = '' except Exception as e: logger.exception(str(e)) self.update_2Dscans() def update_2Dscans(self): try: if not self.h5module.isopen(): self.h5module.open_file(self.h5module.filename, 'a') scans = self.h5module.get_h5file_scans(self.h5module.root()) # settings=[dict(scan_name=node._v_name,path=node._v_pathname, pixmap=nparray2Qpixmap(node.read()))),...] params = [] children = [ child.name() for child in self.settings.child(('scans')).children() ] for scan in scans: if scan['scan_name'] not in children: params.append({ 'name': scan['scan_name'], 'type': 'pixmap_check', 'value': dict(data=scan['data'], checked=False, path=scan['path']) }) self.settings.child(('scans')).addChildren(params) for child in self.settings.child(('scans')).children(): if child.name() not in children: val = child.value() val.update(dict(checked=True)) child.setValue(val) child.sigValueChanged.emit(child, child.value()) except Exception as e: logger.exception(str(e)) def update_h5file(self, h5file): if self.h5module is not None: self.h5module.h5file = h5file self.update_2Dscans() def update_status(self, txt, status_time=0, log_type=None): """ Show the txt message in the status bar with a delay of status_time ms. =============== =========== ======================= **Parameters** **Type** **Description** *txt* string The message to show *status_time* int the delay of showing *log_type* string the type of the log =============== =========== ======================= """ try: self.ui.statusbar.showMessage(txt, status_time) logger.info(txt) except Exception as e: logger.exception(str(e))
class GuiManager(QtGui.QMainWindow): def __init__(self): super(GuiManager, self).__init__() self.createLayout() def closeEvent(self, event): print 'User asked to close the app' self.stopAcquisition() Parameters.isAppKilled = True #time.sleep(0.1) #Wait for the worker death event.accept() # let the window close def startAcquisition(self): if Parameters.isConnected == False: return Parameters.isSaving = Parameters.paramObject.child( "Saving parameters", "saveResults").value() if Parameters.isSaving == True: theDate = datetime.datetime.today().strftime('%Y%m%d') theTime = datetime.datetime.today().strftime('%H%M%S') theName = Parameters.paramObject.child("Saving parameters", "Experiment name").value() #Check counter increment folderParent = 'C:/InterferometryResults/' + theDate counter = 1 folderFound = False theFolder = folderParent + '/' + theName + '_' + str( counter).zfill(3) if not os.path.exists(folderParent): #Make sure parent exist os.makedirs(folderParent) while folderFound == False and counter < 1000: #Loop until folder is found if not os.path.exists(theFolder): #Make sure parent exist os.makedirs(theFolder) folderFound = True else: counter = counter + 1 theFolder = folderParent + '/' + theName + '_' + str( counter).zfill(3) if counter < 1000: Parameters.savingFolder = theFolder if not os.path.exists(theFolder): os.makedirs(theFolder) else: Parameters.isSaving = False #Should not happen. self.btStart.setEnabled(False) self.btStop.setEnabled(True) self.btOpen.setEnabled(False) #Get the parameter values Parameters.nSamples = Parameters.paramObject.child( "Acquisition Parameters", "Number of samples to acquire").value() acqMode = Parameters.paramObject.child("Trigger Options", "Acquisition mode").value() triggerAmplitude = Parameters.paramObject.child( "Trigger Options", "Trigger amplitude").value() motorSpeed = Parameters.paramObject.child( "Acquisition Parameters", "Rotation speed (%)").value() + 28 #28 is calibration nBlocks = int(math.ceil(float(Parameters.nSamples) / 512)) #Must be a multiple of 512 Parameters.nSamples = nBlocks * 512 Parameters.paramObject.child("Acquisition Parameters", "Number of samples to acquire").setValue( Parameters.nSamples) #Set frequency in free mode. These settings could be optimized depending on the computer. freq = 2500000 #10im/s if Parameters.nSamples > 100352: freq = 5000000 #5im/s if Parameters.nSamples > 300032: freq = 12500000 #2im/s if Parameters.nSamples > 1000000: freq = 25000000 #1im/s if Parameters.nSamples > 2000000: freq = 50000000 #0.5im/s #Start the acquisition on the FPGA data = [] data = array('i') data.append(1) data.append(freq) data.append(2) data.append(acqMode) data.append(3) data.append(nBlocks) data.append(4) data.append(motorSpeed) data.append(5) data.append(triggerAmplitude) data.append(6) data.append(0) theBytes = struct.pack('i' * len(data), *data) buf = bytearray(theBytes) Parameters.dev.WriteToPipeIn(128, buf) Parameters.dev.SetWireInValue(0, 1 + 2 + 8, 1 + 2 + 8) Parameters.dev.UpdateWireIns( ) #Enable DDR2 reading and writing and activate memory. time.sleep(0.1) #Wait to make sure everything is ready. TODO: Reduce this. Parameters.dev.SetWireInValue(0, 4, 4) Parameters.dev.UpdateWireIns() #Start acquisition clock. #self.plotTr.enableAutoRange('xy', True) ## stop auto-scaling after the first data set is plotted Parameters.theQueue = Queue.Queue() #Create the queue #self.DisplayUpdater(0) self.workThread.start() #Start the display worker def stopAcquisition(self): print 'Stopping...' if hasattr(self, 'tDisplay'): self.tDisplay.cancel() self.workThread.exitWorker() while self.workThread.isRunning == True: time.sleep( 0.01) #Wait for the worker death, before resetting the board Parameters.dev.SetWireInValue(0, 0, 1 + 2 + 4 + 8) Parameters.dev.UpdateWireIns() #Reset board. #Save format: Results / YYMMDD / ExperimentName_Counter. Could also use the time. if Parameters.isSaving == True: #global state state = Parameters.paramObject.saveState() file = open(Parameters.savingFolder + '\Parameters.txt', 'w') pickle.dump(state, file) file.close() print 'Stopped.' self.btStart.setEnabled(True) self.btStop.setEnabled(False) self.btOpen.setEnabled(True) ''' def DisplayUpdater(self,dummy): if Parameters.isAppKilled == True: return; if Parameters.theQueue.empty(): print 'no data...' while not Parameters.theQueue.empty(): #Empty the display queue data = Parameters.theQueue.get() #print 'data available!' if 'data' in locals(): #print 'display data' + str(data[2]) self.setDataCurveTr(data) tDisplay = Timer(2, self.DisplayUpdater, [0],{}) tDisplay.start() ''' def motorSpeedChanged(self, value): newSpeed = Parameters.paramObject.child("Acquisition Parameters", "Rotation speed (%)").value() newSpeed = newSpeed + 28 #Calibration #Debug #Parameters.paramObject.child("Acquisition Parameters", "Rotation speed (%)").setOpts(enabled=False) #Parameters.paramObject.child("Acquisition Parameters", "Rotation speed (%)").setOpts(visible=False) #Parameters.paramObject.child("Acquisition Parameters", "Rotation speed (%)").setOpts(value=200) data = [] data = array('i') data.append(4) data.append(newSpeed) data.append(0) #Minimum size is 8 'int' data.append(0) data.append(0) data.append(0) data.append(0) data.append(0) theBytes = struct.pack('i' * len(data), *data) Parameters.bufferToDev = bytearray(theBytes) Parameters.bufferToDevReady = True def triggerChanged(self, value): acqMode = Parameters.paramObject.child("Trigger Options", "Acquisition mode").value() triggerAmplitude = Parameters.paramObject.child( "Trigger Options", "Trigger amplitude").value() data = [] data = array('i') data.append(2) data.append(acqMode) data.append(5) data.append(triggerAmplitude) data.append(0) data.append(0) data.append(0) data.append(0) theBytes = struct.pack('i' * len(data), *data) print str(theBytes) Parameters.bufferToDev = bytearray(theBytes) Parameters.bufferToDevReady = True def triggerHalfSwitch(self): Parameters.triggerToDev = True def saveParam(self): #global state state = Parameters.paramObject.saveState() file = open('dump.txt', 'w') pickle.dump(state, file) file.close() def restoreParam(self): #lobal state file = open('dump.txt', 'r') state = pickle.load(file) #add = Parameters.paramObject['Save/Restore functionality', 'Restore State', 'Add missing items'] #rem = Parameters.paramObject['Save/Restore functionality', 'Restore State', 'Remove extra items'] Parameters.paramObject.restoreState(state, addChildren=False, removeChildren=False) #Set the status text (system connected or not) def setStatus(self, isConnected, isError=False): def dotChange( item, nDots ): #Timer function to display a varying number of dots after "retrying" if Parameters.isConnected == True: return if Parameters.isAppKilled == True: return textNotConnected = "System not connected, retrying" item.setText(textNotConnected + ".") #Number of dots varies from 1 to 5 if nDots == 1: textDots = "." nDots = 2 elif nDots == 2: textDots = ".." nDots = 3 elif nDots == 3: textDots = "..." nDots = 4 elif nDots == 4: textDots = "...." nDots = 5 else: textDots = "....." nDots = 1 item.setForeground(QtGui.QColor("red")) item.setText(textNotConnected + textDots) self.timerDotChange = Timer(0.25, dotChange, [self.itemStatus, nDots]) self.timerDotChange.start() if (isError == True): #System not connected print "Error" if hasattr(self, 'timerDotChange'): self.timerDotChange.cancel() time.sleep(0.5) self.itemStatus.setForeground(QtGui.QColor("red")) self.itemStatus.setText( "Error with system. Please restart the application and the system." ) elif (isConnected == False): #System not connected nDots = 1 if hasattr(self, 'timerDotChange'): self.timerDotChange.cancel() self.timerDotChange = Timer(0.25, dotChange, [self.itemStatus, nDots]) self.timerDotChange.start() else: print "Connected" if hasattr(self, 'timerDotChange'): self.timerDotChange.cancel() time.sleep(0.1) self.itemStatus.setForeground(QtGui.QColor("green")) self.itemStatus.setText("System connected.") print "Connected2" #Set the status text (system connected or not) def setCameraStatus(self, isConnected): def dotChange( item, nDots ): #Timer function to display a varying number of dots after "retrying" if Parameters.isCameraConnected == True: return if Parameters.isAppKilled == True: return textNotConnected = "Camera not connected, retrying" item.setText(textNotConnected + ".") #Number of dots varies from 1 to 5 if nDots == 1: textDots = "." nDots = 2 elif nDots == 2: textDots = ".." nDots = 3 elif nDots == 3: textDots = "..." nDots = 4 elif nDots == 4: textDots = "...." nDots = 5 else: textDots = "....." nDots = 1 item.setForeground(QtGui.QColor("red")) item.setText(textNotConnected + textDots) self.timerDotChangeCamera = Timer(0.25, dotChange, [self.itemCameraStatus, nDots]) self.timerDotChangeCamera.start() if (isConnected == False): #System not connected print 'cam oups' nDots = 1 if hasattr(self, 'timerDotChangeCamera'): self.timerDotChangeCamera.cancel() self.timerDotChangeCamera = Timer(0.25, dotChange, [self.itemCameraStatus, nDots]) self.timerDotChangeCamera.start() else: print "Camera connected" if hasattr(self, 'timerDotChangeCamera'): self.timerDotChangeCamera.cancel() time.sleep(0.1) self.itemCameraStatus.setForeground(QtGui.QColor("green")) self.itemCameraStatus.setText("Camera connected.") print "Camera connected2" #Set the status text (system connected or not) def setInfo(self, text): self.tbInfo.append(text) #Set the status text (system connected or not) def setWire(self, mem1, mem2, mem3, mem4, maxValPos): self.itemMemory.setText("Acquisition board memory usage:\nDDR2: " + str(mem1) + " bytes.\nFIFO in: " + str(mem2) + " bytes.\nFIFO out: " + str(mem3) + " bytes.\nFIFO out (minimum): " + str(mem4) + " bytes.") self.itemMaxValPos.setText("Maximum RF value position: " + str(maxValPos)) def createLayout(self): print 'Creating layout...' self.setWindowTitle('Interferometry Acquisition GUI') #self.widget = QtGui.QWidget() #self.setCentralWidget(self.widget) self.layout = pg.LayoutWidget() #self.widget.setLayout(self.layout) self.setCentralWidget(self.layout) #Create GUI sizePolicyBt = QtGui.QSizePolicy(1, 1) sizePolicyBt.setHorizontalStretch(0) sizePolicyBt.setVerticalStretch(0) self.btOpen = QtGui.QPushButton("Open\nprevious results") sizePolicyBt.setHeightForWidth( self.btOpen.sizePolicy().hasHeightForWidth()) self.btOpen.setSizePolicy(sizePolicyBt) self.btOpen.setStyleSheet( "background-color: yellow; font-size: 16px; font: bold") self.btStart = QtGui.QPushButton("Start\nacquisition") sizePolicyBt.setHeightForWidth( self.btStart.sizePolicy().hasHeightForWidth()) self.btStart.setSizePolicy(sizePolicyBt) self.btStart.setStyleSheet( "background-color: green; font-size: 16px; font: bold") self.btStart.clicked.connect(self.startAcquisition) self.btStop = QtGui.QPushButton("Stop\nacquisition") sizePolicyBt.setHeightForWidth( self.btStop.sizePolicy().hasHeightForWidth()) self.btStop.setSizePolicy(sizePolicyBt) self.btStop.setStyleSheet( "background-color: red; font-size: 16px; font: bold") self.btStop.clicked.connect(self.stopAcquisition) self.btStop.setEnabled(False) self.paramTree = ParameterTree() self.paramTree.setParameters(Parameters.paramObject, showTop=False) self.paramTree.setWindowTitle('pyqtgraph example: Parameter Tree') self.paramTree.setMinimumWidth(350) self.paramTree.setMaximumWidth(350) sizePolicyPt = QtGui.QSizePolicy(1, 1) sizePolicyPt.setHorizontalStretch(QtGui.QSizePolicy.Fixed) sizePolicyPt.setVerticalStretch(1) self.paramTree.setSizePolicy(sizePolicyPt) ## Create random 2D data data = np.random.normal(size=( 512, 512)) + pg.gaussianFilter(np.random.normal(size=(512, 512)), (5, 5)) * 20 + 100 data = data[:, :, np.newaxis] data = data.repeat(3, 2) self.plotTl = pg.GraphicsView() self.plotTlImage = pg.ImageItem(data[:, :, :]) #parent=self.plotTl self.plotTlViewBox = pg.ViewBox() self.plotTl.setCentralWidget(self.plotTlViewBox) self.plotTlViewBox.addItem(self.plotTlImage) self.plotTr = pg.PlotWidget(title="Interferometry", labels={ 'left': 'Signal amplitude (A.U.)', 'bottom': 'Distance (mm)' }) #self.plotTlViewBox2.addItem(self.plotTr) self.plotTrCurve = self.plotTr.plot(pen=(255, 0, 0), name='C1') #Red self.plotTrCurve2 = self.plotTr.plot(pen=(0, 255, 0), name='C2') #Green #self.plotTlViewBox2.enableAutoRange('xy', True) ## stop auto-scaling after the first data set is plotted #self.plotTr.addLegend('Test') self.plotTr.setYRange(-1000, 1000) self.plotBl = pg.PlotWidget(title="Distance", labels={ 'left': 'Distance (mm)', 'bottom': 'Number of acquisitions' }) self.plotBlCurve = self.plotBl.plot(pen=(255, 0, 0), name='C1') self.plotBl.enableAutoRange( 'xy', True) ## stop auto-scaling after the first data set is plotted self.plotBl.setMaximumWidth(3500) self.tbInfo = QtGui.QTextEdit() self.tbInfo.setEnabled(False) palette = self.tbInfo.palette() palette.setColor(QtGui.QPalette.Base, QtGui.QColor("white")) #White background self.tbInfo.setPalette(palette) self.tbInfo.setTextColor(QtGui.QColor("black")) self.tbInfo.insertPlainText("Useful information will appear here.") #Create list view of multiple items self.tbStatus = QtGui.QListView() self.tbStatus.setEnabled(False) palette = self.tbStatus.palette() palette.setColor(QtGui.QPalette.Base, QtGui.QColor("white")) #White background self.tbStatus.setPalette(palette) itemModelStatus = QtGui.QStandardItemModel(self.tbStatus) self.tbStatus.setModel(itemModelStatus) #Add system status self.itemStatus = QtGui.QStandardItem() self.setStatus(False) itemModelStatus.appendRow(self.itemStatus) #Add camera status self.itemCameraStatus = QtGui.QStandardItem() self.setCameraStatus(False) itemModelStatus.appendRow(self.itemCameraStatus) #Add memory usage self.itemMemory = QtGui.QStandardItem( "Acquisition board memory usage: N/A") self.itemMemory.setForeground(QtGui.QColor("black")) itemModelStatus.appendRow(self.itemMemory) #Add max value position self.itemMaxValPos = QtGui.QStandardItem( "Maximum RF value position: N/A") self.itemMaxValPos.setForeground(QtGui.QColor("black")) itemModelStatus.appendRow(self.itemMaxValPos) #layout.addWidget(QtGui.QLabel("These are two views of the same data. They should always display the same values."), 0, 0, 1, 2) self.layout.addWidget(self.btOpen, 9, 6, 1, 1) self.layout.addWidget(self.btStart, 9, 7, 1, 1) self.layout.addWidget(self.btStop, 9, 8, 1, 1) self.layout.addWidget(self.paramTree, 0, 0, 10, 3) self.layout.addWidget(self.plotTl, 0, 3, 5, 3) self.layout.addWidget(self.plotTr, 0, 6, 5, 3) self.layout.addWidget(self.plotBl, 5, 3, 5, 3) self.layout.addWidget(self.tbInfo, 5, 6, 2, 3) self.layout.addWidget(self.tbStatus, 7, 6, 2, 3) self.layout.layout.setColumnStretch(3, 1) self.layout.layout.setColumnStretch(4, 1) self.layout.layout.setColumnStretch(5, 1) self.layout.layout.setColumnStretch(6, 1) self.layout.layout.setColumnStretch(7, 1) self.layout.layout.setColumnStretch(8, 1) self.show() self.resize(1500, 800) self.move(100, 100) Parameters.paramObject.param('Save/Restore functionality', 'Save State').sigActivated.connect( self.saveParam) Parameters.paramObject.param('Save/Restore functionality', 'Restore State').sigActivated.connect( self.restoreParam) Parameters.paramObject.child( "Acquisition Parameters", "Rotation speed (%)").sigValueChanged.connect( self.motorSpeedChanged) Parameters.paramObject.child( "Trigger Options", "Acquisition mode").sigValueChanged.connect(self.triggerChanged) Parameters.paramObject.child( "Trigger Options", "Trigger amplitude").sigValueChanged.connect(self.triggerChanged) Parameters.paramObject.child( "Trigger Options", "Trigger switch 1/2").sigActivated.connect(self.triggerHalfSwitch) # adding by emitting signal in different thread self.workThread = AcquisitionWorker() self.workThread.updateDataCamera.connect(self.setDataCurveTl) self.workThread.updateDataInterf.connect(self.setDataCurveTr) self.workThread.updateDataDistance.connect(self.setDataCurveBl) self.workThread.updateDataDistance2.connect(self.setDataCurveBl2) self.workThread.updateWire.connect(self.setWire) self.workThread.setStatus.connect(self.setStatus) self.workThread.setInfo.connect(self.setInfo) self.testCount = 0 #Fill the plots with dummy data self.data = np.random.normal(size=(10, 1000)) self.plotTrXAxis = np.arange(1000) * (0.01) self.plotBlXAxis = np.arange(1000) * (1) self.plotTrCurve.setData(x=self.plotTrXAxis, y=self.data[2 % 10], name='C1') self.plotTrCurve2.setData(x=self.plotTrXAxis, y=self.data[3 % 10], name='C2') self.plotBlCurve.setData(x=self.plotBlXAxis, y=self.data[4 % 10], name='C1') self.valueTest = 1 def setDataCurveTl(self, data): self.dataImage1 = data self.plotTlImage.setImage(data) self.testCount = self.testCount + 1 def setDataCurveTr(self, dataIn): self.plotTrCurve.setData(dataIn[0:len(dataIn):2], name='C1') self.plotTrCurve2.setData(dataIn[1:len(dataIn):2], name='C2') def setDataCurveBl(self, dataIn): self.plotBlCurve.setData(dataIn, name='C1') def setDataCurveBl2(self, xIn, dataIn): self.plotBlCurve.setData(x=xIn, y=dataIn, name='C1')