OpenShot Video Editor  2.0.0
files_model.py
Go to the documentation of this file.
1 ##
2 #
3 # @file
4 # @brief This file contains the project file model, used by the project tree
5 # @author Noah Figg <eggmunkee@hotmail.com>
6 # @author Jonathan Thomas <jonathan@openshot.org>
7 #
8 # @section LICENSE
9 #
10 # Copyright (c) 2008-2018 OpenShot Studios, LLC
11 # (http://www.openshotstudios.com). This file is part of
12 # OpenShot Video Editor (http://www.openshot.org), an open-source project
13 # dedicated to delivering high quality video editing and animation solutions
14 # to the world.
15 #
16 # OpenShot Video Editor is free software: you can redistribute it and/or modify
17 # it under the terms of the GNU General Public License as published by
18 # the Free Software Foundation, either version 3 of the License, or
19 # (at your option) any later version.
20 #
21 # OpenShot Video Editor is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 # GNU General Public License for more details.
25 #
26 # You should have received a copy of the GNU General Public License
27 # along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
28 #
29 
30 import os
31 
32 from PyQt5.QtCore import QMimeData, Qt, pyqtSignal
33 from PyQt5.QtGui import *
34 from PyQt5.QtWidgets import QMessageBox
35 import openshot # Python module for libopenshot (required video editing module installed separately)
36 
37 from classes import updates
38 from classes import info
39 from classes.query import File
40 from classes.logger import log
41 from classes.app import get_app
42 from classes.thumbnail import GenerateThumbnail
43 
44 try:
45  import json
46 except ImportError:
47  import simplejson as json
48 
49 
50 class FileStandardItemModel(QStandardItemModel):
51  ModelRefreshed = pyqtSignal()
52 
53  def __init__(self, parent=None):
54  QStandardItemModel.__init__(self)
55 
56  def mimeData(self, indexes):
57  # Create MimeData for drag operation
58  data = QMimeData()
59 
60  # Get list of all selected file ids
61  files = []
62  for item in indexes:
63  selected_row = self.itemFromIndex(item).row()
64  files.append(self.item(selected_row, 5).text())
65  data.setText(json.dumps(files))
66  data.setHtml("clip")
67 
68  # Return Mimedata
69  return data
70 
71 
73  # This method is invoked by the UpdateManager each time a change happens (i.e UpdateInterface)
74  def changed(self, action):
75 
76  # Something was changed in the 'files' list
77  if len(action.key) >= 1 and action.key[0].lower() == "files":
78  # Refresh project files model
79  if action.type == "insert":
80  # Don't clear the existing items if only inserting new things
81  self.update_model(clear=False)
82  else:
83  # Clear existing items
84  self.update_model(clear=True)
85 
86  def update_model(self, clear=True):
87  log.info("updating files model.")
88  app = get_app()
89 
90  # Get window to check filters
91  win = app.window
92  _ = app._tr
93 
94  # Skip updates (if needed)
95  if self.ignore_update_signal:
96  return
97 
98  # Clear all items
99  if clear:
100  self.model_ids = {}
101  self.model.clear()
102 
103  # Add Headers
104  self.model.setHorizontalHeaderLabels(["", _("Name"), _("Tags"), "", "", ""])
105 
106  # Get list of files in project
107  files = File.filter() # get all files
108 
109  # add item for each file
110  for file in files:
111  path, filename = os.path.split(file.data["path"])
112  tags = ""
113  if "tags" in file.data.keys():
114  tags = file.data["tags"]
115  name = filename
116  if "name" in file.data.keys():
117  name = file.data["name"]
118 
119  if not win.actionFilesShowAll.isChecked():
120  if win.actionFilesShowVideo.isChecked():
121  if not file.data["media_type"] == "video":
122  continue # to next file, didn't match filter
123  elif win.actionFilesShowAudio.isChecked():
124  if not file.data["media_type"] == "audio":
125  continue # to next file, didn't match filter
126  elif win.actionFilesShowImage.isChecked():
127  if not file.data["media_type"] == "image":
128  continue # to next file, didn't match filter
129 
130 
131  if win.filesFilter.text() != "":
132  if not win.filesFilter.text().lower() in filename.lower() \
133  and not win.filesFilter.text().lower() in tags.lower() \
134  and not win.filesFilter.text().lower() in name.lower():
135  continue
136 
137  # Generate thumbnail for file (if needed)
138  if (file.data["media_type"] == "video" or file.data["media_type"] == "image"):
139  # Determine thumb path
140  thumb_path = os.path.join(info.THUMBNAIL_PATH, "{}.png".format(file.id))
141 
142  # Check if thumb exists
143  if not os.path.exists(thumb_path):
144 
145  try:
146  # Convert path to the correct relative path (based on this folder)
147  file_path = file.absolute_path()
148 
149  # Determine if video overlay should be applied to thumbnail
150  overlay_path = ""
151  if file.data["media_type"] == "video":
152  overlay_path = os.path.join(info.IMAGES_PATH, "overlay.png")
153 
154  # Check for start and end attributes (optional)
155  thumbnail_frame = 1
156  if 'start' in file.data.keys():
157  fps = file.data["fps"]
158  fps_float = float(fps["num"]) / float(fps["den"])
159  thumbnail_frame = round(float(file.data['start']) * fps_float) + 1
160 
161  # Create thumbnail image
162  GenerateThumbnail(file_path, thumb_path, thumbnail_frame, 98, 64, os.path.join(info.IMAGES_PATH, "mask.png"), overlay_path)
163 
164  except:
165  # Handle exception
166  msg = QMessageBox()
167  msg.setText(_("{} is not a valid video, audio, or image file.".format(filename)))
168  msg.exec_()
169  continue
170 
171  else:
172  # Audio file
173  thumb_path = os.path.join(info.PATH, "images", "AudioThumbnail.png")
174 
175  row = []
176 
177  # Append thumbnail
178  col = QStandardItem()
179  col.setIcon(QIcon(thumb_path))
180  col.setText(name)
181  col.setToolTip(filename)
182  col.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
183  row.append(col)
184 
185  # Append Filename
186  col = QStandardItem("Name")
187  col.setData(filename, Qt.DisplayRole)
188  col.setText(name)
189  col.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled | Qt.ItemIsEditable)
190  row.append(col)
191 
192  # Append Tags
193  col = QStandardItem("Tags")
194  col.setData(tags, Qt.DisplayRole)
195  col.setText(tags)
196  col.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled | Qt.ItemIsEditable)
197  row.append(col)
198 
199  # Append Media Type
200  col = QStandardItem("Type")
201  col.setData(file.data["media_type"], Qt.DisplayRole)
202  col.setText(file.data["media_type"])
203  col.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled | Qt.ItemIsEditable)
204  row.append(col)
205 
206  # Append Path
207  col = QStandardItem("Path")
208  col.setData(path, Qt.DisplayRole)
209  col.setText(path)
210  col.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | Qt.ItemIsDragEnabled)
211  row.append(col)
212 
213  # Append ID
214  col = QStandardItem("ID")
215  col.setData(file.data["id"], Qt.DisplayRole)
216  col.setText(file.data["id"])
217  col.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | Qt.ItemIsDragEnabled)
218  row.append(col)
219 
220  # Append ROW to MODEL (if does not already exist in model)
221  if not file.data["id"] in self.model_ids:
222  self.model.appendRow(row)
223  self.model_ids[file.data["id"]] = file.data["id"]
224 
225  # Process events in QT (to keep the interface responsive)
226  app.processEvents()
227 
228  # Refresh view and filters (to hide or show this new item)
229  get_app().window.resize_contents()
230 
231  # Emit signal
232  self.model.ModelRefreshed.emit()
233 
234  def __init__(self, *args):
235 
236  # Add self as listener to project data updates (undo/redo, as well as normal actions handled within this class all update the tree model)
237  app = get_app()
238  app.updates.add_listener(self)
239 
240  # Create standard model
242  self.model.setColumnCount(6)
243  self.model_ids = {}
244  self.ignore_update_signal = False
def get_app
Returns the current QApplication instance of OpenShot.
Definition: app.py:55
def GenerateThumbnail
Create thumbnail image, and check for rotate metadata (if any)
Definition: thumbnail.py:35
Interface for classes that listen for changes (insert, update, and delete).
Definition: updates.py:54