Search code examples
pythontkintertkinter-entry

Tkinter Entry() don't get numbers from input


I have a problem with Tkinter Entry. I'm running program in a loop creating and destroying two class instances. In the initial loop, everything works fine. After the game ends I'm destroying the Tkinter instance:

self.frame.destroy()
self.tk.destroy()

Then the program goes back to the main loop:

def main():
  root = Tk()
  data = get_data(root)
  root.mainloop()
  del root
  root = Tk()
  main_window = Main_field(root, data)
  root.mainloop()
  del data, root, main_window
  msg = "Play again?"
  res = messagebox.askyesno("Game Over", message=msg)
  if res:
    del res
    main()
  else:
    quit()

The main object of this loop to create new empty class instances that will work as the first one. The problem is after restart the Entry box don't load the input numbers. I have an empty string when I call the get() method:

class get_data():
  def __init__(self, tk):
     global mines_var
     mines_var = tkinter.StringVar()
     entry_box_mines = Entry(tk, textvariable=mines_var, width=50)
     entry_box_mines.pack()
     entry_box_mines.insert(0, 0)
     self.width = 0
     self.len_ = 0
     self.mines = 0
     self.tk = tk
     start_game_button = Button(tk, text="Start Game", command=lambda: [self.get_attributes()]).pack()

   def get_attributes(self):
     res = self.validate(len_var.get(), width_var.get(), mines_var.get())
     if res == "OK":
        self.tk.destroy()
     else:
        messagebox.showinfo(message=res)

Where am I making the mistake?


Solution

  • Your mistake is

    mines_var = tkinter.StringVar()
    

    which removes the previous tkinter.StringVar() from mines_var and it removes the previous value.

    You should keep only the value in a global variable and later put it back in StringVar().

    You should also use the parent - tk - like in widgets.

    mines_var = tkinter.StringVar(parent, value=global_value)
    

    My version with other changes.

    PEP 8-- Style Guide for Python Code

    import tkinter as tk  # PEP8: `import *` is not preferred
    from tkinter import messagebox
    
    # --- classes ---
    
    class GetDataWindow():  # PEP8: `CamelCaseNames` for classes, and `noun` as name (PL: rzeczownik)
    
        def __init__(self):
    
            self.root = tk.Tk()
    
            # variables
    
            print('[DEBUG] __init__:', global_len, global_width, global_mines)
    
            self.len_var   = tk.StringVar(self.root, value=global_len)
            self.width_var = tk.StringVar(self.root, value=global_width)
            self.mines_var = tk.StringVar(self.root, value=global_mines)
    
            # widgets
    
            tk.Label(self.root, text='Settings').grid(row=0, column=0, columnspan=2)
    
            tk.Label(self.root, text='len:').grid(row=1, column=0, sticky='e')
            tk.Entry(self.root, textvariable=self.len_var).grid(row=1, column=1)
    
            tk.Label(self.root, text='width:').grid(row=2, column=0, sticky='e')
            tk.Entry(self.root, textvariable=self.width_var).grid(row=2, column=1)
    
            tk.Label(self.root, text='mines:').grid(row=3, column=0, sticky='e')
            tk.Entry(self.root, textvariable=self.mines_var).grid(row=3, column=1)
    
            tk.Button(self.root, text="Start Game", command=self.get_attributes).grid(row=4, column=0, columnspan=2)
    
            self.root.mainloop()
    
        def get_attributes(self):
            global global_len
            global global_width
            global global_mines
    
            res = self.validate(self.len_var.get(), self.width_var.get(), self.mines_var.get())
    
            if res == "OK":
                global_len   = self.len_var.get()
                global_width = self.width_var.get()
                global_mines = self.mines_var.get()
                print('[DEBUG] get_attributes:', global_len, global_width, global_mines)
                self.root.destroy()
            else:
                messagebox.showinfo(message=res)
    
        def validate(self, *values):
            return 'OK'
    
    class MainWindow():
    
        def __init__(self):
            self.root = tk.Tk()
    
            tk.Label(self.root, text='Game').grid(row=0, column=0, columnspan=2)
    
            tk.Label(self.root, text='len:').grid(row=1, column=0, sticky='e')
            tk.Label(self.root, text=global_len).grid(row=1, column=1)
    
            tk.Label(self.root, text='width:').grid(row=2, column=0, sticky='e')
            tk.Label(self.root, text=global_width).grid(row=2, column=1)
    
            tk.Label(self.root, text='mines:').grid(row=3, column=0, sticky='e')
            tk.Label(self.root, text=global_mines).grid(row=3, column=1)
    
            tk.Button(self.root, text="OK", command=self.root.destroy).grid(row=4, column=0, columnspan=2)
    
            self.root.mainloop()
    
    # --- main ---
    
    # default value at start
    global_len   = '0'
    global_width = '0'
    global_mines = '0'
    
    while True:
        GetDataWindow()
        MainFieldWindow()
    
        #win = GetDataWindow()
        #del win
        #win = MainWindow()
        #del win
    
        # create and hide main window before creating message
        root = tk.Tk()
        root.withdraw()
    
        res = messagebox.askyesno("Game Over", message="Play again?")
        if not res:
           break
    
        root.destroy()
    

    BTW:

    Of course, it could be done without the keyword global, like:

    len_  = 0
    width = 0
    mines = 0
    
    while True:
    
        win = GetDataWindow(len_, width, mines)
    
        len_  = win.len_
        width = win.width
        mines = win.mines
    
        MainWindow(len_, width, mines)
    
        # ... the rest ...
    

    Or simpler:

    settings = {
        'len': 0,
        'width': 0,
        'mines': 0,
    }
    
    while True:
    
        GetDataWindow(settings)
        MainWindow(settings)
    
        # ... the rest ...
    

    but I skipped this part.

    I could also use IntVar instead of StringVar.