invalid command name Error

Fragen zu Tkinter.
Antworten
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

Hallo ich habe mir ein kleines Tabellen Widget geschrieben.
In das habe ich auch eine kleine Funktion implementiert die eine Zeile löscht, wenn man darauf doppelklickt.
Leider kommt immer die Meldung das diese Tabelle nicht existiert...
Ich habe unten einen Beispiel Code, könnt ihr mir helfen?

Code: Alles auswählen

import tkinter as tk
from tkinter import ttk

class Table(ttk.Treeview):
    def __init__(self, master, data):
        super().__init__(master)
        yscrollbar = ttk.Scrollbar(master, orient="vertical", command=self.yview)
        yscrollbar.grid(row=0, column=2, sticky="NS")
        self.configure(yscrollcommand=yscrollbar.set)
        self.headers = data[0]
        self.sizes = [int(size) for size in data[1]]
        data = data[2:]
        column_names = tuple([str(number) for number in range(len(self.headers))])
        self["columns"] = column_names
        self["show"] = "headings"
        for header, size, name in zip(self.headers, self.sizes, column_names):
            self.column(name, width=int(size), minwidth=int(size), anchor="w")
            self.heading(name, text=header, anchor="w")

        for i, column in enumerate(data):
            self.insert("", i, text="", values=tuple(column))

        self.bind("<Double-1>", self.on_double_click)

    def insert_column(self, entries):
        data = [entry.get() for entry in entries]
        self.insert("", randrange(1000, 10000000), text="", values=tuple(data))
                    
    def get_data(self):
        data = []
        data.append(self.headers)
        data.append(self.sizes)
        for child in self.get_children():
            data.append(self.item(child)["values"])

        return data
    
    def on_double_click(self, event):
        region = self.identify("region", event.x, event.y)
        if region == "heading":
            pass

        else:
            warning = Warning(title="Bestätigung", message=self.item(event.widget.focus())['values'])
            if warning.value:
                self.delete(event.widget.focus())

class Warning:
    def __init__(self, title, message):
        self.value = None
        self.root = tk.Tk()
        self.root.resizable(False, False)
        self.root.title(title)
        self.root.config(bg="white")
        tk.Label(self.root, text="willst du die folgenden Werte wirklich löschen?", bg="white", fg="red").pack()
        tk.Label(self.root, text=message, bg="white").pack()
        tk.Button(self.root, text="Ja", bg="orange", command=self.yes).pack()
        tk.Button(self.root, text="Nein", bg="white", command=self.no).pack()
        self.root.mainloop()
        
    def yes(self):
        self.value = True
        self.root.destroy()
        
    def no(self):
        self.value = False
        self.root.destroy()
        
def main():
    root = tk.Tk()
    table = Table(root, [["Äpfel", "Bananen", "Orangen"], ["60", "60", "60"], ["12", "7", "16"]])
    table.grid(row=0, column=1)
    
if __name__ == "__main__":
    main()
Fehler:

Code: Alles auswählen

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.7/tkinter/__init__.py", line 1705, in __call__
    return self.func(*args)
  File "beispiel.py", line 46, in on_double_click
    self.delete(event.widget.focus())
  File "/usr/lib/python3.7/tkinter/ttk.py", line 1273, in focus
    return self.tk.call(self._w, "focus", item)
_tkinter.TclError: invalid command name ".!table"
invalid command name ".!table"
    while executing
"$w identify row $x $y"
    (procedure "ttk::treeview::DoubleClick" line 2)
    invoked from within
"ttk::treeview::DoubleClick .!table 52 36 "
    (command bound to event)
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Es darf nur ein Exemplar von Tk geben und auch nur einen Aufruf von mainloop, wobei dieser Aufruf in `main` fehlt.
Warning will also statt dessen einen modalen Dialog erzeugen, für Ja/Nein-Abfragen hat Tkinter schon etwas fertiges; das ist auch keine sinnvolle Klasse und wäre besser eine einfache Funktion.
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

