How to create an animated drawing (Phoenix)

Keywords : Drawing, Animation, Motion, Bubbles, Tetris, BufferedPaintDC, GCDC, KeyCode.


Demonstrating :

Tested py3.x, wx4.x and Win10.

Are you ready to use some samples ? ;)

Test, modify, correct, complete, improve and share your discoveries ! (!)


Bubbles toy

img_bubbles_toy.png

Here's a fun little toy program.

It's a little piece of interactive art.

When you run it:

I thought it was cute, at any rate !

If you add anything to the program, (pretty easy to do!,) please, by all means, put your additions here !

Note that this code demonstrates DoubleBufferedDrawing on platforms where such is needed for flicker-free animation.

   1 # bubbles_toy.py
   2 
   3 import wx
   4 import math
   5 import random
   6 
   7 # class MyBubble
   8 # class MyBubblePanel
   9 # class MyBubbleFrame
  10 
  11 #---------------------------------------------------------------------------
  12 
  13 class MyBubble(object):
  14     def __init__(self, x, y, death_size=25, color="GREEN", grow_speed=0.1):
  15 
  16         self.x = x
  17         self.y = y
  18         self.radius = 5
  19         self.death_size = death_size
  20         self.color = color
  21         self.grow_speed = grow_speed
  22 
  23     #-----------------------------------------------------------------------
  24 
  25     def go(self, bubbles):
  26         growth = math.log(self.radius) * self.grow_speed
  27         self.radius = self.radius + growth
  28         if self.radius >= self.death_size:
  29             bubbles.remove(self)
  30 
  31 
  32     def draw(self, dc, bubbles):
  33         dc.SetPen(wx.Pen(self.color, 2))
  34         dc.DrawCircle(int(self.x), int(self.y), int(self.radius))
  35         self.go(bubbles)
  36 
  37 #---------------------------------------------------------------------------
  38 
  39 class MyBubblePanel(wx.Window):
  40     def __init__(self, parent):
  41         wx.Window.__init__(self, parent)
  42 
  43         self.bubbles = []
  44         self.marks = []
  45         self.last_pos = self.ScreenToClient(wx.GetMousePosition())
  46         self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
  47         self.SetBackgroundColour("WHITE")
  48 
  49         self.Bind(wx.EVT_PAINT, self.on_paint)
  50         self.Bind(wx.EVT_SIZE, self.on_size)
  51 
  52         self.Bind(wx.EVT_MOTION, self.on_motion)
  53         self.Bind(wx.EVT_LEFT_UP, self.on_left_up)
  54         self.Bind(wx.EVT_RIGHT_UP, self.on_right_up)
  55         self.Bind(wx.EVT_CHAR, self.on_character)
  56 
  57         wx.CallLater(200, self.SetFocus)
  58 
  59     #-----------------------------------------------------------------------
  60 
  61     def on_size(self, event):
  62         width, height = self.GetClientSize()
  63         self._buffer = wx.Bitmap(width, height)
  64         self.update_drawing()
  65 
  66 
  67     def update_drawing(self):
  68         self.Refresh(False)
  69 
  70 
  71     def on_paint(self, event):
  72         # dc = wx.AutoBufferedPaintDC(self)
  73         # or
  74         dc = wx.BufferedPaintDC(self)
  75         dc = wx.GCDC(dc)
  76         dc.Clear()
  77 
  78         x, y = self.ScreenToClient(wx.GetMousePosition())
  79 
  80         self.draw_x(dc, x, y, 4)
  81         for bubble in self.bubbles:
  82             bubble.draw(dc, self.bubbles)
  83         self.draw_marks(dc)
  84 
  85 
  86     def draw_x(self, dc, x, y, line_width):
  87         dc.SetPen(wx.Pen("BLACK", line_width))
  88         dc.DrawLine(x-5, y-5, x+5, y+5)  # \
  89         dc.DrawLine(x-5, y+5, x+5, y-5)  # /
  90 
  91 
  92     def draw_marks(self, dc):
  93         chains = {}
  94         for (letter, x, y) in self.marks:
  95             self.draw_x(dc, x, y, 2)
  96             dc.DrawText(letter, x-3, y-28)
  97             chains.setdefault(letter, []).append(wx.Point(x,y))
  98         for (key, points) in chains.items():
  99             if len(points) > 1:
 100                 if key == key.upper() or len(points) == 2:
 101                     dc.DrawLines(points)
 102                 else:
 103                     dc.DrawSpline(points)
 104 
 105 
 106     def on_motion(self, event):
 107         x, y = event.GetPosition()
 108         motion_score = (abs(x - self.last_pos.x) + abs(y - self.last_pos.y))
 109         self.last_pos = wx.Point(x, y)
 110         if random.randint(0, motion_score) > 5:
 111             self.bubbles.append(MyBubble(x, y))
 112             if random.randint(0, 100) == 0:
 113                 self.bubbles.append(
 114                     MyBubble(x, y, color="PURPLE", death_size=100, grow_speed=0.5))
 115 
 116 
 117     def on_left_up(self, event):
 118         self.bubbles.append(MyBubble(event.GetX(), event.GetY(),
 119                              color="YELLOW", death_size=50, grow_speed=0.1))
 120 
 121 
 122     def on_right_up(self, event):
 123         self.bubbles.append(MyBubble(event.GetX(), event.GetY(),
 124                              color="BLUE", death_size=80, grow_speed=0.6))
 125 
 126 
 127     def on_character(self, event):
 128         key = event.GetKeyCode()
 129         if key==27:   # Esc key
 130             self.marks = []
 131         else:
 132             x, y = self.ScreenToClient(wx.GetMousePosition())
 133             self.marks.append( (chr(event.GetKeyCode()), x, y) )
 134 
 135 #---------------------------------------------------------------------------
 136 
 137 class MyBubbleFrame(wx.Frame):
 138     def __init__(self, *args, **kw):
 139         wx.Frame.__init__(self, *args, **kw)
 140 
 141         self.SetIcon(wx.Icon('wxwin.ico'))
 142 
 143         self.Bind(wx.EVT_CLOSE, self.on_close)
 144         self.Bind(wx.EVT_TIMER, self.on_timer)
 145 
 146         self.panel = MyBubblePanel(self)
 147         self.timer = wx.Timer(self)
 148         self.timer.Start(20)
 149 
 150     #-----------------------------------------------------------------------
 151 
 152     def on_close(self, event):
 153         self.timer.Stop()
 154         self.Destroy()
 155 
 156 
 157     def on_timer(self, event):
 158         self.panel.update_drawing()
 159 
 160 #---------------------------------------------------------------------------
 161 
 162 app = wx.App(False)
 163 frame = MyBubbleFrame(None, -1, "Bubbles toy !")
 164 frame.Show(True)
 165 app.MainLoop()


Tetris

img_tetris.png

   1 # tetris.py
   2 
   3 """
   4 ZetCode wxPython tutorial.
   5 
   6 This is Tetris game clone in wxPython.
   7 
   8 Author : Jan Bodnar
   9 Website : www.zetcode.com
  10 Link : http://zetcode.com/wxpython/thetetrisgame/
  11 Last modified : May 2018
  12 """
  13 
  14 import wx
  15 import random
  16 
  17 # class Tetris
  18 # class Board
  19 # class Tetrominoes
  20 # class Shape
  21 
  22 #---------------------------------------------------------------------------
  23 
  24 class Tetris(wx.Frame):
  25     """
  26     ...
  27     """
  28     def __init__(self, parent):
  29         wx.Frame.__init__(self, parent, size=(220, 420),
  30             style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER ^ wx.MAXIMIZE_BOX)
  31 
  32         self.initFrame()
  33 
  34     #-----------------------------------------------------------------------
  35 
  36     def initFrame(self):
  37         """
  38         ...
  39         """
  40 
  41         self.statusbar = self.CreateStatusBar()
  42         self.statusbar.SetStatusText('0')
  43         self.board = Board(self)
  44         self.board.SetFocus()
  45         self.board.start()
  46 
  47         #------------
  48 
  49         # Creating the menubar.
  50         menu_bar = wx.MenuBar()
  51 
  52         # Setting up the menu.
  53         file_menu = wx.Menu()
  54 
  55         # wx.ID_ABOUT and wx.ID_EXIT are standard IDs provided by wxWidgets.
  56         item = file_menu.Append(wx.ID_ABOUT, "&About", "Information about Tetris.")
  57         self.Bind(wx.EVT_MENU, self.OnAbout, item)
  58         file_menu.AppendSeparator()
  59         item = file_menu.Append(wx.ID_EXIT, "E&xit", "Exit Tetris.")
  60         self.Bind(wx.EVT_MENU, self.OnQuit, item)
  61 
  62         # Create the "Help" top menu.
  63         help_menu = wx.Menu()
  64         item = help_menu.Append(wx.ID_HELP, "&Shortcuts", "Shortcuts for Tetris.")
  65         self.Bind(wx.EVT_MENU, self.OnHelp, item)
  66 
  67         # Adding the "file_menu" to the menu bar.
  68         menu_bar.Append(file_menu, "&File")
  69         menu_bar.Append(help_menu, "&Help")
  70 
  71         # Adding the menu bar to the frame content.
  72         self.SetMenuBar(menu_bar)
  73 
  74         #------------
  75 
  76         self.SetIcon(wx.Icon("wxwin.ico"))
  77         self.SetTitle("Tetris")
  78         self.CenterOnScreen(wx.BOTH)
  79 
  80 
  81     def OnQuit(self, event):
  82         self.Destroy()
  83 
  84 
  85     def OnAbout(self, event):
  86         dlg = wx.MessageDialog(self,
  87                                "This is a small game.\n",
  88                                "About Tetris",
  89                                wx.OK | wx.CENTRE | wx.ICON_INFORMATION)
  90         dlg.ShowModal()
  91         dlg.Destroy()
  92 
  93 
  94     def OnHelp(self, event):
  95         dlg = wx.MessageDialog(self,
  96                                "AZERTY keyboard :\n"
  97                                "--------------------\n"
  98                                "P \t---------> \tPause\n"
  99                                "S \t---------> \tLEFT\n"
 100                                "F \t---------> \tRIGHT\n"
 101                                "D \t---------> \tDOWN (rotated right)\n"
 102                                "E \t----------> \tUP (rotated left)\n"
 103                                "SPACE \t----> \tAccelerate\n"
 104                                "D \t---------> \tOne line down\n",
 105                                "Shortcuts",
 106                                wx.OK | wx.CENTRE | wx.ICON_INFORMATION)
 107         dlg.ShowModal()
 108         dlg.Destroy()
 109 
 110 #---------------------------------------------------------------------------
 111 
 112 class Board(wx.Panel):
 113     """
 114     ...
 115     """
 116 
 117     BoardWidth = 10
 118     BoardHeight = 22
 119     Speed = 300
 120     ID_TIMER = 1
 121 
 122     def __init__(self, *args, **kw):
 123         super(Board, self).__init__(*args, **kw)
 124 
 125         self.initBoard()
 126 
 127         #------------
 128 
 129         # Delete flickers.
 130         if wx.Platform == "__WXMSW__":
 131             self.SetDoubleBuffered(True)
 132 
 133     #-----------------------------------------------------------------------
 134 
 135     def initBoard(self):
 136         """
 137         ...
 138         """
 139 
 140         self.timer = wx.Timer(self, Board.ID_TIMER)
 141         self.isWaitingAfterLine = False
 142         self.curPiece = Shape()
 143         self.nextPiece = Shape()
 144         self.curX = 0
 145         self.curY = 0
 146         self.numLinesRemoved = 0
 147         self.board = []
 148 
 149         self.isStarted = False
 150         self.isPaused = False
 151 
 152         self.Bind(wx.EVT_PAINT, self.OnPaint)
 153         self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
 154         self.Bind(wx.EVT_TIMER, self.OnTimer, id=Board.ID_TIMER)
 155 
 156         self.clearBoard()
 157 
 158 
 159     def shapeAt(self, x, y):
 160         """
 161         ...
 162         """
 163 
 164         return self.board[(y * Board.BoardWidth) + x]
 165 
 166 
 167     def setShapeAt(self, x, y, shape):
 168         """
 169         ...
 170         """
 171 
 172         self.board[(y * Board.BoardWidth) + x] = shape
 173 
 174 
 175     def squareWidth(self):
 176         """
 177         ...
 178         """
 179 
 180         return self.GetClientSize().GetWidth() // Board.BoardWidth
 181 
 182 
 183     def squareHeight(self):
 184         """
 185         ...
 186         """
 187 
 188         return self.GetClientSize().GetHeight() // Board.BoardHeight
 189 
 190 
 191     def start(self):
 192         """
 193         ...
 194         """
 195 
 196         if self.isPaused:
 197             return
 198 
 199         self.isStarted = True
 200         self.isWaitingAfterLine = False
 201         self.numLinesRemoved = 0
 202         self.clearBoard()
 203 
 204         self.newPiece()
 205         self.timer.Start(Board.Speed)
 206 
 207 
 208     def pause(self):
 209         """
 210         ...
 211         """
 212 
 213         if not self.isStarted:
 214             return
 215 
 216         self.isPaused = not self.isPaused
 217         statusbar = self.GetParent().statusbar
 218 
 219         if self.isPaused:
 220             self.timer.Stop()
 221             statusbar.SetStatusText('paused')
 222         else:
 223             self.timer.Start(Board.Speed)
 224             statusbar.SetStatusText(str(self.numLinesRemoved))
 225 
 226         self.Refresh()
 227 
 228 
 229     def clearBoard(self):
 230         """
 231         ...
 232         """
 233 
 234         for i in range(Board.BoardHeight * Board.BoardWidth):
 235             self.board.append(Tetrominoes.NoShape)
 236 
 237 
 238     def OnPaint(self, event):
 239         """
 240         ...
 241         """
 242 
 243         dc = wx.PaintDC(self)
 244 
 245         size = self.GetClientSize()
 246         boardTop = size.GetHeight() - Board.BoardHeight * self.squareHeight()
 247 
 248         for i in range(Board.BoardHeight):
 249             for j in range(Board.BoardWidth):
 250 
 251                 shape = self.shapeAt(j, Board.BoardHeight - i - 1)
 252 
 253                 if shape != Tetrominoes.NoShape:
 254                     self.drawSquare(dc,
 255                         0 + j * self.squareWidth(),
 256                         boardTop + i * self.squareHeight(), shape)
 257 
 258         if self.curPiece.shape() != Tetrominoes.NoShape:
 259 
 260             for i in range(4):
 261 
 262                 x = self.curX + self.curPiece.x(i)
 263                 y = self.curY - self.curPiece.y(i)
 264 
 265                 self.drawSquare(dc, 0 + x * self.squareWidth(),
 266                     boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(),
 267                     self.curPiece.shape())
 268 
 269 
 270     def OnKeyDown(self, event):
 271         """
 272         ...
 273         """
 274 
 275         if not self.isStarted or self.curPiece.shape() == Tetrominoes.NoShape:
 276             event.Skip()
 277             return
 278 
 279         keycode = event.GetKeyCode()
 280         print("Keycode :", keycode)
 281 
 282         if keycode == ord('P') or keycode == ord('p'):
 283             self.pause()
 284             return
 285 
 286         if self.isPaused:
 287             return
 288 
 289         #-----------------
 290         # AZERTY keyboard.
 291         #-----------------
 292         # elif keycode == wx.WXK_LEFT:
 293         elif keycode == ord('S') or keycode == ord('s'):
 294             self.tryMove(self.curPiece, self.curX - 1, self.curY)
 295 
 296         # elif keycode == wx.WXK_RIGHT:
 297         elif keycode == ord('F') or keycode == ord('f'):
 298             self.tryMove(self.curPiece, self.curX + 1, self.curY)
 299 
 300         # elif keycode == wx.WXK_DOWN:
 301         elif keycode == ord('D') or keycode == ord('d'):
 302             self.tryMove(self.curPiece.rotatedRight(), self.curX, self.curY)
 303 
 304         # elif keycode == wx.WXK_UP:
 305         elif keycode == ord('E') or keycode == ord('e'):
 306             self.tryMove(self.curPiece.rotatedLeft(), self.curX, self.curY)
 307 
 308         elif keycode == wx.WXK_SPACE:
 309             self.dropDown()
 310 
 311         elif keycode == ord('R') or keycode == ord('r'):
 312             self.oneLineDown()
 313 
 314         else:
 315             event.Skip()
 316 
 317 
 318     def OnTimer(self, event):
 319         """
 320         ...
 321         """
 322 
 323         if event.GetId() == Board.ID_TIMER:
 324 
 325             if self.isWaitingAfterLine:
 326                 self.isWaitingAfterLine = False
 327                 self.newPiece()
 328 
 329             else:
 330                 self.oneLineDown()
 331 
 332         else:
 333             event.Skip()
 334 
 335 
 336     def dropDown(self):
 337         """
 338         ...
 339         """
 340 
 341         newY = self.curY
 342 
 343         while newY > 0:
 344             if not self.tryMove(self.curPiece, self.curX, newY - 1):
 345                 break
 346             newY -= 1
 347 
 348         self.pieceDropped()
 349 
 350 
 351     def oneLineDown(self):
 352         """
 353         ...
 354         """
 355 
 356         if not self.tryMove(self.curPiece, self.curX, self.curY - 1):
 357             self.pieceDropped()
 358 
 359 
 360     def pieceDropped(self):
 361         """
 362         ...
 363         """
 364 
 365         for i in range(4):
 366 
 367             x = self.curX + self.curPiece.x(i)
 368             y = self.curY - self.curPiece.y(i)
 369             self.setShapeAt(x, y, self.curPiece.shape())
 370 
 371         self.removeFullLines()
 372 
 373         if not self.isWaitingAfterLine:
 374             self.newPiece()
 375 
 376 
 377     def removeFullLines(self):
 378         """
 379         ...
 380         """
 381 
 382         numFullLines = 0
 383 
 384         statusbar = self.GetParent().statusbar
 385 
 386         rowsToRemove = []
 387 
 388         for i in range(Board.BoardHeight):
 389             n = 0
 390             for j in range(Board.BoardWidth):
 391                 if not self.shapeAt(j, i) == Tetrominoes.NoShape:
 392                     n = n + 1
 393 
 394             if n == 10:
 395                 rowsToRemove.append(i)
 396 
 397         rowsToRemove.reverse()
 398 
 399         for m in rowsToRemove:
 400             for k in range(m, Board.BoardHeight):
 401                 for l in range(Board.BoardWidth):
 402                         self.setShapeAt(l, k, self.shapeAt(l, k + 1))
 403 
 404             numFullLines = numFullLines + len(rowsToRemove)
 405 
 406             if numFullLines > 0:
 407 
 408                 self.numLinesRemoved = self.numLinesRemoved + numFullLines
 409                 statusbar.SetStatusText(str(self.numLinesRemoved))
 410                 self.isWaitingAfterLine = True
 411                 self.curPiece.setShape(Tetrominoes.NoShape)
 412                 self.Refresh()
 413 
 414 
 415     def newPiece(self):
 416         """
 417         ...
 418         """
 419 
 420         self.curPiece = self.nextPiece
 421         statusbar = self.GetParent().statusbar
 422         self.nextPiece.setRandomShape()
 423 
 424         self.curX = Board.BoardWidth // 2 + 1
 425         self.curY = Board.BoardHeight - 1 + self.curPiece.minY()
 426 
 427         if not self.tryMove(self.curPiece, self.curX, self.curY):
 428 
 429             self.curPiece.setShape(Tetrominoes.NoShape)
 430             self.timer.Stop()
 431             self.isStarted = False
 432             statusbar.SetStatusText('Game over')
 433 
 434 
 435     def tryMove(self, newPiece, newX, newY):
 436         """
 437         ...
 438         """
 439 
 440         for i in range(4):
 441 
 442             x = newX + newPiece.x(i)
 443             y = newY - newPiece.y(i)
 444 
 445             if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight:
 446                 return False
 447 
 448             if self.shapeAt(x, y) != Tetrominoes.NoShape:
 449                 return False
 450 
 451         self.curPiece = newPiece
 452         self.curX = newX
 453         self.curY = newY
 454         self.Refresh()
 455 
 456         return True
 457 
 458 
 459     def drawSquare(self, dc, x, y, shape):
 460         """
 461         ...
 462         """
 463 
 464         colors = ['#000000', '#CC6666', '#66CC66', '#6666CC',
 465                   '#CCCC66', '#CC66CC', '#66CCCC', '#DAAA00']
 466 
 467         light = ['#000000', '#F89FAB', '#79FC79', '#7979FC',
 468                  '#FCFC79', '#FC79FC', '#79FCFC', '#FCC600']
 469 
 470         dark = ['#000000', '#803C3B', '#3B803B', '#3B3B80',
 471                  '#80803B', '#803B80', '#3B8080', '#806200']
 472 
 473         pen = wx.Pen(light[shape])
 474         pen.SetCap(wx.CAP_PROJECTING)
 475         dc.SetPen(pen)
 476 
 477         dc.DrawLine(x, y + self.squareHeight() - 1, x, y)
 478         dc.DrawLine(x, y, x + self.squareWidth() - 1, y)
 479 
 480         darkpen = wx.Pen(dark[shape])
 481         darkpen.SetCap(wx.CAP_PROJECTING)
 482         dc.SetPen(darkpen)
 483 
 484         dc.DrawLine(x + 1, y + self.squareHeight() - 1,
 485             x + self.squareWidth() - 1, y + self.squareHeight() - 1)
 486         dc.DrawLine(x + self.squareWidth() - 1,
 487         y + self.squareHeight() - 1, x + self.squareWidth() - 1, y + 1)
 488 
 489         dc.SetPen(wx.TRANSPARENT_PEN)
 490         dc.SetBrush(wx.Brush(colors[shape]))
 491         dc.DrawRectangle(x + 1, y + 1, self.squareWidth() - 2,
 492         self.squareHeight() - 2)
 493 
 494 #---------------------------------------------------------------------------
 495 
 496 class Tetrominoes(object):
 497     """
 498     ...
 499     """
 500 
 501     NoShape = 0
 502     ZShape = 1
 503     SShape = 2
 504     LineShape = 3
 505     TShape = 4
 506     SquareShape = 5
 507     LShape = 6
 508     MirroredLShape = 7
 509 
 510 #---------------------------------------------------------------------------
 511 
 512 class Shape(object):
 513     """
 514     ...
 515     """
 516 
 517     coordsTable = (
 518         ((0, 0),     (0, 0),     (0, 0),     (0, 0)),
 519         ((0, -1),    (0, 0),     (-1, 0),    (-1, 1)),
 520         ((0, -1),    (0, 0),     (1, 0),     (1, 1)),
 521         ((0, -1),    (0, 0),     (0, 1),     (0, 2)),
 522         ((-1, 0),    (0, 0),     (1, 0),     (0, 1)),
 523         ((0, 0),     (1, 0),     (0, 1),     (1, 1)),
 524         ((-1, -1),   (0, -1),    (0, 0),     (0, 1)),
 525         ((1, -1),    (0, -1),    (0, 0),     (0, 1))
 526     )
 527 
 528     def __init__(self):
 529         self.coords = [[0,0] for i in range(4)]
 530         self.pieceShape = Tetrominoes.NoShape
 531 
 532         self.setShape(Tetrominoes.NoShape)
 533 
 534     #-----------------------------------------------------------------------
 535 
 536     def shape(self):
 537         """
 538         ...
 539         """
 540 
 541         return self.pieceShape
 542 
 543 
 544     def setShape(self, shape):
 545         """
 546         ...
 547         """
 548 
 549         table = Shape.coordsTable[shape]
 550         for i in range(4):
 551             for j in range(2):
 552                 self.coords[i][j] = table[i][j]
 553 
 554         self.pieceShape = shape
 555 
 556 
 557     def setRandomShape(self):
 558         """
 559         ...
 560         """
 561 
 562         self.setShape(random.randint(1, 7))
 563 
 564 
 565     def x(self, index):
 566         """
 567         ...
 568         """
 569 
 570         return self.coords[index][0]
 571 
 572 
 573     def y(self, index):
 574         """
 575         ...
 576         """
 577 
 578         return self.coords[index][1]
 579 
 580 
 581     def setX(self, index, x):
 582         """
 583         ...
 584         """
 585 
 586         self.coords[index][0] = x
 587 
 588 
 589     def setY(self, index, y):
 590         """
 591         ...
 592         """
 593 
 594         self.coords[index][1] = y
 595 
 596 
 597     def minX(self):
 598         """
 599         ...
 600         """
 601 
 602         m = self.coords[0][0]
 603         for i in range(4):
 604             m = min(m, self.coords[i][0])
 605 
 606         return m
 607 
 608 
 609     def maxX(self):
 610         """
 611         ...
 612         """
 613 
 614         m = self.coords[0][0]
 615         for i in range(4):
 616             m = max(m, self.coords[i][0])
 617 
 618         return m
 619 
 620 
 621     def minY(self):
 622         """
 623         ...
 624         """
 625 
 626         m = self.coords[0][1]
 627         for i in range(4):
 628             m = min(m, self.coords[i][1])
 629 
 630         return m
 631 
 632 
 633     def maxY(self):
 634         """
 635         ...
 636         """
 637 
 638         m = self.coords[0][1]
 639 
 640         for i in range(4):
 641             m = max(m, self.coords[i][1])
 642 
 643         return m
 644 
 645 
 646     def rotatedLeft(self):
 647         """
 648         ...
 649         """
 650 
 651         if self.pieceShape == Tetrominoes.SquareShape:
 652             return self
 653 
 654         result = Shape()
 655         result.pieceShape = self.pieceShape
 656 
 657         for i in range(4):
 658             result.setX(i, self.y(i))
 659             result.setY(i, -self.x(i))
 660 
 661         return result
 662 
 663 
 664     def rotatedRight(self):
 665         """
 666         ...
 667         """
 668 
 669         if self.pieceShape == Tetrominoes.SquareShape:
 670             return self
 671 
 672         result = Shape()
 673         result.pieceShape = self.pieceShape
 674 
 675         for i in range(4):
 676             result.setX(i, -self.y(i))
 677             result.setY(i, self.x(i))
 678 
 679         return result
 680 
 681 #---------------------------------------------------------------------------
 682 
 683 def main():
 684     app = wx.App()
 685     ex = Tetris(None)
 686     ex.Show(True)
 687     app.MainLoop()
 688 
 689 #---------------------------------------------------------------------------
 690 
 691 if __name__ == '__main__':
 692     main()


Radar graph

img_radar_graph.png

   1 # radar_graph.py
   2 
   3 import wx
   4 import math
   5 import random
   6 
   7 # class MyRadarGraph
   8 # class MyFrame
   9 # class MyApp
  10 
  11 #---------------------------------------------------------------------------
  12 
  13 class MyRadarGraph(wx.Window):
  14     """
  15     A simple radar graph that plots a collection of values in the
  16     range of 0-100 onto a polar coordinate system designed to easily
  17     show outliers, etc.  You might use this kind of graph to monitor
  18     some sort of resource allocation metrics, and a quick glance at
  19     the graph can tell you when conditions are good (within some
  20     accepted tolerance level) or approaching critical levels (total
  21     resource consumption).
  22     """
  23     def __init__(self, parent, title, labels):
  24         wx.Window.__init__(self, parent)
  25 
  26         self.title = title
  27         self.labels = labels
  28         self.data = [0.0] * len(labels)
  29         self.titleFont = wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD)
  30         self.labelFont = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL)
  31 
  32         self.InitBuffer()
  33 
  34         self.Bind(wx.EVT_SIZE, self.OnSize)
  35         self.Bind(wx.EVT_PAINT, self.OnPaint)
  36 
  37     #-----------------------------------------------------------------------
  38 
  39     def OnSize(self, evt):
  40         """
  41         When the window size changes we need a new buffer.
  42         """
  43 
  44         self.InitBuffer()
  45 
  46 
  47     def OnPaint(self, evt):
  48         """
  49         This automatically Blits self.buffer to a wx.PaintDC when
  50         the dc is destroyed, and so nothing else needs done.
  51         """
  52 
  53         dc = wx.BufferedPaintDC(self, self.buffer)
  54 
  55 
  56     def InitBuffer(self):
  57         """
  58         Create the buffer bitmap to be the same size as the window,
  59         then draw our graph to it.  Since we use wx.BufferedDC
  60         whatever is drawn to the buffer is also drawn to the window.
  61         """
  62 
  63         w, h = self.GetClientSize()
  64         self.buffer = wx.Bitmap(w, h)
  65         pdc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
  66         dc = wx.GCDC(pdc)
  67         self.DrawGraph(dc)
  68 
  69 
  70     def GetData(self):
  71         """
  72         ...
  73         """
  74 
  75         return self.data
  76 
  77 
  78     def SetData(self, newData):
  79         """
  80         ...
  81         """
  82 
  83         assert len(newData) == len(self.data)
  84         self.data = newData[:]
  85 
  86         # The data has changed, so update the buffer and the window.
  87         pdc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
  88         dc = wx.GCDC(pdc)
  89         self.DrawGraph(dc)
  90 
  91 
  92     def PolarToCartesian(self, radius, angle, cx, cy):
  93         """
  94         ...
  95         """
  96 
  97         x = radius * math.cos(math.radians(angle))
  98         y = radius * math.sin(math.radians(angle))
  99         return (int(cx+x), int(cy-y))
 100 
 101 
 102     def DrawGraph(self, dc):
 103         """
 104         ...
 105         """
 106 
 107         spacer = 10
 108         scaledmax = 150.0
 109 
 110         dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
 111         dc.Clear()
 112         dw, dh = dc.GetSize()
 113 
 114         # Find out where to draw the title and do it.
 115         dc.SetFont(self.titleFont)
 116         tw, th = dc.GetTextExtent(self.title)
 117         dc.DrawText(self.title, int((dw-tw)/2), int(spacer))
 118 
 119         # Find the center of the space below the title.
 120         th = th + 2*spacer
 121         cx = dw/2
 122         cy = (dh-th)/2 + th
 123 
 124         # Calculate a scale factor to use for drawing the graph
 125         # based on the minimum available width or height.
 126         mindim = min(cx, (dh-th)/2)
 127         scale = mindim/scaledmax
 128 
 129         # Draw the graph axis and "bulls-eye" with rings
 130         # at scaled 25, 50, 75 and 100 positions.
 131         dc.SetPen(wx.Pen("black", 1))
 132         dc.SetBrush(wx.TRANSPARENT_BRUSH)
 133         dc.DrawCircle(int(cx), int(cy), int(25*scale))
 134         dc.DrawCircle(int(cx), int(cy), int(50*scale))
 135         dc.DrawCircle(int(cx), int(cy), int(75*scale))
 136         dc.DrawCircle(int(cx), int(cy), int(100*scale))
 137 
 138         dc.SetPen(wx.Pen("black", 2))
 139         dc.DrawLine(int(cx-110*scale), int(cy), int(cx+110*scale), int(cy))
 140         dc.DrawLine(int(cx), int(cy-110*scale), int(cx), int(cy+110*scale))
 141 
 142         # Now find the coordinates for each data point,
 143         # draw the labels, and find the max data point.
 144         dc.SetFont(self.labelFont)
 145         maxval = 0
 146         angle = 0
 147         polypoints = []
 148         for i, label in enumerate(self.labels):
 149             val = self.data[i]
 150             point = self.PolarToCartesian(int(val*scale), int(angle), int(cx), int(cy))
 151             polypoints.append(point)
 152             print(polypoints)
 153             x, y = self.PolarToCartesian(int(125*scale), int(angle), int(cx), int(cy))
 154             dc.DrawText(str(label), int(x), int(y))
 155             if val > maxval:
 156                 maxval = val
 157             angle = (int(angle) + int(360/len(self.labels)))
 158 
 159         # Set the brush color based on the max value
 160         # (green is good, red is bad).
 161         c = "forest green"
 162         if maxval > 70:
 163             c = "yellow"
 164         if maxval > 95:
 165             c = "red"
 166 
 167         # Finally, draw the plot data as a filled polygon.
 168         dc.SetBrush(wx.Brush(c))
 169         dc.SetPen(wx.Pen("navy", 3))
 170         dc.DrawPolygon(polypoints)
 171 
 172 #---------------------------------------------------------------------------
 173 
 174 class MyFrame(wx.Frame):
 175     def __init__(self):
 176         wx.Frame.__init__(self, None,
 177                           title="Radar graph",
 178                           size=(480, 480))
 179 
 180         self.SetIcon(wx.Icon("wxwin.ico"))
 181         self.SetMinSize((320, 320))
 182 
 183         self.plot = MyRadarGraph(self, """Sample "Radar" Plot""",
 184                           ["A", "B", "C", "D", "E", "F", "G", "H"])
 185 
 186         # Set some random initial data values.
 187         data = []
 188         for d in self.plot.GetData():
 189             data.append(random.randint(0, 75))
 190         self.plot.SetData(data)
 191 
 192         # Create a timer to update the data values.
 193         self.Bind(wx.EVT_TIMER, self.OnTimeout)
 194         self.timer = wx.Timer(self)
 195         self.timer.Start(500)
 196 
 197     #-----------------------------------------------------------------------
 198 
 199     def OnTimeout(self, evt):
 200         """
 201         Simulate the positive or negative growth of each data value.
 202         """
 203 
 204         data = []
 205         for d in self.plot.GetData():
 206             val = d + random.uniform(-5, 5)
 207             if val < 0:
 208                 val = 0
 209             if val > 110:
 210                 val = 110
 211             data.append(val)
 212         self.plot.SetData(data)
 213 
 214 #---------------------------------------------------------------------------
 215 
 216 class MyApp(wx.App):
 217     def OnInit(self):
 218 
 219         #------------
 220 
 221         frame = MyFrame()
 222         self.SetTopWindow(frame)
 223         frame.Show(True)
 224 
 225         return True
 226 
 227 #---------------------------------------------------------------------------
 228 
 229 def main():
 230     app = MyApp(redirect=False)
 231     app.MainLoop()
 232 
 233 #---------------------------------------------------------------------------
 234 
 235 if __name__ == "__main__" :
 236     main()


Lines

img_lines.png

   1 # lines.py
   2 
   3 import wx
   4 import random
   5 
   6 # class MyFrame
   7 # class MyApp
   8 
   9 #---------------------------------------------------------------------------
  10 
  11 class MyFrame(wx.Frame):
  12     """
  13     This window displays a button.
  14     """
  15     def __init__(self, parent, id, title):
  16         wx.Frame.__init__(self, None, -1, title,
  17                          wx.DefaultPosition,
  18                          size=(400, 400),
  19                          style=wx.DEFAULT_FRAME_STYLE |
  20                                wx.NO_FULL_REPAINT_ON_RESIZE)
  21 
  22         self.SetIcon(wx.Icon('wxwin.ico'))
  23         self.SetBackgroundColour(wx.WHITE)
  24         self.SetMinSize((220, 220))
  25 
  26         self.Bind(wx.EVT_CLOSE, self.OnQuit)
  27 
  28         self.Bind(wx.EVT_TIMER, self.OnTimer)
  29         self.Bind(wx.EVT_SIZE, self.BuildImage)
  30 
  31         self.Numtimer = 0
  32         self.NumLines = 100
  33 
  34         self.timer = wx.Timer(self)
  35         self.BuildImage()
  36         self.timer.Start(100)
  37 
  38     #-----------------------------------------------------------------------
  39 
  40     def OnQuit(self, event):
  41         """
  42         ...
  43         """
  44 
  45         self.Destroy()
  46 
  47 
  48     def BuildImage(self, event=None):
  49         """
  50         ...
  51         """
  52 
  53         Size = self.GetClientSize()
  54 
  55         # Make new offscreen bitmap : this bitmap will always have the
  56         # current drawing in it, so it can be used to save the image to
  57         # a file, or whatever.
  58         print("making new buffer :", Size)
  59         self._Buffer = wx.Bitmap(Size[0], Size[1])
  60 
  61         dc = wx.MemoryDC()
  62         dc.SelectObject(self._Buffer)
  63         dc = wx.GCDC(dc)
  64 
  65         self.Lines = []
  66 
  67         for i in range(self.NumLines):
  68             x1, y1, x2, y2 = (random.randint(1, max(Size)),
  69                               random.randint(1, max(Size)),
  70                               random.randint(1, max(Size)),
  71                               random.randint(1, max(Size)))
  72 
  73             color = self.random_color()
  74             self.Lines.append([color, (x1 ,y1, x2, y2)])
  75 
  76         dc.Clear()
  77 
  78         for line in self.Lines:
  79             dc.SetPen(wx.Pen(line[0], 2))
  80             dc.DrawLine(*line[1])
  81 
  82 
  83     def OnTimer(self,event):
  84         """
  85         ...
  86         """
  87 
  88         self.Numtimer += 1
  89         print("Timer fired : %i times" % self.Numtimer)
  90 
  91         # Change one color.
  92         self.Lines[random.randrange(self.NumLines)][0] = self.random_color()
  93 
  94         # Update the screen.
  95         dc = wx.MemoryDC()
  96         dc.SelectObject(self._Buffer)
  97         dc = wx.GCDC(dc)
  98         dc.Clear()
  99 
 100         for line in self.Lines:
 101             dc.SetPen(wx.Pen(line[0], 2))
 102             dc.DrawLine(*line[1])
 103 
 104         del dc
 105 
 106         wx.ClientDC(self).DrawBitmap(self._Buffer, 0, 0)
 107 
 108 
 109     def random_color(self):
 110         """
 111         ...
 112         """
 113 
 114         col = wx.Colour(random.randrange(255),
 115                         random.randrange(255),
 116                         random.randrange(255))
 117 
 118         return col
 119 
 120 #---------------------------------------------------------------------------
 121 
 122 class MyApp(wx.App):
 123     """
 124     ...
 125     """
 126     def OnInit(self):
 127 
 128         #------------
 129 
 130         frame = MyFrame(None, -1, title="Lines")
 131         self.SetTopWindow(frame)
 132         frame.Show(True)
 133 
 134         return True
 135 
 136 #---------------------------------------------------------------------------
 137 
 138 def main():
 139     app = MyApp(False)
 140     app.MainLoop()
 141 
 142 #---------------------------------------------------------------------------
 143 
 144 if __name__ == "__main__" :
 145     main()


