首页 文章

QML GridView不反映C模型的变化

提问于
浏览
0

我遵循Qt 5.5中的Using C++ Models with Qt Quick Views和AbstractItemModel示例项目 . 屏幕类是模型数据; ScreenManager派生自QAbstractListModel,充当QML GridView中的模型源 . 在GridView中,我还在委托中添加了MouseArea和一些动画来实现项目的拖放 .

我的预期结果是当拖动屏幕时,只要它在另一个屏幕的顶部,目标屏幕将移动到拖动屏幕的最后位置,直到按钮释放以放下拖动的屏幕 . 所有运动都应该使用QML中声明的动画 .

现在它可以正确显示屏幕并识别所选屏幕 . 但它无法交换拖放屏幕 . 基础列表已交换元素,但它不反映到视图 .

类似的问题是this,我尝试使用beginMoveRows和endMoveRows . 但是我的程序在调用endMoveRows时崩溃了 . layoutChanged将重新排列整个模型 . 因为我有关于网格项目移动的动画 . layoutChanged会导致不受影响的屏幕从左上角移动到其原始位置 .

编辑:endMoveRows崩溃是由here描述的无效操作引起的 .

编辑:GridView有3 * 5项 . 因为它在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相关代码

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中

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();
}

1 回答

  • 0

    事实证明我需要处理一些极端情况以避免无操作或无效的移动操作 .

    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();
        }
    }
    

相关问题