2017-01-24 27 views
0

У меня есть приложение Qt Quick Controls 2. В main.qml у меня есть, кроме всего прочего холста в целях прокрутки:qml canvas, context 2d сбрасывается после каждой функции вызова js из C++

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 у меня есть два объекта. Сначала позвольте мне вызвать из main.qml функции некоторого класса C++. Второй позволяет мне вызывать функции js из C++. Вот 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 to js.

В elfFileObj is 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). Другие аргументы в этот момент непригодны для использования.

Основная проблема: Это нарисовать прямоугольники на холсте, но после каждого вызова invokeMethod будет удалено холст. Поэтому на холсте всегда остается только последний прямоугольник.

У кого-нибудь есть идея, как сохранить фактическое состояние холста? Спасибо за любую помощь. Это не очень хороший код, но это мой первый опыт работы с qml.

ответ

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 или (еще лучше), составляя вашу сцену, используя пользовательский QQuickItem, который позиционирует другие QQuickItems (или QSGNodes). Canvas (и QQuickPaintedItem), в то время как простой в использовании, не особенно эффективны.

0

Я не решил эту проблему. Я просто перестаю использовать QML и вернусь, чтобы использовать только Qt. Помогает мне.