Exemple #1
0
class AddTracUI(QWidget):

    def __init__(self, parent):
        super(AddTracUI, self).__init__()
        self.title = 'Create TracControl Element'
        self.setLayout(QVBoxLayout(self))
        self.parent = parent
        self.trac = Trac(name='', outputPin=0, enabled=False, icon=Config.icon('tracControl', 'rearDiff'), momentary=False)
        self._nameControl = LineEdit('Name', self)
        self._nameControl.kb.connect(self.showOSK)
        self._outputPinControlLabel = QLabel('Output Pin', self)
        self._outputPinControl = QComboBox(self)
        for _pin in self.parent.availablePins():
            self._outputPinControl.addItem(str(_pin))
        self._outputPinControl.setCurrentIndex(self._outputPinControl.findText(str(self.trac.outputPin)))
        self._enabledControl = QCheckBox('Enabled', self)
        self._iconControlLabel = QLabel('Icon', self)
        self._iconControl = QComboBox(self)
        for _key in Config.icons['tracControl'].keys():
            icon = Config.icon('tracControl', _key)
            self._iconControl.addItem(icon['name'], _key)
            self._iconControl.setItemIcon(self._iconControl.count() - 1, QIcon(icon['path']))
        self._addTracBtn = QPushButton('Add TracControl Element', self)
        self._addTracBtn.clicked.connect(self.__createTracBtnAction)
        self._cancelBtn = QPushButton('Cancel', self)
        self._cancelBtn.clicked.connect(self.__cancel)
        _layout = [
            ['_nameControl'],
            ['_outputPinControlLabel', '_outputPinControl'],
            ['_enabledControl'],
            ['_iconControlLabel', '_iconControl'],
            ['_addTracBtn', '_cancelBtn']
        ]
        for _list in _layout:
            _panel = QWidget(self)
            _panel.setLayout(QHBoxLayout(_panel))
            _panel.layout().setAlignment(Qt.AlignCenter)
            for _control in _list:
                _panel.layout().addWidget(eval('self.%s' % _control))
            self.layout().addWidget(_panel)

    def __createTracBtnAction(self):
        self.trac.name = self._nameControl.text()
        self.trac.outputPin = int(self._outputPinControl.currentText())
        self.trac.enabled = self._enabledControl.isChecked()
        self.trac.icon = self._iconControl.currentData()
        self.parent.tracs.addTrac(self.trac)
        self.parent.tracs.save()
        self.parent.loadUI('config_trac')
        self.parent.enableConfigButtons()

    def __cancel(self):
        self.parent.loadUI('config_trac')
        self.parent.enableConfigButtons()

    def showOSK(self):
        self.window().dock.show()
        self.window().osk.rWidget = self._nameControl
