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.


    Animate allowed to call draw()?

    Pythonista
    draw animation
    5
    19
    8704
    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.
    • shinyformica
      shinyformica last edited by

      @JonB "pulsation" was just a concept, since it doesn't really have a specific meaning. What I'm asking for here is to have the control potentially completely redrawn over and over while it animates (not a single attribute change-over-time, but a modification of the entire object in potentially complex ways). I'll look at Scripter, see what it does...seems like I'll be making use of more brilliant @mikael work.

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

        @shinyformica the other approach of course is to use update which gets called at a fixed fps rate that you define. You would update internal state, then call set_needs_display

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

          @jonb yeah, I was originally thinking of just doing it that way...but I'm already using update() to monitor a network I/O thread and update the interface as stuff comes in, so I was afraid of overloading that with work to do. Seemed like animate() was a good way to avoid it, since it seems to be an alternate thread of execution.

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

            Animate and update are actually both called on the main thread (UI thread). Either way you have to execute things quickly to maintain ui responsiveness.

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

              Well that makes sense, and with some testing it's clear the load the animation puts on the system is very minimal, so I can leave it in an update(). Thanks @JonB !

              mikael 2 Replies Last reply Reply Quote 0
              • mikael
                mikael @shinyformica last edited by

                @shinyformica, Scripter also uses update() under the hood, and was created to avoid writing same kind of boilerplate animation controlling logic there over and over again.

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

                  @shinyformica, as to your example, Scripter contains a pulse effect, and supports cancelling a running animation if you want to restart it (but alas, no simple ”restart” option for now).

                  1 Reply Last reply Reply Quote 0
                  • cvp
                    cvp @shinyformica last edited by

                    @shinyformica Not sure if that helps, little dirty script to perform animation in an independant thread for each control. You can even tap both with two fingers at the same moment. Sorry if not useless

                    import ui
                    import threading
                    import time
                    
                    class my_thread(threading.Thread):
                    
                    	def __init__(self,ctrl):
                    		threading.Thread.__init__(self)
                    		self.ctrl = ctrl
                    		self.delay = 0.05
                    		self.delta = 0.0
                    
                    	def run(self):
                    		self.on = True
                    		while self.delay > 0:
                    			time.sleep(self.delay)
                    			if self.on:
                    				self.ctrl.background_color = (1,0,0)
                    			else:
                    				self.ctrl.background_color = 'white'				
                    			self.on = not self.on
                    			self.delay = self.delay + self.delta			# same or slower
                    			if self.delay >= 1:
                    				self.delay = 0
                    		self.ctrl.background_color = 'white'				
                    
                    class my_ctrl(ui.View):
                    
                    	def __init__(self,frame=None,name=None):
                    		self.frame = frame
                    		self.name = name
                    		self.ctrl = ui.Label(frame = (0,0,self.width,self.height))
                    		self.ctrl.background_color = 'lightgray'
                    		self.ctrl.text = 'tap here'
                    		self.add_subview(self.ctrl)
                    		self.server_thread = my_thread(self.ctrl)
                    		self.server_thread.lock = threading.Lock()
                    		
                    	def touch_began(self,touch):		
                    		self.server_thread.lock.acquire()	
                    		if not self.server_thread.is_alive():
                    			self.server_thread.start()
                    		else:
                    			self.server_thread.delay = 0.05
                    			self.server_thread.delta = 0.0
                    		self.server_thread.lock.release()	
                    		
                    	def touch_ended(self,touch):
                    		self.server_thread.lock.acquire()	
                    		self.server_thread.delta = 0.05
                    		self.server_thread.lock.release()	
                    
                    v = ui.View()
                    
                    ctrl1 = my_ctrl(frame=(10, 10,100,30),name='ctrl1')
                    v.add_subview(ctrl1)
                    ctrl2 = my_ctrl(frame=(10,190,100,30),name='ctrl2')
                    v.add_subview(ctrl2)
                    
                    v.frame = (0,0,300,400)
                    v.present('sheet')
                    
                    
                    1 Reply Last reply Reply Quote 0
                    • shinyformica
                      shinyformica last edited by shinyformica

                      Thanks for the pointers for Scripter, I have that code open for reference :)
                      @mikael. @cvp this is cool, though for the moment I'm just having the control's update() with update_interval set to non-zero do what I need...which is working well enough. In the above code example, I notice you are never calling set_needs_display() in either the thread changing the color or the main code. Does that mean that under the hood, the actual modification of certain attributes automatically causes the control to get redrawn? Is there a list of which attrs will cause that? Or is it basically any attribute which might affect appearance? (color, tint, font, etc.)

                      cvp 1 Reply Last reply Reply Quote 0
                      • cvp
                        cvp @shinyformica last edited by cvp

                        @shinyformica the doc says

                        View.set_needs_display()
                        Marks the view as needing to be redrawn. This usually only makes sense for custom View subclasses that implement a draw method.
                        

                        I never use it if I modify ui elements in another thread

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

                          Good point @cvp...neither do I, I just expect changing an attribute which affects appearance to cause the control to update. My own custom Views call self.set_needs_display() in whatever property setters or methods change appearance. (and look at you, being careful with your thread locks...even in a write-only/read-only case! :))

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

                            You only need to call it off you implement draw. Other display attributes get updated automatically.

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

                              @jonb totally right, I was only really thinking about my own custom view, I was aware that changing attributes on regular controls caused them to update. For my purposes, I was looking for the "right" way to have an animated custom view react to user interaction.

                              a) calling draw() directly is never done, since it has no meaning outside a scheduled drawing context
                              b) calling set_needs_display() is allowed from anywhere, since the actual drawing will be scheduled and executed on the UI thread
                              c) there is no "other thread" which can do drawing/updating of UI elements

                              so, basically however I go about it, just make sure my actual draw() is as efficient as possible so the UI never gets bogged down.

                              This is python, of course, so the GIL is still in play, and all threads have to finish their work as quickly as possible, or yield control, in order to not cause trouble. Actually, I should ask: under the hood, there is only one interpreter instance, right? The python code on the UI thread and the python code on the main thread are both being executed by the same interpreter process? There's no magical communication happening between multiple python interpreters which side-steps the GIL as you can with multiprocessing?

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

                                There is one interpreter for python 2.7 and one for 3.6. but just one process.

                                Depending on what you are doing there are ways to keep draw quick:
                                Drawing an image may be faster than stroking hundreds of paths. You can render static things to an ImageContext, then later draw that inside draw. For instance omz's sketch example does that every touch_ended.

                                For framebuffer type access, see the recent thread which dealt with both some real time audio generation and IOSurfaceWrapper enabling some low level image pixel manipulation and display

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