2011-08-21 14 views
10

मैं पृष्ठभूमि धागे और एक जीयूआई आवेदन के बीच संदेशों का आदान-प्रदान करने के लिए एक कतार का उपयोग कर रहा हूं। वर्तमान में, यह हर समय और फिर एक क्वेरी विधि को कॉल करके किया जाता है।टिंकर: कतार में आइटम के लिए प्रतीक्षा करें

def read_queue(self): 
    try: 
     self.process(self.queue.get(False)) # non-blocking 
    except Queue.Empty: 
     pass 
    finally: 
     self.after(UPDATE_TIME, self.read_queue) 

इस दृष्टिकोण के साथ समस्या यह है कि अगर UPDATE_TIME बहुत बड़ा है, आवेदन संभव तुलना में धीमी नए आइटम संसाधित करेंगे। यदि यह बहुत छोटा है, तो अधिकांश समय कतार की जांच कर खर्च करता है हालांकि यह इस दौरान अन्य सामान कर सकता है।

क्या कोई नया आइटम कतार में आने पर read_queue विधि स्वचालित रूप से ट्रिगर करने का कोई तरीका है? (मैं निश्चित रूप से टी पर एक विधि जब पृष्ठभूमि धागा कतार भरता है कह सकते हैं लेकिन मुझे डर है कि यह मुझे कुछ संगामिति मुद्दों देता है - इसलिए मैं कतारों सब के बाद उपयोग कर रहा हूँ।)

+2

जाहिर है आप अपने जीयूआई में वर्चुअल इवेंट को फायर करने के लिए पृष्ठभूमि थ्रेड से event_generate का उपयोग कर सकते हैं। शायद यह आपकी कतार की स्थिति के बारे में एक तरह की अधिसूचना के रूप में इस्तेमाल किया जा सकता है। http://groups.google.com/group/comp.lang.python/browse_thread/thread/3476fd30bec12367/853bb6f6dd216960?lnk=gst&q=brunel+%2Bevent_generate#853bb6f6dd216960 –

+0

काम करने लगता है। इसे वास्तविक उत्तर के रूप में जोड़ने के लिए स्वतंत्र महसूस करें। – Debilski

उत्तर

5

सारांश: मैं "noob oddy's example code" का उपयोग नहीं करता - एक मूलभूत रूप से त्रुटिपूर्ण दृष्टिकोण है।

मैं एक अजगर गुरु नहीं हूं, लेकिन "noob oddy" द्वारा प्रदान किया गया उदाहरण कोड (जो पृष्ठभूमि थ्रेड के भीतर root.event_generate (...) को कॉल करता है) एक "मौलिक रूप से त्रुटिपूर्ण दृष्टिकोण" प्रतीत होता है। यानी, इंटरनेट पर कई लेख हैं जो "जीयूआई थ्रेड" (जो आमतौर पर मुख्य धागा है) के संदर्भ में टिंकर कार्यों/ऑब्जेक्ट विधियों को कभी भी नहीं बुलाते हैं। उनका उदाहरण "अधिकांश समय" काम करता है, लेकिन यदि आप घटना उत्पादन दर में वृद्धि करते हैं, तो उदाहरण की "क्रैश दर" बढ़ेगी - हालांकि, विशिष्ट व्यवहार घटना उत्पादन दर और प्लेटफॉर्म की प्रदर्शन विशेषताओं पर निर्भर है।

उदाहरण के लिए

,, अजगर 2.7.3 के साथ अपने कोड का उपयोग करता है, तो आप बदलना:

 time.sleep(1) 

रहे हैं:

 time.sleep(0.01) 

तो स्क्रिप्ट/एप्लिकेशन आम तौर पर की 'एक्स' संख्या के बाद दुर्घटना होगा पुनरावृत्तियों।

अधिक खोज के बाद, यदि आपको "टिंकर का उपयोग करना चाहिए", तो यह पृष्ठभूमि थ्रेड से जीयूआई थ्रेड से जानकारी प्राप्त करने का सबसे अधिक "बुलेट प्रूफ विधि" दिखाई देता है, जो कि मतदान के बाद 'बाद()' विजेट विधि का उपयोग करना है थ्रेड-सुरक्षित ऑब्जेक्ट (जैसे 'कतार')। उदाहरण के लिए,