Exemple #2
0
class OWLoadData(widget.OWWidget):
    name = "Load Data"
    icon = "icons/upload.svg"
    priority = 10

    class Outputs:
        data = widget.Output("Data", Orange.data.Table)

    class Information(widget.OWWidget.Information):
        modified = widget.Msg(
            "Uncommited changes\nPress 'Load data' to submit changes")

    class Warning(widget.OWWidget.Warning):
        sampling_in_effect = widget.Msg("Sampling is in effect.")

    class Error(widget.OWWidget.Error):
        row_annotation_mismatch = widget.Msg("Row annotation length mismatch\n"
                                             "Expected {} rows got {}")
        col_annotation_mismatch = widget.Msg(
            "Column annotation length mismatch\n"
            "Expected {} rows got {}")

    _recent = settings.Setting([])  # type: List[str]
    _recent_row_annotations = settings.Setting([])  # type: List[str]
    _recent_col_annotations = settings.Setting([])  # type: List[str]
    _cells_in_rows = settings.Setting(False)
    _col_annotations_enabled = settings.Setting(False)
    _row_annotations_enabled = settings.Setting(False)

    _last_path = settings.Setting("")  # type: str
    _header_rows_count = settings.Setting(1)  # type: int
    _header_cols_count = settings.Setting(1)  # type: int
    _sample_rows_enabled = settings.Setting(False)  # type: bool
    _sample_cols_enabled = settings.Setting(False)  # type: bool
    _sample_cols_p = settings.Setting(10.0)  # type: bool
    _sample_rows_p = settings.Setting(10.0)  # type: bool

    settingsHandler = RunaroundSettingsHandler()

    want_main_area = False
    resizing_enabled = False

    def __init__(self):
        super().__init__()
        self._current_path = ""
        icon_open_dir = self.style().standardIcon(QStyle.SP_DirOpenIcon)

        # Top grid with file selection combo box
        grid = QGridLayout()
        lb = QLabel("File:")
        lb.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.recent_combo = cb = QComboBox(
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon,
            minimumContentsLength=20,
            toolTip="Select a recent file")
        self.recent_model = cb.model()  # type: QStandardItemModel
        self.recent_combo.activated[int].connect(self._select_recent)

        browse = QPushButton("...",
                             autoDefault=False,
                             icon=icon_open_dir,
                             clicked=self.browse)

        # reload = QPushButton("Reload", autoDefault=False, icon=icon_reload)

        grid.addWidget(lb, 0, 0, Qt.AlignVCenter)
        grid.addWidget(cb, 0, 1)
        grid.addWidget(browse, 0, 2)
        # grid.addWidget(reload, 0, 3)

        self.summary_label = label = QLabel("", self)
        label.ensurePolished()
        f = label.font()
        if f.pointSizeF() != -1:
            f.setPointSizeF(f.pointSizeF() * 5 / 6)
        else:
            f.setPixelSize(f.pixelSize() * 5 / 6)
        label.setFont(f)
        grid.addWidget(label, 1, 1, 1, 3)

        self.controlArea.layout().addLayout(grid)

        box = gui.widgetBox(self.controlArea,
                            "Headers and Row Labels",
                            spacing=-1)
        hl = QHBoxLayout()
        hl.setContentsMargins(0, 0, 0, 0)
        self.header_rows_spin = spin = QSpinBox(box,
                                                minimum=0,
                                                maximum=3,
                                                value=self._header_rows_count,
                                                keyboardTracking=False)
        spin.valueChanged.connect(self.set_header_rows_count)
        hl.addWidget(QLabel("Data starts with", box))
        hl.addWidget(self.header_rows_spin)
        hl.addWidget(QLabel("header row(s)", box))
        hl.addStretch(10)
        box.layout().addLayout(hl)

        hl = QHBoxLayout()
        hl.setContentsMargins(0, 0, 0, 0)
        self.header_cols_spin = spin = QSpinBox(box,
                                                minimum=0,
                                                maximum=3,
                                                value=self._header_cols_count,
                                                keyboardTracking=False)
        spin.valueChanged.connect(self.set_header_cols_count)

        hl.addWidget(QLabel("First", box))
        hl.addWidget(self.header_cols_spin)
        hl.addWidget(QLabel("column(s) are row labels", box))
        hl.addStretch(10)
        box.layout().addLayout(hl)

        self.data_struct_box = box = gui.widgetBox(self.controlArea,
                                                   "Input Data Structure")
        gui.radioButtons(box,
                         self,
                         "_cells_in_rows", [
                             "Genes in rows, samples in columns",
                             "Samples in rows, genes in columns"
                         ],
                         callback=self._invalidate)

        box = gui.widgetBox(self.controlArea, "Sample Data", spacing=-1)

        grid = QGridLayout()
        grid.setContentsMargins(0, 0, 0, 0)
        box.layout().addLayout(grid)

        self.sample_rows_cb = cb = QCheckBox(checked=self._sample_rows_enabled)

        spin = QSpinBox(minimum=0,
                        maximum=100,
                        value=self._sample_rows_p,
                        enabled=self._sample_rows_enabled)
        spin.valueChanged.connect(self.set_sample_rows_p)
        suffix = QLabel("% of Samples", enabled=self._sample_rows_enabled)
        cb.toggled.connect(self.set_sample_rows_enabled)
        cb.toggled.connect(spin.setEnabled)
        cb.toggled.connect(suffix.setEnabled)

        grid.addWidget(cb, 0, 0)
        grid.addWidget(spin, 0, 1)
        grid.addWidget(suffix, 0, 2)

        self.sample_cols_cb = cb = QCheckBox(checked=self._sample_cols_enabled)
        spin = QSpinBox(minimum=0,
                        maximum=100,
                        value=self._sample_cols_p,
                        enabled=self._sample_cols_enabled)
        spin.valueChanged.connect(self.set_sample_cols_p)
        suffix = QLabel("% of genes", enabled=self._sample_cols_enabled)
        cb.toggled.connect(self.set_sample_cols_enabled)
        cb.toggled.connect(spin.setEnabled)
        cb.toggled.connect(suffix.setEnabled)

        grid.addWidget(cb, 1, 0)
        grid.addWidget(spin, 1, 1)
        grid.addWidget(suffix, 1, 2)
        grid.setColumnStretch(3, 10)

        self.annotation_files_box = box = gui.widgetBox(
            self.controlArea, "Cell && Gene Annotation Files")
        form = QFormLayout(
            formAlignment=Qt.AlignLeft,
            rowWrapPolicy=QFormLayout.WrapAllRows,
        )
        box.layout().addLayout(form)

        self.row_annotations_cb = cb = QCheckBox(
            "Cell annotations", checked=self._row_annotations_enabled)
        self._row_annotations_w = w = QWidget(
            enabled=self._row_annotations_enabled)
        cb.toggled.connect(self.set_row_annotations_enabled)
        cb.toggled.connect(w.setEnabled)
        hl = QHBoxLayout()
        hl.setContentsMargins(0, 0, 0, 0)
        w.setLayout(hl)
        self.row_annotations_combo = QComboBox(
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon,
            minimumContentsLength=18)
        self.row_annotations_combo.activated.connect(self._invalidate)
        hl.addWidget(self.row_annotations_combo)
        hl.addWidget(
            QPushButton("...",
                        box,
                        autoDefault=False,
                        icon=icon_open_dir,
                        clicked=self.browse_row_annotations))
        # hl.addWidget(QPushButton("Reload", box, autoDefault=False,
        #                          icon=icon_reload))
        form.addRow(cb, w)

        self.col_annotations_cb = cb = QCheckBox(
            "Gene annotations", checked=self._col_annotations_enabled)
        self._col_annotations_w = w = QWidget(
            enabled=self._col_annotations_enabled)
        cb.toggled.connect(self.set_col_annotations_enabled)
        cb.toggled.connect(w.setEnabled)
        hl = QHBoxLayout()
        hl.setContentsMargins(0, 0, 0, 0)
        w.setLayout(hl)
        self.col_annotations_combo = QComboBox(
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon,
            minimumContentsLength=18)
        self.col_annotations_combo.activated.connect(self._invalidate)
        hl.addWidget(self.col_annotations_combo)
        hl.addWidget(
            QPushButton("...",
                        box,
                        autoDefault=False,
                        icon=icon_open_dir,
                        clicked=self.browse_col_annotations))
        # hl.addWidget(QPushButton("Reload", box, autoDefault=False,
        #                          icon=icon_reload))
        form.addRow(cb, w)

        self.controlArea.layout().addStretch(10)
        self.load_data_button = button = VariableTextPushButton(
            "Load data",
            autoDefault=True,
            textChoiceList=["Load data", "Reload"])
        self.load_data_button.setAutoDefault(True)
        button.clicked.connect(self.commit, Qt.QueuedConnection)
        self.controlArea.layout().addWidget(button, alignment=Qt.AlignRight)

        init_recent_paths_model(
            self.recent_model,
            [RecentPath.create(p, []) for p in self._recent],
        )
        init_recent_paths_model(
            self.row_annotations_combo.model(),
            [RecentPath.create(p, []) for p in self._recent_row_annotations])
        init_recent_paths_model(
            self.col_annotations_combo.model(),
            [RecentPath.create(p, []) for p in self._recent_col_annotations])
        self._update_summary()
        self._update_warning()

        if self._last_path != "" and os.path.exists(self._last_path):
            self.set_current_path(self._last_path)
        else:
            self.recent_combo.setCurrentIndex(-1)

    def _update_warning(self):
        if (self._sample_rows_enabled and self._sample_rows_p < 100) or \
                (self._sample_cols_enabled and self._sample_cols_p < 100):
            self.Warning.sampling_in_effect()
        else:
            self.Warning.sampling_in_effect.clear()

    def set_sample_rows_enabled(self, enabled):
        if self._sample_rows_enabled != enabled:
            self._sample_rows_enabled = enabled
            self._update_warning()
            self._invalidate()

    def set_sample_cols_enabled(self, enabled):
        if self._sample_cols_enabled != enabled:
            self._sample_cols_enabled = enabled
            self._update_warning()
            self._invalidate()

    def set_sample_rows_p(self, p):
        if self._sample_rows_p != p:
            self._sample_rows_p = p
            self._update_warning()
            self._invalidate()

    def set_sample_cols_p(self, p):
        if self._sample_cols_p != p:
            self._sample_cols_p = p
            self._update_warning()
            self._invalidate()

    def set_header_rows_count(self, n):
        if self._header_rows_count != n:
            self._header_rows_count = n
            self.header_rows_spin.setValue(n)
            self._invalidate()

    def set_header_cols_count(self, n):
        if self._header_cols_count != n:
            self._header_cols_count = n
            self.header_cols_spin.setValue(n)
            self._invalidate()

    def set_row_annotations_enabled(self, enabled):
        if self._row_annotations_enabled != enabled:
            self._row_annotations_enabled = enabled
            self.row_annotations_cb.setChecked(enabled)
            self._invalidate()

    def set_col_annotations_enabled(self, enabled):
        if self._col_annotations_enabled != enabled:
            self._col_annotations_enabled = enabled
            self.col_annotations_cb.setChecked(enabled)
            self._invalidate()

    def set_current_path(self, path):
        if samepath(self._current_path, path):
            return

        model = self.recent_model
        index = -1
        pathitem = None
        for i in range(model.rowCount()):
            item = model.item(i)
            data = item.data(Qt.UserRole) if item is not None else None
            if isinstance(data, RecentPath) and samepath(path, data.abspath):
                index, pathitem = i, data
                break

        rpaths = []

        if pathitem is None:
            assert index == -1
            pathitem = RecentPath.create(path, rpaths)

        if index != -1:
            item = model.takeRow(index)
        else:
            item = RecentPath_asqstandarditem(pathitem)

        opts = infer_options(path)

        if path.endswith(".count"):
            self.set_header_rows_count(1)
            self.set_header_cols_count(1)
            fixed_format = False
        elif path.endswith(".mtx"):
            self.set_header_rows_count(0)
            self.set_header_cols_count(0)
            fixed_format = False
            self._cells_in_rows = False
        else:
            fixed_format = True

        if opts.transposed is not None:
            self._cells_in_rows = not opts.transposed

        self.data_struct_box.setEnabled(fixed_format)
        self.header_rows_spin.setEnabled(fixed_format)
        self.header_cols_spin.setEnabled(fixed_format)

        model.insertRow(0, item)
        self._current_path = path
        self.recent_combo.setCurrentIndex(0)
        self._update_summary()

        if opts.row_annotation_file is not None:
            index = insert_recent_path(
                self.row_annotations_combo.model(),
                RecentPath.create(opts.row_annotation_file, []))
            self.row_annotations_combo.setCurrentIndex(index)
            self.set_row_annotations_enabled(True)
        else:
            self.row_annotations_combo.setCurrentIndex(-1)

        if opts.column_annotation_file is not None:
            index = insert_recent_path(
                self.col_annotations_combo.model(),
                RecentPath.create(opts.column_annotation_file, []))
            self.col_annotations_combo.setCurrentIndex(index)
            self.set_col_annotations_enabled(True)
        else:
            self.col_annotations_combo.setCurrentIndex(-1)

        if path.endswith(".mtx") \
                and opts.row_annotation_file is not None \
                and opts.column_annotation_file is not None \
                and os.path.basename(opts.row_annotation_file) == "barcodes.tsv" \
                and os.path.basename(opts.column_annotation_file) == "genes.tsv":
            # 10x gene-barcode matrix
            # TODO: The genes/barcodes files should be unconditionally loaded
            # alongside the mtx. The row/col annotations might be used to
            # specify additional sources. For the time being they are put in
            # the corresponding comboboxes and made uneditable.
            self.annotation_files_box.setEnabled(False)
        else:
            self.annotation_files_box.setEnabled(True)

        self._invalidate()

    def _update_summary(self):
        path = self._current_path

        size = None
        ncols = None
        nrows = None

        try:
            st = os.stat(path)
        except OSError:
            pass
        else:
            size = st.st_size

        if os.path.splitext(path)[1] == ".mtx":
            try:
                with open(path, "rb") as f:
                    nrows, ncols = scipy.io.mminfo(f)[:2]
            except OSError:
                pass
            except ValueError:
                pass
        else:
            try:
                with open(path, "rt", encoding="latin-1") as f:
                    sep = separator_from_filename(path)
                    ncols = len(next(csv.reader(f, delimiter=sep)))
                    nrows = sum(1 for _ in f)
            except OSError:
                pass
            except StopIteration:
                pass

        text = []
        if size is not None:
            text += [sizeformat(size)]
        if nrows is not None:
            text += ["{:n} rows".format(nrows)]
        if nrows is not None:
            text += ["{:n} columns".format(ncols)]

        self.summary_label.setText(", ".join(text))

    def current_path(self):
        return self._current_path

    def _select_recent(self, index):
        # type: (int) -> None
        # select a file from the recent list (entered via combo box `activate`)
        assert 0 <= index < self.recent_model.rowCount()
        item = self.recent_model.item(index)
        pathitem = item.data(Qt.UserRole)
        assert isinstance(pathitem, RecentPath)
        self.set_current_path(pathitem.abspath)

    @Slot()
    def browse(self):
        dlg = QFileDialog(self)
        dlg.setAcceptMode(QFileDialog.AcceptOpen)
        dlg.setFileMode(QFileDialog.ExistingFile)

        filters = Formats
        dlg.setNameFilters(filters)
        if filters:
            dlg.selectNameFilter(filters[0])
        if dlg.exec_() == QFileDialog.Accepted:
            f = dlg.selectedNameFilter()
            filename = dlg.selectedFiles()[0]
            self.set_current_path(filename)

    @Slot()
    def browse_row_annotations(self):
        dlg = QFileDialog(self,
                          acceptMode=QFileDialog.AcceptOpen,
                          fileMode=QFileDialog.ExistingFile)
        filters = AnnotationFormats
        dlg.setNameFilters(filters)

        if filters:
            dlg.selectNameFilter(filters[0])
        if dlg.exec_() == QFileDialog.Accepted:
            f = dlg.selectedNameFilter()
            filename = dlg.selectedFiles()[0]
            m = self.row_annotations_combo.model()  # type: QStandardItemModel
            pathitem = RecentPath.create(filename, [])
            index = insert_recent_path(m, pathitem)
            self.row_annotations_combo.setCurrentIndex(index)
            self._invalidate()

    @Slot()
    def browse_col_annotations(self):
        dlg = QFileDialog(self,
                          acceptMode=QFileDialog.AcceptOpen,
                          fileMode=QFileDialog.ExistingFile)
        filters = AnnotationFormats
        dlg.setNameFilters(filters)

        if filters:
            dlg.selectNameFilter(filters[0])
        if dlg.exec_() == QFileDialog.Accepted:
            f = dlg.selectedNameFilter()
            filename = dlg.selectedFiles()[0]
            m = self.col_annotations_combo.model()  # type: QStandardItemModel
            pathitem = RecentPath.create(filename, [])
            index = insert_recent_path(m, pathitem)
            self.col_annotations_combo.setCurrentIndex(index)
            self._invalidate()

    def _invalidate(self):
        self.set_modified(True)

    def set_modified(self, modified):
        if modified:
            text = "Load data"
        else:
            text = "Reload"

        self.load_data_button.setText(text)
        self.load_data_button.setAutoDefault(modified)
        # Setting autoDefault once also sets default which persists even after
        # settings autoDefault back to False??
        self.load_data_button.setDefault(modified)
        self.Information.modified(shown=modified)

    def commit(self):
        path = self._current_path
        if not path:
            return

        transpose = not self._cells_in_rows
        row_annot = self.row_annotations_combo.currentData(Qt.UserRole)
        col_annot = self.col_annotations_combo.currentData(Qt.UserRole)

        if self._row_annotations_enabled and \
                isinstance(row_annot, RecentPath) and \
                os.path.exists(row_annot.abspath):
            row_annot = row_annot.abspath  # type: str
        else:
            row_annot = None

        if self._col_annotations_enabled and \
                isinstance(col_annot, RecentPath) and \
                os.path.exists(col_annot.abspath):
            col_annot = col_annot.abspath  # type: str
        else:
            col_annot = None

        meta_parts = []  # type: List[pd.DataFrame]
        attrs = []  # type: List[ContinuousVariable]
        metas = []  # type: List[StringVariable]

        rstate = np.random.RandomState(0x667)

        skip_row = skip_col = None
        if self._sample_cols_enabled:
            p = self._sample_cols_p
            if p < 100:

                def skip_col(i, p=p):
                    return i > 3 and rstate.uniform(0, 100) > p

        if self._sample_rows_enabled:
            p = self._sample_rows_p
            if p < 100:

                def skip_row(i, p=p):
                    return i > 3 and rstate.uniform(0, 100) > p

        header_rows = self._header_rows_count
        header_rows_indices = []
        if header_rows == 0:
            header_rows = None
        elif header_rows == 1:
            header_rows = 0
            header_rows_indices = [0]
        else:
            header_rows = list(range(header_rows))
            header_rows_indices = header_rows

        header_cols = self._header_cols_count
        header_cols_indices = []
        if header_cols == 0:
            header_cols = None
        elif header_cols == 1:
            header_cols = 0
            header_cols_indices = [0]
        else:
            header_cols = list(range(header_cols))
            header_cols_indices = header_cols

        if transpose:
            _skip_row, _skip_col = skip_col, skip_row
        else:
            _skip_col, _skip_row = skip_col, skip_row

        _userows = _usecols = None
        userows_mask = usecols_mask = None

        if _skip_col is not None:
            ncols = pd.read_csv(path,
                                sep=separator_from_filename(path),
                                index_col=None,
                                nrows=1).shape[1]
            usecols_mask = np.array([
                not _skip_col(i) or i in header_cols_indices
                for i in range(ncols)
            ],
                                    dtype=bool)
            _usecols = np.flatnonzero(usecols_mask)

        if _skip_row is not None:
            userows_mask = []  # record the used rows

            def _skip_row(i, test=_skip_row):
                r = test(i)
                userows_mask.append(r)
                return r

        meta_df_index = None
        row_annot_header = 0
        row_annot_columns = None
        col_annot_header = 0
        col_annot_columns = None

        if os.path.splitext(path)[1] == ".mtx":
            # 10x cellranger output
            X = scipy.io.mmread(path)
            assert isinstance(X, scipy.sparse.coo_matrix)
            if transpose:
                X = X.T
            if _skip_row is not None:
                userows_mask = np.array(
                    [not _skip_row(i) for i in range(X.shape[0])])
                X = X.tocsr()[np.flatnonzero(userows_mask)]
            if _skip_col is not None:
                usecols_mask = np.array(
                    [not _skip_col(i) for i in range(X.shape[1])])
                X = X.tocsc()[:, np.flatnonzero(usecols_mask)]
            X = X.todense(order="F")
            if userows_mask is not None:
                meta_df = pd.DataFrame({}, index=np.flatnonzero(userows_mask))
            else:
                meta_df = pd.DataFrame({}, index=pd.RangeIndex(X.shape[0]))

            meta_df_index = meta_df.index

            row_annot_header = None
            row_annot_columns = ["Barcodes"]
            col_annot_header = None
            col_annot_columns = ["Id", "Gene"]
            leading_cols = leading_rows = 0
        else:
            df = pd.read_csv(path,
                             sep=separator_from_filename(path),
                             index_col=header_cols,
                             header=header_rows,
                             skiprows=_skip_row,
                             usecols=_usecols)

            if _skip_row is not None:
                userows_mask = np.array(userows_mask, dtype=bool)

            if transpose:
                df = df.transpose()
                userows_mask, usecols_mask = usecols_mask, userows_mask
                leading_rows = len(header_cols_indices)
                leading_cols = len(header_rows_indices)
            else:
                leading_rows = len(header_rows_indices)
                leading_cols = len(header_cols_indices)

            X = df.values
            attrs = [ContinuousVariable.make(str(g)) for g in df.columns]

            meta_df = df.iloc[:, :0]  # Take the index # type: pd.DataFrame
            meta_df_index = df.index
            meta_parts = (meta_df, )

        self.Error.row_annotation_mismatch.clear()
        self.Error.col_annotation_mismatch.clear()

        if row_annot is not None:
            row_annot_df = pd.read_csv(row_annot,
                                       sep=separator_from_filename(row_annot),
                                       header=row_annot_header,
                                       names=row_annot_columns,
                                       index_col=None)
            if userows_mask is not None:
                # NOTE: we account for column header/ row index
                expected = len(userows_mask) - leading_rows
            else:
                expected = X.shape[0]
            if len(row_annot_df) != expected:
                self.Error.row_annotation_mismatch(expected, len(row_annot_df))
                row_annot_df = None

            if row_annot_df is not None and userows_mask is not None:
                # use the same sample indices
                indices = np.flatnonzero(userows_mask[leading_rows:])
                row_annot_df = row_annot_df.iloc[indices]
                # if path.endswith(".count") and row_annot.endswith('.meta'):
                #     assert np.all(row_annot_df.iloc[:, 0] == df.index)

            if row_annot_df is not None and meta_df_index is not None:
                # Try to match the leading columns with the meta_df_index.
                # If found then drop the columns (or index if the level does
                # not have a name but the annotation col does)
                drop_cols = []
                drop_index_level = []
                for i in range(meta_df_index.nlevels):
                    meta_df_level = meta_df_index.get_level_values(i)
                    if np.all(row_annot_df.iloc[:, i] == meta_df_level):
                        if meta_df_level.name is None:
                            drop_index_level.append(i)
                        elif meta_df_level.name == row_annot_df.columns[
                                i].name:
                            drop_cols.append(i)

                if drop_cols:
                    row_annot_df = row_annot_df.drop(columns=drop_cols)

                if drop_index_level:
                    for i in reversed(drop_index_level):
                        if isinstance(meta_df.index, pd.MultiIndex):
                            meta_df_index = meta_df_index.droplevel(i)
                        else:
                            assert i == 0
                            meta_df_index = pd.RangeIndex(meta_df_index.size)
                    meta_df = pd.DataFrame({}, index=meta_df_index)

            if row_annot_df is not None:
                meta_parts = (meta_df, row_annot_df)

        if col_annot is not None:
            col_annot_df = pd.read_csv(col_annot,
                                       sep=separator_from_filename(col_annot),
                                       header=col_annot_header,
                                       names=col_annot_columns,
                                       index_col=None)
            if usecols_mask is not None:
                expected = len(usecols_mask) - leading_cols
            else:
                expected = X.shape[1]
            if len(col_annot_df) != expected:
                self.Error.col_annotation_mismatch(expected, len(col_annot_df))
                col_annot_df = None
            if col_annot_df is not None and usecols_mask is not None:
                indices = np.flatnonzero(usecols_mask[leading_cols:])
                col_annot_df = col_annot_df.iloc[indices]

            if col_annot_df is not None:
                assert len(col_annot_df) == X.shape[1]
                if not attrs and X.shape[1]:  # No column names yet
                    attrs = [
                        ContinuousVariable.make(str(v))
                        for v in col_annot_df.iloc[:, 0]
                    ]
                names = [str(c) for c in col_annot_df.columns]
                for var, values in zip(attrs, col_annot_df.values):
                    var.attributes.update(
                        {n: v
                         for n, v in zip(names, values)})

        if meta_parts:
            meta_parts = [
                df_.reset_index() if not df_.index.is_integer() else df_
                for df_ in meta_parts
            ]
            metas = [
                StringVariable.make(name)
                for name in chain(*(_.columns for _ in meta_parts))
            ]
            M = np.hstack(tuple(df_.values for df_ in meta_parts))
        else:
            metas = None
            M = None

        if not attrs and X.shape[1]:
            attrs = Orange.data.Domain.from_numpy(X).attributes

        domain = Orange.data.Domain(attrs, metas=metas)
        d = Orange.data.Table.from_numpy(domain, X, None, M)
        self.Outputs.data.send(d)

        self.set_modified(False)

    def onDeleteWidget(self):
        super().onDeleteWidget()

    def _saveState(self):
        maxitems = 15

        def dataiter(model, role=Qt.UserRole):
            return (model.data(model.index(i, 0), role)
                    for i in range(model.rowCount()))

        def recent_paths(model):
            return [
                el.abspath for el in dataiter(model)
                if isinstance(el, RecentPath)
            ][:maxitems]

        self._recent = recent_paths(self.recent_model)
        self._recent_row_annotations = recent_paths(
            self.row_annotations_combo.model())
        self._recent_col_annotations = recent_paths(
            self.col_annotations_combo.model())
        self._last_path = self._current_path

    def saveSettings(self):
        self._saveState()
        super().saveSettings()
