Ejemplo n.º 1
0
    def startDrag(self, event):
        """
        Overrides bae QListView startDrag function
        :param event: QEvent
        """

        if not self.dragEnabled():
            return

        if self._drag_start_pos and hasattr(event, 'pos'):
            item = self.item_at(event.pos())
            if item and item.drag_enabled():
                self._drag_start_index = self.indexAt(event.pos())
                point = self._drag_start_pos - event.pos()
                dt = self.drag_threshold()
                if point.x() > dt or point.y() > dt or point.x(
                ) < -dt or point.y() < -dt:
                    items = self.selected_items()
                    mime_data = self.mime_data(items)
                    pixmap = self._drag_pixmap(item, items)
                    hotspot = QPoint(pixmap.width() * 0.5,
                                     pixmap.height() * 0.5)
                    self._drag = QDrag(self)
                    self._drag.setPixmap(pixmap)
                    self._drag.setHotSpot(hotspot)
                    self._drag.setMimeData(mime_data)
                    self._drag.start(Qt.MoveAction)
Ejemplo n.º 2
0
    def mouseMoveEvent(self, event):
        """
        Override the behaviour when the mouse is moved
        Doing this we avoid that the tab is moved when tearing off the tab
        """

        if self._select_tab_index < -1:
            pass
        else:
            if event.modifiers() == Qt.ControlModifier:
                self.setCursor(Qt.OpenHandCursor)
            else:
                self.setCursor(Qt.ArrowCursor)

        if not event.buttons() == Qt.LeftButton:
            return

        if not self._drag_start_pos.isNull() and (
                event.pos() - self._drag_start_pos
        ).manhattanLength() < QApplication.startDragDistance():
            self._drag_initiated = True

        if (event.buttons() == Qt.LeftButton
            ) and self._drag_initiated and not self.geometry().contains(
                event.pos()):
            finish_move_event = QMouseEvent(QEvent.MouseMove, event.pos(),
                                            Qt.NoButton, Qt.NoButton,
                                            Qt.NoModifier)
            super(TearOffTabBar, self).mouseMoveEvent(finish_move_event)

            drag = QDrag(self)
            mime_data = QMimeData()
            mime_data.setData('action', 'application/tab-detach')
            drag.setMimeData(mime_data)

            rect = self.tabRect(self.tabAt(event.pos()))
            pixmap_big = QPixmap.grabWidget(
                self.parentWidget().currentWidget()).scaled(
                    640, 480, Qt.KeepAspectRatio)
            drag.setPixmap(pixmap_big)
            drag.setDragCursor(QPixmap(), Qt.LinkAction)

            dragged = drag.exec_(Qt.MoveAction)
            if dragged == Qt.IgnoreAction:
                # moved outside of tab widget
                event.accept()
                self.tabDetached.emit(self.tabAt(self._drag_start_pos),
                                      QCursor.pos())
            elif dragged == Qt.MoveAction:
                # moved inside of tab widget
                if not self._drag_dropped_pos.isNull():
                    event.accept()
                    self.tabMoved.emit(self.tabAt(self._drag_start_pos),
                                       self.tabAt(self._drag_dropped_pos))
                    self._drag_dropped_pos = QPoint()
        else:
            super(TearOffTabBar, self).mouseMoveEvent(event)
Ejemplo n.º 3
0
    def mousePressEvent( self, event ):
        # handle an internal move

        # start a drag event
        if event.button() == Qt.LeftButton and self.dragDropRect().contains(event.pos()):
            # create the pixmap
            pixmap = QPixmap.grabWidget(self, self.rect())

            # create the mimedata
            mimeData = QMimeData()
            mimeData.setText('ItemTitle::%s' % (self.title()))

            # create the drag
            drag = QDrag(self)
            drag.setMimeData(mimeData)
            drag.setPixmap(pixmap)
            drag.setHotSpot(event.pos())

            if not drag.exec_():
                self._accordianWidget.emitItemDragFailed(self)

            event.accept()

        # determine if the expand/collapse should occur
        elif event.button() == Qt.LeftButton and self.expandCollapseRect().contains(event.pos()):
            self._clicked = True
            event.accept()

        else:
            event.ignore()
