Tkinter के ईवेंट लूप के साथ आप अपना कोड कैसे चलाते हैं?


119

मेरा छोटा भाई सिर्फ प्रोग्रामिंग में शामिल हो रहा है, और अपने साइंस फेयर प्रोजेक्ट के लिए, वह आकाश में पक्षियों के झुंड का अनुकरण कर रहा है। उसने अपने लिखे हुए अधिकांश कोड प्राप्त कर लिए हैं, और यह अच्छी तरह से काम करता है, लेकिन पक्षियों को हर पल हिलना पड़ता है ।

Tkinter, हालांकि, अपने स्वयं के ईवेंट लूप के लिए समय हॉग करता है, और इसलिए उसका कोड नहीं चलेगा। ऐसा करने से root.mainloop()रन, रन, और चलता रहे, और केवल एक चीज है इसे चलाता ईवेंट हैंडलर्स है।

क्या मेनलोप के साथ उसका कोड रन करने का कोई तरीका है (मल्टीथ्रेडिंग के बिना, यह भ्रमित है और इसे सरल रखा जाना चाहिए), और यदि हां, तो यह क्या है?

अभी, वह एक बदसूरत हैक के साथ आया था, अपने move()कार्य को बांधने के लिए <b1-motion>, ताकि जब तक वह बटन दबाए रखता है और माउस को विग करता है, तब तक यह काम करता है। लेकिन एक बेहतर तरीका है।

जवाबों:


141

ऑब्जेक्ट afterपर विधि का उपयोग करें Tk:

from tkinter import *

root = Tk()

def task():
    print("hello")
    root.after(2000, task)  # reschedule event in 2 seconds

root.after(2000, task)
root.mainloop()

यहाँ afterविधि के लिए घोषणा और प्रलेखन है :

def after(self, ms, func=None, *args):
    """Call function once after given time.

    MS specifies the time in milliseconds. FUNC gives the
    function which shall be called. Additional parameters
    are given as parameters to the function call.  Return
    identifier to cancel scheduling with after_cancel."""

30
यदि आप टाइमआउट को 0 होने के लिए निर्दिष्ट करते हैं, तो कार्य समाप्त होने के तुरंत बाद इवेंट लूप पर वापस रख दिया जाएगा। यह अन्य घटनाओं को अवरुद्ध नहीं करेगा, जबकि अभी भी जितनी बार हो सके अपना कोड चला रहे हैं।
नाथन

[X] बटन को win32gui.FindWindow (कोई नहीं, 'विंडो टाइटल') के साथ, ठीक से और अच्छी तरह से बंद करने के लिए opencv और tkinter प्राप्त करने की कोशिश करने के लिए घंटों तक मेरे बालों को खींचने की कोशिश करने के बाद, इसने (खिड़की का शीर्षक ') नहीं किया! मैं इस तरह के एक noob हूं ;-)
JxAxMxIxN

यह सबसे अच्छा विकल्प नहीं है; हालाँकि यह इस मामले में काम करता है, यह ज्यादातर स्क्रिप्ट्स के लिए अच्छा नहीं है (यह केवल हर 2 सेकंड में चलता है), और टाइमआउट सेट करने के लिए 0 सेट किया गया है, @Nathan द्वारा पोस्ट किए गए सुझाव के अनुसार क्योंकि यह केवल तब चलता है जब tkinter व्यस्त नहीं है (जो हो सकता है) कुछ जटिल कार्यक्रमों में समस्याओं का कारण)। threadingमॉड्यूल के साथ छड़ी करने के लिए सबसे अच्छा है ।
अनाम

59

समाधान ब्योर्न द्वारा पोस्ट की गई परिणाम एक में "RuntimeError: अलग अपार्टमेंट से Tcl कॉलिंग" संदेश अपने कंप्यूटर पर (RedHat Enterprise 5, अजगर 2.6.1)। ब्योर्न को यह संदेश नहीं मिला होगा, क्योंकि, एक जगह मैंने जाँच की थी , टिंकर के साथ गलत व्यवहार करना अप्रत्याशित और मंच पर निर्भर है।

समस्या यह है कि लगता है app.start() Tk के संदर्भ के रूप में गिना जाता है, क्योंकि ऐप में Tk तत्व शामिल हैं। मैं बदल कर भी इस तय app.start()एक साथ self.start()अंदर __init__। मैंने इसे भी बनाया ताकि सभी Tk संदर्भ या तो फ़ंक्शन केmainloop() अंदर हैं जो कॉल करते हैं या फ़ंक्शन के अंदर होते हैं जिन्हें फ़ंक्शन द्वारा कॉल किया जाता हैmainloop() ("अलग-अलग अपार्टमेंट" त्रुटि से बचने के लिए यह स्पष्ट रूप से महत्वपूर्ण है)।

