omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    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

    Pythonista
    circle circular widget ui.view
    5
    12
    9350
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Phuket2
      Phuket2 last edited by

      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')
      
      1 Reply Last reply Reply Quote 1
      • omz
        omz last edited by

        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()
        
        Phuket2 1 Reply Last reply Reply Quote 1
        • Tizzy
          Tizzy last edited by Tizzy

          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()
          	
          
          
          
          
          Phuket2 1 Reply Last reply Reply Quote 0
          • Phuket2
            Phuket2 @omz last edited by

            @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.

            1 Reply Last reply Reply Quote 0
            • Phuket2
              Phuket2 @Tizzy last edited by

              @Tizzy , hey thanks for your solution. I was actually looking for what @omz suggested. Hope you didn't waste your time.
              But the above also maybe helpful for you as its light weight.
              Once I make it into something reusable,I will post share

              Tizzy 1 Reply Last reply Reply Quote 0
              • dgelessus
                dgelessus last edited by dgelessus

                @Tizzy FYI, io.StringIO exists on Python 2 and 3, and urllib3 is a third-party (not Python standard library) module, so it also exists on both versions. (The Python 3 version of urllib2 is called urllib.) And if you do from PIL import Image, you don't need to do import Image afterwards.

                1 Reply Last reply Reply Quote 0
                • Tizzy
                  Tizzy @Phuket2 last edited by

                  @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 that cStringIO 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 where file=cStringIO.StringIO(urllib2.urlopen(url).read())

                  (Keep in mind I did import io as cStringIO and import urllib.request as urllib2 for when it's in Python3 mode )

                  1 Reply Last reply Reply Quote 0
                  • dgelessus
                    dgelessus last edited by

                    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 and unicode is (almost) full Unicode; in Python 3, default str is full Unicode and bytes is obviously a byte string. (In Python 2, bytes is a valid alternate name for str.)

                    In this case it looks like urllib2.urlopen(url).read() returns bytes, and you're feeding it into a StringIO, which expects a text string. Python 2 is sloppy with the distinction and force-decodes bytes/str using UTF-8 into unicode, so everything works fine there. In Python 3, no automatic conversion happens, and StringIO complains because you gave it bytes instead of a Unicode str.

                    How to solve this:

                    1. Simply import io no matter what Python version you use. Then io.StringIO works with Unicode strings, and io.BytesIO with byte strings. (Also when working with local files, use io.open rather than open. On Python 2 this allows proper use of unicode, and on Python 3 open == io.open.)
                    2. In this case, always use bytes, as you're working with binary image data. Use the name bytes instead of str, BytesIO instead of StringIO, and if you need to use literals, write b"..." 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.)
                    3. 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 and str 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.
                    1 Reply Last reply Reply Quote 0
                    • Tizzy
                      Tizzy last edited by

                      @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.

                      1. you mention io.open instead of open .... do you mean instead of Image.open()? Because that seems to be different? or did you mean in general, and not in this example?

                      2. 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() vs io.BytesIO and Python2 vs 3 by using timeit.Timer().timeit(number=1000) for 1000 operations each. (Done on an iPhone 6s +) Here are the results:

                      Pythonista 2 using cStringIO.StringIO():
                      396.739

                      Pythonista 2 using io.BytesIO:
                      397.933
                      397.154

                      Pythonista 3 using io.BytesIO():
                      383.361

                      As you can see the difference between cStringIO.StringIO() and io.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.

                      1 Reply Last reply Reply Quote 0
                      • Tizzy
                        Tizzy last edited by Tizzy

                        
                        # 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()
                        	
                        
                        
                        1 Reply Last reply Reply Quote 0
                        • dgelessus
                          dgelessus last edited by

                          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's open function backported to Python 2, which means that (among other things) Unicode is handled correctly.

                          On Python 2, there is no dedicated bytes type. Because str is a bytestring in Python 2, bytes is simply an alias for str. In pseudo-Python code:

                          class str(object):
                              ...
                          
                          bytes = str
                          
                          1 Reply Last reply Reply Quote 0
                          • mikael
                            mikael last edited by

                            For reference, for anyone searching for this topic, why was this not done with corner_radius?

                            1 Reply Last reply Reply Quote 0
                            • First post
                              Last post
                            Powered by NodeBB Forums | Contributors