[HELP] TypeError: Expected callable/function
-
I was trying to implement a live updating timer. Well, a love updating label that will ultimately be proportionate to the time passed. I was able to implement it successfully in a test script, but when I popped the previously working function into the actual script where I wanted to use it, I get the Type Error in the title of this post.
I can't seem to figure it out. Does it have something to do with it being inside a main() function?
def main(): def timerCount(): lastTimeSince=0 arbitrary = 345 for i in xrange(arbitrary): print ">>>>>>=======true" timenow = time.time() timeThen= 1421929025.21 timeSince = timenow-timeThen if lastTimeSince <= round(timeSince,0): changingLabel1.text=str(int(round(timeSince,0))) lastTimeSince = round(timeSince,0) return lastTimeSince
The error shows up right at the print command.
-
Re globals, they are okay for quick and dirty scripts, but should be avoided for big complicated things. They are hard to debug, easy to get confused (I.e globals which are not declared as global are readonly), and can have problems with other modules using the same names. At the very least, put everything into a class as instance variables to encapsulate them. Or, pass things in/out of functions.
Re timers.... If you have specific things that get updated at specific intervals, consider ui.delay loops(which are basically equivalent to threading.Timers). Basically, a function calls itself after a delay, in a new thread. While it is waiting, other threads run. You could have the delay be a fixed time, or dependent on some other condition. Just be careful if there are interactions between threads, i.e one thread is modifying values used by another, in which case you have to synchronize using locks or another threadsafe mechanism. Also, be sure to include a stopping condition such as
on_screen
, otherwise it will try to keep running even after errors occur.See a very simple example below, which updates two labels at different rates, using two independent delay loops.
import ui,time class stopwatch(ui.View): def __init__(self): self.frame=(0,0,768,768) self.bg_color='white' fast=ui.Label(frame=(10,10,200,50),name='fast') slow=ui.Label(frame=(10,70,200,50),name='slow') stop=ui.Button(frame=(150,150,80,80),bg_color='red',name='stop') go=ui.Button(frame=(250,150,80,80),bg_color='green',name='go') go.title='go' stop.title='stop' go.action=self.go stop.action=self.stop self.add_subview(fast) self.add_subview(slow) self.add_subview(stop) self.add_subview(go) self._stopped=True def stop(self,sender): self._stopped=True def go(self,sender): if self._stopped: self._stopped=False self.fast_loop() self.slow_loop() def fast_loop(self): if self.on_screen and not self._stopped: self['fast'].text='{:0.14}'.format(time.time()) r=self['fast'].bg_color[0] self['fast'].bg_color=((r+1)%2,1,1) ui.delay(self.fast_loop,0.1) def slow_loop(self): if self.on_screen and not self._stopped: self['slow'].text='{:0.14}'.format(time.time()) r=self['slow'].bg_color[0] self['slow'].bg_color=((r+1)%2,1,1) ui.delay(self.slow_loop,1.0) if __name__=='__main__': v=stopwatch() v.present()
-
So, I return to this thread, friends!
@JonB @ccc I've been refactoring my app to be less crude (one of the reasons I haven't replied to that other template thread we were hanging out in)... In doing so I'm referring back to this timer example you shared with me to craft my main run loop. I just have a couple questions.
-
While making a simpler example that I could actually understand, I ran into a couple errors and a search on stack overflow yielded a tidbit of information: Python recursion is limited to 999 recursions? The only thing is I'm blowing way past that in the example below, well into the thousands of recursions and it appears to work. Am I fundamentally misunderstanding something? (interestingly sys.getrecursionlimit() returns 256.....) - (ALSO interestingly, sys.getrecursionlimit() from pythonista's console returns 300....) (running it from OS X terminal in el capitan = 1000)
-
At the ridiculous delay rate of .00001, while the script is running, according to xCode's tools about 6% - 8% of the iPhone 5 simulator's CPU is being used. If I close the view via the standard close button but don't stop the app, the CPU usage jumps up to over 50% - why is that?
import ui,time import sys rec = sys.getrecursionlimit() print rec class COUNT(object): def __init__(self): self.recrusionsDone = 0 self.view = ui.View() self.view.name = 'Demo' self.view.background_color = 'blue' self.label = ui.Label(text = "Heyyyyyy..",text_color="red") self.label.center = (self.view.width * 0.5, self.view.height * 0.5) self.label.flex = "" self.label.width = self.view.width*2 self.label.alignment = ui.ALIGN_CENTER self.view.add_subview(self.label) self.label.text_color = "yellow" self.view.present('sheet') def crazyCounter(self): self.countRecursions() timeString = time.time() timePlus = str(timeString)+" - "+str(self.recrusionsDone) self.label.text = str(timePlus) ui.delay(self.crazyCounter,0.0001) def countRecursions(self): self.recrusionsDone = self.recrusionsDone+1 a = COUNT().crazyCounter()
-
-
Recursion is when a function calls itself. So calling
countRecursions()
fromcrazyCounter()
is not considered a recursion.import sys def looper(i=1): try: looper(i+1) except RuntimeError as err: print('{}/{} {}'.format(i, sys.getrecursionlimit(), err)) looper()
-
crazyCounter()
calls itself using ui.delay() ..
countRecursions
is just there as a bonus separate counter.. Am I missing something?
def crazyCounter(self): self.countRecursions() timeString = time.time() timePlus = str(timeString) self.label.text = str(timePlus) ui.delay(self.crazyCounter,0.0001)
PS - how are you doing inline (non-block) code snippets on the forum?
-
crazyCounter() calls ui.delay, which adds a task to the background queue, and returns immediately. crazyCounter() then exits, basically with no delay.
If you looked at a stacktrace, each call to crazyCounter() is at most one call deep. The python recursion limit is about how deep the stacktrace can go, effectively.
In contrast,
def crazyRecursion(i): if i>0 return i+crazyRecursion(i-1) else: return i crazyRecursion(5) # 5+4+3+2+1. ok crazyRecursion(1000) # will hit the recursion limit
if you printed a stacktrace inside here, it would be very deep, since each call has to wait for the next call to finish before the function exits.
I suspect the difference you say with cpu usage has to do with how ui.delay works, and shares resources with the ui thread, or else it could be that things slow down through the crazyCounter path when a ui is present (updating labels, etc migt be slower when a view is actually onscreen).
-
block inline can be done with single backticks: like
`crazyCounter` calls itself
-
Thanks for clearing that up. So I'm just trying to wrap my head around ui.delay().
A couple questions come to mind:
-
if you call multiple things using ui.delay, will they all be on the same thread?
---in another forum thread (no pun intended), Jon you suggested I use ui.delay for my button actions which were causing my app to crash. Would there be an issue if it's also used for my main runtime using pseudo-recursion as in the example above.? -
is ui.delay the same as ui.in_background just with the delay?
-
would it be a good idea to put the central runtime of my app into ui.in_background? (keeping in mind my app will have buttons that do other things)
basically.. I just want to do this right this time!!!
-
-
@Tizzy , I have used ui.delay before. I can't answer your question, but have you also looked at ui.cancel_delays. I can't see you call it anywhere
-
sorry, i misspoke above. ui.background puts everything on one thread, queued up by the order of background calls. ui.delay does kick off independant threads, though these may still be shared or managed somehow with the ui system, but I am not sure. ui.delay seems like a fine approach for updating a label with current time.
I would avoid doing anything really time critical using ui.in_background. It is good when you want to ensure the calls are executed in the order they were called, but if you are having some sort of autonomous code that is not triggered by a ui action, a Thread might be better. I think i posted an async_exec decorator somewhere on the forum which is a Thread version of ui.in_background.
Phuket2 offers good advice about using cancel_delays -- you need a way for the loop to cancel. Checking on_screen may be a good approach, you could also set a stop flag, etc. Cancel_delays is useful when you have longish delays, though in your case, you probably would never be able to catch that 0.001 between calls, so you need something inside crazyCounter() to prevent kicking off the ui.delay. Another heavy handed approach would be to redefine crazyCounter in -- your stop or will_close method, that way you don't incur the overhead of checking a flag inside your crazyCounter(), which I gather you want to run as fast as possible.