Merge pull request #36 from djfun/newgui-project-settings
Project files save more settings
This commit is contained in:
commit
f86c33d0e5
|
@ -1,5 +1,4 @@
|
|||
from PyQt4 import QtCore
|
||||
from PyQt4.QtCore import QSettings
|
||||
from PyQt5 import QtCore
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
@ -24,13 +23,20 @@ class Command(QtCore.QObject):
|
|||
epilog='EXAMPLE COMMAND: main.py myvideotemplate.avp '
|
||||
'-i ~/Music/song.mp3 -o ~/video.mp4 '
|
||||
'-c 0 image path=~/Pictures/thisWeeksPicture.jpg '
|
||||
'-c 1 video "preset=My Logo" -c 2 vis layout=classic')
|
||||
'-c 1 video "preset=My Logo" -c 2 vis layout=classic'
|
||||
)
|
||||
self.parser.add_argument(
|
||||
'-i', '--input', metavar='SOUND',
|
||||
help='input audio file')
|
||||
help='input audio file'
|
||||
)
|
||||
self.parser.add_argument(
|
||||
'-o', '--output', metavar='OUTPUT',
|
||||
help='output video file')
|
||||
help='output video file'
|
||||
)
|
||||
self.parser.add_argument(
|
||||
'-e', '--export', action='store_true',
|
||||
help='use input and output files from project file'
|
||||
)
|
||||
|
||||
# optional arguments
|
||||
self.parser.add_argument(
|
||||
|
@ -43,12 +49,19 @@ class Command(QtCore.QObject):
|
|||
nargs='*', action='append')
|
||||
|
||||
self.args = self.parser.parse_args()
|
||||
self.settings = QSettings(
|
||||
os.path.join(self.dataDir, 'settings.ini'), QSettings.IniFormat)
|
||||
self.settings = self.core.settings
|
||||
LoadDefaultSettings(self)
|
||||
|
||||
if self.args.projpath:
|
||||
self.core.openProject(self, self.args.projpath)
|
||||
projPath = self.args.projpath
|
||||
if not os.path.dirname(projPath):
|
||||
projPath = os.path.join(
|
||||
self.settings.value("projectDir"),
|
||||
projPath
|
||||
)
|
||||
if not projPath.endswith('.avp'):
|
||||
projPath += '.avp'
|
||||
self.core.openProject(self, projPath)
|
||||
self.core.selectedComponents = list(
|
||||
reversed(self.core.selectedComponents))
|
||||
self.core.componentListChanged()
|
||||
|
@ -72,13 +85,28 @@ class Command(QtCore.QObject):
|
|||
for arg in args:
|
||||
self.core.selectedComponents[i].command(arg)
|
||||
|
||||
if self.args.input and self.args.output:
|
||||
self.createAudioVisualisation()
|
||||
if self.args.export and self.args.projpath:
|
||||
errcode, data = self.core.parseAvFile(projPath)
|
||||
for key, value in data['WindowFields']:
|
||||
if 'outputFile' in key:
|
||||
output = value
|
||||
if not os.path.dirname(value):
|
||||
output = os.path.join(
|
||||
os.path.expanduser('~'),
|
||||
output
|
||||
)
|
||||
if 'audioFile' in key:
|
||||
input = value
|
||||
self.createAudioVisualisation(input, output)
|
||||
|
||||
elif self.args.input and self.args.output:
|
||||
self.createAudioVisualisation(self.args.input, self.args.output)
|
||||
|
||||
elif 'help' not in sys.argv:
|
||||
self.parser.print_help()
|
||||
quit(1)
|
||||
|
||||
def createAudioVisualisation(self):
|
||||
def createAudioVisualisation(self, input, output):
|
||||
self.videoThread = QtCore.QThread(self)
|
||||
self.videoWorker = video_thread.Worker(self)
|
||||
self.videoWorker.moveToThread(self.videoThread)
|
||||
|
@ -86,8 +114,8 @@ class Command(QtCore.QObject):
|
|||
|
||||
self.videoThread.start()
|
||||
self.videoTask.emit(
|
||||
self.args.input,
|
||||
self.args.output,
|
||||
input,
|
||||
output,
|
||||
list(reversed(self.core.selectedComponents))
|
||||
)
|
||||
|
||||
|
|
|
@ -83,12 +83,12 @@ class Component(__base__.Component):
|
|||
}
|
||||
|
||||
def pickImage(self):
|
||||
imgDir = self.settings.value("backgroundDir", os.path.expanduser("~"))
|
||||
imgDir = self.settings.value("componentDir", os.path.expanduser("~"))
|
||||
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
|
||||
self.page, "Choose Image", imgDir,
|
||||
"Image Files (%s)" % " ".join(self.imageFormats))
|
||||
if filename:
|
||||
self.settings.setValue("backgroundDir", os.path.dirname(filename))
|
||||
self.settings.setValue("componentDir", os.path.dirname(filename))
|
||||
self.page.lineEdit_image.setText(filename)
|
||||
self.update()
|
||||
|
||||
|
|
|
@ -90,7 +90,8 @@ class Video:
|
|||
'This is a fatal error.' % os.path.basename(
|
||||
self.videoPath
|
||||
),
|
||||
detail=str(e)
|
||||
detail=str(e),
|
||||
icon='Warning'
|
||||
)
|
||||
self.parent.stopVideo()
|
||||
break
|
||||
|
@ -188,13 +189,13 @@ class Component(__base__.Component):
|
|||
}
|
||||
|
||||
def pickVideo(self):
|
||||
imgDir = self.settings.value("backgroundDir", os.path.expanduser("~"))
|
||||
imgDir = self.settings.value("componentDir", os.path.expanduser("~"))
|
||||
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
|
||||
self.page, "Choose Video",
|
||||
imgDir, "Video Files (%s)" % " ".join(self.videoFormats)
|
||||
)
|
||||
if filename:
|
||||
self.settings.setValue("backgroundDir", os.path.dirname(filename))
|
||||
self.settings.setValue("componentDir", os.path.dirname(filename))
|
||||
self.page.lineEdit_video.setText(filename)
|
||||
self.update()
|
||||
|
||||
|
|
76
src/core.py
76
src/core.py
|
@ -30,6 +30,10 @@ class Core():
|
|||
# unfrozen
|
||||
self.wd = os.path.dirname(os.path.realpath(__file__))
|
||||
self.componentsPath = os.path.join(self.wd, 'components')
|
||||
self.settings = QtCore.QSettings(
|
||||
os.path.join(self.dataDir, 'settings.ini'),
|
||||
QtCore.QSettings.IniFormat
|
||||
)
|
||||
|
||||
self.loadEncoderOptions()
|
||||
self.videoFormats = Core.appendUppercase([
|
||||
|
@ -169,13 +173,23 @@ class Core():
|
|||
its own showMessage(**kwargs) method for displaying errors.
|
||||
'''
|
||||
if not os.path.exists(filepath):
|
||||
loader.showMessage(msg='Project file not found')
|
||||
loader.showMessage(msg='Project file not found.')
|
||||
return
|
||||
|
||||
errcode, data = self.parseAvFile(filepath)
|
||||
if errcode == 0:
|
||||
try:
|
||||
for i, tup in enumerate(data['Components']):
|
||||
if hasattr(loader, 'window'):
|
||||
for widget, value in data['WindowFields']:
|
||||
widget = eval('loader.window.%s' % widget)
|
||||
widget.blockSignals(True)
|
||||
widget.setText(value)
|
||||
widget.blockSignals(False)
|
||||
|
||||
for key, value in data['Settings']:
|
||||
self.settings.setValue(key, value)
|
||||
|
||||
for tup in data['Components']:
|
||||
name, vers, preset = tup
|
||||
clearThis = False
|
||||
modified = False
|
||||
|
@ -213,7 +227,7 @@ class Core():
|
|||
preset['preset']
|
||||
)
|
||||
except KeyError as e:
|
||||
print('%s missing value %s' % (
|
||||
print('%s missing value: %s' % (
|
||||
self.selectedComponents[i], e)
|
||||
)
|
||||
|
||||
|
@ -221,23 +235,26 @@ class Core():
|
|||
self.clearPreset(i)
|
||||
if hasattr(loader, 'updateComponentTitle'):
|
||||
loader.updateComponentTitle(i, modified)
|
||||
|
||||
except:
|
||||
errcode = 1
|
||||
data = sys.exc_info()
|
||||
|
||||
if errcode == 1:
|
||||
typ, value, _ = data
|
||||
if typ.__name__ == KeyError:
|
||||
typ, value, tb = data
|
||||
if typ.__name__ == 'KeyError':
|
||||
# probably just an old version, still loadable
|
||||
print('file missing value: %s' % value)
|
||||
return
|
||||
if hasattr(loader, 'createNewProject'):
|
||||
loader.createNewProject()
|
||||
msg = '%s: %s' % (typ.__name__, value)
|
||||
import traceback
|
||||
msg = '%s: %s\n\nTraceback:\n' % (typ.__name__, value)
|
||||
msg += "\n".join(traceback.format_tb(tb))
|
||||
loader.showMessage(
|
||||
msg="Project file '%s' is corrupted." % filepath,
|
||||
showCancel=False,
|
||||
icon=QtGui.QMessageBox.Warning,
|
||||
icon='Warning',
|
||||
detail=msg)
|
||||
|
||||
def parseAvFile(self, filepath):
|
||||
|
@ -245,12 +262,16 @@ class Core():
|
|||
Returns dictionary with section names as the keys, each one
|
||||
contains a list of tuples: (compName, version, compPresetDict)
|
||||
'''
|
||||
data = {}
|
||||
validSections = (
|
||||
'Components',
|
||||
'Settings',
|
||||
'WindowFields'
|
||||
)
|
||||
data = {sect: [] for sect in validSections}
|
||||
try:
|
||||
with open(filepath, 'r') as f:
|
||||
def parseLine(line):
|
||||
'''Decides if a file line is a section header'''
|
||||
validSections = ('Components')
|
||||
line = line.strip()
|
||||
newSection = ''
|
||||
|
||||
|
@ -266,7 +287,6 @@ class Core():
|
|||
line, newSection = parseLine(line)
|
||||
if newSection:
|
||||
section = str(newSection)
|
||||
data[section] = []
|
||||
continue
|
||||
if line and section == 'Components':
|
||||
if i == 0:
|
||||
|
@ -283,6 +303,10 @@ class Core():
|
|||
lastCompPreset
|
||||
))
|
||||
i = 0
|
||||
elif line and section:
|
||||
key, value = line.split('=', 1)
|
||||
data[section].append((key, value.strip()))
|
||||
|
||||
return 0, data
|
||||
except:
|
||||
return 1, sys.exc_info()
|
||||
|
@ -354,8 +378,22 @@ class Core():
|
|||
f.write('%s\n' % str(vers))
|
||||
f.write(Core.presetToString(saveValueStore))
|
||||
|
||||
def createProjectFile(self, filepath):
|
||||
def createProjectFile(self, filepath, window=None):
|
||||
'''Create a project file (.avp) using the current program state'''
|
||||
forbiddenSettingsKeys = [
|
||||
'currentProject',
|
||||
'outputAudioBitrate',
|
||||
'outputAudioCodec',
|
||||
'outputContainer',
|
||||
'outputFormat',
|
||||
'outputFrameRate',
|
||||
'outputHeight',
|
||||
'outputPreset',
|
||||
'outputVideoBitrate',
|
||||
'outputVideoCodec',
|
||||
'outputVideoFormat',
|
||||
'outputWidth',
|
||||
]
|
||||
try:
|
||||
if not filepath.endswith(".avp"):
|
||||
filepath += '.avp'
|
||||
|
@ -363,12 +401,28 @@ class Core():
|
|||
os.remove(filepath)
|
||||
with open(filepath, 'w') as f:
|
||||
print('creating %s' % filepath)
|
||||
|
||||
f.write('[Components]\n')
|
||||
for comp in self.selectedComponents:
|
||||
saveValueStore = comp.savePreset()
|
||||
f.write('%s\n' % str(comp))
|
||||
f.write('%s\n' % str(comp.version()))
|
||||
f.write('%s\n' % Core.presetToString(saveValueStore))
|
||||
|
||||
f.write('\n[Settings]\n')
|
||||
for key in self.settings.allKeys():
|
||||
if key not in forbiddenSettingsKeys:
|
||||
f.write('%s=%s\n' % (key, self.settings.value(key)))
|
||||
|
||||
if window:
|
||||
f.write('\n[WindowFields]\n')
|
||||
f.write(
|
||||
'lineEdit_audioFile=%s\n'
|
||||
'lineEdit_outputFile=%s\n' % (
|
||||
window.lineEdit_audioFile.text(),
|
||||
window.lineEdit_outputFile.text()
|
||||
)
|
||||
)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from queue import Queue
|
||||
from PyQt5 import QtCore, QtGui, uic, QtWidgets
|
||||
from PyQt5.QtCore import QSettings, Qt
|
||||
from PyQt5.QtWidgets import QMenu, QShortcut
|
||||
import sys
|
||||
import os
|
||||
|
@ -27,7 +26,9 @@ class PreviewWindow(QtWidgets.QLabel):
|
|||
painter = QtGui.QPainter(self)
|
||||
point = QtCore.QPoint(0, 0)
|
||||
scaledPix = self.pixmap.scaled(
|
||||
size, Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation)
|
||||
size,
|
||||
QtCore.Qt.KeepAspectRatio,
|
||||
transformMode=QtCore.Qt.SmoothTransformation)
|
||||
|
||||
# start painting the label from left upper corner
|
||||
point.setX((size.width() - scaledPix.width())/2)
|
||||
|
@ -59,8 +60,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
# Create data directory, load/create settings
|
||||
self.dataDir = self.core.dataDir
|
||||
self.autosavePath = os.path.join(self.dataDir, 'autosave.avp')
|
||||
self.settings = QSettings(
|
||||
os.path.join(self.dataDir, 'settings.ini'), QSettings.IniFormat)
|
||||
self.settings = self.core.settings
|
||||
LoadDefaultSettings(self)
|
||||
self.presetManager = PresetManager(
|
||||
uic.loadUi(
|
||||
|
@ -94,6 +94,13 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
window.toolButton_selectOutputFile.clicked.connect(
|
||||
self.openOutputFileDialog)
|
||||
|
||||
def changedField():
|
||||
self.autosave()
|
||||
self.updateWindowTitle()
|
||||
|
||||
window.lineEdit_audioFile.textChanged.connect(changedField)
|
||||
window.lineEdit_outputFile.textChanged.connect(changedField)
|
||||
|
||||
window.progressBar_createVideo.setValue(0)
|
||||
|
||||
window.pushButton_createVideo.clicked.connect(
|
||||
|
@ -222,7 +229,9 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
project += '.avp'
|
||||
# open a project from the commandline
|
||||
if not os.path.dirname(project):
|
||||
project = os.path.join(os.path.expanduser('~'), project)
|
||||
project = os.path.join(
|
||||
self.settings.value("projectDir"), project
|
||||
)
|
||||
self.currentProject = project
|
||||
self.settings.setValue("currentProject", project)
|
||||
if os.path.exists(self.autosavePath):
|
||||
|
@ -359,7 +368,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
if os.path.exists(self.autosavePath):
|
||||
os.remove(self.autosavePath)
|
||||
elif force or time.time() - self.lastAutosave >= 0.1:
|
||||
self.core.createProjectFile(self.autosavePath)
|
||||
self.core.createProjectFile(self.autosavePath, self.window)
|
||||
self.lastAutosave = time.time()
|
||||
|
||||
def autosaveExists(self, identical=True):
|
||||
|
@ -426,7 +435,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
self.showMessage(
|
||||
msg='Chosen filename matches a directory, which '
|
||||
'cannot be overwritten. Please choose a different '
|
||||
'filename or move the directory.'
|
||||
'filename or move the directory.',
|
||||
icon='Warning',
|
||||
)
|
||||
return
|
||||
else:
|
||||
|
@ -625,6 +635,13 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
for widget in self.pages:
|
||||
self.window.stackedWidget.removeWidget(widget)
|
||||
self.pages = []
|
||||
for field in (
|
||||
self.window.lineEdit_audioFile,
|
||||
self.window.lineEdit_outputFile
|
||||
):
|
||||
field.blockSignals(True)
|
||||
field.setText('')
|
||||
field.blockSignals(False)
|
||||
|
||||
@disableWhenEncoding
|
||||
def createNewProject(self):
|
||||
|
@ -637,7 +654,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
|
||||
def saveCurrentProject(self):
|
||||
if self.currentProject:
|
||||
self.core.createProjectFile(self.currentProject)
|
||||
self.core.createProjectFile(self.currentProject, self.window)
|
||||
self.updateWindowTitle()
|
||||
else:
|
||||
self.openSaveProjectDialog()
|
||||
|
@ -670,7 +687,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
self.settings.setValue("projectDir", os.path.dirname(filename))
|
||||
self.settings.setValue("currentProject", filename)
|
||||
self.currentProject = filename
|
||||
self.core.createProjectFile(filename)
|
||||
self.core.createProjectFile(filename, self.window)
|
||||
self.updateWindowTitle()
|
||||
|
||||
@disableWhenEncoding
|
||||
|
@ -707,7 +724,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
msg.setModal(True)
|
||||
msg.setText(kwargs['msg'])
|
||||
msg.setIcon(
|
||||
kwargs['icon']
|
||||
eval('QtWidgets.QMessageBox.%s' % kwargs['icon'])
|
||||
if 'icon' in kwargs else QtWidgets.QMessageBox.Information
|
||||
)
|
||||
msg.setDetailedText(kwargs['detail'] if 'detail' in kwargs else None)
|
||||
|
|
|
@ -176,7 +176,7 @@ class PresetManager(QtWidgets.QDialog):
|
|||
msg="%s already exists! Overwrite it?" %
|
||||
os.path.basename(path),
|
||||
showCancel=True,
|
||||
icon=QtWidgets.QMessageBox.Warning,
|
||||
icon='Warning',
|
||||
parent=window)
|
||||
if not ch:
|
||||
# user clicked cancel
|
||||
|
@ -209,7 +209,7 @@ class PresetManager(QtWidgets.QDialog):
|
|||
ch = self.parent.showMessage(
|
||||
msg='Really delete %s?' % name,
|
||||
showCancel=True,
|
||||
icon=QtWidgets.QMessageBox.Warning,
|
||||
icon='Warning',
|
||||
parent=self.window
|
||||
)
|
||||
if not ch:
|
||||
|
|
|
@ -58,7 +58,8 @@ class Worker(QtCore.QObject):
|
|||
msg="Bad frame returned by %s's previewRender method. "
|
||||
"This is a fatal error." %
|
||||
str(component),
|
||||
detail=str(e)
|
||||
detail=str(e),
|
||||
icon='Warning'
|
||||
)
|
||||
quit(1)
|
||||
|
||||
|
|
Reference in New Issue