どうも!たっくんです。
今回は、QListWidgetをカスタマイズして、アイテムをD&Dで自由に移動する方法のご紹介です!(`・ω・´)ゞ
クラスの骨格
QListWidgetを継承して、カスタマイズしたクラスを用意していきます!D&Dが受け付けられるように「setAcceptDrops」を設定します。また、D&Dの開始処理のためにインスタンス変数「__startPos」を用意します。
from PySide import QtCore, QtGui class DraggableList(QtGui.QListWidget): def __init__(self, *args, **kwargs): super(DraggableList, self).__init__(*args, **kwargs) self.setAcceptDrops(True) self.__startPos = QtCore.QPoint()
マウスがクリックされた時の処理
「mousePressEvent」をオーバーライドし、ListWidgetでマウスがクリックされた時の座標を保存します。
from PySide import QtCore, QtGui class DraggableList(QtGui.QListWidget): def __init__(self, *args, **kwargs): super(DraggableList, self).__init__(*args, **kwargs) self.setAcceptDrops(True) self.__startPos = QtCore.QPoint() def mousePressEvent(self, event): self.__startPos = event.pos() super(DraggableList, self).mousePressEvent(event)
Drag中の処理
「mouseMoveEvent」をオーバーライドし、ドラック中の動作をカスタマイズしていきます。詳しくは、各行のコメントをご覧ください(`・ω・´)ゞ
from PySide import QtCore, QtGui class DraggableList(QtGui.QListWidget): def __init__(self, *args, **kwargs): super(DraggableList, self).__init__(*args, **kwargs) self.setAcceptDrops(True) self.__startPos = QtCore.QPoint() def mousePressEvent(self, event): self.__startPos = event.pos() super(DraggableList, self).mousePressEvent(event) def mouseMoveEvent(self, event): # Dragを開始した地点と、今マウスがある地点の距離を求める distance = (event.pos() - self.__startPos).manhattanLength() # 5px以上動いたら、ドラッグしたとみなす。 # これをやらないと、ただ選択したいだけでもD&Dになってしまう。 if distance >= 5: # 現在のアイテムを取得する item = self.currentItem() if item: # QMimeDataに、アイテムのテキストを設定する mimeData = QtCore.QMimeData() mimeData.setText(item.text()) # Dragオブジェクトを作成して、QMimeDataを設定する drag = QtGui.QDrag(self) drag.setMimeData(mimeData) # Dragが終了したとき、正常にMoveActionになったか確認 if drag.exec_(QtCore.Qt.MoveAction) == QtCore.Qt.MoveAction: # アイテムが移動先にできてるので、元のリストから削除する self.takeItem(self.currentRow())
D&Dの許可
D&Dを許可するように「dragEnterEvent」「dragMoveEvent」で、各イベントを「accept」します。D&Dに条件を設けたい場合は、if文で分岐させると良いかと思います!(`・ω・´)ゞ
from PySide import QtCore, QtGui class DraggableList(QtGui.QListWidget): def __init__(self, *args, **kwargs): super(DraggableList, self).__init__(*args, **kwargs) self.setAcceptDrops(True) self.__startPos = QtCore.QPoint() def mousePressEvent(self, event): self.__startPos = event.pos() super(DraggableList, self).mousePressEvent(event) def mouseMoveEvent(self, event): distance = (event.pos() - self.__startPos).manhattanLength() if distance >= 5: item = self.currentItem() if item: mimeData = QtCore.QMimeData() mimeData.setText(item.text()) drag = QtGui.QDrag(self) drag.setMimeData(mimeData) if drag.exec_(QtCore.Qt.MoveAction) == QtCore.Qt.MoveAction: self.takeItem(self.currentRow()) def dragEnterEvent(self, event): event.accept() def dragMoveEvent(self, event): event.accept()
Dropの処理
Dropの処理では「QMimeData」にテキスト情報がある場合は、ListWidgetにアイテムを追加するようにします。
from PySide import QtCore, QtGui class DraggableList(QtGui.QListWidget): def __init__(self, *args, **kwargs): super(DraggableList, self).__init__(*args, **kwargs) self.setAcceptDrops(True) self.__startPos = QtCore.QPoint() def mousePressEvent(self, event): self.__startPos = event.pos() super(DraggableList, self).mousePressEvent(event) def mouseMoveEvent(self, event): distance = (event.pos() - self.__startPos).manhattanLength() if distance >= 5: item = self.currentItem() if item: mimeData = QtCore.QMimeData() mimeData.setText(item.text()) drag = QtGui.QDrag(self) drag.setMimeData(mimeData) if drag.exec_(QtCore.Qt.MoveAction) == QtCore.Qt.MoveAction: self.takeItem(self.currentRow()) def dragEnterEvent(self, event): event.accept() def dragMoveEvent(self, event): event.accept() def dropEvent(self, event): if event.mimeData().hasText(): self.addItem(event.mimeData().text()) event.accept()
実験!
以下のコードで、実験してみます。
widget = QtGui.QWidget() widget.setWindowFlags(QtCore.Qt.Window) widget.resize(640, 240) widget.setLayout(QtGui.QHBoxLayout()) layout = widget.layout() listA = DraggableList(widget) listA.addItem('A') listA.addItem('B') listA.addItem('C') layout.addWidget(listA) listB = DraggableList(widget) layout.addWidget(listB) widget.show()
左右のアイテムをD&Dすると、アイテムを自由に移動することができます!