मैं Tkinter में विंडो क्लोज इवेंट को कैसे संभालूँ?


130

मैं पायथन टिंकर प्रोग्राम में विंडो क्लोज इवेंट ('X' बटन पर क्लिक करने वाला उपयोगकर्ता) कैसे संभाल सकता हूं?

जवाबों:


178

टिंकर प्रोटोकॉल हैंडलर नामक एक तंत्र का समर्थन करता है । यहाँ, शब्द प्रोटोकॉल का तात्पर्य अनुप्रयोग और विंडो प्रबंधक के बीच पारस्परिक क्रिया से है। सबसे अधिक इस्तेमाल किया जाने वाला प्रोटोकॉल कहा जाता है WM_DELETE_WINDOW, और यह परिभाषित करने के लिए उपयोग किया जाता है कि उपयोगकर्ता द्वारा विंडो प्रबंधक का उपयोग करके स्पष्ट रूप से विंडो बंद करने पर क्या होता है।

आप इस प्रोटोकॉल के लिए एक हैंडलर स्थापितprotocol करने के लिए विधि का उपयोग कर सकते हैं (विजेट एक या विजेट होना चाहिए ):TkToplevel

यहाँ आपके पास एक ठोस उदाहरण है:

import tkinter as tk
from tkinter import messagebox

root = tk.Tk()

def on_closing():
    if messagebox.askokcancel("Quit", "Do you want to quit?"):
        root.destroy()

root.protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()

2
यदि आप ट्विस्टेड की तरह कुछ का उपयोग कर रहे हैं जो एक इवेंट लूप को स्वतंत्र रूप से बनाए रखता है या टिंकर (जैसे: ट्विस्टेड रिएक्टर ऑब्जेक्ट) यह सुनिश्चित करता है कि बाहरी मेन लूप को उस उद्देश्य के लिए जो भी स्मेनेमिक्स प्रदान करता है उसके साथ बंद हो जाता है (जैसे: रिएक्टर.स्टॉप () मुड़ के लिए)
ब्रायन जैक

4
विंडोज पर मेरे पायथन 2.7 पर, Tkinterएक सबमॉड्यूल संदेश बॉक्स नहीं था। मैंने इस्तेमाल कियाimport tkMessageBox as messagebox
आयरनमैनमार्क 20

मुझे लगता है कि आपको यह जानना चाहिए कि आपने इस उत्तर और कोड को किसी और / जहां से कॉपी किया है।
क्रिश्चियन डीन

1
मुझे नहीं पता, यह वह कोड नहीं है जिसे मैंने मूल रूप से पोस्ट किया है।
मैट ग्रेगोरी

मेरे लिए काम नहीं करता है। यह ग्राफिक्स के रुकावट के लिए क्लासिक पायथन की अराजक प्रतिक्रिया को नहीं बदलता है जब एक खिड़की को बंद कर देता है (उदाहरण के लिए Alt + 4)।
अपोस्टोलोस

29

मैट ने करीब बटन का एक क्लासिक संशोधन दिखाया है।
अन्य पास विंडो को कम से कम बटन है।
आप होने से इस व्यवहार reproduced कर सकते हैं iconify विधि
होना प्रोटोकॉल विधि का दूसरा तर्क।

विंडोज 7 और 10 पर परीक्षण किया गया कार्य उदाहरण यहां दिया गया है:

# Python 3
import tkinter
import tkinter.scrolledtext as scrolledtext

root = tkinter.Tk()
# make the top right close button minimize (iconify) the main window
root.protocol("WM_DELETE_WINDOW", root.iconify)
# make Esc exit the program
root.bind('<Escape>', lambda e: root.destroy())

# create a menu bar with an Exit command
menubar = tkinter.Menu(root)
filemenu = tkinter.Menu(menubar, tearoff=0)
filemenu.add_command(label="Exit", command=root.destroy)
menubar.add_cascade(label="File", menu=filemenu)
root.config(menu=menubar)

# create a Text widget with a Scrollbar attached
txt = scrolledtext.ScrolledText(root, undo=True)
txt['font'] = ('consolas', '12')
txt.pack(expand=True, fill='both')

root.mainloop()

