简体   繁体   中英

Python threading timer issue

I have been having problems with trying to create a timer program using python. I want it so that a user can enter an amount of time for the timer to count down from and update every 0.1 seconds or so. So far I have this code:

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()

This seems to sort of work for a bit, but it sometimes returns this:

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

I do not know what causes this, and I need a bit of help. Could someone help me find a way to stop this from happening?

Your program is crashing because it is invoking the GTK API from different threads, which is forbidden . Fortunately, it is quite easy to modify it to work correctly - you can use threads, you just need to make sure that all GTK calls are done from the GUI thread, ie the thread that runs the main loop. The simplest way to do that is by having your worker threads not execute GUI calls directly, but dispatch them to the main thread using GObject.idle_add . Thus, instead of calling self.update from the timer, call a new method schedule_update , which arranges for the actual update to be called from the GUI thread. Since the GUI thread is not blocked, the update will in effect run immediately:

  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)

(You will of course need to also import GObject from gi.repository .)

Needless to say, this kind of scheduling is better implemented with GTK main loop timeouts (see GObject.timeout_add ), which don't require any additional scheduling because they execute the callback within the GUI thread in the first place. But there are legitimate situations where use of threads is appropriate - eg to invoke long-running synchronous APIs such as database access without freezing the GUI, or to perform calculations that internally release the GIL.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM