Logging added
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import configparser, os
|
||||
import configparser, os, logging
|
||||
|
||||
|
||||
class Config:
|
||||
@@ -29,16 +29,20 @@ class Config:
|
||||
|
||||
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):
|
||||
|
||||
logging.info('Reading config file "%s"', self._filename)
|
||||
self._cfg.read(self._filename)
|
||||
|
||||
|
||||
def write(self):
|
||||
|
||||
logging.info('Writing config file "%s"', self._filename)
|
||||
with open(self._filename, 'w') as configfile:
|
||||
self._cfg.write(configfile)
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# from time import time, localtime, strftime
|
||||
import logging
|
||||
|
||||
from PIL import Image, ImageOps
|
||||
|
||||
from .PictureList import PictureList
|
||||
from .PictureDimensions import PictureDimensions
|
||||
|
||||
from . import gui
|
||||
|
||||
from.Worker import PictureSaver
|
||||
from .Worker import PictureSaver
|
||||
|
||||
|
||||
class TeardownException(Exception):
|
||||
@@ -40,30 +39,35 @@ class Photobooth:
|
||||
self._cap = camera
|
||||
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')
|
||||
and self._cap.hasPreview ):
|
||||
logging.info('Countdown with preview activated')
|
||||
self._show_counter = self.showCounterPreview
|
||||
else:
|
||||
logging.info('Countdown without preview activated')
|
||||
self._show_counter = self.showCounterNoPreview
|
||||
|
||||
|
||||
def initGpio(self, config):
|
||||
|
||||
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
|
||||
|
||||
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._lampOff = lambda : self._gpio.lampOff(lamp)
|
||||
|
||||
self._gpio.setButton(config.getInt('Gpio', 'trigger_pin'), self.gpioTrigger)
|
||||
self._gpio.setButton(config.getInt('Gpio', 'exit_pin'), self.gpioExit)
|
||||
self._gpio.setButton(triger_pin, self.gpioTrigger)
|
||||
self._gpio.setButton(exit_pin, self.gpioExit)
|
||||
else:
|
||||
self._lampOn = lambda : None
|
||||
self._lampOff = lambda : None
|
||||
@@ -71,7 +75,7 @@ class Photobooth:
|
||||
|
||||
def teardown(self):
|
||||
|
||||
print('Camera teardown')
|
||||
logging.info('Teardown of camera')
|
||||
self.triggerOff()
|
||||
self.setCameraIdle()
|
||||
|
||||
@@ -83,7 +87,7 @@ class Photobooth:
|
||||
try:
|
||||
event_idx = expected.index(str(event))
|
||||
except ValueError:
|
||||
print('Photobooth: Unknown event received: ' + str(event))
|
||||
logging.error('Unknown event received: %s', str(event))
|
||||
raise ValueError('Unknown event received', str(event))
|
||||
|
||||
return event_idx
|
||||
@@ -94,7 +98,7 @@ class Photobooth:
|
||||
events = ['ack', 'cancel', 'teardown']
|
||||
|
||||
if self.recvEvent(events) != 0:
|
||||
print('Teardown of Photobooth requested')
|
||||
logging.info('Teardown of camera requested')
|
||||
raise TeardownException()
|
||||
|
||||
|
||||
@@ -103,16 +107,10 @@ class Photobooth:
|
||||
events = ['triggered', 'teardown']
|
||||
|
||||
if self.recvEvent(events) != 0:
|
||||
print('Teardown of Photobooth requested')
|
||||
logging.info('Teardown of camera requested')
|
||||
raise TeardownException()
|
||||
|
||||
|
||||
# @property
|
||||
# def getNextFilename(self):
|
||||
|
||||
# return self._get_next_filename
|
||||
|
||||
|
||||
@property
|
||||
def showCounter(self):
|
||||
|
||||
@@ -140,7 +138,7 @@ class Photobooth:
|
||||
try:
|
||||
self.trigger()
|
||||
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.recvAck()
|
||||
|
||||
@@ -175,7 +173,6 @@ class Photobooth:
|
||||
|
||||
self._conn.send(gui.CountdownState())
|
||||
self.recvAck()
|
||||
print('ack received')
|
||||
|
||||
|
||||
def showPose(self):
|
||||
@@ -209,11 +206,13 @@ class Photobooth:
|
||||
def enqueueWorkerTasks(self, picture):
|
||||
|
||||
for task in self._worker_list:
|
||||
self._queue.put(( task.do, (picture, ) ))
|
||||
self._queue.put(task.get(picture))
|
||||
|
||||
|
||||
def trigger(self):
|
||||
|
||||
logging.info('Photobooth triggered')
|
||||
|
||||
self._conn.send(gui.GreeterState())
|
||||
self.triggerOff()
|
||||
self.setCameraActive()
|
||||
@@ -224,7 +223,6 @@ class Photobooth:
|
||||
self._conn.send(gui.AssembleState())
|
||||
|
||||
img = self.assemblePictures(pics)
|
||||
# img.save(self.getNextFilename(), 'JPEG')
|
||||
self._conn.send(gui.PictureState(img))
|
||||
|
||||
self.enqueueWorkerTasks(img)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import os, logging
|
||||
|
||||
from glob import glob
|
||||
|
||||
@@ -43,11 +43,13 @@ class PictureList:
|
||||
else:
|
||||
pictures.sort()
|
||||
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('Info: Number of last existing file: ' + str(self.counter))
|
||||
print('Info: Saving assembled pictures as: ' + self.basename + (self.count_width * 'X') + '.jpg')
|
||||
logging.info('Number of last existing file: %d', self.counter)
|
||||
logging.info('Saving assembled pictures as "%s%s.%s"', self.basename,
|
||||
self.count_width * 'X', 'jpg')
|
||||
|
||||
|
||||
def getFilename(self, count):
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
|
||||
from time import localtime, strftime
|
||||
|
||||
from .PictureList import PictureList
|
||||
@@ -13,7 +15,7 @@ class WorkerTask:
|
||||
assert not kwargs
|
||||
|
||||
|
||||
def do(self, picture):
|
||||
def get(self, picture):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@@ -25,21 +27,20 @@ class PictureSaver(WorkerTask):
|
||||
|
||||
super().__init__()
|
||||
|
||||
picture_basename = strftime(config.get('Picture', 'basename'), localtime())
|
||||
self._pic_list = PictureList(picture_basename)
|
||||
self._get_next_filename = self._pic_list.getNext
|
||||
basename = strftime(config.get('Picture', 'basename'), localtime())
|
||||
self._pic_list = PictureList(basename)
|
||||
|
||||
|
||||
@property
|
||||
def getNextFilename(self):
|
||||
@staticmethod
|
||||
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')
|
||||
picture.save(self.getNextFilename(), 'JPEG')
|
||||
return (self.do, (picture, self._pic_list.getNext()))
|
||||
|
||||
|
||||
|
||||
@@ -52,7 +53,7 @@ class Worker:
|
||||
|
||||
def run(self):
|
||||
|
||||
for func, args in iter(self._queue.get, ('teardown', None)):
|
||||
for func, args in iter(self._queue.get, 'teardown'):
|
||||
func(*args)
|
||||
|
||||
return 0
|
||||
|
||||
@@ -20,6 +20,8 @@ class CameraGphoto2(Camera):
|
||||
self.hasIdle = False
|
||||
self._isActive = False
|
||||
|
||||
logging.info('Using python-gphoto2 bindings')
|
||||
|
||||
self._setupLogging()
|
||||
self._setupCamera()
|
||||
|
||||
@@ -27,14 +29,10 @@ class CameraGphoto2(Camera):
|
||||
def cleanup(self):
|
||||
|
||||
self._cap.exit(self._ctxt)
|
||||
# self.setIdle()
|
||||
|
||||
|
||||
def _setupLogging(self):
|
||||
|
||||
logging.basicConfig(
|
||||
format='%(levelname)s: %(name)s: %(message)s',
|
||||
level=logging.ERROR)
|
||||
gp.check_result(gp.use_python_logging())
|
||||
|
||||
|
||||
@@ -67,9 +65,7 @@ class CameraGphoto2(Camera):
|
||||
# self.setActive()
|
||||
|
||||
text = self._cap.get_summary(self._ctxt)
|
||||
print('Summary')
|
||||
print('=======')
|
||||
print(str(text))
|
||||
logging.info('Camera summary: %s', str(text))
|
||||
|
||||
# self.setIdle()
|
||||
|
||||
|
||||
@@ -19,10 +19,7 @@ class CameraGphoto2Cffi(Camera):
|
||||
self.hasPreview = True
|
||||
self.hasIdle = True
|
||||
|
||||
# Avoid output cluttered
|
||||
logging.basicConfig(
|
||||
format='%(levelname)s: %(name)s: %(message)s',
|
||||
level=logging.CRITICAL)
|
||||
logging.info('Using gphoto2-cffi bindings')
|
||||
|
||||
self._setupCamera()
|
||||
|
||||
@@ -30,7 +27,7 @@ class CameraGphoto2Cffi(Camera):
|
||||
def _setupCamera(self):
|
||||
|
||||
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():
|
||||
raise RuntimeError('Camera file format is set to RAW')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from PIL import Image
|
||||
import os, subprocess
|
||||
import os, subprocess, logging
|
||||
|
||||
from . import Camera
|
||||
|
||||
@@ -15,9 +15,13 @@ class CameraGphoto2CommandLine(Camera):
|
||||
self.hasPreview = False
|
||||
self.hasIdle = False
|
||||
|
||||
logging.info('Using gphoto2 via command line')
|
||||
|
||||
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'
|
||||
else:
|
||||
logging.debug('Storing temp files to "/tmp/photobooth.jpg"')
|
||||
self._tmp_filename = '/tmp/photobooth.jpg'
|
||||
|
||||
self.setActive()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
|
||||
from PIL import Image
|
||||
|
||||
import cv2
|
||||
@@ -16,6 +18,8 @@ class CameraOpenCV(Camera):
|
||||
self.hasPreview = True
|
||||
self.hasIdle = True
|
||||
|
||||
logging.info('Using OpenCV')
|
||||
|
||||
self._cap = cv2.VideoCapture()
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from .. import printer
|
||||
@@ -19,6 +21,11 @@ class GuiPostprocess:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def confirm(self, picture):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
|
||||
|
||||
class PrintPostprocess(GuiPostprocess):
|
||||
@@ -33,17 +40,15 @@ class PrintPostprocess(GuiPostprocess):
|
||||
|
||||
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:
|
||||
# self._printer.print(picture)
|
||||
def confirm(self, picture):
|
||||
|
||||
return PrintState(lambda : None, True)
|
||||
|
||||
|
||||
def do(self, picture):
|
||||
|
||||
print('Printing')
|
||||
logging.info('Printing picture')
|
||||
self._printer.print(picture)
|
||||
|
||||
@@ -147,11 +147,12 @@ class TeardownState(GuiState):
|
||||
|
||||
class PrintState(GuiState):
|
||||
|
||||
def __init__(self, handler, **kwargs):
|
||||
def __init__(self, handler, confirmed, **kwargs):
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.handler = handler
|
||||
self.confirmed = confirmed
|
||||
|
||||
|
||||
@property
|
||||
@@ -166,4 +167,19 @@ class PrintState(GuiState):
|
||||
if not callable(handler):
|
||||
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
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import multiprocessing as mp
|
||||
import queue
|
||||
import logging
|
||||
|
||||
from PIL import ImageQt
|
||||
|
||||
@@ -141,7 +142,6 @@ class PyQt5Gui(Gui):
|
||||
elif isinstance(state, PictureState):
|
||||
img = ImageQt.ImageQt(state.picture)
|
||||
self._p.setCentralWidget(PyQt5PictureMessage('', img))
|
||||
# QTimer.singleShot(cfg.getInt('Photobooth', 'display_time') * 1000, self.sendAck)
|
||||
QTimer.singleShot(cfg.getInt('Photobooth', 'display_time') * 1000,
|
||||
lambda : self.postprocessPicture(state.picture))
|
||||
|
||||
@@ -179,6 +179,8 @@ class PyQt5Gui(Gui):
|
||||
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
||||
if reply == QMessageBox.Yes:
|
||||
task.handler()
|
||||
QMessageBox.information(self._p, 'Printing',
|
||||
'Picture sent to printer.', QMessageBox.Ok)
|
||||
else:
|
||||
raise ValueError('Unknown task')
|
||||
|
||||
@@ -217,9 +219,9 @@ class PyQt5Gui(Gui):
|
||||
|
||||
def showError(self, title, message):
|
||||
|
||||
print('ERROR: ' + title + ': ' + message)
|
||||
reply = QMessageBox.warning(self._p, title, message, QMessageBox.Close | QMessageBox.Retry,
|
||||
QMessageBox.Retry)
|
||||
logging.error('%s: %s', title, message)
|
||||
reply = QMessageBox.warning(self._p, title, message,
|
||||
QMessageBox.Close | QMessageBox.Retry, QMessageBox.Retry)
|
||||
if reply == QMessageBox.Retry:
|
||||
self.sendAck()
|
||||
self._lastState()
|
||||
|
||||
@@ -7,8 +7,9 @@ try:
|
||||
except DistributionNotFound:
|
||||
__version__ = 'unknown'
|
||||
|
||||
import multiprocessing as mp
|
||||
import sys
|
||||
import multiprocessing as mp
|
||||
import logging
|
||||
|
||||
from . import camera, gui
|
||||
from .Config import Config
|
||||
@@ -44,7 +45,7 @@ class CameraProcess(mp.Process):
|
||||
if str(event) in ('cancel', 'ack'):
|
||||
return 123
|
||||
else:
|
||||
print('Unknown event received: ' + str(event))
|
||||
logging.error('Unknown event received: %s', str(event))
|
||||
raise RuntimeError('Unknown event received', str(event))
|
||||
|
||||
|
||||
@@ -56,10 +57,11 @@ class CameraProcess(mp.Process):
|
||||
event = self.conn.recv()
|
||||
|
||||
if str(event) != 'start':
|
||||
logging.warning('Unknown event received: %s', str(event))
|
||||
continue
|
||||
|
||||
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)
|
||||
|
||||
@@ -100,13 +102,16 @@ class GuiProcess(mp.Process):
|
||||
|
||||
def run(argv):
|
||||
|
||||
print('Photobooth version:', __version__)
|
||||
logging.info('Photobooth version: %s', __version__)
|
||||
|
||||
# Load configuration
|
||||
config = Config('photobooth.cfg')
|
||||
|
||||
# Create communication objects
|
||||
gui_conn, camera_conn = mp.Pipe()
|
||||
worker_queue = mp.SimpleQueue()
|
||||
|
||||
# Initialize processes
|
||||
camera_proc = CameraProcess(config, camera_conn, worker_queue)
|
||||
camera_proc.start()
|
||||
|
||||
@@ -116,11 +121,13 @@ def run(argv):
|
||||
gui_proc = GuiProcess(argv, config, gui_conn, worker_queue)
|
||||
gui_proc.start()
|
||||
|
||||
# Close endpoints
|
||||
gui_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()
|
||||
camera_proc.join(5)
|
||||
return gui_proc.exitcode
|
||||
@@ -128,6 +135,9 @@ def run(argv):
|
||||
|
||||
def main(argv):
|
||||
|
||||
logging.basicConfig(filename='photobooth.log', level=logging.INFO)
|
||||
logging.getLogger().addHandler(logging.StreamHandler())
|
||||
|
||||
known_status_codes = {
|
||||
999: 'Initializing photobooth',
|
||||
123: 'Restarting photobooth and reloading config'
|
||||
@@ -136,8 +146,10 @@ def main(argv):
|
||||
status_code = 999
|
||||
|
||||
while status_code in known_status_codes:
|
||||
print(known_status_codes[status_code])
|
||||
logging.info(known_status_codes[status_code])
|
||||
|
||||
status_code = run(argv)
|
||||
|
||||
logging.info('Exiting photobooth with status code %d', status_code)
|
||||
|
||||
return status_code
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
|
||||
from PIL import ImageQt
|
||||
|
||||
from PyQt5.QtCore import Qt, QPoint, QSizeF
|
||||
@@ -18,8 +20,11 @@ class PrinterPyQt5(Printer):
|
||||
self._printer = QPrinter(QPrinter.HighResolution)
|
||||
self._printer.setPageSize(QPageSize(QSizeF(*page_size), QPageSize.Millimeter))
|
||||
|
||||
logging.info('Using printer "%s"', self._printer.printerName())
|
||||
|
||||
self._print_pdf = print_pdf
|
||||
if self._print_pdf:
|
||||
logging.info('Using PDF printer')
|
||||
self._counter = 0
|
||||
self._printer.setOutputFormat(QPrinter.PdfFormat)
|
||||
self._printer.setFullPage(True)
|
||||
@@ -32,7 +37,8 @@ class PrinterPyQt5(Printer):
|
||||
self._counter += 1
|
||||
|
||||
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)
|
||||
origin = ( (printable_size.width() - img.width()) // 2,
|
||||
|
||||
Reference in New Issue
Block a user