1

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()
8
  • lambda: return is nonsense, because func = lambda: something <=> def func(): return something Commented Apr 26, 2020 at 17:11
  • And lambda: continue <=> def func(): return continue. Commented Apr 26, 2020 at 17:12
  • 1
    Thanks I know. As stated above. Commented Apr 26, 2020 at 17:55
  • that's good :) I only explained why. Commented Apr 26, 2020 at 17:59
  • It might be me, but your code makes no sense to me. I think you should read up on the event-driven nature of GUI applications. Commented Apr 26, 2020 at 19:26

1 Answer 1

1

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()


Sign up to request clarification or add additional context in comments.

1 Comment

Yes! This does what I want, and uses some methods I haven't used before. Thank you so much!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.