Непоследовательное представление при удалении одной строки из QSqlTableModel в Qml - PullRequest
2 голосов
/ 26 января 2020

Что я пытаюсь сделать?

Я пытаюсь создать список пользователей, чьи данные поступают из базы данных. И я хочу использовать концепцию программирования модель / представление для реализации этого. Более того, я хочу применить к этому списку различные операции, такие как:

  1. Отображение списка
  2. Удаление элемента из списка
  3. Добавление элемента в список
  4. Сортировка списка

В чем проблема?

Первая операция (отображение списка) была легкой, а вторая (удаление элемент из списка), кажется, навязывает несоответствие между моделью и представлением. Независимо от того, какой элемент вы выбрали для удаления, представление всегда показывает, что два элемента были удалены (тогда как на самом деле только выбранный элемент был удален моделью). Два элемента, удаленные представлением, это выбранный элемент и последний элемент. Почему он всегда удаляет последний элемент? Как бы это исправить?

Вот мой код:

usermodel.h:

class UserModel : public QAbstractListModel {

   Q_OBJECT
   public:
         UserModel(QObject *parent = nullptr);
         int rowCount(const QModelIndex &parent = QModelIndex()) const override;
         QVariant data(const QModelIndex &index, int role) const override;
         bool removeRows(int pos, int row, const QModelIndex &parent = QModelIndex()) override;             

         Q_INVOKABLE bool del_row(int);

         // initialize and setup the database 
         static bool createConnection() {
                db = QSqlDatabase::addDatabase("QSQLITE");                  
                db.setDatabaseName("Test");
                if (!db.open()) return false;                   

                QSqlQuery q;
                QStringList t = db.tables();

                // create table users if there is none
                if (!t.contains("users", Qt::CaseInsensitive)){
                    if (!q.exec("create table users (id int primary key, firstname varchar(20))")){
                        return false;
                     }
                }
                q.exec("select * from users");
                // insert new records when there is none
                if (!q.first()){ 
                    q.exec("insert into users values(1, 'Danny')");
                    q.exec("insert into users values(2, 'Christine')");
                    q.exec("insert into users values(3, 'Lars')");
                    q.exec("insert into users values(4, 'Alex')");
                 }
            }

   private:
          QSqlTableModel *model;    // internal data store for models
          static QSqlDatabase db;
};

пользовательский режим. cpp:

QSqlDatabase UserModel::db;

UserModel::UserModel(QObject *parent) : QAbstractListModel (parent) {
    createConnection();
    model = new QSqlTableModel(this, db);
    model->setTable("users");
    model->select();
}

int UserModel::rowCount(const QModelIndex &/*parent*/) const {
    return model->rowCount();
}

QVariant UserModel::data(const QModelIndex &index, int role) const {                         

    // This allows me to return more than one column data when not permitted
    QJsonObject u_data;               
    u_data.insert("id", model->record(index.row()).value(0).toInt());
    u_data.insert("name", model->record(index.row()).value(1).toString());

    if (role == Qt::DisplayRole)
        return u_data;
    return QVariant();
}

bool UserModel::del_row(int row){   
    return removeRows(row, 1);
}

bool UserModel::removeRows(int pos, int rows, const QModelIndex &/*parent*/){
    bool response;      
    int first = pos, last = pos + rows - 1;
    beginRemoveRows(QModelIndex(), first, last);
    response = model->removeRow(first, QModelIndex());
    endRemoveRows();            
    return response;
}

main.qml:

ApplicationWindow {
    visible: true
    width: 640
    height: 480

    ListView {
        anchors.fill: parent
        delegate: SwipeDelegate {
                width: parent.width
                height: 50
                text: user_model.data(user_model.index(index, 0), 0)["name"]
                onClicked: user_model.del_row(model.index)
        }           
        model: UserModel { id: user_model }
    }
}

Примечание : Пожалуйста, не предоставляйте никаких ссылок в реплее, так как я, возможно, не смогу получить к ним доступ, потому что правительство здесь заблокировало большинство сайтов и несколько сайтов в белом списке (один из них - stackoverflow) .

1 Ответ

1 голос
/ 27 января 2020

Я не вижу необходимости создавать модель, являющуюся оболочкой другой модели, поэтому в этом первом решении я предложу решение, использующее класс QSqlTableModel напрямую. С другой стороны, при использовании removeRows() строка не удаляется, но вы должны обновить базу данных, используя метод select():

usermodel.h

#ifndef USERMODEL_H
#define USERMODEL_H

#include <QSqlTableModel>

class UserModel : public QSqlTableModel
{
    Q_OBJECT
public:
    UserModel(QObject *parent = nullptr, QSqlDatabase db = QSqlDatabase());
    QVariant data(const QModelIndex &index, int role) const;
    QHash<int, QByteArray> roleNames() const;
    Q_INVOKABLE void removeRow(int row);
};

#endif // USERMODEL_H

usermodel. cpp

#include "usermodel.h"

#include <QSqlRecord>

UserModel::UserModel(QObject *parent, QSqlDatabase db): QSqlTableModel(parent, db){
    setTable("users");
    select();
}
QVariant UserModel::data(const QModelIndex &index, int role) const{
    QVariant value;
    if (index.isValid()) {
        if (role < Qt::UserRole)
            value = QSqlQueryModel::data(index, role);
        else {
            int columnIdx = role - Qt::UserRole - 1;
            QModelIndex modelIndex = this->index(index.row(), columnIdx);
            value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
        }
    }
    return value;
}

QHash<int, QByteArray> UserModel::roleNames() const{
    QHash<int, QByteArray> roles;
    for (int i = 0; i < record().count(); i ++) {
        roles.insert(Qt::UserRole + i + 1, record().fieldName(i).toUtf8());
    }
    return roles;
}

void UserModel::removeRow(int row){
    removeRows(row, 1, QModelIndex());
    select();
}

main. cpp

#include "usermodel.h"

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QSqlQuery>

static bool createConnection() {
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("Test");
    if (!db.open()) return false;

    QSqlQuery q;
    QStringList t = db.tables();

    // create table users if there is none
    if (!q.exec("CREATE TABLE IF NOT EXISTS users (id int primary key, firstname varchar(20))")){
        return false;
    }
    q.exec("insert into users values(1, 'Danny')");
    q.exec("insert into users values(2, 'Christine')");
    q.exec("insert into users values(3, 'Lars')");
    q.exec("insert into users values(4, 'Alex')");
    return true;
}

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    qmlRegisterType<UserModel>("Database", 1, 0, "UserModel");

    QGuiApplication app(argc, argv);
    if(!createConnection())
        return -1;

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

main.qml

import QtQuick 2.14
import QtQuick.Controls 2.14
import Database 1.0

ApplicationWindow {
    visible: true
    width: 640
    height: 480

    ListView {
        anchors.fill: parent
        delegate: SwipeDelegate {
            width: parent.width
            height: 50
            text: model.firstname
            onClicked: user_model.removeRow(model.index)
        }
        model: UserModel { id: user_model }
    }
}
...