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.


    Scripter for Scene and other updates

    Pythonista
    2
    13
    7219
    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.
    • mikael
      mikael last edited by

      Scrolling banner

      At the beginning of this year, Pythonista 3.2 brought support for ui.View.update, and thus made Scripter available for everyone.

      I have been using Scripter for UI animations in about every one of my projects for the past months, have been happy with its efficiency and stability, and thus wanted to just promote it a bit more.

      Here are some recent updates to the tool:

      1. Scrolling banner view
      2. Scripter in scene animations
      3. Scripter for long-running tasks and polling

      Scrolling banner view

      c = ScrollingBannerLabel(
        text='Happy Pythonista 3.2 *****', 
        text_color='green', 
        font=('Futura', 24), 
        initial_delay=0.5, 
        scrolling_speed=50)
      

      Simple utility view, handy for news ticker type use cases, with an adjustable initial delay and scrolling speed:

      Scripter for Scene Nodes

      Turned out that it was very easy to enable Scripter in Scenes. Whereas on the UI side I would consider Scripter 'essential' (providing functionality that is not really there otherwise), for Scenes it is 'nice to have', or available if you want to use the same syntax in both UI and Scenes.

      Here's an example:

      from scripter import *
      from scene import *
      
      class MyScene (Scene):
          
        @script # setup can be a script
        def setup(self):
            self.background_color = 'black'
            s = self.ship = SpriteNode(
              'spc:PlayerShip1Orange', 
              alpha=0, scale=2, 
              position=self.size/2, 
              parent=self)
            yield 1.0
            pulse(self, 'white')
            s = self.ship
            show(s, duration=2.0)
            scale_to(s, 1, duration=2.0)
            yield
            wobble(s)
            yield
            move_by(s, 0, 100)
            yield
            fly_out(s, 'up')
            yield
            l = LabelNode(
              text='Tap anywhere', 
              position=self.size/2, 
              parent=self)
            reveal_text(l)
            yield 2.0
            hide(l)
      
        @script # touch events can be scripts
        def touch_began(self, touch):
            target = Vector(touch.location)
            vector = target - self.ship.position
            rotate_to(self.ship, vector.degrees-90)
            yield
            move_to(self.ship, *target, duration=0.7, ease_func=sinusoidal)
      
      run(MyScene())
      

      Long-running task, polling etc.

      This is not an update to Scripter, just an expansion of my understanding on what it can be useful for.

      As Scripter has a lot parallels to things like asyncio, it can also be used to run long-running computations without freezing the UI and without threads, or to implement polling for some conditions.

      These require that you can insert a yield statement somewhere in your computation or loop, so calling long-running third party functions is not really an option.

      Phuket2 3 Replies Last reply Reply Quote 1
      • Phuket2
        Phuket2 @mikael last edited by

        @mikael tried out the banner, its nice and simple to use and smooth. I could see a small decrease in performance when I changed the text_colour and bg_color. However, this could be just a perceived thing. I was just doing green text on a black bg. Also the roll to, rotate and rotate by generate the below trace back. I haven't checked them in the lib yet. If you close the error window, and click the buttons they work. So its something in the first call i guess.

        I am glad you posted this thread. I really should try and learn to use your Lib. It's for sure something I can not write myself. But used sparingly and correctly I am sure it could help ppl like me that struggle with animation math make some more professional looking UI's. I assume when making a UI, you would use Scripter along with your other lib gestures.
        Thanks again for your time and effort.

        Traceback (most recent call last):
        File "/private/var/mobile/Containers/Shared/AppGroup/3533032E-E336-4C25-BBC4-112A6BF2AF75/Pythonista3/Documents/site-packages-3/scripter/scripter.py", line 215, in update
        wait_time = next(gen)
        File "/private/var/mobile/Containers/Shared/AppGroup/3533032E-E336-4C25-BBC4-112A6BF2AF75/Pythonista3/Documents/site-packages-3/scripter/scripter.py", line 383, in slide_value
        delta_value = delta_func(start_value, end_value)
        File "/private/var/mobile/Containers/Shared/AppGroup/3533032E-E336-4C25-BBC4-112A6BF2AF75/Pythonista3/Documents/site-packages-3/scripter/scripter.py", line 375, in <lambda>
        delta_func = delta_func if callable(delta_func) else lambda start_value, end_value: end_value - start_value
        TypeError: unsupported operand type(s) for -: 'float' and '_ui.Transform'

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

          @mikael , I have been trying more in Scripter, mind you I am slow :( But 2 things that stand out.

          1. There is no constants definitions for the 'ease' functions. If defined they could appear in the auto complete.
          2. You have done scripts for example fly_out, but not fly_in. It may seem trivial, but you sort of expect to have the inverse script/func in the Lib API. Either though param or seperate func/method call.

          Just my feed back as a simple guy trying to use the Lib.

          Oh, one last thing. It seems like some utility functions that help you prepare a view(objects in a view) to be animated by a script would be nice. I dont have this clear in my mind, but for example a move_offsceen(v) would be an example. Maybe I am wrong, but I am trying to work out the best workflow to create a view, then animate it into existence as generically as possible. I realise, you have the creation of the view you want to present(animate), as well as you also have triggered/actions that your will also want to animate inside your view as a result of user interaction.

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

            @mikael , I hope you dont mind all my replies....

            But I did the below code, just testing. I have got some things wrong, but its ok. But the attempt was to try to make objects within a view a little self describing. Maybe its a very bad idea, but even bad ideas lead to good ideas sometimes :). My example only tries this with one label. But i can see many different ways to approach this. Maybe each obj as a custom class with a std method name like animate_view for example and then the main view could call the animate_view methods in the correct sequence.
            Oh well food for thought! I am guessing you have sort of workflow as you say you have used Scripter in multiple projects you have been working on lately.

            import ui
            from scripter import *
            
            
            def move_offscreen_x(obj):
                if obj.frame.max_x:
                    obj.x = -(obj.x + obj.frame.max_x)
            
            
            def mk_header_panel(w, h, hm=0, vm=0, **kwargs):
                h = ui.View(width=w, height=h, **kwargs)
                lb = ui.Label(frame=h.bounds, text='••• Altered Carbon •••',
                              alignment=ui.ALIGN_CENTER,
                              alpha=1)
                lb.font = ('Arial Rounded MT Bold', 48)
                h.add_subview(lb)
            
                '''
                Just an idea, maybe a bad one! The idea is within the object creation,
                define the script func as well as the params.  I have not thought this out.
                I just wanted to try...
                This example, does not work 100% because the text is aleady rendered in the
                view.
                '''
                h.script = reveal_text
                h.params = {'view': lb, 'duration': 2}
                return h
            
            
            class CardA(ui.View):
                def __init__(self, *args, **kwargs):
                    super().__init__(*args, **kwargs)
                    self.header = None
                    self.cv = ui.View(frame=self.bounds.inset(12, 12),
                                      bg_color='purple',
                                      border_width=.5,
                                      corner_radius=12,
                                      flex='WH'
                                      )
                    self.add_subview(self.cv)
                    self.make_view()
            
                def make_view(self):
                    header = mk_header_panel(self.width, 150, bg_color='deeppink')
                    self.cv.add_subview(header)
                    self.header = header
                    move_offscreen_x(self.header)
                    self.card_reveal_script()
            
                @script
                def card_reveal_script(self):
                    slide_value(self.cv, attribute='x', end_value=6,  target=None, start_value=-self.width, duration=.3, delta_func=None, ease_func='easeIn', current_func=None, map_func=None, side_func=None)
                    yield
                    slide_value(self.header, attribute='x', end_value=0,  target=None, start_value=self.width, duration=1, delta_func=None, ease_func='easeIn', current_func=None, map_func=None, side_func=None)
                    yield
            
                    '''
                    Just trying something different here! See if our object has a script
                    attr, if it does we assume it has a param attr also.
                    Another assumption(does not need to be an assumption) is that the
                    params dict contains a key called view.  Its popped of the dict, then
                    rest of the dict is passed as the **kwargs
                    to the obj.script func/method
                    '''
                    if self.header.script:
                        v = self.header.params.pop('view')
                        self.header.script(v, **self.header.params)
                    yield
            
            if __name__ == '__main__':
                f = (0, 0, 600, 800)
                c = CardA(frame=f)
                c.present('sheet')
            
            1 Reply Last reply Reply Quote 0
            • mikael
              mikael @Phuket2 last edited by

              @Phuket2, thanks for pointing out the error. It is fixed in the repo.

              I will get back to the other points soon.

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

                @mikael , thanks! Rotating examples working now. Maybe its not a big deal, but in the demo, maybe some of the btns like 'roll to' should have the button's enable property set to False until its finished animating. Look its a small issue, But more than likely in an implementation you would want this behaviour.

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

                  Ok, sir @Phuket2!

                  First of all, a big thank you for all the ideas and feedback.

                  Regarding the ease functions, in the current version all functions are available as functions, i.e. can be found with autocomplete. Support for the string arguments was retained for backwards compatibility only.

                  Thus, instead of ease_func='easeIn', just use ease_func=ease_in. Function names are the same as in the reference graphic.

                  Regarding the demos not stopping if you click another button - yes, but I was too lazy to implement it. Also, you can get some crazy fun effects with the current setup.

                  fly_in, appear and other similar PowerPoint-type "view introduction" functions do seem like a good idea. The challenge there is that in order to be animated, the view needs to in the view hierarchy, and thus you suddenly need to care where the view 'starts', not just where you want it to end up.

                  To avoid this, we could have the intro functions take an additional view as a parent view, then the function could take care of placing the view suitably before calling add_subview and running the animation. Not providing the parent would mean that the view would get presented instead.

                  Thus,

                  fade_in(view)
                  

                  would fade the root view into visibility, while

                  fly_in(child, 'from_left', 100, 100, parent=view)
                  

                  would bring in a child view, after having added it as a subview.

                  This would be in line with the rest of the Scripter API, but would not really support the "view manages itself" philosophy.

                  Have you gone further with your experiment?

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

                    @mikael , thanks for the update. It appears that maybe you haven't pushed the last update to the repo. Last update appears to be the corrections for the rotation.

                    Yes I understand your comments about self animating views (so to speak). Over the last few days I haven't had much time to experiment all though I do some stuff each day. But look for whatever reason, animations scare the hell out of me :) Just a mental block about the math. Eg. Easy for me to get confused. So its slow going for me. But, I am going to stick at it. The prospect of being able to construct views like this and other View animations on this site is exciting to me.

                    Anyway, I will keep working on it. I have always been a little suprised that some of these more tantalising animations/transitions views have not shown up in Pythonista apps. Aka the feeling you are using a very polished native app. Its not a criticism, I just think its not a real focus for a lot of Python programmers (UI). Maybe I need to be burnt at the stake for that comment :)

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

                      @Phuket2, yes, fade_in or fly_in are not implemented yet, they were just examples for the discussion, to get a feel for how the API would look and whether they would really match what you were looking for.

                      In terms of the finished look that you get with a little bit of animation here and there, I obviously agree.

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

                        @mikael , ok no problems. I just misunderstood your post. I started having problems yesterday (I think after I updated to 11.2.5) about script not being defined. Strange, the trace back is below. I didn't have much time to found out the cause. The only thing i can think of is how I copied the Scripter code into Pythonista (I did a drag and drop copy from a clone of the repo in Working Copy). I am not really sure. Anyway, I am not really reporting it here as I need to try more things first.
                        Anyway, when I come across this issue, I was just going to make a view that used your wobble script to wobble a ui.Button at regular interval (to draw attention to it). I was just interested to see how this all would work out. I.e the containing view having and update method as well as calling the script on the container views update fire. I assume it would work.
                        As far as I can see there is no way for a script to re-animate given a duration/pause(maybe I am wrong) but I think its an interesting point, as some elements in a ui, you would like them to continue to animate forever, but with a pause between the animations so its not over the top. Even better the pause could have a ease function. Anyway, I am just talking giving my ideas. I dont expect anything from you, its your project.
                        I come from the dummies side of things, meaning the less I need to know about using a Lib the better. But I do understand that libs are also written with a certain expectation that the user of the Lib/module has a degree of understanding of some primitives core to the lib.

                        Traceback (most recent call last):
                        File "/private/var/mobile/Containers/Shared/AppGroup/3533032E-E336-4C25-BBC4-112A6BF2AF75/Pythonista3/Documents/site-packages-3/scripter/scripter-demo.py", line 90, in <module>
                        @script
                        NameError: name 'script' is not defined

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

                          @Phuket2, for what it is worth, I am running 11.2.5, and I do not get the error.

                          For the repeating wiggle, I would do:

                          @script
                          def notice(view):
                            while True:
                              wobble(view)
                              yield 5
                          

                          Could also use different wait times, and could use the ease functions to calculate each wait.

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

                            @mikael, thanks for your reply. As regards to the error I am having, I am pretty sure somehow I have corrupted something. But at the moment my home internet is on and off like a yoyo :( Its cable issues in the streets. So its not so easy to do app downloads etc to reset. I will have to wait until they sort it out, again!

                            I like your example notice! But it really does illustrates something. How you intend your lib to be used. I know its going to seem stupid, but the name of the Lib does says it all. I didn't really get that somehow. I was looking at your lib more from a macro point of view rather than a micro POV.
                            I am not sure I have explained that clearly or not. But as I say, I will keep trying to get my head around it. In terms of doing animated views vrs PowerPoint slides concept. One could say they are they same or very similar(but they are not). I think the great thing is that your lib/module can support either direction. At least I think that...

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

                              @Phuket2, I hear you. I think the approach here is to ”script” and publish the most generalized scripts as ”macros”, but unless you sort of internalize the basic ”philosophy of yielding”, it is very difficult to get the best out of the lib.

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