OpenShot Video Editor  2.0.0
preferences.py
Go to the documentation of this file.
1 ##
2 #
3 # @file
4 # @brief This file loads the Preferences dialog (i.e where is all preferences)
5 # @author Jonathan Thomas <jonathan@openshot.org>
6 # @author Olivier Girard <olivier@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 import operator
32 import functools
33 
34 from PyQt5.QtCore import *
35 from PyQt5.QtWidgets import *
36 from PyQt5.QtGui import QKeySequence
37 from PyQt5 import uic
38 
39 from classes import info, ui_util, settings, qt_types, updates
40 from classes.app import get_app
41 from classes.language import get_all_languages
42 from classes.logger import log
43 from classes.metrics import *
44 import openshot
45 
46 
47 ##
48 # Preferences Dialog
49 class Preferences(QDialog):
50 
51  # Path to ui file
52  ui_path = os.path.join(info.PATH, 'windows', 'ui', 'preferences.ui')
53 
54  def __init__(self):
55 
56  # Create dialog class
57  QDialog.__init__(self)
58 
59  # Load UI from designer
60  ui_util.load_ui(self, self.ui_path)
61 
62  # Init UI
63  ui_util.init_ui(self)
64 
65  # get translations
66  app = get_app()
67  _ = app._tr
68 
69  # Get settings
71 
72  # Dynamically load tabs from settings data
73  self.settings_data = settings.get_settings().get_all_settings()
74 
75  # Track metrics
76  track_metric_screen("preferences-screen")
77 
78  # Load all user values
79  self.params = {}
80  for item in self.settings_data:
81  if "setting" in item and "value" in item:
82  self.params[item["setting"]] = item
83 
84  self.requires_restart = False
85  self.category_names = {}
86  self.category_tabs = {}
87  self.category_sort = {}
88 
89  # Loop through settings and find all unique categories
90  for item in self.settings_data:
91  category = item.get("category")
92  setting_type = item.get("type")
93  sort_category = item.get("sort")
94 
95  # Indicate sorted category
96  if sort_category:
97  self.category_sort[category] = sort_category
98 
99  if not setting_type == "hidden":
100  # Load setting
101  if not category in self.category_names:
102  self.category_names[category] = []
103 
104  # Create scrollarea
105  scroll_area = QScrollArea(self)
106  scroll_area.setWidgetResizable(True)
107  scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
108  scroll_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
109 
110  # Create tab widget and layout
111  layout = QVBoxLayout()
112  tabWidget = QWidget(self)
113  tabWidget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
114  tabWidget.setLayout(layout)
115  scroll_area.setWidget(tabWidget)
116 
117  # Add tab
118  self.tabCategories.addTab(scroll_area, _(category))
119  self.category_tabs[category] = tabWidget
120 
121  # Append translated title
122  item["title_tr"] = _(item.get("title"))
123 
124  # Append settings into correct category
125  self.category_names[category].append(item)
126 
127  # Loop through each category setting, and add them to the tabs
128  for category in self.category_tabs.keys():
129  tabWidget = self.category_tabs[category]
130 
131  # Get list of items in category
132  params = self.category_names[category]
133  if self.category_sort.get(category):
134  # Sort this category by translated title
135  params.sort(key=operator.itemgetter("title_tr"))
136 
137  # Loop through settings for each category
138  for param in params:
139 
140  # Create Label
141  widget = None
142  extraWidget = None
143  label = QLabel()
144  label.setText(_(param["title"]))
145  label.setToolTip(_(param["title"]))
146 
147  if param["type"] == "spinner":
148  # create QDoubleSpinBox
149  widget = QDoubleSpinBox()
150  widget.setMinimum(float(param["min"]))
151  widget.setMaximum(float(param["max"]))
152  widget.setValue(float(param["value"]))
153  widget.setSingleStep(1.0)
154  widget.setToolTip(param["title"])
155  widget.valueChanged.connect(functools.partial(self.spinner_value_changed, param))
156 
157  if param["type"] == "spinner-int":
158  # create QDoubleSpinBox
159  widget = QSpinBox()
160  widget.setMinimum(int(param["min"]))
161  widget.setMaximum(int(param["max"]))
162  widget.setValue(int(param["value"]))
163  widget.setSingleStep(1.0)
164  widget.setToolTip(param["title"])
165  widget.valueChanged.connect(functools.partial(self.spinner_value_changed, param))
166 
167  elif param["type"] == "text":
168  # create QLineEdit
169  widget = QLineEdit()
170  widget.setText(_(param["value"]))
171  widget.textChanged.connect(functools.partial(self.text_value_changed, widget, param))
172 
173  elif param["type"] == "browse":
174  # create QLineEdit
175  widget = QLineEdit()
176  widget.setText(_(param["value"]))
177  widget.textChanged.connect(functools.partial(self.text_value_changed, widget, param))
178  extraWidget = QPushButton(_("Browse..."))
179  extraWidget.clicked.connect(functools.partial(self.selectExecutable, widget, param))
180 
181  elif param["type"] == "bool":
182  # create spinner
183  widget = QCheckBox()
184  if param["value"] == True:
185  widget.setCheckState(Qt.Checked)
186  else:
187  widget.setCheckState(Qt.Unchecked)
188  widget.stateChanged.connect(functools.partial(self.bool_value_changed, widget, param))
189 
190  elif param["type"] == "dropdown":
191 
192  # create spinner
193  widget = QComboBox()
194 
195  # Get values
196  value_list = param["values"]
197  # Overwrite value list (for profile dropdown)
198  if param["setting"] == "default-profile":
199  value_list = []
200  # Loop through profiles
201  for profile_folder in [info.USER_PROFILES_PATH, info.PROFILES_PATH]:
202  for file in os.listdir(profile_folder):
203  # Load Profile and append description
204  profile_path = os.path.join(profile_folder, file)
205  profile = openshot.Profile(profile_path)
206  value_list.append({"name":profile.info.description, "value":profile.info.description})
207  # Sort profile list
208  value_list.sort(key=operator.itemgetter("name"))
209 
210  # Overwrite value list (for language dropdown)
211  if param["setting"] == "default-language":
212  value_list = []
213  # Loop through languages
214  for locale, language, country in get_all_languages():
215  # Load Profile and append description
216  if language:
217  lang_name = "%s (%s)" % (language, locale)
218  value_list.append({"name":lang_name, "value":locale})
219  # Sort profile list
220  value_list.sort(key=operator.itemgetter("name"))
221  # Add Default to top of list
222  value_list.insert(0, {"name":_("Default"), "value":"Default"})
223 
224 
225  # Add normal values
226  box_index = 0
227  for value_item in value_list:
228  k = value_item["name"]
229  v = value_item["value"]
230  # add dropdown item
231  widget.addItem(_(k), v)
232 
233  # select dropdown (if default)
234  if v == param["value"]:
235  widget.setCurrentIndex(box_index)
236  box_index = box_index + 1
237 
238  widget.currentIndexChanged.connect(functools.partial(self.dropdown_index_changed, widget, param))
239 
240 
241  # Add Label and Widget to the form
242  if (widget and label):
243  # Add minimum size
244  label.setMinimumWidth(180);
245  label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
246  widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
247 
248  # Create HBox layout
249  layout_hbox = QHBoxLayout()
250  layout_hbox.addWidget(label)
251  layout_hbox.addWidget(widget)
252 
253  if (extraWidget):
254  layout_hbox.addWidget(extraWidget)
255 
256  # Add widget to layout
257  tabWidget.layout().addLayout(layout_hbox)
258  elif (label):
259  # Add widget to layout
260  tabWidget.layout().addWidget(label)
261 
262  # Add stretch to bottom of layout
263  tabWidget.layout().addStretch()
264 
265  def selectExecutable(self, widget, param):
266  _ = get_app()._tr
267 
268  fileName, fileType = QFileDialog.getOpenFileName(self, _("Select executable file"), QDir.rootPath(), _("All Files (*)"))
269  if fileName:
270  self.s.set(param["setting"], fileName)
271  widget.setText(fileName)
272 
273  ##
274  # Check if the app needs to restart
275  def check_for_restart(self, param):
276  if "restart" in param and param["restart"]:
277  self.requires_restart = True
278 
279  def bool_value_changed(self, widget, param, state):
280  # Save setting
281  if state == Qt.Checked:
282  self.s.set(param["setting"], True)
283  else:
284  self.s.set(param["setting"], False)
285 
286  # Trigger specific actions
287  if param["setting"] == "debug-mode":
288  # Update debug setting of timeline
289  log.info("Setting debug-mode to %s" % (state == Qt.Checked))
290  debug_enabled = (state == Qt.Checked)
291 
292  # Enable / Disable logger
293  openshot.ZmqLogger.Instance().Enable(debug_enabled)
294 
295  elif param["setting"] == "enable-auto-save":
296  # Toggle autosave
297  if (state == Qt.Checked):
298  # Start/Restart autosave timer
299  get_app().window.auto_save_timer.start()
300  else:
301  # Stop autosave timer
302  get_app().window.auto_save_timer.stop()
303 
304  elif param["setting"] == "hardware_decode":
305  if (state == Qt.Checked):
306  # Enable hardware decode
307  openshot.Settings.Instance().HARDWARE_DECODE = True
308  else:
309  # Disable hardware decode
310  openshot.Settings.Instance().HARDWARE_DECODE = False
311 
312  elif param["setting"] == "hardware_encode":
313  if (state == Qt.Checked):
314  # Enable hardware encode
315  openshot.Settings.Instance().HARDWARE_ENCODE = True
316  else:
317  # Disable hardware encode
318  openshot.Settings.Instance().HARDWARE_ENCODE = False
319 
320  elif param["setting"] == "omp_threads_enabled":
321  if (state == Qt.Checked):
322  # Enable OMP multi-threading
323  openshot.Settings.Instance().WAIT_FOR_VIDEO_PROCESSING_TASK = False
324  else:
325  # Disable OMP multi-threading
326  openshot.Settings.Instance().WAIT_FOR_VIDEO_PROCESSING_TASK = True
327 
328  # Check for restart
329  self.check_for_restart(param)
330 
331  def spinner_value_changed(self, param, value):
332  # Save setting
333  self.s.set(param["setting"], value)
334  log.info(value)
335 
336  if param["setting"] == "autosave-interval":
337  # Update autosave interval (# of minutes)
338  get_app().window.auto_save_timer.setInterval(value * 1000 * 60)
339 
340  # Apply cache settings (if needed)
341  if param["setting"] in ["cache-limit-mb", "cache-scale", "cache-quality"]:
342  get_app().window.InitCacheSettings()
343 
344  # Check for restart
345  self.check_for_restart(param)
346 
347  def text_value_changed(self, widget, param, value=None):
348  try:
349  # Attempt to load value from QTextEdit (i.e. multi-line)
350  if not value:
351  value = widget.toPlainText()
352  except:
353  pass
354 
355  # If this setting is a keyboard mapping, parse it first
356  if param.get("category") == "Keyboard":
357  previous_value = value
358  value = QKeySequence(value).toString()
359  log.info("Parsing keyboard mapping via QKeySequence from %s to %s" % (previous_value, value))
360 
361  # Save setting
362  self.s.set(param["setting"], value)
363  log.info(value)
364 
365  # Check for restart
366  self.check_for_restart(param)
367 
368  def dropdown_index_changed(self, widget, param, index):
369  # Save setting
370  value = widget.itemData(index)
371  self.s.set(param["setting"], value)
372  log.info(value)
373 
374  # Apply cache settings (if needed)
375  if param["setting"] in ["cache-mode", "cache-image-format"]:
376  get_app().window.InitCacheSettings()
377 
378  # Check for restart
379  self.check_for_restart(param)
380 
381  ##
382  # Signal for closing Preferences window
383  def closeEvent(self, event):
384  # Invoke the close button
385  self.reject()
386 
387  def reject(self):
388  # Prompt user to restart openshot (if needed)
389  if self.requires_restart:
390  msg = QMessageBox()
391  _ = get_app()._tr
392  msg.setWindowTitle(_("Restart Required"))
393  msg.setText(_("Please restart OpenShot for all preferences to take effect."))
394  msg.exec_()
395 
396  # Close dialog
397  super(Preferences, self).reject()
def track_metric_screen
Track a GUI screen being shown.
Definition: metrics.py:96
def get_app
Returns the current QApplication instance of OpenShot.
Definition: app.py:55
def check_for_restart
Check if the app needs to restart.
Definition: preferences.py:275
def load_ui
Load a Qt *.ui file, and also load an XML parsed version.
Definition: ui_util.py:66
def closeEvent
Signal for closing Preferences window.
Definition: preferences.py:383
def init_ui
Initialize all child widgets and action of a window or dialog.
Definition: ui_util.py:215
Preferences Dialog.
Definition: preferences.py:49
def get_all_languages
Get all language names and countries packaged with OpenShot.
Definition: language.py:142
def get_settings
Get the current QApplication's settings instance.
Definition: settings.py:44