3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
7 # Federico Pellegrin, 2016-2018 (fedepell) adapted for Python
10 This tool helps with finding Python Qt5 tools and libraries,
11 and provides translation from QT5 files to Python code.
13 The following snippet illustrates the tool usage::
23 features = 'py pyqt5',
24 source = 'main.py textures.qrc aboutDialog.ui',
27 Here, the UI description and resource files will be processed
33 Load the "pyqt5" tool.
35 Add into the sources list also the qrc resources files or ui5
36 definition files and they will be translated into python code
37 with the system tools (PyQt5, pyside2, PyQt4 are searched in this
38 order) and then compiled
42 from xml.sax import make_parser
43 from xml.sax.handler import ContentHandler
46 ContentHandler = object
51 from waflib.Tools import python
52 from waflib import Task, Options
53 from waflib.TaskGen import feature, extension
54 from waflib.Configure import conf
55 from waflib import Logs
59 File extension for the resource (.qrc) files
64 File extension for the user interface (.ui) files
68 class XMLHandler(ContentHandler):
75 def startElement(self, name, attrs):
78 def endElement(self, name):
80 self.files.append(str(''.join(self.buf)))
81 def characters(self, cars):
85 def create_pyrcc_task(self, node):
86 "Creates rcc and py task for ``.qrc`` files"
87 rcnode = node.change_ext('.py')
88 self.create_task('pyrcc', node, rcnode)
89 if getattr(self, 'install_from', None):
90 self.install_from = self.install_from.get_bld()
92 self.install_from = self.path.get_bld()
93 self.install_path = getattr(self, 'install_path', '${PYTHONDIR}')
94 self.process_py(rcnode)
97 def create_pyuic_task(self, node):
98 "Create uic tasks and py for user interface ``.ui`` definition files"
99 uinode = node.change_ext('.py')
100 self.create_task('ui5py', node, uinode)
101 if getattr(self, 'install_from', None):
102 self.install_from = self.install_from.get_bld()
104 self.install_from = self.path.get_bld()
105 self.install_path = getattr(self, 'install_path', '${PYTHONDIR}')
106 self.process_py(uinode)
109 def add_pylang(self, node):
110 """Adds all the .ts file into ``self.lang``"""
111 self.lang = self.to_list(getattr(self, 'lang', [])) + [node]
114 def apply_pyqt5(self):
116 The additional parameters are:
118 :param lang: list of translation files (\*.ts) to process
119 :type lang: list of :py:class:`waflib.Node.Node` or string without the .ts extension
120 :param langname: if given, transform the \*.ts files into a .qrc files to include in the binary file
121 :type langname: :py:class:`waflib.Node.Node` or string without the .qrc extension
123 if getattr(self, 'lang', None):
125 for x in self.to_list(self.lang):
126 if isinstance(x, str):
127 x = self.path.find_resource(x + '.ts')
128 qmtasks.append(self.create_task('ts2qm', x, x.change_ext('.qm')))
131 if getattr(self, 'langname', None):
132 qmnodes = [k.outputs[0] for k in qmtasks]
133 rcnode = self.langname
134 if isinstance(rcnode, str):
135 rcnode = self.path.find_or_declare(rcnode + '.qrc')
136 t = self.create_task('qm2rcc', qmnodes, rcnode)
137 create_pyrcc_task(self, t.outputs[0])
139 class pyrcc(Task.Task):
141 Processes ``.qrc`` files
144 run_str = '${QT_PYRCC} ${SRC} -o ${TGT}'
148 return os.path.splitext(self.inputs[0].name)[0]
151 """Parse the *.qrc* files"""
153 Logs.error('No xml.sax support was found, rcc dependencies will be incomplete!')
156 parser = make_parser()
157 curHandler = XMLHandler()
158 parser.setContentHandler(curHandler)
159 fi = open(self.inputs[0].abspath(), 'r')
167 root = self.inputs[0].parent
168 for x in curHandler.files:
169 nd = root.find_resource(x)
174 return (nodes, names)
177 class ui5py(Task.Task):
179 Processes ``.ui`` files for python
182 run_str = '${QT_PYUIC} ${SRC} -o ${TGT}'
185 class ts2qm(Task.Task):
187 Generates ``.qm`` files from ``.ts`` files
190 run_str = '${QT_LRELEASE} ${QT_LRELEASE_FLAGS} ${SRC} -qm ${TGT}'
192 class qm2rcc(Task.Task):
194 Generates ``.qrc`` files from ``.qm`` files
199 """Create a qrc file including the inputs"""
200 txt = '\n'.join(['<file>%s</file>' % k.path_from(self.outputs[0].parent) for k in self.inputs])
201 code = '<!DOCTYPE RCC><RCC version="1.0">\n<qresource>\n%s\n</qresource>\n</RCC>' % txt
202 self.outputs[0].write(code)
205 self.find_pyqt5_binaries()
207 # warn about this during the configuration too
209 Logs.error('No xml.sax support was found, rcc dependencies will be incomplete!')
212 def find_pyqt5_binaries(self):
214 Detects PyQt5 or pyside2 programs such as pyuic5/pyside2-uic, pyrcc5/pyside2-rcc
218 if getattr(Options.options, 'want_pyside2', True):
219 self.find_program(['pyside2-uic'], var='QT_PYUIC')
220 self.find_program(['pyside2-rcc'], var='QT_PYRCC')
221 self.find_program(['pyside2-lupdate'], var='QT_PYLUPDATE')
222 elif getattr(Options.options, 'want_pyqt4', True):
223 self.find_program(['pyuic4'], var='QT_PYUIC')
224 self.find_program(['pyrcc4'], var='QT_PYRCC')
225 self.find_program(['pylupdate4'], var='QT_PYLUPDATE')
227 self.find_program(['pyuic5','pyside2-uic','pyuic4'], var='QT_PYUIC')
228 self.find_program(['pyrcc5','pyside2-rcc','pyrcc4'], var='QT_PYRCC')
229 self.find_program(['pylupdate5', 'pyside2-lupdate','pylupdate4'], var='QT_PYLUPDATE')
232 self.fatal('cannot find the uic compiler for python for qt5')
235 self.fatal('cannot find the rcc compiler for python for qt5')
237 self.find_program(['lrelease-qt5', 'lrelease'], var='QT_LRELEASE')
243 pyqt5opt=opt.add_option_group("Python QT5 Options")
244 pyqt5opt.add_option('--pyqt5-pyside2', action='store_true', default=False, dest='want_pyside2', help='use pyside2 bindings as python QT5 bindings (default PyQt5 is searched first, PySide2 after)')
245 pyqt5opt.add_option('--pyqt5-pyqt4', action='store_true', default=False, dest='want_pyqt4', help='use PyQt4 bindings as python QT5 bindings (default PyQt5 is searched first, PySide2 after, PyQt4 last)')