################################################################################ 
import threading 
import time 
import Queue 
import Tkinter  as Tk 
import Tkconstants as TkConst 
from ScrolledText import ScrolledText 
from tkFont  import Font 

global top 
global dataQ 
global scrText 

def thread_proc(): 
    x = -1 
    dataQ.put(x) 
    x = 0 
    for i in xrange(5): 
     for j in xrange(20): 
      dataQ.put(x) 
      time.sleep(0.1) 
      x += 1 
     time.sleep(0.5) 
    dataQ.put(x) 

def on_after_elapsed(): 
    while True: 
     try: 
      v = dataQ.get(timeout=0.1) 
     except: 
      break 
     scrText.insert(TkConst.END, "value=%d\n" % v) 
     scrText.see(TkConst.END) 
     scrText.update() 
    top.after(100, on_after_elapsed) 

top  = Tk.Tk() 
dataQ = Queue.Queue(maxsize=0) 
f  = Font(family='Courier New', size=12) 
scrText = ScrolledText(master=top, height=20, width=120, font=f) 
scrText.pack(fill=TkConst.BOTH, side=TkConst.LEFT, padx=15, pady=15, expand=True) 
th = threading.Thread(target=thread_proc) 
th.start() 
top.after(100, on_after_elapsed) 
top.mainloop() 
th.join() 
## end of file ################################################################# 
+0

स्पष्टीकरण के लिए धन्यवाद। मैं भूल गया था कि मैंने इस सवाल से पूछा था। मेरे कोड में मैं अंततः 'बाद' का उपयोग करके एक ही समाधान पर बस गया (और प्रतीक्षा समय को ठीक करने के लिए कुछ जीयूआई तत्व जोड़ना - प्रदर्शन ओएस के आधार पर थोड़ा अलग था) और टीई के आंतरिक घटना प्रबंधक के साथ गड़बड़ नहीं कर रहा था । – Debilski

+1

इस कोड में एक बग है। 'Thread_proc' में आप' data' परिभाषित किए बिना 'dataQ.put (v)' करते हैं। –

+1

आपको सत्य को सही से हटा देना चाहिए, अगर किसी सरल के साथ प्रयास/छोड़कर/ब्रेक को प्रतिस्थापित करें, और अनब्लॉकिंग के साथ get (timeout = 0.1) को प्रतिस्थापित करें: वही परिणाम, स्पष्ट (इसलिए, अधिक मजबूत) कोड। (और आपका ऐप आपकी कतार के लिए हर 100ms 100ms मुक्त नहीं करेगा) –

13

एक विकल्प http://tkinter.unpythonic.net/wiki/mtTkinter

mtTkinter हो सकता है

##The only secure way I found to make Tkinter mix with threads is to never 
##issue commands altering the graphical state of the application in another 
##thread than the one where the mainloop was started. Not doing that often 
##leads to random behaviour such as the one you have here. Fortunately, one 
##of the commands that seems to work in secondary threads is event_generate, 
##giving you a means to communicate between threads. If you have to pass 
##information from one thread to another, you can use a Queue. 
## 
##This obviously complicates things a bit, but it may work far better. 
##Please note that the 'when' option *must* be specified in the call to 
##event_generate and *must not* be 'now'. If it's not specified or if it's 
##'now', Tkinter may directly execute the binding in the secondary thread's 
##context. (Eric Brunel) 

import threading 
import time 
import Queue 
from Tkinter import * 

## Create main window 
root = Tk() 

## Communication queue 
commQueue = Queue.Queue() 

## Function run in thread 
def timeThread(): 
    curTime = 0 
    while 1: 
     ## Each time the time increases, put the new value in the queue... 
     commQueue.put(curTime) 
     ## ... and generate a custom event on the main window 
     try: 
      root.event_generate('<<TimeChanged>>', when='tail') 
     ## If it failed, the window has been destoyed: over 
     except TclError: 
      break 
     ## Next 
     time.sleep(1) 
     curTime += 1 

## In the main thread, do usual stuff 
timeVar = IntVar() 
Label(root, textvariable=timeVar, width=8).pack() 

## Use a binding on the custom event to get the new time value 
## and change the variable to update the display 
def timeChanged(event): 
    timeVar.set(commQueue.get()) 

root.bind('<<TimeChanged>>', timeChanged) 

## Run the thread and the GUI main loop 
th=threading.Thread(target=timeThread) 
th.start() 

root.mainloop() 

वहाँ भी है एक समान तरीके से after_idle का उपयोग करने का उल्लेख:

यहाँ एक पृष्ठभूमि धागे से event_generate का उपयोग करने का एक और उदाहरण है।
यानी। root.after_idle (timeChanged)

+1

'gener_event' का उपयोग करना अवधारणात्मक रूप से अधिक आकर्षक है, और @noob oddy द्वारा दो उदाहरण काम कर रहे हैं। नेटवर्क के आधार पर डेटा पुनर्प्राप्त करने के लिए रीयलटाइम प्लॉट बनाने के लिए उन्हें आधार के रूप में उपयोग करना मैंने एक matplotlib आंकड़ा एम्बेड किया। यह लिनक्स में ठीक से काम करता है लेकिन विंडोज़ नहीं (एक्सपी, 7,8.1 सभी एक समान तरीके से व्यवहार करते हैं)। प्रोग्राम शुरू होने पर इवेंट_ जेनरेट कॉल के विस्फोट से संबंधित लगता है। इसे पहले से जमा किए गए सभी डेटा का इंतजार करके इंतजार किया जा सकता है और फिर एक ही घटना उत्पन्न कर रहा है। लेकिन त्रुटि संदेशों ने मुझे विश्वास दिलाया है कि 'event_generate' ** विंडोज़ में थ्रेडसेफ नहीं है ** – NameOfTheRose

+0

** mtTkinter ** विंडोज़ मुद्दों को हल करता है (80,000 घटनाओं के फटने से बचने में कामयाब रहा)। यह अप्रत्यक्ष तरीके से पुष्टि करता है कि यह एक थ्रेडिंग समस्या है। ** mtTkinter **, दृश्य के पीछे, 'बाद' विधि का उपयोग करता है, इसलिए मुझे इसके उपयोग में बहुत अधिक बिंदु दिखाई नहीं देता है। – NameOfTheRose

+0

मुझे लगता है कि टिंकटर को मेरे विंडोज पायथन इंस्टॉलेशन में थ्रेड सपोर्ट के बिना संकलित किया गया है, जबकि लिनक्स इंस्टॉलेशन को थ्रेड सपोर्ट के साथ संकलित किया गया है (कम से कम यही है कि mtTkinter 'root.globalgetvar (' tcl_platform (threaded) का उपयोग करके रिपोर्ट करता है। ') ')। यह शायद व्यवहार में अंतर का कारण है। – NameOfTheRose

2

मतदान os.pipe का उपयोग कर दो धागे के बीच सिंक्रनाइज़ करने के लिए द्वारा केन MumME समाधान से समाप्त किया जा सकता।

tkinter में एक createFilehandler विधि है जिसका उपयोग टीके के चयन लूप में फ़ाइल डिस्क्रिप्टर जोड़ने के लिए किया जा सकता है। फिर आप संकेत दे सकते हैं कि पाइप में एक बाइट लिखकर कतार में कुछ तैयार है।

समाधान इस तरह दिखता है:

import Queue 
import os 

uiThreadQueue = Queue.Queue() ; 

pipe_read, pipe_write = os.pipe() ; 

# call one function from the queue. Triggered by the 
# pipe becoming readable through root.tk.createfilehandler(). 
def serviceQueue(file, mask): 
    os.read(pipe_read, 1) 
    func = uiThreadQueue.get() ; 
    func() 

# enqueue a function to be run in the tkinter UI thread. 
# best used as inUIThread(lambda: self.callSomeFunction(arg1,arg2,arg3)) 
def inUIThread(f): 
    uiThreadQueue.put(f) 
    os.write(pipe_write, "x") 

... set up your widgets, start your threads, etc..... 


root.tk.createfilehandler(pipe_read, tkinter.READABLE, serviceQueue) 
root.mainloop() 

मैं एक अजगर विशेषज्ञ नहीं हूँ, क्षमा करें अगर मैंने किसी भी कोडिंग सम्मेलन में गड़बड़ कर ली है। मैं पाइप के साथ बहुत अच्छा हूं, हालांकि :)

+2

एफवाईआई विंडोज पर कोई 'createfilehandler() 'समर्थन नहीं है, एक प्रदूषित कतार सबसे अच्छा है जो आप कर सकते हैं। – shuckc

संबंधित मुद्दे