30 from functools
import partial
31 from PyQt5.QtCore import Qt, QRectF, QLocale, pyqtSignal, Qt, QObject, QTimer
33 from PyQt5.QtWidgets import QTableView, QAbstractItemView, QMenu, QSizePolicy, QHeaderView, QColorDialog, QItemDelegate, QStyle, QLabel, QPushButton, QHBoxLayout, QFrame
35 from classes.logger
import log
36 from classes.app
import get_app
37 from classes
import info
38 from classes.query
import Clip, Effect, Transition, File
39 from windows.models.properties_model
import PropertiesModel
40 from windows.models.transition_model
import TransitionsModel
41 from windows.models.files_model
import FilesModel
48 import simplejson
as json
53 QItemDelegate.__init__(self, parent, *args)
56 self.
curve_pixmaps = { openshot.BEZIER: QPixmap(os.path.join(info.IMAGES_PATH,
"keyframe-%s.png" % openshot.BEZIER)),
57 openshot.LINEAR: QPixmap(os.path.join(info.IMAGES_PATH,
"keyframe-%s.png" % openshot.LINEAR)),
58 openshot.CONSTANT: QPixmap(os.path.join(info.IMAGES_PATH,
"keyframe-%s.png" % openshot.CONSTANT))
61 def paint(self, painter, option, index):
63 painter.setRenderHint(QPainter.Antialiasing)
66 model =
get_app().window.propertyTableView.clip_properties_model.model
67 row = model.itemFromIndex(index).row()
68 selected_label = model.item(row, 0)
69 selected_value = model.item(row, 1)
70 property = selected_label.data()
73 property_name = property[1][
"name"]
74 property_type = property[1][
"type"]
75 property_max = property[1][
"max"]
76 property_min = property[1][
"min"]
77 readonly = property[1][
"readonly"]
78 keyframe = property[1][
"keyframe"]
79 points = property[1][
"points"]
80 interpolation = property[1][
"interpolation"]
83 if property_type
in [
"float",
"int"]:
85 current_value = QLocale().system().toDouble(selected_value.text())[0]
88 if property_min < 0.0:
89 property_shift = 0.0 - property_min
90 property_min += property_shift
91 property_max += property_shift
92 current_value += property_shift
95 min_max_range = float(property_max) - float(property_min)
96 value_percent = current_value / min_max_range
101 painter.setPen(QPen(Qt.NoPen))
102 if property_type ==
"color":
104 red = property[1][
"red"][
"value"]
105 green = property[1][
"green"][
"value"]
106 blue = property[1][
"blue"][
"value"]
107 painter.setBrush(QBrush(QColor(QColor(red, green, blue))))
110 if option.state & QStyle.State_Selected:
111 painter.setBrush(QBrush(QColor(
"#575757")))
113 painter.setBrush(QBrush(QColor(
"#3e3e3e")))
116 path = QPainterPath()
117 path.addRoundedRect(QRectF(option.rect), 15, 15)
118 painter.fillPath(path, QColor(
"#3e3e3e"))
119 painter.drawPath(path)
122 painter.setBrush(QBrush(QColor(
"#000000")))
123 mask_rect = QRectF(option.rect)
124 mask_rect.setWidth(option.rect.width() * value_percent)
125 painter.setClipRect(mask_rect, Qt.IntersectClip)
128 gradient = QLinearGradient(option.rect.topLeft(), option.rect.topRight())
129 gradient.setColorAt(0, QColor(
"#828282"))
130 gradient.setColorAt(1, QColor(
"#828282"))
133 painter.setBrush(gradient)
134 path = QPainterPath()
135 value_rect = QRectF(option.rect)
136 path.addRoundedRect(value_rect, 15, 15);
137 painter.fillPath(path, gradient)
138 painter.drawPath(path);
139 painter.setClipping(
False)
143 painter.drawPixmap(option.rect.x() + option.rect.width() - 30.0, option.rect.y() + 4, self.
curve_pixmaps[interpolation])
146 painter.setPen(QPen(Qt.white))
147 value = index.data(Qt.DisplayRole)
149 painter.drawText(option.rect, Qt.AlignCenter, value)
157 loadProperties = pyqtSignal(str, str)
161 model = self.clip_properties_model.model
162 row = self.indexAt(event.pos()).row()
163 column = self.indexAt(event.pos()).column()
164 if model.item(row, 0):
170 frame_number = self.clip_properties_model.frame_number
173 value_column_x = self.columnViewportPosition(1)
174 value_column_y = value_column_x + self.columnWidth(1)
175 cursor_value = event.x() - value_column_x
176 cursor_value_percent = cursor_value / self.columnWidth(1)
179 property = self.selected_label.data()
180 except Exception
as ex:
185 property_key = property[0]
186 property_name = property[1][
"name"]
187 property_type = property[1][
"type"]
188 property_max = property[1][
"max"]
189 property_min = property[1][
"min"]
190 property_value = property[1][
"value"]
191 readonly = property[1][
"readonly"]
192 item_id, item_type = self.selected_item.data()
201 get_app().updates.ignore_history =
True
205 if item_type ==
"clip":
207 c = Clip.get(id=item_id)
208 elif item_type ==
"transition":
210 c = Transition.get(id=item_id)
211 elif item_type ==
"effect":
213 c = Effect.get(id=item_id)
216 if property_key
in c.data:
221 if property_type
in [
"float",
"int"]:
222 min_max_range = float(property_max) - float(property_min)
225 if min_max_range > 1000.0:
227 self.
new_value = QLocale().system().toDouble(self.selected_item.text())[0]
245 self.
new_value = property_min + (min_max_range * cursor_value_percent)
255 self.viewport().update()
259 get_app().updates.ignore_history =
False
268 model = self.clip_properties_model.model
269 row = self.indexAt(event.pos()).row()
270 column = self.indexAt(event.pos()).column()
271 if model.item(row, 0):
279 model = self.clip_properties_model.model
281 row = model_index.row()
282 selected_label = model.item(row, 0)
286 property = selected_label.data()
287 property_type = property[1][
"type"]
289 if property_type ==
"color":
291 red = property[1][
"red"][
"value"]
292 green = property[1][
"green"][
"value"]
293 blue = property[1][
"blue"][
"value"]
296 currentColor = QColor(red, green, blue)
297 newColor = QColorDialog.getColor(currentColor)
300 self.clip_properties_model.color_update(self.
selected_item, newColor)
310 self.clip_properties_model.update_item(item_id, item_type)
317 self.clip_properties_model.update_frame(frame_number)
324 self.clip_properties_model.update_model(value)
328 model = self.clip_properties_model.model
329 row = self.indexAt(event.pos()).row()
330 selected_label = model.item(row, 0)
331 selected_value = model.item(row, 1)
333 frame_number = self.clip_properties_model.frame_number
341 property = selected_label.data()
342 property_name = property[1][
"name"]
344 memo = json.loads(property[1][
"memo"]
or "{}")
345 points = property[1][
"points"]
347 property_key = property[0]
348 clip_id, item_type = selected_value.data()
349 log.info(
"Context menu shown for %s (%s) for clip %s on frame %s" % (property_name, property_key, clip_id, frame_number))
350 log.info(
"Points: %s" % points)
351 log.info(
"Property: %s" % str(property))
357 self.transition_model.update_model()
358 self.files_model.update_model()
362 for filesIndex
in range(self.files_model.model.rowCount()):
363 modelIndex = self.files_model.model.index(filesIndex, 0)
364 fileItem = self.files_model.model.itemFromIndex(modelIndex)
365 fileIcon = self.files_model.model.item(fileItem.row(), 0).icon()
366 fileName = self.files_model.model.item(fileItem.row(), 1).text()
367 fileParentPath = self.files_model.model.item(fileItem.row(), 4).text()
370 file_choices.append({
"name": fileName,
"value": os.path.join(fileParentPath, fileName),
"selected":
False,
"icon": fileIcon })
373 self.choices.append({
"name": _(
"Files"),
"value": file_choices,
"selected":
False})
377 for transIndex
in range(self.transition_model.model.rowCount()):
378 modelIndex = self.transition_model.model.index(transIndex, 0)
379 transItem = self.transition_model.model.itemFromIndex(modelIndex)
380 transIcon = self.transition_model.model.item(transItem.row(), 0).icon()
381 transName = self.transition_model.model.item(transItem.row(), 1).text()
382 transPath = self.transition_model.model.item(transItem.row(), 3).text()
385 trans_choices.append({
"name": transName,
"value": transPath,
"selected":
False,
"icon": transIcon })
388 self.choices.append({
"name": _(
"Transitions"),
"value": trans_choices,
"selected":
False})
392 (0.250, 0.100, 0.250, 1.000, _(
"Ease (Default)")),
393 (0.420, 0.000, 1.000, 1.000, _(
"Ease In")),
394 (0.000, 0.000, 0.580, 1.000, _(
"Ease Out")),
395 (0.420, 0.000, 0.580, 1.000, _(
"Ease In/Out")),
397 (0.550, 0.085, 0.680, 0.530, _(
"Ease In (Quad)")),
398 (0.550, 0.055, 0.675, 0.190, _(
"Ease In (Cubic)")),
399 (0.895, 0.030, 0.685, 0.220, _(
"Ease In (Quart)")),
400 (0.755, 0.050, 0.855, 0.060, _(
"Ease In (Quint)")),
401 (0.470, 0.000, 0.745, 0.715, _(
"Ease In (Sine)")),
402 (0.950, 0.050, 0.795, 0.035, _(
"Ease In (Expo)")),
403 (0.600, 0.040, 0.980, 0.335, _(
"Ease In (Circ)")),
404 (0.600, -0.280, 0.735, 0.045, _(
"Ease In (Back)")),
406 (0.250, 0.460, 0.450, 0.940, _(
"Ease Out (Quad)")),
407 (0.215, 0.610, 0.355, 1.000, _(
"Ease Out (Cubic)")),
408 (0.165, 0.840, 0.440, 1.000, _(
"Ease Out (Quart)")),
409 (0.230, 1.000, 0.320, 1.000, _(
"Ease Out (Quint)")),
410 (0.390, 0.575, 0.565, 1.000, _(
"Ease Out (Sine)")),
411 (0.190, 1.000, 0.220, 1.000, _(
"Ease Out (Expo)")),
412 (0.075, 0.820, 0.165, 1.000, _(
"Ease Out (Circ)")),
413 (0.175, 0.885, 0.320, 1.275, _(
"Ease Out (Back)")),
415 (0.455, 0.030, 0.515, 0.955, _(
"Ease In/Out (Quad)")),
416 (0.645, 0.045, 0.355, 1.000, _(
"Ease In/Out (Cubic)")),
417 (0.770, 0.000, 0.175, 1.000, _(
"Ease In/Out (Quart)")),
418 (0.860, 0.000, 0.070, 1.000, _(
"Ease In/Out (Quint)")),
419 (0.445, 0.050, 0.550, 0.950, _(
"Ease In/Out (Sine)")),
420 (1.000, 0.000, 0.000, 1.000, _(
"Ease In/Out (Expo)")),
421 (0.785, 0.135, 0.150, 0.860, _(
"Ease In/Out (Circ)")),
422 (0.680, -0.550, 0.265, 1.550, _(
"Ease In/Out (Back)"))
425 bezier_icon = QIcon(QPixmap(os.path.join(info.IMAGES_PATH,
"keyframe-%s.png" % openshot.BEZIER)))
426 linear_icon = QIcon(QPixmap(os.path.join(info.IMAGES_PATH,
"keyframe-%s.png" % openshot.LINEAR)))
427 constant_icon = QIcon(QPixmap(os.path.join(info.IMAGES_PATH,
"keyframe-%s.png" % openshot.CONSTANT)))
433 Bezier_Menu = QMenu(_(
"Bezier"), self)
434 Bezier_Menu.setIcon(bezier_icon)
435 for bezier_preset
in bezier_presets:
436 preset_action = Bezier_Menu.addAction(bezier_preset[4])
438 menu.addMenu(Bezier_Menu)
439 Linear_Action = menu.addAction(_(
"Linear"))
440 Linear_Action.setIcon(linear_icon)
442 Constant_Action = menu.addAction(_(
"Constant"))
443 Constant_Action.setIcon(constant_icon)
446 Insert_Action = menu.addAction(_(
"Insert Keyframe"))
448 Remove_Action = menu.addAction(_(
"Remove Keyframe"))
450 menu.popup(QCursor.pos())
453 Insert_Action = menu.addAction(_(
"Insert Keyframe"))
455 Remove_Action = menu.addAction(_(
"Remove Keyframe"))
457 menu.popup(QCursor.pos())
462 if type(choice[
"value"]) != list:
464 Choice_Action = menu.addAction(_(choice[
"name"]))
465 Choice_Action.setData(choice[
"value"])
472 SubMenuRoot = QMenu(_(choice[
"name"]), self)
475 for sub_choice
in choice[
"value"]:
476 if len(choice[
"value"]) > SubMenuSize:
477 if not SubMenu
or len(SubMenu.children()) == SubMenuSize
or sub_choice == choice[
"value"][-1]:
480 SubMenuRoot.addMenu(SubMenu)
481 SubMenu = QMenu(str(SubMenuNumber), self)
482 Choice_Action = SubMenu.addAction(_(sub_choice[
"name"]))
484 Choice_Action = SubMenuRoot.addAction(_(sub_choice[
"name"]))
485 Choice_Action.setData(sub_choice[
"value"])
486 subChoiceIcon = sub_choice.get(
"icon")
488 Choice_Action.setIcon(subChoiceIcon)
490 menu.addMenu(SubMenuRoot)
493 menu.popup(QCursor.pos())
496 log.info(
"Bezier_Action_Triggered: %s" % str(preset))
499 self.clip_properties_model.value_updated(self.
selected_item, interpolation=0, interpolation_details=preset)
502 self.clip_properties_model.color_update(self.
selected_item, QColor(
"#000"), interpolation=0, interpolation_details=preset)
505 log.info(
"Linear_Action_Triggered")
508 self.clip_properties_model.value_updated(self.
selected_item, interpolation=1)
511 self.clip_properties_model.color_update(self.
selected_item, QColor(
"#000"), interpolation=1, interpolation_details=[])
514 log.info(
"Constant_Action_Triggered")
517 self.clip_properties_model.value_updated(self.
selected_item, interpolation=2)
520 self.clip_properties_model.color_update(self.
selected_item, QColor(
"#000"), interpolation=2, interpolation_details=[])
523 log.info(
"Insert_Action_Triggered")
525 current_value = QLocale().system().toDouble(self.selected_item.text())[0]
526 self.clip_properties_model.value_updated(self.
selected_item, value=current_value)
529 log.info(
"Remove_Action_Triggered")
530 self.clip_properties_model.remove_keyframe(self.
selected_item)
533 log.info(
"Choice_Action_Triggered")
534 choice_value = self.sender().data()
537 self.clip_properties_model.value_updated(self.
selected_item, value=choice_value)
541 QTableView.__init__(self, *args)
559 self.setModel(self.clip_properties_model.model)
560 self.setSelectionBehavior(QAbstractItemView.SelectRows)
561 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
562 self.setWordWrap(
True)
566 self.setItemDelegateForColumn(1, delegate)
570 horizontal_header = self.horizontalHeader()
571 horizontal_header.setSectionResizeMode(QHeaderView.Stretch)
572 vertical_header = self.verticalHeader()
573 vertical_header.setVisible(
False)
576 self.clip_properties_model.update_model()
577 self.transition_model.update_model()
578 self.files_model.update_model()
581 self.resizeColumnToContents(0)
582 self.resizeColumnToContents(1)
604 item = Clip.get(id=self.
item_id)
608 item = Transition.get(id=self.
item_id)
612 item = Effect.get(id=self.
item_id)
621 for item_id
in get_app().window.selected_clips:
622 clip = Clip.get(id=item_id)
624 item_name = clip.title()
625 item_icon = QIcon(QPixmap(clip.data.get(
'image')))
626 action = menu.addAction(item_name)
627 action.setIcon(item_icon)
628 action.setData({
'item_id':item_id,
'item_type':
'clip'})
632 for effect
in clip.data.get(
'effects'):
633 effect = Effect.get(id=effect.get(
'id'))
635 item_name = effect.title()
636 item_icon = QIcon(QPixmap(os.path.join(info.PATH,
"effects",
"icons",
"%s.png" % effect.data.get(
'class_name').lower())))
637 action = menu.addAction(
' > %s' % _(item_name))
638 action.setIcon(item_icon)
639 action.setData({
'item_id': effect.id,
'item_type':
'effect'})
643 for item_id
in get_app().window.selected_transitions:
644 trans = Transition.get(id=item_id)
646 item_name = _(trans.title())
647 item_icon = QIcon(QPixmap(trans.data.get(
'reader',{}).get(
'path')))
648 action = menu.addAction(_(item_name))
649 action.setIcon(item_icon)
650 action.setData({
'item_id': item_id,
'item_type':
'transition'})
654 for item_id
in get_app().window.selected_effects:
655 effect = Effect.get(id=item_id)
657 item_name = _(effect.title())
658 item_icon = QIcon(QPixmap(os.path.join(info.PATH,
"effects",
"icons",
"%s.png" % effect.data.get(
'class_name').lower())))
659 action = menu.addAction(_(item_name))
660 action.setIcon(item_icon)
661 action.setData({
'item_id': item_id,
'item_type':
'effect'})
669 item_id = self.sender().data()[
'item_id']
670 item_type = self.sender().data()[
'item_type']
671 log.info(
'switch selection to %s:%s' % (item_id, item_type))
674 get_app().window.propertyTableView.loadProperties.emit(item_id, item_type)
688 clip = Clip.get(id=self.
item_id)
691 self.
item_icon = QIcon(QPixmap(clip.data.get(
'image')))
693 trans = Transition.get(id=self.
item_id)
696 self.
item_icon = QIcon(QPixmap(trans.data.get(
'reader', {}).get(
'path')))
698 effect = Effect.get(id=self.
item_id)
701 self.
item_icon = QIcon(QPixmap(os.path.join(info.PATH,
"effects",
"icons",
"%s.png" % effect.data.get(
'class_name').lower())))
709 self.lblSelection.setText(
"<strong>%s</strong>" % _(
"Selection:"))
710 self.btnSelectionName.setText(self.
item_name)
711 self.btnSelectionName.setVisible(
True)
713 self.btnSelectionName.setIcon(self.
item_icon)
715 self.lblSelection.setText(
"<strong>%s</strong>" % _(
"No Selection"))
716 self.btnSelectionName.setVisible(
False)
719 self.btnSelectionName.setMenu(self.
getMenu())
723 QFrame.__init__(self, *args)
732 self.lblSelection.setText(
"<strong>%s</strong>" % _(
"No Selection"))
734 self.btnSelectionName.setVisible(
False)
735 self.btnSelectionName.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
738 self.lblSelection.setTextFormat(Qt.RichText)
741 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