[PySide] GraphicsViewのテスト その2 ドラッグ&ドロップで並び替え
前回のを少し弄って、ドラッグ&ドロップで入れ替え出来るようにしてみました。
以下ソース
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# -*- coding: utf-8 -*- import sys import random from PySide import QtCore, QtGui class SimpleItem(QtGui.QGraphicsItem, QtCore.QObject): itemsize_x = 100 itemsize_y = 100 penWidth = 1.0 num = 0 movePosition = QtCore.Signal(int, int) def __init__(self, num=0): QtGui.QGraphicsItem.__init__(self) QtCore.QObject.__init__(self) self.num = num self.setPosition() self.R = random.randint(0, 255) self.G = random.randint(0, 255) self.B = random.randint(0, 255) def boundingRect(self): return QtCore.QRectF(0, 0, self.itemsize_x + self.penWidth, self.itemsize_y + self.penWidth) def setPosition(self): """ 元のポジションに戻す """ now = self.scenePos() mov_x = (self.num % 5) * self.itemsize_x mov_y = (self.num / 5) * self.itemsize_y mov_x = mov_x + (-1 * now.x()) mov_y = mov_y + (-1 * now.y()) transform = self.transform() transform *= QtGui.QTransform().translate(mov_x, mov_y) self.setTransform(transform) def paint(self, painter, option, widget): painter.setBrush(QtGui.QColor(self.R, self.G, self.B)) rect = QtCore.QRectF(0, 0, self.itemsize_x + self.penWidth, self.itemsize_y + self.penWidth) painter.drawRoundedRect(rect, 0, 0) def shape(self): path = QtGui.QPainterPath() path.addEllipse(0, 0, self.itemsize_x, self.itemsize_y) return path def mousePressEvent(self, event): self.cPos = event.scenePos() self.__pressedButton = event.button() if self.__pressedButton == QtCore.Qt.RightButton: cursorShape = QtCore.Qt.SizeAllCursor else: cursorShape = QtCore.Qt.ClosedHandCursor QtGui.qApp.setOverrideCursor(QtGui.QCursor(cursorShape)) def mouseReleaseEvent(self, event): self.cPos = None self.__pressedButton = None QtGui.qApp.restoreOverrideCursor() # 移動処理 mov = self.scenePos() x = int(mov.x() / 100) y = int(mov.y() / 100) # 元の位置に戻す self.setPosition() super(SimpleItem, self).mouseReleaseEvent(event) self.movePosition.emit(self.num, x + (y * 5)) def mouseMoveEvent(self, event): if not self.cPos: return # 描画位置を変更 cur = event.scenePos() value = cur - self.cPos self.cPos = cur transform = self.transform() transform *= QtGui.QTransform().translate(value.x(), value.y()) # 変更を適用 self.setTransform(transform) class graphicView(QtGui.QGraphicsView): nodes = [] num = 0 def __init__(self): QtGui.QGraphicsView.__init__(self) self.scene = QtGui.QGraphicsScene(self) self.scene.setSceneRect(0, 0, 400, 400) self.setScene(self.scene) def mouseDoubleClickEvent(self, event): node = SimpleItem(self.num) self.scene.addItem(node) self.nodes.append(node) self.num += 1 node.movePosition.connect(self.movePosition) def movePosition(self, before, after): """ ドラッグ&ドロップしたときに、中身を入れ替える """ # 元と先が同じ場合はなにもしない if before == after: return # 全体数より多い場合は、最大値にする if after > len(self.nodes): after = len(self.nodes) - 1 pop = self.nodes.pop(before) self.nodes.insert(after, pop) for i in range(len(self.nodes)): self.nodes[i].num = i self.nodes[i].setPosition() if __name__ == "__main__": app = QtGui.QApplication(sys.argv) ui = QtGui.QDialog() layout = QtGui.QVBoxLayout() ui.setLayout(layout) widget = graphicView() layout.addWidget(widget) ui.show() sys.exit(app.exec_()) |
結構なパワープレイで実装、たぶんもっと真っ当なやり方あるんじゃね?
って気がしますが、とりあえず動けばOKってことで。
動作はこんな感じ。(Chromeだと、クリックしないと再生されない模様)
入れ替えがわかりやすいように、四角の色はランダムで変わるようにしてみました。
ドラッグ→ドロップすると、ドロップ後の位置をSignalでEmitしてあげる
さらに、そのEmit情報を受け取って、Listのソート処理→再描画という流れでやりたい事はできました。
困った・迷った点が2つ。
これは前にもやったのですが、Signalを実装するときには QtGui.QObject を継承しなければいけないところ。
継承しないとNGなのは覚えていたのですが、 __init__内で初期化しないといけないのを忘れていて
Emitの行でPythonが異常終了してしまう現象が発生して原因を探すのに時間がかかってしまいました。
もう1つが、入れ替え処理をするのをQGragphicsViewにEventを実装してみたら
Item側のEventが、View側のEventで上書きされてしまった所。
オブジェクトに対して個別に処理をしたい場合は、Item側にEventをつけてあげるってことでしょうかね。
とりあえずやり方は代替把握したので、次はタイルテクスチャツールに実装してみようかと思います。