wxPython ウインドウ内をボールが跳ね回るグラフィック描画してみる

wx.Timer、wx.BufferedDCを使ってウインドウ内をボールが跳ね回るグラフィックを描画してみる。昔々に何かのプログラム例でみたような気がすることをやってみた。これを発展させればテーブルテニスゲームができるはずだなあ。ということで、いつものごとくメモしておく。

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

import wx

class Ball(object):
    def __init__(self, panel, x, y, color):
        self.pos_x = x
        self.pos_y = y
        self.dir_x = 1
        self.dir_y = 1
        self.pos_dif = 20
        self.pos_x_max, self.pos_y_max = panel.GetSize()
        self.color = color

    def Move(self):
        if self.dir_x > 0:
            self.pos_x += self.pos_dif
        else:
            self.pos_x -= self.pos_dif

        if self.pos_x > self.pos_x_max:
            self.pos_x -= self.pos_dif
            self.dir_x = -1
        
        if self.pos_x < 0:
            self.pos_x += self.pos_dif
            self.dir_x = 1

        if self.dir_y > 0:
            self.pos_y += self.pos_dif
        else:
            self.pos_y -= self.pos_dif

        if self.pos_y > self.pos_y_max:
            self.pos_y -= self.pos_dif
            self.dir_y = -1
        
        if self.pos_y < 0:
            self.pos_y += self.pos_dif
            self.dir_y = 1

    def Draw(self, dc):
        dc.SetPen(wx.Pen(self.color))
        dc.SetBrush(wx.Brush(self.color))
        dc.DrawCircle(self.pos_x, self.pos_y, 10)

class MyWindow(wx.Frame): 
    def __init__(self, parent=None, id=-1, title=None):
        wx.Frame.__init__(self, parent, id, title)
        self.panel = wx.Panel(self, size=(300, 200))
        self.panel.SetBackgroundColour('WHITE')
        self.Fit() 

        self.ball1 = Ball(self.panel, 150, 100, 'red')
        self.ball2 = Ball(self.panel, 100,  50, 'blue')
        self.ball3 = Ball(self.panel,  50, 150, 'green')

        self.Bind(wx.EVT_CLOSE, self.CloseWindow)

        self.cdc = wx.ClientDC(self.panel)
        w, h = self.panel.GetSize()
        self.bmp = wx.EmptyBitmap(w,h)

        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnTimer)
        self.timer.Start(100)

    def CloseWindow(self, event):
        self.timer.Stop()
        wx.Exit()

    def OnTimer(self, event):

        self.bdc = wx.BufferedDC(self.cdc, self.bmp)
        self.bdc.Clear()

        self.ball1.Move()
        self.ball2.Move()
        self.ball3.Move()

        self.ball1.Draw(self.bdc)
        self.ball2.Draw(self.bdc)
        self.ball3.Draw(self.bdc)

        self.bdc.DrawText("Moving Ball",110,70)
        self.cdc.DrawBitmap(self.bmp,0,0)

if __name__ == '__main__':
    app = wx.PySimpleApp() 
    w = MyWindow(title='wxgr-ball')
    w.Center()
    w.Show()
    app.MainLoop()

プログラムを実行すると

  • ボールを3個にしたので、Ballクラスを作って楽できるようにした。
  • self.bdc = wx.BufferedDC(self.cdc, self.bmp)はOnTime関数内に記述しないと、画面が真っ白、なんでだろう?なので、__init__に追い出すことができなかった。まだまだ理解が足りないなあ。
  • 終了するときはWindow右上のXをクリックするのだが、TimerをStopさせないとエラーポップアップがものすごい勢いで表示される。EVT_CLOSEを捕捉してTimerをStopさせるようにした。

実験環境

  • Windows XP
  • Python 2.5.4
  • wxPython 2.9.1.1

参考にしたリンク