Exemple #3
0
class EditTracUI(QWidget):
    def __init__(self, trac, parent):
        super(EditTracUI, self).__init__()
        self.title = 'Edit TracControl Element'
        self.setLayout(QVBoxLayout(self))
        self.layout().setAlignment(Qt.AlignCenter)
        self.parent = parent
        self.trac = trac

        # Init controls
        self._nameControl = LineEdit('Name', self)
        self._nameControl.setText(self.trac.name)
        self._nameControl.kb.connect(self.showOSK)
        self._outputPinControlLabel = QLabel('Output Pin', self)
        self._outputPinControl = QComboBox(self)
        for _pins in self.parent.availablePins():
            self._outputPinControl.addItem(str(_pins))
        for _i in range(self._outputPinControl.count()):
            if self._outputPinControl.itemText(_i) == str(self.trac.outputPin):
                self._outputPinControl.setCurrentIndex(_i)
                break
        self._enabledControl = QCheckBox('Enabled', self)
        self._enabledControl.setChecked(self.trac.enabled)
        self._iconControlLabel = QLabel('Icon Path', self)
        self._iconControl = QComboBox(self)
        for _key in Config.icons['tracControl'].keys():
            icon = Config.icon('tracControl', _key)
            self._iconControl.addItem(icon['name'], _key)
            self._iconControl.setItemIcon(self._iconControl.count() - 1,
                                          QIcon(icon['path']))
        # Set combobox selection to icon variable
        for iconIdx in range(self._iconControl.count()):
            if self.trac.icon is not None and self._iconControl.itemData(
                    iconIdx) == self.trac.icon:
                self._iconControl.setCurrentIndex(iconIdx)
                break
        self._saveBtn = QPushButton('Save', self)
        self._saveBtn.clicked.connect(self.__saveBtnAction)
        self._cancelBtn = QPushButton('Cancel', self)
        self._cancelBtn.clicked.connect(self.__cancel)
        _layout = [['_nameControl'],
                   ['_outputPinControlLabel', '_outputPinControl'],
                   ['_enabledControl'], ['_iconControlLabel', '_iconControl'],
                   ['_saveBtn', '_cancelBtn']]
        for _list in _layout:
            _panel = QWidget(self)
            _panel.setLayout(QHBoxLayout(_panel))
            _panel.layout().setAlignment(Qt.AlignCenter)
            for _ctrl in _list:
                _panel.layout().addWidget(eval('self.%s' % _ctrl))
            self.layout().addWidget(_panel)

    def __saveBtnAction(self):
        self.trac.name = self._nameControl.text()
        self.trac.outputPin = int(self._outputPinControl.currentText())
        self.trac.enabled = self._enabledControl.isChecked()
        self.trac.icon = self._iconControl.currentData()
        self.parent.tracs.save()
        self.parent.loadUI('config_trac')

    def __cancel(self):
        self.parent.loadUI('config_trac')

    def showOSK(self):
        self.window().dock.show()
        self.window().osk.rWidget = self._nameControl
