From: "FeRD (Frank Dana)" <ferdnyc@gmail.com>
Date: Thu, 4 Nov 2021 21:01:39 -0400
Subject: Enforce integer function arguments

---
 src/windows/export.py                     |   8 +-
 src/windows/models/properties_model.py    |  34 +-
 src/windows/process_effect.py             |   2 +-
 src/windows/video_widget.py               | 596 ++++++++++++++++++++----------
 src/windows/views/effects_listview.py     |   7 +-
 src/windows/views/effects_treeview.py     |   7 +-
 src/windows/views/emojis_listview.py      |   7 +-
 src/windows/views/files_listview.py       |   7 +-
 src/windows/views/files_treeview.py       |   7 +-
 src/windows/views/properties_tableview.py |  38 +-
 src/windows/views/transitions_listview.py |   7 +-
 src/windows/views/transitions_treeview.py |   7 +-
 src/windows/views/tutorial.py             |  14 +-
 13 files changed, 476 insertions(+), 265 deletions(-)

diff --git a/src/windows/export.py b/src/windows/export.py
index 9cea65b..1b8236b 100644
--- a/src/windows/export.py
+++ b/src/windows/export.py
@@ -290,7 +290,7 @@ class Export(QDialog):
             percentage_string = format_of_progress_string % (( current_frame - start_frame ) / ( end_frame - start_frame ) * 100)
         else:
             percentage_string = "100%"
-        self.progressExportVideo.setValue(current_frame)
+        self.progressExportVideo.setValue(int(current_frame))
         self.progressExportVideo.setFormat(percentage_string)
         self.setWindowTitle("%s %s" % (percentage_string, title_message))
 
@@ -690,9 +690,9 @@ class Export(QDialog):
         fps_encode = 0
 
         # Init progress bar
-        self.progressExportVideo.setMinimum(self.txtStartFrame.value())
-        self.progressExportVideo.setMaximum(self.txtEndFrame.value())
-        self.progressExportVideo.setValue(self.txtStartFrame.value())
+        self.progressExportVideo.setMinimum(int(self.txtStartFrame.value()))
+        self.progressExportVideo.setMaximum(int(self.txtEndFrame.value()))
+        self.progressExportVideo.setValue(int(self.txtStartFrame.value()))
 
         # Prompt error message
         if self.txtStartFrame.value() == self.txtEndFrame.value():
diff --git a/src/windows/models/properties_model.py b/src/windows/models/properties_model.py
index 1c44587..de6ff75 100644
--- a/src/windows/models/properties_model.py
+++ b/src/windows/models/properties_model.py
@@ -412,8 +412,8 @@ class PropertiesModel(updates.UpdateInterface):
             new_value = None
 
         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))
+            "%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)
 
         # Find this clip
         c = None
@@ -514,35 +514,35 @@ class PropertiesModel(updates.UpdateInterface):
                     try:
                         clip_data[property_key] = int(new_value)
                     except Exception as ex:
-                        log.warn('Invalid Integer value passed to property: %s' % ex)
+                        log.warn('Invalid Integer value passed to property', exc_info=1)
 
                 elif property_type == "float":
                     clip_updated = True
                     try:
                         clip_data[property_key] = float(new_value)
                     except Exception as ex:
-                        log.warn('Invalid Float value passed to property: %s' % ex)
+                        log.warn('Invalid Float value passed to property', exc_info=1)
 
                 elif property_type == "bool":
                     clip_updated = True
                     try:
                         clip_data[property_key] = bool(new_value)
                     except Exception as ex:
-                        log.warn('Invalid Boolean value passed to property: %s' % ex)
+                        log.warn('Invalid Boolean value passed to property', exc_info=1)
 
                 elif property_type == "string":
                     clip_updated = True
                     try:
                         clip_data[property_key] = str(new_value)
-                    except Exception as ex:
-                        log.warn('Invalid String value passed to property: %s' % ex)
+                    except Exception:
+                        log.warn('Invalid String value passed to property', exc_info=1)
 
                 elif property_type in ["font", "caption"]:
                     clip_updated = True
                     try:
                         clip_data[property_key] = str(new_value)
-                    except Exception as ex:
-                        log.warn('Invalid Font/Caption value passed to property: %s' % ex)
+                    except Exception:
+                        log.warn('Invalid Font/Caption value passed to property', exc_info=1)
 
                 elif property_type == "reader":
                     # Transition
@@ -553,8 +553,8 @@ class PropertiesModel(updates.UpdateInterface):
                         clip_data[property_key] = json.loads(clip_object.Reader().Json())
                         clip_object.Close()
                         clip_object = None
-                    except Exception as ex:
-                        log.warn('Invalid Reader value passed to property: %s (%s)' % (value, ex))
+                    except Exception:
+                        log.warn('Invalid Reader value passed to property: %s (%s)', value, exc_info=1)
 
             # Reduce # of clip properties we are saving (performance boost)
             clip_data = {property_key: clip_data.get(property_key)}
@@ -684,9 +684,9 @@ class PropertiesModel(updates.UpdateInterface):
 
             if type == "color":
                 # Color needs to be handled special
-                red = property[1]["red"]["value"]
-                green = property[1]["green"]["value"]
-                blue = property[1]["blue"]["value"]
+                red = int(property[1]["red"]["value"])
+                green = int(property[1]["green"]["value"])
+                blue = int(property[1]["blue"]["value"])
                 col.setBackground(QColor(red, green, blue))
 
             if readonly or type in ["color", "font", "caption"] or choices or label == "Track":
@@ -785,9 +785,9 @@ class PropertiesModel(updates.UpdateInterface):
 
             if type == "color":
                 # Update the color based on the color curves
-                red = property[1]["red"]["value"]
-                green = property[1]["green"]["value"]
-                blue = property[1]["blue"]["value"]
+                red = int(property[1]["red"]["value"])
+                green = int(property[1]["green"]["value"])
+                blue = int(property[1]["blue"]["value"])
                 col.setBackground(QColor(red, green, blue))
 
             # Update helper dictionary
diff --git a/src/windows/process_effect.py b/src/windows/process_effect.py
index e4f3120..ea0c294 100644
--- a/src/windows/process_effect.py
+++ b/src/windows/process_effect.py
@@ -352,7 +352,7 @@ class ProcessEffect(QDialog):
         while(not processing.IsDone() ):
             # update progressbar
             progressionStatus = processing.GetProgress()
-            self.progressBar.setValue(progressionStatus)
+            self.progressBar.setValue(int(progressionStatus))
             time.sleep(0.01)
 
             # Process any queued events
