* [PR PATCH] openshot: update to 2.6.1.
@ 2022-02-23 0:01 tibequadorian
2022-02-23 0:12 ` [PR PATCH] [Updated] " tibequadorian
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: tibequadorian @ 2022-02-23 0:01 UTC (permalink / raw)
To: ml
[-- Attachment #1: Type: text/plain, Size: 1228 bytes --]
There is a new pull request by tibequadorian against master on the void-packages repository
https://github.com/tibequadorian/void-packages openshot
https://github.com/void-linux/void-packages/pull/35798
openshot: update to 2.6.1.
<!-- Uncomment relevant sections and delete options which are not applicable -->
#### Testing the changes
- I tested the changes in this PR: **briefly**
<!--
#### New package
- This new package conforms to the [quality requirements](https://github.com/void-linux/void-packages/blob/master/Manual.md#quality-requirements): **YES**|**NO**
-->
<!-- Note: If the build is likely to take more than 2 hours, please [skip CI](https://github.com/void-linux/void-packages/blob/master/CONTRIBUTING.md#continuous-integration)
and test at least one native build and, if supported, at least one cross build.
Ignore this section if this PR is not skipping CI.
-->
<!--
#### Local build testing
- I built this PR locally for my native architecture, (ARCH-LIBC)
- I built this PR locally for these architectures (if supported. mark crossbuilds):
- aarch64-musl
- armv7l
- armv6l-musl
-->
A patch file from https://github.com/void-linux/void-packages/pull/35798.patch is attached
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-openshot-35798.patch --]
[-- Type: text/x-diff, Size: 124025 bytes --]
From 5ef92256989275841e85f297f5bf0b0e4e2b1f4d Mon Sep 17 00:00:00 2001
From: tibequadorian <tibequadorian@posteo.de>
Date: Wed, 23 Feb 2022 00:41:38 +0100
Subject: [PATCH 1/3] libopenshot-audio: update to 0.2.2.
---
common/shlibs | 2 +-
.../libopenshot-audio/patches/fix-musl.patch | 86 ++++++++++---------
srcpkgs/libopenshot-audio/template | 7 +-
3 files changed, 51 insertions(+), 44 deletions(-)
diff --git a/common/shlibs b/common/shlibs
index 32befc92d641..c66b4fbe6700 100644
--- a/common/shlibs
+++ b/common/shlibs
@@ -2597,7 +2597,7 @@ libax25io.so.0 libax25-0.0.12rc4_1
libmill.so.18 libmill-1.14_1
libges-1.0.so.0 gst1-editing-services-1.6.2_1
libykneomgr.so.0 libykneomgr-0.1.8_1
-libopenshot-audio.so.7 libopenshot-audio-0.2.0_1
+libopenshot-audio.so.8 libopenshot-audio-0.2.2_1
libopenshot.so.19 libopenshot-0.2.5_3
libpqxx-6.3.so libpqxx-6.3.3_1
libndpi.so.3 ndpi-3.4_1
diff --git a/srcpkgs/libopenshot-audio/patches/fix-musl.patch b/srcpkgs/libopenshot-audio/patches/fix-musl.patch
index b5f178d92d8a..532248c86217 100644
--- a/srcpkgs/libopenshot-audio/patches/fix-musl.patch
+++ b/srcpkgs/libopenshot-audio/patches/fix-musl.patch
@@ -1,40 +1,46 @@
---- a/JuceLibraryCode/modules/juce_core/juce_core.cpp 2016-08-30 06:24:27.000000000 +0200
-+++ b/JuceLibraryCode/modules/juce_core/juce_core.cpp 2016-12-12 14:53:23.532613378 +0100
-@@ -97,7 +97,7 @@
- #include <net/if.h>
- #include <sys/ioctl.h>
-
-- #if ! JUCE_ANDROID
-+ #if ! JUCE_ANDROID && defined(__GLIBC__)
- #include <execinfo.h>
- #endif
- #endif
---- a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp 2016-08-30 06:24:27.000000000 +0200
-+++ b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp 2016-12-12 14:58:35.988986030 +0100
-@@ -134,6 +134,8 @@
- }
- }
-
-+ #elif !defined(__GLIBC__)
-+ jassertfalse; // sorry, not implemented yet!
- #else
- void* stack[128];
- int frames = backtrace (stack, numElementsInArray (stack));
---- a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp 2016-08-30 06:24:27.000000000 +0200
-+++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp 2016-12-12 15:07:35.046607788 +0100
-@@ -142,8 +142,15 @@
- return result;
- }
-
-+#if defined(__GLIBC__)
- String SystemStats::getUserLanguage() { return getLocaleValue (_NL_IDENTIFICATION_LANGUAGE); }
- String SystemStats::getUserRegion() { return getLocaleValue (_NL_IDENTIFICATION_TERRITORY); }
-+#else
-+// The identifiers _NL_INDENTIFICATION_LANGUAGE and _TERRIRTORY are not defined in musl libc.
-+// TODO: Find a better fix than just returning nonsense. Inspect env("LANG") perhaps?
-+String SystemStats::getUserLanguage() { return String("en"); }
-+String SystemStats::getUserRegion() { return String("US"); }
-+#endif
- String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getUserRegion(); }
-
- //==============================================================================
+diff --git a/JuceLibraryCode/modules/juce_core/juce_core.cpp b/JuceLibraryCode/modules/juce_core/juce_core.cpp
+index 8bac812..e23b422 100644
+--- a/JuceLibraryCode/modules/juce_core/juce_core.cpp
++++ b/JuceLibraryCode/modules/juce_core/juce_core.cpp
+@@ -92,7 +92,7 @@
+ #include <net/if.h>
+ #include <sys/ioctl.h>
+
+- #if ! JUCE_ANDROID
++ #if ! JUCE_ANDROID && defined(__GLIBC__)
+ #include <execinfo.h>
+ #endif
+ #endif
+diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp
+index 2d7faa3..f132405 100644
+--- a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp
++++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp
+@@ -139,8 +139,15 @@ static String getLocaleValue (nl_item key)
+ return result;
+ }
+
++#if defined(__GLIBC__)
+ String SystemStats::getUserLanguage() { return getLocaleValue (_NL_IDENTIFICATION_LANGUAGE); }
+ String SystemStats::getUserRegion() { return getLocaleValue (_NL_IDENTIFICATION_TERRITORY); }
++#else
++// The identifiers _NL_INDENTIFICATION_LANGUAGE and _TERRIRTORY are not defined in musl libc.
++// TODO: Find a better fix than just returning nonsense. Inspect env("LANG") perhaps?
++String SystemStats::getUserLanguage() { return String("en"); }
++String SystemStats::getUserRegion() { return String("US"); }
++#endif
+ String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getUserRegion(); }
+
+ //==============================================================================
+diff --git a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp
+index 757ea24..6b61e16 100644
+--- a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp
++++ b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp
+@@ -138,7 +138,7 @@ String SystemStats::getStackBacktrace()
+ {
+ String result;
+
+- #if JUCE_ANDROID || JUCE_MINGW
++ #if JUCE_ANDROID || JUCE_MINGW || !defined(__GLIBC__)
+ jassertfalse; // sorry, not implemented yet!
+
+ #elif JUCE_WINDOWS
diff --git a/srcpkgs/libopenshot-audio/template b/srcpkgs/libopenshot-audio/template
index 56c330eafcf1..254f8b6283bd 100644
--- a/srcpkgs/libopenshot-audio/template
+++ b/srcpkgs/libopenshot-audio/template
@@ -1,6 +1,6 @@
# Template file for 'libopenshot-audio'
pkgname=libopenshot-audio
-version=0.2.0
+version=0.2.2
revision=1
build_style=cmake
hostmakedepends="doxygen"
@@ -11,7 +11,7 @@ maintainer="Spencer Hill <spencernh77@gmail.com>"
license="GPL-3.0-or-later"
homepage="https://github.com/OpenShot/libopenshot-audio"
distfiles="https://github.com/OpenShot/libopenshot-audio/archive/v${version}.tar.gz"
-checksum=937ff4f1c2dfb8ab5d56ad85beacaa29dfd5a79af0d9cf647386034fe9882309
+checksum=66bedfda0d8d430598b21bc2dde6c0016a758a6c83467d0273a9d692de10baaf
if [ "$XBPS_TARGET_NO_ATOMIC8" ]; then
makedepends+=" libatomic-devel"
@@ -20,9 +20,10 @@ fi
libopenshot-audio-devel_package() {
short_desc+=" - development files"
- depends+=" ${sourcepkg}>=${version}_${revision}"
+ depends+=" ${sourcepkg}>=${version}_${revision} alsa-lib-devel zlib-devel"
pkg_install() {
vmove usr/include
+ vmove usr/lib/cmake
vmove "usr/lib/*.so"
}
}
From 75f8145b3f01182645f1df55791a5ed864b347d6 Mon Sep 17 00:00:00 2001
From: tibequadorian <tibequadorian@posteo.de>
Date: Wed, 23 Feb 2022 00:42:37 +0100
Subject: [PATCH 2/3] libopenshot: update to 0.2.7.
---
common/shlibs | 2 +-
.../patches/AV_GET_CODEC_CONTEXT-macro.patch | 34 -------------------
srcpkgs/libopenshot/patches/fix-musl.patch | 34 +++++++++++++++++++
srcpkgs/libopenshot/template | 16 +++++----
4 files changed, 45 insertions(+), 41 deletions(-)
delete mode 100644 srcpkgs/libopenshot/patches/AV_GET_CODEC_CONTEXT-macro.patch
create mode 100644 srcpkgs/libopenshot/patches/fix-musl.patch
diff --git a/common/shlibs b/common/shlibs
index c66b4fbe6700..d339115dda39 100644
--- a/common/shlibs
+++ b/common/shlibs
@@ -2598,7 +2598,7 @@ libmill.so.18 libmill-1.14_1
libges-1.0.so.0 gst1-editing-services-1.6.2_1
libykneomgr.so.0 libykneomgr-0.1.8_1
libopenshot-audio.so.8 libopenshot-audio-0.2.2_1
-libopenshot.so.19 libopenshot-0.2.5_3
+libopenshot.so.21 libopenshot-0.2.7_1
libpqxx-6.3.so libpqxx-6.3.3_1
libndpi.so.3 ndpi-3.4_1
liblog.so android-studio-3.0.1_1
diff --git a/srcpkgs/libopenshot/patches/AV_GET_CODEC_CONTEXT-macro.patch b/srcpkgs/libopenshot/patches/AV_GET_CODEC_CONTEXT-macro.patch
deleted file mode 100644
index e6c640a8e11e..000000000000
--- a/srcpkgs/libopenshot/patches/AV_GET_CODEC_CONTEXT-macro.patch
+++ /dev/null
@@ -1,34 +0,0 @@
---- a/include/FFmpegUtilities.h 2020-03-03 09:00:06.000000000 +0100
-+++ b/include/FFmpegUtilities.h 2020-08-19 17:04:58.535806744 +0200
-@@ -163,11 +163,10 @@
- #define AV_FREE_CONTEXT(av_context) avcodec_free_context(&av_context)
- #define AV_GET_CODEC_TYPE(av_stream) av_stream->codecpar->codec_type
- #define AV_FIND_DECODER_CODEC_ID(av_stream) av_stream->codecpar->codec_id
-- auto AV_GET_CODEC_CONTEXT = [](AVStream* av_stream, AVCodec* av_codec) { \
-- AVCodecContext *context = avcodec_alloc_context3(av_codec); \
-- avcodec_parameters_to_context(context, av_stream->codecpar); \
-- return context; \
-- };
-+ #define AV_GET_CODEC_CONTEXT(av_stream, av_codec) \
-+ ({ AVCodecContext *context = avcodec_alloc_context3(av_codec); \
-+ avcodec_parameters_to_context(context, av_stream->codecpar); \
-+ context; })
- #define AV_GET_CODEC_PAR_CONTEXT(av_stream, av_codec) av_codec;
- #define AV_GET_CODEC_FROM_STREAM(av_stream,codec_in)
- #define AV_GET_CODEC_ATTRIBUTES(av_stream, av_context) av_stream->codecpar
-@@ -199,11 +198,10 @@
- #define AV_FREE_CONTEXT(av_context) avcodec_free_context(&av_context)
- #define AV_GET_CODEC_TYPE(av_stream) av_stream->codecpar->codec_type
- #define AV_FIND_DECODER_CODEC_ID(av_stream) av_stream->codecpar->codec_id
-- auto AV_GET_CODEC_CONTEXT = [](AVStream* av_stream, AVCodec* av_codec) { \
-- AVCodecContext *context = avcodec_alloc_context3(av_codec); \
-- avcodec_parameters_to_context(context, av_stream->codecpar); \
-- return context; \
-- };
-+ #define AV_GET_CODEC_CONTEXT(av_stream, av_codec) \
-+ ({ AVCodecContext *context = avcodec_alloc_context3(av_codec); \
-+ avcodec_parameters_to_context(context, av_stream->codecpar); \
-+ context; })
- #define AV_GET_CODEC_PAR_CONTEXT(av_stream, av_codec) av_codec;
- #define AV_GET_CODEC_FROM_STREAM(av_stream,codec_in)
- #define AV_GET_CODEC_ATTRIBUTES(av_stream, av_context) av_stream->codecpar
diff --git a/srcpkgs/libopenshot/patches/fix-musl.patch b/srcpkgs/libopenshot/patches/fix-musl.patch
new file mode 100644
index 000000000000..67424a848086
--- /dev/null
+++ b/srcpkgs/libopenshot/patches/fix-musl.patch
@@ -0,0 +1,34 @@
+diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
+index bde744d7..d07924fb 100644
+--- a/examples/CMakeLists.txt
++++ b/examples/CMakeLists.txt
+@@ -39,11 +39,19 @@ file(TO_NATIVE_PATH "${PROJECT_SOURCE_DIR}/examples/" TEST_MEDIA_PATH)
+ target_compile_definitions(openshot-example PRIVATE
+ -DTEST_MEDIA_PATH="${TEST_MEDIA_PATH}" )
+
++find_library(EXECINFO_FOUND execinfo)
++
+ # Link test executable to the new library
+ target_link_libraries(openshot-example openshot)
++if(EXECINFO_FOUND)
++ target_link_libraries(openshot-example execinfo)
++endif()
+
+ add_executable(openshot-html-example ExampleHtml.cpp)
+ target_link_libraries(openshot-html-example openshot Qt5::Gui)
++if(EXECINFO_FOUND)
++ target_link_libraries(openshot-html-example execinfo)
++endif()
+
+ ############### PLAYER EXECUTABLE ################
+ # Create test executable
+@@ -53,6 +61,9 @@ set_target_properties(openshot-player PROPERTIES AUTOMOC ON)
+
+ # Link test executable to the new library
+ target_link_libraries(openshot-player openshot)
++if(EXECINFO_FOUND)
++ target_link_libraries(openshot-player execinfo)
++endif()
+
+ ############### TEST BLACKMAGIC CAPTURE APP ################
+ if (BLACKMAGIC_FOUND)
diff --git a/srcpkgs/libopenshot/template b/srcpkgs/libopenshot/template
index b59472105b35..274f08e99251 100644
--- a/srcpkgs/libopenshot/template
+++ b/srcpkgs/libopenshot/template
@@ -1,11 +1,11 @@
# Template file for 'libopenshot'
pkgname=libopenshot
-version=0.2.5
-revision=6
-archs="i686 x86_64 ppc64le"
+version=0.2.7
+revision=1
build_style=cmake
-configure_args="-DENABLE_RUBY=OFF -DUSE_SYSTEM_JSONCPP=ON" # Builds fail with Ruby-2.4.1
-hostmakedepends="swig doxygen ruby python3 pkg-config"
+# Builds fail with Ruby-2.4.1
+configure_args="-DENABLE_RUBY=OFF -DUSE_SYSTEM_JSONCPP=ON"
+hostmakedepends="swig doxygen ruby python3 pkg-config qt5-qmake qt5-host-tools"
makedepends="python3-devel ffmpeg-devel libmagick-devel qt5-devel libgomp-devel
libopenshot-audio-devel qt5-multimedia-devel unittest-cpp zeromq-devel cppzmq
jsoncpp-devel"
@@ -15,10 +15,14 @@ maintainer="Spencer Hill <spencernh77@gmail.com>"
license="LGPL-3.0-or-later"
homepage="https://github.com/OpenShot/libopenshot"
distfiles="https://github.com/OpenShot/libopenshot/archive/v${version}.tar.gz"
-checksum=8ae7d226fbd2efbc84da4f7d9d8c7f3cc9616e4de46e1233e3b0a84ac0a429bc
+checksum=568eab6d69d469c5f745f0e25387ca5e000f7c28be48417b0d7770577ac74a28
# FIXME: tests segfault
make_check=extended
+if [ "$XBPS_TARGET_LIBC" = musl ]; then
+ makedepends+=" libexecinfo-devel"
+fi
+
libopenshot-devel_package() {
short_desc+=" - development files"
pkg_install() {
From b0fed979202b5ff4e389407bd314d7c9c6ad0aff Mon Sep 17 00:00:00 2001
From: tibequadorian <tibequadorian@posteo.de>
Date: Wed, 23 Feb 2022 00:42:51 +0100
Subject: [PATCH 3/3] openshot: update to 2.6.1.
---
.../openshot/patches/0001-video_widget.patch | 74 +
.../patches/0002-python3.10_int.patch | 1993 +++++++++++++++++
srcpkgs/openshot/template | 12 +-
3 files changed, 2073 insertions(+), 6 deletions(-)
create mode 100644 srcpkgs/openshot/patches/0001-video_widget.patch
create mode 100644 srcpkgs/openshot/patches/0002-python3.10_int.patch
diff --git a/srcpkgs/openshot/patches/0001-video_widget.patch b/srcpkgs/openshot/patches/0001-video_widget.patch
new file mode 100644
index 000000000000..5e93d764c4bf
--- /dev/null
+++ b/srcpkgs/openshot/patches/0001-video_widget.patch
@@ -0,0 +1,74 @@
+From 9748a13268d66a5949aebc970637b5903756d018 Mon Sep 17 00:00:00 2001
+From: Jonathan Thomas <jonathan@openshot.org>
+Date: Thu, 7 Oct 2021 13:53:09 -0500
+Subject: [PATCH] Support for previewing anamorphic video profiles, including a
+ few code clean-ups.
+
+---
+ src/windows/video_widget.py | 22 +++++++---------------
+ 1 file changed, 7 insertions(+), 15 deletions(-)
+
+diff --git a/src/windows/video_widget.py b/src/windows/video_widget.py
+index 7326598d3..842deb3ba 100644
+--- a/src/windows/video_widget.py
++++ b/src/windows/video_widget.py
+@@ -77,28 +77,20 @@ def changed(self, action):
+ if action.type == "load" and action.values.get("pixel_ratio"):
+ pixel_ratio_changed = True
+ self.pixel_ratio = openshot.Fraction(
+- action.values.get("pixel_ratio").get("num", 16),
+- action.values.get("pixel_ratio").get("den", 9))
++ action.values.get("pixel_ratio").get("num", 1),
++ action.values.get("pixel_ratio").get("den", 1))
+ log.info(
+ "Set video widget pixel aspect ratio to: %s",
+ self.pixel_ratio.ToFloat())
+ elif action.key and action.key[0] == "pixel_ratio":
+ pixel_ratio_changed = True
+ self.pixel_ratio = openshot.Fraction(
+- action.values.get("num", 16),
+- action.values.get("den", 9))
++ action.values.get("num", 1),
++ action.values.get("den", 1))
+ log.info(
+ "Update: Set video widget pixel aspect ratio to: %s",
+ self.pixel_ratio.ToFloat())
+
+- # Update max size (to size of video preview viewport)
+- if display_ratio_changed or pixel_ratio_changed:
+- timeline = get_app().window.timeline_sync.timeline
+- timeline.SetMaxSize(
+- round(self.width() * self.pixel_ratio.ToFloat()),
+- round(self.height() * 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):
+@@ -236,7 +228,7 @@ 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)
++ source_size = QSize(source_width, source_height * self.pixel_ratio.Reciprocal().ToDouble())
+
+ # Determine scale of clip
+ scale = self.transforming_clip.data['scale']
+@@ -405,7 +397,7 @@ def paintEvent(self, event, *args):
+ self.mutex.unlock()
+
+ def centeredViewport(self, width, height):
+- """ Calculate size of viewport to maintain apsect ratio """
++ """ Calculate size of viewport to maintain aspect ratio """
+
+ # Calculate padding
+ top_padding = (height - (height * self.zoom)) / 2.0
+@@ -416,7 +408,7 @@ def centeredViewport(self, width, height):
+ height = height * self.zoom
+
+ # Calculate which direction to scale (for perfect centering)
+- aspectRatio = self.aspect_ratio.ToFloat() * self.pixel_ratio.ToFloat()
++ aspectRatio = self.aspect_ratio.ToFloat()
+ heightFromWidth = width / aspectRatio
+ widthFromHeight = height * aspectRatio
+
diff --git a/srcpkgs/openshot/patches/0002-python3.10_int.patch b/srcpkgs/openshot/patches/0002-python3.10_int.patch
new file mode 100644
index 000000000000..8f6dc53dbf40
--- /dev/null
+++ b/srcpkgs/openshot/patches/0002-python3.10_int.patch
@@ -0,0 +1,1993 @@
+From a3088503500e79877ce985e4784f75478d9b792e Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@gmail.com>
+Date: Mon, 20 Sep 2021 05:36:19 -0400
+Subject: [PATCH 1/6] Preferences: Fix logging calls
+
+---
+ src/launch.py | 2 +-
+ src/windows/preferences.py | 18 +++++++++++-------
+ 2 files changed, 12 insertions(+), 8 deletions(-)
+
+diff --git a/src/launch.py b/src/launch.py
+index 9fc600b23..b614e9bc7 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
+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 1b14896d2057df80b0b20ba22e1380ba9e9bd6e6 Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@gmail.com>
+Date: Thu, 4 Nov 2021 21:01:39 -0400
+Subject: [PATCH 2/6] 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 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 9fc55120f36c36b2b6b67e499128993c25e535cf Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@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 1f058f730bb36068bce5c645ecea45b98279b614 Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@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 7df87ddc189c5e5031fbb19df13378428fab951e Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@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 33cf68ca0b1ea57edd5dec3dbb8ba06d6a3f8fa4 Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@gmail.com>
+Date: Thu, 25 Nov 2021 02:18:32 -0500
+Subject: [PATCH 6/6] properties_model: Fix bad logging call, Codacy flags
+
+---
+ src/windows/models/properties_model.py | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/src/windows/models/properties_model.py b/src/windows/models/properties_model.py
+index 40897f642..695e9f39c 100644
+--- a/src/windows/models/properties_model.py
++++ b/src/windows/models/properties_model.py
+@@ -444,7 +444,7 @@ def value_updated(self, item, interpolation=-1, value=None, interpolation_detail
+ log.debug("%s: update property %s. %s", log_id, property_key, clip_data.get(property_key))
+
+ # Check the type of property (some are keyframe, and some are not)
+- if property_type != "reader" and type(clip_data[property_key]) == dict:
++ if property_type != "reader" and isinstance(clip_data[property_key], dict):
+ # Keyframe
+ # Loop through points, find a matching points on this frame
+ found_point = False
+@@ -517,21 +517,21 @@ def value_updated(self, item, interpolation=-1, value=None, interpolation_detail
+ clip_updated = True
+ try:
+ clip_data[property_key] = int(new_value)
+- except Exception as ex:
++ except Exception:
+ 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:
++ except Exception:
+ 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:
++ except Exception:
+ log.warn('Invalid Boolean value passed to property', exc_info=1)
+
+ elif property_type == "string":
+@@ -558,7 +558,7 @@ def value_updated(self, item, interpolation=-1, value=None, interpolation_detail
+ clip_object.Close()
+ clip_object = None
+ except Exception:
+- log.warn('Invalid Reader value passed to property: %s (%s)', value, exc_info=1)
++ log.warn('Invalid Reader value passed to property: %s', value, exc_info=1)
+
+ # Reduce # of clip properties we are saving (performance boost)
+ clip_data = {property_key: clip_data.get(property_key)}
diff --git a/srcpkgs/openshot/template b/srcpkgs/openshot/template
index d16ea925d223..4248baa81e39 100644
--- a/srcpkgs/openshot/template
+++ b/srcpkgs/openshot/template
@@ -1,12 +1,11 @@
# Template file for 'openshot'
pkgname=openshot
-version=2.5.1
-revision=3
-archs="i686 x86_64 ppc64le"
+version=2.6.1
+revision=1
wrksrc="${pkgname}-qt-${version}"
build_style=python3-module
-hostmakedepends="python3"
-makedepends="ffmpeg-devel python3-PyQt5 python3-setuptools"
+hostmakedepends="python3 python3-setuptools"
+makedepends="ffmpeg-devel python3-PyQt5"
depends="ImageMagick libopenshot mlt python3-PyQt5-svg
python3-PyQt5-webkit python3-httplib2 python3-pyzmq python3-requests"
short_desc="Open-source, non-linear video editor for Linux"
@@ -14,4 +13,5 @@ maintainer="Spencer Hill <spencernh77@gmail.com>"
license="GPL-3.0-or-later"
homepage="https://www.openshot.org"
distfiles="https://github.com/OpenShot/openshot-qt/archive/v${version}.tar.gz"
-checksum=4c25eb9a5de42e749de4c6ca2f7a313c60e1283fe52d70c121629dbb8bb2df7b
+checksum=11651d5e7287da3c766ce6b447aef8da5cdadaf5626a2816e9025c831d0e1a66
+make_check=no # tests are broken
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PR PATCH] [Updated] openshot: update to 2.6.1.
2022-02-23 0:01 [PR PATCH] openshot: update to 2.6.1 tibequadorian
@ 2022-02-23 0:12 ` tibequadorian
2022-02-24 14:47 ` tibequadorian
2022-02-25 23:52 ` [PR PATCH] [Merged]: " Piraty
2 siblings, 0 replies; 4+ messages in thread
From: tibequadorian @ 2022-02-23 0:12 UTC (permalink / raw)
To: ml
[-- Attachment #1: Type: text/plain, Size: 1313 bytes --]
There is an updated pull request by tibequadorian against master on the void-packages repository
https://github.com/tibequadorian/void-packages openshot
https://github.com/void-linux/void-packages/pull/35798
openshot: update to 2.6.1.
Patches for openshot are already upstream so maybe we should wait for 2.6.2.
<!-- Uncomment relevant sections and delete options which are not applicable -->
#### Testing the changes
- I tested the changes in this PR: **briefly**
<!--
#### New package
- This new package conforms to the [quality requirements](https://github.com/void-linux/void-packages/blob/master/Manual.md#quality-requirements): **YES**|**NO**
-->
<!-- Note: If the build is likely to take more than 2 hours, please [skip CI](https://github.com/void-linux/void-packages/blob/master/CONTRIBUTING.md#continuous-integration)
and test at least one native build and, if supported, at least one cross build.
Ignore this section if this PR is not skipping CI.
-->
<!--
#### Local build testing
- I built this PR locally for my native architecture, (ARCH-LIBC)
- I built this PR locally for these architectures (if supported. mark crossbuilds):
- aarch64-musl
- armv7l
- armv6l-musl
-->
A patch file from https://github.com/void-linux/void-packages/pull/35798.patch is attached
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-openshot-35798.patch --]
[-- Type: text/x-diff, Size: 124106 bytes --]
From c3c3357aca2851de9d12303413ddcdf1d8a12225 Mon Sep 17 00:00:00 2001
From: tibequadorian <tibequadorian@posteo.de>
Date: Wed, 23 Feb 2022 00:41:38 +0100
Subject: [PATCH 1/3] libopenshot-audio: update to 0.2.2.
---
common/shlibs | 2 +-
.../libopenshot-audio/patches/fix-musl.patch | 86 ++++++++++---------
srcpkgs/libopenshot-audio/template | 7 +-
3 files changed, 51 insertions(+), 44 deletions(-)
diff --git a/common/shlibs b/common/shlibs
index 32befc92d641..c66b4fbe6700 100644
--- a/common/shlibs
+++ b/common/shlibs
@@ -2597,7 +2597,7 @@ libax25io.so.0 libax25-0.0.12rc4_1
libmill.so.18 libmill-1.14_1
libges-1.0.so.0 gst1-editing-services-1.6.2_1
libykneomgr.so.0 libykneomgr-0.1.8_1
-libopenshot-audio.so.7 libopenshot-audio-0.2.0_1
+libopenshot-audio.so.8 libopenshot-audio-0.2.2_1
libopenshot.so.19 libopenshot-0.2.5_3
libpqxx-6.3.so libpqxx-6.3.3_1
libndpi.so.3 ndpi-3.4_1
diff --git a/srcpkgs/libopenshot-audio/patches/fix-musl.patch b/srcpkgs/libopenshot-audio/patches/fix-musl.patch
index b5f178d92d8a..532248c86217 100644
--- a/srcpkgs/libopenshot-audio/patches/fix-musl.patch
+++ b/srcpkgs/libopenshot-audio/patches/fix-musl.patch
@@ -1,40 +1,46 @@
---- a/JuceLibraryCode/modules/juce_core/juce_core.cpp 2016-08-30 06:24:27.000000000 +0200
-+++ b/JuceLibraryCode/modules/juce_core/juce_core.cpp 2016-12-12 14:53:23.532613378 +0100
-@@ -97,7 +97,7 @@
- #include <net/if.h>
- #include <sys/ioctl.h>
-
-- #if ! JUCE_ANDROID
-+ #if ! JUCE_ANDROID && defined(__GLIBC__)
- #include <execinfo.h>
- #endif
- #endif
---- a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp 2016-08-30 06:24:27.000000000 +0200
-+++ b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp 2016-12-12 14:58:35.988986030 +0100
-@@ -134,6 +134,8 @@
- }
- }
-
-+ #elif !defined(__GLIBC__)
-+ jassertfalse; // sorry, not implemented yet!
- #else
- void* stack[128];
- int frames = backtrace (stack, numElementsInArray (stack));
---- a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp 2016-08-30 06:24:27.000000000 +0200
-+++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp 2016-12-12 15:07:35.046607788 +0100
-@@ -142,8 +142,15 @@
- return result;
- }
-
-+#if defined(__GLIBC__)
- String SystemStats::getUserLanguage() { return getLocaleValue (_NL_IDENTIFICATION_LANGUAGE); }
- String SystemStats::getUserRegion() { return getLocaleValue (_NL_IDENTIFICATION_TERRITORY); }
-+#else
-+// The identifiers _NL_INDENTIFICATION_LANGUAGE and _TERRIRTORY are not defined in musl libc.
-+// TODO: Find a better fix than just returning nonsense. Inspect env("LANG") perhaps?
-+String SystemStats::getUserLanguage() { return String("en"); }
-+String SystemStats::getUserRegion() { return String("US"); }
-+#endif
- String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getUserRegion(); }
-
- //==============================================================================
+diff --git a/JuceLibraryCode/modules/juce_core/juce_core.cpp b/JuceLibraryCode/modules/juce_core/juce_core.cpp
+index 8bac812..e23b422 100644
+--- a/JuceLibraryCode/modules/juce_core/juce_core.cpp
++++ b/JuceLibraryCode/modules/juce_core/juce_core.cpp
+@@ -92,7 +92,7 @@
+ #include <net/if.h>
+ #include <sys/ioctl.h>
+
+- #if ! JUCE_ANDROID
++ #if ! JUCE_ANDROID && defined(__GLIBC__)
+ #include <execinfo.h>
+ #endif
+ #endif
+diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp
+index 2d7faa3..f132405 100644
+--- a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp
++++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp
+@@ -139,8 +139,15 @@ static String getLocaleValue (nl_item key)
+ return result;
+ }
+
++#if defined(__GLIBC__)
+ String SystemStats::getUserLanguage() { return getLocaleValue (_NL_IDENTIFICATION_LANGUAGE); }
+ String SystemStats::getUserRegion() { return getLocaleValue (_NL_IDENTIFICATION_TERRITORY); }
++#else
++// The identifiers _NL_INDENTIFICATION_LANGUAGE and _TERRIRTORY are not defined in musl libc.
++// TODO: Find a better fix than just returning nonsense. Inspect env("LANG") perhaps?
++String SystemStats::getUserLanguage() { return String("en"); }
++String SystemStats::getUserRegion() { return String("US"); }
++#endif
+ String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getUserRegion(); }
+
+ //==============================================================================
+diff --git a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp
+index 757ea24..6b61e16 100644
+--- a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp
++++ b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp
+@@ -138,7 +138,7 @@ String SystemStats::getStackBacktrace()
+ {
+ String result;
+
+- #if JUCE_ANDROID || JUCE_MINGW
++ #if JUCE_ANDROID || JUCE_MINGW || !defined(__GLIBC__)
+ jassertfalse; // sorry, not implemented yet!
+
+ #elif JUCE_WINDOWS
diff --git a/srcpkgs/libopenshot-audio/template b/srcpkgs/libopenshot-audio/template
index 56c330eafcf1..254f8b6283bd 100644
--- a/srcpkgs/libopenshot-audio/template
+++ b/srcpkgs/libopenshot-audio/template
@@ -1,6 +1,6 @@
# Template file for 'libopenshot-audio'
pkgname=libopenshot-audio
-version=0.2.0
+version=0.2.2
revision=1
build_style=cmake
hostmakedepends="doxygen"
@@ -11,7 +11,7 @@ maintainer="Spencer Hill <spencernh77@gmail.com>"
license="GPL-3.0-or-later"
homepage="https://github.com/OpenShot/libopenshot-audio"
distfiles="https://github.com/OpenShot/libopenshot-audio/archive/v${version}.tar.gz"
-checksum=937ff4f1c2dfb8ab5d56ad85beacaa29dfd5a79af0d9cf647386034fe9882309
+checksum=66bedfda0d8d430598b21bc2dde6c0016a758a6c83467d0273a9d692de10baaf
if [ "$XBPS_TARGET_NO_ATOMIC8" ]; then
makedepends+=" libatomic-devel"
@@ -20,9 +20,10 @@ fi
libopenshot-audio-devel_package() {
short_desc+=" - development files"
- depends+=" ${sourcepkg}>=${version}_${revision}"
+ depends+=" ${sourcepkg}>=${version}_${revision} alsa-lib-devel zlib-devel"
pkg_install() {
vmove usr/include
+ vmove usr/lib/cmake
vmove "usr/lib/*.so"
}
}
From bb61a44b5ece18e2805a5b6e614d55ce0a9866ca Mon Sep 17 00:00:00 2001
From: tibequadorian <tibequadorian@posteo.de>
Date: Wed, 23 Feb 2022 00:42:37 +0100
Subject: [PATCH 2/3] libopenshot: update to 0.2.7.
enable for all archs and libc
---
common/shlibs | 2 +-
.../patches/AV_GET_CODEC_CONTEXT-macro.patch | 34 -------------------
srcpkgs/libopenshot/patches/fix-musl.patch | 34 +++++++++++++++++++
srcpkgs/libopenshot/template | 16 +++++----
4 files changed, 45 insertions(+), 41 deletions(-)
delete mode 100644 srcpkgs/libopenshot/patches/AV_GET_CODEC_CONTEXT-macro.patch
create mode 100644 srcpkgs/libopenshot/patches/fix-musl.patch
diff --git a/common/shlibs b/common/shlibs
index c66b4fbe6700..d339115dda39 100644
--- a/common/shlibs
+++ b/common/shlibs
@@ -2598,7 +2598,7 @@ libmill.so.18 libmill-1.14_1
libges-1.0.so.0 gst1-editing-services-1.6.2_1
libykneomgr.so.0 libykneomgr-0.1.8_1
libopenshot-audio.so.8 libopenshot-audio-0.2.2_1
-libopenshot.so.19 libopenshot-0.2.5_3
+libopenshot.so.21 libopenshot-0.2.7_1
libpqxx-6.3.so libpqxx-6.3.3_1
libndpi.so.3 ndpi-3.4_1
liblog.so android-studio-3.0.1_1
diff --git a/srcpkgs/libopenshot/patches/AV_GET_CODEC_CONTEXT-macro.patch b/srcpkgs/libopenshot/patches/AV_GET_CODEC_CONTEXT-macro.patch
deleted file mode 100644
index e6c640a8e11e..000000000000
--- a/srcpkgs/libopenshot/patches/AV_GET_CODEC_CONTEXT-macro.patch
+++ /dev/null
@@ -1,34 +0,0 @@
---- a/include/FFmpegUtilities.h 2020-03-03 09:00:06.000000000 +0100
-+++ b/include/FFmpegUtilities.h 2020-08-19 17:04:58.535806744 +0200
-@@ -163,11 +163,10 @@
- #define AV_FREE_CONTEXT(av_context) avcodec_free_context(&av_context)
- #define AV_GET_CODEC_TYPE(av_stream) av_stream->codecpar->codec_type
- #define AV_FIND_DECODER_CODEC_ID(av_stream) av_stream->codecpar->codec_id
-- auto AV_GET_CODEC_CONTEXT = [](AVStream* av_stream, AVCodec* av_codec) { \
-- AVCodecContext *context = avcodec_alloc_context3(av_codec); \
-- avcodec_parameters_to_context(context, av_stream->codecpar); \
-- return context; \
-- };
-+ #define AV_GET_CODEC_CONTEXT(av_stream, av_codec) \
-+ ({ AVCodecContext *context = avcodec_alloc_context3(av_codec); \
-+ avcodec_parameters_to_context(context, av_stream->codecpar); \
-+ context; })
- #define AV_GET_CODEC_PAR_CONTEXT(av_stream, av_codec) av_codec;
- #define AV_GET_CODEC_FROM_STREAM(av_stream,codec_in)
- #define AV_GET_CODEC_ATTRIBUTES(av_stream, av_context) av_stream->codecpar
-@@ -199,11 +198,10 @@
- #define AV_FREE_CONTEXT(av_context) avcodec_free_context(&av_context)
- #define AV_GET_CODEC_TYPE(av_stream) av_stream->codecpar->codec_type
- #define AV_FIND_DECODER_CODEC_ID(av_stream) av_stream->codecpar->codec_id
-- auto AV_GET_CODEC_CONTEXT = [](AVStream* av_stream, AVCodec* av_codec) { \
-- AVCodecContext *context = avcodec_alloc_context3(av_codec); \
-- avcodec_parameters_to_context(context, av_stream->codecpar); \
-- return context; \
-- };
-+ #define AV_GET_CODEC_CONTEXT(av_stream, av_codec) \
-+ ({ AVCodecContext *context = avcodec_alloc_context3(av_codec); \
-+ avcodec_parameters_to_context(context, av_stream->codecpar); \
-+ context; })
- #define AV_GET_CODEC_PAR_CONTEXT(av_stream, av_codec) av_codec;
- #define AV_GET_CODEC_FROM_STREAM(av_stream,codec_in)
- #define AV_GET_CODEC_ATTRIBUTES(av_stream, av_context) av_stream->codecpar
diff --git a/srcpkgs/libopenshot/patches/fix-musl.patch b/srcpkgs/libopenshot/patches/fix-musl.patch
new file mode 100644
index 000000000000..67424a848086
--- /dev/null
+++ b/srcpkgs/libopenshot/patches/fix-musl.patch
@@ -0,0 +1,34 @@
+diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
+index bde744d7..d07924fb 100644
+--- a/examples/CMakeLists.txt
++++ b/examples/CMakeLists.txt
+@@ -39,11 +39,19 @@ file(TO_NATIVE_PATH "${PROJECT_SOURCE_DIR}/examples/" TEST_MEDIA_PATH)
+ target_compile_definitions(openshot-example PRIVATE
+ -DTEST_MEDIA_PATH="${TEST_MEDIA_PATH}" )
+
++find_library(EXECINFO_FOUND execinfo)
++
+ # Link test executable to the new library
+ target_link_libraries(openshot-example openshot)
++if(EXECINFO_FOUND)
++ target_link_libraries(openshot-example execinfo)
++endif()
+
+ add_executable(openshot-html-example ExampleHtml.cpp)
+ target_link_libraries(openshot-html-example openshot Qt5::Gui)
++if(EXECINFO_FOUND)
++ target_link_libraries(openshot-html-example execinfo)
++endif()
+
+ ############### PLAYER EXECUTABLE ################
+ # Create test executable
+@@ -53,6 +61,9 @@ set_target_properties(openshot-player PROPERTIES AUTOMOC ON)
+
+ # Link test executable to the new library
+ target_link_libraries(openshot-player openshot)
++if(EXECINFO_FOUND)
++ target_link_libraries(openshot-player execinfo)
++endif()
+
+ ############### TEST BLACKMAGIC CAPTURE APP ################
+ if (BLACKMAGIC_FOUND)
diff --git a/srcpkgs/libopenshot/template b/srcpkgs/libopenshot/template
index b59472105b35..274f08e99251 100644
--- a/srcpkgs/libopenshot/template
+++ b/srcpkgs/libopenshot/template
@@ -1,11 +1,11 @@
# Template file for 'libopenshot'
pkgname=libopenshot
-version=0.2.5
-revision=6
-archs="i686 x86_64 ppc64le"
+version=0.2.7
+revision=1
build_style=cmake
-configure_args="-DENABLE_RUBY=OFF -DUSE_SYSTEM_JSONCPP=ON" # Builds fail with Ruby-2.4.1
-hostmakedepends="swig doxygen ruby python3 pkg-config"
+# Builds fail with Ruby-2.4.1
+configure_args="-DENABLE_RUBY=OFF -DUSE_SYSTEM_JSONCPP=ON"
+hostmakedepends="swig doxygen ruby python3 pkg-config qt5-qmake qt5-host-tools"
makedepends="python3-devel ffmpeg-devel libmagick-devel qt5-devel libgomp-devel
libopenshot-audio-devel qt5-multimedia-devel unittest-cpp zeromq-devel cppzmq
jsoncpp-devel"
@@ -15,10 +15,14 @@ maintainer="Spencer Hill <spencernh77@gmail.com>"
license="LGPL-3.0-or-later"
homepage="https://github.com/OpenShot/libopenshot"
distfiles="https://github.com/OpenShot/libopenshot/archive/v${version}.tar.gz"
-checksum=8ae7d226fbd2efbc84da4f7d9d8c7f3cc9616e4de46e1233e3b0a84ac0a429bc
+checksum=568eab6d69d469c5f745f0e25387ca5e000f7c28be48417b0d7770577ac74a28
# FIXME: tests segfault
make_check=extended
+if [ "$XBPS_TARGET_LIBC" = musl ]; then
+ makedepends+=" libexecinfo-devel"
+fi
+
libopenshot-devel_package() {
short_desc+=" - development files"
pkg_install() {
From 1224d38663b0ee5f27a2ed7a98148f26ae56f102 Mon Sep 17 00:00:00 2001
From: tibequadorian <tibequadorian@posteo.de>
Date: Wed, 23 Feb 2022 00:42:51 +0100
Subject: [PATCH 3/3] openshot: update to 2.6.1.
enable for all archs and libc
patches are upstream
---
.../openshot/patches/0001-video_widget.patch | 74 +
.../patches/0002-python3.10_int.patch | 1993 +++++++++++++++++
srcpkgs/openshot/template | 12 +-
3 files changed, 2073 insertions(+), 6 deletions(-)
create mode 100644 srcpkgs/openshot/patches/0001-video_widget.patch
create mode 100644 srcpkgs/openshot/patches/0002-python3.10_int.patch
diff --git a/srcpkgs/openshot/patches/0001-video_widget.patch b/srcpkgs/openshot/patches/0001-video_widget.patch
new file mode 100644
index 000000000000..5e93d764c4bf
--- /dev/null
+++ b/srcpkgs/openshot/patches/0001-video_widget.patch
@@ -0,0 +1,74 @@
+From 9748a13268d66a5949aebc970637b5903756d018 Mon Sep 17 00:00:00 2001
+From: Jonathan Thomas <jonathan@openshot.org>
+Date: Thu, 7 Oct 2021 13:53:09 -0500
+Subject: [PATCH] Support for previewing anamorphic video profiles, including a
+ few code clean-ups.
+
+---
+ src/windows/video_widget.py | 22 +++++++---------------
+ 1 file changed, 7 insertions(+), 15 deletions(-)
+
+diff --git a/src/windows/video_widget.py b/src/windows/video_widget.py
+index 7326598d3..842deb3ba 100644
+--- a/src/windows/video_widget.py
++++ b/src/windows/video_widget.py
+@@ -77,28 +77,20 @@ def changed(self, action):
+ if action.type == "load" and action.values.get("pixel_ratio"):
+ pixel_ratio_changed = True
+ self.pixel_ratio = openshot.Fraction(
+- action.values.get("pixel_ratio").get("num", 16),
+- action.values.get("pixel_ratio").get("den", 9))
++ action.values.get("pixel_ratio").get("num", 1),
++ action.values.get("pixel_ratio").get("den", 1))
+ log.info(
+ "Set video widget pixel aspect ratio to: %s",
+ self.pixel_ratio.ToFloat())
+ elif action.key and action.key[0] == "pixel_ratio":
+ pixel_ratio_changed = True
+ self.pixel_ratio = openshot.Fraction(
+- action.values.get("num", 16),
+- action.values.get("den", 9))
++ action.values.get("num", 1),
++ action.values.get("den", 1))
+ log.info(
+ "Update: Set video widget pixel aspect ratio to: %s",
+ self.pixel_ratio.ToFloat())
+
+- # Update max size (to size of video preview viewport)
+- if display_ratio_changed or pixel_ratio_changed:
+- timeline = get_app().window.timeline_sync.timeline
+- timeline.SetMaxSize(
+- round(self.width() * self.pixel_ratio.ToFloat()),
+- round(self.height() * 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):
+@@ -236,7 +228,7 @@ 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)
++ source_size = QSize(source_width, source_height * self.pixel_ratio.Reciprocal().ToDouble())
+
+ # Determine scale of clip
+ scale = self.transforming_clip.data['scale']
+@@ -405,7 +397,7 @@ def paintEvent(self, event, *args):
+ self.mutex.unlock()
+
+ def centeredViewport(self, width, height):
+- """ Calculate size of viewport to maintain apsect ratio """
++ """ Calculate size of viewport to maintain aspect ratio """
+
+ # Calculate padding
+ top_padding = (height - (height * self.zoom)) / 2.0
+@@ -416,7 +408,7 @@ def centeredViewport(self, width, height):
+ height = height * self.zoom
+
+ # Calculate which direction to scale (for perfect centering)
+- aspectRatio = self.aspect_ratio.ToFloat() * self.pixel_ratio.ToFloat()
++ aspectRatio = self.aspect_ratio.ToFloat()
+ heightFromWidth = width / aspectRatio
+ widthFromHeight = height * aspectRatio
+
diff --git a/srcpkgs/openshot/patches/0002-python3.10_int.patch b/srcpkgs/openshot/patches/0002-python3.10_int.patch
new file mode 100644
index 000000000000..8f6dc53dbf40
--- /dev/null
+++ b/srcpkgs/openshot/patches/0002-python3.10_int.patch
@@ -0,0 +1,1993 @@
+From a3088503500e79877ce985e4784f75478d9b792e Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@gmail.com>
+Date: Mon, 20 Sep 2021 05:36:19 -0400
+Subject: [PATCH 1/6] Preferences: Fix logging calls
+
+---
+ src/launch.py | 2 +-
+ src/windows/preferences.py | 18 +++++++++++-------
+ 2 files changed, 12 insertions(+), 8 deletions(-)
+
+diff --git a/src/launch.py b/src/launch.py
+index 9fc600b23..b614e9bc7 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
+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 1b14896d2057df80b0b20ba22e1380ba9e9bd6e6 Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@gmail.com>
+Date: Thu, 4 Nov 2021 21:01:39 -0400
+Subject: [PATCH 2/6] 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 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 9fc55120f36c36b2b6b67e499128993c25e535cf Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@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 1f058f730bb36068bce5c645ecea45b98279b614 Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@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 7df87ddc189c5e5031fbb19df13378428fab951e Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@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 33cf68ca0b1ea57edd5dec3dbb8ba06d6a3f8fa4 Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@gmail.com>
+Date: Thu, 25 Nov 2021 02:18:32 -0500
+Subject: [PATCH 6/6] properties_model: Fix bad logging call, Codacy flags
+
+---
+ src/windows/models/properties_model.py | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/src/windows/models/properties_model.py b/src/windows/models/properties_model.py
+index 40897f642..695e9f39c 100644
+--- a/src/windows/models/properties_model.py
++++ b/src/windows/models/properties_model.py
+@@ -444,7 +444,7 @@ def value_updated(self, item, interpolation=-1, value=None, interpolation_detail
+ log.debug("%s: update property %s. %s", log_id, property_key, clip_data.get(property_key))
+
+ # Check the type of property (some are keyframe, and some are not)
+- if property_type != "reader" and type(clip_data[property_key]) == dict:
++ if property_type != "reader" and isinstance(clip_data[property_key], dict):
+ # Keyframe
+ # Loop through points, find a matching points on this frame
+ found_point = False
+@@ -517,21 +517,21 @@ def value_updated(self, item, interpolation=-1, value=None, interpolation_detail
+ clip_updated = True
+ try:
+ clip_data[property_key] = int(new_value)
+- except Exception as ex:
++ except Exception:
+ 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:
++ except Exception:
+ 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:
++ except Exception:
+ log.warn('Invalid Boolean value passed to property', exc_info=1)
+
+ elif property_type == "string":
+@@ -558,7 +558,7 @@ def value_updated(self, item, interpolation=-1, value=None, interpolation_detail
+ clip_object.Close()
+ clip_object = None
+ except Exception:
+- log.warn('Invalid Reader value passed to property: %s (%s)', value, exc_info=1)
++ log.warn('Invalid Reader value passed to property: %s', value, exc_info=1)
+
+ # Reduce # of clip properties we are saving (performance boost)
+ clip_data = {property_key: clip_data.get(property_key)}
diff --git a/srcpkgs/openshot/template b/srcpkgs/openshot/template
index d16ea925d223..4248baa81e39 100644
--- a/srcpkgs/openshot/template
+++ b/srcpkgs/openshot/template
@@ -1,12 +1,11 @@
# Template file for 'openshot'
pkgname=openshot
-version=2.5.1
-revision=3
-archs="i686 x86_64 ppc64le"
+version=2.6.1
+revision=1
wrksrc="${pkgname}-qt-${version}"
build_style=python3-module
-hostmakedepends="python3"
-makedepends="ffmpeg-devel python3-PyQt5 python3-setuptools"
+hostmakedepends="python3 python3-setuptools"
+makedepends="ffmpeg-devel python3-PyQt5"
depends="ImageMagick libopenshot mlt python3-PyQt5-svg
python3-PyQt5-webkit python3-httplib2 python3-pyzmq python3-requests"
short_desc="Open-source, non-linear video editor for Linux"
@@ -14,4 +13,5 @@ maintainer="Spencer Hill <spencernh77@gmail.com>"
license="GPL-3.0-or-later"
homepage="https://www.openshot.org"
distfiles="https://github.com/OpenShot/openshot-qt/archive/v${version}.tar.gz"
-checksum=4c25eb9a5de42e749de4c6ca2f7a313c60e1283fe52d70c121629dbb8bb2df7b
+checksum=11651d5e7287da3c766ce6b447aef8da5cdadaf5626a2816e9025c831d0e1a66
+make_check=no # tests are broken
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PR PATCH] [Updated] openshot: update to 2.6.1.
2022-02-23 0:01 [PR PATCH] openshot: update to 2.6.1 tibequadorian
2022-02-23 0:12 ` [PR PATCH] [Updated] " tibequadorian
@ 2022-02-24 14:47 ` tibequadorian
2022-02-25 23:52 ` [PR PATCH] [Merged]: " Piraty
2 siblings, 0 replies; 4+ messages in thread
From: tibequadorian @ 2022-02-24 14:47 UTC (permalink / raw)
To: ml
[-- Attachment #1: Type: text/plain, Size: 1481 bytes --]
There is an updated pull request by tibequadorian against master on the void-packages repository
https://github.com/tibequadorian/void-packages openshot
https://github.com/void-linux/void-packages/pull/35798
openshot: update to 2.6.1.
Closes #33897
Patches for openshot are already upstream so maybe we should wait for 2.6.2.
<!-- Uncomment relevant sections and delete options which are not applicable -->
#### Testing the changes
- I tested the changes in this PR: **briefly**
Removed the restriction on arch or libc so help for testing on musl and ARM is appreciated.
- [x] aarch64 glibc with wayland is confirmed to work.
<!--
#### New package
- This new package conforms to the [quality requirements](https://github.com/void-linux/void-packages/blob/master/Manual.md#quality-requirements): **YES**|**NO**
-->
<!-- Note: If the build is likely to take more than 2 hours, please [skip CI](https://github.com/void-linux/void-packages/blob/master/CONTRIBUTING.md#continuous-integration)
and test at least one native build and, if supported, at least one cross build.
Ignore this section if this PR is not skipping CI.
-->
<!--
#### Local build testing
- I built this PR locally for my native architecture, (ARCH-LIBC)
- I built this PR locally for these architectures (if supported. mark crossbuilds):
- aarch64-musl
- armv7l
- armv6l-musl
-->
A patch file from https://github.com/void-linux/void-packages/pull/35798.patch is attached
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-openshot-35798.patch --]
[-- Type: text/x-diff, Size: 122522 bytes --]
From 7e91c46c332e83f0b89e2c97701890765f2fafb5 Mon Sep 17 00:00:00 2001
From: tibequadorian <tibequadorian@posteo.de>
Date: Wed, 23 Feb 2022 00:41:38 +0100
Subject: [PATCH 1/3] libopenshot-audio: update to 0.2.2.
---
common/shlibs | 2 +-
.../libopenshot-audio/patches/fix-musl.patch | 86 ++++++++++---------
srcpkgs/libopenshot-audio/template | 7 +-
3 files changed, 51 insertions(+), 44 deletions(-)
diff --git a/common/shlibs b/common/shlibs
index 019618205973..8e2feccbee06 100644
--- a/common/shlibs
+++ b/common/shlibs
@@ -2597,7 +2597,7 @@ libax25io.so.0 libax25-0.0.12rc4_1
libmill.so.18 libmill-1.14_1
libges-1.0.so.0 gst1-editing-services-1.6.2_1
libykneomgr.so.0 libykneomgr-0.1.8_1
-libopenshot-audio.so.7 libopenshot-audio-0.2.0_1
+libopenshot-audio.so.8 libopenshot-audio-0.2.2_1
libopenshot.so.19 libopenshot-0.2.5_3
libpqxx-6.3.so libpqxx-6.3.3_1
libndpi.so.3 ndpi-3.4_1
diff --git a/srcpkgs/libopenshot-audio/patches/fix-musl.patch b/srcpkgs/libopenshot-audio/patches/fix-musl.patch
index b5f178d92d8a..532248c86217 100644
--- a/srcpkgs/libopenshot-audio/patches/fix-musl.patch
+++ b/srcpkgs/libopenshot-audio/patches/fix-musl.patch
@@ -1,40 +1,46 @@
---- a/JuceLibraryCode/modules/juce_core/juce_core.cpp 2016-08-30 06:24:27.000000000 +0200
-+++ b/JuceLibraryCode/modules/juce_core/juce_core.cpp 2016-12-12 14:53:23.532613378 +0100
-@@ -97,7 +97,7 @@
- #include <net/if.h>
- #include <sys/ioctl.h>
-
-- #if ! JUCE_ANDROID
-+ #if ! JUCE_ANDROID && defined(__GLIBC__)
- #include <execinfo.h>
- #endif
- #endif
---- a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp 2016-08-30 06:24:27.000000000 +0200
-+++ b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp 2016-12-12 14:58:35.988986030 +0100
-@@ -134,6 +134,8 @@
- }
- }
-
-+ #elif !defined(__GLIBC__)
-+ jassertfalse; // sorry, not implemented yet!
- #else
- void* stack[128];
- int frames = backtrace (stack, numElementsInArray (stack));
---- a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp 2016-08-30 06:24:27.000000000 +0200
-+++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp 2016-12-12 15:07:35.046607788 +0100
-@@ -142,8 +142,15 @@
- return result;
- }
-
-+#if defined(__GLIBC__)
- String SystemStats::getUserLanguage() { return getLocaleValue (_NL_IDENTIFICATION_LANGUAGE); }
- String SystemStats::getUserRegion() { return getLocaleValue (_NL_IDENTIFICATION_TERRITORY); }
-+#else
-+// The identifiers _NL_INDENTIFICATION_LANGUAGE and _TERRIRTORY are not defined in musl libc.
-+// TODO: Find a better fix than just returning nonsense. Inspect env("LANG") perhaps?
-+String SystemStats::getUserLanguage() { return String("en"); }
-+String SystemStats::getUserRegion() { return String("US"); }
-+#endif
- String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getUserRegion(); }
-
- //==============================================================================
+diff --git a/JuceLibraryCode/modules/juce_core/juce_core.cpp b/JuceLibraryCode/modules/juce_core/juce_core.cpp
+index 8bac812..e23b422 100644
+--- a/JuceLibraryCode/modules/juce_core/juce_core.cpp
++++ b/JuceLibraryCode/modules/juce_core/juce_core.cpp
+@@ -92,7 +92,7 @@
+ #include <net/if.h>
+ #include <sys/ioctl.h>
+
+- #if ! JUCE_ANDROID
++ #if ! JUCE_ANDROID && defined(__GLIBC__)
+ #include <execinfo.h>
+ #endif
+ #endif
+diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp
+index 2d7faa3..f132405 100644
+--- a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp
++++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp
+@@ -139,8 +139,15 @@ static String getLocaleValue (nl_item key)
+ return result;
+ }
+
++#if defined(__GLIBC__)
+ String SystemStats::getUserLanguage() { return getLocaleValue (_NL_IDENTIFICATION_LANGUAGE); }
+ String SystemStats::getUserRegion() { return getLocaleValue (_NL_IDENTIFICATION_TERRITORY); }
++#else
++// The identifiers _NL_INDENTIFICATION_LANGUAGE and _TERRIRTORY are not defined in musl libc.
++// TODO: Find a better fix than just returning nonsense. Inspect env("LANG") perhaps?
++String SystemStats::getUserLanguage() { return String("en"); }
++String SystemStats::getUserRegion() { return String("US"); }
++#endif
+ String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getUserRegion(); }
+
+ //==============================================================================
+diff --git a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp
+index 757ea24..6b61e16 100644
+--- a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp
++++ b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp
+@@ -138,7 +138,7 @@ String SystemStats::getStackBacktrace()
+ {
+ String result;
+
+- #if JUCE_ANDROID || JUCE_MINGW
++ #if JUCE_ANDROID || JUCE_MINGW || !defined(__GLIBC__)
+ jassertfalse; // sorry, not implemented yet!
+
+ #elif JUCE_WINDOWS
diff --git a/srcpkgs/libopenshot-audio/template b/srcpkgs/libopenshot-audio/template
index 56c330eafcf1..254f8b6283bd 100644
--- a/srcpkgs/libopenshot-audio/template
+++ b/srcpkgs/libopenshot-audio/template
@@ -1,6 +1,6 @@
# Template file for 'libopenshot-audio'
pkgname=libopenshot-audio
-version=0.2.0
+version=0.2.2
revision=1
build_style=cmake
hostmakedepends="doxygen"
@@ -11,7 +11,7 @@ maintainer="Spencer Hill <spencernh77@gmail.com>"
license="GPL-3.0-or-later"
homepage="https://github.com/OpenShot/libopenshot-audio"
distfiles="https://github.com/OpenShot/libopenshot-audio/archive/v${version}.tar.gz"
-checksum=937ff4f1c2dfb8ab5d56ad85beacaa29dfd5a79af0d9cf647386034fe9882309
+checksum=66bedfda0d8d430598b21bc2dde6c0016a758a6c83467d0273a9d692de10baaf
if [ "$XBPS_TARGET_NO_ATOMIC8" ]; then
makedepends+=" libatomic-devel"
@@ -20,9 +20,10 @@ fi
libopenshot-audio-devel_package() {
short_desc+=" - development files"
- depends+=" ${sourcepkg}>=${version}_${revision}"
+ depends+=" ${sourcepkg}>=${version}_${revision} alsa-lib-devel zlib-devel"
pkg_install() {
vmove usr/include
+ vmove usr/lib/cmake
vmove "usr/lib/*.so"
}
}
From 2dd2236bc8f6c2d36a6e180359be5655e86418d0 Mon Sep 17 00:00:00 2001
From: tibequadorian <tibequadorian@posteo.de>
Date: Wed, 23 Feb 2022 00:42:37 +0100
Subject: [PATCH 2/3] libopenshot: update to 0.2.7.
enable for all archs and libc
---
common/shlibs | 2 +-
.../patches/AV_GET_CODEC_CONTEXT-macro.patch | 34 -------------------
srcpkgs/libopenshot/template | 19 ++++++-----
3 files changed, 12 insertions(+), 43 deletions(-)
delete mode 100644 srcpkgs/libopenshot/patches/AV_GET_CODEC_CONTEXT-macro.patch
diff --git a/common/shlibs b/common/shlibs
index 8e2feccbee06..db11c39550d4 100644
--- a/common/shlibs
+++ b/common/shlibs
@@ -2598,7 +2598,7 @@ libmill.so.18 libmill-1.14_1
libges-1.0.so.0 gst1-editing-services-1.6.2_1
libykneomgr.so.0 libykneomgr-0.1.8_1
libopenshot-audio.so.8 libopenshot-audio-0.2.2_1
-libopenshot.so.19 libopenshot-0.2.5_3
+libopenshot.so.21 libopenshot-0.2.7_1
libpqxx-6.3.so libpqxx-6.3.3_1
libndpi.so.3 ndpi-3.4_1
liblog.so android-studio-3.0.1_1
diff --git a/srcpkgs/libopenshot/patches/AV_GET_CODEC_CONTEXT-macro.patch b/srcpkgs/libopenshot/patches/AV_GET_CODEC_CONTEXT-macro.patch
deleted file mode 100644
index e6c640a8e11e..000000000000
--- a/srcpkgs/libopenshot/patches/AV_GET_CODEC_CONTEXT-macro.patch
+++ /dev/null
@@ -1,34 +0,0 @@
---- a/include/FFmpegUtilities.h 2020-03-03 09:00:06.000000000 +0100
-+++ b/include/FFmpegUtilities.h 2020-08-19 17:04:58.535806744 +0200
-@@ -163,11 +163,10 @@
- #define AV_FREE_CONTEXT(av_context) avcodec_free_context(&av_context)
- #define AV_GET_CODEC_TYPE(av_stream) av_stream->codecpar->codec_type
- #define AV_FIND_DECODER_CODEC_ID(av_stream) av_stream->codecpar->codec_id
-- auto AV_GET_CODEC_CONTEXT = [](AVStream* av_stream, AVCodec* av_codec) { \
-- AVCodecContext *context = avcodec_alloc_context3(av_codec); \
-- avcodec_parameters_to_context(context, av_stream->codecpar); \
-- return context; \
-- };
-+ #define AV_GET_CODEC_CONTEXT(av_stream, av_codec) \
-+ ({ AVCodecContext *context = avcodec_alloc_context3(av_codec); \
-+ avcodec_parameters_to_context(context, av_stream->codecpar); \
-+ context; })
- #define AV_GET_CODEC_PAR_CONTEXT(av_stream, av_codec) av_codec;
- #define AV_GET_CODEC_FROM_STREAM(av_stream,codec_in)
- #define AV_GET_CODEC_ATTRIBUTES(av_stream, av_context) av_stream->codecpar
-@@ -199,11 +198,10 @@
- #define AV_FREE_CONTEXT(av_context) avcodec_free_context(&av_context)
- #define AV_GET_CODEC_TYPE(av_stream) av_stream->codecpar->codec_type
- #define AV_FIND_DECODER_CODEC_ID(av_stream) av_stream->codecpar->codec_id
-- auto AV_GET_CODEC_CONTEXT = [](AVStream* av_stream, AVCodec* av_codec) { \
-- AVCodecContext *context = avcodec_alloc_context3(av_codec); \
-- avcodec_parameters_to_context(context, av_stream->codecpar); \
-- return context; \
-- };
-+ #define AV_GET_CODEC_CONTEXT(av_stream, av_codec) \
-+ ({ AVCodecContext *context = avcodec_alloc_context3(av_codec); \
-+ avcodec_parameters_to_context(context, av_stream->codecpar); \
-+ context; })
- #define AV_GET_CODEC_PAR_CONTEXT(av_stream, av_codec) av_codec;
- #define AV_GET_CODEC_FROM_STREAM(av_stream,codec_in)
- #define AV_GET_CODEC_ATTRIBUTES(av_stream, av_context) av_stream->codecpar
diff --git a/srcpkgs/libopenshot/template b/srcpkgs/libopenshot/template
index b59472105b35..71dff12b5d61 100644
--- a/srcpkgs/libopenshot/template
+++ b/srcpkgs/libopenshot/template
@@ -1,11 +1,11 @@
# Template file for 'libopenshot'
pkgname=libopenshot
-version=0.2.5
-revision=6
-archs="i686 x86_64 ppc64le"
+version=0.2.7
+revision=1
build_style=cmake
-configure_args="-DENABLE_RUBY=OFF -DUSE_SYSTEM_JSONCPP=ON" # Builds fail with Ruby-2.4.1
-hostmakedepends="swig doxygen ruby python3 pkg-config"
+# Builds fail with Ruby-2.4.1
+configure_args="-DENABLE_RUBY=OFF -DUSE_SYSTEM_JSONCPP=ON"
+hostmakedepends="swig doxygen ruby python3 pkg-config qt5-qmake qt5-host-tools"
makedepends="python3-devel ffmpeg-devel libmagick-devel qt5-devel libgomp-devel
libopenshot-audio-devel qt5-multimedia-devel unittest-cpp zeromq-devel cppzmq
jsoncpp-devel"
@@ -15,9 +15,12 @@ maintainer="Spencer Hill <spencernh77@gmail.com>"
license="LGPL-3.0-or-later"
homepage="https://github.com/OpenShot/libopenshot"
distfiles="https://github.com/OpenShot/libopenshot/archive/v${version}.tar.gz"
-checksum=8ae7d226fbd2efbc84da4f7d9d8c7f3cc9616e4de46e1233e3b0a84ac0a429bc
-# FIXME: tests segfault
-make_check=extended
+checksum=568eab6d69d469c5f745f0e25387ca5e000f7c28be48417b0d7770577ac74a28
+
+if [ "$XBPS_TARGET_LIBC" = musl ]; then
+ makedepends+=" libexecinfo-devel"
+ LIBS="-lexecinfo"
+fi
libopenshot-devel_package() {
short_desc+=" - development files"
From 2566006abf2c58257977cd128dfbad75ae0c0a78 Mon Sep 17 00:00:00 2001
From: tibequadorian <tibequadorian@posteo.de>
Date: Wed, 23 Feb 2022 00:42:51 +0100
Subject: [PATCH 3/3] openshot: update to 2.6.1.
enable for all archs and libc
patches are upstream
---
.../openshot/patches/0001-video_widget.patch | 74 +
.../patches/0002-python3.10_int.patch | 1993 +++++++++++++++++
srcpkgs/openshot/template | 12 +-
3 files changed, 2073 insertions(+), 6 deletions(-)
create mode 100644 srcpkgs/openshot/patches/0001-video_widget.patch
create mode 100644 srcpkgs/openshot/patches/0002-python3.10_int.patch
diff --git a/srcpkgs/openshot/patches/0001-video_widget.patch b/srcpkgs/openshot/patches/0001-video_widget.patch
new file mode 100644
index 000000000000..5e93d764c4bf
--- /dev/null
+++ b/srcpkgs/openshot/patches/0001-video_widget.patch
@@ -0,0 +1,74 @@
+From 9748a13268d66a5949aebc970637b5903756d018 Mon Sep 17 00:00:00 2001
+From: Jonathan Thomas <jonathan@openshot.org>
+Date: Thu, 7 Oct 2021 13:53:09 -0500
+Subject: [PATCH] Support for previewing anamorphic video profiles, including a
+ few code clean-ups.
+
+---
+ src/windows/video_widget.py | 22 +++++++---------------
+ 1 file changed, 7 insertions(+), 15 deletions(-)
+
+diff --git a/src/windows/video_widget.py b/src/windows/video_widget.py
+index 7326598d3..842deb3ba 100644
+--- a/src/windows/video_widget.py
++++ b/src/windows/video_widget.py
+@@ -77,28 +77,20 @@ def changed(self, action):
+ if action.type == "load" and action.values.get("pixel_ratio"):
+ pixel_ratio_changed = True
+ self.pixel_ratio = openshot.Fraction(
+- action.values.get("pixel_ratio").get("num", 16),
+- action.values.get("pixel_ratio").get("den", 9))
++ action.values.get("pixel_ratio").get("num", 1),
++ action.values.get("pixel_ratio").get("den", 1))
+ log.info(
+ "Set video widget pixel aspect ratio to: %s",
+ self.pixel_ratio.ToFloat())
+ elif action.key and action.key[0] == "pixel_ratio":
+ pixel_ratio_changed = True
+ self.pixel_ratio = openshot.Fraction(
+- action.values.get("num", 16),
+- action.values.get("den", 9))
++ action.values.get("num", 1),
++ action.values.get("den", 1))
+ log.info(
+ "Update: Set video widget pixel aspect ratio to: %s",
+ self.pixel_ratio.ToFloat())
+
+- # Update max size (to size of video preview viewport)
+- if display_ratio_changed or pixel_ratio_changed:
+- timeline = get_app().window.timeline_sync.timeline
+- timeline.SetMaxSize(
+- round(self.width() * self.pixel_ratio.ToFloat()),
+- round(self.height() * 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):
+@@ -236,7 +228,7 @@ 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)
++ source_size = QSize(source_width, source_height * self.pixel_ratio.Reciprocal().ToDouble())
+
+ # Determine scale of clip
+ scale = self.transforming_clip.data['scale']
+@@ -405,7 +397,7 @@ def paintEvent(self, event, *args):
+ self.mutex.unlock()
+
+ def centeredViewport(self, width, height):
+- """ Calculate size of viewport to maintain apsect ratio """
++ """ Calculate size of viewport to maintain aspect ratio """
+
+ # Calculate padding
+ top_padding = (height - (height * self.zoom)) / 2.0
+@@ -416,7 +408,7 @@ def centeredViewport(self, width, height):
+ height = height * self.zoom
+
+ # Calculate which direction to scale (for perfect centering)
+- aspectRatio = self.aspect_ratio.ToFloat() * self.pixel_ratio.ToFloat()
++ aspectRatio = self.aspect_ratio.ToFloat()
+ heightFromWidth = width / aspectRatio
+ widthFromHeight = height * aspectRatio
+
diff --git a/srcpkgs/openshot/patches/0002-python3.10_int.patch b/srcpkgs/openshot/patches/0002-python3.10_int.patch
new file mode 100644
index 000000000000..8f6dc53dbf40
--- /dev/null
+++ b/srcpkgs/openshot/patches/0002-python3.10_int.patch
@@ -0,0 +1,1993 @@
+From a3088503500e79877ce985e4784f75478d9b792e Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@gmail.com>
+Date: Mon, 20 Sep 2021 05:36:19 -0400
+Subject: [PATCH 1/6] Preferences: Fix logging calls
+
+---
+ src/launch.py | 2 +-
+ src/windows/preferences.py | 18 +++++++++++-------
+ 2 files changed, 12 insertions(+), 8 deletions(-)
+
+diff --git a/src/launch.py b/src/launch.py
+index 9fc600b23..b614e9bc7 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
+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 1b14896d2057df80b0b20ba22e1380ba9e9bd6e6 Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@gmail.com>
+Date: Thu, 4 Nov 2021 21:01:39 -0400
+Subject: [PATCH 2/6] 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 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 9fc55120f36c36b2b6b67e499128993c25e535cf Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@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 1f058f730bb36068bce5c645ecea45b98279b614 Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@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 7df87ddc189c5e5031fbb19df13378428fab951e Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@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 33cf68ca0b1ea57edd5dec3dbb8ba06d6a3f8fa4 Mon Sep 17 00:00:00 2001
+From: "FeRD (Frank Dana)" <ferdnyc@gmail.com>
+Date: Thu, 25 Nov 2021 02:18:32 -0500
+Subject: [PATCH 6/6] properties_model: Fix bad logging call, Codacy flags
+
+---
+ src/windows/models/properties_model.py | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/src/windows/models/properties_model.py b/src/windows/models/properties_model.py
+index 40897f642..695e9f39c 100644
+--- a/src/windows/models/properties_model.py
++++ b/src/windows/models/properties_model.py
+@@ -444,7 +444,7 @@ def value_updated(self, item, interpolation=-1, value=None, interpolation_detail
+ log.debug("%s: update property %s. %s", log_id, property_key, clip_data.get(property_key))
+
+ # Check the type of property (some are keyframe, and some are not)
+- if property_type != "reader" and type(clip_data[property_key]) == dict:
++ if property_type != "reader" and isinstance(clip_data[property_key], dict):
+ # Keyframe
+ # Loop through points, find a matching points on this frame
+ found_point = False
+@@ -517,21 +517,21 @@ def value_updated(self, item, interpolation=-1, value=None, interpolation_detail
+ clip_updated = True
+ try:
+ clip_data[property_key] = int(new_value)
+- except Exception as ex:
++ except Exception:
+ 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:
++ except Exception:
+ 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:
++ except Exception:
+ log.warn('Invalid Boolean value passed to property', exc_info=1)
+
+ elif property_type == "string":
+@@ -558,7 +558,7 @@ def value_updated(self, item, interpolation=-1, value=None, interpolation_detail
+ clip_object.Close()
+ clip_object = None
+ except Exception:
+- log.warn('Invalid Reader value passed to property: %s (%s)', value, exc_info=1)
++ log.warn('Invalid Reader value passed to property: %s', value, exc_info=1)
+
+ # Reduce # of clip properties we are saving (performance boost)
+ clip_data = {property_key: clip_data.get(property_key)}
diff --git a/srcpkgs/openshot/template b/srcpkgs/openshot/template
index d16ea925d223..4248baa81e39 100644
--- a/srcpkgs/openshot/template
+++ b/srcpkgs/openshot/template
@@ -1,12 +1,11 @@
# Template file for 'openshot'
pkgname=openshot
-version=2.5.1
-revision=3
-archs="i686 x86_64 ppc64le"
+version=2.6.1
+revision=1
wrksrc="${pkgname}-qt-${version}"
build_style=python3-module
-hostmakedepends="python3"
-makedepends="ffmpeg-devel python3-PyQt5 python3-setuptools"
+hostmakedepends="python3 python3-setuptools"
+makedepends="ffmpeg-devel python3-PyQt5"
depends="ImageMagick libopenshot mlt python3-PyQt5-svg
python3-PyQt5-webkit python3-httplib2 python3-pyzmq python3-requests"
short_desc="Open-source, non-linear video editor for Linux"
@@ -14,4 +13,5 @@ maintainer="Spencer Hill <spencernh77@gmail.com>"
license="GPL-3.0-or-later"
homepage="https://www.openshot.org"
distfiles="https://github.com/OpenShot/openshot-qt/archive/v${version}.tar.gz"
-checksum=4c25eb9a5de42e749de4c6ca2f7a313c60e1283fe52d70c121629dbb8bb2df7b
+checksum=11651d5e7287da3c766ce6b447aef8da5cdadaf5626a2816e9025c831d0e1a66
+make_check=no # tests are broken
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PR PATCH] [Merged]: openshot: update to 2.6.1.
2022-02-23 0:01 [PR PATCH] openshot: update to 2.6.1 tibequadorian
2022-02-23 0:12 ` [PR PATCH] [Updated] " tibequadorian
2022-02-24 14:47 ` tibequadorian
@ 2022-02-25 23:52 ` Piraty
2 siblings, 0 replies; 4+ messages in thread
From: Piraty @ 2022-02-25 23:52 UTC (permalink / raw)
To: ml
[-- Attachment #1: Type: text/plain, Size: 1312 bytes --]
There's a merged pull request on the void-packages repository
openshot: update to 2.6.1.
https://github.com/void-linux/void-packages/pull/35798
Description:
Closes #33897
Patches for openshot are already upstream so maybe we should wait for 2.6.2.
<!-- Uncomment relevant sections and delete options which are not applicable -->
#### Testing the changes
- I tested the changes in this PR: **briefly**
Removed the restriction on arch or libc so help for testing on musl and ARM is appreciated.
- [x] aarch64 glibc with wayland is confirmed to work.
<!--
#### New package
- This new package conforms to the [quality requirements](https://github.com/void-linux/void-packages/blob/master/Manual.md#quality-requirements): **YES**|**NO**
-->
<!-- Note: If the build is likely to take more than 2 hours, please [skip CI](https://github.com/void-linux/void-packages/blob/master/CONTRIBUTING.md#continuous-integration)
and test at least one native build and, if supported, at least one cross build.
Ignore this section if this PR is not skipping CI.
-->
<!--
#### Local build testing
- I built this PR locally for my native architecture, (ARCH-LIBC)
- I built this PR locally for these architectures (if supported. mark crossbuilds):
- aarch64-musl
- armv7l
- armv6l-musl
-->
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2022-02-25 23:52 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-23 0:01 [PR PATCH] openshot: update to 2.6.1 tibequadorian
2022-02-23 0:12 ` [PR PATCH] [Updated] " tibequadorian
2022-02-24 14:47 ` tibequadorian
2022-02-25 23:52 ` [PR PATCH] [Merged]: " Piraty
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).