qthelpers.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. # -*- coding: utf-8 -*-
  2. #
  3. # Copyright © 2009-2011 Pierre Raybaut
  4. # Licensed under the terms of the MIT License
  5. # (copied from Spyder source code [spyderlib.qt])
  6. #
  7. # Qt5 migration would not have been possible without
  8. # 2014-2015 Spyder Development Team work
  9. # (MIT License too, same parent project)
  10. """Qt utilities"""
  11. # winpython.qt becomes winpython._vendor.qtpy
  12. from winpython._vendor.qtpy.QtWidgets import (
  13. QAction,
  14. QStyle,
  15. QWidget,
  16. QApplication,
  17. QLabel,
  18. QVBoxLayout,
  19. QHBoxLayout,
  20. QLineEdit,
  21. QMenu,
  22. QToolButton,
  23. )
  24. from winpython._vendor.qtpy.QtGui import (
  25. QIcon,
  26. QKeyEvent,
  27. QKeySequence,
  28. QPixmap,
  29. )
  30. from winpython._vendor.qtpy.QtCore import (
  31. Signal,
  32. QObject,
  33. Qt,
  34. QLocale,
  35. QTranslator,
  36. QLibraryInfo,
  37. QEvent,
  38. Slot,
  39. )
  40. from winpython._vendor.qtpy.compat import (
  41. to_qvariant,
  42. from_qvariant,
  43. )
  44. import os
  45. import re
  46. import os.path as osp
  47. import sys
  48. # Local import
  49. from winpython import config
  50. from winpython.py3compat import (
  51. is_text_string,
  52. to_text_string,
  53. )
  54. def get_icon(name):
  55. """Return QIcon from icon name"""
  56. return QIcon(osp.join(config.IMAGE_PATH, name))
  57. class MacApplication(QApplication):
  58. """Subclass to be able to open external files with our Mac app"""
  59. open_external_file = Signal(str)
  60. def __init__(self, *args):
  61. QApplication.__init__(self, *args)
  62. def event(self, event):
  63. if event.type() == QEvent.FileOpen:
  64. fname = str(event.file())
  65. # PyQt4 old SIGNAL: self.emit(SIGNAL('open_external_file(QString)'), fname)
  66. self.open_external_file.emit(fname)
  67. return QApplication.event(self, event)
  68. def qapplication(translate=True):
  69. """Return QApplication instance
  70. Creates it if it doesn't already exist"""
  71. if (
  72. sys.platform == "darwin"
  73. and 'Spyder.app' in __file__
  74. ):
  75. SpyderApplication = MacApplication
  76. else:
  77. SpyderApplication = QApplication
  78. app = SpyderApplication.instance()
  79. if not app:
  80. # Set Application name for Gnome 3
  81. # https://groups.google.com/forum/#!topic/pyside/24qxvwfrRDs
  82. app = SpyderApplication(['Spyder'])
  83. if translate:
  84. install_translator(app)
  85. return app
  86. def file_uri(fname):
  87. """Select the right file uri scheme according to the operating system"""
  88. if os.name == 'nt':
  89. # Local file
  90. if re.search(r'^[a-zA-Z]:', fname):
  91. return 'file:///' + fname
  92. # UNC based path
  93. else:
  94. return 'file://' + fname
  95. else:
  96. return 'file://' + fname
  97. QT_TRANSLATOR = None
  98. def install_translator(qapp):
  99. """Install Qt translator to the QApplication instance"""
  100. global QT_TRANSLATOR
  101. if QT_TRANSLATOR is None:
  102. qt_translator = QTranslator()
  103. if qt_translator.load(
  104. "qt_" + QLocale.system().name(),
  105. QLibraryInfo.location(
  106. QLibraryInfo.TranslationsPath
  107. ),
  108. ):
  109. QT_TRANSLATOR = (
  110. qt_translator
  111. ) # Keep reference alive
  112. if QT_TRANSLATOR is not None:
  113. qapp.installTranslator(QT_TRANSLATOR)
  114. def keybinding(attr):
  115. """Return keybinding"""
  116. ks = getattr(QKeySequence, attr)
  117. return from_qvariant(
  118. QKeySequence.keyBindings(ks)[0], str
  119. )
  120. def _process_mime_path(path, extlist):
  121. if path.startswith(r"file://"):
  122. if os.name == 'nt':
  123. # On Windows platforms, a local path reads: file:///c:/...
  124. # and a UNC based path reads like: file://server/share
  125. if path.startswith(
  126. r"file:///"
  127. ): # this is a local path
  128. path = path[8:]
  129. else: # this is a unc path
  130. path = path[5:]
  131. else:
  132. path = path[7:]
  133. if osp.exists(path):
  134. if (
  135. extlist is None
  136. or osp.splitext(path)[1] in extlist
  137. ):
  138. return path
  139. def mimedata2url(source, extlist=None):
  140. """
  141. Extract url list from MIME data
  142. extlist: for example ('.py', '.pyw')
  143. """
  144. pathlist = []
  145. if source.hasUrls():
  146. for url in source.urls():
  147. path = _process_mime_path(
  148. to_text_string(url.toString()), extlist
  149. )
  150. if path is not None:
  151. pathlist.append(path)
  152. elif source.hasText():
  153. for rawpath in to_text_string(
  154. source.text()
  155. ).splitlines():
  156. path = _process_mime_path(rawpath, extlist)
  157. if path is not None:
  158. pathlist.append(path)
  159. if pathlist:
  160. return pathlist
  161. def action2button(
  162. action,
  163. autoraise=True,
  164. text_beside_icon=False,
  165. parent=None,
  166. ):
  167. """Create a QToolButton directly from a QAction object"""
  168. if parent is None:
  169. parent = action.parent()
  170. button = QToolButton(parent)
  171. button.setDefaultAction(action)
  172. button.setAutoRaise(autoraise)
  173. if text_beside_icon:
  174. button.setToolButtonStyle(
  175. Qt.ToolButtonTextBesideIcon
  176. )
  177. return button
  178. def toggle_actions(actions, enable):
  179. """Enable/disable actions"""
  180. if actions is not None:
  181. for action in actions:
  182. if action is not None:
  183. action.setEnabled(enable)
  184. def create_action(
  185. parent,
  186. text,
  187. shortcut=None,
  188. icon=None,
  189. tip=None,
  190. toggled=None,
  191. triggered=None,
  192. data=None,
  193. menurole=None,
  194. context=Qt.WindowShortcut,
  195. ):
  196. """Create a QAction"""
  197. action = QAction(text, parent)
  198. if triggered is not None:
  199. # PyQt4 old SIGNAL: parent.connect(action, SIGNAL("triggered()"), triggered)
  200. action.triggered.connect(triggered)
  201. if toggled is not None:
  202. # PyQt4 old SIGNAL: parent.connect(action, SIGNAL("toggled(bool)"), toggled)
  203. action.toggled.connect(toggled)
  204. action.setCheckable(True)
  205. if icon is not None:
  206. if is_text_string(icon):
  207. icon = get_icon(icon)
  208. action.setIcon(icon)
  209. if shortcut is not None:
  210. action.setShortcut(shortcut)
  211. if tip is not None:
  212. action.setToolTip(tip)
  213. action.setStatusTip(tip)
  214. if data is not None:
  215. action.setData(to_qvariant(data))
  216. if menurole is not None:
  217. action.setMenuRole(menurole)
  218. # TODO: Hard-code all shortcuts and choose context=Qt.WidgetShortcut
  219. # (this will avoid calling shortcuts from another dockwidget
  220. # since the context thing doesn't work quite well with these widgets)
  221. action.setShortcutContext(context)
  222. return action
  223. def add_actions(target, actions, insert_before=None):
  224. """Add actions to a menu"""
  225. previous_action = None
  226. target_actions = list(target.actions())
  227. if target_actions:
  228. previous_action = target_actions[-1]
  229. if previous_action.isSeparator():
  230. previous_action = None
  231. for action in actions:
  232. if (action is None) and (
  233. previous_action is not None
  234. ):
  235. if insert_before is None:
  236. target.addSeparator()
  237. else:
  238. target.insertSeparator(insert_before)
  239. elif isinstance(action, QMenu):
  240. if insert_before is None:
  241. target.addMenu(action)
  242. else:
  243. target.insertMenu(insert_before, action)
  244. elif isinstance(action, QAction):
  245. if insert_before is None:
  246. target.addAction(action)
  247. else:
  248. target.insertAction(insert_before, action)
  249. previous_action = action
  250. def get_std_icon(name, size=None):
  251. """Get standard platform icon
  252. Call 'show_std_icons()' for details"""
  253. if not name.startswith('SP_'):
  254. name = 'SP_' + name
  255. icon = (
  256. QWidget()
  257. .style()
  258. .standardIcon(getattr(QStyle, name))
  259. )
  260. if size is None:
  261. return icon
  262. else:
  263. return QIcon(icon.pixmap(size, size))