Created
August 26, 2014 15:45
-
-
Save oglops/01cc6ff1a09f0b3e890c to your computer and use it in GitHub Desktop.
enable drag and drop / extended selection at the same time by setting QAbstractItemView.DragSelectingState, set optimum dragging hotzone, fix single click after dragging selection, enable clicking checkbox toggle and keep selection after spacebar toggling
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
from PyQt4 import QtGui, QtCore | |
from PyQt4.QtCore import Qt, QString | |
import sys | |
import os | |
class ThumbListWidget(QtGui.QListWidget): | |
_rows_to_del = [] | |
def __init__(self, type, parent=None): | |
super(ThumbListWidget, self).__init__(parent) | |
self.setIconSize(QtCore.QSize(124, 124)) | |
self.setDragDropMode(QtGui.QAbstractItemView.DragDrop) | |
self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) | |
self.setAcceptDrops(True) | |
self._dropping = False | |
self.setSelectionRectVisible(True) | |
self.connect(self, QtCore.SIGNAL("dropped"), self.items_dropped) | |
def dragEnterEvent(self, event): | |
if event.mimeData().hasUrls(): | |
event.accept() | |
else: | |
super(ThumbListWidget, self).dragEnterEvent(event) | |
def dragMoveEvent(self, event): | |
if event.mimeData().hasUrls(): | |
event.setDropAction(QtCore.Qt.CopyAction) | |
event.accept() | |
else: | |
super(ThumbListWidget, self).dragMoveEvent(event) | |
def dropEvent(self, event): | |
if event.mimeData().hasUrls(): | |
event.setDropAction(QtCore.Qt.CopyAction) | |
event.accept() | |
links = [] | |
for url in event.mimeData().urls(): | |
links.append(str(url.toLocalFile())) | |
self.emit(QtCore.SIGNAL("dropped"), links) | |
else: | |
if event.source() is self: | |
event.setDropAction(QtCore.Qt.MoveAction) | |
else: | |
event.setDropAction(QtCore.Qt.CopyAction) | |
self._dropping = True | |
super(ThumbListWidget, self).dropEvent(event) | |
self._dropping = False | |
def rowsInserted(self, parent, start, end): | |
if self._dropping: | |
self.emit(QtCore.SIGNAL("dropped"), (start, end)) | |
super(ThumbListWidget, self).rowsInserted(parent, start, end) | |
def dataChanged(self, start, end): | |
if self._dropping: | |
for row in range(start.row(), end.row() + 1): | |
index = self.indexFromItem(self.item(row)) | |
shot = index.data().toString() | |
# print len(self.findItems(shot, Qt.MatchExactly)) | |
if len(self.findItems(shot, Qt.MatchExactly)) > 1: | |
self._rows_to_del.append(row) | |
self._rows_to_del.reverse() | |
for row in self._rows_to_del: | |
self.takeItem(row) | |
self._rows_to_del = [] | |
def items_dropped(self, arg): | |
start, end = arg | |
print range(start, end + 1) | |
for row in range(start, end + 1): | |
item = self.item(row) | |
item.setFlags(item.flags() | Qt.ItemIsUserCheckable) | |
item.setCheckState(Qt.Checked) | |
def keyPressEvent(self, event): | |
if event.key() == Qt.Key_Space: | |
if self.selectedItems(): | |
new_state = Qt.Unchecked if self.selectedItems()[0].checkState() else Qt.Checked | |
for item in self.selectedItems(): | |
if item.flags() & Qt.ItemIsUserCheckable: | |
# print 'set new state:', new_state | |
item.setCheckState(new_state) | |
self.viewport().update() | |
elif event.key() == Qt.Key_Delete: | |
for item in self.selectedItems(): | |
self.takeItem(self.row(item)) | |
def mousePressEvent(self, event): | |
item = self.selectedCheckStateItem(event.pos()) | |
if item: | |
new_state = Qt.Unchecked if item.checkState()==Qt.Checked else Qt.Checked | |
QtGui.QApplication.processEvents() | |
self.setSelectedCheckStates(new_state,item) | |
QtGui.QApplication.processEvents() | |
self.viewport().update() | |
else: | |
QtGui.QListWidget.mousePressEvent(self, event) | |
self.mousePressPos = (event.pos()) | |
def setSelectedCheckStates(self,state,click_item): | |
for item in self.selectedItems(): | |
if item is not click_item: | |
item.setCheckState(state) | |
def selectedCheckStateItem(self,pos): | |
item = self.itemAt(pos) | |
if item: | |
opt = QtGui.QStyleOptionButton() | |
opt.rect = self.visualItemRect(item) | |
rect = self.style().subElementRect(QtGui.QStyle.SE_ViewItemCheckIndicator, opt) | |
if item in self.selectedItems() and rect.contains(pos): | |
return item | |
return 0 | |
def mouseMoveEvent(self, event): | |
if (event.pos() - self.mousePressPos).manhattanLength() > QtGui.QApplication.startDragDistance(): | |
item = self.itemAt(event.pos()) | |
if item: | |
rect = self.visualItemRect(item) | |
# offset = 100 | |
char_width = QtGui.QFontMetricsF(QtGui.QFont(self.font())).width('a') | |
offset = len(item.data(0).toString()) * char_width | |
if rect.translated(offset, 0).contains(event.pos()): | |
self.setState(QtGui.QAbstractItemView.DragSelectingState) | |
QtGui.QListWidget.mouseMoveEvent(self, event) | |
class Dialog_01(QtGui.QMainWindow): | |
def __init__(self): | |
super(QtGui.QMainWindow, self).__init__() | |
self.listItems = {} | |
myQWidget = QtGui.QWidget() | |
myBoxLayout = QtGui.QVBoxLayout() | |
myQWidget.setLayout(myBoxLayout) | |
self.setCentralWidget(myQWidget) | |
self.listWidgetA = ThumbListWidget(self) | |
for i in range(12): | |
QtGui.QListWidgetItem('Item ' + str(i), self.listWidgetA) | |
all_items = self.listWidgetA.findItems(QString('*'), Qt.MatchWrap | Qt.MatchWildcard) | |
for item in all_items: | |
item.setFlags(item.flags() & ~Qt.ItemIsUserCheckable) | |
myBoxLayout.addWidget(self.listWidgetA) | |
self.listWidgetB = ThumbListWidget(self) | |
self.listWidgetA.setAcceptDrops(False) | |
myBoxLayout.addWidget(self.listWidgetB) | |
if __name__ == '__main__': | |
app = QtGui.QApplication(sys.argv) | |
dialog_1 = Dialog_01() | |
dialog_1.show() | |
dialog_1.resize(480, 320) | |
sys.exit(app.exec_()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment