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.
how to update ui.Label display during computation?
-
as the title says, i update a ui.Label during a loop computation, to see progress, but the label display is updated only at the end... so it doesnt show progress.
i have tried to implement a subclass of ui.view with an update(), but i failed (nothing appears any more, probably my bad)
I vaguely remember reading things about decorators and main thread, but i have no experience with this.
Any help? Thanks.
-
@jmv38 could you post the code?
-
You could try to put this line before the function here you perform your loop
@on_main_thread
-
@cvp thank you, but it doesnt solve it
i have a class with
class Neural_Network(object): @on_main_thread def trainAll(self, iterations): for i in range(iterations): finalLoss = self.lossArray[i] showLearning(i,finalLoss)
showLearning is
def showLearning(i,v): c = 'lightgreen' trainInfo.bg_color = c txt = 'step {:d} : {:5.2f}%'.format(i, int(10000*float(1-v))/100) trainInfo.text = txt
trainInfo is a ui.Label defined globally, thati would like to update after each i cycle
-
@jmv38 please, try something like
def ui_label(): global global_txt trainInfo.text = global_txt def showLearning(i,v): global global_txt c = 'lightgreen' trainInfo.bg_color = c global_txt = 'step {:d} : {:5.2f}%'.format(i, int(10000*float(1-v))/100) ui.delay(ui_label,0.01)
-
@cvp thank you
i tried your code, but it doesnt change: no intermediate update. -
@jmv38 try this one, I can't test because I don't have entire code
import ui import threading class my_thread(threading.Thread): global trainInfo def __init__(self, iterations, lossArray): threading.Thread.__init__(self) self.iterations = iterations self.lossArray = lossArray # <============ was erroneous, Sorry def run(self): for i in range(self.iterations): finalLoss = self.lossArray[i] self.showLearning(i,finalLoss) def showLearning(self,i,v): c = 'lightgreen' trainInfo.bg_color = c txt = 'step {:d} : {:5.2f}%'.format(i, int(10000*float(1-v))/100) trainInfo.text = txt class Neural_Network(object): def trainAll(self, iterations): neural_thread = my_thread(iterations, self.lossArray) neural_thread.start()
-
This works
import ui import threading class my_thread(threading.Thread): global trainInfo def __init__(self, iterations, lossArray): threading.Thread.__init__(self) self.iterations = iterations self.lossArray = lossArray def run(self): for i in range(self.iterations): finalLoss = self.lossArray[i] self.showLearning(i,finalLoss) def showLearning(self,i,v): c = 'lightgreen' trainInfo.bg_color = c txt = 'step {:d} : {:5.2f}%'.format(i, int(10000*float(1-v))/100) trainInfo.text = txt class Neural_Network(object): def __init__(self): self.lossArray = [1]*1000 self.trainAll(100) def trainAll(self, iterations): neural_thread = my_thread(iterations, self.lossArray) neural_thread.start() v =ui.View() v.frame = (0,0,500,500) trainInfo = ui.Label() trainInfo.frame = (10,10,200,32) v.add_subview(trainInfo) v.present('sheet') x = Neural_Network()
-
@cvp thank you
i have checked your code: works correctly!
i have to think a little bit before i can include it into my project.
Because it is a bit tricky and i dont want it to come in the way when i do my future versions...
Strange that there is no simpler way to do this simple thing...
Thanks! -
-
You could probably use UI.in_background around your loop. That is what it is for.
You didn't show the rest of your context-- does your loop get called from a UI event like a button action? The key to remember -- NOTHING is updated in the UI until your button action (or touch moved, etc ) until your action returns. UI.in_background allows your button to return, queueing up the work on a shared background thread.
-
@JonB Could we say that a thread like I did is almost like ui_in_background thread for what concerns UI update?
-
@JonB the code is not ready, so i was reluctant to share it...
but if you want here is a gist:
https://gist.github.com/3acaab3b4b1556abbdbd3f5483d9d88e
draw in the 6 skech boxes, press ‘train’: training last 30s, and at the end the label is updated. I would like it to update live. (it is actually updated at each step)
Thanks. -
@jmv38 I've tried your code with
import objc_util . . . @ui.in_background def showLearning(i,v):
-
@cvp thank you
it seems to work but i have doubts: it seems that the whole code executes, without the ui.label updating, then all the updates occur at the end. As if they are buffered somewhere, then all the updates occur very quickly at once.
So i dont think it really works. -
@jmv38 you're right, sorry
-
@cvp don’t be sorry, thank you for trying to help!
if you have any other idea, you are welcome. -
hello
i managed to get it: I launched my function and my loop steps with a ui.delay() call. So my ui.button actions can return and the ui.label can be updated (i followed JonB directions). And then it works.
I will post the code in the neural networks thread.
here is the idea:def trainAll(self, iterations= None): if iterations: self.iterations = iterations self.lossArray = [] loss = np.mean(np.square(y - NN.forward(X))) if self.iterations > 0: self.lossArray.append(loss) self.train(X, y) showLearning(len(self.lossArray),loss) self.iterations-=1 ui.delay(self.trainAll, 0.1)
-
yeah, ui.in_background would work, but you might need to wrap the actual label setting in a on_main_thread, or perhaps call set_needs_display.
the delay allows the callback to return, but then runs the other bit on the main thread, so is also good -- though no other ui will be allowed, which maybe is a good thing in this case.
See this thread for another option -- you can define a decorator that runs a method in a nee python thread, so the convienece of a decorator but without the drawback of reponsivity issues.
https://forum.omz-software.com/topic/3495/label-text-not-displayed-until-end-of-button-action/5