2025, Oct 16 16:32

Tkinter में dependent ComboBox विजेट्स जोड़ने का साफ तरीका

Python Tkinter में dependent ComboBox लागू करने का साफ तरीका: dictionary mapping से विकल्प जोड़ें, ComboboxSelected पर values अपडेट करें और UI स्थिर रखें.

मशीन मेंटेनेंस रिकॉर्ड करने के लिए छोटा सा Tkinter टूल बनाना आसान रहता है—जब तक कि आपको परस्पर-निर्भर इनपुट की जरूरत न पड़े: एक ComboBox, जो दूसरे के विकल्प तय करता है. एक आम गलती है सिर्फ Python का वेरिएबल बदल देना और मान लेना कि UI खुद‑ब‑खुद बदल जाएगा. ऐसा नहीं होता. नीचे इसी मुद्दे की केंद्रित व्याख्या और Tkinter में dependent ComboBox विजेट्स को जोड़ने का साफ, भरोसेमंद तरीका दिया गया है.

समस्या

लक्ष्य यह है कि पहले ComboBox में चुनी गई मशीन के आधार पर दूसरे ComboBox में कार्यों की सूची दिखाई जाए. Callback यह समझ लेता है कि कौन‑सी सूची लेनी है, लेकिन UI रिफ्रेश नहीं होता और दूसरा ड्रॉपडाउन शुरुआती placeholder ही दिखाता रहता है.

import tkinter as tk
from tkinter import ttk
tasks_pool = ['Maybe it will work this time?']
def machine_task_select(event):
    """generate task combobox based on machine selection"""
    global tasks_pool
    jf_tasks = ['Coolant', 'Waylube', 'Spindle Oil', 'Laser Reflector', 'Air Filter', 'Check Unlcamp/Clamp', 'Check ATC']
    dia_tasks = ['Coolant', 'Spindle Cone', 'Clean Blume Laser', 'Check Spindle Oil', 'Check Grease', 'Check Fluid Press', 'Check Chiller', 'Clean Chip Basket Recovery Tray', 'Electrical Cabinet AC Air Filter', 'Column Air Filter']
    tak_tasks = ['Coolant', 'Spindle Oil', 'Waylube', 'Air Filter']
    act3_tasks = ['Coolant', 'Spindle Cone', 'Clean Blume Laser', 'Spindle Oil','Waylube', 'Coupling Lube', 'Air Filter']
    hart_tasks = ['Coolant', 'Oil', 'Air Filter']
    herm_tasks = ['Coolant', 'Grease', 'Air Filter']
    choice = str(box_machine.get())
    if choice == machines[0] or choice == machines[1] or choice == machines[2] or choice == machines[3]:
        tasks_pool = jf_tasks
    elif choice == machines[4] or str(box_machine.get()) == machines[5]:
        tasks_pool = dia_tasks
    elif choice == machines[6]:
        tasks_pool = tak_tasks
    elif choice == machines[7]:
        tasks_pool = act3_tasks
    elif choice == machines[8]:
        tasks_pool = hart_tasks
    elif choice == machines[9]:
        tasks_pool = herm_tasks
    else:
        tasks_pool = ['Am I Insane?']
    return tasks_pool
app = tk.Tk()
app.title("Maintenance Recorder")
app.resizable(width=False, height=False)
panel = tk.Frame(master=app)
panel.pack()
machines = ['JF1', 'JF2', 'JF1500', 'JF1600', 'Diamond 1', 'Diamond 2', 'Takumi B8', 'Active 3000', 'Hartford', 'Hermle C400U']
box_machine = ttk.Combobox(master=panel, width=50, values=machines, state='readonly')
box_machine.bind('<>', machine_task_select)
lbl_machine = tk.Label(master=panel, text='Machine:')
box_task = ttk.Combobox(master=panel, width=50, values=tasks_pool, state='readonly')
lbl_task = tk.Label(master=panel, text='Task Completed:')
lbl_machine.grid(row=0, column=0, sticky='e')
box_machine.grid(row=0, column=1, sticky='w')
lbl_task.grid(row=4, column=0, sticky='e')
box_task.grid(row=4, column=1, sticky='w')
app.mainloop()

असल में क्या हो रहा है

