Created
September 1, 2014 02:12
-
-
Save oglops/2048c8642cb1fa44b4a3 to your computer and use it in GitHub Desktop.
drop items with itemWidgets by recreating them recursively in dropEvent
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2 | |
import os | |
import sys | |
import re | |
from PyQt4 import QtGui, QtCore | |
from PyQt4.QtCore import Qt, QString | |
class CommandWidget(QtGui.QDialog): | |
def __init__(self, parent=None, val=None): | |
super(CommandWidget, self).__init__() | |
self.layout = QtGui.QHBoxLayout(self) | |
browseBtn = ElideButton(parent) | |
browseBtn.setMinimumSize(QtCore.QSize(0, 25)) | |
browseBtn.setText(QString(val)) | |
browseBtn.setStyleSheet("text-align: left") | |
self.layout.addWidget(browseBtn) | |
self.browseBtn = browseBtn | |
self.browseBtn.clicked.connect(self.browseCommandScript) | |
self.browseBtn.setIconSize(QtCore.QSize(64, 64)) | |
def browseCommandScript(self): | |
script = QtGui.QFileDialog.getOpenFileName( | |
self, 'Select Script file', '/tmp/crap', "Executable Files (*)") | |
if script: | |
self._script = script | |
old_text = str(self.browseBtn.text()).strip() | |
old_text = re.search('^script [\d-]*', old_text).group() | |
self.browseBtn.setText(('%s %s' % (old_text, script))) | |
def clone(self): | |
clone = CommandWidget(val=str(self.browseBtn.text())) | |
# clone.browseBtn.setText(self.borwseBtn.text()) | |
return clone | |
class ElideButton(QtGui.QPushButton): | |
def __init__(self, parent=None): | |
super(ElideButton, self).__init__(parent) | |
font = self.font() | |
font.setPointSize(10) | |
self.setFont(font) | |
def paintEvent(self, event): | |
painter = QtGui.QStylePainter(self) | |
metrics = QtGui.QFontMetrics(self.font()) | |
elided = metrics.elidedText(self.text(), Qt.ElideRight, self.width()) | |
option = QtGui.QStyleOptionButton() | |
self.initStyleOption(option) | |
option.text = '' | |
painter.drawControl(QtGui.QStyle.CE_PushButton, option) | |
painter.drawText(self.rect(), Qt.AlignLeft | Qt.AlignVCenter, elided) | |
class MyTreeWidget(QtGui.QTreeWidget): | |
# 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 keyPressEvent(self, event): | |
'delete currently selected item' | |
QtGui.QTreeWidget.keyPressEvent(self, event) | |
key = event.key() | |
if self.currentItem(): | |
root = self.invisibleRootItem() | |
parent = self.currentItem().parent() or root | |
if key == Qt.Key_Delete: | |
parent.removeChild(self.currentItem()) | |
def dropEvent(self, event): | |
pos = event.pos() | |
item = self.itemAt(pos) | |
dragItem = self.currentItem() | |
if item: | |
index = self.indexFromItem(item) | |
self.model().setData(index, 0, Qt.UserRole) | |
if item is self.currentItem(): | |
QtGui.QTreeWidget.dropEvent(self, event) | |
event.accept() | |
return | |
self.recreate_widget(dragItem, event, for_drop=True) | |
# recursively recreate widgets because dragged item may have children | |
def recreate_widget_for_item(item): | |
# if item has children, call the function on each child | |
for i in range(item.childCount()): | |
self.recreate_widget(item.child(i), event) | |
# if current child has its own children, call function on each grandchild | |
if item.child(i).childCount(): | |
recreate_widget_for_item(item.child(i)) | |
# call the recursive funtion on current dragged item, so that all of its descendants are handled ( widgets recreated ) | |
recreate_widget_for_item(dragItem) | |
# self.expandAll() | |
self.updateGeometry() | |
def recreate_widget(self, dragItem, event=None, for_drop=False): | |
'recreate widget for given treeWidgetItem, this is an awkward workaround for using widgets on items with treeWidget but still want to modify the items in tree widget' | |
# recreate command widget | |
data = dragItem.data(0, Qt.UserRole + 1).toPyObject() | |
print 'in recreate_widget:', data | |
commandWidget = CommandWidget(self, val=data) | |
widgets = [commandWidget] | |
if for_drop: | |
# drop happens here by calling QTreeWidget class dropEvent | |
QtGui.QTreeWidget.dropEvent(self, event) | |
else: | |
# if this function is not called by drag'n'drop | |
# remove existing item widgets | |
# for i in range(len(widgets)): | |
# self.removeItemWidget(dragItem, i) | |
pass | |
self.setItemWidget(dragItem, 0, commandWidget) | |
# print 'recreated widgets:',commandWidget | |
# for index, widget in enumerate(widgets): | |
# self.setItemWidget(dragItem, index, widget) | |
# widget.treeWidgetItem = dragItem | |
dragItem.setExpanded(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[boxui_tree="true"]::item:selected { | |
border: 1px solid #567dbc; | |
} | |
QTreeView[boxui_tree="true"]::item { | |
border-bottom: 1px solid #FFF; | |
} | |
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.treeWidget.setUniformRowHeights(True) | |
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)).strip()[7:] | |
i = '%s-%s' % (i, parent.childCount() + 1) | |
# item = QtGui.QTreeWidgetItem(parent, ['script %s' % i, '1', '150']) | |
script = ' script %s' % i | |
item = QtGui.QTreeWidgetItem(parent, [script, '1', '150']) | |
commandWidget = CommandWidget(val=script) | |
self.treeWidget.setItemWidget(item, 0, commandWidget) | |
# set data on item | |
item.setData(0, Qt.UserRole + 1, script) | |
print 'set data', script, 'on item i', i | |
# store a reference on commandwidget, so when we altered it, we can update the data on item | |
commandWidget.treeWidgetItem = item | |
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