Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
CTkComboBox - not working
#11
(Aug-28-2023, 01:56 PM)deanhystad Wrote: Yes, YOU could do this with CTk combo boxes too.

May I please ask if you can show me with my original code?
much appreciated :)
Reply
#12
I provided a demonstration of how you can use tk.StringVar.trace() to solve your problem. It should be easy for you to take that idea and implement it into your original code. I think classes are far superior to lists for organizing this type of code, but you can use trace with lists. You'll have to modify the code to pass a column index, like you did with the <ComboSelected> bind() call. Be aware that trace passes three arguments to the callback function as opposed to 1 argument from bind. Other than that, the code will be nearly identical.

Classes are really soooooooo much better for this type of problem that you should consider learning about them. Since everything in Python is an obect, and all objects are instances of classes, understanding how Classes work will really help you understand how Python works. After doing the obligatory "Hello World" program to verify you installed Python correctly, any serious exploration of Python should immediately introduce you to classes.

As a comparison, this is my earlier code that uses custom classes converted to not use any new classes. This shows how to use trace() to call a function and pass an index argument.
import tkinter as tk
import tkinter.ttk as ttk

models_data = {
    "Caterpillar LHD models": {
        "R1300G": ["DB 3.4", "DB 2.8", "DB 2.5", "DB 3.1"],
        "R1600H": ["DB 5.9", "DB 4.8", "DB 4.2", "DB 5.6"],
        "R1700": ["DB 5.7", "DB 6.1", "DB 6.6", "DB 7.5", "DB 8"],
        "R1700XE": ["DB 5.7", "DB 6.1", "DB 6.6", "DB 7.5"],
        "R1700G": ["DB 4.6", "DB 5", "DB 6.6", "DB 5.7", "DB 7.3", "DB 8.8"],
        "R2900G": ["DB 8.9", "DB 7.2", "DB 6.3", "DB 8.3"],
        "R2900": ["DB 6.3", "DB 7.2", "DB 8.3", "DB 8.9"],
        "R2900XE": ["DB 7.4", "DB 8.6", "DB 9.2", "DB 9.8"],
        "R3000H": ["DB 10.5", "DB 8.9", "DB 9.5"],
    },
    "Sandvik LHD models": {
        "SANDVIK LH517i": ["DB 7", "DB 7.6", "DB 8.6", "DB 9.1", "DB 8.4"],
        "SANDVIK LH621i": ["DB 8.0", "DB 9.0", "DB 10.7", "DB 11.2"],
        "SANDVIK LH515i": ["DB 6.3", "DB 6.8", "DB 7.5"],
        "SANDVIK LH514": ["DB 6.2", "DB 7", "DB 5.4"],
        "Toro™ LH625iE": ["DB 10"],
        "Sandvik LH514E": ["DB 4.6", "DB 5", "DB 5.4", "DB 6.2", "DB 7"],
        "Toro™ LH514BE": ["DB 4.6", "DB 5", "DB 5.4", "DB 6.2", "DB 7", "DB 7.5"],
        "Sandvik LH409E": ["DB 3.8", "DB 4.3", "DB 4.6"],
    },
    "Epiroc LHD models": {
        "Epiroc ST14 SG": [
            "DB 4.7",
            "DB 5",
            "DB 5.4",
            "DB 5.8",
            "DB 6.4",
            "DB 7.0",
            "DB 7.8",
        ],
        "Epiroc ST18 SG": ["DB 9.7", "DB 8.8", "DB 7.9", "DB 7.3", "DB 6.7", "DB 6.3"],
        "Epiroc ST14": [
            "DB 4.7",
            "DB 5",
            "DB 5.4",
            "DB 5.8",
            "DB 6.4",
            "DB 7.0",
            "DB 7.8",
        ],
        "Epiroc ST18 S": ["DB 9.7", "DB 8.8", "DB 7.9", "DB 7.3", "DB 6.7", "DB 6.3"],
    },
    "Komatsu LHD models": {
        "WX18H": ["DB 8.2", "DB 9.2", "DB 10", "DB 11.2"],
        "WX22H": ["DB 10", "DB 11", "DB 12.2", "DB 13.8"],
    },
}


def selection(index):
    """Return selected manufacturer, model, bucket."""
    c = columns[index]
    return c.group.var.get(), c.model.var.get(), c.bucket.var.get()


def group_cb(index):
    """Group selection changed callback."""
    # Selecting a group changes the model combobox choices.
    c = columns[index]
    values = list(models_data[c.group.var.get()])
    c.model["values"] = values
    # Changing the model combobox selection will call model_cb(index).
    c.model.var.set(values[0])


def model_cb(index):
    """Model selection changed callback."""
    # Selecting a model changes the bucket combobox choices.
    c = columns[index]
    values = list(models_data[c.group.var.get()][c.model.var.get()])
    c.bucket["values"] = values
    # Changing the bucket combobox selection will call bucket_cb(index).
    c.bucket.var.set(values[0])


def bucket_cb(index):
    """Bucket selection changed callback."""
    # Execute the column callback function if there is one.
    if columns[index].command:
        columns[index].command(selection(index))


root = tk.Tk()
width = max(len(key) for key in models_data)
columns = [tk.Frame(root) for _ in range(2)]
for index, (c, group) in enumerate(zip(columns, models_data)):
    c.command = print
    c.pack(side=tk.LEFT, padx=5, pady=5)

    # Add group, model and bucket Comboboxes to the column.
    var = tk.StringVar()
    var.trace("w", lambda a, b, c, i=index: group_cb(i))    # <-- Call group_cb(index) when var changes value.
    c.group = ttk.Combobox(c, values=list(models_data), textvariable=var, width=width)
    c.group.var = var
    c.group.pack(pady=5)

    var = tk.StringVar()
    var.trace("w", lambda a, b, c, i=index: model_cb(i))
    c.model = ttk.Combobox(c, textvariable=var, width=width)
    c.model.var = var
    c.model.pack(pady=5)

    var = tk.StringVar()
    var.trace("w", lambda a, b, c, i=index: bucket_cb(i))
    c.bucket = ttk.Combobox(c, textvariable=var, width=width)
    c.bucket.var = var
    c.bucket.pack(pady=5)

    c.group.var.set(group)

root.mainloop()
I think this looks terrible, even using some clean-up tricks like associating the StringVar object with the Combobox and the comboboxes with the column. Pylance hates adding attributes to objects, but I think pylance would prefer we all write C++ with python syntax..

Notice how using the power of classes removes so much repetition. Compare this:
    var = tk.StringVar()
    var.trace("w", lambda a, b, c, i=index: group_cb(i))
    c.group = ttk.Combobox(c, values=list(models_data), textvariable=var, width=width)
    c.group.var = var

    var = tk.StringVar()
    var.trace("w", lambda a, b, c, i=index: model_cb(i))
    c.model = ttk.Combobox(c, textvariable=var, width=width)
    c.model.var = var

    var = tk.StringVar()
    var.trace("w", lambda a, b, c, i=index: bucket_cb(i))
    c.bucket = ttk.Combobox(c, textvariable=var, width=width)
    c.bucket.var = var
To this:
        self.group = ComboBox(self, values=list(models_data), command=self._group_cb, width=width)
        self.model = ComboBox(self, command=self._model_cb, width=width)
        self.bucket = ComboBox(self, command=self._bucket_cb, width=width)
Less code repetition means less typing, less code to read, less code to test, and most importantly, less code to maintain. I think it also makes code easier to understand because the meaning is not hidden in a mess of bookkeeping.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020