diff --git a/src/windows/video_widget.py b/src/windows/video_widget.py
index 842deb3..f918460 100644
--- a/src/windows/video_widget.py
+++ b/src/windows/video_widget.py
@@ -25,8 +25,11 @@
  along with OpenShot Library.  If not, see <http://www.gnu.org/licenses/>.
  """
 
+import json
+
 from PyQt5.QtCore import (
-    Qt, QCoreApplication, QPointF, QPoint, QRect, QRectF, QSize, QMutex, QTimer
+    Qt, QCoreApplication, QMutex, QTimer,
+    QPoint, QPointF, QSize, QSizeF, QRect, QRectF,
 )
 from PyQt5.QtGui import (
     QTransform, QPainter, QPixmap, QColor, QPen, QBrush, QCursor, QImage, QRegion
@@ -41,8 +44,6 @@ from classes.logger import log
 from classes.app import get_app
 from classes.query import Clip, Effect
 
-import json
-
 
 class VideoWidget(QWidget, updates.UpdateInterface):
     """ A QWidget used on the video display widget """
@@ -92,37 +93,68 @@ class VideoWidget(QWidget, updates.UpdateInterface):
                     self.pixel_ratio.ToFloat())
 
 
-    def drawTransformHandler(self, painter, sx, sy, source_width, source_height, origin_x, origin_y,
-     x1=None, y1=None, x2=None, y2=None, rotation = None):
+    def drawTransformHandler(
+        self, painter, sx, sy, source_width, source_height,
+        origin_x, origin_y,
+        x1=None, y1=None, x2=None, y2=None, rotation = None
+    ):
         # Draw transform corners and center origin circle
         # Corner size
         cs = self.cs
         os = 12.0
 
+        csx = cs / sx
+        csy = cs / sy
+
         # Rotate the transform handler
         if rotation:
-            bbox_center_x = (((x1*source_width + x2*source_width) / 2.0) ) - ( (os/2) /sx)
-            bbox_center_y = (((y1*source_height + y2*source_height) / 2.0) ) - ( (os/2) /sy)
+            bbox_center_x = ((x1*source_width + x2*source_width) / 2.0) - ((os / 2) / sx)
+            bbox_center_y = ((y1*source_height + y2*source_height) / 2.0) - ((os / 2) / sy)
             painter.translate(bbox_center_x, bbox_center_y)
             painter.rotate(rotation)
             painter.translate(-bbox_center_x, -bbox_center_y)
 
-        if(x1 and y1 and x2 and y2):
+        if all([x1, y1, x2, y2]):
             # Calculate bounds of clip
-            self.clipBounds = QRectF(QPointF(x1*source_width, y1*source_height), QPointF(x2*source_width, y2*source_height))
+            self.clipBounds = QRectF(
+                QPointF(x1 * source_width, y1 * source_height),
+                QPointF(x2 * source_width, y2 * source_height)
+            )
             # Calculate 4 corners coordinates
-            self.topLeftHandle = QRectF(x1*source_width -(cs/sx/2.0), y1*source_height-(cs/sy/2.0), cs/sx, cs/sy)
-            self.topRightHandle = QRectF(x2*source_width-(cs/sx/2.0), y1*source_height-(cs/sy/2.0), cs/sx, cs/sy)
-            self.bottomLeftHandle = QRectF(x1*source_width -(cs/sx/2.0), y2*source_height-(cs/sy/2.0), cs/sx, cs/sy)
-            self.bottomRightHandle = QRectF(x2*source_width-(cs/sx/2.0), y2*source_height-(cs/sy/2.0), cs/sx, cs/sy)
+            self.topLeftHandle = QRectF(
+                x1 * source_width - (csx / 2.0),
+                y1 * source_height - (csy / 2.0),
+                csx,
+                csy)
+            self.topRightHandle = QRectF(
+                x2 * source_width - (csx / 2.0),
+                y1 * source_height - (csy / 2.0),
+                csx,
+                csy)
+            self.bottomLeftHandle = QRectF(
+                x1 * source_width - (csx / 2.0),
+                y2 * source_height - (csy / 2.0),
+                csx,
+                csy)
+            self.bottomRightHandle = QRectF(
+                x2 * source_width - (csx / 2.0),
+                y2 * source_height - (csy / 2.0),
+                csx,
+                csy)
         else:
             # Calculate bounds of clip
-            self.clipBounds = QRectF(QPointF(0.0, 0.0), QPointF(source_width, source_height))
+            self.clipBounds = QRectF(
+                QPointF(0.0, 0.0),
+                QPointF(source_width, source_height))
             # Calculate 4 corners coordinates
-            self.topLeftHandle = QRectF(-cs/sx/2.0, -cs/sy/2.0, cs/sx, cs/sy)
-            self.topRightHandle = QRectF(source_width - (cs/sx) + cs/sx/2.0, -cs/sy/2.0, cs/sx, cs/sy)
-            self.bottomLeftHandle = QRectF(-cs/sx/2.0, source_height - (cs/sy) + cs/sy/2.0, cs/sx, cs/sy)
-            self.bottomRightHandle = QRectF(source_width - (cs/sx) + cs/sx/2.0, source_height - (cs/sy) + cs/sy/2.0, cs/sx, cs/sy)
+            self.topLeftHandle = QRectF(
+                -csx / 2.0, -csy / 2.0, csx, csy)
+            self.topRightHandle = QRectF(
+                source_width - csx / 2.0, -csy / 2.0, csx, csy)
+            self.bottomLeftHandle = QRectF(
+                -csx / 2.0, source_height - csy / 2.0, csx, csy)
+            self.bottomRightHandle = QRectF(
+                source_width - csx / 2.0, source_height - csy / 2.0, csx, csy)
 
         # Draw 4 corners
         pen = QPen(QBrush(QColor("#53a0ed")), 1.5)
@@ -133,47 +165,110 @@ class VideoWidget(QWidget, updates.UpdateInterface):
         painter.drawRect(self.bottomLeftHandle)
         painter.drawRect(self.bottomRightHandle)
 
-        if(x1 and y1 and x2 and y2):
+        if all([x1, y1, x2, y2]):
             # Calculate 4 side coordinates
-            self.topHandle = QRectF(((x1*source_width+x2*source_width) / 2.0) - (cs/sx/2.0), (y1*source_height)-cs/sy/2.0, cs/sx, cs/sy)
-            self.bottomHandle = QRectF(((x1*source_width+x2*source_width) / 2.0) - (cs/sx/2.0), (y2*source_height)-( cs/sy/2.0), cs/sx, cs/sy)
-            self.leftHandle = QRectF((x1*source_width)-(cs/sx/2.0), ((y1*source_height+y2*source_height) / 2.0) - (cs/sy/2.0), cs/sx, cs/sy)
-            self.rightHandle = QRectF((x2*source_width) - (cs/sx) + cs/sx/2.0, ((y1*source_height+y2*source_height) / 2.0) - (cs/sy/2.0), cs/sx, cs/sy)
+            self.topHandle = QRectF(
+                ((x1 + x2) * source_width - csx) / 2.0,
+                (y1 * source_height) - csy / 2.0,
+                csx,
+                csy)
+            self.bottomHandle = QRectF(
+                ((x1 + x2) * source_width - csx) / 2.0,
+                (y2 * source_height) - csy / 2.0,
+                csx,
+                csy)
+            self.leftHandle = QRectF(
+                (x1 * source_width) - csx / 2.0,
+                ((y1 + y2) * source_height - csy) / 2.0,
+                csx,
+                csy)
+            self.rightHandle = QRectF(
+                (x2 * source_width) - csx / 2.0,
+                ((y1 + y2) * source_height - csy) / 2.0,
+                csx, csy)
 
         else:
             # Calculate 4 side coordinates
-            self.topHandle = QRectF((source_width / 2.0) - (cs/sx/2.0), -cs/sy/2.0, cs/sx, cs/sy)
-            self.bottomHandle = QRectF((source_width / 2.0) - (cs/sx/2.0), source_height - (cs/sy) + cs/sy/2.0, cs/sx, cs/sy)
-            self.leftHandle = QRectF(-cs/sx/2.0, (source_height / 2.0) - (cs/sy/2.0), cs/sx, cs/sy)
-            self.rightHandle = QRectF(source_width - (cs/sx) + cs/sx/2.0, (source_height / 2.0) - (cs/sy/2.0), cs/sx, cs/sy)
+            self.topHandle = QRectF(
+                (source_width - csx) / 2.0,
+                -csy / 2.0,
+                csx,
+                csy)
+            self.bottomHandle = QRectF(
+                (source_width - csx) / 2.0,
+                source_height - (csy / 2.0),
+                csx,
+                csy)
+            self.leftHandle = QRectF(
+                -csx / 2.0,
+                (source_height - csy) / 2.0,
+                csx,
+                csy)
+            self.rightHandle = QRectF(
+                source_width - (csx / 2.0),
+                (source_height - csy) / 2.0,
+                csx,
+                csy)
 
         # Calculate shear handles
-        self.topShearHandle = QRectF(self.topLeftHandle.x(), self.topLeftHandle.y(), self.clipBounds.width(), self.topLeftHandle.height())
-        self.leftShearHandle = QRectF(self.topLeftHandle.x(), self.topLeftHandle.y(), self.topLeftHandle.width(), self.clipBounds.height())
-        self.rightShearHandle = QRectF(self.topRightHandle.x(), self.topRightHandle.y(), self.topRightHandle.width(), self.clipBounds.height())
-        self.bottomShearHandle = QRectF(self.bottomLeftHandle.x(), self.bottomLeftHandle.y(), self.clipBounds.width(), self.topLeftHandle.height())
+        self.topShearHandle = QRectF(
+            self.topLeftHandle.x(),
+            self.topLeftHandle.y(),
+            self.clipBounds.width(),
+            self.topLeftHandle.height())
+        self.leftShearHandle = QRectF(
+            self.topLeftHandle.x(),
+            self.topLeftHandle.y(),
+            self.topLeftHandle.width(),
+            self.clipBounds.height())
+        self.rightShearHandle = QRectF(
+            self.topRightHandle.x(),
+            self.topRightHandle.y(),
+            self.topRightHandle.width(),
+            self.clipBounds.height())
+        self.bottomShearHandle = QRectF(
+            self.bottomLeftHandle.x(),
+            self.bottomLeftHandle.y(),
+            self.clipBounds.width(),
+            self.topLeftHandle.height())
 
         # Draw 4 sides (centered)
-        painter.drawRect(self.topHandle)
-        painter.drawRect(self.bottomHandle)
-        painter.drawRect(self.leftHandle)
-        painter.drawRect(self.rightHandle)
-        painter.drawRect(self.clipBounds)
+        painter.drawRects([
+            self.topHandle,
+            self.bottomHandle,
+            self.leftHandle,
+            self.rightHandle,
+            self.clipBounds,
+            ])
 
         # Calculate center coordinate
-        if(x1 and y1 and x2 and y2):
+        if all([x1, y1, x2, y2]):
             cs = 5.0
             os = 7.0
-            self.centerHandle = QRectF( (((x1*source_width+x2*source_width) / 2.0) ) - (os/sx), (((y1*source_height+y2*source_height) / 2.0) ) - (os/sy), os/sx*2.0, os/sy*2.0)
+            self.centerHandle = QRectF(
+                ((x1 + x2) * source_width / 2.0) - (os / sx),
+                ((y1 + y2) * source_height / 2.0) - (os / sy),
+                os / sx * 2.0,
+                os / sy * 2.0
+            )
         else:
-            self.centerHandle = QRectF((source_width * origin_x) - (os/sx), (source_height * origin_y) - (os/sy), os/sx*2.0, os/sy*2.0)
+            self.centerHandle = QRectF(
+                source_width * origin_x - (os / sx),
+                source_height * origin_y - (os / sy),
+                os / sx * 2.0,
+                os / sy * 2.0)
 
         # Draw origin
         painter.drawEllipse(self.centerHandle)
-        painter.drawLine(self.centerHandle.x() + (self.centerHandle.width()/2.0), self.centerHandle.y() + (self.centerHandle.height()/2.0) - self.centerHandle.height(),
-                            self.centerHandle.x() + (self.centerHandle.width()/2.0), self.centerHandle.y() + (self.centerHandle.height()/2.0) + self.centerHandle.height())
-        painter.drawLine(self.centerHandle.x() + (self.centerHandle.width()/2.0) - self.centerHandle.width(), self.centerHandle.y() + (self.centerHandle.height()/2.0),
-                            self.centerHandle.x() + (self.centerHandle.width()/2.0) + self.centerHandle.width(), self.centerHandle.y() + (self.centerHandle.height()/2.0))
+
+        # Draw cross at origin center, extending beyond ellipse by 25%
+        center = self.centerHandle.center()
+        halfW = QPointF(self.centerHandle.width() * 0.75, 0)
+        halfH = QPointF(0, self.centerHandle.height() * 0.75)
+        painter.drawLines(
+            center - halfW, center + halfW,
+            center - halfH, center + halfH,
+        )
 
         # Remove transform
         painter.resetTransform()
@@ -185,7 +280,11 @@ class VideoWidget(QWidget, updates.UpdateInterface):
 
         # Paint custom frame image on QWidget
         painter = QPainter(self)
-        painter.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.TextAntialiasing, True)
+        painter.setRenderHints(
+            QPainter.Antialiasing
+            | QPainter.SmoothPixmapTransform
+            | QPainter.TextAntialiasing,
+            True)
 
         # Fill the whole widget with the solid color
         painter.fillRect(event.rect(), QColor("#191919"))
@@ -197,7 +296,7 @@ class VideoWidget(QWidget, updates.UpdateInterface):
             # DRAW FRAME
             # Calculate new frame image size, maintaining aspect ratio
             pixSize = self.current_image.size()
-            pixSize.scale(event.rect().width(), event.rect().height(), Qt.KeepAspectRatio)
+            pixSize.scale(event.rect().size(), Qt.KeepAspectRatio)
             self.curr_frame_size = pixSize
 
             # Scale image
@@ -228,13 +327,19 @@ class VideoWidget(QWidget, updates.UpdateInterface):
             # Determine original size of clip's reader
             source_width = self.transforming_clip.data['reader']['width']
             source_height = self.transforming_clip.data['reader']['height']
-            source_size = QSize(source_width, source_height * self.pixel_ratio.Reciprocal().ToDouble())
+            pixel_adjust = self.pixel_ratio.Reciprocal().ToDouble()
+            source_size = QSize(
+                int(source_width),
+                int(source_height * pixel_adjust))
 
             # Determine scale of clip
             scale = self.transforming_clip.data['scale']
 
             # Set scale as STRETCH if the clip is attached to an object
-            if (raw_properties.get('parentObjectId').get('memo') != 'None' and len(raw_properties.get('parentObjectId').get('memo')) > 0 ):
+            if (
+                    raw_properties.get('parentObjectId').get('memo') != 'None'
+                    and len(raw_properties.get('parentObjectId').get('memo')) > 0
+            ):
                 scale = openshot.SCALE_STRETCH
 
             if scale == openshot.SCALE_FIT:
@@ -244,12 +349,7 @@ class VideoWidget(QWidget, updates.UpdateInterface):
                 source_size.scale(player_width, player_height, Qt.IgnoreAspectRatio)
 
             elif scale == openshot.SCALE_CROP:
-                width_size = QSize(player_width, round(player_width / (float(source_width) / float(source_height))))
-                height_size = QSize(round(player_height / (float(source_height) / float(source_width))), player_height)
-                if width_size.width() >= player_width and width_size.height() >= player_height:
-                    source_size.scale(width_size.width(), width_size.height(), Qt.KeepAspectRatio)
-                else:
-                    source_size.scale(height_size.width(), height_size.height(), Qt.KeepAspectRatio)
+                source_size.scale(player_width, player_height, Qt.KeepAspectRatioByExpanding)
 
             # Get new source width / height (after scaling mode applied)
             source_width = source_size.width()
@@ -290,9 +390,6 @@ class VideoWidget(QWidget, updates.UpdateInterface):
                 x += player_width - scaled_source_width  # right
                 y += (player_height - scaled_source_height)  # bottom
 
-            # Track gravity starting coordinate
-            self.gravity_point = QPointF(x, y)
-
             # Adjust x,y for location
             x_offset = raw_properties.get('location_x').get('value')
             y_offset = raw_properties.get('location_y').get('value')
@@ -334,7 +431,6 @@ class VideoWidget(QWidget, updates.UpdateInterface):
                     raw_properties_effect = json.loads(self.transforming_effect_object.PropertiesJSON(clip_frame_number))
                     # Get properties for the first object in dict. PropertiesJSON should return one object at the time
                     tmp = raw_properties_effect.get('objects')
-                    tmp2 = tmp.keys()
                     obj_id = list(tmp.keys())[0]
                     raw_properties_effect = raw_properties_effect.get('objects').get(obj_id)
 
@@ -347,10 +443,19 @@ class VideoWidget(QWidget, updates.UpdateInterface):
                             y1 = raw_properties_effect['y1']['value']
                             x2 = raw_properties_effect['x2']['value']
                             y2 = raw_properties_effect['y2']['value']
-                            self.drawTransformHandler(painter, sx, sy, source_width, source_height, origin_x, origin_y,
-                                x1, y1, x2, y2, rotation)
+                            self.drawTransformHandler(
+                                painter,
+                                sx, sy,
+                                source_width, source_height,
+                                origin_x, origin_y,
+                                x1, y1, x2, y2,
+                                rotation)
             else:
-                self.drawTransformHandler(painter, sx, sy, source_width, source_height, origin_x, origin_y)
+                self.drawTransformHandler(
+                    painter,
+                    sx, sy,
+                    source_width, source_height,
+                    origin_x, origin_y)
 
         if self.region_enabled:
             # Paint region selector onto video preview
@@ -381,11 +486,21 @@ class VideoWidget(QWidget, updates.UpdateInterface):
                 pen = QPen(QBrush(QColor("#53a0ed")), 1.5)
                 pen.setCosmetic(True)
                 painter.setPen(pen)
-                painter.drawRect(self.regionTopLeftHandle.x() - (cs/2.0/self.zoom), self.regionTopLeftHandle.y() - (cs/2.0/self.zoom), self.regionTopLeftHandle.width() / self.zoom, self.regionTopLeftHandle.height() / self.zoom)
-                painter.drawRect(self.regionBottomRightHandle.x() - (cs/2.0/self.zoom), self.regionBottomRightHandle.y() - (cs/2.0/self.zoom), self.regionBottomRightHandle.width() / self.zoom, self.regionBottomRightHandle.height() / self.zoom)
-                region_rect = QRectF(self.regionTopLeftHandle.x(), self.regionTopLeftHandle.y(),
-                                        self.regionBottomRightHandle.x() - self.regionTopLeftHandle.x(),
-                                        self.regionBottomRightHandle.y() - self.regionTopLeftHandle.y())
+                painter.drawRect(
+                    self.regionTopLeftHandle.x() - (cs / 2.0 / self.zoom),
+                    self.regionTopLeftHandle.y() - (cs / 2.0 / self.zoom),
+                    self.regionTopLeftHandle.width() / self.zoom,
+                    self.regionTopLeftHandle.height() / self.zoom)
+                painter.drawRect(
+                    self.regionBottomRightHandle.x() - (cs / 2.0 / self.zoom),
+                    self.regionBottomRightHandle.y() - (cs / 2.0 / self.zoom),
+                    self.regionBottomRightHandle.width() / self.zoom,
+                    self.regionBottomRightHandle.height() / self.zoom)
+                region_rect = QRectF(
+                    self.regionTopLeftHandle.x(),
+                    self.regionTopLeftHandle.y(),
+                    self.regionBottomRightHandle.x() - self.regionTopLeftHandle.x(),
+                    self.regionBottomRightHandle.y() - self.regionTopLeftHandle.y())
                 painter.drawRect(region_rect)
 
             # Remove transform
@@ -399,23 +514,15 @@ class VideoWidget(QWidget, updates.UpdateInterface):
     def centeredViewport(self, width, height):
         """ Calculate size of viewport to maintain aspect ratio """
 
-        # Calculate padding
-        top_padding = (height - (height * self.zoom)) / 2.0
-        left_padding = (width - (width * self.zoom)) / 2.0
+        window_size = QSizeF(width, height)
+        window_rect = QRectF(QPointF(0, 0), window_size)
 
-        # Adjust parameters to zoom
-        width = width * self.zoom
-        height = height * self.zoom
+        aspectRatio = self.aspect_ratio.ToFloat() * self.pixel_ratio.ToFloat()
+        viewport_size = QSizeF(aspectRatio, 1).scaled(window_size, Qt.KeepAspectRatio)
+        viewport_rect = QRectF(QPointF(0, 0), viewport_size)
+        viewport_rect.moveCenter(window_rect.center())
 
-        # Calculate which direction to scale (for perfect centering)
-        aspectRatio = self.aspect_ratio.ToFloat()
-        heightFromWidth = width / aspectRatio
-        widthFromHeight = height * aspectRatio
-
-        if heightFromWidth <= height:
-            return QRect(left_padding, ((height - heightFromWidth) / 2) + top_padding, width, heightFromWidth)
-        else:
-            return QRect(((width - widthFromHeight) / 2.0) + left_padding, top_padding, widthFromHeight, height)
+        return viewport_rect.toRect()
 
     def present(self, image, *args):
         """ Present the current frame """
@@ -453,12 +560,16 @@ class VideoWidget(QWidget, updates.UpdateInterface):
         # This can be used other widgets to display the selected region
         if self.region_enabled:
             # Get region coordinates
-            region_rect = QRectF(self.regionTopLeftHandle.x(), self.regionTopLeftHandle.y(),
-                                 self.regionBottomRightHandle.x() - self.regionTopLeftHandle.x(),
-                                 self.regionBottomRightHandle.y() - self.regionTopLeftHandle.y()).normalized()
+            region_rect = QRectF(
+                self.regionTopLeftHandle.x(),
+                self.regionTopLeftHandle.y(),
+                self.regionBottomRightHandle.x() - self.regionTopLeftHandle.x(),
+                self.regionBottomRightHandle.y() - self.regionTopLeftHandle.y()
+            ).normalized()
 
             # Map region (due to zooming)
-            mapped_region_rect = self.region_transform.mapToPolygon(region_rect.toRect()).boundingRect()
+            mapped_region_rect = self.region_transform.mapToPolygon(
+                region_rect.toRect()).boundingRect()
 
             # Render a scaled version of the region (as a QImage)
             # TODO: Grab higher quality pixmap from the QWidget, as this method seems to be 1/2 resolution
@@ -466,14 +577,25 @@ class VideoWidget(QWidget, updates.UpdateInterface):
             scale = 3.0
 
             # Map rect to transform (for scaling video elements)
-            mapped_region_rect = QRect(mapped_region_rect.x(), mapped_region_rect.y(), mapped_region_rect.width() * scale, mapped_region_rect.height() * scale)
+            mapped_region_rect = QRect(
+                mapped_region_rect.x(),
+                mapped_region_rect.y(),
+                int(mapped_region_rect.width() * scale),
+                int(mapped_region_rect.height() * scale))
 
             # Render QWidget onto scaled QImage
-            self.region_qimage = QImage(mapped_region_rect.width(), mapped_region_rect.height(), QImage.Format_RGBA8888)
+            self.region_qimage = QImage(
+                mapped_region_rect.size(), QImage.Format_RGBA8888)
             region_painter = QPainter(self.region_qimage)
-            region_painter.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.TextAntialiasing, True)
+            region_painter.setRenderHints(
+                QPainter.Antialiasing
+                | QPainter.SmoothPixmapTransform
+                | QPainter.TextAntialiasing,
+                True)
             region_painter.scale(scale, scale)
-            self.render(region_painter, QPoint(0,0), QRegion(mapped_region_rect, QRegion.Rectangle))
+            self.render(
+                region_painter, QPoint(0, 0),
+                QRegion(mapped_region_rect, QRegion.Rectangle))
             region_painter.end()
 
         # Inform UpdateManager to accept updates, and only store our final update
@@ -489,7 +611,8 @@ class VideoWidget(QWidget, updates.UpdateInterface):
     def rotateCursor(self, pixmap, rotation, shear_x, shear_y):
         """Rotate cursor based on the current transform"""
         rotated_pixmap = pixmap.transformed(
-            QTransform().rotate(rotation).shear(shear_x, shear_y).scale(0.8, 0.8), Qt.SmoothTransformation)
+            QTransform().rotate(rotation).shear(shear_x, shear_y).scale(0.8, 0.8),
+            Qt.SmoothTransformation)
         return QCursor(rotated_pixmap)
 
     def getTransformMode(self, rotation, shear_x, shear_y, event):
@@ -632,6 +755,10 @@ class VideoWidget(QWidget, updates.UpdateInterface):
 
             # Transform clip object
             if self.transform_mode:
+
+                x_motion = event.pos().x() - self.mouse_position.x()
+                y_motion = event.pos().y() - self.mouse_position.y()
+
                 if self.transform_mode == 'origin':
                     # Get current keyframe value
                     origin_x = raw_properties.get('origin_x').get('value')
@@ -640,8 +767,8 @@ class VideoWidget(QWidget, updates.UpdateInterface):
                     scale_y = raw_properties.get('scale_y').get('value')
 
                     # Calculate new location coordinates
-                    origin_x += (event.pos().x() - self.mouse_position.x()) / (self.clipBounds.width() * scale_x)
-                    origin_y += (event.pos().y() - self.mouse_position.y()) / (self.clipBounds.height() * scale_y)
+                    origin_x += x_motion / (self.clipBounds.width() * scale_x)
+                    origin_y += y_motion / (self.clipBounds.height() * scale_y)
 
                     # Constrain to clip
                     if origin_x < 0.0:
@@ -653,8 +780,13 @@ class VideoWidget(QWidget, updates.UpdateInterface):
                     if origin_y > 1.0:
                         origin_y = 1.0
                     # Update keyframe value (or create new one)
-                    self.updateClipProperty(self.transforming_clip.id, clip_frame_number, 'origin_x', origin_x, refresh=False)
-                    self.updateClipProperty(self.transforming_clip.id, clip_frame_number, 'origin_y', origin_y)
+                    self.updateClipProperty(
+                        self.transforming_clip.id, clip_frame_number,
+                        'origin_x', origin_x,
+                        refresh=False)
+                    self.updateClipProperty(
+                        self.transforming_clip.id, clip_frame_number,
+                        'origin_y', origin_y)
 
                 elif self.transform_mode == 'location':
                     # Get current keyframe value
@@ -662,12 +794,17 @@ class VideoWidget(QWidget, updates.UpdateInterface):
                     location_y = raw_properties.get('location_y').get('value')
 
                     # Calculate new location coordinates
-                    location_x += (event.pos().x() - self.mouse_position.x()) / viewport_rect.width()
-                    location_y += (event.pos().y() - self.mouse_position.y()) / viewport_rect.height()
+                    location_x += x_motion / viewport_rect.width()
+                    location_y += y_motion / viewport_rect.height()
 
                     # Update keyframe value (or create new one)
-                    self.updateClipProperty(self.transforming_clip.id, clip_frame_number, 'location_x', location_x, refresh=False)
-                    self.updateClipProperty(self.transforming_clip.id, clip_frame_number, 'location_y', location_y)
+                    self.updateClipProperty(
+                        self.transforming_clip.id, clip_frame_number,
+                        'location_x', location_x,
+                        refresh=False)
+                    self.updateClipProperty(
+                        self.transforming_clip.id, clip_frame_number,
+                        'location_y', location_y)
 
                 elif self.transform_mode == 'shear_top':
                     # Get current keyframe shear value
@@ -677,11 +814,13 @@ class VideoWidget(QWidget, updates.UpdateInterface):
                     # Calculate new location coordinates
                     aspect_ratio = (self.clipBounds.width() / self.clipBounds.height()) * 2.0
                     shear_x -= (
-                        event.pos().x() - self.mouse_position.x()) / (
+                        x_motion) / (
                         (self.clipBounds.width() * scale_x) / aspect_ratio)
 
                     # Update keyframe value (or create new one)
-                    self.updateClipProperty(self.transforming_clip.id, clip_frame_number, 'shear_x', shear_x)
+                    self.updateClipProperty(
+                        self.transforming_clip.id, clip_frame_number,
+                        'shear_x', shear_x)
 
                 elif self.transform_mode == 'shear_bottom':
                     # Get current keyframe shear value
@@ -691,11 +830,13 @@ class VideoWidget(QWidget, updates.UpdateInterface):
                     # Calculate new location coordinates
                     aspect_ratio = (self.clipBounds.width() / self.clipBounds.height()) * 2.0
                     shear_x += (
-                        event.pos().x() - self.mouse_position.x()) / (
+                        x_motion) / (
                         (self.clipBounds.width() * scale_x) / aspect_ratio)
 
                     # Update keyframe value (or create new one)
-                    self.updateClipProperty(self.transforming_clip.id, clip_frame_number, 'shear_x', shear_x)
+                    self.updateClipProperty(
+                        self.transforming_clip.id, clip_frame_number,
+                        'shear_x', shear_x)
 
                 elif self.transform_mode == 'shear_left':
                     # Get current keyframe shear value
@@ -706,11 +847,13 @@ class VideoWidget(QWidget, updates.UpdateInterface):
                     aspect_ratio = (
                         self.clipBounds.height() / self.clipBounds.width()) * 2.0
                     shear_y -= (
-                        event.pos().y() - self.mouse_position.y()) / (
+                        y_motion) / (
                         self.clipBounds.height() * scale_y / aspect_ratio)
 
                     # Update keyframe value (or create new one)
-                    self.updateClipProperty(self.transforming_clip.id, clip_frame_number, 'shear_y', shear_y)
+                    self.updateClipProperty(
+                        self.transforming_clip.id, clip_frame_number,
+                        'shear_y', shear_y)
 
                 elif self.transform_mode == 'shear_right':
                     # Get current keyframe shear value
@@ -718,13 +861,16 @@ class VideoWidget(QWidget, updates.UpdateInterface):
                     shear_y = raw_properties.get('shear_y').get('value')
 
                     # Calculate new location coordinates
-                    aspect_ratio = (self.clipBounds.height() / self.clipBounds.width()) * 2.0
+                    aspect_ratio = (
+                        self.clipBounds.height() / self.clipBounds.width()) * 2.0
                     shear_y += (
-                        event.pos().y() - self.mouse_position.y()) / (
+                        y_motion) / (
                         self.clipBounds.height() * scale_y / aspect_ratio)
 
                     # Update keyframe value (or create new one)
-                    self.updateClipProperty(self.transforming_clip.id, clip_frame_number, 'shear_y', shear_y)
+                    self.updateClipProperty(
+                        self.transforming_clip.id, clip_frame_number,
+                        'shear_y', shear_y)
 
                 elif self.transform_mode == 'rotation':
                     # Get current rotation keyframe value
@@ -733,71 +879,68 @@ class VideoWidget(QWidget, updates.UpdateInterface):
                     scale_y = max(float(raw_properties.get('scale_y').get('value')), 0.001)
 
                     # Calculate new location coordinates
-                    is_on_left = event.pos().x() < self.originHandle.x()
+                    is_on_right = event.pos().x() > self.originHandle.x()
                     is_on_top = event.pos().y() < self.originHandle.y()
 
-                    if is_on_top:
-                        rotation += (
-                            event.pos().x() - self.mouse_position.x()) / (
-                            (self.clipBounds.width() * scale_x) / 90)
-                    else:
-                        rotation -= (
-                            event.pos().x() - self.mouse_position.x()) / (
-                            (self.clipBounds.width() * scale_x) / 90)
-
-                    if is_on_left:
-                        rotation -= (
-                            event.pos().y() - self.mouse_position.y()) / (
-                            (self.clipBounds.height() * scale_y) / 90)
-                    else:
-                        rotation += (
-                            event.pos().y() - self.mouse_position.y()) / (
-                            (self.clipBounds.height() * scale_y) / 90)
+                    x_adjust = x_motion / ((self.clipBounds.width() * scale_x) / 90)
+                    rotation += (x_adjust if is_on_top else -x_adjust)
+
+                    y_adjust = y_motion / ((self.clipBounds.height() * scale_y) / 90)
+                    rotation += (y_adjust if is_on_right else -y_adjust)
 
                     # Update keyframe value (or create new one)
-                    self.updateClipProperty(self.transforming_clip.id, clip_frame_number, 'rotation', rotation)
+                    self.updateClipProperty(
+                        self.transforming_clip.id,
+                        clip_frame_number,
+                        'rotation', rotation)
 
                 elif self.transform_mode.startswith('scale_'):
                     # Get current scale keyframe value
                     scale_x = max(float(raw_properties.get('scale_x').get('value')), 0.001)
                     scale_y = max(float(raw_properties.get('scale_y').get('value')), 0.001)
 
+                    half_w = self.clipBounds.width() / 2.0
+                    half_h = self.clipBounds.height() / 2.0
+
                     if self.transform_mode == 'scale_top_right':
-                        scale_x += (event.pos().x() - self.mouse_position.x()) / (self.clipBounds.width() / 2.0)
-                        scale_y -= (event.pos().y() - self.mouse_position.y()) / (self.clipBounds.height() / 2.0)
+                        scale_x += x_motion / half_w
+                        scale_y -= y_motion / half_h
                     elif self.transform_mode == 'scale_bottom_right':
-                        scale_x += (event.pos().x() - self.mouse_position.x()) / (self.clipBounds.width() / 2.0)
-                        scale_y += (event.pos().y() - self.mouse_position.y()) / (self.clipBounds.height() / 2.0)
+                        scale_x += x_motion / half_w
+                        scale_y += y_motion / half_h
                     elif self.transform_mode == 'scale_top_left':
-                        scale_x -= (event.pos().x() - self.mouse_position.x()) / (self.clipBounds.width() / 2.0)
-                        scale_y -= (event.pos().y() - self.mouse_position.y()) / (self.clipBounds.height() / 2.0)
+                        scale_x -= x_motion / half_w
+                        scale_y -= y_motion / half_h
                     elif self.transform_mode == 'scale_bottom_left':
-                        scale_x -= (event.pos().x() - self.mouse_position.x()) / (self.clipBounds.width() / 2.0)
-                        scale_y += (event.pos().y() - self.mouse_position.y()) / (self.clipBounds.height() / 2.0)
+                        scale_x -= x_motion / half_w
+                        scale_y += y_motion / half_h
                     elif self.transform_mode == 'scale_top':
-                        scale_y -= (event.pos().y() - self.mouse_position.y()) / (self.clipBounds.height() / 2.0)
+                        scale_y -= y_motion / half_h
                     elif self.transform_mode == 'scale_bottom':
-                        scale_y += (event.pos().y() - self.mouse_position.y()) / (self.clipBounds.height() / 2.0)
+                        scale_y += y_motion / half_h
                     elif self.transform_mode == 'scale_left':
-                        scale_x -= (event.pos().x() - self.mouse_position.x()) / (self.clipBounds.width() / 2.0)
+                        scale_x -= x_motion / half_w
                     elif self.transform_mode == 'scale_right':
-                        scale_x += (event.pos().x() - self.mouse_position.x()) / (self.clipBounds.width() / 2.0)
+                        scale_x += x_motion / half_w
 
                     if int(QCoreApplication.instance().keyboardModifiers() & Qt.ControlModifier) > 0:
                         # If CTRL key is pressed, fix the scale_y to the correct aspect ration
-                        if scale_x and scale_y:
+                        if scale_x:
                             scale_y = scale_x
                         elif scale_y:
                             scale_x = scale_y
-                        elif scale_x:
-                            scale_y = scale_x
 
                     # Update keyframe value (or create new one)
                     both_scaled = scale_x != 0.001 and scale_y != 0.001
                     if scale_x != 0.001:
-                        self.updateClipProperty(self.transforming_clip.id, clip_frame_number, 'scale_x', scale_x, refresh=(not both_scaled))
+                        self.updateClipProperty(
+                            self.transforming_clip.id, clip_frame_number,
+                            'scale_x', scale_x,
+                            refresh=(not both_scaled))
                     if scale_y != 0.001:
-                        self.updateClipProperty(self.transforming_clip.id, clip_frame_number, 'scale_y', scale_y)
+                        self.updateClipProperty(
+                            self.transforming_clip.id, clip_frame_number,
+                            'scale_y', scale_y)
 
             # Force re-paint
             self.update()
@@ -808,16 +951,29 @@ class VideoWidget(QWidget, updates.UpdateInterface):
             cs = self.cs
 
             # Adjust existing region coordinates (if any)
-            if not self.mouse_dragging and self.resize_button.isVisible() and self.resize_button.rect().contains(event.pos()):
+            if (not self.mouse_dragging
+                and self.resize_button.isVisible()
+                and self.resize_button.rect().contains(event.pos())
+            ):
                 # Mouse over resize button (and not currently dragging)
                 self.setCursor(Qt.ArrowCursor)
-            elif self.region_transform and self.regionTopLeftHandle and self.region_transform.mapToPolygon(self.regionTopLeftHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+            elif (
+                    self.region_transform
+                    and self.regionTopLeftHandle
+                    and self.region_transform.mapToPolygon(
+                        self.regionTopLeftHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill)
+                    ):
                 if not self.region_mode or self.region_mode == 'scale_top_left':
                     self.setCursor(self.rotateCursor(self.cursors.get('resize_fdiag'), 0, 0, 0))
                 # Set the region mode
                 if self.mouse_dragging and not self.region_mode:
                     self.region_mode = 'scale_top_left'
-            elif self.region_transform and self.regionBottomRightHandle and self.region_transform.mapToPolygon(self.regionBottomRightHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+            elif (
+                    self.region_transform
+                    and self.regionBottomRightHandle
+                    and self.region_transform.mapToPolygon(
+                        self.regionBottomRightHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill)
+                    ):
                 if not self.region_mode or self.region_mode == 'scale_bottom_right':
                     self.setCursor(self.rotateCursor(self.cursors.get('resize_fdiag'), 0, 0, 0))
                 # Set the region mode
@@ -829,13 +985,25 @@ class VideoWidget(QWidget, updates.UpdateInterface):
             # Initialize new region coordinates at current event.pos()
             if self.mouse_dragging and not self.region_mode:
                 self.region_mode = 'scale_bottom_right'
-                self.regionTopLeftHandle = QRectF(self.region_transform_inverted.map(event.pos()).x(), self.region_transform_inverted.map(event.pos()).y(), cs, cs)
-                self.regionBottomRightHandle = QRectF(self.region_transform_inverted.map(event.pos()).x(), self.region_transform_inverted.map(event.pos()).y(), cs, cs)
+                self.regionTopLeftHandle = QRectF(
+                    self.region_transform_inverted.map(event.pos()).x(),
+                    self.region_transform_inverted.map(event.pos()).y(),
+                    cs, cs)
+                self.regionBottomRightHandle = QRectF(
+                    self.region_transform_inverted.map(event.pos()).x(),
+                    self.region_transform_inverted.map(event.pos()).y(),
+                    cs, cs)
 
             # Move existing region coordinates
             if self.mouse_dragging:
-                diff_x = self.region_transform_inverted.map(event.pos()).x() - self.region_transform_inverted.map(self.mouse_position).x()
-                diff_y = self.region_transform_inverted.map(event.pos()).y() - self.region_transform_inverted.map(self.mouse_position).y()
+                diff_x = int(
+                    self.region_transform_inverted.map(event.pos()).x()
+                    - self.region_transform_inverted.map(self.mouse_position).x()
+                )
+                diff_y = int(
+                    self.region_transform_inverted.map(event.pos()).y()
+                    - self.region_transform_inverted.map(self.mouse_position).y()
+                )
                 if self.region_mode == 'scale_top_left':
                     self.regionTopLeftHandle.adjust(diff_x, diff_y, diff_x, diff_y)
                 elif self.region_mode == 'scale_bottom_right':
@@ -864,12 +1032,11 @@ class VideoWidget(QWidget, updates.UpdateInterface):
             if self.mouse_dragging and not self.transform_mode:
                 self.original_clip_data = self.transforming_clip.data
 
-
-
             if self.transforming_effect_object.info.has_tracked_object:
                 # Get properties of effect at current frame
                 raw_properties = json.loads(self.transforming_effect_object.PropertiesJSON(clip_frame_number))
-                 # Get properties for the first object in dict. PropertiesJSON should return one object at the time
+                # Get properties for the first object in dict.
+                # PropertiesJSON should return one object at the time
                 obj_id = list(raw_properties.get('objects').keys())[0]
                 raw_properties = raw_properties.get('objects').get(obj_id)
 
@@ -883,18 +1050,28 @@ class VideoWidget(QWidget, updates.UpdateInterface):
                 # Transform effect object
                 if self.transform_mode:
 
+                    x_motion = event.pos().x() - self.mouse_position.x()
+                    y_motion = event.pos().y() - self.mouse_position.y()
+
                     if self.transform_mode == 'location':
                         # Get current keyframe value
                         location_x = raw_properties.get('delta_x').get('value')
                         location_y = raw_properties.get('delta_y').get('value')
 
                         # Calculate new location coordinates
-                        location_x += (event.pos().x() - self.mouse_position.x()) / viewport_rect.width()
-                        location_y += (event.pos().y() - self.mouse_position.y()) / viewport_rect.height()
+                        location_x += x_motion / viewport_rect.width()
+                        location_y += y_motion / viewport_rect.height()
 
                         # Update keyframe value (or create new one)
-                        self.updateEffectProperty(self.transforming_effect.id, clip_frame_number, obj_id, 'delta_x', location_x, refresh=False)
-                        self.updateEffectProperty(self.transforming_effect.id, clip_frame_number, obj_id, 'delta_y', location_y)
+                        self.updateEffectProperty(
+                            self.transforming_effect.id, clip_frame_number,
+                            obj_id,
+                            'delta_x', location_x,
+                            refresh=False)
+                        self.updateEffectProperty(
+                            self.transforming_effect.id, clip_frame_number,
+                            obj_id,
+                            'delta_y', location_y)
 
                     elif self.transform_mode == 'rotation':
                         # Get current rotation keyframe value
@@ -903,63 +1080,70 @@ class VideoWidget(QWidget, updates.UpdateInterface):
                         scale_y = max(float(raw_properties.get('scale_y').get('value')), 0.001)
 
                         # Calculate new location coordinates
-                        is_on_left = event.pos().x() < self.originHandle.x()
+                        is_on_right = event.pos().x() > self.originHandle.x()
                         is_on_top = event.pos().y() < self.originHandle.y()
 
-                        if is_on_top:
-                            rotation += (event.pos().x() - self.mouse_position.x()) / ((self.clipBounds.width() * scale_x) / 90)
-                        else:
-                            rotation -= (event.pos().x() - self.mouse_position.x()) / ((self.clipBounds.width() * scale_x) / 90)
+                        x_adjust = x_motion / (self.clipBounds.width() * scale_x / 90)
+                        rotation += (x_adjust if is_on_top else -x_adjust)
 
-                        if is_on_left:
-                            rotation -= (event.pos().y() - self.mouse_position.y()) / ((self.clipBounds.height() * scale_y) / 90)
-                        else:
-                            rotation += (event.pos().y() - self.mouse_position.y()) / ((self.clipBounds.height() * scale_y) / 90)
+                        y_adjust = y_motion / (self.clipBounds.height() * scale_y / 90)
+                        rotation += (y_adjust if is_on_right else -y_adjust)
 
                         # Update keyframe value (or create new one)
-                        self.updateEffectProperty(self.transforming_effect.id, clip_frame_number, obj_id, 'rotation', rotation)
+                        self.updateEffectProperty(
+                            self.transforming_effect.id,
+                            clip_frame_number, obj_id,
+                            'rotation', rotation)
 
                     elif self.transform_mode.startswith('scale_'):
                         # Get current scale keyframe value
                         scale_x = max(float(raw_properties.get('scale_x').get('value')), 0.001)
                         scale_y = max(float(raw_properties.get('scale_y').get('value')), 0.001)
 
+                        half_w = self.clipBounds.width() / 2.0
+                        half_h = self.clipBounds.height() / 2.0
+
                         if self.transform_mode == 'scale_top_right':
-                            scale_x += (event.pos().x() - self.mouse_position.x()) / (self.clipBounds.width() / 2.0)
-                            scale_y -= (event.pos().y() - self.mouse_position.y()) / (self.clipBounds.height() / 2.0)
+                            scale_x += x_motion / half_w
+                            scale_y -= y_motion / half_h
                         elif self.transform_mode == 'scale_bottom_right':
-                            scale_x += (event.pos().x() - self.mouse_position.x()) / (self.clipBounds.width() / 2.0)
-                            scale_y += (event.pos().y() - self.mouse_position.y()) / (self.clipBounds.height() / 2.0)
+                            scale_x += x_motion / half_w
+                            scale_y += y_motion / half_h
                         elif self.transform_mode == 'scale_top_left':
-                            scale_x -= (event.pos().x() - self.mouse_position.x()) / (self.clipBounds.width() / 2.0)
-                            scale_y -= (event.pos().y() - self.mouse_position.y()) / (self.clipBounds.height() / 2.0)
+                            scale_x -= x_motion / half_w
+                            scale_y -= y_motion / half_h
                         elif self.transform_mode == 'scale_bottom_left':
-                            scale_x -= (event.pos().x() - self.mouse_position.x()) / (self.clipBounds.width() / 2.0)
-                            scale_y += (event.pos().y() - self.mouse_position.y()) / (self.clipBounds.height() / 2.0)
+                            scale_x -= x_motion / half_w
+                            scale_y += y_motion / half_h
                         elif self.transform_mode == 'scale_top':
-                            scale_y -= (event.pos().y() - self.mouse_position.y()) / (self.clipBounds.height() / 2.0)
+                            scale_y -= y_motion / half_h
                         elif self.transform_mode == 'scale_bottom':
-                            scale_y += (event.pos().y() - self.mouse_position.y()) / (self.clipBounds.height() / 2.0)
+                            scale_y += y_motion / half_h
                         elif self.transform_mode == 'scale_left':
-                            scale_x -= (event.pos().x() - self.mouse_position.x()) / (self.clipBounds.width() / 2.0)
+                            scale_x -= x_motion / half_w
                         elif self.transform_mode == 'scale_right':
-                            scale_x += (event.pos().x() - self.mouse_position.x()) / (self.clipBounds.width() / 2.0)
+                            scale_x += x_motion / half_w
 
                         if int(QCoreApplication.instance().keyboardModifiers() & Qt.ControlModifier) > 0:
-                            # If CTRL key is pressed, fix the scale_y to the correct aspect ration
-                            if scale_x and scale_y:
+                            # If CTRL key is pressed, fix the scale_y to the correct aspect ratio
+                            if scale_x:
                                 scale_y = scale_x
                             elif scale_y:
                                 scale_x = scale_y
-                            elif scale_x:
-                                scale_y = scale_x
 
                         # Update keyframe value (or create new one)
                         both_scaled = scale_x != 0.001 and scale_y != 0.001
                         if scale_x != 0.001:
-                            self.updateEffectProperty(self.transforming_effect.id, clip_frame_number, obj_id, 'scale_x', scale_x, refresh=(not both_scaled))
+                            self.updateEffectProperty(
+                                self.transforming_effect.id,
+                                clip_frame_number, obj_id,
+                                'scale_x', scale_x,
+                                refresh=(not both_scaled))
                         if scale_y != 0.001:
-                            self.updateEffectProperty(self.transforming_effect.id, clip_frame_number, obj_id, 'scale_y', scale_y)
+                            self.updateEffectProperty(
+                                self.transforming_effect.id,
+                                clip_frame_number, obj_id,
+                                'scale_y', scale_y)
 
             # Force re-paint
             self.update()
@@ -1017,8 +1201,15 @@ class VideoWidget(QWidget, updates.UpdateInterface):
             # No clip found
             return
 
-        for point in c.data['objects'][obj_id][property_key]["Points"]:
-            log.info("looping points: co.X = %s" % point["co"]["X"])
+        try:
+            props = c.data['objects'][obj_id]
+            points_list = props[property_key]["Points"]
+        except (TypeError, KeyError):
+            log.error("Corrupted project data!", exc_info=1)
+            return
+
+        for point in points_list:
+            log.info("looping points: co.X = %s", point["co"]["X"])
 
             if point["co"]["X"] == frame_number:
                 found_point = True
@@ -1028,12 +1219,15 @@ class VideoWidget(QWidget, updates.UpdateInterface):
 
         if not found_point and new_value != None:
             effect_updated = True
-            log.info("Created new point at X=%s" % frame_number)
-            c.data['objects'][obj_id][property_key]["Points"].append({'co': {'X': frame_number, 'Y': new_value}, 'interpolation': openshot.BEZIER})
+            log.info("Created new point at X=%s", frame_number)
+            points_list.append({
+                'co': { 'X': frame_number, 'Y': new_value },
+                'interpolation': openshot.BEZIER,
+                })
 
         # Reduce # of clip properties we are saving (performance boost)
         #TODO: This is too slow when draging transform handlers
-        c.data = {'objects': {obj_id: c.data.get('objects').get(obj_id)}}
+        c.data = {'objects': {obj_id: c.data.get('objects', {}).get(obj_id)}}
 
         if effect_updated:
             c.save()
@@ -1045,10 +1239,10 @@ class VideoWidget(QWidget, updates.UpdateInterface):
         """Signal to refresh viewport (i.e. a property might have changed that effects the preview)"""
 
         # Update reference to clip
-        if self and self.transforming_clip:
+        if self.transforming_clip:
             self.transforming_clip = Clip.get(id=self.transforming_clip.id)
 
-        if self and self.transforming_effect:
+        if self.transforming_effect:
             self.transforming_effect = Effect.get(id=self.transforming_effect.id)
 
     def transformTriggered(self, clip_id):
@@ -1058,7 +1252,7 @@ class VideoWidget(QWidget, updates.UpdateInterface):
 
         # Disable Transform UI
         # Is this the same clip_id already being transformed?
-        if self and self.transforming_clip and not clip_id:
+        if self.transforming_clip and not clip_id:
             # Clear transform
             self.transforming_clip = None
             need_refresh = True
@@ -1083,7 +1277,7 @@ class VideoWidget(QWidget, updates.UpdateInterface):
 
         # Disable Transform UI
         # Is this the same clip_id already being transformed?
-        if self and self.transforming_effect and not effect_id:
+        if self.transforming_effect and not effect_id:
             # Clear transform
             self.transforming_effect = None
             self.transforming_clip = None
@@ -1107,12 +1301,8 @@ class VideoWidget(QWidget, updates.UpdateInterface):
 
     def regionTriggered(self, clip_id):
         """Handle the 'select region' signal when it's emitted"""
