Postprocessing dialogue prettified

This commit is contained in:
Balthasar Reuter
2018-07-03 01:30:00 +02:00
parent 3c78be2d71
commit 0a752d310b
9 changed files with 307 additions and 160 deletions

View File

@@ -46,6 +46,8 @@ greeter_time = 3
countdown_time = 8
# Display time of assembled picture (shown after last shot)
display_time = 5
# Timeout for postprocessing (shown after review)
postprocess_time = 60
[Picture]
# Basedir of output pictures

View File

@@ -1,62 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Photobooth - a flexible photo booth software
# Copyright (C) 2018 Balthasar Reuter <photobooth at re - web dot eu>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from .. import printer
from ..util import lookup_and_import
from .GuiState import PrintState
class GuiPostprocess:
def __init__(self, **kwargs):
assert not kwargs
def get(self, picture):
raise NotImplementedError()
def confirm(self, picture):
raise NotImplementedError()
class PrintPostprocess(GuiPostprocess):
def __init__(self, printer_module, page_size, **kwargs):
super().__init__(**kwargs)
Printer = lookup_and_import(printer.modules, printer_module, 'printer')
self._printer = Printer(page_size, True)
def get(self, picture):
return PrintState(lambda: self.do(picture), False)
def confirm(self, picture):
return PrintState(lambda: None, True)
def do(self, picture):
logging.info('Printing picture')
self._printer.print(picture)

View File

@@ -0,0 +1,104 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Photobooth - a flexible photo booth software
# Copyright (C) 2018 Balthasar Reuter <photobooth at re - web dot eu>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import queue
from .. import printer
from ..util import lookup_and_import
class GuiPostprocessor:
def __init__(self, config):
super().__init__()
self._task_list = []
self._queue = queue.Queue()
if config.getBool('Printer', 'enable'):
module = config.get('Printer', 'module')
size = (config.getInt('Printer', 'width'),
config.getInt('Printer', 'height'))
self._task_list.append(PrintPostprocess(module, size))
def get(self, picture):
tasks = [task.get(picture) for task in self._task_list]
return tasks
class PostprocessTask:
def __init__(self):
super().__init__()
def get(self, picture):
raise NotImplementedError()
class PostprocessItem:
def __init__(self, label, action):
super().__init__()
self.label = label
self.action = action
@property
def label(self):
return self._label
@label.setter
def label(self, label):
if not isinstance(label, str):
raise TypeError('Label must be a string')
self._label = label
@property
def action(self):
return self._action
@action.setter
def action(self, action):
if not callable(action):
raise TypeError('Action must be callable')
self._action = action
class PrintPostprocess(PostprocessTask):
def __init__(self, printer_module, page_size, **kwargs):
super().__init__(**kwargs)
Printer = lookup_and_import(printer.modules, printer_module, 'printer')
self._printer = Printer(page_size, True)
def get(self, picture):
return PostprocessItem('Print', lambda: self._printer.print(picture))

View File

