diff --git a/photobooth/gui/Qt5Gui/Frames.py b/photobooth/gui/Qt5Gui/Frames.py index f633dea..c766353 100644 --- a/photobooth/gui/Qt5Gui/Frames.py +++ b/photobooth/gui/Qt5Gui/Frames.py @@ -82,23 +82,22 @@ class IdleMessage(QtWidgets.QFrame): def __init__(self): super().__init__() + self.setObjectName('IdleMessage') - self._message = 'Hit the button!' + self._message_label = 'Hit the' + self._message_button = 'Button!' - def _paintMessage(self, painter): + self.initFrame() - f = self.font() - f.setPixelSize(self.height() / 5) - painter.setFont(f) + def initFrame(self): - rect = self.rect() - painter.drawText(rect, QtCore.Qt.AlignCenter, self._message) + lbl = QtWidgets.QLabel(self._message_label) + btn = QtWidgets.QPushButton(self._message_button) - def paintEvent(self, event): - - painter = QtGui.QPainter(self) - self._paintMessage(painter) - painter.end() + lay = QtWidgets.QVBoxLayout() + lay.addWidget(lbl) + lay.addWidget(btn) + self.setLayout(lay) class GreeterMessage(QtWidgets.QFrame): @@ -106,34 +105,31 @@ class GreeterMessage(QtWidgets.QFrame): def __init__(self, num_x, num_y): super().__init__() + self.setObjectName('GreeterMessage') - self._title = 'Get ready!' + self._text_title = 'Get ready!' + self._text_button = 'Start countdown' if num_x * num_y > 1: - self._text = ('Capturing {} pictures...'.format(num_x * num_y)) + self._text_label = ('for {} pictures...'.format(num_x * num_y)) else: - self._text = 'Starting the countdown...' + self._text_label = '' - def _paintMessage(self, painter): + self.initFrame() - f = self.font() + def initFrame(self): - f.setPixelSize(self.height() / 5) - painter.setFont(f) - rect = QtCore.QRect(0, self.height() * 1 / 5, - self.width(), self.height() * 3 / 10) - painter.drawText(rect, QtCore.Qt.AlignCenter, self._title) + ttl = QtWidgets.QLabel(self._text_title) + ttl.setObjectName('title') + btn = QtWidgets.QPushButton(self._text_button) + btn.setObjectName('button') + lbl = QtWidgets.QLabel(self._text_label) + lbl.setObjectName('message') - f.setPixelSize(self.height() / 8) - painter.setFont(f) - rect = QtCore.QRect(0, self.height() * 3 / 5, - self.width(), self.height() * 3 / 10) - painter.drawText(rect, QtCore.Qt.AlignCenter, self._text) - - def paintEvent(self, event): - - painter = QtGui.QPainter(self) - self._paintMessage(painter) - painter.end() + lay = QtWidgets.QVBoxLayout() + lay.addWidget(ttl) + lay.addWidget(btn) + lay.addWidget(lbl) + self.setLayout(lay) class PoseMessage(QtWidgets.QFrame): @@ -141,35 +137,22 @@ class PoseMessage(QtWidgets.QFrame): def __init__(self, num_picture, num_x, num_y): super().__init__() + self.setObjectName('PoseMessage') - self._title = 'Pose!' if num_x * num_y > 1: self._text = 'Picture {} of {}...'.format(num_picture, num_x * num_y) else: self._text = 'Taking a photo...' - def _paintMessage(self, painter): + self.initFrame() - f = self.font() + def initFrame(self): - f.setPixelSize(self.height() / 5) - painter.setFont(f) - rect = QtCore.QRect(0, self.height() * 1 / 5, - self.width(), self.height() * 3 / 10) - painter.drawText(rect, QtCore.Qt.AlignCenter, self._title) - - f.setPixelSize(self.height() / 8) - painter.setFont(f) - rect = QtCore.QRect(0, self.height() * 3 / 5, - self.width(), self.height() * 3 / 10) - painter.drawText(rect, QtCore.Qt.AlignCenter, self._text) - - def paintEvent(self, event): - - painter = QtGui.QPainter(self) - self._paintMessage(painter) - painter.end() + lbl = QtWidgets.QLabel(self._text) + lay = QtWidgets.QVBoxLayout() + lay.addWidget(lbl) + self.setLayout(lay) class PictureMessage(QtWidgets.QFrame): @@ -177,6 +160,7 @@ class PictureMessage(QtWidgets.QFrame): def __init__(self, picture): super().__init__() + self.setObjectName('PictureMessage') self._picture = picture @@ -205,10 +189,20 @@ class WaitMessage(QtWidgets.QFrame): def __init__(self, message): super().__init__() + self.setObjectName('WaitMessage') - self._message = message + self._text = message self._clock = Widgets.SpinningWaitClock() + self.initFrame() + + def initFrame(self): + + lbl = QtWidgets.QLabel(self._text) + lay = QtWidgets.QVBoxLayout() + lay.addWidget(lbl) + self.setLayout(lay) + def showEvent(self, event): self.startTimer(100) @@ -218,23 +212,12 @@ class WaitMessage(QtWidgets.QFrame): self._clock.value += 1 self.update() - def _paintMessage(self, painter): - - f = self.font() - f.setPixelSize(self.height() / 8) - painter.setFont(f) - - rect = QtCore.QRect(0, self.height() * 3 / 5, self.width(), - self.height() * 3 / 10) - painter.drawText(rect, QtCore.Qt.AlignCenter, self._message) - def paintEvent(self, event): offset = ((self.width() - self._clock.width()) // 2, (self.height() - self._clock.height()) // 2) painter = QtGui.QPainter(self) - self._paintMessage(painter) self._clock.render(painter, QtCore.QPoint(*offset), self._clock.visibleRegion(), QtWidgets.QWidget.DrawChildren) @@ -246,6 +229,7 @@ class CountdownMessage(QtWidgets.QFrame): def __init__(self, time, action): super().__init__() + self.setObjectName('CountdownMessage') self._step_size = 50 self._value = time * (1000 // self._step_size) @@ -309,7 +293,8 @@ class CountdownMessage(QtWidgets.QFrame): if self.picture is not None: pix = QtGui.QPixmap.fromImage(self.picture) - pix = pix.scaled(self.size(), QtCore.Qt.KeepAspectRatio, + pix = pix.scaled(self.contentsRect().size(), + QtCore.Qt.KeepAspectRatio, QtCore.Qt.FastTransformation) origin = ((self.width() - pix.width()) // 2, (self.height() - pix.height()) // 2) @@ -479,6 +464,10 @@ class Settings(QtWidgets.QFrame): idx = [x for x, m in enumerate(module_list) if m[0] == current_module] cb.setCurrentIndex(idx[0] if len(idx) > 0 else -1) + # Fix bug in Qt to allow changing the items in a stylesheet + delegate = QtWidgets.QStyledItemDelegate() + cb.setItemDelegate(delegate) + return cb def createGuiSettings(self): @@ -645,11 +634,11 @@ class Settings(QtWidgets.QFrame): layout = QtWidgets.QFormLayout() layout.addRow('Number of shots per picture:', lay_num) layout.addRow('Size of assembled picture [px]:', lay_size) - layout.addRow('Minimum distance between shots in picture [px]:', + layout.addRow('Min. distance between shots [px]:', lay_dist) - layout.addRow('Output directory (strftime directives possible):', + layout.addRow('Output directory (strftime possible):', lay_file) - layout.addRow('Basename of files (strftime directives possible):', + layout.addRow('Basename of files (strftime possible):', basename) widget = QtWidgets.QWidget() diff --git a/photobooth/gui/Qt5Gui/PyQt5Gui.py b/photobooth/gui/Qt5Gui/PyQt5Gui.py index 3c513f4..1e17f88 100644 --- a/photobooth/gui/Qt5Gui/PyQt5Gui.py +++ b/photobooth/gui/Qt5Gui/PyQt5Gui.py @@ -21,6 +21,7 @@ import logging import os from PyQt5 import QtCore +from PyQt5 import QtGui from PyQt5 import QtWidgets from PIL import ImageQt @@ -91,6 +92,12 @@ class PyQt5Gui(GuiSkeleton): self._app.setStyleSheet(stylesheet) self._gui = PyQt5MainWindow(self._cfg, self._handleKeypressEvent) + fonts = ['photobooth/gui/Qt5Gui/fonts/AmaticSC-Regular.ttf', + 'photobooth/gui/Qt5Gui/fonts/AmaticSC-Bold.ttf'] + self._fonts = QtGui.QFontDatabase() + for font in fonts: + self._fonts.addApplicationFont(font) + def _initReceiver(self): self._receiver = Receiver.Receiver([self._conn]) @@ -200,7 +207,7 @@ class PyQt5Gui(GuiSkeleton): self._disableTrigger() num_pic = (self._cfg.getInt('Picture', 'num_x'), - self._cfg.getInt('Picture', 'num_x')) + self._cfg.getInt('Picture', 'num_y')) greeter_time = self._cfg.getInt('Photobooth', 'greeter_time') * 1000 self._setWidget(Frames.GreeterMessage(*num_pic)) @@ -219,7 +226,7 @@ class PyQt5Gui(GuiSkeleton): def _showPose(self, state): num_pic = (self._cfg.getInt('Picture', 'num_x'), - self._cfg.getInt('Picture', 'num_x')) + self._cfg.getInt('Picture', 'num_y')) self._setWidget(Frames.PoseMessage(state.num_picture, *num_pic)) def _showAssemble(self, state): @@ -267,8 +274,8 @@ class PyQt5MainWindow(QtWidgets.QMainWindow): if self._cfg.getBool('Gui', 'fullscreen'): self.showFullScreen() else: - self.resize(self._cfg.getInt('Gui', 'width'), - self._cfg.getInt('Gui', 'height')) + self.setFixedSize(self._cfg.getInt('Gui', 'width'), + self._cfg.getInt('Gui', 'height')) self.show() def closeEvent(self, e): diff --git a/photobooth/gui/Qt5Gui/__init__.py b/photobooth/gui/Qt5Gui/__init__.py index c4c6ee5..dd001be 100644 --- a/photobooth/gui/Qt5Gui/__init__.py +++ b/photobooth/gui/Qt5Gui/__init__.py @@ -19,6 +19,7 @@ # Available style sheets as tuples of (style name, style file) styles = (('default', 'stylesheets/default.qss'), - ('dark', 'stylesheets/dark.qss')) + ('dark', 'stylesheets/dark.qss'), + ('pastel', 'stylesheets/pastel.qss')) from .PyQt5Gui import PyQt5Gui # noqa diff --git a/photobooth/gui/Qt5Gui/fonts/AmaticSC-Bold.ttf b/photobooth/gui/Qt5Gui/fonts/AmaticSC-Bold.ttf new file mode 100644 index 0000000..96a7ab6 Binary files /dev/null and b/photobooth/gui/Qt5Gui/fonts/AmaticSC-Bold.ttf differ diff --git a/photobooth/gui/Qt5Gui/fonts/AmaticSC-Regular.ttf b/photobooth/gui/Qt5Gui/fonts/AmaticSC-Regular.ttf new file mode 100644 index 0000000..fc4c362 Binary files /dev/null and b/photobooth/gui/Qt5Gui/fonts/AmaticSC-Regular.ttf differ diff --git a/photobooth/gui/Qt5Gui/fonts/OFL.txt b/photobooth/gui/Qt5Gui/fonts/OFL.txt new file mode 100644 index 0000000..d6d6630 --- /dev/null +++ b/photobooth/gui/Qt5Gui/fonts/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2015 The Amatic SC Project Authors (https://github.com/googlefonts/AmaticSC) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/photobooth/gui/Qt5Gui/images/arrow.png b/photobooth/gui/Qt5Gui/images/arrow.png new file mode 100644 index 0000000..f7d32ac Binary files /dev/null and b/photobooth/gui/Qt5Gui/images/arrow.png differ diff --git a/photobooth/gui/Qt5Gui/images/arrow.svg b/photobooth/gui/Qt5Gui/images/arrow.svg new file mode 100644 index 0000000..f6ebb4c --- /dev/null +++ b/photobooth/gui/Qt5Gui/images/arrow.svg @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/photobooth/gui/Qt5Gui/images/camera.png b/photobooth/gui/Qt5Gui/images/camera.png new file mode 100644 index 0000000..9ca2139 Binary files /dev/null and b/photobooth/gui/Qt5Gui/images/camera.png differ diff --git a/photobooth/gui/Qt5Gui/images/camera.svg b/photobooth/gui/Qt5Gui/images/camera.svg new file mode 100644 index 0000000..81e49a5 --- /dev/null +++ b/photobooth/gui/Qt5Gui/images/camera.svg @@ -0,0 +1,82 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/photobooth/gui/Qt5Gui/images/checkmark.png b/photobooth/gui/Qt5Gui/images/checkmark.png new file mode 100644 index 0000000..44868ff Binary files /dev/null and b/photobooth/gui/Qt5Gui/images/checkmark.png differ diff --git a/photobooth/gui/Qt5Gui/images/checkmark.svg b/photobooth/gui/Qt5Gui/images/checkmark.svg new file mode 100644 index 0000000..634ac28 --- /dev/null +++ b/photobooth/gui/Qt5Gui/images/checkmark.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/photobooth/gui/Qt5Gui/images/down.png b/photobooth/gui/Qt5Gui/images/down.png new file mode 100644 index 0000000..a5012aa Binary files /dev/null and b/photobooth/gui/Qt5Gui/images/down.png differ diff --git a/photobooth/gui/Qt5Gui/images/up-down.svg b/photobooth/gui/Qt5Gui/images/up-down.svg new file mode 100644 index 0000000..318c4d5 --- /dev/null +++ b/photobooth/gui/Qt5Gui/images/up-down.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/photobooth/gui/Qt5Gui/images/up.png b/photobooth/gui/Qt5Gui/images/up.png new file mode 100644 index 0000000..640acf6 Binary files /dev/null and b/photobooth/gui/Qt5Gui/images/up.png differ diff --git a/photobooth/gui/Qt5Gui/stylesheets/pastel.qss b/photobooth/gui/Qt5Gui/stylesheets/pastel.qss new file mode 100644 index 0000000..19635cd --- /dev/null +++ b/photobooth/gui/Qt5Gui/stylesheets/pastel.qss @@ -0,0 +1,182 @@ +/* Outer items */ + +QWidget { + background-color: transparent; + color: #eeeeee; + font-family: AmaticSC, sans-serif; + font-size: 50px; +} + +QMainWindow { + background: #ffffff qlineargradient(x1:0 y1:1, x2:1, y2:0, stop:0 rgba(255,165,150,255), stop:1 rgba(0,228,255,112)); + color: #eeeeee; +} + +/* General controls */ + +QPushButton { + background-color: transparent; + border-style: outset; + border-width: 1px; + border-radius: 15px; + border-color: #eeeeee; + padding: 10px; +} + +QPushButton:pressed { + background-color: #66ffffff; +} + +/* Idle Screen */ + +QFrame#IdleMessage { + background-image: url(photobooth/gui/Qt5Gui/images/arrow.png); + background-repeat:; no-repeat; + padding: 80px 400px 120px 80px; +} + +QFrame#IdleMessage QLabel { + font-size: 160px; + qproperty-alignment: AlignCenter; +} + +QFrame#IdleMessage QPushButton { + border: none; + color: rgba(255, 27, 0, 200); + font-size: 200px; + text-align: center; +} + +QFrame#IdleMessage QPushButton:pressed { + background-color: rgba(255, 27, 0, 200); + color: #eeeeee; +} + +/* Greeter Screen */ + +QFrame#GreeterMessage { + padding: 50px; +} + +QFrame#GreeterMessage QLabel#title { + font-size: 180px; + qproperty-alignment: AlignCenter; +} + +QFrame#GreeterMessage QPushButton#button { + border: none; + font-size: 120px; + margin: 60px 0 20px 0; + min-height: 160px; + padding: 0; + text-align: center; +} + +QFrame#GreeterMessage QLabel#message { + font-size: 120px; + qproperty-alignment: AlignCenter; +} + +/* Countdown Screen */ + +QFrame#CountdownMessage { + margin: 20px; + padding: 30px; + background-color: #eeeeee; +} + +/* Pose Screen */ + +QFrame#PoseMessage { + background-image: url(photobooth/gui/Qt5Gui/images/camera.png); + background-repeat: no-repeat; + padding: 380px 80px 80px 80px; +} + +QFrame#PoseMessage QLabel { + font-size: 120px; + qproperty-alignment: AlignCenter; +} + +/* Wait Screen */ + +QFrame#WaitMessage { + padding: 350px 80px 80px 80px; +} + +QFrame#WaitMessage QLabel { + font-size: 110px; + qproperty-alignment: AlignCenter; +} + +/* Picture Screen*/ + +QFrame#PictureMessage { + margin: 40px; +} + +/* Customizing settings */ + +QTabWidget::pane { + background-color: #eeeeee; + border-style: outset; + border-width: 1px; + border-radius: 15px; + border-color: #eeeeee; + color: #333333; + padding: 10px; +} + +QTabWidget::tab-bar { + alignment: center; +} + +QTabBar::tab { + background-color: transparent; + border-style: outset; + border-width: 2px; + border-top-left-radius: 15px; + border-top-right-radius: 15px; + border-color: #eeeeee; + padding: 8px; +} + +QTabBar::tab:selected { + background-color: #66ffffff; +} + +QTabWidget QWidget { + color: #333333; +} + +QCheckBox::indicator { + width: 45px; + height: 45px; + background-color: transparent; + border-style: outset; + border-width: 2px; + border-radius: 5px; + border-color: #333333; +} + +QCheckBox::indicator::checked { + background-image: url(photobooth/gui/Qt5Gui/images/checkmark.png); + background-repeat: no-repeat; +} + +QComboBox, QDateEdit, QLineEdit, QSpinBox, QTimeEdit { + background-color: #eeeeee; + color: #333333; +} + +QComboBox QAbstractItemView { + background-color: #cccccc; + color: #333333; + selection-background-color: #eeeeee; + selection-color: #333333; +} + +QComboBox QAbstractItemView::item { + margin: 5px; + min-height: 50px; +}