QListViewにファイルをD&Dする!!

Batch的な処理をするツールを作る時、処理したいファイルの指定や、ディレクトリの指定っというのがあると思います。そういう時、QListViewなどにファイルをD&Dして指定できたらいいなーっと思う時があります!

PySide(Qt)でのD&Dはちょーっと厄介というか、面倒くさいので、メモも兼ねてご紹介したいと思います!

クラスの骨格

まずは、QListViewを継承したクラスを用意します。

from PySide import QtCore
from PySide import QtGui

class ListView(QtGui.QListView):
	def __init__(self, *args, **kwargs):
		super(ListView, self).__init__(*args, **kwargs)

D&Dできるためのフラグ

デフォルトではD&Dできないので、フラグの設定を済ませます。

from PySide import QtCore
from PySide import QtGui

class ListView(QtGui.QListView):
	def __init__(self, *args, **kwargs):
		super(ListView, self).__init__(*args, **kwargs)
		
		self.setDragEnabled(False)
		self.setAcceptDrops(True)
		self.setDropIndicatorShown(True)

「self.setDragEnabled(False)」は、QListView内でのドラッグのON/OFFです。ファイルのリストをD&Dで並び替える機能はつけないので、ここではOFFにしました。

「self.setAcceptDrops(True)」は、QListViewにドロップを受け付けるON/OFFです。ONにすることで、ドロップを受け付けるようになります。

「self.setDropIndicatorShown(True)」は、ドロップインジケータの表示ON/OFFです。ONにすることで、ドロップした際の状態がわかりやすくなります!

dragEnterEventの実装

この関数は、D&D操作がウィジェットに来た時に実行さ、その操作を受け付けるかどうかを決めます。今回は、ファイルのD&Dですので、URLの有無で、受け付けるかどうか決めます。

D&Dされたファイルの情報は、引数のeventから取り出していきます。
from PySide import QtCore
from PySide import QtGui

class ListView(QtGui.QListView):
	def __init__(self, *args, **kwargs):
		super(ListView, self).__init__(*args, **kwargs)
		
		self.setDragEnabled(False)
		self.setAcceptDrops(True)
		self.setDropIndicatorShown(True)
		
	def dragEnterEvent(self, event):
		if event.mimeData().hasUrls():
			event.accept()
		
		else:
			super(ListView, self).dragEnterEvent(event)

dragMoveEventの実装

この関数は、D&D操作を受け入れた後に継続的に実行され、その操作を受け付けるかどうかを決めます。今回は、ファイルのD&Dですので、URLの有無で、受け付けるかどうか決めます。

D&Dされたファイルの情報は、引数のeventから取り出していきます。
from PySide import QtCore
from PySide import QtGui

class ListView(QtGui.QListView):
	def __init__(self, *args, **kwargs):
		super(ListView, self).__init__(*args, **kwargs)
		
		self.setDragEnabled(False)
		self.setAcceptDrops(True)
		self.setDropIndicatorShown(True)
		
	def dragEnterEvent(self, event):
		if event.mimeData().hasUrls():
			event.accept()
		
		else:
			super(ListView, self).dragEnterEvent(event)
	
	def dragMoveEvent(self, event):
		if event.mimeData().hasUrls():
			event.accept()
			
		else:
			super(ListView, self).dragMoveEvent(event)

dropEventの実装

この関数は、ウィジェット上でドロップイベントが発生した時に実行されます。そう!今回のメインの部分です。この関数でも、URLがあるかキチンと調べてから、アイテムの追加を行っています。

from PySide import QtCore
from PySide import QtGui

class ListView(QtGui.QListView):
	def __init__(self, *args, **kwargs):
		super(ListView, self).__init__(*args, **kwargs)
		
		self.setDragEnabled(False)
		self.setAcceptDrops(True)
		self.setDropIndicatorShown(True)
		
	def dragEnterEvent(self, event):
		if event.mimeData().hasUrls():
			event.accept()
		
		else:
			super(ListView, self).dragEnterEvent(event)
	
	def dragMoveEvent(self, event):
		if event.mimeData().hasUrls():
			event.accept()
			
		else:
			super(ListView, self).dragMoveEvent(event)
			
	def dropEvent(self, event):
		if event.mimeData().hasUrls():
			model = self.model()
			urls = event.mimeData().urls()
			for url in urls:
				filename = url.toLocalFile()
				print type(url)
				item = QtGui.QStandardItem(filename)
				model.appendRow(item)
		
			event.accept()
		else:
			super(ListView, self).dropEvent(event)

URLが存在する場合は、1つづつ処理をしていきます。URLの情報は、「QUrl」のインスタンスです。ここではファイルのフルパスを表示したかったので「toLocalFile()」を使って取得しています。他にもファイルの情報を取り出すメソッドがあるので、お好みで変更してください。

取り出したURLは、Viewに設定されたModelにQStandardItemのインスタンスを追加していきます。

処理が終わったら、イベントを受け入れたことを通知するために「accept()」を実行しましょう。

実行テスト

上記のListVIewを使い、QStandardItemModelなどを設定してください。その後UIにファイルをD&Dすると、冒頭のような動作になります。

ざっくり試したい場合は、以下のようなコードになるかと思います。

model = QtGui.QStandardItemModel()
window = ListView()
window.setWindowFlags(QtCore.Qt.Window)
window.setModel(model)
window.show()