_tkinter.TclError: bad listbox index "": must be active, anchor, end, @ x, y, or a number

1

Hello everyone I am currently trying to insert x amount of Listboxes in each Frame of a Notebook, depending on the number of elements in a dictionary and all this from a for loop, now I have created a function for me to print the name of the selected listbox, but the problem is that it only prints the name of the listboxes that have been created in the last Frame, the ones that are created in the first Frame when selecting them, I skipped the error. Excuse me if I explain myself fatally, but I'm very new to Python and really the words are not my thing :). Without further ado I say goodbye thank you very much in advance for the help.

Here's the code I've summarized as much as I could.

from tkinter import *
from tkinter import ttk

root=Tk()
note = ttk.Notebook(root)


def mostrar_nombre(event):
    nombrel = str((listbox .get(listbox .curselection())))
    print(nombrel)


lista1={"Pestaña1": ["nota1", "nota2"], "Pestaña2": ["block1","block2","block3"]}

tabs = []

for i in (lista1): # Introduce los frames dentro de la lista tabs[] para su futuro uso.

    tabs.append(ttk.Frame(note))

contador1=-1
contador2=-1

for i in (lista1):
    contador1 += 1

    numero= int(contador1)

    note.add(tabs[numero], text=i) # Comienza a crear los frames. i=Pestaña1- i=Pestaña2

    for e in(lista1[i]):

        contador2 += 1
        numero = int(contador1)

        global listbox
        listbox = Listbox(tabs[numero])
        listbox.grid(row=0, column=0)
        listbox.bind("<<ListboxSelect>>", mostrar_nombre) # Comienza a asignar los listboxs a cada pestaña.

        for e in (lista1[i]):
            listbox.insert(END, e) # Asigna a cada listbox creado un nombre.

note.pack()
root.mainloop()
    
asked by J.M.C 19.09.2018 в 17:43
source

1 answer

0

There are several points to consider:

  • In the callback you use listbox.get and listbox.curselection , where listbox is the global variable generated in for . This causes that the Listbox of the one that tries to obtain the index is always the last created in the cycle (last assignment listbox = Listbox(tabs[numero]) ). If you try to select in another Listbox the callback will be called but try to get the last Listbox selected by the commented before and not the one that generates the event, which has no selected item and therefore the commented exception occurs.

    The solution is to use the event that the callback receives as an argument and get the reference to the widget that launches it by:

      listbox = event.widget
    
  • The <<ListboxSelect>> event is not only launched when an item is actively selected, any change in the selection of Listbox throws it. For example, if the item is deselected, the event is also generated.

  • Also, by default tkinter.Listbox loses the selection when another widget takes it for itself .

    This causes that when you select an item in a list and then select an item from another list, two events are launched, one for the new selection and another for the deselection of the item from the previous list. The previous list is left without a selected element, so listbox .curselection() returns an empty tuple and get fails as a result, as before, when it does not provide a valid index.

You have several options to solve the problem of the last two points:

  • Use a conditional in the callback to filter those calls resulting from the loss of the selection:

    def mostrar_nombre(event):
        listbox = event.widget
        index = listbox.curselection()
        if index:
            value = listbox.get(index[0])
            print(value)
    
  • Set the Listbox so that you do not lose the selection when you switch from one to another. This is achieved through the exportselection attribute giving it a value of False .

    I leave an example of your code with this last option, simplifying some aspects of it:

    import tkinter as tk
    from tkinter import ttk
    
    
    def mostrar_nombre(event):
        listbox = event.widget
        index = listbox.curselection()
        value = listbox.get(index[0])
        print(value)
    
    pestañas = {
                "Pestaña1": ["nota1", "nota2"],
                "Pestaña2": ["block1","block2","block3"]
               }
    
    note = ttk.Notebook()
    
    for pestaña, items in pestañas.items():
        frame = ttk.Frame(note)
        note.add(frame, text=pestaña)
        listbox = tk.Listbox(frame, exportselection=False)
        listbox.grid(row=0, column=0)
        listbox.bind("<<ListboxSelect>>", mostrar_nombre)
    
        for item in items:
           listbox.insert(tk.END, item)
    
    note.pack()
    note.mainloop()
    

    If you need a list of references to frames for the future, simply use append in the same for :

    tabs = []
    
    for pestaña, items in pestañas.items():
        frame = ttk.Frame(note)
        tabs.append(frame)
        ....
    
answered by 20.09.2018 / 00:25
source