Tom
Tom

Reputation: 1063

Python/Tkinter: Use button to control for loop

I have a for loop that is meant to run through a list, display some items in tkinter, wait for a button to be pushed, and then store some Entry and Checkbutton data. The code below is a MRE of the basics of what I'm trying to do. In the case below, when the Button is hit, I want to return to the loop_function and gather the variables from the button_function.

I thought perhaps using something like lambda: continue or lambda: return might bring it back to the first function, but those throw errors.

Any ideas?

from tkinter import *

class TestClass(Frame):
    def __init__(self, parent=None):
        self.parent = parent
        Frame.__init__(self)
        self.main = self.master
        self.f = Frame(self.parent)
        self.f.pack()

        (Button(self.f, text='Start', 
            command = self.loop_function)
            .grid(column=0, row=0, padx=10, pady=10))

    def loop_function(self):
        name_list = ['Luke', 'Han', 'Leia', 'Chewie']

        for n in name_list:
            self.button_function(n)
            force_user = self.fu.get()
            side = self.sd.get()
            print(n, force_user, side)

    def button_function(self, n):
        self.fu = IntVar(value=1)
        self.sd = StringVar(value='rebel')

        self.fu_e = Checkbutton(self.f, variable=self.fu)
        self.sd_e = Entry(self.f, textvariable=self.sd)

        col = 0
        lbl_list = ['Name', 'Force User?', 'Side']
        for l in lbl_list:
            (Label(self.f, text=l, width=11, anchor=W)
                .grid(column=col, row=0, padx=10, pady=10))
            col += 1

        (Label(self.f, text=n, width=11, anchor=W)
            .grid(column=0, row=1, padx=5))
        self.fu_e.grid(column=1, row=1)
        self.sd_e.grid(column=2, row=1)
        (Button(self.f, text='Save', 
            command = lambda: print('WAIT HERE!!'))
            .grid(column=1, row=2, padx=10, pady=10))

if __name__ == '__main__':
    root=Tk()
    ui = TestClass(root)
    ui.pack()
    root.mainloop()

Upvotes: 1

Views: 190

Answers (1)

Lydia van Dyke
Lydia van Dyke

Reputation: 2516

I think the following code does what you want to do. After clicking on the button Start the user gets a dialog where she can enter properties of the first user Luke. By clicking on the button Save the entered data is stored in some way. Then the properties of the next user (Han) can be edited.

A for loop is not the correct approach here. Instead we want to listen for the click events of the start and save buttons. In my solution, when the user clicks Start, the event handler pick_next_player is being called. This method always picks the next element from an iterator that I wrapped around name_list. Then the GUI elements are being rendered with your button_function.

The event handler save_action listens to the click event of the Save button. It collects the values that the user entered, stores it into self.results and displays the next player by calling pick_next_player.

When the last player has been saved, this script just prints a line 'finished ...' to the console. I assume you are going to stop the script or close the dialog there. But this is of course up to you.

from tkinter import *


class TestClass(Frame):
    def __init__(self, parent=None):
        self.parent = parent
        Frame.__init__(self)
        self.main = self.master
        self.f = Frame(self.parent)
        self.f.pack()

        (
            Button(self.f, text='Start', command=self.pick_next_player)
                .grid(column=0, row=0, padx=10, pady=10)
        )

        self.name_list = ['Luke', 'Han', 'Leia', 'Chewie']
        self.name_iter = iter(self.name_list)
        self.results = []
        self.current_name = None

    def pick_next_player(self):
        try:
            self.current_name = next(self.name_iter)
        except StopIteration:
            print(f"finished: {self.results}")
            return

        self.button_function()

    def button_function(self):
        self.fu = IntVar(value=1)
        self.sd = StringVar(value='rebel')

        self.fu_e = Checkbutton(self.f, variable=self.fu)
        self.sd_e = Entry(self.f, textvariable=self.sd)

        col = 0
        lbl_list = ['Name', 'Force User?', 'Side']
        for l in lbl_list:
            (Label(self.f, text=l, width=11, anchor=W)
             .grid(column=col, row=0, padx=10, pady=10))
            col += 1

        (
            Label(self.f, text=self.current_name, width=11, anchor=W)
                .grid(column=0, row=1, padx=5)
        )
        self.fu_e.grid(column=1, row=1)
        self.sd_e.grid(column=2, row=1)
        (
            Button(self.f, text='Save', command=self.save_action)
                .grid(column=1, row=2, padx=10, pady=10)
        )

    def save_action(self):
        force_user = self.fu.get()
        side = self.sd.get()
        print(f"saving {self.current_name}, {force_user}, {side}")
        self.results.append({'name': self.current_name, 'force': force_user, 'faction': side})
        self.pick_next_player()


if __name__ == '__main__':
    root = Tk()
    ui = TestClass(root)
    ui.pack()
    root.mainloop()


Upvotes: 1

Related Questions