2025, Oct 31 06:02

Tkinter में progress bar फ्रीज़? time.sleep छोड़ें, .after अपनाएं

Tkinter में time.sleep से event loop ब्लॉक होकर progress bar जाम हो जाता है। इस गाइड में .after के साथ non‑blocking अपडेट और responsive UI पाने का तरीका जानें।

छोटे, पुन: उपयोग योग्य UI घटक बनाना अच्छी आदत है, लेकिन GUI कोड का एक सख्त नियम है: इवेंट लूप को ब्लॉक न करें। इसका क्लासिक उदाहरण Tkinter का प्रोग्रेस विंडो है, जो उसे हिलाते ही या फोकस बदलते ही "Not Responding" दिखाने लगता है, जबकि स्क्रिप्ट पर्दे के पीछे चलती रहती है। नीचे इसका न्यूनतम डेमो और सीधा समाधान दिया है।

समस्या: काम के दौरान ठहर जाने वाला प्रोग्रेस बार

विंडो फिर से पेंट करना और इवेंट प्रोसेस करना बंद कर देती है, क्योंकि मुख्य थ्रेड sleep के साथ लूप में फँसा रहता है। प्रोग्राम की लॉजिक चलती रहती है, मगर UI को सांस लेने का मौका नहीं मिलता।

from tkinter import *
from tkinter.ttk import *
from datetime import *
import time
def boot():
    started_at = datetime.now().replace(microsecond=0)
    print(started_at, "\n")
    total_units = 100
    root = Tk()
    pct_var = StringVar()
    status_var = StringVar()
    left_var = StringVar()
    done = 0
    step = 5
    pct_lbl = Label(root, textvariable=pct_var).pack()
    status_lbl = Label(root, textvariable=status_var).pack()
    left_lbl = Label(root, textvariable=left_var).pack()
    prog = Progressbar(root, orient=HORIZONTAL, length=300)
    prog.pack(pady=10)
    while (done < total_units):
        done += step
        time.sleep(1)
        prog['value'] += (step/total_units) * 100
        pct_var.set(str(int((done/total_units)*100)) + "%")
        status_var.set(str(done) + " files completed!")
        left_var.set(str(int(total_units - done)) + " left to complete.")
        root.update_idletasks()
    root.mainloop()
if __name__ == '__main__':
    boot()

ऐसा क्यों होता है

मुख्य थ्रेड में time.sleep() के साथ लूप लगाने से Tkinter का mainloop ब्लॉक हो जाता है। जब तक लूप सो रहा होता है या काम कर रहा होता है, लंबित अपडेट और यूज़र इवेंट प्रोसेस नहीं हो पाते, इसलिए OS विंडो को unresponsive मान लेता है। सिर्फ update_idletasks() कॉल करना काफी नहीं है, क्योंकि कोड mainloop को नियमित रूप से नियंत्रण वापस नहीं देता।

समाधान: .after() से काम शेड्यूल करें

लूप और sleep की जगह .after() से आवधिक अपडेट शेड्यूल करें। इससे इवेंट लूप री-ड्रॉ और यूज़र क्रियाओं को संभालने के लिए मुक्त रहता है, और आपका प्रोग्रेस बार प्रतिक्रियाशील बना रहता है।

from tkinter import *
from tkinter.ttk import *
from datetime import *
import time
def boot():
    started_at = datetime.now().replace(microsecond=0)
    print(started_at, "\n")
    total_units = 100
    root = Tk()
    pct_var = StringVar()
    status_var = StringVar()
    left_var = StringVar()
    done = 0
    step = 5
    pct_lbl = Label(root, textvariable=pct_var).pack()
    status_lbl = Label(root, textvariable=status_var).pack()
    left_lbl = Label(root, textvariable=left_var).pack()
    prog = Progressbar(root, orient=HORIZONTAL, length=300)
    prog.pack(pady=10)
    def tick(current):
        progress = round((current / total_units) * 100, 0)
        prog['value'] = progress
        pct_var.set(f'{progress:.0f}%')
        status_var.set(f'{current} files completed!')
        left_var.set(f'{total_units - current} left to complete.')
        if current < total_units:
            root.after(1000, tick, current + step)
        if current == total_units:
            quit()
    tick(done)
    root.mainloop()
if __name__ == '__main__':
    boot()

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

GUI फ्रेमवर्क इवेंट‑ड्रिवन होते हैं। अगर आप sleep या लंबे लूप से mainloop को ब्लॉक कर देते हैं, तो UI इनपुट या री‑ड्रॉ प्रोसेस नहीं कर पाता—नतीजा जमी हुई विंडो और खराब अनुभव। .after() के जरिए लूप जैसा व्यवहार कराते हुए आप रिस्पॉन्सिवनेस बनाए रखते हैं, और लॉजिक भी सरल व पुन: उपयोग योग्य रहती है।

मुख्य बातें

Tkinter के मुख्य थ्रेड को ब्लॉकिंग लूप्स और time.sleep() से दूर रखें। UI अपडेट और बैकग्राउंड जैसे प्रोग्रेस टिक शेड्यूल करने के लिए .after() का उपयोग करें। काम पूरा होने पर प्रोग्राम बंद कराना हो तो प्रोग्रेस अंतिम अवस्था पर पहुँचते ही quit() कॉल करें।

यह लेख StackOverflow के प्रश्न (लेखक: Lee Donovan) और acw1668 के उत्तर पर आधारित है।