Python和C++混合使用QML开发GUI
作者:糖果
pyqt和qml结合的中文资很少,在baidu上搜索,基本上就是浪费时间。在国外的blog上,有零星的几篇,但是介绍好的少。在stackoverflow上看到一篇关于pyside,发现pyside,发现pyside果然给力,那就开始我们的pyside游戏之旅吧。
【编辑器】
目前的编辑环境是,Eclipse+PyDev, Erics, QtCreator这三个工具一起使用。用前两者进PY代码编辑,用QtCreator进行QML编辑和设计。
【概要】
用PyQt,C++,QML实现一个简单但的文本输入框值得取得和设置。从编码角度来看,我们需要在Python中调用QML的function方法,并通过参数传递把python中设定的变量值给QML.需要在QML中调用Python定义的函数方法,并把QML中InputText的Text值传递给Python.无论控件和业务逻辑多复杂都是如此。
Python代码
import sys
from PySide import QtCore, QtDeclarative, QtGui
class QtInterface(QtCore.QObject):
signaller_in_txt = QtCore.Signal(str)
signaller_out_txt = QtCore.Signal()
def __init__(self):
QtCore.QObject.__init__(self)
self.in_txt = "test"
#@的这种声明方式,会在后面的部分介绍,并且参考链接中,有一篇文也介绍的很清晰。
#这是一个不带参数的Slot函数。
@QtCore.Slot()
def getInputText(self):
print self.in_txt
#这个一个带参数的Slot函数,我们就是利用这个参数,在QML中,调用这个函数,并把InputText的text值发送过来,并且在函数中,打印出这个传递值。
@QtCore.Slot(str)
def setInputText(self, text):
print text
#在updateValues函数中,通过信号发射,调用QML中的function函数,并将对控件的设置值,传递过去。信号变量声明,在类中,和与对应的QML函数简历毁掉联系,是在main函数中完成的。
def updateValues(self):
# self.signaller_in_txt.emit(str(self.in_txt))
self.signaller_out_txt.emit()
MainView是主要View视图,继承了基类,QDeclarativeView,继承了最大化,最小话和关闭窗体的机能。
class MainView(QtDeclarative.QDeclarativeView):
def __init__(self, parent=None):
#构造父类
super( MainView, self).__init__(parent)
#设定窗体Title内容
self.setWindowTitle("Counter")
#设定与本地QML关联
self.setSource( QtCore.QUrl.fromLocalFile('abc.qml'))
#设定窗体尺寸变化的模式,继承了父类的模式
self.setResizeMode( QtDeclarative.QDeclarativeView.SizeRootObjectToView)
创建一个QtGui的应用实例
qApplication = QtGui.QApplication(sys.argv)
#主视图创建
window = MainView()
#显示主视图
window.show()
#取得用于解析QML的类实例
qcontext = window.rootContext()
interface = QtInterface()
#将用户自己的QtObject子类和窗体类建立连接
qcontext.setContextProperty("qInterface", interface)
#将信号和QML函数建立映射关联。
interface.signaller_score_a.connect(window.rootObject().updateScoreA)
interface.signaller_in_txt.connect(window.rootObject().updateInText)
interface.signaller_out_txt.connect(window.rootObject().getInTxt)
#退出应用
sys.exit(qApplication.exec_())
QML代码
QML语言基本UI元素的描述信息和功能函数,QML本身可以通过自己的函数定义执行来完成一定程度上功能,完全和背后的语言(C++,Python)脱离关系。而且在很多的平台上使用,甚至包括移动平台,可以和我的WEB服务器很好的链接,传递数据。
import QtQuick 1.1
Rectangle {
id: rectangle1
width: 480
height: 272
gradient: Gradient {
GradientStop {
id: gradientStop1
position: 0
color: "#ffffff"
}
GradientStop {
position: 1
color: "#abc09f"
}
}
//UML中的函数,要通过emit发射信号调用。
function updateInText(string) {
in_txt.text = string
}
function updateIn() {
in_txt.text = "ozzy"
}
//在Python中,通过emit调用getInTxt函数
function getInTxt() {
//console.log基本就是JavaScript的用法。
console.log("debug")
return (in_txt.text)
}
Text {
id: score_a
x: 150
y: 74
width: 131
height: 48
text: qsTr("Text")
verticalAlignment: Text.AlignVCenter
font.pixelSize: 12
}
MouseArea {
id: a_scored
x: 303
y: 200
}
Rectangle {
id: team_a
x: 150
y: 148
width: 127
height: 46
color: "#4e3a3a"
radius: 10
TextInput {
id: team_a_txt
x: 24
y: 13
width: 80
height: 20
text: qsTr("A")
selectionColor: "#316cc4"
horizontalAlignment: TextInput.AlignHCenter
font.pixelSize: 12
}
MouseArea {
id: team_a_score_ma
x: 1
y: 0
width: 126
height: 46
onClicked: {
qInterface.aScored()
}
}
}
TextInput {
id: in_txt
x: 345
y: 67
width: 80
height: 20
text: qsTr("InputText")
selectionColor: "#316cc4"
font.pixelSize: 12
MouseArea {
id: in_txt_ma
x: -17
y: 77
width: 115
height: 57
z: 2
//直接在MouseArea中添加对应的事件处理
onClicked: {
//qInterface是在Python中建立的映射关系,通过这个对象实例,就可以直接调用Python中的函数方法,并且可以传递参数
qInterface.setInputText(in_txt.text)
//下面的这个函数被注释掉了,因为getText()是一个C++写的方法
//console.log(qInterface.getText())
}
}
}
Rectangle {
id: get_in_text
x: 322
y: 148
width: 127
height: 46
color: "#4e3a3a"
radius: 10
Text {
id: text1
x: 38
y: 15
width: 40
height: 16
text: qsTr("Enter")
font.pixelSize: 12
}
}
}
C++代码
#ifndef LOGIN_H
#define LOGIN_H
#include <QObject>
class Login: public QObject
{
Q_OBJECT
public:
//Q_INVOKABLE关键字,可以让QML直接调用C++方法。相当于 濮阳天python中的@QtCore.Slot()
Q_INVOKABLE QString getText(void) const;
Login(QObject *parent = 0);
virtual ~Login();
signals:
void setInputText(const QString &s);
public slots:
void setText(const QString &s);
};
#endif // LOGIN_H
#include "login.h"
Login::Login(QObject *parent)
:QObject(parent)
{
QObject::connect(this, SIGNAL(setInputText(QString)), this, SLOT(setText(QString)));
}
Login::~Login() {
}
QString LS::getText(void) const
{
return "from C++ Code";
}
void LS::setText(const QString &s) {
qDebug("this is string.");
qDebug("%s", s.toLocal8Bit().data());
}
【后记】
上面的代码可以看到,PyQt和C公用一个QML代码,QML几乎不变(不是几乎,就是一样的)。PyQT(PySide)更适应快速开发。用C实现性能要求比较高的共同部分,则更有优势。
Python中QML调用Python函数,只要把python的函数声明为@QtCore.Slot
Python调用QML函数,需要定义信号和connect QML的函数。C是,UML调用C函数,只要把C函数声明过为Q_INVOKABLE。C调用QML函数,需要声明Signal和Connect Slot函数。这点Python和C++的流程保持一致。
【参考】