Qt里控件自绘的那点事

前言

  最近一直在做控件自绘方面的事情,自己设计了一些控件和界面,下面把过程当中的经验记录下来。
  现在写界面的话,一种是用C++,一种是用QML,这两种都有用过,所以一并写出来。

QWidget中的自绘

  在QWidget中做自绘一般就是重写void paintEvent(QPaintEvent * event)函数,然后在这个函数里利用QPainter类进行绘制,在这里提供一段代码:

void InfoWidget::paintEvent(QPaintEvent * event)
{
	Q_UNUSED(event);
	QPainter painter(this);
	QPen linePen(QColor(174, 240, 108));
	linePen.setWidth(8);
	painter.setPen(linePen);
	painter.setBrush(QBrush(QColor(57,62,70,150)));
	painter.setRenderHint(QPainter::HighQualityAntialiasing);

	int mw = this->width();
	int mh = this->height();
	int ma = 30;
	int padding = 10;
	QPointF points[16] ={QPointF(padding * 2 + ma + padding, padding), QPointF(mw/2, padding), QPointF(mw/2 + ma + padding, padding + ma + padding), 
						QPointF(mw - padding, padding + ma + padding), QPointF(mw - padding, mh - padding), QPointF(mw - 3 * padding, mh - padding), 
						QPointF(mw - 4 * padding, mh - 2 * padding), QPointF(5 * padding, mh - 2 * padding), QPointF(4 * padding, mh - padding), 
						QPointF(2 * padding, mh - padding), QPointF(2 * padding, mh - padding - mh/4), QPointF(padding, mh- padding - mh/4 - padding), 
						QPointF(padding, mh/4 + padding), QPointF(2 * padding, mh/4), QPointF(2* padding, padding + ma + padding), 
						QPointF(padding * 2 + ma + padding, padding)};
	painter.drawPolygon(points, 16);

	painter.setFont(QFont(QString::fromLocal8Bit("微软雅黑"),42));
	painter.drawText(padding * 2 + ma + padding, padding, mw/2-ma-3*padding, ma + padding, Qt::AlignHCenter | Qt::AlignVCenter, QString::fromLocal8Bit("测试信息"));

}

  上段代码画出来的是一个不规则的窗体,setPen其实就是画的边框的样式,setBrush就是填充边框区域的样式。drawText的文字是在指定的矩形里进行绘制的,文字的样式通过setFont进行设置。画完之后就相当于有了一个背景,接下来其实加入布局、加其他组件都跟正常的窗口差不多的,只不过这时的布局位置需要你自己去调整,要根据坐标去调整相应的布局位置。

QML中的自绘

  我个人其实是非常喜欢用QML来写界面的,因为搭建界面真的非常方便快捷,而且与C++的交互方式也是非常强大,还有一个原因就是用Canvas做组件自绘非常地方便,搭配动画效果就可以做出很酷炫的界面了。先上一段示例代码:

import QtQuick 2.7
import QtQuick.Window 2.0
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import QtQuick.Controls.Material 2.2
import QtQuick.Controls.Styles 1.4
import "../CustomComponent"

Rectangle{
    id: rhomboidButton
    implicitWidth: mw
    implicitHeight: mh
    border.width: 0
    color: Qt.rgba(0.2, 0.2, 0.2, 0);

    property int mw : 150           //边框宽度
    property int mh : 40             //边框高度
    property int padding: 4
    property int gap : (mh-2*padding)/2;

    property string outlineColor: "#FF8100"
    property string shadowColor:  "#FF9800"
    property string textColor:      "#64FF98"
    
    property string title: "展示线路"
    signal buttonClicked()
    
    states: [
            State {
                name: "default"
                PropertyChanges { target: titleText; opacity: 0.5 }
                PropertyChanges { target: outline; opacity: 0.5 }
                PropertyChanges { target: aniCircle1; opacity: 0.5 }
                PropertyChanges { target: aniCircle2; opacity: 0.5 }
            },
            State {
                name: "hovered"
                PropertyChanges { target: titleText; opacity: 1 }
                PropertyChanges { target: outline; opacity: 1 }
                PropertyChanges { target: aniCircle1; opacity: 1 }
                PropertyChanges { target: aniCircle2; opacity: 1 }
            }
        ]

    state: "default"

    MouseArea{
        anchors.fill: parent
        acceptedButtons: Qt.LeftButton
        hoverEnabled: true
        onClicked: {
            buttonClicked();
        }

        onPressed: {
            titleText.topPadding = padding/2;
        }

        onReleased: {
            titleText.topPadding = 0;
        }

        onEntered: {
            rhomboidButton.state = "hovered";
        }

        onExited: {
           rhomboidButton.state = "default";
        }
    }

    Text{
        id: titleText
        anchors.fill: parent
        verticalAlignment: Text.AlignVCenter
        horizontalAlignment: Text.AlignHCenter
        text: title
        font.family: "微软雅黑"
        font.pointSize: 12
        color: textColor
        z: 10
    }

    Canvas {
        id: outline
        anchors.fill: parent
        contextType: "2d"
        z: 5

        onPaint: {
            var ctx = outline.getContext("2d");
            ctx.strokeStyle = outlineColor;
            ctx.fillStyle = Qt.rgba(0.2, 0.2, 0.2, 0.5);
            ctx.lineWidth = 2;
            ctx.shadowBlur = 2;
            ctx.shadowColor = shadowColor;
            ctx.beginPath();
            ctx.moveTo(padding, padding);
            ctx.lineTo(mw - padding - gap, padding);
            ctx.lineTo(mw - padding, mh - padding);
            ctx.lineTo(padding + gap, mh - padding);
            ctx.lineTo(padding, padding);
            ctx.stroke();
            ctx.fill();
        }
    }

    Canvas {
        id: aniCircle1
        x:0
        y:0
        width: rhomboidButton.width
        height: rhomboidButton.height
        contextType: "2d"
        z: 10

        onPaint: {
            var ctx = aniCircle1.getContext("2d");
            ctx.strokeStyle = "#EEF4F2";
            ctx.fillStyle = "#EEF4F2";
            ctx.lineWidth = 4;
            ctx.shadowBlur = 2;
            ctx.shadowColor = shadowColor;

            ctx.beginPath();
            ctx.arc(padding, padding, padding/2, 0, Math.PI*2, false);
            ctx.stroke();
            ctx.fill();
        }
    }

    PathAnimation{
        id: pathAnim1
        target: aniCircle1
        running: true
        duration: 4500
        anchorPoint: Qt.point(padding,padding)
        path: Path{
            startX: padding
            startY: padding
            PathLine{x: mw - padding - gap;  y: padding ;}
            PathLine{x: mw - padding;        y: mh - padding }
            PathLine{x: padding + gap;       y: mh - padding }
            PathLine{x: padding;             y: padding }
        }
        easing.type: Easing.InOutSine
        loops: Animation.Infinite
    }

    Canvas {
        id: aniCircle2
        x:0
        y:0
        width: rhomboidButton.width
        height: rhomboidButton.height
        contextType: "2d"
        z: 10

        onPaint: {
            var ctx = aniCircle2.getContext("2d");
            ctx.strokeStyle = "#EEF4F2";
            ctx.fillStyle = "#EEF4F2";
            ctx.lineWidth = 4;
            ctx.shadowBlur = 2;
            ctx.shadowColor = shadowColor;

            ctx.beginPath();
            ctx.arc(mw - padding, mh - padding, padding/2, 0, Math.PI*2, false);
            ctx.stroke();
            ctx.fill();
        }
    }

    PathAnimation{
        id: pathAnim2
        target: aniCircle2
        running: true
        duration: 4500
        anchorPoint: Qt.point(mw - padding,mh - padding)
        path: Path{
            startX: mw - padding
            startY: mh - padding
            PathLine{x: padding + gap;       y: mh - padding }
            PathLine{x: padding;             y: padding }
            PathLine{x: mw - padding - gap;  y: padding ;}
            PathLine{x: mw - padding;        y: mh - padding }
        }
        easing.type: Easing.InOutSine
        loops: Animation.Infinite
    }
}

在这里插入图片描述
  上面的动图就是做出来的效果了,主要是使用状态机来实现鼠标悬浮时样式的改变,同时使用Canvas绘制了简单的平行四边形和两个动态的小圆圈。QML的代码非常简单易懂,所以就不作过多的解释了。

后话

  其实不管是用QPainter还是用Canvas,我觉得绘制原理都是差不多的,熟悉一种之后另一种自然也很容易掌握,不过QML下的动画功能在QWidget中实现起来确实不太方便,总的来说还是推荐使用QML进行组件的自绘。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鬼马行天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值