Skip to content

Commit

Permalink
Added better integration with stabilizer effect
Browse files Browse the repository at this point in the history
  • Loading branch information
BrennoCaldato committed Jul 15, 2020
1 parent d4e88ec commit 83f6e41
Show file tree
Hide file tree
Showing 5 changed files with 318 additions and 8 deletions.
Binary file added src/effects/icons/stabilizer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/effects/icons/tracker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
298 changes: 298 additions & 0 deletions src/example-effect-init.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
Index: src/windows/process_effect.py
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/windows/process_effect.py (date 1594417794000)
+++ src/windows/process_effect.py (date 1594417794000)
@@ -0,0 +1,115 @@
+"""
+ @file
+ @brief This file loads the Initialize Effects / Pre-process effects dialog
+ @author Jonathan Thomas <jonathan@openshot.org>
+
+ @section LICENSE
+
+ Copyright (c) 2008-2018 OpenShot Studios, LLC
+ (http://www.openshotstudios.com). This file is part of
+ OpenShot Video Editor (http://www.openshot.org), an open-source project
+ dedicated to delivering high quality video editing and animation solutions
+ to the world.
+
+ OpenShot Video Editor is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OpenShot Video Editor is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
+ """
+
+import os
+import sys
+import time
+
+from PyQt5.QtCore import *
+from PyQt5.QtGui import QIcon, QStandardItemModel, QStandardItem
+from PyQt5.QtWidgets import *
+from PyQt5 import uic
+import openshot # Python module for libopenshot (required video editing module installed separately)
+
+from classes import info, ui_util, settings, qt_types, updates
+from classes.app import get_app
+from classes.logger import log
+from classes.metrics import *
+
+
+class ProcessEffect(QDialog):
+ """ Choose Profile Dialog """
+ progress = pyqtSignal(int)
+
+ # Path to ui file
+ ui_path = os.path.join(info.PATH, 'windows', 'ui', 'process-effect.ui')
+
+ def __init__(self, clip_id, effect_name):
+
+ # Create dialog class
+ QDialog.__init__(self)
+
+ # Load UI from designer & init
+ ui_util.load_ui(self, self.ui_path)
+ ui_util.init_ui(self)
+
+ # get translations
+ _ = get_app()._tr
+
+ # Pause playback (to prevent crash since we are fixing to change the timeline's max size)
+ get_app().window.actionPlay_trigger(None, force="pause")
+
+ # Track metrics
+ track_metric_screen("process-effect-screen")
+
+ # Init form
+ self.progressBar.setValue(0)
+ self.txtAdvanced.setText("{}")
+ self.setWindowTitle(_("%s: Initialize Effect") % effect_name)
+ self.clip_id = clip_id
+ self.effect_name = effect_name
+
+ # Add combo entries
+ self.cboOptions.addItem("Option 1", 1)
+ self.cboOptions.addItem("Option 2", 2)
+ self.cboOptions.addItem("Option 3", 3)
+
+ # Add buttons
+ self.cancel_button = QPushButton(_('Cancel'))
+ self.process_button = QPushButton(_('Process Effect'))
+ self.buttonBox.addButton(self.process_button, QDialogButtonBox.AcceptRole)
+ self.buttonBox.addButton(self.cancel_button, QDialogButtonBox.RejectRole)
+
+ def accept(self):
+ """ Start processing effect """
+ # Disable UI
+ self.cboOptions.setEnabled(False)
+ self.txtAdvanced.setEnabled(False)
+ self.process_button.setEnabled(False)
+
+ # DO WORK HERE, and periodically set progressBar value
+ # Access C++ timeline and find the Clip instance which this effect should be applied to
+ timeline_instance = get_app().window.timeline_sync.timeline
+ for clip_instance in timeline_instance.Clips():
+ if clip_instance.Id() == self.clip_id:
+ print("Apply effect: %s to clip: %s" % (self.effect_name, clip_instance.Id()))
+
+ # EXAMPLE progress updates
+ for value in range(1, 100, 4):
+ self.progressBar.setValue(value)
+ time.sleep(0.25)
+
+ # Process any queued events
+ QCoreApplication.processEvents()
+
+ # Accept dialog
+ super(ProcessEffect, self).accept()
+
+ def reject(self):
+ # Cancel dialog
+ self.exporting = False
+ super(ProcessEffect, self).reject()
Index: src/windows/ui/process-effect.ui
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/windows/ui/process-effect.ui (date 1594417794000)
+++ src/windows/ui/process-effect.ui (date 1594417794000)
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Dialog</class>
+ <widget class="QDialog" name="Dialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>410</width>
+ <height>193</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>%s: Initialize Effect</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Progress:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QProgressBar" name="progressBar">
+ <property name="value">
+ <number>24</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Option:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="cboOptions"/>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Advanced:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QTextEdit" name="txtAdvanced">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>75</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::NoButton</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>Dialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>Dialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
Index: src/windows/views/timeline_webview.py
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/windows/views/timeline_webview.py (date 1592265000000)
+++ src/windows/views/timeline_webview.py (date 1594417794000)
@@ -38,7 +38,7 @@
from PyQt5.QtCore import QFileInfo, pyqtSlot, QUrl, Qt, QCoreApplication, QTimer
from PyQt5.QtGui import QCursor, QKeySequence
from PyQt5.QtWebKitWidgets import QWebView
-from PyQt5.QtWidgets import QMenu
+from PyQt5.QtWidgets import QMenu, QDialog

