Logging added

This commit is contained in:
Balthasar Reuter
2018-05-14 21:13:56 +02:00
parent e341fbc052
commit 5e2739bec8
13 changed files with 122 additions and 75 deletions

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import configparser, os import configparser, os, logging
class Config: class Config:
@@ -29,16 +29,20 @@ class Config:
def defaults(self): def defaults(self):
self._cfg.read(os.path.join(os.path.dirname(__file__), 'defaults.cfg')) filename = os.path.join(os.path.dirname(__file__), 'defaults.cfg')
logging.info('Reading config file "%s"', filename)
self._cfg.read(filename)
def read(self): def read(self):
logging.info('Reading config file "%s"', self._filename)
self._cfg.read(self._filename) self._cfg.read(self._filename)
def write(self): def write(self):
logging.info('Writing config file "%s"', self._filename)
with open(self._filename, 'w') as configfile: with open(self._filename, 'w') as configfile:
self._cfg.write(configfile) self._cfg.write(configfile)

View File

@@ -1,11 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# from time import time, localtime, strftime import logging
from PIL import Image, ImageOps from PIL import Image, ImageOps
from .PictureList import PictureList
from .PictureDimensions import PictureDimensions from .PictureDimensions import PictureDimensions
from . import gui from . import gui
@@ -40,30 +39,35 @@ class Photobooth:
self._cap = camera self._cap = camera
self._pic_dims = PictureDimensions(config, self._cap.getPicture().size) self._pic_dims = PictureDimensions(config, self._cap.getPicture().size)
# picture_basename = strftime(config.get('Picture', 'basename'), localtime())
# self._pic_list = PictureList(picture_basename)
# self._get_next_filename = self._pic_list.getNext
if ( config.getBool('Photobooth', 'show_preview') if ( config.getBool('Photobooth', 'show_preview')
and self._cap.hasPreview ): and self._cap.hasPreview ):
logging.info('Countdown with preview activated')
self._show_counter = self.showCounterPreview self._show_counter = self.showCounterPreview
else: else:
logging.info('Countdown without preview activated')
self._show_counter = self.showCounterNoPreview self._show_counter = self.showCounterNoPreview
def initGpio(self, config): def initGpio(self, config):
if config.getBool('Gpio', 'enable'): if config.getBool('Gpio', 'enable'):
lamp_pin = config.getInt('Gpio', 'lamp_pin')
trigger_pin = config.getInt('Gpio', 'trigger_pin')
exit_pin = config.getInt('Gpio', 'exit_pin')
logging.info('GPIO enabled (lamp_pin=%d, trigger_pin=%d, exit_pin=%d)',
lamp_pin, trigger_pin, exit_pin)
from Gpio import Gpio from Gpio import Gpio
self._gpio = Gpio() self._gpio = Gpio()
lamp = self._gpio.setLamp(config.getInt('Gpio', 'lamp_pin')) lamp = self._gpio.setLamp(lamp_pin)
self._lampOn = lambda : self._gpio.lampOn(lamp) self._lampOn = lambda : self._gpio.lampOn(lamp)
self._lampOff = lambda : self._gpio.lampOff(lamp) self._lampOff = lambda : self._gpio.lampOff(lamp)
self._gpio.setButton(config.getInt('Gpio', 'trigger_pin'), self.gpioTrigger) self._gpio.setButton(triger_pin, self.gpioTrigger)
self._gpio.setButton(config.getInt('Gpio', 'exit_pin'), self.gpioExit) self._gpio.setButton(exit_pin, self.gpioExit)
else: else:
self._lampOn = lambda : None self._lampOn = lambda : None
self._lampOff = lambda : None self._lampOff = lambda : None
@@ -71,7 +75,7 @@ class Photobooth:
def teardown(self): def teardown(self):
print('Camera teardown') logging.info('Teardown of camera')
self.triggerOff() self.triggerOff()
self.setCameraIdle() self.setCameraIdle()
@@ -83,7 +87,7 @@ class Photobooth:
try: try:
event_idx = expected.index(str(event)) event_idx = expected.index(str(event))
except ValueError: except ValueError:
print('Photobooth: Unknown event received: ' + str(event)) logging.error('Unknown event received: %s', str(event))
raise ValueError('Unknown event received', str(event)) raise ValueError('Unknown event received', str(event))
return event_idx return event_idx
@@ -94,7 +98,7 @@ class Photobooth:
events = ['ack', 'cancel', 'teardown'] events = ['ack', 'cancel', 'teardown']
if self.recvEvent(events) != 0: if self.recvEvent(events) != 0:
print('Teardown of Photobooth requested') logging.info('Teardown of camera requested')
raise TeardownException() raise TeardownException()
@@ -103,16 +107,10 @@ class Photobooth:
events = ['triggered', 'teardown'] events = ['triggered', 'teardown']
if self.recvEvent(events) != 0: if self.recvEvent(events) != 0:
print('Teardown of Photobooth requested') logging.info('Teardown of camera requested')
raise TeardownException() raise TeardownException()
# @property
# def getNextFilename(self):
# return self._get_next_filename
@property @property
def showCounter(self): def showCounter(self):
@@ -140,7 +138,7 @@ class Photobooth:
try: try:
self.trigger() self.trigger()
except RuntimeError as e: except RuntimeError as e:
print('Camera error: ' + str(e)) logging.error('Camera error: %s', str(e))
self._conn.send( gui.ErrorState('Camera error', str(e)) ) self._conn.send( gui.ErrorState('Camera error', str(e)) )
self.recvAck() self.recvAck()
@@ -175,7 +173,6 @@ class Photobooth:
self._conn.send(gui.CountdownState()) self._conn.send(gui.CountdownState())
self.recvAck() self.recvAck()
print('ack received')
def showPose(self): def showPose(self):
@@ -209,11 +206,13 @@ class Photobooth:
def enqueueWorkerTasks(self, picture): def enqueueWorkerTasks(self, picture):
for task in self._worker_list: for task in self._worker_list:
self._queue.put(( task.do, (picture, ) )) self._queue.put(task.get(picture))
def trigger(self): def trigger(self):
logging.info('Photobooth triggered')
self._conn.send(gui.GreeterState()) self._conn.send(gui.GreeterState())
self.triggerOff() self.triggerOff()
self.setCameraActive() self.setCameraActive()
@@ -224,7 +223,6 @@ class Photobooth:
self._conn.send(gui.AssembleState()) self._conn.send(gui.AssembleState())
img = self.assemblePictures(pics) img = self.assemblePictures(pics)
# img.save(self.getNextFilename(), 'JPEG')
self._conn.send(gui.PictureState(img)) self._conn.send(gui.PictureState(img))
self.enqueueWorkerTasks(img) self.enqueueWorkerTasks(img)

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os, logging
from glob import glob from glob import glob
@@ -43,11 +43,13 @@ class PictureList:
else: else:
pictures.sort() pictures.sort()
last_picture = pictures[-1] last_picture = pictures[-1]
self.counter = int(last_picture[-(self.count_width+len(self.suffix)):-len(self.suffix)]) self.counter = int(last_picture[
-(self.count_width+len(self.suffix)):-len(self.suffix)])
# Print initial infos # Print initial infos
print('Info: Number of last existing file: ' + str(self.counter)) logging.info('Number of last existing file: %d', self.counter)
print('Info: Saving assembled pictures as: ' + self.basename + (self.count_width * 'X') + '.jpg') logging.info('Saving assembled pictures as "%s%s.%s"', self.basename,
self.count_width * 'X', 'jpg')
def getFilename(self, count): def getFilename(self, count):

