My friend Ranganath had some problem with implementing a stopwatch in Java. I told him it would be a piece of cake in python and I would do it in a hour or so . The only thing I do was to call a callback function , whenever I clicked the start button which would set the text in the entry to the time required . This was my code for the call – back function when I clicked on the toggle button.
def start(self , widget): if widget.get_active(): widget.set_label('Stop') self.s = time.time() while 1: time.sleep(0.5) self.update() else: self.entry.set_text(time.time() - self.s) widget.set_label('Start') def update(self): self.entry.set_text(time.time() - self.s)
But the program was hanging , and giving me an unresponsive window.
Then my senior-cum-friend Jay asked me to look at multithreading in gtk , for modifying text , label or entry in a gtk window . So I read a bit of multi – threading in python . This is an example of a simple thread in python.
import threading , time def foo_fun(name , sleeptime): print 'Thread %s is running' % (str(i)) time.sleep(sleeptime) if __name__ == '__main__': for i in range(5): threading.Thread(target = foo_fun , args = (i , 1)
A major feature of multithreading is that it once a string is set up , it does not wait for that to end, the other string is simulaneously set up and run. This increases speed and can sometimes lead to varied output. My understanding of this is based on the answers given in the question asked on stack.
Now coming back to the stopwatch question , two major things should be done according to the blog – link Jay gave me , first one is that gtk Threads are initialized in this way gtk.gdk.threads_init() , and whenever something is modified in the window , gobject.idle_add() should be called.
This is my code for the stopwatch program. First the main window
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.set_title('Stopwatch') self.window.set_size_request(200,200) self.table = gtk.Table(10,20,True) self.entry = gtk.Entry() self.entry.set_editable(False) self.table.attach(self.entry , 0 , 20 , 2 , 4) self.button = gtk.ToggleButton(label = 'Start') self.button.connect('toggled' , self.count) self.table.attach(self.button , 6 , 14 , 6 ,10) self.image = gtk.Image() self.image.set_from_file('stop.jpg') self.table.attach(self.image , 0 ,20, 0 ,10) self.window.add(self.table) self.window.connect('delete-event' , gtk.main_quit) self.window.show_all()
Nothing new about this except the fact that Ive used a toggle button , and used an image that I downloaded from
Google as background.
Rest of my code
def count(self,widget): if widget.get_active(): self.s = time.time() threading.Thread(target = self.ent_change).start() widget.set_label('Stop') else: widget.set_label('Start') def ent_change(self): while 1: time.sleep(0.01) gobject.idle_add(self.change) if self.button.get_active() == 0: break def change(self): show = time.time() - self.s hour = show / 3600 minutes = (show % 3600) / 60 seconds = show - (int(hour) * 3600) - (int(minutes) * 60) string = ' %s : %s : %0.2f ' % (str(int(hour)),str(int(minutes)),seconds) self.entry.set_text(string)
Looking at each function one by one
Once the toggle button is toggled with , it calls the function count. If the widget is already on , at the time
of toggling , it changes the label to Stop and records time.time() in a variable that can be accessed throughout
and it starts a thread which calls the function ent_change. If the widget is off , it just changes the button l
abel to ‘Stop’
This runs an infinite loop which calls upon the function change using gobject.idle_add() every 0.01 seconds. Ev
erytime it checks if the button is active or not. If it isnt it terminates the loop , while breaking the thread.
It just updates the text in the entry box every 0.01 seconds
This is a screenshot showing my running stopwatch.