2014-05-14 17 views
5

Sono nuovo di Qt, e da quello che ho letto su qt-project.org e in altri luoghi; QtQuick sembra un'opzione interessante per la sua capacità di lavorare su dispositivi basati su puntatore e touch. Il mio problema è far sì che funzioni bene con C++.QtQuick, immagini dinamiche e C++

Ho deciso di scrivere una variante di Conway's Game of Life come passo successivo dopo "Hello World". Sono completamente disorientato su come ottenere la "scheda" - una [altezza] [larghezza] [byte-per-pixel] array di caratteri - integrata nel grafico della scena.

Fondamentalmente, il processo è che "LifeBoard" scorre le sue regole e aggiorna il char */image. Ho questo semplice QML:

:::QML 
ApplicationWindow { 
    id:   life_app_window 
    visible: true 
    title: qsTr("Life") 

    menuBar: MenuBar { 
     Menu { 
      title: qsTr("File") 
      MenuItem { 
       text: qsTr("Quit") 
       onTriggered: Qt.quit(); 
      } 
     } 
    } 

    toolBar: ToolBar { 
     id: lifeToolBar; 
     ToolButton { 
      id: toolButtonQuit 
      text: qsTr("Quit") 
      onClicked: Qt.quit() 
     } 
     ToolButton { 
      id: toolButtonStop 
      text: qsTr("Stop") 
      enabled: false 
      //onClicked: 
     } 
     ToolButton { 
      id: toolButtonStart 
      text: qsTr("Start") 
      enabled: true 
      //onClicked: //Start life. 
     } 
     ToolButton { 
      id: toolButtonReset 
      text: qsTr("Stop") 
      // onClicked: //Reset life. 
     } 
    } 

    Flow { 
     id: flow1 
     anchors.fill: parent 
     //***** 
     // WHAT GOES HERE 
     //***** 
    } 

    statusBar: StatusBar { 
     enabled: false 
     Text { 
      // Get me from number of iterations 
      text: qsTr("Iterations.") 
     } 
    } 
} 

voglio immagine provenire da una classe con un api po 'come questo:

class Life { 
    public: 
     QImage getImage() {} 
     // Or 
     char* getPixels(int h, int w, QImage::Format_ARGB8888) {} 
} 

non ho idea, e ora guado attraverso esercitazioni no Aiuto. Come si collega un'immagine char * in C++ a ??? in QML in modo che QML possa avviare/arrestare il ciclo "Life" e quindi il ciclo "Life" e aggiornare il char array e notificare a QML di ridisegnarlo?


Nota: Ho guardato sottoclassi QQuickImageProvider in base alla informazioni here. Il problema con questo approccio è che non riesco a vedere come lasciare C++ "guidare" l'immagine sullo schermo. Desidero passare il controllo da QML a C++ e lasciare che C++ dica a QML quando aggiornare il display con l'immagine modificata. C'è una soluzione con questo approccio? O un altro approccio interamente.

+0

Difficile da dire senza vedere il C++. È possibile utilizzare QListModel o QAbstractItemModel per inviare un elenco di dati da C++ a QML ed essere in grado di aggiornarli da C++. Quindi in QML "solo" usi il componente Image {} in un GridView per rappresentare il tuo gioco di griglia simile. – koopajah

+0

@koopajah Voglio che il codice di manipolazione delle immagini sia in una serie di funzioni private. Quello che voglio è avere QML del segnale C++ che l'immagine di aggiornamento sia pronta e che QML mostri quell'immagine. Inoltre QML dovrebbe essere in grado di chiamare gli slot "stop", "start" e "reset" per inizializzare/sospendere le iterazioni di vita. – justinzane

risposta

8

Il primo modo per farlo sarebbe creare un Rectangle per ogni pixel di gioco in QML, che potrebbe essere di fantasia per una scheda 8x8, ma non per una scheda 100x100, poiché è necessario scrivere manualmente il codice QML per ciascun pixel.

Così andrei per le immagini create in C++ e esposte a QML. Li chiami tramite image provider per consentire il caricamento asincrono. Lascia che Life esegua solo la logica.

L'immagine viene chiamato da QML come questo:

Image { 
    id: board 
    source: "image://gameoflife/board" 
    height: 400 
    width: 400 
} 

Ora gameoflife è il nome del provider dell'immagine e board il cosiddetto id è possibile utilizzare in seguito.

Registrati gameoflife in voi main.cpp

LifeImageProvider *lifeIP = new LifeImageProvider(life); 
engine.addImageProvider("gameoflife", lifeIP); 

dove engine è il vostro principale QQmlApplicationEngine e life un'istanza del motore Life gioco.

LifeImageProvider è la tua classe per creare pixeldata. Inizia così in qualche modo

class LifeImageProvider : public QQuickImageProvider 
{ 
public: 
    LifeImageProvider(Life *myLifeEngine); 
    QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize); 

private: 
    Life *myLifeEngine_; 
}; 

Il metodo importante è requestPixmap, che viene chiamato da QML. Devi implementarlo.

Per aggiornare il piano di gioco quando Life invia un segnale stateChanged(), espongono life come un oggetto globale per QML:

context->setContextProperty("life", &life); 

È possibile associare il segnale di QML

Image { 
    id: board 
    source: "image://gameoflife/board" 
    height: 400 
    width: 400 
} 

Connections { 
    target: life 
    onStateChanged: { 
     board.source = "image://gameoflife/board?" + Math.random() 
     // change URL to refresh image. Add random URL part to avoid caching 
    } 
} 
+0

Come si inverte il controllo in modo che QML non inizi la visualizzazione tramite 'requestPixmap' ma piuttosto ascolti un segnale da C++ e aggiorni il display (tramite' requestPixmap' o altrimenti) quando riceve detto segnale? L'obiettivo è di avere, se possibile, QML essere per lo più passivi, fornendo solo i widget dell'interfaccia mentre i tempi e la logica sono in C++. – justinzane

+0

Vedere l'elemento 'Connections' che ho postato. Questo collega il segnale 'stateChanged' di' life' con e aggiorna il meccanismo, che chiama 'requestPixmap'. –

+1

La necessità di "scrivere manualmente il codice QML per ciascun pixel" non è necessariamente vera; Una volta ho implementato Life in QML utilizzando un elemento "Grid" http://qt-project.org/doc/qt-5/qml-qtquick-grid.html con un "Repeater" per eliminare gli stati delle celle http: // qt-project.org/doc/qt-5/qml-qtquick-repeater.html; era orribilmente lento (~ 20x20 era il limite pratico), sebbene ci fossero alcuni aspetti carini come se fosse facile specificare effetti di transizione animati di nascita/morte cellulare. Un secondo tentativo di fare una vita all-QML ha prodotto questo: http://stackoverflow.com/a/24467011/24283 che è molto più veloce. – timday

1

Solo per divertimento, e a rischio di downvotes per una risposta completamente tangenziale, ecco un GameOfLife implementato interamente in QML, basta inserirlo in un file .qml ed eseguirlo con qmlscene. Funziona su Qt 5.3.0, e gira sorprendentemente (per me) velocemente su un vecchio lappy Core 2 Duo. Sono sicuro che non sarà mai così veloce/efficiente come una soluzione basata su C++ QQuickImageProvider, ma rende evidente il fatto che è possibile fare parecchio in QML senza ricorrere a C++.

import QtQuick 2.2 

Rectangle { 
    id: main 
    width: 640 
    height: 640 
    color: '#000088' 

    Timer { 
    interval: 1000/60 
    running: true 
    repeat: true 
    onTriggered: {advance();display();} 
    } 

    Component { 
    id: cellComponent 
    Rectangle { 
     objectName: 'cell' 
     property int row: 0 
     property int col: 0  
     x: main.width/2+width*col 
     y: main.height/2+height*row  
     width: 5 
     height: 5 
     radius: 2 
     smooth: true 
     color: '#ffcc00' 
    } 
    } 

    property var cells: null 

    Component.onCompleted: { 
    cells=[[-1, 0],[-1, 1],[ 0,-1],[ 0, 0],[ 1, 0]]; 
    display(); 
    } 

    function display() { 
    // Just completely regenerate display field each frame 
    // TODO: might be nicer to do differential updates, would allow birth/death animations 

    // Nuke all previously displayed cells 
    for (var i=0;i<children.length;i++) { 
     if (children[i].objectName=='cell') { 
     children[i].destroy(); 
     } 
    } 

    // Show current set of cells 
    for (var i=0;i<cells.length;i++) { 
     var c=cellComponent.createObject(
     main, 
     {'row':cells[i][0],'col':cells[i][1]} 
    ); 
    } 
    } 

    function advance() { 

    // Build a hash of the currently alive cells and a neighbour count (includes self) 
    var a=new Object; 
    var n=new Object; 
    for (var i=0;i<cells.length;i++) { 
     var p=cells[i] 
     var r=p[0]; 
     var c=p[1]; 
     if (!(r in a)) a[r]=new Object; 
     a[r][c]=1; 
     for (var dr=r-1;dr<=r+1;dr++) { 
     for (var dc=c-1;dc<=c+1;dc++) { 
      if (!(dr in n)) n[dr]=new Object; 
      if (!(dc in n[dr])) n[dr][dc]=0; 
      n[dr][dc]+=1; 
     } 
     } 
    } 

    // For all live cells, assess viability 
    var kill=[]; 
    var stay=[]; 
    for (var r in a) { 
     for (var c in a[r]) { 
     if (n[r][c]-1<2 || n[r][c]-1>3) 
      kill.push([Number(r),Number(c)]); 
     else 
      stay.push([Number(r),Number(c)]); 
     } 
    } 

    // For neighbours of live cells, assess potential for births 
    var born=[]; 
    for (var r in n) { 
     for (var c in n[r]) { 
     if (!((r in a) && (c in a[r]))) { 
      if (n[r][c]==3) 
      born.push([Number(r),Number(c)]); 
     } 
     } 
    } 

    cells=stay.concat(born) 
    } 
} 

E per una versione pura QML usando GLSL (tramite un ricorsiva QML ShaderEffect) per calcolare il Gioco delle regole di vita sulla GPU see here.

Problemi correlati