Ejemplo n.º 4
0
 def mousePressEvent(self, event):
     if self.dragDropMode() and event.button() == Qt.LeftButton and self.dragDropRect().contains(event.pos()):
         pixmap = QPixmap.grabWidget(self, self.rect())
         mimeData = QMimeData()
         mimeData.setText('ItemTitle::%s' % self.title())
         drag = QDrag(self)
         drag.setMimeData(mimeData)
         drag.setPixmap(pixmap)
         drag.setHotSpot(event.pos())
         if not drag.exec_():
             self._expanderWidget.emitItemDragFailed(self)
         event.accept()
     elif event.button() == Qt.LeftButton and self.expandCollapseRect().contains(event.pos()):
         self._clicked = True
         event.accept()
     else:
         event.ignore()
Ejemplo n.º 5
0
 def mousePressEvent(self, event):
     if event.button() == Qt.LeftButton and self.get_drag_drop_rect().contains(event.pos()):
         pixmap = QPixmap.grabWidget(self, self.rect())
         mime_data = QMimeData()
         mime_data.setText('ItemTitle::{}'.format(self.title()))
         drag = QDrag(self)
         drag.setMimeData(mime_data)
         drag.setPixmap(pixmap)
         drag.setHotSpot(event.pos())
         if not drag.exec_():
             self._accordion_widget.emit_item_drag_failed(self)
         event.accept()
     # Check if the expand/collapse should happen
     elif event.button() == Qt.LeftButton and self.expand_collapse_rect().contains(event.pos()):
         self._clicked = True
         event.accept()
     else:
         event.ignore()
Ejemplo n.º 6
0
    def startDrag(self, supported_actions):
        """
        Function that is called when we start dragging an item
        This function set ups the mime data and creates a copy of the image
        of the item that is show in cursor position while dragging action
        is enabled
        """

        index = self.currentIndex()
        model = self.model()
        item = model.itemFromIndex(index)
        drag = QDrag(self)
        mime_data = QMimeData()
        mime_data.setData('item_text', QByteArray(str(item.text())))
        mime_data.setData('item_size', QByteArray('%.1f %.1f' % (item.size.width(), item.size.height())))
        url = QUrl.fromLocalFile(item.text())
        mime_data.setUrls(url)
        drag.setMimeData(mime_data)
        pixmap = item.icon().pixmap(50, 50)
        drag.setDragCursor(pixmap, Qt.CopyAction)
        drag.start()