अंत में, मैंने एक कॉलबैक के साथ एक प्रोटोकॉल हैंडलर जोड़ा, क्योंकि इसके बिना प्रोग्राम उस समय त्रुटि से बाहर निकलता है जब उपयोगकर्ता द्वारा Tk विंडो बंद की जाती है।

संशोधित कोड इस प्रकार है:

# Run tkinter code in another thread

import tkinter as tk
import threading

class App(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)
        self.start()

    def callback(self):
        self.root.quit()

    def run(self):
        self.root = tk.Tk()
        self.root.protocol("WM_DELETE_WINDOW", self.callback)

        label = tk.Label(self.root, text="Hello World")
        label.pack()

        self.root.mainloop()


app = App()
print('Now we can continue running code while mainloop runs!')

for i in range(100000):
    print(i)

आप runविधि में तर्क कैसे पारित करेंगे ? मैं यह पता नहीं लगा सकता कि कैसे ...
TheDoctor

5
आमतौर पर आप तर्कों को पारित करते हैं __init__(..), उन्हें स्टोर करते हैं selfऔर उनका उपयोग करते हैंrun(..)
आंद्रे होल्जनर

1
रूट चेतावनी देते हुए बिल्कुल नहीं दिखाता: `चेतावनी: NSWindow ड्रैग रीजन को केवल मुख्य थ्रेड पर अमान्य किया जाना चाहिए! यह भविष्य में `एक अपवाद फेंक देते हैं
बॉब Bobster

1
यह टिप्पणी बहुत अधिक मान्यता की हकदार है। गजब का।
डैनियल रेहानियान

यह एक जीवन रक्षक है। जीयूआई के बाहर कोड को जिंक से बाहर निकलने के लिए अजगर की स्क्रिप्ट से बाहर निकलने में सक्षम नहीं होने पर जीवित रहने के लिए टिंकर के धागे की जांच होनी चाहिए। कुछ इस तरहwhile app.is_alive(): etc
m3nda

21

जब आप अपना लूप लिखते हैं, जैसा कि अनुकार (मुझे लगता है) में, आपको updateफ़ंक्शन को कॉल करने की ज़रूरत है जो करता mainloopहै: आपके परिवर्तनों के साथ विंडो को अपडेट करता है, लेकिन आप इसे अपने लूप में करते हैं।

def task():
   # do something
   root.update()

while 1:
   task()  

10
आप होना चाहिए बहुत प्रोग्रामिंग इस तरह से सावधान। यदि किसी भी घटना का कारण बनता taskहै, तो आप नेस्टेड इवेंट लूप के साथ समाप्त हो जाएंगे, और यह बुरा है। जब तक आप पूरी तरह से नहीं समझ पाते कि इवेंट लूप कैसे काम करते हैं, आपको updateहर कीमत पर कॉल करने से बचना चाहिए ।
ब्रायन ओकले

मैंने एक बार इस तकनीक का उपयोग किया था - ठीक काम करता है लेकिन आप इसे कैसे करते हैं, इसके आधार पर, आपके पास UI में कुछ चौंका देने वाला हो सकता है।
jldupont

@ ब्रायन ओकली क्या एक लूप को अपडेट किया जाता है? और यह कैसे समस्याग्रस्त होगा?
ग्रीन ०५

6

एक अन्य विकल्प यह है कि टिंकर को एक अलग थ्रेड पर निष्पादित किया जाए। इसे करने का एक तरीका इस प्रकार है:

import Tkinter
import threading

class MyTkApp(threading.Thread):
    def __init__(self):
        self.root=Tkinter.Tk()
        self.s = Tkinter.StringVar()
        self.s.set('Foo')
        l = Tkinter.Label(self.root,textvariable=self.s)
        l.pack()
        threading.Thread.__init__(self)

    def run(self):
        self.root.mainloop()


app = MyTkApp()
app.start()

# Now the app should be running and the value shown on the label
# can be changed by changing the member variable s.
# Like this:
# app.s.set('Bar')

हालांकि, सावधान रहें, मल्टीथ्रेडेड प्रोग्रामिंग कठिन है और पैर में अपने आप को शूट करना वास्तव में आसान है। उदाहरण के लिए आपको सावधान रहना होगा जब आप ऊपर के नमूना वर्ग के सदस्य चर को बदलते हैं ताकि आप टिंकर के ईवेंट लूप से बाधित न हों।


