class DataViewer(QtWidgets.QMainWindow): def __init__(self, data_directory=None, window_title='Data browser', default_parameter='amplitude', extensions=None, verbose=1): """ Contstructs a simple viewer for Qcodes data. Args: data_directory (string or None): The directory to scan for experiments. default_parameter (string): A name of default parameter to plot. extensions (list): A list with the data file extensions to filter. verbose (int): The logging verbosity level. """ super(DataViewer, self).__init__() if extensions is None: extensions = ['dat', 'hdf5'] self.verbose = verbose self.default_parameter = default_parameter self.data_directories = [None] * 2 self.directory_index = 0 if data_directory is None: data_directory = qcodes.DataSet.default_io.base_location self.extensions = extensions # setup GUI self.dataset = None self.text = QtWidgets.QLabel() # logtree self.logtree = QtWidgets.QTreeView() self.logtree.setSelectionBehavior( QtWidgets.QAbstractItemView.SelectRows) self._treemodel = QtGui.QStandardItemModel() self.logtree.setModel(self._treemodel) # metatabs self.meta_tabs = QtWidgets.QTabWidget() self.meta_tabs.addTab(QtWidgets.QWidget(), 'metadata') self.__debug = dict() if isinstance(QtPlot, QWidget): self.qplot = QtPlot() else: self.qplot = QtPlot(remote=False) if isinstance(self.qplot, QWidget): self.plotwindow = self.qplot else: self.plotwindow = self.qplot.win topLayout = QtWidgets.QHBoxLayout() self.filterbutton = QtWidgets.QPushButton() self.filterbutton.setText('Filter data') self.filtertext = QtWidgets.QLineEdit() self.outCombo = QtWidgets.QComboBox() topLayout.addWidget(self.text) topLayout.addWidget(self.filterbutton) topLayout.addWidget(self.filtertext) treesLayout = QtWidgets.QHBoxLayout() treesLayout.addWidget(self.logtree) treesLayout.addWidget(self.meta_tabs) vertLayout = QtWidgets.QVBoxLayout() vertLayout.addItem(topLayout) vertLayout.addItem(treesLayout) vertLayout.addWidget(self.plotwindow) self.pptbutton = QtWidgets.QPushButton() self.pptbutton.setText('Send data to powerpoint') self.clipboardbutton = QtWidgets.QPushButton() self.clipboardbutton.setText('Copy image to clipboard') bLayout = QtWidgets.QHBoxLayout() bLayout.addWidget(self.outCombo) bLayout.addWidget(self.pptbutton) bLayout.addWidget(self.clipboardbutton) vertLayout.addItem(bLayout) widget = QtWidgets.QWidget() widget.setLayout(vertLayout) self.setCentralWidget(widget) self.setWindowTitle(window_title) self.logtree.header().resizeSection(0, 280) # disable edit self.logtree.setEditTriggers( QtWidgets.QAbstractItemView.NoEditTriggers) self.set_data_directory(data_directory) self.logtree.doubleClicked.connect(self.log_callback) self.outCombo.currentIndexChanged.connect(self.combobox_callback) self.filterbutton.clicked.connect( lambda: self.update_logs(filter_str=self.filtertext.text())) self.pptbutton.clicked.connect(self.ppt_callback) self.clipboardbutton.clicked.connect(self.clipboard_callback) menuBar = self.menuBar() menuDict = { '&Data': { '&Reload Data': self.update_logs, '&Preload all Info': self.load_info, '&Quit': self.close }, '&Folder': { '&Select Dir1': lambda: self.select_directory(index=0), 'Select &Dir2': lambda: self.select_directory(index=1), '&Toggle Dirs': self.toggle_data_directory }, '&Help': { '&Info': self.show_help } } for (k, menu) in menuDict.items(): mb = menuBar.addMenu(k) for (kk, action) in menu.items(): act = QtWidgets.QAction(kk, self) mb.addAction(act) act.triggered.connect(action) if self.verbose >= 2: print('created gui...') # get logs from disk self.update_logs() self.datatag = None self.logtree.setColumnHidden(2, True) self.logtree.setColumnHidden(3, True) self.show() def set_data_directory(self, data_directory, index=0): self.data_directories[index] = data_directory self.data_directory = data_directory self.disk_io = qcodes.DiskIO(data_directory) logging.info('DataViewer: data directory %s' % data_directory) self.text.setText('Log files at %s' % self.data_directory) def show_help(self): """ Show help dialog """ self.infotext = "Dataviewer for qcodes datasets" QtWidgets.QMessageBox.information(self, 'qtt dataviwer control info', self.infotext) def toggle_data_directory(self): index = (self.directory_index + 1) % len(self.data_directories) self.directory_index = index self.data_directory = self.data_directories[index] self.disk_io = qcodes.DiskIO(self.data_directory) logging.info('DataViewer: data directory %s' % self.data_directory) self.text.setText('Log files at %s' % self.data_directory) self.update_logs() def ppt_callback(self): if self.dataset is None: print('no data selected') return qtt.utilities.tools.addPPT_dataset(self.dataset, customfig=self.qplot) def clipboard_callback(self): self.qplot.copyToClipboard() @staticmethod def get_data_info(metadata): params = [] try: if 'loop' in metadata.keys(): sv = metadata['loop']['sweep_values'] params.append( '%s [%.2f to %.2f %s]' % (sv['parameter']['label'], sv['values'][0]['first'], sv['values'][0]['last'], sv['parameter']['unit'])) for act in metadata['loop']['actions']: if 'sweep_values' in act.keys(): sv = act['sweep_values'] params.append( '%s [%.2f - %.2f %s]' % (sv['parameter']['label'], sv['values'][0]['first'], sv['values'][0]['last'], sv['parameter']['unit'])) infotxt = ' ,'.join(params) infotxt = infotxt + ' | ' + ', '.join( [('%s' % (v['label'])) for (k, v) in metadata['arrays'].items() if not v['is_setpoint']]) elif 'scanjob' in metadata.keys(): sd = metadata['scanjob']['sweepdata'] params.append('%s [%.2f to %.2f]' % (sd['param'], sd['start'], sd['end'])) if 'stepdata' in metadata['scanjob']: sd = metadata['scanjob']['stepdata'] params.append('%s [%.2f to %.2f]' % (sd['param'], sd['start'], sd['end'])) infotxt = ' ,'.join(params) infotxt = infotxt + ' | ' + \ ', '.join(metadata['scanjob']['minstrument']) else: infotxt = 'info about plot' except BaseException: infotxt = 'info about plot' return infotxt def load_info(self): try: for row in range(self._treemodel.rowCount()): index = self._treemodel.index(row, 0) i = 0 while (index.child(i, 0).data() is not None): filename = index.child(i, 3).data() loc = '\\'.join(filename.split('\\')[:-1]) tempdata = qcodes.DataSet(loc) tempdata.read_metadata() infotxt = DataViewer.get_data_info(tempdata.metadata) self._treemodel.setData(index.child(i, 1), infotxt) if 'comment' in tempdata.metadata.keys(): self._treemodel.setData(index.child(i, 4), tempdata.metadata['comment']) i = i + 1 except Exception as e: print(e) def select_directory(self, index=0): d = QtWidgets.QFileDialog(caption='Select data directory') d.setFileMode(QFileDialog.Directory) if d.exec(): datadir = d.selectedFiles()[0] self.set_data_directory(datadir, index) print('update logs') self.update_logs() @staticmethod def find_datafiles(datadir, extensions=None, show_progress=True): """ Find all datasets in a directory with a given extension """ if extensions is None: extensions = ['dat', 'hdf5'] dd = [] for e in extensions: dd += qtt.pgeometry.findfilesR(datadir, '.*%s' % e, show_progress=show_progress) datafiles = sorted(dd) return datafiles @staticmethod def _filename2datetag(filename): """ Parse a filename to a date tag and base filename """ if filename.endswith('.json'): datetag = filename.split(os.sep)[-1].split('_')[0] logtag = filename.split(os.sep)[-1][:-5] else: # other formats, assumed to be in normal form datetag, logtag = filename.split(os.sep)[-3:-1] return datetag, logtag def update_logs(self, filter_str=None): ''' Update the list of measurements ''' model = self._treemodel self.datafiles = self.find_datafiles(self.data_directory, self.extensions) dd = self.datafiles if filter_str: dd = [s for s in dd if filter_str in s] if self.verbose: print('DataViewer: found %d files' % (len(dd))) model.clear() model.setHorizontalHeaderLabels( ['Log', 'Arrays', 'location', 'filename', 'Comments']) logs = dict() for _, filename in enumerate(dd): try: datetag, logtag = self._filename2datetag(filename) if datetag not in logs: logs[datetag] = dict() logs[datetag][logtag] = filename except Exception: pass self.logs = logs if self.verbose >= 2: print('DataViewer: create gui elements') for i, datetag in enumerate(sorted(logs.keys())[::-1]): if self.verbose >= 2: print('DataViewer: datetag %s ' % datetag) parent1 = QtGui.QStandardItem(datetag) for j, logtag in enumerate(sorted(logs[datetag])): filename = logs[datetag][logtag] child1 = QtGui.QStandardItem(logtag) child2 = QtGui.QStandardItem('info about plot') if self.verbose >= 2: print('datetag %s, logtag %s' % (datetag, logtag)) child3 = QtGui.QStandardItem(os.path.join(datetag, logtag)) child4 = QtGui.QStandardItem(filename) parent1.appendRow([child1, child2, child3, child4]) model.appendRow(parent1) self.logtree.setColumnWidth(0, 240) self.logtree.setColumnHidden(2, True) self.logtree.setColumnHidden(3, True) if self.verbose >= 2: print('DataViewer: update_logs done') def _create_meta_tree(self, meta_dict): metatree = QtWidgets.QTreeView() _metamodel = QtGui.QStandardItemModel() metatree.setModel(_metamodel) metatree.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) _metamodel.setHorizontalHeaderLabels(['metadata', 'value']) try: self.fill_item(_metamodel, meta_dict) return metatree except Exception as ex: print(ex) def update_meta_tabs(self): ''' Update metadata tree ''' meta = self.dataset.metadata self.meta_tabs.clear() if 'gates' in meta.keys(): self.meta_tabs.addTab(self._create_meta_tree(meta['gates']), 'gates') elif meta.get('station', dict()).get('instruments', dict()).get('gates', None) is not None: self.meta_tabs.addTab( self._create_meta_tree( meta['station']['instruments']['gates']), 'gates') if meta.get('station', dict()).get('instruments', None) is not None: if 'instruments' in meta['station'].keys(): self.meta_tabs.addTab( self._create_meta_tree(meta['station']['instruments']), 'instruments') self.meta_tabs.addTab(self._create_meta_tree(meta), 'metadata') def fill_item(self, item, value): ''' recursive population of tree structure with a dict ''' def new_item(parent, text, val=None): child = QtGui.QStandardItem(text) self.fill_item(child, val) parent.appendRow(child) if value is None: return elif isinstance(value, dict): for key, val in sorted(value.items()): if type(val) in [str, float, int]: child = [ QtGui.QStandardItem(str(key)), QtGui.QStandardItem(str(val)) ] item.appendRow(child) else: new_item(item, str(key), val) else: new_item(item, str(value)) def get_plot_parameter(self): ''' Return parameter to be plotted ''' param_name = self.outCombo.currentText() if param_name is not '': return param_name parameters = self.dataset.arrays.keys() if self.default_parameter in parameters: return self.default_parameter return self.dataset.default_parameter_name() def selected_data_file(self): """ Return currently selected data file """ return self.datatag def combobox_callback(self, index): if not self._update_plot_: return param_name = self.get_plot_parameter() if self.dataset is not None: self.update_plot(param_name) def log_callback(self, index): """ Function called when. a log entry is selected """ logging.info('logCallback: index %s' % str(index)) self.__debug['last'] = index pp = index.parent() row = index.row() tag = pp.child(row, 2).data() filename = pp.child(row, 3).data() self.filename = filename self.datatag = tag if tag is None: return if self.verbose >= 2: print('DataViewer logCallback: tag %s, filename %s' % (tag, filename)) try: logging.debug('DataViewer: load tag %s' % tag) data = DataViewer.load_data(filename, tag) if not data: raise ValueError('File invalid (%s) ...' % filename) self.dataset = data self.update_meta_tabs() data_keys = data.arrays.keys() infotxt = DataViewer.get_data_info(data.metadata) q = pp.child(row, 1).model() q.setData(pp.child(row, 1), infotxt) if 'comment' in data.metadata.keys(): q.setData(pp.child(row, 2), data.metadata['comment']) self.reset_combo_items(data, data_keys) param_name = self.get_plot_parameter() self.update_plot(param_name) except Exception as e: print('logCallback! error: %s' % str(e)) logging.exception(e) return def reset_combo_items(self, data, keys): old_key = self.outCombo.currentText() self._update_plot_ = False self.outCombo.clear() for key in keys: if not getattr(data, key).is_setpoint: self.outCombo.addItem(key) if old_key in keys: self.outCombo.setCurrentIndex(self.outCombo.findText(old_key)) self._update_plot_ = True return @staticmethod def load_data(filename, tag): if filename.endswith('.json'): location = filename else: # qcodes datasets are found by filename, but should be loaded by directory... location = os.path.split(filename)[0] data = qtt.data.load_dataset(location) return data def update_plot(self, parameter): self.qplot.clear() if parameter is None: logging.info('could not find parameter for DataSet') return else: logging.info('using plotting parameter %s' % parameter) self.qplot.add(getattr(self.dataset, parameter))
class data_viewer(QtWidgets.QMainWindow, Ui_dataviewer): """docstring for virt_gate_matrix_GUI""" def __init__(self, datadir=None, window_title='Data browser'): # set graphical user interface instance_ready = True self.app = QtCore.QCoreApplication.instance() if self.app is None: instance_ready = False self.app = QtWidgets.QApplication([]) super(QtWidgets.QMainWindow, self).__init__() self.app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5()) self.setupUi(self) if datadir is None: datadir = DataSet.default_io.base_location # set-up tree view for data self._treemodel = QtGui.QStandardItemModel() self.data_view.setModel(self._treemodel) self.tabWidget.addTab(QtWidgets.QWidget(), 'metadata') self.qplot = QtPlot(remote=False) self.plotwindow = self.qplot.win self.qplot.max_len = 10 self.horizontalLayout_4.addWidget(self.plotwindow) # Fix some initializations in the window self.splitter_2.setSizes([int(self.height() / 2)] * 2) self.splitter.setSizes( [int(self.width() / 3), int(2 * self.width() / 3)]) # connect callbacks self.data_view.doubleClicked.connect(self.logCallback) self.actionReload_data.triggered.connect(self.updateLogs) self.actionPreload_all_info.triggered.connect(self.loadInfo) self.actionAdd_directory.triggered.connect(self.selectDirectory) self.filter_button.clicked.connect( lambda: self.updateLogs(filter_str=self.filter_input.text())) self.send_ppt.clicked.connect(self.pptCallback) self.copy_im.clicked.connect(self.clipboardCallback) self.split_data.clicked.connect(self.split_dataset) # initialize defaults self.extensions = ['dat', 'hdf5'] self.dataset = None self.datatag = None self.datadirlist = [] self.datadirindex = 0 self.color_list = [ pg.mkColor(cl) for cl in qcodes.plots.pyqtgraph.color_cycle ] self.current_params = [] self.subpl_ind = dict() self.splitted = False # add default directory self.addDirectory(datadir) self.updateLogs() # Launch app self.show() if instance_ready == False: self.app.exec() def find_datafiles(self, datadir, extensions=['dat', 'hdf5'], show_progress=True): """ Find all datasets in a directory with a given extension """ dd = [] for e in extensions: dd += self.findfiles(datadir, e) dd.sort() datafiles = sorted(dd) return datafiles def loadInfo(self): logging.debug('loading info') try: for row in range(self._treemodel.rowCount()): index = self._treemodel.index(row, 0) i = 0 while (index.child(i, 0).data() is not None): filename = index.child(i, 3).data() loc = os.path.dirname(filename) tempdata = qcodes.DataSet(loc) tempdata.read_metadata() infotxt = self.getArrayStr(tempdata.metadata) self._treemodel.setData(index.child(i, 1), infotxt) i = i + 1 except Exception as e: logging.warning(e) def setDatadir(self, newindex): logging.info(f'Setting datadir with index: {newindex}') oldindex = self.datadirindex self.datadirindex = newindex datadir = self.datadirlist[newindex] self.io = DiskIO(datadir) logging.info('DataViewer: data directory %s' % datadir) self.logfile.setText('Log files at %s' % datadir) self.menuFolder.actions()[oldindex + 1].setText( self.menuFolder.actions()[oldindex + 1].text()[2:]) self.menuFolder.actions()[newindex + 1].setText( '>>' + self.menuFolder.actions()[newindex + 1].text()) self.updateLogs() def selectDirectory(self): from qtpy.QtWidgets import QFileDialog d = QtWidgets.QFileDialog(caption='Select data directory') d.setFileMode(QFileDialog.Directory) if d.exec(): datadir = d.selectedFiles()[0] self.addDirectory(datadir) def addDirectory(self, datadir): newindex = len(self.datadirlist) self.datadirlist.append(datadir) if len(self.datadirlist) == 1: datadir = '>>' + datadir new_act = QtWidgets.QAction(datadir, self) new_act.triggered.connect(partial(self.setDatadir, newindex)) self.menuFolder.addAction(new_act) self.setDatadir(newindex) def updateLogs(self, filter_str=None): ''' Update the list of measurements ''' logging.info('updating logs') model = self._treemodel self.datafiles = self.find_datafiles( self.datadirlist[self.datadirindex], self.extensions) dd = self.datafiles if filter_str: dd = [s for s in dd if filter_str in s] logging.info(f'DataViewer: found {len(dd)} files') model.clear() model.setHorizontalHeaderLabels( ['Log', 'Arrays', 'location', 'filename']) logs = dict() for i, d in enumerate(dd): try: datetag, logtag = d.split(os.sep)[-3:-1] if datetag not in logs: logs[datetag] = dict() logs[datetag][logtag] = d except Exception as e: print(e) pass self.logs = logs logging.debug('DataViewer: create gui elements') for i, datetag in enumerate(sorted(logs.keys())[::-1]): logging.debug(f'DataViewer: datetag {datetag}') parent1 = QtGui.QStandardItem(datetag) for j, logtag in enumerate(sorted(logs[datetag])): filename = logs[datetag][logtag] child1 = QtGui.QStandardItem(logtag) child2 = QtGui.QStandardItem('info about plot') logging.debug(f'datetag: {datetag}, logtag: {logtag}') child3 = QtGui.QStandardItem(os.path.join(datetag, logtag)) child4 = QtGui.QStandardItem(filename) parent1.appendRow([child1, child2, child3, child4]) model.appendRow(parent1) self.data_view.setColumnWidth(0, 240) self.data_view.setColumnHidden(2, True) self.data_view.setColumnHidden(3, True) logging.debug('DataViewer: updateLogs done') def logCallback(self, index): """ Function called when. a log entry is selected """ logging.info('logCallback: index %s' % str(index)) oldtab_index = self.tabWidget.currentIndex() pp = index.parent() row = index.row() tag = pp.child(row, 2).data() filename = pp.child(row, 3).data() self.filename = filename self.datatag = tag if tag is None: return logging.debug( f'DataViewer logCallback: tag {tag}, filename {filename}') try: logging.debug('DataViewer: load tag %s' % tag) data = self.loadData(filename, tag) if not data: raise ValueError('File invalid (%s) ...' % filename) self.dataset = data self.updateMetaTabs() try: self.tabWidget.setCurrentIndex(oldtab_index) except: pass data_keys = data.arrays.keys() infotxt = self.getArrayStr(data.metadata) q = pp.child(row, 1).model() q.setData(pp.child(row, 1), infotxt) self.resetComboItems(data, data_keys) except Exception as e: print('logCallback! error: %s' % str(e)) logging.exception(e) def resetComboItems(self, data, keys): # Clearing old stuff self.clearLayout(self.data_select_lay) self.qplot.clear() self.boxes = dict() self.box_labels = dict() self.param_keys = list() to_plot = list() # Loop through keys and add graphics items for key in keys: if not getattr(data, key).is_setpoint: box = QtWidgets.QCheckBox() box.clicked.connect(self.checkbox_callback) box.setText(key) label = QtWidgets.QLabel() self.data_select_lay.addRow(box, label) self.boxes[key] = box self.box_labels[key] = label self.param_keys.append(key) if key in self.subpl_ind.keys(): self.boxes[key].setChecked(True) to_plot.append(key) self.data_select_lay.setLabelAlignment(QtCore.Qt.AlignLeft) # If no old parameters can be plotted, defined first one if not to_plot: def_key = list(self.boxes.values())[0].text() to_plot.append(self.boxes[def_key].text()) self.boxes[def_key].setChecked(True) # Update the parameter plots self.subpl_ind = dict() self.current_params = list() self.updatePlots(to_plot) if self.splitted: self.split_dataset() def clearPlots(self): self.qplot.clear() self.current_params = list( set(self.current_params) - set(self.subpl_ind.keys())) self.subpl_ind = dict() def clearLayout(self, layout): if layout is not None: while layout.count(): item = layout.takeAt(0) widget = item.widget() if widget is not None: widget.deleteLater() else: self.clearLayout(item.layout()) def checkbox_callback(self, state): if self.splitted: self.clearPlots() self.splitted = False to_plot = [] for param in self.param_keys: box = self.boxes[param] if box.isChecked(): to_plot.append(box.text()) self.updatePlots(to_plot) def updatePlots(self, param_names): for param_name in set(param_names + self.current_params): param = getattr(self.dataset, param_name) if param_name not in param_names: self.current_params.remove(param_name) if param.shape == (1, ): self.removeValue(param_name) elif len(param.shape) < 3: self.removePlot(param_name) elif param_name not in self.current_params: self.current_params.append(param_name) if param.shape == (1, ): self.addValue(param_name) elif len(param.shape) < 3: self.addPlot(param_name) def addPlot(self, plot): logging.info(f'adding param {plot}') self.subpl_ind[plot] = len(self.subpl_ind) self.qplot.add(getattr(self.dataset, plot), subplot=len(self.subpl_ind), color=self.color_list[0]) def removePlot(self, plot): logging.info(f'removing param {plot}') # Deleting graphics items plot_index = self.subpl_ind[plot] subplot = self.qplot.subplots[plot_index] subplot.clear() self.qplot.win.removeItem(subplot) subplot.deleteLater() try: hist = self.qplot.traces[plot_index]['plot_object']['hist'] self.qplot.win.removeItem(hist) hist.deleteLater() except: pass # Own bookkeeping self.subpl_ind.pop(plot) # Removing from qcodes qplot (does not have proper function for this) self.qplot.traces.pop(plot_index) self.qplot.subplots.remove(subplot) for (key, val) in self.subpl_ind.items(): if val > plot_index: self.subpl_ind[key] = val - 1 def addValue(self, plot): val = getattr(self.dataset, plot).ndarray[0] self.box_labels[plot].setText(str(val)) def removeValue(self, plot): self.box_labels[plot].setText('') def loadData(self, filename, tag): location = os.path.split(filename)[0] data = qcodes.data.data_set.load_data(location) return data def _create_meta_tree(self, meta_dict): metatree = QtWidgets.QTreeView() _metamodel = QtGui.QStandardItemModel() metatree.setModel(_metamodel) metatree.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) _metamodel.setHorizontalHeaderLabels(['metadata', 'value']) try: self.fill_item(_metamodel, meta_dict) return metatree except Exception as ex: print(ex) def fill_item(self, item, value): ''' recursive population of tree structure with a dict ''' def new_item(parent, text, val=None): child = QtGui.QStandardItem(text) self.fill_item(child, val) parent.appendRow(child) if value is None: return elif isinstance(value, dict): for key, val in sorted(value.items()): if type(val) in [ str, float, int ] or (type(val) is list and not any(isinstance(el, list) for el in val)): child = [ QtGui.QStandardItem(str(key)), QtGui.QStandardItem(str(val)) ] item.appendRow(child) else: new_item(item, str(key), val) else: new_item(item, str(value)) def parse_gates(self, gates_obj): gate_dict = dict() for (gate, val) in gates_obj['parameters'].items(): if gate != 'IDN': gate_dict[gate] = val['value'] return gate_dict def updateMetaTabs(self): ''' Update metadata tree ''' meta = self.dataset.metadata self.tabWidget.clear() try: gate_tree = self.parse_gates( meta['station']['instruments']['gates']) self.tabWidget.addTab(self._create_meta_tree(gate_tree), 'gates') except: pass self.tabWidget.addTab(self._create_meta_tree(meta), 'metadata') if 'pc0' in meta.keys(): self.pulse_plot = pg.PlotWidget() self.pulse_plot.addLegend() try: baseband_freqs = meta['LOs'] except: pass end_time = 0 for name, pdict in meta['pc0'].items(): if 'baseband' in name: end_time = max([end_time] + [pulse['stop'] for pulse in pdict.values()]) for (j, (name, pdict)) in enumerate(meta['pc0'].items()): legend_name = name.replace('_baseband', '').replace('_pulses', '') x_plot = list() y_plot = list() if 'baseband' in name: timepoints = set([ x[key] for x in meta['pc0'][name].values() for key in ['start', 'stop'] ]) timepoints.add(end_time) for tp in sorted(timepoints): point1 = 0 point2 = 0 for (seg_name, seg_dict) in meta['pc0'][name].items(): if seg_dict['start'] < tp and seg_dict[ 'stop'] > tp: # active segement point1 += tp / ( seg_dict['stop'] - seg_dict['start'] ) * (seg_dict['v_stop'] - seg_dict['v_stop']) point2 += tp / ( seg_dict['stop'] - seg_dict['start'] ) * (seg_dict['v_stop'] - seg_dict['v_stop']) elif seg_dict['start'] == tp: point2 += seg_dict['v_start'] elif seg_dict['stop'] == tp: point1 += seg_dict['v_stop'] x_plot += [tp, tp] y_plot += [point1, point2] elif 'pulses' in name: try: baseband = baseband_freqs[name.replace('_pulses', '')] except: logging.warning( 'No baseband frequency found, assuming 0') baseband = 0 x = list() y = list() for (seg_name, seg_dict) in meta['pc0'][name].items(): x_ar = np.arange(seg_dict['start'], seg_dict['stop']) xx_ar = x_ar - seg_dict['start'] f_rl = (seg_dict['frequency'] - baseband) / 1e9 y_ar = np.sin( 2 * np.pi * f_rl * xx_ar + seg_dict['start_phase']) * seg_dict['amplitude'] x = x + list(x_ar) + [seg_dict['stop']] y = y + list(y_ar) + [0] x_plot = x y_plot = y self.pulse_plot.setLabel('left', 'Voltage', 'mV') self.pulse_plot.setLabel('bottom', 'Time', 'ns') self.pulse_plot.plot(x_plot, y_plot, pen=self.color_list[j % len(self.color_list)], name=legend_name) self.tabWidget.addTab(self.pulse_plot, 'AWG Pulses') def pptCallback(self): if self.dataset is None: print('no data selected') return addPPT_dataset(self.dataset, customfig=self.qplot) def clipboardCallback(self): self.qplot.copyToClipboard() def getArrayStr(self, metadata): params = [] infotxt = '' try: if 'loop' in metadata.keys(): sv = metadata['loop']['sweep_values'] params.append( '%s [%.2f to %.2f %s]' % (sv['parameter']['label'], sv['values'][0]['first'], sv['values'][0]['last'], sv['parameter']['unit'])) for act in metadata['loop']['actions']: if 'sweep_values' in act.keys(): sv = act['sweep_values'] params.append( '%s [%.2f - %.2f %s]' % (sv['parameter']['label'], sv['values'][0]['first'], sv['values'][0]['last'], sv['parameter']['unit'])) infotxt = ' ,'.join(params) + ' | ' infotxt = infotxt + ', '.join([('%s' % (v['label'])) for ( k, v) in metadata['arrays'].items() if not v['is_setpoint']]) except BaseException: infotxt = 'info about plot' return infotxt def split_dataset(self): to_split = [] for bp in self.subpl_ind.keys(): plot_shape = getattr(self.dataset, bp).shape if len(plot_shape) == 2: to_split.append(bp) self.clearPlots() for (i, zname) in enumerate(to_split): tmp = getattr(self.dataset, zname) yname = tmp.set_arrays[0].array_id xname = tmp.set_arrays[1].array_id try: ii = np.where( np.isnan(self.dataset.arrays[zname][:, -1]) == True)[0][0] except: ii = len(self.dataset.arrays[yname][:]) even = list(range(0, ii, 2)) odd = list(range(1, ii, 2)) self.qplot.add(self.dataset.arrays[xname][0], self.dataset.arrays[yname][odd], self.dataset.arrays[zname][odd], ylabel=self.dataset.arrays[yname].label, xlabel=self.dataset.arrays[xname].label, zlabel=self.dataset.arrays[zname].label + '_odd', yunit=self.dataset.arrays[yname].unit, zunit=self.dataset.arrays[zname].unit, subplot=2 * i + 1) self.qplot.add(self.dataset.arrays[xname][0], self.dataset.arrays[yname][even], self.dataset.arrays[zname][even], ylabel=self.dataset.arrays[yname].label, xlabel=self.dataset.arrays[xname].label, zlabel=self.dataset.arrays[zname].label + '_even', yunit=self.dataset.arrays[yname].unit, zunit=self.dataset.arrays[zname].unit, subplot=2 * i + 2) self.splitted = True def findfiles(self, path, extension): filelist = list() for dirname, dirnames, filenames in os.walk(path): # print path to all filenames. for filename in filenames: fullfile = os.path.join(dirname, filename) if fullfile.split('.')[-1] == extension: filelist.append(fullfile) return filelist
class DataViewer(QtWidgets.QWidget): """ Simple data browser for Qcodes datasets """ def __init__(self, datadir=None, window_title='Data browser', default_parameter='amplitude', extensions=['dat', 'hdf5'], verbose=1): """ Simple viewer for Qcodes data Args: datadir (string or None): directory to scan for experiments default_parameter (string): name of default parameter to plot """ super(DataViewer, self).__init__() self.verbose = verbose self.default_parameter = default_parameter if datadir is None: datadir = qcodes.DataSet.default_io.base_location self.extensions = extensions # setup GUI self.dataset = None self.text = QtWidgets.QLabel() # logtree self.logtree = QtWidgets.QTreeView() self.logtree.setSelectionBehavior( QtWidgets.QAbstractItemView.SelectRows) self._treemodel = QtGui.QStandardItemModel() self.logtree.setModel(self._treemodel) # metatabs self.meta_tabs = QtWidgets.QTabWidget() self.meta_tabs.addTab(QtWidgets.QWidget(), 'metadata') self.__debug = dict() if isinstance(QtPlot, QWidget): self.qplot = QtPlot() # remote=False, interval=0) else: self.qplot = QtPlot(remote=False) # remote=False, interval=0) if isinstance(self.qplot, QWidget): self.plotwindow = self.qplot else: self.plotwindow = self.qplot.win topLayout = QtWidgets.QHBoxLayout() self.select_dir = QtWidgets.QPushButton() self.select_dir.setText('Select directory') self.reloadbutton = QtWidgets.QPushButton() self.reloadbutton.setText('Reload data') self.loadinfobutton = QtWidgets.QPushButton() self.loadinfobutton.setText('Preload info') self.outCombo = QtWidgets.QComboBox() topLayout.addWidget(self.text) topLayout.addWidget(self.select_dir) topLayout.addWidget(self.reloadbutton) topLayout.addWidget(self.loadinfobutton) treesLayout = QtWidgets.QHBoxLayout() treesLayout.addWidget(self.logtree) treesLayout.addWidget(self.meta_tabs) vertLayout = QtWidgets.QVBoxLayout() vertLayout.addItem(topLayout) vertLayout.addItem(treesLayout) vertLayout.addWidget(self.plotwindow) self.pptbutton = QtWidgets.QPushButton() self.pptbutton.setText('Send data to powerpoint') self.clipboardbutton = QtWidgets.QPushButton() self.clipboardbutton.setText('Copy image to clipboard') bLayout = QtWidgets.QHBoxLayout() bLayout.addWidget(self.outCombo) bLayout.addWidget(self.pptbutton) bLayout.addWidget(self.clipboardbutton) vertLayout.addItem(bLayout) self.setLayout(vertLayout) self.setWindowTitle(window_title) self.logtree.header().resizeSection(0, 280) # disable edit self.logtree.setEditTriggers( QtWidgets.QAbstractItemView.NoEditTriggers) self.setDatadir(datadir) self.logtree.doubleClicked.connect(self.logCallback) self.outCombo.currentIndexChanged.connect(self.comboCallback) self.select_dir.clicked.connect(self.selectDirectory) self.reloadbutton.clicked.connect(self.updateLogs) self.loadinfobutton.clicked.connect(self.loadInfo) self.pptbutton.clicked.connect(self.pptCallback) self.clipboardbutton.clicked.connect(self.clipboardCallback) if self.verbose >= 2: print('created gui...') # get logs from disk self.updateLogs() self.datatag = None self.logtree.setColumnHidden(2, True) self.logtree.setColumnHidden(3, True) self.show() def setDatadir(self, datadir): self.datadir = datadir self.io = qcodes.DiskIO(datadir) logging.info('DataViewer: data directory %s' % datadir) self.text.setText('Log files at %s' % self.datadir) def pptCallback(self): if self.dataset is None: print('no data selected') return qtt.utilities.tools.addPPT_dataset(self.dataset, customfig=self.qplot) def clipboardCallback(self): self.qplot.copyToClipboard() def getArrayStr(self, metadata): params = [] try: if 'loop' in metadata.keys(): sv = metadata['loop']['sweep_values'] params.append( '%s [%.2f to %.2f %s]' % (sv['parameter']['label'], sv['values'][0]['first'], sv['values'][0]['last'], sv['parameter']['unit'])) for act in metadata['loop']['actions']: if 'sweep_values' in act.keys(): sv = act['sweep_values'] params.append( '%s [%.2f - %.2f %s]' % (sv['parameter']['label'], sv['values'][0]['first'], sv['values'][0]['last'], sv['parameter']['unit'])) infotxt = ' ,'.join(params) infotxt = infotxt + ' | ' + ', '.join( [('%s' % (v['label'])) for (k, v) in metadata['arrays'].items() if not v['is_setpoint']]) elif 'scanjob' in metadata.keys(): sd = metadata['scanjob']['sweepdata'] params.append('%s [%.2f to %.2f]' % (sd['param'], sd['start'], sd['end'])) if 'stepdata' in metadata['scanjob']: sd = metadata['scanjob']['stepdata'] params.append('%s [%.2f to %.2f]' % (sd['param'], sd['start'], sd['end'])) infotxt = ' ,'.join(params) infotxt = infotxt + ' | ' + \ ', '.join(metadata['scanjob']['minstrument']) else: infotxt = 'info about plot' except BaseException: infotxt = 'info about plot' return infotxt def loadInfo(self): try: for row in range(self._treemodel.rowCount()): index = self._treemodel.index(row, 0) i = 0 while (index.child(i, 0).data() is not None): filename = index.child(i, 3).data() loc = '\\'.join(filename.split('\\')[:-1]) tempdata = qcodes.DataSet(loc) tempdata.read_metadata() infotxt = self.getArrayStr(tempdata.metadata) self._treemodel.setData(index.child(i, 1), infotxt) if 'comment' in tempdata.metadata.keys(): self._treemodel.setData(index.child(i, 4), tempdata.metadata['comment']) i = i + 1 except Exception as e: print(e) def selectDirectory(self): from qtpy.QtWidgets import QFileDialog d = QtWidgets.QFileDialog(caption='Select data directory') d.setFileMode(QFileDialog.Directory) if d.exec(): datadir = d.selectedFiles()[0] self.setDatadir(datadir) print('update logs') self.updateLogs() @staticmethod def find_datafiles(datadir, extensions=['dat', 'hdf5'], show_progress=True): """ Find all datasets in a directory with a given extension """ dd = [] for e in extensions: dd += qtt.pgeometry.findfilesR(datadir, '.*%s' % e, show_progress=show_progress) datafiles = sorted(dd) #datafiles = [os.path.join(datadir, d) for d in datafiles] return datafiles def updateLogs(self): ''' Update the list of measurements ''' model = self._treemodel self.datafiles = self.find_datafiles(self.datadir, self.extensions) dd = self.datafiles if self.verbose: print('DataViewer: found %d files' % (len(dd))) model.clear() model.setHorizontalHeaderLabels( ['Log', 'Arrays', 'location', 'filename', 'Comments']) logs = dict() for i, d in enumerate(dd): try: datetag, logtag = d.split(os.sep)[-3:-1] if datetag not in logs: logs[datetag] = dict() logs[datetag][logtag] = d except Exception: pass self.logs = logs if self.verbose >= 2: print('DataViewer: create gui elements') for i, datetag in enumerate(sorted(logs.keys())[::-1]): if self.verbose >= 2: print('DataViewer: datetag %s ' % datetag) parent1 = QtGui.QStandardItem(datetag) for j, logtag in enumerate(sorted(logs[datetag])): filename = logs[datetag][logtag] child1 = QtGui.QStandardItem(logtag) child2 = QtGui.QStandardItem('info about plot') if self.verbose >= 2: print('datetag %s, logtag %s' % (datetag, logtag)) child3 = QtGui.QStandardItem(os.path.join(datetag, logtag)) child4 = QtGui.QStandardItem(filename) parent1.appendRow([child1, child2, child3, child4]) model.appendRow(parent1) # span container columns # self.logtree.setFirstColumnSpanned( # i, self.logtree.rootIndex(), True) self.logtree.setColumnWidth(0, 240) self.logtree.setColumnHidden(2, True) self.logtree.setColumnHidden(3, True) if self.verbose >= 2: print('DataViewer: updateLogs done') def _create_meta_tree(self, meta_dict): metatree = QtWidgets.QTreeView() _metamodel = QtGui.QStandardItemModel() metatree.setModel(_metamodel) metatree.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) _metamodel.setHorizontalHeaderLabels(['metadata', 'value']) try: self.fill_item(_metamodel, meta_dict) return metatree except Exception as ex: print(ex) def updateMetaTabs(self): ''' Update metadata tree ''' meta = self.dataset.metadata self.meta_tabs.clear() if 'gates' in meta.keys(): self.meta_tabs.addTab(self._create_meta_tree(meta['gates']), 'gates') elif meta.get('station', dict()).get('instruments', dict()).get('gates', None) is not None: self.meta_tabs.addTab( self._create_meta_tree( meta['station']['instruments']['gates']), 'gates') if meta.get('station', dict()).get('instruments', None) is not None: if 'instruments' in meta['station'].keys(): self.meta_tabs.addTab( self._create_meta_tree(meta['station']['instruments']), 'instruments') self.meta_tabs.addTab(self._create_meta_tree(meta), 'metadata') def fill_item(self, item, value): ''' recursive population of tree structure with a dict ''' def new_item(parent, text, val=None): child = QtGui.QStandardItem(text) self.fill_item(child, val) parent.appendRow(child) if value is None: return elif isinstance(value, dict): for key, val in sorted(value.items()): if type(val) in [str, float, int]: child = [ QtGui.QStandardItem(str(key)), QtGui.QStandardItem(str(val)) ] item.appendRow(child) else: new_item(item, str(key), val) else: new_item(item, str(value)) def getPlotParameter(self): ''' Return parameter to be plotted ''' param_name = self.outCombo.currentText() if param_name is not '': return param_name parameters = self.dataset.arrays.keys() if self.default_parameter in parameters: return self.default_parameter return self.dataset.default_parameter_name() def selectedDatafile(self): """ Return currently selected data file """ return self.datatag def comboCallback(self, index): if not self._update_plot_: return param_name = self.getPlotParameter() if self.dataset is not None: self.updatePlot(param_name) def logCallback(self, index): """ Function called when. a log entry is selected """ logging.info('logCallback: index %s' % str(index)) self.__debug['last'] = index pp = index.parent() row = index.row() tag = pp.child(row, 2).data() filename = pp.child(row, 3).data() self.filename = filename self.datatag = tag if tag is None: return if self.verbose >= 2: print('DataViewer logCallback: tag %s, filename %s' % (tag, filename)) try: logging.debug('DataViewer: load tag %s' % tag) data = self.loadData(filename, tag) if not data: raise ValueError('File invalid (%s) ...' % filename) self.dataset = data self.updateMetaTabs() data_keys = data.arrays.keys() infotxt = self.getArrayStr(data.metadata) q = pp.child(row, 1).model() q.setData(pp.child(row, 1), infotxt) if 'comment' in data.metadata.keys(): q.setData(pp.child(row, 2), data.metadata['comment']) self.resetComboItems(data, data_keys) param_name = self.getPlotParameter() self.updatePlot(param_name) except Exception as e: print('logCallback! error: %s' % str(e)) logging.exception(e) return def resetComboItems(self, data, keys): old_key = self.outCombo.currentText() self._update_plot_ = False self.outCombo.clear() for key in keys: if not getattr(data, key).is_setpoint: self.outCombo.addItem(key) if old_key in keys: self.outCombo.setCurrentIndex(self.outCombo.findText(old_key)) self._update_plot_ = True return def loadData(self, filename, tag): location = os.path.split(filename)[0] data = qtt.data.load_dataset(location) return data def updatePlot(self, parameter): self.qplot.clear() if parameter is None: logging.info('could not find parameter for DataSet') return else: logging.info('using plotting parameter %s' % parameter) self.qplot.add(getattr(self.dataset, parameter))