如何用Qt实现组件供QML使用

版权声明:本文系作者原创。未经许可,不得转载。

       
用Qt实现一个UI:一个圆形图标在圆圈内或圆圈上拖动,但不能拖出到圆圈外。当拖动到圆圈上时,高亮图标和圆圈。类似有RingLock。

1、继承QQuickPaintedItem类,该类为QQuickItem的子类。QQuickItem用于不用显示UI的供QML使用的组件;QQuickPaintedItem用于需要显示UI的供QML使用的组件。本案例中,需要画图,故而继承QQuickPaintedItem。

/*imagedragwidget.h*/

#ifndef IMAGEDRAGWIDGET_H
#define IMAGEDRAGWIDGET_H
#include 
#include 
#include 
class imageDragWidget : public QQuickPaintedItem
{
    Q_OBJECT
public:
    explicit imageDragWidget(QQuickPaintedItem *parent = 0);
    ~imageDragWidget();
signals:
    //鼠标按下
    void dragPress();
    //鼠标在圆圈内移动
    void dragMoveIn();
    //鼠标在圆圈上移动
    void dragMoveOn();
    //鼠标释放
    void dragRelease();
    //鼠标移出圆圈,确认关机
    void dragOut();
public slots:
protected:
    void paint(QPainter * painter);
    void mouseMoveEvent(QMouseEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
private:
    //判断鼠标和圆圈的位置关系:圆圈外、圆圈上、圆圈内
    int circleContain(void);
    //判断鼠标和图标的位置关系:图标外、图标上、图标内
    int powerContain(void);
    //得到鼠标与圆心连线和圆圈的交点
    QPoint GetPoint(QPoint currentPoint, QPoint circleCenter, int raduis);
private:
    QPixmap *circle_defaultImg;
    QPixmap *circle_boldImg;
    QPixmap *power_haloImg;
    QPixmap *power_solidImg;
    QPixmap *power_defaultImg;
    //当前圆圈图片
    QPixmap *circleImg;
    //圆圈图片所在矩形
    QRect *circleImgRect;
    //当前图标图片
    QPixmap *powerImg;
    //图标图片所在矩形
    QRect *powerImgRect;
    //当前鼠标所在位置
    QPoint currentMousePoint;
    //图标所在位置
    QPoint powerCenterPoint;
    //鼠标是否按下的标志
    bool pressFlag;
    //鼠标是否移出的标志
    bool isOut;
    //宽度缩放比例
    double widthScale;
    //高度缩放比例
    double heightScale;
};
#endif // IMAGEDRAGWIDGET_H

/*imagedragwidget.cpp*/


#include "imagedragwidget.h"
#include 
imageDragWidget::imageDragWidget(QQuickPaintedItem *parent) :
    QQuickPaintedItem(parent)
{    
    //得到屏幕尺寸
    QScreen *screen = QGuiApplication::primaryScreen();
    int screen_width = screen->size().width();
    int screen_height = screen->size().height();
    qDebug()<<"屏幕尺寸: "<width();
    int circle_height = circleImg->height();
    //设置圆圈图片在实际屏幕上的尺寸
    //滑动图标的尺寸128*128
    int circle_width_in_widget = widget_width - 128*widgetScale;
    int circle_height_in_widget = widget_height - 128*widgetScale;
    qDebug()<<"滑动圆圈尺寸: "<moveCenter(QPoint(widget_width/2, widget_height/2));
    powerImg = power_defaultImg;
    int power_width = powerImg->width();
    int power_height = powerImg->height();
    powerImgRect = new QRect(0, 0, power_width*widthScale, power_height*heightScale);
    //图标图片移到控件中心
    powerImgRect->moveCenter(circleImgRect->center());
    powerCenterPoint = circleImgRect->center();
}
void imageDragWidget::paint(QPainter *painter)
{    
    painter->drawPixmap(*circleImgRect, *circleImg);
    painter->drawPixmap(*powerImgRect, *powerImg);
}
void imageDragWidget::mouseMoveEvent(QMouseEvent *event)
{
    if(pressFlag) {
        //鼠标已按下
        int power_width = powerImgRect->width();
        int power_height = powerImgRect->height();
        int circle_width = circleImgRect->width();
        int circle_height = circleImgRect->height();
        currentMousePoint = event->pos();        
        int flag = circleContain();
        if(flag < 0) {           
            //鼠标在圆圈内,则图标移动到鼠标位置
            powerImg = power_haloImg;
            circleImg = circle_defaultImg;
            powerImgRect->moveCenter(currentMousePoint);
            powerCenterPoint = currentMousePoint;
            isOut = false;
        } else if(flag == 0) {           
            //鼠标在圆圈上,则图标移动到鼠标位置,同时更换图片
            powerImg = power_solidImg;
            circleImg = circle_boldImg;
            powerImgRect->moveCenter(currentMousePoint);
            powerCenterPoint = currentMousePoint;
            isOut = true;
        } else {            
            //鼠标在圆圈外
            isOut = true;
            if(powerContain() > 0) {               
                //鼠标在圆圈外且在图标外,则等同于鼠标释放。图标回到控件中心。
                powerImg = power_defaultImg;
                powerImgRect->moveCenter(circleImgRect->center());
                pressFlag = false;
                circleImg = circle_defaultImg;
            } else {                
                //鼠标在圆圈外且不在图标外,则图标移到鼠标与控件中心连线和圆圈的交点。
                powerImg = power_solidImg;
                circleImg = circle_boldImg;
                powerCenterPoint = GetPoint(currentMousePoint,
                                            circleImgRect->center(), circleImgRect->width()/2);
                powerImgRect->moveCenter(powerCenterPoint);
            }
        }
        powerImgRect->setHeight(power_height);
        powerImgRect->setWidth(power_width);
        circleImgRect->setHeight(circle_height);
        circleImgRect->setWidth(circle_width);
        update();
        if(pressFlag&&(!isOut)) {
            //鼠标按下且在圆圈内
            emit dragMoveIn();
        } else if(pressFlag&&isOut){
            //鼠标按下且在圆圈上
            emit dragMoveOn();
        } else if((!pressFlag)&&(isOut)){
            //鼠标在圆圈外且在图标外,则等同于鼠标释放。
            emit dragRelease();
        }
        if(isOut&&(!pressFlag)) {
            //鼠标在圆圈外且在图标外,确认关机。
            emit dragOut();
        }
    }
}
void imageDragWidget::mousePressEvent(QMouseEvent *event)
{    
    currentMousePoint = event->pos();    
    if(powerContain() <= 0) {
        //鼠标进入到图标内,则表示按下
        pressFlag = true;
        int power_width = powerImgRect->width();
        int power_height = powerImgRect->height();
        powerImg = power_haloImg;
        powerImgRect->setHeight(power_height);
        powerImgRect->setWidth(power_width);
        update();
        emit dragPress();
    }
}
void imageDragWidget::mouseReleaseEvent(QMouseEvent *event)
{
    //鼠标释放,图标回到控件中心
    currentMousePoint = event->pos();   
    pressFlag = false;
    int power_width = powerImgRect->width();
    int power_height = powerImgRect->height();
    powerCenterPoint = circleImgRect->center();
    powerImg = power_defaultImg;
    powerImgRect->moveCenter(circleImgRect->center());
    powerImgRect->setHeight(power_height);
    powerImgRect->setWidth(power_width);
    int circle_width = circleImgRect->width();
    int circle_height = circleImgRect->height();
    circleImg = circle_defaultImg;
    circleImgRect->setHeight(circle_height);
    circleImgRect->setWidth(circle_width);
    update();
    emit dragRelease();
    if(isOut) {
        emit dragOut();
    }
}
//判断鼠标是否在圆圈内
//1:圆圈外;0:圆圈上;-1:圆圈内
int imageDragWidget::circleContain(void)
{
    int delta = 0;
    int raduis = 0;
    QPoint p1 = QPoint(0, 0);
    QPoint p2 = QPoint(0, 0);
    int ret = 0;
    p1 = currentMousePoint;
    p2 = circleImgRect->center();
    delta = qSqrt(qPow(p1.x() - p2.x(), 2) + qPow(p1.y() - p2.y(), 2));
    raduis = circleImgRect->width()/2;
    if(delta > raduis) {
        ret = 1;
    } else if(delta < raduis) {
        ret = -1;
    } else {
        ret = 0;
    }
    return ret;
}
//判断鼠标是否在图标内
//1:图标外;0:图标上;-1:图标内
int imageDragWidget::powerContain(void)
{
    int delta = 0;
    int raduis = 0;
    QPoint p1 = QPoint(0, 0);
    QPoint p2 = QPoint(0, 0);
    int ret = 0;
    p1 = currentMousePoint;
    p2 = powerCenterPoint;
    delta = qSqrt(qPow(p1.x() - p2.x(), 2) + qPow(p1.y() - p2.y(), 2));
    raduis = (powerImgRect->width()/2)*128/350;   //整张图片350px*350px,图标为128*128
    if(delta > raduis) {
        ret = 1;
    } else if(delta < raduis) {
        ret = -1;
    } else {
        ret = 0;
    }
    return ret;
}
//求线段与圆圈的交点,该线段一个端点为圆心,另一个端点在圆外。
QPoint imageDragWidget::GetPoint(QPoint currentPoint, QPoint circleCenter, int raduis)
{
    int cx = circleCenter.x(); //圆心横坐标
    int cy = circleCenter.y(); //圆心纵坐标
    int edx = currentPoint.x(); //圆外点的横坐标
    int edy = currentPoint.y();  //圆外点的纵坐标
    int r = raduis;  //半径
    int x = 0; //交点横坐标
    int y = 0; //交点纵坐标
    QPoint p = QPoint(0, 0); //交点
    if((edx>cx)&&(edy==cy)) {
        //右轴;
        x = cx+r;
        y = cy;
    } else if((edx==cx) && (edycy)) {
        //下轴;
        x = cx;
        y = cy + r;
    } else {
        //不在坐标轴上
        //求得直线方程
        double k = ((double)(edy - cy) ) / (edx - cx);
        double b = edy - k*edx;
        //列方程
        /*
            (1 + k^2)*x^2 - x*(2*cx -2*k*(b -cy) ) + cx*cx + ( b - cy)*(b - cy) - r*r = 0
          */
        double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
        double c = cx*cx + (b - cy)*(b- cy) - r*r;
        double a = (1 + k*k);
        double b1 = (2*cx - 2*k*(b - cy));
        //得到下面的简化方程
        // a*x^2 - b1*x + c = 0;
        double tmp = sqrt(b1*b1 - 4*a*c);
        x1 = ( b1 + tmp )/(2*a);
        y1 = k*x1 + b;
        x2 = ( b1 - tmp)/(2*a);
        y2 = k*x2 + b;
        if((edx>cx)&&(edy>cy)) {
            //第四象限;
            x = x1;
            y = y1;
        } else if((edx>cx)&&(edycy)) {
            //第三象限;
            x = x2;
            y = y2;
        }
    }
    p.setX(x);
    p.setY(y);
    return p;
}
imageDragWidget::~imageDragWidget()
{
    delete circle_defaultImg;
    delete circle_boldImg;
    delete power_haloImg;
    delete power_solidImg;
    delete power_defaultImg;
    delete circleImgRect;
    delete powerImgRect;
}


2、在主函数中将该组件注册成服务,以便QML能够看到。

/*main.cpp*/


#include "imagedragwidget.h"

……

int main(int argc, char *argv[]) {

……

qmlRegisterType("cn.cmos.service.imageDragWidget", 1, 0, "ImageDragWidget");

……

}

3 、在QML中使用该组件。


/*ShutdownWidget.qml*/


import QtQuick 2.0
import cn.cmos.service.imageDragWidget 1.0
//圆圈、图标和标签的控件
Rectangle {
    id: shutdownWidget
    anchors.fill: parent
    color: "black"
    //确认关机
    signal pullOutside
    //鼠标按下
    signal dragPress
    //鼠标在圆圈内移动
    signal dragMoveIn
    //鼠标在圆圈上移动
    signal dragMoveOn
    //鼠标释放
    signal dragRelease
    onDragPress: {
        dragOutText.visible = false
    }
    onDragMoveIn: {
        dragOutText.visible = false
        powerOffText.visible = false
    }
    onDragMoveOn: {
        powerOffText.visible = true
    }
    onDragRelease: {
        dragOutText.visible = true
        powerOffText.visible = false
    }
    //“拖动关机”的标签
    Text {
        id: dragOutText
        y: parent.width/6
        horizontalAlignment: Text.AlignHCenter
        verticalAlignment: Text.AlignVCenter
        font.pixelSize:35
        color: "white"
        anchors.horizontalCenter: parent.horizontalCenter
    }
    //“确认关机”的标签
    Text {
        id: powerOffText
        visible: false
        horizontalAlignment: Text.AlignHCenter
        verticalAlignment: Text.AlignVCenter
        font.pixelSize:35
        color: "white"
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.verticalCenter: parent.verticalCenter
    }
    //圆圈和图标的控件
    ImageDragWidget {
        id: imagedragitem
        anchors.fill: parent
        Component.onCompleted: {
            // 连接控件信号和组件的槽
            this.dragOut.connect(shutdownWidget.pullOutside);
            this.dragPress.connect(shutdownWidget.dragPress);
            this.dragMoveIn.connect(shutdownWidget.dragMoveIn);
            this.dragMoveOn.connect(shutdownWidget.dragMoveOn);
            this.dragRelease.connect(shutdownWidget.dragRelease);
            console.log("open shutdown ImageDragWidget...")
        }
    }
    //该控件的信号
    signal languageChanged(string name)
    //该控件的相应信号的响应函数
    onLanguageChanged: {
        translator()
    }
    Component.onCompleted: {
        // 连接组件信号和控件的槽
        cmostranslate.langChanged.connect(shutdownWidget.languageChanged);
        // 调用组件函数初始化语言名称
        cmostranslate.trans();
    }
    //翻译
    function translator() {
        dragOutText.text = qsTr("Drag out to power off")
        powerOffText.text = qsTr("Power off")
    }
}

附注:该需求亦可用QML和javasript来实现。

/*Circle.js*/


//判断点是否在圆内
function contain(x1, y1, x2, y2, raduis)
{
    var delta = 0;
    var ret = 0;
    delta = Math.round(Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)));
    if(delta > raduis) {
        ret = 1;
    } else if(delta < raduis) {
        ret = -1;
    } else {
        ret = 0;
    }
    return ret;
}
//求线段与圆圈的交点,该线段一个端点为圆心,另一个端点在圆外。
function getPoint(currentPointX, currentPointY, circleCenterX, circleCenterY, raduis)
{
    var cx = circleCenterX; //圆心横坐标
    var cy = circleCenterY; //圆心纵坐标
    var edx = currentPointX; //圆外点的横坐标
    var edy = currentPointY;  //圆外点的纵坐标
    var r = raduis;  //半径
    var x = 0; //交点横坐标
    var y = 0; //交点纵坐标
    if((edx>cx)&&(edy===cy)) {
        //右轴;
        x = cx+r;
        y = cy;
    } else if((edx===cx) && (edycy)) {
        //下轴;
        x = cx;
        y = cy + r;
    } else {
        //不在坐标轴
        //求得直线方程
        var k = (edy - cy) / (edx - cx);
        var b = edy - k*edx;
        //列方程
        /*
            (1 + k^2)*x^2 - x*(2*cx -2*k*(b -cy) ) + cx*cx + ( b - cy)*(b - cy) - r*r = 0
          */
        var x1 = 0, y1 = 0, x2 = 0, y2 = 0;
        var c = cx*cx + (b - cy)*(b- cy) - r*r;
        var a = (1 + k*k);
        var b1 = (2*cx - 2*k*(b - cy));
        //得到下面的简化方程
        // a*x^2 - b1*x + c = 0;
        var tmp = Math.sqrt(b1*b1 - 4*a*c);
        x1 = ( b1 + tmp )/(2*a);
        y1 = k*x1 + b;
        x2 = ( b1 - tmp)/(2*a);
        y2 = k*x2 + b;
        if((edx>cx)&&(edy>cy)) {
            //第四象限;
            x = x1;
            y = y1;
        } else if((edx>cx)&&(edycy)) {
            //第三象限;
            x = x2;
            y = y2;
        }
    }
    return [Math.round(x), Math.round(y)];
}



