def __init__(self, settings, **kwargs):
        Logger.info('PhotoboothApp: __init__().')

        super(PhotoboothApp, self).__init__(**kwargs)

        self.settings = settings
        self.sm = None
        self.state_machine = PhotoboothState()
        self.countdown = None
        self.processes = []
        photobuffer = '/tmp/photobooth'
        self.photonames = {
            PhotoboothState.PHOTO1: os.path.join(photobuffer, 'photo1.jpg'),
            PhotoboothState.PHOTO2: os.path.join(photobuffer, 'photo2.jpg'),
            PhotoboothState.PHOTO3: os.path.join(photobuffer, 'photo3.jpg'),
        }
        self.montage_image = os.path.join(photobuffer, 'montage.jpg')
        self.canvas_image = os.path.join(photobuffer, 'canvas.jpg')
        self.print_image = os.path.join(photobuffer, 'composite.jpg')
        if not os.path.exists(photobuffer):
            os.makedirs(photobuffer)

        if self.settings.save and not os.path.exists(self.settings.save):
            os.makedirs(self.settings.save)

        cmd = (
            'convert '
            '-size {width}x{height} '
            'xc: {color} '
            '{filename}'.format(
                width=self.settings.image_width,
                height=self.settings.image_height,
                color=self.settings.background_color,
                filename=self.canvas_image
            )
        )
        subprocess.call(shlex.split(cmd))