Sirius3 hat geschrieben: Montag 27. Juli 2020, 09:14 Es darf nur ein Exemplar von Tk geben und auch nur einen Aufruf von mainloop, wobei dieser Aufruf in `main` fehlt.
Das zweite Tk wurde zu Toplevel und mainloop ist jetzt auch da :lol: .
Der Fehler bleibt aber der gleiche...
Und wie würdest denn du eine sinnvolle Warning schreiben?-
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Naja, schön wäre es gewesen, wenn Du den korrigierten Code auch noch gezeigt hättest.
Nachdem ich die Warning-Klasse durch einen Aufruf von tkinter.messagebox.askyesno ersetzt hatte, funktionierte es bei mir ohne Fehler.
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

Wieso geht denn meine nicht?
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Das kann niemand sagen, weil es gerade eine globale Kristallkugel-Störung gibt.
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

Code: Alles auswählen

import tkinter as tk
from tkinter import ttk, messagebox

class Table(ttk.Treeview):
    def __init__(self, master, data):
        super().__init__(master)
        yscrollbar = ttk.Scrollbar(master, orient="vertical", command=self.yview)
        yscrollbar.grid(row=0, column=2, sticky="NS")
        self.configure(yscrollcommand=yscrollbar.set)
        self.headers = data[0]
        self.sizes = [int(size) for size in data[1]]
        data = data[2:]
        column_names = tuple([str(number) for number in range(len(self.headers))])
        self["columns"] = column_names
        self["show"] = "headings"
        for header, size, name in zip(self.headers, self.sizes, column_names):
            self.column(name, width=int(size), minwidth=int(size), anchor="w")
            self.heading(name, text=header, anchor="w")

        for i, column in enumerate(data):
            self.insert("", i, text="", values=tuple(column))

        self.bind("<Double-1>", self.on_double_click)

    def insert_column(self, entries):
        data = [entry.get() for entry in entries]
        self.insert("", randrange(1000, 10000000), text="", values=tuple(data))
                    
    def get_data(self):
        data = []
        data.append(self.headers)
        data.append(self.sizes)
        for child in self.get_children():
            data.append(self.item(child)["values"])

        return data
    
    def on_double_click(self, event):
        region = self.identify("region", event.x, event.y)
        if region == "heading":
            pass

        else:
            warning = Warning(title="Bestätigung", message=self.item(event.widget.focus())['values'])
            if warning.value:
                self.delete(event.widget.focus())

class Warning:
    def __init__(self, title, message):
        self.value = None
        self.root = tk.Toplevel()
        self.root.resizable(False, False)
        self.root.title(title)
        self.root.config(bg="white")
        tk.Label(self.root, text="willst du die folgenden Werte wirklich löschen?", bg="white", fg="red").pack()
        tk.Label(self.root, text=message, bg="white").pack()
        tk.Button(self.root, text="Ja", bg="orange", command=self.yes).pack()
        tk.Button(self.root, text="Nein", bg="white", command=self.no).pack()
        self.root.mainloop()
        
    def yes(self):
        self.value = True
        self.root.destroy()
        
    def no(self):
        self.value = False
        self.root.destroy()
        
def main():
    root = tk.Tk()
    table = Table(root, [["Äpfel", "Bananen", "Orangen"], ["60", "60", "60"], ["12", "7", "16"]])
    table.grid(row=0, column=1)
    root.mainloop()
    
if __name__ == "__main__":
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 14085
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Fire Spike: Es darf immer noch nur einen Aufruf von `mainloop()` geben. Der zweite ist erst zuende wenn das Hauptfenster geschlossen wird. *Dann* wird versucht das `delete()` auszuführen, was natürlich nicht mehr geht, weil das Hauptfenster, und damit auch die Tabelle weg ist. Und das führt dann zu der Ausnahme die Du siehst.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

Was sollte ich sonst machen?
Ohne mainloop ist warning.value ja immer None, da es nicht auf das Fenster wartet...
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Daher habe ich ja schon ganz zu Anfang geschrieben, dass Du einen Dialog verwenden mußt.
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

Da habe ich Style Probleme weil ich ttk.Style verwende :oops:
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Dann mußt Du die Style-Probleme lösen.
Benutzeravatar
peterpy
User
Beiträge: 188
Registriert: Donnerstag 7. März 2013, 11:35

Hallo Fire Spike,
mit deiner Abfrage:

Code: Alles auswählen

import tkinter as tk
from tkinter import ttk

