Professional Documents
Culture Documents
@file
@brief This file loads the Addtotimeline dialog (i.e add several clips in the
timeline)
@author Jonathan Thomas <jonathan@openshot.org>
@author Olivier Girard <olivier@openshot.org>
@section LICENSE
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.
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 uuid
from operator import itemgetter
from random import shuffle, randint, uniform
import openshot
import json
class AddToTimeline(QDialog):
""" Add To timeline Dialog """
# New index
new_index = max(selected_index - 1, 0)
log.info(new_index)
# Refresh tree
self.treeFiles.refresh_view()
selected_index = None
if self.treeFiles.selected:
selected_index = self.treeFiles.selected.row()
# New index
new_index = min(selected_index + 1, len(files) - 1)
log.info(new_index)
# Refresh tree
self.treeFiles.refresh_view()
# Shuffle files
shuffle(self.treeFiles.timeline_model.files)
# Refresh tree
self.treeFiles.refresh_view()
selected_index = None
if self.treeFiles.selected:
selected_index = self.treeFiles.selected.row()
# Remove item
files.pop(selected_index)
# Refresh tree
self.treeFiles.refresh_view()
# Update total
self.updateTotal()
def accept(self):
""" Ok button clicked """
log.info('accept')
# Init position
position = start_position
random_transition = False
if transition_path == "random":
random_transition = True
# Get frames per second
fps = get_app().project.get("fps")
fps_float = float(fps["num"]) / float(fps["den"])
if 'start' in file.data:
start_time = file.data['start']
new_clip["start"] = start_time
if 'end' in file.data:
end_time = file.data['end']
new_clip["end"] = end_time
# Scale animation
start_scale = uniform(0.5, 1.5)
end_scale = uniform(0.85, 1.15)
# Scale animation
start_scale = 1.0
end_scale = 1.25
# Scale animation
start_scale = 1.25
end_scale = 1.0
# Add keyframes
start = openshot.Point(round(start_time * fps_float) + 1,
start_scale, openshot.BEZIER)
start_object = json.loads(start.Json())
end = openshot.Point(round(end_time * fps_float) + 1, end_scale,
openshot.BEZIER)
end_object = json.loads(end.Json())
new_clip["gravity"] = openshot.GRAVITY_CENTER
new_clip["scale_x"]["Points"].append(start_object)
new_clip["scale_x"]["Points"].append(end_object)
new_clip["scale_y"]["Points"].append(start_object)
new_clip["scale_y"]["Points"].append(end_object)
# Add keyframes
start_x = openshot.Point(round(start_time * fps_float) + 1,
animate_start_x, openshot.BEZIER)
start_x_object = json.loads(start_x.Json())
end_x = openshot.Point(round(end_time * fps_float) + 1,
animate_end_x, openshot.BEZIER)
end_x_object = json.loads(end_x.Json())
start_y = openshot.Point(round(start_time * fps_float) + 1,
animate_start_y, openshot.BEZIER)
start_y_object = json.loads(start_y.Json())
end_y = openshot.Point(round(end_time * fps_float) + 1,
animate_end_y, openshot.BEZIER)
end_y_object = json.loads(end_y.Json())
new_clip["gravity"] = openshot.GRAVITY_CENTER
new_clip["location_x"]["Points"].append(start_x_object)
new_clip["location_x"]["Points"].append(end_x_object)
new_clip["location_y"]["Points"].append(start_y_object)
new_clip["location_y"]["Points"].append(end_y_object)
if transition_path:
# Add transition for this clip (if any)
# Open up QtImageReader for transition Image
if random_transition:
random_index = randint(0, len(self.transitions))
transition_path = self.transitions[random_index]
brightness = openshot.Keyframe()
brightness.AddPoint(1, 1.0, openshot.BEZIER)
brightness.AddPoint(
round(
min(transition_length, end_time - start_time)
* fps_float
) + 1,
-1.0,
openshot.BEZIER)
contrast = openshot.Keyframe(3.0)
# Create transition dictionary
transitions_data = {
"layer": track_num,
"title": "Transition",
"type": "Mask",
"start": 0,
"end": min(transition_length, end_time - start_time),
"brightness": json.loads(brightness.Json()),
"contrast": json.loads(contrast.Json()),
"reader": json.loads(transition_reader.Json()),
"replace_image": False
}
# Create transition
tran = Transition()
tran.data = transitions_data
tran.save()
# Save Clip
clip.data = new_clip
clip.save()
# Clear transaction
get_app().updates.transaction_id = None
# Accept dialog
super(AddToTimeline, self).accept()
def updateTotal(self):
"""Calculate the total length of what's about to be added to the
timeline"""
fade_value = self.cmbFade.currentData()
fade_length = self.txtFadeLength.value()
transition_path = self.cmbTransition.currentData()
transition_length = self.txtTransitionLength.value()
total = 0.0
for file in self.treeFiles.timeline_model.files:
# Adjust clip duration, start, and end
duration = file.data["duration"]
if file.data["media_type"] == "image":
duration = self.txtImageLength.value()
if total != 0.0:
# Don't subtract time from initial clip
if not transition_path:
# No transitions
if fade_value is not None:
# Fade clip - subtract the fade length
duration -= fade_length
else:
# Transition
duration -= transition_length
# Update label
total_parts = time_parts.secondsToTime(total, fps["num"], fps["den"])
timestamp = "%s:%s:%s:%s" % (total_parts["hour"], total_parts["min"],
total_parts["sec"], total_parts["frame"])
self.lblTotalLengthValue.setText(timestamp)
def reject(self):
""" Cancel button clicked """
log.info('reject')
# Accept dialog
super(AddToTimeline, self).reject()
# Init UI
ui_util.init_ui(self)
# Get settings
self.settings = self.app.get_settings()
# Track metrics
track_metric_screen("add-to-timeline-screen")
# Refresh view
self.treeFiles.refresh_view()
self.cmbTransition.addItem(_('None'), None)
self.cmbTransition.addItem(_('Random'), 'random')
self.transitions = []
for group in transition_groups:
dir = group["dir"]
files = group["files"]
# Add item
self.transitions.append(path)
self.cmbTransition.addItem(QIcon(thumb_path), _(trans_name), path)
# Connections
self.btnMoveUp.clicked.connect(self.btnMoveUpClicked)
self.btnMoveDown.clicked.connect(self.btnMoveDownClicked)
self.btnShuffle.clicked.connect(self.btnShuffleClicked)
self.btnRemove.clicked.connect(self.btnRemoveClicked)
self.btnBox.accepted.connect(self.accept)
self.btnBox.rejected.connect(self.reject)
# Update total
self.updateTotal()