View File

@@ -1,6 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging
from time import localtime, strftime from time import localtime, strftime
from .PictureList import PictureList from .PictureList import PictureList
@@ -13,7 +15,7 @@ class WorkerTask:
assert not kwargs assert not kwargs
def do(self, picture): def get(self, picture):
raise NotImplementedError() raise NotImplementedError()
@@ -25,21 +27,20 @@ class PictureSaver(WorkerTask):
super().__init__() super().__init__()
picture_basename = strftime(config.get('Picture', 'basename'), localtime()) basename = strftime(config.get('Picture', 'basename'), localtime())
self._pic_list = PictureList(picture_basename) self._pic_list = PictureList(basename)
self._get_next_filename = self._pic_list.getNext
@property @staticmethod
def getNextFilename(self): def do(picture, filename):
return self._get_next_filename logging.info('Saving picture as %s', filename)
picture.save(filename, 'JPEG')
def do(self, picture): def get(self, picture):
print('saving picture') return (self.do, (picture, self._pic_list.getNext()))
picture.save(self.getNextFilename(), 'JPEG')
@@ -52,7 +53,7 @@ class Worker:
def run(self): def run(self):
for func, args in iter(self._queue.get, ('teardown', None)): for func, args in iter(self._queue.get, 'teardown'):
func(*args) func(*args)
return 0 return 0

