- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试基于 QAbstractItemModel 创建自己的模型。看起来效果很好。它通过了模型测试断言。
当我删除一行时,我遇到了这个奇怪的问题。删除操作正常。但随后其他行变得不可选择(并非全部)。您遇到过这样的行为吗?
在什么情况下QTreeView可以决定该行不能被选择?
有什么想法吗?如果需要,我可以提供整个模型的实现。
<小时/>编辑:作为替代方案,我正在寻找 100% 工作 QAbstractItemModel + QtSql + QTreeView 实现的示例。模型应提供添加和删除方法,并且必须通过模型测试。这也可以回答我的问题:-)
<小时/>编辑:下面是我的源代码。压缩一点,使其更小
ps 我现在发现parent() 实现中有一个错误。删除一行后nodeParams[*].row 中的值包含不正确的位置。如何在不将整个树加载到内存中的情况下解决这个问题?
class TasksModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit TasksModel(QObject *parent = 0);
virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const;
virtual Qt::ItemFlags flags ( const QModelIndex & index ) const;
virtual int columnCount ( const QModelIndex & parent = QModelIndex() ) const;
virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
virtual int rowCount (const QModelIndex & parent = QModelIndex() ) const;
virtual bool hasChildren ( const QModelIndex & parent = QModelIndex() ) const;
virtual void sort ( int column, Qt::SortOrder order = Qt::AscendingOrder );
virtual QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const;
virtual QModelIndex parent ( const QModelIndex & index ) const;
virtual bool setData ( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole );
virtual bool setHeaderData ( int section, Qt::Orientation orientation, const QVariant & value, int role = Qt::EditRole );
int selectedId;
QModelIndex indexForId(int id);
// add,remove..
int addTask(QMap<QString,QVariant> params);
void removeTask(int id, bool children);
private:
int nrOfColumns;
QSqlDatabase* dbh;
mutable QMap<qint64, QSqlQuery*> subQueries;
mutable QMap<qint64, int> rowsCount;
mutable QSqlQuery topQuery;
mutable int topRowsCount;
mutable bool topQueryReady;
QSqlQuery* verifyAndPrepareQuery (const QModelIndex& index) const;
int totalCount(const qint64 id, bool force=false) const;
void recountTotalCount(const qint64 id) const;
struct NodeParams {
int row;
int parentId;
};
mutable QMap<qint64, NodeParams> nodeParams;
signals:
public slots:
};
// ------------------ implementation ---------------------------
TasksModel::TasksModel(QObject *parent) : QAbstractItemModel(parent)
{
nrOfColumns = 2;
topQueryReady = false;
topRowsFetched = 0;
topRowsCount = 0;
selectedId = 0;
// db connection
dbh = Config::connection();
}
QVariant TasksModel::data ( const QModelIndex & index, int role ) const
{
if (!index.isValid()) return QVariant();
int column = index.column();
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
QSqlQuery* query = verifyAndPrepareQuery(index.parent());
if (!query->seek(index.row())) return QVariant("x");
switch (column)
{
case 0: return query->value(2).toString();
case 1: return query->value(4).toString() +"%";
}
}
else if (role == Qt::CheckStateRole) {
// set status of checkbox in 2nd column
if (column == 1) {
QSqlQuery* query = verifyAndPrepareQuery(index.parent());
if (!query->seek(index.row())) return QVariant();
if (query->value(3).toInt() > 0)
return Qt::Checked;
else
return Qt::Unchecked;
}
}
else if (role == Qt::TextAlignmentRole) {
switch (column)
{
case 0: return Qt::AlignLeft + Qt::AlignVCenter;
case 1: return Qt::AlignRight + Qt::AlignVCenter;
}
}
return QVariant();
}
Qt::ItemFlags TasksModel::flags ( const QModelIndex & index ) const
{
if (!index.isValid()) return 0;
Qt::ItemFlags result = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
if (index.column()==0) {
result |= Qt::ItemIsEditable;
}
else if (index.column()==1) {
result |= Qt::ItemIsUserCheckable;
}
return result;
}
QVariant TasksModel::headerData ( int section, Qt::Orientation orientation, int role) const
{
return QVariant();
}
int TasksModel::columnCount ( const QModelIndex & parent ) const
{
return nrOfColumns;
}
int TasksModel::rowCount (const QModelIndex & parent) const
{
if (parent.isValid() && parent.column() != 0)
return 0;
int id;
if (parent.isValid())
id = parent.internalId();
else
id = 0;
return totalCount(id);
}
bool TasksModel::hasChildren ( const QModelIndex & parent) const
{
if (parent.isValid()) {
if (totalCount(parent.internalId()) > 0) return true;
} else {
if (totalCount(0) > 0) return true;
}
return false;
}
void TasksModel::sort ( int column, Qt::SortOrder order )
{
}
// TreeView methods
QModelIndex TasksModel::index ( int row, int column, const QModelIndex& parent ) const
{
if (row < 0 || column < 0 || column >= nrOfColumns)
// || (parent.isValid() && parent.column() != 0))
return QModelIndex();
QSqlQuery* query = verifyAndPrepareQuery(parent);
if (!query->seek(row)) return QModelIndex();
int id = query->value(0).toInt();
if (!nodeParams.contains(id)) {
NodeParams params;
params.parentId = (int)query->value(1).toInt();
params.row = row;
nodeParams.insert(id, params);
}
return QAbstractItemModel::createIndex(row, column, id);
}
QModelIndex TasksModel::parent ( const QModelIndex & index ) const
{
return QModelIndex();
if (!index.isValid()) { return QModelIndex(); }
if (!nodeParams.contains(index.internalId())) { qDebug("b"); return QModelIndex();}
NodeParams itemParams = nodeParams.value(index.internalId());
if (itemParams.parentId == 0) return QModelIndex();
if (!nodeParams.contains(itemParams.parentId)) { qDebug("d"); return QModelIndex(); }
NodeParams parentParams = nodeParams.value(itemParams.parentId);
int parentId = itemParams.parentId;
int parentRow = parentParams.row;
return QAbstractItemModel::createIndex(parentRow, 0, parentId);
}
// Edit methods
bool TasksModel::setData ( const QModelIndex & index, const QVariant & value, int role )
{
return false;
}
bool TasksModel::setHeaderData ( int section, Qt::Orientation orientation, const QVariant & value, int role )
{
return false;
}
// Build and return query object for current index parent
QSqlQuery* TasksModel::verifyAndPrepareQuery (const QModelIndex& index) const
{
if (!index.isValid()) {
// prepare query for root
if (!topQueryReady) {
QString sql = "SELECT id,id_parent,title,complete,completion_rate,priority,date_start,date_deadline,date_preferred FROM tasks WHERE id_parent = 0";
topQuery = QSqlQuery(sql, *dbh);
topRowsFetched = 0;
topRowsCount = 0;
topQueryReady = true;
}
return &topQuery;
} else {
// prepare queries for subitems (queries stored in subQueries QMap)
qint64 id = index.internalId();
if (!subQueries.contains(id)) {
QString sql = "SELECT id,id_parent,title,complete,completion_rate,priority,date_start,date_deadline,date_preferred FROM tasks WHERE id_parent = "+ QString::number(id);
QSqlQuery* querySub = new QSqlQuery(sql, *dbh);
subQueries.insert(id, querySub);
rowsFetched.insert(id, 0);
return querySub;
}
return subQueries.value(id);
}
}
int TasksModel::totalCount(const qint64 id, bool force) const
{
force = true; // temporary setting, to force recalculation in each request, to be optimized
if (id > 0) {
if (!rowsCount.contains(id) || force) {
QString sql = "SELECT COUNT(*) FROM tasks WHERE id_parent = "+ QString::number(id);
QSqlQuery countQuery(sql, *dbh);
countQuery.next();
int count = countQuery.value(0).toInt();
rowsCount[id] = count;
return count;
}
return rowsCount.value(id);
} else {
if (topRowsCount == 0 || force) {
QString sql = "SELECT COUNT(*) FROM tasks WHERE id_parent = 0 ";
QSqlQuery countQuery(sql, *dbh);
countQuery.next();
topRowsCount = countQuery.value(0).toInt();
}
return topRowsCount;
}
}
void TasksModel::recountTotalCount(const qint64 id) const
{
// reset variables related to rowsCount and data functions. Called after new child is created or removed
if (id > 0) {
rowsCount.remove(id);
subQueries.remove(id);
}
else {
topRowsCount = 0;
topQueryReady = false;
}
totalCount(id);
}
QModelIndex TasksModel::indexForId(int id)
{
// convert id to index based on data stored in nodeParams
if (id == 0) return QModelIndex();
if (!nodeParams.contains(id)) { qDebug() << "z"; return QModelIndex(); }
NodeParams params = nodeParams.value(id);
return QAbstractItemModel::createIndex(params.row, 0, id);
}
// CRUD
int TasksModel::addTask(QMap<QString,QVariant> params)
{
// create record
QString sql;
if (params.value("complete").toInt() == 1)
params["completion_rate"] = 100;
// Add task
QSqlQuery query(*dbh);
sql = "INSERT INTO tasks (id_parent,id_sibling,position,title,description,complete,completion_rate,priority,date_start,date_deadline,date_preferred) VALUES (?,?,?,?,?,?,?,?,?,?,?)";
query.prepare(sql);
query.addBindValue(params.value("id_parent", 0));
query.addBindValue(params.value("id_sibling", 0));
query.addBindValue(params.value("position", 0));
query.addBindValue(params.value("title", ""));
query.addBindValue(params.value("description", ""));
query.addBindValue(params.value("complete", 0));
query.addBindValue(params.value("completion_rate", 0));
query.addBindValue(params.value("priority", 0));
query.addBindValue(params.value("date_start", 0));
query.addBindValue(params.value("date_deadline", 0));
query.addBindValue(params.value("date_preferred", 0));
// begin insert
int parentId = params.value("id_parent").toInt();
int count = totalCount(parentId);
beginInsertRows(indexForId(parentId), count, count);
query.exec();
int taskId = query.lastInsertId().toInt();
// update nodeParams map
NodeParams subNodeParams;
subNodeParams.row = count;
subNodeParams.parentId = parentId;
nodeParams[taskId] = subNodeParams;
recountTotalCount(parentId);
verifyAndPrepareQuery(indexForId(parentId));
endInsertRows();
// insert finished
return taskId;
}
// method recursively removes task and its children
void TasksModel::removeTask(int id, bool children)
{
if (!nodeParams.contains(id)) return;
NodeParams taskParams = nodeParams.value(id);
QString sql;
QSqlQuery query(*dbh);
// remove children
if (children) {
sql = "SELECT id FROM tasks WHERE id_parent = "+ QString::number(id);
QSqlQuery query2(sql, *dbh);
while (query2.next()) {
removeTask(query2.value(0).toInt(), true);
}
}
// remove task (tasks)
beginRemoveRows(indexForId(taskParams.parentId), taskParams.row, taskParams.row);
sql = "DELETE FROM tasks WHERE id = "+ QString::number(id);
query.exec(sql);
// update ui
recountTotalCount(taskParams.parentId);
endRemoveRows();
nodeParams.remove(id);
// remove task (tasks_parents)
sql = "DELETE FROM tasks_parents WHERE id_task = "+ QString::number(id) +" AND id_parent = "+ QString::number(taskParams.parentId);
query.exec(sql);
verifyAndPrepareQuery(indexForId(taskParams.parentId));
}
最佳答案
查看模型的源代码会很有帮助,
如果没有这个,我会从检查什么开始 QAbstractItemModel::flags方法正在返回您无法选择的项目
关于qt - QAbstractItemModel + QTreeView 什么会导致项目变得不可选择?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7462513/
我在 android 代码中使用 asmack XMPP。我可以正常登录 XMPP 服务器,但是当我尝试创建新用户时出现问题。我想要实现的是: 以管理员身份登录。 创建一个新用户。 从管理员注销。 以
这是我的标记页面,其中有一个按钮可以从数据库中搜索数据并显示在网格中 这是我背后的代码 if (!IsPostBack) { LblInfo.Text = "Page Load
当我多次将相同的 float 值插入到我的集合中时,本应花费恒定时间的 x in s 检查变得非常慢。为什么? 时序x in s的输出: 0.06 microseconds 0.09 mi
我有一个小型聊天客户端,可以将所有历史记录存储在 sqlite 数据库中。当用户单击我的应用程序中的 history 选项卡时,我的应用程序会获取所有相关历史记录并将其显示在 QWebView 中。我
我是一名优秀的程序员,十分优秀!