22
33This module replaces itself with the most desirable binding.
44
5- Resolution order:
5+ Project goals:
6+ Qt.py was born in the film and visual effects industry to address
7+ the growing need for the development of software capable of running
8+ with more than one flavour of the Qt bindings for Python - PySide,
9+ PySide2, PyQt4 and PyQt5.
10+
11+ 1. Build for one, run with all
12+ 2. Explicit is better than implicit
13+ 3. Support co-existence
14+
15+ Default resolution order:
616 - PySide2
717 - PyQt5
818 - PySide
2131import os
2232import sys
2333
24- __version__ = "0.3.4"
34+ __version__ = "0.4.0"
35+
36+ # All unique members of Qt.py
37+ __added__ = list ()
38+
39+ # Members copied from elsewhere, such as QtGui -> QtWidgets
40+ __remapped__ = list ()
41+
42+ # Existing members modified in some way
43+ __modified__ = list ()
44+
45+
46+ def remap (object , name , value , safe = True ):
47+ """Prevent accidental assignment of existing members
48+
49+ Arguments:
50+ object (object): Parent of new attribute
51+ name (str): Name of new attribute
52+ value (object): Value of new attribute
53+ safe (bool): Whether or not to guarantee that
54+ the new attribute was not overwritten.
55+ Can be set to False under condition that
56+ it is superseded by extensive testing.
57+
58+ """
59+
60+ if safe :
61+ # Cannot alter original binding.
62+ if hasattr (object , name ):
63+ raise AttributeError ("Cannot override existing name: "
64+ "%s.%s" % (object .__name__ , name ))
2565
66+ # Cannot alter classes of functions
67+ if type (object ).__name__ != "module" :
68+ raise AttributeError ("%s != 'module': Cannot alter "
69+ "anything but modules" % object )
2670
27- def _pyqt5 ():
71+ __remapped__ .append (name )
72+ setattr (object , name , value )
73+
74+
75+ def add (object , name , value ):
76+ """Identical to :func:`remap` and provided for readability only"""
77+ __added__ .append (name )
78+ remap (object , name , value )
79+
80+
81+ def pyqt5 ():
2882 import PyQt5 .Qt
83+ from PyQt5 import QtCore , uic
2984
30- # Remap
31- PyQt5 .QtCore .Signal = PyQt5 .QtCore .pyqtSignal
32- PyQt5 .QtCore .Slot = PyQt5 .QtCore .pyqtSlot
33- PyQt5 .QtCore .Property = PyQt5 .QtCore .pyqtProperty
85+ remap (QtCore , "Signal" , QtCore .pyqtSignal )
86+ remap (QtCore , "Slot" , QtCore .pyqtSlot )
87+ remap (QtCore , "Property" , QtCore .pyqtProperty )
3488
35- # Add
36- PyQt5 .__wrapper_version__ = __version__
37- PyQt5 .__binding__ = "PyQt5"
38- PyQt5 .__binding_version__ = PyQt5 .QtCore .PYQT_VERSION_STR
39- PyQt5 .__qt_version__ = PyQt5 .QtCore .QT_VERSION_STR
40- PyQt5 .load_ui = pyqt5_load_ui
89+ add (PyQt5 , "__wrapper_version__" , __version__ )
90+ add (PyQt5 , "__binding__" , "PyQt5" )
91+ add (PyQt5 , "__binding_version__" , QtCore .PYQT_VERSION_STR )
92+ add (PyQt5 , "__qt_version__" , QtCore .QT_VERSION_STR )
93+ add (PyQt5 , "__added__" , __added__ )
94+ add (PyQt5 , "__remapped__" , __remapped__ )
95+ add (PyQt5 , "__modified__" , __modified__ )
96+ add (PyQt5 , "load_ui" , lambda fname : uic .loadUi (fname ))
4197
4298 return PyQt5
4399
44100
45- def _pyqt4 ():
101+ def pyqt4 ():
46102 # Attempt to set sip API v2 (must be done prior to importing PyQt4)
47103 import sip
48104 try :
@@ -61,144 +117,91 @@ def _pyqt4():
61117 raise ImportError
62118
63119 import PyQt4 .Qt
64-
65- # Remap
66- PyQt4 . QtWidgets = PyQt4 . QtGui
67- PyQt4 . QtCore . Signal = PyQt4 . QtCore .pyqtSignal
68- PyQt4 . QtCore . Slot = PyQt4 . QtCore .pyqtSlot
69- PyQt4 . QtCore . Property = PyQt4 . QtCore .pyqtProperty
70- PyQt4 . QtCore . QItemSelection = PyQt4 . QtGui .QItemSelection
71- PyQt4 . QtCore . QStringListModel = PyQt4 . QtGui .QStringListModel
72- PyQt4 . QtCore . QItemSelectionModel = PyQt4 . QtGui .QItemSelectionModel
73- PyQt4 . QtCore . QSortFilterProxyModel = PyQt4 . QtGui .QSortFilterProxyModel
74- PyQt4 . QtCore . QAbstractProxyModel = PyQt4 . QtGui .QAbstractProxyModel
120+ from PyQt4 import QtCore , QtGui , uic
121+
122+ remap ( PyQt4 , " QtWidgets" , QtGui )
123+ remap ( QtCore , " Signal" , QtCore .pyqtSignal )
124+ remap ( QtCore , " Slot" , QtCore .pyqtSlot )
125+ remap ( QtCore , " Property" , QtCore .pyqtProperty )
126+ remap ( QtCore , " QItemSelection" , QtGui .QItemSelection )
127+ remap ( QtCore , " QStringListModel" , QtGui .QStringListModel )
128+ remap ( QtCore , " QItemSelectionModel" , QtGui .QItemSelectionModel )
129+ remap ( QtCore , " QSortFilterProxyModel" , QtGui .QSortFilterProxyModel )
130+ remap ( QtCore , " QAbstractProxyModel" , QtGui .QAbstractProxyModel )
75131
76132 try :
77133 from PyQt4 import QtWebKit
78- PyQt4 . QtWebKitWidgets = QtWebKit
134+ remap ( PyQt4 , " QtWebKitWidgets" , QtWebKit )
79135 except ImportError :
80136 # QtWebkit is optional in Qt , therefore might not be available
81137 pass
82138
83- # Add
84- PyQt4 .__wrapper_version__ = __version__
85- PyQt4 .__binding__ = "PyQt4"
86- PyQt4 .__binding_version__ = PyQt4 .QtCore .PYQT_VERSION_STR
87- PyQt4 .__qt_version__ = PyQt4 .QtCore .QT_VERSION_STR
88- PyQt4 .load_ui = pyqt4_load_ui
139+ add (PyQt4 , "__wrapper_version__" , __version__ )
140+ add (PyQt4 , "__binding__" , "PyQt4" )
141+ add (PyQt4 , "__binding_version__" , QtCore .PYQT_VERSION_STR )
142+ add (PyQt4 , "__qt_version__" , QtCore .QT_VERSION_STR )
143+ add (PyQt4 , "__added__" , __added__ )
144+ add (PyQt4 , "__remapped__" , __remapped__ )
145+ add (PyQt4 , "__modified__" , __modified__ )
146+ add (PyQt4 , "load_ui" , lambda fname : uic .loadUi (fname ))
89147
90148 return PyQt4
91149
92150
93- def _pyside2 ():
151+ def pyside2 ():
94152 import PySide2
95- from PySide2 import QtGui , QtCore
153+ from PySide2 import QtGui , QtCore , QtUiTools
96154
97- # Remap
98- QtCore .QStringListModel = QtGui .QStringListModel
155+ remap (QtCore , "QStringListModel" , QtGui .QStringListModel )
99156
100- # Add
101- PySide2 .__wrapper_version__ = __version__
102- PySide2 .__binding__ = "PySide2"
103- PySide2 .__binding_version__ = PySide2 .__version__
104- PySide2 .__qt_version__ = PySide2 .QtCore .qVersion ()
105- PySide2 .load_ui = pyside2_load_ui
157+ add (PySide2 , "__wrapper_version__" , __version__ )
158+ add (PySide2 , "__binding__" , "PySide2" )
159+ add (PySide2 , "__binding_version__" , PySide2 .__version__ )
160+ add (PySide2 , "__qt_version__" , PySide2 .QtCore .qVersion ())
161+ add (PySide2 , "__added__" , __added__ )
162+ add (PySide2 , "__remapped__" , __remapped__ )
163+ add (PySide2 , "__modified__" , __modified__ )
164+ add (PySide2 , "load_ui" , lambda fname : QtUiTools .QUiLoader ().load (fname ))
106165
107166 return PySide2
108167
109168
110- def _pyside ():
169+ def pyside ():
111170 import PySide
112- from PySide import QtGui , QtCore
113- QtCore , QtGui # bypass linter warnings
171+ from PySide import QtGui , QtCore , QtUiTools
114172
115- # Remap
116- PySide .QtWidgets = PySide .QtGui
117- PySide .QtCore .QSortFilterProxyModel = PySide .QtGui .QSortFilterProxyModel
118- PySide .QtCore .QStringListModel = PySide .QtGui .QStringListModel
119- PySide .QtCore .QItemSelection = PySide .QtGui .QItemSelection
120- PySide .QtCore .QItemSelectionModel = PySide .QtGui .QItemSelectionModel
121- PySide .QtCore .QAbstractProxyModel = PySide .QtGui .QAbstractProxyModel
173+ remap (PySide , "QtWidgets" , QtGui )
174+ remap (QtCore , "QSortFilterProxyModel" , QtGui .QSortFilterProxyModel )
175+ remap (QtCore , "QStringListModel" , QtGui .QStringListModel )
176+ remap (QtCore , "QItemSelection" , QtGui .QItemSelection )
177+ remap (QtCore , "QItemSelectionModel" , QtGui .QItemSelectionModel )
178+ remap (QtCore , "QAbstractProxyModel" , QtGui .QAbstractProxyModel )
122179
123180 try :
124181 from PySide import QtWebKit
125- PySide . QtWebKitWidgets = QtWebKit
182+ remap ( PySide , " QtWebKitWidgets" , QtWebKit )
126183 except ImportError :
127184 # QtWebkit is optional in Qt , therefore might not be available
128185 pass
129186
130- # Add
131- PySide .__wrapper_version__ = __version__
132- PySide .__binding__ = "PySide"
133- PySide .__binding_version__ = PySide .__version__
134- PySide .__qt_version__ = PySide .QtCore .qVersion ()
135- PySide .load_ui = pyside_load_ui
187+ add (PySide , "__wrapper_version__" , __version__ )
188+ add (PySide , "__binding__" , "PySide" )
189+ add (PySide , "__binding_version__" , PySide .__version__ )
190+ add (PySide , "__qt_version__" , PySide .QtCore .qVersion ())
191+ add (PySide , "__added__" , __added__ )
192+ add (PySide , "__remapped__" , __remapped__ )
193+ add (PySide , "__modified__" , __modified__ )
194+ add (PySide , "load_ui" , lambda fname : QtUiTools .QUiLoader ().load (fname ))
136195
137196 return PySide
138197
139198
140- def pyside_load_ui (fname ):
141- """Read Qt Designer .ui `fname`
142-
143- Args:
144- fname (str): Absolute path to .ui file
145-
146- Usage:
147- >> from Qt import load_ui
148- >> class MyWindow(QtWidgets.QWidget):
149- .. fname = 'my_ui.ui'
150- .. self.ui = load_ui(fname)
151- ..
152- >> window = MyWindow()
153-
154- """
155-
156- from PySide import QtUiTools
157- return QtUiTools .QUiLoader ().load (fname )
158-
159-
160- def pyside2_load_ui (fname ):
161- """Read Qt Designer .ui `fname`
162-
163- Args:
164- fname (str): Absolute path to .ui file
165-
166- """
167-
168- from PySide2 import QtUiTools
169- return QtUiTools .QUiLoader ().load (fname )
170-
171-
172- def pyqt4_load_ui (fname ):
173- """Read Qt Designer .ui `fname`
174-
175- Args:
176- fname (str): Absolute path to .ui file
177-
178- """
179-
180- from PyQt4 import uic
181- return uic .loadUi (fname )
182-
183-
184- def pyqt5_load_ui (fname ):
185- """Read Qt Designer .ui `fname`
186-
187- Args:
188- fname (str): Absolute path to .ui file
189-
190- """
191-
192- from PyQt5 import uic
193- return uic .loadUi (fname )
194-
195-
196- def _log (text , verbose ):
199+ def log (text , verbose ):
197200 if verbose :
198201 sys .stdout .write (text )
199202
200203
201- def _init ():
204+ def init ():
202205 """Try loading each binding in turn
203206
204207 Please note: the entire Qt module is replaced with this code:
@@ -211,21 +214,20 @@ def _init():
211214
212215 preferred = os .getenv ("QT_PREFERRED_BINDING" )
213216 verbose = os .getenv ("QT_VERBOSE" ) is not None
214- bindings = (_pyside2 , _pyqt5 , _pyside , _pyqt4 )
217+ bindings = (pyside2 , pyqt5 , pyside , pyqt4 )
215218
216219 if preferred :
217-
218220 # Internal flag (used in installer)
219221 if preferred == "None" :
220222 sys .modules [__name__ ].__wrapper_version__ = __version__
221223 return
222224
223225 preferred = preferred .split (os .pathsep )
224226 available = {
225- "PySide2" : _pyside2 ,
226- "PyQt5" : _pyqt5 ,
227- "PySide" : _pyside ,
228- "PyQt4" : _pyqt4
227+ "PySide2" : pyside2 ,
228+ "PyQt5" : pyqt5 ,
229+ "PySide" : pyside ,
230+ "PyQt4" : pyqt4
229231 }
230232
231233 try :
@@ -237,19 +239,19 @@ def _init():
237239 )
238240
239241 for binding in bindings :
240- _log ("Trying %s" % binding .__name__ [1 :], verbose )
242+ log ("Trying %s" % binding .__name__ [1 :], verbose )
241243
242244 try :
243245 sys .modules [__name__ ] = binding ()
244246 return
245247
246248 except ImportError as e :
247- _log (" - ImportError(\" %s\" )\n " % e , verbose )
249+ log (" - ImportError(\" %s\" )\n " % e , verbose )
248250
249251 continue
250252
251253 # If not binding were found, throw this error
252254 raise ImportError ("No Qt binding were found." )
253255
254256
255- _init ()
257+ init ()
0 commit comments