QML Canvas - 为啥 requestPaint() 函数不调用绘画?

Posted

技术标签:

【中文标题】QML Canvas - 为啥 requestPaint() 函数不调用绘画?【英文标题】:QML Canvas - Why is the requestPaint() function not calling the paint?QML Canvas - 为什么 requestPaint() 函数不调用绘画? 【发布时间】:2015-04-29 06:45:34 【问题描述】:

可重现的例子:

在这里,我使用 QML 画布绘制了一条贝泽曲线。即使我显式更改了开始和结束值从而导致信号生成,绘画也只会被调用一次。

也可以查看输出。 为什么 requestPaint() 函数不调用paint?

Test.qml

import QtQuick 2.4

Canvas

    id: head; width: parent.width; height: parent.height

    property int    curveStartX: -1
    property int    curveStartY: -1
    property int    curveEndX: -1
    property int    curveEndY: -1

    Rectangle
    
        id: startControlPoint
        x:  head.curveStartX; y: head.curveStartY; width: 15; height: 15; color: "red"; radius: 100

        onXChanged:  
         
           console.log("start called x1"); 
           head.requestPaint(); 
           console.log("start called x2")
        

        onYChanged:  
         
           console.log("start called y1"); 
           head.requestPaint(); 
           console.log("start called y2")
        
    

    Rectangle
    
        id: endControlPoint
        x: head.curveEndX; y: head.curveEndY; width: 15; height: 15; color: "red"; radius: 100
        onXChanged:  head.requestPaint ()
        onYChanged:  head.requestPaint ()
    

    onPaint:
    
        console.log ("Paint got called!")

        var ctx = getContext ("2d");
        ctx.beginPath ();
        ctx.clearRect (0, 0, head.height, head.width);
        ctx.fill ();

        ctx.strokeStyle = head.curveColor
        ctx.lineWidth   = 2;

        ctx.beginPath ();
        // start point of the curve.
        ctx.moveTo (head.curveStartX, head.curveStartY)
        ctx.bezierCurveTo (startControlPoint.x, startControlPoint.y,
                           endControlPoint.x, endControlPoint.y,
                           head.curveEndX, head.curveEndY);
        ctx.stroke ();
    

main.qml

import QtQuick 2.4
import QtQuick.Window 2.2

Window

    visible: true
    height: 500
    width: 500

    Test
    
        id: what
    

    Component.onCompleted:
    
        what.curveStartX = 0
        what.curveStartY = 0

        what.curveEndX = 50
        what.curveEndY = 50
        /////////////////////////////////////////
        what.curveStartX = 10
        what.curveStartY = 10

        what.curveEndX = 60
        what.curveEndY = 60
    

输出:

QML debugging is enabled. Only use this in a safe environment.
qml: start called x1
qml: start called x2
qml: start called y1
qml: start called y2
qml: start called x1
qml: start called x2
qml: start called y1
qml: start called y2
qml: start called x1
qml: start called x2
qml: start called y1
qml: start called y2
qml: Paint got called!

【问题讨论】:

【参考方案1】:

requestPaint()请求 Canvas 被重新绘制。如果你看看QQuickCanvasItem::requestPaint()

void QQuickCanvasItem::requestPaint()

    markDirty(d_func()->canvasWindow);

可以看到它调用了QQuickCanvasItem::markDirty()

void QQuickCanvasItem::markDirty(const QRectF& rect)

    Q_D(QQuickCanvasItem);
    if (!d->available)
        return;

    d->dirtyRect |= rect;

    polish();

依次调用QQuickItem::polish()

void QQuickItem::polish()

    Q_D(QQuickItem);
    if (!d->polishScheduled) 
        d->polishScheduled = true;
        if (d->window) 
            QQuickWindowPrivate *p = QQuickWindowPrivate::get(d->window);
            bool maybeupdate = p->itemsToPolish.isEmpty();
            p->itemsToPolish.insert(this);
            if (maybeupdate) d->window->maybeUpdate();
        
    

因此,只要场景图确定准备就绪,就会发出 paint() 信号。信号在QQuickCanvasItem::updatePolish() 中发出。

【讨论】:

没错。因此,不要像@TheIndependentAquarius 那样实现你的绘画程序。一种方法是缓存您的点(例如使用数组),并且仅在有一定数量的此类点可用时才调用重绘。请求减少,绘画例程可以正确完成。

以上是关于QML Canvas - 为啥 requestPaint() 函数不调用绘画?的主要内容,如果未能解决你的问题,请参考以下文章

在没有 Canvas 的 QML 中绘制虚线圆

qml 中是不是可以重绘 Canvas 对象的内容?

在没有Canvas的QML中绘制虚线圆圈

QML Canvas:渲染中的不同行为

QML Canvas.requestAnimationFrame 爆炸

如何在 QML Canvas 对象中检索 QPainter 对象