Python和C++混合使用QML开发GUI

5k 词

作者:糖果

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++的流程保持一致。

【参考】

  1. PytSide

  2. 在QML中使用JavaScript和Sqlite

  3. 关于QML中调用qt类中的信号,槽,成员函数,属性做记录

  4. Connecting Qt signal to QML function

  5. @符号在python中的作用

  6. Filling and reading QML UI forms from python.