0

I just forayed into tkinter for the first time and am running into some issues. I want to display several lists to users, and store their selections for each list in a dictionary (used to filter several columns of a dataframe later). Suppose, for instance, there are two lists: 1) One labeled "Brand", containing 'Brand X' and 'Brand Y' as options, 2) another "Customer Type", containing "New," "Existing," "All."

In sum, when all is said and done, if a user picks "Brand X", "New", and "All", then I'd get a dictionary back of {'Brand':['Brand X'],'Customer Type':['New','All']}. Getting one list is easy... but looping through the lists is presenting problems.

I have the below code so far:

from tkinter import *
from tkinter import ttk


class checkList(Frame):
    def __init__(self, options, parent=None):
        Frame.__init__(self, parent)
        self.makeHeader()
        self.options = options
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.makeWidgets(self.options)
        self.selections = []

    def makeHeader(self):
        header = ttk.Label(self,text='Please select options to limit on.')
        header.pack(side=TOP)
        self.header = header

    def makeWidgets(self, options):
        for key in self.options.keys():
            lbl = ttk.Label(self, text=key)
            lbl.pack(after=self.header)
            listbox = Listbox(self, selectmode=MULTIPLE, exportselection=0)
            listbox.pack(side=LEFT)
            for item in self.options[key]:
                listbox.insert(END, item)
            listbox.bind('<<ListboxSelect>>', self.onselect)
            self.listbox = listbox

    def onselect(self, event):
        selections = self.listbox.curselection()
        selections = [int(x) for x in selections]
        self.selections = [self.options[x] for x in selections]


if __name__ == '__main__':
    options = {'Brand':['Brand','Brand Y'], 'Customer Type': ['All Buyers','New Buyers','Existing Buyers']}
    checkList(options).mainloop()

Needless to say, the [self.options[x] for x in selections] works great with just one list, but since I have a dictionary, I really need [self.options[key][x] for x in selections]. However, I can't figure out how to pass the key at any given point in the loop. Is there a way to achieve what I'm trying to do?

2 Answers 2

1

The "magic" you're looking for to pass the key is simple because the tkinter objects are extensible. Here's your code working, I believe, the way you want:

from tkinter import *
from tkinter import ttk


class checkList(Frame):
    def __init__(self, options, parent=None):
        Frame.__init__(self, parent)
        self.makeHeader()
        self.options = options
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.listboxes = [] # New
        self.makeWidgets(self.options)
        self.selections = {} # Changed

    def makeHeader(self):
        header = ttk.Label(self,text='Please select options to limit on.')
        header.pack(side=TOP)
        self.header = header

    def makeWidgets(self, options):
        for key in self.options.keys():
            lbl = ttk.Label(self, text=key)
            lbl.pack(after=self.header)
            listbox = Listbox(self, selectmode=MULTIPLE, exportselection=0)
            listbox.key = key # here's the magic you were asking about...
            listbox.pack(side=LEFT)
            self.listboxes.append(listbox) # New
            for item in self.options[key]:
                listbox.insert(END, item)
            listbox.bind('<<ListboxSelect>>', self.onselect)
            self.listbox = listbox

    def onselect(self, event):
        for lb in self.listboxes:
            selections = lb.curselection()
            selections = [int(x) for x in selections]
            self.selections[lb.key] = [self.options[lb.key][x] for x in selections]
        print(self.selections)


if __name__ == '__main__': #   \/
    options = {'Brand':['Brand X','Brand Y'], 'Customer Type': ['All Buyers','New Buyers','Existing Buyers']}
    checkList(options).mainloop()
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you!! Makes perfect sense.
0

With the code you posted, you only have access to the last ListBox created by makeWidgets in onselect.

With minimal changes:

from tkinter import *
from tkinter import ttk

class checkList(Frame):
    def __init__(self, options, parent=None):
        Frame.__init__(self, parent)
        self.listboxes = []
        self.selections = {}
        self.makeHeader()
        self.options = options
        self.pack(expand=YES, fill=BOTH, side=LEFT)
        self.makeWidgets(self.options)

    def makeHeader(self):
        header = ttk.Label(self,text='Please select options to limit on.')
        header.pack(side=TOP)
        self.header = header

    def makeWidgets(self, options):
        for key in self.options.keys():
            lbl = ttk.Label(self, text=key)
            lbl.pack(after=self.header)
            listbox = Listbox(self, selectmode=MULTIPLE, exportselection=0)
            listbox.pack(side=LEFT)
            for item in self.options[key]:
                listbox.insert(END, item)
            listbox.bind('<<ListboxSelect>>', self.onselect)
            self.listboxes.append(listbox)

    def onselect(self, event):
        for (option, options), listbox in zip(self.options.items(), self.listboxes):
            self.selections[option] = [options[x] for x in map(int, listbox.curselection())]
        print(self.selections)

if __name__ == '__main__':
    options = {'Brand':['Brand','Brand Y'], 'Customer Type': ['All Buyers','New Buyers','Existing Buyers']}
    checkList(options).mainloop()

This recreates selections every time either ListBox selection is modified. Alternatively, you could use event to determine which ListBox selection was modified and update the corresponding part of selections. This would require initializing selections, though.

Comments

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.