Exemple #4
0
class OWLoadData(widget.OWWidget):
    name = ""
    icon = "icons/LoadData.svg"
    priority = 110

    class Outputs:
        data = widget.Output("Data", Table)

    class Information(widget.OWWidget.Information):
        modified = widget.Msg(
            "Uncommited changes\nPress 'Load data' to submit changes")

    class Warning(widget.OWWidget.Warning):
        sampling_in_effect = widget.Msg("Sampling is in effect.")

    class Error(widget.OWWidget.Error):
        row_annotation_mismatch = widget.Msg("Row annotation length mismatch\n"
                                             "Expected {} rows got {}")
        col_annotation_mismatch = widget.Msg(
            "Column annotation length mismatch\n"
            "Expected {} rows got {}")
        inadequate_headers = widget.Msg("Headers and Row Labels error")
        reading_error = widget.Msg("Cannot read data using given parameters.")

    _recent = settings.Setting([])  # type: List[RecentPath]
    _recent_row_annotations = settings.Setting([])  # type: List[RecentPath]
    _recent_col_annotations = settings.Setting([])  # type: List[RecentPath]
    _cells_in_rows = settings.Setting(False)
    _col_annotations_enabled = settings.Setting(False)
    _row_annotations_enabled = settings.Setting(False)

    _last_path = settings.Setting("")  # type: str
    _header_rows_count = settings.Setting(1)  # type: int
    _header_cols_count = settings.Setting(1)  # type: int
    _sample_rows_enabled = settings.Setting(False)  # type: bool
    _sample_cols_enabled = settings.Setting(False)  # type: bool
    _sample_cols_p = settings.Setting(10.0)  # type: bool
    _sample_rows_p = settings.Setting(10.0)  # type: bool

    settingsHandler = RunaroundSettingsHandler()

    want_main_area = False
    resizing_enabled = False

    cells_in_rows_changed = Signal()

    def __init__(self):
        super().__init__()
        self._current_path = ""
        self._data_loader = Loader()
        icon_open_dir = self.style().standardIcon(QStyle.SP_DirOpenIcon)

        # Top grid with file selection combo box
        self.file_layout = grid = QGridLayout()
        lb = QLabel("File:")
        lb.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.recent_combo = cb = QComboBox(
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon,
            minimumContentsLength=20,
            toolTip="Select a recent file")
        self.recent_model = cb.model()  # type: QStandardItemModel
        self.recent_combo.activated[int].connect(self._select_recent)

        browse = QPushButton("...",
                             autoDefault=False,
                             icon=icon_open_dir,
                             clicked=self.browse)

        # reload = QPushButton("Reload", autoDefault=False, icon=icon_reload)

        grid.addWidget(lb, 0, 0, Qt.AlignVCenter)
        grid.addWidget(cb, 0, 1)
        grid.addWidget(browse, 0, 2)
        # grid.addWidget(reload, 0, 3)

        self.summary_label = label = QLabel("", self)
        label.ensurePolished()
        f = label.font()
        if f.pointSizeF() != -1:
            f.setPointSizeF(f.pointSizeF() * 5 / 6)
        else:
            f.setPixelSize(f.pixelSize() * 5 / 6)
        label.setFont(f)
        grid.addWidget(label, 1, 1, 1, 3)

        self.controlArea.layout().addLayout(grid)

        box = gui.widgetBox(self.controlArea,
                            "Headers and Row Labels",
                            spacing=-1)
        hl = QHBoxLayout()
        hl.setContentsMargins(0, 0, 0, 0)
        self.header_rows_spin = spin = QSpinBox(box,
                                                minimum=0,
                                                maximum=3,
                                                value=self._header_rows_count,
                                                keyboardTracking=False)
        spin.valueChanged.connect(self.set_header_rows_count)
        hl.addWidget(QLabel("Data starts with", box))
        hl.addWidget(self.header_rows_spin)
        hl.addWidget(QLabel("header row(s)", box))
        hl.addStretch(10)
        box.layout().addLayout(hl)

        hl = QHBoxLayout()
        hl.setContentsMargins(0, 0, 0, 0)
        self.header_cols_spin = spin = QSpinBox(box,
                                                minimum=0,
                                                maximum=3,
                                                value=self._header_cols_count,
                                                keyboardTracking=False)
        spin.valueChanged.connect(self.set_header_cols_count)

        hl.addWidget(QLabel("First", box))
        hl.addWidget(self.header_cols_spin)
        hl.addWidget(QLabel("column(s) are row labels", box))
        hl.addStretch(10)
        box.layout().addLayout(hl)

        self.data_struct_box = box = gui.widgetBox(self.controlArea,
                                                   "Input Data Structure")
        gui.radioButtons(box,
                         self,
                         "_cells_in_rows", [
                             "Genes in rows, cells in columns",
                             "Cells in rows, genes in columns"
                         ],
                         callback=self._cells_in_rows_changed)

        box = gui.widgetBox(self.controlArea, "Sample Data", spacing=-1)

        grid = QGridLayout()
        grid.setContentsMargins(0, 0, 0, 0)
        box.layout().addLayout(grid)

        self.sample_rows_cb = cb = QCheckBox(checked=self._sample_rows_enabled)
        self.sample_rows_p_spin = spin = QSpinBox(minimum=0,
                                                  maximum=100,
                                                  value=self._sample_rows_p)
        spin.valueChanged.connect(self.set_sample_rows_p)
        suffix = QLabel("% of cells")
        cb.toggled.connect(self.set_sample_rows_enabled)

        grid.addWidget(cb, 0, 0)
        grid.addWidget(spin, 0, 1)
        grid.addWidget(suffix, 0, 2)

        self.sample_cols_cb = cb = QCheckBox(checked=self._sample_cols_enabled)
        self.sample_cols_p_spin = spin = QSpinBox(minimum=0,
                                                  maximum=100,
                                                  value=self._sample_cols_p)
        spin.valueChanged.connect(self.set_sample_cols_p)
        suffix = QLabel("% of genes")
        cb.toggled.connect(self.set_sample_cols_enabled)

        grid.addWidget(cb, 1, 0)
        grid.addWidget(spin, 1, 1)
        grid.addWidget(suffix, 1, 2)
        grid.setColumnStretch(3, 10)

        self.annotation_files_box = box = gui.widgetBox(
            self.controlArea, "Cell && Gene Annotation Files")
        form = QFormLayout(
            formAlignment=Qt.AlignLeft,
            rowWrapPolicy=QFormLayout.WrapAllRows,
        )
        box.layout().addLayout(form)

        self.row_annotations_cb = cb = QCheckBox(
            "Cell annotations", checked=self._row_annotations_enabled)
        self._row_annotations_w = w = QWidget(
            enabled=self._row_annotations_enabled)
        cb.toggled.connect(self.set_row_annotations_enabled)
        cb.toggled.connect(w.setEnabled)
        hl = QHBoxLayout()
        hl.setContentsMargins(0, 0, 0, 0)
        w.setLayout(hl)
        self.row_annotations_combo = QComboBox(
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon,
            minimumContentsLength=18)
        self.row_annotations_combo.activated.connect(
            self._row_annotations_combo_changed)
        hl.addWidget(self.row_annotations_combo)
        hl.addWidget(
            QPushButton("...",
                        box,
                        autoDefault=False,
                        icon=icon_open_dir,
                        clicked=self.browse_row_annotations))
        # hl.addWidget(QPushButton("Reload", box, autoDefault=False,
        #                          icon=icon_reload))
        form.addRow(cb, w)

        self.col_annotations_cb = cb = QCheckBox(
            "Gene annotations", checked=self._col_annotations_enabled)
        self._col_annotations_w = w = QWidget(
            enabled=self._col_annotations_enabled)
        cb.toggled.connect(self.set_col_annotations_enabled)
        cb.toggled.connect(w.setEnabled)
        hl = QHBoxLayout()
        hl.setContentsMargins(0, 0, 0, 0)
        w.setLayout(hl)
        self.col_annotations_combo = QComboBox(
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon,
            minimumContentsLength=18)
        self.col_annotations_combo.activated.connect(
            self._col_annotations_combo_changed)
        hl.addWidget(self.col_annotations_combo)
        hl.addWidget(
            QPushButton("...",
                        box,
                        autoDefault=False,
                        icon=icon_open_dir,
                        clicked=self.browse_col_annotations))
        # hl.addWidget(QPushButton("Reload", box, autoDefault=False,
        #                          icon=icon_reload))
        form.addRow(cb, w)

        self.controlArea.layout().addStretch(10)
        self.load_data_button = button = VariableTextPushButton(
            "Load data",
            autoDefault=True,
            textChoiceList=["Load data", "Reload"])
        self.load_data_button.setAutoDefault(True)
        button.clicked.connect(self.commit, Qt.QueuedConnection)
        self.controlArea.layout().addWidget(button, alignment=Qt.AlignRight)

        init_recent_paths_model(
            self.recent_model,
            [self.resolve_path(p) for p in self._recent],
        )
        init_recent_paths_model(
            self.row_annotations_combo.model(),
            [self.resolve_path(p) for p in self._recent_row_annotations])
        init_recent_paths_model(
            self.col_annotations_combo.model(),
            [self.resolve_path(p) for p in self._recent_col_annotations])
        self._update_summary()
        self._update_warning()

        if self._last_path != "" and os.path.exists(self._last_path):
            QTimer.singleShot(0,
                              lambda: self.set_current_path(self._last_path))
        else:
            self.recent_combo.setCurrentIndex(-1)

    def resolve_path(self, path):
        basedir = self.workflowEnv().get("basedir", None)
        if not basedir or not path:
            return path
        return path.resolve([("basedir", basedir)]) or path

    def _cells_in_rows_changed(self):
        self._data_loader.transposed = not self._cells_in_rows
        self._invalidate()
        self.cells_in_rows_changed.emit()

    def _row_annotations_combo_changed(self):
        path = self.row_annotations_combo.currentData(Qt.UserRole)
        if isinstance(path, RecentPath) and os.path.exists(path.abspath):
            self._data_loader.row_annotation_file = path  # type: RecentPath
        else:
            self._data_loader.row_annotation_file = None
        self._invalidate()

    def _col_annotations_combo_changed(self):
        path = self.col_annotations_combo.currentData(Qt.UserRole)
        if isinstance(path, RecentPath) and os.path.exists(path.abspath):
            self._data_loader.col_annotation_file = path  # type: RecentPath
        else:
            self._data_loader.col_annotation_file = None
        self._invalidate()

    def _update_warning(self):
        if (self._sample_rows_enabled and self._sample_rows_p < 100) or \
                (self._sample_cols_enabled and self._sample_cols_p < 100):
            self.Warning.sampling_in_effect()
        else:
            self.Warning.sampling_in_effect.clear()

    def set_sample_rows_enabled(self, enabled, commit=True):
        if self._sample_rows_enabled != enabled:
            self._sample_rows_enabled = enabled
            self.sample_rows_cb.setChecked(enabled)
            self._update_warning()
            self._data_loader.sample_rows_enabled = enabled
            if commit:
                self._invalidate()

    def set_sample_cols_enabled(self, enabled, commit=True):
        if self._sample_cols_enabled != enabled:
            self._sample_cols_enabled = enabled
            self.sample_cols_cb.setChecked(enabled)
            self._update_warning()
            self._data_loader.sample_cols_enabled = enabled
            if commit:
                self._invalidate()

    def set_sample_rows_p(self, p, commit=True):
        if self._sample_rows_p != p:
            self._sample_rows_p = p
            self._update_warning()
            self.sample_rows_p_spin.setValue(p)
            self._data_loader.sample_rows_p = p
            if commit:
                self._invalidate()

    def set_sample_cols_p(self, p, commit=True):
        if self._sample_cols_p != p:
            self._sample_cols_p = p
            self._update_warning()
            self.sample_cols_p_spin.setValue(p)
            self._data_loader.sample_cols_p = p
            if commit:
                self._invalidate()

    def set_header_rows_count(self, n, commit=True):
        if self._header_rows_count != n:
            self._header_rows_count = n
            self.header_rows_spin.setValue(n)
            self._data_loader.header_rows_count = n
            if commit:
                self._invalidate()

    def set_header_cols_count(self, n, commit=True):
        if self._header_cols_count != n:
            self._header_cols_count = n
            self.header_cols_spin.setValue(n)
            self._data_loader.header_cols_count = n
            if commit:
                self._invalidate()

    def set_row_annotations_enabled(self, enabled, commit=True):
        if self._row_annotations_enabled != enabled:
            self._row_annotations_enabled = enabled
            self.row_annotations_cb.setChecked(enabled)
            self._data_loader.row_annotations_enabled = enabled
            if commit:
                self._invalidate()

    def set_col_annotations_enabled(self, enabled, commit=True):
        if self._col_annotations_enabled != enabled:
            self._col_annotations_enabled = enabled
            self.col_annotations_cb.setChecked(enabled)
            self._data_loader.col_annotations_enabled = enabled
            if commit:
                self._invalidate()

    def set_current_path(self, path):
        if samepath(self._current_path, path):
            return

        model = self.recent_model
        index = -1
        pathitem = None
        for i in range(model.rowCount()):
            item = model.item(i)
            data = item.data(Qt.UserRole) if item is not None else None
            if isinstance(data, RecentPath) and samepath(path, data.abspath):
                index, pathitem = i, data
                break

        rpaths = []

        if pathitem is None:
            assert index == -1
            pathitem = RecentPath.create(path, rpaths)

        if index != -1:
            item = model.takeRow(index)
        else:
            item = RecentPath_asqstandarditem(pathitem)

        model.insertRow(0, item)
        self._current_path = path
        self.recent_combo.setCurrentIndex(0)

        self._data_loader = get_data_loader(path)
        self._update_summary()
        self.setup_gui()
        self._invalidate()

    def setup_gui(self):
        """ Use loader predefined values. If the value is None, set
        loader's parameter to widget's setting value.
        """
        loader = self._data_loader
        if loader.header_rows_count is not None:
            self.set_header_rows_count(loader.header_rows_count, False)
        else:
            loader.header_rows_count = self._header_rows_count

        if loader.header_cols_count is not None:
            self.set_header_cols_count(loader.header_cols_count, False)
        else:
            loader.header_cols_count = self._header_cols_count

        if loader.transposed is not None:
            self._cells_in_rows = not loader.transposed
        else:
            loader.transposed = not self._cells_in_rows

        if loader.sample_rows_enabled is not None:
            self.set_sample_rows_enabled(loader.sample_rows_enabled, False)
        else:
            loader.sample_rows_enabled = self._sample_rows_enabled

        if loader.sample_cols_enabled is not None:
            self.set_sample_cols_enabled(loader.sample_cols_enabled, False)
        else:
            loader.sample_cols_enabled = self._sample_cols_enabled

        if loader.sample_rows_p is not None:
            self.set_sample_rows_p(loader.sample_rows_p, False)
        else:
            loader.sample_rows_p = self._sample_rows_p

        if loader.sample_cols_p is not None:
            self.set_sample_cols_p(loader.sample_cols_p, False)
        else:
            loader.sample_cols_p = self._sample_cols_p

        if loader.row_annotation_file is not None:
            index = insert_recent_path(
                self.row_annotations_combo.model(),
                self.resolve_path(loader.row_annotation_file))
            self.row_annotations_combo.setCurrentIndex(index)
            self.set_row_annotations_enabled(loader.row_annotations_enabled,
                                             False)
        else:
            self.row_annotations_combo.setCurrentIndex(-1)
            self.set_row_annotations_enabled(False, False)

        if loader.col_annotation_file is not None:
            index = insert_recent_path(
                self.col_annotations_combo.model(),
                self.resolve_path(loader.col_annotation_file))
            self.col_annotations_combo.setCurrentIndex(index)
            self.set_col_annotations_enabled(loader.col_annotations_enabled,
                                             False)
        else:
            self.col_annotations_combo.setCurrentIndex(-1)
            self.set_col_annotations_enabled(False, False)

        self.header_rows_spin.setEnabled(loader.FIXED_FORMAT)
        self.header_cols_spin.setEnabled(loader.FIXED_FORMAT)
        self.data_struct_box.setEnabled(loader.FIXED_FORMAT)

        self.annotation_files_box.setEnabled(loader.ENABLE_ANNOTATIONS)

    def _update_summary(self):
        size = self._data_loader.file_size
        ncols = self._data_loader.n_cols
        nrows = self._data_loader.n_rows
        text = []
        if size is not None:
            text += [sizeformat(size)]
        if nrows is not None:
            text += ["{:n} rows".format(nrows)]
        if nrows is not None:
            text += ["{:n} columns".format(ncols)]

        self.summary_label.setText(", ".join(text))

    def current_path(self):
        return self._current_path

    def _select_recent(self, index):
        # type: (int) -> None
        # select a file from the recent list (entered via combo box `activate`)
        assert 0 <= index < self.recent_model.rowCount()
        item = self.recent_model.item(index)
        pathitem = item.data(Qt.UserRole)
        assert isinstance(pathitem, RecentPath)
        self.set_current_path(pathitem.abspath)

    @Slot()
    def browse(self):
        dlg = QFileDialog(self)
        dlg.setAcceptMode(QFileDialog.AcceptOpen)
        dlg.setFileMode(QFileDialog.ExistingFile)

        filters = Formats
        dlg.setNameFilters(filters)
        if filters:
            dlg.selectNameFilter(filters[0])
        if dlg.exec_() == QFileDialog.Accepted:
            filename = dlg.selectedFiles()[0]
            self.set_current_path(filename)

    @Slot()
    def browse_row_annotations(self):
        dlg = QFileDialog(self,
                          acceptMode=QFileDialog.AcceptOpen,
                          fileMode=QFileDialog.ExistingFile)
        filters = AnnotationFormats
        dlg.setNameFilters(filters)

        if filters:
            dlg.selectNameFilter(filters[0])
        if dlg.exec_() == QFileDialog.Accepted:
            filename = dlg.selectedFiles()[0]
            m = self.row_annotations_combo.model()  # type: QStandardItemModel
            pathitem = RecentPath.create(filename, [])
            index = insert_recent_path(m, pathitem)
            self.row_annotations_combo.setCurrentIndex(index)
            self._invalidate()

    @Slot()
    def browse_col_annotations(self):
        dlg = QFileDialog(self,
                          acceptMode=QFileDialog.AcceptOpen,
                          fileMode=QFileDialog.ExistingFile)
        filters = AnnotationFormats
        dlg.setNameFilters(filters)

        if filters:
            dlg.selectNameFilter(filters[0])
        if dlg.exec_() == QFileDialog.Accepted:
            filename = dlg.selectedFiles()[0]
            m = self.col_annotations_combo.model()  # type: QStandardItemModel
            pathitem = RecentPath.create(filename, [])
            index = insert_recent_path(m, pathitem)
            self.col_annotations_combo.setCurrentIndex(index)
            self._invalidate()

    def _invalidate(self):
        self.set_modified(True)

    def set_modified(self, modified):
        if modified:
            text = "Load data"
        else:
            text = "Reload"

        self.load_data_button.setText(text)
        self.load_data_button.setAutoDefault(modified)
        # Setting autoDefault once also sets default which persists even after
        # settings autoDefault back to False??
        self.load_data_button.setDefault(modified)
        self.Information.modified(shown=modified)

    def commit(self):
        path = self._current_path
        if not path:
            return
        self.Outputs.data.send(self._data_loader())
        self.show_error_messages()
        self.set_modified(False)

    def show_error_messages(self):
        self.Error.row_annotation_mismatch.clear()
        self.Error.col_annotation_mismatch.clear()
        self.Error.inadequate_headers.clear()
        errors = self._data_loader.errors
        if len(errors["row_annot_mismatch"]):
            self.Error.row_annotation_mismatch(*errors["row_annot_mismatch"])
        if len(errors["col_annot_mismatch"]):
            self.Error.col_annotation_mismatch(*errors["col_annot_mismatch"])
        if len(errors["inadequate_headers"]):
            self.Error.inadequate_headers(*errors["inadequate_headers"])
        if len(errors["reading_error"]):
            self.Error.reading_error()

    def onDeleteWidget(self):
        super().onDeleteWidget()

    def _saveState(self):
        maxitems = 15

        def dataiter(model, role=Qt.UserRole):
            return (model.data(model.index(i, 0), role)
                    for i in range(model.rowCount()))

        def recent_paths(model):
            return [
                self.relocate_path(el) for el in dataiter(model)
                if isinstance(el, RecentPath)
            ][:maxitems]

        self._recent = recent_paths(self.recent_model)
        self._recent_row_annotations = recent_paths(
            self.row_annotations_combo.model())
        self._recent_col_annotations = recent_paths(
            self.col_annotations_combo.model())
        self._last_path = self._current_path

    def relocate_path(self, path):
        basedir = self.workflowEnv().get("basedir", None)
        if not basedir or not path:
            return path
        return RecentPath.create(path.abspath, [("basedir", basedir)])

    def saveSettings(self):
        self._saveState()
        super().saveSettings()
