ComponentError exception wraps previewRender

probably where errors are likeliest to be found
This commit is contained in:
tassaron 2017-07-23 22:55:41 -04:00
parent d38109453c
commit d92fc6373f
7 changed files with 104 additions and 60 deletions

View File

@ -146,6 +146,12 @@ class Command(QtCore.QObject):
if 'detail' in kwargs:
print(kwargs['detail'])
@QtCore.pyqtSlot(str, str)
def videoThreadError(self, msg, detail):
print(msg)
print(detail)
quit(1)
def drawPreview(self, *args):
pass

View File

@ -6,54 +6,64 @@ from PyQt5 import uic, QtCore, QtWidgets
import os
def commandWrapper(func):
'''Intercepts each component's command() method to check for global args'''
def decorator(self, arg):
if arg.startswith('preset='):
from presetmanager import getPresetDir
_, preset = arg.split('=', 1)
path = os.path.join(getPresetDir(self), preset)
if not os.path.exists(path):
print('Couldn\'t locate preset "%s"' % preset)
quit(1)
else:
print('Opening "%s" preset on layer %s' % (
preset, self.compPos)
)
self.core.openPreset(path, self.compPos, preset)
# Don't call the component's command() method
return
else:
return func(self, arg)
return decorator
def propertiesWrapper(func):
'''Intercepts the usual properties if the properties are locked.'''
def decorator(self):
if self._lockedProperties is not None:
return self._lockedProperties
else:
return func(self)
return decorator
def errorWrapper(func):
'''Intercepts the usual error message if it is locked.'''
def decorator(self):
if self._lockedError is not None:
return self._lockedError
else:
return func(self)
return decorator
class ComponentMetaclass(type(QtCore.QObject)):
'''
Checks the validity of each Component class imported, and
mutates some attributes for easier use by the core program.
E.g., takes only major version from version string & decorates methods
'''
def renderWrapper(func):
def decorator(self, *args, **kwargs):
try:
return func(self, *args, **kwargs)
except:
from toolkit.frame import BlankFrame
try:
raise ComponentError(self, 'renderer', immediate=True)
except ComponentError:
return BlankFrame()
return decorator
def commandWrapper(func):
'''Intercepts each component's command() method to check for global args'''
def decorator(self, arg):
if arg.startswith('preset='):
from presetmanager import getPresetDir
_, preset = arg.split('=', 1)
path = os.path.join(getPresetDir(self), preset)
if not os.path.exists(path):
print('Couldn\'t locate preset "%s"' % preset)
quit(1)
else:
print('Opening "%s" preset on layer %s' % (
preset, self.compPos)
)
self.core.openPreset(path, self.compPos, preset)
# Don't call the component's command() method
return
else:
return func(self, arg)
return decorator
def propertiesWrapper(func):
'''Intercepts the usual properties if the properties are locked.'''
def decorator(self):
if self._lockedProperties is not None:
return self._lockedProperties
else:
return func(self)
return decorator
def errorWrapper(func):
'''Intercepts the usual error message if it is locked.'''
def decorator(self):
if self._lockedError is not None:
return self._lockedError
else:
return func(self)
return decorator
def __new__(cls, name, parents, attrs):
if 'ui' not in attrs:
# Use module name as ui filename by default
@ -62,7 +72,11 @@ class ComponentMetaclass(type(QtCore.QObject)):
)[0]
# if parents[0] == QtCore.QObject: else:
decorate = ('names', 'error', 'audio', 'command', 'properties')
decorate = (
'names', # Class methods
'error', 'audio', 'properties', # Properties
'previewRender', 'command',
)
# Auto-decorate methods
for key in decorate:
@ -76,13 +90,16 @@ class ComponentMetaclass(type(QtCore.QObject)):
attrs[key] = property(attrs[key])
if key == 'command':
attrs[key] = commandWrapper(attrs[key])
attrs[key] = cls.commandWrapper(attrs[key])
if key == 'previewRender':
attrs[key] = cls.renderWrapper(attrs[key])
if key == 'properties':
attrs[key] = propertiesWrapper(attrs[key])
attrs[key] = cls.propertiesWrapper(attrs[key])
if key == 'error':
attrs[key] = errorWrapper(attrs[key])
attrs[key] = cls.errorWrapper(attrs[key])
# Turn version string into a number
try:
@ -223,11 +240,11 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass):
if kwarg in ('presetNames', 'commandArgs'):
setattr(self, '_%s' % kwarg, kwargs[kwarg])
else:
raise BadComponentInit(
raise ComponentError(
self,
'Nonsensical keywords to trackWidgets.',
immediate=True)
except BadComponentInit:
except ComponentError:
continue
def update(self):
@ -366,7 +383,7 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass):
'''
class BadComponentInit(AttributeError):
class ComponentError(RuntimeError):
'''
Indicates a Python error in constructing a component.
Raising this locks the component into an error state,
@ -397,9 +414,7 @@ class BadComponentInit(AttributeError):
)
if immediate:
caller.parent.showMessage(
msg=string, detail=detail, icon='Warning'
)
caller._error.emit(string, detail)
else:
caller.lockProperties(['error'])
caller.lockError((string, detail))

