我遵循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();
}
答案 0 :(得分:0)
事实证明我需要处理一些极端情况以避免无操作或无效的移动操作。
class Baz {
init(){
print("initializing a Baz")
}
}
struct Foo {
static let bar = Baz()
}
var z = Foo.bar
z = Foo.bar