इस उदाहरण में हम उपयोगकर्ता को दो नए निकास विकल्प देते हैं:
क्लासिक फ़ाइल → बाहर निकलें, और Escबटन भी ।


12

Tkinter गतिविधि के आधार पर, और विशेष रूप से Tkinter.after का उपयोग करते समय, इस गतिविधि को रोकना destroy()- यहां तक ​​कि प्रोटोकॉल (), एक बटन आदि का उपयोग करके - इस गतिविधि को परेशान करेगा ("त्रुटि निष्पादित करते समय") बजाय इसे समाप्त करने के। । लगभग हर मामले में सबसे अच्छा समाधान एक ध्वज का उपयोग करना है। यहां इसका उपयोग करने का एक सरल, मूर्खतापूर्ण उदाहरण दिया गया है (हालांकि मैं निश्चित हूं कि आप में से अधिकांश को इसकी आवश्यकता नहीं है! :)

from Tkinter import *

def close_window():
  global running
  running = False  # turn off while loop
  print( "Window closed")

root = Tk()
root.protocol("WM_DELETE_WINDOW", close_window)
cv = Canvas(root, width=200, height=200)
cv.pack()

running = True;
# This is an endless loop stopped only by setting 'running' to 'False'
while running: 
  for i in range(200): 
    if not running: 
        break
    cv.create_oval(i, i, i+1, i+1)
    root.update() 

यह ग्राफिक्स गतिविधि को अच्छी तरह से समाप्त करता है। आपको केवल runningसही जगह पर जांच करने की आवश्यकता है ।


4

मैं अपना ध्यान इस ओर लाने के लिए अपॉस्तोलोस द्वारा उत्तर देना चाहता हूं। यहाँ स्पष्ट विवरण और उदाहरण कोड के साथ वर्ष 2019 में पायथन 3 के लिए और अधिक विस्तृत उदाहरण दिया गया है।


इस तथ्य से सावधान रहें कि destroy()(या कस्टम विंडो क्लोजिंग हैंडलर बिल्कुल भी नहीं है) यूजर के बंद होने पर विंडो और उसके सभी रनिंग कॉलबैक को तुरंत नष्ट कर देगा।

यह आपके लिए खराब हो सकता है, आपकी वर्तमान टिंकर गतिविधि पर निर्भर करता है, और विशेष रूप से tkinter.after(आवधिक कॉलबैक) का उपयोग करते समय। आप एक कॉलबैक का उपयोग कर रहे होंगे जो कुछ डेटा को संसाधित करता है और डिस्क पर लिखता है ... उस स्थिति में, आप स्पष्ट रूप से चाहते हैं कि डेटा लेखन अचानक समाप्त हुए बिना समाप्त हो जाए।

इसके लिए सबसे अच्छा उपाय है कि आप एक ध्वज का उपयोग करें। इसलिए जब उपयोगकर्ता विंडो बंद करने का अनुरोध करता है, तो आप उस ध्वज को चिह्नित करते हैं, और फिर उस पर प्रतिक्रिया करते हैं।

(ध्यान दें: मैं सामान्य रूप से GUIs को अच्छी तरह से समझाया कक्षाओं और अलग-अलग वर्कर थ्रेड्स के रूप में डिज़ाइन करता हूं, और मैं निश्चित रूप से "ग्लोबल" का उपयोग नहीं करता हूं (मैं इसके बजाय क्लास इंस्टेंस वेरिएबल्स का उपयोग करता हूं), लेकिन इसका मतलब प्रदर्शित करने के लिए एक सरल, स्ट्रिप-डाउन उदाहरण है जब उपयोगकर्ता विंडो बंद करता है तो Tk अचानक आपके आवधिक कॉलबैक को मारता है ...)

from tkinter import *
import time

# Try setting this to False and look at the printed numbers (1 to 10)
# during the work-loop, if you close the window while the periodic_call
# worker is busy working (printing). It will abruptly end the numbers,
# and kill the periodic callback! That's why you should design most
# applications with a safe closing callback as described in this demo.
safe_closing = True

# ---------

busy_processing = False
close_requested = False

def close_window():
    global close_requested
    close_requested = True
    print("User requested close at:", time.time(), "Was busy processing:", busy_processing)

