Esempio n. 1
0
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))
Esempio n. 2
0
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
Esempio n. 3
0
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))