3
यकीन नहीं होता कि यह काम कर सकता है। बस कुछ इसी तरह की कोशिश की और मुझे "RuntimeError: मुख्य धागा मुख्य लूप में नहीं है"।
जुलडूपोंट

5
jldupont: मुझे "RuntimeError: कॉलिंग Tcl फ्रॉम डिफरेंट एपबॉक्स" (संभवतः एक अलग संस्करण में एक ही त्रुटि)। फिक्स को Tk को रन () में इनिशियलाइज़ करना था, __init __ () में नहीं। इसका मतलब है कि आप उसी धागे में Tk को इनिशियलाइज़ कर रहे हैं जिस तरह से आप mainloop () में कहते हैं।
mgiuca

2

यह एक जीपीएस रीडर और डेटा प्रस्तोता क्या होगा का पहला कार्यशील संस्करण है। जिस तरह से बहुत कम त्रुटि संदेश के साथ tkinter एक बहुत ही नाजुक चीज है। यह सामान नहीं डालता है और यह नहीं बताता है कि समय क्यों है। एक अच्छे WYSIWYG फॉर्म डेवलपर से आने में बहुत मुश्किल होती है। वैसे भी, यह एक सेकंड में 10 बार एक छोटी दिनचर्या चलाता है और एक फॉर्म पर जानकारी प्रस्तुत करता है। इसे होने में कुछ समय लगा। जब मैंने 0 का टाइमर मान आजमाया, तो फॉर्म कभी नहीं आया। मेरा सर अब दर्द करता है! 10 या अधिक बार प्रति सेकंड मेरे लिए काफी अच्छा है। मुझे उम्मीद है कि यह किसी और की मदद करता है। माइक मोरो

import tkinter as tk
import time

def GetDateTime():
  # Get current date and time in ISO8601
  # https://en.wikipedia.org/wiki/ISO_8601 
  # https://xkcd.com/1179/
  return (time.strftime("%Y%m%d", time.gmtime()),
          time.strftime("%H%M%S", time.gmtime()),
          time.strftime("%Y%m%d", time.localtime()),
          time.strftime("%H%M%S", time.localtime()))

class Application(tk.Frame):

  def __init__(self, master):

    fontsize = 12
    textwidth = 9

    tk.Frame.__init__(self, master)
    self.pack()

    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             text='Local Time').grid(row=0, column=0)
    self.LocalDate = tk.StringVar()
    self.LocalDate.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             textvariable=self.LocalDate).grid(row=0, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             text='Local Date').grid(row=1, column=0)
    self.LocalTime = tk.StringVar()
    self.LocalTime.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             textvariable=self.LocalTime).grid(row=1, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             text='GMT Time').grid(row=2, column=0)
    self.nowGdate = tk.StringVar()
    self.nowGdate.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             textvariable=self.nowGdate).grid(row=2, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             text='GMT Date').grid(row=3, column=0)
    self.nowGtime = tk.StringVar()
    self.nowGtime.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             textvariable=self.nowGtime).grid(row=3, column=1)

    tk.Button(self, text='Exit', width = 10, bg = '#FF8080', command=root.destroy).grid(row=4, columnspan=2)

    self.gettime()
  pass

  def gettime(self):
    gdt, gtm, ldt, ltm = GetDateTime()
    gdt = gdt[0:4] + '/' + gdt[4:6] + '/' + gdt[6:8]
    gtm = gtm[0:2] + ':' + gtm[2:4] + ':' + gtm[4:6] + ' Z'  
    ldt = ldt[0:4] + '/' + ldt[4:6] + '/' + ldt[6:8]
    ltm = ltm[0:2] + ':' + ltm[2:4] + ':' + ltm[4:6]  
    self.nowGtime.set(gdt)
    self.nowGdate.set(gtm)
    self.LocalTime.set(ldt)
    self.LocalDate.set(ltm)

    self.after(100, self.gettime)
   #print (ltm)  # Prove it is running this and the external code, too.
  pass

root = tk.Tk()
root.wm_title('Temp Converter')
app = Application(master=root)

w = 200 # width for the Tk root
h = 125 # height for the Tk root

# get display screen width and height
ws = root.winfo_screenwidth()  # width of the screen
hs = root.winfo_screenheight() # height of the screen

# calculate x and y coordinates for positioning the Tk root window

#centered
#x = (ws/2) - (w/2)
#y = (hs/2) - (h/2)

#right bottom corner (misfires in Win10 putting it too low. OK in Ubuntu)
x = ws - w
y = hs - h - 35  # -35 fixes it, more or less, for Win10

#set the dimensions of the screen and where it is placed
root.geometry('%dx%d+%d+%d' % (w, h, x, y))

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