-        if self and not clip_id:
-            # Clear transform
-            self.region_enabled = False
-        else:
-            self.region_enabled = True
-
+        # Clear transform
+        self.region_enabled = bool(not clip_id)
         get_app().window.refreshFrameSignal.emit()
 
     def resizeEvent(self, event):
@@ -1140,7 +1330,7 @@ class VideoWidget(QWidget, updates.UpdateInterface):
             ratio = float(project_size.width()) / float(project_size.height())
             even_width = round(project_size.width() / 2.0) * 2
             even_height = round(round(even_width / ratio) / 2.0) * 2
-            project_size = QSize(even_width, even_height)
+            project_size = QSize(int(even_width), int(even_height))
 
         # Emit signal that video widget changed size
         self.win.MaxSizeChanged.emit(project_size)
@@ -1204,7 +1394,6 @@ class VideoWidget(QWidget, updates.UpdateInterface):
         self.mouse_dragging = False
         self.mouse_position = None
         self.transform_mode = None
-        self.gravity_point = None
         self.original_clip_data = None
         self.region_qimage = None
         self.region_transform = None
@@ -1213,8 +1402,8 @@ class VideoWidget(QWidget, updates.UpdateInterface):
         self.regionTopLeftHandle = None
         self.regionBottomRightHandle = None
         self.curr_frame_size = None # Frame size