Exemple #5
0
class ColorGradientSelection(QWidget):
    activated = Signal(int)

    currentIndexChanged = Signal(int)
    thresholdsChanged = Signal(float, float)
    centerChanged = Signal(float)

    def __init__(self, *args, thresholds=(0.0, 1.0), center=None, **kwargs):
        super().__init__(*args, **kwargs)

        low = round(clip(thresholds[0], 0., 1.), 2)
        high = round(clip(thresholds[1], 0., 1.), 2)
        high = max(low, high)
        self.__threshold_low, self.__threshold_high = low, high
        self.__center = center
        form = QFormLayout(formAlignment=Qt.AlignLeft,
                           labelAlignment=Qt.AlignLeft,
                           fieldGrowthPolicy=QFormLayout.AllNonFixedFieldsGrow)
        form.setContentsMargins(0, 0, 0, 0)
        self.gradient_cb = QComboBox(
            None,
            objectName="gradient-combo-box",
        )
        self.gradient_cb.setAttribute(Qt.WA_LayoutUsesWidgetRect)
        icsize = self.style().pixelMetric(QStyle.PM_SmallIconSize, None,
                                          self.gradient_cb)
        self.gradient_cb.setIconSize(QSize(64, icsize))
        model = itemmodels.ContinuousPalettesModel()
        model.setParent(self)

        self.gradient_cb.setModel(model)
        self.gradient_cb.activated[int].connect(self.activated)
        self.gradient_cb.currentIndexChanged.connect(self.currentIndexChanged)

        if center is not None:

            def __on_center_changed():
                self.__center = float(self.center_edit.text() or "0")
                self.centerChanged.emit(self.__center)

            self.center_box = QWidget()
            center_layout = QHBoxLayout()
            self.center_box.setLayout(center_layout)
            width = QFontMetrics(self.font()).boundingRect("9999999").width()
            self.center_edit = QLineEdit(text=f"{self.__center}",
                                         maximumWidth=width,
                                         placeholderText="0",
                                         alignment=Qt.AlignRight)
            self.center_edit.setValidator(QDoubleValidator())
            self.center_edit.editingFinished.connect(__on_center_changed)
            center_layout.setContentsMargins(0, 0, 0, 0)
            center_layout.addStretch(1)
            center_layout.addWidget(QLabel("Centered at"))
            center_layout.addWidget(self.center_edit)
            self.gradient_cb.currentIndexChanged.connect(
                self.__update_center_visibility)
        else:
            self.center_box = None

        slider_low = Slider(objectName="threshold-low-slider",
                            minimum=0,
                            maximum=100,
                            value=int(low * 100),
                            orientation=Qt.Horizontal,
                            tickPosition=QSlider.TicksBelow,
                            pageStep=10,
                            toolTip=self.tr("Low gradient threshold"),
                            whatsThis=self.tr(
                                "Applying a low threshold will squeeze the "
                                "gradient from the lower end"))
        slider_high = Slider(objectName="threshold-low-slider",
                             minimum=0,
                             maximum=100,
                             value=int(high * 100),
                             orientation=Qt.Horizontal,
                             tickPosition=QSlider.TicksAbove,
                             pageStep=10,
                             toolTip=self.tr("High gradient threshold"),
                             whatsThis=self.tr(
                                 "Applying a high threshold will squeeze the "
                                 "gradient from the higher end"))
        form.setWidget(0, QFormLayout.SpanningRole, self.gradient_cb)
        if self.center_box:
            form.setWidget(1, QFormLayout.SpanningRole, self.center_box)
        form.addRow(self.tr("Low:"), slider_low)
        form.addRow(self.tr("High:"), slider_high)
        self.slider_low = slider_low
        self.slider_high = slider_high
        self.slider_low.valueChanged.connect(self.__on_slider_low_moved)
        self.slider_high.valueChanged.connect(self.__on_slider_high_moved)
        self.setLayout(form)

    def setModel(self, model: QAbstractItemModel) -> None:
        self.gradient_cb.setModel(model)

    def model(self) -> QAbstractItemModel:
        return self.gradient_cb.model()

    def findData(self, data: Any, role: Qt.ItemDataRole) -> int:
        return self.gradient_cb.findData(data, role)

    def setCurrentIndex(self, index: int) -> None:
        self.gradient_cb.setCurrentIndex(index)
        self.__update_center_visibility()

    def currentIndex(self) -> int:
        return self.gradient_cb.currentIndex()

    currentIndex_ = Property(int,
                             currentIndex,
                             setCurrentIndex,
                             notify=currentIndexChanged)

    def currentData(self, role=Qt.UserRole) -> Any:
        return self.gradient_cb.currentData(role)

    def thresholds(self) -> Tuple[float, float]:
        return self.__threshold_low, self.__threshold_high

    thresholds_ = Property(object, thresholds, notify=thresholdsChanged)

    def thresholdLow(self) -> float:
        return self.__threshold_low

    def setThresholdLow(self, low: float) -> None:
        self.setThresholds(low, max(self.__threshold_high, low))

    thresholdLow_ = Property(float,
                             thresholdLow,
                             setThresholdLow,
                             notify=thresholdsChanged)

    def thresholdHigh(self) -> float:
        return self.__threshold_high

    def setThresholdHigh(self, high: float) -> None:
        self.setThresholds(min(self.__threshold_low, high), high)

    def center(self) -> float:
        return self.__center

    def setCenter(self, center: float) -> None:
        self.__center = center
        self.center_edit.setText(f"{center}")
        self.centerChanged.emit(center)

    thresholdHigh_ = Property(float,
                              thresholdLow,
                              setThresholdLow,
                              notify=thresholdsChanged)

    def __on_slider_low_moved(self, value: int) -> None:
        high = self.slider_high
        old = self.__threshold_low, self.__threshold_high
        self.__threshold_low = value / 100.
        if value >= high.value():
            self.__threshold_high = value / 100.
            high.setSliderPosition(value)
        new = self.__threshold_low, self.__threshold_high
        if new != old:
            self.thresholdsChanged.emit(*new)

    def __on_slider_high_moved(self, value: int) -> None:
        low = self.slider_low
        old = self.__threshold_low, self.__threshold_high
        self.__threshold_high = value / 100.
        if low.value() >= value:
            self.__threshold_low = value / 100
            low.setSliderPosition(value)
        new = self.__threshold_low, self.__threshold_high
        if new != old:
            self.thresholdsChanged.emit(*new)

    def setThresholds(self, low: float, high: float) -> None:
        low = round(clip(low, 0., 1.), 2)
        high = round(clip(high, 0., 1.), 2)
        if low > high:
            high = low
        if self.__threshold_low != low or self.__threshold_high != high:
            self.__threshold_high = high
            self.__threshold_low = low
            self.slider_low.setSliderPosition(low * 100)
            self.slider_high.setSliderPosition(high * 100)
            self.thresholdsChanged.emit(high, low)

    def __update_center_visibility(self):
        if self.center_box is None:
            return

        palette = self.currentData()
        self.center_box.setVisible(
            isinstance(palette, colorpalettes.Palette)
            and palette.flags & palette.Flags.Diverging != 0)
