Qt: предотвращение перетаскивания дочерних виджетов и отображение запрещенного курсора - PullRequest
0 голосов
/ 05 мая 2020

Я хочу включить функцию перетаскивания в некоторых пользовательских виджетах, чтобы разрешить переупорядочивание и перестановку виджетов в макете. Функциональность basi c работает, но я хотел бы предотвратить падение виджета на себя или на своего дочернего элемента. Это просто в методе dropEvent, но я не могу найти способ также отображать «запрещенный» курсор при перетаскивании, чтобы пользователь знал, что падение не будет разрешено.

В приведенном ниже примере показана тестовая реализация, в которой виджеты от «One» до «Five» можно перетаскивать (перестановки не произойдет, на терминале будет напечатано только сообщение, вам может потребоваться нажать Ctrl или что-то еще, чтобы начать перетаскивание ). Проблемная строка - это первая ev.accept() в методе dragEnterEvent. Приняв событие, курсор отображается в «разрешенном» состоянии (для меня хватка за руку). Например, попытка перетащить «Один» на «Три» выглядит как разрешенная, хотя ничего не произойдет. Однако игнорирование события приводит к тому, что событие распространяется на родительский объект, поэтому в этом случае перетаскивание «Три» на «Три» приводит к тому, что вместо этого «Один» получает отбрасывание. Установка атрибута WA_NoMousePropagation, похоже, не имеет никакого значения.

Итак, мне нужен способ «принять» событие, чтобы оно не передавалось на родительский элемент, но все равно отображалось «запрещенный» курсор, как будто событие никто не принял. Есть идеи?

#!/usr/bin/env python3

import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *


class WidgetMimeData(QtCore.QMimeData):
    def __init__(self, *args, **kwargs):
      super().__init__(*args, **kwargs)
      self.itemObject = None

    def hasFormat(self, mime):
        if (self.itemObject and (mime == 'widgetitem')):
            return True
        return super().hasFormat(mime)

    def setItem(self, obj):
        self.itemObject = obj

    def item(self):
        return self.itemObject


class DraggableWidget(QGroupBox):
    def __init__(self):
        QWidget.__init__(self)
        layout = QVBoxLayout()
        self.setLayout(layout)
        self.setAcceptDrops(True)

    def addWidget(self, widget):
        return self.layout().addWidget(widget)

    def mouseMoveEvent(self, ev):
        pixmap = QPixmap(self.size())
        pixmap.fill(QtCore.Qt.transparent)
        painter = QPainter()
        painter.begin(pixmap)
        painter.setOpacity(0.8)
        painter.drawPixmap(0, 0, self.grab())
        painter.end()
        drag = QDrag(self)
        mimedata = WidgetMimeData()
        mimedata.setItem(self)
        drag.setMimeData(mimedata)
        drag.setPixmap(pixmap)
        drag.setHotSpot(ev.pos())
        drag.exec_(QtCore.Qt.MoveAction)

    def dragEnterEvent(self, ev):
        item = ev.mimeData().item()
        if item.isAncestorOf(self):
          #ev.ignore()
          ev.accept()
        else:
          ev.accept()

    def dropEvent(self, ev):
        item = ev.mimeData().item()
        if not item.isAncestorOf(self):
          print('dropped on', self.layout().itemAt(0).widget().text())
        ev.accept()


class HelloWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        w1 = DraggableWidget()
        w1.addWidget(QLabel('One'))
        w2 = DraggableWidget()
        w2.addWidget(QLabel('Two'))
        w3 = DraggableWidget()
        w3.addWidget(QLabel('Three'))
        w4 = DraggableWidget()
        w4.addWidget(QLabel('Four'))
        w5 = DraggableWidget()
        w5.addWidget(QLabel('Five'))

        w1.addWidget(w3)
        w1.addWidget(w4)
        w2.addWidget(w5)

        layout = QVBoxLayout()
        layout.addWidget(w1)
        layout.addWidget(w2)
        layout.addStretch(1)

        centralWidget = QWidget(self)          
        centralWidget.setLayout(layout)
        self.setCentralWidget(centralWidget)   


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    mainWin = HelloWindow()
    mainWin.show()
    sys.exit( app.exec_() )

1 Ответ

1 голос
/ 05 мая 2020

Вероятно, самый простой способ - всегда принимать событие в dragEnterEvent, а в dragMoveEvent игнорировать его, если источник события является предком self, т.е.

def dragEnterEvent(self, ev):
    ev.accept()

def dragMoveEvent(self, ev):
    item = ev.source()
    if item.isAncestorOf(self):
        ev.ignore()

Игнорируя событие в dragMoveEvent вам также не понадобится проверка в dropEvent, вы можете просто выполнить

def dropEvent(self, ev):
    print('Dropped of', self.layout().itemAt(0).widget().text())
    ev.accept()
...