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.
    • 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