Exemple #6
0
class ColorGradientSelection(QWidget):
    activated = Signal(int)

    currentIndexChanged = Signal(int)
    thresholdsChanged = Signal(float, float)

    def __init__(self, *args, thresholds=(0.0, 1.0), **kwargs):
        super().__init__(*args, **kwargs)

        low = round(clip(thresholds[0], 0., 1.), 2)
        high = round(clip(thresholds[1], 0., 1.), 2)
        high = max(low, high)
        self.__threshold_low, self.__threshold_high = low, high
        form = QFormLayout(
            formAlignment=Qt.AlignLeft,
            labelAlignment=Qt.AlignLeft,
            fieldGrowthPolicy=QFormLayout.AllNonFixedFieldsGrow
        )
        form.setContentsMargins(0, 0, 0, 0)
        self.gradient_cb = QComboBox(
            None, objectName="gradient-combo-box",
        )
        self.gradient_cb.setAttribute(Qt.WA_LayoutUsesWidgetRect)
        icsize = self.style().pixelMetric(
            QStyle.PM_SmallIconSize, None, self.gradient_cb
        )
        self.gradient_cb.setIconSize(QSize(64, icsize))
        model = itemmodels.ContinuousPalettesModel()
        model.setParent(self)

        self.gradient_cb.setModel(model)
        self.gradient_cb.activated[int].connect(self.activated)
        self.gradient_cb.currentIndexChanged.connect(self.currentIndexChanged)

        slider_low = QSlider(
            objectName="threshold-low-slider", minimum=0, maximum=100,
            value=int(low * 100), orientation=Qt.Horizontal,
            tickPosition=QSlider.TicksBelow, pageStep=10,
            toolTip=self.tr("Low gradient threshold"),
            whatsThis=self.tr("Applying a low threshold will squeeze the "
                              "gradient from the lower end")
        )
        slider_high = QSlider(
            objectName="threshold-low-slider", minimum=0, maximum=100,
            value=int(high * 100), orientation=Qt.Horizontal,
            tickPosition=QSlider.TicksAbove, pageStep=10,
            toolTip=self.tr("High gradient threshold"),
            whatsThis=self.tr("Applying a high threshold will squeeze the "
                              "gradient from the higher end")
        )
        form.setWidget(0, QFormLayout.SpanningRole, self.gradient_cb)
        form.addRow(self.tr("Low:"), slider_low)
        form.addRow(self.tr("High:"), slider_high)
        self.slider_low = slider_low
        self.slider_high = slider_high
        self.slider_low.valueChanged.connect(self.__on_slider_low_moved)
        self.slider_high.valueChanged.connect(self.__on_slider_high_moved)
        self.setLayout(form)

    def setModel(self, model: QAbstractItemModel) -> None:
        self.gradient_cb.setModel(model)

    def model(self) -> QAbstractItemModel:
        return self.gradient_cb.model()

    def findData(self, data: Any, role: Qt.ItemDataRole) -> int:
        return self.gradient_cb.findData(data, role)

    def setCurrentIndex(self, index: int) -> None:
        self.gradient_cb.setCurrentIndex(index)

    def currentIndex(self) -> int:
        return self.gradient_cb.currentIndex()

    currentIndex_ = Property(
        int, currentIndex, setCurrentIndex, notify=currentIndexChanged)

    def currentData(self, role=Qt.UserRole) -> Any:
        return self.gradient_cb.currentData(role)

    def thresholds(self) -> Tuple[float, float]:
        return self.__threshold_low, self.__threshold_high

    thresholds_ = Property(object, thresholds, notify=thresholdsChanged)

    def thresholdLow(self) -> float:
        return self.__threshold_low

    def setThresholdLow(self, low: float) -> None:
        self.setThresholds(low, max(self.__threshold_high, low))

    thresholdLow_ = Property(
        float, thresholdLow, setThresholdLow, notify=thresholdsChanged)

    def thresholdHigh(self) -> float:
        return self.__threshold_high

    def setThresholdHigh(self, high: float) -> None:
        self.setThresholds(min(self.__threshold_low, high), high)

    thresholdHigh_ = Property(
        float, thresholdLow, setThresholdLow, notify=thresholdsChanged)

    def __on_slider_low_moved(self, value: int) -> None:
        high = self.slider_high
        old = self.__threshold_low, self.__threshold_high
        self.__threshold_low = value / 100.
        if value >= high.value():
            self.__threshold_high = value / 100.
            high.setSliderPosition(value)
        new = self.__threshold_low, self.__threshold_high
        if new != old:
            self.thresholdsChanged.emit(*new)

    def __on_slider_high_moved(self, value: int) -> None:
        low = self.slider_low
        old = self.__threshold_low, self.__threshold_high
        self.__threshold_high = value / 100.
        if low.value() >= value:
            self.__threshold_low = value / 100
            low.setSliderPosition(value)
        new = self.__threshold_low, self.__threshold_high
        if new != old:
            self.thresholdsChanged.emit(*new)

    def setThresholds(self, low: float, high: float) -> None:
        low = round(clip(low, 0., 1.), 2)
        high = round(clip(high, 0., 1.), 2)
        if low > high:
            high = low
        if self.__threshold_low != low or self.__threshold_high != high:
            self.__threshold_high = high
            self.__threshold_low = low
            self.slider_low.setSliderPosition(low * 100)
            self.slider_high.setSliderPosition(high * 100)
            self.thresholdsChanged.emit(high, low)
