ComponentError exception wraps previewRender
probably where errors are likeliest to be found
This commit is contained in:
parent
d38109453c
commit
d92fc6373f
|
@ -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
|
||||
|
||||
|
|
119
src/component.py
119
src/component.py
|
@ -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))
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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():
|
||||
|
|
Reference in New Issue