Skip to content

Commit f23c282

Browse files
authored
Merge pull request #3794 from OpenShot/caption-effect
New Effect Property Editors (font and captions) + new effect thumbnails
2 parents abbb94b + dd040cc commit f23c282

24 files changed

+281
-77
lines changed

src/classes/time_parts.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ def timecodeToSeconds(time_code="00:00:00:00", fps_num=30, fps_den=1):
6767
seconds = (hours * 60 * 60) + (mins * 60) + secs + (frames / fps_float)
6868
return seconds
6969

70-
def secondsToTimecode(time_in_seconds=0.0, fps_num=30, fps_den=1):
70+
def secondsToTimecode(time_in_seconds=0.0, fps_num=30, fps_den=1, use_milliseconds=False):
7171
"""Return a formatted time code HH:MM:SS:FRAME"""
72-
return "%(hour)s:%(min)s:%(sec)s:%(frame)s" % secondsToTime(time_in_seconds, fps_num, fps_den)
72+
if use_milliseconds:
73+
return "%(hour)s:%(min)s:%(sec)s:%(milli)s" % secondsToTime(time_in_seconds, fps_num, fps_den)
74+
return "%(hour)s:%(min)s:%(sec)s:%(frame)s" % secondsToTime(time_in_seconds, fps_num, fps_den)

src/effects/icons/bars.png

382 Bytes
Loading

src/effects/icons/blur.png

3.23 KB
Loading

src/effects/icons/brightness.png

2.14 KB
Loading

src/effects/icons/caption.png

20.6 KB
Loading

src/effects/icons/chromakey.png

688 Bytes
Loading

src/effects/icons/colorshift.png

1.86 KB
Loading

src/effects/icons/crop.png

6.05 KB
Loading

src/effects/icons/deinterlace.png

14.8 KB
Loading

src/effects/icons/hue.png

2.08 KB
Loading

src/effects/icons/mask.png

1.98 KB
Loading

src/effects/icons/negate.png

2.34 KB
Loading

src/effects/icons/objectdetector.png

17 KB
Loading

src/effects/icons/pixelate.png

4.58 KB
Loading

src/effects/icons/saturation.png

2.73 KB
Loading

src/effects/icons/shift.png

1.9 KB
Loading

src/effects/icons/stabilizer.png

21 KB
Loading

src/effects/icons/tracker.png

19.8 KB
Loading

src/effects/icons/wave.png

308 Bytes
Loading

src/settings/_default.settings

+2-2
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,14 @@
7878
"setting": "title_editor"
7979
},
8080
{
81-
"value": "AAAA/wAAAAD9AAAAAwAAAAAAAAEnAAAC3/wCAAAAAvwAAAJeAAAApwAAAAAA////+gAAAAACAAAAAfsAAAAYAGQAbwBjAGsASwBlAHkAZgByAGEAbQBlAAAAAAD/////AAAAAAAAAAD7AAAAHABkAG8AYwBrAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAJwAAAt8AAACnAP///wAAAAEAAAEcAAABQPwCAAAAAfsAAAAYAGQAbwBjAGsASwBlAHkAZgByAGEAbQBlAQAAAVgAAAAVAAAAAAAAAAAAAAACAAAERgAAAtj8AQAAAAH8AAAAAAAABEYAAAD6AP////wCAAAAAvwAAAAnAAABwAAAALQA/////AEAAAAC/AAAAAAAAAFcAAAAewD////6AAAAAAIAAAAD+wAAABIAZABvAGMAawBGAGkAbABlAHMBAAAAAP////8AAACYAP////sAAAAeAGQAbwBjAGsAVAByAGEAbgBzAGkAdABpAG8AbgBzAQAAAAD/////AAAAmAD////7AAAAFgBkAG8AYwBrAEUAZgBmAGUAYwB0AHMBAAAAAP////8AAACYAP////sAAAASAGQAbwBjAGsAVgBpAGQAZQBvAQAAAWIAAALkAAAARwD////7AAAAGABkAG8AYwBrAFQAaQBtAGUAbABpAG4AZQEAAAHtAAABEgAAAJYA////AAAERgAAAAEAAAABAAAAAgAAAAEAAAAC/AAAAAEAAAACAAAAAQAAAA4AdABvAG8AbABCAGEAcgEAAAAA/////wAAAAAAAAAA",
81+
"value": "AAAA/wAAAAD9AAAAAwAAAAAAAAEnAAAC3/wCAAAAA/wAAAJeAAAApwAAAAAA////+gAAAAACAAAAAfsAAAAYAGQAbwBjAGsASwBlAHkAZgByAGEAbQBlAAAAAAD/////AAAAAAAAAAD7AAAAHABkAG8AYwBrAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAJwAAAt8AAAChAP////sAAAAYAGQAbwBjAGsAVAB1AHQAbwByAGkAYQBsAgAAAAAAAAAAAAAAyAAAAGQAAAABAAABHAAAAUD8AgAAAAH7AAAAGABkAG8AYwBrAEsAZQB5AGYAcgBhAG0AZQEAAAFYAAAAFQAAAAAAAAAAAAAAAgAABEYAAALY/AEAAAAC/AAAAAAAAANnAAAA+gD////8AgAAAAL8AAAAJwAAAcAAAACvAP////wBAAAAAvwAAAAAAAABFQAAAHsA////+gAAAAACAAAAA/sAAAASAGQAbwBjAGsARgBpAGwAZQBzAQAAAAD/////AAAAkgD////7AAAAHgBkAG8AYwBrAFQAcgBhAG4AcwBpAHQAaQBvAG4AcwEAAAAA/////wAAAJIA////+wAAABYAZABvAGMAawBFAGYAZgBlAGMAdABzAQAAAAD/////AAAAkgD////7AAAAEgBkAG8AYwBrAFYAaQBkAGUAbwEAAAEbAAACTAAAAEcA////+wAAABgAZABvAGMAawBUAGkAbQBlAGwAaQBuAGUBAAAB7QAAARIAAACWAP////wAAANtAAAA2QAAAIIA////+gAAAAECAAAAAvsAAAAiAGQAbwBjAGsAQwBhAHAAdABpAG8AbgBFAGQAaQB0AG8AcgAAAAAA/////wAAAJgA////+wAAABQAZABvAGMAawBFAG0AbwBqAGkAcwEAAADFAAACOgAAAJIA////AAAERgAAAAEAAAABAAAAAgAAAAEAAAAC/AAAAAEAAAACAAAAAQAAAA4AdABvAG8AbABCAGEAcgEAAAAA/////wAAAAAAAAAA",
8282
"title": "",
8383
"type": "hidden",
8484
"category": "Qt",
8585
"setting": "window_state_v2"
8686
},
8787
{
88-
"value": "AdnQywACAAAAAABWAAAAMwAABJsAAANqAAAAVgAAAE8AAASbAAADagAAAAAAAAAAB4A=",
88+
"value": "AdnQywACAAAAAAGbAAAAcQAABeAAAAOoAAABmwAAAI0AAAXgAAADqAAAAAAAAAAAB4A=",
8989
"title": "",
9090
"type": "hidden",
9191
"category": "Qt",

src/windows/main_window.py

+132-56
Original file line numberDiff line numberDiff line change
@@ -28,61 +28,61 @@
2828
"""
2929

3030
import os
31-
import sys
3231
import platform
3332
import shutil
33+
import sys
3434
import webbrowser
35+
from copy import deepcopy
3536
from time import sleep
3637
from uuid import uuid4
37-
from copy import deepcopy
3838

39+
import openshot # Python module for libopenshot (required video editing module installed separately)
3940
from PyQt5.QtCore import (
4041
Qt, pyqtSignal, QCoreApplication, PYQT_VERSION_STR,
4142
QTimer, QDateTime, QFileInfo, QUrl,
4243
)
43-
from PyQt5.QtGui import QIcon, QCursor, QKeySequence
44+
from PyQt5.QtGui import QIcon, QCursor, QKeySequence, QTextCursor
4445
from PyQt5.QtWidgets import (
4546
QMainWindow, QWidget, QDockWidget,
4647
QMessageBox, QDialog, QFileDialog, QInputDialog,
4748
QAction, QActionGroup, QSizePolicy,
4849
QStatusBar, QToolBar, QToolButton,
49-
QLineEdit, QSlider, QLabel, QComboBox,
50-
)
51-
import openshot # Python module for libopenshot (required video editing module installed separately)
50+
QLineEdit, QSlider, QLabel, QComboBox, QTextEdit
51+
)
5252

53-
from windows.views.timeline_webview import TimelineWebView
5453
from classes import info, ui_util, settings, qt_types, updates
55-
from classes import openshot_rc # noqa
5654
from classes.app import get_app
55+
from classes.conversion import zoomToSeconds, secondsToZoom
56+
from classes.exporters.edl import export_edl
57+
from classes.exporters.final_cut_pro import export_xml
58+
from classes.importers.edl import import_edl
59+
from classes.importers.final_cut_pro import import_xml
5760
from classes.logger import log
58-
from classes.timeline import TimelineSync
59-
from classes.query import Clip, Transition, Marker, Track
6061
from classes.metrics import (
6162
track_metric_session, track_metric_screen,
6263
track_metric_error, track_exception_stacktrace,
6364
)
64-
from classes.version import get_current_Version
65-
from classes.conversion import zoomToSeconds, secondsToZoom
65+
from classes.query import Clip, Transition, Marker, Track
6666
from classes.thumbnail import httpThumbnailServerThread
67+
from classes.time_parts import secondsToTimecode
68+
from classes.timeline import TimelineSync
69+
from classes.version import get_current_Version
70+
from windows.models.effects_model import EffectsModel
71+
from windows.models.emoji_model import EmojisModel
6772
from windows.models.files_model import FilesModel
68-
from windows.views.files_treeview import FilesTreeView
69-
from windows.views.files_listview import FilesListView
7073
from windows.models.transition_model import TransitionsModel
71-
from windows.views.transitions_treeview import TransitionsTreeView
72-
from windows.views.transitions_listview import TransitionsListView
73-
from windows.models.emoji_model import EmojisModel
74-
from windows.views.emojis_listview import EmojisListView
75-
from windows.models.effects_model import EffectsModel
76-
from windows.views.effects_treeview import EffectsTreeView
74+
from windows.preview_thread import PreviewParent
75+
from windows.video_widget import VideoWidget
7776
from windows.views.effects_listview import EffectsListView
77+
from windows.views.effects_treeview import EffectsTreeView
78+
from windows.views.emojis_listview import EmojisListView
79+
from windows.views.files_listview import FilesListView
80+
from windows.views.files_treeview import FilesTreeView
7881
from windows.views.properties_tableview import PropertiesTableView, SelectionLabel
82+
from windows.views.timeline_webview import TimelineWebView
83+
from windows.views.transitions_listview import TransitionsListView
84+
from windows.views.transitions_treeview import TransitionsTreeView
7985
from windows.views.tutorial import TutorialManager
80-
from windows.video_widget import VideoWidget
81-
from windows.preview_thread import PreviewParent
82-
from classes.exporters.edl import export_edl
83-
from classes.exporters.final_cut_pro import export_xml
84-
from classes.importers.edl import import_edl
85-
from classes.importers.final_cut_pro import import_xml
8686

8787

8888
class MainWindow(QMainWindow, updates.UpdateWatcher):
@@ -113,6 +113,8 @@ class MainWindow(QMainWindow, updates.UpdateWatcher):
113113
OpenProjectSignal = pyqtSignal(str)
114114
ThumbnailUpdated = pyqtSignal(str)
115115
FileUpdated = pyqtSignal(str)
116+
CaptionTextUpdated = pyqtSignal(str, object)
117+
CaptionTextLoaded = pyqtSignal(str, object)
116118

117119
# Docks are closable, movable and floatable
118120
docks_frozen = False
@@ -2169,21 +2171,20 @@ def actionSimple_View_trigger(self):
21692171

21702172
# Set initial size of docks
21712173
simple_state = "".join([
2172-
"AAAA/wAAAAD9AAAAAwAAAAAAAAEnAAAC3/wCAAAAAvwAAAJeAAAApwAAAAAA////",
2173-
"+gAAAAACAAAAAfsAAAAYAGQAbwBjAGsASwBlAHkAZgByAGEAbQBlAAAAAAD/////",
2174-
"AAAAAAAAAAD7AAAAHABkAG8AYwBrAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAJwAAAt8AAACfAP///",
2175-
"wAAAAEAAAEcAAABQPwCAAAAAfsAAAAYAGQAbwBjAGsASwBlAHkAZgByAGEAbQBlAQAAAVg",
2176-
"AAAAVAAAAAAAAAAAAAAACAAAFEgAAAvP8AQAAAAH8AAAAAAAABRIAAAD6AP////",
2177-
"wCAAAAAvwAAAAnAAAB0QAAAK0A/////AEAAAAC/AAAAAAAAAHaAAAAewD////",
2178-
"6AAAAAAIAAAAE+wAAABIAZABvAGMAawBGAGkAbABlAHMBAAAAAP////8AAACRAP////",
2179-
"sAAAAeAGQAbwBjAGsAVAByAGEAbgBzAGkAdABpAG8AbgBzAQAAAAD/////AAAAkQD////",
2180-
"7AAAAFgBkAG8AYwBrAEUAZgBmAGUAYwB0AHMBAAAAAP////8AAACRAP////",
2181-
"sAAAAUAGQAbwBjAGsARQBtAG8AagBpAHMBAAAAJwAAAdEAAACRAP////",
2182-
"sAAAASAGQAbwBjAGsAVgBpAGQAZQBvAQAAAeAAAAMyAAAARwD////",
2183-
"7AAAAGABkAG8AYwBrAFQAaQBtAGUAbABpAG4AZQEAAAH+AAABHAAAAJYA////",
2184-
"AAAFEgAAAAEAAAABAAAAAgAAAAEAAAAC/",
2185-
"AAAAAEAAAACAAAAAQAAAA4AdABvAG8AbABCAGEAcgEAAAAA/////wAAAAAAAAAA"
2186-
])
2174+
"AAAA/wAAAAD9AAAAAwAAAAAAAAEnAAAC3/wCAAAAA/wAAAJeAAAApwAAAAAA////+gAAAAACAAAAAfsAAAA"
2175+
"YAGQAbwBjAGsASwBlAHkAZgByAGEAbQBlAAAAAAD/////AAAAAAAAAAD7AAAAHABkAG8AYwBrAFAAcgBvAH"
2176+
"AAZQByAHQAaQBlAHMAAAAAJwAAAt8AAAChAP////sAAAAYAGQAbwBjAGsAVAB1AHQAbwByAGkAYQBsAgAAA"
2177+
"AAAAAAAAAAAyAAAAGQAAAABAAABHAAAAUD8AgAAAAH7AAAAGABkAG8AYwBrAEsAZQB5AGYAcgBhAG0AZQEA"
2178+
"AAFYAAAAFQAAAAAAAAAAAAAAAgAABEYAAALY/AEAAAAC/AAAAAAAAANnAAAA+gD////8AgAAAAL8AAAAJwA"
2179+
"AAcAAAACvAP////wBAAAAAvwAAAAAAAABFQAAAHsA////+gAAAAACAAAAA/sAAAASAGQAbwBjAGsARgBpAG"
2180+
"wAZQBzAQAAAAD/////AAAAkgD////7AAAAHgBkAG8AYwBrAFQAcgBhAG4AcwBpAHQAaQBvAG4AcwEAAAAA/"
2181+
"////wAAAJIA////+wAAABYAZABvAGMAawBFAGYAZgBlAGMAdABzAQAAAAD/////AAAAkgD////7AAAAEgBk"
2182+
"AG8AYwBrAFYAaQBkAGUAbwEAAAEbAAACTAAAAEcA////+wAAABgAZABvAGMAawBUAGkAbQBlAGwAaQBuAGU"
2183+
"BAAAB7QAAARIAAACWAP////wAAANtAAAA2QAAAIIA////+gAAAAECAAAAAvsAAAAiAGQAbwBjAGsAQwBhAH"
2184+
"AAdABpAG8AbgBFAGQAaQB0AG8AcgAAAAAA/////wAAAJgA////+wAAABQAZABvAGMAawBFAG0AbwBqAGkAc"
2185+
"wEAAADFAAACOgAAAJIA////AAAERgAAAAEAAAABAAAAAgAAAAEAAAAC/AAAAAEAAAACAAAAAQAAAA4AdABv"
2186+
"AG8AbABCAGEAcgEAAAAA/////wAAAAAAAAAA"
2187+
])
21872188
self.restoreState(qt_types.str_to_bytes(simple_state))
21882189
QCoreApplication.processEvents()
21892190

@@ -2213,21 +2214,18 @@ def actionAdvanced_View_trigger(self):
22132214

22142215
# Set initial size of docks
22152216
advanced_state = "".join([
2216-
"AAAA/wAAAAD9AAAAAwAAAAAAAADxAAAC+vwCAAAAAvsAAAAcAGQAbwBjAGsAUAB",
2217-
"yAG8AcABlAHIAdABpAGUAcwEAAAAnAAAC+gAAAJ8A/////AAAAl4AAACnAAAAAAD////",
2218-
"6AAAAAAIAAAAB+wAAABgAZABvAGMAawBLAGUAeQBmAHIAYQBtAGUAAAAAAP////",
2219-
"8AAAAAAAAAAAAAAAEAAACZAAAC+vwCAAAAAvsAAAAYAGQAbwBjAGsASwBlAHkAZg",
2220-
"ByAGEAbQBlAQAAAVgAAAAVAAAAAAAAAAD7AAAAFgBkAG8AYwBrAEUAZgBmAGUAY",
2221-
"wB0AHMBAAAAJwAAAvoAAACRAP///wAAAAIAAAN8AAAC8/wBAAAAAfwAAAD3AAAD",
2222-
"fAAAAPoA/////AIAAAAC/AAAACcAAAHZAAABRAD////8AQAAAAL8AAAA9wAAAVsAAAB7AP////",
2223-
"wCAAAAAvsAAAASAGQAbwBjAGsARgBpAGwAZQBzAQAAACcAAADkAAAAkQD////",
2224-
"8AAABEQAAAO8AAACtAQAAG/oAAAAAAQAAAAL7AAAAHgBkAG8AYwBrAFQAcg",
2225-
"BhAG4AcwBpAHQAaQBvAG4AcwEAAAAA/////wAAAGwA////",
2226-
"+wAAABQAZABvAGMAawBFAG0AbwBqAGkAcwEAAAD3AAABHQAAAFgA////",
2227-
"+wAAABIAZABvAGMAawBWAGkAZABlAG8BAAACWAAAAhsAAABHAP////",
2228-
"sAAAAYAGQAbwBjAGsAVABpAG0AZQBsAGkAbgBlAQAAAgYAAAEUAAAAlgD///",
2229-
"8AAAN8AAAAAQAAAAEAAAACAAAAAQAAAAL8AAAAAQAAAAIAAAABAAAADgB0",
2230-
"AG8AbwBsAEIAYQByAQAAAAD/////AAAAAAAAAAA="
2217+
"AAAA/wAAAAD9AAAAAwAAAAAAAADxAAAC3/wCAAAAAvsAAAAcAGQAbwBjAGsAUAByAG8AcABlAHIAdABpAGUAcw"
2218+
"EAAAAnAAAC3wAAAKEA/////AAAAl4AAACnAAAAAAD////6AAAAAAIAAAAB+wAAABgAZABvAGMAawBLAGUAeQBm"
2219+
"AHIAYQBtAGUAAAAAAP////8AAAAAAAAAAAAAAAEAAACZAAAC3/wCAAAAAvsAAAAYAGQAbwBjAGsASwBlAHkAZg"
2220+
"ByAGEAbQBlAQAAAVgAAAAVAAAAAAAAAAD8AAAAJwAAAt8AAAC1AQAAHPoAAAAAAQAAAAL7AAAAFgBkAG8AYwBr"
2221+
"AEUAZgBmAGUAYwB0AHMBAAADrQAAAJkAAABYAP////sAAAAiAGQAbwBjAGsAQwBhAHAAdABpAG8AbgBFAGQAaQ"
2222+
"B0AG8AcgEAAAAA/////wAAAFgA////AAAAAgAAArAAAALY/AEAAAAB/AAAAPcAAAKwAAAA+gD////8AgAAAAL8"
2223+
"AAAAJwAAAcgAAAFHAP////wBAAAAAvwAAAD3AAAArgAAAIIA/////AIAAAAC+wAAABIAZABvAGMAawBGAGkAbA"
2224+
"BlAHMBAAAAJwAAAOQAAACSAP////wAAAERAAAA3gAAAK8BAAAc+gAAAAABAAAAAvsAAAAeAGQAbwBjAGsAVABy"
2225+
"AGEAbgBzAGkAdABpAG8AbgBzAQAAAAD/////AAAAbAD////7AAAAFABkAG8AYwBrAEUAbQBvAGoAaQBzAQAAAP"
2226+
"cAAAEdAAAAggD////7AAAAEgBkAG8AYwBrAFYAaQBkAGUAbwEAAAGrAAAB/AAAAEcA////+wAAABgAZABvAGMA"
2227+
"awBUAGkAbQBlAGwAaQBuAGUBAAAB9QAAAQoAAACWAP///wAAArAAAAABAAAAAQAAAAIAAAABAAAAAvwAAAABAA"
2228+
"AAAgAAAAEAAAAOAHQAbwBvAGwAQgBhAHIBAAAAAP////8AAAAAAAAAAA=="
22312229
])
22322230
self.restoreState(qt_types.str_to_bytes(advanced_state))
22332231
QCoreApplication.processEvents()
@@ -2263,6 +2261,57 @@ def actionTutorial_trigger(self):
22632261
self.tutorial_manager.exit_manager()
22642262
self.tutorial_manager = TutorialManager(self)
22652263

2264+
def actionInsertTimestamp_trigger(self, event):
2265+
"""Insert the current timestamp into the caption editor
2266+
In the format: 00:00:23,000 --> 00:00:24,500. first click to set the initial timestamp,
2267+
move the playehad, second click to set the end timestamp.
2268+
"""
2269+
# Get translation function
2270+
app = get_app()
2271+
_ = app._tr
2272+
2273+
if self.captionTextEdit.isReadOnly():
2274+
return
2275+
2276+
# Calculate fps / current seconds
2277+
fps = get_app().project.get("fps")
2278+
fps_float = float(fps["num"]) / float(fps["den"])
2279+
current_position = (self.preview_thread.current_frame - 1) / fps_float
2280+
2281+
# Get cursor / current line of text (where cursor is located)
2282+
cursor = self.captionTextEdit.textCursor()
2283+
self.captionTextEdit.moveCursor(QTextCursor.StartOfLine)
2284+
line_text = cursor.block().text()
2285+
self.captionTextEdit.moveCursor(QTextCursor.EndOfLine)
2286+
2287+
# Insert text at cursor position
2288+
current_timestamp = secondsToTimecode(current_position, fps["num"], fps["den"], use_milliseconds=True)
2289+
if "-->" in line_text:
2290+
self.captionTextEdit.insertPlainText("%s\n%s" % (current_timestamp, _("Enter caption text...")))
2291+
else:
2292+
self.captionTextEdit.insertPlainText("%s --> " % (current_timestamp))
2293+
2294+
def captionTextEdit_TextChanged(self):
2295+
"""Caption text was edited, start the save timer (to prevent spamming saves)"""
2296+
self.caption_save_timer.start()
2297+
2298+
def caption_editor_save(self):
2299+
"""Emit the CaptionTextUpdated signal (and if that property is active/selected, it will be saved)"""
2300+
self.CaptionTextUpdated.emit(self.captionTextEdit.toPlainText(), self.caption_model_row)
2301+
2302+
def caption_editor_load(self, new_caption_text, caption_model_row):
2303+
"""Load the caption editor with text, or disable it if empty string detected"""
2304+
self.caption_model_row = caption_model_row
2305+
self.captionTextEdit.setPlainText(new_caption_text.strip())
2306+
if not caption_model_row:
2307+
self.captionTextEdit.setReadOnly(True)
2308+
else:
2309+
self.captionTextEdit.setReadOnly(False)
2310+
2311+
# Show this dock
2312+
self.dockCaptionEditor.show()
2313+
self.dockCaptionEditor.raise_()
2314+
22662315
def SetWindowTitle(self, profile=None):
22672316
""" Set the window title based on a variety of factors """
22682317

@@ -2332,6 +2381,9 @@ def addSelection(self, item_id, item_type, clear_existing=False):
23322381
elif item_type == "effect":
23332382
self.selected_effects.clear()
23342383

2384+
# Clear caption editor (if nothing is selected)
2385+
get_app().window.CaptionTextLoaded.emit("", None)
2386+
23352387
if item_id:
23362388
# If item_id is not blank, store it
23372389
if item_type == "clip" and item_id not in self.selected_clips:
@@ -2363,6 +2415,9 @@ def removeSelection(self, item_id, item_type):
23632415
# Clear transform (if no other clips are selected)
23642416
self.TransformSignal.emit("")
23652417

2418+
# Clear caption editor (if nothing is selected)
2419+
get_app().window.CaptionTextLoaded.emit("", None)
2420+
23662421
# Move selection to next selected clip (if any)
23672422
self.show_property_id = ""
23682423
self.show_property_type = ""
@@ -2594,6 +2649,27 @@ def setup_toolbars(self):
25942649
self.timelineToolbar.addAction(self.actionCenterOnPlayhead)
25952650
self.timelineToolbar.addSeparator()
25962651

2652+
# Add Video Preview toolbar
2653+
self.captionToolbar = QToolBar(_("Caption Toolbar"))
2654+
2655+
# Add Caption text editor widget
2656+
self.captionTextEdit = QTextEdit()
2657+
self.captionTextEdit.setReadOnly(True)
2658+
2659+
# Playback controls (centered)
2660+
self.captionToolbar.addAction(self.actionInsertTimestamp)
2661+
self.tabCaptions.layout().addWidget(self.captionToolbar)
2662+
self.tabCaptions.layout().addWidget(self.captionTextEdit)
2663+
2664+
# Hook up caption editor signal
2665+
self.captionTextEdit.textChanged.connect(self.captionTextEdit_TextChanged)
2666+
self.caption_save_timer = QTimer()
2667+
self.caption_save_timer.setInterval(100)
2668+
self.caption_save_timer.setSingleShot(True)
2669+
self.caption_save_timer.timeout.connect(self.caption_editor_save)
2670+
self.CaptionTextLoaded.connect(self.caption_editor_load)
2671+
self.caption_model_row = None
2672+
25972673
# Get project's initial zoom value
25982674
initial_scale = get_app().project.get("scale") or 15
25992675
# Round non-exponential scale down to next lowest power of 2

0 commit comments

Comments
 (0)