[libopenshot/f35] (5 commits) ...Rebuild
by Leigh Scott
Summary of changes:
665578c... New upstream release (*)
ef5c122... Add missing alsa build requires (*)
3e6d7f6... Add opencv build requires (*)
3df8b5a... New upstream release (*)
e912f0c... Rebuild (*)
(*) This commit already existed in another branch; no separate mail sent
3 years
[libopenshot] Rebuild
by Leigh Scott
commit e912f0c37b4d6782aa0a828c553d68ac9792098c
Author: Leigh Scott <leigh123linux(a)gmail.com>
Date: Mon Nov 8 12:05:13 2021 +0000
Rebuild
libopenshot.spec | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
---
diff --git a/libopenshot.spec b/libopenshot.spec
index c5e1d5c..1c718ce 100644
--- a/libopenshot.spec
+++ b/libopenshot.spec
@@ -5,7 +5,7 @@
Name: libopenshot
Version: 0.2.7
-Release: 1%{?dist}
+Release: 2%{?dist}
Summary: Library for creating and editing videos
License: LGPLv3+
@@ -113,6 +113,9 @@ applications that use %{name}.
%{ruby_vendorarchdir}/*
%changelog
+* Mon Nov 08 2021 Leigh Scott <leigh123linux(a)gmail.com> - 0.2.7-2
+- rebuilt
+
* Tue Sep 07 2021 Leigh Scott <leigh123linux(a)gmail.com> - 0.2.7-1
- New upstream release
3 years
[openshot] Update to git snapshot
by Leigh Scott
commit dfb9b1293e8aff2c3fd262bc8394872efa3d7e75
Author: Leigh Scott <leigh123linux(a)gmail.com>
Date: Mon Nov 8 11:26:29 2021 +0000
Update to git snapshot
.gitignore | 1 +
openshot.spec | 20 +-
py310_fix.patch | 1968 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
sources | 2 +-
4 files changed, 1986 insertions(+), 5 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index f78e9a3..4af0481 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,3 +16,4 @@ openshot-1.4.3.tar.gz
/openshot-qt-2.5.1.tar.gz
/openshot-qt-2.6.0.tar.gz
/openshot-qt-2.6.1.tar.gz
+/openshot-b72327d1163f12362ff5d2a38751d3e2c61f7ba6.tar.gz
diff --git a/openshot.spec b/openshot.spec
index cc7696f..d667fd1 100644
--- a/openshot.spec
+++ b/openshot.spec
@@ -1,9 +1,13 @@
+%global commit b72327d1163f12362ff5d2a38751d3e2c61f7ba6
+%global date 20211104
+%global shortcommit0 %(c=%{commit}; echo ${c:0:7})
+
# Redirect find_lang to our patched version
%global find_lang %{_sourcedir}/openshot-find-lang.sh %{buildroot}
Name: openshot
-Version: 2.6.1
-Release: 1%{?dist}
+Version: 2.6.2
+Release: 0.1%{?shortcommit0:.%{date}git%{shortcommit0}}%{?dist}
Summary: Create and edit videos and movies
Group: Applications/Multimedia
@@ -11,7 +15,8 @@ License: GPLv3+
URL: http://www.openshot.org
%global distname %{name}-qt
-Source0: https://github.com/OpenShot/%{distname}/archive/v%{version}/%{distname}-%...
+#Source0: https://github.com/OpenShot/%{distname}/archive/v%{version}/%{distname}-%...
+Source0: https://github.com/OpenShot/%{distname}/archive/%{commit}/%{name}-%{commi...
# QT translation files are installed to a non-standard location
Source100: openshot-find-lang.sh
@@ -19,6 +24,9 @@ Source100: openshot-find-lang.sh
# Add openshot-owner@rpmfusion to appdata as update_contact
Patch1: openshot-rpmfusion-contact.patch
+# https://github.com/OpenShot/openshot-qt/pull/4527
+Patch2: py310_fix.patch
+
BuildArch: noarch
# libopenshot is unavailable on ppc64le, see rfbz #5528
ExcludeArch: ppc64le
@@ -80,7 +88,7 @@ Requires: %{name} = %{version}-%{release}
%prep
-%autosetup -p1 -n %{distname}-%{version}
+%autosetup -p1 -n %{distname}-%{commit}
%build
@@ -155,6 +163,10 @@ fi
%changelog
+* Mon Nov 08 2021 Leigh Scott <leigh123linux(a)gmail.com> - 2.6.2-0.1.20211104gitb72327d
+- Update to git snapshot
+- Patch for python-3.10
+
* Tue Sep 07 2021 Leigh Scott <leigh123linux(a)gmail.com> - 2.6.1-1
- New upstream release
diff --git a/py310_fix.patch b/py310_fix.patch
new file mode 100644
index 0000000..6b5af93
--- /dev/null
+++ b/py310_fix.patch
@@ -0,0 +1,1968 @@
+From 2e2b96863d93d0260cbccb7bb0be3141653cbfa4 Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc(a)gmail.com>
+Date: Mon, 20 Sep 2021 05:36:19 -0400
+Subject: [PATCH 1/6] Preferences: Fix logging calls
+
+---
+ src/windows/preferences.py | 18 +++++++++++-------
+ 1 file changed, 11 insertions(+), 7 deletions(-)
+
+diff --git a/src/windows/preferences.py b/src/windows/preferences.py
+index bf7d67b0c..325f496b3 100644
+--- a/src/windows/preferences.py
++++ b/src/windows/preferences.py
+@@ -102,7 +102,7 @@ def __init__(self):
+
+ def txtSearch_changed(self):
+ """textChanged event handler for search box"""
+- log.info("Search for %s" % self.txtSearch.text())
++ log.info("Search for %s", self.txtSearch.text())
+
+ # Populate preferences
+ self.Populate(filter=self.txtSearch.text())
+@@ -317,7 +317,7 @@ def Populate(self, filter=""):
+ value_list.remove(value_item)
+
+ # Remove hardware mode items which cannot decode the example video
+- log.debug("Preparing to test hardware decoding: %s" % (value_list))
++ log.debug("Preparing to test hardware decoding: %s", value_list)
+ for value_item in list(value_list):
+ v = value_item["value"]
+ if (not self.testHardwareDecode(value_list, v, 0)
+@@ -470,7 +470,7 @@ def bool_value_changed(self, widget, param, state):
+ # Trigger specific actions
+ if param["setting"] == "debug-mode":
+ # Update debug setting of timeline
+- log.info("Setting debug-mode to %s" % (state == Qt.Checked))
++ log.info("Setting debug-mode to %s", state == Qt.Checked)
+ debug_enabled = (state == Qt.Checked)
+
+ # Enable / Disable logger
+@@ -528,7 +528,9 @@ def text_value_changed(self, widget, param, value=None):
+ if param.get("category") == "Keyboard":
+ previous_value = value
+ value = QKeySequence(value).toString()
+- log.info("Parsing keyboard mapping via QKeySequence from %s to %s" % (previous_value, value))
++ log.info(
++ "Parsing keyboard mapping via QKeySequence from %s to %s",
++ previous_value, value)
+
+ # Save setting
+ self.s.set(param["setting"], value)
+@@ -604,11 +606,13 @@ def testHardwareDecode(self, all_decoders, decoder, decoder_card="0"):
+ if reader.GetFrame(0).CheckPixel(0, 0, 2, 133, 255, 255, 5):
+ is_supported = True
+ self.hardware_tests_cards[decoder_card].append(int(decoder))
+- log.debug("Successful hardware decoder! %s (%s-%s)" % (decoder_name, decoder, decoder_card))
++ log.debug(
++ "Successful hardware decoder! %s (%s-%s)",
++ decoder_name, decoder, decoder_card)
+ else:
+ log.debug(
+ "CheckPixel failed testing hardware decoding (i.e. wrong color found): %s (%s-%s)",
+- (decoder_name, decoder, decoder_card))
++ decoder_name, decoder, decoder_card)
+
+ reader.Close()
+ clip.Close()
+@@ -616,7 +620,7 @@ def testHardwareDecode(self, all_decoders, decoder, decoder_card="0"):
+ except Exception:
+ log.debug(
+ "Exception trying to test hardware decoding (this is expected): %s (%s-%s)",
+- (decoder_name, decoder, decoder_card))
++ decoder_name, decoder, decoder_card)
+
+ # Resume current settings
+ openshot.Settings.Instance().HARDWARE_DECODER = current_decoder
+
+From bf7b87d72ad8c4ec459c5b05a21be5f9948ae0fb Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc(a)gmail.com>
+Date: Thu, 4 Nov 2021 21:01:39 -0400
+Subject: [PATCH 2/6] Enforce integer function arguments
+
+---
+ src/tests/query_tests.py | 3 +
+ 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 +-
+ 14 files changed, 479 insertions(+), 265 deletions(-)
+
+diff --git a/src/tests/query_tests.py b/src/tests/query_tests.py
+index afdb770ee..09f2a9f13 100644
+--- a/src/tests/query_tests.py
++++ b/src/tests/query_tests.py
+@@ -334,6 +334,9 @@ def test_add_file(self):
+ def main():
+ global app
+ info.LOG_LEVEL_CONSOLE = "ERROR"
++ # Raise exception on any warning, including warning types
++ # like DeprecationWarning that are normally suppressed
++ os.environ["PYTHONWARNINGS"] = "error"
+ try:
+ app = OpenShotApp(sys.argv, mode="unittest")
+ except Exception:
+diff --git a/src/windows/export.py b/src/windows/export.py
+index a624eb2e2..6461afb25 100644
+--- a/src/windows/export.py
++++ b/src/windows/export.py
+@@ -290,7 +290,7 @@ def updateProgressBar(self, title_message, start_frame, end_frame, current_frame
+ 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 @@ def titlestring(sec, fps, mess):
+ 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 c3236ed84..40897f642 100644
+--- a/src/windows/models/properties_model.py
++++ b/src/windows/models/properties_model.py
+@@ -414,8 +414,8 @@ def value_updated(self, item, interpolation=-1, value=None, interpolation_detail
+ 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
+@@ -518,35 +518,35 @@ def value_updated(self, item, interpolation=-1, value=None, interpolation_detail
+ 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
+@@ -557,8 +557,8 @@ def value_updated(self, item, interpolation=-1, value=None, interpolation_detail
+ 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)}
+@@ -688,9 +688,9 @@ def set_property(self, property, filter, c, item_type, object_id=None):
+
+ 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":
+@@ -789,9 +789,9 @@ def set_property(self, property, filter, c, item_type, object_id=None):
+
+ 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 e4f3120c0..ea0c2946e 100644
+--- a/src/windows/process_effect.py
++++ b/src/windows/process_effect.py
+@@ -352,7 +352,7 @@ def accept(self):
+ 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 d5c89a204..f33696c5c 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, QIcon, QColor, QPen, QBrush, QCursor, QImage, QRegion
+@@ -41,8 +44,6 @@
+ 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 """
+@@ -86,37 +87,68 @@ def changed(self, action):
+ 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)
+@@ -127,47 +159,110 @@ def drawTransformHandler(self, painter, sx, sy, source_width, source_height, ori
+ 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()
+@@ -179,7 +274,11 @@ def paintEvent(self, event, *args):
+
+ # 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"))
+@@ -191,7 +290,7 @@ def paintEvent(self, event, *args):
+ # 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 (take into account display scaling for High DPI monitors)
+@@ -223,13 +322,19 @@ def paintEvent(self, event, *args):
+ # 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:
+@@ -239,12 +344,7 @@ def paintEvent(self, event, *args):
+ 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()
+@@ -285,9 +385,6 @@ def paintEvent(self, event, *args):
+ 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')
+@@ -329,7 +426,6 @@ def paintEvent(self, event, *args):
+ 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)
+
+@@ -342,10 +438,19 @@ def paintEvent(self, event, *args):
+ 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
+@@ -376,11 +481,21 @@ def paintEvent(self, event, *args):
+ 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
+@@ -394,23 +509,15 @@ def paintEvent(self, event, *args):
+ 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 """
+@@ -448,12 +555,16 @@ def mouseReleaseEvent(self, event):
+ # 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
+@@ -461,14 +572,25 @@ def mouseReleaseEvent(self, event):
+ 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
+@@ -484,7 +606,8 @@ def mouseReleaseEvent(self, event):
+ 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):
+@@ -627,6 +750,10 @@ def mouseMoveEvent(self, event):
+
+ # 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')
+@@ -635,8 +762,8 @@ def mouseMoveEvent(self, event):
+ 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:
+@@ -648,8 +775,13 @@ def mouseMoveEvent(self, event):
+ 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
+@@ -657,12 +789,17 @@ def mouseMoveEvent(self, event):
+ 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
+@@ -672,11 +809,13 @@ def mouseMoveEvent(self, event):
+ # 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
+@@ -686,11 +825,13 @@ def mouseMoveEvent(self, event):
+ # 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
+@@ -701,11 +842,13 @@ def mouseMoveEvent(self, event):
+ 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
+@@ -713,13 +856,16 @@ def mouseMoveEvent(self, event):
+ 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
+@@ -728,71 +874,68 @@ def mouseMoveEvent(self, event):
+ 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()
+@@ -803,16 +946,29 @@ def mouseMoveEvent(self, event):
+ 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
+@@ -824,13 +980,25 @@ def mouseMoveEvent(self, event):
+ # 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':
+@@ -859,12 +1027,11 @@ def mouseMoveEvent(self, event):
+ 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)
+
+@@ -878,18 +1045,28 @@ def mouseMoveEvent(self, event):
+ # 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
+@@ -898,63 +1075,70 @@ def mouseMoveEvent(self, event):
+ 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()
+@@ -1012,8 +1196,15 @@ def updateEffectProperty(self, effect_id, frame_number, obj_id, property_key, ne
+ # 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
+@@ -1023,12 +1214,15 @@ def updateEffectProperty(self, effect_id, frame_number, obj_id, property_key, ne
+
+ 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()
+@@ -1040,10 +1234,10 @@ def refreshTriggered(self):
+ """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):
+@@ -1053,7 +1247,7 @@ def transformTriggered(self, clip_id):
+
+ # 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
+@@ -1078,7 +1272,7 @@ def keyFrameTransformTriggered(self, effect_id, clip_id):
+
+ # 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
+@@ -1102,12 +1296,8 @@ def keyFrameTransformTriggered(self, effect_id, clip_id):
+
+ 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):
+@@ -1135,7 +1325,7 @@ def delayed_resize_callback(self):
+ 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)
+@@ -1199,7 +1389,6 @@ def __init__(self, watch_project=True, *args):
+ 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
+@@ -1208,8 +1397,8 @@ def __init__(self, watch_project=True, *args):
+ 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; }')
+@@ -1251,7 +1440,6 @@ def __init__(self, watch_project=True, *args):
+
+ # 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 b7da28dc4..3ce3b4164 100644
+--- a/src/windows/views/effects_listview.py
++++ b/src/windows/views/effects_listview.py
+@@ -36,7 +36,8 @@
+
+ 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
+@@ -69,8 +70,8 @@ def startDrag(self, event):
+ # 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 6a5ab79f4..910593524 100644
+--- a/src/windows/views/effects_treeview.py
++++ b/src/windows/views/effects_treeview.py
+@@ -36,7 +36,8 @@
+
+ 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
+@@ -70,8 +71,8 @@ def startDrag(self, supportedActions):
+ # 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 6f09bc562..cb13c35a7 100644
+--- a/src/windows/views/emojis_listview.py
++++ b/src/windows/views/emojis_listview.py
+@@ -39,7 +39,8 @@
+
+ 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
+@@ -57,8 +58,8 @@ def startDrag(self, event):
+ 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 83cdd0272..0e67306ae 100644
+--- a/src/windows/views/files_listview.py
++++ b/src/windows/views/files_listview.py
+@@ -38,7 +38,8 @@
+
+ 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()
+@@ -113,8 +114,8 @@ def startDrag(self, supportedActions):
+ # 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 d3fe74d88..776b71cc2 100644
+--- a/src/windows/views/files_treeview.py
++++ b/src/windows/views/files_treeview.py
+@@ -41,7 +41,8 @@
+
+ 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):
+
+@@ -114,8 +115,8 @@ def startDrag(self, supportedActions):
+ # 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 e79b13644..fdaa7ee24 100644
+--- a/src/windows/views/properties_tableview.py
++++ b/src/windows/views/properties_tableview.py
+@@ -53,8 +53,13 @@
+
+
+ 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 @@ def paint(self, painter, option, index):
+ 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 @@ def paint(self, painter, option, index):
+ 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 @@ def paint(self, painter, option, index):
+
+ 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))
+@@ -818,9 +826,9 @@ def Color_Picker_Triggered(self, cur_property):
+ _ = 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)
+@@ -865,7 +873,7 @@ def __init__(self, *args):
+ 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
+
+@@ -890,7 +898,7 @@ def __init__(self, *args):
+ 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 09af86b2b..3ba3346f5 100644
+--- a/src/windows/views/transitions_listview.py
++++ b/src/windows/views/transitions_listview.py
+@@ -36,7 +36,8 @@
+
+ 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()
+@@ -70,8 +71,8 @@ def startDrag(self, supportedActions):
+ # 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 6f903e913..7aa0cd481 100644
+--- a/src/windows/views/transitions_treeview.py
++++ b/src/windows/views/transitions_treeview.py
+@@ -36,7 +36,8 @@
+
+ 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
+@@ -68,8 +69,8 @@ def startDrag(self, event):
+ # 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 e2d7fb861..24124799f 100644
+--- a/src/windows/views/tutorial.py
++++ b/src/windows/views/tutorial.py
+@@ -53,7 +53,12 @@ def paintEvent(self, event, *args):
+
+ 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 @@ def paintEvent(self, event, *args):
+ 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 @@ def process(self, parent_name=None):
+
+ # 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
+
+From 477b6c88a0a343625127469a2ddbf05d4deedd79 Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc(a)gmail.com>
+Date: Thu, 4 Nov 2021 21:02:56 -0400
+Subject: [PATCH 3/6] VideoWidget: New checkTransformMode
+
+Replacement for getTransformMode with less repetitious code
+---
+ src/windows/video_widget.py | 160 ++++++++++++++----------------------
+ 1 file changed, 61 insertions(+), 99 deletions(-)
+
+diff --git a/src/windows/video_widget.py b/src/windows/video_widget.py
+index f33696c5c..1b9c35b54 100644
+--- a/src/windows/video_widget.py
++++ b/src/windows/video_widget.py
+@@ -610,106 +610,68 @@ def rotateCursor(self, pixmap, rotation, shear_x, shear_y):
+ Qt.SmoothTransformation)
+ return QCursor(rotated_pixmap)
+
+- def getTransformMode(self, rotation, shear_x, shear_y, event):
++ def checkTransformMode(self, rotation, shear_x, shear_y, event):
++ handle_uis = [
++ {"handle": self.centerHandle, "mode": 'origin', "cursor": 'hand'},
++ {"handle": self.topRightHandle, "mode": 'scale_top_right', "cursor": 'resize_bdiag'},
++ {"handle": self.topHandle, "mode": 'scale_top', "cursor": 'resize_y'},
++ {"handle": self.topLeftHandle, "mode": 'scale_top_left', "cursor": 'resize_fdiag'},
++ {"handle": self.leftHandle, "mode": 'scale_left', "cursor": 'resize_x'},
++ {"handle": self.rightHandle, "mode": 'scale_right', "cursor": 'resize_x'},
++ {"handle": self.bottomLeftHandle, "mode": 'scale_bottom_left', "cursor": 'resize_bdiag'},
++ {"handle": self.bottomHandle, "mode": 'scale_bottom', "cursor": 'resize_y'},
++ {"handle": self.bottomRightHandle, "mode": 'scale_bottom_right', "cursor": 'resize_fdiag'},
++ {"handle": self.topShearHandle, "mode": 'shear_top', "cursor": 'shear_x'},
++ {"handle": self.leftShearHandle, "mode": 'shear_left', "cursor": 'shear_y'},
++ {"handle": self.rightShearHandle, "mode": 'shear_right', "cursor": 'shear_y'},
++ {"handle": self.bottomShearHandle, "mode": 'shear_bottom', "cursor": 'shear_x'},
++ ]
++ non_handle_uis = {
++ "region": self.clipBounds,
++ "inside": {"mode": 'location', "cursor": 'move'},
++ "outside": {"mode": 'rotation', "cursor": "rotate"}
++ }
++
+ # Mouse over resize button (and not currently dragging)
+- 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()
++ ):
+ self.setCursor(Qt.ArrowCursor)
+- # Determine if cursor is over a handle
+- elif self.transform.mapToPolygon(self.centerHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+- if not self.transform_mode or self.transform_mode == 'origin':
+- self.setCursor(self.rotateCursor(self.cursors.get('hand'), rotation, shear_x, shear_y))
+- # Set the transform mode
+- if self.mouse_dragging and not self.transform_mode:
+- self.transform_mode = 'origin'
+- elif self.transform.mapToPolygon(self.topRightHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+- if not self.transform_mode or self.transform_mode == 'scale_top_right':
+- self.setCursor(self.rotateCursor(self.cursors.get('resize_bdiag'), rotation, shear_x, shear_y))
+- # Set the transform mode
+- if self.mouse_dragging and not self.transform_mode:
+- self.transform_mode = 'scale_top_right'
+- elif self.transform.mapToPolygon(self.topHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+- if not self.transform_mode or self.transform_mode == 'scale_top':
+- self.setCursor(self.rotateCursor(self.cursors.get('resize_y'), rotation, shear_x, shear_y))
+- # Set the transform mode
+- if self.mouse_dragging and not self.transform_mode:
+- self.transform_mode = 'scale_top'
+- elif self.transform.mapToPolygon(self.topLeftHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+- if not self.transform_mode or self.transform_mode == 'scale_top_left':
+- self.setCursor(self.rotateCursor(self.cursors.get('resize_fdiag'), rotation, shear_x, shear_y))
+- # Set the transform mode
+- if self.mouse_dragging and not self.transform_mode:
+- self.transform_mode = 'scale_top_left'
+- elif self.transform.mapToPolygon(self.leftHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+- if not self.transform_mode or self.transform_mode == 'scale_left':
+- self.setCursor(self.rotateCursor(self.cursors.get('resize_x'), rotation, shear_x, shear_y))
+- # Set the transform mode
+- if self.mouse_dragging and not self.transform_mode:
+- self.transform_mode = 'scale_left'
+- elif self.transform.mapToPolygon(self.rightHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+- if not self.transform_mode or self.transform_mode == 'scale_right':
+- self.setCursor(self.rotateCursor(self.cursors.get('resize_x'), rotation, shear_x, shear_y))
+- # Set the transform mode
+- if self.mouse_dragging and not self.transform_mode:
+- self.transform_mode = 'scale_right'
+- elif self.transform.mapToPolygon(self.bottomLeftHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+- if not self.transform_mode or self.transform_mode == 'scale_bottom_left':
+- self.setCursor(self.rotateCursor(self.cursors.get('resize_bdiag'), rotation, shear_x, shear_y))
+- # Set the transform mode
+- if self.mouse_dragging and not self.transform_mode:
+- self.transform_mode = 'scale_bottom_left'
+- elif self.transform.mapToPolygon(self.bottomHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+- if not self.transform_mode or self.transform_mode == 'scale_bottom':
+- self.setCursor(self.rotateCursor(self.cursors.get('resize_y'), rotation, shear_x, shear_y))
+- # Set the transform mode
+- if self.mouse_dragging and not self.transform_mode:
+- self.transform_mode = 'scale_bottom'
+- elif self.transform.mapToPolygon(self.bottomRightHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+- if not self.transform_mode or self.transform_mode == 'scale_bottom_right':
+- self.setCursor(self.rotateCursor(self.cursors.get('resize_fdiag'), rotation, shear_x, shear_y))
+- # Set the transform mode
+- if self.mouse_dragging and not self.transform_mode:
+- self.transform_mode = 'scale_bottom_right'
+- elif self.transform.mapToPolygon(self.topShearHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+- if not self.transform_mode or self.transform_mode == 'shear_top':
+- self.setCursor(self.rotateCursor(self.cursors.get('shear_x'), rotation, shear_x, shear_y))
+- # Set the transform mode
+- if self.mouse_dragging and not self.transform_mode:
+- self.transform_mode = 'shear_top'
+- elif self.transform.mapToPolygon(self.leftShearHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+- if not self.transform_mode or self.transform_mode == 'shear_left':
+- self.setCursor(self.rotateCursor(self.cursors.get('shear_y'), rotation, shear_x, shear_y))
+- # Set the transform mode
+- if self.mouse_dragging and not self.transform_mode:
+- self.transform_mode = 'shear_left'
+- elif self.transform.mapToPolygon(self.rightShearHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+- if not self.transform_mode or self.transform_mode == 'shear_right':
+- self.setCursor(self.rotateCursor(self.cursors.get('shear_y'), rotation, shear_x, shear_y))
+- # Set the transform mode
+- if self.mouse_dragging and not self.transform_mode:
+- self.transform_mode = 'shear_right'
+- elif self.transform.mapToPolygon(self.bottomShearHandle.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+- if not self.transform_mode or self.transform_mode == 'shear_bottom':
+- self.setCursor(self.rotateCursor(self.cursors.get('shear_x'), rotation, shear_x, shear_y))
+- # Set the transform mode
+- if self.mouse_dragging and not self.transform_mode:
+- self.transform_mode = 'shear_bottom'
+- elif self.transform.mapToPolygon(self.clipBounds.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+- if not self.transform_mode or self.transform_mode == 'location':
+- self.setCursor(self.rotateCursor(self.cursors.get('move'), rotation, shear_x, shear_y))
+- # Set the transform mode
+- if self.mouse_dragging and not self.transform_mode:
+- self.transform_mode = 'location'
+- elif not self.transform.mapToPolygon(self.clipBounds.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
+- if not self.transform_mode or self.transform_mode == 'rotation':
+- self.setCursor(self.rotateCursor(self.cursors.get('rotate'), rotation, shear_x, shear_y))
+- # Set the transform mode
+- if self.mouse_dragging and not self.transform_mode:
+- self.transform_mode = 'rotation'
+- elif not self.transform_mode:
+- # Reset cursor when not over a handle
+- self.setCursor(QCursor(Qt.ArrowCursor))
++ self.transform_mode = None
++ return
++
++ # If mouse is over a handle, set corresponding pointer/mode
++ for h in handle_uis:
++ if self.transform.mapToPolygon(
++ h["handle"].toRect()
++ ).containsPoint(event.pos(), Qt.OddEvenFill):
++ # Handle contains cursor
++ if self.transform_mode and self.transform_mode != h["mode"]:
++ # We're in different xform mode, skip
++ continue
++ if self.mouse_dragging:
++ self.transform_mode = h["mode"]
++ self.setCursor(self.rotateCursor(
++ self.cursors.get(h["cursor"]), rotation, shear_x, shear_y))
++ return
++
++ # If not over any handles, determne inside/outside clip rectangle
++ r = non_handle_uis.get("region")
++ if self.transform.mapToPolygon(r.toRect()).containsPoint(event.pos(), Qt.OddEvenFill):
++ nh = non_handle_uis.get("inside", {})
++ else:
++ nh = non_handle_uis.get("outside", {})
++ if self.mouse_dragging and not self.transform_mode:
++ self.transform_mode = nh.get("mode")
++ if not self.transform_mode or self.transform_mode == nh.get("mode"):
++ self.setCursor(self.rotateCursor(
++ self.cursors.get(nh.get("cursor")), rotation, shear_x, shear_y))
+
+- return True
++
++ # If we got this far and we don't have a transform mode, reset the cursor
++ if not self.transform_mode:
++ self.setCursor(QCursor(Qt.ArrowCursor))
+
+ def mouseMoveEvent(self, event):
+ """Capture mouse events on video preview window """
+@@ -746,7 +708,7 @@ def mouseMoveEvent(self, event):
+ if self.mouse_dragging and not self.transform_mode:
+ self.original_clip_data = self.transforming_clip.data
+
+- _ = self.getTransformMode(rotation, shear_x, shear_y, event)
++ self.checkTransformMode(rotation, shear_x, shear_y, event)
+
+ # Transform clip object
+ if self.transform_mode:
+@@ -1040,7 +1002,7 @@ def mouseMoveEvent(self, event):
+ self.mutex.unlock()
+ return
+
+- _ = self.getTransformMode(0, 0, 0, event)
++ self.checkTransformMode(0, 0, 0, event)
+
+ # Transform effect object
+ if self.transform_mode:
+
+From 62c30fa882642f1bb24b494b0c76ea0738a18cd6 Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc(a)gmail.com>
+Date: Thu, 4 Nov 2021 21:42:48 -0400
+Subject: [PATCH 4/6] VideoWidget: Protect property accesses
+
+---
+ src/windows/video_widget.py | 61 +++++++++++++++++++++----------------
+ 1 file changed, 34 insertions(+), 27 deletions(-)
+
+diff --git a/src/windows/video_widget.py b/src/windows/video_widget.py
+index 1b9c35b54..a4d42969f 100644
+--- a/src/windows/video_widget.py
++++ b/src/windows/video_widget.py
+@@ -635,7 +635,7 @@ def checkTransformMode(self, rotation, shear_x, shear_y, event):
+ # Mouse over resize button (and not currently dragging)
+ if (not self.mouse_dragging
+ and self.resize_button.isVisible()
+- and self.resize_button.rect().contains(event.pos()
++ and self.resize_button.rect().contains(event.pos())
+ ):
+ self.setCursor(Qt.ArrowCursor)
+ self.transform_mode = None
+@@ -668,11 +668,6 @@ def checkTransformMode(self, rotation, shear_x, shear_y, event):
+ self.setCursor(self.rotateCursor(
+ self.cursors.get(nh.get("cursor")), rotation, shear_x, shear_y))
+
+-
+- # If we got this far and we don't have a transform mode, reset the cursor
+- if not self.transform_mode:
+- self.setCursor(QCursor(Qt.ArrowCursor))
+-
+ def mouseMoveEvent(self, event):
+ """Capture mouse events on video preview window """
+ self.mutex.lock()
+@@ -1121,27 +1116,37 @@ def updateClipProperty(self, clip_id, frame_number, property_key, new_value, ref
+ # No clip found
+ return
+
+- for point in c.data[property_key]["Points"]:
+- log.info("looping points: co.X = %s" % point["co"]["X"])
++ # Property missing? Create it!
++ if property_key not in c.data:
++ c.data[property_key] = {"Points": []}
++ log.warning(
++ "%s: Added missing '%s' to property data",
++ clip_id, property_key)
+
+- if point["co"]["X"] == frame_number:
++ points = c.data.get(property_key).get("Points")
++ for point in points:
++ co = point.get("co", {})
++ log.info("looping points: co.X = %s" % co.get("X"))
++
++ if co.get("X") == frame_number:
+ found_point = True
+ clip_updated = True
+- point["interpolation"] = openshot.BEZIER
+- point["co"]["Y"] = float(new_value)
++ point.update({
++ "co": {"X": frame_number, "Y": float(new_value)},
++ "interpolation": openshot.BEZIER,
++ })
+
+ if not found_point and new_value is not None:
+ clip_updated = True
+- log.info("Created new point at X=%s", frame_number)
++ log.info("Creating new point at X=%s", frame_number)
+ c.data[property_key]["Points"].append({
+- 'co': {'X': frame_number, 'Y': new_value},
++ 'co': {'X': frame_number, 'Y': float(new_value)},
+ 'interpolation': openshot.BEZIER
+ })
+
+- # Reduce # of clip properties we are saving (performance boost)
+- c.data = {property_key: c.data.get(property_key)}
+-
+ if clip_updated:
++ # Reduce # of clip properties we are saving (performance boost)
++ c.data = {property_key: c.data.get(property_key)}
+ c.save()
+ # Update the preview
+ if refresh:
+@@ -1166,27 +1171,29 @@ def updateEffectProperty(self, effect_id, frame_number, obj_id, property_key, ne
+ return
+
+ for point in points_list:
+- log.info("looping points: co.X = %s", point["co"]["X"])
++ co = point.get("co", {})
++ log.info("looping points: co.X = %s", co.get("X"))
+
+- if point["co"]["X"] == frame_number:
++ if co.get("X") == frame_number:
+ found_point = True
+ effect_updated = True
+- point["interpolation"] = openshot.BEZIER
+- point["co"]["Y"] = float(new_value)
++ point.update({
++ "co": {"X": frame_number, "Y": float(new_value)},
++ "interpolation": openshot.BEZIER,
++ })
+
+- if not found_point and new_value != None:
++ if not found_point and new_value is not None:
+ effect_updated = True
+- log.info("Created new point at X=%s", frame_number)
++ log.info("Creating new point at X=%s", frame_number)
+ points_list.append({
+- 'co': { 'X': frame_number, 'Y': new_value },
++ 'co': {'X': frame_number, 'Y': float(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)}}
+-
+ if effect_updated:
++ # 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.save()
+ # Update the preview
+ if refresh:
+
+From 2092dc4782260f939fdb83e3e88bc05644fbc7c5 Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc(a)gmail.com>
+Date: Thu, 4 Nov 2021 21:52:19 -0400
+Subject: [PATCH 5/6] classes/thumbnail: Fix dangling filehandles
+
+---
+ src/classes/thumbnail.py | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/src/classes/thumbnail.py b/src/classes/thumbnail.py
+index cca47d68f..dac7422a1 100644
+--- a/src/classes/thumbnail.py
++++ b/src/classes/thumbnail.py
+@@ -209,13 +209,13 @@ def do_GET(self):
+
+ # Send message back to client
+ if os.path.exists(thumb_path):
+- if not only_path:
+- self.wfile.write(open(thumb_path, 'rb').read())
+- else:
++ if only_path:
+ self.wfile.write(bytes(thumb_path, "utf-8"))
++ else:
++ with open(thumb_path, 'rb') as f:
++ self.wfile.write(f.read())
+
+ # Pause processing of request (since we don't currently use thread pooling, this allows
+ # the threads to be processed without choking the CPU as much
+ # TODO: Make HTTPServer work with a limited thread pool and remove this sleep() hack.
+ time.sleep(0.01)
+-
+
+From 40d7a2b46c81f249d45b79fc0217ceea8f57cb87 Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc(a)gmail.com>
+Date: Sun, 7 Nov 2021 00:17:45 -0400
+Subject: [PATCH 6/6] Auto-set PYTHONWARNINGS=always envvar
+
+---
+ src/launch.py | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/src/launch.py b/src/launch.py
+index 9fc600b23..2717c2bbb 100755
+--- a/src/launch.py
++++ b/src/launch.py
+@@ -41,7 +41,7 @@
+ """
+
+ import sys
+-import os.path
++import os
+ import argparse
+
+ from PyQt5.QtCore import Qt
+@@ -209,4 +209,7 @@ def main():
+
+
+ if __name__ == "__main__":
++ # Raise exception on any warning, including warning types
++ # like DeprecationWarning that are normally suppressed
++ os.putenv("PYTHONWARNINGS", "always")
+ main()
diff --git a/sources b/sources
index d181fef..406417c 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-SHA512 (openshot-qt-2.6.1.tar.gz) = 9e2d1eae5d72ec2deab914e464dac0d84a864ba2ca63104fad57c90fc907764d5683149fb8136d50030cae993cef9a977e995b0783868988de4668e71a3a5ee2
+SHA512 (openshot-b72327d1163f12362ff5d2a38751d3e2c61f7ba6.tar.gz) = f8b8e9288e4368ea6097bdce339c2d488b4dc59c6aa4accf693485153da22a44980c5b48bfc1c3dc0e838b66c08c5cb49e41207d66da7635e0d0a7dab0798aae
3 years
[lpf-mscore-fonts] Add Obsoletes: old font names
by Sérgio M. Basto
commit c0a555cb22709c3527d9f9c471ebbb0e064eabf8
Author: Sérgio M. Basto <sergio(a)serjux.com>
Date: Sun Nov 7 23:21:39 2021 +0000
Add Obsoletes: old font names
lpf-mscore-fonts.spec | 5 ++++-
mscore-fonts.spec.in | 22 ++++++++++++++++++++--
2 files changed, 24 insertions(+), 3 deletions(-)
---
diff --git a/lpf-mscore-fonts.spec b/lpf-mscore-fonts.spec
index d3d2089..30410ed 100644
--- a/lpf-mscore-fonts.spec
+++ b/lpf-mscore-fonts.spec
@@ -3,7 +3,7 @@
Name: lpf-mscore-fonts
Version: 2.2
-Release: 4%{?dist}
+Release: 5%{?dist}
Summary: Bootstrap package building mscore-fonts using lpf
License: MIT
@@ -63,6 +63,9 @@ mscore-fonts non-redistributable package.
%changelog
+* Sun Nov 07 2021 Sérgio Basto <sergio(a)serjux.com> - 2.2-5
+- Add Obsoletes: old font names
+
* Wed May 26 2021 Sérgio Basto <sergio(a)serjux.com> - 2.2-4
- Rename packages from mscore-fonts ms-core-fonts
diff --git a/mscore-fonts.spec.in b/mscore-fonts.spec.in
index 8624b93..feda518 100644
--- a/mscore-fonts.spec.in
+++ b/mscore-fonts.spec.in
@@ -1,4 +1,5 @@
%global fontname ms-core
+%global oldfontname mscore
# directory to unpack truetype fonts from the cab into
%global fontdir %{_datadir}/fonts/%{fontname}
@@ -7,7 +8,7 @@
Summary: Microsoft core TrueType fonts for better Windows Compatibility
Name: %{fontname}-fonts
Version: 1.0
-Release: 2%{?dist}
+Release: 4%{?dist}
URL: http://mscorefonts2.sourceforge.net/
License: non-redistributable, no commercial use, no modifications permitted
@@ -75,6 +76,7 @@ Common support files for %{fontname}-fonts packages including licenses.
%package -n %{fontname}-arial-fonts
Summary: %{fontname} Arial ttf font
Requires: %{name}-common = %{version}-%{release}
+Obsoletes: %{oldfontname}-arial-fonts <= %{version}-%{release}
%description -n %{fontname}-arial-fonts
Microsoft Arial font for the web that prior to 2002 was available from
@@ -88,6 +90,7 @@ website.
%package -n %{fontname}-times-fonts
Summary: %{fontname} Times ttf font
Requires: %{name}-common = %{version}-%{release}
+Obsoletes: %{oldfontname}-times-fonts <= %{version}-%{release}
%description -n %{fontname}-times-fonts
Microsoft Times font for the web that prior to 2002 was available from
@@ -101,6 +104,7 @@ website.
%package -n %{fontname}-trebuchet-fonts
Summary: %{fontname} Trebuchet ttf font
Requires: %{name}-common = %{version}-%{release}
+Obsoletes: %{oldfontname}-trebuchet-fonts <= %{version}-%{release}
%description -n %{fontname}-trebuchet-fonts
Microsoft Trebuchet font for the web that prior to 2002 was available
@@ -114,6 +118,8 @@ on the Microsoft website.
%package -n %{fontname}-verdana-fonts
Summary: %{fontname} verdana ttf font
Requires: %{name}-common = %{version}-%{release}
+Obsoletes: %{oldfontname}-verdana-fonts <= %{version}-%{release}
+
%description -n %{fontname}-verdana-fonts
Microsoft verdana font for the web that prior to 2002 was available from
@@ -127,6 +133,7 @@ website.
%package -n %{fontname}-andale-fonts
Summary: %{fontname} Andale Mono ttf font
Requires: %{name}-common = %{version}-%{release}
+Obsoletes: %{oldfontname}-andale-fonts <= %{version}-%{release}
%description -n %{fontname}-andale-fonts
Andale Mono font for the web that prior to 2002 was available from
@@ -138,6 +145,7 @@ http://www.microsoft.com/typography/fontpack.
%package -n %{fontname}-comic-fonts
Summary: %{fontname} comic ttf font
Requires: %{name}-common = %{version}-%{release}
+Obsoletes: %{oldfontname}-comic-fonts <= %{version}-%{release}
%description -n %{fontname}-comic-fonts
Comic bold and regular font for the web that prior to 2002 was available
@@ -149,6 +157,7 @@ from http://www.microsoft.com/typography/fontpack.
%package -n %{fontname}-courier-fonts
Summary: %{fontname} courier ttf font
Requires: %{name}-common = %{version}-%{release}
+Obsoletes: %{oldfontname}-courier-fonts <= %{version}-%{release}
%description -n %{fontname}-courier-fonts
Courier bold, bold italic, italic and regular font for the web that prior
@@ -160,6 +169,7 @@ to 2002 was available from http://www.microsoft.com/typography/fontpack.
%package -n %{fontname}-georgia-fonts
Summary: %{fontname} georgia ttf font
Requires: %{name}-common = %{version}-%{release}
+Obsoletes: %{oldfontname}-georgia-fonts <= %{version}-%{release}
%description -n %{fontname}-georgia-fonts
Georgia font for the web that prior to 2002 was available from
@@ -171,6 +181,7 @@ http://www.microsoft.com/typography/fontpack.
%package -n %{fontname}-impact-fonts
Summary: %{fontname} impact ttf font
Requires: %{name}-common = %{version}-%{release}
+Obsoletes: %{oldfontname}-impact-fonts <= %{version}-%{release}
%description -n %{fontname}-impact-fonts
Impact font for the web that prior to 2002 was available from
@@ -182,6 +193,7 @@ http://www.microsoft.com/typography/fontpack.
%package -n %{fontname}-webdings-fonts
Summary: %{fontname} webdings ttf font
Requires: %{name}-common = %{version}-%{release}
+Obsoletes: %{oldfontname}-webdings-fonts <= %{version}-%{release}
%description -n %{fontname}-webdings-fonts
Webdings font for the web that prior to 2002 was available from
@@ -226,7 +238,13 @@ done
%changelog
-* Fri Feb 14 2014 Alec Leamas<leamas.alec(a)gmail.com>
+* Sun Nov 07 2021 Sérgio Basto <sergio(a)serjux.com> - 1.0-4
+- Add Obsoletes: old font names
+
+* Sat May 29 2021 Sérgio Basto <sergio(a)serjux.com> - 1.0-3
+- Rename packages from mscore-fonts ms-core-fonts
+
+* Fri Feb 14 2014 Alec Leamas<leamas.alec(a)gmail.com> 1.0-2
- Lower config priority (55 -> 61)
- New description, downplay fonts usefullness.
3 years