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.


    Cover Flow view

    Pythonista
    1.6 custom-view mac scene
    6
    51
    38850
    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.
    • ccc
      ccc last edited by

      Please make it a Github repo so that we can add Pythonista-Tools link to it.

      1 Reply Last reply Reply Quote 1
      • Webmaster4o
        Webmaster4o last edited by

        Sure, @ccc I'll do that when I'm finished with this bit.

        1 Reply Last reply Reply Quote 2
        • Webmaster4o
          Webmaster4o last edited by Webmaster4o

          @JonB None of the numbers besides 512 make sense to me as logical fractions of 1024. Where did they come from?

          Additionally, the sentence

          divide by 1024 and multiply by screen width

          is confusing to me, because 1024 IS the screen width... x/1024*1024 = x

          Also, if it's not a lot of work, could you try to explain the math behind your equations? I don't like to borrow code I don't understand, it's a mini-policy of mine. (No, I don't understand all the code behind all the modules I import, I mean I don't like to insert others' code into my own without understanding it)

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

            Yes you correct re screen width... my point is if you want this to scale for a different screen width, like say 768 in portrait, you would divide by 1024, and multiply by screen width. in retrospect, you could just scale the final numbers.

            when i plotted width vs index, and noticed it looked a lot like an exponential decay about the center.. That gives an exp(-abs(i-N/2)) form. The leading factor is the width of the central frame. To get the width at the end, i played with the factor and exponent inside the exponential. Then, i experimented with scaling for more frames, keeping the center width and end width the same. I thought you would still want a big difference betwenn center and next frames, so i played with the exponent.

            Likewise, i plotted xc vs w, and saw a similar exponential factor, and again some trial and error. Y vs w appeared very linear, so i just fit a line.

            1 Reply Last reply Reply Quote 1
            • Webmaster4o
              Webmaster4o last edited by

              Spent today working on image2ASCII, will get to this tomorrow.

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

                I get the feeling I'm using your code wrong. What am I doing that isn't as intended? some of your sentences don't make a lot of sense to me:

                the relationship between center and width from the original

                I've implemented your code as follows:

                #Make frames for all views
                N = 9
                frames = []
                for i in range(N):
                	#Thanks to @JonB for this code.
                	w=370*exp(-2.4*(abs((i-N/2.0-0.5)/N)**(1.38/N**0.25)))
                	xc=512+copysign(470.*log(w/370.),i-N/2)
                	yc= (192-w)
                	frames.append((xc+.5*w, yc+.5*w, w, w))
                

                Looks pretty good for the first half, the rest are messed up. I feel stupid, but if you could please correct my mistakes that'd be wonderful. (>ლ)

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

                  Sorry, I think I made an off-by-one error in the width equation. The i-N/2.0-0.5 should be i-N/2.0+0.5.

                  Also, I think you want frames.append((xc-0.5*w, yc-0.5*w,w,w)) instead of +0.5 in both places

                  1 Reply Last reply Reply Quote 1
                  • Webmaster4o
                    Webmaster4o last edited by

                    Thanks! It actually needed to be (xc-0.5*w, yc+0.5*w, w, w) in order for it to work correctly.

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

                      by the way, one of the nice things re: the equation is that you can use touch_moved to create continuous motion along the curve. Basically you would associate each image with a index variable, touch_moved generates a fractional index offset added to each one, say, the x part of the motion divided by the center diff, and wrapping around negative values. still use a zero time animatin to sync up the motion. Then touch_ended would round to the nearest index, and do a slower animation to snap everybody in place. You could also use the "velocity" of the last swipe to over scan.

                      1 Reply Last reply Reply Quote 1
                      • Webmaster4o
                        Webmaster4o last edited by Webmaster4o

                        New code, supports different screen sizes, and auto-resizes with device rotation. Also supports arbitrary numbers of images. It will display your images whether you have 1 image or 100. Now running it will also pick random images from the included images to present.

                        # coding: utf-8
                        import ui
                        from PIL import Image
                        from io import BytesIO
                        from time import sleep
                        from math import exp, log, copysign, ceil
                        
                        BGCOLOR = '#0F0F0F'
                        
                        def pil_to_ui(img):
                        	b = BytesIO()
                        	img.save(b, "PNG")
                        	data = b.getvalue()
                        	b.close()
                        	return ui.Image.from_data(data)
                        
                        				
                        class CoverFlow(ui.View):
                        	def __init__(self, images):
                        		self.images = [pil_to_ui(image) for image in images]
                        		if len(self.images) < 9:
                        			self.images *= int(ceil(9./len(self.images)))
                        		self.frame = (0, 0, 1024, 768)
                        		self.oldframe = (0, 0, 1024)
                        		self.background_color = BGCOLOR
                        		#Make frames for all views
                        		N = 9
                        		frames = []
                        		#Create frames
                        		for i in xrange(N):
                        			#Thanks to @JonB for this code.
                        			w=370/1024.*self.width*exp(-2.4*(abs((i-N/2.0+0.5)/N)**(1.38/N**0.25)))
                        			xc=512/1024.*self.width+copysign(470.0/1024*self.width*log(w/(370.0/1024*self.width)),i-N/2)
                        			yc= (192/1024.*self.width-w)
                        			frames.append((xc-0.5*w, yc+0.5*w, w, w))
                        		
                        		#Create subviews for the minimum of 9 images
                        		for index, frame in enumerate(frames):
                        			iv = ui.ImageView(frame=frame)
                        			iv.image = self.images[index]
                        			iv.prev_frame = iv.frame
                        			self.add_subview(iv)
                        			if index > 4:
                        				iv.send_to_back()
                        				
                        		#Handle any additional images that are provided
                        		for i in self.images[9:]:
                        			iv9 = ui.ImageView(frame=frames[-1])
                        			iv9.image = i
                        			iv9.prev_frame = iv9.frame
                        			self.add_subview(iv9)
                        			iv9.send_to_back()
                        	
                        	def layout(self):
                        		if self.frame != self.oldframe:
                        			N = 9
                        			frames = []
                        			#Create frames
                        			for i in range(N):
                        				#Thanks to @JonB for this code.
                        				w=370/1024.*self.width*exp(-2.4*(abs((i-N/2.0+0.5)/N)**(1.38/N**0.25)))
                        				xc=512/1024.*self.width+copysign(470.0/1024*self.width*log(w/(370.0/1024*self.width)),i-N/2)
                        				yc= (192/1024.*self.width-w)
                        				frames.append((xc-0.5*w, yc+0.5*w, w, w))
                        			
                        			for sv in self.subviews:
                        				self.remove_subview(sv)
                        			#Create subviews for the minimum of 9 images
                        			for index, frame in enumerate(frames):
                        				iv = ui.ImageView(frame=frame)
                        				iv.image = self.images[index]
                        				iv.prev_frame = iv.frame
                        				self.add_subview(iv)
                        				if index > 4:
                        					iv.send_to_back()
                        					
                        			#Handle any additional images that are provided
                        			for i in self.images[9:]:
                        				iv9 = ui.ImageView(frame=frames[-1])
                        				iv9.image = i
                        				iv9.prev_frame = iv9.frame
                        				self.add_subview(iv9)
                        				iv9.send_to_back()
                        			self.oldframe = self.frame
                        		
                        	def anim(self):
                        		#Change frames
                        		len_subviews = len(self.subviews)
                        		for i, sub in enumerate(self.subviews):
                        			next = self.subviews[(i + (1 if self.touch_direction else -1)) % len_subviews]
                        			sub.frame = next.prev_frame
                        		for sub in self.subviews:
                        			sub.prev_frame = sub.frame
                        		
                        		#Resort self.images
                        		if self.touch_direction:
                        			self.images = [self.images[-1]]+self.images
                        			self.images.pop(-1)
                        		else:
                        			self.images = self.images + [self.images[0]]
                        			self.images.pop(0)
                        		#Reorder layers based on the fact that biggest images are in front
                        		#Bring smallest images to front first
                        		for s in sorted(self.subviews, key=lambda x: x.frame[3]): s.bring_to_front()
                        
                        	def touch_began(self, touch):
                        		self.touch_direction = None
                        
                        	def touch_moved(self, touch):
                        		if touch.location != touch.prev_location:
                        			self.touch_direction = int(touch.location.x > touch.prev_location.x)
                        		else:
                        			pass
                        
                        	def touch_ended(self, touch):
                        		if self.touch_direction != None:
                        			ui.animate(self.anim, duration=0.25)
                        
                        if __name__ == '__main__':
                        	#Pick random images from default textures
                        	import os, random
                        	app_path = os.path.abspath(os.path.join(os.__file__, '../../../..'))
                        	os.chdir(app_path + '/Textures')
                        	imagenames = os.listdir(os.curdir)
                        	validnames = []
                        	for x in imagenames:
                        		if not (x.startswith('ionicons') or x.startswith('Typicons')):
                        			validnames.append(x)
                        	
                        	images = [Image.open(random.choice(validnames)) for x in xrange(15)]
                        	view = ui.View(background_color=BGCOLOR)
                        	cf = CoverFlow(images)
                        	view.add_subview(cf)
                        	cf.present(hide_title_bar=1)
                        

                        layout literally removes all the subviews and recreates them, because I couldn't figure out how to adjust the frames easily, but the layout method takes under a hundredth of a second to perform so I don't think it's a problem.

                        Phuket2 1 Reply Last reply Reply Quote 2
                        • Webmaster4o
                          Webmaster4o last edited by

                          Realized I can cut out most of __init__, gist is here. I think I'm done with it, now. I'd like to maybe get rid of the objects animating sliding behind, I'll edit the gist for that.

                          1 Reply Last reply Reply Quote 1
                          • techteej
                            techteej last edited by

                            @Webmaster4o I would recommend making it a repo instead- as you can accept pull requests and make it easier to collaborate and build on.

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

                              Sure. I've also just realized that the only thing (I think) preventing it from being 1.5-compatible is the assigning of arbitrary attributes to views (sv.prev_frame). I should be able to eliminate this easily, then it'll work on non-beta devices.

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

                                Having trouble setting alpha to 0 for the frame passing behind. I tried to set the frames alpha before and after animating, but this doesn't work because code after ui.animate is executed before the animation has finished. My code was this, but it's sloppy and doesn't work:
                                def animate(self):
                                len_subviews = len(self.subviews)

                                	for i, sub in enumerate(self.subviews):
                                		next = self.subviews[(i + (1 if self.touch_direction else -1)) % len_subviews]
                                		if next.prev_frame[0] < sub.prev_frame[0]:
                                			resetter = sub
                                			sub.alpha = 0
                                	
                                	def anim():
                                		#Change frames
                                		for i, sub in enumerate(self.subviews):
                                			next = self.subviews[(i + (1 if self.touch_direction else -1)) % len_subviews]
                                			sub.frame = next.prev_frame
                                
                                	#Animate
                                	ui.animate(anim, 0.25)
                                	resetter.alpha = 1
                                	#Re-sort self.images
                                	if self.touch_direction:
                                		self.images = [self.images[-1]]+self.images
                                		self.images.pop(-1)
                                	else:
                                		self.images = self.images + [self.images[0]]
                                		self.images.pop(0)
                                	
                                	#Reorder layers based on the fact that biggest images are in front,
                                	#Bring smallest images to front first
                                	for s in sorted(self.subviews, key=lambda x: x.frame[3]): s.bring_to_front()
                                	
                                	#Reassign prev_frame
                                	for sub in self.subviews:
                                		sub.prev_frame = sub.frame
                                
                                1 Reply Last reply Reply Quote 0
                                • Phuket2
                                  Phuket2 @Webmaster4o last edited by

                                  @Webmaster4o, I don't have much time to look at the forum at the moment. Many friends in town now. But your CoverFlow class is a great improvement.

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

                                    Gist updated with backwards compatibility to 1.5, successful on my iPhone running ios7 and 1.5. I probably will end up making it a repo soon.

                                    1 Reply Last reply Reply Quote 1
                                    • TutorialDoctor
                                      TutorialDoctor last edited by

                                      Nice work! Coming along nicely. I am wondering if this could browse IOS photos? What would you use it for?

                                      Phuket2 1 Reply Last reply Reply Quote 0
                                      • Webmaster4o
                                        Webmaster4o last edited by

                                        Browsing iOS photos is a good idea.

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

                                          @TutorialDoctor, you ask what it could be used for. I think is just nice ui control, same as a slider. I think a nice step fwd would be to be able to provide a ui.view. Then not just images anymore. Also the cover controller doesn't care, just providing a series of frames with all the correct math and animations and touch events.

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