qml canvas,上下文2d在从c ++

时间:2017-01-24 10:03:01

标签: c++ qt canvas qml elf

我有Qt Quick Controls 2应用程序。在main.qml中我除了滚动视图中的其他东西之外还有:

Rectangle {
    id: graph
    width: mainArea.width / 3 - 14;
    height: mainArea.height - 20;
    ScrollView{
        anchors.fill: parent;
        Canvas {
            id:canvasGraph;
            width: graph.width;
            height: graph.height;
            property bool paintB: false;

            property string colorRect: "#FFFF40";
            property string name: "ELF header";

            property int paintX: 0;
            property int paintY: 0;

            property int widthP: 160;
            property int heightP: 30;

            property int textX: (paintX + (widthP / 2)) - 15/*func return int length of text*/;
            property int textY: (paintY + (heightP / 2)) + 3;

            onPaint:{
                if (paintB){
                    var ctx = canvasGraph.getContext('2d');
                    ctx.beginPath();
                    ctx.font = "normal 12px serif";
                    ctx.fillStyle = colorRect;
                    ctx.strokeRect(paintX, paintY, widthP, heightP);
                    ctx.fillRect(paintX, paintY, widthP, heightP);
                    ctx.strokeText("ELF header", textX, textY);
                    ctx.closePath();
                    ctx.save();
                }
            }
            MouseArea{
                id: canvasArea;
                anchors.fill: parent;
                onPressed: {
                   paint(mouseX,mouseY,"aaa",1);
                }
            }
        }
    }
}

起初我尝试通过js函数绘制到画布,这里:

function paint(x, y, name, type) {
    canvasGraph.paintB = true;
    canvasGraph.paintX = x;
    canvasGraph.paintY = y;
    canvasGraph.requestPaint();
}

通过在画布上按鼠标来调用此功能。它工作得很好,它逐个绘制矩形。但只有一个问题是,在调整应用程序窗口大小后,除了最后一个之外的所有矩形都会丢失。但这不是主要问题,因为它有效,而这个问题我以后可以解决。

对于绘制图表,我需要C ++库(ELFIO,用于读取ELF文件)。所以在main.cpp中我有两个对象。首先允许我从某些C ++类的main.qml函数调用。第二个允许我从C ++调用js函数。这是main.cpp:

QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);

QScopedPointer<elfFile> elfFileObj(new elfFile); 

QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));

engine.rootContext()->setContextProperty("elfFileObj", elfFileObj.data()); //this is for calling c++ from qml

QObject *rof = engine.rootObjects().first(); 
elfFileObj.data()->rofS = rof; //this one is for calling js func from c++
return app.exec();

如何看,读取ELF文件是从对象 elfFileObj 进行管理,其中是保存加载的ELF文件的公共变量和保存对象的变量 rofS .qml到js函数。

elfFileObj Q_INVOKABLE int loadELF(QString fileName); 其中Q_INVOKABLE是宏,这确保了这个函数可以从qml文件调用。功能:

int elfFile::loadELF(QString fileName)
{
    string fileNameReal = (fileName).toStdString().substr(7);
    if (!reader.load(fileNameReal.c_str())){
         return -1;
    }

    QVariant x(30);
    QVariant y(10);
    QVariant name("ELF header");
    QVariant type(1);

    QMetaObject::invokeMethod(rofS, "paint", Q_ARG(QVariant,x), Q_ARG(QVariant,y), Q_ARG(QVariant,name), Q_ARG(QVariant,type));
    y = QVariant(40);
    QMetaObject::invokeMethod(rofS, "paint", Q_ARG(QVariant,x), Q_ARG(QVariant,y), Q_ARG(QVariant,name), Q_ARG(QVariant,type));
}

我尝试逐个绘制两个矩形。 QMetaObject :: invokeMethod 应该调用js函数,它们在(x,y)上绘制矩形。其他args在这个时刻无法使用。

主要问题:它在画布上绘制矩形,但在每次调用invokeMethod之后都会清除画布。因此在画布上始终只保留最后一个矩形。

有人知道,如何保存画布的实际状态?谢谢你的帮助。 它不是很好的代码,但它是我第一次使用qml。

2 个答案:

答案 0 :(得分:0)

画布作为命令式绘图API,只有一个像素数据的哑缓冲区。绘制调用完成后,它没有矩形或其他任何对象的概念。因此,您负责通过onPaint处理程序显示的所有内容。它不会将画布内容从一个帧清除到另一个帧(作为优化),但它会(必要时)在调整窗口大小时将其清除,因为它必须分配不同大小的缓冲区。

您可以在此处看到此行为:

import QtQuick 2.6

Canvas {
    id: canvasGraph;
    width: 500
    height: 500
    property int paintY: 10;

    onPaint:{
        var ctx = canvasGraph.getContext('2d');
        ctx.beginPath();
        ctx.font = "normal 12px serif";
        ctx.fillStyle = "#ff0000";
        ctx.strokeRect(10, paintY, 160, 30);
        ctx.fillRect(10, paintY, 160, 30);
        ctx.closePath();
        ctx.save();
    }

    Timer {
        interval: 16
        running: true
        repeat: true
        onTriggered: {
            canvasGraph.requestPaint();
            canvasGraph.paintY += 10
            if (canvasGraph.paintY > canvasGraph.height)
                canvasGraph.paintY = 10
        }
    }
}

尝试使用qmlscene运行此示例并调整窗口大小。您会注意到所有内容都会在调整大小时被清除,除了它绘制的一个矩形。

所以,如果你想要保留所有的矩形,那么你每次都需要在onPaint处理程序中绘制它们(并使用clearRect或其他一些方法来填充背景以消除那些没有的东西。如果你正在移动东西或使它们变得不可见,那就不再属于那里了。

另一方面,我不能直接解释为什么invokeMethod会导致它清除,因为你还没有真正提供足够的代码。它可能是它调整画布大小(导致缓冲区重新分配,并被清除)。无论哪种方式,鉴于上述情况,我都说它并非全部相关。

毕竟,虽然我没有完全了解你所做的事情,但我建议Canvas可能不是做你想做的最好的工具。你可能想要查看QQuickPaintedItem,或者(更好的是)使用定制其他QQuickItems(或QSGNodes)的自定义QQuickItem来编写场景。 Canvas(和QQuickPaintedItem)虽然易于使用,但性能并不高。

答案 1 :(得分:0)

我没有解决这个问题。我只是停止使用QML并返回使用Qt。它帮助了我。