DragでWidgetを動かす方法!!

takkun
どうも!たっくんです!

ちょっとした遊び心や、ツールの使い勝手や欲しい情報へのアクセス性などのUXの為に、Widgetが動かせたりするといいなーっと思ったことございませんか!?

そこで、PySideでマウスのイベントを上書きして、Widgetを動かせるようにする方法をご紹介したいと思います!(`・ω・´)ゞ

Classの骨格

サンプルの紹介として、見た目のわかりやすいQPushButtonを継承してDragで動くようにしていきたいと思います!

from PySide import QtCore, QtGui

class DraggableButton(QtGui.QPushButton):
	def __init__(self, *args, **kwargs):
		super(DraggableButton, self).__init__(*args, **kwargs)

Dragした時にWidgetを動かくために、Drag中であるか判断するインスタンス変数「__isDrag」と、ドラッグを開始した座標を保存するインスタンス変数「__startPos」を用意します。

from PySide import QtCore, QtGui

class DraggableButton(QtGui.QPushButton):
	def __init__(self, *args, **kwargs):
		super(DraggableButton, self).__init__(*args, **kwargs)
		self.__isDrag = False
		self.__startPos = QtCore.QPoint(0, 0)

Drag開始を検知する!

Drag開始を検知するには、「mousePressEvent」をオーバーライドします。Drag中であるステータスを「__isDrag」に設定し、マウスの座標を「__startPos」に記憶させます。

from PySide import QtCore, QtGui

class DraggableButton(QtGui.QPushButton):
	def __init__(self, *args, **kwargs):
		super(DraggableButton, self).__init__(*args, **kwargs)
		self.__isDrag = False
		self.__startPos = QtCore.QPoint(0, 0)
	
	def mousePressEvent(self, event):
		self.__isDrag = True
		self.__startPos = event.pos()
		super(DraggableButton, self).mousePressEvent(event)

Drag終了を検知する!

Drag終了を検知するには、「mouseReleaseEvent」をオーバーライドします。Drag中であるステータスを「__isDrag」に設定し無駄に動かないようにします。

from PySide import QtCore, QtGui

class DraggableButton(QtGui.QPushButton):
	def __init__(self, *args, **kwargs):
		super(DraggableButton, self).__init__(*args, **kwargs)
		self.__isDrag = False
		self.__startPos = QtCore.QPoint(0, 0)
	
	def mousePressEvent(self, event):
		self.__isDrag = True
		self.__startPos = event.pos()
		super(DraggableButton, self).mousePressEvent(event)
	
	def mouseReleaseEvent(self, event):
		self.__isDrag = False
		super(DraggableButton, self).mouseReleaseEvent(event)

Dragに合わせて動かす!

Drag中の処理は、「mouseMoveEvent」をオーバーライドします。「__isDrag」が「True」の場合だけ処理するようにします。

動かす量は、「Drag開始の座標」と「Drag先の座標」の差分から求められます。

だけど!

Widgetを動かせる「move」は親のWidgetを中心とするので、「mapToParent」で座標を変換してから動かします(*´∀`)b

from PySide import QtCore, QtGui

class DraggableButton(QtGui.QPushButton):
	def __init__(self, *args, **kwargs):
		super(DraggableButton, self).__init__(*args, **kwargs)
		self.__isDrag = False
		self.__startPos = QtCore.QPoint(0, 0)
	
	def mousePressEvent(self, event):
		self.__isDrag = True
		self.__startPos = event.pos()
		super(DraggableButton, self).mousePressEvent(event)
	
	def mouseReleaseEvent(self, event):
		self.__isDrag = False
		super(DraggableButton, self).mouseReleaseEvent(event)
		
	def mouseMoveEvent(self, event):
		if self.__isDrag:
			self.move(self.mapToParent(event.pos() - self.__startPos))
			
		super(DraggableButton, self).mouseMoveEvent(event)

テスト!

スタンドアローンのPythonでも平気ですが、ここではMayaを使って実験してみます!テストコードは以下のように、Windowを作ってDraggableButtonを設定します。

window = QtGui.QWidget()
window.setWindowFlags(QtCore.Qt.Window)
window.resize(1280, 720)
 
layout = QtGui.QVBoxLayout(window)
 
button = DraggableButton('DraggableButton', window)
button.setFixedSize(128, 32)
layout.addWidget(button)
 
window.show()

実行して、ボタンをマウス(左・中・右)でドラッグしてみると、、、

レイアウトを挟んでいる関係で、Windowの大きさを変えると位置がリセットされます。