Returning Image Data from a Product
Hi all, I'm getting very confused about a Zope product I'm working on, and I'm hoping somebody can share a clue on this. I've already figured out that I can load an image included with my product into PIL, with a line like this (which took a long time to figure out, any better solutions to this would be appreciated, too): im = PIL.Image.open( Globals.ImageFile('www/myimg.png', globals()).path) and then, I can load a PIL image into a contained Zope Image object (I was actually figuring that Globals.ImageFile would load a Zope image, but it seems not -- weird. Instead it's some kind of file access object, hence the "path" attribute used above). imf = StringIO() im.save(imf, "PNG") imf.seek(0) self.result_image.data = imf.read() self.result_image.width = 32 self.result_image.height = 32 self.result_image.content_type='image/png' self.result_image.size = len(self.result_image.data) ("im" is the PIL image, "self.result_image" is the Zope image, and "imf" is a StringIO object containing the image). Now the part that stumps me -- how do I return this image back to the caller? Specifically: * I want to return the PNG Image (i.e. a block of data * * with mime type 'image/png'), NOT an HTML image tag * * containing a reference to result_image . * I've tried: return self.result_image which returns a serialized image tag (i.e. just like return self.result_image.tag() which I've also tried). return self.result_image.data returns the PNG image alright, but the mime type is set to 'text/plain'! The kicker is, I have a related module which does do this successfully, by simply using: return self.result_image But I can't figure out why this one doesn't do it. So I need to have a better understanding of what makes this happen in order to figure out what the deal is. There are a lot of (apparently) trivial differences between the two, but each time I come across something that looks like the critical difference, it turns out not to change anything, so it's pretty darned tedious to do the experimental approach. So I figure I could use some theoretical predictions to guide me. :) Some of the differences I've already tried: Changing the base class from SimpleItem to Image (this is the class of the containing object, not the zope image object which is initialized within it -- i.e.: class myspecial_image_maker(Image): <-- this is the superclass that I changed from SimpleItem this class is not viewable as an image, so has no particular reason to be one. def __init__(self, id, title): self.id = id self.title = title self.result_image = OFS.Image.Image( 'result_image', 'Result Image', '', content_type='image/png') Also, on my earlier class, I want to use a URL interface for caching reasons, so I overloaded __getattr__, like this: def __getattr__(self, keystr): try: # omitted lengthy code to catch my coded URLs # code like that quoted above to load/make a PIL # image and pack into the Zope result_image image. return self.result_image except: Image.__getattr__(self, keystr) (So if anything goes wrong, or my code doesn't catch the attribute, it falls through to the superclass's __getattr__ -- this is pretty nice, but also tedious to debug, I have to warn you -- if you snare up standard attributes with your __getattr__, Zope gets very confused!). I remind you that this code does what I wanted -- i.e when you point your browser at the desired "coded" URL, you get the PNG image itself, with correct 'image/png' mime type. So you can do something like this: <img src="<dtml-var expr="Avatars.anonymous_png">/s32" width="32" height="32" border="0"/> where "anonymous_png" is an object of my earlier class. The coded URL "s32" asks for a version of the image resized to 32x32, which PIL takes care of. This code *works*. Now I'm trying to do it again, calling another function to generate an image from scratch (i.e. not contained in the class), based on some rules and PIL code, generated from the coded URL. But for some reason this *doesn't* work, and I'm trying to figure out what determines this, so I can see if I can find the difference (not to mention that this sounds like I will learn something significant about Zope). As you can see, I also changed the __getattr__ to use Image and not SimpleItem. Some of my test code, in order to avoid the complexity of interacting with the __getattr__ code, returns the image from a normal python method, like this: class ... def testimg(self): "Need this docstring or zope complains ;)" # omitted code (as above) to load a PIL image and convert # to a zope image return self.result_image This returns the image *tag* every time I've tried it. Is it possible that it matters whether I'm using __getattr__ or a python method to return this? Is there some hidden difference that I just haven't found yet (perhaps completely unrelated to what I've shown). The whole module is a bit long to post on the list, and it includes a lot of trivial details that would probably hinder more than help. I'm hoping I've already included the relevant parts above. I think I've tried returning the image from the __getattr__ method (to no avail), but I've been through so many variations, I really can't remember now! BTW, the actual module size is: % wc NaryaImage.py 385 1284 11050 NaryaImage.py Thanks for any ideas! Terry -- ------------------------------------------------------ Terry Hancock hancock@anansispaceworks.com Anansi Spaceworks http://www.anansispaceworks.com P.O. Box 60583 Pasadena, CA 91116-6583 ------------------------------------------------------
Terry Hancock wrote:
I've tried:
return self.result_image
which returns a serialized image tag (i.e. just like
return self.result_image.tag()
which I've also tried).
return self.result_image.data
returns the PNG image alright, but the mime type is set to 'text/plain'!
The kicker is, I have a related module which does do this successfully, by simply using:
return self.result_image
I have had a similar problem once. The problem is probably that the id of your imagefile and the attribute name should be the same. Something like this in your product: from OFS import Image self. result_image = Image.Image('result_image', imageTitle, file, content_type, precondition) instead of: from OFS import Image self.image = Image.Image('result_image', imageTitle, file, content_type, precondition) Or else you are confusing matters. The tag will be made like: <img src="result_image"> but you image will actually have the url <img src="image"> I remember submitting a one line patch but never got an answer and can no longer remember what it was. But it was something about setting the id of the image afterwards. You could probably browse the source for "tag()" method in the image Module and then set it something like; self.image._id = 'image' if that is a good solution for you. regards Max M
Terry Hancock wrote:
* I want to return the PNG Image (i.e. a block of data * * with mime type 'image/png'), NOT an HTML image tag * * containing a reference to result_image . *
Is it possible that it matters whether I'm using __getattr__ or a python method to return this?
OKAY. This is the critical difference -- I just found that out by testing. However, I still think it would be very edifying to hear someone explain why. :) I can see that there *is* a distinction here between the return value for the __getattr__ method and a fixed Python method, but I don't really understand it. To recap and (hopefully) clarify, the problem: class MyTestClass(OFS.SimpleItem.SimpleItem): def __getattr__(self, keystr): "Access my data by catching the attribute lookup" # Decode keystr and catch a URL: try: if keystr != 'Foo' : raise AttribError # omitted PIL code to read and convert an image # which is now stored in self.result_image return self.result_image except: OFS.SimpleItem.SimpleItem.__getattr__(keystr) def Foo2(self): "Direct access by Python method" # omitted PIL code to read and convert an image # which is now stored in self.result_image # (i.e. same as above) return self.result_image If I do this, pointing my browser at an instance, called, say "TestObject": http://myzope/TestObject/Foo returns a PNG image (i.e. a block of data with type 'image/png' which is rendered by the browser), while: http://myzope/TestObject/Foo2 returns an HTML-coded image tag, containing src="http://myzope/TestObject/result_image". This is not rendered by the browser, unless I wrap it in <html><body>...</body></html> tags. So why is that? Why do I care? Well, mainly because the browser will try to cache "result_image" if it's always the same URL, whereas the __getattr__ approach has a unique URL (in the example it doesn't matter, but actually I want to use some coded URL, like "s50" = "scaled to 50x50 pixels". Thanks! Terry -- ------------------------------------------------------ Terry Hancock hancock@anansispaceworks.com Anansi Spaceworks http://www.anansispaceworks.com P.O. Box 60583 Pasadena, CA 91116-6583 ------------------------------------------------------
participants (2)
-
Max M -
Terry Hancock