Exemple #7
0
class EditLightUI(QWidget):
    def __init__(self, light, parent):
        super(EditLightUI, self).__init__()
        self.title = 'Edit Lighting Element'
        self.parent = parent
        self.light = light
        self.setLayout(QVBoxLayout(self))
        self.layout().setAlignment(Qt.AlignCenter)

        # Init Name text control
        self._nameControl = LineEdit('Name', self)
        self._nameControl.setText(self.light.name)
        self._nameControl.kb.connect(self.showOSK)
        # Init Output Pin dropdown control
        self._outputPinControlLabel = QLabel('Output Pin', self)
        self._outputPinControl = QComboBox(self)
        for x in self.parent.availablePins():
            self._outputPinControl.addItem(str(x))
        for i in range(self._outputPinControl.count()):
            if self._outputPinControl.itemText(i) == str(self.light.outputPin):
                self._outputPinControl.setCurrentIndex(i)
                break
        del x, i
        # Init Enabled checkbox control and set value
        self._enabledControl = QCheckBox('Enabled', self)
        self._enabledControl.setChecked(self.light.enabled)
        # Init Icon dropdown control
        self._iconControlLabel = QLabel('Icon Path', self)
        self._iconControl = QComboBox(self)
        for key in Config.icons['lights'].keys():
            icon = Config.icon('lights', key)
            self._iconControl.addItem(icon['name'], key)
            self._iconControl.setItemIcon(self._iconControl.count() - 1,
                                          QIcon(icon['path']))
        for i in range(self._iconControl.count()):
            if self.light.icon is not None and self._iconControl.itemData(
                    i) == self.light.icon:
                self._iconControl.setCurrentIndex(i)
                break
        del key, i
        # Init Strobe checkbox control and set value
        self._strobeControl = QCheckBox('Strobe', self)
        self._strobeControl.setChecked(self.light.strobe)
        # Init Save button
        self._saveBtn = QPushButton('Save', self)
        self._saveBtn.clicked.connect(self.__saveBtnAction)
        # Init cancel button
        self._cancelBtn = QPushButton('Cancel', self)
        self._cancelBtn.clicked.connect(self.__cancel)
        # Assign control layout
        layoutList = [['_nameControl'],
                      ['_outputPinControlLabel', '_outputPinControl'],
                      ['_enabledControl', '_strobeControl'],
                      ['_iconControlLabel', '_iconControl'],
                      ['_saveBtn', '_cancelBtn']]
        for l in layoutList:
            panel = QWidget(self)
            panel.setLayout(QHBoxLayout(panel))
            panel.layout().setAlignment(Qt.AlignCenter)
            for ctrl in l:
                panel.layout().addWidget(eval('self.%s' % ctrl))
            self.layout().addWidget(panel)
        # Destroy local variables
        del l, ctrl

    def __saveBtnAction(self):
        self.light.name = self._nameControl.text()
        self.light.outputPin = int(self._outputPinControl.currentText())
        self.light.enabled = self._enabledControl.isChecked()
        self.light.icon = self._iconControl.currentData()
        self.light.strobe = self._strobeControl.isChecked()
        self.parent.lights.save()
        self.parent.loadUI("config_light")

    def __cancel(self):
        self.parent.loadUI("config_light")

    def showOSK(self):
        self.window().dock.show()
        self.window().osk.rWidget = self._nameControl
