Qt QItemSelection::indexes() 返回错误
Posted
技术标签:
【中文标题】Qt QItemSelection::indexes() 返回错误【英文标题】:Qt QItemSelection::indexes() return is wrong 【发布时间】:2014-10-14 13:11:01 【问题描述】:我从 QAbstractItemModel 实现了一个派生类,它似乎工作正常。 然后我创建了一个 QItemSelectionModel 并将其分配给提到的 QAbstractItemModel。
我的 QAbstractItemModel 不是小部件,也不会显示,只管理层次结构。 When the selection is changed, and the QItemSelection model emits the selection changed signal the QItemSelections selected and deselected seem to contain the right data.
当我调用他们的 ::indexes() 函数来获取所选项目的索引时,问题出现了,它不返回任何项目,即使我知道项目已被选中并且 ::width() 和 ::height()函数返回正确的值。
基本示例代码: (以下是演示问题的工作示例和文件)
class DerivedModel : public QAbstractItemModel
DerivedModel(QObject* parent) : QAbstractItemModel(parent)
,m_selectionModel(nullptr)
//create the selection model and assign this model to it
m_selectionModel = new QItemSelectionModel(this, this);
...
//all needed overload functions
//the DerivedModel works great
...
private:
QItemSelectionModel* m_selectionModel;
//in a different object called SceneModel (a QGraphicsScene which shows graphical items based on the DerivedModel) which is connected to the selection models selectionChanged() signal I query the new selection
SceneModel::setSelectedItems(const QItemSelection& selected, const QItemSelection& deselected)
int selectionSize_A = selected.size(); //this returns correct number of selected items
int selectionSize_B = selected.indexes().size(); //this returns 0 -> WRONG
int selectionSize_C = selected.value(0).indexes().size(); //this returns 0 -> WRONG
int selectionSize_CA = selected.value(0).width(); //this returns correct
int selectionSize_CB = selected.value(0).height(); //this returns correct
//if I purposefully try to access the 1st selected index via QItemSelectionRange::topLeft() all is good and I get the index:
QItemSelectionRange range = selected.value(0);
QModelIndex topLeft = range.topLeft(); //cool, i get the 1st selected index
//it seems there is a problem with the ::indexes function, so dived into the Qt5 source and basically implemented again whats done there and it works.
包含 cmake 构建的文件的链接: https://drive.google.com/file/d/0Bz03DnXr46WXYXRCeExtaHZadUU/view?usp=sharing
那里发生了什么: 创建一个 DerivedModel 并在根项 (ROOT) 下保存 2 个项(A 和 B)。 按下按钮向 QItemSelectionModel 发出信号以选择/取消选择 A 或 B。 如果在模型中找到该项目,则打印“Found Item :)”,表明该项目存在并且对模型可用。 QGraphicsView 拥有一个场景(从 QGraphicsScene 派生)。 该场景是空的,仅表示从选择模型接收 selectionChange 信号的对象。 当它接收到该信号时,它会打印“场景接收到的项目选择更改”,因此我们可以看到信号已经通过。 然后是真正的东西:
-
我们计算了传递的“选定”变量中有多少 QItemRanges,这是正确的。
我们计算了在传递的“选定”变量 (selected.indexes()) 的所有范围内有多少索引,该变量返回 0,这是错误的,我们很快就会看到
我们手动访问“已选择”变量 (selected.value(0).topLeft()) 中第一个范围内的第一个索引,并看到它确实拥有指向正确项目的索引,这表明存在问题。
如果有人知道某事,或者在我的方法中发现错误,请告诉我。 谢谢!
Linux Manjaro Gcc 4.9.1 Qt5.3
DerivedModel.h:
#ifndef DERIVEDMODEL_H
#define DERIVEDMODEL_H
#include <QAbstractItemModel>
//fwd declaration
QT_FORWARD_DECLARE_CLASS(QItemSelectionModel)
class Item;
class DerivedModel : public QAbstractItemModel
Q_OBJECT
public:
//model is a singleton, function to get instance
static DerivedModel& instance();
explicit DerivedModel(QObject* parent);
virtual ~DerivedModel();
/////////////////model overloads//////////////////////////////
QVariant data(const QModelIndex& index, int role) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex& index) const;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const Q_UNUSED(parent); return 1;
//////////////////////////////////////////////////////////////
//get the item from an index
Item* item(const QModelIndex& index) const return static_cast<Item*>(index.internalPointer());
//get the index from an item name
const QModelIndex indexFromName(const QString& name);
//add an item
void addItem(const QString& name, Item* parent=nullptr);
//get the selection model
QItemSelectionModel* selectionModel() const return m_selectionModel;
private:
//the instance of the singleton to return
static DerivedModel* m_instance;
//the root object for the model
//never actually used
Item* m_rootItem;
//selection model for handeling selection
QItemSelectionModel* m_selectionModel;
;
#endif
DerivedModel.cpp
#include "DerivedModel.h"
#include "Item.h"
#include <QItemSelectionModel>
#include <QDebug>
//init static member
DerivedModel* DerivedModel::m_instance = nullptr;
DerivedModel& DerivedModel::instance()
//check if set
if(!m_instance)
qDebug() << "ERROR model instance not set";
std::abort();
return *m_instance;
DerivedModel::DerivedModel(QObject* parent):
QAbstractItemModel(parent)
,m_rootItem(nullptr)
,m_selectionModel(nullptr)
//set the instance
m_instance = this;
//creae root item
m_rootItem = new Item("ROOT");
//init selection model
m_selectionModel = new QItemSelectionModel(this, this);
DerivedModel::~DerivedModel()
//selection model is child so gets deleted
QVariant DerivedModel::data(const QModelIndex& index, int role) const
//if the index is valid
if(!index.isValid())
qDebug() << "Index not valid!";
return QVariant();
//switch role
switch(role)
case Qt::DisplayRole:
QString name = static_cast<Item*>(index.internalPointer())->name();
return name;
break;
default:
return QVariant();
Qt::ItemFlags DerivedModel::flags(const QModelIndex& index) const
//check valid
if(!index.isValid()) return 0;
return static_cast<Item*>(index.internalPointer())->flags();
QVariant DerivedModel::headerData(int section, Qt::Orientation orientation, int role) const
//unused for now
Q_UNUSED(section);
Q_UNUSED(orientation);
if(role==Qt::DisplayRole) return QVariant("HeaderData");
else return QVariant();
QModelIndex DerivedModel::index(int row, int column, const QModelIndex& parent) const
Item* parentItem(nullptr);
//is valid?
if(!parent.isValid())
parentItem = m_rootItem;
else
parentItem = item(parent);
//child pointer holder
Item* childItem = parentItem->children().value(row);
//is null?
if(childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
QModelIndex DerivedModel::parent(const QModelIndex& index) const
//check valid
if(!index.isValid()) return QModelIndex();
//get child
Item* childItem = static_cast<Item*>(index.internalPointer());
//find parent
Item* parentItem = childItem->parent();
//is null?
if(parentItem == m_rootItem) return QModelIndex();
return createIndex(parentItem->parent()->children().indexOf(parentItem), 0, parentItem);
int DerivedModel::rowCount(const QModelIndex& parent) const
//parent holder
Item* parentItem;
//check 0 column (not sure why, but is in example, maybe the model iterates also through different columns)
if(parent.column()>0) return 0;
//check valid
if(!parent.isValid()) parentItem = m_rootItem;
else parentItem = static_cast<Item*>(parent.internalPointer());
return parentItem->children().length();
const QModelIndex DerivedModel::indexFromName(const QString& name)
//make a match based on the name
//and return 1st match
QModelIndex index = match(DerivedModel::index(0,0,QModelIndex()),
Qt::DisplayRole, name, 1,
Qt::MatchFlags(Qt::MatchExactly|Qt::MatchRecursive))
.value(0);
return index;
void DerivedModel::addItem(const QString& name, Item* parent)
//check parent
if(!parent) parent = m_rootItem;
//create the item
//will be deleted once parent is deleted
new Item(name, parent);
项目.h:
#ifndef ITEM_H
#define ITEM_H
#include <QString>
#include <QList>
#include <QDebug>
class Item
public:
Item(const QString& name, Item* parent=nullptr) :
m_name(name), m_parent(parent)
//add as child to parent
if(parent) parent->addChild(this);
//set the flag to enable selection
m_flags = Qt::ItemIsSelectable;
qDebug() << "Created Item "+name;
;
~Item()
//delete children
for (auto& child : m_children)
delete child;
;
//get the name
const QString& name() const return m_name;
//get the flags
const Qt::ItemFlags& flags() const return m_flags;
//gte the parent
Item* parent() const return m_parent;
//get the children
const QList<Item*>& children() return m_children;
//add a child
void addChild(Item* item) m_children.append(item);
private:
//name
QString m_name;
//flags
Qt::ItemFlags m_flags;
//parent
Item* m_parent;
//list og children
QList<Item*> m_children;
;
#endif
场景.h:
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
//fwd dec
QT_FORWARD_DECLARE_CLASS(QItemSelection)
QT_FORWARD_DECLARE_CLASS(QGraphicsRectItem)
class Scene : public QGraphicsScene
public:
Scene(QObject* parent);
virtual ~Scene()
public slots:
//pass the selection to the squares
void setSelection(const QItemSelection& selected, const QItemSelection& deselected);
;
#endif
场景.cpp:
#include "Scene.h"
#include "DerivedModel.h"
#include "Item.h"
#include <QItemSelectionModel>
#include <QGraphicsRectItem>
#include <QRect>
#include <QDebug>
Scene::Scene(QObject* parent) : QGraphicsScene(parent)
//connect to the models selection change
connect(DerivedModel::instance().selectionModel(), &QItemSelectionModel::selectionChanged,
this, &Scene::setSelection);
void Scene::setSelection(const QItemSelection& selected, const QItemSelection& deselected)
Q_UNUSED(deselected);
//testing changes
int A = selected.size();
int B = selected.indexes().size();
QModelIndex index = selected.value(0).topLeft();
QString name = "";
if(index.isValid()) name = static_cast<Item*>(index.internalPointer())->name();
qDebug() << "Scene recieved item selection change";
qDebug() << "Number of selected QItemRanges from source = "+QString::number(A);
qDebug() << "Number of selected INDEXES from source = "+QString::number(B);
qDebug() << "Manually accessd 1st index in 1st range returns item named: "+name;
小部件.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
//fwd dec
QT_FORWARD_DECLARE_CLASS(QPushButton)
QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
QT_FORWARD_DECLARE_CLASS(QHBoxLayout)
QT_FORWARD_DECLARE_CLASS(QGraphicsView)
class DerivedModel;
class Scene;
class Widget : public QWidget
public:
Widget(QWidget* parent=nullptr);
virtual ~Widget()
private slots:
//button toggle alot
void toggle(bool state);
private:
//layout
QVBoxLayout* m_mainVBLayout;
//horizontal layout for the buttons
QHBoxLayout* m_HBButtonsLayout;
//GraphicsView widget for the scene
QGraphicsView* m_graphicsView;
//the graphics scene recieving the change event where the problem is
Scene* m_scene;
//push buttons
QPushButton* m_button1;
QPushButton* m_button2;
//the DerivedModel
DerivedModel* m_model;
;
#endif
Widget.cpp:
#include "Widget.h"
#include "DerivedModel.h"
#include "Scene.h"
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGraphicsView>
#include <QDebug>
#include <QItemSelection>
Widget::Widget(QWidget* parent) : QWidget(parent)
,m_mainVBLayout(nullptr)
,m_HBButtonsLayout(nullptr)
,m_graphicsView(nullptr)
,m_scene(nullptr)
,m_button1(nullptr)
,m_button2(nullptr)
,m_model(nullptr)
//init the DerivedModel
m_model = new DerivedModel(this);
//add two items to the model
m_model->addItem("A");
m_model->addItem("B");
//create the main layout
m_mainVBLayout = new QVBoxLayout(this);
//create the buttons layout
m_HBButtonsLayout = new QHBoxLayout;
//add it to the main layout
m_mainVBLayout->addLayout(m_HBButtonsLayout);
//create the buttons
m_button1 = new QPushButton("A", this);
m_button2 = new QPushButton("B", this);
//set them to be checkable
m_button1->setCheckable(true);
m_button2->setCheckable(true);
//connect their signals
connect(m_button1, &QPushButton::toggled, this, &Widget::toggle);
connect(m_button2, &QPushButton::toggled, this, &Widget::toggle);
//add them to the layout
m_HBButtonsLayout->addWidget(m_button1);
m_HBButtonsLayout->addWidget(m_button2);
//create the graphics view
m_graphicsView = new QGraphicsView(this);
//create the scene
m_scene = new Scene(this);
m_scene->setSceneRect(QRect(0,0,50,25));
//set its scene
m_graphicsView->setScene(m_scene);
//add the graphics view to the layout
m_mainVBLayout->addWidget(m_graphicsView);
void Widget::toggle(bool state)
//get the sender
QPushButton* button(nullptr);
if(sender()==m_button1) button = m_button1;
else button = m_button2;
//get the name of the item related to the button to change
QString name = button->text();
//get the index based on the name
//im using the instance of the DerivedModel because this is how I implement it in my project;
QModelIndex itemIndex = DerivedModel::instance().indexFromName(name);
//check if index is valid
if(!itemIndex.isValid())
qDebug() << "Index for item "+name+" not valid!";
return;
else
qDebug() << "Found Item :)";
//create a QItemSelection as how it is in my project
QItemSelection selection;
//add the index to the selection
selection.select(itemIndex, itemIndex);
//check the state
if(state)
//add to the selection
DerivedModel::instance().selectionModel()->select(selection, QItemSelectionModel::Select);
else
//remove from selection
DerivedModel::instance().selectionModel()->select(selection, QItemSelectionModel::Deselect);
【问题讨论】:
如果您准备一个工作示例会很有帮助。我现在能说的是这种方法的算法从Qt 4.8到5.0不同。 谢谢,制作一个并将编辑我的问题。 添加了一个工作示例:) 你能不能把它弄小一点?将所有内容放入一个文件中,消除不必要的冗长等。这实际上不应该超过 100-150 行。 【参考方案1】:好的,所以我找到了罪魁祸首:)
如果其他人有同样的问题,我的错误出现在我派生的 QAbstractItemModel 类的 ::flags() 函数中: 在定义中,我没有调用基类 QAbstractItemModel::flags(index) 函数,一旦我调用它而不是自己返回标志,一切顺利。
所以我认为只要你的item有模型可以调用的Qt::flags flags()函数,就不必重新实现QAbstractItemModel::flags()函数了。
似乎模型无论如何都在通过 QModelIndex::flags() 函数查询标志。
感谢“Ezee”和“Kuba Ober”愿意提供帮助。
【讨论】:
以上是关于Qt QItemSelection::indexes() 返回错误的主要内容,如果未能解决你的问题,请参考以下文章