在我的示例中,我希望用户能够在所有类型的系列之间拖放子项。但是,我想限制用户无法将某个人拖放到另一个人身上,或者将某个人作为根对象放弃,这反过来会使其成为一个家庭。
我已经非常接近了。但是有一个bug。如果用户将某个人拖动到一个家庭,将鼠标悬停在家庭上方的光标上,如果您小心地将光标移动到该系列的最顶端,您将看到该指示器,允许您放下该项目以便#&# 39;高于家庭,不属于家庭。代码测试为true并允许用户成功删除此人。结果将该人置于家庭之外。下图显示了我的意思。
代码主要集中在第75-125行之间。
# Imports
# ------------------------------------------------------------------------------
import sys
from PySide import QtCore, QtGui
# Class Object
# ------------------------------------------------------------------------------
class Person():
def __init__(self, name="", age=0):
self.name = name
self.age = age
class Family():
def __init__(self, name=""):
self.name = name
DROP_VALIDATION_DICT = {
Person : ( Family, None ),
Family : ( None, ),
}
# Custom QTreeWidgetItem
# ------------------------------------------------------------------------------
class CustomTreeNode( QtGui.QTreeWidgetItem ):
def __init__( self, parent, data ):
super( CustomTreeNode, self ).__init__( parent )
self.data = data
@property
def data(self):
return self._data
@data.setter
def data(self, value):
self._data = value
self.setText( 0, self.data.name )
def update(self):
if self.data:
self.setText(0, self.data.name)
# Custom QTreeWidgetItem
# ------------------------------------------------------------------------------
class CustomTreeWidget( QtGui.QTreeWidget ):
def __init__(self, parent=None):
QtGui.QTreeWidget.__init__(self, parent)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.setItemsExpandable(True)
self.setAnimated(True)
self.setDragEnabled(True)
self.setDropIndicatorShown(True)
self.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.setAlternatingRowColors(True)
# self._dragroot = self.itemRootIndex() # added
# signals
self.itemExpanded.connect(self.on_item_expanded)
self.itemCollapsed.connect(self.on_item_collapsed)
def set_headers(self, headers=[]):
self.setColumnCount( len(headers) )
self.setHeaderLabels( headers )
def keyPressEvent(self, event):
if (event.key() == QtCore.Qt.Key_Escape and
event.modifiers() == QtCore.Qt.NoModifier):
self.clearSelection()
else:
QtGui.QTreeWidget.keyPressEvent(self, event)
def mousePressEvent(self, event):
item = self.itemAt(event.pos())
if item is None:
self.clearSelection()
QtGui.QTreeWidget.mousePressEvent(self, event)
def on_item_expanded(self):
key_mod = QtGui.QApplication.keyboardModifiers()
if key_mod == QtCore.Qt.ControlModifier:
self.expandAll()
def on_item_collapsed(self):
key_mod = QtGui.QApplication.keyboardModifiers()
if key_mod == QtCore.Qt.ControlModifier:
self.collapseAll()
# drag-n-drop with constraints
def dragEnterEvent(self, event):
item = self.itemAt(event.pos())
if item is not None and ( isinstance(item.data, Family) ):
# if event.mimeData().hasUrls():
event.acceptProposedAction()
else:
super(CustomTreeWidget, self).dragEnterEvent(event)
def dragMoveEvent(self, event):
print "testing"
print "moving..."
item = self.itemAt(event.pos())
rect = self.visualItemRect(item)
# QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.ForbiddenCursor))
print rect, event.pos()
print "Cursor: ", event.pos().y(), event.pos().y()+2
if event.pos().y() < rect.y()+2:
print "not inside"
else:
print "inside"
super(CustomTreeWidget, self).dragMoveEvent(event)
def dropEvent(self, event):
item = self.itemAt(event.pos())
if item is not None and ( isinstance(item.data, Family) ):
super(CustomTreeWidget,self).dropEvent(event)
else:
print "ignored"
event.setDropAction(QtCore.Qt.IgnoreAction)
# UI
# ------------------------------------------------------------------------------
class ExampleWidget(QtGui.QWidget):
def __init__( self, parent=None ):
super(ExampleWidget, self).__init__()
self.initUI()
def initUI(self):
# widgets
self.treewidget = CustomTreeWidget()
self.treewidget.set_headers( ["items"] )
self.btn_add_family_tree_node = QtGui.QPushButton("Add Family")
self.btn_add_person_tree_node = QtGui.QPushButton("Add Person")
# layout
self.mainLayout = QtGui.QGridLayout(self)
self.mainLayout.addWidget(self.btn_add_family_tree_node, 0,0)
self.mainLayout.addWidget(self.btn_add_person_tree_node, 0,1)
self.mainLayout.addWidget(self.treewidget, 1,0,1,3)
# signals
self.btn_add_family_tree_node.clicked.connect(self.add_family_tree_node_clicked)
self.btn_add_person_tree_node.clicked.connect(self.add_person_tree_node_clicked)
# display
self.resize(300, 400)
self.show()
self.center_window(self, True)
# test data
self.add_test_families("Roberstons", ["Kevin", "Mindy", "Riley"])
self.add_test_families("Rodriguez", ["Matt", "Kim", "Stephanie"])
def add_test_families(self, name, children):
family = Family( name )
node = CustomTreeNode( self.treewidget, family )
node.setExpanded(True)
# # disable drag and drop flags
# # QtCore.Qt.ItemIsDragEnabled
# flags = QtCore.Qt.ItemIsSelectable | \
# QtCore.Qt.ItemIsUserCheckable | \
# QtCore.Qt.ItemIsEnabled | \
# QtCore.Qt.ItemIsDropEnabled
# node.setFlags(flags)
for i in xrange(len(children)):
person = Person( children[i] )
CustomTreeNode( node, person )
# Functions
# --------------------------------------------------------------------------
def add_family_tree_node_clicked(self):
print "Created new family!"
roots = [self.treewidget]
text, ok = QtGui.QInputDialog.getText(self, 'Input Dialog', 'Enter family name:')
if ok:
for root in roots:
family = Family( text )
node = CustomTreeNode( root, family )
node.setExpanded(True)
self.treewidget.itemSelectionChanged.emit()
def add_person_tree_node_clicked(self):
print "Created new person!"
roots = self.treewidget.selectedItems()
text, ok = QtGui.QInputDialog.getText(self, 'Input Dialog', 'Enter your name:')
if ok:
for root in roots:
person = Person( text )
node = CustomTreeNode( root, person )
node.setExpanded(True)
self.treewidget.itemSelectionChanged.emit()
def center_window(self, window , cursor=False):
qr = window.frameGeometry()
cp = QtGui.QCursor.pos() if cursor else QtGui.QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
window.move(qr.topLeft())
# __name__
# ------------------------------------------------------------------------------
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = ExampleWidget()
sys.exit(app.exec_())