Started refactoring into multiple files
This commit is contained in:
35
camera.py
Normal file
35
camera.py
Normal file
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python
|
||||
# Created by br@re-web.eu, 2015
|
||||
|
||||
import subprocess
|
||||
|
||||
class CameraException(Exception):
|
||||
"""Custom exception class to handle camera class errors"""
|
||||
pass
|
||||
|
||||
class Camera_gPhoto:
|
||||
"""Camera class providing functionality to take pictures using gPhoto 2"""
|
||||
|
||||
def __init__(self):
|
||||
# Print the abilities of the connected camera
|
||||
print(self.call_gphoto("-a", "/dev/null"))
|
||||
|
||||
def call_gphoto(self, action, filename):
|
||||
# Try to run the command
|
||||
try:
|
||||
cmd = "gphoto2 --force-overwrite --quiet " + action + " --filename " + filename
|
||||
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
|
||||
if "ERROR" in output:
|
||||
raise subprocess.CalledProcessError(returncode=0, cmd=cmd, output=output)
|
||||
except subprocess.CalledProcessError as e:
|
||||
if "Canon EOS Capture failed: 2019" in e.output:
|
||||
raise CameraException("Can't focus! Move and try again!")
|
||||
elif "No camera found" in e.output:
|
||||
raise CameraException("No (supported) camera detected!")
|
||||
else:
|
||||
raise CameraException("Unknown error!\n" + '\n'.join(e.output.split('\n')[1:3]))
|
||||
return output
|
||||
|
||||
def take_picture(self, filename="/tmp/picture.jpg"):
|
||||
self.call_gphoto("--capture-image-and-download", filename)
|
||||
return filename
|
||||
200
gui.py
Normal file
200
gui.py
Normal file
@@ -0,0 +1,200 @@
|
||||
#!/usr/bin/env python
|
||||
# Created by br@re-web.eu, 2015
|
||||
|
||||
# TODO:
|
||||
# - base everything on surfaces to allow stacking
|
||||
# - incorporate render_textrect
|
||||
# - restructure mainloop
|
||||
|
||||
import pygame
|
||||
|
||||
try:
|
||||
import pygame.fastevent as EventModule
|
||||
except ImportError:
|
||||
import pygame.event as EventModule
|
||||
|
||||
|
||||
class TextRectException:
|
||||
def __init__(self, message = None):
|
||||
self.message = message
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
def render_textrect(string, font, rect, text_color, background_color, justification=0, valign=0):
|
||||
"""Returns a surface containing the passed text string, reformatted
|
||||
to fit within the given rect, word-wrapping as necessary. The text
|
||||
will be anti-aliased.
|
||||
|
||||
Source: http://www.pygame.org/pcr/text_rect/index.php
|
||||
|
||||
Takes the following arguments:
|
||||
|
||||
string - the text you wish to render. \n begins a new line.
|
||||
font - a Font object
|
||||
rect - a rectstyle giving the size of the surface requested.
|
||||
text_color - a three-byte tuple of the rgb value of the
|
||||
text color. ex (0, 0, 0) = BLACK
|
||||
background_color - a three-byte tuple of the rgb value of the surface.
|
||||
justification - 0 (default) left-justified
|
||||
1 horizontally centered
|
||||
2 right-justified
|
||||
valign - 0 (default) top aligned
|
||||
1 vertically centered
|
||||
2 bottom aligned
|
||||
|
||||
Returns the following values:
|
||||
|
||||
Success - a surface object with the text rendered onto it.
|
||||
Failure - raises a TextRectException if the text won't fit onto the surface.
|
||||
"""
|
||||
|
||||
final_lines = []
|
||||
|
||||
requested_lines = string.splitlines()
|
||||
|
||||
# Create a series of lines that will fit on the provided
|
||||
# rectangle.
|
||||
|
||||
accumulated_height = 0
|
||||
for requested_line in requested_lines:
|
||||
if font.size(requested_line)[0] > rect.width:
|
||||
words = requested_line.split(' ')
|
||||
# if any of our words are too long to fit, return.
|
||||
for word in words:
|
||||
if font.size(word)[0] >= rect.width:
|
||||
raise TextRectException, "The word " + word + " is too long to fit in the rect passed."
|
||||
# Start a new line
|
||||
accumulated_line = ""
|
||||
for word in words:
|
||||
test_line = accumulated_line + word + " "
|
||||
# Build the line while the words fit.
|
||||
if font.size(test_line)[0] < rect.width:
|
||||
accumulated_line = test_line
|
||||
else:
|
||||
accumulated_height += font.size(test_line)[1]
|
||||
final_lines.append(accumulated_line)
|
||||
accumulated_line = word + " "
|
||||
accumulated_height += font.size(accumulated_line)[1]
|
||||
final_lines.append(accumulated_line)
|
||||
else:
|
||||
accumulated_height += font.size(requested_line)[1]
|
||||
final_lines.append(requested_line)
|
||||
|
||||
# Check height of the text and align vertically
|
||||
|
||||
if accumulated_height >= rect.height:
|
||||
raise TextRectException, "Once word-wrapped, the text string was too tall to fit in the rect."
|
||||
|
||||
if valign == 0:
|
||||
voffset = 0
|
||||
elif valign == 1:
|
||||
voffset = int((rect.height - accumulated_height) / 2)
|
||||
elif valign == 2:
|
||||
voffset = rect.height - accumulated_height
|
||||
else:
|
||||
raise TextRectException, "Invalid valign argument: " + str(valign)
|
||||
|
||||
# Let's try to write the text out on the surface.
|
||||
|
||||
surface = pygame.Surface(rect.size)
|
||||
surface.fill(background_color)
|
||||
|
||||
accumulated_height = 0
|
||||
for line in final_lines:
|
||||
if line != "":
|
||||
tempsurface = font.render(line, 1, text_color)
|
||||
if justification == 0:
|
||||
surface.blit(tempsurface, (0, voffset + accumulated_height))
|
||||
elif justification == 1:
|
||||
surface.blit(tempsurface, ((rect.width - tempsurface.get_width()) / 2, voffset + accumulated_height))
|
||||
elif justification == 2:
|
||||
surface.blit(tempsurface, (rect.width - tempsurface.get_width(), voffset + accumulated_height))
|
||||
else:
|
||||
raise TextRectException, "Invalid justification argument: " + str(justification)
|
||||
accumulated_height += font.size(line)[1]
|
||||
|
||||
return surface
|
||||
|
||||
|
||||
class GUI_PyGame:
|
||||
"""A GUI class using PyGame"""
|
||||
|
||||
def __init__(self, name, size):
|
||||
# Call init routines
|
||||
pygame.init()
|
||||
if hasattr(EventModule, 'init'):
|
||||
EventModule.init()
|
||||
|
||||
# Window name
|
||||
pygame.display.set_caption(name)
|
||||
|
||||
# Hide mouse cursor
|
||||
pygame.mouse.set_cursor(*pygame.cursors.load_xbm('transparent.xbm','transparent.msk'))
|
||||
|
||||
# Store screen and size
|
||||
self.size = size
|
||||
self.screen = pygame.display.set_mode(self.size, pygame.FULLSCREEN)
|
||||
|
||||
# Clear screen
|
||||
self.clear()
|
||||
|
||||
def clear(self, color=(0,0,0)):
|
||||
self.screen.fill(color)
|
||||
|
||||
def apply(self):
|
||||
pygame.display.update()
|
||||
|
||||
def get_size(self):
|
||||
return self.size
|
||||
|
||||
def trigger_event(self, event_id, event_channel):
|
||||
EventModule.post(EventModule.Event(event_id, channel=event_channel))
|
||||
|
||||
def show_picture(self, filename, size=(0,0), offset=(0,0)):
|
||||
# Use window size if none given
|
||||
if size == (0,0):
|
||||
size = self.size
|
||||
# Load image from file
|
||||
image = pygame.image.load(filename)
|
||||
# Extract image size and determine scaling
|
||||
image_size = image.get_rect().size
|
||||
image_scale = min([min(a,b)/b for a,b in zip(size, image_size)])
|
||||
# New image size
|
||||
new_size = [int(a*image_scale) for a in image_size]
|
||||
# Update offset
|
||||
offset = tuple(a+int((b-c)/2) for a,b,c in zip(offset, size, new_size))
|
||||
# Apply scaling and display picture
|
||||
image = pygame.transform.scale(image, new_size).convert()
|
||||
self.screen.blit(image, offset)
|
||||
|
||||
def show_message(self, msg, color=(245,245,245), bg=(0,0,0)):
|
||||
# Choose font
|
||||
font = pygame.font.Font(None, 144)
|
||||
# Create rectangle for text
|
||||
rect = pygame.Rect((0, 0, self.size[0], self.size[1]))
|
||||
# Render text
|
||||
text = render_textrect(msg, font, rect, color, bg, 1, 1)
|
||||
self.screen.blit(text, rect.topleft)
|
||||
|
||||
def mainloop(self, filename, handle_keypress, handle_mousebutton, handle_gpio_event):
|
||||
while True:
|
||||
# Ignore all input that happened before entering the loop
|
||||
EventModule.get()
|
||||
# Clear display
|
||||
self.clear()
|
||||
# Show idle-picture and message
|
||||
if filename != None:
|
||||
self.show_picture(filename)
|
||||
self.show_message("Hit the button!")
|
||||
# Render everything
|
||||
self.apply()
|
||||
# Wait for event
|
||||
event = EventModule.wait()
|
||||
# Handle the event
|
||||
if event.type == pygame.QUIT: return
|
||||
elif event.type == pygame.KEYDOWN: handle_keypress(event.key)
|
||||
elif event.type == pygame.MOUSEBUTTONUP: handle_mousebutton(event.button, event.pos)
|
||||
elif event.type == gpio_pygame_event: handle_gpio_event(event.channel)
|
||||
|
||||
def teardown(self):
|
||||
pygame.quit()
|
||||
Reference in New Issue
Block a user