30 from functools
import partial
31 from operator
import itemgetter
32 from PyQt5.QtCore import Qt, QRectF, QLocale, pyqtSignal, Qt, QObject, QTimer
34 from PyQt5.QtWidgets import QTableView, QAbstractItemView, QMenu, QSizePolicy, QHeaderView, QColorDialog, QItemDelegate, QStyle, QLabel, QPushButton, QHBoxLayout, QFrame
36 from classes.logger
import log
37 from classes.app
import get_app
38 from classes
import info
39 from classes.query
import Clip, Effect, Transition, File
40 from windows.models.properties_model
import PropertiesModel
41 from windows.models.transition_model
import TransitionsModel
42 from windows.models.files_model
import FilesModel
49 import simplejson
as json
54 QItemDelegate.__init__(self, parent, *args)
57 self.
curve_pixmaps = { openshot.BEZIER: QPixmap(os.path.join(info.IMAGES_PATH,
"keyframe-%s.png" % openshot.BEZIER)),
58 openshot.LINEAR: QPixmap(os.path.join(info.IMAGES_PATH,
"keyframe-%s.png" % openshot.LINEAR)),
59 openshot.CONSTANT: QPixmap(os.path.join(info.IMAGES_PATH,
"keyframe-%s.png" % openshot.CONSTANT))
62 def paint(self, painter, option, index):
64 painter.setRenderHint(QPainter.Antialiasing)
67 model =
get_app().window.propertyTableView.clip_properties_model.model
68 row = model.itemFromIndex(index).row()
69 selected_label = model.item(row, 0)
70 selected_value = model.item(row, 1)
71 property = selected_label.data()
74 property_name = property[1][
"name"]
75 property_type = property[1][
"type"]
76 property_max = property[1][
"max"]
77 property_min = property[1][
"min"]
78 readonly = property[1][
"readonly"]
79 keyframe = property[1][
"keyframe"]
80 points = property[1][
"points"]
81 interpolation = property[1][
"interpolation"]
84 if property_type
in [
"float",
"int"]:
86 current_value = QLocale().system().toDouble(selected_value.text())[0]
89 if property_min < 0.0:
90 property_shift = 0.0 - property_min
91 property_min += property_shift
92 property_max += property_shift
93 current_value += property_shift
96 min_max_range = float(property_max) - float(property_min)
97 value_percent = current_value / min_max_range
102 painter.setPen(QPen(Qt.NoPen))
103 if property_type ==
"color":
105 red = property[1][
"red"][
"value"]
106 green = property[1][
"green"][
"value"]
107 blue = property[1][
"blue"][
"value"]
108 painter.setBrush(QBrush(QColor(QColor(red, green, blue))))
111 if option.state & QStyle.State_Selected:
112 painter.setBrush(QBrush(QColor(
"#575757")))
114 painter.setBrush(QBrush(QColor(
"#3e3e3e")))
117 path = QPainterPath()
118 path.addRoundedRect(QRectF(option.rect), 15, 15)
119 painter.fillPath(path, QColor(
"#3e3e3e"))
120 painter.drawPath(path)
123 painter.setBrush(QBrush(QColor(
"#000000")))
124 mask_rect = QRectF(option.rect)
125 mask_rect.setWidth(option.rect.width() * value_percent)
126 painter.setClipRect(mask_rect, Qt.IntersectClip)
129 gradient = QLinearGradient(option.rect.topLeft(), option.rect.topRight())
130 gradient.setColorAt(0, QColor(
"#828282"))
131 gradient.setColorAt(1, QColor(
"#828282"))
134 painter.setBrush(gradient)
135 path = QPainterPath()
136 value_rect = QRectF(option.rect)
137 path.addRoundedRect(value_rect, 15, 15);
138 painter.fillPath(path, gradient)
139 painter.drawPath(path);
140 painter.setClipping(
False)
144 painter.drawPixmap(option.rect.x() + option.rect.width() - 30.0, option.rect.y() + 4, self.
curve_pixmaps[interpolation])
147 painter.setPen(QPen(Qt.white))
148 value = index.data(Qt.DisplayRole)
150 painter.drawText(option.rect, Qt.AlignCenter, value)
158 loadProperties = pyqtSignal(str, str)
162 model = self.clip_properties_model.model
163 row = self.indexAt(event.pos()).row()
164 column = self.indexAt(event.pos()).column()
165 if model.item(row, 0):
171 frame_number = self.clip_properties_model.frame_number
174 value_column_x = self.columnViewportPosition(1)
175 value_column_y = value_column_x + self.columnWidth(1)
176 cursor_value = event.x() - value_column_x
177 cursor_value_percent = cursor_value / self.columnWidth(1)
180 property = self.selected_label.data()
181 except Exception
as ex:
186 property_key = property[0]
187 property_name = property[1][
"name"]
188 property_type = property[1][
"type"]
189 property_max = property[1][
"max"]
190 property_min = property[1][
"min"]
191 property_value = property[1][
"value"]
192 readonly = property[1][
"readonly"]
193 item_id, item_type = self.selected_item.data()
202 get_app().updates.ignore_history =
True
206 if item_type ==
"clip":
208 c = Clip.get(id=item_id)
209 elif item_type ==
"transition":
211 c = Transition.get(id=item_id)
212 elif item_type ==
"effect":
214 c = Effect.get(id=item_id)
217 if property_key
in c.data:
222 if property_type
in [
"float",
"int"]
and property_name !=
"Track":
223 min_max_range = float(property_max) - float(property_min)
226 if min_max_range > 1000.0:
228 self.
new_value = QLocale().system().toDouble(self.selected_item.text())[0]
246 self.
new_value = property_min + (min_max_range * cursor_value_percent)
256 self.viewport().update()
260 get_app().updates.ignore_history =
False
269 model = self.clip_properties_model.model
270 row = self.indexAt(event.pos()).row()
271 column = self.indexAt(event.pos()).column()
272 if model.item(row, 0):
284 model = self.clip_properties_model.model
286 row = model_index.row()
287 selected_label = model.item(row, 0)
291 property = selected_label.data()
292 property_type = property[1][
"type"]
294 if property_type ==
"color":
296 red = property[1][
"red"][
"value"]
297 green = property[1][
"green"][
"value"]
298 blue = property[1][
"blue"][
"value"]
301 currentColor = QColor(red, green, blue)
302 newColor = QColorDialog.getColor(currentColor, self, _(
"Select a Color"),
303 QColorDialog.DontUseNativeDialog)
306 self.clip_properties_model.color_update(self.
selected_item, newColor)
316 self.clip_properties_model.update_item(item_id, item_type)
323 self.clip_properties_model.update_frame(frame_number)
330 self.clip_properties_model.update_model(value)
334 model = self.clip_properties_model.model
335 row = self.indexAt(event.pos()).row()
336 selected_label = model.item(row, 0)
337 selected_value = model.item(row, 1)
339 frame_number = self.clip_properties_model.frame_number
347 property = selected_label.data()
348 property_name = property[1][
"name"]
350 memo = json.loads(property[1][
"memo"]
or "{}")
351 points = property[1][
"points"]
353 property_key = property[0]
354 clip_id, item_type = selected_value.data()
355 log.info(
"Context menu shown for %s (%s) for clip %s on frame %s" % (property_name, property_key, clip_id, frame_number))
356 log.info(
"Points: %s" % points)
357 log.info(
"Property: %s" % str(property))
363 self.transition_model.update_model()
364 self.files_model.update_model()
368 for filesIndex
in range(self.files_model.model.rowCount()):
369 modelIndex = self.files_model.model.index(filesIndex, 0)
370 fileItem = self.files_model.model.itemFromIndex(modelIndex)
371 fileIcon = self.files_model.model.item(fileItem.row(), 0).icon()
372 fileName = self.files_model.model.item(fileItem.row(), 1).text()
373 fileParentPath = self.files_model.model.item(fileItem.row(), 4).text()
376 file_choices.append({
"name": fileName,
"value": os.path.join(fileParentPath, fileName),
"selected":
False,
"icon": fileIcon })
379 self.choices.append({
"name": _(
"Files"),
"value": file_choices,
"selected":
False})
383 for transIndex
in range(self.transition_model.model.rowCount()):
384 modelIndex = self.transition_model.model.index(transIndex, 0)
385 transItem = self.transition_model.model.itemFromIndex(modelIndex)
386 transIcon = self.transition_model.model.item(transItem.row(), 0).icon()
387 transName = self.transition_model.model.item(transItem.row(), 1).text()
388 transPath = self.transition_model.model.item(transItem.row(), 3).text()
391 trans_choices.append({
"name": transName,
"value": transPath,
"selected":
False,
"icon": transIcon })
394 self.choices.append({
"name": _(
"Transitions"),
"value": trans_choices,
"selected":
False})
400 all_tracks =
get_app().project.get([
"layers"])
401 display_count = len(all_tracks)
402 for track
in reversed(sorted(all_tracks, key=itemgetter(
'number'))):
404 track_name = track.get(
"label")
or _(
"Track %s") % display_count
405 self.choices.append({
"name": track_name,
"value": track.get(
"number"),
"selected":
False })
410 (0.250, 0.100, 0.250, 1.000, _(
"Ease (Default)")),
411 (0.420, 0.000, 1.000, 1.000, _(
"Ease In")),
412 (0.000, 0.000, 0.580, 1.000, _(
"Ease Out")),
413 (0.420, 0.000, 0.580, 1.000, _(
"Ease In/Out")),
415 (0.550, 0.085, 0.680, 0.530, _(
"Ease In (Quad)")),
416 (0.550, 0.055, 0.675, 0.190, _(
"Ease In (Cubic)")),
417 (0.895, 0.030, 0.685, 0.220, _(
"Ease In (Quart)")),
418 (0.755, 0.050, 0.855, 0.060, _(
"Ease In (Quint)")),
419 (0.470, 0.000, 0.745, 0.715, _(
"Ease In (Sine)")),
420 (0.950, 0.050, 0.795, 0.035, _(
"Ease In (Expo)")),
421 (0.600, 0.040, 0.980, 0.335, _(
"Ease In (Circ)")),
422 (0.600, -0.280, 0.735, 0.045, _(
"Ease In (Back)")),
424 (0.250, 0.460, 0.450, 0.940, _(
"Ease Out (Quad)")),
425 (0.215, 0.610, 0.355, 1.000, _(
"Ease Out (Cubic)")),
426 (0.165, 0.840, 0.440, 1.000, _(
"Ease Out (Quart)")),
427 (0.230, 1.000, 0.320, 1.000, _(
"Ease Out (Quint)")),
428 (0.390, 0.575, 0.565, 1.000, _(
"Ease Out (Sine)")),
429 (0.190, 1.000, 0.220, 1.000, _(
"Ease Out (Expo)")),
430 (0.075, 0.820, 0.165, 1.000, _(
"Ease Out (Circ)")),
431 (0.175, 0.885, 0.320, 1.275, _(
"Ease Out (Back)")),
433 (0.455, 0.030, 0.515, 0.955, _(
"Ease In/Out (Quad)")),
434 (0.645, 0.045, 0.355, 1.000, _(
"Ease In/Out (Cubic)")),
435 (0.770, 0.000, 0.175, 1.000, _(
"Ease In/Out (Quart)")),
436 (0.860, 0.000, 0.070, 1.000, _(
"Ease In/Out (Quint)")),
437 (0.445, 0.050, 0.550, 0.950, _(
"Ease In/Out (Sine)")),
438 (1.000, 0.000, 0.000, 1.000, _(
"Ease In/Out (Expo)")),
439 (0.785, 0.135, 0.150, 0.860, _(
"Ease In/Out (Circ)")),
440 (0.680, -0.550, 0.265, 1.550, _(
"Ease In/Out (Back)"))
443 bezier_icon = QIcon(QPixmap(os.path.join(info.IMAGES_PATH,
"keyframe-%s.png" % openshot.BEZIER)))
444 linear_icon = QIcon(QPixmap(os.path.join(info.IMAGES_PATH,
"keyframe-%s.png" % openshot.LINEAR)))
445 constant_icon = QIcon(QPixmap(os.path.join(info.IMAGES_PATH,
"keyframe-%s.png" % openshot.CONSTANT)))
451 Bezier_Menu = QMenu(_(
"Bezier"), self)
452 Bezier_Menu.setIcon(bezier_icon)
453 for bezier_preset
in bezier_presets:
454 preset_action = Bezier_Menu.addAction(bezier_preset[4])
456 menu.addMenu(Bezier_Menu)
457 Linear_Action = menu.addAction(_(
"Linear"))
458 Linear_Action.setIcon(linear_icon)
460 Constant_Action = menu.addAction(_(
"Constant"))
461 Constant_Action.setIcon(constant_icon)
464 Insert_Action = menu.addAction(_(
"Insert Keyframe"))
466 Remove_Action = menu.addAction(_(
"Remove Keyframe"))
468 menu.popup(QCursor.pos())
471 Insert_Action = menu.addAction(_(
"Insert Keyframe"))
473 Remove_Action = menu.addAction(_(
"Remove Keyframe"))
475 menu.popup(QCursor.pos())
480 if type(choice[
"value"]) != list:
482 Choice_Action = menu.addAction(_(choice[
"name"]))
483 Choice_Action.setData(choice[
"value"])
490 SubMenuRoot = QMenu(_(choice[
"name"]), self)
493 for sub_choice
in choice[
"value"]:
494 if len(choice[
"value"]) > SubMenuSize:
495 if not SubMenu
or len(SubMenu.children()) == SubMenuSize
or sub_choice == choice[
"value"][-1]:
498 SubMenuRoot.addMenu(SubMenu)
499 SubMenu = QMenu(str(SubMenuNumber), self)
500 Choice_Action = SubMenu.addAction(_(sub_choice[
"name"]))
502 Choice_Action = SubMenuRoot.addAction(_(sub_choice[
"name"]))
503 Choice_Action.setData(sub_choice[
"value"])
504 subChoiceIcon = sub_choice.get(
"icon")
506 Choice_Action.setIcon(subChoiceIcon)
508 menu.addMenu(SubMenuRoot)
511 menu.popup(QCursor.pos())
514 log.info(
"Bezier_Action_Triggered: %s" % str(preset))
517 self.clip_properties_model.value_updated(self.
selected_item, interpolation=0, interpolation_details=preset)
520 self.clip_properties_model.color_update(self.
selected_item, QColor(
"#000"), interpolation=0, interpolation_details=preset)
523 log.info(
"Linear_Action_Triggered")
526 self.clip_properties_model.value_updated(self.
selected_item, interpolation=1)
529 self.clip_properties_model.color_update(self.
selected_item, QColor(
"#000"), interpolation=1, interpolation_details=[])
532 log.info(
"Constant_Action_Triggered")
535 self.clip_properties_model.value_updated(self.
selected_item, interpolation=2)
538 self.clip_properties_model.color_update(self.
selected_item, QColor(
"#000"), interpolation=2, interpolation_details=[])
541 log.info(
"Insert_Action_Triggered")
543 current_value = QLocale().system().toDouble(self.selected_item.text())[0]
544 self.clip_properties_model.value_updated(self.
selected_item, value=current_value)
547 log.info(
"Remove_Action_Triggered")
548 self.clip_properties_model.remove_keyframe(self.
selected_item)
551 log.info(
"Choice_Action_Triggered")
552 choice_value = self.sender().data()
555 self.clip_properties_model.value_updated(self.
selected_item, value=choice_value)
559 QTableView.__init__(self, *args)
577 self.setModel(self.clip_properties_model.model)
578 self.setSelectionBehavior(QAbstractItemView.SelectRows)
579 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
580 self.setWordWrap(
True)
584 self.setItemDelegateForColumn(1, delegate)
588 horizontal_header = self.horizontalHeader()
589 horizontal_header.setSectionResizeMode(QHeaderView.Stretch)
590 vertical_header = self.verticalHeader()
591 vertical_header.setVisible(
False)
594 self.clip_properties_model.update_model()
595 self.transition_model.update_model()
596 self.files_model.update_model()
599 self.resizeColumnToContents(0)
600 self.resizeColumnToContents(1)
622 item = Clip.get(id=self.
item_id)
626 item = Transition.get(id=self.
item_id)
630 item = Effect.get(id=self.
item_id)
639 for item_id
in get_app().window.selected_clips:
640 clip = Clip.get(id=item_id)
642 item_name = clip.title()
643 item_icon = QIcon(QPixmap(clip.data.get(
'image')))
644 action = menu.addAction(item_name)
645 action.setIcon(item_icon)
646 action.setData({
'item_id':item_id,
'item_type':
'clip'})
650 for effect
in clip.data.get(
'effects'):
651 effect = Effect.get(id=effect.get(
'id'))
653 item_name = effect.title()
654 item_icon = QIcon(QPixmap(os.path.join(info.PATH,
"effects",
"icons",
"%s.png" % effect.data.get(
'class_name').lower())))
655 action = menu.addAction(
' > %s' % _(item_name))
656 action.setIcon(item_icon)
657 action.setData({
'item_id': effect.id,
'item_type':
'effect'})
661 for item_id
in get_app().window.selected_transitions:
662 trans = Transition.get(id=item_id)
664 item_name = _(trans.title())
665 item_icon = QIcon(QPixmap(trans.data.get(
'reader',{}).get(
'path')))
666 action = menu.addAction(_(item_name))
667 action.setIcon(item_icon)
668 action.setData({
'item_id': item_id,
'item_type':
'transition'})
672 for item_id
in get_app().window.selected_effects:
673 effect = Effect.get(id=item_id)
675 item_name = _(effect.title())
676 item_icon = QIcon(QPixmap(os.path.join(info.PATH,
"effects",
"icons",
"%s.png" % effect.data.get(
'class_name').lower())))
677 action = menu.addAction(_(item_name))
678 action.setIcon(item_icon)
679 action.setData({
'item_id': item_id,
'item_type':
'effect'})
687 item_id = self.sender().data()[
'item_id']
688 item_type = self.sender().data()[
'item_type']
689 log.info(
'switch selection to %s:%s' % (item_id, item_type))
692 get_app().window.propertyTableView.loadProperties.emit(item_id, item_type)
706 clip = Clip.get(id=self.
item_id)
709 self.
item_icon = QIcon(QPixmap(clip.data.get(
'image')))
711 trans = Transition.get(id=self.
item_id)
714 self.
item_icon = QIcon(QPixmap(trans.data.get(
'reader', {}).get(
'path')))
716 effect = Effect.get(id=self.
item_id)
719 self.
item_icon = QIcon(QPixmap(os.path.join(info.PATH,
"effects",
"icons",
"%s.png" % effect.data.get(
'class_name').lower())))
727 self.lblSelection.setText(
"<strong>%s</strong>" % _(
"Selection:"))
728 self.btnSelectionName.setText(self.
item_name)
729 self.btnSelectionName.setVisible(
True)
731 self.btnSelectionName.setIcon(self.
item_icon)
733 self.lblSelection.setText(
"<strong>%s</strong>" % _(
"No Selection"))
734 self.btnSelectionName.setVisible(
False)
737 self.btnSelectionName.setMenu(self.
getMenu())
741 QFrame.__init__(self, *args)
750 self.lblSelection.setText(
"<strong>%s</strong>" % _(
"No Selection"))
752 self.btnSelectionName.setVisible(
False)
753 self.btnSelectionName.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
756 self.lblSelection.setTextFormat(Qt.RichText)
759 hbox.setContentsMargins(0,0,0,0)
def Constant_Action_Triggered
def Remove_Action_Triggered
def get_app
Returns the current QApplication instance of OpenShot.
def Choice_Action_Triggered
def Linear_Action_Triggered
def filter_changed
Filter the list of properties.
def select_item
Update the selected item in the properties window.
The label to display selections.
A Properties Table QWidget used on the main window.
def doubleClickedCB
Double click handler for the property table.
def Insert_Action_Triggered
def select_frame
Update the values of the selected clip, based on the current frame.
def Bezier_Action_Triggered