-        self.zoom = 1.0 # Zoom of widget (does not affect video, only workspace)
-        self.cs = 14.0 # Corner size of Transform Handler rectangles
+        self.zoom = 1.0  # Zoom of widget (does not affect video, only workspace)
+        self.cs = 14.0  # Corner size of Transform Handler rectangles
         self.resize_button = QPushButton(_('Reset Zoom'), self)
         self.resize_button.hide()
         self.resize_button.setStyleSheet('QPushButton { margin: 10px; padding: 2px; }')
@@ -1255,7 +1444,6 @@ class VideoWidget(QWidget, updates.UpdateInterface):
 
         # Show Property timer
         # Timer to use a delay before sending MaxSizeChanged signals (so we don't spam libopenshot)
-        self.delayed_size = None
         self.delayed_resize_timer = QTimer(self)
         self.delayed_resize_timer.setInterval(200)
         self.delayed_resize_timer.setSingleShot(True)
diff --git a/src/windows/views/effects_listview.py b/src/windows/views/effects_listview.py
index 110dee2..3d4fb5d 100644
--- a/src/windows/views/effects_listview.py
+++ b/src/windows/views/effects_listview.py
@@ -35,7 +35,8 @@ from classes.logger import log
 
 class EffectsListView(QListView):
     """ A TreeView QWidget used on the main window """
-    drag_item_size = 48
+    drag_item_size = QSize(48, 48)
+    drag_item_center = QPoint(24, 24)
 
     def contextMenuEvent(self, event):
         # Set context menu mode
@@ -68,8 +69,8 @@ class EffectsListView(QListView):
         # Start drag operation
         drag = QDrag(self)
         drag.setMimeData(self.model().mimeData(selected))
-        drag.setPixmap(icon.pixmap(QSize(self.drag_item_size, self.drag_item_size)))
-        drag.setHotSpot(QPoint(self.drag_item_size / 2, self.drag_item_size / 2))
+        drag.setPixmap(icon.pixmap(self.drag_item_size))
+        drag.setHotSpot(self.drag_item_center)
         drag.exec_()
 
     def filter_changed(self):
diff --git a/src/windows/views/effects_treeview.py b/src/windows/views/effects_treeview.py
index 3e7e99b..2012396 100644
--- a/src/windows/views/effects_treeview.py
+++ b/src/windows/views/effects_treeview.py
@@ -35,7 +35,8 @@ from classes.logger import log
 
 class EffectsTreeView(QTreeView):
     """ A TreeView QWidget used on the main window """
-    drag_item_size = 48
+    drag_item_size = QSize(48, 48)
+    drag_item_center = QPoint(24, 24)
 
     def contextMenuEvent(self, event):
         # Set context menu mode
@@ -69,8 +70,8 @@ class EffectsTreeView(QTreeView):
         # Start drag operation
         drag = QDrag(self)
         drag.setMimeData(self.model().mimeData(selected))
-        drag.setPixmap(icon.pixmap(QSize(self.drag_item_size, self.drag_item_size)))
-        drag.setHotSpot(QPoint(self.drag_item_size / 2, self.drag_item_size / 2))
+        drag.setPixmap(icon.pixmap(self.drag_item_size))
+        drag.setHotSpot(self.drag_item_center)
         drag.exec_()
 
     def refresh_columns(self):
diff --git a/src/windows/views/emojis_listview.py b/src/windows/views/emojis_listview.py
index b377951..eacc5ba 100644
--- a/src/windows/views/emojis_listview.py
+++ b/src/windows/views/emojis_listview.py
@@ -38,7 +38,8 @@ import json
 
 class EmojisListView(QListView):
     """ A QListView QWidget used on the main window """
-    drag_item_size = 48
+    drag_item_size = QSize(48, 48)
+    drag_item_center = QPoint(24, 24)
 
     def dragEnterEvent(self, event):
         # If dragging urls onto widget, accept
@@ -56,8 +57,8 @@ class EmojisListView(QListView):
         drag = QDrag(self)
         drag.setMimeData(self.model.mimeData(selected))
         icon = self.model.data(selected[0], Qt.DecorationRole)
-        drag.setPixmap(icon.pixmap(QSize(self.drag_item_size, self.drag_item_size)))
-        drag.setHotSpot(QPoint(self.drag_item_size / 2, self.drag_item_size / 2))
+        drag.setPixmap(icon.pixmap(self.drag_item_size))
+        drag.setHotSpot(self.drag_item_center)
 
         # Create emoji file before drag starts
         data = json.loads(drag.mimeData().text())
diff --git a/src/windows/views/files_listview.py b/src/windows/views/files_listview.py
index 07bb46a..d2ed14e 100644
--- a/src/windows/views/files_listview.py
+++ b/src/windows/views/files_listview.py
@@ -37,7 +37,8 @@ from classes.query import File
 
 class FilesListView(QListView):
     """ A ListView QWidget used on the main window """
-    drag_item_size = 48
+    drag_item_size = QSize(48, 48)
+    drag_item_center = QPoint(24, 24)
 
     def contextMenuEvent(self, event):
         event.accept()
@@ -112,8 +113,8 @@ class FilesListView(QListView):
         # Start drag operation
         drag = QDrag(self)
         drag.setMimeData(self.model().mimeData(selected))
-        drag.setPixmap(icon.pixmap(QSize(self.drag_item_size, self.drag_item_size)))
-        drag.setHotSpot(QPoint(self.drag_item_size / 2, self.drag_item_size / 2))
+        drag.setPixmap(icon.pixmap(self.drag_item_size))
+        drag.setHotSpot(self.drag_item_center)
         drag.exec_()
 
     # Without defining this method, the 'copy' action doesn't show with cursor
