2015-02-24 31 views
4

我一直在嘗試使用python創建一個計時器程序的問題。我希望它能夠讓用戶輸入一段時間讓計時器從每隔0.1秒鐘左右開始倒計時並更新。到目前爲止,我有這樣的代碼:Python線程計時器問題

from gi.repository import Gtk 
import time 
import threading 

class TimerWindow(Gtk.Window): 
    def __init__(self): 
    Gtk.Window.__init__(self, title = "Timer") 

    self.box = Gtk.Box(spacing = 1) 
    self.add(self.box) 

    self.entry = Gtk.Entry() 
    self.entry.connect("activate", self.start, self.entry) 
    self.box.pack_start(self.entry, True, True, 0) 

    def start(self, widget, entry): 
    starttime = time.time() 
    totaltime = float(entry.get_text()) 
    self.update(starttime, totaltime, entry) 

    def update(self, starttime, totaltime, entry): 
    entry.set_text(str(totaltime - (time.time() - starttime))) 
    if float(entry.get_text()) > 0: 
     t = threading.Timer(0.1, self.update, [starttime, totaltime, entry]) 
     t.start() 

win = TimerWindow() 
win.connect("delete-event", Gtk.main_quit) 
win.set_keep_above(True) 
win.show_all() 
Gtk.main() 

這似乎排序的一些工作,但它有時會返回此:

timer.py:31: Warning: g_object_ref: assertion 'object->ref_count > 0' failed 
    Gtk.main() 
Segmentation fault 

我不知道是什麼原因造成這一點,我需要一點點的幫助。有人能幫我找到一種方法來阻止這種情況的發生嗎?

回答

2

你的程序崩潰了,因爲它是從不同線程調用GTK API的,這是forbidden。幸運的是,修改它可以很容易地正常工作 - 您可以使用線程,您只需確保所有GTK調用都是從GUI線程(即運行主循環的線程)完成的。最簡單的方法是讓工作線程不直接執行GUI調用,而是使用GObject.idle_add將它們分派到主線程。因此,調用一個新的方法schedule_update,而不是從定時器調用self.update,它將安排從GUI線程調用實際的update。由於GUI線程沒有被阻塞,更新將立即生效運行:

def update(self, starttime, totaltime, entry): 
    entry.set_text(str(totaltime - (time.time() - starttime))) 
    if float(entry.get_text()) > 0: 
     t = threading.Timer(0.1, self.schedule_update, [starttime, totaltime, entry]) 
     t.start() 

    def schedule_update(self, *args): 
     GObject.idle_add(self.update, *args) 

(你當然還需要從gi.repository進口GObject

不用說,這種安排是更好地使用GTK主循環超時實現(請參閱GObject.timeout_add),它們不需要任何額外的調度,因爲它們首先在GUI線程內執行回調。但是有合適的情況使用線程是適當的 - 例如在不凍結GUI的情況下調用長時間運行的同步API(如數據庫訪問),或執行內部釋放GIL的計算。

+0

'GObject.timeout_add'方式效果很好。使用線程時,即使在添加schedule_update之後,我仍然會收到與以前相同的錯誤。我可能做錯了什麼,但畢竟,我對python線程和東西很新,所以不管。 – 2015-02-25 00:32:09

+0

等一下,沒關係。我很蠢。我沒有看到線程開始應該運行schedule_update而不是更新。這個答案是100%正確的。 – 2015-02-25 01:44:17

+0

@KZhang如果'GObject.timeout_add'符合你的目的,隨意忘記線程(爲此目的),因爲它們引入了許多複雜性和問題。一旦你獲得了Python和GTK的更多經驗,你將能夠在必要時安全地使用線程。 – user4815162342 2015-02-25 07:57:06