Ipython Widgets (how to make a timer) - python

I made this timer widget that works the way I want, but it locks up the notebook so I can't execute other code cells at the same time.
Here's an example of what I mean:
Any ideas for getting the widget to execute in the background?
In case it helps, here is the code that makes the above widget:
import ipywidgets as widgets
from IPython.display import display, Javascript
from traitlets import Unicode, validate
import time
class Timer(widgets.DOMWidget):
_view_name = Unicode('HelloView').tag(sync=True)
_view_module = Unicode('hello').tag(sync=True)
_view_module_version = Unicode('0.1.0').tag(sync=True)
value = Unicode('00:00:00').tag(sync=True)
def timeit(self, b, limit=180):
#display(self)
hours = 0
mins = 0
secs = 0
for i in range(1,(limit*60+1)):
if i%60 == 0:
if i%3600 == 0:
secs = 0
mins = 0
hours += 1
else:
secs = 0
mins += 1
else:
secs += 1
time.sleep(1)
self.value = '{hour:02}:{minute:02}:{second:02}'.format(hour=hours,minute=mins,second=secs)
def display_timer(timer):
button = widgets.Button(description="Start Timer", button_style='info')
display(button)
display(timer)
button.on_click(timer.timeit)

I found a solution. Read this doc for details.
You use the threading library to make your update function (in my case the timer that updates every second) run separately so you can still execute other code cells.
Also, I tried doing a couple %%timeit 's on simple test functions like this:
[x*=x for i in range(20)]
And it looks like having this timer widget running in the background didn't cause a significant increase in execution time.

Related

How do I update ipywidget values while running code?

So I want to create a simple UI in Jupyter notebook where:
A counter "number" is incremented every second
If the checkbox "pause" is checked, "number" is not incremented
If the button "trigger" is pressed, "number" is decremented, regardless of pause status
So far I tried a few variants of the code below but it doesn't work; it seems like the widget values aren't updating when running the while loop. Is there a way to fix it or another way to do this? Thanks!
import ipywidgets as widgets
import time
from IPython.display import display, clear_output
btn = widgets.Button(description = "Trigger")
pause = widgets.Checkbox(value = False, description = "Paused?")
number = widgets.Label("0")
wid = widgets.VBox([btn, number, pause])
display(wid)
def triggered(b):
number.value = str(int(number.value) - 1)
btn.on_click(triggered)
while True:
time.sleep(1)
while (pause.value == True):
time.sleep(3)
number.value = str(int(number.value) + 1)
As mentioned by ac24, the trick is to run your counter function in a different thread. You can use the threading library to do that. Below, I defined a function counter and launched it in a different thread. That way, the user is still able to interact with the widgets while the counter function is running.
An important thing to keep in mind is that once you launched your thread there isn't many elegant ways to kill it. This means that it's better to set up a total_duration variable rather than using while True.
See code below:
import ipywidgets as widgets
import time
from IPython.display import display, clear_output
import threading
btn = widgets.Button(description = "Trigger")
pause = widgets.Checkbox(value = False, description = "Paused?")
number = widgets.Label("0")
wid = widgets.VBox([btn,number,pause])
display(wid)
def triggered(b):
number.value = str(int(number.value) - 1)
btn.on_click(triggered)
def counter(number,pause,total_duration):
for t in range(total_duration):
if not pause.value:
time.sleep(1)
number.value = str(int(number.value) + 1)
elif pause.value:
time.sleep(3)
total_duration=60
thread = threading.Thread(target=counter, args=(number,pause,total_duration,))
thread.start()

How can I show an emergent window every hour?

Using tkinter, I'm doing an emergent window to remind me to drink water. At this point it works but now I want the section of def trinke_wasser to repeat every hour. I tried with the time module but in the end, I didnĀ“t know where to write it.
Here is my code:
from tkinter import *
from PIL import Image
from PIL import ImageTk
fenstern = Tk()
fenstern.title("Warnung")
fenstern.geometry("900x600")
datei = Image.open('wasser_pic.png')
bild = ImageTk.PhotoImage(datei)
img = Label(fenstern, image = bild)
img.place(x=0, y=0)
def choice(option):
pop.destroy()
def trinke_wasser():
global pop
fenstern.wm_state('iconic')
pop = Toplevel(fenstern)
pop.title('popup')
pop.geometry("900x600")
back= Label(pop, image = bild)
back.place(x=0, y=0)
rhamen = Frame(pop, bg = "white")
rhamen.pack(pady = 5)
yes = Button(rhamen, text = "YES", command = lambda: choice ("yes"), bg = "orange")
yes.grid(row=0, column=1)
die_Taste = Button(fenstern, text = "Beginnen", command = trinke_wasser )
die_Taste.pack(pady=100)
fenstern.mainloop()
With the time library, there is a sleep function which waits for a number of specified seconds. So if we calculate the number of seconds in an hour or 3600. We can call the sleep function which will wait for exactly 1 hour.
import time
time.sleep(3600)
So if you put the sleep function at the end of your trinke_wasser function. It will tell you to drink water, wait an hour and do it again, over and over
In your current program, you just execute the trinke_wasser() function once upon pressing a key. What you want to do is to open a scheduler function that calls the function once every hour.
But you will have to allow this function to be terminated for good. So, you need a global boolean variable that may be turned off to end the loop. (you could also just end the program execution by force, but that's not nice)
So, what you want to do is to import the time library by adding import time at the beginning of the script.
After fenstern is defined, just add an attribute to this window that is True as long as you don't make it false: fenstern.running = True
Then, you can make your scheduler function:
def nerv_mich(delay = 3600):
while fenstern.running:
trinke_wasser()
time.sleep(3600)
This function will run forever and ask you to drink water ever 3600 seconds.
You just have to adjust the command of the first button:
die_Taste = Button(fenstern, text = "Beginnen", command = nerv_mich)
Now, you just have to turn it off by using a second button just after the definition of yes
fertig = Button(rhamen, text = "Fertig jetzt!", command = lambda: choice ("fertig"), bg = "orange")
yes.grid(row=2, column=1)
Finally, you have to change the function choice() to really end the whole thing after the option passed to it is "fertig":
def choice(option):
pop.destroy()
if option=="fertig": fenstern.running=False
That should work. You can also use fentstern.destroy() to completely end your program at this point. To be honest, you probably don't really need this initial window.

Python Tkinter While Thread

Well i am a bit of newb at python, and i am getting hard to make a thread in Tkinter , as you all know using while in Tkinter makes it Not Responding and the script still running.
def scheduler():
def wait():
schedule.run_pending()
time.sleep(1)
return
Hours = ScheduleTest()
if len(Hours) == 0:
print("You need to write Hours, Example: 13:30,20:07")
if len(Hours) > 0:
print("Scheduled: ", str(Hours))
if len(Hours) == 1:
schedule.every().day.at(Hours[0]).do(Jumper)
print("Will jump 1 time")
elif len(Hours) == 2:
schedule.every().day.at(Hours[0]).do(Jumper)
schedule.every().day.at(Hours[1]).do(Jumper)
print("Will jump 2 times")
elif len(Hours) == 3:
schedule.every().day.at(Hours[0]).do(Jumper)
schedule.every().day.at(Hours[1]).do(Jumper)
schedule.every().day.at(Hours[2]).do(Jumper)
print("Will jump 3 times")
while True:
t = threading.Thread(target=wait)
t.start()
return
scheduler()
i have tried to do something like this but it still makes tkinter not responding
Thanks in advance.
When to use the after method; faking while without threading
As mentioned in a comment, In far most cases, you do not need threading to run a "fake" while loop. You can use the after() method to schedule your actions, using tkinter's mainloop as a "coat rack" to schedule things, pretty much exactly like you would in a while loop.
This works in all situations where you can simply throw out commands with e.g. subprocess.Popen(), update widgets, show messages etc.
It does not work when the scheduled process takes a lot of time, running inside the mainloop. Therefore time.sleep() is a bummer; it will simply hold the mainloop.
How it works
Within that limitation however, you can run complicated tasks, schedule actions even set break (-equivalent) conditions.
Simply create a function, initiate it with window.after(0, <function>). Inside the function, (re-) schedule the function with window.after(<time_in_milliseconds>, <function>).
To apply a break- like condition, simply rout the process (inside the function) not to be scheduled again.
An example
This is best illustrated with a simplified example:
from tkinter import *
import time
class TestWhile:
def __init__(self):
self.window = Tk()
shape = Canvas(width=200, height=0).grid(column=0, row=0)
self.showtext = Label(text="Wait and see...")
self.showtext.grid(column=0, row=1)
fakebutton = Button(
text="Useless button"
)
fakebutton.grid(column=0, row=2)
# initiate fake while
self.window.after(0, self.fakewhile)
self.cycles = 0
self.window.minsize(width=200, height=50)
self.window.title("Test 123(4)")
self.window.mainloop()
def fakewhile(self):
# You can schedule anything in here
if self.cycles == 5:
self.showtext.configure(text="Five seconds passed")
elif self.cycles == 10:
self.showtext.configure(text="Ten seconds passed...")
elif self.cycles == 15:
self.showtext.configure(text="I quit...")
"""
If the fake while loop should only run a limited number of times,
add a counter
"""
self.cycles = self.cycles+1
"""
Since we do not use while, break will not work, but simply
"routing" the loop to not being scheduled is equivalent to "break":
"""
if self.cycles <= 15:
self.window.after(1000, self.fakewhile)
else:
# start over again
self.cycles = 0
self.window.after(1000, self.fakewhile)
# or: fakebreak, in that case, uncomment below and comment out the
# two lines above
# pass
TestWhile()
In the example above, we run a scheduled process for fifteen seconds. While the loop runs, several simple tasks are performed, in time, by the function fakewhile().
After these fivteen seconds, we can start over again or "break". Just uncomment the indicated section to see...

How to end program running after given time in Python

I'd like my Python program to run an algorithm for a given number of seconds and then to print the best result so far and to end.
What is the best way to do so?
I tried the following but it did not work(the program kept running after the printing):
def printBestResult(self):
print(self.bestResult)
sys.exit()
def findBestResult(self,time):
self.t = threading.Timer(time, self.printBestResult)
self.t.start()
while(1):
# find best result
Untested code, but something like this?
import time
threshold = 60
start = time.time()
best_run = threshold
while time.time()-start < threshold:
run_start = time.time()
doSomething()
run_time = time.time() - start
if run_time < best_run:
best_run = run_time
On unix, you can use signals -- This code times out after 1 second and counts how many times it iterates through the while loop in that time:
import signal
import sys
def handle_alarm(args):
print args.best_val
sys.exit()
class Foo(object):
pass
self=Foo() #some mutable object to mess with in the loop
self.best_val=0
signal.signal(signal.SIGALRM,lambda *args: handle_alarm(self))
signal.alarm(1) #timeout after 1 second
while True:
self.best_val+=1 # do something to mutate "self" here.
Or, you could easily have your alarm_handler raise an exception which you then catch outside the while loop, printing your best result.
If you want to do this with threads, a good way is to use an Event. Note that signal.alarm won't work in Windows, so I think threading is your best bet unless in that case.
import threading
import time
import random
class StochasticSearch(object):
def __init__(self):
self.halt_event = threading.Event()
def find_best_result(self, duration):
halt_thread = threading.Timer(duration, self.halt_event.set)
halt_thread.start()
best_result = 0
while not self.halt_event.is_set():
result = self.search()
best_result = result if result > best_result else best_result
time.sleep(0.5)
return best_result
def search(self):
val = random.randrange(0, 10000)
print 'searching for something; found {}'.format(val)
return val
print StochasticSearch().find_best_result(3)
You need an exit condition, or the program will run forever (or until it runs out of memory). Add one yourself.

In MAYA 2009, is it possible to capture the cube rotate event?

I need to call a function ( Maya-Python ) based on cube rotationX. For that I have to capture the event, programmatically.
I tried using while loop but It stucks in the loop, Nothing can be done in that time.
I tried theading (python), still same.
Can it be done this or other way? If yes, How?
Maya 2009 in Windows XP
Some failed code references:
import maya.cmds as cmds
while (count < 90):
lock = cmds.getAttr('pCube1.rotateX',lock=False)
print lock
count = count + 1
Here Python wise:
#!/usr/bin/python
import thread
import time
# Define a function for the thread
def cubeRotateX( threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
try:
thread.start_new_thread( cubeRotateX, ("Thread-1", 2, ) )
except:
print "Error: unable to start thread"
while 1:
pass
It sounds like a scriptJob may be what you're after. Here's a simple example below. However, in this example the callback will only be called when you release the mouse from rotating.
import maya.cmds
def myRotateCallback():
print 'do something'
maya.cmds.scriptJob( attributeChange=['pCube1.rotateX', myRotateCallback] )
If you want to receive continuous callbacks while rotating the cube, you can do that at the maya API level with MNodeMessage::addNodeDirtyPlugCallback.

Categories