Countdown prettier
This commit is contained in:
@@ -4,18 +4,21 @@
|
|||||||
from PIL import ImageQt
|
from PIL import ImageQt
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QObject, QPoint, QThread, QTimer, pyqtSignal
|
from PyQt5.QtCore import Qt, QObject, QPoint, QThread, QTimer, pyqtSignal
|
||||||
from PyQt5.QtWidgets import (QApplication, QCheckBox, QComboBox, QFormLayout, QFrame, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLayout, QLineEdit, QMainWindow, QMessageBox, QPushButton, QVBoxLayout)
|
from PyQt5.QtWidgets import (QApplication, QCheckBox, QComboBox, QFormLayout, QFrame, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLayout, QLineEdit, QMainWindow, QMessageBox, QPushButton, QVBoxLayout, QWidget)
|
||||||
from PyQt5.QtGui import QImage, QPainter, QPixmap
|
from PyQt5.QtGui import QImage, QPainter, QPixmap
|
||||||
|
|
||||||
import math
|
import math
|
||||||
from PyQt5.QtGui import QBrush, QPen, QColor
|
from PyQt5.QtGui import QBrush, QPen, QColor
|
||||||
from PyQt5.QtCore import QRect
|
from PyQt5.QtCore import QRect
|
||||||
|
|
||||||
|
from .PyQt5GuiHelpers import QRoundProgressBar
|
||||||
|
|
||||||
from . import *
|
from . import *
|
||||||
from .. import camera, printer
|
from .. import camera, printer
|
||||||
|
|
||||||
from ..printer.PrinterPyQt5 import PrinterPyQt5 as Printer
|
from ..printer.PrinterPyQt5 import PrinterPyQt5 as Printer
|
||||||
|
|
||||||
|
|
||||||
class PyQt5Gui(Gui):
|
class PyQt5Gui(Gui):
|
||||||
|
|
||||||
def __init__(self, argv, config):
|
def __init__(self, argv, config):
|
||||||
@@ -649,17 +652,20 @@ class PyQt5WaitMessage(QFrame):
|
|||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PyQt5CountdownMessage(QFrame):
|
class PyQt5CountdownMessage(QFrame):
|
||||||
|
|
||||||
def __init__(self, time, action):
|
def __init__(self, time, action):
|
||||||
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self._counter = time
|
self._step_size = 100
|
||||||
|
self._counter = time * (1000 // self._step_size)
|
||||||
self._action = action
|
self._action = action
|
||||||
self._picture = None
|
self._picture = None
|
||||||
|
|
||||||
self.initFrame()
|
self.initFrame()
|
||||||
|
self.initProgressBar(time)
|
||||||
|
|
||||||
|
|
||||||
def initFrame(self):
|
def initFrame(self):
|
||||||
@@ -667,6 +673,27 @@ class PyQt5CountdownMessage(QFrame):
|
|||||||
self.setStyleSheet('background-color: white;')
|
self.setStyleSheet('background-color: white;')
|
||||||
|
|
||||||
|
|
||||||
|
def initProgressBar(self, time):
|
||||||
|
|
||||||
|
self._bar = QRoundProgressBar()
|
||||||
|
self._bar.setBarStyle(QRoundProgressBar.StyleLine)
|
||||||
|
self._bar.setFixedSize(200, 200)
|
||||||
|
|
||||||
|
self._bar.setDataPenWidth(7)
|
||||||
|
self._bar.setOutlinePenWidth(10)
|
||||||
|
|
||||||
|
self._bar.setDecimals(0)
|
||||||
|
self._bar.setFormat('%v')
|
||||||
|
|
||||||
|
self._bar.setRange(0, time)
|
||||||
|
self._bar.setValue(time)
|
||||||
|
|
||||||
|
|
||||||
|
def updateProgressBar(self):
|
||||||
|
|
||||||
|
self._bar.setValue(self._counter / (1000 // self._step_size))
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def counter(self):
|
def counter(self):
|
||||||
|
|
||||||
@@ -694,28 +721,34 @@ class PyQt5CountdownMessage(QFrame):
|
|||||||
|
|
||||||
if self._picture != None:
|
if self._picture != None:
|
||||||
pix = QPixmap.fromImage(self._picture)
|
pix = QPixmap.fromImage(self._picture)
|
||||||
pix = pix.scaled(self.rect().size(), Qt.KeepAspectRatio, Qt.FastTransformation)
|
pix = pix.scaled(self.size(), Qt.KeepAspectRatio, Qt.FastTransformation)
|
||||||
origin = ( (self.rect().width() - pix.width()) // 2,
|
origin = ( (self.width() - pix.width()) // 2,
|
||||||
(self.rect().height() - pix.height()) // 2 )
|
(self.height() - pix.height()) // 2 )
|
||||||
painter.drawPixmap(QPoint(*origin), pix)
|
painter.drawPixmap(QPoint(*origin), pix)
|
||||||
|
|
||||||
painter.drawText(event.rect(), Qt.AlignCenter, str(self.counter))
|
# painter.drawText(event.rect(), Qt.AlignCenter, str(self.counter))
|
||||||
painter.end()
|
painter.end()
|
||||||
|
|
||||||
|
offset = ( (self.width() - self._bar.width()) // 2,
|
||||||
|
(self.height() - self._bar.height()) // 2 )
|
||||||
|
self._bar.render(self, QPoint(*offset), self._bar.visibleRegion(), QWidget.DrawChildren)
|
||||||
|
|
||||||
|
|
||||||
def showEvent(self, event):
|
def showEvent(self, event):
|
||||||
|
|
||||||
self._timer = self.startTimer(1000)
|
self._timer = self.startTimer(self._step_size)
|
||||||
|
|
||||||
|
|
||||||
def timerEvent(self, event):
|
def timerEvent(self, event):
|
||||||
|
|
||||||
self._counter -= 1
|
self._counter -= 1
|
||||||
self.update()
|
|
||||||
|
|
||||||
if self._counter == 0:
|
if self._counter == 0:
|
||||||
self.killTimer(self._timer)
|
self.killTimer(self._timer)
|
||||||
self._action()
|
self._action()
|
||||||
|
else:
|
||||||
|
self.updateProgressBar()
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
|
||||||
class PyQt5PictureMessage(QFrame):
|
class PyQt5PictureMessage(QFrame):
|
||||||
|
|||||||
299
photobooth/gui/PyQt5GuiHelpers.py
Normal file
299
photobooth/gui/PyQt5GuiHelpers.py
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Adaptation of QRoundProgressBar from
|
||||||
|
# https://sourceforge.net/projects/qroundprogressbar/
|
||||||
|
# to PyQt5, using the PyQt4-version offered at
|
||||||
|
# https://stackoverflow.com/a/33583019
|
||||||
|
|
||||||
|
from math import ceil
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, Qt, QtWidgets
|
||||||
|
|
||||||
|
class QRoundProgressBar(QtWidgets.QWidget):
|
||||||
|
|
||||||
|
StyleDonut = 1
|
||||||
|
StylePie = 2
|
||||||
|
StyleLine = 3
|
||||||
|
|
||||||
|
PositionLeft = 180
|
||||||
|
PositionTop = 90
|
||||||
|
PositionRight = 0
|
||||||
|
PositionBottom = -90
|
||||||
|
|
||||||
|
UF_VALUE = 1
|
||||||
|
UF_PERCENT = 2
|
||||||
|
UF_MAX = 4
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.min = 0
|
||||||
|
self.max = 100
|
||||||
|
self.value = 25
|
||||||
|
|
||||||
|
self.nullPosition = self.PositionTop
|
||||||
|
self.barStyle = self.StyleDonut
|
||||||
|
self.outlinePenWidth =1
|
||||||
|
self.dataPenWidth = 1
|
||||||
|
self.rebuildBrush = False
|
||||||
|
self.format = "%p%"
|
||||||
|
self.decimals = 1
|
||||||
|
self.updateFlags = self.UF_PERCENT
|
||||||
|
self.gradientData = []
|
||||||
|
self.donutThicknessRatio = 0.75
|
||||||
|
|
||||||
|
def setRange(self, min, max):
|
||||||
|
self.min = min
|
||||||
|
self.max = max
|
||||||
|
|
||||||
|
if self.max < self.min:
|
||||||
|
self.max, self.min = self.min, self.max
|
||||||
|
|
||||||
|
if self.value < self.min:
|
||||||
|
self.value = self.min
|
||||||
|
elif self.value > self.max:
|
||||||
|
self.value = self.max
|
||||||
|
|
||||||
|
if not self.gradientData:
|
||||||
|
self.rebuildBrush = True
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setMinimum(self, min):
|
||||||
|
self.setRange(min, self.max)
|
||||||
|
|
||||||
|
def setMaximum(self, max):
|
||||||
|
self.setRange(self.min, max)
|
||||||
|
|
||||||
|
def setValue(self, val):
|
||||||
|
if self.value != val:
|
||||||
|
if val < self.min:
|
||||||
|
self.value = self.min
|
||||||
|
elif val > self.max:
|
||||||
|
self.value = self.max
|
||||||
|
else:
|
||||||
|
self.value = val
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setNullPosition(self, position):
|
||||||
|
if position != self.nullPosition:
|
||||||
|
self.nullPosition = position
|
||||||
|
if not self.gradientData:
|
||||||
|
self.rebuildBrush = True
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setBarStyle(self, style):
|
||||||
|
if style != self.barStyle:
|
||||||
|
self.barStyle = style
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setOutlinePenWidth(self, penWidth):
|
||||||
|
if penWidth != self.outlinePenWidth:
|
||||||
|
self.outlinePenWidth = penWidth
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setDataPenWidth(self, penWidth):
|
||||||
|
if penWidth != self.dataPenWidth:
|
||||||
|
self.dataPenWidth = penWidth
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setDataColors(self, stopPoints):
|
||||||
|
if stopPoints != self.gradientData:
|
||||||
|
self.gradientData = stopPoints
|
||||||
|
self.rebuildBrush = True
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setFormat(self, format):
|
||||||
|
if format != self.format:
|
||||||
|
self.format = format
|
||||||
|
self.valueFormatChanged()
|
||||||
|
|
||||||
|
def resetFormat(self):
|
||||||
|
self.format = ''
|
||||||
|
self.valueFormatChanged()
|
||||||
|
|
||||||
|
def setDecimals(self, count):
|
||||||
|
if count >= 0 and count != self.decimals:
|
||||||
|
self.decimals = count
|
||||||
|
self.valueFormatChanged()
|
||||||
|
|
||||||
|
def setDonutThicknessRatio(self, val):
|
||||||
|
self.donutThicknessRatio = max(0., min(val, 1.))
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def paintEvent(self, event):
|
||||||
|
outerRadius = min(self.width(), self.height())
|
||||||
|
baseRect = QtCore.QRectF(1, 1, outerRadius-2, outerRadius-2)
|
||||||
|
|
||||||
|
buffer = QtGui.QImage(outerRadius, outerRadius, QtGui.QImage.Format_ARGB32)
|
||||||
|
buffer.fill(0)
|
||||||
|
|
||||||
|
p = QtGui.QPainter(buffer)
|
||||||
|
p.setRenderHint(QtGui.QPainter.Antialiasing)
|
||||||
|
|
||||||
|
# data brush
|
||||||
|
self.rebuildDataBrushIfNeeded()
|
||||||
|
|
||||||
|
# background
|
||||||
|
# self.drawBackground(p, buffer.rect())
|
||||||
|
|
||||||
|
# base circle
|
||||||
|
self.drawBase(p, baseRect)
|
||||||
|
|
||||||
|
# data circle
|
||||||
|
arcStep = 360.0 / (self.max - self.min) * self.value
|
||||||
|
self.drawValue(p, baseRect, self.value, arcStep)
|
||||||
|
|
||||||
|
# center circle
|
||||||
|
innerRect, innerRadius = self.calculateInnerRect(baseRect, outerRadius)
|
||||||
|
self.drawInnerBackground(p, innerRect)
|
||||||
|
|
||||||
|
# text
|
||||||
|
self.drawText(p, innerRect, innerRadius, ceil(self.value))
|
||||||
|
|
||||||
|
# finally draw the bar
|
||||||
|
p.end()
|
||||||
|
|
||||||
|
painter = QtGui.QPainter(self)
|
||||||
|
painter.drawImage(0, 0, buffer)
|
||||||
|
|
||||||
|
def drawBackground(self, p, baseRect):
|
||||||
|
p.fillRect(baseRect, self.palette().window())
|
||||||
|
|
||||||
|
def drawBase(self, p, baseRect):
|
||||||
|
bs = self.barStyle
|
||||||
|
if bs == self.StyleDonut:
|
||||||
|
p.setPen(QtGui.QPen(self.palette().shadow().color(), self.outlinePenWidth))
|
||||||
|
p.setBrush(self.palette().base())
|
||||||
|
p.drawEllipse(baseRect)
|
||||||
|
elif bs == self.StylePie:
|
||||||
|
p.setPen(QtGui.QPen(self.palette().base().color(), self.outlinePenWidth))
|
||||||
|
p.setBrush(self.palette().base())
|
||||||
|
p.drawEllipse(baseRect)
|
||||||
|
elif bs == self.StyleLine:
|
||||||
|
color = self.palette().base().color()
|
||||||
|
color.setAlpha(100)
|
||||||
|
brush = self.palette().base()
|
||||||
|
brush.setColor(color)
|
||||||
|
p.setPen(QtGui.QPen(self.palette().base().color(), self.outlinePenWidth))
|
||||||
|
p.setBrush(brush)
|
||||||
|
# p.drawEllipse(baseRect)
|
||||||
|
# p.setPen(QtGui.QPen(self.palette().base().color(), self.outlinePenWidth))
|
||||||
|
# p.setBrush(Qt.Qt.NoBrush)
|
||||||
|
p.drawEllipse(baseRect.adjusted(self.outlinePenWidth/2, self.outlinePenWidth/2, -self.outlinePenWidth/2, -self.outlinePenWidth/2))
|
||||||
|
|
||||||
|
|
||||||
|
def drawValue(self, p, baseRect, value, arcLength):
|
||||||
|
# nothing to draw
|
||||||
|
if value == self.min:
|
||||||
|
return
|
||||||
|
|
||||||
|
# for Line style
|
||||||
|
if self.barStyle == self.StyleLine:
|
||||||
|
p.setPen(QtGui.QPen(self.palette().highlight().color(), self.dataPenWidth))
|
||||||
|
p.setBrush(Qt.Qt.NoBrush)
|
||||||
|
p.drawArc(baseRect.adjusted(self.outlinePenWidth/2, self.outlinePenWidth/2, -self.outlinePenWidth/2, -self.outlinePenWidth/2),
|
||||||
|
self.nullPosition * 16,
|
||||||
|
-arcLength * 16)
|
||||||
|
return
|
||||||
|
|
||||||
|
# for Pie and Donut styles
|
||||||
|
dataPath = QtGui.QPainterPath()
|
||||||
|
dataPath.setFillRule(Qt.Qt.WindingFill)
|
||||||
|
|
||||||
|
# pie segment outer
|
||||||
|
dataPath.moveTo(baseRect.center())
|
||||||
|
dataPath.arcTo(baseRect, self.nullPosition, -arcLength)
|
||||||
|
dataPath.lineTo(baseRect.center())
|
||||||
|
|
||||||
|
p.setBrush(self.palette().highlight())
|
||||||
|
p.setPen(QtGui.QPen(self.palette().shadow().color(), self.dataPenWidth))
|
||||||
|
p.drawPath(dataPath)
|
||||||
|
|
||||||
|
def calculateInnerRect(self, baseRect, outerRadius):
|
||||||
|
# for Line style
|
||||||
|
if self.barStyle == self.StyleLine:
|
||||||
|
innerRadius = outerRadius - self.outlinePenWidth
|
||||||
|
else: # for Pie and Donut styles
|
||||||
|
innerRadius = outerRadius * self.donutThicknessRatio
|
||||||
|
|
||||||
|
delta = (outerRadius - innerRadius) / 2.
|
||||||
|
innerRect = QtCore.QRectF(delta, delta, innerRadius, innerRadius)
|
||||||
|
return innerRect, innerRadius
|
||||||
|
|
||||||
|
def drawInnerBackground(self, p, innerRect):
|
||||||
|
if self.barStyle == self.StyleDonut:
|
||||||
|
p.setBrush(self.palette().alternateBase())
|
||||||
|
|
||||||
|
cmod = p.compositionMode()
|
||||||
|
p.setCompositionMode(QtGui.QPainter.CompositionMode_Source)
|
||||||
|
|
||||||
|
p.drawEllipse(innerRect)
|
||||||
|
|
||||||
|
p.setCompositionMode(cmod)
|
||||||
|
|
||||||
|
def drawText(self, p, innerRect, innerRadius, value):
|
||||||
|
if not self.format:
|
||||||
|
return
|
||||||
|
|
||||||
|
text = self.valueToText(value)
|
||||||
|
|
||||||
|
# !!! to revise
|
||||||
|
f = self.font()
|
||||||
|
# f.setPixelSize(innerRadius * max(0.05, (0.35 - self.decimals * 0.08)))
|
||||||
|
# f.setPixelSize(innerRadius * 1.8 / len(text))
|
||||||
|
f.setPixelSize(innerRadius * 0.8 / len(text))
|
||||||
|
p.setFont(f)
|
||||||
|
|
||||||
|
textRect = innerRect
|
||||||
|
p.setPen(self.palette().text().color())
|
||||||
|
p.drawText(textRect, Qt.Qt.AlignCenter, text)
|
||||||
|
|
||||||
|
def valueToText(self, value):
|
||||||
|
textToDraw = self.format
|
||||||
|
|
||||||
|
format_string = '{' + ':.{}f'.format(self.decimals) + '}'
|
||||||
|
|
||||||
|
if self.updateFlags & self.UF_VALUE:
|
||||||
|
textToDraw = textToDraw.replace("%v", format_string.format(value))
|
||||||
|
|
||||||
|
if self.updateFlags & self.UF_PERCENT:
|
||||||
|
percent = (value - self.min) / (self.max - self.min) * 100.0
|
||||||
|
textToDraw = textToDraw.replace("%p", format_string.format(percent))
|
||||||
|
|
||||||
|
if self.updateFlags & self.UF_MAX:
|
||||||
|
m = self.max - self.min + 1
|
||||||
|
textToDraw = textToDraw.replace("%m", format_string.format(m))
|
||||||
|
|
||||||
|
return textToDraw
|
||||||
|
|
||||||
|
def valueFormatChanged(self):
|
||||||
|
self.updateFlags = 0;
|
||||||
|
|
||||||
|
if "%v" in self.format:
|
||||||
|
self.updateFlags |= self.UF_VALUE
|
||||||
|
|
||||||
|
if "%p" in self.format:
|
||||||
|
self.updateFlags |= self.UF_PERCENT
|
||||||
|
|
||||||
|
if "%m" in self.format:
|
||||||
|
self.updateFlags |= self.UF_MAX
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def rebuildDataBrushIfNeeded(self):
|
||||||
|
if self.rebuildBrush:
|
||||||
|
self.rebuildBrush = False
|
||||||
|
|
||||||
|
dataBrush = QtGui.QConicalGradient()
|
||||||
|
dataBrush.setCenter(0.5,0.5)
|
||||||
|
dataBrush.setCoordinateMode(QtGui.QGradient.StretchToDeviceMode)
|
||||||
|
|
||||||
|
for pos, color in self.gradientData:
|
||||||
|
dataBrush.setColorAt(1.0 - pos, color)
|
||||||
|
|
||||||
|
# angle
|
||||||
|
dataBrush.setAngle(self.nullPosition)
|
||||||
|
|
||||||
|
p = self.palette()
|
||||||
|
p.setBrush(QtGui.QPalette.Highlight, dataBrush)
|
||||||
|
self.setPalette(p)
|
||||||
Reference in New Issue
Block a user