Buffered with controls

img_buffered_with_controls.png

   1 # buffered_with_controls.py
   2 
   3 import random
   4 import wx
   5 import wx.lib.buttons  as  buttons
   6 
   7 # In ms.
   8 REFRESH_RATE = 1000
   9 
  10 # class MyBufferedWindow
  11 # class MyDrawWindow
  12 # class MyFrame
  13 # class MyApp
  14 
  15 #-------------------------------------------------------------------------------
  16 
  17 class MyBufferedWindow(wx.Window):
  18     def __init__(self, parent, id,
  19                  pos=wx.DefaultPosition,
  20                  size=wx.DefaultSize,
  21                  style=wx.NO_FULL_REPAINT_ON_RESIZE):
  22         wx.Window.__init__(self, parent, id, pos, size,
  23                            style | wx.CLIP_CHILDREN)
  24 
  25         self.Bind(wx.EVT_PAINT, self.OnPaint)
  26         self.Bind(wx.EVT_SIZE, self.OnSize)
  27 
  28         # OnSize called to make sure the buffer is initialized.
  29         # This might result in OnSize getting called twice on some
  30         # platforms at initialization, but little harm done.
  31         self.OnSize(None)
  32 
  33     #-------------------------------------------------------------------
  34 
  35     def Draw(self, dc):
  36         """
  37         Just here as a place holder.
  38         This method should be over-ridden when sub-classed.
  39         """
  40 
  41         pass
  42 
  43 
  44     def OnPaint(self, event):
  45         """
  46         All that is needed here is to draw the buffer to screen.
  47         """
  48 
  49         dc = wx.BufferedPaintDC(self, self._Buffer)
  50 
  51 
  52     def OnSize(self, event):
  53         """
  54         ...
  55         """
  56 
  57         # The Buffer init is done here, to make sure the
  58         # buffer is always the same size as the Window.
  59         self.Width, self.Height = self.GetClientSize()
  60 
  61         # Make new off screen bitmap : this bitmap will always have the
  62         # current drawing in it, so it can be used to save the image to
  63         # a file, or whatever.
  64         self._Buffer = wx.Bitmap(self.Width, self.Height)
  65         self.UpdateDrawing()
  66 
  67 
  68     def SaveToFile(self,FileName, FileType):
  69         """
  70         This will save the contents of the buffer
  71         to the specified file. See the wxWindows docs
  72         for wx.Bitmap::SaveFile for the details.
  73         """
  74 
  75         self._Buffer.SaveFile(FileName, FileType)
  76 
  77 
  78     def UpdateDrawing(self):
  79         """
  80         This would get called if the drawing needed to change, for whatever reason.
  81         The idea here is that the drawing is based on some data generated
  82         elsewhere in the system. If that data changes, the drawing needs to
  83         be updated.
  84         """
  85 
  86         dc = wx.MemoryDC()
  87         dc.SelectObject(self._Buffer)
  88         self.Draw(dc)
  89         # This forces a Paint event, so the screen gets updated.
  90         self.Refresh()
  91         # If it's not getting updated fast enough, this should force it.
  92         # self.Update()
  93 
  94 #-------------------------------------------------------------------------------
  95 
  96 class MyDrawWindow(MyBufferedWindow):
  97     def __init__(self, parent, id=-1):
  98         """
  99         Any data the Draw() function needs must be initialized before
 100         calling MyBufferedWindow.__init__, as it will call the Draw
 101         function.
 102         """
 103 
 104         self.DrawData = {}
 105 
 106         MyBufferedWindow.__init__(self, parent, id)
 107 
 108         #------------
 109 
 110         # Simplified init method.
 111         self.BtnNormal()
 112         self.BtnBitmap()
 113         self.CheckB()
 114 
 115     #-------------------------------------------------------------------
 116 
 117     def Draw(self, dc):
 118         """
 119         ...
 120         """
 121 
 122         dc.SetBackground(wx.Brush("Gray"))
 123         dc.Clear() # Make sure you clear the bitmap !
 124 
 125         # Here's the actual drawing code.
 126         for key,data in self.DrawData.items():
 127             if key == "Rectangles":
 128                 dc.SetBrush(wx.YELLOW_BRUSH)
 129                 dc.SetPen(wx.Pen("VIOLET", 4))
 130                 for r in data:
 131                     dc.DrawRectangle(*r)
 132 
 133             elif key == "Ellipses":
 134                 dc.SetBrush(wx.Brush("GREEN"))
 135                 dc.SetPen(wx.Pen("CADET BLUE", 2))
 136                 for r in data:
 137                     dc.DrawEllipse(*r)
 138 
 139             elif key == "Polygons":
 140                 dc.SetBrush(wx.Brush("SALMON"))
 141                 dc.SetPen(wx.Pen("VIOLET RED", 4))
 142                 for r in data:
 143                     dc.DrawPolygon(r)
 144 
 145 
 146     def BtnNormal(self):
 147         """
 148         ...
 149         """
 150 
 151         self.btnNormal = wx.Button(self, 600, "normal btn", pos=(100, 100))
 152         self.Bind(wx.EVT_BUTTON, self.OnNormalButton, id=600)
 153 
 154 
 155     def OnNormalButton(self,event=None):
 156         """
 157         ...
 158         """
 159 
 160         print("Normal Button Clicked")
 161 
 162 
 163     def BtnBitmap(self):
 164         """
 165         ...
 166         """
 167 
 168         # Only bitmap buttons seam to work.
 169         self.buttonTest2 = buttons.GenBitmapTextButton(self, 700, None,
 170                                                        "bitmap btn",
 171                                                        pos=(250, 250))
 172         self.Bind(wx.EVT_BUTTON, self.OnBitmapButton, id=700)
 173 
 174 
 175     def OnBitmapButton(self,event=None):
 176         """
 177         ...
 178         """
 179 
 180         print("Bitmap Button Clicked")
 181 
 182 
 183     def CheckB(self):
 184         """
 185         ...
 186         """
 187 
 188         self.checkB = wx.CheckBox(self, 800, "test", pos=(300, 50))
 189 
 190 #-------------------------------------------------------------------------------
 191 
 192 class MyFrame(wx.Frame):
 193     def __init__(self):
 194         wx.Frame.__init__(self, None, -1,
 195                           "Double Buffered with controls",
 196                          wx.DefaultPosition,
 197                          size=(420, 420),
 198                          style=wx.DEFAULT_FRAME_STYLE |
 199                                wx.NO_FULL_REPAINT_ON_RESIZE)
 200 
 201         #------------
 202 
 203         self.SetIcon(wx.Icon('wxwin.ico'))
 204         self.SetMinSize((320, 320))
 205 
 206         #------------
 207 
 208         # Delete flicker.
 209         if wx.Platform == "__WXMSW__":
 210             self.SetDoubleBuffered(True)
 211 
 212         #------------
 213 
 214         # Set up the menuBar.
 215         menuBar = wx.MenuBar()
 216 
 217         file_menu = wx.Menu()
 218 
 219         file_menu.Append(500, "E&xit", "Terminate the program")
 220         self.Bind(wx.EVT_MENU, self.OnQuit, id=500)
 221         menuBar.Append(file_menu, "&File")
 222 
 223         draw_menu = wx.Menu()
 224 
 225         draw_menu.Append(400, "&New Drawing","Update the Drawing Data")
 226         self.Bind(wx.EVT_MENU,self.NewDrawing, id=400)
 227 
 228         draw_menu.Append(300, "&Save Drawing\tAlt-I", "")
 229         self.Bind(wx.EVT_MENU, self.SaveToFile, id=300)
 230 
 231         menuBar.Append(draw_menu, "&Draw")
 232 
 233         draw_menu.Append(200, "&Start Periodic Update\tF5")
 234         self.Bind(wx.EVT_MENU, self.StartUpdate, id=200)
 235 
 236         self.SetMenuBar(menuBar)
 237 
 238         draw_menu.Append(100, "&Stop Periodic Update\tF6")
 239         self.Bind(wx.EVT_MENU, self.StopUpdate, id=100)
 240 
 241         self.SetMenuBar(menuBar)
 242 
 243         #------------
 244 
 245         self.Window = MyDrawWindow(self)
 246 
 247         #------------
 248 
 249         # Now set up the timer.
 250         self.timer = wx.Timer(self)
 251         self.Bind(wx.EVT_TIMER, self.Notify)
 252 
 253     #-------------------------------------------------------------------
 254 
 255     def StartUpdate(self, event):
 256         """
 257         ...
 258         """
 259 
 260         # Time between events (in milliseconds).
 261         self.timer.Start(1000)
 262 
 263 
 264     def StopUpdate(self, event=None):
 265         """
 266         ...
 267         """
 268 
 269         self.timer.Stop()
 270 
 271 
 272     def Notify(self, event):
 273         """
 274         ...
 275         """
 276 
 277         self.NewDrawing(None)
 278 
 279 
 280     def OnQuit(self, event):
 281         """
 282         ...
 283         """
 284 
 285         self.Close(True)
 286 
 287 
 288     def NewDrawing(self, event):
 289         """
 290         ...
 291         """
 292 
 293         self.Window.DrawData = self.MakeNewData()
 294         self.Window.UpdateDrawing()
 295 
 296 
 297     def SaveToFile(self, event):
 298         """
 299         ...
 300         """
 301 
 302         dlg = wx.FileDialog(self, "Choose a file name to save the image as a PNG",
 303                             defaultDir="",
 304                             defaultFile="",
 305                             wildcard="*.png",
 306                             style=wx.FD_SAVE)
 307         if dlg.ShowModal() == wx.ID_OK:
 308             self.Window.SaveToFile(dlg.GetPath(), wx.BITMAP_TYPE_PNG)
 309         dlg.Destroy()
 310 
 311 
 312     def MakeNewData(self):
 313         """
 314         This method makes some random data to draw things with.
 315         """
 316 
 317         MaxX, MaxY = self.Window.GetClientSize()
 318         DrawData = {}
 319 
 320         # Make some random rectangles.
 321         l = []
 322         for i in range(2):
 323             w = random.randint(1, int(MaxX/2))
 324             h = random.randint(1, int(MaxY/2))
 325             x = random.randint(1, int(MaxX-w))
 326             y = random.randint(1, int(MaxY-h))
 327             l.append( (x,y,w,h) )
 328         DrawData["Rectangles"] = l
 329 
 330         return DrawData
 331 
 332 #-------------------------------------------------------------------------------
 333 
 334 class MyApp(wx.App):
 335     def OnInit(self):
 336         # Called so a PNG can be saved.
 337         # wx.InitAllImageHandlers()
 338         frame = MyFrame()
 339         frame.Show(True)
 340 
 341         # Initialize a drawing.
 342         # It doesn't seem like this should be here, but the Frame does
 343         # not get sized until Show() is called, so it doesn't work if
 344         # it is put in the __init__ method.
 345         frame.NewDrawing(None)
 346 
 347         self.SetTopWindow(frame)
 348 
 349         return True
 350 
 351 #-------------------------------------------------------------------------------
 352 
 353 if __name__ == "__main__":
 354     app = MyApp(False)
 355     app.MainLoop()


