Greetings;
I've hacked together a simple analog clock using wxPyhton 2.4 an Python 2.2. For the most part, it works. However, every so often there is a visbile flicker across the main frame of the clock face as the time updates.
To perform the updates, I draw the clock face and the hour, minutes and second hands to a wxMemoryDC instance in the main frame's OnPaint event handler, and then blit the contents of the completed wxMemoryDC to the wxPaintDC:
def OnPaint(self, event):
clientAreaSize = self.GetClientSizeTuple()
clientBitmap = wxEmptyBitmap(clientAreaSize[0], clientAreaSize[1])
memoryDC = wxMemoryDC()
memoryDC.SelectObject(clientBitmap)
self.Draw(memoryDC)
paintDC = wxPaintDC(self)
paintDC.BeginDrawing()
# paintDC.Clear()
paintDC.Blit(0, 0, clientAreaSize[0], clientAreaSize[1], memoryDC,
0, 0)
paintDC.EndDrawing()
memoryDC.SelectObject(wxNullBitmap)
In short, I thought that by drawing the entire clock face to the memoryDC and then blitting it to the paintDC would be quicker and avoid any flickering problems.
I've tried a number of different approaches, including providing an erase background handler, and setting the style flag on the frame window for wxCLIP_CHILDREN, all to no avail. The flickering still occurs. I'm running the application on a Windows 2000 SP3 based system, with an NVIDIA GeForce2 MX.
I've included the complete listing below, and would greatly appreciate any feedback or suggestions on how I can eliminate the flickering.
Thank you in advance for your time and help.
William Wonneberger
wberger@ccil.org / alundi@acm.org
http://www.ccil.org/~wberger
"The answer is that using a Stradivarius violin to pound nails should not be considered a sound construction technique."
R. Schwartz & T. Phoenix
Learning Perl, 3rd Edition
#!python
···
#
# wxClock - A simple analog clock using the wxPython library.
# $Header: G:\\rcs\\D\\work\\Python\\wxPython\\wxClock.py,v 1.0 2003-02-23 20:35:15-05 wberger Exp wberger $
#
# Revision History:
# $Log: wxClock.py,v $
# Revision 1.0 2003-02-23 20:35:15-05 wberger
# Initial revision
#
import math, sys, string, time
from wxPython.wx import *
class ClockFrame(wxFrame):
def point(self, tick, range, radius):
angle = tick * (360.0 / range)
radiansPerDegree = math.pi / 180
pointX = int(round(radius * math.sin(angle * radiansPerDegree)))
pointY = int(round(radius * math.cos(angle * radiansPerDegree)))
return wxPoint(pointX, pointY)
def __init__(self):
# Initialize the timer that drives the update of the clock face...
self.timer = wxPyTimer(self.Notify)
self.minuteMarks = 60
self.hourMarks = 12
# Initialize the wxFrame...
wxFrame.__init__(self, None, -1, 'wxClock', wxDefaultPosition,
wxSize(325, 375))
# Initialize the default clock settings...
backgroundColor = wxColour(red = 225, green = 225, blue = 225)
self.backgroundColor = [75, 75, 75]
self.SetBackgroundColour(backgroundColor)
self.lineColor = [253, 198, 70]
self.lineCount = 60
self.lineEnding = 1
self.lineEndingColor = [251, 196, 68]
self.timer.Start(950)
# Event handlers...
EVT_PAINT(self, self.OnPaint)
# For handling a paint, create an identical device context in memory
# and use the Draw() method to update the device context in memory.
# Once the entire client area has been redrawn in memory, bit blit
# client area device context in memory out to the actual paint
# device context. This is essentially performing a buffered update or
# paint of the entire client area of the main application frame.
def OnPaint(self, event):
clientAreaSize = self.GetClientSizeTuple()
clientBitmap = wxEmptyBitmap(clientAreaSize[0], clientAreaSize[1])
memoryDC = wxMemoryDC()
memoryDC.SelectObject(clientBitmap)
self.Draw(memoryDC)
paintDC = wxPaintDC(self)
paintDC.BeginDrawing()
# paintDC.Clear()
paintDC.Blit(0, 0, clientAreaSize[0], clientAreaSize[1], memoryDC,
0, 0)
paintDC.EndDrawing()
memoryDC.SelectObject(wxNullBitmap)
# Using the current settings, render the points and line endings for the
# circle inside the specified device context. In this case, the DC is
# a memory based device context that will be blitted to the actual
# display DC inside the OnPaint() event handler.
def Draw(self, drawDC):
backgroundBrush = wxBrush(wxColour(self.backgroundColor[0],
self.backgroundColor[1],
self.backgroundColor[2]),
wxSOLID)
drawDC.SetBackground(backgroundBrush)
drawDC.Clear()
numberOfLines = self.lineCount
# Based on the client area frame size, determine the center...
frameSize = self.GetClientSizeTuple()
wholeWindowSize = self.GetSizeTuple()
widthPadding = wholeWindowSize[0] - frameSize[0]
heightPadding = (wholeWindowSize[1] - frameSize[1]) / 2
centerX = (frameSize[0] - widthPadding) / 2
centerY = ((frameSize[1] - heightPadding) / 2)
# Draw the marks for hours and minutes...
self.DrawTimeMarks(drawDC, self.minuteMarks, centerX, centerY, 4)
self.DrawTimeMarks(drawDC, self.hourMarks, centerX, centerY, 9)
currentTime = time.localtime(time.time())
hour, minutes, seconds = currentTime[3:6]
radius = centerX
x, y = self.point(hour, 12, (radius * .65))
hourX, hourY = (x + centerX), (centerY - y)
x, y = self.point(minutes, 60, (radius * .85))
minutesX, minutesY = (x + centerX), (centerY - y)
x, y = self.point(seconds, 60, (radius * .85))
secondsX, secondsY = (x + centerX), (centerY - y)
# Draw the hour hand...
lineColor = wxColour(self.lineColor[0], self.lineColor[1],
self.lineColor[2])
linePen = wxPen(lineColor, 3, wxSOLID)
drawDC.SetPen(linePen)
drawDC.DrawLine(centerX, centerY, hourX, hourY)
# Using the same DC pen, draw the minutes hand...
drawDC.DrawLine(centerX, centerY, minutesX, minutesY)
# Adjust the thickness of the line pen for seconds and draw the
# seconds hand...
linePen = wxPen(lineColor, 1, wxSOLID)
drawDC.SetPen(linePen)
drawDC.DrawLine(centerX, centerY, secondsX, secondsY)
# Draw the specified set of line marks inside the clock face for the
# hours or minutes...
def DrawTimeMarks(self, drawDC, markCount, centerX, centerY, markSize):
for i in range(markCount):
x, y = self.point(i + 1, markCount, centerX - 16)
scaledX, scaledY = (x + centerX), (centerY - y)
lineEndingColor = wxColour(self.lineEndingColor[0],
self.lineEndingColor[1],
self.lineEndingColor[2])
lineEndingBrush = wxBrush(lineEndingColor, wxSOLID)
drawDC.SetBrush(lineEndingBrush)
if self.lineEnding != 0:
if self.lineEnding == 2:
drawDC.DrawEllipse(scaledX - 2, scaledY, markSize, markSize)
else:
drawDC.DrawRectangle(scaledX - 3, scaledY, markSize, markSize)
def OnQuit(self, event):
self.timer.Stop()
self.Close(true)
def OnAdjustSettings(self, event):
settingsDialog = wxClockSettingsDialog(self)
settingsDialog.ShowModal()
returnCode = settingsDialog.GetReturnCode()
# If the OK button was clicked, get the new settings and repaint
# the wxClock frame...
if(returnCode == wxID_OK):
numberOfLines = settingsDialog.GetLines()
self.lineEnding = settingsDialog.GetLineEnding()
self.lineCount = numberOfLines
self.lineColor = settingsDialog.GetLineColor()
self.backgroundColor = settingsDialog.GetBackgroundColor()
backgroundColor = wxColour(self.backgroundColor[0],
self.backgroundColor[1],
self.backgroundColor[2])
self.SetBackgroundColour(backgroundColor)
self.lineEndingColor = settingsDialog.GetLineEndingColor()
# Force an OnPaint event to be handled to update the
# entire client area of the application frame...
clientSize = self.GetClientSizeTuple()
clientRect = (0, 0, clientSize[0], clientSize[1])
self.Refresh(1, clientRect)
def Notify(self):
clientSize = self.GetClientSizeTuple()
clientRect = (0, 0, clientSize[0], clientSize[1])
self.Refresh(1, clientRect)
class App(wxApp):
def OnInit(self):
frame = ClockFrame()
frame.Centre(wxBOTH)
frame.Show(true)
self.SetTopWindow(frame)
return true
theApp = App(0)
theApp.MainLoop()