Resize required always to redraw

“”
The code below worked great in wxPython 3.X under Python 2.7 and earlier.

Below is only the drawing portion of this program.
It has been modified simply to make it Python 3.x and wxPython 4.x compatable.

I had to upgrade to wxPython 4.0.7 gtk2.
Prior to this I always got an error that said that both wx.Window objects in the code below were not valid DC’s.

Now the code works “sort of”.
After initially drawing correctly, the only way I can get an redrawn plot is by physically resizing the GUI (using the mouse).
I have tried faking it out by forceing a window resize from within the code.
In this case, I always get an Infinite loop of resizeing calls.
So far no matter how I write this code (double buffered or not) or even calling OnResize from within OnPaint makes no difference.
Am missing something, doing something wrong, or are there still bugs.
Clearly, there were in the DC code before I upgraded from wxPython 4.0.1 to 4.0.7

“”"

class SpectraPlot(wx.Window):
“”"
A simplifed spectra ploting class
“”"

def __init__(self, parent, pos, size, name="plotframe", ppPos = (125,50), ppSizeDiff = (200,150), style = wx.NO_FULL_REPAINT_ON_RESIZE):
	wx.Window.__init__(self, parent=parent, id=wx.ID_ANY, pos=pos, size=size, name=name, style=style)

	self.SetBackgroundColour(ct.LIGHT_GREY)
	self.SD_Color = [ct.GREEN, ct.RED, ct.BLUE, ct.CYAN, ct.ORANGE, ct.LIGHT_STEEL_BLUE]
	
	self.parent = parent
	self.frame = parent
	self.pos = pos
	fpx, fpy = self.pos
	self.size = size
	fsx, fsy = self.size
	self.xOffset = ppPos[0]
	self.yOffset = ppPos[1]
	self.xSizediff = ppSizeDiff[0]
	self.ySizediff = ppSizeDiff[1]
	ppPos=(self.xOffset,self.yOffset)
	ppSize=(fsx-self.xSizediff, fsy-self.ySizediff)
	
	self.plotpanel = wx.Window(self, wx.ID_ANY, pos=ppPos, size=ppSize, style = wx.BORDER, name ="plotpanel")
	self.plotpanel.SetBackgroundStyle(wx.BG_STYLE_PAINT)
	self.plotpanel.SetBackgroundColour(wx.WHITE)

	self.Bind(wx.EVT_SIZE, self.OnResize)
	self.Bind(wx.EVT_PAINT, self.OnPaint)
	self.plotpanel.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
	self.plotpanel.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
	self.plotpanel.Bind(wx.EVT_MOTION, self.OnMouseMotion)
	self.plotpanel.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)

>>>>>

def InitBuffers(self):
	self.plotframeSize = self.GetSize()
	(self.xW, self.yH) = self.plotframeSize

	self.ppW = self.xW-self.xSizediff
	self.ppH = self.yH-self.ySizediff
	self.plotpanel.SetSize((self.ppW, self.ppH))

	self._plotbuffer = wx.Bitmap((self.ppW, self.ppH))
	if USE_BUFFER:

		dc = wx.BufferedPaintDC(self.plotpanel, self._plotbuffer)
	else:
		dc = wx.MemoryDC()
		dc.SelectObject(self._plotbuffer)

	dc.SetBackground(wx.Brush(self.plotpanel.GetBackgroundColour()))
	dc.Clear()

	self._framebuffer = wx.Bitmap(self.plotframeSize)
	if USE_BUFFER:

“”"
This does not seem to work only the MemoryDC approach below works. This is different from the older wxPython3.x and Python2.7.x where the BufferedPaintDC approach is the one I used. But I do not think this is the issue with repainting.
“”"
fdc = wx.BufferedPaintDC(self, self._framebuffer)
else:
fdc = wx.MemoryDC()
fdc.SelectObject(self._framebuffer)

	fdc.SetBackground(wx.Brush(self.GetBackgroundColour()))
	fdc.Clear()

	self.drawAxis(dc, fdc)

	try:
		self.UpperLeft = (self.xEq.minPix, self.yEq.minPix)
		self.LowerRight = (self.xEq.maxPix, self.yEq.maxPix)
	except:
		ft.line_comment("Exception when plotting data")

	self.plot(dc)

	if self.drawbox:
		self.drawEmptyBox(dc)


def plot(self, dc, color = None):
	"""
		Plot the data if it exists
	"""
	ft.line_comment("In my_plotlib.plot")
	ideal_calc = wx.FindWindowByName("ideal_calc")
	if not color:
		color = wx.BLACK_PEN
		
	if self.Spec_Data and ideal_calc.SD_Flag:
		ideal_calc.reg_data.Enable()
		pixel = self.getSpec_data(self.Spec_Data)
		dc.SetPen(wx.Pen(self.SD_Color[(self.SD2plot +1) % 6]))
		dc.DrawLines(pixel)
	else:
		self.scale = 1.0
		ideal_calc.reg_data.Disable()

	data = self.Data
	IC_flag = ideal_calc.Data_Flag
	if data and (len(data) > 0) and IC_flag:
		pixel = self.getPix()
		dc.SetPen(color)
		dc.DrawLines(pixel)

	if len(self.LLmarkers) > 0:	#LineList markers
		for marker in self.LLmarkers:
			(mark, color) = marker
			dc.SetPen(wx.Pen(color))
			xPix = self.xEq.calcPix(mark)
			dc.DrawLine(xPix, self.UpperLeft[1], xPix, self.LowerRight[1])

	if len(self.markers) > 0:
		for marker in self.markers:
			(mark, color) = marker
			dc.SetPen(wx.Pen(color))
			xPix = self.xEq.calcPix(mark)
			dc.DrawLine(xPix, self.UpperLeft[1], xPix, self.LowerRight[1])			

def OnPaint(self, evt):
	"""
		repaint screen
		
		Note that both the commented out BufferedPaintDC calls below work as well and produce the same results
		
	"""
	
	ft.line_comment("In my_plotlib.OnPaint")

fdc = wx.BufferedPaintDC(self, self._framebuffer)

	fdc = wx.PaintDC(self)
	fdc.DrawBitmap(self._framebuffer, 0, 0)

dc = wx.BufferedPaintDC(self.plotpanel, self._plotbuffer)

	dc = wx.PaintDC(self.plotpanel)
	dc.DrawBitmap(self._plotbuffer, 0, 0)
	evt.Skip()


def OnResize(self, evt):
	ft.line_comment("In my_plotlib.OnResize")
	self.setLines((self.wave_min, self.wave_max))
	evt.Skip()		

If you enclose your code in triple-backticks (```) then the code will be formatted properly in your message and it will be easier to read. Also, if you can reduce the code to a small-as-possible and runnable sample application then it will be easier to help you, and you may actually find the solution on your own as you go through that exercise.

Finally, based on your description, it sounds like you are not calling Refresh when a redraw is needed. Refresh basically causes the system to send a new paint event in the near future (basically when there are no pending events and when the system is ready for the screen to be updated.)

So, a typical workflow would be something like this:

  1. Data or program state changes that requires changes on the screen
  2. Update the buffer bitmap if buffering is being used
  3. Call Refresh or RefreshRect
  4. The EVT_PAINT handler is called later and redraws the content of the window (either by drawing the buffer bitmap, or by updating the DC using the necessary drawing methods.

If you need to update the display sooner than the normal paint event timeline, (in almost all cases you probably do not) then you can call Update after the Refresh.

Thanks … I have tried calling Refresh with no apparent impact. The odd thing to me is the code worked well under older versions ov wxPython. I will make some of the changes to the formating of my message but it is very difficult to cut this down much it is a very big program. I have already cut it way down. The small apps I have tried work, but then there is a lot going on in the full program. Not to mention 10 years of development. I will keep working at it. I was in a simial quandry on the “Invaid DC” issue a few weeks back until I updated to wxPyton 4.0.7. That is why I think something may be amiss still.

But thanks for the help, I appreciate all feedback

No changes below just all text.

This code worked great in wxPython 3.X under Python 2.7 and earlier.

Below is only the drawing portion of this program.
It has been modified simply to make it Python 3.x and wxPython 4.x compatable.

I had to upgrade to wxPython 4.0.7 gtk2.
Prior to this I always got an error that said the both wx.Window objects in the code belo were not valid DC’s.

Now the code works “sort of”
After initalling drwaing correctly, the only way I can get an redrawn drawing is by physiccally resizing the GUI
I have tried faking it out by forceing a window resize from within the code.
In this case and I always get an infant loop of resizeing calls.
So far no matter how I write this code (dubble buffered or not) or even calling OnResize from within OnPaint makes no differentc.
Am I doing something wrong or are there still bugs.
Clearly there were in the DC code before I upgraded from wxPython 4.0.1 to 4.0.7


class SpectraPlot(wx.Window):
	"""
		A simplifed spectra ploting class
	"""


	def __init__(self, parent, pos, size, name="plotframe", ppPos = (125,50), ppSizeDiff = (200,150), style = wx.NO_FULL_REPAINT_ON_RESIZE):
		wx.Window.__init__(self, parent=parent, id=wx.ID_ANY, pos=pos, size=size, name=name, style=style)

		self.SetBackgroundColour(ct.LIGHT_GREY)
		self.SD_Color = [ct.GREEN, ct.RED, ct.BLUE, ct.CYAN, ct.ORANGE, ct.LIGHT_STEEL_BLUE]
		
		self.parent = parent
		self.frame = parent
		self.pos = pos
		fpx, fpy = self.pos
		self.size = size
		fsx, fsy = self.size
		self.xOffset = ppPos[0]
		self.yOffset = ppPos[1]
		self.xSizediff = ppSizeDiff[0]
		self.ySizediff = ppSizeDiff[1]
		ppPos=(self.xOffset,self.yOffset)
		ppSize=(fsx-self.xSizediff, fsy-self.ySizediff)
		
		self.plotpanel = wx.Window(self, wx.ID_ANY, pos=ppPos, size=ppSize, style = wx.BORDER, name ="plotpanel")
		self.plotpanel.SetBackgroundStyle(wx.BG_STYLE_PAINT)
		self.plotpanel.SetBackgroundColour(wx.WHITE)

		self.Bind(wx.EVT_SIZE, self.OnResize)
		self.Bind(wx.EVT_PAINT, self.OnPaint)
		self.plotpanel.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
		self.plotpanel.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
		self.plotpanel.Bind(wx.EVT_MOTION, self.OnMouseMotion)
		self.plotpanel.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
		
#		>>>>>
		
	def InitBuffers(self):
		self.plotframeSize = self.GetSize()
		(self.xW, self.yH) = self.plotframeSize

		self.ppW = self.xW-self.xSizediff
		self.ppH = self.yH-self.ySizediff
		self.plotpanel.SetSize((self.ppW, self.ppH))

		self._plotbuffer = wx.Bitmap((self.ppW, self.ppH))
		if USE_BUFFER:
			dc = wx.BufferedPaintDC(self.plotpanel, self._plotbuffer)
		else:
			dc = wx.MemoryDC()
			dc.SelectObject(self._plotbuffer)

		dc.SetBackground(wx.Brush(self.plotpanel.GetBackgroundColour()))
		dc.Clear()

		self._framebuffer = wx.Bitmap(self.plotframeSize)
		if USE_BUFFER:
			fdc = wx.BufferedPaintDC(self, self._framebuffer)
		else:
			fdc = wx.MemoryDC()
			fdc.SelectObject(self._framebuffer)

		fdc.SetBackground(wx.Brush(self.GetBackgroundColour()))
		fdc.Clear()

		self.drawAxis(dc, fdc)

		try:
			self.UpperLeft = (self.xEq.minPix, self.yEq.minPix)
			self.LowerRight = (self.xEq.maxPix, self.yEq.maxPix)
		except:
			ft.line_comment("Exception when plotting data")

		self.plot(dc)

		if self.drawbox:
			self.drawEmptyBox(dc)


	def plot(self, dc, color = None):
		"""
			Plot the data if it exists
		"""
		ft.line_comment("In my_plotlib.plot")
		ideal_calc = wx.FindWindowByName("ideal_calc")
		if not color:
			color = wx.BLACK_PEN
			
		if self.Spec_Data and ideal_calc.SD_Flag:
			ideal_calc.reg_data.Enable()
			pixel = self.getSpec_data(self.Spec_Data)
			dc.SetPen(wx.Pen(self.SD_Color[(self.SD2plot +1) % 6]))
			dc.DrawLines(pixel)
		else:
			self.scale = 1.0
			ideal_calc.reg_data.Disable()

		data = self.Data
		IC_flag = ideal_calc.Data_Flag
		if data and (len(data) > 0) and IC_flag:
			pixel = self.getPix()
			dc.SetPen(color)
			dc.DrawLines(pixel)

		if len(self.LLmarkers) > 0:	#LineList markers
			for marker in self.LLmarkers:
				(mark, color) = marker
				dc.SetPen(wx.Pen(color))
				xPix = self.xEq.calcPix(mark)
				dc.DrawLine(xPix, self.UpperLeft[1], xPix, self.LowerRight[1])

		if len(self.markers) > 0:
			for marker in self.markers:
				(mark, color) = marker
				dc.SetPen(wx.Pen(color))
				xPix = self.xEq.calcPix(mark)
				dc.DrawLine(xPix, self.UpperLeft[1], xPix, self.LowerRight[1])			

	def OnPaint(self, evt):
		"""
			repaint screen
			
			Note that both the commented out BufferedPaintDC calls below work as well and produce the same results
			
		"""
		
		ft.line_comment("In my_plotlib.OnPaint")
#		fdc = wx.BufferedPaintDC(self, self._framebuffer)
		fdc = wx.PaintDC(self)
		fdc.DrawBitmap(self._framebuffer, 0, 0)

#		dc = wx.BufferedPaintDC(self.plotpanel, self._plotbuffer)
		dc = wx.PaintDC(self.plotpanel)
		dc.DrawBitmap(self._plotbuffer, 0, 0)
		evt.Skip()


	def OnResize(self, evt):
		ft.line_comment("In my_plotlib.OnResize")
		self.setLines((self.wave_min, self.wave_max))
		evt.Skip()		

Refresh worked … I must not have been using it properly (or in the correct place before) Or maybe more likely I tried it back when I had other issues that are now fixed.

Thanks again!

1 Like