root = Tk()
if safe_closing:
    root.protocol("WM_DELETE_WINDOW", close_window)
lbl = Label(root)
lbl.pack()

def periodic_call():
    global busy_processing

    if not close_requested:
        busy_processing = True
        for i in range(10):
            print((i+1), "of 10")
            time.sleep(0.2)
            lbl["text"] = str(time.time()) # Will error if force-closed.
            root.update() # Force redrawing since we change label multiple times in a row.
        busy_processing = False
        root.after(500, periodic_call)
    else:
        print("Destroying GUI at:", time.time())
        try: # "destroy()" can throw, so you should wrap it like this.
            root.destroy()
        except:
            # NOTE: In most code, you'll wanna force a close here via
            # "exit" if the window failed to destroy. Just ensure that
            # you have no code after your `mainloop()` call (at the
            # bottom of this file), since the exit call will cause the
            # process to terminate immediately without running any more
            # code. Of course, you should NEVER have code after your
            # `mainloop()` call in well-designed code anyway...
            # exit(0)
            pass

root.after_idle(periodic_call)
root.mainloop()

यह कोड आपको दिखाएगा कि WM_DELETE_WINDOWहैंडलर चलता है, जबकि हमारा रिवाज periodic_call()काम / छोरों के बीच में व्यस्त है!

हम कुछ अतिरंजित .after()मूल्यों का उपयोग करते हैं: 500 मिलीसेकंड। यह सिर्फ आपके लिए बहुत आसान बनाने के लिए है, जो कि समय-समय पर व्यस्त रहने के दौरान बंद होने के बीच के अंतर को देखने के लिए है, या नहीं ... यदि आप संख्याओं को अपडेट करते समय बंद करते हैं, तो आप देखेंगे कि आपके आवधिक कॉल के दौरान क्याWM_DELETE_WINDOW हुआ था " व्यस्त प्रसंस्करण: सच है ”। यदि आप संख्याओं को रोकते हैं, तो (जिसका अर्थ है कि आवधिक कॉलबैक उस समय संसाधित नहीं हो रहा है), आप देखते हैं कि यह "व्यस्त नहीं" होने के दौरान हुआ।

वास्तविक दुनिया के उपयोग में, आपका .after()कोई व्यक्ति 30-100 मिलीसेकंड का उपयोग करेगा, जिसके पास एक उत्तरदायी GUI होगा। यह केवल आपको प्रदर्शन को समझने में मदद करने के लिए एक प्रदर्शन है, जो कि Tk के डिफ़ॉल्ट के खिलाफ खुद को बचाने के लिए "व्यवहार बंद करते समय सभी कार्यों को तुरंत बाधित करता है।"

संक्षेप में: WM_DELETE_WINDOWहैंडलर को एक ध्वज सेट करें , और फिर उस ध्वज को समय-समय पर और मैन्युअल रूप .destroy()से खिड़की की जांच करें जब वह सुरक्षित हो (जब आपका ऐप सभी काम के साथ हो)।

पुनश्च: आप उपयोगकर्ता WM_DELETE_WINDOWसे पूछने के लिए भी उपयोग कर सकते हैं कि क्या वे वास्तव में खिड़की बंद करना चाहते हैं; और यदि वे उत्तर नहीं देते हैं, तो आप ध्वज को सेट नहीं करते हैं। यह बहुत सरल है। आप बस अपने में एक संदेश बॉक्स दिखाते हैं WM_DELETE_WINDOWऔर उपयोगकर्ता के उत्तर के आधार पर ध्वज सेट करते हैं।


1

सरल संस्करण का प्रयास करें:

import tkinter

window = Tk()

closebutton = Button(window, text='X', command=window.destroy)
closebutton.pack()

window.mainloop()

या यदि आप अधिक कमांड जोड़ना चाहते हैं:

import tkinter

window = Tk()


def close():
    window.destroy()
    #More Functions


closebutton = Button(window, text='X', command=close)
closebutton.pack()

window.mainloop()

प्रश्न विंडो को बंद करने के लिए ओएस के एक्स बटन के बारे में है, न कि एक नियमित बटन नियंत्रण के लिए।
user1318499

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.