@@ -309,6 +309,44 @@ class CountdownMessage(QtWidgets.QFrame):
painter.end()
class PostprocessMessage(Widgets.TransparentOverlay):
def __init__(self, parent, tasks, idle_handle, timeout=None,
timeout_handle=None):
if timeout_handle is None:
timeout_handle = idle_handle
super().__init__(parent, timeout, timeout_handle)
self.setObjectName('PostprocessMessage')
self.initFrame(tasks, idle_handle)
def initFrame(self, tasks, idle_handle):
def disableAndCall(button, handle):
button.setEnabled(False)
handle()
def createButton(task):
button = QtWidgets.QPushButton(task.label)
button.clicked.connect(lambda: disableAndCall(button, task.action))
return button
buttons = [createButton(task) for task in tasks]
buttons.append(QtWidgets.QPushButton('Start over'))
buttons[-1].clicked.connect(idle_handle)
button_lay = QtWidgets.QGridLayout()
for i, button in enumerate(buttons):
pos = divmod(i, 2)
button_lay.addWidget(button, *pos)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(QtWidgets.QLabel('Happy?'))
layout.addLayout(button_lay)
self.setLayout(layout)
class SetDateTime(QtWidgets.QFrame):
def __init__(self, cancel_action, restart_action):
@@ -539,11 +577,18 @@ class Settings(QtWidgets.QFrame):
displ_time.setValue(self._cfg.getInt('Photobooth', 'display_time'))
self.add('Photobooth', 'display_time', displ_time)
postproc_time = QtWidgets.QSpinBox()
postproc_time.setRange(0, 1000)
postproc_time.setValue(self._cfg.getInt('Photobooth',
'postprocess_time'))
self.add('Photobooth', 'postprocess_time', postproc_time)
layout = QtWidgets.QFormLayout()
layout.addRow('Show preview during countdown:', preview)
layout.addRow('Greeter time before countdown [s]:', greet_time)
layout.addRow('Countdown time [s]:', count_time)
layout.addRow('Picture display time [s]:', displ_time)
layout.addRow('Postprocess timeout [s]:', postproc_time)
widget = QtWidgets.QWidget()
widget.setLayout(layout)
@@ -734,6 +779,8 @@ class Settings(QtWidgets.QFrame):
str(self.get('Photobooth', 'countdown_time').text()))
self._cfg.set('Photobooth', 'display_time',
str(self.get('Photobooth', 'display_time').text()))
self._cfg.set('Photobooth', 'postprocess_time',
str(self.get('Photobooth', 'postprocess_time').text()))
self._cfg.set('Camera', 'module',
camera.modules[self.get('Camera',

View File

@@ -1,61 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Photobooth - a flexible photo booth software
# Copyright (C) 2018 Balthasar Reuter <photobooth at re - web dot eu>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import queue
from .. import GuiState
from ..GuiPostprocess import PrintPostprocess
class Postprocessor:
def __init__(self, config):
super().__init__()
self._task_list = []
self._queue = queue.Queue()
if config.getBool('Printer', 'enable'):
module = config.get('Printer', 'module')
size = (config.getInt('Printer', 'width'),
config.getInt('Printer', 'height'))
self._task_list.append(PrintPostprocess(module, size))
def fill(self, picture):
for task in self._task_list:
self._queue.put(task.get(picture))
def work(self, msg_box):
while True:
try:
task = self._queue.get(block=False)
except queue.Empty:
return
if isinstance(task, GuiState.PrintState):
if msg_box.question('Print picture?',
'Do you want to print the picture?'):
task.handler()
msg_box.information('Printing...',
'Picture sent to printer.')
else:
raise ValueError('Unknown task')

View File

@@ -28,10 +28,10 @@ from PIL import ImageQt
from .. import GuiState
from ..GuiSkeleton import GuiSkeleton
from ..GuiPostprocessor import GuiPostprocessor
from . import styles
from . import Frames
from . import Postprocessor
from . import Receiver
@@ -48,7 +48,7 @@ class PyQt5Gui(GuiSkeleton):
self._initUI(argv)
self._initReceiver()
self._postprocess = Postprocessor.Postprocessor(self._cfg)
self._postprocess = GuiPostprocessor(self._cfg)
def run(self):
@@ -152,12 +152,6 @@ class PyQt5Gui(GuiSkeleton):
elif self._is_trigger and event.key() == QtCore.Qt.Key_Space:
self.handleState(GuiState.TriggerState())
def _postprocessPicture(self, picture):
self._postprocess.fill(picture)
self._postprocess.work(MessageBox(self._gui))
self._sendAck()
def _showWelcomeScreen(self):
self._disableTrigger()
@@ -239,22 +233,27 @@ class PyQt5Gui(GuiSkeleton):
review_time = self._cfg.getInt('Photobooth', 'display_time') * 1000
self._setWidget(Frames.PictureMessage(img))
QtCore.QTimer.singleShot(review_time, lambda:
self._postprocessPicture(state.picture))
self._showPostprocess(state.picture))
def _showPostprocess(self, picture):
tasks = self._postprocess.get(picture)
postproc_t = self._cfg.getInt('Photobooth', 'postprocess_time')
Frames.PostprocessMessage(self._gui.centralWidget(), tasks,
self._sendAck, postproc_t * 1000)
def _showError(self, state):
logging.error('%s: %s', state.title, state.message)
reply = QtWidgets.QMessageBox.warning(self._gui, state.title,
state.message,
QtWidgets.QMessageBox.Close |
QtWidgets.QMessageBox.Retry,
QtWidgets.QMessageBox.Retry)
if reply == QtWidgets.QMessageBox.Retry:
self._sendAck()
self._lastState()
else:
self._sendCancel()
self._showWelcomeScreen()
def exec(*handles):
for handle in handles:
handle()
MessageBox(self, MessageBox.RETRY, state.title, state.message,
exec(self._sendAck, self._lastState),
exec(self._sendCancel, self._showWelcomeScreen))
class PyQt5MainWindow(QtWidgets.QMainWindow):
@@ -296,23 +295,67 @@ class PyQt5MainWindow(QtWidgets.QMainWindow):
self._handle_key(event)
class MessageBox:
class MessageBox(QtWidgets.QWidget):
def __init__(self, parent):
QUESTION = 1
RETRY = 2
INFORMATION = 3
super().__init__()
def __init__(self, parent, type, title, message, *handles):
self._parent = parent
super().__init__(parent)
def question(self, title, message):
if type == MessageBox.QUESTION:
self.question(title, message, *handles)
elif type == MessageBox.RETRY:
self.retry(title, message, *handles)
else:
raise ValueError('Unknown type specified')
reply = QtWidgets.QMessageBox.question(self._parent, title, message,
QtWidgets.QMessageBox.Yes |
QtWidgets.QMessageBox.No,
QtWidgets.QMessageBox.No)
return reply == QtWidgets.QMessageBox.Yes
def question(self, title, message, *handles):
def information(self, title, message):
lbl_title = QtWidgets.QLabel(title)
lbl_title.setObjectName('title')
QtWidgets.QMessageBox.information(self._parent, title, message,
QtWidgets.QMessageBox.Ok)
lbl_message = QtWidgets.QLabel(message)
lbl_message.setObjectName('message')
btn_yes = QtWidgets.QPushButton('Yes')
btn_yes.clicked.connect(handles[0])
btn_no = QtWidgets.QPushButton('No')
btn_no.clicked.connect(handles[1])
lay_buttons = QtWidgets.QHBoxLayout()
lay_buttons.addWidget(btn_yes)
lay_buttons.addWidget(btn_no)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(lbl_title)
layout.addWidget(lbl_message)
layout.addLayout(lay_buttons)
self.setLayout(layout)
def retry(self, title, message, *handles):
lbl_title = QtWidgets.QLabel(title)
lbl_title.setObjectName('title')
lbl_message = QtWidgets.QLabel(message)
lbl_message.setObjectName('message')
btn_retry = QtWidgets.QPushButton('Retry')
btn_retry.clicked.connect(handles[0])
btn_cancel = QtWidgets.QPushButton('Cancel')
btn_cancel.clicked.connect(handles[1])
lay_buttons = QtWidgets.QHBoxLayout()
lay_buttons.addWidget(btn_retry)
lay_buttons.addWidget(btn_cancel)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(lbl_title)
layout.addWidget(lbl_message)
layout.addLayout(lay_buttons)
self.setLayout(layout)

View File

@@ -178,3 +178,35 @@ class RoundProgressBar(QtWidgets.QWidget):
self._drawText(painter, inner_rect, inner_radius)
painter.end()
class TransparentOverlay(QtWidgets.QWidget):
def __init__(self, parent, timeout=None, timeout_handle=None):
super().__init__(parent)
self.setObjectName('TransparentOverlay')
rect = parent.rect()
rect.adjust(50, 50, -50, -50)
self.setGeometry(rect)
if timeout is not None:
self._handle = timeout_handle
self._timer = self.startTimer(timeout)
self.show()
def paintEvent(self, event):
opt = QtWidgets.QStyleOption()
opt.initFrom(self)
painter = QtGui.QPainter(self)
self.style().drawPrimitive(QtWidgets.QStyle.PE_Widget, opt, painter,
self)
painter.end()
def timerEvent(self, event):
self.killTimer(self._timer)
self._handle()

View File

@@ -24,14 +24,14 @@ QPushButton {
}
QPushButton:pressed {
background-color: #66ffffff;
background-color: #66eeeeee;
}
/* Idle Screen */
QFrame#IdleMessage {
background-image: url(photobooth/gui/Qt5Gui/images/arrow.png);
background-repeat:; no-repeat;
background-repeat: no-repeat;
padding: 80px 400px 120px 80px;
}
@@ -80,9 +80,13 @@ QFrame#GreeterMessage QLabel#message {
/* Countdown Screen */
QFrame#CountdownMessage {
background-color: #eeeeee;
border-style: outset;
border-width: 2px;
border-radius: 30px;
border-color: #eeeeee;
margin: 20px;
padding: 30px;
background-color: #eeeeee;
}
/* Pose Screen */
@@ -109,12 +113,48 @@ QFrame#WaitMessage QLabel {
qproperty-alignment: AlignCenter;
}
/* Picture Screen*/
/* Picture Screen */
QFrame#PictureMessage {
margin: 30px;
}
/* Overlay message */
QWidget#TransparentOverlay {
background-color: #aaeeeeee;
border-style: outset;
border-width: 2px;
border-radius: 30px;
border-color: #eeeeee;
color: #333333;
padding: 40px;
}
/* Postprocess message */
QWidget#PostprocessMessage QLabel {
color: #333333;
font-size: 110px;
qproperty-alignment: AlignCenter;
}
QWidget#PostprocessMessage QPushButton {
color: #333333;
border-color: #333333;
margin: 20px;
}
QWidget#PostprocessMessage QPushButton:pressed {
background-color: #66eeeeee;
}
QWidget#PostprocessMessage QPushButton:disabled {
background-color: #66eeeeee;
color: #33eeeeee;
border-color: #33eeeeee;
}
/* Customizing settings */
QTabWidget::pane {

View File

@@ -53,6 +53,8 @@ class PrinterPyQt5(Printer):
self._printer.setOutputFileName('print_%d.pdf' % self._counter)
self._counter += 1
logging.info('Printing picture')
img = ImageQt.ImageQt(picture)
img = img.scaled(self._printer.pageRect().size(),
QtCore.Qt.KeepAspectRatio,