2015/06/26(金) [n年前の日記]
#1 [python] PySideで画像の表示とドラッグ移動
Python + wxPython で、
PySide の QPainter の、おそらくは drawImage() あたりを使えば、wxPython と同様に「画像を描画」ができそうだと分かったけれど。どうやら PySide には QGraphicsView、QGraphicsScene、QGraphicsPixmapItem とやらがあって、ソレを使うと比較的簡単に画像を表示できそうなので試してみたり。
以下を参考にさせてもらいつつ、自分も実験。
_ヲドリテヒヅル PyQtでイメージビューワ
_PyQtでファイルのドラッグ&ドロップ - None is None is None
以下のような感じになった。
なるほど、たしかにここまでなら、簡単に実現できる…。PySide(PyQt)って便利だな。
ここまでできたら、後はアレコレ付け加えれば、画像レイアウトツールぐらいは作れそう。仮にそういうツールを作ったとして、C# + .NET で書いたツールと比べると、
デメリットとしては、exe形式を用意するのが少し面倒。おそらく py2exe、cx_Freeze、PyInstaller 等のどれかしらを使うことになるのかな。ググってみたら、 _Packaging PySide applications on Windows - Qt Wiki に解説が。
- ファイルのドラッグアンドドロップ
- 画像表示
- 画像のドラッグ移動
PySide の QPainter の、おそらくは drawImage() あたりを使えば、wxPython と同様に「画像を描画」ができそうだと分かったけれど。どうやら PySide には QGraphicsView、QGraphicsScene、QGraphicsPixmapItem とやらがあって、ソレを使うと比較的簡単に画像を表示できそうなので試してみたり。
以下を参考にさせてもらいつつ、自分も実験。
_ヲドリテヒヅル PyQtでイメージビューワ
_PyQtでファイルのドラッグ&ドロップ - None is None is None
以下のような感じになった。
#! /usr/bin/env python
# -*- mode: python; Encoding: utf-8; coding: utf-8 -*-
"""PySide と QGraphicsView を使って画像を表示するサンプル
QGraphicsView に画像ファイルをドラッグアンドドロップすると表示する。
各画像はマウスドラッグで位置を移動できる。
"""
import os
import sys
import re
from PySide import QtGui, QtCore
class MyScene(QtGui.QGraphicsScene):
"""ビューを表示するためのシーン"""
def __init__(self, *argv, **keywords):
"""コンストラクタ"""
super(MyScene, self).__init__(*argv, **keywords)
def setFile(self, filepath):
"""イメージをアイテムとしてシーンに追加するためのメソッド。"""
# 画像ファイルを読み込んでItemにする
pixmap = QtGui.QPixmap(filepath)
item = QtGui.QGraphicsPixmapItem(pixmap)
# Itemを移動可能に設定。
# これだけでマウスによるドラッグ移動ができてしまう
item.setFlags(QtGui.QGraphicsItem.ItemIsMovable)
self.addItem(item) # Itemを登録
class MyView(QtGui.QGraphicsView):
"""メインとなるビュー"""
def __init__(self):
"""コンストラクタ"""
super(MyView, self).__init__()
self.setWindowTitle("DnD Image and drag") # ウインドウタイトルを指定
self.resize(640, 480) # ウインドウのサイズを指定
# QGraphicsViewの設定
self.setCacheMode(QtGui.QGraphicsView.CacheBackground)
self.setRenderHints(QtGui.QPainter.Antialiasing |
QtGui.QPainter.SmoothPixmapTransform |
QtGui.QPainter.TextAntialiasing
)
# QGraphicsSceneを生成
scene = MyScene(self)
scene.setSceneRect(QtCore.QRectF(self.rect()))
self.setScene(scene)
# ファイルのドラッグアンドドロップを許可
self.setAcceptDrops(True)
def resizeEvent(self, event):
"""リサイズ時にシーンの矩形を更新"""
super(MyView, self).resizeEvent(event)
self.scene().setSceneRect(QtCore.QRectF(self.rect()))
def dragEnterEvent(self, event):
"""ファイルドラッグがウインドウに入ってきた時の処理"""
if event.mimeData().hasUrls():
# ファイルパス群がドラッグされてるならイベントを有効化
event.accept()
else:
event.ignore()
def dropEvent(self, event):
"""ファイルがドロップされた時の処理"""
for url in event.mimeData().urls():
# URL(file:///)をファイルパスの形に変換
filepath = str(url.toLocalFile())
# 画像ファイルの拡張子かチェック
root, ext = os.path.splitext(filepath)
if re.match(r'^\.(png|jpg|bmp|gif)$', ext, re.IGNORECASE):
# ファイル存在チェック
if os.path.isfile(filepath):
# シーンに画像を追加
self.scene().setFile(filepath)
def dragMoveEvent(self, event):
"""ドラッグされた時の処理
QGraphicsView が QGraphicsScene を含む場合は、
dragMoveEvent をオーバーライドしないとよろしくない
"""
pass
def dragLeaveEvent(self, event):
"""ドラッグが抜けた時の処理
dragMoveEvent と同様にオーバーライドする
"""
pass
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
v = MyView()
v.show()
sys.exit(app.exec_())
なるほど、たしかにここまでなら、簡単に実現できる…。PySide(PyQt)って便利だな。
ここまでできたら、後はアレコレ付け加えれば、画像レイアウトツールぐらいは作れそう。仮にそういうツールを作ったとして、C# + .NET で書いたツールと比べると、
- Python + PySide のスクリプトならWindows以外でも動かせる。
- エクスポート結果が気に入らなければ、スクリプトを気軽に書き換えて修正もできる。
デメリットとしては、exe形式を用意するのが少し面倒。おそらく py2exe、cx_Freeze、PyInstaller 等のどれかしらを使うことになるのかな。ググってみたら、 _Packaging PySide applications on Windows - Qt Wiki に解説が。
◎ ハマった点。 :
ちとハマったのは、QGraphicsView にファイルをD&D(ドラッグアンドドロップ)する処理。setAcceptDrops(True) を呼んでファイルのD&D受け付けOKにして、dragEnterEvent、dropEvent を書いただけでは反応しなくて。ただの QWidget なら、ソレで済むのだけど…。
ググってみたら、QGraphicsView の場合、そこに登録される QGraphicsScene にイベントを渡すため、dragMoveEvent 等が少し特殊なことをしているそうで。dragEnterEvent の中で「イベントを有効にしなさい」と指定しているのに、dragMoveEvent が、ソレを上書き・再設定しちゃって反応しなくなるようで。
解決策は、以下のページでまとめられていた。
_[SOLVED] Drag and Drop の動作,実装方法など | Qt Forum
_QGraphicsViewのdropEventを使う - メモ
いくつか手はあるけど、dragMoveEvent、dragLeaveEvent を上書きしてしまうのが簡単らしい。とりあえず、それぞれ何もしないように pass だけ書いてみたところ、反応するようになった。
ググってみたら、QGraphicsView の場合、そこに登録される QGraphicsScene にイベントを渡すため、dragMoveEvent 等が少し特殊なことをしているそうで。dragEnterEvent の中で「イベントを有効にしなさい」と指定しているのに、dragMoveEvent が、ソレを上書き・再設定しちゃって反応しなくなるようで。
解決策は、以下のページでまとめられていた。
_[SOLVED] Drag and Drop の動作,実装方法など | Qt Forum
_QGraphicsViewのdropEventを使う - メモ
いくつか手はあるけど、dragMoveEvent、dragLeaveEvent を上書きしてしまうのが簡単らしい。とりあえず、それぞれ何もしないように pass だけ書いてみたところ、反応するようになった。
[ ツッコむ ]
以上、1 日分です。
