my latest system for presentations

Kyler Laird Kyler at news.Lairds.org
Wed Jul 28 17:08:28 CEST 2004


Last night I gave a presentation about our therapy dogs to a local
group.  I don't often give presentations and I have a great distaste
for anything approaching "PowerPoint".  When I was asked to present
to this group I said that I'd briefly cover a few topics and then go
into whatever areas interested the audience.  I needed a system to
support this.

I detest being tethered to a computer at the front of a room so
years ago I used x2x on my Sony PictureBook to control a computer
driving a projector.  That worked o.k. and I wanted to maintain that
freedom but because I would be choosing photos from a large
collection I really wanted a way to select from lists on the remote
computer and "send" them to the projecting computer.

I recently got an HP TC1100 (tablet computer) through work (donated
by HP).  I decided to whip up something to take advantage of its
capabilities.  I spent all of a couple hours kludging together a
solution that worked surprisingly well.

On the remote machine I ran Gnome's Nautilus file manager.  (I would
have used KDE's but it insists on "opening" images using its internal
viewer.)  I set the viewer to be a simple script that appends the
first argument (file name) to a temporary file.
	#!/bin/sh
	echo $1 >>/tmp/remote_display.log
Next, I moved that file to the projector machine by running another
process to monitor that file.
	% tail -s 0.1 -f /tmp/remote_display.log | ssh projector "cat >/tmp/remote_display.log"
(Yes, I could have used named pipes/sockets/etc. and I could have
avoided the use of the extra file on the projector computer but this
was easy to create and debug.)

On the projector machine I whipped up a simple Python program by
gluing together code snippets from various places.  It monitors the
file and displays the images requested.  That code is at the end of
the message and has a lot of stuff hardcoded in it.  (I started this
right before I needed to use it and got sloppier as my deadline
approached.)  I rsync-ed the images between the two computers so that
they were in identical locations.

For the presentation, I simply connected the projector machine to the
projector, established an ad-hoc wireless network, ran the monitor
script on the remote computer and ran the slaved viewer on the
projector computer.  I could then select an image in Nautilus on the
remote computer and have it nearly instantly appear on the projector.

The script also works through a randomized list of photos if it does
not receive any action for 30 seconds.  (That was a little too
short.)  This worked well at giving the audience something else to
see if I went into a potentially uninteresting tangent.  It gave them
a taste of our family and resulted in a couple of questions for
further information about things in those photos.  (Because I left it
running after setup, it was viewed for quite awhile before I even
arrived for the presentation.)

I did also have x2x running so that if I took the pen to the edge of
the remote computer, I could control the pointer on the projector.

I was impressed that I could quickly grab a simple toolkit (PyGame)
and whip up something that fulfilled my needs.  I was especially
happy that it worked flawlessly on the first try.  (The first time I
ran the system for more than a few minutes was during the
presentation.)  This is what Python is known for providing but it
still amazes me each time I experience it.

--kyler


==================================================================
slaved_viewer.py
==================================================================
#!/usr/bin/env python

import time
import pygame
import pygame.locals
import time
import os
import glob
import sys
import random

pygame.init()

#screen = pygame.display.set_mode((1280, 1024))

screen_modes = [
	((1024, 768), pygame.locals.FULLSCREEN),
	((256,128), 0),
	((640,480), 0),
	((640,1024), 0),
]
screen_mode_i = 0

tick = 0.1
ticks_per_second = 1.0 / tick
timeout_to_slideshow_count = ticks_per_second * 30
slideshow_ticks_per_image = ticks_per_second * 6

# Load some images to use when nothing else is active.
slideshow_filenames = glob.glob('/home/kyler/common/images/presentations/dogs/*.jpg')
random.shuffle(slideshow_filenames)
slideshow_index = 0

def display_image(filename):
	try:
		image = pygame.image.load(filename).convert()
	except:
		print 'error on %s' % (filename)
		return(True)

	(image_width, image_height) = image.get_size()
	rect = screen.get_rect()
	(rect_width, rect_height) = (rect.right, rect.bottom)	

	width_scale = float(rect_width) / image_width
	height_scale = float(rect_height) / image_height

	scale = min(width_scale, height_scale)

	scale_val = (int(image_width * scale), int(image_height * scale))

	image = pygame.transform.scale(image, scale_val)
	(image_width, image_height) = image.get_size()


	display_surface = pygame.display.get_surface()
	display_surface.fill((0,0,0))
	screen.blit(image, ((rect_width - image_width) / 2, (rect_height - image_height) / 2))


	pygame.display.flip()
	return(False)


def display_movie(filename):
	movie = pygame.movie.Movie(filename)
	movie.set_display(screen, (0, 0, screen.get_width(), screen.get_height()))
	movie.play()
	pygame.display.flip()

def display_file(filename):
	(file_root, file_extension) = os.path.splitext(os.path.basename(filename))
	file_extension = file_extension.lower()
	if file_extension in ['.jpg', '.gif', '.png', '.jpeg']:
		display_image(filename)
	#elif file_extension in ['.mpg']:
	#	return(display_movie(filename))
	else:
		print 'unsupported extension (%s)' % (file_extension)	
		return(True)
	
	text = file_root

	x_pos = 0
	y_pos = screen.get_height() - 40
	ren = pygame.font.Font(None, 30).render(text, 0, (255, 0, 230))
	screen.blit(ren, (x_pos, y_pos))

	pygame.display.flip()
	
if 1:
	screen = pygame.display.set_mode(*screen_modes[screen_mode_i])

	# Open the file that we're going to watch for image paths.
	file = open('/tmp/remote_display.log', 'r')

	# Read everything that's there already.
	file.read()

	sleep_count = 0

	time.sleep(tick)
	while True:
		event = pygame.event.poll()
		if event:
			if event.type == pygame.locals.KEYDOWN:
				sleep_count = 0
				key = event.key
				print 'key press (%d)' % (key)
				# Quit.
				if key == ord('q'):
					sys.exit(0)
				# Change display mode.
				elif key == ord('f'):
					screen_mode_i = (screen_mode_i + 1) % len(screen_modes)
					pygame.display.set_mode(*screen_modes[screen_mode_i])
			elif event.type == pygame.locals.MOUSEMOTION:
				sleep_count = 0

			continue

		where = file.tell()
		line = file.readline()
		if line:
			sleep_count = 0
			filename = line[:-1]
			print filename
			display_file(filename)
			continue

		sleep_count += 1

		if (sleep_count >= timeout_to_slideshow_count) and (sleep_count % slideshow_ticks_per_image == 0):
			display_file(slideshow_filenames[slideshow_index])
			slideshow_index = (slideshow_index + 1) % len(slideshow_filenames)

		time.sleep(tick)
		file.seek(where)




More information about the Python-list mailing list