wxPython 画像をドラッグ操作でリサイズできるようにしました。
動作
デフォルトで、Lキーを押すと画像収納フォルダ開きます。フォルダから画像ファイルをwxのウィンドウ上にドロップすると画像が表示されます。
zキーを押して、画像上でドラッグ(左クリックしたままマウスを動かす)すると
それにつれて画像がリサイズします。
yキーを押せば又デフォルト状態に戻ります。
参考サイト
wxPythonで画像表示とかドラッグ移動とか勉強中http://blawat2015.no-ip.com/~mieki256/diary/201506251.html
コードの概要
フォルダからドロップした画像のデータは、MyObjを経てmyobjListに保存されますが、myobjList上のbmpをリサイズしても画像がおかしくなりました。
で、一旦myobjListから取り出しDrawPanel上のself.bmp としてリサイズ
そしてまた元のmyobjListに戻しています。
描画は、DrawPanel内のDrawの
dc.DrawBitmap(self.bmp, self.bmp_pos.x, self.bmp_pos.y, True) リサイズ用
all_obj_draw(dc) ドロップされた画像用
で描画されています。
全コード
E:\MyBackups\goolgedrive\myprg_main\python_my_prg\wxpython\dc\お絵かき3\gazou_scale_two.
# coding: UTF-8
#MyObjのリスト
myobjList = []
class MyFrame(wx.Frame):
#メニューバー 、ステータスバー,ツールバーなどはFrmae上し
#か置けないため、paelを作成し、そこで描画する
## *args 可変長タプル **kwargs 可変長辞書
def __init__(self, parent, *args ,**kargs):
frame = wx.Frame.__init__(self, parent, *args, **kargs)
pal = DrawPanel(self)
#画像、またはエクスプローラ上の画像ファイルをドラッグした時に
#描画のアップデートをする関数を指定する
func = pal.InitBuffer
#画像のドラッグ機能をMyFrameに組み込む Panelには組み込めない
#ようだ
MetaMyFileDropTarget(self, func)
self.Show()
"""ダブルバッファで表示するFrame"""
class DrawPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.bmp = None
self.bmp_pos = wx.Point(0,0)
self.InitBuffer()
self.child_list = []
da = DropAction(self)
self.child_list.append(da)
dp = ResizeAction(self)
self.child_list.append(dp)
self.bd = DropMyBind(self, self.child_list)
# RefreshによってOnPaintがよばれている
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_SIZE, self.OnSize)
"""画面書き換え要求があった時に呼ばれる処理"""
def OnPaint(self, event):
print "OnPaint"
dc = wx.BufferedPaintDC(self, self.buffer)
"""ウインドウサイズが変更された時に呼ばれる処理"""
#かなり複雑になるのでこのイベントやめる
#以下のコードによりかなりbufferを大きくしているので、OnSizeの必要がない。
# self.buffer = wx.EmptyBitmap(1280, 708)
def OnSize(self, event):
print "OnSize"
pass
"""描画更新"""
def InitBuffer(self):
#print "UpdateDrawing"
self.buffer = wx.EmptyBitmap(1280, 708)
dc = wx.BufferedDC(None, self.buffer)
dc.SetBackground(wx.Brush('white'))
dc.Clear() # デバイスコンテキストでクリア
self.Draw(dc) # 実際の描画処理
# Falseを指定して背景を消さなくしたら画面のちらつきが出なくなった
self.Refresh(eraseBackground=False)
"""画面を一旦クリアしないと画像がずれたように描画される"""
def clearWindow(self):
#print "UpdateDrawing"
self.buffer = wx.EmptyBitmap(1280, 708)
dc = wx.BufferedDC(None, self.buffer)
dc.SetBackground(wx.Brush('white'))
dc.Clear() # デバイスコンテキストでクリア
#self.Draw(dc) # 実際の描画処理
# Falseを指定して背景を消さなくしたら画面のちらつきが出なくなった
#self.Refresh(eraseBackground=False)
"""実際の描画処理"""
def Draw(self, dc):
#print "Draw"
dc.SetBrush(wx.Brush(wx.Colour(128, 255, 128)))
dc.SetPen(wx.Pen("RED", 3))
#背景が透過の画像でテストするため、わざと円の描画を大きくしている。
#ウィンドウの背景が。黄緑色?になっている
#つまり 輪郭が赤中が黄緑色?になる
dc.DrawCircle(100, 100, 1000)
if self.bmp is not None:
#引数の数字は画像の位置座標
#Trueで背景透過の画像も透過のままで表示できる
dc.DrawBitmap(self.bmp, self.bmp_pos.x, self.bmp_pos.y, True)
#myobjListで今までドラッグした画像を保存しておいて
#ドラッグ完了すると、ドラッグ中の画像もふくめて
#描画している
"""ドラッグした画像すべてを描画する """
all_obj_draw(dc)
"""ドラッグした画像すべてを描画する """
#myobjListにはMyObjのobjがすべて入っている
def all_obj_draw(dc):
for obj in myobjList:
#obj.setScl()
obj.Draw(dc) # オブジェクトを描画
"""マウス座標と重なってるオブジェクトを返す"""
def FindMyObj(pos):
result = None
myobj_index = 0
for obj in myobjList:
#posマウスの座標と重なっているobjがあればTrue
if obj.HitTest(pos):
myobj_index = myobjList.index(obj)
result = obj
return result, myobj_index
"""マウスドラッグで移動できるオブジェクト用のクラス"""
#そのオブジェクトの描画、座標の管理などをしている
class MyObj():
def __init__(self, bmp, x=0, y=0):
self.bmp = bmp # bitmapを記録
self.pos = wx.Point(x, y) # 表示位置を記録
self.diff_pos = wx.Point(0, 0)
self.width = self.bmp.GetWidth()
self.height = self.bmp.GetHeight()
self.scl = 1
#以下のコードだと引数の大きさに画像がちぎれて来るだけ
#self.bmp.SetSize((50,50))
#画像のサイズ変更
#self.bmp = scale_bitmap(self.bmp, 150, 150)
"""与えられた座標とアタリ判定して結果を返す"""
def HitTest(self, pos):
#pntはマウスpos selfはオブジェクト
#で、オブジェクト内にマウスが入っていればTrue
rect = self.GetRect() # 矩形領域を取得
return rect.InsideXY(pos.x, pos.y) # 座標が矩形内に入ってるか調べる
"""画像の表示位値から画像の幅高さが入る矩形領域を返す"""
def GetRect(self):
return wx.Rect(self.pos.x, self.pos.y,
self.bmp.GetWidth(), self.bmp.GetHeight())
"""マウス座標と自分の座標の相対値を記録。"""
""" この情報がないと、画像をドラッグした時の表示位置がしっくりこない"""
def SavePosDiff(self, pnt):
#pnt:マウス座標
self.diff_pos.x = self.pos.x - pnt.x
self.diff_pos.y = self.pos.y - pnt.y
"""与えられたDCを使って画像を描画する"""
def Draw(self, dc):
print "MyObj Draw"
if self.bmp.Ok():
r = self.GetRect() # 矩形領域を取得
print "MyObj Draw self.GetRect()",r
# ペンを設定しないと何故か描画できない
# 私の環境ではok
#dc.SetPen(wx.Pen(wx.BLACK, 4))
# 画像を描画
dc.DrawBitmap(self.bmp, r.x, r.y, True)
#dc.DrawBitmap(self.bmp, self.pos.x, self.pos.y, True)
# 画像枠を描画
dc.SetBrush(wx.TRANSPARENT_BRUSH) # 透明塗り潰し
dc.SetPen(wx.Pen(wx.RED, 1)) # 赤い線を指定
# 矩形を描画
dc.DrawRectangle(r.x, r.y, r.width, r.height)
"""ドラッグアンドドロップ担当クラス"""
"""あとで、MyFrameに組み込まれる """
class MyFileDropTarget(wx.FileDropTarget):
def __init__(self, func):
wx.FileDropTarget.__init__(self)
self.func = func
#self.dc = dc
self.x = 0
self.y = 0
#この関数どこからもよびだされていない。
#内部的によびだされいるのだろう
#ただ単にドロップ時に実行する関数を指定しているだけ
#自動的にドロップ時のfilenamesを取得している
"""ファイルをドロップした時の処理"""
def OnDropFiles(self, x, y, filenames):
print "OnDropFiles"
#実験したらわかるが以下のfor文をとおさないことには実際の
#パスが得られない
for filepath in filenames:
#ファイルをドロップした時に実行される関数を以下に設定
self.evt_foo(filepath, self.func)
#ファイルをドロップした時に実際に実行される関数
def evt_foo(self, filepath, func):
print "evt_foo"
#ファイルパスから直接ビットマップを作成している?
b = wx.Bitmap(filepath)
#画像のファイルパスと座標を受け取っている
obj = MyObj(b, self.x, self.y)
myobjList.append(obj)
self.x += 32
self.y += 32
# 描画更新
func()
#MyFileDropTargetを組み込む関数
def MetaMyFileDropTarget(main_obj, func):
# ファイルドロップの対象をフレーム全体に
#MyFrameにドロップされたら...という設定
main_obj.droptarget = MyFileDropTarget(func)
# ファイルドロップ受け入れを設定
main_obj.SetDropTarget(main_obj.droptarget)
class DropMyBind():
def __init__(self, parent, child_list):
self.key = 89
self.parent = parent
self.drop = child_list[0]
self.resize = child_list[1]
self.foo = self.drop
#メインbind
self.parent.Bind(wx.EVT_KEY_DOWN, self.OnKeyDownEvent)
#サブbind
self.parent.Bind(wx.EVT_LEFT_DOWN, self.OnLeftButtonDownEvent)
self.parent.Bind(wx.EVT_LEFT_UP, self.OnLeftButtonUpEvent)
self.parent.Bind(wx.EVT_MOTION, self.OnMotionEvent)
def OnKeyDownEvent(self, event):
print "bind OnKeyDownEvent"
#g71 c67 x88 y89
self.key = event.GetKeyCode()
print self.key
#resizeの作業をする
#dropの作業をする場合は drawで parent.bd.key = 89 として
#ここのkeyを89にする。 dropからdrawに行く場合も同様
#キーzがおされた
if 90==self.key:
self.foo = self.resize
print "bind self.key", self.key
#drop の作業をする
#キーyがおされた
if 89==self.key:
self.foo = self.drop
print "bind self.key", self.key
#xキーがおされた.ダミー処理
if 88==self.key:
pass
print "bind self.key", self.key
#キーにより書くイベント処理関数に分岐
self.foo.OnKeyDownEvent(event)
def OnLeftButtonDownEvent(self, event):
print "bind OnLeftButtonDownEvent"
self.foo.OnLeftButtonDownEvent(event)
def OnLeftButtonUpEvent(self, event):
self.foo.OnLeftButtonUpEvent(event)
def OnMotionEvent(self, event):
self.foo.OnMotionEvent(event)
class DropAction():
def __init__(self, parent):
print "dropAction"
# マウスドラッグ処理用の変数を確保
self.parent = parent
self.drag_myobj = None
self.drag_start_pos = wx.Point(0, 0)
self.key = 0
"""マウスの左ボタンが押された時の処理"""
def OnLeftButtonDownEvent(self, event):
print "drop OnLeftButtonDownEvent"
# マウス座標を取得
pos = event.GetPosition()
"""マウス座標と重なってるオブジェクトを取得"""
myobj_tuple = FindMyObj(pos)
myobj = myobj_tuple[0]
#objが空でないなら、何かobjがあったら
#isのほうが==より処理がはやい
if myobj is not None:
#ドラッグ移動するオブジェクトを記憶
self.drag_myobj = myobj
# ドラッグ開始時のマウス座標を記録
#以下の変数どこにもつかわれていない
self.drag_start_pos = pos
#マウス座標と自分の座標の相対値を記録。"""
#自分:myobjの画像のことだろう
#posでマウスposを引数にしている
self.drag_myobj.SavePosDiff(pos)
"""マウスの左ボタンが離された時の処理"""
def OnLeftButtonUpEvent(self, event):
print "drop_OnLeftButtonUpEvent"
if self.drag_myobj is not None:
pos = event.GetPosition()
self.drag_myobj.pos.x = pos.x + self.drag_myobj.diff_pos.x
self.drag_myobj.pos.y = pos.y + self.drag_myobj.diff_pos.y
print self.drag_myobj.pos.x , self.drag_myobj.pos.y
self.drag_myobj = None
#parentの描画更新の関数の呼び出し
self.parent.clearWindow()
self.parent.InitBuffer()
"マウスカーソルが動いた時の処理"""
def OnMotionEvent(self, event):
print "drop_OnMotionEvent"
if self.drag_myobj is None:
# ドラッグしてるオブジェクトが無いなら処理しない
return
# ドラッグしてるオブジェクトの座標値をマウス座標で更新
pos = event.GetPosition()
self.drag_myobj.pos.x = pos.x + self.drag_myobj.diff_pos.x
self.drag_myobj.pos.y = pos.y + self.drag_myobj.diff_pos.y
#parentの描画更新の関数の呼び出し
self.parent.clearWindow()
self.parent.InitBuffer() # 描画更新
#Lキーをおしてファイルダイヤログをひらく
def OnKeyDownEvent(self, event):
print "drop_OnKeyDownEvent"
self.key = event.GetKeyCode()
print self.key
# キーLがおされた
if 76==self.key:
self.openDialog()
def openDialog(self):
dp = "E:\MyBackups\goolgedrive \myprg_main\python_my_prg\wxpython\dc\gazou_drop"
#osのファイルダイヤログをよびだす
# 引数 dp 開くフォルダ "":デフォルトのファイル名
# *.png : pngファイルだけ表示
"""Dialogの親は、parentになる"""
d = wx.FileDialog(self.parent, "画像ファイル", dp,"","*.png")
d.ShowModal()
d.Destroy()
class ResizeAction():
#def __init__(self, bmp):
def __init__(self, parent):
print "ResizeAction"
self.drag_myobj = None #bitmap画像の事
self.drag_start_pos = wx.Point(0, 0)
self.diff_pos = wx.Point(0, 0)
self.bmp = None
self.bmp_pos = wx.Point(0, 0)
self.key = 0
self.parent = parent
#self.parent = parent
self.width = 0
self.height = 0
self.myobj_index = 0
self.gs = None
def OnKeyDownEvent(self, event):
print "resize OnKeyDownEvent"
def OnLeftButtonDownEvent(self, event):
print "resize_OnLeftButtonDownEvent"
# マウス座標を取得
pos = event.GetPosition()
"""マウス座標と重なってるオブジェクトを取得"""
myobj_tuple = FindMyObj(pos)
myobj = myobj_tuple[0]
self.myobj_index = myobj_tuple[1]
if myobj is not None:
self.drag_start_pos = pos
self.drag_myobj = myobj
self.bmp = self.drag_myobj.bmp
self.bmp_pos = myobj.pos
self.parent.bmp_pos = myobj.pos
#一旦リサイズする画像をリストから削除、この画像は
#parentのDrawBitmapで画像表示
del myobjList[self.myobj_index]
self.gs = GazouScale(self.bmp)
def OnLeftButtonUpEvent(self, event):
print "resize_OnLeftButtonUpEvent"
self.drag_myobj = None
#myobjListにこの画像を再び追加、この画像は、all_obj_drawから描画
obj = MyObj(self.bmp, self.bmp_pos.x, self.bmp_pos.y)
myobjList.append(obj)
self.parent.bmp = None
#parentの描画更新の関数の呼び出し
self.parent.clearWindow()
self.parent.InitBuffer()
def OnMotionEvent(self, event):
print "resize OnMotionEvent"
if self.drag_myobj is None:
# ドラッグしてるオブジェクトが無いなら処理しない
return
pos = event.GetPosition()
self.diff_pos = pos - self.drag_start_pos
self.bmp = self.gs.scale_bitmap(self.diff_pos)
self.parent.bmp = self.gs.scale_bitmap(self.diff_pos)
#parentの描画更新の関数の呼び出し
self.parent.clearWindow()
self.parent.InitBuffer() # 描画更新
""" 画像のリサイズ """
#myobjListのbmpをリサイズする方法もあるが、これだとリサイズすると画像が
#なぜかくずれてくる。で、一旦myobjListからbmpだけを取り出し、リサイズ
# そして又myobjListにもどしている。
class GazouScale():
def __init__(self, bmp):
print "GazouScale"
self.bmp = bmp
#self.parent = parent
self.width = self.bmp.GetWidth()
self.height = self.bmp.GetHeight()
#画像のリサイズ
def scale_bitmap(self, diff_pos):
print "scale_bitmap"
#5 ドラッグに対してあまりdiff_posが大きくならにようにする
self.width += diff_pos.x/5
self.height += diff_pos.y/5
#あまり小さくなるとエラーを起こす
if self.width < 50:
self.width = 50
if self.height < 50:
self.height = 50
print "self.width",self.width
print "self.height",self.height
image = wx.ImageFromBitmap(self.bmp)
#下の行のコードでは一部透過とならない所ができる
#image = image.Scale(self.width, self.height, wx.IMAGE_QUALITY_HIGH)
image = image.Scale(self.width, self.height)
result = wx.BitmapFromImage(image)
return result
if __name__ == '__main__':
app = wx.App(False)
MyFrame(None, -1, "Draw Test 4", size=(800, 600))
app.MainLoop()
0 件のコメント:
コメントを投稿