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.


    [Lab] ui.animate - sliding in views

    Pythonista
    lab ui.animate ui.view
    6
    19
    14128
    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 didn't say [share] code because I am just trying, I know what I have done us crappy. I have touched on ui.animate a few times, but never got in to it. But I know it's very powerful. I am surprised I don't see more ui examples here using it. What I have put below is crap. Just to spark some ideas. Should be possible to make a single function with all sort of presentation permutations for a subview.

      '''
      	Pythonista Forum - @Phuket2
      '''
      import ui, editor
      
      def slide_up(p, v, reverse = False, delay = 1.0 ):
      	v.y = p.height if not reverse else -p.height
      	
      	def animation():
      			v.y = 0
      	
      	ui.animate(animation, delay)
      	
      def slide_in(p, v, reverse = False, delay = 1.0 ):
      	v.x = p.width if not reverse else -p.width
      	
      	def animation():
      			v.x = 0
      	
      	ui.animate(animation, delay)	
      
      class MyClass2(ui.View):
      	def __init__(self, *args, **kwargs):
      		super().__init__(*args, **kwargs)
      		self.bg_color = 'deeppink'
      		btn = ui.Button(frame=(30, 30, 100, 100))
      		btn.title = 'hit me'
      		btn.border_width =2
      		btn.border_color = 'white'
      		btn.corner_radius = btn.width / 2
      		self.add_subview(btn)
      	
      	def draw(self):
      		# do a draw, just to see how it works
      		r = ui.Rect(*self.bounds).inset(20, 20)
      		s = ui.Path.rect(*r)
      		s.line_width = 10
      		ui.set_color('blue')
      		s.stroke()
      		
      		
      class MyClass(ui.View):
      	def __init__(self, *args, **kwargs):
      		super().__init__(*args, **kwargs)
      	
      	def add_view(self, v):
      		self.add_subview(v)
      		# comment out either line below for 1 effect
      		slide_up(self, v, reverse=False, delay = 3)
      		slide_in(self, v, reverse=False, delay=.8)
      	
      
      if __name__ == '__main__':
      	_use_theme = True
      	w, h = 540, 540
      	f = ui.Rect(0, 0, w, h)
      	style='sheet'
      	mc = MyClass(frame=f, bg_color='white')
      	
      	if not _use_theme:
      		mc.present(style = style, animated=False)
      	else:
      		editor.present_themed(mc, theme_name='Oceanic', style=style, animated=False)
      	mc2 = MyClass2(frame = f)
      	mc.add_view(mc2)
      ```python
      1 Reply Last reply Reply Quote 1
      • JonB
        JonB last edited by JonB

        For sliding views around, it might be worth considering animating ui.Transforms, instead of frame. The nice thing about transform is that they "remember" where the view belongs, which can simplify the logic of restoring the view. For instance, i think I tried my hand at a splitview a while back, the logic to handle a left split vs right split was annoying when using .x and y, either full of if/else, or filled with equations that combine conditions e.g. v.x =-(1-righthanded)*v.width+ righthanded*(v.superview.width-v.width) that are hard to test or follow.

        With ui.Transform, you set the view's frame once, then just manipulate its transform. You can use ui.Transform() to get back the default. You do have to be a little careful about keeping the difference between frame and bounds straight (use bounds for working in the transformed coordinate system, use frame for working relative to the superview).

        It is also easier to create some fun effects:
        https://gist.github.com/d7dbf8bfa6ace92b38430e6f8b80e995

        Phuket2 2 Replies Last reply Reply Quote 1
        • cvp
          cvp last edited by

          Really nice, I like it

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

            @JonB , I also like your example. Nice effect. I did know about animating the transforms, but just a little. I thought I would start at the beginning with something simple although very limited. I have tended to copy paste the transforms code without understanding it.
            But this post has worked, got a great example an explanation and the small fact I went back to the beginning and thought about what I was doing made it easy to follow your example 😁
            I am sure it will help others here also. So thanks once again.

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

              Try this image carousel.
              https://gist.github.com/balachandrana/de389c3bd84f006ad4c8d5e3d1cd4a8d

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

                @JonB , sorry I question before I fall into the rabbit hole. Do you think it's possible to get varing speeds for say flying in a view from the left edge. Using your code , I did a fly in just using the translation. Works nice.
                Then I modified so it only flew in part the way, then on the completion func, I reset the translation and recalled ui.animate with a different delay. In principle it sort of works but jumps around a bit. So essentially it does not work the way I did it. I just thought you may know off the top of your head if this is a fool errand, or is it plausible?

                Anyway, regardless for doing simple fly ins it's very nice and simple, and they look effective. Controlling the acceleration would just be a bonus
                Also with your full example, as you say, you can get some nice effects easily just playing with the numbers.

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

                  This is a strange one. Maybe it's suppose to work like this. But if you do v.transform=ui.Transform.scale(-0.1, -0.1), passing both negative numbers it also rotates as well as scales. If one number is not negative then only the scale is done. Strange, maybe some shortcut

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

                    for whatever reason, ui.animate does not let you control the various curveease functions that ios has has. Simplicity i guess. Technically you might be able to use objc, the block based animations could be wrapped up to look very similar to ui.animate. Didn't @Webmaster4o add something like that to his ui2 project?

                    indeed:
                    https://github.com/controversial/ui2/blob/master/ui2/animate.py

                    there are a few easing types in ui2 which control the start and end decelerations.
                    You can also (with ui.animate too) run multiple parallel animations, with different durations to get different effects, though not always repeatable.

                    If you want to delve deeper, at a low level you could define your own timing function (i think that is called keyframe animation, but might require some low level objc)

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

                      @Phuket2 I would have expected that scaling one axis negative makes it flip about that axis, while two axes effective rotates 180 degrees... is that what happens?

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

                        @JonB 😂😂😂, yes that's what's happening. And different combos produce interesting results. Commercial style animations. I was applying this to a button. But I am still 😂 Because it makes sense to you and not me. Scale, flipping on its axis or rotating depending on combinations of positive and negative numbers is beyond my comprehension. At least for now. I am still trying to get into it. Trying many things

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

                          i think you are probably old enough to have used an overhead projector -- scaling negative in one axis flips the sheet over (text reads backwards). Then flipping the other axis (again turn over), you will get text that reads correctly, but is upside down.

                          so, if you drew some arrows on the sheet

                             y
                                ^
                                |
                                +---> x
                          

                          A scale of -1 in x means the view is flipped like a page in a book, the x axis now points negative relative to the parent)

                               y
                               ^
                               | 
                          x<---+
                          

                          note this is now a "left handed" coordinate system. text will be backwards.

                          Next, keeping x scale negative, make the y scale negative,which flips y

                                  
                          x<---+
                               |
                               v y
                          

                          which gets you back to a right handed cordinate system, but which has now been rotated 180 degrees.

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

                            Almost no time to reply today. It was raining friends. I am not sure when ui.animate got a new param, delay. I know has not always been there. But it's a game changer. Easy to present multiple views in a staggered fashion with transform. Also , very useful for controlling subview transformations. Below is crap, but still illustrates the meaning or possibilitys.

                            '''
                                    Pythonista Forum - @Phuket2
                            '''
                            import ui, editor
                            from random import choice, randint
                            
                            _colors=['rosybrown', 'antiquewhite', 'lightsteelblue', 'white', 'darkblue', 'darkviolet', 'plum', 'darkcyan', 'blanchedalmond', 'chocolate', 'sienna', 'tomato', 'peachpuff', 'lightyellow', 'bisque', 'aqua', 'oldlace', 'maroon', 'palegreen', 'chartreuse', 'darkturquoise', 'linen', 'magenta', 'lemonchiffon', 'powderblue', 'papayawhip', 'gold', 'khaki', 'lightseagreen', 'darkred', 'floralwhite', 'turquoise', 'mediumspringgreen', 'indianred', 'lightgreen', 'crimson', 'mintcream', 'lavender', 'purple', 'orchid', 'darkslateblue', 'whitesmoke', 'moccasin', 'beige', 'mistyrose', 'dodgerblue', 'hotpink', 'lightcoral', 'goldenrod', 'coral', 'cadetblue', 'black', 'mediumseagreen', 'gainsboro', 'paleturquoise', 'darkgreen', 'darkkhaki']
                            
                            class Panel(ui.View):
                            	def __init__(self, text, *args, **kwargs):
                            		self.bg_color = 'cornflowerblue'
                            		super().__init__(*args, **kwargs)
                            		
                            		self.corner_radius = 6
                            		self.make_view(text)
                            		
                            	def make_view(self, text):
                            		lb = ui.Label(name = 'lb', frame = self.frame)
                            		lb.text = text
                            		lb.font=('Arial Rounded MT Bold', 24)
                            		lb.size_to_fit()
                            		lb.center = self.bounds.center()
                            		self.add_subview(lb)
                            	
                            def e(v, t, d, x, y):
                            		'''
                            			v = the view to animate
                            			t = duration
                            			d = delay
                            			x = x
                            			y = y
                            		'''
                            		def a():
                            			v.transform=ui.Transform()
                            			
                            		def complete():
                            			pass
                            			
                            		v.transform=ui.Transform.translation(x, y)
                            		ui.animate(a, duration = t, delay = d,  completion = complete)		
                            class MyClass(ui.View):
                            	def __init__(self, *args, **kwargs):
                            		super().__init__(*args, **kwargs)
                            		
                            				
                            if __name__ == '__main__':
                            	_use_theme = True
                            	animated  = False
                            	w, h = 600, 800
                            	f = (0, 0, w, h)
                            	
                            	mc = MyClass(frame=f, bg_color='white')
                            	
                            	if not _use_theme:
                            		mc.present('sheet', animated=animated)
                            	else:
                            		editor.present_themed(mc, theme_name='Cool Glow', style='sheet', animated=False)
                            
                            	r = ui.Rect(*mc.bounds).inset(20, 20)
                            	r.height = 100
                            
                            	delay = .3
                            	x = choice([-1, 1, 0]) * mc.width
                            	y = choice([-1, 1, 0]) * mc.height
                            	for i in range(6):
                            		p = Panel(str(i),frame = r, bg_color=choice(_colors))
                            		mc.add_subview(p)
                            		
                            		e(p,.5, delay * (i*(i * .3)), x, y)
                            		r.y = r.max_y + 20
                            
                            1 Reply Last reply Reply Quote 0
                            • tileyon
                              tileyon @JonB last edited by

                              @JonB ui2 reports an error at animate.py line 77

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

                                You ought to talk to @Webmaster4o, since he's the author of ui2.. even better, post an issue over in github, with specifics of your issue (traceback, and code to reproduce). Since luke is big into unittest, and published a demo using all of the functionality of ui2, I am guessing this is a problem with your code.

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

                                  @tileyon I get error in completion parameter of animate.py. The completion parameter in "animate.py" seems to expect function with one parameter but the same in ui.animate expects function with no parameters. I do not know whether you get the same error. Anyway here is the modified code of JonB's example that runs either with animate.py or with ui.animate based on USE_ANIMATE variable. This may help to fix your problem.

                                  import ui
                                  import animate
                                  
                                  USE_ANIMATE = True
                                  #USE_ANIMATE = False
                                  
                                  def shrink(sender):
                                      def a():       
                                          v.transform=ui.Transform.rotation(-30).concat(
                                              ui.Transform.scale(0.1,0.1)).concat(ui.Transform.translation(300,300))
                                          v.alpha=0
                                          
                                      def compl(dummy=1):
                                          v.hidden=True
                                          b2.hidden=False
                                      if USE_ANIMATE:
                                          animate.animate(a,.5, completion=lambda dummy:compl(dummy))
                                      else:
                                          ui.animate(a,.5,completion=compl)
                                      
                                  def expand(sender):
                                      v.transform=ui.Transform.rotation(-30).concat(
                                          ui.Transform.scale(0.1,0.1)).concat(ui.Transform.translation(300,300))
                                      v.alpha=0.1
                                      v.hidden=False
                                      b2.hidden=True
                                      def a():
                                          v.transform=ui.Transform() #default
                                          v.alpha=1
                                      def compl(dummy=1):
                                          pass
                                      if USE_ANIMATE:
                                          animate.animate(a,.3 ,completion=lambda dummy:compl(dummy))
                                      else:
                                          ui.animate(a,.3,completion=compl)
                                      
                                  v=ui.View(bg_color='#ffc280',frame=(0,0,200,200))
                                  v.add_subview(ui.TextView(name='text',frame=(20,40,60,40)))
                                  v['text'].text='Click above'
                                  root=ui.View(frame=(0,0,560,560),bg_color='white')
                                  v.center=root.bounds.center()
                                  b=ui.Button(frame=(0,0,50,50))
                                  v.add_subview(b)
                                  b.title='Shrink'
                                  b.action=shrink
                                  b2=ui.Button(title='expand',frame=(root.width,root.height,-100,-100))
                                  b2.hidden=True
                                  b2.action=expand
                                  root.add_subview(b2)
                                  root.present('sheet')
                                  root.add_subview(v)
                                  
                                  
                                  
                                  tileyon 1 Reply Last reply Reply Quote 0
                                  • tileyon
                                    tileyon @abcabc last edited by

                                    @abcabc Yes I do get a syntax error in "animate.py" at line 77 that I reproduce below:

                                    def init(self, *animations, completion=None):

                                    JonB's code works perfectly if USE_ANIMATE is false. Otherwise, I get the same "line 77 error". Thank you for the code sample, its views are very nice indeed.

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

                                      ok. My code works in python 3.5. I have modified the code to work in python 2.7 and it is available in the following gist.

                                      https://gist.github.com/balachandrana/097b35a63c3bcf8b64ec198413df9b7e

                                      Python 2.7 does not allow keyword initialization after *args. Here is the modified portion the code.

                                      class ChainedAnimation(object):
                                          """Represents a series of several animations to be played in sequence."""
                                          def __init__(self, *animations, **kwargs):
                                              if hasattr(kwargs, 'completion'):
                                                  self.completion = kwargs['completion']
                                              else:
                                                  self.completion = None
                                      
                                      1 Reply Last reply Reply Quote 0
                                      • ccc
                                        ccc last edited by

                                        class ChainedAnimation(object):
                                            """Represents a series of several animations to be played in sequence."""
                                            def __init__(self, *animations, **kwargs):
                                                self.completion = kwargs.get('completion', None)
                                        
                                        1 Reply Last reply Reply Quote 0
                                        • abcabc
                                          abcabc last edited by

                                          Thanks. Corrected in gist.

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