Callback यह निकाल तो लेता है कि कौन‑सी कार्य‑सूची दिखनी चाहिए, लेकिन ComboBox को कभी यह नहीं बताया जाता कि नई सूची अपनानी है. सिर्फ Python वेरिएबल बदलने से विजेट की स्थिति नहीं बदलती. Tkinter विजेट्स मनचाहे global वेरिएबल्स से बंधे नहीं होते; ttk.Combobox में जो दिख रहा है उसे बदलने के लिए उसके values को widget configuration से स्पष्ट रूप से अपडेट करना पड़ता है, और यदि आप textvariable उपयोग कर रहे हैं तो उसे भी सेट करना होता है.

देखभाल के नज़रिए से भी दिक्कत है: लंबी if/elif शृंखला के बजाय मशीन से कार्य‑सूची का सीधा mapping रखें. इससे UI अपडेट करना आसान हो जाता है और गलतियों की गुंजाइश घटती है.

समाधान

पहले ComboBox की चयनित वैल्यू को दूसरे ComboBox के विकल्पों से जोड़ने के लिए dictionary का उपयोग करें. पहले ComboBox पर एक callback bind करें जो दूसरे ComboBox के values अपडेट करे और कोई डिफ़ॉल्ट आइटम चुन दे. इससे व्यवहार अनुमानित रहता है और कोड सघन व साफ रहता है.

import tkinter as tk
from tkinter import ttk
def set_secondary_options(_evt) -> None:
    """Update the values in secondary_box when primary_box changes"""
    picked = sel_primary.get()
    options = mapping[picked]
    secondary_box.config(values=options)
    sel_secondary.set(options[0])
ui = tk.Tk()
# कुंजियाँ पहले कॉम्बोबॉक्स को भरती हैं; हर कुंजी की सूची दूसरे को भरती है
mapping = {
    'foo': ['fee', 'fi', 'fo', 'fum'],
    'bar': ['fizz', 'buzz'],
    'baz': ['zip', 'zap', 'zoop'],
}
first_keys = list(mapping.keys())
second_defaults = mapping[first_keys[0]]
# चुने गए वर्तमान मान रखने वाले वेरिएबल
sel_primary = tk.StringVar(ui, value=first_keys[0])
sel_secondary = tk.StringVar(ui, value=second_defaults[0])
# पहला कॉम्बोबॉक्स: दूसरे को अपडेट करने के लिए कॉलबैक बाइंड किया गया है
primary_box = ttk.Combobox(ui, values=first_keys, textvariable=sel_primary)
primary_box.bind('<<ComboboxSelected>>', set_secondary_options)
# दूसरा कॉम्बोबॉक्स
secondary_box = ttk.Combobox(ui, values=second_defaults, textvariable=sel_secondary)
primary_box.pack(side='left', padx=5, pady=5)
secondary_box.pack(side='left', padx=5, pady=5)
ui.mainloop()

यह क्यों मायने रखता है

आंतरिक टूल्स और डेटा‑एंट्री यूटिलिटीज़ में निर्भर इनपुट आम हैं. अगर UI निर्धारक तरीके से अपडेट नहीं होता, तो उपयोगकर्ता पुराने विकल्प देखते हैं और रिकॉर्ड असंगत हो सकते हैं. इवेंट फायर होने पर विजेट की स्थिति को स्पष्ट रूप से फिर से कॉन्फ़िगर करना—यही फर्क है उस फ़ील्ड के बीच जो सही दिखती है और उस फ़ील्ड के बीच जो वाकई सही होती है.

समापन विचार

Tkinter में dependent ComboBox विजेट्स जोड़ते समय, स्पष्टता के लिए mapping संरचना अपनाएँ, driving कंट्रोल के '<<ComboboxSelected>>' इवेंट पर bind करें, और dependent विजेट को .config(values=...) से अपडेट करें. यदि आप कोई डिफ़ॉल्ट चयन बनाए रखते हैं, तो values अपडेट करते ही StringVar को किसी वैध विकल्प पर सेट कर दें. पठनीयता के लिए PEP 8 मानें: फ़ंक्शंस और वेरिएबल्स के लिए lower_case_names का उपयोग करें, और Frame तथा Label जैसी क्लासेस के लिए CamelCaseNames सुरक्षित रखें. इन छोटी प्रथाओं से dynamic dropdowns सरल, अनुमानित और बढ़ाने में आसान बने रहते हैं.

यह लेख StackOverflow पर प्रश्न (लेखक: TheGman117) और JRiggles के उत्तर पर आधारित है.