2016-01-07 28 views
0

我遵循Qt 5.5中的Using C++ Models with Qt Quick Views和AbstractItemModel示例項目。屏幕類是模型數據; ScreenManager由QAbstractListModel派生,充當QML GridView中的模型源。在GridView中,我還在委託中添加了一個MouseArea和一些動畫來實現項目拖放。QML GridView並不反映C++模型中的變化

我的預期結果是拖動屏幕時,每當它位於另一個屏幕的頂部時,目標屏幕將被移動到拖動屏幕的最後位置,直到按鈕釋放以放下拖動的屏幕。所有的動作都應該使用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(); 
} 

回答

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