[SOLVED] QML/C++ questions



  • I have an existing C++ GUI using a QGraphicsScene and I'd like to add some QML elements to it. In particular I want a popup with a nice flickable ListView and the only way I think that can be done is by using the QML ListView. I've gotten my QML item to show up when the user clicks on a particular QGraphicsObject but am having some issues that are pretty basic.

    1. The ListView is displayed and can scroll but it's not possible to select any of items in the list.
    2. Assuming I get selection working, how do I notify my C++ code that the user has selected something in the list?

    Here's the C++ and QML code I'm using as a testbed

    @#include <QtGui/QApplication>
    #include <QGraphicsObject>
    #include <QGraphicsScene>
    #include <QGraphicsView>
    #include <QPaintEngine>
    #include <QDebug>
    #include <QtDeclarative/QtDeclarative>

    class Graphic : public QGraphicsObject
    {
    public:
    Graphic(QGraphicsScene* scene):scene(scene)
    {
    }

    virtual void paint(QPainter painter, const QStyleOptionGraphicsItem option, QWidget widget)
    {
    Q_UNUSED(option);
    Q_UNUSED(widget);
    QRectF rc = boundingRect();
    QRectF normalizedRc = QRectF(0, 0, rc.width(), rc.height());
    painter->fillRect(normalizedRc, Qt::red);
    painter->setPen(Qt::black);
    painter->drawText(normalizedRc, Qt::TextWordWrap, "Press Me");
    }
    QRectF boundingRect() const { return QRectF(0, 0, 200, 20); }
    protected:
    QGraphicsScene
    scene;
    virtual void mousePressEvent(QGraphicsSceneMouseEvent
    event)
    {
    QStringList items;
    for( int ix = 0; ix < 20; ix++)
    {
    QString str;
    items.append(str.sprintf("item %i", ix+1));
    }
    QDeclarativeEngine
    engine = new QDeclarativeEngine;
    QDeclarativeContext *context = new QDeclarativeContext(engine->rootContext());
    context->setContextProperty("myModel", items);
    QDeclarativeComponent component(engine, QUrl::fromLocalFile("Popup.qml"));
    QDeclarativeItem *item = qobject_cast<QDeclarativeItem *>(component.create(context));
    item->setWidth(boundingRect().width());
    item->setHeight(100);
    item->setPos(pos().x(), pos().y() +boundingRect().height() );
    scene->addItem(item);
    }

    };

    int main(int argc, char argv[])
    {
    QApplication a(argc, argv);
    QGraphicsScene
    scene = new QGraphicsScene(0, 0, 400, 400);
    Graphic* gr = new Graphic(scene);
    gr->setPos(100,100);
    scene->addItem(gr);
    QGraphicsView view(scene);
    view.show();
    return a.exec();
    }
    @

    @
    import Qt 4.7

    Rectangle
    {
    color:"lightgray"
    border.color:"gray"
    border.width:1
    ListView
    {
    anchors.margins:2
    anchors.fill:parent
    clip: true;
    model: myModel;
    delegate: Text
    {
    text:modelData
    }
    highlight: Rectangle
    {
    color: "lightsteelblue"
    radius: 5
    }
    }
    }
    @



  • I'd recommend using a QAbstractListModel subclass for the model.
    Or.
    An easy way I can think of is making a helper class sub-classing QObject, than registering it as a context property. But if you stick to using a simple QList for the model there is no way your QML list view will get notified that the models data has changed.

    @
    class Helper : public QObject
    {
    Q_OBJECT
    public:
    //Constructor ect.
    //...
    Q_INVOKABLE void entryClicked(int index);
    //.....
    }

    //In C++
    context->setContextProperty("Helper",myHelper);

    //QML in delegate
    MouseArea{
    anchors.fill: parent
    onClicked: {
    Helper.entryClicked(index)
    }
    }
    @
    Just an idea.



  • Thanks - that appears to work as far as letting me know when an item is clicked in the ListView. The odd thing is that it still isn't selecting the item when I click on it. Is that something that I have to manually deal with? One might assume that the ListView would select the item for me when it was clicked.

    Another issue would be if the user didn't use the mouse to select the item - what if they used the cursor keys to move the selection. In that case I wouldn't get any feedback that had happened.

    Is there really no way to be easily notified when an item ( or items with multi selection ) is selected in a ListView?



  • I missed the first line of your answer about using a QAbstractListModel. Since I didn't have any luck using the QStringList ( and trying the stringlistmodel example showed that selection didn't work their either ) I thought I've give that a try.

    I implemented a QAbstractListModel that uses a QStringList to back the data - basically I copied the code from here - http://www.java2s.com/Code/Cpp/Qt/stringlistmodelexample.htm. When I assign that to my context it appears to get the correct number of items but it doesn't display my item data and prints the following error in Application Output
    @
    file:///C:/code/test/QmlTest-build-desktop/Popup.qml:24: ReferenceError: Can't find variable: modelData
    @
    Any idea what I should use in my QML delegate to reference the data inside my QAbstractListModel? Apparently modelData is not the correct thing to use.



  • Ok, so I figured out there is a QStringListModel which derives from QAbstractListModel. Switching to that and I still have the same issues. I can't select any of the items in the ListView and I can't figure out how to access the string inside the delegate - it can't find modelData.



  • Hi,

    http://doc.qt.nokia.com/4.7-snapshot/qdeclarativemodels.html should give more information on using the modelData property (which types of models it works with, etc).

    You do need to set the current item explicitly using the mouse. Typically this is done similar to the following (from the QML pathview example):

    @
    //inside your delegate
    MouseArea {
    anchors.fill: parent
    onClicked: view.currentIndex = index
    }
    @

    This doesn't seem to be well documented at the moment -- I've created "QTBUG-18601":http://bugreports.qt.nokia.com/browse/QTBUG-18601 for this.

    Regards,
    Michael



  • Thanks. That's exactly what I was looking for. It would also be nice to document how one determines that something in the ListView was selected. After a couple more hours yesterday I finally figured out I could add a signal to my QML which would be called from onCurrentIndexChanged and then hook that signal up to a slot in my C++ code.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.