Gui refactored
This commit is contained in:
@@ -7,7 +7,7 @@ from PIL import Image, ImageOps
|
||||
|
||||
from .PictureDimensions import PictureDimensions
|
||||
|
||||
from . import gui
|
||||
from .gui import GuiState
|
||||
|
||||
from .Worker import PictureSaver
|
||||
|
||||
@@ -113,7 +113,7 @@ class Photobooth:
|
||||
def initRun(self):
|
||||
|
||||
self.setCameraIdle()
|
||||
self._conn.send(gui.IdleState())
|
||||
self._conn.send(GuiState.IdleState())
|
||||
self.triggerOn()
|
||||
|
||||
def run(self):
|
||||
@@ -131,7 +131,7 @@ class Photobooth:
|
||||
self.trigger()
|
||||
except RuntimeError as e:
|
||||
logging.error('Camera error: %s', str(e))
|
||||
self._conn.send(gui.ErrorState('Camera error', str(e)))
|
||||
self._conn.send(GuiState.ErrorState('Cam error', str(e)))
|
||||
self.recvAck()
|
||||
|
||||
except TeardownException:
|
||||
@@ -149,22 +149,22 @@ class Photobooth:
|
||||
|
||||
def showCountdownPreview(self):
|
||||
|
||||
self._conn.send(gui.CountdownState())
|
||||
self._conn.send(GuiState.CountdownState())
|
||||
|
||||
while not self._conn.poll():
|
||||
picture = ImageOps.mirror(self._cap.getPreview())
|
||||
self._conn.send(gui.PreviewState(picture=picture))
|
||||
self._conn.send(GuiState.PreviewState(picture=picture))
|
||||
|
||||
self.recvAck()
|
||||
|
||||
def showCountdownNoPreview(self):
|
||||
|
||||
self._conn.send(gui.CountdownState())
|
||||
self._conn.send(GuiState.CountdownState())
|
||||
self.recvAck()
|
||||
|
||||
def showPose(self, num_picture):
|
||||
|
||||
self._conn.send(gui.PoseState(num_picture))
|
||||
self._conn.send(GuiState.PoseState(num_picture))
|
||||
|
||||
def captureSinglePicture(self, num_picture):
|
||||
|
||||
@@ -199,24 +199,24 @@ class Photobooth:
|
||||
|
||||
logging.info('Photobooth triggered')
|
||||
|
||||
self._conn.send(gui.GreeterState())
|
||||
self._conn.send(GuiState.GreeterState())
|
||||
self.triggerOff()
|
||||
|
||||
self.setCameraActive()
|
||||
self.recvAck()
|
||||
|
||||
pics = self.capturePictures()
|
||||
self._conn.send(gui.AssembleState())
|
||||
self._conn.send(GuiState.AssembleState())
|
||||
|
||||
img = self.assemblePictures(pics)
|
||||
self._conn.send(gui.PictureState(img))
|
||||
self._conn.send(GuiState.ReviewState(picture=img))
|
||||
|
||||
self.enqueueWorkerTasks(img)
|
||||
|
||||
self.setCameraIdle()
|
||||
self.recvAck()
|
||||
|
||||
self._conn.send(gui.IdleState())
|
||||
self._conn.send(GuiState.IdleState())
|
||||
self.triggerOn()
|
||||
|
||||
def gpioTrigger(self):
|
||||
@@ -225,7 +225,7 @@ class Photobooth:
|
||||
|
||||
def gpioExit(self):
|
||||
|
||||
self._conn.send(gui.TeardownState())
|
||||
self._conn.send(GuiState.TeardownState())
|
||||
|
||||
def triggerOff(self):
|
||||
|
||||
@@ -235,4 +235,4 @@ class Photobooth:
|
||||
def triggerOn(self):
|
||||
|
||||
self._lampOn()
|
||||
self._gpioTrigger = lambda: self._conn.send(gui.TriggerState())
|
||||
self._gpioTrigger = lambda: self._conn.send(GuiState.TriggerState())
|
||||
|
||||
@@ -30,7 +30,7 @@ class PrintPostprocess(GuiPostprocess):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
Printer = lookup_and_import(printer.modules, printer_module, 'printer')
|
||||
self._printer = Printer(page_size)
|
||||
self._printer = Printer(page_size, True)
|
||||
|
||||
def get(self, picture):
|
||||
|
||||
|
||||
170
photobooth/gui/GuiSkeleton.py
Normal file
170
photobooth/gui/GuiSkeleton.py
Normal file
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import GuiState
|
||||
|
||||
|
||||
class GuiSkeleton:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
@property
|
||||
def idle(self):
|
||||
|
||||
return self._idle
|
||||
|
||||
@idle.setter
|
||||
def idle(self, handle):
|
||||
|
||||
if not callable(handle):
|
||||
raise ValueError('Function handle for "idle" must be callable')
|
||||
|
||||
self._idle = handle
|
||||
|
||||
@property
|
||||
def trigger(self):
|
||||
|
||||
return self._trigger
|
||||
|
||||
@trigger.setter
|
||||
def trigger(self, handle):
|
||||
|
||||
if not callable(handle):
|
||||
raise ValueError('Function handle for "trigger" must be callable')
|
||||
|
||||
self._trigger = handle
|
||||
|
||||
@property
|
||||
def greeter(self):
|
||||
|
||||
return self._greeter
|
||||
|
||||
@greeter.setter
|
||||
def greeter(self, handle):
|
||||
|
||||
if not callable(handle):
|
||||
raise ValueError('Function handle for "greeter" must be callable')
|
||||
|
||||
self._greeter = handle
|
||||
|
||||
@property
|
||||
def countdown(self):
|
||||
|
||||
return self._countdown
|
||||
|
||||
@countdown.setter
|
||||
def countdown(self, handle):
|
||||
|
||||
if not callable(handle):
|
||||
raise ValueError(('Function handle for "countdown" must be '
|
||||
'callable'))
|
||||
|
||||
self._countdown = handle
|
||||
|
||||
@property
|
||||
def preview(self):
|
||||
|
||||
return self._preview
|
||||
|
||||
@preview.setter
|
||||
def preview(self, handle):
|
||||
|
||||
if not callable(handle):
|
||||
raise ValueError('Function handle for "preview" must be callable')
|
||||
|
||||
self._preview = handle
|
||||
|
||||
@property
|
||||
def pose(self):
|
||||
|
||||
return self._pose
|
||||
|
||||
@pose.setter
|
||||
def pose(self, handle):
|
||||
|
||||
if not callable(handle):
|
||||
raise ValueError('Function handle for "pose" must be callable')
|
||||
|
||||
self._pose = handle
|
||||
|
||||
@property
|
||||
def assemble(self):
|
||||
|
||||
return self._assemble
|
||||
|
||||
@assemble.setter
|
||||
def assemble(self, handle):
|
||||
|
||||
if not callable(handle):
|
||||
raise ValueError('Function handle for "assemble" must be callable')
|
||||
|
||||
self._assemble = handle
|
||||
|
||||
@property
|
||||
def review(self):
|
||||
|
||||
return self._review
|
||||
|
||||
@review.setter
|
||||
def review(self, handle):
|
||||
|
||||
if not callable(handle):
|
||||
raise ValueError('Function handle for "review" must be callable')
|
||||
|
||||
self._review = handle
|
||||
|
||||
@property
|
||||
def teardown(self):
|
||||
|
||||
return self._teardown
|
||||
|
||||
@teardown.setter
|
||||
def teardown(self, handle):
|
||||
|
||||
if not callable(handle):
|
||||
raise ValueError('Function handle for "teardown" must be callable')
|
||||
|
||||
self._teardown = handle
|
||||
|
||||
@property
|
||||
def error(self):
|
||||
|
||||
return self._error
|
||||
|
||||
@error.setter
|
||||
def error(self, handle):
|
||||
|
||||
if not callable(handle):
|
||||
raise ValueError('Function handle for "error" must be callable')
|
||||
|
||||
self._error = handle
|
||||
|
||||
def handleState(self, state):
|
||||
|
||||
if not isinstance(state, GuiState.GuiState):
|
||||
raise ValueError('Not a GuiState object received')
|
||||
|
||||
if isinstance(state, GuiState.IdleState):
|
||||
self.idle(state)
|
||||
elif isinstance(state, GuiState.TriggerState):
|
||||
self.trigger(state)
|
||||
elif isinstance(state, GuiState.GreeterState):
|
||||
self.greeter(state)
|
||||
elif isinstance(state, GuiState.CountdownState):
|
||||
self.countdown(state)
|
||||
elif isinstance(state, GuiState.PreviewState):
|
||||
self.preview(state)
|
||||
elif isinstance(state, GuiState.PoseState):
|
||||
self.pose(state)
|
||||
elif isinstance(state, GuiState.AssembleState):
|
||||
self.assemble(state)
|
||||
elif isinstance(state, GuiState.ReviewState):
|
||||
self.review(state)
|
||||
elif isinstance(state, GuiState.TeardownState):
|
||||
self.teardown(state)
|
||||
elif isinstance(state, GuiState.ErrorState):
|
||||
self.error(state)
|
||||
else:
|
||||
raise ValueError('Unknown state received')
|
||||
@@ -144,6 +144,13 @@ class PreviewState(PictureState):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
|
||||
class ReviewState(PictureState):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
|
||||
class TeardownState(GuiState):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
@@ -1,318 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import multiprocessing as mp
|
||||
import queue
|
||||
import logging
|
||||
|
||||
from PIL import ImageQt
|
||||
|
||||
from PyQt5 import QtGui, QtCore, QtWidgets
|
||||
|
||||
import math
|
||||
|
||||
from .Qt5Gui import Frames
|
||||
|
||||
from .PyQt5GuiHelpers import QRoundProgressBar
|
||||
|
||||
from . import *
|
||||
|
||||
|
||||
class PyQt5Gui(Gui):
|
||||
|
||||
def __init__(self, argv, config):
|
||||
|
||||
super().__init__()
|
||||
|
||||
global cfg
|
||||
cfg = config
|
||||
|
||||
self._app = QtWidgets.QApplication(argv)
|
||||
self._p = PyQt5MainWindow()
|
||||
self._lastState = self.showStart
|
||||
|
||||
self._postprocessList = []
|
||||
self._postprocessQueue = queue.Queue()
|
||||
|
||||
if cfg.getBool('Printer', 'enable'):
|
||||
self._postprocessList.append( PrintPostprocess( cfg.get('Printer', 'module'),
|
||||
(cfg.getInt('Printer', 'width'), cfg.getInt('Printer', 'height')) ) )
|
||||
|
||||
|
||||
def run(self, camera_conn, worker_queue):
|
||||
|
||||
receiver = PyQt5Receiver([camera_conn])
|
||||
receiver.notify.connect(self.handleState)
|
||||
receiver.start()
|
||||
|
||||
self._conn = camera_conn
|
||||
self._queue = worker_queue
|
||||
|
||||
self.showStart()
|
||||
|
||||
exit_code = self._app.exec_()
|
||||
self._p = None
|
||||
|
||||
return exit_code
|
||||
|
||||
|
||||
def close(self):
|
||||
|
||||
self._p.close()
|
||||
|
||||
|
||||
def restart(self):
|
||||
|
||||
self._app.exit(123)
|
||||
|
||||
|
||||
def sendAck(self):
|
||||
|
||||
self._conn.send('ack')
|
||||
|
||||
|
||||
def sendCancel(self):
|
||||
|
||||
self._conn.send('cancel')
|
||||
|
||||
|
||||
def sendTrigger(self):
|
||||
|
||||
self._conn.send('triggered')
|
||||
|
||||
|
||||
def sendTeardown(self):
|
||||
|
||||
self._conn.send('teardown')
|
||||
|
||||
|
||||
def handleKeypressEvent(self, event):
|
||||
|
||||
if event.key() == QtCore.Qt.Key_Escape:
|
||||
self.handleState(TeardownState())
|
||||
elif event.key() == QtCore.Qt.Key_Space:
|
||||
self.handleState(TriggerState())
|
||||
|
||||
|
||||
def handleKeypressEventNoTrigger(self, event):
|
||||
|
||||
if event.key() == QtCore.Qt.Key_Escape:
|
||||
self.handleState(TeardownState())
|
||||
|
||||
|
||||
def handleState(self, state):
|
||||
|
||||
if not isinstance(state, GuiState):
|
||||
raise ValueError('Invalid data received')
|
||||
|
||||
if isinstance(state, IdleState):
|
||||
self.showIdle()
|
||||
|
||||
elif isinstance(state, TriggerState):
|
||||
self.sendTrigger()
|
||||
|
||||
elif isinstance(state, GreeterState):
|
||||
global cfg
|
||||
self._p.handleKeypressEvent = self.handleKeypressEventNoTrigger
|
||||
# self._p.setCentralWidget( PyQt5GreeterMessage(
|
||||
self._p.setCentralWidget( Frames.GreeterMessage(
|
||||
cfg.getInt('Picture', 'num_x'), cfg.getInt('Picture', 'num_y') ) )
|
||||
QtCore.QTimer.singleShot(cfg.getInt('Photobooth', 'greeter_time') * 1000, self.sendAck)
|
||||
|
||||
elif isinstance(state, CountdownState):
|
||||
# self._p.setCentralWidget(PyQt5CountdownMessage(cfg.getInt('Photobooth', 'countdown_time'), self.sendAck))
|
||||
countdown_time = cfg.getInt('Photobooth', 'countdown_time')
|
||||
self._p.setCentralWidget(Frames.CountdownMessage(countdown_time,
|
||||
self.sendAck))
|
||||
|
||||
elif isinstance(state, PreviewState):
|
||||
self._p.centralWidget().picture = ImageQt.ImageQt(state.picture)
|
||||
self._p.centralWidget().update()
|
||||
|
||||
elif isinstance(state, PoseState):
|
||||
# self._p.setCentralWidget(PyQt5PoseMessage())
|
||||
self._p.setCentralWidget(Frames.PoseMessage(state.num_picture,
|
||||
cfg.getInt('Picture', 'num_x'), cfg.getInt('Picture', 'num_y')))
|
||||
|
||||
elif isinstance(state, AssembleState):
|
||||
self._p.setCentralWidget(Frames.WaitMessage('Processing picture...'))
|
||||
# self._p.setCentralWidget(PyQt5WaitMessage('Processing picture...'))
|
||||
|
||||
elif isinstance(state, PictureState):
|
||||
img = ImageQt.ImageQt(state.picture)
|
||||
# self._p.setCentralWidget(PyQt5PictureMessage(img))
|
||||
self._p.setCentralWidget(Frames.PictureMessage(img))
|
||||
QtCore.QTimer.singleShot(cfg.getInt('Photobooth', 'display_time') * 1000,
|
||||
lambda : self.postprocessPicture(state.picture))
|
||||
|
||||
elif isinstance(state, TeardownState):
|
||||
self._conn.send('teardown')
|
||||
self.showStart()
|
||||
|
||||
elif isinstance(state, ErrorState):
|
||||
self.showError(state.title, state.message)
|
||||
|
||||
else:
|
||||
raise ValueError('Unknown state')
|
||||
|
||||
|
||||
def postprocessPicture(self, picture):
|
||||
|
||||
for task in self._postprocessList:
|
||||
self._postprocessQueue.put(task.get(picture))
|
||||
|
||||
self.handleQueue()
|
||||
|
||||
|
||||
def handleQueue(self):
|
||||
|
||||
while True:
|
||||
try:
|
||||
task = self._postprocessQueue.get(block = False)
|
||||
except queue.Empty:
|
||||
self.sendAck()
|
||||
break
|
||||
else:
|
||||
if isinstance(task, PrintState):
|
||||
reply = QtWidgets.QMessageBox.question(self._p, 'Print picture?',
|
||||
'Do you want to print the picture?',
|
||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
|
||||
if reply == QtWidgets.QMessageBox.Yes:
|
||||
task.handler()
|
||||
QtWidgets.QMessageBox.information(self._p, 'Printing',
|
||||
'Picture sent to printer.', QtWidgets.QMessageBox.Ok)
|
||||
else:
|
||||
raise ValueError('Unknown task')
|
||||
|
||||
|
||||
def showStart(self):
|
||||
|
||||
self._p.handleKeypressEvent = lambda event : None
|
||||
self._lastState = self.showStart
|
||||
self._p.setCentralWidget(Frames.Start(self.showStartPhotobooth, self.showSettings, self.close))
|
||||
if QtWidgets.QApplication.overrideCursor() != 0:
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
|
||||
|
||||
def showSettings(self):
|
||||
|
||||
global cfg
|
||||
self._p.handleKeypressEvent = lambda event : None
|
||||
self._lastState = self.showSettings
|
||||
self._p.setCentralWidget(Frames.Settings(cfg, self.showSettings, self.showStart, self.restart))
|
||||
|
||||
|
||||
def showStartPhotobooth(self):
|
||||
|
||||
self._lastState = self.showStartPhotobooth
|
||||
self._conn.send('start')
|
||||
# self._p.setCentralWidget(PyQt5WaitMessage('Starting the photobooth...'))
|
||||
self._p.setCentralWidget(Frames.WaitMessage('Starting the photobooth...'))
|
||||
if cfg.getBool('Gui', 'hide_cursor'):
|
||||
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.BlankCursor)
|
||||
|
||||
|
||||
def showIdle(self):
|
||||
|
||||
self._p.handleKeypressEvent = self.handleKeypressEvent
|
||||
self._lastState = self.showIdle
|
||||
self._p.setCentralWidget(Frames.IdleMessage())
|
||||
# self._p.setCentralWidget(PyQt5IdleMessage())
|
||||
|
||||
|
||||
def showError(self, title, message):
|
||||
|
||||
logging.error('%s: %s', title, message)
|
||||
reply = QtWidgets.QMessageBox.warning(self._p, title, message,
|
||||
QtWidgets.QMessageBox.Close | QtWidgets.QMessageBox.Retry, QtWidgets.QMessageBox.Retry)
|
||||
if reply == QtWidgets.QMessageBox.Retry:
|
||||
self.sendAck()
|
||||
self._lastState()
|
||||
else:
|
||||
self.sendCancel()
|
||||
self.showStart()
|
||||
|
||||
|
||||
class PyQt5Receiver(QtCore.QThread):
|
||||
|
||||
notify = QtCore.pyqtSignal(object)
|
||||
|
||||
def __init__(self, conn):
|
||||
|
||||
super().__init__()
|
||||
|
||||
self._conn = conn
|
||||
|
||||
|
||||
def handle(self, state):
|
||||
|
||||
self.notify.emit(state)
|
||||
|
||||
|
||||
def run(self):
|
||||
|
||||
while self._conn:
|
||||
for c in mp.connection.wait(self._conn):
|
||||
try:
|
||||
state = c.recv()
|
||||
except EOFError:
|
||||
break
|
||||
else:
|
||||
self.handle(state)
|
||||
|
||||
|
||||
|
||||
class PyQt5MainWindow(QtWidgets.QMainWindow):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
self.handleKeypressEvent = lambda event : None
|
||||
|
||||
self.initUI()
|
||||
|
||||
|
||||
@property
|
||||
def handleKeypressEvent(self):
|
||||
|
||||
return self._handle_key
|
||||
|
||||
|
||||
@handleKeypressEvent.setter
|
||||
def handleKeypressEvent(self, func):
|
||||
|
||||
if not callable(func):
|
||||
raise ValueError('Keypress event handler must be callable')
|
||||
|
||||
self._handle_key = func
|
||||
|
||||
|
||||
def initUI(self):
|
||||
|
||||
global cfg
|
||||
|
||||
self.setWindowTitle('Photobooth')
|
||||
|
||||
if cfg.getBool('Gui', 'fullscreen'):
|
||||
self.showFullScreen()
|
||||
else:
|
||||
self.resize(cfg.getInt('Gui', 'width'),
|
||||
cfg.getInt('Gui', 'height'))
|
||||
self.show()
|
||||
|
||||
|
||||
def closeEvent(self, e):
|
||||
|
||||
reply = QtWidgets.QMessageBox.question(self, 'Confirmation', "Quit Photobooth?",
|
||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
|
||||
|
||||
if reply == QtWidgets.QMessageBox.Yes:
|
||||
e.accept()
|
||||
else:
|
||||
e.ignore()
|
||||
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
|
||||
self.handleKeypressEvent(event)
|
||||
@@ -1,310 +0,0 @@
|
||||
#!/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 * 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:
|
||||
perc = (value - self.min) / (self.max - self.min) * 100.0
|
||||
textToDraw = textToDraw.replace("%p", format_string.format(perc))
|
||||
|
||||
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)
|
||||
45
photobooth/gui/Qt5Gui/Postprocessor.py
Normal file
45
photobooth/gui/Qt5Gui/Postprocessor.py
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
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')
|
||||
278
photobooth/gui/Qt5Gui/PyQt5Gui.py
Normal file
278
photobooth/gui/Qt5Gui/PyQt5Gui.py
Normal file
@@ -0,0 +1,278 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from PIL import ImageQt
|
||||
|
||||
from .. import GuiState
|
||||
from ..GuiSkeleton import GuiSkeleton
|
||||
|
||||
from . import Frames
|
||||
from . import Postprocessor
|
||||
from . import Receiver
|
||||
|
||||
|
||||
class PyQt5Gui(GuiSkeleton):
|
||||
|
||||
def __init__(self, argv, config, camera_conn, worker_queue):
|
||||
|
||||
super().__init__()
|
||||
|
||||
self._cfg = config
|
||||
self._conn = camera_conn
|
||||
|
||||
self._registerCallbacks()
|
||||
self._initUI(argv)
|
||||
self._initReceiver()
|
||||
|
||||
self._postprocess = Postprocessor.Postprocessor(self._cfg)
|
||||
|
||||
def run(self):
|
||||
|
||||
self._showWelcomeScreen()
|
||||
exit_code = self._app.exec_()
|
||||
self._gui = None
|
||||
return exit_code
|
||||
|
||||
def close(self):
|
||||
|
||||
self._gui.close()
|
||||
|
||||
def restart(self):
|
||||
|
||||
self._app.exit(123)
|
||||
|
||||
def _registerCallbacks(self):
|
||||
|
||||
self.idle = self._showIdle
|
||||
self.trigger = self._sendTrigger
|
||||
self.greeter = self._showGreeter
|
||||
self.countdown = self._showCountdown
|
||||
self.preview = self._showPreview
|
||||
self.pose = self._showPose
|
||||
self.assemble = self._showAssemble
|
||||
self.review = self._showReview
|
||||
self.teardown = self._sendTeardown
|
||||
self.error = self._showError
|
||||
|
||||
def _initUI(self, argv):
|
||||
|
||||
self._disableTrigger()
|
||||
|
||||
self._app = QtWidgets.QApplication(argv)
|
||||
self._gui = PyQt5MainWindow(self._cfg, self._handleKeypressEvent)
|
||||
|
||||
def _initReceiver(self):
|
||||
|
||||
self._receiver = Receiver.Receiver([self._conn])
|
||||
self._receiver.notify.connect(self.handleState)
|
||||
self._receiver.start()
|
||||
|
||||
def _setWidget(self, widget):
|
||||
|
||||
self._gui.setCentralWidget(widget)
|
||||
|
||||
def _enableEscape(self):
|
||||
|
||||
self._is_escape = True
|
||||
|
||||
def _disableEscape(self):
|
||||
|
||||
self._is_escape = False
|
||||
|
||||
def _enableTrigger(self):
|
||||
|
||||
self._is_trigger = True
|
||||
|
||||
def _disableTrigger(self):
|
||||
|
||||
self._is_trigger = False
|
||||
|
||||
def _sendStart(self):
|
||||
|
||||
self._conn.send('start')
|
||||
|
||||
def _sendTrigger(self, state):
|
||||
|
||||
self._conn.send('triggered')
|
||||
|
||||
def _sendAck(self):
|
||||
|
||||
self._conn.send('ack')
|
||||
|
||||
def _sendCancel(self):
|
||||
|
||||
self._conn.send('cancel')
|
||||
|
||||
def _sendTeardown(self, state):
|
||||
|
||||
self._conn.send('teardown')
|
||||
self._showWelcomeScreen()
|
||||
|
||||
def _handleKeypressEvent(self, event):
|
||||
|
||||
if self._is_escape and event.key() == QtCore.Qt.Key_Escape:
|
||||
self.handleState(GuiState.TeardownState())
|
||||
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()
|
||||
self._disableEscape()
|
||||
self._lastHandle = self._showWelcomeScreen
|
||||
self._setWidget(Frames.Start(self._showStart, self._showSettings,
|
||||
self.close))
|
||||
if QtWidgets.QApplication.overrideCursor() != 0:
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
|
||||
def _showSettings(self):
|
||||
|
||||
self._disableTrigger()
|
||||
self._disableEscape()
|
||||
self._lastHandle = self._showSettings
|
||||
self._setWidget(Frames.Settings(self._cfg, self._showSettings,
|
||||
self._showWelcomeScreen, self.restart))
|
||||
|
||||
def _showStart(self, state):
|
||||
|
||||
self._disableTrigger()
|
||||
self._enableEscape()
|
||||
self._lastHandle = self._showWelcomeScreen
|
||||
self._sendStart()
|
||||
self._setWidget(Frames.WaitMessage('Starting the photobooth...'))
|
||||
if self._cfg.getBool('Gui', 'hide_cursor'):
|
||||
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.BlankCursor)
|
||||
|
||||
def _showIdle(self, state):
|
||||
|
||||
self._enableEscape()
|
||||
self._enableTrigger()
|
||||
self._lastHandle = self._showIdle
|
||||
self._setWidget(Frames.IdleMessage())
|
||||
|
||||
def _showGreeter(self, state):
|
||||
|
||||
self._enableEscape()
|
||||
self._disableTrigger()
|
||||
|
||||
num_pic = (self._cfg.getInt('Picture', 'num_x'),
|
||||
self._cfg.getInt('Picture', 'num_x'))
|
||||
greeter_time = self._cfg.getInt('Photobooth', 'greeter_time') * 1000
|
||||
|
||||
self._setWidget(Frames.GreeterMessage(*num_pic))
|
||||
QtCore.QTimer.singleShot(greeter_time, self._sendAck)
|
||||
|
||||
def _showCountdown(self, state):
|
||||
|
||||
countdown_time = self._cfg.getInt('Photobooth', 'countdown_time')
|
||||
self._setWidget(Frames.CountdownMessage(countdown_time, self._sendAck))
|
||||
|
||||
def _showPreview(self, state):
|
||||
|
||||
self._gui.centralWidget().picture = ImageQt.ImageQt(state.picture)
|
||||
self._gui.centralWidget().update()
|
||||
|
||||
def _showPose(self, state):
|
||||
|
||||
num_pic = (self._cfg.getInt('Picture', 'num_x'),
|
||||
self._cfg.getInt('Picture', 'num_x'))
|
||||
self._setWidget(Frames.PoseMessage(state.num_picture, *num_pic))
|
||||
|
||||
def _showAssemble(self, state):
|
||||
|
||||
self._setWidget(Frames.WaitMessage('Processing picture...'))
|
||||
|
||||
def _showReview(self, state):
|
||||
|
||||
img = ImageQt.ImageQt(state.picture)
|
||||
review_time = self._cfg.getInt('Photobooth', 'display_time') * 1000
|
||||
self._setWidget(Frames.PictureMessage(img))
|
||||
QtCore.QTimer.singleShot(review_time, lambda:
|
||||
self._postprocessPicture(state.picture))
|
||||
|
||||
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()
|
||||
|
||||
|
||||
class PyQt5MainWindow(QtWidgets.QMainWindow):
|
||||
|
||||
def __init__(self, config, keypress_handler):
|
||||
|
||||
super().__init__()
|
||||
|
||||
self._cfg = config
|
||||
self._handle_key = keypress_handler
|
||||
self._initUI()
|
||||
|
||||
def _initUI(self):
|
||||
|
||||
self.setWindowTitle('Photobooth')
|
||||
|
||||
if self._cfg.getBool('Gui', 'fullscreen'):
|
||||
self.showFullScreen()
|
||||
else:
|
||||
self.resize(self._cfg.getInt('Gui', 'width'),
|
||||
self._cfg.getInt('Gui', 'height'))
|
||||
self.show()
|
||||
|
||||
def closeEvent(self, e):
|
||||
|
||||
reply = QtWidgets.QMessageBox.question(self, 'Confirmation',
|
||||
"Quit Photobooth?",
|
||||
QtWidgets.QMessageBox.Yes |
|
||||
QtWidgets.QMessageBox.No,
|
||||
QtWidgets.QMessageBox.No)
|
||||
|
||||
if reply == QtWidgets.QMessageBox.Yes:
|
||||
e.accept()
|
||||
else:
|
||||
e.ignore()
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
|
||||
self._handle_key(event)
|
||||
|
||||
|
||||
class MessageBox:
|
||||
|
||||
def __init__(self, parent):
|
||||
|
||||
super().__init__()
|
||||
|
||||
self._parent = parent
|
||||
|
||||
def question(self, title, message):
|
||||
|
||||
reply = QtWidgets.QMessageBox.question(self._parent, title, message,
|
||||
QtWidgets.QMessageBox.Yes |
|
||||
QtWidgets.QMessageBox.No,
|
||||
QtWidgets.QMessageBox.No)
|
||||
return reply == QtWidgets.QMessageBox.Yes
|
||||
|
||||
def information(self, title, message):
|
||||
|
||||
QtWidgets.QMessageBox.information(self._parent, title, message,
|
||||
QtWidgets.QMessageBox.Ok)
|
||||
31
photobooth/gui/Qt5Gui/Receiver.py
Normal file
31
photobooth/gui/Qt5Gui/Receiver.py
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import multiprocessing as mp
|
||||
|
||||
from PyQt5 import QtCore
|
||||
|
||||
|
||||
class Receiver(QtCore.QThread):
|
||||
|
||||
notify = QtCore.pyqtSignal(object)
|
||||
|
||||
def __init__(self, conn):
|
||||
|
||||
super().__init__()
|
||||
self._conn = conn
|
||||
|
||||
def handle(self, state):
|
||||
|
||||
self.notify.emit(state)
|
||||
|
||||
def run(self):
|
||||
|
||||
while self._conn:
|
||||
for c in mp.connection.wait(self._conn):
|
||||
try:
|
||||
state = c.recv()
|
||||
except EOFError:
|
||||
break
|
||||
else:
|
||||
self.handle(state)
|
||||
4
photobooth/gui/Qt5Gui/__init__.py
Normal file
4
photobooth/gui/Qt5Gui/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .PyQt5Gui import PyQt5Gui # noqa
|
||||
@@ -1,19 +1,21 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .GuiState import * # noqa
|
||||
from .GuiPostprocess import * # noqa
|
||||
# from .GuiState import * # noqa
|
||||
# from .GuiPostprocess import * # noqa
|
||||
|
||||
from . import GuiState # noqa
|
||||
|
||||
# Available gui modules as tuples of (config name, module name, class name)
|
||||
modules = (('PyQt5', 'PyQt5Gui', 'PyQt5Gui'), )
|
||||
modules = (('PyQt5', 'Qt5Gui', 'PyQt5Gui'), )
|
||||
|
||||
|
||||
class Gui:
|
||||
# class Gui:
|
||||
|
||||
def __init__(self):
|
||||
# def __init__(self):
|
||||
|
||||
pass
|
||||
# pass
|
||||
|
||||
def run(self, camera_conn, worker_queue):
|
||||
# def run(self, camera_conn, worker_queue):
|
||||
|
||||
raise NotImplementedError()
|
||||
# raise NotImplementedError()
|
||||
|
||||
@@ -32,7 +32,7 @@ class CameraProcess(mp.Process):
|
||||
|
||||
def run_camera(self):
|
||||
|
||||
try:
|
||||
# try:
|
||||
cap = lookup_and_import(
|
||||
camera.modules, self.cfg.get('Camera', 'module'), 'camera')
|
||||
|
||||
@@ -40,14 +40,14 @@ class CameraProcess(mp.Process):
|
||||
self.cfg, cap, self.conn, self.worker_queue)
|
||||
return photobooth.run()
|
||||
|
||||
except BaseException as e:
|
||||
self.conn.send(gui.ErrorState('Camera error', str(e)))
|
||||
event = self.conn.recv()
|
||||
if str(event) in ('cancel', 'ack'):
|
||||
return 123
|
||||
else:
|
||||
logging.error('Unknown event received: %s', str(event))
|
||||
raise RuntimeError('Unknown event received', str(event))
|
||||
# except BaseException as e:
|
||||
# self.conn.send(gui.GuiState.ErrorState('Camera error', str(e)))
|
||||
# event = self.conn.recv()
|
||||
# if str(event) in ('cancel', 'ack'):
|
||||
# return 123
|
||||
# else:
|
||||
# logging.error('Unknown event received: %s', str(event))
|
||||
# raise RuntimeError('Unknown event received', str(event))
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -96,7 +96,7 @@ class GuiProcess(mp.Process):
|
||||
|
||||
Gui = lookup_and_import(gui.modules, self.cfg.get('Gui', 'module'),
|
||||
'gui')
|
||||
sys.exit(Gui(self.argv, self.cfg).run(self.conn, self.queue))
|
||||
sys.exit(Gui(self.argv, self.cfg, self.conn, self.queue).run())
|
||||
|
||||
|
||||
def run(argv):
|
||||
|
||||
Reference in New Issue
Block a user