簡體   English   中英

Qt:防止拖放子部件並顯示禁止 cursor

[英]Qt: Prevent drag-and-drop on child widgets and show forbidden cursor

我想在一些自定義小部件中啟用拖放功能,以允許在布局中重新排序和重新排列小部件。 基本功能正在運行,但我想防止將小部件放在自身或自身的孩子上。 這在dropEvent方法中很簡單,但我找不到在拖動時也顯示“禁止”cursor 的方法,以便用戶知道不允許放置。

下面的例子展示了一個測試實現,其中“一”到“五”的小部件可以拖放(不會發生重新排列,只會在終端上打印一條消息,您可能需要按Ctrl或其他東西來啟動一個拖)。 問題行是dragEnterEvent方法中的第一個ev.accept() 通過接受該事件,cursor 顯示在“允許的”state(對我來說是一只手)中。 例如,嘗試將“一”拖放到“三”上似乎是允許的,但不會發生任何事情。 但是,忽略事件會導致事件傳播到父級,因此在這種情況下,將“三”拖放到“三”上會導致“一”得到下降。 設置WA_NoMousePropagation屬性似乎沒有任何區別。

所以,我需要的是一種“接受”事件的方法,這樣它就不會傳播到父級,但仍然顯示“禁止”cursor,就好像沒有人接受這個事件一樣。 有任何想法嗎?

#!/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_() )

可能最簡單的方法是始終在dragEnterEvent中接受事件,並且在事件源是self的祖先時在dragMoveEvent中忽略它,即

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()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM