Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Circle view for ui
-
i am trying to make a circular view/widget like we have for our pics in the forum. I think I am going about it the right way. I am using a custom ui.View class and overriding the draw method.
I can make a white circle that is clipped...Yeah 🎉🎉🎉
Not what I need though. I need to be able to invert my clipping so I get a keyhole effect. The idea is that this view would be placed on top of a image view, just showing the circular cut out of the image beneath.
I think the answer lies with the append_path method. For some reason, I can think through it. Any help appreciated.import ui class CircularView(ui.View): def __init__(self): pass def draw(self): oval = ui.Path.oval(0,0, self.width, self.height) rect = ui.Path.rect(0,0, self.width, self.height) #rect.append_path(oval) ui.Path.add_clip(oval) ui.set_color('white') oval.fill() if __name__ == '__main__': cv = CircularView() cv.present('sheet')
-
Something like this should work as your
draw
method:def draw(self): oval = ui.Path.oval(*self.bounds) rect = ui.Path.rect(*self.bounds) oval.append_path(rect) oval.eo_fill_rule = True oval.add_clip() ui.set_color('white') rect.fill()
-
UPDATED: WORKS IN PYTHON 2 AND 3 ->
If what you're looking for is a cropped round image, this works# coding: utf-8 #these are fillers for phhton 3 changes try: import cStringIO cStringIO.BytesIO = cStringIO.StringIO print("old cString") import urllib2 except ImportError: import io as cStringIO #from io import StringIO as cStringIO import urllib.request as urllib2 print("new stringIO") #ALSO WORKS INSTEAD OF TRY EXCEPT ABOVE FOR PYTHON 2 AND 3 #import io as cStringIO #import urllib2 import ui from PIL import Image, ImageOps, ImageDraw import io def grabImageFromURL(url): url=url #load image from url and show it imageDataFromURL = urllib2.urlopen(url).read() print("imageData from URL of Type: "+str(type(imageDataFromURL))) return imageDataFromURL def circleMaskViewFromImageData(imageData): imageDataFromURL=imageData #load image from url and show it #imageDataFromURL = urllib2.urlopen(url).read() file=cStringIO.BytesIO(imageDataFromURL) img = Image.open(file) #img = io.open(file) #begin mask creation bigsize = (img.size[0] * 3, img.size[1] * 3) mask = Image.new('L', bigsize, 0) draw = ImageDraw.Draw(mask) draw.ellipse((0, 0) + bigsize, fill=255) mask = mask.resize(img.size, Image.ANTIALIAS) img.putalpha(mask) #show final masked image #img.show() img=pil2ui(img) return img def circleMaskViewFromURL(url): url = url imageData = grabImageFromURL(url) maskedImage = circleMaskViewFromImageData(imageData) return maskedImage # pil <=> ui def pil2ui(imgIn): with io.BytesIO() as bIO: imgIn.save(bIO, 'PNG') imgOut = ui.Image.from_data(bIO.getvalue()) del bIO return imgOut def wrapper(func, *args, **kwargs): #console.clear() def wrapped(): return func(*args, **kwargs) return wrapped if __name__=="__main__": testURL = "http://vignette2.wikia.nocookie.net/jamesbond/images/3/31/Vesper_Lynd_(Eva_Green)_-_Profile.jpg/revision/latest?cb=20130506215331" #____TIMING TEST__________ #testImage = grabImageFromURL(testURL) #wrapped = wrapper(circleMaskViewFromImageData, testImage) #b= timeit.Timer(wrapped).timeit(number=1000) #print(b) #_____END TIMING TEST______ circleMaskViewFromURL(testURL).show()
-
@omz , thanks. Works great. I searched the forum, could not find a reference to it. So surprised. I would have thought a lot would have been asking about this.
-
-
@Tizzy FYI,
io.StringIO
exists on Python 2 and 3, andurllib3
is a third-party (not Python standard library) module, so it also exists on both versions. (The Python 3 version ofurllib2
is calledurllib
.) And if you dofrom PIL import Image
, you don't need to doimport Image
afterwards. -
@Phuket2 No worries had this lying around.
@dgelessus thanks for the tip. Yeah I think I imported image separately because at one point it wasn't working..I'm not sure why (I don't remember) but maybe one of the betas? Goes to show my modus operandi - smack it with a hammer until it works. I'll go ahead and take it out. The reason I didn't do just
io.StringIO
is because I was under the impressions thatcStringIO
is faster, at least when in Python 2 - please correct me if I'm wrong.I've amended the script above.
I thought I had this working in Python 3 but I guess not! As it currently stands in pythonista 3 I get the error
TypeError: initial_value must be str or None, not bytes
at line 23 wherefile=cStringIO.StringIO(urllib2.urlopen(url).read())
(Keep in mind I did
import io as cStringIO
andimport urllib.request as urllib2
for when it's in Python3 mode ) -
If you're dealing with Python 3 compatibility, you need to be very careful about what "string" types you're using. In Python 2, default
str
is a byte string andunicode
is (almost) full Unicode; in Python 3, defaultstr
is full Unicode andbytes
is obviously a byte string. (In Python 2,bytes
is a valid alternate name forstr
.)In this case it looks like
urllib2.urlopen(url).read()
returnsbytes
, and you're feeding it into aStringIO
, which expects a text string. Python 2 is sloppy with the distinction and force-decodesbytes
/str
using UTF-8 intounicode
, so everything works fine there. In Python 3, no automatic conversion happens, andStringIO
complains because you gave itbytes
instead of a Unicodestr
.How to solve this:
- Simply
import io
no matter what Python version you use. Thenio.StringIO
works with Unicode strings, andio.BytesIO
with byte strings. (Also when working with local files, useio.open
rather thanopen
. On Python 2 this allows proper use ofunicode
, and on Python 3open == io.open
.) - In this case, always use
bytes
, as you're working with binary image data. Use the namebytes
instead ofstr
,BytesIO
instead ofStringIO
, and if you need to use literals, writeb"..."
instead of"..."
. (This of course only applies to cases where you want to use binary data. When working with text, you should use Unicode when possible.) - When writing scripts that should work on Python 2 and 3, start in Python 3. There you will get errors when you mix up
bytes
andstr
by accident, instead of "magical" conversion like in Python 2. Once your code works on Python 3, try to run it on Python 2 and fix what doesn't work.
- Simply
-
@dgelessus Thanks so much for imparting your knowledge on me. I've edited the above script such that the code is the same, but the namespace changes based on if it's python 2 or 3 (there's probably a reason this is not recommended but i set
cStringIO.BytesIO = cStringIO.StringIO
for the python 2 case, and it works in both now. ) I also broke out the getting an image from url and masking it parts into separate functions.-
you mention
io.open
instead ofopen
.... do you mean instead ofImage.open()
? Because that seems to be different? or did you mean in general, and not in this example? -
about bytes - if i do
print(type(bytes(imageDataFromURL)))
it still prints out as<type "str">
in python2.
Finally, I decided to test
cStringIO.StringIO()
vsio.BytesIO
and Python2 vs 3 by usingtimeit.Timer().timeit(number=1000)
for 1000 operations each. (Done on an iPhone 6s +) Here are the results:Pythonista 2 using cStringIO.StringIO():
396.739Pythonista 2 using io.BytesIO:
397.933
397.154Pythonista 3 using io.BytesIO():
383.361As you can see the difference between
cStringIO.StringIO()
andio.BytesIO()
seems to be negligible.Pythonista 3 w/ io.BytesIO seems to outperform Pythonista 2 and io.BytesIO by average of about .0133 seconds per run which is a small but I believe notable gain especially if you do this operation over and over again.
So, in conclusion, there doesn't seem to be any reason to use cStringIO.StringIO() over io.BytesIO() in python2.
I'll post the simplified code to reflect that conclusion below. -
-
# coding: utf-8 #import time import ui import io from PIL import Image, ImageOps, ImageDraw #Try except for python2 vs 3 try: import urllib2 print("pyth2") except ImportError: import urllib.request as urllib2 print("pyth3") def grabImageFromURL(url): url=url #load image from url and show it imageDataFromURL = urllib2.urlopen(url).read() print("imageData from URL of Type: ",type(bytes(imageDataFromURL))) return imageDataFromURL def circleMaskViewFromImageData(imageData): imageDataFromURL=imageData #imageDataFromURL = urllib2.urlopen(url).read() #broken out into separate function ^^^ file=io.BytesIO(imageDataFromURL) img = Image.open(file) #img = io.open(file) ???? #begin mask creation bigsize = (img.size[0] * 3, img.size[1] * 3) mask = Image.new('L', bigsize, 0) draw = ImageDraw.Draw(mask) draw.ellipse((0, 0) + bigsize, fill=255) mask = mask.resize(img.size, Image.ANTIALIAS) img.putalpha(mask) #show final masked image #img.show() img=pil2ui(img) return img def circleMaskViewFromURL(url): url = url imageData = grabImageFromURL(url) maskedImage = circleMaskViewFromImageData(imageData) return maskedImage def pil2ui(imgIn): #pil image to ui image with io.BytesIO() as bIO: imgIn.save(bIO, 'PNG') imgOut = ui.Image.from_data(bIO.getvalue()) del bIO return imgOut def wrapper(func, *args, **kwargs): #wrapper function for timing with parameters def wrapped(): return func(*args, **kwargs) return wrapped if __name__=="__main__": testURL = "http://vignette2.wikia.nocookie.net/jamesbond/images/3/31/Vesper_Lynd_(Eva_Green)_-_Profile.jpg/revision/latest?cb=20130506215331" #____TIMING TEST__________ #testImage = grabImageFromURL(testURL) #wrapped = wrapper(circleMaskViewFromImageData, testImage) #b= timeit.Timer(wrapped).timeit(number=1000) #print(b) #_____END TIMING TEST______ circleMaskViewFromURL(testURL).show()
-
Forget that with
io.open
for now - that is for reading local files (I forgot for a moment that you read the data from a URL, not a file).io.open
is Python 3'sopen
function backported to Python 2, which means that (among other things) Unicode is handled correctly.On Python 2, there is no dedicated
bytes
type. Becausestr
is a bytestring in Python 2,bytes
is simply an alias forstr
. In pseudo-Python code:class str(object): ... bytes = str
-
For reference, for anyone searching for this topic, why was this not done with
corner_radius
?