aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrianna2017-06-25 10:40:48 -0400
committerGitHub2017-06-25 10:40:48 -0400
commit55423ca4aa206ec9a082942ca89da7dcb4f452bc (patch)
treec65d2d5c908f472389dee4d1e03f8fe88e88dacf
parent1bb67d1513122ca7fb02e60a92339bd1a73dbee3 (diff)
parenta2838a0c3898f999e71f76e6e8d5691155438aea (diff)
Merge pull request #35 from djfun/newgui-bugfixes
Newgui bugfixes
-rw-r--r--src/components/__base__.py5
-rw-r--r--src/components/color.py7
-rw-r--r--src/components/image.py5
-rw-r--r--src/components/original.py5
-rw-r--r--src/components/text.py5
-rw-r--r--src/components/video.py53
-rw-r--r--src/core.py25
-rw-r--r--src/main.py9
-rw-r--r--src/mainwindow.py116
-rw-r--r--src/presetmanager.py3
-rw-r--r--src/preview_thread.py14
-rw-r--r--src/video_thread.py9
12 files changed, 163 insertions, 93 deletions
diff --git a/src/components/__base__.py b/src/components/__base__.py
index 9b7b958..84d41c8 100644
--- a/src/components/__base__.py
+++ b/src/components/__base__.py
@@ -1,4 +1,4 @@
-from PyQt5 import QtGui, QtCore, QtWidgets
+from PyQt5 import uic, QtGui, QtCore, QtWidgets
from PIL import Image
import os
@@ -114,6 +114,9 @@ class Component(QtCore.QObject):
except:
return (255, 255, 255)
+ def loadUi(self, filename):
+ return uic.loadUi(os.path.join(self.core.componentsPath, filename))
+
'''
### Reference methods for creating a new component
### (Inherit from this class and define these)
diff --git a/src/components/color.py b/src/components/color.py
index f1fb2b2..253ac83 100644
--- a/src/components/color.py
+++ b/src/components/color.py
@@ -1,5 +1,5 @@
from PIL import Image, ImageDraw
-from PyQt5 import uic, QtGui, QtCore, QtWidgets
+from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtGui import QColor
from PIL.ImageQt import ImageQt
import os
@@ -13,8 +13,7 @@ class Component(__base__.Component):
def widget(self, parent):
self.parent = parent
- page = uic.loadUi(os.path.join(
- os.path.dirname(os.path.realpath(__file__)), 'color.ui'))
+ page = self.loadUi('color.ui')
self.color1 = (0, 0, 0)
self.color2 = (133, 133, 133)
@@ -177,7 +176,7 @@ class Component(__base__.Component):
self.sizeWidth, self.sizeHeight
)
painter.end()
- imBytes = image.bits().asstring(image.numBytes())
+ imBytes = image.bits().asstring(image.byteCount())
return Image.frombytes('RGBA', (width, height), imBytes)
def loadPreset(self, pr, presetName=None):
diff --git a/src/components/image.py b/src/components/image.py
index 3517af6..143ae59 100644
--- a/src/components/image.py
+++ b/src/components/image.py
@@ -1,5 +1,5 @@
from PIL import Image, ImageDraw
-from PyQt5 import uic, QtGui, QtCore, QtWidgets
+from PyQt5 import QtGui, QtCore, QtWidgets
import os
from . import __base__
@@ -12,8 +12,7 @@ class Component(__base__.Component):
def widget(self, parent):
self.parent = parent
self.settings = parent.settings
- page = uic.loadUi(os.path.join(
- os.path.dirname(os.path.realpath(__file__)), 'image.ui'))
+ page = self.loadUi('image.ui')
self.imagePath = ''
self.x = 0
self.y = 0
diff --git a/src/components/original.py b/src/components/original.py
index 0d5001c..0185e0d 100644
--- a/src/components/original.py
+++ b/src/components/original.py
@@ -1,6 +1,6 @@
import numpy
from PIL import Image, ImageDraw
-from PyQt5 import uic, QtGui, QtCore, QtWidgets
+from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtGui import QColor
import os
from . import __base__
@@ -17,8 +17,7 @@ class Component(__base__.Component):
self.parent = parent
self.visColor = (255, 255, 255)
- page = uic.loadUi(os.path.join(
- os.path.dirname(os.path.realpath(__file__)), 'original.ui'))
+ page = self.loadUi('original.ui')
page.comboBox_visLayout.addItem("Classic")
page.comboBox_visLayout.addItem("Split")
page.comboBox_visLayout.addItem("Bottom")
diff --git a/src/components/text.py b/src/components/text.py
index 76961c9..7f4659f 100644
--- a/src/components/text.py
+++ b/src/components/text.py
@@ -1,6 +1,6 @@
from PIL import Image, ImageDraw
from PyQt5.QtGui import QPainter, QColor, QFont
-from PyQt5 import uic, QtGui, QtCore, QtWidgets
+from PyQt5 import QtGui, QtCore, QtWidgets
from PIL.ImageQt import ImageQt
import os
import io
@@ -29,8 +29,7 @@ class Component(__base__.Component):
self.xPosition = width / 2 - fm.width(self.title)/2
self.yPosition = height / 2 * 1.036
- page = uic.loadUi(os.path.join(
- os.path.dirname(os.path.realpath(__file__)), 'text.ui'))
+ page = self.loadUi('text.ui')
page.comboBox_textAlign.addItem("Left")
page.comboBox_textAlign.addItem("Middle")
page.comboBox_textAlign.addItem("Right")
diff --git a/src/components/video.py b/src/components/video.py
index 70247e1..44f88a5 100644
--- a/src/components/video.py
+++ b/src/components/video.py
@@ -1,6 +1,7 @@
from PIL import Image, ImageDraw
-from PyQt5 import uic, QtGui, QtCore, QtWidgets
+from PyQt5 import QtGui, QtCore, QtWidgets
import os
+import math
import subprocess
import threading
from queue import PriorityQueue
@@ -79,9 +80,20 @@ class Video:
self.frameNo += 1
# If we run out of frames, use the last good frame and loop.
- if len(self.currentFrame) == 0:
- self.frameBuffer.put((self.frameNo-1, self.lastFrame))
- continue
+ try:
+ if len(self.currentFrame) == 0:
+ self.frameBuffer.put((self.frameNo-1, self.lastFrame))
+ continue
+ except AttributeError as e:
+ self.parent.showMessage(
+ msg='%s couldn\'t be loaded. '
+ 'This is a fatal error.' % os.path.basename(
+ self.videoPath
+ ),
+ detail=str(e)
+ )
+ self.parent.stopVideo()
+ break
self.currentFrame = pipe.stdout.read(self.chunkSize)
if len(self.currentFrame) != 0:
@@ -97,10 +109,7 @@ class Component(__base__.Component):
def widget(self, parent):
self.parent = parent
self.settings = parent.settings
- page = uic.loadUi(os.path.join(
- os.path.dirname(os.path.realpath(__file__)),
- 'video.ui'
- ))
+ page = self.loadUi('video.ui')
self.videoPath = ''
self.x = 0
self.y = 0
@@ -243,28 +252,32 @@ def scale(scale, width, height, returntype=None):
width = (float(width) / 100.0) * float(scale)
height = (float(height) / 100.0) * float(scale)
if returntype == str:
- return (str(int(width)), str(int(height)))
+ return (str(math.ceil(width)), str(math.ceil(height)))
elif returntype == int:
- return (int(width), int(height))
+ return (math.ceil(width), math.ceil(height))
else:
return (width, height)
def finalizeFrame(self, imageData, width, height):
- if self.distort:
- try:
+ try:
+ if self.distort:
image = Image.frombytes(
'RGBA',
(width, height),
imageData)
- except ValueError:
- print('#### ignored invalid data caused by distortion ####')
- image = self.blankFrame(width, height)
- else:
- image = Image.frombytes(
- 'RGBA',
- scale(self.scale, width, height, int),
- imageData)
+ else:
+ image = Image.frombytes(
+ 'RGBA',
+ scale(self.scale, width, height, int),
+ imageData)
+
+ except ValueError:
+ print(
+ '### BAD VIDEO SELECTED ###\n'
+ 'Video will not export with these settings'
+ )
+ return self.blankFrame(width, height)
if self.scale != 100 \
or self.xPosition != 0 or self.yPosition != 0:
diff --git a/src/core.py b/src/core.py
index c80d60e..fdba1c4 100644
--- a/src/core.py
+++ b/src/core.py
@@ -29,6 +29,7 @@ class Core():
else:
# unfrozen
self.wd = os.path.dirname(os.path.realpath(__file__))
+ self.componentsPath = os.path.join(self.wd, 'components')
self.loadEncoderOptions()
self.videoFormats = Core.appendUppercase([
@@ -66,14 +67,12 @@ class Core():
def findComponents(self):
def findComponents():
- srcPath = os.path.join(self.wd, 'components')
- if os.path.exists(srcPath):
- for f in sorted(os.listdir(srcPath)):
- name, ext = os.path.splitext(f)
- if name.startswith("__"):
- continue
- elif ext == '.py':
- yield name
+ for f in sorted(os.listdir(self.componentsPath)):
+ name, ext = os.path.splitext(f)
+ if name.startswith("__"):
+ continue
+ elif ext == '.py':
+ yield name
self.modules = [
import_module('components.%s' % name)
for name in findComponents()
@@ -93,10 +92,12 @@ class Core():
return None
component = self.modules[moduleIndex].Component(
- moduleIndex, compPos, self)
+ moduleIndex, compPos, self
+ )
self.selectedComponents.insert(
compPos,
- component)
+ component
+ )
self.componentListChanged()
# init component's widget for loading/saving presets
@@ -177,6 +178,7 @@ class Core():
for i, tup in enumerate(data['Components']):
name, vers, preset = tup
clearThis = False
+ modified = False
# add loaded named presets to savedPresets dict
if 'preset' in preset and preset['preset'] is not None:
@@ -186,6 +188,7 @@ class Core():
origSaveValueStore = self.getPreset(filepath2)
if origSaveValueStore:
self.savedPresets[nam] = dict(origSaveValueStore)
+ modified = not origSaveValueStore == preset
else:
# saved preset was renamed or deleted
clearThis = True
@@ -217,7 +220,7 @@ class Core():
if clearThis:
self.clearPreset(i)
if hasattr(loader, 'updateComponentTitle'):
- loader.updateComponentTitle(i)
+ loader.updateComponentTitle(i, modified)
except:
errcode = 1
data = sys.exc_info()
diff --git a/src/main.py b/src/main.py
index a8dd562..5b54fc7 100644
--- a/src/main.py
+++ b/src/main.py
@@ -7,6 +7,15 @@ import preview_thread
import video_thread
+def disableWhenEncoding(func):
+ def decorator(*args):
+ if args[0].encoding:
+ return
+ else:
+ return func(*args)
+ return decorator
+
+
def LoadDefaultSettings(self):
self.resolutions = [
'1920x1080',
diff --git a/src/mainwindow.py b/src/mainwindow.py
index 7a9e397..76c2b62 100644
--- a/src/mainwindow.py
+++ b/src/mainwindow.py
@@ -12,7 +12,7 @@ import core
import preview_thread
import video_thread
from presetmanager import PresetManager
-from main import LoadDefaultSettings
+from main import LoadDefaultSettings, disableWhenEncoding
class PreviewWindow(QtWidgets.QLabel):
@@ -54,6 +54,7 @@ class MainWindow(QtWidgets.QMainWindow):
self.pages = [] # widgets of component settings
self.lastAutosave = time.time()
+ self.encoding = False
# Create data directory, load/create settings
self.dataDir = self.core.dataDir
@@ -149,16 +150,18 @@ class MainWindow(QtWidgets.QMainWindow):
for i, comp in enumerate(self.core.modules):
action = self.compMenu.addAction(comp.Component.__doc__)
action.triggered.connect(
- lambda _, item=i: self.core.insertComponent(0, item, self))
+ lambda _, item=i: self.core.insertComponent(0, item, self)
+ )
self.window.pushButton_addComponent.setMenu(self.compMenu)
componentList.dropEvent = self.dragComponent
componentList.itemSelectionChanged.connect(
- self.changeComponentWidget)
-
+ self.changeComponentWidget
+ )
self.window.pushButton_removeComponent.clicked.connect(
- lambda _: self.removeComponent())
+ lambda: self.removeComponent()
+ )
componentList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
componentList.customContextMenuRequested.connect(
@@ -173,7 +176,8 @@ class MainWindow(QtWidgets.QMainWindow):
currentRes = i
window.comboBox_resolution.setCurrentIndex(currentRes)
window.comboBox_resolution.currentIndexChanged.connect(
- self.updateResolution)
+ self.updateResolution
+ )
self.window.pushButton_listMoveUp.clicked.connect(
lambda: self.moveComponent(-1)
@@ -185,14 +189,17 @@ class MainWindow(QtWidgets.QMainWindow):
# Configure the Projects Menu
self.projectMenu = QMenu()
self.window.menuButton_newProject = self.projectMenu.addAction(
- "New Project")
+ "New Project"
+ )
self.window.menuButton_newProject.triggered.connect(
- self.createNewProject)
-
+ lambda: self.createNewProject()
+ )
self.window.menuButton_openProject = self.projectMenu.addAction(
- "Open Project")
+ "Open Project"
+ )
self.window.menuButton_openProject.triggered.connect(
- self.openOpenProjectDialog)
+ lambda: self.openOpenProjectDialog()
+ )
action = self.projectMenu.addAction("Save Project")
action.triggered.connect(self.saveCurrentProject)
@@ -207,6 +214,7 @@ class MainWindow(QtWidgets.QMainWindow):
self.openPresetManager
)
+ self.updateWindowTitle()
window.show()
if project and project != self.autosavePath:
@@ -282,10 +290,15 @@ class MainWindow(QtWidgets.QMainWindow):
def updateWindowTitle(self):
appName = 'Audio Visualizer'
- if self.currentProject:
- appName += ' - %s' % \
- os.path.splitext(
- os.path.basename(self.currentProject))[0]
+ try:
+ if self.currentProject:
+ appName += ' - %s' % \
+ os.path.splitext(
+ os.path.basename(self.currentProject))[0]
+ if self.autosaveExists(identical=False):
+ appName += '*'
+ except AttributeError:
+ pass
self.window.setWindowTitle(appName)
@QtCore.pyqtSlot(int, dict)
@@ -345,7 +358,7 @@ class MainWindow(QtWidgets.QMainWindow):
if not self.currentProject:
if os.path.exists(self.autosavePath):
os.remove(self.autosavePath)
- elif force or time.time() - self.lastAutosave >= 2.0:
+ elif force or time.time() - self.lastAutosave >= 0.1:
self.core.createProjectFile(self.autosavePath)
self.lastAutosave = time.time()
@@ -391,7 +404,7 @@ class MainWindow(QtWidgets.QMainWindow):
"Video Files (%s);; All Files (*)" % " ".join(
self.core.videoFormats))
- if not fileName == "":
+ if fileName:
self.settings.setValue("outputDir", os.path.dirname(fileName))
self.window.lineEdit_outputFile.setText(fileName)
@@ -402,33 +415,50 @@ class MainWindow(QtWidgets.QMainWindow):
def createAudioVisualisation(self):
# create output video if mandatory settings are filled in
- if self.window.lineEdit_audioFile.text() and \
- self.window.lineEdit_outputFile.text():
- self.canceled = False
- self.progressBarUpdated(-1)
- self.videoThread = QtCore.QThread(self)
- self.videoWorker = video_thread.Worker(self)
- self.videoWorker.moveToThread(self.videoThread)
- self.videoWorker.videoCreated.connect(self.videoCreated)
- self.videoWorker.progressBarUpdate.connect(self.progressBarUpdated)
- self.videoWorker.progressBarSetText.connect(
- self.progressBarSetText)
- self.videoWorker.imageCreated.connect(self.showPreviewImage)
- self.videoWorker.encoding.connect(self.changeEncodingStatus)
- self.videoThread.start()
- outputPath = self.window.lineEdit_outputFile.text()
+ audioFile = self.window.lineEdit_audioFile.text()
+ outputPath = self.window.lineEdit_outputFile.text()
+
+ if audioFile and outputPath and self.core.selectedComponents:
if not os.path.dirname(outputPath):
outputPath = os.path.join(
os.path.expanduser("~"), outputPath)
- self.videoTask.emit(
- self.window.lineEdit_audioFile.text(),
- outputPath,
- self.core.selectedComponents)
+ if outputPath and os.path.isdir(outputPath):
+ self.showMessage(
+ msg='Chosen filename matches a directory, which '
+ 'cannot be overwritten. Please choose a different '
+ 'filename or move the directory.'
+ )
+ return
else:
- self.showMessage(
- msg="You must select an audio file and output filename.")
+ if not audioFile or not outputPath:
+ self.showMessage(
+ msg="You must select an audio file and output filename."
+ )
+ elif not self.core.selectedComponents:
+ self.showMessage(
+ msg="Not enough components."
+ )
+ return
+
+ self.canceled = False
+ self.progressBarUpdated(-1)
+ self.videoThread = QtCore.QThread(self)
+ self.videoWorker = video_thread.Worker(self)
+ self.videoWorker.moveToThread(self.videoThread)
+ self.videoWorker.videoCreated.connect(self.videoCreated)
+ self.videoWorker.progressBarUpdate.connect(self.progressBarUpdated)
+ self.videoWorker.progressBarSetText.connect(
+ self.progressBarSetText)
+ self.videoWorker.imageCreated.connect(self.showPreviewImage)
+ self.videoWorker.encoding.connect(self.changeEncodingStatus)
+ self.videoThread.start()
+ self.videoTask.emit(
+ audioFile,
+ outputPath,
+ self.core.selectedComponents)
def changeEncodingStatus(self, status):
+ self.encoding = status
if status:
self.window.pushButton_createVideo.setEnabled(False)
self.window.pushButton_Cancel.setEnabled(True)
@@ -490,6 +520,7 @@ class MainWindow(QtWidgets.QMainWindow):
self.newTask.emit(self.core.selectedComponents)
# self.processTask.emit()
self.autosave(force)
+ self.updateWindowTitle()
def showPreviewImage(self, image):
self.previewWindow.changePixmap(image)
@@ -595,6 +626,7 @@ class MainWindow(QtWidgets.QMainWindow):
self.window.stackedWidget.removeWidget(widget)
self.pages = []
+ @disableWhenEncoding
def createNewProject(self):
self.openSaveChangesDialog('starting a new project')
@@ -602,11 +634,11 @@ class MainWindow(QtWidgets.QMainWindow):
self.currentProject = None
self.settings.setValue("currentProject", None)
self.drawPreview(True)
- self.updateWindowTitle()
def saveCurrentProject(self):
if self.currentProject:
self.core.createProjectFile(self.currentProject)
+ self.updateWindowTitle()
else:
self.openSaveProjectDialog()
@@ -638,9 +670,10 @@ class MainWindow(QtWidgets.QMainWindow):
self.settings.setValue("projectDir", os.path.dirname(filename))
self.settings.setValue("currentProject", filename)
self.currentProject = filename
- self.updateWindowTitle()
self.core.createProjectFile(filename)
+ self.updateWindowTitle()
+ @disableWhenEncoding
def openOpenProjectDialog(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
self.window, "Open Project File",
@@ -651,7 +684,6 @@ class MainWindow(QtWidgets.QMainWindow):
def openProject(self, filepath, prompt=True):
if not filepath or not os.path.exists(filepath) \
or not filepath.endswith('.avp'):
- self.updateWindowTitle()
return
self.clear()
@@ -660,7 +692,6 @@ class MainWindow(QtWidgets.QMainWindow):
self.openSaveChangesDialog('opening another project')
self.currentProject = filepath
- self.updateWindowTitle()
self.settings.setValue("currentProject", filepath)
self.settings.setValue("projectDir", os.path.dirname(filepath))
# actually load the project using core method
@@ -668,6 +699,7 @@ class MainWindow(QtWidgets.QMainWindow):
if self.window.listWidget_componentList.count() == 0:
self.drawPreview()
self.autosave(True)
+ self.updateWindowTitle()
def showMessage(self, **kwargs):
parent = kwargs['parent'] if 'parent' in kwargs else self.window
diff --git a/src/presetmanager.py b/src/presetmanager.py
index 44203e5..069bf62 100644
--- a/src/presetmanager.py
+++ b/src/presetmanager.py
@@ -123,7 +123,8 @@ class PresetManager(QtWidgets.QDialog):
def clearPreset(self, compI=None):
'''Functions on mainwindow level from the context menu'''
compI = self.parent.window.listWidget_componentList.currentRow()
- self.core.clearPreset(compI, self.parent)
+ self.core.clearPreset(compI)
+ self.parent.updateComponentTitle(compI, False)
def openSavePresetDialog(self):
'''Functions on mainwindow level from the context menu'''
diff --git a/src/preview_thread.py b/src/preview_thread.py
index 4a46d51..769656b 100644
--- a/src/preview_thread.py
+++ b/src/preview_thread.py
@@ -49,8 +49,18 @@ class Worker(QtCore.QObject):
components = nextPreviewInformation["components"]
for component in reversed(components):
- frame = Image.alpha_composite(
- frame, component.previewRender(self))
+ try:
+ frame = Image.alpha_composite(
+ frame, component.previewRender(self)
+ )
+ except ValueError as e:
+ self.parent.showMessage(
+ msg="Bad frame returned by %s's previewRender method. "
+ "This is a fatal error." %
+ str(component),
+ detail=str(e)
+ )
+ quit(1)
self._image = ImageQt(frame)
self.imageCreated.emit(QtGui.QImage(self._image))
diff --git a/src/video_thread.py b/src/video_thread.py
index b45381c..9b0bf56 100644
--- a/src/video_thread.py
+++ b/src/video_thread.py
@@ -2,7 +2,6 @@ from PyQt5 import QtCore, QtGui, uic
from PyQt5.QtCore import pyqtSignal, pyqtSlot
from PIL import Image, ImageDraw, ImageFont
from PIL.ImageQt import ImageQt
-import core
import numpy
import subprocess as sp
import sys
@@ -13,6 +12,8 @@ import time
from copy import copy
import signal
+import core
+
class Worker(QtCore.QObject):
@@ -87,8 +88,10 @@ class Worker(QtCore.QObject):
self.encoding.emit(True)
self.components = components
self.outputFile = outputFile
- self.bgI = 0 # tracked video frame
+
self.reset()
+
+ self.bgI = 0 # tracked video frame
self.width = int(self.core.settings.value('outputWidth'))
self.height = int(self.core.settings.value('outputHeight'))
progressBarValue = 0
@@ -171,7 +174,7 @@ class Worker(QtCore.QObject):
self.staticComponents = {}
numComps = len(self.components)
for compNo, comp in enumerate(self.components):
- pStr = "Analyzing audio..."
+ pStr = "Starting components..."
self.progressBarSetText.emit(pStr)
properties = None
properties = comp.preFrameRender(