Merge pull request #1790 from fedepell/pyqt5-separate

pyqt5 extra to support qt5 files to python conversion
This commit is contained in:
ita1024 2016-07-29 18:57:56 +02:00 committed by GitHub
commit 1308001a9e
6 changed files with 415 additions and 0 deletions

View File

@ -0,0 +1,2 @@
change me to see qrc dependencies!

View File

@ -0,0 +1,5 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="test.txt">res/test.txt</file>
</qresource>
</RCC>

View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>myfirstgui</class>
<widget class="QDialog" name="myfirstgui">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>411</width>
<height>247</height>
</rect>
</property>
<property name="windowTitle">
<string>My First Gui!</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>20</x>
<y>210</y>
<width>381</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
<widget class="QLineEdit" name="myTextInput">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>101</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QListWidget" name="listWidget">
<property name="geometry">
<rect>
<x>120</x>
<y>10</y>
<width>281</width>
<height>192</height>
</rect>
</property>
</widget>
<widget class="QPushButton" name="clearBtn">
<property name="geometry">
<rect>
<x>10</x>
<y>180</y>
<width>101</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>clear</string>
</property>
</widget>
<widget class="QPushButton" name="addBtn">
<property name="geometry">
<rect>
<x>10</x>
<y>40</y>
<width>101</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>add</string>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>myfirstgui</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>258</x>
<y>274</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>myfirstgui</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>clearBtn</sender>
<signal>clicked()</signal>
<receiver>listWidget</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>177</x>
<y>253</y>
</hint>
<hint type="destinationlabel">
<x>177</x>
<y>174</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,24 @@
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from firstgui import Ui_myfirstgui
class MyFirstGuiProgram(Ui_myfirstgui):
def __init__(self, dialog):
Ui_myfirstgui.__init__(self)
self.setupUi(dialog)
# Connect "add" button with a custom function (addInputTextToListbox)
self.addBtn.clicked.connect(self.addInputTextToListbox)
def addInputTextToListbox(self):
txt = self.myTextInput.text()
self.listWidget.addItem(txt)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
dialog = QtWidgets.QDialog()
prog = MyFirstGuiProgram(dialog)
dialog.show()
sys.exit(app.exec_())

29
playground/pyqt5/wscript Normal file
View File

@ -0,0 +1,29 @@
#! /usr/bin/env python
# encoding: utf-8#
# Federico Pellegrin, 2016 (fedepell)
"""
Python QT5 helper tools example:
converts QT5 Designer tools files (UI and QRC) into python files with
the appropriate tools (pyqt5 and pyside2 searched) and manages their
python compilation and installation using standard python waf Tool
"""
def options(opt):
# Load also python to demonstrate mixed calls
opt.load('python pyqt5')
def configure(conf):
# Load also python to demonstrate mixed calls
conf.load('python pyqt5')
conf.check_python_version((2,7,4))
def build(bld):
# Demostrates mixed usage of py and pyqt5 module, and tests also install_path and install_from
# (since generated files go into build it has to be reset inside the pyqt5 tool)
bld(features="py pyqt5", source="src/sample.py src/firstgui.ui", install_path="${PREFIX}/play/", install_from="src/")
# Simple usage on a resource file. If a file referenced inside the resource changes it will be rebuilt
# as the qrc XML is parsed and dependencies are calculated
bld(features="pyqt5", source="sampleRes.qrc")

225
waflib/extras/pyqt5.py Normal file
View File

@ -0,0 +1,225 @@
#!/usr/bin/env python
# encoding: utf-8
# Federico Pellegrin, 2016 (fedepell) adapted for Python
"""
This tool helps with finding Python Qt5 tools and libraries,
and provides translation from QT5 files to Python code.
The following snippet illustrates the tool usage::
def options(opt):
opt.load('py pyqt5')
def configure(conf):
conf.load('py pyqt5')
def build(bld):
bld(
features = 'py pyqt5',
source = 'main.py textures.qrc aboutDialog.ui',
)
Here, the UI description and resource files will be processed
to generate code.
Usage
=====
Load the "pyqt5" tool.
Add into the sources list also the qrc resources files or ui5
definition files and they will be translated into python code
with the system tools (PyQt5 or pyside2 are searched in this
order) and then compiled
"""
try:
from xml.sax import make_parser
from xml.sax.handler import ContentHandler
except ImportError:
has_xml = False
ContentHandler = object
else:
has_xml = True
import os, sys
from waflib.Tools import python
from waflib import Task, Utils, Options, Errors, Context
from waflib.TaskGen import feature, extension
from waflib.Configure import conf
from waflib import Logs
EXT_RCC = ['.qrc']
"""
File extension for the resource (.qrc) files
"""
EXT_UI = ['.ui']
"""
File extension for the user interface (.ui) files
"""
class XMLHandler(ContentHandler):
"""
Parses ``.qrc`` files
"""
def __init__(self):
self.buf = []
self.files = []
def startElement(self, name, attrs):
if name == 'file':
self.buf = []
def endElement(self, name):
if name == 'file':
self.files.append(str(''.join(self.buf)))
def characters(self, cars):
self.buf.append(cars)
@extension(*EXT_RCC)
def create_pyrcc_task(self, node):
"Creates rcc and py task for ``.qrc`` files"
rcnode = node.change_ext('.py')
self.create_task('pyrcc', node, rcnode)
if getattr(self, 'install_from', None):
self.install_from = self.install_from.get_bld()
else:
self.install_from = self.path.get_bld()
self.install_path = getattr(self, 'install_path', '${PYTHONDIR}')
self.process_py(rcnode)
@extension(*EXT_UI)
def create_pyuic_task(self, node):
"Create uic tasks and py for user interface ``.ui`` definition files"
uinode = node.change_ext('.py')
self.create_task('ui5py', node, uinode)
if getattr(self, 'install_from', None):
self.install_from = self.install_from.get_bld()
else:
self.install_from = self.path.get_bld()
self.install_path = getattr(self, 'install_path', '${PYTHONDIR}')
self.process_py(uinode)
@extension('.ts')
def add_pylang(self, node):
"""Adds all the .ts file into ``self.lang``"""
self.lang = self.to_list(getattr(self, 'lang', [])) + [node]
@feature('pyqt5')
def apply_pyqt5(self):
"""
The additional parameters are:
:param lang: list of translation files (\*.ts) to process
:type lang: list of :py:class:`waflib.Node.Node` or string without the .ts extension
:param langname: if given, transform the \*.ts files into a .qrc files to include in the binary file
:type langname: :py:class:`waflib.Node.Node` or string without the .qrc extension
"""
if getattr(self, 'lang', None):
qmtasks = []
for x in self.to_list(self.lang):
if isinstance(x, str):
x = self.path.find_resource(x + '.ts')
qmtasks.append(self.create_task('ts2qm', x, x.change_ext('.qm')))
if getattr(self, 'langname', None):
qmnodes = [x.outputs[0] for x in qmtasks]
rcnode = self.langname
if isinstance(rcnode, str):
rcnode = self.path.find_or_declare(rcnode + '.qrc')
t = self.create_task('qm2rcc', qmnodes, rcnode)
k = create_pyrcc_task(self, t.outputs[0])
class pyrcc(Task.Task):
"""
Processes ``.qrc`` files
"""
color = 'BLUE'
run_str = '${QT_PYRCC} ${SRC} -o ${TGT}'
ext_out = ['.py']
def rcname(self):
return os.path.splitext(self.inputs[0].name)[0]
def scan(self):
"""Parse the *.qrc* files"""
if not has_xml:
Logs.error('No xml.sax support was found, rcc dependencies will be incomplete!')
return ([], [])
parser = make_parser()
curHandler = XMLHandler()
parser.setContentHandler(curHandler)
fi = open(self.inputs[0].abspath(), 'r')
try:
parser.parse(fi)
finally:
fi.close()
nodes = []
names = []
root = self.inputs[0].parent
for x in curHandler.files:
nd = root.find_resource(x)
if nd: nodes.append(nd)
else: names.append(x)
return (nodes, names)
class ui5py(Task.Task):
"""
Processes ``.ui`` files for python
"""
color = 'BLUE'
run_str = '${QT_PYUIC} ${SRC} -o ${TGT}'
ext_out = ['.py']
class ts2qm(Task.Task):
"""
Generates ``.qm`` files from ``.ts`` files
"""
color = 'BLUE'
run_str = '${QT_LRELEASE} ${QT_LRELEASE_FLAGS} ${SRC} -qm ${TGT}'
class qm2rcc(Task.Task):
"""
Generates ``.qrc`` files from ``.qm`` files
"""
color = 'BLUE'
after = 'ts2qm'
def run(self):
"""Create a qrc file including the inputs"""
txt = '\n'.join(['<file>%s</file>' % k.path_from(self.outputs[0].parent) for k in self.inputs])
code = '<!DOCTYPE RCC><RCC version="1.0">\n<qresource>\n%s\n</qresource>\n</RCC>' % txt
self.outputs[0].write(code)
def configure(self):
self.find_pyqt5_binaries()
# warn about this during the configuration too
if not has_xml:
Logs.error('No xml.sax support was found, rcc dependencies will be incomplete!')
@conf
def find_pyqt5_binaries(self):
"""
Detects PyQt5 or pyside2 programs such as pyuic5/pyside2-uic, pyrcc5/pyside2-rcc
"""
env = self.env
self.find_program(['pyuic5','pyside2-uic'], var='QT_PYUIC')
if not env.QT_PYUIC:
self.fatal('cannot find the uic compiler for python for qt5')
self.find_program(['pyrcc5','pyside2-rcc'], var='QT_PYRCC')
if not env.QT_PYUIC:
self.fatal('cannot find the rcc compiler for python for qt5')
self.find_program(['pylupdate5', 'pyside2-lupdate'], var='QT_PYLUPDATE')
self.find_program(['lrelease-qt5', 'lrelease'], var='QT_LRELEASE')