Ejemplo n.º 1
0
    def _convert_items_to_indices(
            self,
            items: Iterable[KnechtItem],
            proxy_model: QSortFilterProxyModel = None
    ) -> Iterator[QModelIndex]:
        for item in items:
            index = self.model.get_index_from_item(item)

            proxy_index = None
            if proxy_model:
                proxy_index = proxy_model.mapFromSource(index)

            if proxy_index:
                yield proxy_index
                continue

            if index:
                yield index
Ejemplo n.º 2
0
class SearchBarEditor(QTableView):
    """A Google-like search bar, implemented as a QTableView with a _CustomLineEditDelegate in the first row.
    """

    data_committed = Signal()

    def __init__(self, parent, tutor=None):
        """Initializes instance.

        Args:
            parent (QWidget): parent widget
            tutor (QWidget, NoneType): another widget used for positioning.
        """
        super().__init__(parent)
        self._tutor = tutor
        self._base_size = QSize()
        self._base_offset = QPoint()
        self._original_text = None
        self._orig_pos = None
        self.first_index = QModelIndex()
        self.model = QStandardItemModel(self)
        self.proxy_model = QSortFilterProxyModel(self)
        self.proxy_model.setSourceModel(self.model)
        self.proxy_model.filterAcceptsRow = self._proxy_model_filter_accepts_row
        self.setModel(self.proxy_model)
        self.verticalHeader().hide()
        self.horizontalHeader().hide()
        self.setShowGrid(False)
        self.setMouseTracking(True)
        self.setTabKeyNavigation(False)
        delegate = _CustomLineEditDelegate(self)
        delegate.text_edited.connect(self._handle_delegate_text_edited)
        self.setItemDelegateForRow(0, delegate)

    def set_data(self, current, items):
        """Populates model.

        Args:
            current (str)
            items (Sequence(str))
        """
        item_list = [QStandardItem(current)]
        for item in items:
            qitem = QStandardItem(item)
            item_list.append(qitem)
            qitem.setFlags(~Qt.ItemIsEditable)
        self.model.invisibleRootItem().appendRows(item_list)
        self.first_index = self.proxy_model.mapFromSource(
            self.model.index(0, 0))

    def set_base_size(self, size):
        self._base_size = size

    def set_base_offset(self, offset):
        self._base_offset = offset

    def update_geometry(self):
        """Updates geometry.
        """
        self.horizontalHeader().setDefaultSectionSize(self._base_size.width())
        self.verticalHeader().setDefaultSectionSize(self._base_size.height())
        self._orig_pos = self.pos() + self._base_offset
        if self._tutor:
            self._orig_pos += self._tutor.mapTo(self.parent(),
                                                self._tutor.rect().topLeft())
        self.refit()

    def refit(self):
        self.move(self._orig_pos)
        table_height = self.verticalHeader().length()
        size = QSize(self._base_size.width(),
                     table_height + 2).boundedTo(self.parent().size())
        self.resize(size)
        # Adjust position if widget is outside parent's limits
        bottom_right = self.mapToGlobal(self.rect().bottomRight())
        parent_bottom_right = self.parent().mapToGlobal(
            self.parent().rect().bottomRight())
        x_offset = max(0, bottom_right.x() - parent_bottom_right.x())
        y_offset = max(0, bottom_right.y() - parent_bottom_right.y())
        self.move(self.pos() - QPoint(x_offset, y_offset))

    def data(self):
        return self.first_index.data(Qt.EditRole)

    @Slot("QString")
    def _handle_delegate_text_edited(self, text):
        """Filters model as the first row is being edited."""
        self._original_text = text
        self.proxy_model.setFilterRegExp("^" + text)
        self.proxy_model.setData(self.first_index, text)
        self.refit()

    def _proxy_model_filter_accepts_row(self, source_row, source_parent):
        """Always accept first row.
        """
        if source_row == 0:
            return True
        return QSortFilterProxyModel.filterAcceptsRow(self.proxy_model,
                                                      source_row,
                                                      source_parent)

    def keyPressEvent(self, event):
        """Sets data from current index into first index as the user navigates
        through the table using the up and down keys.
        """
        super().keyPressEvent(event)
        event.accept(
        )  # Important to avoid unhandled behavior when trying to navigate outside view limits
        # Initialize original text. TODO: Is there a better place for this?
        if self._original_text is None:
            self.proxy_model.setData(self.first_index, event.text())
            self._handle_delegate_text_edited(event.text())
        # Set data from current index in model
        if event.key() in (Qt.Key_Up, Qt.Key_Down):
            current = self.currentIndex()
            if current.row() == 0:
                self.proxy_model.setData(self.first_index, self._original_text)
            else:
                self.proxy_model.setData(self.first_index, current.data())

    def currentChanged(self, current, previous):
        super().currentChanged(current, previous)
        self.edit_first_index()

    def edit_first_index(self):
        """Edits first index if valid and not already being edited.
        """
        if not self.first_index.isValid():
            return
        if self.isPersistentEditorOpen(self.first_index):
            return
        self.edit(self.first_index)

    def mouseMoveEvent(self, event):
        """Sets the current index to the one hovered by the mouse."""
        if not self.currentIndex().isValid():
            return
        index = self.indexAt(event.pos())
        if index.row() == 0:
            return
        self.setCurrentIndex(index)

    def mousePressEvent(self, event):
        """Commits data."""
        index = self.indexAt(event.pos())
        if index.row() == 0:
            return
        self.proxy_model.setData(self.first_index, index.data(Qt.EditRole))
        self.data_committed.emit()