class Table(ttk.Treeview):
    def __init__(self, master, data):
        super().__init__(master)
        yscrollbar = ttk.Scrollbar(master, orient="vertical", command=self.yview)
        yscrollbar.grid(row=0, column=2, sticky="NS")
        self.configure(yscrollcommand=yscrollbar.set)
        self.headers = data[0]
        self.sizes = [int(size) for size in data[1]]
        data = data[2:]
        column_names = tuple([str(number) for number in range(len(self.headers))])
        self["columns"] = column_names
        self["show"] = "headings"
        for header, size, name in zip(self.headers, self.sizes, column_names):
            self.column(name, width=int(size), minwidth=int(size), anchor="w")
            self.heading(name, text=header, anchor="w")
        for i, column in enumerate(data):
            self.insert("", i, text="", values=tuple(column))
        self.bind("<Double-1>", self.on_double_click)

    def insert_column(self, entries):
        data = [entry.get() for entry in entries]
        self.insert("", randrange(1000, 10000000), text="", values=tuple(data))
                    
    def get_data(self):
        data = []
        data.append(self.headers)
        data.append(self.sizes)
        for child in self.get_children():
            data.append(self.item(child)["values"])
        return data
    
    def on_double_click(self, event):
        region = self.identify("region", event.x, event.y)
        if region == "heading":
            pass
        else:
            self.warning(title="Bestätigung",
                         message=self.item(event.widget.focus())['values'])            
            if self.warning:                
                self.index=event.widget.focus()                

    def warning(self, title, message):        
        self.root = tk.Toplevel()
        self.root.resizable(False, False)
        self.root.title(title)
        self.root.config(bg="white")
        tk.Label(self.root, text="willst du die folgenden Werte wirklich löschen?", bg="white", fg="red").pack()
        tk.Label(self.root, text=message, bg="white").pack()
        tk.Button(self.root, text="Ja", bg="orange",
                  command=self.yes).pack()
        tk.Button(self.root, text="Nein", bg="white", command=self.no).pack()
        
    def yes(self):
        self.delete(self.index)        
        self.root.destroy()        
        
    def no(self):        
        self.root.destroy()           
        
def main():
    root = tk.Tk()
    table = Table(root, [["Äpfel", "Bananen", "Orangen"], ["60", "60", "60"], ["12", "7", "16"]])
    table.grid(row=0, column=1)
    root.mainloop()
    
if __name__ == "__main__":
    main()
oder mit der Messagebox

Code: Alles auswählen

import tkinter as tk
from tkinter import ttk, messagebox

class Table(ttk.Treeview):
    def __init__(self, master, data):
        super().__init__(master)
        yscrollbar = ttk.Scrollbar(master, orient="vertical", command=self.yview)
        yscrollbar.grid(row=0, column=2, sticky="NS")
        self.configure(yscrollcommand=yscrollbar.set)
        self.headers = data[0]
        self.sizes = [int(size) for size in data[1]]
        data = data[2:]
        column_names = tuple([str(number) for number in range(len(self.headers))])
        self["columns"] = column_names
        self["show"] = "headings"
        for header, size, name in zip(self.headers, self.sizes, column_names):
            self.column(name, width=int(size), minwidth=int(size), anchor="w")
            self.heading(name, text=header, anchor="w")

        for i, column in enumerate(data):
            self.insert("", i, text="", values=tuple(column))

        self.bind("<Double-1>", self.on_double_click)

    def insert_column(self, entries):
        data = [entry.get() for entry in entries]
        self.insert("", randrange(1000, 10000000), text="", values=tuple(data))
                    
    def get_data(self):
        data = []
        data.append(self.headers)
        data.append(self.sizes)
        for child in self.get_children():
            data.append(self.item(child)["values"])
        return data
    
    def on_double_click(self, event):
        region = self.identify("region", event.x, event.y)
        if region == "heading":
            pass
        else:
            frage = "willst du die \nfolgenden Werte \nwirklich löschen?"
            werte = self.item(event.widget.focus())['values']
            meldung = "{}{}".format(frage, werte)
            warning = messagebox.askyesno(title="Bestätigung", message=meldung)
            self.delete(event.widget.focus())
        
def main():
    root = tk.Tk()
    table = Table(root, [["Äpfel", "Bananen", "Orangen"], ["60", "60", "60"], ["12", "7", "16"]])
    table.grid(row=0, column=1)
    root.mainloop()
    
if __name__ == "__main__":
    main()
Was gefällt dir besser?
Gruss
Peter
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

Vielen Dank peterpy :)
Antworten