View File

@@ -20,6 +20,8 @@ class CameraGphoto2(Camera):
self.hasIdle = False self.hasIdle = False
self._isActive = False self._isActive = False
logging.info('Using python-gphoto2 bindings')
self._setupLogging() self._setupLogging()
self._setupCamera() self._setupCamera()
@@ -27,14 +29,10 @@ class CameraGphoto2(Camera):
def cleanup(self): def cleanup(self):
self._cap.exit(self._ctxt) self._cap.exit(self._ctxt)
# self.setIdle()
def _setupLogging(self): def _setupLogging(self):
logging.basicConfig(
format='%(levelname)s: %(name)s: %(message)s',
level=logging.ERROR)
gp.check_result(gp.use_python_logging()) gp.check_result(gp.use_python_logging())
@@ -67,9 +65,7 @@ class CameraGphoto2(Camera):
# self.setActive() # self.setActive()
text = self._cap.get_summary(self._ctxt) text = self._cap.get_summary(self._ctxt)
print('Summary') logging.info('Camera summary: %s', str(text))
print('=======')
print(str(text))
# self.setIdle() # self.setIdle()

View File

@@ -19,10 +19,7 @@ class CameraGphoto2Cffi(Camera):
self.hasPreview = True self.hasPreview = True
self.hasIdle = True self.hasIdle = True
# Avoid output cluttered logging.info('Using gphoto2-cffi bindings')
logging.basicConfig(
format='%(levelname)s: %(name)s: %(message)s',
level=logging.CRITICAL)
self._setupCamera() self._setupCamera()
@@ -30,7 +27,7 @@ class CameraGphoto2Cffi(Camera):
def _setupCamera(self): def _setupCamera(self):
self._cap = gp.Camera() self._cap = gp.Camera()
print(self._cap.supported_operations) logging.info('Supported operations: %s', self._cap.supported_operations)
if 'raw' in self._cap.config['imgsettings']['imageformat'].value.lower(): if 'raw' in self._cap.config['imgsettings']['imageformat'].value.lower():
raise RuntimeError('Camera file format is set to RAW') raise RuntimeError('Camera file format is set to RAW')

View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from PIL import Image from PIL import Image
import os, subprocess import os, subprocess, logging
from . import Camera from . import Camera
@@ -15,9 +15,13 @@ class CameraGphoto2CommandLine(Camera):
self.hasPreview = False self.hasPreview = False
self.hasIdle = False self.hasIdle = False
logging.info('Using gphoto2 via command line')
if os.access('/dev/shm', os.W_OK): if os.access('/dev/shm', os.W_OK):
logging.debug('Storing temp files to "/dev/shm/photobooth.jpg"')
self._tmp_filename = '/dev/shm/photobooth.jpg' self._tmp_filename = '/dev/shm/photobooth.jpg'
else: else:
logging.debug('Storing temp files to "/tmp/photobooth.jpg"')
self._tmp_filename = '/tmp/photobooth.jpg' self._tmp_filename = '/tmp/photobooth.jpg'
self.setActive() self.setActive()

View File

@@ -1,6 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging
from PIL import Image from PIL import Image
import cv2 import cv2
@@ -16,6 +18,8 @@ class CameraOpenCV(Camera):
self.hasPreview = True self.hasPreview = True
self.hasIdle = True self.hasIdle = True
logging.info('Using OpenCV')
self._cap = cv2.VideoCapture() self._cap = cv2.VideoCapture()

View File

