Created
September 9, 2015 09:06
-
-
Save peace098beat/63306bfdbca24c7e4cc2 to your computer and use it in GitHub Desktop.
[PySide] ドラッグドロップでファイル一覧を表示するQListViewウィジェット
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
#! encoding:utf-8 | |
""" | |
gui_fileListWidget.py | |
Description: | |
ドラッグアンドドロップでファイルリストを表示するウィジェット | |
本当のデータはmodelが格納している。 | |
Delegeteはリストのindex毎にModeからデータを読み出している。 | |
Example: | |
class MainWindow(QMainWindow): | |
def __init__(self): | |
QMainWindow.__init__(self) | |
self.setAcceptDrops(True) | |
data = [ | |
{u"name":u"file1.wav"}, | |
{u"name":u"file2.wav"} | |
] | |
# モデルとデリゲートを生成 | |
self.myListModel = CustomListModel(data=data) | |
self.myListDelegete = CustomListDelegate() | |
# QListViewを使うためには下のコードを書くだけ | |
self.myListView = QListView() | |
self.myListView.setModel(self.myListModel) | |
self.myListView.setItemDelegate(self.myListDelegete) | |
# UI配置 | |
self.widget = QWidget() | |
layout = QVBoxLayout(self.widget) | |
layout.addWidget(self.myListView) | |
self.setCentralWidget(self.widget) | |
# ドラッグアンドドロップのイベント処理 | |
def dragEnterEvent(self, event): | |
#ドラッグイベントの検知 | |
print 'dragEnterEvent' | |
if event.mimeData().hasUrls(): | |
event.accept() | |
else: | |
event.ignore() | |
def dropEvent(self, event): | |
#ドロップイベントの検知 | |
print 'dropEvent' | |
# リストのクリア | |
self.myListModel.clearItems() | |
files = [unicode(u.toLocalFile()) for u in event.mimeData().urls()] | |
for fname in files: | |
# print 'dropEvent::', type(fname) | |
f = os.path.basename(fname) | |
self.myListModel.addItem({u"name":f}) | |
# self.myListModel.clearItems() | |
""" | |
import sys | |
import time | |
from PySide.QtCore import * | |
from PySide.QtGui import * | |
# -*- coding:utf-8 -*- | |
import sys | |
import os | |
import warnings | |
from PySide.QtCore import * | |
from PySide.QtGui import * | |
# ============================================================================= | |
class CustomListModel(QAbstractListModel): | |
""" データを格納するモデルクラス | |
リストに表示するデータ名だけでなく、その他の情報も持っていられる。 | |
返却時に指定されたデータを渡す | |
""" | |
def __init__(self, parent=None, data=[]): | |
super(CustomListModel, self).__init__(parent) | |
self.__items = data | |
def clearItems(self): | |
# データのクリア | |
self.__items = [] | |
def addItem(self, data): | |
# データを格納 | |
self.__items.append(data) | |
# シグナル発行 | |
self.dataChanged.emit(0, 1) | |
def addItems(self, datas): | |
self.clearItems() | |
for fname in datas: | |
# f = os.path.basename(fname) | |
self.addItem({u"name": fname}) | |
def rowCount(self, parent=QModelIndex()): | |
'''行の数(アイテムの数)を返却 | |
※ 継承に必要 | |
''' | |
return len(self.__items) | |
def data(self, index, role=Qt.DisplayRole): | |
'''指定されたデータを返却 | |
※ 継承に必要 | |
例) | |
DisplayRole: テキストデータでメインの文字列表示に使われる | |
ToolTipRole: ツールチップ(マウスをホバーした際に出る補助テキスト)の表示に使われる | |
BackgroundRole: 背景の描画に使われる | |
ForegroundRole: 文字の色などに使われる | |
''' | |
if not index.isValid(): | |
return None | |
# 行番号が境界内に収まっているか | |
if not 0 <= index.row() < len(self.__items): | |
return None | |
# 指定されたデータを返却 | |
if role == Qt.DisplayRole: | |
return self.__items[index.row()].get("name") | |
# 文字色を返却 | |
elif role == Qt.ForegroundRole: | |
# color = self.__items[index.row()].get("color", []) | |
color = [20, 20, 20] | |
return QColor(*color) | |
# 背景色を返却 | |
elif role == Qt.BackgroundRole: | |
# color = self.__items[index.row()].get("bgcolor", []) | |
color = [20, 20, 20] | |
return QColor(*color) | |
# Thumbnailキーの画像ファイル名を返す | |
elif role == Qt.UserRole: | |
# return self.__items[index.row()].get("thumbnail", "") | |
return 'ball2.png' | |
else: | |
return None | |
class CustomListDelegate(QStyledItemDelegate): | |
"""オリジナルのデリゲータ | |
Listの表示を任意にできるが、Delegateを使うとデフォルトのカラー等は反映されなくなる | |
""" | |
THUMB_WIDTH = 30 | |
MARGIN = 5 | |
def __init__(self, parent=None): | |
super(CustomListDelegate, self).__init__(parent) | |
def paint(self, painter, option, index): | |
'''(継承メソッド)''' | |
# imgファイルの存在場所を指定 | |
CURRENT_PATH = os.curdir | |
thumbName = 'ball2.png' | |
BG_COLOR = [150, 150, 150] | |
TXT_COLOR = [100, 10, 100] | |
# 背景色の変更 | |
# =================== | |
bgColor = QColor(*BG_COLOR) | |
# 選択状態の判定 | |
if option.state & QStyle.State_Selected: | |
# 背景を描く | |
bgBrush = QBrush(bgColor) | |
bgPen = QPen(bgColor, 0.5, Qt.SolidLine) | |
painter.setPen(bgPen) | |
painter.setBrush(bgBrush) | |
painter.drawRect(option.rect) | |
# indexからデータを取り出す | |
name = index.data(Qt.DisplayRole) | |
# サムネイル画像の表示 | |
# ======================== | |
imgpath = os.path.join(CURRENT_PATH, "images", thumbName) | |
# ファイルの存在確認 | |
if not os.path.exists(imgpath): | |
print('File Not exist') | |
warnings.warn('file not exist') | |
thumbImage = QPixmap(imgpath).scaled(self.THUMB_WIDTH, self.THUMB_WIDTH) | |
# 画像の表示場所を指定 | |
r = QRect(option.rect.left(), option.rect.top(), self.THUMB_WIDTH, self.THUMB_WIDTH) | |
painter.drawPixmap(r, thumbImage) | |
# 文字色の変更 | |
# ============== | |
tcolor = QColor(*TXT_COLOR) | |
# ペンを持たせる | |
pen = QPen(tcolor, 0.5, Qt.SolidLine) | |
painter.setPen(pen) | |
# テキストを描く | |
r = QRect(option.rect.left() + self.THUMB_WIDTH + self.MARGIN, | |
option.rect.top(), | |
option.rect.width() - self.THUMB_WIDTH - self.MARGIN, | |
option.rect.height()) | |
painter.drawText(r, | |
Qt.AlignVCenter | Qt.AlignLeft, | |
"" + os.path.basename(name)) | |
def sizeHint(self, option, index): | |
return QSize(100, self.THUMB_WIDTH) | |
# ============================================================================= | |
## MasterOfMainWindow | |
class MainWindow(QMainWindow): | |
""" Our Main Window Class | |
""" | |
fileloaded = Signal(str) | |
def __init__(self): | |
QMainWindow.__init__(self) | |
# self.setWindowIcon(QIcon('./FiSig/icon/icon_fifi.png')) | |
self.setAcceptDrops(True) | |
# ==================================== | |
# メンバ変数の定義 | |
# ==================================== | |
self.fileName = None | |
# パス名 path の正規化された絶対パスを返します。 | |
self.abspath = None | |
# パス名 path の末尾のファイル名部分を返します。 | |
self.basename = None | |
# パス名 path のディレクトリ名を返します。 | |
self.dirname = None | |
# pathが実在するパスか、オープンしているファイル記述子を参照している場合 True を返します。 | |
self.exists = None | |
# 拡張子無しファイル名と、拡張子を返す | |
self.name, self.ext = None, None | |
# ==================================== | |
# UIの生成 | |
# ==================================== | |
data = [ | |
{u"name": u"file1.wav"}, | |
{u"name": u"file2.wav"} | |
] | |
# モデルとデリゲートを生成 | |
self.myListModel = CustomListModel(data=data) | |
self.myListDelegete = CustomListDelegate() | |
# QListViewを使うためには下のコードを書くだけ | |
self.myListView = QListView() | |
self.myListView.setModel(self.myListModel) | |
self.myListView.setItemDelegate(self.myListDelegete) | |
self.widget = QWidget() | |
layout = QVBoxLayout(self.widget) | |
layout.addWidget(self.myListView) | |
self.setCentralWidget(self.widget) | |
# ==================================== | |
# シグナルスロットのコネクト | |
# ==================================== | |
self.connect(self.myListView, SIGNAL("clicked(QModelIndex)"), self.slot1) | |
@Slot() | |
def slot1(self, index): | |
print 'listslot' | |
print index.row() | |
print index.data(Qt.DisplayRole) | |
name = index.data(Qt.DisplayRole) | |
self.cahangeFilePath(name) | |
############################################## | |
# スロット | |
############################################## | |
@Slot() | |
def openFileDialog(self): | |
""" ファイルオープンダイアログの表示 | |
""" | |
fname, filt = QFileDialog.getOpenFileName(self, 'Open file', '/home', 'Wave Files (*.wav);; All Files (*)') | |
# print 'openFileDialog loadfile', str(fname) | |
# 呼び出したファイルの格納 | |
self.cahangeFilePath(fname) | |
############################################## | |
# ドラッグアンドドロップのイベント処理 | |
############################################## | |
def dragEnterEvent(self, event): | |
"""ドラッグイベントの検知 | |
""" | |
print 'dragEnterEvent' | |
if event.mimeData().hasUrls(): | |
event.accept() | |
else: | |
event.ignore() | |
def dropEvent(self, event): | |
"""ドロップイベントの検知 | |
..Todo: フォルダ内ファイルの読み込み対応 | |
""" | |
print 'dropEvent' | |
files = [unicode(u.toLocalFile()) for u in event.mimeData().urls()] | |
# リストを消去 | |
self.myListModel.clearItems() | |
for fname in files: | |
# f = os.path.basename(fname) | |
self.myListModel.addItem({u"name": fname}) | |
# 呼び出したファイルを格納 | |
# file = files[0] | |
# self.cahangeFilePath(fname) | |
############################################## | |
# cahangeFilePath | |
# ファイルが呼び出された後の処理 | |
############################################## | |
def cahangeFilePath(self, filepath): | |
""" 読み込んだファイル名をメンバ変数に格納し、シグナルを発行 | |
""" | |
# 文字コードの保障 | |
if not isinstance(filepath, unicode): | |
filepath = unicode(filepath) | |
# パスを整理し、メンバ変数に格納 | |
self.parseFilename(filepath) | |
# シグナルの発行 | |
self.fileloaded.emit(self.name) | |
############################################## | |
# サブ関数:: ファイル名をパースし保管 | |
############################################## | |
def parseFilename(self, filepath): | |
# パス名 path の正規化された絶対パスを返します。 | |
self.abspath = os.path.abspath(filepath) | |
# パス名 path の末尾のファイル名部分を返します。 | |
self.basename = os.path.basename(filepath) | |
# パス名 path のディレクトリ名を返します。 | |
self.dirname = os.path.dirname(filepath) | |
# pathが実在するパスか、オープンしているファイル記述子を参照している場合 True を返します。 | |
self.exists = os.path.exists(filepath) | |
# 拡張子無しファイル名と、拡張子を返す | |
self.name, self.ext = os.path.splitext(self.basename) | |
print "------------- fileloaded ----------------" | |
print "abspath", type(self.abspath), str(self.abspath) | |
print "basename", type(self.basename), str(self.basename) | |
print "dirname", type(self.dirname), str(self.dirname) | |
print "exists", type(self.exists), str(self.exists) | |
print "name", type(self.name), str(self.name) | |
print "ext", type(self.ext), str(self.ext) | |
print "Please help SimpleFileLoader" | |
print "-----------------------------------------" | |
# ============================================================================= | |
## DEMO | |
if __name__ == '__main__': | |
# Exception Handling | |
try: | |
myApp = QApplication(sys.argv) | |
mainWindow = MainWindow() | |
mainWindow.show() | |
myApp.exec_() | |
sys.exit(0) | |
except NameError: | |
print("Name Error:", sys.exc_info()[1]) | |
except SystemExit: | |
print("Closing Window...") | |
except Exception: | |
print(sys.exc_info()[1]) | |
# ----------------------------------------------------------------------------- | |
# EOF | |
# ----------------------------------------------------------------------------- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment