2019年1月9日水曜日

WxPythonでエクスプローラーから画像ファイルをドロップして画像表示。及び画像をマウスで移動させます。




参考サイトサイト

wxPythonで画像表示とかドラッグ移動とか勉強中
http://blawat2015.no-ip.com/~mieki256/diary/201506251.html

上サイトのファイルがすばらしかったので自分なりに分析して、わかりやすいよう解説してみました。(幾分コードが変えてあります)


エクスプローラーから画像ファイルドロップ、コードの概要

以下のようにそれに関係あるコードのみ書き出しました。
①MyFrameクラスにてMyFrameにドロップされるよう設定
②wx.FileDropTargetを継承したMyFileDropTargetクラスを作成。そのなかにOnDropFiles関数
を作成しドロップした画像のフルパスを取得します。
③それを引数として、MyFrame内関数LoadImage(filepath)を呼び出します。
④LoadImage→UpdateDrawing→MyObjのDraw でドラッグした画像の表示
MyObjは画像の描画、座標の管理などをしています。

class MyFrame(wx.Frame):
    def __init__(self, parent=None, title=""):
        wx.Frame.__init__(self, parent=parent, title=title, size=(800, 600))
        ...........
        ...........
        # マウスカーソルを動かした時に呼ばれるメソッドを割り当てる
        self.Bind(wx.EVT_MOTION, self.OnMouseMotion)


        # ファイルドロップの対象をフレーム全体に
        #MyFrameにドロップされるよう設定
        self.droptarget = MyFileDropTarget(self)
        self.SetDropTarget(self.droptarget)


    """D&Dされた画像をロードして描画"""
    #たぶんドラッグが終わったときに実行
    #だとしたら、マウスイベントのupでも実行できるのでは?
    #できなかった。マウスイベントは引き数の数がきまっている
    def LoadImage(self, filepath):
        print "LoadImage"
        x, y = 0, 0

        #ファイルパスから直接ビットマップを作成している?
        b = wx.Bitmap(filepath)
        #画像のビットマップと座標を受け取っている
        obj = MyObj(b,  x, y)
        self.objs.append(obj)
        x += 32
        y += 32
        self.UpdateDrawing()  # 描画更新


"""ドラッグアンドドロップ担当クラス"""
class MyFileDropTarget(wx.FileDropTarget):
    def __init__(self, obj):
        wx.FileDropTarget.__init__(self)
        #MyFrameに以下の設定があるので、objにはMyFrameがはいる
        #self.droptarget = MyFileDropTarget(self)
        self.obj = obj 


    #ファイルをドロップされた場合に、そのファイルを引数にして
    #MyFrameのLoadImageを呼び出しているだけ。
    """ファイルをドロップした時の処理"""
    def OnDropFiles(self, x, y, filenames):
        #実験したらわかるが以下のfor文をとおさないことには実際の
        #パスが得られない
        for filepath in filenames:
            self.obj.LoadImage(filepath) 


"""マウスドラッグで移動できるオブジェクト用のクラス"""
#その画像の描画、座標の管理などをしている
class MyObj():
    def __init__(self, bmp, x=0, y=0):
        self.bmp = bmp  # bitmapを記録
        self.pos = wx.Point(x, y)  # 表示位置を記録、self.posはタプル
        self.diff_pos = wx.Point(0, 0)



表示された画像をマウスで動かすコードの概要
①マウスの左ボタンが押されたら
  OnMouseLeftDown→MyObjのSavePosDiff(pos)→diff_pos = 画像の座標-マウス座標 を記録。
②マウスカーソルが動いた時
  ドラッグしてる画像の座標がマウス座標で更新されます。
③マウスの左ボタンが離されたら
 OnMouseLeftUp→MyObjのpos = 今のマウスのpos + diff_pos が代入されます。
 つまり移動後のマウスのposに、最初の画像とマウス座標の差を加味されたものが、MyObjの
 画像のposに入ります。
 さらに、UpdateDrawing→Draw→MyObjのDraw でドラッグした画像の表示されます。



全コード

E:\MyBackups\goolgedrive\myprg_main\python_my_prg\wxpython\dc\drop_gazou_hyouzi5_simple.py

# coding: UTF-8
#wxPythonで画像表示とかドラッグ移動とか勉強中
#http://blawat2015.no-ip.com/~mieki256/diary/201506251.html


"""
Lキーをおして画像ファイルダイヤログ(エクスプローラー)をひらく
エクスプローラーから画像をドロップしてMyFrame上に表示。

画像ファイルをドラッグアンドドロップして移動させてMyFrame上に表示。
"""

import wx
dn = 0

"""マウスドラッグで移動できるオブジェクト用のクラス"""
#そのオブジェクトの描画、座標の管理などをしている
# obj = MyObj(b,  self.x_drop, self.y_drop)となっており、(bは画像)
#画像ごとに、MyObjで管理されている。
class MyObj():
    def __init__(self, bmp, x=0, y=0):
        self.bmp = bmp  # bitmapを記録
        self.pos = wx.Point(x, y)  # 表示位置を記録、self.posはタプル
        self.diff_pos = wx.Point(0, 0)
        print "MyObj self.pos",self.pos

    """与えられた座標とアタリ判定して結果を返す"""
    def HitTest(self, pnt):
        #pntはマウスpos selfはオブジェクト
        #で、オブジェクト内にマウスが入っていればTrue
        rect = self.GetRect()                # 画像大きさに合わせての矩形領域を取得
        return rect.InsideXY(pnt.x, pnt.y)  # 座標が矩形内に入ってるか調べる

    """矩形領域を返す"""
    #ここでは矩形を設定してその中にマウスが入れば、画像の位置
    #をかえているだけで、特別なことは何もしていない。
    #ちょとなれないメソッドがでただけで....
    def GetRect(self):
        return wx.Rect(self.pos.x, self.pos.y,
                       self.bmp.GetWidth(), self.bmp.GetHeight())


    """マウス座標と自分の座標の相対値を記録。"""
    def SavePosDiff(self, pnt):
        """
        マウス座標と自分の座標の相対値を記録。
        この情報がないと、画像をドラッグした時の表示位置がしっくりこない
        """
        #自分:クリックしたオブジェクトのことか?
        #self.drag_obj.SavePosDiff(pos) とあるのでyes
        self.diff_pos.x = self.pos.x - pnt.x
        self.diff_pos.y = self.pos.y - pnt.y


    # #通常の?画像表示
    # Bitmap = None
    # image = wx.Image(bitmap_image) #bitmap_image:元画像ふぁいる
    # Bitmap = image.ConvertToBitmap()

    # buffer = wx.EmptyBitmap(680,480)
    # dc = wx.BufferedDC(None, self.buffer)
    # dc.DrawBitmap(Bitmap, x, y, True)

    # #このファイルの画像表示
    # self._buffer = wx.EmptyBitmap(*size)
    # dc = wx.BufferedDC(None, self._buffer)
    # b = wx.Bitmap(filepath) #ここが違う
    # obj = MyObj(b,  x, y)
    # dc.DrawBitmap(self.bmp, r.x, r.y, True) 



    """与えられたDCを使って画像を描画する"""
    def Draw(self, dc, select_enable):
        print "MyObj Draw"
        if self.bmp.Ok():
            r = self.GetRect()  # 矩形領域を取得
            # ペンを設定しないと何故か描画できない
            # 私の環境ではok
            #dc.SetPen(wx.Pen(wx.BLACK, 4))
            # 画像を描画
            dc.DrawBitmap(self.bmp, r.x, r.y, True) 

            #select_enableは画像のまわりに赤い枠を描くかどうかの
            #フラッグ
            if select_enable:
                # 画像枠を描画
                dc.SetBrush(wx.TRANSPARENT_BRUSH)  # 透明塗り潰し
                dc.SetPen(wx.Pen(wx.RED, 1))  # 赤い線を指定
                # 矩形を描画
                dc.DrawRectangle(r.x, r.y, r.width, r.height)

            return True

        else:
            return False



