I think this one is the kind of problem that will make me change much of my code.
The program is basically a genetic algorithm that finds the best solutions of a given function and plots them over generations passed.
It only has some inputs and the run button. The run button uses a for loop to output the data in two lists and then plots them
def run(self):
b = []
a = []
self.ga = Ga()
for i in range(self.generations):
ga.run(self.v.get())
b.append(ga.best().fitness)
a.append(ga.fitness_average)
self.graph(a,b)
Is there a way I can access the data being parsed into the lists during run time and use them to continuously update a Label ? The algorithm can take some time to run and I thought showing some values on the screen while running would be a nice feature.
CodePudding user response:
Sorry for the SUUPER long wait, internet went out for several hours
Hopefully the comments give you enough of an understanding to plop it into your code and get it all to work how you want, but if they don't, please feel free to ask for assistance, it's what i'm here for
Anyway, i assume your "run" function is a part of a class because of self so make sure to change the target for the thread to fit
#----------------- NEED THESE BEFORE EVERYTHING ELSE BUT DON'T NEED THEM EXACTLY HERE(but can get rid of time import if you dont want to use it)
import threading
import time
gettingData = True
#----------------- NEED THESE BEFORE EVERYTHING ELSE BUT DON'T NEED THEM EXACTLY HERE(but can get rid of time import if you dont want to use it)want to use it
def run(self):
b = []
a = []
self.ga = Ga()
while gettingData:
time.sleep(1) #without this, it will be looping as soon as it finishes, which may use too many resources
ga.run(self.v.get())
b.append(ga.best().fitness)
a.append(ga.fitness_average)
self.graph(a,b)
#threading stuff, just starts a new thread and continues along with its day
thread = threading.Thread(target=whateverThisClassIsCalled.run)
thread.start()
#continue with other code you want to use as the while loop in "run" will now be constantly running
And if you want to stop the loop at any point:
gettingData = False #this stops the while loop which will allow your thread to run through the function to completion
#you can add a thread.join() after gettingData if you wish to wait for the thread to close(it to stop appending and graphing etc) before executing code coming after
CodePudding user response:
While multi-threading can be a way out for tkinter apps, tkinter itself is not thread-safe, and events or calls into tkinter made from other threads may cause strange errors.
This can be run in a single thread - the catch is that tkinter won't , by default, update anything on screen, until your long run
function is over and control is resumed to the tkinter loop.
However, all that is needed to update the screen at any time is to call the .update()
method on your widgets, or top-level widgets.
Since your function do calculations, it would not be a nice design to hard-code "window.update()" calls inside it. Instead, a design where an optional "update" callback is passed into your "run" function will be more elegant, and allow the same function to be run with other interfaces as text user interface program in the terminal, other gui toolkit like Qt or GTK, or even a web application.
I'd love to write "working code" of this example, but since you did not post a self contained example, I can't take my time to write a tkinter UI with the labels to update from scratch.
As I was writing the snippet bellow I saw your "run" is supposed to be a method. Please post self contained minimal, complete, executable examples, not out of context functions, when asking questions. If you app is assembled as a class, then update
may be a method, instead of a callback passed as paremeter as I described above.
The idea goes like:
import tkinter as tk
class Mistery:
def __init__(mistery_instance):
# code to create the main window assigned
# to "selfroot", the needed widgets, and an
# action that will trigger the "run"
# function
self.root = tkinter.Tk()
"""you did not show your code. A lot of
over 15 year old documentation suggest inheriting
your own class
from "tk.Tk" and build your app on that class -
that definetelly is not a good
thing to do: there are _hundreds_ of methods on
a tkinter class that would
name-clash with your methods and you would never
know it. Just associate
your widet as an
attribute as in the line above
"""
self.label1 = ...
self.label2 = ...
# code to create the remainder of the UI
# and at some point, create a callback to execute "self.run"
...
def update(self,a, b):
#code to set the display entries in label1 and label2
...
root.update() # <- this ensures the window is repainted with the new values
def run(self, update_interval=100):
b = []
a = []
self.ga = Ga()
for i in range(self.generations):
ga.run(self.v.get())
b.append(ga.best().fitness)
a.append(ga.fitness_average)
if i % update_interval == 0:
self.update(a, b)
self.graph(a,b)
m = Mistery()
tkinter.mainloop()