[英]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.