"""ドラッグアンドドロップ担当クラス"""
class MyFileDropTarget(wx.FileDropTarget):
    def __init__(self, obj):
        wx.FileDropTarget.__init__(self)
        #MyFrameに以下の設定があるので、objにはMyFrameがはいる
        #self.droptarget = MyFileDropTarget(self)
        self.obj = obj 

    #この関数どこからもよびだされていない。
    #内部的によびだされいるのだろう
    #ファイルをドロップされた場合に、そのファイルを引数にして
    #MyFrameのLoadImageを呼び出しているだけ。
    """ファイルをドロップした時の処理"""
    def OnDropFiles(self, x, y, filenames):
        #実験したらわかるが以下のfor文をとおさないことには実際の
        #パスが得られない
        for filepath in filenames:
            self.obj.LoadImage(filepath) 

        print "filenames",filenames
        print "filepath",filepath
        print
        #実行結果
        #filenames                [u'E:\\MyBackups\\goolgedrive\\myprg_main\\python_my_prg\\wxpython\\dc\\gazou_drop\\ball.png']
        #filepath E:\MyBackups\goolgedrive\myprg_main\python_my_prg\wxpython\dc\gazou_drop\ball.png


"""ダブルバッファで表示するFrame"""
class MyFrame(wx.Frame):
    def __init__(self, parent=None, title=""):
        wx.Frame.__init__(self, parent=parent, title=title, size=(800, 600))

        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnMouseLeftUp)
        # マウスカーソルを動かした時に呼ばれるメソッドを割り当てる
        self.Bind(wx.EVT_MOTION, self.OnMouseMotion)
        self.Bind(wx.EVT_KEY_DOWN,  self.OnKeyDownEvent)

        # 画像格納用リストを初期化
        self.objs = []
        # マウスドラッグ処理用の変数を確保
        self.drag_obj = None
        self.drag_start_pos = wx.Point(0, 0)
        # 描画用バッファ初期化のために一度 OnSize() を呼ぶ
        self.OnSize()

        # ファイルドロップの対象をフレーム全体に
        #MyFrameにドロップされるよう設定
        self.droptarget = MyFileDropTarget(self)
        self.SetDropTarget(self.droptarget)

        self.x_drop = 0
        self.y_drop = 0


    """D&Dされた画像をロードして描画"""
    #たぶんドラッグが終わったときに実行
    #だとしたら、マウスイベントのupでも実行できるのでは?
    #できなかった。マウスイベントは引き数の数がきまっている
    def LoadImage(self, filepath):
        print "LoadImage"

        #ファイルパスから直接ビットマップを作成している?
        b = wx.Bitmap(filepath)
        #画像のビットマップと座標を受け取っている
        obj = MyObj(b,  self.x_drop, self.y_drop)
        self.objs.append(obj)
        #ドロップした画像を少しづつずらして表示
        self.x_drop += 32
        self.y_drop += 32
        self.UpdateDrawing()  # 描画更新


    """ウインドウサイズが変更された時に呼ばれる処理"""
    def OnSize(self, event=None):
        # クライアントのウインドウサイズを取得
        #ClientSize :クライアントサイズは、ウィジェットに属する
        #任意の罫線の内側のウィジェットの領域を表し、EVT_PAINTイ
        #ベントで描画可能な領域です。 ウィジェットに境界線がない
        #場合、クライアントのサイズはそのサイズと同じです
        size = self.ClientSize 

        # ウインドウサイズで、空の描画用バッファ(bitmap)を作成
        #*size 可変長変数。
        #タプルを関数に渡すときに * を付けると、タプルの各要素を
        #関数の引数として展開されて渡される
        self._buffer = wx.EmptyBitmap(*size)
        print size # (792, 573)
        self.UpdateDrawing()  # 描画更新


    """描画更新"""
    def UpdateDrawing(self):
        print "UpdateDrawing"
        dc = wx.BufferedDC(None, self._buffer)
        self.Draw(dc)  # 実際の描画処理
        print "Refresh"
        self.Refresh(eraseBackground=False)


    """実際の描画処理"""
    def Draw(self, dc):
        print "Draw"
        dc.Clear()  # デバイスコンテキストでクリア

        dc.SetBrush(wx.Brush(wx.Colour(128, 255, 128)))
        dc.SetPen(wx.Pen("RED", 3))
        #つまり 輪郭が赤中が黄緑色?になる
        dc.DrawCircle(10, 10, 100)


        #objsで今までドラッグした画像を保存しておいて
        #ドラッグ完了すると、ドラッグ中の画像もふくめて
        #描画している
        for obj in self.objs:
            #MyObjのDraw呼び出し
            obj.Draw(dc, True)  # オブジェクトを描画
            #obj.Draw(dc, False)  # オブジェクトを描画
           
            # 画像を3つドラッグした場合の関数のかかわり
            # 画像のドラッグの順序により最初の方の画像が
            #  最後の方の画像に隠れて表示される
           


    """画面書き換え要求があった時に呼ばれる処理"""
    def OnPaint(self, event=None):
        dc = wx.BufferedPaintDC(self, self._buffer)


    """マウス座標と重なってるオブジェクトを返す"""
    def FindObj(self, pnt):
        result = None
        for obj in self.objs:
            #pntマウスの座標と重なっているobjがあればTrue
            if obj.HitTest(pnt):
                result = obj
        return result


    """マウスの左ボタンが押された時の処理"""
    def OnMouseLeftDown(self, event):
        # マウス座標を取得
        pos = event.GetPosition() 
        #dnの変数もなくていいような?
        global dn
        dn += 1
        print "mouse down"
        print dn, pos
        #マウス座標と重なってるオブジェクトを取得
        obj = self.FindObj(pos) 
        #objが空でないなら、何かobjがあったら
        #isのほうが==より処理がはやい
        if obj is not None:
            #ドラッグ移動するオブジェクトを記憶
            self.drag_obj = obj 
            #drag_start_posはつかわれていないような?
            # ドラッグ開始時のマウス座標を記録
            self.drag_start_pos = pos 
            #diff_pos = 画像の座標-マウス座標 を記録。
            self.drag_obj.SavePosDiff(pos)


    """マウスの左ボタンが離された時の処理"""
    def OnMouseLeftUp(self, event):
        if self.drag_obj is not None:
            pos = event.GetPosition()
            #移動後のマウスのposに、最初の画像とマウス座標の差を加味する
            #それをdrag_objつまりMyObjのposに代入する
            #つまり画像のposがかわる。
            #diff_pos.x = self.pos.x - pnt.x
            self.drag_obj.pos.x = pos.x + self.drag_obj.diff_pos.x
            self.drag_obj.pos.y = pos.y + self.drag_obj.diff_pos.y
            print "mouse up"
            print self.drag_obj.pos.x , self.drag_obj.pos.y

        self.drag_obj = None
        print "mouse up None"
        #UpdateDrawing→Draw→MyObjのDraw でドラッグした画像の表示
        self.UpdateDrawing()


    """マウスカーソルが動いた時の処理"""
    def OnMouseMotion(self, event):
        print "mouse motion"
        if self.drag_obj is None:
            # ドラッグしてるオブジェクトが無いなら処理しない
            return

        # ドラッグしてるオブジェクトの座標値をマウス座標で更新
        pos = event.GetPosition()
        self.drag_obj.pos.x = pos.x + self.drag_obj.diff_pos.x
        self.drag_obj.pos.y = pos.y + self.drag_obj.diff_pos.y
        self.UpdateDrawing()  # 描画更新


    #Lキーをおしてファイルダイヤログをひらく
    def OnKeyDownEvent(self, event):
        k = event.GetKeyCode()
        print k
        # キーLがおされた
        if 76==k:
            self.openDialog()


    def openDialog(self):
        dp = "E:\MyBackups\goolgedrive\myprg_main\python_my_prg\wxpython\dc\gazou_drop"
        #osのファイルダイヤログをよびだす
        # 引数 dp 開くフォルダ "":デフォルトのファイル名 
        # *.png : pngファイルだけ表示
        d = wx.FileDialog(self, "画像ファイル", dp,"","*.png")
        d.ShowModal()
        d.Destroy()


if __name__ == '__main__':
    # メイン処理
    app = wx.App(False)
    frame = MyFrame(None, "DnD Image display use Double Buffer")
    frame.Show()
    app.MainLoop()

0 件のコメント:

コメントを投稿

About

参加ユーザー

連絡フォーム

名前

メール *

メッセージ *

ブログ アーカイブ

ページ

Featured Posts