from classes import info, updates
from classes import settings
@@ -2939,6 +2939,9 @@
# Add Effect
def addEffect(self, effect_names, position):
log.info("addEffect: %s at %s" % (effect_names, position))
+ # Translation object
+ _ = get_app()._tr
+
# Get name of effect
name = effect_names[0]

@@ -2951,13 +2954,23 @@
# Loop through clips on the closest layer
possible_clips = Clip.filter(layer=closest_layer)
for clip in possible_clips:
- if js_position == 0 or (
- clip.data["position"] <= js_position <= clip.data["position"]
- + (clip.data["end"] - clip.data["start"])
- ):
+ if js_position == 0 or (clip.data["position"] <= js_position <= clip.data["position"] + (clip.data["end"] - clip.data["start"])):
log.info("Applying effect to clip")
log.info(clip)

+ # Handle custom effect dialogs
+ if name in ["Bars", "Stabilize", "Tracker"]:
+
+ from windows.process_effect import ProcessEffect
+ win = ProcessEffect(clip.id, name)
+ # Run the dialog event loop - blocking interaction on this window during this time
+ result = win.exec_()
+ if result == QDialog.Accepted:
+ log.info('Start processing')
+ else:
+ log.info('Cancel processing')
+ return
+
# Create Effect
effect = openshot.EffectInfo().CreateEffect(name)

@@ -2970,6 +2983,7 @@

# Update clip data for project
self.update_clip_data(clip.data, only_basic_props=False, ignore_reader=True)
+ break

# Without defining this method, the 'copy' action doesn't show with cursor
def dragMoveEvent(self, event):
16 changes: 11 additions & 5 deletions src/windows/process_effect.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,21 @@ def accept(self):
timeline_instance = get_app().window.timeline_sync.timeline
for clip_instance in timeline_instance.Clips():
if clip_instance.Id() == self.clip_id:
self.protobufPath = openshot.ClipProcessingJobs(self.effect_name, clip_instance).stabilizeVideo(clip_instance)
self.effect = openshot.EffectInfo().CreateEffect(self.effect_name, "/media/brenno/Data/projects/openshot/stabilization.data")
# self.effect.SetJson('{"Stabilizer":{"protobuf_data_path": "/home/gustavostahl/LabVisao/VideoEditor/openshot-qt/stabilization.data"}}')
# clip_instance.AddEffect(self.effect)
# return self.effect
print("Apply effect: %s to clip: %s" % (self.effect_name, clip_instance.Id()))


# EXAMPLE progress updates
for value in range(1, 100, 4):
self.progressBar.setValue(value)
time.sleep(0.25)
# for value in range(1, 100, 4):
# self.progressBar.setValue(value)
# time.sleep(0.25)

# Process any queued events
QCoreApplication.processEvents()
# # Process any queued events
# QCoreApplication.processEvents()

# Accept dialog
super(ProcessEffect, self).accept()
Expand Down
12 changes: 9 additions & 3 deletions src/windows/views/timeline_webview.py
Original file line number Diff line number Diff line change
Expand Up @@ -2959,27 +2959,33 @@ def addEffect(self, effect_names, position):
log.info(clip)

# Handle custom effect dialogs
if name in ["Bars", "Stabilize", "Tracker"]:
if name in ["Bars", "Stabilizer", "Tracker"]:

from windows.process_effect import ProcessEffect
win = ProcessEffect(clip.id, name)
win = ProcessEffect(clip.id, "Stabilizer")
# Run the dialog event loop - blocking interaction on this window during this time
result = win.exec_()

if result == QDialog.Accepted:
log.info('Start processing')
else:
log.info('Cancel processing')
return

effect = win.effect


# Create Effect
effect = openshot.EffectInfo().CreateEffect(name)
else:
effect = openshot.EffectInfo().CreateEffect(name)

# Get Effect JSON
effect.Id(get_app().project.generate_id())
effect_json = json.loads(effect.Json())

# Append effect JSON to clip
clip.data["effects"].append(effect_json)
print(clip.data["effects"])

# Update clip data for project
self.update_clip_data(clip.data, only_basic_props=False, ignore_reader=True)
Expand Down

0 comments on commit 83f6e41

Please sign in to comment.