I'd like to get the values of the slider widgets within some loop. However, I would like these to update in real time.
For example see below. In the code pictured, I initialise some sliders, then run the loop below. Whilst the loop is running I move the sliders around. The printed values from the loop do not change from the initial value. When I execute the last cell, the updated slider values are shown.
Here's a solution inspired by the last example in asyncronous widgets (as suggested by #Fabio Perez):
import threading
from IPython.display import display
import ipywidgets as widgets
def f(a, b):
return
w = interactive(f, a=10, b=20)
def work(w):
for ii in range(10):
time.sleep(1)
print(w.kwargs, w.result)
thread = threading.Thread(target=work, args=(w,))
display(w)
thread.start()
This starts a separate thread which checks the values of the widgets.
Related
I set up a Jupyter Notebook where I'm running a recursive function that clears the output like so, creating an animated output:
from IPython.display import clear_output
active = True
def animate:
myOutput = ""
# do stuff
clear_output(wait=True)
print(myOutput)
sleep(0.2)
if active:
animate()
and that's working perfectly.
But now I want to add in one more step: A speed toggle. What I'm animating is a debugging visualization of a cursor moving through interpreted code as an interpreter I'm writing parses that code. I tried conditional slow-downs to have more time to read what's going on as the parsing continues, but what I really need is to be able to click a button to toggle the speed between fast and slow. Maybe I'll use a slider, but for now I just want a button for proof of concept.
This sounds simple enough. Note that I'm writing this statefully as a class because I need to read / write the state from within another imported class.
Jupyter block 1:
import ipywidgets as widgets
from IPython.display import display
out = widgets.Output()
class ToggleState():
def __init__(self):
self.button = widgets.Button(description="Toggle")
self.button.on_click(self.toggle)
display(self.button)
self.toggleState = False
print("Toggle State:", self.toggleState)
def toggle(self, arg): # arg has to be accepted here to meet on_click requirements
self.toggleState = not self.toggleState
print("Toggle State:", self.toggleState)
def read(self):
return self.toggleState
toggleState = ToggleState()
Then, in Jupyter block 2, note I decided to to do this in a separate block because the clear_output I'm doing with the animate func clears the button if it's in the same block, and therein lies the problem:
active = True
def animate:
myOutput = ""
# do stuff
clear_output(wait=True)
print(myOutput)
if toggleState.read():
sleep(5)
else:
sleep(0.2)
if active:
animate()
But the problem with this approach was that two blocks don't actually run at the same time (without using parallel kernels which is way more complexity than I care for) so that button can't keep receiving input in the previous block. Seems obvious now, but I didn't think about it.
How can I clear input in a way that doesn't delete my button too (so I can put the button in the animating block)?
Edit:
I thought I figured the solution, but only part of it:
Using the Output widget:
out = widgets.Output()
and
with out:
clear_output(wait=True) # clears only the logged output
# logging code
We can render to two separate stdouts within the same block. This works to an extent, as in the animation renders and the button isn't cleared. But still while the animation loop is running the button seems to be incapable of processing input. So it does seem like a synchronous code / event loop blocking problem. What's the issue here?
Do I need an alternative to sleep that frees up the event loop?
Edit 2:
After searching async code in Python, I learned about asyncio but I'm still struggling. Jupyter already runs the code via asyncio.run(), but the components obviously have to be defined as async for that to matter. I defined animate as async and tried using async sleeps, but the event loops still seems to be locked for the button.
In a published Jupyter notebook is there a way to insert a widget that says "running" or something similar when I am running a function.
I am aware of the tqdm function but to the best of my knowledge this is only when the function / process contains a for-loop.
I currently have a series of dropdown widgets with a submit button but some of the functions take a while for the calcs to run so i have no way of telling if the're running or not
Cheers
The way I have done this in the past is to have a function as a context manager, that displays some value to a Text widget to indicate that the function is running. You could also use an Output widget to display a indeterminate 'progress' bar like the one below:
https://www.iselect.com.au/content/themes/iselect/images/post/loader.gif
import ipywidgets as ipyw
import time
from contextlib import contextmanager
label = ipyw.Text('Ready')
button = ipyw.Button(description='Click me')
#contextmanager
def show_loading():
label.value = 'Running...'
yield
label.value = 'Ready'
def long_running_function(self):
with show_loading():
time.sleep(2)
button.on_click(long_running_function)
display(button)
display(label)
I am looking for a way to use a PYSimpleGUI progress bar... without a loop
I have looked for several days on the internet with no luck to find an example.
seems everybody does their example with a loop, or on a timer.
I'd like to do something more like a definition that I Can call to to update
I dont know what to change to make it a manually updated item...
I want to be able to tell it i=0 at teh beginning of my script
and periodically place update marks thru the script(i=i+4)
so that I can update it as each Major Step in my script is done
This is the PySimpleGUI script, plus some lines showing what I want to do
This currently auto iterates...and I do not know how to change it
I'm just trying to learn and cant find any examples online to do what I want to do.
import PySimpleGUI as sg
import time
from time import sleep
import PySimpleGUI as sg
def prog():
layout = [[sg.Text('Completed Tasks')],
[sg.ProgressBar(100, orientation='h', size=(50, 20), key='progressbar')],
[sg.Cancel()]]
window = sg.Window('Progress').Layout(layout)
progress_bar = window.FindElement('progressbar')
for i in range(100):
event, values = window.Read(timeout=0)
progress_bar.UpdateBar(i + 4)
time.sleep(2)
window.Close()
prog()
time.sleep(2)
#______________________________________________________________
#I'd like to be able to do this
#i=0 at this point
prog()
#do Scripty Stuff
#Update Progress Bar Manually
#i=4 at this point
#do more scriptic writings
#Update Progress bar Manually
#i=8 at this point
#and so forth and so on until I reach 100
Figured it out
just leave everything as a single line, not a definition
heres an example to help others
I just did real numbers in the Update bar section
but you can use variables (i=0) and then update it in the script with an i=i+1
and then use i as your number in the update bar function
i=0
progress_bar.UpdateBar(i, 5)
#i returns a value of 0
i=i+1
progress_bar.UpdateBar(i, 5)
#i now returns a vlaue of 1
#repeat until you reach your maximum value
#this Script will create a Progress Bar
#The Progress will be Manually Updated using the format listed below
#progress_bar.UpdateBar(Value of Bar, Maximum Bar Value)
#the Window uses a .Finalize Function to make the window Persistent
#Import the PySimpleGUI Library
import PySimpleGUI as sg
#Import the Time Library for use in this script
import time
#this is for the Layout Design of the Window
layout = [[sg.Text('Custom Text')],
[sg.ProgressBar(1, orientation='h', size=(20, 20), key='progress')],
]
#This Creates the Physical Window
window = sg.Window('Window Title', layout).Finalize()
progress_bar = window.FindElement('progress')
#This Updates the Window
#progress_bar.UpdateBar(Current Value to show, Maximum Value to show)
progress_bar.UpdateBar(0, 5)
#adding time.sleep(length in Seconds) has been used to Simulate adding your script in between Bar Updates
time.sleep(.5)
progress_bar.UpdateBar(1, 5)
time.sleep(.5)
progress_bar.UpdateBar(2, 5)
time.sleep(.5)
progress_bar.UpdateBar(3, 5)
time.sleep(.5)
progress_bar.UpdateBar(4, 5)
time.sleep(.5)
progress_bar.UpdateBar(5, 5)
time.sleep(.5)
#I paused for 3 seconds at the end to give you time to see it has completed before closing the window
time.sleep(3)
#This will Close The Window
window.Close()
Just a quick note for everyone that is finding this answer in 2021 or later:
The window.FindElement() is deprecated, one uses window.find_element() nowadays :)
#soundtechscott: Thanks for your answer, I also encountered that issue today and your solution worked out for me.
I have a PyQt5 application that looks like this This. Now, with the "bailar" button I want the character on the grid to look left and right a couple of times. I do this in the following method:
def dance(self):
"""
Make the P1 dance
"""
p1 = self._objects['P1']
x = p1.x
y = p1.y
cell = self.worldGrid[y][x]
for i in xrange(3):
print("Moving my head...")
cell.objectLeaves('P1')
p1.pixmap = p1.pixmap.transformed(QTransform().scale(-1, 1))
cell.objectArrives('P1', p1)
time.sleep(0.2)
However, the label containing the pixmap updates only at the last iteration. I know this must be a problem of the update function being asynchronous and the time.sleep() blocking the main thread, but I don't know how else could I show the animation. I tried using a QThread with the moveToThread method, but it failed as the gird widget is a child of the main window. Any ideas?
You see only the last frame of your animation because the UI isn't updated by Qt until the dance function terminates. If you add QtGui.qApp.processEvents() just before the time.sleep(0.2) statement, the UI will be updated for every frame of the animation.
Note that the user still cannot interact with your application until the animation has finished. This might not be a problem for short animations like this, but for longer animations you might better use a QTimer or the Qt animation framework that Brendan Abel suggested.
I'm working on my first Python and GTK script. I'm trying to do a counter/timer. The problem I have is that, while the logging function returns the proper values every second, the gtk.label is not updated. What am I doing wrong?
def startTimer(self, buttonStart):
self.imgTimer.set_from_stock(Gtk.STOCK_YES, 2)
self.runTimer(120)
def runTimer(self, timeout):
for i in reversed(range(0,timeout)):
logging.debug(i) #returns values
self.labelTimer.set_text(i) #doesn't do anything
time.sleep(1)
You don't give GTK+ a chance to draw the updated the label. You should either use threads (see PyGTK FAQ), or something like
while gtk.events_pending ():
gtk.main_iteration ()
after updating the label.