View File

@ -7,7 +7,7 @@ import threading
from queue import PriorityQueue
from core import Core
from component import Component, BadComponentInit
from component import Component, ComponentError
from toolkit.frame import BlankFrame
from toolkit.ffmpeg import testAudioStream
from toolkit import openPipe, checkOutput
@ -195,14 +195,14 @@ class Component(Component):
self.updateChunksize(width, height)
try:
self.video = Video(
ffmpeg=self.core.FFMPEG_BIN, #videoPath=self.videoPath,
ffmpeg=self.core.FFMPEG_BIN, videoPath=self.videoPath,
width=width, height=height, chunkSize=self.chunkSize,
frameRate=int(self.settings.value("outputFrameRate")),
parent=self.parent, loopVideo=self.loopVideo,
component=self, scale=self.scale
) if os.path.exists(self.videoPath) else None
except KeyError:
raise BadComponentInit(self, 'Frame Fetcher initialization')
raise ComponentError(self, 'Frame Fetcher initialization')
def frameRender(self, layerNo, frameNo):
if self.video:

View File

@ -76,6 +76,9 @@ class Core:
component
)
self.componentListChanged()
self.selectedComponents[compPos]._error.connect(
loader.videoThreadError
)
# init component's widget for loading/saving presets
self.selectedComponents[compPos].widget(loader)

View File

@ -578,7 +578,11 @@ class MainWindow(QtWidgets.QMainWindow):
detail=detail,
icon='Warning',
)
self.stopVideo()
try:
self.stopVideo()
except AttributeError as e:
if 'videoWorker' not in str(e):
raise
def changeEncodingStatus(self, status):
self.encoding = status
@ -684,8 +688,6 @@ class MainWindow(QtWidgets.QMainWindow):
# connect to signal that adds an asterisk when modified
self.core.selectedComponents[index].modified.connect(
self.updateComponentTitle)
self.core.selectedComponents[index]._error.connect(
self.videoThreadError)
self.pages.insert(index, self.core.selectedComponents[index].page)
stackedWidget.insertWidget(index, self.pages[index])

View File

@ -41,15 +41,33 @@ class PaintColor(QtGui.QColor):
super().__init__(b, g, r, a)
def defaultSize(framefunc):
'''Makes width/height arguments optional'''
def decorator(*args):
if len(args) < 2:
newArgs = list(args)
if len(args) == 0 or len(args) == 1:
height = int(core.Core.settings.value("outputHeight"))
newArgs.append(height)
if len(args) == 0:
width = int(core.Core.settings.value("outputWidth"))
newArgs.insert(0, width)
args = tuple(newArgs)
return framefunc(*args)
return decorator
def FloodFrame(width, height, RgbaTuple):
return Image.new("RGBA", (width, height), RgbaTuple)
@defaultSize
def BlankFrame(width, height):
'''The base frame used by each component to start drawing.'''
return FloodFrame(width, height, (0, 0, 0, 0))
@defaultSize
def Checkerboard(width, height):
'''
A checkerboard to represent transparency to the user.

View File

@ -18,7 +18,7 @@ from threading import Thread, Event
import time
import signal
from component import BadComponentInit
from component import ComponentError
from toolkit import openPipe
from toolkit.ffmpeg import readAudioFile, createFfmpegCommand
from toolkit.frame import Checkerboard
@ -160,7 +160,7 @@ class Worker(QtCore.QObject):
progressBarUpdate=self.progressBarUpdate,
progressBarSetText=self.progressBarSetText
)
except BadComponentInit:
except ComponentError:
pass
if 'error' in comp.properties():