|
28 | 28 | import os
|
29 | 29 | import fnmatch
|
30 | 30 |
|
31 |
| -from PyQt5.QtCore import QMimeData, Qt, QSize |
32 |
| -from PyQt5.QtGui import QStandardItemModel, QStandardItem, QIcon, QPixmap |
| 31 | +from PyQt5.QtCore import Qt, QObject, QMimeData |
| 32 | +from PyQt5.QtGui import QStandardItemModel, QStandardItem, QIcon |
33 | 33 | from PyQt5.QtWidgets import QMessageBox
|
34 | 34 | import openshot # Python module for libopenshot (required video editing module installed separately)
|
35 | 35 |
|
|
41 | 41 |
|
42 | 42 |
|
43 | 43 | class TitleStandardItemModel(QStandardItemModel):
|
44 |
| - def __init__(self, parent=None): |
45 |
| - QStandardItemModel.__init__(self) |
| 44 | + def __init__(self, *args, **kwargs): |
| 45 | + super().__init__(*args, **kwargs) |
| 46 | + self.setObjectName("titles.model") |
46 | 47 |
|
47 | 48 | def mimeData(self, indexes):
|
48 | 49 | # Create MimeData for drag operation
|
49 | 50 | data = QMimeData()
|
50 | 51 |
|
51 | 52 | # Get list of all selected file ids
|
52 |
| - files = [] |
53 |
| - for item in indexes: |
54 |
| - selected_row = self.itemFromIndex(item).row() |
55 |
| - files.append(self.item(selected_row, 2).text()) |
| 53 | + files = [ |
| 54 | + self.itemFromIndex(i).data(TitleRoles.PathRole) |
| 55 | + for i in indexes |
| 56 | + ] |
56 | 57 | data.setText(json.dumps(files))
|
57 | 58 | data.setHtml("title")
|
58 | 59 |
|
59 | 60 | # Return Mimedata
|
60 | 61 | return data
|
61 | 62 |
|
62 | 63 |
|
63 |
| -class TitlesModel(): |
64 |
| - def update_model(self, clear=True): |
65 |
| - log.info("updating title model.") |
66 |
| - app = get_app() |
| 64 | +class TitleRoles: |
| 65 | + PathRole = Qt.UserRole + 11 |
| 66 | + |
67 | 67 |
|
68 |
| - _ = app._tr |
| 68 | +class TitlesModel(QObject): |
| 69 | + def update_model(self, clear=True): |
| 70 | + log.debug("Updating title model") |
| 71 | + # Get window to check filters |
| 72 | + _ = self.app._tr |
69 | 73 |
|
70 | 74 | # Clear all items
|
71 | 75 | if clear:
|
72 | 76 | self.model_paths = {}
|
73 | 77 | self.model.clear()
|
74 | 78 |
|
75 | 79 | # Add Headers
|
76 |
| - self.model.setHorizontalHeaderLabels([_("Thumb"), _("Name")]) |
| 80 | + self.model.setHorizontalHeaderLabels([_("Name")]) |
77 | 81 |
|
78 |
| - # get a list of files in the OpenShot /transitions directory |
| 82 | + # get a list of files in the OpenShot package directory |
79 | 83 | titles_dir = os.path.join(info.PATH, "titles")
|
80 |
| - |
81 |
| - # Add build-in templates |
82 |
| - titles_list = [] |
83 |
| - for filename in sorted(os.listdir(titles_dir)): |
84 |
| - titles_list.append(os.path.join(titles_dir, filename)) |
85 |
| - |
| 84 | + titles_list = [ |
| 85 | + os.path.join(titles_dir, filename) |
| 86 | + for filename in sorted(os.listdir(titles_dir)) |
| 87 | + ] |
86 | 88 | # Add user-defined titles (if any)
|
87 |
| - for filename in sorted(os.listdir(info.USER_TITLES_PATH)): |
88 |
| - if fnmatch.fnmatch(filename, '*.svg'): |
89 |
| - titles_list.append(os.path.join(info.USER_TITLES_PATH, filename)) |
| 89 | + titles_list.extend([ |
| 90 | + os.path.join(info.USER_TITLES_PATH, filename) |
| 91 | + for filename in sorted(os.listdir(info.USER_TITLES_PATH)) |
| 92 | + if fnmatch.fnmatch(filename, '*.svg') |
| 93 | + ]) |
90 | 94 |
|
91 | 95 | for path in sorted(titles_list):
|
92 | 96 | filename = os.path.basename(path)
|
@@ -116,78 +120,62 @@ def update_model(self, clear=True):
|
116 | 120 |
|
117 | 121 | # Check for thumbnail path (in build-in cache)
|
118 | 122 | thumb_path = os.path.join(info.IMAGES_PATH, "cache", "{}.png".format(fileBaseName))
|
119 |
| - |
120 |
| - # Check built-in cache (if not found) |
121 | 123 | if not os.path.exists(thumb_path):
|
122 |
| - # Check user folder cache |
| 124 | + # Check user folder cache (if not found) |
123 | 125 | thumb_path = os.path.join(info.CACHE_PATH, "{}.png".format(fileBaseName))
|
124 |
| - |
125 |
| - # Generate thumbnail (if needed) |
126 | 126 | if not os.path.exists(thumb_path):
|
127 |
| - |
| 127 | + # Generate thumbnail (if needed) |
128 | 128 | try:
|
129 | 129 | # Reload this reader
|
130 | 130 | clip = openshot.Clip(path)
|
131 | 131 | reader = clip.Reader()
|
132 |
| - |
133 |
| - # Open reader |
134 | 132 | reader.Open()
|
135 | 133 |
|
136 |
| - # Save thumbnail |
| 134 | + # Save thumbnail to disk |
137 | 135 | reader.GetFrame(0).Thumbnail(
|
138 | 136 | thumb_path, 98, 64,
|
139 | 137 | os.path.join(info.IMAGES_PATH, "mask.png"),
|
140 | 138 | "", "#000", True, "png", 85
|
141 | 139 | )
|
142 | 140 | reader.Close()
|
143 | 141 | clip.Close()
|
144 |
| - |
145 | 142 | except Exception as ex:
|
146 |
| - # Handle exception |
147 | 143 | log.info('Failed to open {} as title: {}'.format(filename, ex))
|
148 | 144 | msg = QMessageBox()
|
149 | 145 | msg.setText(_("%s is not a valid image file." % filename))
|
150 | 146 | msg.exec_()
|
151 | 147 | continue
|
152 |
| - |
153 |
| - row = [] |
154 |
| - |
155 |
| - # Append thumbnail |
156 |
| - col = QStandardItem() |
157 |
| - icon_pixmap = QPixmap(thumb_path) |
158 |
| - scaled_pixmap = icon_pixmap.scaled(QSize(93, 62), Qt.IgnoreAspectRatio, Qt.SmoothTransformation) |
159 |
| - col.setIcon(QIcon(scaled_pixmap)) |
160 |
| - col.setText(title_name) |
161 |
| - col.setToolTip(title_name) |
162 |
| - col.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | Qt.ItemIsDragEnabled) |
163 |
| - row.append(col) |
164 |
| - |
165 |
| - # Append Filename |
166 |
| - col = QStandardItem("Name") |
167 |
| - col.setData(title_name, Qt.DisplayRole) |
168 |
| - col.setText(title_name) |
169 |
| - col.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | Qt.ItemIsDragEnabled) |
170 |
| - row.append(col) |
171 |
| - |
172 |
| - # Append Path |
173 |
| - col = QStandardItem("Path") |
174 |
| - col.setData(path, Qt.DisplayRole) |
175 |
| - col.setText(path) |
176 |
| - col.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | Qt.ItemIsDragEnabled) |
177 |
| - row.append(col) |
178 |
| - |
179 |
| - # Append ROW to MODEL (if does not already exist in model) |
| 148 | + # Create item entry for model |
| 149 | + flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | Qt.ItemIsDragEnabled |
| 150 | + item = QStandardItem(QIcon(thumb_path), title_name) |
| 151 | + item.setData(path, TitleRoles.PathRole) |
| 152 | + item.setToolTip(title_name) |
| 153 | + item.setFlags(flags) |
| 154 | + # Append to MODEL (if does not already exist) |
180 | 155 | if path not in self.model_paths:
|
181 |
| - self.model.appendRow(row) |
| 156 | + self.model.appendRow([item]) |
182 | 157 | self.model_paths[path] = path
|
183 | 158 |
|
184 | 159 | # Process events in QT (to keep the interface responsive)
|
185 |
| - app.processEvents() |
186 |
| - |
187 |
| - def __init__(self, *args): |
| 160 | + self.app.processEvents() |
188 | 161 |
|
| 162 | + def __init__(self, *args, **kwargs): |
| 163 | + super().__init__(*args, **kwargs) |
| 164 | + self.setObjectName("TitlesModel") |
189 | 165 | # Create standard model
|
190 | 166 | self.app = get_app()
|
191 |
| - self.model = TitleStandardItemModel() |
192 |
| - self.model.setColumnCount(3) |
| 167 | + self.model = TitleStandardItemModel(self.parent()) |
| 168 | + self.model.setColumnCount(1) |
193 | 169 | self.model_paths = {}
|
| 170 | + |
| 171 | + # Attempt to load model testing interface, if requested |
| 172 | + # (will only succeed with Qt 5.11+) |
| 173 | + if info.MODEL_TEST: |
| 174 | + try: |
| 175 | + # Create model tester objects |
| 176 | + from PyQt5.QtTest import QAbstractItemModelTester |
| 177 | + QAbstractItemModelTester( |
| 178 | + self.model, QAbstractItemModelTester.FailureReportingMode.Warning) |
| 179 | + log.info("Enabled model tests for title editor data") |
| 180 | + except ImportError: |
| 181 | + pass |
0 commit comments