@@ -1,6 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging
from PyQt5.QtWidgets import QMessageBox from PyQt5.QtWidgets import QMessageBox
from .. import printer from .. import printer
@@ -19,6 +21,11 @@ class GuiPostprocess:
raise NotImplementedError() raise NotImplementedError()
def confirm(self, picture):
raise NotImplementedError()
class PrintPostprocess(GuiPostprocess): class PrintPostprocess(GuiPostprocess):
@@ -33,17 +40,15 @@ class PrintPostprocess(GuiPostprocess):
def get(self, picture): def get(self, picture):
return PrintState(lambda : self.do(picture)) return PrintState(lambda : self.do(picture), False)
# reply = QMessageBox.question(parent, 'Print?',
# 'Do you want to print the picture?',
# QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
# if reply == QMessageBox.Yes: def confirm(self, picture):
# self._printer.print(picture)
return PrintState(lambda : None, True)
def do(self, picture): def do(self, picture):
print('Printing') logging.info('Printing picture')
self._printer.print(picture) self._printer.print(picture)

View File

@@ -147,11 +147,12 @@ class TeardownState(GuiState):
class PrintState(GuiState): class PrintState(GuiState):
def __init__(self, handler, **kwargs): def __init__(self, handler, confirmed, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.handler = handler self.handler = handler
self.confirmed = confirmed
@property @property
@@ -167,3 +168,18 @@ class PrintState(GuiState):
raise ValueError('handler must be callable') raise ValueError('handler must be callable')
self._handler = handler self._handler = handler
@property
def confirmed(self):
return self._confirmed
@confirmed.setter
def confirmed(self, confirmed):
if not isinstance(confirmed, bool):
raise ValueError('confirmed status must be bool')
self._confirmed = confirmed

View File

@@ -3,6 +3,7 @@
import multiprocessing as mp import multiprocessing as mp
import queue import queue
import logging
from PIL import ImageQt from PIL import ImageQt
@@ -141,7 +142,6 @@ class PyQt5Gui(Gui):
elif isinstance(state, PictureState): elif isinstance(state, PictureState):
img = ImageQt.ImageQt(state.picture) img = ImageQt.ImageQt(state.picture)
self._p.setCentralWidget(PyQt5PictureMessage('', img)) self._p.setCentralWidget(PyQt5PictureMessage('', img))
# QTimer.singleShot(cfg.getInt('Photobooth', 'display_time') * 1000, self.sendAck)
QTimer.singleShot(cfg.getInt('Photobooth', 'display_time') * 1000, QTimer.singleShot(cfg.getInt('Photobooth', 'display_time') * 1000,
lambda : self.postprocessPicture(state.picture)) lambda : self.postprocessPicture(state.picture))
@@ -179,6 +179,8 @@ class PyQt5Gui(Gui):
QMessageBox.Yes | QMessageBox.No, QMessageBox.No) QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.Yes: if reply == QMessageBox.Yes:
task.handler() task.handler()
QMessageBox.information(self._p, 'Printing',
'Picture sent to printer.', QMessageBox.Ok)
else: else:
raise ValueError('Unknown task') raise ValueError('Unknown task')
@@ -217,9 +219,9 @@ class PyQt5Gui(Gui):
def showError(self, title, message): def showError(self, title, message):
print('ERROR: ' + title + ': ' + message) logging.error('%s: %s', title, message)
reply = QMessageBox.warning(self._p, title, message, QMessageBox.Close | QMessageBox.Retry, reply = QMessageBox.warning(self._p, title, message,
QMessageBox.Retry) QMessageBox.Close | QMessageBox.Retry, QMessageBox.Retry)
if reply == QMessageBox.Retry: if reply == QMessageBox.Retry:
self.sendAck() self.sendAck()
self._lastState() self._lastState()

View File

@@ -7,8 +7,9 @@ try:
except DistributionNotFound: except DistributionNotFound:
__version__ = 'unknown' __version__ = 'unknown'
import multiprocessing as mp
import sys import sys
import multiprocessing as mp
import logging
from . import camera, gui from . import camera, gui
from .Config import Config from .Config import Config
@@ -44,7 +45,7 @@ class CameraProcess(mp.Process):
if str(event) in ('cancel', 'ack'): if str(event) in ('cancel', 'ack'):
return 123 return 123
else: else:
print('Unknown event received: ' + str(event)) logging.error('Unknown event received: %s', str(event))
raise RuntimeError('Unknown event received', str(event)) raise RuntimeError('Unknown event received', str(event))
@@ -56,10 +57,11 @@ class CameraProcess(mp.Process):
event = self.conn.recv() event = self.conn.recv()
if str(event) != 'start': if str(event) != 'start':
logging.warning('Unknown event received: %s', str(event))
continue continue
status_code = self.run_camera() status_code = self.run_camera()
print('Camera exit: ', str(status_code)) logging.info('Camera exited with status code %d', status_code)
sys.exit(status_code) sys.exit(status_code)
@@ -100,13 +102,16 @@ class GuiProcess(mp.Process):
def run(argv): def run(argv):
print('Photobooth version:', __version__) logging.info('Photobooth version: %s', __version__)
# Load configuration
config = Config('photobooth.cfg') config = Config('photobooth.cfg')
# Create communication objects
gui_conn, camera_conn = mp.Pipe() gui_conn, camera_conn = mp.Pipe()
worker_queue = mp.SimpleQueue() worker_queue = mp.SimpleQueue()
# Initialize processes
camera_proc = CameraProcess(config, camera_conn, worker_queue) camera_proc = CameraProcess(config, camera_conn, worker_queue)
camera_proc.start() camera_proc.start()
@@ -116,11 +121,13 @@ def run(argv):
gui_proc = GuiProcess(argv, config, gui_conn, worker_queue) gui_proc = GuiProcess(argv, config, gui_conn, worker_queue)
gui_proc.start() gui_proc.start()
# Close endpoints
gui_conn.close() gui_conn.close()
camera_conn.close() camera_conn.close()
gui_proc.join()
worker_queue.put(('teardown', None)) # Wait for processes to finish
gui_proc.join()
worker_queue.put('teardown')
worker_proc.join() worker_proc.join()
camera_proc.join(5) camera_proc.join(5)
return gui_proc.exitcode return gui_proc.exitcode
@@ -128,6 +135,9 @@ def run(argv):
def main(argv): def main(argv):
logging.basicConfig(filename='photobooth.log', level=logging.INFO)
logging.getLogger().addHandler(logging.StreamHandler())
known_status_codes = { known_status_codes = {
999: 'Initializing photobooth', 999: 'Initializing photobooth',
123: 'Restarting photobooth and reloading config' 123: 'Restarting photobooth and reloading config'
@@ -136,8 +146,10 @@ def main(argv):
status_code = 999 status_code = 999
while status_code in known_status_codes: while status_code in known_status_codes:
print(known_status_codes[status_code]) logging.info(known_status_codes[status_code])
status_code = run(argv) status_code = run(argv)
logging.info('Exiting photobooth with status code %d', status_code)
return status_code return status_code

View File

@@ -1,6 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging
from PIL import ImageQt from PIL import ImageQt
from PyQt5.QtCore import Qt, QPoint, QSizeF from PyQt5.QtCore import Qt, QPoint, QSizeF
@@ -18,8 +20,11 @@ class PrinterPyQt5(Printer):
self._printer = QPrinter(QPrinter.HighResolution) self._printer = QPrinter(QPrinter.HighResolution)
self._printer.setPageSize(QPageSize(QSizeF(*page_size), QPageSize.Millimeter)) self._printer.setPageSize(QPageSize(QSizeF(*page_size), QPageSize.Millimeter))
logging.info('Using printer "%s"', self._printer.printerName())
self._print_pdf = print_pdf self._print_pdf = print_pdf
if self._print_pdf: if self._print_pdf:
logging.info('Using PDF printer')
self._counter = 0 self._counter = 0
self._printer.setOutputFormat(QPrinter.PdfFormat) self._printer.setOutputFormat(QPrinter.PdfFormat)
self._printer.setFullPage(True) self._printer.setFullPage(True)
@@ -32,7 +37,8 @@ class PrinterPyQt5(Printer):
self._counter += 1 self._counter += 1
img = ImageQt.ImageQt(picture) img = ImageQt.ImageQt(picture)
img = img.scaled(self._printer.pageRect().size(), Qt.KeepAspectRatio, Qt.SmoothTransformation) img = img.scaled(self._printer.pageRect().size(), Qt.KeepAspectRatio,
Qt.SmoothTransformation)
printable_size = self._printer.pageRect(QPrinter.DevicePixel) printable_size = self._printer.pageRect(QPrinter.DevicePixel)
origin = ( (printable_size.width() - img.width()) // 2, origin = ( (printable_size.width() - img.width()) // 2,