Exemple #8
0
class EditOBAUI(QWidget):
    def __init__(self, oba, parent):
        super(EditOBAUI, self).__init__()
        self.parent = parent
        self.oba = oba
        self.setLayout(QVBoxLayout(self))
        self.layout().setAlignment(Qt.AlignCenter)

        # Init Name text control
        self._nameControl = LineEdit('Name', self)
        self._nameControl.setText(self.oba.name)
        self._nameControl.kb.connect(self.showOSK)
        # Init Output Pin dropdown control
        self._outputPinControlLabel = QLabel('Output Pin', self)
        self._outputPinControl = QComboBox(self)
        for _pin in self.parent.availablePins():
            self._outputPinControl.addItem(str(_pin))
        for _i in range(self._outputPinControl.count()):
            if self._outputPinControl.itemText(_i) == str(self.oba.outputPin):
                self._outputPinControl.setCurrentIndex(_i)
                break
        # Init Momentary checkbox control and set value
        self._momentaryControl = QCheckBox('Momentary', self)
        self._momentaryControl.setChecked(self.oba.momentary)
        # Init Enabled checkbox control and set value
        self._enabledControl = QCheckBox('Enabled', self)
        self._enabledControl.setChecked(self.oba.enabled)
        # Init Icon dropdown control
        self._iconControlLabel = QLabel('Icon Path', self)
        self._iconControl = QComboBox(self)
        for _key in Config.icons['oba'].keys():
            icon = Config.icon('oba', _key)
            self._iconControl.addItem(icon['name'], _key)
            self._iconControl.setItemIcon(self._iconControl.count() - 1,
                                          QIcon(icon['path']))
        for _i in range(self._iconControl.count()):
            # Set current index if matching icon attribute
            if self.oba.icon is not None and self._iconControl.itemData(
                    _i) == self.oba.icon:
                self._iconControl.setCurrentIndex(_i)
                break
        # Init Save button
        self._saveBtn = QPushButton('Save', self)
        self._saveBtn.clicked.connect(self.__saveBtnAction)
        # Init cancel button
        self._cancelBtn = QPushButton('Cancel', self)
        self._cancelBtn.clicked.connect(self.__cancel)
        # Assign control layout
        _layout = [['_nameControl'],
                   ['_outputPinControlLabel', '_outputPinControl'],
                   ['_momentaryControl', '_enabledControl'],
                   ['_iconControlLabel', '_iconControl'],
                   ['_saveBtn', '_cancelBtn']]
        for _list in _layout:
            _panel = QWidget(self)
            _panel.setLayout(QHBoxLayout(_panel))
            _panel.layout().setAlignment(Qt.AlignCenter)
            _panel.layout().setSpacing(20)
            for _control in _list:
                _panel.layout().addWidget(eval('self.%s' % _control))
            self.layout().addWidget(_panel)

    def __saveBtnAction(self):
        self.oba.name = self._nameControl.text()
        self.oba.outputPin = int(self._outputPinControl.currentText())
        self.oba.enabled = self._enabledControl.isChecked()
        self.oba.icon = self._iconControl.currentData()
        self.oba.momentary = self._momentaryControl.isChecked()
        self.parent.obas.save()
        self.parent.loadUI('config_oba')

    def __cancel(self):
        self.parent.loadUI('config_oba')

    def showOSK(self):
        self.window().dock.show()
        self.window().osk.rWidget = self._nameControl