Balls

img_balls.png

   1 # balls.py
   2 
   3 # https://bty.sakura.ne.jp/wp/archives/78
   4 
   5 import wx
   6 import wx.lib.mixins.listctrl as listmix
   7 
   8 # class MyBall
   9 # class MyFrame
  10 # class MyApp
  11 
  12 #---------------------------------------------------------------------------
  13 
  14 class MyBall(object):
  15     def __init__(self, panel, x, y, color, pen):
  16 
  17         #------------
  18 
  19         self.pos_x = x
  20         self.pos_y = y
  21         self.dir_x = 1
  22         self.dir_y = 1
  23         self.pos_dif = 20
  24         self.pos_x_max, self.pos_y_max = panel.GetSize()
  25         self.color = color
  26         self.pen = pen
  27 
  28     #-----------------------------------------------------------------------
  29 
  30     def Move(self):
  31         """
  32         ...
  33         """
  34 
  35         if self.dir_x > 0:
  36             self.pos_x += self.pos_dif
  37         else:
  38             self.pos_x -= self.pos_dif
  39 
  40         if self.pos_x > self.pos_x_max:
  41             self.pos_x -= self.pos_dif
  42             self.dir_x = -1
  43 
  44         if self.pos_x < 0:
  45             self.pos_x += self.pos_dif
  46             self.dir_x = 1
  47 
  48         if self.dir_y > 0:
  49             self.pos_y += self.pos_dif
  50         else:
  51             self.pos_y -= self.pos_dif
  52 
  53         if self.pos_y > self.pos_y_max:
  54             self.pos_y -= self.pos_dif
  55             self.dir_y = -1
  56 
  57         if self.pos_y < 0:
  58             self.pos_y += self.pos_dif
  59             self.dir_y = 1
  60 
  61 
  62     def Draw(self, dc):
  63         """
  64         ...
  65         """
  66 
  67         dc.SetPen(wx.Pen(self.pen, 3))
  68         dc.SetBrush(wx.Brush(self.color))
  69         dc.DrawCircle(self.pos_x, self.pos_y, 20)
  70 
  71 #---------------------------------------------------------------------------
  72 
  73 class MyFrame(wx.Frame):
  74     def __init__(self, title):
  75         wx.Frame.__init__(self, None, -1,
  76                           title)
  77 
  78         #------------
  79 
  80         self.SetIcon(wx.Icon('wxwin.ico'))
  81         self.SetMinSize((420, 240))
  82 
  83         #------------
  84 
  85         self.panel = wx.Panel(self, size=(404, 200))
  86         self.panel.SetBackgroundColour('light gray')
  87         self.Fit()
  88 
  89         self.ball1 = MyBall(self.panel, 150, 100, 'red', 'black')
  90         self.ball2 = MyBall(self.panel, 100,  50, 'blue', 'gray')
  91         self.ball3 = MyBall(self.panel,  50, 150, 'green', 'blue')
  92 
  93         self.Bind(wx.EVT_CLOSE, self.CloseWindow)
  94 
  95         #------------
  96 
  97         self.cdc = wx.ClientDC(self.panel)
  98         w, h = self.panel.GetSize()
  99         self.bmp = wx.Bitmap(w, h)
 100 
 101         #------------
 102 
 103         self.timer = wx.Timer(self)
 104         self.Bind(wx.EVT_TIMER, self.OnTimer)
 105         self.timer.Start(100)
 106 
 107     #-----------------------------------------------------------------------
 108 
 109     def CloseWindow(self, event):
 110         """
 111         ...
 112         """
 113 
 114         self.timer.Stop()
 115         self.Destroy()
 116         wx.Exit()
 117 
 118 
 119     def OnTimer(self, event):
 120         """
 121         ...
 122         """
 123 
 124         bdc = wx.BufferedDC(self.cdc, self.bmp)
 125         gcdc = wx.GCDC(bdc)
 126         gcdc.SetBackground(wx.Brush("light gray"))
 127         gcdc.Clear()
 128 
 129         self.ball1.Move()
 130         self.ball2.Move()
 131         self.ball3.Move()
 132 
 133         self.ball1.Draw(gcdc)
 134         self.ball2.Draw(gcdc)
 135         self.ball3.Draw(gcdc)
 136 
 137         gcdc.DrawText("Moving Ball", 110, 70)
 138         gcdc.DrawBitmap(self.bmp, 0, 0)
 139 
 140 #---------------------------------------------------------------------------
 141 
 142 class MyApp(wx.App):
 143     def OnInit(self):
 144 
 145         #------------
 146 
 147         frame = MyFrame("Balls")
 148         self.SetTopWindow(frame)
 149         frame.Show(True)
 150 
 151         return True
 152 
 153 #---------------------------------------------------------------------------
 154 
 155 def main():
 156     app = MyApp(False)
 157     app.MainLoop()
 158 
 159 #---------------------------------------------------------------------------
 160 
 161 if __name__ == "__main__" :
 162     main()


Download source

source.zip


Additional Information

Link :

http://zetcode.com/wxpython/thetetrisgame/

- - - - -

https://wiki.wxpython.org/TitleIndex

https://docs.wxpython.org/


Thanks to

LionKimbro (bubbles_toy.py coding), Jan Bodnar (tetris.py coding), Robin Dunn / Noel Rappin (radar_graph.py coding), Chris Barker (lines / buffered_with_controls.py coding), Sakura (balls.py coding), the wxPython community...


About this page

Date(d/m/y) Person (bot) Comments :

22/12/19 - Ecco (Updated page for wxPython Phoenix).

05/01/20 - Ecco (Added examples for wxPython Phoenix).


Comments

- I love it! I've refactored the code and made it more OO, and also fixed an AssertionError on Windows. -- RobinDunn

- I'm honored! Thank you! -- LionKimbro

- This is a fantastic example of custom animation in wx. I've updated it to use AutoBufferedPaintDC, rather than explicitly using a double-buffered context, so that it gets optimal performance even on systems where double-buffering isn't needed (like OS X). -- JoeStrout

How to create an animated drawing (Phoenix) (last edited 2020-12-13 16:55:05 by Ecco)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.