diff --git a/camera.py b/camera.py index 17758bc..e507e41 100644 --- a/camera.py +++ b/camera.py @@ -11,7 +11,9 @@ except ImportError: class CameraException(Exception): """Custom exception class to handle camera class errors""" - pass + def __init__(self, message, recoverable=False): + self.message = message + self.recoverable = recoverable class Camera_cv: @@ -35,23 +37,28 @@ class Camera_gPhoto: def __init__(self, picture_size): self.picture_size = picture_size - # Print the abilities of the connected camera - # print(self.call_gphoto("-a", "/dev/null")) + # Print the capabilities of the connected camera + try: + print(self.call_gphoto("-a", "/dev/null")) + except: + print("Warning: Can't list camera capabilities. Do you have gPhoto2 installed?") def call_gphoto(self, action, filename): # Try to run the command try: - cmd = "gphoto2 --force-overwrite --quiet " + action + " --filename " + filename + 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!") + raise CameraException("Can't focus! Move a little bit!", True) elif "No camera found" in e.output: - raise CameraException("No (supported) camera detected!") + raise CameraException("No (supported) camera detected!", False) + elif "command not found" in e.output: + raise CameraException("gPhoto2 not found!", False) else: - raise CameraException("Unknown error!\n" + '\n'.join(e.output.split('\n')[1:3])) + raise CameraException("Unknown error!\n" + '\n'.join(e.output.split('\n')[1:3]), False) return output def take_picture(self, filename="/tmp/picture.jpg"): diff --git a/photobooth.py b/photobooth.py index 89ced20..783490b 100755 --- a/photobooth.py +++ b/photobooth.py @@ -49,19 +49,31 @@ display_time = 10 ############### class PictureList: - """Class to manage images and count them""" + """A simple helper class. + + It provides the filenames for the assembled pictures and keeps count + of taken and previously existing pictures. + """ + def __init__(self, basename): + """Initialize filenames to the given basename and search for + existing files. Set the counter accordingly. + """ + # Set basename and suffix self.basename = basename self.suffix = ".jpg" self.count_width = 5 + # Ensure directory exists dirname = os.path.dirname(self.basename) if not os.path.exists(dirname): os.makedirs(dirname) + # Find existing files count_pattern = "[0-9]" * self.count_width pictures = glob(self.basename + count_pattern + self.suffix) + # Get number of latest file if len(pictures) == 0: self.counter = 0 @@ -69,8 +81,10 @@ class PictureList: pictures.sort() last_picture = pictures[-1] self.counter = int(last_picture[-(self.count_width+len(self.suffix)):-len(self.suffix)]) - print("Number of last existing file: " + str(self.counter) + "(" + str(len(pictures)) + ")") - print("Saving as: " + self.basename) + + # Print initial infos + print("Info: Number of last existing file: " + str(self.counter)) + print("Info: Saving assembled pictures as: " + self.basename + "XXXXX.jpg") def get(self, count): return self.basename + str(count).zfill(self.count_width) + self.suffix @@ -84,6 +98,11 @@ class PictureList: class Photobooth: + """The main class. + + It contains all the logic for the photobooth. + """ + def __init__(self, display_size, picture_basename, picture_size, pose_time, display_time, trigger_channel, shutdown_channel, lamp_channel): self.display = GuiModule('Photobooth', display_size) @@ -277,16 +296,34 @@ class Photobooth: # Take pictures filenames = [i for i in range(4)] for x in range(4): - self.display.clear() - self.display.show_message("S M I L E !!!\n\n" + str(x+1) + " of 4") - self.display.apply() + # Try each picture up to 3 times + for attempt in range(3): + self.display.clear() + self.display.show_message("S M I L E !!!\n\n" + str(x+1) + " of 4") + self.display.apply() - tic = clock() - filenames[x] = self.camera.take_picture("/tmp/photobooth_%02d.jpg" % x) - toc = clock() - tic + tic = clock() - if toc < 1.0: - sleep(1.0 - toc) + try: + filenames[x] = self.camera.take_picture("/tmp/photobooth_%02d.jpg" % x) + break + except CameraException as e: + # On recoverable errors: display message and retry + if e.recoverable: + if attempt < 3: + self.display.clear() + self.display.show_message(e.message) + self.display.apply() + sleep(1) + else: + raise CameraException("Giving up! Please start again!", False) + else: + raise e + + # Sleep for a little bit if necessary + toc = clock() - tic + if toc < 1.0: + sleep(1.0 - toc) # Show 'Wait' self.display.clear()