N
N
Neyury2016-12-20 14:10:42
Qt
Neyury, 2016-12-20 14:10:42

How to output information from QVector correctly?

It is required to implement a simple directory. Created a class describing the directory object. I added several instances of the class to QVector and tried to display this in a table, creating a QTableWidgetItem for each field, but in the end, with a non-empty vector, the program with code 3 crashes. So apparently I'm doing something wrong, and the following questions arise:
How to clear widget (or Layout) from all child widgets? (Each time the directory changes, I planned to re-create a new table)
How would you implement changing information output? (Maybe I'm thinking in the wrong direction)

Answer the question

In order to leave comments, you need to log in

1 answer(s)
F
Fat Lorrie, 2016-12-20
@Neyury

Discover Model/View in Qt. In particular, your problem with directories is described here .
In short: you have a model where you bind your storage method ( QVectorin your case) to a standard interface QAbstractItemModel(or its descendants QAbstractListModel, QAbstractTableModelor QAbstractTreeModel) that can be passed to one of the standard view widgets ( QListView, QTableView, QTreeView). By implementing the interface (overriding the virtual methods for databoth setDatarole== Qt::DisplayRoleand Qt::EditRole), you can simply change the state of the model, and the view widgets will pick up the changes automatically.
In addition, there is also such a thing - QDataWidgetMapper. He knows how to bind widgets (even ordinary ones, not View ones) to certain fields in the model.
Пример объемный, но минимально полный, содержащий всю необходимую инфраструктуру. Допустим, мы хотим создать редактор для некоторых статей (блога?). Нам нужен простой класс Article, чтобы собрать в кучу данные о каждой отдельной статье:

class Article
{
public:
    Article(int id, const QString& title, const QString& content) 
        : _id(id), _title(title), _content(content) {}

    int id() const { return _id; } 
    void setId(int id) { _id = id; }

    QString title() const { return _title; } 
    void setTitle(const QString& title) { _title = title; }

    QString content() const { return _content; }
    void setContent(const QString& content) { _content = content; }

private:
    int     _id;
    QString _title;
    QString _content;
};

Для него напишем модель хранения этих статей - ArticlesModel, содержащей массив статей и методы для их добавления/удаления. Кроме того, для нее мы реализуем стандартный интерфейс QAbstractTableModel (методы rowCount, columnCount, data и setData), чтобы виджеты знали, как оттуда брать и как обновлять данные.
class ArticlesModel :
        public QAbstractTableModel
{
public:
    ArticlesModel(QObject *parent=nullptr);

    void addArticle(const Article& art);
    void removeArticle(int id);

    //QAbstractTableModel interface implamentation
    int rowCount(const QModelIndex &/*parent*/) const override;
    int columnCount(const QModelIndex &/*parent*/) const override;

    bool data(QModelIndex& index, const QVariant &value, int role) const override;
    QVariant setData(QModelIndex& index, int role) override;
    
private:
    std::vector<Article> _articles;
};

// =================================================================

ArticlesModel(QObject *parent/*=nullptr*/) 
    : QAbstractTableModel(parent)
{
}

void ArticlesModel::addArticle(const Article& art) {
    _articles.push_back(art);
}
void ArticlesModel::removeArticle(int id) {
    auto found = std::find_if(begin(_articles), end(_articles), 
                            [id](const Article& art) {
                                art.id() == id;
                            });
    if (found != std::end(_articles)) {
        _articles.erase(artIt);
    }
}


int ArticlesModel::rowCount(const QModelIndex &/*parent*/) const override {
    return _articles.size();
}
int ArticlesModel::columnCount(const QModelIndex &/*parent*/) const override {
    return 2;
}

bool ArticlesModel::data(QModelIndex& index, const QVariant &value, int role) const override {
    if (!index.isValid) return QVariant();

    if (role == Qt::EditRole) {
        switch (column) {
            case 0: 
                QVariant(_articles[index.row()].setTitle(value.toString()));
                return true; 
            case 1:
                QVariant(_articles[index.row()].setContent(value.toString()));
                return true; 
        }
    }
    return false;
}

QVariant ArticlesModel::setData(QModelIndex& index, int role) override {
    if (!index.isValid) return QVariant();

    if (role == Qt::DisplayRole || role == Qt::EditRole) {
        switch (column) {
            case 0: return QVariant(_articles[index.row()].title()); 
            case 1: return QVariant(_articles[index.row()].content());
        }
    }
    return QVariant();
}

Тогда можно сделать так:
// this - это какой-то родительский виджет

auto titleEditor = new QLineEdit(this);
auto contentEditor = new QTextEdit(this);

QAbstractTableModel *model = new ArticlesModel(this);
model->addArticle(Article(1, "Hello title", "Hello content"));
model->addArticle(Article(2, "Another title", "Another content"));

auto mapper = new QDataWidgetMapper(this);
mapper->setModel(model);
mapper->addMapping(titleEditor, 0);
mapper->addMapping(contentEditor, 1);
mapper->toFirst(); // Переключаемся на первый элемент модели

Теперь мы можем добавлять/удалять статьи в model->add/removeArticle(const Article&), переходить с заметки на заметку с помощью mapper->setCurrentIndex(int index), менять содержимое заметок в редакторе.
Можно добавить QListView, так же передать туда модель через setModel() и связать сигнал изменения текущего элемента списка с тем же действием mapper, добавить пару кнопок для добавления/удаления заметок и мы получим полноценный редактор.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question