Ejemplo n.º 3
0
class SmartSyncDialog(object):
    def __init__(self, parent):
        self._dialog = QDialog(parent)
        self._dialog.setWindowIcon(QIcon(':/images/icon.png'))
        self._ui = Ui_Dialog()
        self._ui.setupUi(self._dialog)

        self._ui.centralWidget.setFrameShape(QFrame.NoFrame)
        self._ui.centralWidget.setLineWidth(1)

        self._model = None
        self._proxy_model = QSortFilterProxyModel()

        self._view = self._ui.folder_list_view
        self._view.setModel(self._proxy_model)
        self._view.expanded.connect(self.on_item_expanded)

        self._offline_paths = None

        # for frameless window moving
        self._x_coord = 0
        self._y_coord = 0
        self._dialog.mousePressEvent = self.on_mouse_press_event
        self._dialog.mouseMoveEvent = self.on_mouse_move_event

        self._loader_movie = QMovie(":/images/loader.gif")
        self._ui.loader_label.setMovie(self._loader_movie)

    def on_mouse_press_event(self, ev):
        self._x_coord = ev.x()
        self._y_coord = ev.y()

    def on_mouse_move_event(self, ev):
        self._dialog.move(
            ev.globalX() - self._x_coord, ev.globalY() - self._y_coord)

    def on_item_expanded(self, index):
        if self._model:
            self._model.on_item_expanded(self._proxy_model.mapToSource(index))

    def show(self, root_path, hide_dotted=False):
        if LOGGING_ENABLED:
            logger.info(
                "Opening smart sync dialog for path '%s'...", root_path)

        self._model = TreeModel(
            root_path, hide_dotted=hide_dotted)

        self._proxy_model.setSourceModel(self._model)

        self.show_cursor_loading(show_movie=True)
        # Execute dialog
        result = self._dialog.exec_()
        if result == QDialog.Accepted:
            offline_dirs = self._model.get_added_to_offline_paths()
            new_online = list(self._model.get_removed_from_offline_paths())
            new_offline = list(offline_dirs - self._offline_paths)
            if LOGGING_ENABLED:
                logger.debug("new offline dirs %s, new online dirs %s",
                             new_offline, new_online)
            return new_offline, new_online
        else:
            return [], []

    def set_offline_paths(self, offline_paths):
        self._offline_paths = offline_paths
        logger.debug("offline paths %s", offline_paths)
        self._model.set_offline_dirs(offline_paths)
        self.show_cursor_normal()

        self._view.expand(self._proxy_model.mapFromSource(
            self._model.get_root_path_index()))
        self._proxy_model.sort(0, Qt.AscendingOrder)


    def show_cursor_loading(self, show_movie=False):
        if show_movie:
            self._ui.stackedWidget.setCurrentIndex(1)
            self._loader_movie.start()
        else:
            self._dialog.setCursor(Qt.WaitCursor)

    def show_cursor_normal(self):
        self._dialog.setCursor(Qt.ArrowCursor)
        if self._loader_movie.state() == QMovie.Running:
            self._loader_movie.stop()
        self._ui.stackedWidget.setCurrentIndex(0)