Search code examples
pythontkinterscriptingstatusbaryield

Pass a variable between two scripts?


I'm having issues passing variables between two scripts in python without ending the imported script. I would like for the status bar in my tkinter GUI to update to reflect what number the counter in script2 is on. I've included the code below:

from tkinter import *
import script2

root = Tk()
canvas = Canvas(root, width=300, height=300)
canvas.pack()

def runScript():
    var = script2.information()

button1 = Button(root, text="Click Me!", command=runScript)
button1.pack()

var = StringVar()
var.set('Waiting for Input...')
status = Label(root, textvariable=var, bd=1, relief=SUNKEN, anchor=W)
status.pack(side=BOTTOM, fill=X)

root.mainloop()

script2.py would be:

#script2
import time

def information():
    variable = 0
    while variable < 500:
        yield variable
        variable += 1
        print(variable)
        time.sleep(1)

I understand that this has to do with generators, but my understanding was that yield would pause the loop, send the data to the original program, let it process it, and then un-pause the loop. Is there a better way of understanding this process?


Solution

  • For one thing, script2.information() is a generator function which means you will have to manually iterate the generator object returned on the first call it to get successive values.

    For another, tkinter doesn't support multithreading. One thing you can do is schedule for a function to be called after a certain amount of time using the universal widget method after(). In this case, it can be used to schedule a call to an (added) function that iterates the generator object after it's been created by calling script2.information() and updates the StringVar() widget accordingly each time it's called.

    You also need to change script2.py so it doesn't call time.sleep(). Doing so will make your tkinter GUI program hang whenever it's called (since it temporarily interrupts execution of the tkinter mainloop()).

    Modified script2.py:

    import time
    
    def information():
        variable = 0
        while variable < 60:  # changed from 500 for testing
            yield variable
            variable += 1
            print(variable)
    #        time.sleep(1)  # don't call in tkinter programs
    

    main script:

    from tkinter import *
    import script2
    
    DELAY = 100  # in millisecs
    
    root = Tk()
    canvas = Canvas(root, width=300, height=300)
    canvas.pack()
    
    def do_update(gen, var):
        try:
            next_value = next(gen)
        except StopIteration:
            var.set('Done!')
        else:
            var.set(next_value)
            root.after(DELAY, do_update, gen, var)  # call again after delay
    
    def run_script(var):
        gen = script2.information()  # create generator object
        do_update(gen, var)  # start iterating generator and updating var
    
    var = StringVar()
    var.set('Waiting for Input...')
    
    button1 = Button(root, text="Run script!", command=lambda: run_script(var))
    button1.pack()
    
    status = Label(root, textvariable=var, bd=1, relief=SUNKEN, anchor=W)
    status.pack(side=BOTTOM, fill=X)
    
    root.mainloop()