Skip to content

Instantly share code, notes, and snippets.

@oglops
Created September 1, 2014 02:12
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oglops/2048c8642cb1fa44b4a3 to your computer and use it in GitHub Desktop.
Save oglops/2048c8642cb1fa44b4a3 to your computer and use it in GitHub Desktop.
drop items with itemWidgets by recreating them recursively in dropEvent
#!/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