はねまわるウインドウな例

つかまえられないメインフォーム

tags:gui, tips
created:2006-03-12T12:45:52

勝手に動き回る迷惑なGUIを作成してみました。

ファイルリスト

  • bounds.py
  • wx_utils.py
  • shape_image.png
  • setup.py

resource.xrcは今回ありません。

スクリーンショット

動作中の様子:

../../../_images/bounds.png

リソース内容

今回XRCを使っていません。

img2pyを用いて、「shape_image.png」を Pythonコードに変換した「shape_image.py」から 画像を取り出して使っています。

img2pyの使い方

以下のようにすると、

img2py shape_image.png shape_image.py

イメージからpythonコードを生成します。

こうして作成されたコードはモジュールとして利用でき、 以下のように利用できます。

import shape_image
bmp = shape_image.getBitmap()

bmpはビットマップオブジェクト(wxBitmap)インスタンスです。

メインコード

bounds.py

#!/usr/bin/local/python
# -*- coding: utf-8 -*-

import sys
import os
import wx
import wx_utils

import shape_image

import math

class MainFrame(wx.Frame):
  u"""MainFrame class deffinition.
  """
  binder = wx_utils.bind_manager()

  def __init__(self, parent=None):
    wx.Frame.__init__(self, parent, style = wx.FRAME_SHAPED | wx.NO_BORDER)
    self.bmp = shape_image.getBitmap()
    self.SetClientSize(self.bmp.GetSize())
    self.SetShape(wx.RegionFromBitmap(self.bmp))
    self.binder.bindall(self)
    self.offset = tuple(i/2.0 for i in self.bmp.GetSize())
    self.position =  tuple(i/2.0 for i in wx.GetDisplaySize())
    self.radius = self.offset[0]
    self.xmin = self.radius
    self.xmax = wx.GetDisplaySize()[0] - self.radius
    self.ymin = self.radius
    self.ymax = wx.GetDisplaySize()[1] - self.radius
    self.vector = (0.0, 0.0)
    self.timer = wx.Timer(self)
    self.timer.Start(1)

  def push(self, new):
    nx, ny = new
    vx, vy = self.vector
    self.vector = (vx*0.99+nx, vy*0.99+ny)

  @binder(wx.EVT_TIMER)
  def OnUpdate(self, event):
    px, py = self.position
    vx, vy = self.vector
    px += vx
    py += vy
    self.position = (px, py)
    fx, fy = 0,0
    if px<self.xmin:
      fx = self.xmin-px
    if px>self.xmax:
      fx = self.xmax-px
    if py<self.ymin:
      fy = self.ymin-py
    if py>self.ymax:
      fy = self.ymax-py
    self.push((fx,fy))
    tx, ty = self.offset
    self.SetPosition((px-tx, py-ty))

  @binder(wx.EVT_LEFT_DOWN)
  def OnLeftDown(self, event):
    wx.SafeShowMessage(u'メッセージ', u'つかまった!')
    self.Close()

  @binder(wx.EVT_MOTION)
  def OnMouseOver(self, event):
    x, y = self.offset
    px, py = event.GetPosition()
    x -= px
    y -= py
    distance = math.sqrt(x*x+y*y)
    rate = (self.radius - distance)/distance
    fx, fy = x*rate, y*rate
    self.push((fx,fy))

  @binder(wx.EVT_PAINT)
  def OnPaint(self, event):
    dc = wx.PaintDC(self)
    dc.DrawBitmap(self.bmp, 0,0, True)

# startup application.
if __name__=='__main__':
  app = wx.App(False)
  frame = MainFrame()
  app.SetTopWindow(frame)
  frame.Show()
  app.MainLoop()

解説

概要

シェイプドウインドウやタイマーイベント、 マウスモーション検知イベント利用のサンプルになっています。

マウスカーソルを検知すると逃げる方向に最短方向に逃げます。

ペナルティ法?の衝突シミュレーション演算を入れてますので ビリヤードのようにリアルに跳ね返ります。

一応ウインドウのリージョン内で左クリックできるとメッセージが出て終了しますが、 私は成功出来たことがありません。

タイマーイベントは10ms以下の周期はガタガタと揺れまくりです。

こういった高頻度のタイマーイベントの処理にはもうひとつwx.EVT_IDLEがありますが、 こちらはベストの最短頻度で処理をさせたい時に便利です。

ただし、周期はばらばらです。