OpenShot Video Editor  2.0.0
video_widget.py
Go to the documentation of this file.
1 ##
2 #
3 # @file
4 # @brief This file contains the video preview QWidget (based on a QLabel)
5 # @author Jonathan Thomas <jonathan@openshot.org>
6 #
7 # @section LICENSE
8 #
9 # Copyright (c) 2008-2018 OpenShot Studios, LLC
10 # (http://www.openshotstudios.com). This file is part of
11 # OpenShot Video Editor (http://www.openshot.org), an open-source project
12 # dedicated to delivering high quality video editing and animation solutions
13 # to the world.
14 #
15 # OpenShot Video Editor is free software: you can redistribute it and/or modify
16 # it under the terms of the GNU General Public License as published by
17 # the Free Software Foundation, either version 3 of the License, or
18 # (at your option) any later version.
19 #
20 # OpenShot Video Editor is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
24 #
25 # You should have received a copy of the GNU General Public License
26 # along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
27 #
28 
29 from PyQt5.QtCore import QSize, Qt, QCoreApplication, QPointF, QRect, QRectF, QMutex, QTimer
30 from PyQt5.QtGui import *
31 from PyQt5.QtWidgets import QSizePolicy, QWidget
32 import openshot # Python module for libopenshot (required video editing module installed separately)
33 
34 from classes.logger import log
35 from classes.app import get_app
36 from classes.query import Clip
37 
38 try:
39  import json
40 except ImportError:
41  import simplejson as json
42 
43 
44 ##
45 # A QWidget used on the video display widget
46 class VideoWidget(QWidget):
47 
48  ##
49  # Custom paint event
50  def paintEvent(self, event, *args):
51  self.mutex.lock()
52 
53  # Paint custom frame image on QWidget
54  painter = QPainter(self)
55  painter.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.TextAntialiasing, True)
56 
57  # Fill background black
58  painter.fillRect(event.rect(), self.palette().window())
59 
60  if self.current_image:
61  # DRAW FRAME
62  # Calculate new frame image size, maintaining aspect ratio
63  pixSize = self.current_image.size()
64  pixSize.scale(event.rect().size(), Qt.KeepAspectRatio)
65 
66  # Scale image
67  scaledPix = self.current_image.scaled(pixSize, Qt.KeepAspectRatio, Qt.SmoothTransformation)
68 
69  # Calculate center of QWidget and Draw image
70  center = self.centeredViewport(self.width(), self.height())
71  painter.drawImage(center, scaledPix)
72 
73  if self.transforming_clip:
74  # Draw transform handles on top of video preview
75  # Get framerate
76  fps = get_app().project.get(["fps"])
77  fps_float = float(fps["num"]) / float(fps["den"])
78 
79  # Determine frame # of clip
80  start_of_clip = round(float(self.transforming_clip.data["start"]) * fps_float)
81  position_of_clip = (float(self.transforming_clip.data["position"]) * fps_float) + 1
82  playhead_position = float(get_app().window.preview_thread.current_frame)
83  clip_frame_number = round(playhead_position - position_of_clip) + start_of_clip + 1
84 
85  # Get properties of clip at current frame
86  raw_properties = json.loads(self.transforming_clip_object.PropertiesJSON(clip_frame_number))
87 
88  # Get size of current video player
89  player_width = self.rect().width()
90  player_height = self.rect().height()
91 
92  # Determine original size of clip's reader
93  source_width = self.transforming_clip.data['reader']['width']
94  source_height = self.transforming_clip.data['reader']['height']
95  source_size = QSize(source_width, source_height)
96 
97  # Determine scale of clip
98  scale = self.transforming_clip.data['scale']
99  if scale == openshot.SCALE_FIT:
100  source_size.scale(player_width, player_height, Qt.KeepAspectRatio)
101 
102  elif scale == openshot.SCALE_STRETCH:
103  source_size.scale(player_width, player_height, Qt.IgnoreAspectRatio)
104 
105  elif scale == openshot.SCALE_CROP:
106  width_size = QSize(player_width, round(player_width / (float(source_width) / float(source_height))))
107  height_size = QSize(round(player_height / (float(source_height) / float(source_width))), player_height)
108  if width_size.width() >= player_width and width_size.height() >= player_height:
109  source_size.scale(width_size.width(), width_size.height(), Qt.KeepAspectRatio)
110  else:
111  source_size.scale(height_size.width(), height_size.height(), Qt.KeepAspectRatio)
112 
113  # Get new source width / height (after scaling mode applied)
114  source_width = source_size.width()
115  source_height = source_size.height()
116 
117  # Init X/Y
118  x = 0.0
119  y = 0.0
120 
121  # Get scaled source image size (scale_x, scale_y)
122  sx = max(float(raw_properties.get('scale_x').get('value')), 0.001)
123  sy = max(float(raw_properties.get('scale_y').get('value')), 0.001)
124  scaled_source_width = source_width * sx
125  scaled_source_height = source_height * sy
126 
127  # Determine gravity of clip
128  gravity = self.transforming_clip.data['gravity']
129  if gravity == openshot.GRAVITY_TOP_LEFT:
130  x += self.centeredViewport(self.width(), self.height()).x() # nudge right
131  y += self.centeredViewport(self.width(), self.height()).y() # nudge down
132  elif gravity == openshot.GRAVITY_TOP:
133  x = (player_width - scaled_source_width) / 2.0 # center
134  y += self.centeredViewport(self.width(), self.height()).y() # nudge down
135  elif gravity == openshot.GRAVITY_TOP_RIGHT:
136  x = player_width - scaled_source_width # right
137  x -= self.centeredViewport(self.width(), self.height()).x() # nudge left
138  y += self.centeredViewport(self.width(), self.height()).y() # nudge down
139  elif gravity == openshot.GRAVITY_LEFT:
140  y = (player_height - scaled_source_height) / 2.0 # center
141  x += self.centeredViewport(self.width(), self.height()).x() # nudge right
142  elif gravity == openshot.GRAVITY_CENTER:
143  x = (player_width - scaled_source_width) / 2.0 # center
144  y = (player_height - scaled_source_height) / 2.0 # center
145  elif gravity == openshot.GRAVITY_RIGHT:
146  x = player_width - scaled_source_width # right
147  y = (player_height - scaled_source_height) / 2.0 # center
148  x -= self.centeredViewport(self.width(), self.height()).x() # nudge left
149  elif gravity == openshot.GRAVITY_BOTTOM_LEFT:
150  y = (player_height - scaled_source_height) # bottom
151  x += self.centeredViewport(self.width(), self.height()).x() # nudge right
152  y -= self.centeredViewport(self.width(), self.height()).y() # nudge up
153  elif gravity == openshot.GRAVITY_BOTTOM:
154  x = (player_width - scaled_source_width) / 2.0 # center
155  y = (player_height - scaled_source_height) # bottom
156  y -= self.centeredViewport(self.width(), self.height()).y() # nudge up
157  elif gravity == openshot.GRAVITY_BOTTOM_RIGHT:
158  x = player_width - scaled_source_width # right
159  y = (player_height - scaled_source_height) # bottom
160  x -= self.centeredViewport(self.width(), self.height()).x() # nudge left
161  y -= self.centeredViewport(self.width(), self.height()).y() # nudge up
162 
163  # Track gravity starting coordinate
164  self.gravity_point = QPointF(x, y)
165 
166  # Scale to fit in widget
167  final_size = QSize(source_width, source_height)
168 
169  # Adjust x,y for location
170  x_offset = raw_properties.get('location_x').get('value')
171  y_offset = raw_properties.get('location_y').get('value')
172  x += (scaledPix.width() * x_offset)
173  y += (scaledPix.height() * y_offset)
174 
175  self.transform = QTransform()
176 
177  # Apply translate/move
178  if x or y:
179  self.transform.translate(x, y)
180 
181  # Apply scale
182  if sx or sy:
183  self.transform.scale(sx, sy)
184 
185  # Apply shear
186  shear_x = raw_properties.get('shear_x').get('value')
187  shear_y = raw_properties.get('shear_y').get('value')
188  if shear_x or shear_y:
189  self.transform.shear(shear_x, shear_y)
190 
191  # Apply rotation
192  rotation = raw_properties.get('rotation').get('value')
193  if rotation:
194  origin_x = x - self.centeredViewport(self.width(), self.height()).x() + (scaled_source_width / 2.0)
195  origin_y = y - self.centeredViewport(self.width(), self.height()).y() + (scaled_source_height / 2.0)
196  self.transform.translate(origin_x, origin_y)
197  self.transform.rotate(rotation)
198  self.transform.translate(-origin_x, -origin_y)
199 
200  # Apply transform
201  painter.setTransform(self.transform)
202 
203  # Draw transform corners and center origin circle
204  # Corner size
205  cs = 6.0
206  os = 12.0
207 
208  # Calculate 4 corners coordinates
209  self.topLeftHandle = QRectF(0.0, 0.0, cs/sx, cs/sy)
210  self.topRightHandle = QRectF(source_width - (cs/sx), 0, cs/sx, cs/sy)
211  self.bottomLeftHandle = QRectF(0.0, source_height - (cs/sy), cs/sx, cs/sy)
212  self.bottomRightHandle = QRectF(source_width - (cs/sx), source_height - (cs/sy), cs/sx, cs/sy)
213 
214  # Draw 4 corners
215  painter.fillRect(self.topLeftHandle, QBrush(QColor("#53a0ed")))
216  painter.fillRect(self.topRightHandle, QBrush(QColor("#53a0ed")))
217  painter.fillRect(self.bottomLeftHandle, QBrush(QColor("#53a0ed")))
218  painter.fillRect(self.bottomRightHandle, QBrush(QColor("#53a0ed")))
219 
220  # Calculate 4 side coordinates
221  self.topHandle = QRectF(0.0 + (source_width / 2.0) - (cs/sx/2.0), 0, cs/sx, cs/sy)
222  self.bottomHandle = QRectF(0.0 + (source_width / 2.0) - (cs/sx/2.0), source_height - (cs/sy), cs/sx, cs/sy)
223  self.leftHandle = QRectF(0.0, (source_height / 2.0) - (cs/sy/2.0), cs/sx, cs/sy)
224  self.rightHandle = QRectF(source_width - (cs/sx), (source_height / 2.0) - (cs/sy/2.0), cs/sx, cs/sy)
225 
226  # Draw 4 sides (centered)
227  painter.fillRect(self.topHandle, QBrush(QColor("#53a0ed")))
228  painter.fillRect(self.bottomHandle, QBrush(QColor("#53a0ed")))
229  painter.fillRect(self.leftHandle, QBrush(QColor("#53a0ed")))
230  painter.fillRect(self.rightHandle, QBrush(QColor("#53a0ed")))
231 
232  # Calculate center coordinate
233  self.centerHandle = QRectF((source_width / 2.0) - (os/sx), (source_height / 2.0) - (os/sy), os/sx*2.0, os/sy*2.0)
234 
235  # Draw origin
236  painter.setBrush(QColor(83, 160, 237, 122))
237  painter.setPen(Qt.NoPen)
238  painter.drawEllipse(self.centerHandle)
239 
240  # Draw translucent rectangle
241  self.clipRect = QRectF(0, 0, final_size.width(), final_size.height())
242 
243  # Remove transform
244  painter.resetTransform()
245 
246  # End painter
247  painter.end()
248 
249  self.mutex.unlock()
250 
251  ##
252  # Set a new aspect ratio
253  def SetAspectRatio(self, new_aspect_ratio, new_pixel_ratio):
254  self.aspect_ratio = new_aspect_ratio
255  self.pixel_ratio = new_pixel_ratio
256 
257  ##
258  # Calculate size of viewport to maintain apsect ratio
259  def centeredViewport(self, width, height):
260 
261  aspectRatio = self.aspect_ratio.ToFloat() * self.pixel_ratio.ToFloat()
262  heightFromWidth = width / aspectRatio
263  widthFromHeight = height * aspectRatio
264 
265  if heightFromWidth <= height:
266  return QRect(0, (height - heightFromWidth) / 2, width, heightFromWidth)
267  else:
268  return QRect((width - widthFromHeight) / 2.0, 0, widthFromHeight, height)
269 
270  ##
271  # Present the current frame
272  def present(self, image, *args):
273 
274  # Get frame's QImage from libopenshot
275  self.current_image = image
276 
277  # Force repaint on this widget
278  self.repaint()
279 
280  ##
281  # Connect signals to renderer
282  def connectSignals(self, renderer):
283  renderer.present.connect(self.present)
284 
285  ##
286  # Capture mouse press event on video preview window
287  def mousePressEvent(self, event):
288  self.mouse_pressed = True
289  self.mouse_dragging = False
290  self.mouse_position = event.pos()
291  self.transform_mode = None
292 
293  # Ignore undo/redo history temporarily (to avoid a huge pile of undo/redo history)
294  get_app().updates.ignore_history = True
295 
296  ##
297  # Capture mouse release event on video preview window
298  def mouseReleaseEvent(self, event):
299  self.mouse_pressed = False
300  self.mouse_dragging = False
301  self.transform_mode = None
302 
303  # Inform UpdateManager to accept updates, and only store our final update
304  get_app().updates.ignore_history = False
305 
306  # Add final update to undo/redo history
307  if self.original_clip_data:
308  get_app().updates.apply_last_action_to_history(self.original_clip_data)
309 
310  # Clear original data
311  self.original_clip_data = None
312 
313  ##
314  # Capture mouse events on video preview window
315  def mouseMoveEvent(self, event):
316  self.mutex.lock()
317 
318  if self.mouse_pressed:
319  self.mouse_dragging = True
320 
321  if self.transforming_clip:
322  # Get framerate
323  fps = get_app().project.get(["fps"])
324  fps_float = float(fps["num"]) / float(fps["den"])
325 
326  # Get current clip's position
327  start_of_clip = float(self.transforming_clip.data["start"])
328  end_of_clip = float(self.transforming_clip.data["end"])
329  position_of_clip = float(self.transforming_clip.data["position"])
330  playhead_position = float(get_app().window.preview_thread.current_frame) / fps_float
331 
332  # Get the rect where the video is actually drawn (without the black borders, etc...)
333  viewport_rect = self.centeredViewport(self.width(), self.height())
334 
335  # Make back-up of clip data
336  if self.mouse_dragging and not self.transform_mode:
337  self.original_clip_data = self.transforming_clip.data
338 
339  # Determine if cursor is over a handle
340  if self.transform.mapRect(self.topRightHandle).contains(event.pos()):
341  self.setCursor(QCursor(Qt.SizeBDiagCursor))
342  # Set the transform mode
343  if self.mouse_dragging and not self.transform_mode:
344  self.transform_mode = 'scale_top_right'
345 
346  elif self.transform.mapRect(self.topHandle).contains(event.pos()):
347  self.setCursor(QCursor(Qt.SizeVerCursor))
348  # Set the transform mode
349  if self.mouse_dragging and not self.transform_mode:
350  self.transform_mode = 'scale_top'
351 
352  elif self.transform.mapRect(self.topLeftHandle).contains(event.pos()):
353  self.setCursor(QCursor(Qt.SizeFDiagCursor))
354  # Set the transform mode
355  if self.mouse_dragging and not self.transform_mode:
356  self.transform_mode = 'scale_top_left'
357 
358  elif self.transform.mapRect(self.leftHandle).contains(event.pos()):
359  self.setCursor(QCursor(Qt.SizeHorCursor))
360  # Set the transform mode
361  if self.mouse_dragging and not self.transform_mode:
362  self.transform_mode = 'scale_left'
363 
364  elif self.transform.mapRect(self.rightHandle).contains(event.pos()):
365  self.setCursor(QCursor(Qt.SizeHorCursor))
366  # Set the transform mode
367  if self.mouse_dragging and not self.transform_mode:
368  self.transform_mode = 'scale_right'
369 
370  elif self.transform.mapRect(self.bottomLeftHandle).contains(event.pos()):
371  self.setCursor(QCursor(Qt.SizeBDiagCursor))
372  # Set the transform mode
373  if self.mouse_dragging and not self.transform_mode:
374  self.transform_mode = 'scale_bottom_left'
375 
376  elif self.transform.mapRect(self.bottomHandle).contains(event.pos()):
377  self.setCursor(QCursor(Qt.SizeVerCursor))
378  # Set the transform mode
379  if self.mouse_dragging and not self.transform_mode:
380  self.transform_mode = 'scale_bottom'
381 
382  elif self.transform.mapRect(self.bottomRightHandle).contains(event.pos()):
383  self.setCursor(QCursor(Qt.SizeFDiagCursor))
384  # Set the transform mode
385  if self.mouse_dragging and not self.transform_mode:
386  self.transform_mode = 'scale_bottom_right'
387 
388  elif self.transform.mapRect(self.centerHandle).contains(event.pos()):
389  self.setCursor(QCursor(Qt.SizeAllCursor))
390  # Set the transform mode
391  if self.mouse_dragging and not self.transform_mode:
392  self.transform_mode = 'location'
393  # Determine x,y offsets for gravity
394  self.corner_offset_x = event.pos().x() - self.transform.mapRect(self.topLeftHandle).x()
395  self.corner_offset_y = event.pos().y() - self.transform.mapRect(self.topLeftHandle).y()
396 
397 
398  elif not self.transform_mode:
399  # Reset cursor when not over a handle
400  self.setCursor(QCursor(Qt.ArrowCursor))
401 
402  # Determine frame # of clip
403  start_of_clip_frame = round(float(self.transforming_clip.data["start"]) * fps_float) + 1
404  position_of_clip_frame = (float(self.transforming_clip.data["position"]) * fps_float) + 1
405  playhead_position_frame = float(get_app().window.preview_thread.current_frame)
406  clip_frame_number = round(playhead_position_frame - position_of_clip_frame) + start_of_clip_frame
407 
408  # Transform clip object
409  if self.transform_mode:
410  if self.transform_mode == 'location':
411  # Calculate new location coordinates
412  location_x = (event.pos().x() - self.gravity_point.x() - self.corner_offset_x) / viewport_rect.width()
413  location_y = (event.pos().y() - self.gravity_point.y() - self.corner_offset_y) / viewport_rect.height()
414 
415  # Save new location
416  self.updateProperty(self.transforming_clip.id, clip_frame_number, 'location_x', location_x)
417  self.updateProperty(self.transforming_clip.id, clip_frame_number, 'location_y', location_y)
418 
419  elif self.transform_mode.startswith('scale_'):
420  scale_x = None
421  scale_y = None
422 
423  # Calculate new location coordinates
424  center_x = self.transform.mapRect(self.centerHandle).x() + (self.transform.mapRect(self.centerHandle).width() / 2.0)
425  center_y = self.transform.mapRect(self.centerHandle).y() + (self.transform.mapRect(self.centerHandle).height() / 2.0)
426 
427  if self.transform_mode == 'scale_top_right':
428  scale_x = (event.pos().x() - center_x) / (viewport_rect.width() / 2.0)
429  scale_y = (center_y - event.pos().y()) / (viewport_rect.height() / 2.0)
430  elif self.transform_mode == 'scale_bottom_right':
431  scale_x = (event.pos().x() - center_x) / (viewport_rect.width() / 2.0)
432  scale_y = (event.pos().y() - center_y) / (viewport_rect.height() / 2.0)
433  elif self.transform_mode == 'scale_top_left':
434  scale_x = (center_x - event.pos().x()) / (viewport_rect.width() / 2.0)
435  scale_y = (center_y - event.pos().y()) / (viewport_rect.height() / 2.0)
436  elif self.transform_mode == 'scale_bottom_left':
437  scale_x = (center_x - event.pos().x()) / (viewport_rect.width() / 2.0)
438  scale_y = (event.pos().y() - center_y) / (viewport_rect.height() / 2.0)
439  elif self.transform_mode == 'scale_top':
440  scale_y = (center_y - event.pos().y()) / (viewport_rect.height() / 2.0)
441  elif self.transform_mode == 'scale_bottom':
442  scale_y = (event.pos().y() - center_y) / (viewport_rect.height() / 2.0)
443  elif self.transform_mode == 'scale_left':
444  scale_x = (center_x - event.pos().x()) / (viewport_rect.width() / 2.0)
445  elif self.transform_mode == 'scale_right':
446  scale_x = (event.pos().x() - center_x) / (viewport_rect.width() / 2.0)
447 
448  if int(QCoreApplication.instance().keyboardModifiers() & Qt.ControlModifier) > 0:
449  # If CTRL key is pressed, fix the scale_y to the correct aspect ration
450  if scale_x and scale_y:
451  scale_y = scale_x
452  elif scale_y:
453  scale_x = scale_y
454  elif scale_x:
455  scale_y = scale_x
456 
457  # Save new location
458  if scale_x != None:
459  self.updateProperty(self.transforming_clip.id, clip_frame_number, 'scale_x', scale_x)
460  if scale_y != None:
461  self.updateProperty(self.transforming_clip.id, clip_frame_number, 'scale_y', scale_y)
462 
463  # Force re-paint
464  self.update()
465 
466  # Update mouse position
467  self.mouse_position = event.pos()
468 
469  self.mutex.unlock()
470 
471  ##
472  # Update a keyframe property to a new value, adding or updating keyframes as needed
473  def updateProperty(self, id, frame_number, property_key, new_value):
474  found_point = False
475  clip_updated = False
476 
477  c = Clip.get(id=id)
478  if not c:
479  # No clip found
480  return
481 
482  for point in c.data[property_key]["Points"]:
483  log.info("looping points: co.X = %s" % point["co"]["X"])
484 
485  if point["co"]["X"] == frame_number:
486  found_point = True
487  clip_updated = True
488  point["interpolation"] = openshot.BEZIER
489  point["co"]["Y"] = float(new_value)
490 
491  if not found_point and new_value != None:
492  clip_updated = True
493  log.info("Created new point at X=%s" % frame_number)
494  c.data[property_key]["Points"].append({'co': {'X': frame_number, 'Y': new_value}, 'interpolation': openshot.BEZIER})
495 
496  # Reduce # of clip properties we are saving (performance boost)
497  c.data = {property_key: c.data.get(property_key)}
498 
499  # Save changes
500  if clip_updated:
501  # Save
502  c.save()
503 
504  # Update the preview
505  get_app().window.refreshFrameSignal.emit()
506 
507  ##
508  # Signal to refresh viewport (i.e. a property might have changed that effects the preview)
509  def refreshTriggered(self):
510 
511  # Update reference to clip
512  if self and self.transforming_clip:
513  self.transforming_clip = Clip.get(id=self.transforming_clip.id)
514 
515  ##
516  # Handle the transform signal when it's emitted
517  def transformTriggered(self, clip_id):
518  need_refresh = False
519  # Disable Transform UI
520  if self and self.transforming_clip:
521  # Is this the same clip_id already being transformed?
522  if not clip_id:
523  # Clear transform
524  self.transforming_clip = None
525  need_refresh = True
526 
527  # Get new clip for transform
528  if clip_id:
529  self.transforming_clip = Clip.get(id=clip_id)
530 
531  if self.transforming_clip:
533  clips = get_app().window.timeline_sync.timeline.Clips()
534  for clip in clips:
535  if clip.Id() == self.transforming_clip.id:
536  self.transforming_clip_object = clip
537  need_refresh = True
538  break
539 
540  # Update the preview and reselct current frame in properties
541  if need_refresh:
542  get_app().window.refreshFrameSignal.emit()
543  get_app().window.propertyTableView.select_frame(get_app().window.preview_thread.player.Position())
544 
545  ##
546  # Widget resize event
547  def resizeEvent(self, event):
548  self.delayed_size = self.centeredViewport(event.size().width(), event.size().height())
549  self.delayed_resize_timer.start()
550 
551  # Pause playback (to prevent crash since we are fixing to change the timeline's max size)
552  self.win.actionPlay_trigger(event, force="pause")
553 
554  ##
555  # Callback for resize event timer (to delay the resize event, and prevent lots of similar resize events)
557  # Stop timer
558  self.delayed_resize_timer.stop()
559 
560  # Emit signal that video widget changed size
561  self.win.MaxSizeChanged.emit(self.delayed_size)
562 
563  def __init__(self, *args):
564  # Invoke parent init
565  QWidget.__init__(self, *args)
566 
567  # Init aspect ratio settings (default values)
568  self.aspect_ratio = openshot.Fraction()
569  self.pixel_ratio = openshot.Fraction()
570  self.aspect_ratio.num = 16
571  self.aspect_ratio.den = 9
572  self.pixel_ratio.num = 1
573  self.pixel_ratio.den = 1
574  self.transforming_clip = None
575  self.transforming_clip_object = None
576  self.transform = None
577  self.topLeftHandle = None
578  self.topRightHandle = None
579  self.bottomLeftHandle = None
580  self.bottomRightHandle = None
581  self.topHandle = None
582  self.bottomHandle = None
583  self.leftHandle = None
584  self.rightHandle = None
585  self.centerHandle = None
586  self.mouse_pressed = False
587  self.mouse_dragging = False
588  self.mouse_position = None
589  self.transform_mode = None
590  self.corner_offset_x = None
591  self.corner_offset_y = None
592  self.clipRect = None
593  self.gravity_point = None
594  self.original_clip_data = None
595 
596  # Mutex lock
597  self.mutex = QMutex()
598 
599  # Init Qt style properties (black background, etc...)
600  p = QPalette()
601  p.setColor(QPalette.Window, QColor("#191919"))
602  super().setPalette(p)
603  super().setAttribute(Qt.WA_OpaquePaintEvent)
604  super().setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
605 
606  # Set mouse tracking
607  self.setMouseTracking(True)
608 
609  # Init current frame's QImage
610  self.current_image = None
611 
612  # Get a reference to the window object
613  self.win = get_app().window
614 
615  # Show Property timer
616  # Timer to use a delay before sending MaxSizeChanged signals (so we don't spam libopenshot)
617  self.delayed_size = None
618  self.delayed_resize_timer = QTimer()
619  self.delayed_resize_timer.setInterval(200)
620  self.delayed_resize_timer.timeout.connect(self.delayed_resize_callback)
621  self.delayed_resize_timer.stop()
622 
623  # Connect to signals
624  self.win.TransformSignal.connect(self.transformTriggered)
625  self.win.refreshFrameSignal.connect(self.refreshTriggered)
def paintEvent
Custom paint event.
Definition: video_widget.py:50
def resizeEvent
Widget resize event.
def get_app
Returns the current QApplication instance of OpenShot.
Definition: app.py:55
def refreshTriggered
Signal to refresh viewport (i.e.
def delayed_resize_callback
Callback for resize event timer (to delay the resize event, and prevent lots of similar resize events...
def transformTriggered
Handle the transform signal when it's emitted.
def mouseMoveEvent
Capture mouse events on video preview window.
def present
Present the current frame.
def connectSignals
Connect signals to renderer.
def updateProperty
Update a keyframe property to a new value, adding or updating keyframes as needed.
def SetAspectRatio
Set a new aspect ratio.
def centeredViewport
Calculate size of viewport to maintain apsect ratio.
def mousePressEvent
Capture mouse press event on video preview window.
A QWidget used on the video display widget.
Definition: video_widget.py:46
def mouseReleaseEvent
Capture mouse release event on video preview window.