Thursday, July 10, 2008

Showing a webcam image in the desktop window using Python. Part 3: self-refreshing app

This is the last part of a three-part series. In the first part, I've described how to read image from the web. The second part was dedicated to showing images in a window on the desktop. This last part will deal with auto-refreshing of the displayed image. This is particularly useful when you want to display a webcam image which is regularly updated.
To handle autorefresh we will use a Tkinter method after() which will call a defined function after some given delay.
To the previous code, we will add the following function:


def timingloop(start=0):
now = time.clock()
if start == 1:
getimagefromweb(myurl)
if now > container.start + container.refresh:
getimagefromweb(myurl)
container.start = now
else:
remains = str(int(container.refresh - now + container.start))
message = 'Next update in '+remains+' seconds.'
container.statusbarVar.set(message)
container.statusbar.update()
container.main.after(200, timingloop)

This function controls downloading new images by calling getimagefromweb() and checks when to do it by comparing saved times with current time. Finally, the function calls itself after 200 miliseconds.
For the program to work you also have to add some other stuff into the code. To the beginning add "import time" to import time library. The last part of the script has to be slightly modified as follows:


# the script
myurl = 'http://siemens.mesto-zatec.cz/obrazek.jpg'
container = Container()
container.width = 800
container.height = 602

container.start = time.clock()
container.refresh = 20 # window refresh rate in seconds

drawwindow()
timingloop(1)

mainloop()
print 'end OK'

We have added two new variables with timestamp and a refresh rate in seconds after which the image will be re-loaded. Finally, the getimagefromweb() function has been replaced by the timingloop() with parameter "1" which tells it that it is the start of the program and that it should load the image immediately. After the first timingloop() there is enough time to call mainloop() without which the program hangs (and I'm not sure why). As you can see using after() is a bit unintuitive because you can call other functions after "after()" was called and is still running in the background.

Tuesday, July 8, 2008

Showing a webcam image in the desktop window using Python. Part 2: displaying images in windows

In the previous post I have shown how to download an image from web with the use of Python. Today I will show code which takes this image and draws it in a standalone window.
Before we start it is necessary to be prepared somewhat. You need a specialised library to make windows. I 'm using Tkinter which comes with Python as default. There are more options like wxwidgets but I've started with Tkinter and so far it has covered all my needs. It seemed quite tricky to learn so I have found a good tutorial on Tkinter and learned it that way. Unfortunately, it is only in czech and currently seems offline so I cannot link to it anyway :-(.
To deal with images I'm using the PIL or Python Imaging Library. This you will need to download and install in order to use following code. Good news is that it's free :-)
Programming windowed applications is a lot more complicated than simple beginner stuff and in today's code there are commands which I don't know why they are there...
The code:

from Tkinter import *
import urllib
from PIL import Image, ImageTk

class Container:
pass

def drawwindow():
main = Tk()
main.title('Zatec webcam') # window title
main.resizable(width=False, height=False)
container.main = main
container.canvas = Canvas(main, width=container.width, height=container.height)
container.canvas.pack(expand=1, fill=BOTH)
container.statusbarVar = StringVar()
container.statusbar = Label(main, textvariable=container.statusbarVar)
container.statusbar.pack()
container.statusbarVar.set('Ready.')
container.statusbar.update()


def showimage(image):
'''Loads given image and puts it into the window'''
img = Image.open(image)
photo = ImageTk.PhotoImage(img)
container.canvas.create_image(container.width/2+2, container.height/2, image=photo)
container.obr = photo # why is this line necessary?

def getimagefromweb(url):
'''Downloads content from given url and saves it as image.'''
container.statusbarVar.set('Reading new image from web...')
container.statusbar.update()
try:
u = urllib.urlopen(url) # open url
container.statusbarVar.set('Url opened...')
container.statusbar.update()

content = u.read() # read the opened url
container.statusbarVar.set('Url read...')
container.statusbar.update()

u.close() # url was closed
container.statusbarVar.set('Url closed...')
container.statusbar.update()

except IOError:
print('IOError')
pass
except:
print('Unknown url error')

# saving what was downloaded
f = open('img.jpg','wb')
f.write(content)
f.close() # file was closed

# showing the image
showimage('img.jpg')
container.statusbarVar.set('Ready.')
container.statusbar.update()

# the script
myurl = 'http://siemens.mesto-zatec.cz/obrazek.jpg'
container = Container()
container.width = 800
container.height = 602

drawwindow()
getimagefromweb(myurl)

mainloop()
print 'end OK'

The class Container is used to store information which is passed between functions. The code could be rewritten to form a single class but it works this way too :-).
There are three functions. Drawwindow is used to create window with desired elements in it (Canvas and Label). To provide some info on download status there is a text in the Label. This is done with the help of StringVar() which is from Tkinter library. If we used only a string, it would be immutable later on. After the Label is changed, an .update() function must be called to change it on the display too.
The showimage() function gets an image file from disk and puts it into the Canvas. There is some magic in it, sorry about that.
The third function, getimagefromweb(), is adopted from previous post by adding info into the Label and after the image saved it calls the showimage() function.
In the script, we first draw the window elements with drawwindow() and then download an image and paste it into the Canvas with getimagefromweb().
The last command, mainloop(), tells Tkinter that we are ready and draws everything on the display. Without this command nothing will be shown (though it will be ready). Also, after mainloop is called, the focus is changed from your script to the GUI which is driven by user action. After you close the window the focus is returned to your python script.

Sunday, July 6, 2008

Showing a webcam image in the desktop window using Python. Part 1: reading image from web

I will describe everything that is necessary to show a window with a webcam (or any other) image in it. I'm using similar app to display a stock price chart (downloaded from a website) which is regularly updated. I will divide the text into three parts because I think this will be easier to grasp than one large article. This part will deal with downloading web content, in the second one the content will be displayed and in the third, final, one I will cover the self-refreshing which was the most difficult to figure out.
Again, I believe there is more than one way to do it. What follows is how I solved it.
There is a nice built-in library in Python called urllib which we will use to download the image in question.
The code is simple:

import urllib

def getimagefromweb(url):
'''Downloads content from given url and saves it as image.'''
try:
u = urllib.urlopen(url) # open url
content = u.read() # read the openned url
u.close() # url was closed

except IOError:
print('IOError')
except:
print('Unknown url error')

# saving what was downloaded
f = open('img.jpg','wb')
f.write(content)
f.close() # file was closed


myurl = 'http://siemens.mesto-zatec.cz/obrazek.jpg'
getimagefromweb(myurl)
print 'all OK'

The (only) function "getimagefromweb" receives a url with content to be downloaded and then reads from this adress in the same way that is used when reading/writing files. When dealing with web it is very advisable to use error-catching code like this one. It is common that the website will not respond in time and without error catching your application would crash.
As a final task the function saves image to harddisk so that it can be later re-read and shown on the desktop. This is a workaroud because I could not figure out a way how to display it directly.
The url leads to the webcam of the main square of the Zatec city where I was born and have grown up. It has quite interesting medieval centre so its nice to have it shown on desktop :-)