OpenShot Video Editor  2.0.0
ui_util.py
Go to the documentation of this file.
1 ##
2 #
3 # @file
4 # @brief This file contains PyQt help functions, to translate the interface, load icons, and connect signals
5 # @author Noah Figg <eggmunkee@hotmail.com>
6 # @author Jonathan Thomas <jonathan@openshot.org>
7 # @author Olivier Girard <eolinwen@gmail.com>
8 #
9 # @section LICENSE
10 #
11 # Copyright (c) 2008-2018 OpenShot Studios, LLC
12 # (http://www.openshotstudios.com). This file is part of
13 # OpenShot Video Editor (http://www.openshot.org), an open-source project
14 # dedicated to delivering high quality video editing and animation solutions
15 # to the world.
16 #
17 # OpenShot Video Editor is free software: you can redistribute it and/or modify
18 # it under the terms of the GNU General Public License as published by
19 # the Free Software Foundation, either version 3 of the License, or
20 # (at your option) any later version.
21 #
22 # OpenShot Video Editor is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 # GNU General Public License for more details.
26 #
27 # You should have received a copy of the GNU General Public License
28 # along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
29 #
30 
31 import os
32 import xml.etree.ElementTree
33 import time
34 
35 from PyQt5.QtCore import QDir, QLocale
36 from PyQt5.QtGui import QIcon
37 from PyQt5.QtWidgets import *
38 from PyQt5 import uic
39 
40 from classes.logger import log
41 from classes import settings
42 
43 DEFAULT_THEME_NAME = "Humanity"
44 
45 
46 ##
47 # Load the current OS theme, or fallback to a default one
48 def load_theme():
49 
51 
52  # If theme not reported by OS
53  if QIcon.themeName() == '' and not s.get("theme") == "No Theme":
54 
55  # Address known Ubuntu bug of not reporting configured theme name, use default ubuntu theme
56  if os.getenv('DESKTOP_SESSION') == 'ubuntu':
57  QIcon.setThemeName('unity-icon-theme')
58 
59  # Windows/Mac use packaged theme
60  else:
61  QIcon.setThemeName(DEFAULT_THEME_NAME)
62 
63 
64 ##
65 # Load a Qt *.ui file, and also load an XML parsed version
66 def load_ui(window, path):
67  # Attempt to load the UI file 5 times
68  # This is a hack, and I'm trying to avoid a really common error which might be a
69  # race condition. [zipimport.ZipImportError: can't decompress data; zlib not available]
70  # This error only happens when cx_Freeze is used, and the app is launched.
71  error = None
72  for attempt in range(1,6):
73  try:
74  # Load ui from configured path
75  uic.loadUi(path, window)
76 
77  # Successfully loaded UI file, so clear any previously encountered errors
78  error = None
79  break
80 
81  except Exception as ex:
82  # Keep track of this error
83  error = ex
84  time.sleep(0.1)
85 
86  # Raise error (if any)
87  if error:
88  raise error
89 
90  # Save xml tree for ui
91  window.uiTree = xml.etree.ElementTree.parse(path)
92 
93 
94 ##
95 # Get a QIcon, and fallback to default theme if OS does not support themes.
96 def get_default_icon(theme_name):
97 
98  # Default path to backup icons
99  start_path = ":/icons/" + DEFAULT_THEME_NAME + "/"
100  icon_path = search_dir(start_path, theme_name)
101  return QIcon(icon_path), icon_path
102 
103 
104 ##
105 # Search for theme name
106 def search_dir(base_path, theme_name):
107 
108  # Search each entry in this directory
109  base_dir = QDir(base_path)
110  for e in base_dir.entryList():
111  # Path to current item
112  path = base_dir.path() + "/" + e
113  base_filename = e.split('.')[0]
114 
115  # If file matches theme name, return
116  if base_filename == theme_name:
117  return path
118 
119  # If this is a directory, search within it
120  dir = QDir(path)
121  if dir.exists():
122  # If found below, return it
123  res = search_dir(path, theme_name)
124  if res:
125  return res
126 
127  # If no match found in dir, return None
128  return None
129 
130 
131 ##
132 # Get either the current theme icon or fallback to default theme (for custom icons). Returns None if none
133 # found or empty name.
134 def get_icon(theme_name):
135 
136  if theme_name:
137  has_icon = QIcon.hasThemeIcon(theme_name)
138  if not has_icon:
139  log.warn('Icon theme {} not found. Will use backup icon.'.format(theme_name))
140  fallback_icon, fallback_path = get_default_icon(theme_name)
141  # log.info('Fallback icon path for {} is {}'.format(theme_name, fallback_path))
142  if has_icon or fallback_icon:
143  return QIcon.fromTheme(theme_name, fallback_icon)
144  return None
145 
146 
147 ##
148 # Using the window xml, set the icon on the given element, or if theme_name passed load that icon.
149 def setup_icon(window, elem, name, theme_name=None):
150 
151  type_filter = 'action'
152  if isinstance(elem, QWidget): # Search for widget with name instead
153  type_filter = 'widget'
154  # Find iconset in tree (if any)
155  iconset = window.uiTree.find('.//' + type_filter + '[@name="' + name + '"]/property[@name="icon"]/iconset')
156  if iconset != None or theme_name: # For some reason "if iconset:" doesn't work the same as "!= None"
157  if not theme_name:
158  theme_name = iconset.get('theme', '')
159  # Get Icon (either current theme or fallback)
160  icon = get_icon(theme_name)
161  if icon:
162  elem.setIcon(icon)
163 
164 
165 ##
166 # Initialize language and icons of the given element
167 def init_element(window, elem):
168 
169  _translate = QApplication.instance().translate
170 
171  name = ''
172  if hasattr(elem, 'objectName'):
173  name = elem.objectName()
174  connect_auto_events(window, elem, name)
175 
176  # Handle generic translatable properties
177  if hasattr(elem, 'setText') and hasattr(elem, 'text') and elem.text() != "":
178  elem.setText(_translate("", elem.text()))
179  if hasattr(elem, 'setToolTip') and hasattr(elem, 'toolTip') and elem.toolTip() != "":
180  elem.setToolTip(_translate("", elem.toolTip()))
181  if hasattr(elem, 'setWindowTitle') and hasattr(elem, 'windowTitle') and elem.windowTitle() != "":
182  elem.setWindowTitle(_translate("", elem.windowTitle()))
183  if hasattr(elem, 'setTitle') and hasattr(elem, 'title') and elem.title() != "":
184  elem.setTitle(_translate("", elem.title()))
185  if hasattr(elem, 'setPlaceholderText') and hasattr(elem, 'placeholderText') and elem.placeholderText() != "":
186  elem.setPlaceholderText(_translate("", elem.placeholderText()))
187  if hasattr(elem, 'setLocale'):
188  elem.setLocale(QLocale().system())
189  # Handle tabs differently
190  if isinstance(elem, QTabWidget):
191  for i in range(elem.count()):
192  elem.setTabText(i, _translate("", elem.tabText(i)))
193  elem.setTabToolTip(i, _translate("", elem.tabToolTip(i)))
194  # Set icon if possible
195  if hasattr(elem, 'setIcon') and name != '': # Has ability to set its icon
196  setup_icon(window, elem, name)
197 
198 
199 ##
200 # Connect any events in a *.ui file with matching Python method names
201 def connect_auto_events(window, elem, name):
202 
203  # If trigger slot available check it
204  if hasattr(elem, 'trigger'):
205  func_name = name + "_trigger"
206  if hasattr(window, func_name) and callable(getattr(window, func_name)):
207  func = getattr(window, func_name)
208  log.info("Binding event {}:{}".format(window.objectName(), func_name))
209  elem.triggered.connect(getattr(window, func_name))
210  if hasattr(elem, 'click'):
211  func_name = name + "_click"
212  if hasattr(window, func_name) and callable(getattr(window, func_name)):
213  func = getattr(window, func_name)
214  log.info("Binding event {}:{}".format(window.objectName(), func_name))
215  elem.clicked.connect(getattr(window, func_name))
216 
217 
218 ##
219 # Initialize all child widgets and action of a window or dialog
220 def init_ui(window):
221  log.info('Initializing UI for {}'.format(window.objectName()))
222 
223  try:
224  # Set locale & window title on the window object
225  if hasattr(window, 'setWindowTitle') and window.windowTitle() != "":
226  _translate = QApplication.instance().translate
227  window.setWindowTitle(_translate("", window.windowTitle()))
228 
229  # Center window
230  center(window)
231 
232  # Loop through all widgets
233  for widget in window.findChildren(QWidget):
234  init_element(window, widget)
235 
236  # Loop through all actions
237  for action in window.findChildren(QAction):
238  # log.info('Initializing element: {}'.format(action))
239  init_element(window, action)
240  except:
241  log.info('Failed to initialize an element on {}'.format(window.objectName()))
242 
243 ##
244 # Center a window on the main window
245 def center(window):
246  from classes.app import get_app
247 
248  frameGm = window.frameGeometry()
249  centerPoint = get_app().window.frameGeometry().center()
250  frameGm.moveCenter(centerPoint)
251  window.move(frameGm.topLeft())
252 
253 def transfer_children(from_widget, to_widget):
254  log.info("Transferring children from '{}' to '{}'".format(from_widget.objectName(), to_widget.objectName()))
def setup_icon(window, elem, name, theme_name=None)
Using the window xml, set the icon on the given element, or if theme_name passed load that icon...
Definition: ui_util.py:149
def center(window)
Center a window on the main window.
Definition: ui_util.py:245
def get_app()
Returns the current QApplication instance of OpenShot.
Definition: app.py:55
def get_icon(theme_name)
Get either the current theme icon or fallback to default theme (for custom icons).
Definition: ui_util.py:134
def get_default_icon(theme_name)
Get a QIcon, and fallback to default theme if OS does not support themes.
Definition: ui_util.py:96
def transfer_children(from_widget, to_widget)
Definition: ui_util.py:253
def get_settings()
Get the current QApplication&#39;s settings instance.
Definition: settings.py:44
def connect_auto_events(window, elem, name)
Connect any events in a *.ui file with matching Python method names.
Definition: ui_util.py:201
def load_theme()
Load the current OS theme, or fallback to a default one.
Definition: ui_util.py:48
def search_dir(base_path, theme_name)
Search for theme name.
Definition: ui_util.py:106
def init_ui(window)
Initialize all child widgets and action of a window or dialog.
Definition: ui_util.py:220
def init_element(window, elem)
Initialize language and icons of the given element.
Definition: ui_util.py:167
def load_ui(window, path)
Load a Qt *.ui file, and also load an XML parsed version.
Definition: ui_util.py:66