Я создаю простое приложение QMainWindow следующим образом:
Заголовочный файл:
//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QAbstractItemModel>
#include <QTreeView>
#include <QVBoxLayout>
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MyTreeModel : public QAbstractItemModel
{
public:
Q_OBJECT
public:
MyTreeModel(QObject *parent=NULL);
QModelIndex parent(const QModelIndex &index) const;
QModelIndex index(int row, int column,const QModelIndex &parent = QModelIndex()) const;
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation,int role = Qt::DisplayRole) const;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
QTreeView * view;
MyTreeModel *model;
private:
Ui::MainWindow *ui;
QVBoxLayout * layout;
};
#endif // MAINWINDOW_H
Исходный файл:
//mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
QModelIndex MyTreeModel::parent(const QModelIndex &index) const
{
return QModelIndex();
}
QVariant MyTreeModel::data(const QModelIndex &index, int role) const
{
return "111";
}
QVariant MyTreeModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
if (section==0)
return "Column0";
else if (section==1)
return "Column1";
else if (section==2)
return "Column2";
else
return QVariant();
}
else
return QVariant();
}
QModelIndex MyTreeModel::index(int row, int column, const QModelIndex &parent)
const
{
return createIndex(row, column, NULL);
}
int MyTreeModel::columnCount(const QModelIndex &parent) const
{
return 3;
}
int MyTreeModel::rowCount(const QModelIndex &parent) const
{
return 2;
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
model=new MyTreeModel();
view= new QTreeView(this);
view->setModel(model);
layout= new QVBoxLayout(this);
layout->addWidget(view);
QWidget *window = new QWidget();
window->setLayout(layout);
setCentralWidget(window);
}
MainWindow::~MainWindow()
{
delete ui;
}
Я ожидаю, что он покажет 2 строки * 3 столбца «111», но это не так. Он показывает только заголовок. Если я изменю QVariant MyTreeModel::headerData
на:
QVariant MyTreeModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (section==0)
return "Column0";
else if (section==1)
return "Column1";
else if (section==2)
return "Column2";
else
return QVariant();
}
Это ничего не покажет. Заголовок также исчезает.
Почему?
@Г.М. Я использую Qt 4, в котором нет этого класса.
@William Qt4 устарел как минимум 10 лет. Даже если реализация программирования модели/представления Qt за это время не сильно изменилась, тот факт, что вы используете такую старую библиотеку, должен быть выделен в вашем сообщении (и в его тегах), иначе мы бы (правильно) Предположим, что вы используете поддерживаемые в настоящее время версии, которые могут вести себя по-другому.
В вашем коде есть несколько проблем. Во-первых, поскольку это одноуровневая модель, члены index
, rowCount
и columnCount
должны возвращать «реальные» значения только тогда, когда указанный родительский индекс недействителен (т. е. номинальный корневой индекс). Итак, эти участники становятся...
QModelIndex MyTreeModel::index (int row, int column, const QModelIndex &parent) const
{
if (parent.isValid())
return {};
return createIndex(row, column, nullptr);
}
int MyTreeModel::columnCount (const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return 3;
}
int MyTreeModel::rowCount (const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return 2;
}
Во-вторых, ваш MyTreeModel::data
участник всегда возвращается (эффективно) QString("111")
. Это не удастся для тех ролей, где QString
невозможно преобразовать в ожидаемый тип: например. QSize
для Qt::SizeHintRole
. Итак, участник data
становится...
QVariant MyTreeModel::data(const QModelIndex &index, int role) const
{
if (role != Qt::DisplayRole)
return {};
return "111";
}
С учетом вышеуказанных изменений код работает должным образом.
Следует также отметить, что, хотя это может иметь смысл для простоты тестового примера (который по сути создает двухмерную модель таблицы), использование null в качестве указателя для createIndex()
в принципе неверно при создании модели, которая могла бы быть древовидной.
@Г.М. Ваш измененный код работает! Спасибо. Фактически, когда я просто изменяю функцию данных как вашу, она может отображать 2 строки * 3 столбца «111». Но программа вылетает, когда я нажимаю стрелку в древовидном представлении, чтобы развернуть строку. Не знаю почему? Мне интересно узнать о роли и времени индекса, данных, rowCount, columnsCount, родителя в модели. Как они работают вместе, чтобы отображать фактические данные в представлении?
@musicamante Если в createIndex() не используется нулевой указатель, что мне следует использовать для древовидной модели?
@ Уильям, обычно тебя не должно слишком волновать «время» index()
, data()
, rowCount()
и columnCount()
: они могут вызываться в самые разные моменты в зависимости от различных потребностей. Причина, по которой вы должны все реализовать, Г.М. говорит не из-за порядка, в котором могут вызываться функции, а из-за того, что их реализация должна быть выполнена соответствующим образом: аргумент parent
rowCount()
и columnCount()
используется для возврата количества возможных дочерних элементов для данного родительского элемента, и поскольку вы мы всегда произвольно возвращаем значение >= 0, это означает, что »
@William » у каждого элемента есть другие дочерние элементы (в вашем примере 6, 3 строки и 2 столбца). Это неправильно, поскольку вызывает бесконечную рекурсию, поскольку у каждого из этих дочерних элементов, в свою очередь, будут другие дочерние элементы. Я настоятельно рекомендую вам терпеливо изучить Руководство по программированию модели/представления и выполнить несколько правильных обучающих тестов, начиная с одномерных моделей («списков»), затем двумерных («таблиц») и, наконец, древовидных моделей, все с связанные представления: QListView, QTableView, QTreeView соответственно. Третий аргумент »
@William » createIndex()
обычно не требуется для простых моделей (1D и 2D), поскольку их логические координаты в моделях очевидны, но древовидные модели требуют ссылки на данную строку и столбец, иначе модель не сможет узнать, элемент в строке X и столбце Y относится к верхнему уровню или дочернему, главному дочернему элементу и т. д. В документе выше (и в документации QAbstractItemModel) есть некоторые пояснения по этому поводу, но вам действительно следует начать с правильного понимания более простые модели перед переходом к древовидным; это шаги, которые вы не можете пропустить.
Предоставляет ли QAbstractItemModelTester какую-либо полезную диагностику?