简体   繁体   中英

Changing grid position with a button in python

I am trying to make the radio buttons change the position of the entry field (called assignment)

what i expected was for it to grid it next to the button but instead i get this error

Exception in Tkinter callback
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/tkinter/__init__.py", line 1702, in __call__
    return self.func(*args)
  File "<string>", line 16, in <lambda>
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/tkinter/__init__.py", line 2223, in grid_configure
    + self._options(cnf, kw))
_tkinter.TclError: bad window path name ".!application.!entry"`

the issue is on line 16 with the lambda function

ive tried making the variable in the function, i've also tried having it run exec.

here is the code

from tkinter import *
import random
class application(Frame):
    def __init__(self,master ):
        Frame.__init__(self, master)
        self.grid()
        self.create_widgets()
    def create_widgets(self, ):
        assignment = Entry(self)

        for i in self.winfo_children():
            i.destroy()
        self.var = IntVar()
        for i in range(8):
            Radiobutton(self, text = ('class ' + str(i + 1)), variable = self.var, value = (i+1), command = lambda i = i: assignment.grid()).grid(row = i, column = 1)
        for i in range(8):
            exec('class' + str(i) + ' = Label(self, text = \'34\')\nclass' + str(i) + '.grid(row = '+str(i)+',column =  3)')

        print(self.var.get())

root = Tk()
root.title('dumb kid idiot test')
root.geometry('500x500')
app = application(root)
root.mainloop()

I am not sure why you are using exec to create a label. There is a much safer way to do this just like your raidiobutton.

One major problem you have is you are not using i that you define in your lambda. Nothing can be updated if you do not apply that value to the grid.

Also this code is destroying your entry field right away.

for i in self.winfo_children():
    i.destroy()

I cleaned up your example a bit and fixed the lambda issue.

Try this:

import tkinter as tk


class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.geometry('300x225')
        self.title('dumb kid idiot test')

        self.entry = tk.Entry(self)
        self.var = tk.IntVar(self)

        for i in range(8):
            tk.Radiobutton(self, text=('class ' + str(i + 1)), variable=self.var, value=(i+1),
                           command=lambda i=i: self.entry.grid(row=i, column=4)).grid(row=i, column=1)
        for i in range(8):
            tk.Label(self, text='34').grid(row=i, column=3)


if __name__ == '__main__':
    App().mainloop()

You may also want a function/method to handle the grid chance just in case you also want your button to do other things when clicked.

import tkinter as tk


class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.geometry('300x225')
        self.title('dumb kid idiot test')

        self.entry = tk.Entry(self)
        self.var = tk.IntVar(self)
        for i in range(8):
            tk.Radiobutton(self, text=('class ' + str(i + 1)), variable=self.var, value=(i+1),
                           command=lambda i=i: self.move_entry(i)).grid(row=i, column=1)
        for i in range(8):
            tk.Label(self, text='34').grid(row=i, column=3)

    def move_entry(self, ndex):
        self.entry.grid(row=ndex, column=4)


if __name__ == '__main__':
    App().mainloop()

Let's look at this code:

def create_widgets(self, ):
    assignment = Entry(self)

    for i in self.winfo_children():
        i.destroy()

You are creating an entry widget, and then you almost immediately delete it since it is a child of self . Therefore, any code that later tries to use or modify the widget will fail with the exact error you are giving it.

If you plan on using the entry, don't delete it. You can do that in a couple of ways:

  • You can add an explicit check in your loop so that you don't call delete on the entry widget,
  • Instead of looping over all children, save the widgets you wish to destroy in a list and iterate over that list instead of all children.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM