class PlotCurve(CtrlNode): """Generates a plot curve from x/y data""" nodeName = 'PlotCurve' uiTemplate = [ ('color', 'color'), ] def __init__(self, name): CtrlNode.__init__(self, name, terminals={ 'x': { 'io': 'in' }, 'y': { 'io': 'in' }, 'plot': { 'io': 'out' } }) self.item = PlotDataItem() def process(self, x, y, display=True): #print "scatterplot process" if not display: return {'plot': None} self.item.setData(x, y, pen=self.ctrls['color'].color()) return {'plot': self.item}
def update_line(self, index: int, lineplot: pg.PlotDataItem, orientation: str): """Template function for creating callbacks which update every PlotDataItem according to current cursor position.""" linedata = self.data_slice1d(index) if orientation == 'h': lineplot.setData(self.data.axes[index], linedata) else: lineplot.setData(linedata, self.data.axes[index])
def update_line(self, index: int, lineplot: pg.PlotDataItem, orientation: str, _=None): """Template function for creating callbacks which update every PlotDataItem according to current cursor position.""" x = self.cursor.get_cut(index).squeeze() if orientation == 'h': lineplot.setData(self.data.axes[index], x.values) else: lineplot.setData(x.values, self.data.axes[index])
class ComponentPlotWidget(SpectraPlotWidget): def __init__(self, *args, **kwargs): super(ComponentPlotWidget, self).__init__(linePos=800, txtPosRatio=0.35, *args, **kwargs) self.cross = PlotDataItem([800], [0], symbolBrush=(255, 255, 255), symbolPen=(255, 255, 255), symbol='+', symbolSize=25) self._x, self._y = None, None self.ymax, self.zmax = 0, 100 def getEnergy(self): if self._y is not None: x_val = self.line.value() idx = val2ind(x_val, self._x) x_val = self._x[idx] y_val = self._y[idx] txt_html = f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\ X = {x_val: .2f}, Y = {y_val: .4f}</div>' self.txt.setHtml(txt_html) self.cross.setData([x_val], [y_val]) def plot(self, x, y, *args, **kwargs): # set up infinity line and get its position plot_item = self.plotItem.plot(x, y, *args, **kwargs) self.addItem(self.line) self.addItem(self.cross) x_val = self.line.value() idx = val2ind(x_val, x) x_val = x[idx] y_val = y[idx] txt_html = f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\ X = {x_val: .2f}, Y = {y_val: .4f}</div>' self.txt.setHtml(txt_html) self.txt.setZValue(self.zmax - 1) self.cross.setData([x_val], [y_val]) self.cross.setZValue(self.zmax) ymax = max(y) if ymax > self.ymax: self.ymax = ymax self._x, self._y = x, y r = self.txtPosRatio self.txt.setPos(r * x[-1] + (1 - r) * x[0], self.ymax) self.addItem(self.txt) return plot_item
class PlotCurve(CtrlNode): """Generates a plot curve from x/y data""" nodeName = "PlotCurve" uiTemplate = [("color", "color")] def __init__(self, name): CtrlNode.__init__(self, name, terminals={"x": {"io": "in"}, "y": {"io": "in"}, "plot": {"io": "out"}}) self.item = PlotDataItem() def process(self, x, y, display=True): # print "scatterplot process" if not display: return {"plot": None} self.item.setData(x, y, pen=self.ctrls["color"].color()) return {"plot": self.item}
class PlotCurve(CtrlNode): """Generates a plot curve from x/y data""" nodeName = 'PlotCurve' uiTemplate = [ ('color', 'color'), ] def __init__(self, name): CtrlNode.__init__(self, name, terminals={ 'x': {'io': 'in'}, 'y': {'io': 'in'}, 'plot': {'io': 'out'} }) self.item = PlotDataItem() def process(self, x, y, display=True): #print "scatterplot process" if not display: return {'plot': None} self.item.setData(x, y, pen=self.ctrls['color'].color()) return {'plot': self.item}
def set_experiment_view(self): self.ui.graphicsView.clear() plot = PlotDataItem(pen="k") duration = (self.ui.spinBoxExperimentDurationMinutes.value() * 60) + self.ui.spinBoxExperimentDurationSeconds.value() xs = np.linspace(0, duration, 2) ys = [1, 1] plot.setData(xs, ys) self.ui.graphicsView.addItem(plot) for ii in range(len(self.Stims.df)): start = self.Stims.df.time_on.iloc[ii] stop = self.Stims.df.time_off.iloc[ii] if self.Stims.df.message_on.iloc[ii].startswith("w"): box = LinearRegionItem(values=(start, stop), brush=(255, 255, 250, 230), movable=False) self.ui.graphicsView.addItem(box) elif self.Stims.df.message_on.iloc[ii].startswith("n"): on = self.Stims.df.message_on.iloc[ii][1:] r = int(on[:3]) g = int(on[3:6]) b = int(on[6:]) box = LinearRegionItem(values=(start, stop), brush=(r, g, b, 50), movable=False) self.ui.graphicsView.addItem(box) elif self.Stims.df.message_on.iloc[ii].startswith("v"): box = LinearRegionItem(values=(start, stop), brush="k", movable=False) self.ui.graphicsView.addItem(box) self.ui.comboBoxSelectStimId.clear() for stim_id in set(self.Stims.df.id): self.ui.comboBoxSelectStimId.addItem(stim_id)
class Evaluator(QtGui.QMainWindow): """docstring for CXIWindow""" def __init__(self): super(Evaluator, self).__init__() # load and adjust layout dir_ = os.path.dirname(os.path.abspath(__file__)) uic.loadUi(dir_ + '/' + 'layout.ui', self) self.splitterH.setSizes([self.width() * 0.7, self.width() * 0.3]) self.splitterV.setSizes([self.height() * 0.7, self.height() * 0.3]) self.labelItem = PlotDataItem(pen=pg.mkPen('y', width=1, style=QtCore.Qt.SolidLine), name='labels') self.probItem = PlotDataItem(pen=pg.mkPen('c', width=1, style=QtCore.Qt.SolidLine), name='prob') self.evalPlot.addItem(self.labelItem) self.evalPlot.addItem(self.probItem) # initilize some parameters self.frame_id = 0 # setup menu slots self.actionOpenNpz.triggered.connect(self.loadNpz) self.actionOpenModel.triggered.connect(self.loadModel) # setup parameter tree params_list = [ { 'name': u'文件信息', 'type': 'group', 'children': [ { 'name': u'路径', 'type': 'str', 'value': u'未设置', 'readonly': True }, { 'name': u'帧数', 'type': 'str', 'value': u'未设置', 'readonly': True }, { 'name': u'长X高', 'type': 'str', 'value': u'未设置', 'readonly': True }, { 'name': u'模型', 'type': 'str', 'value': u'未设置', 'readonly': True }, ] }, { 'name': u'基本操作', 'type': 'group', 'children': [ { 'name': u'当前帧', 'type': 'int', 'value': self.frame_id }, { 'name': u'开始测试', 'type': 'action' }, ] }, ] self.params = Parameter.create(name='params', type='group', children=params_list) self.parameterTree.setParameters(self.params, showTop=False) # parameter connection self.params.param(u'基本操作', u'当前帧').sigValueChanged.connect( self.frameChangedSlot) self.params.param(u'基本操作', u'开始测试').sigActivated.connect(self.eval) def eval(self): cnn_dir = join(dirname(dirname(abspath(__file__))), 'cnn') sys.path.insert(0, cnn_dir) import cnn_eval ckpt_file = self.model_file[:-5] probs = cnn_eval.eval(data_file=self.data_file, ckpt_file=ckpt_file, crop_size=(100, 270)) self.probs = probs self.probItem.setData(probs[:, 1]) def loadModel(self): fpath = str( QtGui.QFileDialog.getOpenFileName(self, '打开文件', '', 'META File (*.meta)')) self.model_file = fpath self.params.param(u'文件信息', u'模型').setValue(fpath) def frameChangedSlot(self, _, frame_id): self.frame_id = int(frame_id) self.updateDisp() def loadNpz(self): fpath = str( QtGui.QFileDialog.getOpenFileName(self, '打开文件', '', 'NPZ File (*.npz)')) self.data_file = fpath self.data = np.load(fpath) self.params.param(u'文件信息', u'路径').setValue(fpath) shape = self.data['frames'].shape self.params.param(u'文件信息', u'帧数').setValue(shape[0]) self.params.param(u'文件信息', u'长X高').setValue('%dX%d' % (shape[2], shape[1])) self.updateDisp() self.updatePlot() def updateDisp(self): image = self.data['frames'][self.frame_id].T self.imageView.setImage(image, autoRange=False, autoLevels=False, autoHistogramRange=False) def updatePlot(self): labels = self.data['labels'] self.labelItem.setData(labels)
class makeTimeseriesCurveNode(NodeWithCtrlWidget): """Prepare Timeseries for plotting. Generate curve that can be viewed with node *TimeseriesPlot* and pd.Series with datetime stored in Index """ nodeName = "Make Curve" uiTemplate = [ {'name': 'Y:signal', 'type': 'list', 'value': None, 'default': None, 'values': [None], 'tip': 'Signal Data-Values (Y-axis)'}, {'name': 'X:datetime', 'type': 'list', 'value': None, 'default': None, 'values': [None], 'tip': 'Datetime Values (X-axis)'}, {'name': 'tz correct', 'type': 'float', 'value': 0, 'default': 0, 'suffix': ' hours', 'tip': '<float>\nONLY FOR CURVE!!!\nTimezone correction\nNumber of hours to add/substract from result. Due to missing\ntimezone settings it may be nessesary to use this parameter.\nCheck the results manually with *TimeseriesPlot* Node'}, {'name': 'color', 'type': 'color', 'tip': 'Curve color'}, {'name': 'Display Line', 'type': 'bool', 'value': True, 'tip': 'display line-curve between data points', 'children': [ {'name': 'Style', 'type': 'list', 'tip': 'Style', 'values': {'solid': QtCore.Qt.SolidLine, 'dash': QtCore.Qt.DashLine, 'dash-dot': QtCore.Qt.DashDotLine, 'dot-dot': QtCore.Qt.DotLine, 'dash-dot-dot': QtCore.Qt.DashDotDotLine }}, {'name': 'Linewidth', 'type': 'float', 'value': 1., 'limits': (0., 20.), 'step': 0.1, 'tip': 'Linewidth'}, ]}, {'name': 'Display Data Points', 'type': 'bool', 'value': False, 'tip': 'display data points as scatter', 'children': [ {'name': 'Symbol', 'type': 'list', 'tip': 'Symbol for data points', 'value': 'o', 'values': {'circle': 'o', 'triangle': 't', 'square': 's', 'pentagon': 'p', 'hexagon': 'h', 'star': 'star', 'cross': '+', 'diamond': 'd'}}, {'name': 'Size', 'type': 'int', 'value': 5, 'limits': (0, 1000), 'tip': 'Symbol size'}, ]}, ] def __init__(self, name, parent=None): super(makeTimeseriesCurveNode, self).__init__(name, parent=parent, terminals={'df': {'io': 'in'}, 'pd.Series': {'io': 'out'}, 'Curve': {'io': 'out'}}, color=(150, 150, 250, 150)) self._plotRequired = False self.item = PlotDataItem(clipToView=False) def _createCtrlWidget(self, **kwargs): return makeTimeseriesCurveNodeCtrlWidget(**kwargs) def process(self, df): if df is None: del self.item self.item = None return {'Curve': None, 'pd.Series': None } if self.item is None: self.item = PlotDataItem(clipToView=False) colname = [col for col in df.columns if isNumpyNumeric(df[col].dtype)] self._ctrlWidget.param('Y:signal').setLimits(colname) colname = [col for col in df.columns if isNumpyDatetime(df[col].dtype)] self._ctrlWidget.param('X:datetime').setLimits(colname) with BusyCursor(): kwargs = self.ctrlWidget().prepareInputArguments() #self.item = PlotDataItem(clipToView=False) t = df[kwargs['X:datetime']].values # part 1 timeSeries = pd.DataFrame(data=df[kwargs['Y:signal']].values, index=t, columns=[kwargs['Y:signal']]) # part 2 # convert time b = t.astype(np.dtype('datetime64[s]')) timeStamps = b.astype(np.int64)-kwargs['tz correct']*60*60+time.timezone # now create curve pen = fn.mkPen(color=kwargs['color'], width=kwargs['width'], style=kwargs['style']) self.item.setData(timeStamps, df[kwargs['Y:signal']].values, pen=pen, name=kwargs['Y:signal']) self.item.setSymbol(kwargs['symbol']) if kwargs['symbol'] is not None: self.item.setSymbolPen(kwargs['color']) self.item.setSymbolBrush(kwargs['color']) self.item.setSymbolSize(kwargs['symbolSize']) return {'Curve': self.item, 'pd.Series': timeSeries }
class MapViewWidget(DynImageView): sigShowSpectra = Signal(int) def __init__(self, *args, **kwargs): super(MapViewWidget, self).__init__(*args, **kwargs) # self.scene.sigMouseMoved.connect(self.showSpectra) self.scene.sigMouseClicked.connect(self.showSpectra) self.view.invertY(True) # add arrow # self.arrow = ArrowItem(angle=60, headLen=15, tipAngle=45, baseAngle=30, brush = (200, 80, 20)) # self.arrow.setPos(0, 0) self.cross = PlotDataItem([0], [0], symbolBrush=(200, 0, 0), symbolPen=(200, 0, 0), symbol='+', symbolSize=16) self.view.addItem(self.cross) self.cross.hide() #add txt self.txt = TextItem('', anchor=(0, 0)) self.addItem(self.txt) def setEnergy(self, lineobject): E = lineobject.value() # map E to index i = val2ind(E, self.wavenumbers) # print('E:', E, 'wav:', self.wavenumbers[i]) self.setCurrentIndex(i) def showSpectra(self, event): pos = event.pos() if self.view.sceneBoundingRect().contains( pos ): # Note, when axes are added, you must get the view with self.view.getViewBox() mousePoint = self.view.mapSceneToView(pos) x, y = int(mousePoint.x()), int(mousePoint.y()) y = self.row - y - 1 try: ind = self.rc2ind[(y, x)] self.sigShowSpectra.emit(ind) # print(x, y, ind, x + y * self.n_col) #update arrow self.cross.setData([x + 0.5], [self.row - y - 0.5]) self.cross.show() # update text self.txt.setHtml( f'<div style="text-align: center"><span style="color: #FFF; font-size: 8pt">X: {x}</div>\ <div style="text-align: center"><span style="color: #FFF; font-size: 8pt">Y: {y}</div>\ <div style="text-align: center"><span style="color: #FFF; font-size: 8pt">Point: #{ind}</div>' ) except Exception: self.cross.hide() def setHeader(self, header: NonDBHeader, field: str, *args, **kwargs): self.header = header self.field = field imageEvent = next(header.events(fields=['image'])) self.rc2ind = imageEvent['rc_index'] self.wavenumbers = imageEvent['wavenumbers'] # make lazy array from document data = None try: data = header.meta_array(field) self.row = data.shape[1] self.col = data.shape[2] self.txt.setPos(self.col, 0) except IndexError: msg.logMessage( 'Header object contained no frames with field ' '{field}' '.', msg.ERROR) if data is not None: # kwargs['transform'] = QTransform(1, 0, 0, -1, 0, data.shape[-2]) self.setImage(img=data, *args, **kwargs) self._data = data def updateImage(self, autoHistogramRange=True): super(MapViewWidget, self).updateImage(autoHistogramRange) self.ui.roiPlot.setVisible(False) def setImage(self, img, **kwargs): super(MapViewWidget, self).setImage(img, **kwargs) self.ui.roiPlot.setVisible(False) def makeMask(self, thresholds): peak1550 = val2ind(1550, self.wavenumbers) thr1550 = thresholds[0] mask = self._data[peak1550] > thr1550 mask = mask.astype(np.int) return mask
class imMobilize(QtWidgets.QWidget): def __init__(self, parent=None, *args): # run the widget, get the form QtWidgets.QWidget.__init__(self, parent, *args) self.ui = Ui_Form() self.ui.setupUi(self) os.chdir("D:\\") self.working_directory_selected = False """ ============================================================== Connecting the buttons and labels in the serial control group ============================================================== """ self.arduino = Arduino() # populate serial available combobox self.update_serial_available() # connect scan button self.ui.buttonSerialScan.clicked.connect(self.update_serial_available) # connect connect/disconnect button self.ui.buttonSerialConnect.clicked.connect( self.serial_connect_disconnect) # try: # self.serial_connect_disconnect() # except: # QtWidgets.QMessageBox.warning(self, "No controller found", "Could not connect to a suitable controller \n Connect Manually.") self.ui.lineeditWriteToSerial.editingFinished.connect( self.write_to_serial) self.ui.buttonWriteToSerial.clicked.connect(self.write_to_serial) self.ui.checkBoxReadSerialLive.clicked.connect(self.listen_to_serial) self.logged_temperature = np.array([]) self.temp_plot_xvals = np.array([]) """ ===================================================================== Connecting the buttons and controls in the light stim controls group ===================================================================== """ # connect the save_stim button self.ui.buttonSaveStim.clicked.connect(self.save_lightstim) # Style the three sliders (color the handles) # self.ui.sliderRed.setStyleSheet("QSlider::handle:vertical {background-color: red; border: 1px outset; border-radius: 3px;}") # self.ui.sliderGreen.setStyleSheet("QSlider::handle:vertical {background-color: lime; border: 1px outset; border-radius: 3px;}") # self.ui.sliderBlue.setStyleSheet("QSlider::handle:vertical {background-color: cyan; border: 1px outset; border-radius: 3px;}") self.ui.toggleWhiteColor.valueChanged.connect( self.toggle_lightstim_type) self.toggle_lightstim_type() """ ==================================================================== Connecting buttons and controls in vibration stimuli group ==================================================================== """ self.ui.buttonSaveVibrationStim.clicked.connect( self.save_vibration_stim) """ ===================================================================== Connecting the buttons and controls in the stimuli manager group ===================================================================== """ self.Stims = StimuliManager(arduino=self.arduino) self.set_experiment_view() self.set_lightstim_name_lineedit() self.set_vibrationstim_name_lineedit() self.ui.buttonDeleteSelectedStim.clicked.connect( self.delete_selected_stim) self.ui.buttonDeleteAllStims.clicked.connect(self.delete_all_stims) self.ui.buttonLoadStimulusProfile.clicked.connect(self.load_stimuli) self.ui.buttonSaveStimulusProfile.clicked.connect(self.save_stimuli) """ ===================================================================== Connecting the buttons and controls in the MetaData entry group ===================================================================== """ self.ui.buttonSetWorkingDirectory.clicked.connect( self.set_working_directory) self.ui.labelMetaDataDrugName.setVisible(False) self.ui.lineeditMetaDataDrugName.setVisible(False) self.ui.labelMetaDataGenetics.setVisible(False) self.ui.lineeditMetaDataGenetics.setVisible(False) self.ui.spinBoxExperimentDurationMinutes.valueChanged.connect( self.set_experiment_view) self.ui.spinBoxExperimentDurationSeconds.valueChanged.connect( self.set_experiment_view) self.ui.checkBoxExperimentAutonaming.clicked.connect( self.set_experiment_autonaming) self.set_experiment_autonaming() self.experiment_live = False self.ui.buttonStartExperiment.clicked.connect(self.start_experiment) self.path = os.curdir self.experiment_name = self.ui.lineeditExperimentName.text() self.experiment_path = os.path.join(self.path, "Exp_" + self.experiment_name) """ ====================================================================== Connecting buttons and controls to the camera manager group ====================================================================== """ self.detect_cameras() self.ui.buttonCameraConnectDisconnect.clicked.connect( self.camera_connect_disconnect) self.ui.buttonCameraPreview.clicked.connect(self.toggle_preview) self.ui.buttonScanForCameras.clicked.connect(self.detect_cameras) self.ui.spinBoxFramerate.valueChanged.connect( self.set_camera_framerate) for x in range(-2, -12, -1): self.ui.comboboxCameraExposure.addItem(str(2**x)) self.ui.comboboxCameraExposure.currentTextChanged.connect( self.set_camera_exposure) self.ui.sliderCameraBrightness.sliderMoved.connect( self.set_camera_brightness) self.ui.sliderCameraGamma.sliderMoved.connect(self.set_camera_gamma) self.ui.buttonCameraResetProperties.clicked.connect( self.reset_camera_properties) #This is for IR light, so actually belongs to arduino, but it is located here in the GUI. self.ui.sliderIRLight.sliderReleased.connect(self.set_IR_light) """ ====================================================================== Connecting buttons and controls to the video manager ====================================================================== """ self.set_autonaming(True) self.ui.buttonSetPath.clicked.connect(self.set_path) self.ui.checkboxAutoNaming.toggled.connect(self.set_autonaming) self.ui.buttonRecordVideo.clicked.connect(self.record_video) def detect_cameras(self): self.ui.comboBoxConnectedCameras.clear() for ii in range(3): cap = cv2.VideoCapture(ii) if not cap.isOpened(): pass else: cam = "Camera " + str(ii + 1) self.ui.comboBoxConnectedCameras.addItem(cam) def camera_connect_disconnect(self): if not hasattr(self, "cam") or not self.cam.cap.isOpened(): camera_number = int(self.ui.comboBoxConnectedCameras.currentText(). split(" ")[1]) - 1 self.cam = Camera(camera=camera_number) if self.cam.cap.isOpened(): self.ui.groupBoxCameraControls.setEnabled(True) self.ui.groupBoxVideoControls.setEnabled(True) self.ui.comboBoxConnectedCameras.setEnabled(False) self.ui.labelCameraConnectionStatus.setText( "Connected to Camera " + str(camera_number + 1)) self.ui.labelCameraConnectionStatus.setStyleSheet( "color: green") self.ui.buttonCameraConnectDisconnect.setText("Disconnect") self.ui.buttonCameraConnectDisconnect.setStyleSheet( "color: red") t = self.ui.comboboxCameraExposure.findText( str(2**(self.cam.exposure))) self.ui.comboboxCameraExposure.setCurrentIndex(t) self.ui.sliderCameraBrightness.setValue(self.cam.brightness) self.ui.labelCameraBrighness.setNum(self.cam.brightness) self.ui.sliderCameraGamma.setValue(self.cam.gamma * 10) self.ui.labelCameraGamma.setNum(self.cam.gamma) self.ui.sliderCameraGamma.setEnabled(False) else: self.toggle_preview(False) self.cam.stop() self.cam.cap.release() self.ui.buttonCameraPreview.setChecked(False) self.ui.groupBoxCameraControls.setEnabled(False) self.ui.groupBoxVideoControls.setEnabled(False) self.ui.comboBoxConnectedCameras.setEnabled(True) self.ui.buttonCameraConnectDisconnect.setText("Connect") self.ui.buttonCameraConnectDisconnect.setStyleSheet("color: black") self.ui.labelCameraConnectionStatus.setText( "Status: Not Connected") self.ui.labelCameraConnectionStatus.setStyleSheet("color: black") def toggle_lightstim_type(self): val = self.ui.toggleWhiteColor.value() if val == 1: self.lightStimType = "White" self.ui.labelLightStimTypeLED.setEnabled(False) self.ui.labelLightStimTypeWhite.setEnabled(True) self.ui.widgetLEDSliders.setEnabled(False) self.ui.toggleWhiteColor.setStyleSheet( "QSlider::handle:horizontal {background-color: rgba(255,255,255,255); border: 2px outset; width: 10px; height: 10px; border-radius: 5px;}" "QSlider::groove:horizontal {height: 10px; width: 30px; background-color: rgba(255, 0,0, 100); border-radius: 5px; border: 1px solid;}" ) self.ui.sliderRed.setStyleSheet( "QSlider::handle:vertical {background-color: grey; border: 1px outset; border-radius: 3px;}" ) self.ui.sliderGreen.setStyleSheet( "QSlider::handle:vertical {background-color: grey; border: 1px outset; border-radius: 3px;}" ) self.ui.sliderBlue.setStyleSheet( "QSlider::handle:vertical {background-color: grey; border: 1px outset; border-radius: 3px;}" ) if val == 0: self.lightStimType = "LED" self.ui.labelLightStimTypeLED.setEnabled(True) self.ui.labelLightStimTypeWhite.setEnabled(False) self.ui.widgetLEDSliders.setEnabled(True) self.ui.toggleWhiteColor.setStyleSheet( "QSlider::handle:horizontal {background-color: rgba(255,100,0,100); border: 2px outset; width: 10px; height: 10px; border-radius: 5px;}" "QSlider::groove:horizontal {height: 10px; width: 30px; background-color: rgba(255, 255, 255,100); border-radius: 5px; border: 1px solid;}" ) self.ui.sliderRed.setStyleSheet( "QSlider::handle:vertical {background-color: red; border: 1px outset; border-radius: 3px;}" ) self.ui.sliderGreen.setStyleSheet( "QSlider::handle:vertical {background-color: lime; border: 1px outset; border-radius: 3px;}" ) self.ui.sliderBlue.setStyleSheet( "QSlider::handle:vertical {background-color: cyan; border: 1px outset; border-radius: 3px;}" ) def set_experiment_view(self): self.ui.graphicsView.clear() plot = PlotDataItem(pen="k") duration = (self.ui.spinBoxExperimentDurationMinutes.value() * 60) + self.ui.spinBoxExperimentDurationSeconds.value() xs = np.linspace(0, duration, 2) ys = [1, 1] plot.setData(xs, ys) self.ui.graphicsView.addItem(plot) for ii in range(len(self.Stims.df)): start = self.Stims.df.time_on.iloc[ii] stop = self.Stims.df.time_off.iloc[ii] if self.Stims.df.message_on.iloc[ii].startswith("w"): box = LinearRegionItem(values=(start, stop), brush=(255, 255, 250, 230), movable=False) self.ui.graphicsView.addItem(box) elif self.Stims.df.message_on.iloc[ii].startswith("n"): on = self.Stims.df.message_on.iloc[ii][1:] r = int(on[:3]) g = int(on[3:6]) b = int(on[6:]) box = LinearRegionItem(values=(start, stop), brush=(r, g, b, 50), movable=False) self.ui.graphicsView.addItem(box) elif self.Stims.df.message_on.iloc[ii].startswith("v"): box = LinearRegionItem(values=(start, stop), brush="k", movable=False) self.ui.graphicsView.addItem(box) self.ui.comboBoxSelectStimId.clear() for stim_id in set(self.Stims.df.id): self.ui.comboBoxSelectStimId.addItem(stim_id) # def checkStimSafeguard(self): # if self.ui.checkboxDirectStim.isChecked() is False: # self.ui.listwidgetAllStims.itemDoubleClicked.disconnect(self.single_stim) # elif self.ui.checkboxDirectStim.isChecked(): # self.ui.listwidgetAllStims.itemDoubleClicked.connect(self.single_stim) def update_serial_available(self): """Scans the available comports and updates the combobox with the resulst""" self.ui.comboboxSerialAvailable.clear() self.arduino.scan_comports() for port in list(self.arduino.port_dict.keys()): self.ui.comboboxSerialAvailable.addItem(port) def serial_connect_disconnect(self): """Connects to selected serial if disconnected and vice versa""" if self.ui.buttonSerialConnect.text().lower() == "connect": port = self.ui.comboboxSerialAvailable.currentText() self.arduino.connect(port) if self.arduino.ser.isOpen(): self.ui.labelSerialConnected.setText("Connected: " + self.arduino.port) self.ui.labelSerialConnected.setStyleSheet("color: green") self.ui.buttonSerialConnect.setText("Disconnect") self.ui.checkBoxReadSerialLive.setEnabled(True) self.ui.sliderIRLight.setEnabled(True) elif self.ui.buttonSerialConnect.text().lower() == "disconnect": self.ui.checkBoxReadSerialLive.setChecked(False) self.arduino.disconnect() if self.arduino.ser.isOpen() == False: self.ui.labelSerialConnected.setText("Status: Not connected") self.ui.labelSerialConnected.setStyleSheet("color: black") self.ui.buttonSerialConnect.setText("Connect") self.ui.checkBoxReadSerialLive.setEnabled(False) self.ui.checkBoxReadSerialLive.setChecked(False) self.ui.sliderIRLight.setEnabled(False) def listen_to_serial(self): """Starts a thread that listens on the self.Arduino.ser serial connection and updates the GUI with incoming messages""" if self.ui.checkBoxReadSerialLive.isChecked(): self.ui.graphicsViewSerial.clear() self.temperature_plot = PlotDataItem(pen=(0, 153, 153)) self.temperature_plot_2 = PlotDataItem(pen=(255, 128, 0)) self.temperature_plot.setDownsampling(auto=True, method="mean") self.temperature_plot_2.setDownsampling(auto=True, method="mean") self.ui.graphicsViewSerial.addItem(self.temperature_plot) self.ui.graphicsViewSerial.addItem(self.temperature_plot_2) t = Thread(target=self.start_listening, args=()) t.start() def start_listening(self): """To be started in separate thread: listens to Serial port and updates gui via separate method if new message comes in""" self.arduino.ser.flushInput() self.logged_temperature = np.array([]) self.logged_temperature_2 = np.array([]) self.logged_temperature_time = np.array([]) start = time.time() while self.ui.checkBoxReadSerialLive.isChecked( ) and self.arduino.ser.isOpen(): if self.arduino.ser.in_waiting > 0: message = self.arduino.ser.readline().decode() elapsed_time = time.time() - start self.update_serial_display(message, np.round(elapsed_time, 2)) else: time.sleep(0.01) else: pass def update_serial_display(self, message, elapsed_time): temp1, temp2 = message.split(" ") self.ui.labelSerialLiveIncoming.setText(str(elapsed_time)) self.ui.labelSerialLiveTemperature.setText(message) temp1 = float(temp1) temp2 = float(temp2) self.logged_temperature_time = np.hstack( [self.logged_temperature_time, elapsed_time]) self.logged_temperature = np.hstack([self.logged_temperature, temp1]) self.logged_temperature_2 = np.hstack( [self.logged_temperature_2, temp2]) self.temperature_plot.setData(x=self.logged_temperature_time, y=self.logged_temperature) self.temperature_plot_2.setData(x=self.logged_temperature_time, y=self.logged_temperature_2) def write_to_serial(self): """Send message in the lineEdit widget to the serial port""" message = self.ui.lineeditWriteToSerial.text() self.arduino.write(message) self.ui.lineeditWriteToSerial.clear() def set_lightstim_name_lineedit(self): c = 1 while "LightStim" + str(len(self.Stims.df) + c) in self.Stims.df.id: c += 1 newname = "LightStim" + str(len(self.Stims.df) + c) self.ui.lineeditLightStimName.setText(newname) def save_lightstim(self): """Takes the current settings in the lightstim box and adds them to stim manager""" start_time = self.ui.spinboxStimStart.value() stop_time = self.ui.spinboxStimDuration.value() + start_time stim_id = self.ui.lineeditLightStimName.text() if self.lightStimType == "LED": start_message = "n" for slider in [ self.ui.sliderRed, self.ui.sliderGreen, self.ui.sliderBlue ]: start_message += str(slider.value()).zfill(3) stop_message = "nC" elif self.lightStimType == "White": start_message = "wS" stop_message = "wC" self.Stims.add_stimulus(start_time, stop_time, start_message, stop_message, stim_id) self.set_lightstim_name_lineedit() self.ui.comboBoxSelectStimId.clear() stim_ids = list(set(self.Stims.df.id)) stim_ids.sort() for stim_id in stim_ids: self.ui.comboBoxSelectStimId.addItem(stim_id) self.set_experiment_view() def set_vibrationstim_name_lineedit(self): c = 1 while "VibrationStim" + str(len(self.Stims.df) + c) in self.Stims.df.id: c += 1 newname = "VibrationStim" + str(len(self.Stims.df) + c) self.ui.lineeditVibrationStimName.setText(newname) def save_vibration_stim(self): """takes the current settings in the vibration stim box and adds it to the stim manager""" start_time = self.ui.spinBoxVibrationStart.value() duration = self.ui.spinBoxVibrationDuration.value() freq = self.ui.spinBoxVibrationFrequency.value() start_message = "v" + str(freq).zfill(3) + str(int( duration * 1000)).zfill(4) stop_message = "" stim_id = self.ui.lineeditVibrationStimName.text() self.Stims.add_stimulus(start_time, start_time + duration, start_message, stop_message, stim_id) self.set_vibrationstim_name_lineedit() self.ui.comboBoxSelectStimId.clear() stim_ids = list(set(self.Stims.df.id)) stim_ids.sort() for stim_id in stim_ids: self.ui.comboBoxSelectStimId.addItem(stim_id) self.set_experiment_view() def delete_selected_stim(self): stim_id = self.ui.comboBoxSelectStimId.currentText() self.Stims.delete_stimulus(stim_id) self.set_experiment_view() def delete_all_stims(self): self.Stims.delete_all_stims() self.set_experiment_view() def load_stimuli(self): file = QtWidgets.QFileDialog.getOpenFileName(self, "Select a Stimuli File") file = file[0] if os.path.exists(file) and file.endswith(".txt"): df = pd.read_csv(file, delimiter="\t") self.Stims.df = df self.set_experiment_view() elif not file: pass else: QtWidgets.QMessageBox.warning(self, "Invalid Filename", "Invalid Stimulus File selected.") def save_stimuli(self): filename_selected = False while not filename_selected: filename = QtWidgets.QFileDialog.getSaveFileName( self, "Save Stimuli file as")[0] if not filename: break else: if not filename.endswith(".txt"): filename += ".txt" self.Stims.df.to_csv(filename, sep="\t") filename_selected = True def toggle_preview(self, event): if event: self.cam.preview() self.ui.buttonCameraPreview.setText("Stop") self.ui.buttonCameraPreview.setStyleSheet("color: red") else: self.ui.buttonCameraPreview.setText("Preview") self.ui.buttonCameraPreview.setStyleSheet("color: Black") self.cam.stop() def set_autonaming(self, bool): if bool: self.ui.lineeditVideoName.setText( time.strftime("%Y%m%d_%H%M%S_video")) def set_experiment_autonaming(self): if self.ui.checkBoxExperimentAutonaming.isChecked(): name = time.strftime("%Y%m%d_%H%M%S") name += "_" name += str(self.ui.spinboxCrowdsize.value()) name += "_" name += str(self.ui.spinBoxExperimentDurationMinutes.value()) + "m" name += str( self.ui.spinBoxExperimentDurationSeconds.value()) + "s_" if self.ui.checkboxMetaDataDrugs.isChecked(): name += self.ui.lineeditMetaDataDrugName.text() + "_" else: name += "None_" if self.ui.checkboxMetaDataGenetics.isChecked(): name += self.ui.lineeditMetaDataGenetics.text() + "_" else: name += "None_" if len(self.Stims.df) > 0: name += "Light" else: name += "None" self.ui.lineeditExperimentName.setText(name) else: pass def set_camera_framerate(self, fps): self.cam.framerate = fps # if self.cam.do_gamma == False: # self.ui.sliderCameraGamma.setVisible(False) # self.ui.labelCameraGamma.setVisible(False) # else: # self.ui.sliderCameraGamma.setVisible(True) # self.ui.labelCameraGamma.setVisible(True) def set_camera_brightness(self, brightness): self.cam.brightness = brightness def set_camera_gamma(self, gamma): gamma = gamma / 10 self.cam.gamma = gamma self.ui.labelCameraGamma.setNum(gamma) def set_camera_exposure(self, exp): # If set exposure is longer than framerate allows, then adjust framerate exp = float(exp) if exp > (1 / self.cam.framerate): print( "\n WARNING: Adjusting framerate to accomodate for exposure \n" ) # self.set_camera_framerate(1 / exp) self.ui.spinBoxFramerate.setValue(int(1 / exp)) exp = np.log2(exp) self.cam.exposure = exp def set_IR_light(self): val = self.ui.sliderIRLight.value() self.arduino.write("i" + str(val).zfill(3)) sys.stdout.write('\n IR Light set to {}. {}% of power'.format( val, (val / 255) * 100)) def set_path(self): self.path = QtGui.QFileDialog.getExistingDirectory() print(self.path) def set_working_directory(self): self.working_directory_selected = False while not self.working_directory_selected: self.path = QtGui.QFileDialog.getExistingDirectory() if os.path.exists(self.path): os.chdir(self.path) self.working_directory_selected = True else: q = QtWidgets.QMessageBox.question( self, "No path selected", "Do you wish to not select a path? \n Path then defaults to D:\\" ) if q == QtWidgets.QMessageBox.Yes: self.path = "D:\\" self.working_directory_selected = True def reset_camera_properties(self): self.cam.brightness = 0 self.ui.sliderCameraBrightness.setValue(0) self.ui.labelCameraBrighness.setNum(0) self.cam.exposure = -5.0 t = self.ui.comboboxCameraExposure.findText(str(2**-5)) self.ui.comboboxCameraExposure.setCurrentIndex(t) self.cam.gamma = 1 self.ui.sliderCameraGamma.setValue(10) self.ui.labelCameraGamma.setNum(1) self.cam.framerate = 30 self.ui.spinBoxFramerate.setValue(30) def record_video(self, event): if event: self.ui.buttonRecordVideo.setText("Stop") self.ui.buttonRecordVideo.setStyleSheet("color:red") self.ui.groupBoxCameraControls.setEnabled(False) self.ui.groupBoxCameraConnection.setEnabled(False) if self.ui.checkboxAutoNaming.isChecked(): self.ui.lineeditVideoName.setText( time.strftime("%Y%m%d_%H%M" + "_experiment")) name = os.path.join(self.path, self.ui.lineeditVideoName.text()) duration = self.ui.spinBoxVideoTimeMinutes.value() * 60 duration += self.ui.spinBoxVideoTimeSeconds.value() preview = self.ui.checkBoxPreviewWhileRecording.isChecked() self.cam.video(name, duration, preview) t = Thread(target=self.wait_for_recording_end) t.start() else: self.ui.groupBoxCameraControls.setEnabled(True) self.ui.groupBoxCameraConnection.setEnabled(True) self.ui.buttonRecordVideo.setText("Record") self.ui.buttonRecordVideo.setStyleSheet("color:Black") self.cam.stop() def wait_for_recording_end(self): time.sleep(0.1) while self.cam.alive == True: time.sleep(0.1) else: self.record_video(False) # self.ui.buttonRecordVideo.setChecked(False) # """ ========================================================================================== METHODS TO RUN AN ENTIRE EXPERIMENT ========================================================================================== """ def start_experiment(self, event): if event: if not hasattr(self, "cam"): QtWidgets.QMessageBox.warning( self, "Not Connected Warning", "No open connection with Camera") self.ui.buttonStartExperiment.setChecked(False) elif not self.cam.cap.isOpened(): QtWidgets.QMessageBox.warning( self, "Not Connected Warning", "No open connection with Camera") self.ui.buttonStartExperiment.setChecked(False) elif not self.arduino.connected: QtWidgets.QMessageBox.warning( self, "Not Connected Warning", "No open connection with Controller") self.ui.buttonStartExperiment.setChecked(False) else: if not self.working_directory_selected: QtWidgets.QMessageBox.warning( self, "No valid working directory", "Select a directory to save your experiment") self.set_working_directory() self.experiment_live = True self.ui.buttonStartExperiment.setStyleSheet( 'color: rgba(255,0,0,255)') self.ui.buttonStartExperiment.setText('Interrupt') self.set_experiment_autonaming() self.ui.widgetMetaData.setEnabled(False) self.experiment_name = self.ui.lineeditExperimentName.text() self.experiment_path = os.path.join( self.path, "Exp_" + self.experiment_name) if not os.path.exists( self.experiment_path) or not os.path.isdir( self.experiment_path): os.mkdir(self.experiment_path) if self.ui.checkBoxReadSerialLive.isChecked(): self.ui.checkBoxReadSerialLive.setChecked(False) self.ui.checkBoxReadSerialLive.setChecked(True) self.listen_to_serial() duration = ( self.ui.spinBoxExperimentDurationMinutes.value() * 60) + self.ui.spinBoxExperimentDurationSeconds.value() preview = self.ui.checkBoxPreviewWhileRecording.isChecked() self.cam.video( os.path.join(self.experiment_path, self.experiment_name), duration, preview) self.Stims.start_stimuli() t = Thread(target=self.wait_for_experiment_end, args=()) t.start() else: self.experiment_live = False self.cam.stop() self.ui.checkBoxReadSerialLive.setChecked(False) self.ui.buttonStartExperiment.setStyleSheet("color: Black") self.ui.buttonStartExperiment.setText("Start Experiment") df = pd.DataFrame() df["date"] = [time.strftime("%Y%m%d")] df["time"] = [time.strftime("%H%M%S")] df["duration"] = [ (self.ui.spinBoxExperimentDurationMinutes.value() * 60) + self.ui.spinBoxExperimentDurationSeconds.value() ] df["drugs"] = [self.ui.lineeditMetaDataDrugName.text()] df["genetics"] = [self.ui.lineeditMetaDataGenetics.text()] df["age"] = [self.ui.spinboxAge] df["framerate"] = [self.cam.framerate] df["dechorionated"] = [ self.ui.checkboxMetaDataDechorionation.isChecked() ] df["exposture"] = [ float(self.ui.comboboxCameraExposure.currentText()) ] df["gamma"] = [self.cam.gamma] df["brightness"] = [self.cam.brightness] df["infrared"] = [self.ui.sliderIRLight.value()] df.to_csv(os.path.join(self.experiment_path, "metadata.txt"), sep="\t") self.Stims.df.to_csv(os.path.join(self.experiment_path, "stimuli_profile.txt"), sep="\t") df = pd.DataFrame(np.hstack([ self.logged_temperature_time.reshape(-1, 1), self.logged_temperature.reshape(-1, 1), self.logged_temperature_2.reshape(-1, 1) ]), columns=["time", "temperature", "temperature2"]) df.set_index("time", inplace=True) df.to_csv(os.path.join(self.experiment_path, "logged_temperatures.txt"), sep="\t") self.ui.widgetMetaData.setEnabled(True) def wait_for_experiment_end(self): time.sleep(0.2) # Give camera a chance to start. while self.cam.alive: time.sleep(0.2) else: if self.experiment_live: self.start_experiment(False) self.ui.buttonStartExperiment.setChecked(False) # def start_experiment(self): # # if self.experiment_live: # self.experiment_live = False # self.cam.stop() # self.ui.checkBoxReadSerialLive.setChecked(False) # self.ui.buttonStartExperiment.setStyleSheet("color: Black") # self.ui.buttonStartExperiment.setText("Start Experiment") # # df = pd.DataFrame() # df["date"] = [time.strftime("%Y%m%d")] # df["time"] = [time.strftime("%H%M%S")] # df["duration"] = [(self.ui.spinBoxExperimentDurationMinutes.value() * 60) + self.ui.spinBoxExperimentDurationSeconds.value()] # df["drugs"] = [self.ui.lineeditMetaDataDrugName.text()] # df["genetics"] = [self.ui.lineeditMetaDataGenetics.text()] # df["framerate"] = [self.cam.framerate] # df["dechorionated"] = [self.ui.checkboxMetaDataDechorionation.isChecked()] # df["exposture"] = [float(self.ui.comboboxCameraExposure.currentText())] # df["gamma"] = [self.cam.brightness] # df["brightness"] = [self.cam.brightness] # df["infrared"] = [self.ui.sliderIRLight.value] # # # df.to_csv(os.path.join(self.experiment_path, "metadata.txt"), sep="\t") # # self.Stims.df.to_csv(os.path.join(self.experiment_path, "stimuli_profile.txt"), sep="\t") # df = pd.DataFrame(np.hstack([self.logged_temperature_time.reshape(-1,1), self.logged_temperature.reshape(-1,1), self.logged_temperature_2.reshape(-1,1)]), columns = ["time", "temperature", "temperature2"]) # df.set_index("time", inplace=True) # df.to_csv(os.path.join(self.experiment_path, "logged_temperatures.txt"), sep="\t") # # # self.ui.groupboxMetaData.setEnabled(True) # # elif not self.experiment_live: # if not hasattr(self, "cam"): # QtWidgets.QMessageBox.warning(self, "Not Connected Warning", "No open connection with Camera or Controller") # elif self.cam.cap.isOpened() != True or self.arduino.ser.isOpen() != True: # QtWidgets.QMessageBox.warning(self, "Not Connected Warning", "No open connection with Camera or Controller") # else: # self.experiment_live = True # self.ui.buttonStartExperiment.setStyleSheet('color: rgba(255,0,0,255)') # self.ui.buttonStartExperiment.setText('Interrupt') # # self.set_experiment_autonaming() # self.ui.groupboxMetaData.setEnabled(False) # self.experiment_name = self.ui.lineeditExperimentName.text() # # self.experiment_path = os.path.join(self.path, "Exp_" + self.experiment_name) # if not os.path.exists(self.experiment_path) or not os.path.isdir(self.experiment_path): # os.mkdir(self.experiment_path) # # if self.ui.checkBoxReadSerialLive.isChecked(): # self.ui.checkBoxReadSerialLive.setChecked(False) # self.ui.checkBoxReadSerialLive.setChecked(True) # self.listen_to_serial() # # duration = (self.ui.spinBoxExperimentDurationMinutes.value() * 60) + self.ui.spinBoxExperimentDurationSeconds.value() # self.cam.video(os.path.join(self.experiment_path, self.experiment_name), duration) # self.Stims.start_stimuli() # t = Thread(target=self.wait_for_experiment_end, args=()) # t.start() # def wait_for_experiment_end(self): # time.sleep(0.2) # Give camera a chance to start. # while self.cam.alive: # time.sleep(0.1) # else: # if self.experiment_live: # self.start_experiment() def closeEvent(self, event): QtWidgets.QMessageBox.warning(self, "Closing", "Nothing you can do about it") self.arduino.ser.close() self.cam.cap.release() event.accept() print("yay")
class baselinePlotWidget(SpectraPlotWidget): def __init__(self): super(baselinePlotWidget, self).__init__() self.line.setValue(800) self.txt = TextItem('', anchor=(0, 0)) self.cross = PlotDataItem([800], [0], symbolBrush=(255, 255, 0), symbolPen=(255, 255, 0), symbol='+',symbolSize=20) self.line.sigPositionChanged.connect(self.getMu) self._mu = None def plot(self, x, y, *args, **kwargs): # set up infinity line and get its position self.plotItem.plot(x, y, *args, **kwargs) self.addItem(self.line) self.addItem(self.cross) x_val = self.line.value() if x_val == 0: y_val = 0 else: idx = val2ind(x_val, self.wavenumbers) x_val = self.wavenumbers[idx] y_val = y[idx] if not self._meanSpec: txt_html = f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\ Spectrum #{self.spectrumInd}</div>' else: txt_html = f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\ {self._mean_title}</div>' txt_html += f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\ X = {x_val: .2f}, Y = {y_val: .4f}</div>' self.txt = TextItem(html=txt_html, anchor=(0, 0)) ymax = np.max(y) self._y = y r = self.txtPosRatio self.txt.setPos(r * x[-1] + (1 - r) * x[0], 0.95 * ymax) self.cross.setData([x_val], [y_val]) self.addItem(self.txt) def getMu(self): if self._mu is not None: x_val = self.line.value() if x_val == 0: y_val = 0 else: idx = val2ind(x_val, self._x) x_val = self._x[idx] y_val = self._mu[idx] txt_html = f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\ X = {x_val: .2f}, Y = {y_val: .4f}</div>' self.txt.setHtml(txt_html) self.cross.setData([x_val], [y_val]) def addDataCursor(self, x, y): self.addItem(self.line) self.addItem(self.cross) ymax = np.max(y) self.txt.setText('') r = self.txtPosRatio self.txt.setPos(r * x[-1] + (1 - r) * x[0], 0.95 * ymax) self.addItem(self.txt) self.getMu() def plotBase(self, dataGroup, plotType='raw'): """ make plots for Larch Group object :param dataGroup: Larch Group object :return: """ # add legend self.plotItem.addLegend(offset=(-1, -1)) x = self._x = dataGroup.energy # self._x, self._mu for getEnergy y = self._mu = dataGroup.specTrim n = len(x) # array length self._y = None # disable getEnergy func if plotType == 'raw': self.plotItem.plot(x, y, name='Raw', pen=mkPen('w', width=2)) elif plotType == 'rubber_base': self.plotItem.plot(x, y, name='Raw', pen=mkPen('w', width=2)) self.plotItem.plot(x, dataGroup.rubberBaseline, name='Rubberband baseline', pen=mkPen('g', style=Qt.DotLine, width=2)) self.plotItem.plot(dataGroup.wav_anchor, dataGroup.spec_anchor, symbol='o', symbolPen='r', symbolBrush=0.5) elif plotType == 'kohler_base': self.plotItem.plot(x, y, name='Raw', pen=mkPen('w', width=2)) self.plotItem.plot(x, dataGroup.kohlerBaseline, name='Kohler EMSC baseline', pen=mkPen('g', style=Qt.DotLine, width=2)) elif plotType == 'rubberband': y = self._mu = dataGroup.rubberDebased self.plotItem.plot(x, y, name='Rubberband debased', pen=mkPen('r', width=2)) elif plotType == 'kohler': y = self._mu = dataGroup.kohlerDebased self.plotItem.plot(x, y, name='Kohler EMSC debased', pen=mkPen('r', width=2)) elif plotType == 'deriv2_rubberband': y = self._mu = dataGroup.rubberDebased # for data cursor scale, offset = self.alignTwoCurve(dataGroup.rubberDebased[n//4:n*3//4], dataGroup.deriv2_rubber[n//4:n*3//4]) deriv2Scaled = dataGroup.deriv2_rubber * scale + offset ymin, ymax = np.min(y), np.max(y) self.getViewBox().setYRange(ymin, ymax, padding=0.1) self.plotItem.plot(x, y, name='Rubberband debased', pen=mkPen('r', width=2)) self.plotItem.plot(x, deriv2Scaled, name='2nd derivative (scaled, Rubberband)', pen=mkPen('g', width=2)) elif plotType == 'deriv2_kohler': y = self._mu = dataGroup.kohlerDebased scale, offset = self.alignTwoCurve(dataGroup.kohlerDebased[n//4:n*3//4], dataGroup.deriv2_kohler[n//4:n*3//4]) deriv2Scaled = dataGroup.deriv2_kohler * scale + offset ymin, ymax = np.min(y), np.max(y) self.getViewBox().setYRange(ymin, ymax, padding=0.1) self.plotItem.plot(x, y, name='Rubberband debased', pen=mkPen('r', width=2)) self.plotItem.plot(x, deriv2Scaled, name='2nd derivative (scaled, Kohler)', pen=mkPen('g', width=2)) # add infinityline, cross self.addDataCursor(x, y) def alignTwoCurve(self, y1, y2): """ Align the scale of y2 to that of y1 :param y1: the main curve :param y2: the curve to be aligned :return: scale: scale factor offset: y offset """ y1Range, y2Range = np.max(y1) - np.min(y1), np.max(y2) - np.min(y2) scale = y1Range / y2Range y = y2 * scale offset = np.max(y1) - np.max(y) return scale, offset
class SpectraPlotWidget(PlotWidget): sigEnergyChanged = Signal(object) def __init__(self, linePos=650, txtPosRatio=0.35, invertX=True, *args, **kwargs): """ A widget to display a 1D spectrum :param linePos: the initial position of the InfiniteLine :param txtPosRatio: a coefficient that determines the relative position of the textItem :param invertX: whether to invert X-axis """ super(SpectraPlotWidget, self).__init__(*args, **kwargs) self._data = None assert (txtPosRatio >= 0) and (txtPosRatio <= 1), 'Please set txtPosRatio value between 0 and 1.' self.txtPosRatio = txtPosRatio self.positionmap = dict() self.wavenumbers = None self._meanSpec = True # whether current spectrum is a mean spectrum self.line = InfiniteLine(movable=True) self.line.setPen((255, 255, 0, 200)) self.line.setValue(linePos) self.line.sigPositionChanged.connect(self.sigEnergyChanged) self.line.sigPositionChanged.connect(self.getEnergy) self.addItem(self.line) self.cross = PlotDataItem([linePos], [0], symbolBrush=(255, 0, 0), symbolPen=(255, 0, 0), symbol='+', symbolSize=20) self.cross.setZValue(100) self.addItem(self.cross) self.txt = TextItem() self.getViewBox().invertX(invertX) self.spectrumInd = 0 self.selectedPixels = None self._y = None def getEnergy(self, lineobject): if self._y is not None: x_val = lineobject.value() idx = val2ind(x_val, self.wavenumbers) x_val = self.wavenumbers[idx] y_val = self._y[idx] if not self._meanSpec: txt_html = toHtml(f'Spectrum #{self.spectrumInd}') else: txt_html = toHtml(f'{self._mean_title}') txt_html += toHtml(f'X = {x_val: .2f}, Y = {y_val: .4f}') self.txt.setHtml(txt_html) self.cross.setData([x_val], [y_val]) def setHeader(self, header: NonDBHeader, field: str, *args, **kwargs): self.header = header self.field = field # get wavenumbers spectraEvent = next(header.events(fields=['spectra'])) self.wavenumbers = spectraEvent['wavenumbers'] self.N_w = len(self.wavenumbers) self.rc2ind = spectraEvent['rc_index'] # make lazy array from document data = None try: data = header.meta_array(field) except IndexError: msg.logMessage(f'Header object contained no frames with field {field}.', msg.ERROR) if data is not None: # kwargs['transform'] = QTransform(1, 0, 0, -1, 0, data.shape[-2]) self._data = data def showSpectra(self, i=0): if (self._data is not None) and (i < len(self._data)): self.getViewBox().clear() self._meanSpec = False self.spectrumInd = i self.plot(self.wavenumbers, self._data[i]) def getSelectedPixels(self, selectedPixels): self.selectedPixels = selectedPixels # print(selectedPixels) def clearAll(self): # remove legend _legend = self.plotItem.legend if (_legend is not None) and (_legend.scene() is not None): _legend.scene().removeItem(_legend) self.getViewBox().clear() def showMeanSpectra(self): self._meanSpec = True self.getViewBox().clear() if self.selectedPixels is not None: n_spectra = len(self.selectedPixels) tmp = np.zeros((n_spectra, self.N_w)) for j in range(n_spectra): # j: jth selected pixel row_col = tuple(self.selectedPixels[j]) tmp[j, :] = self._data[self.rc2ind[row_col]] self._mean_title = f'ROI mean of {n_spectra} spectra' else: n_spectra = len(self._data) tmp = np.zeros((n_spectra, self.N_w)) for j in range(n_spectra): tmp[j, :] = self._data[j] self._mean_title = f'Total mean of {n_spectra} spectra' if n_spectra > 0: meanSpec = np.mean(tmp, axis=0) else: meanSpec = np.zeros_like(self.wavenumbers) + 1e-3 self.plot(self.wavenumbers, meanSpec) def plot(self, x, y, *args, **kwargs): # set up infinity line and get its position self.plotItem.plot(x, y, *args, **kwargs) self.addItem(self.line) self.addItem(self.cross) x_val = self.line.value() if x_val == 0: y_val = 0 else: idx = val2ind(x_val, self.wavenumbers) x_val = self.wavenumbers[idx] y_val = y[idx] if not self._meanSpec: txt_html = toHtml(f'Spectrum #{self.spectrumInd}') else: txt_html = toHtml(f'{self._mean_title}') txt_html += toHtml(f'X = {x_val: .2f}, Y = {y_val: .4f}') self.txt.setHtml(txt_html) ymax = np.max(y) self._y = y r = self.txtPosRatio self.txt.setPos(r * x[-1] + (1 - r) * x[0], ymax) self.cross.setData([x_val], [y_val]) self.addItem(self.txt)
class OrbitView(GraphicsLayoutWidget): def __init__(self, orbit=None, axis="X", use_sector_ticks=True, parent=None, ymin=-1.0, ymax=1.0, name=None, label=None, units=None, draw_timer=None, magnet_list=None): super(OrbitView, self).__init__(parent=parent) axis = axis.lower() if axis not in ["x", "y", "tmit"]: raise Exception("Axis must be 'x', 'y', or 'tmit'") self.axis = axis self.use_sector_ticks = use_sector_ticks self.sector_ticks = [[], []] self.ci.layout.setSpacing(0.0) self.plotLabel = self.addLabel(text=name, row=0, col=0, rowspan=3, angle=-90) self.up_magnet_view = MagnetView(magnet_list=orbit, direction="up") self.up_magnet_view.hideAxis('left') self.up_magnet_view.hideAxis('bottom') self.ci.layout.setRowStretchFactor(0, 3) self.plotItem = self.addPlot(name=name, row=0, col=1) self.ci.layout.setRowStretchFactor(1, 0) self.down_magnet_view = MagnetView(magnet_list=orbit, direction="down") self.down_magnet_view.hideAxis('left') self.up_magnet_view.setXLink(self.plotItem) self.down_magnet_view.setXLink(self.plotItem) self.ci.layout.setRowStretchFactor(2, 0) self.show_magnet_buttons = False if axis != "tmit" and magnet_list is not None: self.show_magnet_views(True) self.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate ) # Greatly improves drawing performance. self.plotItem.setMouseEnabled(y=False) #Customize the right-click menu. self.plotItem.setMenuEnabled(enableMenu=False, enableViewBoxMenu=None) reset_view_range = QAction("Reset View Range", self.plotItem.vb.menu) reset_view_range.triggered.connect(self.reset_range) self.plotItem.vb.scene().contextMenu = [] existing_menu_actions = self.plotItem.vb.menu.actions() self.plotItem.vb.menu.insertAction(existing_menu_actions[0], reset_view_range) for action in existing_menu_actions: if str(action.text()) == "View All": self.plotItem.vb.menu.removeAction(action) self.plotItem.showGrid(y=True) #self.plotItem.getAxis('left').setStyle(tickTextWidth=60) #self.plotItem.getAxis('left').setStyle(autoExpandTextSpace=False) #if label is not None: # self.plotItem.getAxis('left').enableAutoSIPrefix(enable=False) # self.plotItem.getAxis('left').setLabel(text=label, units=units) self.bpm_brush = QBrush(QColor(0, 255, 0)) self.energy_bpm_brush = QBrush(QColor(100, 200, 255)) self.ymin = ymin #Y axis goes from self.ymin to self.ymax by default. self.ymax = ymax self.yminlimit = 10.0 * ymin #This is the limit on the Y axis range. self.ymaxlimit = 10.0 * ymax #This is the upper limit on the Y axis range. self.plotItem.setLimits(minYRange=0.04, maxYRange=abs(self.ymaxlimit - self.yminlimit)) self.axis_pen = QPen(QBrush(QColor(255, 255, 255)), 0) self.axis_pen.setCapStyle(Qt.FlatCap) self.bpm_pen = QPen(self.bpm_brush, 2) self.bpm_pen.setCosmetic(True) self.bpm_pen.setCapStyle(Qt.FlatCap) self.no_beam_brush = QBrush(QColor(0, 255, 0, 45)) self.no_beam_pen = QPen(self.no_beam_brush, 2) self.no_beam_pen.setCosmetic(True) self.no_beam_pen.setCapStyle(Qt.FlatCap) self.energy_bpm_pen = QPen(self.energy_bpm_brush, 2) self.energy_bpm_pen.setCosmetic(True) self.energy_bpm_pen.setCapStyle(Qt.FlatCap) self.fit_brush = QBrush(QColor(255, 255, 255, 255)) self.fit_pen = QPen(self.fit_brush, 0) self.fit_pen.setCosmetic(True) self.fit_pen.setCapStyle(Qt.FlatCap) self.axis_line = QGraphicsLineItem(0.0, 0.0, 1.0, 0.0) self.axis_line.setPen(self.axis_pen) self.plotItem.addItem(self.axis_line, ignoreBounds=True) self.lines = {} self.orbit = None self.needs_initial_range = True self.set_draw_timer(draw_timer) self._display_fit = False self.fit_data_item = None self.fit_options = {} if orbit is not None: self.set_orbit(orbit) def make_right_click_menu(self): menu = QMenu(self) return menu def display_fit(self, enabled=True): if enabled and self.fit_data_item is None: self.fit_data_item = PlotDataItem(pen=self.fit_pen) self.plotItem.addItem(self.fit_data_item) elif not enabled: self.plotItem.removeItem(self.fit_data_item) self.fit_data_item = None self._display_fit = enabled def set_draw_timer(self, new_timer, start=False): try: self.draw_timer.timeout.disconnect(self.redraw_bpms) except: pass if new_timer is None: new_timer = QTimer(self) new_timer.setInterval(int(1000 / 60)) self.draw_timer = new_timer self.draw_timer.timeout.connect(self.redraw_bpms) if start: self.draw_timer.start() def set_orbit(self, orbit, reset_range=True): if self.orbit == orbit: return old_range = None old_zmax = None old_zmin = None if self.orbit is not None: old_range = self.plotItem.viewRect() old_zmax = self.orbit.zmax() old_zmin = self.orbit.zmin() self.clear_orbit() self.orbit = orbit extent = self.orbit.zmax() - self.orbit.zmin() self.plotItem.setLimits(xMin=self.orbit.zmin() - (0.02 * extent), xMax=self.orbit.zmax() + (0.02 * extent)) self.plotItem.enableAutoRange(enable=False) self.axis_line.setLine(self.orbit.zmin(), 0.0, self.orbit.zmax(), 0.0) for bpm in self.orbit: line = BPMLineItem(bpm) self.lines[bpm.name] = line self.set_pen_for_bpm(bpm) self.plotItem.addItem(self.lines[bpm.name]) if self.use_sector_ticks and (old_zmax != orbit.zmax() or old_zmin != orbit.zmin()): self.sector_ticks = [[], []] self.sector_ticks[0] = self.orbit.sector_locations() unit_nums = [name.split(":")[-1] for name in self.orbit.names()] self.sector_ticks[1] = zip(self.orbit.z_vals(), unit_nums) self.plotItem.getAxis('bottom').setTicks(self.sector_ticks) self.plotItem.getAxis('bottom').setStyle(textFillLimits=[(0, 0.72)]) self.plotItem.showGrid(x=True) if reset_range or self.needs_initial_range: self.reset_range() self.needs_initial_range = False else: self.plotItem.setRange(old_range, padding=0.0, update=False) self.draw_timer.start() def show_magnet_views(self, enabled): if enabled == self.show_magnet_buttons: return self.show_magnet_buttons = enabled if enabled: self.addItem(self.up_magnet_view, row=1, col=1) self.addItem(self.down_magnet_view, row=2, col=1) self.up_magnet_view.setXLink(self.plotItem) self.down_magnet_view.setXLink(self.plotItem) else: self.removeItem(self.up_magnet_view) self.removeItem(self.down_magnet_view) def set_magnet_list(self, magnet_list): self.up_magnet_view.set_magnets(magnet_list, reset_range=False) self.down_magnet_view.set_magnets(magnet_list, reset_range=False) @pyqtSlot(bool) def reset_range(self, checked=False): self.plotItem.enableAutoRange(axis=ViewBox.XAxis) self.plotItem.setYRange(self.ymin, self.ymax) def wheelEvent(self, event): if event.modifiers() == Qt.ShiftModifier: numPixels = event.pixelDelta() numDegrees = event.angleDelta() if not numPixels.isNull(): s = (1.005)**(numPixels.y()) else: s = (1.005)**(numDegrees.y() * (-1.0 / 8.0)) self.plotItem.vb.scaleBy(y=s) else: super(OrbitView, self).wheelEvent(event) def clear_orbit(self): self.draw_timer.stop() auto_range_x_enabled = self.plotItem.vb.state['autoRange'][0] auto_range_y_enabled = self.plotItem.vb.state['autoRange'][1] self.plotItem.enableAutoRange(enable=False) if self.orbit is None: return for bpm in self.orbit: self.plotItem.removeItem(self.lines[bpm.name]) self.plotItem.enableAutoRange(x=auto_range_x_enabled, y=auto_range_y_enabled) self.lines = {} @pyqtSlot() def redraw_bpms(self): for bpm in self.orbit: self.set_pen_for_bpm(bpm) self.lines[bpm.name].setLine(bpm.z, 0.0, bpm.z, bpm[self.axis]) self.update_fit() def set_pen_for_bpm(self, bpm): if bpm.severity(self.axis) != 0: self.lines[bpm.name].setPen(self.no_beam_pen) else: if bpm.is_energy_bpm: self.lines[bpm.name].setPen(self.energy_bpm_pen) else: self.lines[bpm.name].setPen(self.bpm_pen) def update_fit(self): if not self._display_fit: return if self.orbit.fit_data is None: if self.fit_data_item is not None: self.fit_data_item.hide() return fit_data = None if self.axis == 'x': fit_data = self.orbit.fit_data['xpos'] elif self.axis == 'y': fit_data = self.orbit.fit_data['ypos'] self.fit_data_item.show() self.fit_data_item.setData(x=self.orbit.fit_data['zs'], y=fit_data) @pyqtSlot() def stop(self): self.draw_timer.stop() @pyqtSlot() def start(self): if self.orbit is not None: self.draw_timer.start() def setXLink(self, view): return self.plotItem.setXLink(view.plotItem) def setYLink(self, view): return self.plotItem.setYLink(view.plotItem)
class Screen(QWidget): def __init__(self): super().__init__() grid_layout = QGridLayout() self.setLayout(grid_layout) timer = QTimer(self) timer.timeout.connect(self.update_data) timer.start(1000) temp_title = QLabel( text='Tempture:', alignment=Qt.AlignCenter, styleSheet=TITLE_STYLES, ) grid_layout.addWidget(temp_title) self.temp_data = PlotDataItem(temps) temp_widget = PlotWidget() temp_widget.addItem(self.temp_data) temp_widget.setXRange(0, max_history) temp_widget.setYRange(-10, 60) grid_layout.addWidget(temp_widget) fan_title = QLabel( text='Fan', alignment=Qt.AlignCenter, styleSheet=TITLE_STYLES, ) grid_layout.addWidget(fan_title) self.fan_data = PlotDataItem(fans) fan_widget = PlotWidget() fan_widget.addItem(self.fan_data) fan_widget.setXRange(0, max_history) fan_widget.setYRange(0, 100) grid_layout.addWidget(fan_widget) last_log_title = QLabel( text='Lastest Event', alignment=Qt.AlignCenter, styleSheet=TITLE_STYLES, ) grid_layout.addWidget(last_log_title) self.last_log = QLabel() self.last_log.resize(WINDOW_HEIGHT, WINDOW_WIDTH) self.last_log.setStyleSheet('font-weight: bold;' 'background-color: grey;') grid_layout.addWidget(self.last_log) logs_title = QLabel( text='Event History', alignment=Qt.AlignCenter, styleSheet=TITLE_STYLES, ) grid_layout.addWidget(logs_title) self.logs = QPlainTextEdit(self) self.logs.resize(WINDOW_HEIGHT, WINDOW_WIDTH) self.logs.resize(400, 200) grid_layout.addWidget(self.logs) self.setWindowTitle('Screen') def update_data(self): global counter # Fixme: Foo data temps.append(counter) fans.append(counter) new_log = f'Event {counter} happend' counter += randint(-5, 10) self.temp_data.setData(temps[-max_history:]) self.fan_data.setData(fans[-max_history:]) if new_log: date_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') date_log = f'{date_time}: {new_log}' self.logs.setPlainText(f'{self.last_log.text()}' '\n' f'{self.logs.toPlainText()}') self.last_log.setText(date_log) self.update()
class CurveItem: """Represents a curve to be plotted in a diagram.""" def __init__(self, subscription_id, driver_addr, sig_name, y_axis, linecolor, linestyle, linemarker): """ Initializes an instance of class CurveItem. driver_addr - IcePAP driver address. sig_name - Signal name. y_axis - Y axis to plot against. """ self.subscription_id = subscription_id self.driver_addr = driver_addr self.signal_name = sig_name self.y_axis = y_axis self.array_time = [] self.array_val = [] self.val_min = 0 self.val_max = 0 self.color = linecolor self.pen = {'color': linecolor, 'width': 1, 'style': linestyle} self.symbol = linemarker self.curve = None self.lock = RLock() self.signature = '' self.update_signature() def update_signature(self): """Sets the new value of the signature string.""" self.signature = '{}:{}:{}'.format(self.driver_addr, self.signal_name, self.y_axis) def create_curve(self): """Creates a new plot item.""" with self.lock: if self.symbol != '': self.curve = PlotDataItem(x=self.array_time, y=self.array_val, pen=self.pen, symbol=self.symbol, symbolBrush=QtGui.QBrush(self.color), symbolPen=self.color) else: self.curve = PlotDataItem(x=self.array_time, y=self.array_val, pen=self.pen) return self.curve def update_curve(self, time_min, time_max): """Updates the curve with recent collected data.""" with self.lock: idx_min = self.get_time_index(time_min) idx_max = self.get_time_index(time_max) self.curve.setData(x=self.array_time[idx_min:idx_max], y=self.array_val[idx_min:idx_max]) def in_range(self, t): """ Check to see if time is within range of collected data. t - Time value. Return: True if time is within range of collected data. Otherwise False. """ with self.lock: if self.array_time and \ self.array_time[0] < t < self.array_time[-1]: return True return False def start_time(self): """ Get time for first data sample. Return: Time of the first collected data sample. -1 if none. """ with self.lock: if self.array_time: return self.array_time[0] return -1 def collect(self, new_data): """Store new collected data.""" with self.lock: if not self.array_val: self.val_min = self.val_max = new_data[0][1] for t, v in new_data: self.array_time.append(t) self.array_val.append(v) if v > self.val_max: self.val_max = v elif v < self.val_min: self.val_min = v def get_y(self, time_val): """ Retrieve the signal value corresponding to the provided time value. t_val - Time value. Return: Signal value corresponding to an adjacent sample in time. """ with self.lock: idx = self.get_time_index(time_val) return self.array_val[idx] def clear(self): self.array_time[:] = [] self.array_val[:] = [] def get_time_index(self, time_val): """ Retrieve the sample index corresponding to the provided time value. t_val - Time value. Return: Index of a sample adjacent to the provided time value. """ with self.lock: if not self.array_time: return -1 if len(self.array_time) == 1: return 0 time_min = self.array_time[0] time_max = self.array_time[-1] if time_val < time_min: return 0 elif time_val > time_max: return len(self.array_time) delta_t = time_max - time_min t = time_val - time_min idx = int((t / delta_t) * len(self.array_time)) while self.array_time[idx] > time_val: idx -= 1 while self.array_time[idx] < time_val: idx += 1 return idx
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) _dir = os.path.dirname(os.path.abspath(__file__)) #uic.loadUi(_dir + '/' + 'layout.ui', self) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.profileWidget.hide() self.ui.smallDataWidget.hide() self.ui.splitter_3.setSizes([ self.width() * 0.7, self.width() * 0.3 ]) # splitter between image_view/plot_widget and file list self.ui.splitter_2.setSizes([ self.height() * 0.7, self.height() * 0.3 ]) # splitter between image_view and plot widget self.ui.splitter.setSizes([ self.ui.splitter.width() / 3., self.ui.splitter.width() / 3., self.ui.splitter.width() / 3. ]) self.setAcceptDrops(True) self.ui.fileList.setColumnWidth(0, 200) self.filepath = None # current filepath self.imageData = None # original image data, 2d or 3d self.imageShape = None self.dispData = None # 2d data for plot self.dispShape = None self.mask = None self.curve = None self.h5obj = None self.acceptedFileTypes = [ u'npy', u'npz', u'h5', u'mat', u'cxi', u'tif' ] self.dispItem = self.ui.imageView.getImageItem() self.ringItem = pg.ScatterPlotItem() self.centerMarkItem = pg.ScatterPlotItem() self.ui.imageView.getView().addItem(self.ringItem) self.ui.imageView.getView().addItem(self.centerMarkItem) self.curveItem = PlotDataItem() self.ui.curveWidget.addItem(self.curveItem) self.ui.curveWidget.getPlotItem().setTitle(title='Curve Plot') # basic operation for image and curve plot self.showImage = True self.axis = 'x' self.frameIndex = 0 self.maskFlag = False self.imageLog = False self.binaryFlag = False self.FFT = False self.FFTSHIFT = False self.dispThreshold = 0 self.center = [0, 0] self.showRings = False self.ringRadiis = [] self.showCurvePlot = True self.profileItem = PlotDataItem(pen=pg.mkPen( 'y', width=1, style=QtCore.Qt.SolidLine), name='profile') self.smoothItem = PlotDataItem(pen=pg.mkPen('g', width=2, style=QtCore.Qt.DotLine), name='smoothed profile') self.thresholdItem = PlotDataItem(pen=pg.mkPen('r'), width=1, style=QtCore.Qt.DashLine, name='threshold') self.ui.profileWidget.addLegend() self.ui.profileWidget.addItem(self.profileItem) self.ui.profileWidget.addItem(self.smoothItem) self.ui.profileWidget.addItem(self.thresholdItem) self.ui.profileWidget.getPlotItem().setTitle(title='Profile Plot') # profile option self.showProfile = False self.profileType = 'radial' self.profileMode = 'sum' # extrema search self.extremaSearch = False self.extremaType = 'max' self.extremaWinSize = 11 self.extremaThreshold = 1.0 # ratio compared to mean value # angular option self.angularRmin = 0. self.angularRmax = np.inf # across center line option self.lineAngle = 0. self.lineWidth = 1 # profile smoothing self.smoothFlag = False self.smoothWinSize = 15 self.polyOrder = 3 # small data option self.smallDataItem = pg.ScatterPlotItem() self.ui.smallDataWidget.addItem(self.smallDataItem) self.ui.smallDataWidget.getPlotItem().setTitle(title='Small Data') self.showSmallData = False self.smallDataFile = None self.smallDataset = None self.smallDataSorted = False self.smallDataPaths = None self.smallDataFrames = None self.smallData = None # display option self.imageAutoRange = False self.imageAutoLevels = False self.imageAutoHistogramRange = True self.curvePlotAutoRange = True self.curvePlotLog = False self.profilePlotAutoRange = True self.profilePlotLog = False self.smallDataPlotAutoRange = True self.smallDataPlotLog = False params_list = [{ 'name': 'Data Info', 'type': 'group', 'children': [{ 'name': 'File', 'type': 'str', 'value': 'not set', 'readonly': True }, { 'name': 'Dataset', 'type': 'str', 'value': 'not set', 'readonly': True }, { 'name': 'Mask', 'type': 'str', 'value': 'not set', 'readonly': True }, { 'name': 'Image Shape', 'type': 'str', 'value': 'unknown', 'readonly': True }, { 'name': 'Image Friedel Score', 'type': 'float', 'readonly': True }, { 'name': 'Curve Length', 'type': 'int', 'readonly': 'True' }] }, { 'name': 'Basic Operation', 'type': 'group', 'children': [{ 'name': 'Image', 'type': 'group', 'children': [ { 'name': 'Axis', 'type': 'list', 'values': ['x', 'y', 'z'], 'value': self.axis }, { 'name': 'Frame Index', 'type': 'int', 'value': self.frameIndex }, { 'name': 'Apply Mask', 'type': 'bool', 'value': self.imageLog }, { 'name': 'Apply Log', 'type': 'bool', 'value': self.maskFlag }, { 'name': 'Apply FFT', 'type': 'bool', 'value': self.FFT }, { 'name': 'Apply FFT-SHIFT', 'type': 'bool', 'value': self.FFTSHIFT }, { 'name': 'Binaryzation', 'type': 'bool', 'value': self.binaryFlag }, { 'name': 'Threshold', 'type': 'float', 'value': self.dispThreshold }, { 'name': 'Center x', 'type': 'int', 'value': self.center[1] }, { 'name': 'Center y', 'type': 'int', 'value': self.center[0] }, { 'name': 'Show Rings', 'type': 'bool' }, { 'name': 'Ring Radiis', 'type': 'str', 'value': '' }, ] }, { 'name': 'Curve Plot', 'type': 'group', 'children': [ { 'name': 'TODO', 'type': 'str' }, ] }] }, { 'name': 'Image to Feature Profile', 'type': 'group', 'children': [{ 'name': 'Show Profile', 'type': 'bool', 'value': self.showProfile }, { 'name': 'Feature', 'type': 'list', 'values': ['radial', 'angular', 'across center line'], 'value': self.profileType }, { 'name': 'Profile Mode', 'type': 'list', 'values': ['sum', 'mean'], 'value': self.profileMode }, { 'name': 'Angular Option', 'type': 'group', 'children': [ { 'name': 'R min', 'type': 'float', 'value': self.angularRmin }, { 'name': 'R max', 'type': 'float', 'value': self.angularRmax }, ] }, { 'name': 'Across Center Line Option', 'type': 'group', 'children': [ { 'name': 'Angle', 'type': 'float', 'value': self.lineAngle }, { 'name': 'Width/pixel', 'type': 'int', 'value': self.lineWidth }, ] }, { 'name': 'Smoothing', 'type': 'group', 'children': [ { 'name': 'Enable Smoothing', 'type': 'bool', 'value': self.smoothFlag }, { 'name': 'Window Size', 'type': 'int', 'value': self.smoothWinSize }, { 'name': 'Poly-Order', 'type': 'int', 'value': self.polyOrder }, ] }, { 'name': 'Extrema Search', 'type': 'group', 'children': [ { 'name': 'Enable Extrema Search', 'type': 'bool', 'value': self.extremaSearch }, { 'name': 'Extrema Type', 'type': 'list', 'values': ['max', 'min'], 'value': self.extremaType }, { 'name': 'Extrema WinSize', 'type': 'int', 'value': self.extremaWinSize }, { 'name': 'Extrema Threshold', 'type': 'float', 'value': self.extremaThreshold }, ] }] }, { 'name': 'Small Data', 'type': 'group', 'children': [ { 'name': 'Filepath', 'type': 'str' }, { 'name': 'Dataset', 'type': 'str' }, { 'name': 'Show data', 'type': 'bool', 'value': self.showSmallData }, { 'name': 'Sort', 'type': 'bool', 'value': self.smallDataSorted }, ] }, { 'name': 'Display Option', 'type': 'group', 'children': [ { 'name': 'Image', 'type': 'group', 'children': [ { 'name': 'autoRange', 'type': 'bool', 'value': self.imageAutoRange }, { 'name': 'autoLevels', 'type': 'bool', 'value': self.imageAutoLevels }, { 'name': 'autoHistogramRange', 'type': 'bool', 'value': self.imageAutoHistogramRange }, ] }, { 'name': 'Curve Plot', 'type': 'group', 'children': [ { 'name': 'autoRange', 'type': 'bool', 'value': self.curvePlotAutoRange }, { 'name': 'Log', 'type': 'bool', 'value': self.curvePlotLog }, ] }, { 'name': 'Profile Plot', 'type': 'group', 'children': [ { 'name': 'autoRange', 'type': 'bool', 'value': self.profilePlotAutoRange }, { 'name': 'Log', 'type': 'bool', 'value': self.profilePlotLog }, ] }, { 'name': 'Small Data Plot', 'type': 'group', 'children': [ { 'name': 'autoRange', 'type': 'bool', 'value': self.smallDataPlotAutoRange }, { 'name': 'Log', 'type': 'bool', 'value': self.smallDataPlotLog }, ] }, ] }] self.params = Parameter.create(name='params', type='group', children=params_list) self.ui.parameterTree.setParameters(self.params, showTop=False) self.ui.fileList.itemDoubleClicked.connect(self.changeDatasetSlot) self.ui.fileList.customContextMenuRequested.connect( self.showFileMenuSlot) self.ui.imageView.scene.sigMouseMoved.connect(self.mouseMoved) self.smallDataItem.sigClicked.connect(self.smallDataClicked) self.ui.lineEdit.returnPressed.connect(self.addFilesSlot) self.params.param('Basic Operation', 'Image', 'Axis').sigValueChanged.connect(self.axisChangedSlot) self.params.param('Basic Operation', 'Image', 'Frame Index').sigValueChanged.connect( self.frameIndexChangedSlot) self.params.param('Basic Operation', 'Image', 'Apply Mask').sigValueChanged.connect( self.applyMaskSlot) self.params.param('Basic Operation', 'Image', 'Apply Log').sigValueChanged.connect( self.applyImageLogSlot) self.params.param('Basic Operation', 'Image', 'Binaryzation').sigValueChanged.connect( self.binaryImageSlot) self.params.param('Basic Operation', 'Image', 'Apply FFT').sigValueChanged.connect( self.applyFFTSlot) self.params.param('Basic Operation', 'Image', 'Apply FFT-SHIFT').sigValueChanged.connect( self.applyFFTSHIFTSlot) self.params.param('Basic Operation', 'Image', 'Binaryzation').sigValueChanged.connect( self.binaryImageSlot) self.params.param('Basic Operation', 'Image', 'Threshold').sigValueChanged.connect( self.setDispThresholdSlot) self.params.param('Basic Operation', 'Image', 'Center x').sigValueChanged.connect( self.centerXChangedSlot) self.params.param('Basic Operation', 'Image', 'Center y').sigValueChanged.connect( self.centerYChangedSlot) self.params.param('Basic Operation', 'Image', 'Show Rings').sigValueChanged.connect( self.showRingsSlot) self.params.param('Basic Operation', 'Image', 'Ring Radiis').sigValueChanged.connect( self.ringRadiiSlot) self.params.param('Image to Feature Profile', 'Show Profile').sigValueChanged.connect( self.showProfileSlot) self.params.param('Image to Feature Profile', 'Feature').sigValueChanged.connect( self.setProfileTypeSlot) self.params.param('Image to Feature Profile', 'Profile Mode').sigValueChanged.connect( self.setProfileModeSlot) self.params.param('Image to Feature Profile', 'Angular Option', 'R min').sigValueChanged.connect( self.setAngularRminSlot) self.params.param('Image to Feature Profile', 'Angular Option', 'R max').sigValueChanged.connect( self.setAngularRmaxSlot) self.params.param( 'Image to Feature Profile', 'Across Center Line Option', 'Angle').sigValueChanged.connect(self.setLineAngleSlot) self.params.param( 'Image to Feature Profile', 'Across Center Line Option', 'Width/pixel').sigValueChanged.connect(self.setLineWidthSlot) self.params.param('Image to Feature Profile', 'Smoothing', 'Enable Smoothing').sigValueChanged.connect( self.setSmoothSlot) self.params.param('Image to Feature Profile', 'Smoothing', 'Window Size').sigValueChanged.connect( self.setWinSizeSlot) self.params.param('Image to Feature Profile', 'Smoothing', 'Poly-Order').sigValueChanged.connect( self.setPolyOrderSlot) self.params.param('Image to Feature Profile', 'Extrema Search', 'Enable Extrema Search').sigValueChanged.connect( self.setExtremaSearchSlot) self.params.param('Image to Feature Profile', 'Extrema Search', 'Extrema Type').sigValueChanged.connect( self.setExtremaTypeSlot) self.params.param('Image to Feature Profile', 'Extrema Search', 'Extrema WinSize').sigValueChanged.connect( self.setExtremaWinSizeSlot) self.params.param('Image to Feature Profile', 'Extrema Search', 'Extrema Threshold').sigValueChanged.connect( self.setExtremaThresholdSlot) self.params.param('Small Data', 'Filepath').sigValueChanged.connect( self.setSmallDataFilepathSlot) self.params.param('Small Data', 'Dataset').sigValueChanged.connect( self.setSmallDatasetSlot) self.params.param('Small Data', 'Show data').sigValueChanged.connect( self.showSmallDataSlot) self.params.param('Small Data', 'Sort').sigValueChanged.connect( self.sortSmallDataSlot) self.params.param('Display Option', 'Image', 'autoRange').sigValueChanged.connect( self.imageAutoRangeSlot) self.params.param('Display Option', 'Image', 'autoLevels').sigValueChanged.connect( self.imageAutoLevelsSlot) self.params.param('Display Option', 'Image', 'autoHistogramRange').sigValueChanged.connect( self.imageAutoHistogramRangeSlot) self.params.param('Display Option', 'Curve Plot', 'autoRange').sigValueChanged.connect( self.curvePlotAutoRangeSlot) self.params.param('Display Option', 'Curve Plot', 'Log').sigValueChanged.connect( self.curvePlotLogModeSlot) self.params.param('Display Option', 'Profile Plot', 'autoRange').sigValueChanged.connect( self.profilePlotAutoRangeSlot) self.params.param('Display Option', 'Profile Plot', 'Log').sigValueChanged.connect( self.profilePlotLogModeSlot) self.params.param('Display Option', 'Small Data Plot', 'autoRange').sigValueChanged.connect( self.smallDataPlotAutoRangeSlot) self.params.param('Display Option', 'Small Data Plot', 'Log').sigValueChanged.connect( self.smallDataPlotLogModeSlot) def addFilesSlot(self): filePattern = str(self.ui.lineEdit.text()) #print_with_timestamp('file(s) pattern: %s' %filePattern) files = glob.glob(filePattern) #print_with_timestamp('adding file(s): %s \n Total Num: %d' %(str(files), len(files))) for i in xrange(len(files)): self.maybeAddFile(files[i]) def smallDataClicked(self, _, points): _temp_file = '.temp.npy' pos = points[0].pos() index = int(points[0].pos()[0]) if self.smallDataSorted: index = np.argsort(self.smallData)[index] filepath = self.smallDataPaths[index] frame = self.smallDataFrames[index] #print_with_timestamp('showing file: %s, frame: %d' %(filepath, frame)) make_temp_file(filepath, frame, _temp_file) maybeExistIndex = self.ui.fileList.indexOf(_temp_file) if maybeExistIndex != -1: self.ui.fileList.takeTopLevelItem(maybeExistIndex) item = FileItem(filepath=_temp_file) self.ui.fileList.insertTopLevelItem(0, item) self.changeDatasetSlot(item, 0) def setSmallDataFilepathSlot(self, _, filepath): #print_with_timestamp('set filepath for small data: %s' % str(filepath)) self.smallDataFile = filepath self.maybeShowSmallData() def setSmallDatasetSlot(self, _, dataset): #print_with_timestamp('set dataset for small data: %s' % str(dataset)) self.smallDataset = dataset self.maybeShowSmallData() def showSmallDataSlot(self, _, showSmallData): #print_with_timestamp('set show small data: %s' % str(showSmallData)) # self.showSmallData = showSmallData if showSmallData: self.ui.smallDataWidget.show() else: self.ui.smallDataWidget.hide() self.maybeShowSmallData() def sortSmallDataSlot(self, _, sort): #print_with_timestamp('set show small data sorted: %s' % str(sort)) self.smallDataSorted = sort self.maybeShowSmallData() def dragEnterEvent(self, event): urls = event.mimeData().urls() for url in urls: if QtCore.QString(url.toLocalFile()).startsWith('/.file/id='): dropFile = getFilepathFromLocalFileID(url) else: dropFile = url.toLocalFile() fileInfo = QtCore.QFileInfo(dropFile) ext = fileInfo.suffix() if ext in self.acceptedFileTypes: event.accept() return None event.ignore() return None def dropEvent(self, event): urls = event.mimeData().urls() for url in urls: if QtCore.QString(url.toLocalFile()).startsWith('/.file/id='): dropFile = getFilepathFromLocalFileID(url) else: dropFile = url.toLocalFile() self.maybeAddFile(dropFile) def maybeAddFile(self, filepath): ext = QtCore.QFileInfo(filepath).suffix() if ext in self.acceptedFileTypes: maybeExistIndex = self.ui.fileList.indexOf(filepath) if maybeExistIndex != -1: self.ui.fileList.takeTopLevelItem(maybeExistIndex) item = FileItem(filepath=filepath) if item.childCount() > 0: self.ui.fileList.insertTopLevelItem(0, item) def loadData(self, filepath, datasetName): self.filepath = str(filepath) basename = os.path.basename(self.filepath) data = load_data(self.filepath, datasetName) if type(data) == list: if self.h5obj is not None: self.h5obj.close() self.h5obj = None self.h5obj = data[0] data = data[1] if len(data.shape) == 1: # 1d curve if data.size == 1: # single number return None else: self.curve = data return None self.imageData = data self.imageShape = data.shape _shape_str = '' if len(self.imageShape) == 2: _x, _y = self.imageShape _shape_str = 'x: %d, y: %d' % (_x, _y) elif len(self.imageShape) == 3: _x, _y, _z = self.imageShape _shape_str = 'x: %d, y: %d, z: %d' % (_x, _y, _z) self.params.param('Data Info', 'Image Shape').setValue(_shape_str) self.params.param('Data Info', 'File').setValue(basename) self.params.param('Data Info', 'Dataset').setValue(datasetName) if len(self.imageShape) == 3: self.dispShape = self.imageData.shape[1:3] else: self.dispShape = self.imageShape self.center = self.calcCenter() self.setCenterInfo() def setCenterInfo(self): self.params.param('Basic Operation', 'Image', 'Center x').setValue(self.center[0]) self.params.param('Basic Operation', 'Image', 'Center y').setValue(self.center[1]) def calcCenter(self): if len(self.imageShape) == 2: center = [self.imageShape[1] // 2, self.imageShape[0] // 2] return center assert len(self.imageShape) == 3 if self.axis == 'x': center = [self.imageShape[2] // 2, self.imageShape[1] // 2] elif self.axis == 'y': center = [self.imageShape[2] // 2, self.imageShape[0] // 2] else: center = [self.imageShape[1] // 2, self.imageShape[0] // 2] return center def maybeShowSmallData(self): if self.showSmallData: if not self.ui.smallDataWidget.isVisible(): self.ui.smallDataWidget.show() self.ui.smallDataWidget.setLabels(bottom='index', left='metric') if self.smallDataFile is not None and self.smallDataset is not None: paths, frames, smallData = load_smalldata( self.smallDataFile, self.smallDataset) self.smallDataPaths = paths self.smallDataFrames = frames self.smallData = smallData if self.smallDataSorted: index_array = np.argsort(self.smallData).astype(np.int32) self.smallDataItem.setData(x=np.arange( self.smallData.size), y=self.smallData[index_array]) else: self.smallDataItem.setData(x=np.arange( self.smallData.size), y=self.smallData) def maybePlotProfile(self): if self.showProfile and self.dispData is not None: if self.mask is None: self.mask = np.ones_like(self.dispData) mask = self.mask.copy() if self.maskFlag == True: assert mask.shape == self.dispData.shape if self.profileType == 'radial': profile = calc_radial_profile(self.dispData, self.center, mask=mask, mode=self.profileMode) elif self.profileType == 'angular': annulus_mask = make_annulus(self.dispShape, self.angularRmin, self.angularRmax) profile = calc_angular_profile(self.dispData, self.center, mask=mask * annulus_mask, mode=self.profileMode) else: # across center line profile = calc_across_center_line_profile( self.dispData, self.center, angle=self.lineAngle, width=self.lineWidth, mask=self.mask, mode=self.profileMode) if self.profilePlotLog == True: profile[profile < 1.] = 1. self.profileItem.setData(profile) if self.profileType == 'radial': self.ui.profileWidget.setTitle('Radial Profile') self.ui.profileWidget.setLabels(bottom='r/pixel') elif self.profileType == 'angular': self.ui.profileWidget.setTitle('Angular Profile') self.ui.profileWidget.setLabels(bottom='theta/deg') else: self.ui.profileWidget.setTitle('Across Center Line Profile') self.ui.profileWidget.setLabels(bottom='index/pixel') if self.smoothFlag == True: smoothed_profile = savgol_filter(profile, self.smoothWinSize, self.polyOrder) if self.profilePlotLog == True: smoothed_profile[smoothed_profile < 1.] = 1. self.smoothItem.setData(smoothed_profile) else: self.smoothItem.clear() profile_with_noise = profile.astype(np.float) + np.random.rand( profile.size ) * 1E-5 # add some noise to avoid same integer value in profile if self.extremaSearch == True: if self.extremaType == 'max': extrema_indices = argrelmax(profile_with_noise, order=self.extremaWinSize)[0] else: extrema_indices = argrelmin(profile_with_noise, order=self.extremaWinSize)[0] #print_with_timestamp('before filtered by threshold: %s' %str(extrema_indices)) extremas = profile[extrema_indices] filtered_extrema_indices = extrema_indices[ extremas > self.extremaThreshold * profile.mean()] filtered_extremas = profile[filtered_extrema_indices] #print_with_timestamp('after filtered by threshold: %s' %(filtered_extrema_indices)) self.thresholdItem.setData( np.ones_like(profile) * profile.mean() * self.extremaThreshold) else: self.thresholdItem.clear() def calcDispData(self): if self.imageData is None: return None elif len(self.imageShape) == 3: _x, _y, _z = self.imageShape if self.axis == 'x': if 0 <= self.frameIndex < _x: dispData = self.imageData[self.frameIndex, :, :] else: #print_with_timestamp("ERROR! Index out of range. %s axis frame %d" %(self.axis, self.frameIndex)) QtGui.QMessageBox.question( self, 'Error', "ERROR! Index out of range. %s axis frame %d" % (self.axis, self.frameIndex), QtGui.QMessageBox.Ok) return None elif self.axis == 'y': if 0 <= self.frameIndex < _y: dispData = self.imageData[:, self.frameIndex, :] else: #print_with_timestamp("ERROR! Index out of range. %s axis frame %d" %(self.axis, self.frameIndex)) QtGui.QMessageBox.question( self, 'Error', "ERROR! Index out of range. %s axis frame %d" % (self.axis, self.frameIndex), QtGui.QMessageBox.Ok) return None else: if 0 <= self.frameIndex < _z: dispData = self.imageData[:, :, self.frameIndex] else: #print_with_timestamp("ERROR! Index out of range. %s axis frame %d" %(self.axis, self.frameIndex)) QtGui.QMessageBox.question( self, 'Error', "ERROR! Index out of range. %s axis frame %d" % (self.axis, self.frameIndex), QtGui.QMessageBox.Ok) return None elif len(self.imageShape) == 2: dispData = self.imageData if isinstance(dispData, np.ndarray): dispData = dispData.copy() else: dispData = np.asarray(dispData).copy() if self.imageLog: dispData[dispData < 1.] = 1. dispData = np.log(dispData) if self.FFT: dispData = np.abs(np.fft.fft2(dispData)) if self.FFTSHIFT: dispData = np.fft.fftshift(dispData) return dispData def closeEvent(self, event): global data_viewer_window reply = QtGui.QMessageBox.question(self, 'Message', "Are you sure to quit?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: #print_with_timestamp('Bye-Bye.') #pg.exit() if self.h5obj is not None: self.h5obj.close() del data_viewer_window data_viewer_window = None else: event.ignore() def changeDatasetSlot(self, item, column): if isinstance(item, DatasetItem): datasetItem = item fileItem = datasetItem.parent() else: assert isinstance(item, FileItem) fileItem = item datasetItem = fileItem.child(0) self.loadData(fileItem.filepath, datasetItem.datasetName) # maybe 1d, 2d or 3d dataset self.maybeChangeCurve(name=datasetItem.datasetName) self.maybeChangeDisp() self.maybePlotProfile() def maybeChangeCurve(self, name=None): if not self.showCurvePlot or self.curve is None: return None self.curveItem.setData(self.curve) if name is not None: self.ui.curveWidget.getPlotItem().setTitle('Curve Plot -- %s' % name) def maybeChangeDisp(self): if not self.showImage or self.imageData is None: return None dispData = self.calcDispData() if dispData is None: return None self.dispShape = dispData.shape if self.maskFlag: if self.mask is None: self.mask = np.ones(self.dispShape) assert self.mask.shape == self.dispShape dispData *= self.mask if self.binaryFlag: dispData[dispData < self.dispThreshold] = 0. dispData[dispData >= self.dispThreshold] = 1. self.dispData = dispData # set dispData to distItem. Note: transpose the dispData to show image with same manner in matplotlib self.ui.imageView.setImage( self.dispData.T, autoRange=self.imageAutoRange, autoLevels=self.imageAutoLevels, autoHistogramRange=self.imageAutoHistogramRange) if self.showRings: if len(self.ringRadiis) > 0: _cx = np.ones_like(self.ringRadiis) * self.center[0] _cy = np.ones_like(self.ringRadiis) * self.center[1] self.ringItem.setData(_cx, _cy, size=self.ringRadiis * 2., symbol='o', brush=(255, 255, 255, 0), pen='r', pxMode=False) self.centerMarkItem.setData([self.center[0]], [self.center[1]], size=10, symbol='+', brush=(255, 255, 255, 0), pen='r', pxMode=False) Friedel_score = calc_Friedel_score(self.dispData, self.center, mask=self.mask, mode='relative') self.params.param('Data Info', 'Image Friedel Score').setValue(Friedel_score) def setLineAngleSlot(self, _, lineAngle): #print_with_timestamp('set line angle: %s' %str(lineAngle)) self.lineAngle = lineAngle self.maybePlotProfile() def setLineWidthSlot(self, _, lineWidth): #print_with_timestamp('set line width: %s' %str(lineWidth)) self.lineWidth = lineWidth self.maybePlotProfile() def applyImageLogSlot(self, _, imageLog): #print_with_timestamp('set image log: %s' %str(imageLog)) self.imageLog = imageLog self.maybeChangeDisp() self.maybePlotProfile() def applyFFTSlot(self, _, FFT): #print_with_timestamp('set image fft: %s' %str(FFT)) self.FFT = FFT self.maybeChangeDisp() self.maybePlotProfile() def applyFFTSHIFTSlot(self, _, FFTSHIFT): #print_with_timestamp('set image fft-shift: %s' %str(FFTSHIFT)) self.FFTSHIFT = FFTSHIFT self.maybeChangeDisp() self.maybePlotProfile() def setExtremaSearchSlot(self, _, extremaSearch): #print_with_timestamp('set extrema search: %s' %str(extremaSearch)) self.extremaSearch = extremaSearch self.maybePlotProfile() def setExtremaWinSizeSlot(self, _, extremaWinSize): #print_with_timestamp('set extrema window size: %s' %str(extremaWinSize)) self.extremaWinSize = extremaWinSize self.maybePlotProfile() def setExtremaTypeSlot(self, _, extremaType): #print_with_timestamp('set extrema type: %s' %str(extremaType)) self.extremaType = extremaType self.maybePlotProfile() def setExtremaThresholdSlot(self, _, extremaThreshold): #print_with_timestamp('set extrema threshold: %s' %str(extremaThreshold)) self.extremaThreshold = extremaThreshold self.maybePlotProfile() def axisChangedSlot(self, _, axis): #print_with_timestamp('axis changed.') self.axis = axis self.center = self.calcCenter() self.setCenterInfo() self.maybeChangeDisp() self.maybePlotProfile() def frameIndexChangedSlot(self, _, frameIndex): #print_with_timestamp('frame index changed') self.frameIndex = frameIndex self.maybeChangeDisp() self.maybePlotProfile() def showProfileSlot(self, _, showProfile): #print_with_timestamp('show or hide radial profile changed') if showProfile: #print_with_timestamp('show profile') self.ui.profileWidget.show() else: self.ui.profileWidget.hide() self.maybePlotProfile() def centerXChangedSlot(self, _, centerX): #print_with_timestamp('center X changed') self.center[0] = centerX self.maybeChangeDisp() self.maybePlotProfile() def centerYChangedSlot(self, _, centerY): #print_with_timestamp('center Y changed') self.center[1] = centerY self.maybeChangeDisp() self.maybePlotProfile() def showFileMenuSlot(self, position): fileMenu = QtGui.QMenu() item = self.ui.fileList.currentItem() if isinstance(item, DatasetItem): setAsMask = fileMenu.addAction("Set As Mask") action = fileMenu.exec_(self.ui.fileList.mapToGlobal(position)) if action == setAsMask: filepath = item.parent().filepath mask = load_data(filepath, item.datasetName) if len(mask.shape) != 2: raise ValueError( '%s:%s can not be used as mask. Mask data must be 2d.' % (filepath, item.datasetName)) self.mask = np.asarray(mask) self.params.param('Data Info', 'Mask').setValue( "%s::%s" % (os.path.basename(filepath), item.datasetName)) elif isinstance(item, FileItem): deleteAction = fileMenu.addAction("Delete") action = fileMenu.exec_(self.ui.fileList.mapToGlobal(position)) if action == deleteAction: #print('deleting selected file') for item in self.ui.fileList.selectedItems(): self.ui.fileList.takeTopLevelItem( self.ui.fileList.indexOfTopLevelItem(item)) def applyMaskSlot(self, _, mask): #print_with_timestamp('turn on mask: %s' % str(mask)) self.maskFlag = mask self.maybeChangeDisp() self.maybePlotProfile() def imageAutoRangeSlot(self, _, imageAutoRange): #print_with_timestamp('set image autorange: %s' % str(imageAutoRange)) self.imageAutoRange = imageAutoRange self.maybeChangeDisp() def imageAutoLevelsSlot(self, _, imageAutoLevels): #print_with_timestamp('set image autolevels: %s' % str(imageAutoLevels)) self.imageAutoLevels = imageAutoLevels self.maybeChangeDisp() def imageAutoHistogramRangeSlot(self, _, imageAutoHistogramRange): #print_with_timestamp('set image autohistogram: %s' % str(imageAutoHistogramRange)) self.imageAutoHistogramRange = imageAutoHistogramRange self.maybeChangeDisp() def curvePlotAutoRangeSlot(self, _, plotAutoRange): #print_with_timestamp('set curve plot autorange: %s' % str(plotAutoRange)) self.curvePlotAutoRange = plotAutoRange def curvePlotLogModeSlot(self, _, log): #print_with_timestamp('set curve plot log mode: %s' % str(log)) if log: self.ui.curveWidget.setLogMode(y=True) else: self.ui.curveWidget.setLogMode(y=False) def profilePlotAutoRangeSlot(self, _, plotAutoRange): #print_with_timestamp('set profile plot autorange: %s' % str(plotAutoRange)) if plotAutoRange: self.ui.profileWidget.getViewBox().enableAutoRange() else: self.ui.profileWidget.getViewBox().disableAutoRange() self.maybePlotProfile() def profilePlotLogModeSlot(self, _, log): #print_with_timestamp('set profile plot log mode: %s' % str(log)) self.maybePlotProfile() if log: self.ui.profileWidget.setLogMode(y=True) else: self.ui.profileWidget.setLogMode(y=False) def smallDataPlotAutoRangeSlot(self, _, plotAutoRange): #print_with_timestamp('set small data plot autorange: %s' % str(plotAutoRange)) if plotAutoRange: self.ui.smallDataWidget.getViewBox().enableAutoRange() else: self.ui.smallDataWidget.getViewBox().disableAutoRange() self.maybePlotProfile() def smallDataPlotLogModeSlot(self, _, log): #print_with_timestamp('set small data log mode: %s' % str(log)) if log: self.ui.smallDataWidget.setLogMode(y=True) else: self.ui.smallDataWidget.setLogMode(y=False) def showRingsSlot(self, _, showRings): #print_with_timestamp('show rings: %s' %showRings) self.showRings = showRings if not self.showRings: self.ringItem.clear() self.maybeChangeDisp() def ringRadiiSlot(self, _, ringRadiis): #print_with_timestamp('set ring radiis: %s' %str(ringRadiis)) ringRadiisStrList = ringRadiis.split() ringRadiis = [] for ringRadiisStr in ringRadiisStrList: ringRadiis.append(float(ringRadiisStr)) self.ringRadiis = np.asarray(ringRadiis) self.maybeChangeDisp() def setProfileTypeSlot(self, _, profileType): #print_with_timestamp('set profile type: %s' %str(profileType)) self.profileType = profileType self.maybePlotProfile() def setProfileModeSlot(self, _, profileMode): #print_with_timestamp('set profile mode: %s' %str(profileMode)) self.profileMode = profileMode self.maybePlotProfile() def binaryImageSlot(self, _, binaryImage): #print_with_timestamp('apply Binaryzation: %s' %str(binaryImage)) self.binaryFlag = binaryImage self.maybeChangeDisp() self.maybePlotProfile() def setDispThresholdSlot(self, _, threshold): #print_with_timestamp('set disp threshold: %.1f' %threshold) self.dispThreshold = threshold self.maybeChangeDisp() self.maybePlotProfile() def setSmoothSlot(self, _, smooth): #print_with_timestamp('set smooth: %s' %str(smooth)) self.smoothFlag = smooth self.maybeChangeDisp() self.maybePlotProfile() def setWinSizeSlot(self, _, winSize): winSize = winSize if winSize % 2 == 0: winSize += 1 # winSize must be odd #print_with_timestamp('set smooth winsize: %d' %winSize) self.smoothWinSize = winSize self.maybeChangeDisp() self.maybePlotProfile() def setPolyOrderSlot(self, _, polyOrder): #print_with_timestamp('set poly order: %d' %polyOrder) self.polyOrder = polyOrder self.maybeChangeDisp() self.maybePlotProfile() def setAngularRminSlot(self, _, Rmin): #print_with_timestamp('set angular Rmin to %.1f' %Rmin) self.angularRmin = Rmin self.maybeChangeDisp() self.maybePlotProfile() def setAngularRmaxSlot(self, _, Rmax): #print_with_timestamp('set angular Rmax to %.1f' %Rmax) self.angularRmax = Rmax self.maybeChangeDisp() self.maybePlotProfile() def mouseMoved(self, pos): if self.dispShape is None: return None mouse_point = self.ui.imageView.view.mapToView(pos) x, y = int(mouse_point.x()), int(mouse_point.y()) filename = os.path.basename(str(self.filepath)) if 0 <= x < self.dispData.shape[1] and 0 <= y < self.dispData.shape[0]: self.ui.statusbar.showMessage( "%s x:%d y:%d I:%.2E" % (filename, x, y, self.dispData[y, x]), 5000) else: pass
class arrayNormaliserNode(Node): nodeName = 'arrayNormaliser' sigUpdatePlot = QtCore.Signal(object) def __init__(self, name): Node.__init__(self, name, terminals={ 'dataIn': { 'io': 'in' }, 'dataOut': { 'io': 'out' }, 'plotItems': { 'io': 'out' } }) color = (220, 220, 25, 255) self.plotDataItem = PlotDataItem(stepMode=True, fillLevel=0, pen={ 'color': color, 'width': 2 }) self.plotRegion = LinearRegionItem([0, 1], movable=True) self.plotRegion.sigRegionChangeFinished.connect(self.regionChanged) self.sigUpdatePlot.connect(self.updatePlot) def updatePlot(self, xy): self.plotDataItem.setData(*xy) def regionChanged(self): self.regionLimits = self.plotRegion.getRegion() self.update() def process(self, dataIn, display=True): if len(dataIn.shape) != 1: data = dataIn[:, -1] else: data = dataIn self.extremeLimits = np.nanmin(data), np.nanmax(data) # if not self.plotWidget.closed: # the plotWidget attribute is never removed but it is invalidated when the widget is closed y, x = np.histogram(data, bins=100) # self.plotWidget.clear() self.sigUpdatePlot.emit((x, y)) # self.plotWidget.addItem(self.plotRegion) if hasattr(self, 'regionLimits'): mi, ma = self.regionLimits else: mi, ma = self.extremeLimits # if hasattr(self, 'plotRegion'): # self.plotRegion.setRegion((mi, ma)) dataOut = (data - mi) / (ma - mi) # print (dataOut) return { 'dataOut': dataOut, 'plotItems': [self.plotRegion, self.plotDataItem] }
class ScanPlot(PlotItem): # scan is the scan json object def __init__(self,parent,scan): # name of independent variable input = scan[INPUT] x_label = input.get(NAME,'input') x_units = input.get(UNITS,'arb') # name of dependent variable output = scan[OUTPUT] if type(output) is dict: y_label = output.get(NAME,'output') y_units = output.get(UNITS,'arb') else: y_label = 'output' y_units = 'arb' # scan title title = scan.get( NAME, '%s vs. %s' % (y_label,x_label) ) labels = { 'bottom':'%s (%s)' % (x_label,x_units), 'left':'%s (%s)' % (y_label,y_units) } # initialize pyqtgraph plot item with title and axis labels PlotItem.__init__( self, title = title, labels = labels ) # are we setting input to optimal value of scan result? self.optimizing = scan.get(OPTIMIZE,False) # if we have multiple outputs and are optimizing, which output to optimize? self.checking_optimize = scan.get(CHECK_OPTIMIZE,False) self.click_deferred = None self.optimize_axis = scan.get(OPTIMIZE_AXIS,0) # are we saving scan data to datavault? self.saving = scan.get(SAVE,False) # are we returning to input's original position after scan? self.returning = scan.get(RETURN,False) self.scan = scan self.__parent = parent def mouseClickEvent(self,event): if self.click_deferred is not None: self.click_deferred.callback(self.getViewBox().mapSceneToView(event.scenePos()).x()) event.accept() self.click_deferred = None else: PlotItem.mouseClickEvent(self,event) # performs set up for scan def start(self): return self.set_scan_items() @inlineCallbacks def set_scan_items(self): scan = self.scan outputs = scan[OUTPUT] if type(outputs) is dict: outputs = [outputs] if self.is_saving(): # get labrad connection cxn = yield connectAsync() # get handle to data vault data_vault = cxn.data_vault # list of folders (trunk to leaf) for dataset directory save_dir = scan.get(SAVE_DIR,[]) # go to scans dir in dv yield data_vault.cd(data_vault_dir+save_dir,True) independent_variables = [ '%s [%s]' % ( scan[INPUT].get(NAME,'input'), scan[INPUT].get(UNITS,'arb') ) ] dependent_variables = [ '%s [%s]' % ( output.get(NAME,'output'), output.get(UNITS,'arb') ) for output in outputs ] save_name = scan.get(SAVE_NAME,None) default_name = text=scan.get(NAME,'') if save_name is None: save_name, result = QtGui.QInputDialog.getText( self.__parent, 'enter dataset name', 'enter title for data vault dataset', text = default_name ) if not result: save_name = default_name # create new dataset yield data_vault.new( str(save_name), independent_variables, dependent_variables ) # make note of dataset creation yield data_vault.add_parameter( 'time', get_datetime() ) self.data_vault = data_vault # instantiate scan input object self.input = INPUTS[ scan[INPUT][CLASS] ]( **mangle(scan[INPUT].get(ARGS,{})) ) # note initial position if we are planning to return to it after scan if self.is_returning(): self._return_input = yield self.input._get_input() # instantiate scan output object self.outputs = [ OUTPUTS[ output[CLASS] ]( **mangle(output.get(ARGS,{})) ) for output in outputs ] # intialize x and y values self.x_data = [] self.y_datas = [] # clear any stuff that happens to be in plot (TODO: do we ever scan the same object twice?) for item in self.allChildItems(): self.removeItem(item) # if optimizing or have multiple sources, \ # add legend to distinguish different sources if self.is_optimizing() or len(outputs) > 1: self.addLegend() if self.is_optimizing(): # initialize fit curve self.fit_curve = PlotDataItem( name='fit (%s)' % outputs[ self.optimize_axis ].get( NAME, 'output %d' % (self.optimize_axis+1) ), pen={'color':'BBB','width':2} ) # initialize data curves self.curves = [ self.plot( name=output.get(NAME,'output %d' % (index + 1)), pen=None, symbolSize=5, symbolPen=None, symbolBrush=('F55','5F5','55F','FF5','5FF','F5F')[index % 6] ) for index, output in enumerate(outputs) ] def show_fit(self): self.addItem(self.fit_curve) # put input back to initial position def return_input(self): return self.input.set_input(self._return_input) # called to increment scan by one step @inlineCallbacks def step(self): # ask input for next x value x = yield self.input.get_input() # check if scan input is done and err if so if x is None: returnValue(False) y = [] output_deferreds = [ output.get_output() for output in self.outputs ] for deferred in output_deferreds: y_value = yield deferred y.append(y_value) # add new values to data arrays self.x_data.append(x) self.y_datas.append(y) # update dataset with new datapoint if saving if self.is_saving(): yield self.data_vault.add([x]+y) for curve, y_data in zip(self.curves,zip(*self.y_datas)): # update data curve curve.setData(self.x_data,y_data) # indicate successful step returnValue(True) def is_optimizing(self): return self.optimizing def is_saving(self): return self.saving def is_returning(self): return self.returning # fit data to gaussian to find input value that optimizes output and set input to that value @inlineCallbacks def optimize_input(self): x_arr = np.array(self.x_data) y_arr = np.array(zip(*self.y_datas)[self.optimize_axis]) params = self.estimate_gaussian_parameters( x_arr,y_arr ) try: params, _ = curve_fit( self.gaussian, x_arr, y_arr, params ) except RuntimeError: pass x_fit = np.linspace(x_arr.min(),x_arr.max(),FIT_POINTS) self.fit_curve.setData( x_fit, self.gaussian(x_fit,*params) ) if self.fit_curve not in self.listDataItems(): self.show_fit() input = int(np.round(params[0])) if self.checking_optimize: result = QtGui.QMessageBox.question( self.__parent, 'check optimize', 'is optimize result of %d ok?' % input, QtGui.QMessageBox.Yes | QtGui.QMessageBox.No ) if result == QtGui.QMessageBox.No: message_box = QtGui.QMessageBox(self.__parent) click_button = message_box.addButton('click',QtGui.QMessageBox.AcceptRole) enter_button = message_box.addButton('enter',QtGui.QMessageBox.RejectRole) message_box.setText('specify how to enter location') message_box.setInformativeText('click location on graph or enter in value?') message_box.exec_() if message_box.clickedButton() == click_button: self.click_deferred = Deferred() input = yield self.click_deferred input = int(np.round(input)) else: new_input, result = QtGui.QInputDialog.getInt( self.__parent, 'enter location', 'location', input ) yield self.input.set_input(input) @staticmethod def gaussian(x,mean,std,amplitude,offset): return amplitude * np.exp(- 1. / 2. * np.square( ( x - mean ) / std) ) + offset @staticmethod def estimate_gaussian_parameters(x,y): min = y.min() max = y.max() offset = min amplitude = max - min mean_index = y.argmax() mean = x[mean_index] threshold = min + (max - min) / 2 right_estimate = None index = mean_index while True: if index == len(y): break if y[index] < threshold: right_estimate = abs(x[index] - mean) / 2.355 * 2 index += 1 left_estimate = None index = mean_index while True: if index < 0: break if y[index] < threshold: left_estimate = abs(x[index] - mean) / 2.355 * 2 index -= 1 if right_estimate is None and left_estimate is None: std = abs(x[len(y)/2]-x[0]) elif right_estimate is None: std = left_estimate elif left_estimate is None: std = right_estimate else: std = ( left_estimate + right_estimate ) / 2. return (mean,std,amplitude,offset)