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.
    • Webmaster4o
      Webmaster4o last edited by

      The Mac finder has a view called cover flow, which looks like this:


      Icons/file previews are displayed with the selected one in front and the rest behind on either side.

      I'd like to try to emulate this with a custom view class, where square buttons are displayed, each with an image on it. Ideally, the user would be able to swipe in the cover flow view and it would animate as it does on my Mac, then they can tap one to perform an action.

      Is this feasible in UI, or is it beyond the scope of UI and I should try to do it in scene? It will ultimately be in a UI, so for scene I would have to use a SceneView. I'm kind of leaning towards using scene because of touch handling, but I wanted to see what other people thought.

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

        I would imagine there would be a way for a scroll view to work horizontally, that would probably be ideal - I'm not sure if there is though. Haven't used it yet.

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

          This would work, but the effect I'm going for, not everything moves at the same pace. Items flow at different speeds based on their position. Here's a video I found showing the animation.

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

            @Webmaster4o Right...I have a Mac so I know exactly what you mean ;). Just trying to point in the right direction

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

              implemented as a slider, i think this could be done. You will probably want both a position and a scale component, and will need to brint to front the view moving across the center. It probably wont be able to be that smooth, or have the "physics" , though if you delve into objective c, then maybe.

              as a view where you swipe on the views, it gets tricky because without objc you have no control over which view gets the touch events.

              if you just want to draw covers, and not have them "interactive", i.e just have images but not buttons, etc, then this could be done within a draw method.

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

                Thanks @JonB. Right now I'm trying to write a function for the x positions of the imageviews based on the length of the stack and their position in the stack

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

                  @Webmaster4o, I think the I VirtualView Class am working on , could be helpful, even though its slanted for vertical display, but could be easily modified for horizontal display!
                  worth a look.
                  https://github.com/Phuket2/VirtualView.git

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

                    All the examples I can find online rely HEAVILY on OpenGL. :|

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

                      People have tried to do this in the past with pygame/pyopenGL, I'm gonna look at some of those examples. I got a UI set up:


                      The UI is the width of my iPad mini screen, and half the height. I'll setup ananimate function that scales and moves each ImageView to the position of the next. Then replace that view with the original, but all the images are shifted, so there is no jump, but the layout of the imageviews is the same.

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

                        Looking forward to seeing more!

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

                          Translated pyui to code, images in place. Have a commitment now, so I possibly won't be back for 4 hours.

                          # coding: utf-8
                          import ui
                          from PIL import Image
                          from io import BytesIO
                          
                          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]
                          		self.frame = (0, 0, 1024, 384)
                          		self.background_color='#0F0F0F'
                          		
                          		
                          		#Create subviews
                          		iv0 = ui.ImageView(frame=(-100, 142, 100, 100), name='iv0')
                          		iv0.image = self.images[0]
                          		self.add_subview(iv0)
                          		
                          		iv1 = ui.ImageView(frame=(-10, 122, 140, 140), name='iv1')
                          		iv1.image = self.images[1]
                          		self.add_subview(iv1)
                          		
                          		iv2 = ui.ImageView(frame=(90, 102, 180, 180), name='iv2')
                          		iv2.image = self.images[2]
                          		self.add_subview(iv2)
                          		
                          		iv3 = ui.ImageView(frame=(180, 72, 240, 240), name='iv3')
                          		iv3.image = self.images[3]
                          		self.add_subview(iv3)
                          		
                          		iv4 = ui.ImageView(frame=(327, 7, 370, 370), name='iv4')
                          		iv4.image = self.images[4]
                          		self.add_subview(iv4)
                          		
                          		iv5 = ui.ImageView(frame=(604, 72, 240, 240), name='iv5')
                          		iv5.image = self.images[5]
                          		self.add_subview(iv5)
                          		iv5.send_to_back()
                          		
                          		iv6 = ui.ImageView(frame=(754, 102, 180, 180), name='iv6')
                          		iv6.image = self.images[6]
                          		self.add_subview(iv6)
                          		iv6.send_to_back()
                          		
                          		iv7 = ui.ImageView(frame=(894, 122, 140, 140), name='iv7')
                          		iv7.image = self.images[7]
                          		self.add_subview(iv7)
                          		iv7.send_to_back()
                          		
                          		iv8 = ui.ImageView(frame=(1024, 142, 100, 100), name='iv8')
                          		iv8.image = self.images[8]
                          		self.add_subview(iv8)
                          		iv8.send_to_back()
                          		
                          	def did_load(self):
                          		self.images = [s.image for s in self.subviews]
                          
                          if __name__ == '__main__':
                          	
                          	names = ['test:Boat', 'test:Lenna', 'test:Pythonista', 'test:Peppers', 'test:Sailboat', 'test:Bridge', 'test:Mandrill', 'emj:Baby_Chick_3', 'emj:Tulip']
                          	images = [Image.open(n) for n in names]
                          	
                          	view = ui.View(background_color='#0f0f0f')
                          	view.add_subview(CoverFlow(images))
                          	view.present(hide_title_bar=True)	
                          
                          1 Reply Last reply Reply Quote 1
                          • Webmaster4o
                            Webmaster4o last edited by

                            Started animation. Somehow the first and last images end up stacked during animation, so that needs to be fixed.

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

                              Stopping for tonight. First day of high school tomorrow, that means summer's over and I have a lot less time. Here's what I have now. Fixed most bugs. I still want to tweak the animation, but here it is.

                              # coding: utf-8
                              import ui
                              from PIL import Image
                              from io import BytesIO
                              
                              
                              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]
                              		self.frame = (0, 0, 1024, 384)
                              		self.background_color='#0F0F0F'
                              		
                              		
                              		#Create subviews
                              		iv0 = ui.ImageView(frame=(-100, 142, 100, 100), name='iv0')
                              		iv0.image = self.images[0]
                              		iv0.prev_frame = iv0.frame
                              		self.add_subview(iv0)
                              		
                              		iv1 = ui.ImageView(frame=(-10, 122, 140, 140), name='iv1')
                              		iv1.image = self.images[1]
                              		iv1.prev_frame = iv1.frame
                              		self.add_subview(iv1)
                              		
                              		iv2 = ui.ImageView(frame=(90, 102, 180, 180), name='iv2')
                              		iv2.image = self.images[2]
                              		iv2.prev_frame = iv2.frame
                              		self.add_subview(iv2)
                              		
                              		iv3 = ui.ImageView(frame=(180, 72, 240, 240), name='iv3')
                              		iv3.image = self.images[3]
                              		iv3.prev_frame = iv3.frame
                              		self.add_subview(iv3)
                              		
                              		iv4 = ui.ImageView(frame=(327, 7, 370, 370), name='iv4')
                              		iv4.image = self.images[4]
                              		iv4.prev_frame = iv4.frame
                              		self.add_subview(iv4)
                              		
                              		iv5 = ui.ImageView(frame=(604, 72, 240, 240), name='iv5')
                              		iv5.image = self.images[5]
                              		self.add_subview(iv5)
                              		iv5.prev_frame = iv5.frame
                              		iv5.send_to_back()
                              		
                              		iv6 = ui.ImageView(frame=(754, 102, 180, 180), name='iv6')
                              		iv6.image = self.images[6]
                              		self.add_subview(iv6)
                              		iv6.prev_frame = iv6.frame
                              		iv6.send_to_back()
                              		
                              		iv7 = ui.ImageView(frame=(894, 122, 140, 140), name='iv7')
                              		iv7.image = self.images[7]
                              		self.add_subview(iv7)
                              		iv7.prev_frame = iv7.frame
                              		iv7.send_to_back()
                              		
                              		iv8 = ui.ImageView(frame=(1024, 142, 100, 100), name='iv8')
                              		iv8.image = self.images[8]
                              		self.add_subview(iv8)
                              		iv8.prev_frame = iv8.frame
                              		iv8.send_to_back()
                              
                              				
                              	def right_anim(self):
                              		for sub in self.subviews:
                              			index = self.subviews.index(sub)
                              			next = self.subviews[index+1] if index<8 else self.subviews[0]
                              			sub.frame = next.prev_frame
                              		for sub in self.subviews:
                              			sub.prev_frame = sub.frame
                              				
                              		#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 left_anim(self):
                              		for sub in self.subviews:
                              			index = self.subviews.index(sub)
                              			next = self.subviews[index-1] if index>0 else self.subviews[8]
                              			sub.frame = next.prev_frame
                              		for sub in self.subviews:
                              			sub.prev_frame = sub.frame
                              				
                              		#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):
                              		pass
                              		
                              	def touch_moved(self, touch):
                              		if touch.location[0]<touch.prev_location[0]:
                              			self.touch_direction = 0
                              		elif touch.location[0]>touch.prev_location[0]:
                              			self.touch_direction = 1
                              			
                              	def touch_ended(self, touch):
                              		if self.touch_direction:
                              			ui.animate(self.right_anim, duration=1.0)
                              		else:
                              			ui.animate(self.left_anim, duration=1.0)
                              
                              if __name__ == '__main__':
                              	
                              	names = ['test:Boat', 'test:Lenna', 'test:Pythonista', 'test:Peppers', 'test:Sailboat', 'test:Bridge', 'test:Mandrill', 'emj:Baby_Chick_3', 'emj:Tulip']
                              	images = [Image.open(n) for n in names]
                              	
                              	view = ui.View(background_color='#0f0f0f')
                              	view.add_subview(CoverFlow(images))
                              	view.present(hide_title_bar=True)
                              
                              Phuket2 1 Reply Last reply Reply Quote 2
                              • Phuket2
                                Phuket2 @Webmaster4o last edited by

                                @Webmaster4o , very nice. Only thing I would say is the animate time. I am on a iPad Air 2 and it did not feel responsive enough. I changed your values to from 1.0 to 0.5, a lot nicer for animation experience. Maybe a nice addition would be to look at the hardware to set the animation delay. Gives me the idea to do the same also for some of the stuff I am trying to write.

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

                                  Yeah. It still needs work in the following areas:

                                  1. It only works with exactly 9 images, it should work with as many as you need
                                  2. You can see the last image sliding behind to the first
                                  3. It only supports the screen size of my iPad mini

                                  But, I'm going to school now

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

                                    Yeah, I can see needs a few things done. But still good. I will try to get into a loop handling 1 to x images. Try that is :)
                                    Also use layout so that its adjusts to screen size and orientation.

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

                                      A simplification of pil_to_ui() and a simplification of CoverFlow.__init__() via the addition of make_image_view(). One of the things that still needs doing is to calculate frames based on screen size/orientation.

                                      def pil_to_ui(img):
                                          with BytesIO() as b:
                                              img.save(b, "PNG")
                                              return ui.Image.from_data(b.getvalue())
                                      
                                      def make_image_view(image, frame, index):
                                          image_view = ui.ImageView(frame=frame, name='iv{}'.format(index))
                                          image_view.image = image
                                          image_view.prev_frame = frame
                                          return image_view
                                      
                                      class CoverFlow(ui.View):
                                          def __init__(self, images):
                                              self.images = (pil_to_ui(image) for image in images)
                                              self.frame = (0, 0, 1024, 384)
                                              self.background_color='#0F0F0F'
                                              
                                              frames = ( (-100, 142, 100, 100), (-10, 122, 140, 140),
                                                         (90, 102, 180, 180), (180, 72, 240, 240),
                                                         (327, 7, 370, 370), (604, 72, 240, 240),
                                                         (754, 102, 180, 180), (894, 122, 140, 140),
                                                         (1024, 142, 100, 100) )
                                      
                                              #Create subviews
                                              for i, image in enumerate(self.images):
                                                  self.add_subview(make_image_view(image, frames[i], i))
                                      
                                      1 Reply Last reply Reply Quote 0
                                      • ccc
                                        ccc last edited by ccc

                                        Combined left_anim() and right_anim() into a single anim() method. Tightened up touch_moved() and touch_ended() to make the ui more responsive. My sense is that Apple's CoverFlow starts to move in touch_moved() whereas this version does not start to move until touch_ended().

                                            def anim(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]
                                                    sub.frame = next.prev_frame
                                                for sub in self.subviews:
                                                    sub.prev_frame = sub.frame
                                                        
                                                #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):
                                                pass
                                                
                                            def touch_moved(self, touch):
                                                self.touch_direction = int(touch.location.x > touch.prev_location.x)
                                                    
                                            def touch_ended(self, touch):
                                                ui.animate(self.anim, duration=0.25)
                                        
                                        Phuket2 1 Reply Last reply Reply Quote 1
                                        • Webmaster4o
                                          Webmaster4o last edited by

                                          @ccc Like your use of enumerate, it reminded me that enumerate exists. My code can be so much cleaner with it.

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

                                            @ccc, looks very nice. I was also rewriting , but not as good as you. But I was trying to find a a nice mathematical way to derive the frames. Also, you pumped into my head about enumerate, is so handy.

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