Ejemplo n.º 7
0
class ViewerListView(mixinview.ViewerViewWidgetMixin, QListView):

    DEFAULT_DRAG_THRESHOLD = consts.LIST_DEFAULT_DRAG_THRESHOLD

    itemMoved = Signal(object)
    itemDropped = Signal(object)
    itemClicked = Signal(object)
    itemDoubleClicked = Signal(object)

    def __init__(self, *args, **kwargs):
        QListView.__init__(self, *args, **kwargs)
        mixinview.ViewerViewWidgetMixin.__init__(self)

        self.setSpacing(5)
        self.setMouseTracking(True)
        self.setSelectionRectVisible(True)
        self.setViewMode(QListView.IconMode)
        self.setResizeMode(QListView.Adjust)
        self.setSelectionMode(QListView.ExtendedSelection)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setAcceptDrops(True)
        self.setDragEnabled(True)
        self.setDragDropMode(QAbstractItemView.DragDrop)

        self._tree_widget = None
        self._rubber_band = None
        self._rubber_band_start_pos = None
        self._rubber_band_color = QColor(Qt.white)
        self._custom_sort_order = list()

        self._drag = None
        self._drag_start_pos = None
        self._drag_start_index = None
        self._drop_enabled = True

        self.clicked.connect(self._on_index_clicked)
        self.doubleClicked.connect(self._on_index_double_clicked)

    # ============================================================================================================
    # OVERRIDES
    # ============================================================================================================

    def startDrag(self, event):
        """
        Overrides bae QListView startDrag function
        :param event: QEvent
        """

        if not self.dragEnabled():
            return

        if self._drag_start_pos and hasattr(event, 'pos'):
            item = self.item_at(event.pos())
            if item and item.drag_enabled():
                self._drag_start_index = self.indexAt(event.pos())
                point = self._drag_start_pos - event.pos()
                dt = self.drag_threshold()
                if point.x() > dt or point.y() > dt or point.x(
                ) < -dt or point.y() < -dt:
                    items = self.selected_items()
                    mime_data = self.mime_data(items)
                    pixmap = self._drag_pixmap(item, items)
                    hotspot = QPoint(pixmap.width() * 0.5,
                                     pixmap.height() * 0.5)
                    self._drag = QDrag(self)
                    self._drag.setPixmap(pixmap)
                    self._drag.setHotSpot(hotspot)
                    self._drag.setMimeData(mime_data)
                    self._drag.start(Qt.MoveAction)

    def endDrag(self):
        """
        Function that ends current drag
        """

        self._drag_start_pos = None
        self._drag_start_index = None
        if self._drag:
            del self._drag
            self._drag = None

    def dragEnterEvent(self, event):
        """
        Overrides bae QListView dragEnterEvent function
        :param event: QDragEvent
        """

        mimedata = event.mimeData()
        if (mimedata.hasText() or mimedata.hasUrls()) and self.drop_enabled():
            event.accept()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        """
        Overrides bae QListView dragMoveEvent function
        :param event: QDragEvent
        """

        mimedata = event.mimeData()
        if (mimedata.hasText() or mimedata.hasUrls()) and self.drop_enabled():
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        """
        Overrides bae QListView dropEvent function
        :param event: QDropEvent
        """

        item = self.item_at(event.pos())
        selected_items = self.selected_item()
        if selected_items and item:
            if self.tree_widget().is_sort_by_custom_order():
                self.move_items(selected_items, item)
            else:
                LOGGER.info(
                    'You can only re-order items when sorting by custom order')

        if item:
            item.drop_event(event)

        self.itemDropped.emit(event)

    # ============================================================================================================
    # OVERRIDES - MIXIN
    # ============================================================================================================

    def mousePressEvent(self, event):
        """
        Overrides base QListView mousePressEvent function
        :param event: QMouseEvent
        """

        item = self.item_at(event.pos())
        if not item:
            self.clearSelection()

        mixinview.ViewerViewWidgetMixin.mousePressEvent(self, event)
        if event.isAccepted():
            QListView.mousePressEvent(self, event)
            if item:
                # NOTE: This causes viewer tree widget selectionChanged signal to be emitted multiple times.
                # NOTE: This causes that item preview widgets are created twice when selecting an item in the viewer.
                # NOTE: For this reason, we block tree widgets signals before selecting the item
                with qt_contexts.block_signals(self.tree_widget()):
                    item.setSelected(True)

        self.endDrag()
        self._drag_start_pos = event.pos()

        is_left_button = self.mouse_press_button() == Qt.LeftButton
        is_item_draggable = item and item.drag_enabled()
        is_selection_empty = not self.selected_items()

        if is_left_button and (is_selection_empty or not is_item_draggable):
            self.rubber_band_start_event(event)

    def mouseMoveEvent(self, event):
        """
        Overrides base QListView mouseMoveEvent function
        :param event: QMouseEvent
        """

        if not self.is_dragging_items():
            is_left_button = self.mouse_press_button() == Qt.LeftButton
            if is_left_button and self.rubber_band().isHidden(
            ) and self.selected_items():
                self.startDrag(event)
            else:
                mixinview.ViewerViewWidgetMixin.mouseMoveEvent(self, event)
                QListView.mouseMoveEvent(self, event)
            if is_left_button:
                self.rubber_band_move_event(event)

    def mouseReleaseEvent(self, event):
        """
        Override base QListView mouseReleaseEvent function
        :param event: QMouseEvent
        """

        item = self.item_at(event.pos())
        items = self.selected_items()
        mixinview.ViewerViewWidgetMixin.mouseReleaseEvent(self, event)
        if item not in items:
            if event.button() != Qt.MidButton:
                QListView.mouseReleaseEvent(self, event)
        elif not items:
            QListView.mouseReleaseEvent(self, event)

        self.endDrag()
        self.rubber_band().hide()

    # ============================================================================================================
    # BASE
    # ============================================================================================================

    def scroll_to_item(self, item, pos=None):
        """
        Ensures that the item is visible
        :param item: LibraryItem
        :param pos: QPoint or None
        """

        index = self.index_from_item(item)
        pos = pos or QAbstractItemView.PositionAtCenter

        self.scrollTo(index, pos)

    # ============================================================================================================
    # TREE WIDGET
    # ============================================================================================================

    def tree_widget(self):
        """
        Return the tree widget that contains the items
        :return: LibraryTreeWidget
        """

        return self._tree_widget

    def set_tree_widget(self, tree_widget):
        """
        Set the tree widget that contains the items
        :param tree_widget: LibraryTreeWidget
        """

        self._tree_widget = tree_widget
        self.setModel(tree_widget.model())
        self.setSelectionModel(tree_widget.selectionModel())

    def items(self):
        """
        Return all the items
        :return: list(LibraryItem)
        """

        return self.tree_widget().items()

    def row_at(self, pos):
        """
        Returns the row for the given pos
        :param pos: QPoint
        :return:
        """

        return self.tree_widget().row_at(pos)

    def item_at(self, pos):
        """
        Returns a pointer to the item at the coordinates p
        The coordinates are relative to the tree widget's viewport
        :param pos: QPoint
        :return: LibraryItem
        """

        index = self.indexAt(pos)
        return self.item_from_index(index)

    def selected_item(self):
        """
        Returns the last selected non-hidden item
        :return: QTreeWidgetItem
        """

        return self.tree_widget().selected_item()

    def selected_items(self):
        """
        Returns a list of all selected non-hidden items
        :return: list(QTreeWidgetItem)
        """

        return self.tree_widget().selectedItems()

    def insert_item(self, row, item):
        """
        Inserts the item at row in the top level in the view
        :param row: int
        :param item: QTreeWidgetItem
        """

        self.tree_widget().insertTopLevelItem(row, item)

    def take_items(self, items):
        """
        Removes and returns the items from the view
        :param items: list(QTreeWidgetItem)
        :return: list(QTreeWidgetItem)
        """

        for item in items:
            row = self.tree_widget().indexOfTopLevelItem(item)
            self.tree_widget().takeTopLevelItem(row)

        return items

    def set_indexes_selected(self, indexes, value):
        """
        Set the selected state for the given indexes
        :param indexes: list(QModelIndex)
        :param value: bool
        """

        items = self.items_from_indexes(indexes)
        self.set_items_selected(items, value)

    def set_items_selected(self, items, value):
        """
        Sets the selected state for the given items
        :param items: list(LibraryItem)
        :param value: bool
        """

        with qt_contexts.block_signals(self.tree_widget()):
            try:
                for item in items:
                    item.setSelected(value)
            except Exception:
                LOGGER.error(str(traceback.format_exc()))

    def move_items(self, items, item_at):
        """
        Moves the given items to the position at the given row
        :param items: list(LibraryItem)
        :param item_at: LibraryItem
        """

        scroll_value = self.verticalScrollBar().value()
        self.tree_widget().move_items(items, item_at)
        self.itemMoved.emit(items[-1])
        self.verticalScrollBar().setValue(scroll_value)

    def index_from_item(self, item):
        """
        Returns QModelIndex associated with the given item
        :param item: LibraryItem
        :return: QModelIndex
        """

        return self.tree_widget().indexFromItem(item)

    def item_from_index(self, index):
        """
        Return a pointer to the LibraryItem associated with the given model index
        :param index: QModelIndex
        :return: LibraryItem
        """

        return self.tree_widget().itemFromIndex(index)

    def items_from_urls(self, urls):
        """
        Returns items from the given URL objects
        :param urls: list(QUrl)
        :return: DataItem
        """

        items = list()
        for url in urls:
            item = self.item_from_url(url)
            if item:
                items.append(item)

        return items

    def item_from_url(self, url):
        """
        Returns the item from the given url object
        :param url: QUrl
        :return: DataItem
        """

        return self.item_from_path(url.path())

    def items_from_paths(self, paths):
        """
        Returns the items from the given paths
        :param paths: list(str)
        :return: QUrl
        """

        items = list()
        for path in paths:
            item = self.item_from_path(path)
            if item:
                items.append(item)

        return items

    def item_from_path(self, path):
        """
        Returns the item from the given path
        :param path: str
        :return: DataItem
        """

        for item in self.items():
            item_path = item.url().path()
            if item_path and path == item_path:
                return item

        return None

    # ============================================================================================================
    # DRAG & DROP
    # ============================================================================================================

    def drop_enabled(self):
        """
        Returns whether drop functionality is enabled or not
        :return: bool
        """

        return self._drop_enabled

    def set_drop_enabled(self, flag):
        """
        Sets whether drop functionality is enabled or not
        :param flag: bool
        """

        self._drop_enabled = flag

    def drag_threshold(self):
        """
        Returns current drag threshold
        :return: float
        """

        return self.DEFAULT_DRAG_THRESHOLD

    def is_dragging_items(self):
        """
        Returns whether the user is currently dragging items or not
        :return: bool
        """

        return bool(self._drag)

    def mime_data(self, items):
        """
        Returns drag mime data
        :param items: list(LibraryItem)
        :return: QMimeData
        """

        mimedata = QMimeData()
        urls = [item.url() for item in items]
        text = '\n'.join([item.mime_text() for item in items])
        mimedata.setUrls(urls)
        mimedata.setText(text)

        return mimedata

    # ============================================================================================================
    # RUBBER BAND
    # ============================================================================================================

    def create_rubber_band(self):
        """
        Creates a new instance of the selection rubber band
        :return: QRubberBand
        """

        rubber_band = QRubberBand(QRubberBand.Rectangle, self)
        palette = QPalette()
        color = self.rubber_band_color()
        palette.setBrush(QPalette.Highlight, QBrush(color))
        rubber_band.setPalette(palette)

        return rubber_band

    def rubber_band(self):
        """
        Retursn the selection rubber band for this widget
        :return: QRubberBand
        """

        if not self._rubber_band:
            self.setSelectionRectVisible(False)
            self._rubber_band = self.create_rubber_band()

        return self._rubber_band

    def rubber_band_color(self):
        """
        Returns the rubber band color for this widget
        :return: QColor
        """

        return self._rubber_band_color

    def set_rubber_band_color(self, color):
        """
        Sets the color for the rubber band
        :param color: QColor
        """

        self._rubber_band = None
        self._rubber_band_color = color

    def rubber_band_start_event(self, event):
        """
        Triggered when the user presses an empty area
        :param event: QMouseEvent
        """

        self._rubber_band_start_pos = event.pos()
        rect = QRect(self._rubber_band_start_pos, QSize())
        rubber_band = self.rubber_band()
        rubber_band.setGeometry(rect)
        rubber_band.show()

    def rubber_band_move_event(self, event):
        """
        Triggered when the user moves the mouse over the current viewport
        :param event: QMouseEvent
        """

        if self.rubber_band() and self._rubber_band_start_pos:
            rect = QRect(self._rubber_band_start_pos, event.pos())
            rect = rect.normalized()
            self.rubber_band().setGeometry(rect)

    # ============================================================================================================
    # INTERNAL
    # ============================================================================================================

    def _drag_pixmap(self, item, items):
        """
        Internal function that shows the pixmap for the given item during drag operation
        :param item: LibraryItem
        :param items: list(LibraryItem)
        :return: QPixmap
        """

        rect = self.visualRect(self.index_from_item(item))
        pixmap = QPixmap()
        pixmap = pixmap.grabWidget(self, rect)
        if len(items) > 1:
            custom_width = 35
            custom_padding = 5
            custom_text = str(len(items))
            custom_x = pixmap.rect().center().x() - float(custom_width * 0.5)
            custom_y = pixmap.rect().top() + custom_padding
            custom_rect = QRect(custom_x, custom_y, custom_width, custom_width)
            painter = QPainter(pixmap)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setPen(Qt.NoPen)
            painter.setBrush(self.viewer().background_selected_color())
            painter.drawEllipse(custom_rect.center(),
                                float(custom_width * 0.5),
                                float(custom_width * 0.5))
            font = QFont('Serif', 12, QFont.Light)
            painter.setFont(font)
            painter.setPen(self.viewer().text_selected_color())
            painter.drawText(custom_rect, Qt.AlignCenter, str(custom_text))

        return pixmap

    # ============================================================================================================
    # CALLBACKS
    # ============================================================================================================

    def _on_index_clicked(self, index):
        """
        Callback function that is called when the user clicks on an item
        :param index: QModelIndex
        """

        item = self.item_from_index(index)
        item.clicked()
        self.set_items_selected([item], True)
        self.itemClicked.emit(item)

    def _on_index_double_clicked(self, index):
        """
        Callback function that is called when the user double clicks on an item
        :param index: QModelIndex
        """

        item = self.item_from_index(index)
        self.set_items_selected([item], True)
        item.double_clicked()
        self.itemDoubleClicked.emit(item)