diff --git a/src/windows/views/files_treeview.py b/src/windows/views/files_treeview.py
index a7e7220..3cec239 100644
--- a/src/windows/views/files_treeview.py
+++ b/src/windows/views/files_treeview.py
@@ -40,7 +40,8 @@ from classes.query import File
 
 class FilesTreeView(QTreeView):
     """ A TreeView QWidget used on the main window """
-    drag_item_size = 48
+    drag_item_size = QSize(48, 48)
+    drag_item_center = QPoint(24, 24)
 
     def contextMenuEvent(self, event):
 
@@ -113,8 +114,8 @@ class FilesTreeView(QTreeView):
         # Start drag operation
         drag = QDrag(self)
         drag.setMimeData(self.model().mimeData(selected))
-        drag.setPixmap(icon.pixmap(QSize(self.drag_item_size, self.drag_item_size)))
-        drag.setHotSpot(QPoint(self.drag_item_size / 2, self.drag_item_size / 2))
+        drag.setPixmap(icon.pixmap(self.drag_item_size))
+        drag.setHotSpot(self.drag_item_center)
         drag.exec_()
 
     # Without defining this method, the 'copy' action doesn't show with cursor
diff --git a/src/windows/views/properties_tableview.py b/src/windows/views/properties_tableview.py
index 89886f3..2fe056c 100644
--- a/src/windows/views/properties_tableview.py
+++ b/src/windows/views/properties_tableview.py
@@ -53,8 +53,13 @@ import openshot
 
 
 class PropertyDelegate(QItemDelegate):
-    def __init__(self, parent=None, *args):
-        QItemDelegate.__init__(self, parent, *args)
+    def __init__(self, parent=None, *args, **kwargs):
+
+        self.model = kwargs.pop("model", None)
+        if not self.model:
+            log.error("Cannot create delegate without data model!")
+
+        super().__init__(parent, *args, **kwargs)
 
         # pixmaps for curve icons
         self.curve_pixmaps = {
@@ -68,7 +73,7 @@ class PropertyDelegate(QItemDelegate):
         painter.setRenderHint(QPainter.Antialiasing)
 
         # Get data model and selection
-        model = get_app().window.propertyTableView.clip_properties_model.model
+        model = self.model
         row = model.itemFromIndex(index).row()
         selected_label = model.item(row, 0)
         selected_value = model.item(row, 1)
@@ -104,16 +109,16 @@ class PropertyDelegate(QItemDelegate):
         painter.setPen(QPen(Qt.NoPen))
         if property_type == "color":
             # Color keyframe
-            red = cur_property[1]["red"]["value"]
-            green = cur_property[1]["green"]["value"]
-            blue = cur_property[1]["blue"]["value"]
-            painter.setBrush(QBrush(QColor(QColor(red, green, blue))))
+            red = int(cur_property[1]["red"]["value"])
+            green = int(cur_property[1]["green"]["value"])
+            blue = int(cur_property[1]["blue"]["value"])
+            painter.setBrush(QColor(red, green, blue))
         else:
             # Normal Keyframe
             if option.state & QStyle.State_Selected:
-                painter.setBrush(QBrush(QColor("#575757")))
+                painter.setBrush(QColor("#575757"))
             else:
-                painter.setBrush(QBrush(QColor("#3e3e3e")))
+                painter.setBrush(QColor("#3e3e3e"))
 
         if readonly:
             # Set text color for read only fields
@@ -146,7 +151,10 @@ class PropertyDelegate(QItemDelegate):
 
             if points > 1:
                 # Draw interpolation icon on top
-                painter.drawPixmap(option.rect.x() + option.rect.width() - 30.0, option.rect.y() + 4, self.curve_pixmaps[interpolation])
+                painter.drawPixmap(
+                    int(option.rect.x() + option.rect.width() - 30.0),
+                    int(option.rect.y() + 4),
+                    self.curve_pixmaps[interpolation])
 
             # Set text color
             painter.setPen(QPen(Qt.white))
@@ -815,9 +823,9 @@ class PropertiesTableView(QTableView):
         _ = get_app()._tr
 
         # Get current value of color
-        red = cur_property[1]["red"]["value"]
-        green = cur_property[1]["green"]["value"]
-        blue = cur_property[1]["blue"]["value"]
+        red = int(cur_property[1]["red"]["value"])
+        green = int(cur_property[1]["green"]["value"])
+        blue = int(cur_property[1]["blue"]["value"])
 
         # Show color dialog
         currentColor = QColor(red, green, blue)
@@ -862,7 +870,7 @@ class PropertiesTableView(QTableView):
         self.files_model = self.win.files_model.model
 
         # Connect to update signals, so our menus stay current
-        self.win.files_model.ModelRefreshed.connect(self.refresh_menu)
+        self.files_model.dataChanged.connect(self.refresh_menu)
         self.win.transition_model.ModelRefreshed.connect(self.refresh_menu)
         self.menu_reset = False
 
@@ -887,7 +895,7 @@ class PropertiesTableView(QTableView):
         self.setWordWrap(True)
 
         # Set delegate
-        delegate = PropertyDelegate()
+        delegate = PropertyDelegate(model=self.clip_properties_model.model)
         self.setItemDelegateForColumn(1, delegate)
         self.previous_x = -1
 
diff --git a/src/windows/views/transitions_listview.py b/src/windows/views/transitions_listview.py
index b62f0cf..21f285b 100644
--- a/src/windows/views/transitions_listview.py
+++ b/src/windows/views/transitions_listview.py
@@ -35,7 +35,8 @@ from classes.logger import log
 
 class TransitionsListView(QListView):
     """ A QListView QWidget used on the main window """
-    drag_item_size = 48
+    drag_item_size = QSize(48, 48)
+    drag_item_center = QPoint(24, 24)
 
     def contextMenuEvent(self, event):
         event.accept()
@@ -69,8 +70,8 @@ class TransitionsListView(QListView):
         # Start drag operation
         drag = QDrag(self)
         drag.setMimeData(self.model().mimeData(selected))
-        drag.setPixmap(icon.pixmap(QSize(self.drag_item_size, self.drag_item_size)))
-        drag.setHotSpot(QPoint(self.drag_item_size / 2, self.drag_item_size / 2))
+        drag.setPixmap(icon.pixmap(self.drag_item_size))
+        drag.setHotSpot(self.drag_item_center)
         drag.exec_()
 
     def filter_changed(self):
diff --git a/src/windows/views/transitions_treeview.py b/src/windows/views/transitions_treeview.py
index 9e7c5c3..a3a59bf 100644
--- a/src/windows/views/transitions_treeview.py
+++ b/src/windows/views/transitions_treeview.py
@@ -35,7 +35,8 @@ from classes.logger import log
 
 class TransitionsTreeView(QTreeView):
     """ A TreeView QWidget used on the main window """
-    drag_item_size = 48
+    drag_item_size = QSize(48, 48)
+    drag_item_center = QPoint(24, 24)
 
     def contextMenuEvent(self, event):
         # Set context menu mode
@@ -67,8 +68,8 @@ class TransitionsTreeView(QTreeView):
         # Start drag operation
         drag = QDrag(self)
         drag.setMimeData(self.model().mimeData(selected))
-        drag.setPixmap(icon.pixmap(QSize(self.drag_item_size, self.drag_item_size)))
-        drag.setHotSpot(QPoint(self.drag_item_size / 2, self.drag_item_size / 2))
+        drag.setPixmap(icon.pixmap(self.drag_item_size))
+        drag.setHotSpot(self.drag_item_center)
         drag.exec_()
 
     def refresh_columns(self):
diff --git a/src/windows/views/tutorial.py b/src/windows/views/tutorial.py
index e2d7fb8..2412479 100644
--- a/src/windows/views/tutorial.py
+++ b/src/windows/views/tutorial.py
@@ -53,7 +53,12 @@ class TutorialDialog(QWidget):
 
         painter.setPen(QPen(frameColor, 2))
         painter.setBrush(self.palette().color(QPalette.Window))
-        painter.drawRoundedRect(QRectF(31, 0, self.width() - 31, self.height()), 10, 10)
+        painter.drawRoundedRect(
+            QRectF(31, 0,
+                   self.width() - 31,
+                   self.height()
+                   ),
+            10, 10)
 
         # Paint blue triangle (if needed)
         if self.arrow:
@@ -61,7 +66,8 @@ class TutorialDialog(QWidget):
             path = QPainterPath()
             path.moveTo(0, 35)
             path.lineTo(31, 35 - arrow_height)
-            path.lineTo(31, (35 - arrow_height) + (arrow_height * 2))
+            path.lineTo(
+                31, int((35 - arrow_height) + (arrow_height * 2)))
             path.lineTo(0, 35)
             painter.fillPath(path, frameColor)
 
@@ -199,7 +205,9 @@ class TutorialManager(object):
 
             # Create tutorial
             self.position_widget = tutorial_object
-            self.offset = QPoint(tutorial_details["x"], tutorial_details["y"])
+            self.offset = QPoint(
+                int(tutorial_details["x"]),
+                int(tutorial_details["y"]))
             tutorial_dialog = TutorialDialog(tutorial_id, tutorial_details["text"], tutorial_details["arrow"], self)
 
             # Connect signals
