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
-
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:
- Scrolling banner view
- Scripter in scene animations
- 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. -
@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 , I have been trying more in Scripter, mind you I am slow :( But 2 things that stand out.
- There is no constants definitions for the 'ease' functions. If defined they could appear in the auto complete.
- 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.
-
@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')
-
@Phuket2, thanks for pointing out the error. It is fixed in the repo.
I will get back to the other points soon.
-
@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.
-
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 useease_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?
-
@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 :)
-
@Phuket2, yes,
fade_in
orfly_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.
-
@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 -
@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.
-
@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... -
@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.