Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oglops/5133d5d9ef9b32410c81 to your computer and use it in GitHub Desktop.
Save oglops/5133d5d9ef9b32410c81 to your computer and use it in GitHub Desktop.
修复不能拖放到本行其他列,以及拖到其他行其他列的时候,没有画框的问题
#!/usr/bin/env python2
# problems:
# dragging 3 items, two nested, one root level, only 2 of 3 are moved if left mouse button dragging , MMB is fine
import os
import sys
import re
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import Qt, QString
class MyTreeView(QtGui.QTreeView):
def __init__(self, parent=None):
super(MyTreeView, self).__init__(parent)
self.dropIndicatorRect = QtCore.QRect()
def paintEvent(self, event):
painter = QtGui.QPainter(self.viewport())
self.drawTree(painter, event.region())
# in original implementation, it calls an inline function paintDropIndicator here
self.paintDropIndicator(painter)
def paintDropIndicator(self, painter):
if self.state() == QtGui.QAbstractItemView.DraggingState:
opt = QtGui.QStyleOption()
opt.init(self)
opt.rect = self.dropIndicatorRect
rect = opt.rect
brush = QtGui.QBrush(QtGui.QColor(Qt.black))
if rect.height() == 0:
pen = QtGui.QPen(brush, 1, QtCore.Qt.DashLine)
painter.setPen(pen)
painter.drawLine(rect.topLeft(), rect.topRight())
else:
pen = QtGui.QPen(brush, 1, QtCore.Qt.DashLine)
painter.setPen(pen)
painter.drawRect(rect)
class MyTreeWidget(QtGui.QTreeWidget, MyTreeView):
# def mouseMoveEvent(self, e):
# if self.state()==QtGui.QAbstractItemView.DraggingState:
# mimeData = self.model().mimeData(self.selectedIndexes())
# drag = QtGui.QDrag(self)
# drag.setMimeData(mimeData)
# drag.exec_(QtCore.Qt.MoveAction)
def startDrag(self, supportedActions):
listsQModelIndex = self.selectedIndexes()
if listsQModelIndex:
mimeData = QtCore.QMimeData()
dataQMimeData = self.model().mimeData(listsQModelIndex)
# if not dataQMimeData:
# return None
dragQDrag = QtGui.QDrag(self)
# dragQDrag.setPixmap(QtGui.QPixmap('test.jpg')) # <- For put your custom image here
dragQDrag.setMimeData(dataQMimeData)
defaultDropAction = QtCore.Qt.IgnoreAction
if ((supportedActions & QtCore.Qt.CopyAction) and (self.dragDropMode() != QtGui.QAbstractItemView.InternalMove)):
defaultDropAction = QtCore.Qt.CopyAction
dragQDrag.exec_(supportedActions, defaultDropAction)
def dragMoveEvent(self, event):
pos = event.pos()
item = self.itemAt(pos)
if item:
index = self.indexFromItem(item) # this always get the default 0 column index
rect = self.visualRect(index)
rect_left = self.visualRect(index.sibling(index.row(), 0))
# rect_right = self.visualRect(index.sibling(index.row(), self.columnCount() - 1))
rect_right = self.visualRect(index.sibling(index.row(), self.header().logicalIndex(self.columnCount() - 1))) # in case section has been moved
self.dropIndicatorPosition = self.position(event.pos(), rect, index)
if self.dropIndicatorPosition == self.AboveItem:
self.dropIndicatorRect = QtCore.QRect(rect_left.left(), rect_left.top(), rect_right.right() - rect_left.left(), 0)
event.accept()
elif self.dropIndicatorPosition == self.BelowItem:
self.dropIndicatorRect = QtCore.QRect(rect_left.left(), rect_left.bottom(), rect_right.right() - rect_left.left(), 0)
event.accept()
elif self.dropIndicatorPosition == self.OnItem:
self.dropIndicatorRect = QtCore.QRect(rect_left.left(), rect_left.top(), rect_right.right() - rect_left.left(), rect.height())
event.accept()
else:
self.dropIndicatorRect = QtCore.QRect()
self.model().setData(index, self.dropIndicatorPosition, Qt.UserRole)
# self.setState(QtGui.QAbstractItemView.DraggingState)
# This is necessary or else the previously drawn rect won't be erased
self.viewport().update()
def dropEvent(self, event):
pos = event.pos()
item = self.itemAt(pos)
if item is self.currentItem():
QtGui.QTreeWidget.dropEvent(self, event)
event.accept()
return
if item:
index = self.indexFromItem(item)
self.model().setData(index, 0, Qt.UserRole)
if event.source == self and event.dropAction() == Qt.MoveAction or self.dragDropMode() == QtGui.QAbstractItemView.InternalMove:
topIndex = QtCore.QModelIndex()
col = -1
row = -1
l = [event, row, col, topIndex]
if self.dropOn(l):
event, row, col, topIndex = l
idxs = self.selectedIndexes()
indexes = []
existing_rows = set()
for i in idxs:
if i.row() not in existing_rows:
indexes.append(i)
existing_rows.add(i.row())
if topIndex in indexes:
return
dropRow = self.model().index(row, col, topIndex)
taken = []
indexes_reverse = indexes[:]
indexes_reverse.reverse()
i = 0
for index in indexes_reverse:
parent = self.itemFromIndex(index)
if not parent or not parent.parent():
# if not parent or not isinstance(parent.parent(),QtGui.QTreeWidgetItem):
taken.append(self.takeTopLevelItem(index.row()))
else:
taken.append(parent.parent().takeChild(index.row()))
i += 1
# break
taken.reverse()
for index in indexes:
if row == -1:
if topIndex.isValid():
parent = self.itemFromIndex(topIndex)
parent.insertChild(parent.childCount(), taken[0])
taken = taken[1:]
else:
self.insertTopLevelItem(self.topLevelItemCount(), taken[0])
taken = taken[1:]
else:
r = dropRow.row() if dropRow.row() >= 0 else row
if topIndex.isValid():
parent = self.itemFromIndex(topIndex)
parent.insertChild(min(r, parent.childCount()), taken[0])
taken = taken[1:]
else:
self.insertTopLevelItem(min(r, self.topLevelItemCount()), taken[0])
taken = taken[1:]
event.accept()
QtGui.QTreeWidget.dropEvent(self, event)
self.expandAll()
def position(self, pos, rect, index):
r = QtGui.QAbstractItemView.OnViewport
# margin*2 must be smaller than row height, or the drop onItem rect won't show
margin = 10
if pos.y() - rect.top() < margin:
r = QtGui.QAbstractItemView.AboveItem
elif rect.bottom() - pos.y() < margin:
r = QtGui.QAbstractItemView.BelowItem
# this rect is always the first column rect
# elif rect.contains(pos, True):
elif pos.y() - rect.top() > margin and rect.bottom() - pos.y() > margin:
r = QtGui.QAbstractItemView.OnItem
return r
def dropOn(self, l):
event, row, col, index = l
root = self.rootIndex()
if self.viewport().rect().contains(event.pos()):
index = self.indexAt(event.pos())
if not index.isValid() or not self.visualRect(index).contains(event.pos()):
index = root
if index != root:
dropIndicatorPosition = self.position(event.pos(), self.visualRect(index), index)
if self.dropIndicatorPosition == self.AboveItem:
print 'dropon above'
row = index.row()
col = index.column()
index = index.parent()
elif self.dropIndicatorPosition == self.BelowItem:
print 'dropon below'
row = index.row() + 1
col = index.column()
index = index.parent()
elif self.dropIndicatorPosition == self.OnItem:
print 'dropon onItem'
pass
elif self.dropIndicatorPosition == self.OnViewport:
pass
else:
pass
else:
self.dropIndicatorPosition = self.OnViewport
l[0], l[1], l[2], l[3] = event, row, col, index
# if not self.droppingOnItself(event, index):
return True
class TheUI(QtGui.QDialog):
def __init__(self, args=None, parent=None):
super(TheUI, self).__init__(parent)
self.layout1 = QtGui.QVBoxLayout(self)
treeWidget = MyTreeWidget()
treeWidget.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
# treeWidget.setSelectionRectVisible(True)
button1 = QtGui.QPushButton('Add')
button2 = QtGui.QPushButton('Add Child')
self.layout1.addWidget(treeWidget)
self.layout2 = QtGui.QHBoxLayout()
self.layout2.addWidget(button1)
self.layout2.addWidget(button2)
self.layout1.addLayout(self.layout2)
treeWidget.setHeaderHidden(True)
self.treeWidget = treeWidget
self.button1 = button1
self.button2 = button2
self.button1.clicked.connect(lambda *x: self.addCmd())
self.button2.clicked.connect(lambda *x: self.addChildCmd())
HEADERS = ("script", "chunksize", "mem")
self.treeWidget.setHeaderLabels(HEADERS)
self.treeWidget.setColumnCount(len(HEADERS))
self.treeWidget.setColumnWidth(0, 160)
self.treeWidget.header().show()
self.treeWidget.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.treeWidget.setStyleSheet('''
QTreeView {
show-decoration-selected: 1;
}
QTreeView::item:hover {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #e7effd, stop: 1 #cbdaf1);
}
QTreeView::item:selected:active{
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #6ea1f1, stop: 1 #567dbc);
}
QTreeView::item:selected:!active {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #6b9be8, stop: 1 #577fbf);
}
''')
self.resize(500, 500)
for i in xrange(6):
item = self.addCmd(i)
if i in (3, 4):
self.addChildCmd()
if i == 4:
self.addCmd('%s-2' % i, parent=item)
self.treeWidget.expandAll()
self.setStyleSheet("QTreeWidget::item{ height: 30px; }")
def addChildCmd(self):
parent = self.treeWidget.currentItem()
self.addCmd(parent=parent)
self.treeWidget.setCurrentItem(parent)
def addCmd(self, i=None, parent=None):
'add a level to tree widget'
root = self.treeWidget.invisibleRootItem()
if not parent:
parent = root
if i is None:
if parent == root:
i = self.treeWidget.topLevelItemCount()
else:
i = str(parent.text(0))[7:]
i = '%s-%s' % (i, parent.childCount() + 1)
item = QtGui.QTreeWidgetItem(parent, ['script %s' % i, '1', '150'])
self.treeWidget.setCurrentItem(item)
self.treeWidget.expandAll()
return item
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
gui = TheUI()
gui.show()
app.exec_()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment