简体   繁体   English

QML GridView不能反映C ++模型中的更改

[英]QML GridView doesn't reflect changes in C++ model

I follow the Using C++ Models with Qt Quick Views and AbstractItemModel example project in Qt 5.5. 我遵循Qt 5.5中的“ 将C ++模型与Qt快速视图和AbstractItemModel一起使用”示例项目。 Screen class is the model data; 屏幕类是模型数据; ScreenManager is derived from QAbstractListModel acting as the model source in QML GridView. ScreenManager从QAbstractListModel派生而来,它是QML GridView中的模型源。 In the GridView, I also add a MouseArea and some animation in delegate to implement items drag and drop. 在GridView中,我还在委托中添加了MouseArea和一些动画来实现项目的拖放。

My expected result is when dragging a screen around, whenever it's on top of another screen, the target screen will be moved to the last position of dragged screen until button release to drop the dragged screen. 我的预期结果是在拖动屏幕时,每当它在另一个屏幕上方时,目标屏幕都将移动到拖动屏幕的最后位置,直到释放按钮以放下被拖动的屏幕为止。 All the movement should use the animation declared in QML. 所有运动都应使用QML中声明的动画。

Now it could show screens correctly and recognize the selected screen. 现在它可以正确显示屏幕并识别选定的屏幕。 But it fails to swap the dragged and dropped screens. 但是它无法交换拖放的屏幕。 The underlying list has swapped the element but it doesn't reflect to the view. 底层列表交换了元素,但未反映到视图。

A similar question is this , I try to use beginMoveRows and endMoveRows. 类似的问题是这个 ,我尝试使用beginMoveRows和endMoveRows。 But my program crashes on calling endMoveRows. 但是我的程序在调用endMoveRows时崩溃。 layoutChanged would rearrange the whole model. layoutChanged将重新排列整个模型。 Because I have animation on grid item movement. 因为我有关于网格项目移动的动画。 layoutChanged would cause non affected screens shifting from top left to their original position. layoutChanged将导致不受影响的屏幕从左上角移到其原始位置。

Edit: endMoveRows crash is caused by invalid operation described here . 编辑:endMoveRows崩溃是由此处描述的无效操作引起的。

Edit: the GridView has a 3 * 5 items. 编辑:GridView有一个3 * 5项。 Since it's in a QList, I assume I only need to move on rows. 由于它在QList中,因此我假设我只需要在行上移动。

Screen.h 屏幕

class Screen
{
    public:
    Screen(QString name, int gridId, bool active = false);

    QString name() const;
    int gridId() const;
    bool active() const;

    void setActive(bool a);

private:
    QString m_name;
    int m_gridId;
    bool m_active;
};

ScreenManager.h 屏幕管理器

#include "Screen.h"

#include <QAbstractListModel>

class ScreenManager : public QAbstractListModel
{
    Q_OBJECT
public:
    enum ScreenRoles {
        NameRole = Qt::UserRole + 1,
        GridIDRole,
        ActiveRole
    };

    ScreenManager();

    void addScreen(const Screen& screen);
    int rowCount(const QModelIndex& parent = QModelIndex()) const;
    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;

    Q_INVOKABLE int getScreenGridId(int index);
    Q_INVOKABLE bool getScreenActive(int index);
    Q_INVOKABLE void swapScreens(int index1, int index2);

protected:
    QHash<int, QByteArray> roleNames() const;

private:
    QList<Screen> m_screens;
};

ScreenManager.cpp 屏幕管理器

#include "ScreenManager.h"

#include "Screen.h"

ScreenManager::ScreenManager()
{
    int index = 0;
    for (;index < 15; index++) {
        addScreen(Screen(QString ("Screen%1").arg(index), index, true));
    }
}

void ScreenManager::addScreen(const Screen& screen)
{
    beginInsertRows(QModelIndex(), rowCount(), rowCount());
    m_screens << screen;
    endInsertRows();
}

int ScreenManager::rowCount(const QModelIndex& parent) const {
    Q_UNUSED(parent);
    return m_screens.count();
}

QVariant ScreenManager::data(const QModelIndex& index, int role) const
{
    if (index.row() < 0 || index.row() >= m_screens.count())
        return QVariant();

    const Screen& screen = m_screens[index.row()];
    if (role == NameRole)
        return screen.name();
    else if (role == GridIDRole)
        return screen.gridId();
    else if (role == ActiveRole)
        return screen.active();
    return QVariant();
}

QHash<int, QByteArray> ScreenManager::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[NameRole] = "name";
    roles[GridIDRole] = "gridId";
    roles[ActiveRole] = "active";
    return roles;
}

int ScreenManager::getScreenGridId(int index)
{
    return  m_screens.at(index).gridId();
}

bool ScreenManager::getScreenActive(int index)
{
    return  m_screens.at(index).active();
}

void ScreenManager::swapScreens(int index1, int index2)
{
    int min = index1 < index2 ? index1 : index2;
    int max = index1 > index2 ? index1 : index2;
    bool r = beginMoveRows(QModelIndex(), min, min, QModelIndex(), max);
    r = beginMoveRows(QModelIndex(), max-1, max-1, QModelIndex(), min);
    m_screens.swap(index1, index2);
    endMoveRows();
}

QML GridView related code QML GridView相关代码

ScreenManager {
    id : screenManager
}
GridView {
        id: gridView
        x: 82
        y: 113
        width: cellWidth * 5
        height: cellHeight * 3
        clip: true
        anchors.bottom: parent.bottom
        anchors.bottomMargin: 70
        anchors.topMargin: 100
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.top: parent.top
        flickableDirection: Flickable.HorizontalAndVerticalFlick
        cellWidth: 90; cellHeight: 90;
        property bool ignoreMovementAnimation: true

        MouseArea {
            id: gridViewMouseArea
            hoverEnabled: true
            preventStealing : true
            property int currentGridId: -1
            property int preIndex
            property int index: gridView.indexAt(mouseX, mouseY)
            anchors.fill: parent
            onPressAndHold: {
                currentGridId = screenManager.getScreenGridId(index)
                preIndex = index
                preIndexBackup = preIndex
                gridView.ignoreMovementAnimation = false
            }
            onReleased: currentGridId = -1
            onPositionChanged: {
                if (currentGridId != -1 && index != -1 && index != preIndex) {
                    if (screenManager.getScreenActive(index)) {
                        screenManager.swapScreens(preIndex, index)
                        preIndex = index
                    }
                }
            }
        }

        model: screenManager
        delegate: Component {
            Item {
                id: gridViewDelegate
                width: gridView.cellWidth; height: gridView.cellHeight

                Image {
                    id: itemImage
                    parent: gridView
                    x: gridViewDelegate.x + 5
                    y: gridViewDelegate.y + 5
                    width: gridViewDelegate.width - 10
                    height: gridViewDelegate.height - 10;
                    fillMode: Image.PreserveAspectFit
                    smooth: true
                    source: "qrc:/res/image/screen_icon.png"
                    visible: active

                    Text {
                        text: name
                        anchors.horizontalCenter: parent.horizontalCenter
                        anchors.verticalCenter: parent.verticalCenter
                    }

                    Rectangle {
                        anchors.fill: parent;
                        border.color: "grey"
                        border.width: 6
                        color: "transparent"; radius: 5
                        visible: itemImage.state === "active"
                    }

                    // specify the movement's animation for non-active screen icons
                    Behavior on x {
                        enabled: !gridView.ignoreMovementAnimation && itemImage.state !== "active"
                        NumberAnimation { duration: 400; easing.type: Easing.OutBack }
                    }
                    Behavior on y {
                        enabled: !gridView.ignoreMovementAnimation && itemImage.state !== "active"
                        NumberAnimation { duration: 400; easing.type: Easing.OutBack }
                    }

                    // specify the shaking animation for non-active screen icons when hold one icon
                    SequentialAnimation on rotation {
                        NumberAnimation { to:  2; duration: 60 }
                        NumberAnimation { to: -2; duration: 120 }
                        NumberAnimation { to:  0; duration: 60 }
                        running: gridViewMouseArea.currentGridId != -1 && itemImage.state !== "active"
                        loops: Animation.Infinite
                        alwaysRunToEnd: true
                    }

                    // specify the active screen's new position and size
                    states: State {
                        name: "active"
                        when: gridViewMouseArea.currentGridId == gridId
                        PropertyChanges {
                            target: itemImage
                            x: gridViewMouseArea.mouseX - width/2
                            y: gridViewMouseArea.mouseY - height/2
                            scale: 0.5
                            z: 10
                        }
                    }

                    // specify the scale speed for the active screen icon
                    transitions: Transition {
                        NumberAnimation { property: "scale"; duration: 200}
                    }
                }
            }
        }
    }

main.cpp main.cpp

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    qmlRegisterType<ScreenManager>("com.gui", 1, 0, "ScreenManager");

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

    return app.exec();
}

It turns out I need to deal some corner cases to avoid no-op or invalid move operation. 事实证明,我需要处理一些极端情况,以避免无操作或无效的移动操作。

void ScreenManager::swapScreens(int index1, int index2)
{
    int min = index1 < index2 ? index1 : index2;
    int max = index1 > index2 ? index1 : index2;
    m_screens.swap(index1, index2);
    beginMoveRows(QModelIndex(), max, max, QModelIndex(), min);
    endMoveRows();

    if (max - min > 1) {
        beginMoveRows(QModelIndex(), min + 1, min + 1, QModelIndex(), max + 1);
        endMoveRows();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM