Csongor Csongor - 2 months ago 13
Python Question

How to update drawing area in GTK

I'm trying to make a clock using GTK in python 2. The version I have at this point is just a basic one, it will be much more complicated, however I need to make it refresh first. In the end it will be a screensaver, hopefully.

I have tried to understand how queue_draw() works, but I can't figure it out. I'm new to GTK and not experienced in using classes (although I'm trying). These two are causing my problem I guess.
Can someone help me with this part please :D
I know there are posts about this issue, still I couldn't figure out how to make this working :(
Thank you

I'm using Ubuntu 14.04, python 2.

Here's the code I have at the moment:

#!/usr/bin/env python
import time
import gtk
import math

class Base:
def destroy(self, widget, data=None):
gtk.main_quit()

def close(self, widget):
self.window.destroy()

def rotate(self, center, point, angle):
#http://stackoverflow.com/questions/12161277/how-to-rotate-a-vertex-around-a-certain-point
px = point[0]
py = point[1]
rx = center[0] + (px - center[0]) * math.cos(math.radians(angle)) - (py - center[1]) * math.sin(math.radians(angle))
ry = center[1] + (px - center[0]) * math.sin(math.radians(angle)) + (py - center[1]) * math.cos(math.radians(angle))
return(rx, ry)

# This function will be called whenever the drawing area is exposed:
def expose_handler(self, widget, event) :
w, h = widget.window.get_size()
center = [int(w / 2), int(h / 2)]
xgc = widget.window.new_gc()
#%H for a 24 hour system as %I is for a 12 hour system
TIME = time.strftime("%Y-%m-%d %I:%M:%S") #add this to get GMT time# , time.gmtime())
H, M, S = TIME.split(" ")[1].split(":")
H, M, S = int(H), int(M), int(S)

Dial_radius = (h / 2 - ((h / 2) / 10)) / 3
H_size, M_size, S_size = (Dial_radius / 2), Dial_radius, Dial_radius

H_angle = H * (360 / 12) + (float(M) / 60) * (360 / 12)
M_angle = M * (360 / 60) + (float(S) / 60) * (360 / 60)
S_angle = S * (360 / 60)

H_zero = [center[0], center[1] - H_size]
M_zero = [center[0], center[1] - M_size]
S_zero = [center[0], center[1] - S_size]

Hx, Hy = self.rotate(center, H_zero, H_angle)
Mx, My = self.rotate(center, M_zero, M_angle)
Sx, Sy = self.rotate(center, S_zero, S_angle)

#Hour
xgc.set_rgb_fg_color(gtk.gdk.color_parse("red"))
widget.window.draw_line(xgc, center[0], center[1], int(Hx), int(Hy))

#Minute
xgc.set_rgb_fg_color(gtk.gdk.color_parse("green"))
widget.window.draw_line(xgc, center[0], center[1], int(Mx), int(My))

#Second
xgc.set_rgb_fg_color(gtk.gdk.color_parse("blue"))
widget.window.draw_line(xgc, center[0], center[1], int(Sx), int(Sy))



def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_position(gtk.WIN_POS_CENTER)
#self.window.fullscreen()
self.window.set_size_request(600, 400)

self.vbox = gtk.VBox()
self.window.add(self.vbox)

# Create an area to draw in:
self.drawing_area = gtk.DrawingArea()
self.vbox.pack_start(self.drawing_area)
self.drawing_area.show()

self.drawing_area.connect("expose-event", self.expose_handler)
self.drawing_area.show()
self.window.show_all()
self.window.connect("destroy", self.destroy)

def main(self):
gtk.main()

if __name__ == "__main__":
base = Base()
base.main()


I've updated the code with acw1668's code, now it's working, thanks again, and it look like this:

#!/usr/bin/env python
import time
import gtk
import math
import glib

class Base:
def destroy(self, widget, data=None):
gtk.main_quit()

def close(self, widget):
self.window.destroy()

def refresh_clock(self):
self.drawing_area.queue_draw()
return True

def rotate(self, center, point, angle):
#http://stackoverflow.com/questions/12161277/how-to-rotate-a-vertex-around-a-certain-point
px = point[0]
py = point[1]
rx = center[0] + (px - center[0]) * math.cos(math.radians(angle)) - (py - center[1]) * math.sin(math.radians(angle))
ry = center[1] + (px - center[0]) * math.sin(math.radians(angle)) + (py - center[1]) * math.cos(math.radians(angle))
return(rx, ry)

# This function will be called whenever the drawing area is exposed:
def expose_handler(self, widget, event) :
w, h = widget.window.get_size()
center = [int(w / 2), int(h / 2)]
xgc = widget.window.new_gc()
#%H for a 24 hour system as %I is for a 12 hour system
TIME = time.strftime("%Y-%m-%d %I:%M:%S") #add this to get GMT time# , time.gmtime())
H, M, S = TIME.split(" ")[1].split(":")
H, M, S = int(H), int(M), int(S)

Dial_radius = (h / 2 - ((h / 2) / 10)) / 3
H_size, M_size, S_size = (Dial_radius / 2), Dial_radius, Dial_radius

H_angle = H * (360 / 12) + (float(M) / 60) * (360 / 12)
M_angle = M * (360 / 60) + (float(S) / 60) * (360 / 60)
S_angle = S * (360 / 60)

H_zero = [center[0], center[1] - H_size]
M_zero = [center[0], center[1] - M_size]
S_zero = [center[0], center[1] - S_size]

Hx, Hy = self.rotate(center, H_zero, H_angle)
Mx, My = self.rotate(center, M_zero, M_angle)
Sx, Sy = self.rotate(center, S_zero, S_angle)

#Hour
xgc.set_rgb_fg_color(gtk.gdk.color_parse("red"))
widget.window.draw_line(xgc, center[0], center[1], int(Hx), int(Hy))

#Minute
xgc.set_rgb_fg_color(gtk.gdk.color_parse("green"))
widget.window.draw_line(xgc, center[0], center[1], int(Mx), int(My))

#Second
xgc.set_rgb_fg_color(gtk.gdk.color_parse("blue"))
widget.window.draw_line(xgc, center[0], center[1], int(Sx), int(Sy))



def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_position(gtk.WIN_POS_CENTER)
#self.window.fullscreen()
self.window.set_size_request(600, 400)

self.vbox = gtk.VBox()
self.window.add(self.vbox)

# Create an area to draw in:
self.drawing_area = gtk.DrawingArea()
self.vbox.pack_start(self.drawing_area)
self.drawing_area.show()

self.drawing_area.connect("expose-event", self.expose_handler)
self.drawing_area.show()
self.window.show_all()
self.window.connect("destroy", self.destroy)

def main(self):
glib.timeout_add_seconds(1, self.refresh_clock)
gtk.main()

if __name__ == "__main__":
base = Base()
base.main()

Answer

You can use glib.timeout_add_seconds(...) to setup a timer to call queue_draw() every second:

import glib

Add a function to refresh the clock:

def refresh_clock(self):
    self.drawing_area.queue_draw()
    return True

Update main():

def main(self):
    glib.timeout_add_seconds(1, self.refresh_clock)
    gtk.main()
Comments