Reputation: 3425
Edit As I was updating this with the bellow edit I realized during my tests that it is something seemingly wrong with the traceback function of python. I will post a new question
Edit: Updated to make it much more simple of a question
The issue
The problem is when calling a function in the EVT_SIZE event handler that has to resize the panel for whatever reason, it will become an infinite loop causing a recursion error.
This use to be solved (in 2.8) with a boolean true/false variable on whether the function inside the EVT_SIZE handler. However this doesn't work in 2.9 because it seems the events have become asynchronous (or at least in some way it means the SetSize call is allowed to return, the flag reset, THEN the event handler is called).
Examples of situations
The Default (no handling of it any way
import wx
class TestPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
self.Bind(wx.EVT_SIZE, self.OnSize, self)
wx.CallLater(10000, self.ResizeThing)
def ResizeThing(self):
print "Doing our resize thing...."
# calculate whatever we need to calculate
# seems we got to change it in some way
self.SetSize(self.GetSize() + (30, 30))
return "whatever we'd return"
def OnSize(self, event):
print "Size event got to resize"
self.ResizeThing()
if __name__ == '__main__':
app = wx.App(False)
f = wx.Frame(None, -1)
TestPanel(f)
f.Show()
app.MainLoop()
2.8 Solution using a boolean flag
Is there a way to resize a panel without the evt size event firing because when it does it causes an infinite loop (on 2.9 but not 2.8 for some reason).
Notable code
def __init__(...):
..... other stuff....
self.Bind(wx.EVT_SIZE, self.OnSize, self)
def OnSize(self, event):
# if we didn't have it we would have a infinite recursion going evt_size -> update -> evt_size -> update -> evt_size -> update... forever
if not self.setting_size:
# update current size and set artwork
print self.GetSize()
print self.current_size
print "OnSize"
self.current_size = self.GetSize()
print self.current_size
self.UpdateArtwork(self.current_img)
else:
event.Skip()
def UpdateArtwork(self, img):
... get scaled image and set the bitmap
img = self.scale(img)
self.SetBitmap(img.ConvertToBitmap())
def scale(self, img):
print "Calling Scale"
# size the window to the correct dimensions
# get the size the sizer thinks is best
wW = self.GetBestVirtualSize()[0] # the width that the sizer reccomends
wH = self.GetBestVirtualSize()[1] # the height the sizer recommends
W = img.GetWidth()
H = img.GetHeight()
# modifiy the sizer recommendations to fit the image aspect ratio
# use bigger side as the base
if wW > wH:
# round to nearest integer value sense we are using future division
# get the new wH base on the image aspect ratio
wH = round(H/W * wW) # H/W how many H pixels per one W
else:
wW = round(W/H * wH) # W/H how many W pixels per one H
... this is SUPPOSE to prevent a loop by flagging it
self.setting_size = True
print time.time()
print "Setting size in scale..."
self.current_size = (wW, wH)
print self.current_size
self.SetSize((wW, wH))
print time.time()
self.setting_size = False
print time.time()
# then scale the image based on the panel size
return img.Scale(wW, wH)
However this doesn't work sense the EVTs are non synchronous so it fires after resetting it to false and then causes an infinite loop!
Full code
# for proper scaling calculations (such things as .6451 going becoming 0)
from __future__ import division
import time
import wx
from twisted.web import client
from twisted.python import log
try:
import cStringIO as StringIO
except ImportError:
import StringIO
class URLImage(wx.StaticBitmap):
'''Allows a easy mechanism for setting url images'''
def __init__(self, parent, default_image):
wx.StaticBitmap.__init__(self, parent, -1)
self.current_img = None
self.current_url = None
self.current_size = self.GetSize()
self.di_d = default_image
self.default_image = wx.Image(default_image, wx.BITMAP_TYPE_ANY)
self.current_img = self.default_image
self.SetArtwork(self.default_image)
self.Bind(wx.EVT_SIZE, self.OnSize, self)
def OnSize(self, event):
# if we didn't have it we would have a infinite recursion going evt_size -> update -> evt_size -> update -> evt_size -> update... forever
if not self.current_size == self.GetSize():
# update current size and set artwork
print self.GetSize()
print self.current_size
print "OnSize"
self.current_size = self.GetSize()
print self.current_size
self.UpdateArtwork(self.current_img)
else:
event.Skip()
def SetArtwork(self, img):
# for bitmaps (use corresponding method for urls)
self.current_url = None # the current artwork isn't a url, it is a bitmap
print "SetArtwork Updating Artwork"
self.UpdateArtwork(img)
def SetDefaultImage(self):
# this is to change the image to the default
# NOT to change the default image to some other one
# ^(like Text SetLabel changes the Label)
# similiar to SetArtwork
self.current_url = None
print "SetDefault Updating Artwork"
self.UpdateArtwork(self.default_image)
def SetArtworkFromURL(self, url = None):
if url == self.current_url:
print "[URLImage] Duplicate URL"
return
else:
# set back the defualt art
print "Defaulting for URL loading"
self.UpdateArtwork(self.default_image)
# update current_url
self.current_url = url
if url == None:
return
d = client.getPage(url.encode("ascii"), method = "GET", agent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.2b4) Gecko/20091124 Firefox/3.6b4 (.NET CLR 3.5.30729)')
d.addCallback(lambda data: self.UpdateArtwork(wx.ImageFromStream(StringIO.StringIO(data))))
d.addErrback(self.error)
def UpdateArtwork(self, img):
# ALBUM ART
# From: http://www.blog.pythonlibrary.org/2010/03/26/creating-a-simple-photo-viewer-with-wxpython/
# scale the image, preserving the aspect ratio
self.current_img = img
print "Update Artwork"
img = self.scale(img)
self.SetBitmap(img.ConvertToBitmap())
def scale(self, img):
print "Calling Scale"
# size the window to the correct dimensions
# get the size the sizer thinks is best
wW = self.GetBestVirtualSize()[0] # the width that the sizer reccomends
wH = self.GetBestVirtualSize()[1] # the height the sizer recommends
W = img.GetWidth()
H = img.GetHeight()
# modifiy the sizer recommendations to fit the image aspect ratio
# use bigger side as the base
if wW > wH:
# round to nearest integer value sense we are using future division
# get the new wH base on the image aspect ratio
wH = round(H/W * wW) # H/W how many H pixels per one W
else:
wW = round(W/H * wH) # W/H how many W pixels per one H
self.setting_size = True
print time.time()
print "Setting size in scale..."
self.current_size = (wW, wH)
print self.current_size
self.SetSize((wW, wH))
print time.time()
self.setting_size = False
print time.time()
# then scale the image based on the panel size
return img.Scale(wW, wH)
def error(self, err_):
''' Error callback for fetching the album art'''
self.current_url = None# no current (succesful) url artwork set
self.SetArtwork(self.default_image)
log.msg("Error getting Album Artwork")
log.err(err_)
Upvotes: 1
Views: 4257
Reputation: 114098
how bout just
print "Setting size in scale..."
#unbind Size Event
self.UnBind(wx.EVT_SIZE)
self.current_size = (wW, wH)
print self.current_size
self.SetSize((wW, wH))
print time.time()
self.Update() #force update so it does not trigger after rebind
#rebind Size Event
self.Bind(wx.EVT_SIZE, self.OnSize, self)
Upvotes: 2