def main(dataSrc): plottrlog.LEVEL = logging.INFO app = QtWidgets.QApplication([]) fc, win = autoplot(plotWidgetClass=plotWidgetClass) dataThread = QtCore.QThread() dataSrc.moveToThread(dataThread) dataSrc.dataready.connect( lambda d: win.setInput(data=d, resetDefaults=False)) dataSrc.nomoredata.connect(dataThread.quit) dataThread.started.connect(dataSrc.gimmesomedata) win.windowClosed.connect(dataThread.quit) dataThread.start() if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): QtWidgets.QApplication.instance().exec_()
def setImage(self, x: np.ndarray, y: np.ndarray, z: np.ndarray) -> None: """Set data to be plotted as image. Clears the plot before creating a new image item that gets places in the plot and linked to the colorscale. :param x: x coordinates (as 2D meshgrid) :param y: y coordinates (as 2D meshgrid) :param z: data values (as 2D meshgrid) :return: None """ self.clearPlot() self.img = pg.ImageItem() self.plot.addItem(self.img) self.img.setImage(z) self.img.setRect(QtCore.QRectF(x.min(), y.min(), x.max() - x.min(), y.max() - y.min())) self.colorbar.setImageItem(self.img) self.colorbar.rounding = (z.max() - z.min()) * 1e-2 self.colorbar.setLevels((z.min(), z.max()))
def __init__(self, parent: Optional[QtWidgets.QWidget] = None): super().__init__(parent=parent) self.plotDataType = PlotDataType.unknown self.plotType = PlotType.empty # The default complex behavior is set here. self.complexRepresentation = ComplexRepresentation.realAndImag # A toolbar for configuring the plot self.plotOptionsToolBar = AutoPlotToolBar('Plot options', self) self.layout().insertWidget(1, self.plotOptionsToolBar) self.plotOptionsToolBar.plotTypeSelected.connect( self._plotTypeFromToolBar) self.plotOptionsToolBar.complexRepresentationSelected.connect( self._complexPreferenceFromToolBar) scaling = dpiScalingFactor(self) iconSize = int(36 + 8 * (scaling - 1)) self.plotOptionsToolBar.setIconSize(QtCore.QSize(iconSize, iconSize)) self.setMinimumSize(int(640 * scaling), int(480 * scaling))
class MonitorIntervalInput(QtGui.QWidget): """ Simple form-like widget for entering a monitor/refresh interval. Only has a label and a spin-box as input. It's signal `intervalChanged(int)' is emitted when the value of the spinbox has changed. """ intervalChanged = QtCore.pyqtSignal(int) def __init__(self, parent=None): super().__init__(parent) self.spin = QtGui.QSpinBox() layout = QtGui.QFormLayout() layout.addRow('Refresh interval (s)', self.spin) self.setLayout(layout) self.spin.valueChanged.connect(self.spinValueChanged) @QtCore.pyqtSlot(int) def spinValueChanged(self, val): self.intervalChanged.emit(val)
def image_test(): app = QtWidgets.QApplication([]) # create data x = np.linspace(0, 10, 51) y = np.linspace(-4, 4, 51) xx, yy = np.meshgrid(x, y, indexing='ij') zz = np.cos(xx)*np.exp(-(yy-1.)**2) # layout widget pgWidget = pg.GraphicsLayoutWidget() # main plot imgPlot = pgWidget.addPlot(title='my image', row=0, col=0) img = pg.ImageItem() imgPlot.addItem(img) # histogram and colorbar hist = pg.HistogramLUTItem() hist.setImageItem(img) pgWidget.addItem(hist) hist.gradient.loadPreset('viridis') # cut elements pgWidget2 = pg.GraphicsLayoutWidget() # plots for x and y cuts xplot = pgWidget2.addPlot(row=1, col=0) yplot = pgWidget2.addPlot(row=0, col=0) xplot.addLegend() yplot.addLegend() # add crosshair to main plot vline = pg.InfiniteLine(angle=90, movable=False, pen='r') hline = pg.InfiniteLine(angle=0, movable=False, pen='b') imgPlot.addItem(vline, ignoreBounds=True) imgPlot.addItem(hline, ignoreBounds=True) def crossMoved(event): pos = event[0].scenePos() if imgPlot.sceneBoundingRect().contains(pos): origin = imgPlot.vb.mapSceneToView(pos) vline.setPos(origin.x()) hline.setPos(origin.y()) vidx = np.argmin(np.abs(origin.x()-x)) hidx = np.argmin(np.abs(origin.y()-y)) yplot.clear() yplot.plot(zz[vidx, :], y, name='vertical cut', pen=pg.mkPen('r', width=2), symbol='o', symbolBrush='r', symbolPen=None) xplot.clear() xplot.plot(x, zz[:, hidx], name='horizontal cut', pen=pg.mkPen('b', width=2), symbol='o', symbolBrush='b', symbolPen=None) proxy = pg.SignalProxy(imgPlot.scene().sigMouseClicked, slot=crossMoved) dg = widgetDialog(pgWidget, title='pyqtgraph image test') dg2 = widgetDialog(pgWidget2, title='line cuts') # setting the data img.setImage(zz) img.setRect(QtCore.QRectF(0, -4, 10, 8.)) hist.setLevels(zz.min(), zz.max()) # formatting imgPlot.setLabel('left', "Y", units='T') imgPlot.setLabel('bottom', "X", units='A') xplot.setLabel('left', 'Z') xplot.setLabel('bottom', "X", units='A') yplot.setLabel('left', "Y", units='T') yplot.setLabel('bottom', "Z") if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): QtWidgets.QApplication.instance().exec_()
class GridOptionWidget(QtGui.QWidget): optionSelected = QtCore.pyqtSignal(object) def __init__(self, parent=None): super().__init__(parent) self._emitUpdate = True # make radio buttons and layout ## self.buttons = { GridOption.noGrid: QtGui.QRadioButton('No grid'), GridOption.guessShape: QtGui.QRadioButton('Guess shape'), GridOption.specifyShape: QtGui.QRadioButton('Specify shape'), } btnLayout = QtGui.QVBoxLayout() self.btnGroup = QtGui.QButtonGroup(self) for opt in GridOption: btn = self.buttons[opt] self.btnGroup.addButton(btn, opt.value) btnLayout.addWidget(btn) btnBox = QtGui.QGroupBox('Grid') btnBox.setLayout(btnLayout) # make shape spec widget self.shapeSpec = ShapeSpecificationWidget() shapeLayout = QtGui.QVBoxLayout() shapeLayout.addWidget(self.shapeSpec) shapeBox = QtGui.QGroupBox('Shape') shapeBox.setLayout(shapeLayout) # Widget layout layout = QtGui.QHBoxLayout() layout.addWidget(btnBox) layout.addWidget(shapeBox) layout.addStretch() self.setLayout(layout) # Connect signals/slots # self.btnGroup.buttonToggled.connect(self.gridButtonSelected) self.shapeSpec.confirm.clicked.connect(self.shapeSpecified) # Default settings self.buttons[GridOption.noGrid].setChecked(True) self.enableShapeEdit(False) def getGrid(self): activeBtn = self.btnGroup.checkedButton() activeId = self.btnGroup.id(activeBtn) opts = {} if GridOption(activeId) == GridOption.specifyShape: opts['shape'] = self.shapeSpec.getShape() return GridOption(activeId), opts def setGrid(self, grid): # This function should not trigger an emission for an update. # We only want that when the user sets the grid in the UI, # to avoid recursive calls self._emitUpdate = False method, opts = grid for k, btn in self.buttons.items(): if k == method: btn.setChecked(True) if method == GridOption.specifyShape: self.setShape(opts['shape']) @QtCore.pyqtSlot(QtGui.QAbstractButton, bool) def gridButtonSelected(self, btn, checked): if checked: # only emit the signal when the update is from the UI if self._emitUpdate: self.signalGridOption(self.getGrid()) if GridOption(self.btnGroup.id(btn)) == GridOption.specifyShape: self.enableShapeEdit(True) else: self.enableShapeEdit(False) self._emitUpdate = True @QtCore.pyqtSlot() def shapeSpecified(self): self.signalGridOption(self.getGrid()) def signalGridOption(self, grid): self.optionSelected.emit(grid) def setAxes(self, axes): self.shapeSpec.setAxes(axes) if self.getGrid()[0] == GridOption.specifyShape: self.enableShapeEdit(True) else: self.enableShapeEdit(False) def setShape(self, shape): self.shapeSpec.setShape(shape) def enableShapeEdit(self, enable): self.shapeSpec.enableEditing(enable)
class DataGridder(Node): """ A node that can put data onto or off a grid. Has one property: grid. That can have the following values: * ... """ nodeName = "Gridder" uiClass = DataGridderNodeWidget shapeDetermined = QtCore.pyqtSignal(tuple) axesList = QtCore.pyqtSignal(list) def __init__(self, *arg, **kw): self._grid = GridOption.noGrid, {} self._shape = None self._invalid = False super().__init__(*arg, **kw) # Properties @property def grid(self): return self._grid @grid.setter @updateOption('grid') def grid(self, val: Tuple[GridOption, Dict[str, Any]]): try: method, opts = val except TypeError: raise ValueError(f"Invalid grid specification.") if not method in GridOption: raise ValueError(f"Invalid grid method specification.") if not isinstance(opts, dict): raise ValueError(f"Invalid grid options specification.") self._grid = val # Processing def validateOptions(self, data: Any): if not super().validateOptions(data): return False return True def process(self, **kw): data = kw['dataIn'] if data is None: return None data = super().process(**kw) if data is None: return None data = data['dataOut'].copy() self.axesList.emit(data.axes()) dout = None method, opts = self._grid if isinstance(data, DataDict): if method is GridOption.noGrid: dout = data.expand() elif method is GridOption.guessShape: dout = dd.datadict_to_meshgrid(data) elif method is GridOption.specifyShape: dout = dd.datadict_to_meshgrid(data, target_shape=opts['shape']) elif isinstance(data, MeshgridDataDict): if method is GridOption.noGrid: dout = dd.meshgrid_to_datadict(data) elif method is GridOption.guessShape: dout = data elif method is GridOption.specifyShape: self.logger().warning( f"Data is already on grid. Ignore shape.") dout = data else: self.logger().error( f"Unknown data type {type(data)}.") return None if dout is None: return None if hasattr(dout, 'shape'): self.shapeDetermined.emit(dout.shape()) else: self.shapeDetermined.emit(tuple()) return dict(dataOut=dout) ### Setup UI def setupUi(self): super().setupUi() self.axesList.connect(self.ui.setAxes) self.shapeDetermined.connect(self.ui.setShape)
def __init__(self, parent: Optional[QtWidgets.QWidget] = None, dbPath: Optional[str] = None): """Constructor for :class:`QCodesDBInspector`.""" super().__init__(parent) self._plotWindows: Dict[int, WindowDict] = {} self.filepath = dbPath self.dbdf = None self.monitor = QtCore.QTimer() # flag for determining what has been loaded so far. # * None: nothing opened yet. # * -1: empty DS open. # * any value > 0: run ID from the most recent loading. self.latestRunId = None self.setWindowTitle('Plottr | QCoDeS dataset inspectr') ### GUI elements # Main Selection widgets self.dateList = DateList() self._selected_dates: Tuple[str, ...] = () self.runList = RunList() self.runInfo = RunInfo() rightSplitter = QtWidgets.QSplitter(QtCore.Qt.Vertical) rightSplitter.addWidget(self.runList) rightSplitter.addWidget(self.runInfo) rightSplitter.setSizes([400, 200]) splitter = QtWidgets.QSplitter() splitter.addWidget(self.dateList) splitter.addWidget(rightSplitter) splitter.setSizes([100, 500]) self.setCentralWidget(splitter) # status bar self.status = QtWidgets.QStatusBar() self.setStatusBar(self.status) # toolbar self.toolbar = self.addToolBar('Data monitoring') # toolbar item: monitor interval self.monitorInput = MonitorIntervalInput() self.monitorInput.setToolTip('Set to 0 for disabling') self.monitorInput.intervalChanged.connect(self.setMonitorInterval) self.toolbar.addWidget(self.monitorInput) self.toolbar.addSeparator() # toolbar item: auto-launch plotting self.autoLaunchPlots = FormLayoutWrapper([('Auto-plot new', QtWidgets.QCheckBox())]) tt = "If checked, and automatic refresh is running, " tt += " launch plotting window for new datasets automatically." self.autoLaunchPlots.setToolTip(tt) self.toolbar.addWidget(self.autoLaunchPlots) # menu bar menu = self.menuBar() fileMenu = menu.addMenu('&File') # action: load db file loadAction = QtWidgets.QAction('&Load', self) loadAction.setShortcut('Ctrl+L') loadAction.triggered.connect(self.loadDB) fileMenu.addAction(loadAction) # action: updates from the db file refreshAction = QtWidgets.QAction('&Refresh', self) refreshAction.setShortcut('R') refreshAction.triggered.connect(self.refreshDB) fileMenu.addAction(refreshAction) # sizing scaledSize = 640 * rint(self.logicalDpiX() / 96.0) self.resize(scaledSize, scaledSize) ### Thread workers # DB loading. can be slow, so nice to have in a thread. self.loadDBProcess = LoadDBProcess() self.loadDBThread = QtCore.QThread() self.loadDBProcess.moveToThread(self.loadDBThread) self.loadDBProcess.pathSet.connect(self.loadDBThread.start) self.loadDBProcess.dbdfLoaded.connect(self.DBLoaded) self.loadDBProcess.dbdfLoaded.connect(self.loadDBThread.quit) self.loadDBThread.started.connect( self.loadDBProcess.loadDB) # type: ignore[attr-defined] ### connect signals/slots self.dbdfUpdated.connect(self.updateDates) self.dbdfUpdated.connect(self.showDBPath) self.dateList.datesSelected.connect(self.setDateSelection) self.dateList.fileDropped.connect(self.loadFullDB) self.runList.runSelected.connect(self.setRunSelection) self.runList.runActivated.connect(self.plotRun) self._sendInfo.connect(self.runInfo.setInfo) self.monitor.timeout.connect(self.monitorTriggered) if self.filepath is not None: self.loadFullDB(self.filepath)
def setupUi(self, MainWindow: QtWidgets.QMainWindow) -> None: MainWindow.setObjectName("MainWindow") MainWindow.resize(935, 569) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget) self.verticalLayout.setObjectName("verticalLayout") self.splitter = QtWidgets.QSplitter(self.centralwidget) self.splitter.setOrientation(QtCore.Qt.Horizontal) self.splitter.setObjectName("splitter") self.fileList = DataFileList(self.splitter) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(1) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.fileList.sizePolicy().hasHeightForWidth()) self.fileList.setSizePolicy(sizePolicy) self.fileList.setAlternatingRowColors(False) self.fileList.setUniformRowHeights(True) self.fileList.setObjectName("fileList") self.fileContents = DataFileContent(self.splitter) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(2) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.fileContents.sizePolicy().hasHeightForWidth()) self.fileContents.setSizePolicy(sizePolicy) self.fileContents.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.fileContents.setUniformRowHeights(True) self.fileContents.setAllColumnsShowFocus(False) self.fileContents.setObjectName("fileContents") self.verticalLayout.addWidget(self.splitter) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 935, 22)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.monitorToolBar = MonitorToolBar(MainWindow) self.monitorToolBar.setObjectName("monitorToolBar") MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.monitorToolBar) self.autoPlotNewAction = QtWidgets.QAction(MainWindow) self.autoPlotNewAction.setCheckable(True) self.autoPlotNewAction.setObjectName("autoPlotNewAction") self.monitorToolBar.addAction(self.autoPlotNewAction) self.retranslateUi(MainWindow) MainWindow.dataFileSelected.connect(self.fileContents.setData) self.fileList.dataFileSelected.connect(MainWindow.processFileSelection) self.fileContents.customContextMenuRequested['QPoint'].connect( self.fileContents.onCustomContextMenuRequested ) # type: ignore[index] self.fileContents.plotRequested.connect(MainWindow.plotSelected) self.fileList.itemSelectionChanged.connect( self.fileList.processSelection) self.fileList.newDataFilesFound.connect(MainWindow.onNewDataFilesFound) QtCore.QMetaObject.connectSlotsByName(MainWindow)