class PhotoboothApp(App):
    def __init__(self, settings, **kwargs):
        Logger.info('PhotoboothApp: __init__().')

        super(PhotoboothApp, self).__init__(**kwargs)

        self.settings = settings
        self.sm = None
        self.state_machine = PhotoboothState()
        self.countdown = None
        self.processes = []
        photobuffer = '/tmp/photobooth'
        self.photonames = {
            PhotoboothState.PHOTO1: os.path.join(photobuffer, 'photo1.jpg'),
            PhotoboothState.PHOTO2: os.path.join(photobuffer, 'photo2.jpg'),
            PhotoboothState.PHOTO3: os.path.join(photobuffer, 'photo3.jpg'),
        }
        self.montage_image = os.path.join(photobuffer, 'montage.jpg')
        self.canvas_image = os.path.join(photobuffer, 'canvas.jpg')
        self.print_image = os.path.join(photobuffer, 'composite.jpg')
        if not os.path.exists(photobuffer):
            os.makedirs(photobuffer)

        if self.settings.save and not os.path.exists(self.settings.save):
            os.makedirs(self.settings.save)

        cmd = (
            'convert '
            '-size {width}x{height} '
            'xc: {color} '
            '{filename}'.format(
                width=self.settings.image_width,
                height=self.settings.image_height,
                color=self.settings.background_color,
                filename=self.canvas_image
            )
        )
        subprocess.call(shlex.split(cmd))

    def build(self):
        """Build UI.

        User interface objects stored in the photoboothapp must be created here,
        not in __init__().
        """
        Logger.info('PhotoboothApp: build().')

        self.sm = ScreenMgr(self, transition=NoTransition())
        return self.sm

    def start_event(self):
        """Waiting screen start button pressed."""
        Logger.info('PhotoboothApp: start_event().')

        self.state_machine.transition_to(PhotoboothState.COUNTDOWN1)
        self.sm.pb_screens[ScreenMgr.COUNTDOWN].start_countdown(
            self.settings.initial_wait_time
        )
        self.sm.current = ScreenMgr.COUNTDOWN

    def photo_event(self):
        """Time to take a picture."""
        Logger.info('PhotoboothApp: photo_event().')

        if self.state_machine.state not in (
            PhotoboothState.COUNTDOWN1,
            PhotoboothState.COUNTDOWN2,
            PhotoboothState.COUNTDOWN3
        ):
            Logger.error(
                'photo event occurred unexpectedly in "%s"',
                self.state_machine.state
            )
            self.sm.current = ScreenMgr.WAITING
            self.state_machine.transition_to(PhotoboothState.WAITING)
            return

        if self.state_machine.state == PhotoboothState.COUNTDOWN1:
            state = PhotoboothState.PHOTO1
        elif self.state_machine.state == PhotoboothState.COUNTDOWN2:
            state = PhotoboothState.PHOTO2
        else:
            state = PhotoboothState.PHOTO3

        # Update UI.
        self.sm.pb_screens[ScreenMgr.CHEESE].on_entry()
        self.sm.current = ScreenMgr.CHEESE

        # Take the picture.
        self.capture_image(self.photonames[state])

        self.state_machine.transition_to(state)

    def photo_complete_event(self):
        """Camera finished taking picture."""
        Logger.info('PhotoboothApp: photo_complete_event().')

        if self.state_machine.state not in (
            PhotoboothState.PHOTO1,
            PhotoboothState.PHOTO2,
            PhotoboothState.PHOTO3
        ):
            Logger.error(
                'photo complete event occurred unexpectedly in "%s"',
                self.state_machine.state
            )
            self.sm.current = ScreenMgr.WAITING
            self.state_machine.transition_to(PhotoboothState.WAITING)
            return

        if self.state_machine.state == PhotoboothState.PHOTO1:
            state = PhotoboothState.COUNTDOWN2
            self.sm.pb_screens[ScreenMgr.COUNTDOWN].start_countdown(
                self.settings.wait_time
            )
            self.sm.current = ScreenMgr.COUNTDOWN
        elif self.state_machine.state == PhotoboothState.PHOTO2:
            state = PhotoboothState.COUNTDOWN3
            self.sm.pb_screens[ScreenMgr.COUNTDOWN].start_countdown(
                self.settings.wait_time
            )
            self.sm.current = ScreenMgr.COUNTDOWN
        else:
            if self.settings.skip_select:
                state = PhotoboothState.PRINTING
                self.sm.pb_screens[ScreenMgr.PRINTING].on_entry()
                self.sm.current = ScreenMgr.PRINTING
            else:
                state = PhotoboothState.SELECTING
                self.sm.pb_screens[ScreenMgr.SELECTING].on_entry()
                self.sm.current = ScreenMgr.SELECTING

        self.state_machine.transition_to(state)

    def cancel_event(self):
        """Session canceled."""
        Logger.info('PhotoboothApp: cancel_event().')

        if self.state_machine.state != PhotoboothState.SELECTING:
            Logger.error(
                'cancel event occurred unexpectedly in "%s"',
                self.state_machine.state
            )

        self.sm.current = ScreenMgr.WAITING
        self.state_machine.transition_to(PhotoboothState.WAITING)

    def print_event(self):
        """Print request."""
        Logger.info('PhotoboothApp: print_event().')

        if self.state_machine.state != PhotoboothState.SELECTING:
            Logger.error(
                'print event occurred unexpectedly in "%s"',
                self.state_machine.state
            )
            self.sm.current = ScreenMgr.WAITING
            self.state_machine.transition_to(PhotoboothState.WAITING)
            return

        self.sm.pb_screens[ScreenMgr.PRINTING].on_entry()
        self.sm.current = ScreenMgr.PRINTING
        self.state_machine.transition_to(PhotoboothState.PRINTING)

    def capture_image(self, filename):
        """Launch process to capture image with camera."""
        Logger.info('PhotoboothApp: capture_image(%s).', filename)

        cmd = (
            'gphoto2 '
            '--capture-image-and-download '
            '--filename {filename} '
            '--keep '
            '--force-overwrite'.format(filename=filename)
        )
        cmd = shlex.split(cmd)
        self.processes = [subprocess.Popen(cmd)]

    def resize_images(self):
        """Launch processes to resize images."""
        Logger.info('PhotoboothApp: resize_images().')

        cmd = 'convert {src} -resize {width}x{height} {dest}'
        self.processes = [
            subprocess.Popen(
                shlex.split(
                    cmd.format(
                        src=fname,
                        width=880,
                        height=580,
                        dest=self.resized(fname)
                    )
                )
            )
            for fname in self.photonames.itervalues()
        ]

    def compose_photo(self):
        """Launch process to compose photo."""
        Logger.info('PhotoboothApp: compose_print().')

        cmd = (
            'montage {photo1} {photo2} {photo3} {logo} '
            '-mode concatenate '
            '-tile 2 '
            '-frame 10 '
            '-mattecolor none '
            '-background {background_color} '
            '{dest}'.format(
                photo1=self.resized(self.photonames[PhotoboothState.PHOTO1]),
                photo2=self.resized(self.photonames[PhotoboothState.PHOTO2]),
                photo3=self.resized(self.photonames[PhotoboothState.PHOTO3]),
                logo=self.settings.logo,
                background_color=self.settings.background_color,
                dest=self.montage_image
            )
        )
        self.processes = [subprocess.Popen(shlex.split(cmd))]

    def composite_photo(self):
        Logger.info('PhotoboothApp: composite_print().')

        cmd = (
            'composite '
            '-gravity center '
            '{montage} {canvas} {composite}'.format(
                montage=self.montage_image,
                canvas=self.canvas_image,
                composite=self.print_image
            )
        )
        self.processes = [subprocess.Popen(shlex.split(cmd))]

    def print_photo(self):
        """Launch process to print photo."""
        Logger.info('PhotoboothApp: print_photo().')

        if self.settings.save:
            dest = os.path.join(
                self.settings.save,
                'img_{}.jpg'.format(
                    datetime.datetime.now().strftime('%H%M%S')
                )
            )
            Logger.info('Saving photo to %s.', dest)
            shutil.copy(self.print_image, dest)

        if self.settings.printer:
            cmd = (
                'lp '
                '-d {printer} '
                '{photo}'.format(
                    printer=self.settings.printer,
                    photo=self.print_image
                )
            )
            Logger.info('Printing photo. %s', cmd)
            self.processes = [subprocess.Popen(shlex.split(cmd))]

        else:
            self.processes = []

    def print_complete(self):
        """Print photo process complete."""
        Logger.info('PhotoboothApp: print_complete().')

        self.sm.current = ScreenMgr.WAITING
        self.state_machine.transition_to(PhotoboothState.WAITING)

    def processing(self):
        """Check to see if we are still background processing anything."""
        Logger.info('PhotoboothApp: processing().')

        if any((process.poll() is None for process in self.processes)):
            # Still processing.
            return True

        return False

    @staticmethod
    def resized(name):
        base, ext = os.path.splitext(name)
        return '{base}_resized{ext}'.format(base=base, ext=ext)