30 from collections
import OrderedDict
31 from operator
import itemgetter
36 from classes
import updates
37 from classes
import info
38 from classes.query
import Clip, Transition, Effect, File
39 from classes.logger
import log
40 from classes.app
import get_app
46 import simplejson
as json
51 QStandardItemModel.__init__(self)
60 selected_row = self.itemFromIndex(item).row()
61 property_names.append(self.item(selected_row, 0).data())
62 data.setText(json.dumps(property_names))
73 if action.key
and action.key[0]
in [
"clips",
"effects"]
and action.type
in [
"update",
"insert"]:
74 log.info(action.values)
85 self.update_timer.start()
97 log.info(
"Update item: %s" % item_type)
99 if item_type ==
"clip":
101 clips =
get_app().window.timeline_sync.timeline.Clips()
103 if clip.Id() == item_id:
108 self.selected.append((c, item_type))
110 if item_type ==
"transition":
112 trans =
get_app().window.timeline_sync.timeline.Effects()
114 if tran.Id() == item_id:
119 self.selected.append((t, item_type))
121 if item_type ==
"effect":
123 clips =
get_app().window.timeline_sync.timeline.Clips()
125 for effect
in clip.Effects():
126 if effect.Id() == item_id:
134 self.selected.append((e, item_type))
158 if item_type ==
"effect":
160 effect = Effect.get(id=clip.Id())
165 parent_clip_id = effect.parent[
"id"]
168 clips =
get_app().window.timeline_sync.timeline.Clips()
170 if c.Id() == parent_clip_id:
176 fps =
get_app().project.get([
"fps"])
177 fps_float = float(fps[
"num"]) / float(fps[
"den"])
180 requested_time = float(frame_number - 1) / fps_float
183 time_diff = (requested_time - clip.Position()) + clip.Start()
187 min_frame_number = round((clip.Start() * fps_float)) + 1
188 max_frame_number = round((clip.End() * fps_float)) + 1
207 property = self.model.item(item.row(), 0).data()
208 property_name = property[1][
"name"]
209 property_type = property[1][
"type"]
210 closest_point_x = property[1][
"closest_point_x"]
211 property_type = property[1][
"type"]
212 property_key = property[0]
213 clip_id, item_type = item.data()
219 if item_type ==
"clip":
221 c = Clip.get(id=clip_id)
222 elif item_type ==
"transition":
224 c = Transition.get(id=clip_id)
225 elif item_type ==
"effect":
227 c = Effect.get(id=clip_id)
231 if property_key
in c.data:
232 log.info(
"remove keyframe: %s" % c.data)
236 if property_type ==
"color":
237 keyframe_list = [c.data[property_key][
"red"], c.data[property_key][
"blue"], c.data[property_key][
"green"]]
239 keyframe_list = [c.data[property_key]]
242 for keyframe
in keyframe_list:
247 point_to_delete =
None
248 for point
in keyframe[
"Points"]:
252 point_to_delete = point
254 if point[
"co"][
"X"] == closest_point_x:
255 closest_point = point
258 if not point_to_delete:
259 point_to_delete = closest_point
264 log.info(
"Found point to delete at X=%s" % point_to_delete[
"co"][
"X"])
265 keyframe[
"Points"].remove(point_to_delete)
268 c.data = {property_key: c.data[property_key]}
276 get_app().window.refreshFrameSignal.emit()
279 self.parent.clearSelection()
283 def color_update(self, item, new_color, interpolation=-1, interpolation_details=[]):
286 property = self.model.item(item.row(), 0).data()
287 property_type = property[1][
"type"]
288 closest_point_x = property[1][
"closest_point_x"]
289 previous_point_x = property[1][
"previous_point_x"]
290 property_key = property[0]
291 clip_id, item_type = item.data()
293 if property_type ==
"color":
298 if item_type ==
"clip":
300 c = Clip.get(id=clip_id)
301 elif item_type ==
"transition":
303 c = Transition.get(id=clip_id)
304 elif item_type ==
"effect":
306 c = Effect.get(id=clip_id)
310 if property_key
in c.data:
311 log.info(
"color update: %s" % c.data)
314 for color, new_value
in [(
"red", new_color.red()), (
"blue", new_color.blue()), (
"green", new_color.green())]:
319 for point
in c.data[property_key][color][
"Points"]:
320 log.info(
"looping points: co.X = %s" % point[
"co"][
"X"])
321 if interpolation == -1
and point[
"co"][
"X"] == self.
frame_number:
326 point[
"co"][
"Y"] = new_value
327 log.info(
"updating point: co.X = %s to value: %s" % (point[
"co"][
"X"], float(new_value)))
330 elif interpolation > -1
and point[
"co"][
"X"] == previous_point_x:
334 point[
"interpolation"] = interpolation
335 if interpolation == 0:
336 point[
"handle_right"] = point.get(
"handle_right")
or {
"Y": 0.0,
"X": 0.0}
337 point[
"handle_right"][
"X"] = interpolation_details[0]
338 point[
"handle_right"][
"Y"] = interpolation_details[1]
340 log.info(
"updating interpolation mode point: co.X = %s to %s" % (point[
"co"][
"X"], interpolation))
341 log.info(
"use interpolation preset: %s" % str(interpolation_details))
343 elif interpolation > -1
and point[
"co"][
"X"] == closest_point_x:
347 point[
"interpolation"] = interpolation
348 if interpolation == 0:
349 point[
"handle_left"] = point.get(
"handle_left")
or {
"Y": 0.0,
"X": 0.0}
350 point[
"handle_left"][
"X"] = interpolation_details[2]
351 point[
"handle_left"][
"Y"] = interpolation_details[3]
353 log.info(
"updating interpolation mode point: co.X = %s to %s" % (point[
"co"][
"X"], interpolation))
354 log.info(
"use interpolation preset: %s" % str(interpolation_details))
359 log.info(
"Created new point at X=%s" % self.
frame_number)
360 c.data[property_key][color][
"Points"].append({
'co': {
'X': self.
frame_number,
'Y': new_value},
'interpolation': 1})
363 c.data = {property_key: c.data[property_key]}
371 get_app().window.refreshFrameSignal.emit()
374 self.parent.clearSelection()
378 def value_updated(self, item, interpolation=-1, value=None, interpolation_details=[]):
387 property = self.model.item(item.row(), 0).data()
388 property_name = property[1][
"name"]
389 closest_point_x = property[1][
"closest_point_x"]
390 previous_point_x = property[1][
"previous_point_x"]
391 property_type = property[1][
"type"]
392 property_key = property[0]
393 clip_id, item_type = item.data()
401 elif property_type ==
"string":
403 new_value = item.text()
404 elif property_type ==
"bool":
406 if item.text() == _(
"False"):
410 elif property_type ==
"int":
412 new_value = QLocale().system().toInt(item.text())[0]
415 new_value = QLocale().system().toFloat(item.text())[0]
419 log.info(
"%s for %s changed to %s at frame %s with interpolation: %s at closest x: %s" % (property_key, clip_id, new_value, self.
frame_number, interpolation, closest_point_x))
426 if item_type ==
"clip":
428 c = Clip.get(id=clip_id)
429 elif item_type ==
"transition":
431 c = Transition.get(id=clip_id)
432 elif item_type ==
"effect":
434 c = Effect.get(id=clip_id)
438 if property_key
in c.data:
439 log.info(
"value updated: %s" % c.data)
442 if property_type !=
"reader" and type(c.data[property_key]) == dict:
446 point_to_delete =
None
447 for point
in c.data[property_key][
"Points"]:
448 log.info(
"looping points: co.X = %s" % point[
"co"][
"X"])
449 if interpolation == -1
and point[
"co"][
"X"] == self.
frame_number:
454 if new_value !=
None:
455 point[
"co"][
"Y"] = float(new_value)
456 log.info(
"updating point: co.X = %s to value: %s" % (point[
"co"][
"X"], float(new_value)))
458 point_to_delete = point
461 elif interpolation > -1
and point[
"co"][
"X"] == previous_point_x:
465 point[
"interpolation"] = interpolation
466 if interpolation == 0:
467 point[
"handle_right"] = point.get(
"handle_right")
or {
"Y": 0.0,
"X": 0.0}
468 point[
"handle_right"][
"X"] = interpolation_details[0]
469 point[
"handle_right"][
"Y"] = interpolation_details[1]
471 log.info(
"updating interpolation mode point: co.X = %s to %s" % (point[
"co"][
"X"], interpolation))
472 log.info(
"use interpolation preset: %s" % str(interpolation_details))
474 elif interpolation > -1
and point[
"co"][
"X"] == closest_point_x:
478 point[
"interpolation"] = interpolation
479 if interpolation == 0:
480 point[
"handle_left"] = point.get(
"handle_left")
or {
"Y": 0.0,
"X": 0.0}
481 point[
"handle_left"][
"X"] = interpolation_details[2]
482 point[
"handle_left"][
"Y"] = interpolation_details[3]
484 log.info(
"updating interpolation mode point: co.X = %s to %s" % (point[
"co"][
"X"], interpolation))
485 log.info(
"use interpolation preset: %s" % str(interpolation_details))
490 log.info(
"Found point to delete at X=%s" % point_to_delete[
"co"][
"X"])
491 c.data[property_key][
"Points"].remove(point_to_delete)
494 elif not found_point
and new_value !=
None:
496 log.info(
"Created new point at X=%s" % self.
frame_number)
497 c.data[property_key][
"Points"].append({
'co': {
'X': self.
frame_number,
'Y': new_value},
'interpolation': 1})
501 if property_type ==
"int":
504 c.data[property_key] = int(new_value)
506 elif property_type ==
"float":
509 c.data[property_key] = new_value
511 elif property_type ==
"bool":
514 c.data[property_key] = bool(new_value)
516 elif property_type ==
"string":
519 c.data[property_key] = str(new_value)
521 elif property_type ==
"reader":
527 clip_object = openshot.Clip(value)
529 c.data[property_key] = json.loads(clip_object.Reader().Json())
533 log.info(
'Failed to load %s into Clip object for reader property' % value)
536 c.data = {property_key: c.data.get(property_key)}
544 get_app().window.refreshFrameSignal.emit()
547 self.parent.clearSelection()
550 log.info(
"updating clip properties model.")
555 self.update_timer.stop()
567 raw_properties = json.loads(c.PropertiesJSON(self.
frame_number))
568 all_properties = OrderedDict(sorted(raw_properties.items(), key=
lambda x: x[1][
'name']))
569 log.info(
"Getting properties for frame %s: %s" % (self.
frame_number, str(all_properties)))
586 self.model.setHorizontalHeaderLabels([_(
"Property"), _(
"Value")])
590 for property
in all_properties.items():
591 label = property[1][
"name"]
593 value = property[1][
"value"]
594 type = property[1][
"type"]
595 memo = property[1][
"memo"]
596 readonly = property[1][
"readonly"]
597 keyframe = property[1][
"keyframe"]
598 points = property[1][
"points"]
599 interpolation = property[1][
"interpolation"]
600 closest_point_x = property[1][
"closest_point_x"]
601 choices = property[1][
"choices"]
604 transparency_label = _(
"Transparency")
606 selected_choice =
None
608 selected_choice = [c
for c
in choices
if c[
"selected"] ==
True][0][
"name"]
611 if filter
and filter.lower()
not in name.lower():
623 col = QStandardItem(
"Property")
624 col.setText(_(label))
625 col.setData(property)
626 if keyframe
and points > 1:
627 col.setBackground(QColor(
"green"))
629 col.setBackground(QColor(42, 130, 218))
631 col.setFlags(Qt.ItemIsEnabled)
633 col.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
637 col = QStandardItem(
"Value")
639 col.setText(_(selected_choice))
640 elif type ==
"string":
646 col.setText(_(
"True"))
648 col.setText(_(
"False"))
649 elif type ==
"color":
652 elif type ==
"reader":
653 reader_json = json.loads(memo
or "{}")
654 reader_path = reader_json.get(
"path",
"/")
655 (dirName, fileName) = os.path.split(reader_path)
656 col.setText(fileName)
657 elif type ==
"int" and label ==
"Track":
659 all_tracks =
get_app().project.get([
"layers"])
660 display_count = len(all_tracks)
662 for track
in reversed(sorted(all_tracks, key=itemgetter(
'number'))):
663 if track.get(
"number") == value:
664 display_label = track.get(
"label")
667 track_name = display_label
or _(
"Track %s") % display_count
668 col.setText(track_name)
671 col.setText(
"%d" % value)
674 col.setText(QLocale().system().toString(float(value),
"f", precision=2))
675 col.setData((c.Id(), item_type))
678 my_icon = QPixmap(os.path.join(info.IMAGES_PATH,
"keyframe-%s.png" % interpolation))
679 col.setData(my_icon, Qt.DecorationRole)
683 col.setBackground(QColor(
"green"))
685 col.setBackground(QColor(42, 130, 218))
689 red = property[1][
"red"][
"value"]
690 green = property[1][
"green"][
"value"]
691 blue = property[1][
"blue"][
"value"]
692 col.setBackground(QColor(red, green, blue))
694 if readonly
or type ==
"color" or choices:
695 col.setFlags(Qt.ItemIsEnabled)
697 col.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | Qt.ItemIsEditable)
701 self.model.appendRow(row)
706 col = self.
items[name][
"row"][0]
707 col.setData(property)
710 if keyframe
and points > 1:
711 col.setBackground(QColor(
"green"))
713 col.setBackground(QColor(42, 130, 218))
715 col.setBackground(QStandardItem(
"Empty").background())
721 col = self.
items[name][
"row"][1]
723 col.setText(_(selected_choice))
724 elif type ==
"string":
730 col.setText(_(
"True"))
732 col.setText(_(
"False"))
733 elif type ==
"color":
736 elif type ==
"int" and label ==
"Track":
738 all_tracks =
get_app().project.get([
"layers"])
739 display_count = len(all_tracks)
741 for track
in reversed(sorted(all_tracks, key=itemgetter(
'number'))):
742 if track.get(
"number") == value:
743 display_label = track.get(
"label")
746 track_name = display_label
or _(
"Track %s") % display_count
747 col.setText(track_name)
749 col.setText(
"%d" % value)
750 elif type ==
"reader":
751 reader_json = json.loads(property[1].get(
"memo",
"{}"))
752 reader_path = reader_json.get(
"path",
"/")
753 (dirName, fileName) = os.path.split(reader_path)
754 col.setText(
"%s" % fileName)
757 col.setText(QLocale().system().toString(float(value),
"f", precision=2))
761 my_icon = QPixmap(os.path.join(info.IMAGES_PATH,
"keyframe-%s.png" % interpolation))
762 col.setData(my_icon, Qt.DecorationRole)
766 col.setBackground(QColor(
"green"))
768 col.setBackground(QColor(42, 130, 218))
772 col.setBackground(QStandardItem(
"Empty").background())
776 col.setData(my_icon, Qt.DecorationRole)
780 red = property[1][
"red"][
"value"]
781 green = property[1][
"green"][
"value"]
782 blue = property[1][
"blue"][
"value"]
783 col.setBackground(QColor(red, green, blue))
789 self.
items[name] = {
"row": row,
"property": property}
802 self.model.setHorizontalHeaderLabels([_(
"Property"), _(
"Value")])
824 self.model.setColumnCount(2)
829 self.update_timer.setInterval(100)
831 self.update_timer.stop()
839 get_app().updates.add_listener(self)
def get_app
Returns the current QApplication instance of OpenShot.
def value_updated
Table cell change event - also handles context menu to update interpolation value.
def remove_keyframe
Remove an existing keyframe (if any)
def color_update
Insert/Update a color keyframe for the selected row.
Interface for classes that listen for changes (insert, update, and delete).