/*ShutdownArea.qml*/


import QtQuick 2.2
import "Circle.js" as Circle
Rectangle {
    id:shutdownArea
    color: "black"
    anchors.fill: parent
    //"确认关机"的信号
    signal pullOutside
    //圆圈中心坐标
    property real circle_centerX: width/2
    property real circle_centerY: height/2
    //图标中心坐标
    property point powerImageCenter: Qt.point(0, 0);
    //鼠标是否按下的标志
    property bool pressFlag: false
    //鼠标是否移出的标志
    property bool isOut: false
    Timer {
        id: shutdownAreaTimer
        property int stateFlag: 0
        interval: 1000; running: true; repeat: true; triggeredOnStart: true
        onTriggered:  {
            console.log("shutdownDragTimer trigger")
            //定时器切换状态,随后关掉定时器
            stateFlag = stateFlag + 1
            if(stateFlag == 1) {
                shutdownArea.state = "begin"
            } else if(stateFlag == 2) {
                shutdownArea.state = "middle"
            } else if(stateFlag == 3) {
                shutdownArea.state = "end"
            } else {
                shutdownAreaTimer.stop()
            }
        }
    }
    //“拖动关机”和“确认关机”的标签
    Text {
        id: dragText
        y: (parent.height-height)/align
        text: dragOutText
        property string dragOutText: ""
        property string powerOffText: ""
        property int align: 4
        horizontalAlignment: Text.AlignHCenter
        verticalAlignment: Text.AlignVCenter
        font.pixelSize:35
        color: "white"
        anchors.horizontalCenter: parent.horizontalCenter
    }
    Image {
        id: circleImage
        width: (452/580)*parent.width    //滑动范围圆圈图片尺寸452*452, 滑动图标的尺寸128*128
        // 图标所在图片尺寸为:350*350
        height: width
        anchors.centerIn: parent
        source: circle_default_image
        property string circle_default_image: "qrc:/images/circle_default.png"
        property string circle_bold_image: "qrc:/images/circle_bold.png"
    }
    Image {
        id: powerImage
        //滑动范围圆圈图片尺寸452*452, 滑动图标的尺寸128*128
        // 图标所在图片尺寸为:350*350
        x: (580-350)/(2*580)*parent.width
        y: (580-350)/(2*580)*parent.height
        width: (350/580)*parent.width
        height: width
        source: power_default_image
        property string power_default_image: "qrc:/images/power_default.png"
        property string power_halo_image: "qrc:/images/power_halo.png"
        property string power_solid_image: "qrc:/images/power_solid.png"
        function moveCenter(centerX, centerY) {
            powerImage.x = centerX - width/2
            powerImage.y = centerY - height/2
        }
    }
    state: "initializtion"
    states: [
        State {
            name: "initializtion"
            PropertyChanges { target: dragText; visible: false }
            PropertyChanges { target: dragText; opacity: 0.0 }
            PropertyChanges { target: circleImage; visible: false }
            PropertyChanges { target: circleImage; scale: 0.0 }
            PropertyChanges { target: powerImage; visible: false }
            PropertyChanges { target: powerImage; scale: 0.0 }
        },
        State {
            name: "begin"
            PropertyChanges { target: dragText; visible: false }
            PropertyChanges { target: dragText; opacity: 0.0 }
            PropertyChanges { target: circleImage; visible: true }
            PropertyChanges { target: circleImage; scale: 0.0 }
            PropertyChanges { target: powerImage; visible: true }
            PropertyChanges { target: powerImage; scale: 0.0 }
        },
        State {
            name: "middle"
            PropertyChanges { target: dragText; visible: false }
            PropertyChanges { target: dragText; opacity: 0.0 }
            PropertyChanges { target: circleImage; visible: true }
            PropertyChanges { target: circleImage; scale: 128/452 }
            PropertyChanges { target: powerImage; visible: true }
            PropertyChanges { target: powerImage; scale: 1.0 }
        },
        State {
            name: "end"
            PropertyChanges { target: dragText; visible: true }
            PropertyChanges { target: dragText; opacity: 1.0 }
            PropertyChanges { target: circleImage; visible: true }
            PropertyChanges { target: circleImage; scale: 1.0 }
            PropertyChanges { target: powerImage; visible: true }
            PropertyChanges { target: powerImage; scale: 1.0 }
        }
    ]
    transitions: [
        Transition {
            from: "initializtion"; to: "begin"
        },
        Transition {
            from: "begin"; to: "middle"
            PropertyAnimation {
                target: circleImage
                properties: "scale"; duration: 1000
            }
            PropertyAnimation {
                target: powerImage
                properties: "scale"; duration: 1000
            }
        },
        Transition {
            from: "middle"; to: "end"
            PropertyAnimation {
                target: circleImage
                properties: "scale"; duration: 1000
            }
            PropertyAnimation {
                target: dragText
                properties: "opacity"; duration: 1000; easing.type: Easing.InExpo
            }
        },
        Transition {
            from: "end"; to: "initializtion"
        }
    ]
    MouseArea {
        id: dragArea
        anchors.fill: parent
        onPressed: {
            var power_result = Circle.contain(mouse.x, mouse.y, circle_centerX, circle_centerY, (128/350)*powerImage.width/2);
            if(power_result !== 1) {
                //图标上或内
                pressFlag = true;
                powerImage.source = powerImage.power_halo_image;
                dragText.text = "";
            }
        }
        onReleased: {
            powerImage.moveCenter(circle_centerX, circle_centerY);
            pressFlag = false;
            powerImage.source = powerImage.power_default_image;
            circleImage.source = circleImage.circle_default_image;
            dragText.text = dragText.dragOutText;
            dragText.align = 4;
            if(isOut) {
                shutdownArea.pullOutside()
            }
        }
        onPositionChanged: {
            if(pressFlag) {
                var circle_result = Circle.contain(mouse.x, mouse.y, circle_centerX, circle_centerY, circleImage.width/2);
                if(circle_result === 1) {
                    isOut = true;
                    //圆圈外
                    var power_result = Circle.contain(mouse.x, mouse.y, powerImageCenter.x, powerImageCenter.y, (128/350)*powerImage.width/2);
                    if(power_result === 1) {
                        //圈圈外且图标外
                        powerImage.moveCenter(circle_centerX, circle_centerY);
                        pressFlag = false;
                        powerImage.source = powerImage.power_default_image;
                        circleImage.source = circleImage.circle_default_image;
                        dragText.text = dragText.dragOutText;
                        dragText.align = 4;
                        //     shutdownArea.pullOutside()
                    } else {
                        //圈圈外且非图标外
                        var crossover_point = Circle.getPoint(mouse.x, mouse.y, circle_centerX, circle_centerY, circleImage.width/2);
                        console.log("交点: " + crossover_point)
                        powerImage.moveCenter(crossover_point[0], crossover_point[1]);
                        powerImageCenter = Qt.point(crossover_point[0], crossover_point[1])
                        powerImage.source = powerImage.power_solid_image;
                        circleImage.source = circleImage.circle_bold_image;
                        dragText.text = dragText.powerOffText;
                        dragText.align = 2;
                    }
                } else if (circle_result === 0) {
                    //圆圈上
                    powerImageCenter = Qt.point(mouse.x, mouse.y)
                    powerImage.source = powerImage.power_solid_image;
                    circleImage.source = circleImage.circle_bold_image;
                    dragText.text = dragText.powerOffText;
                    dragText.align = 2;
                    isOut = true;
                } else {
                    //圆圈内
                    powerImageCenter = Qt.point(mouse.x, mouse.y)
                    powerImage.moveCenter(mouse.x, mouse.y)
                    powerImage.source = powerImage.power_halo_image;
                    circleImage.source = circleImage.circle_default_image;
                    dragText.text = "";
                    isOut = false;
                }
            }
            if(isOut&&(!pressFlag)) {
                //鼠标在圆圈外且在图标外,确认关机。
                shutdownArea.pullOutside()
            }
        }
    }
    //该控件的信号
    signal languageChanged(string name)
    //该控件的相应信号的响应函数
    onLanguageChanged: {
        translator()
    }
    Component.onCompleted: {
        // 连接组件信号和控件的槽
        cmostranslate.langChanged.connect(shutdownArea.languageChanged);
        // 调用组件函数初始化语言名称
        cmostranslate.trans();
    }
    //翻译
    function translator() {
        dragText.dragOutText = qsTr("Drag Down")
        dragText.powerOffText = qsTr("Power off")
    }
}     


                                                        

你可能感兴趣的:(qt和qml调用,qml,qt)