mieki256's diary



2015/06/26(金) [n年前の日記]

#1 [python] PySideで画像の表示とドラッグ移動

Python + wxPython で、 ができたので、今度はソレを Python + PySideでやってみようかと。

PySide の QPainter の、おそらくは drawImage() あたりを使えば、wxPython と同様に「画像を描画」ができそうだと分かったけれど。どうやら PySide には QGraphicsView、QGraphicsScene、QGraphicsPixmapItem とやらがあって、ソレを使うと比較的簡単に画像を表示できそうなので試してみたり。

以下を参考にさせてもらいつつ、自分も実験。

_ヲドリテヒヅル PyQtでイメージビューワ
_PyQtでファイルのドラッグ&ドロップ - None is None is None

以下のような感じになった。
sshot_pyside_dnd_01.png

#! /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 で書いたツールと比べると、 てなメリットがありそうだなと。

デメリットとしては、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 だけ書いてみたところ、反応するようになった。

以上です。

過去ログ表示

Prev - 2015/06 - Next
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

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project