Qt QItemSelection::indexes()返回错误

Qt QItemSelection::indexes() return is wrong

本文关键字:返回 错误 indexes QItemSelection Qt      更新时间:2023-10-16

我从QAbstractItemModel实现了一个派生类,它似乎工作得很好。然后我创建了一个QItemSelectionModel,并将其分配给上面提到的QAbstractItemModel。

我的QAbstractItemModel不是一个小部件,也不显示,只管理一个层次结构。当选择发生变化,并且QItemSelection模型发出选择发生变化的信号时,选择和取消选择的QItemSelections似乎包含正确的数据。

当我调用他们的::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的信号。如果在模型中找到项目,则打印"找到的项目:)",显示该项目存在并且可用于模型。QGraphicsView包含一个场景(源自QGraphicsScene)。该场景是空的,并且仅表示从选择模型接收selectionChange信号的对象。当它接收到该信号时,它会打印"场景接收项目选择更改",这样我们就可以看到信号已经通过。然后是真正的东西:

  1. 我们得到了传递的"selected"变量中有多少QItemRanges的计数,这是正确的
  2. 我们得到了在传递的"selected"变量(selected.indexes())的所有范围内有多少索引的计数,该变量返回0,这是错误的,我们很快就会看到
  3. 我们手动访问"selected"变量(selected.value(0).topLeft())中第一个范围内的第一个索引,发现它确实包含一个指向正确项的索引,这表明了问题所在

如果有人知道一些事情,或者看到我的方法有错误,请告诉我。谢谢

Linux Manjaro Gcc 4.9.1 Qt5.3

衍生模型.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

衍生模型.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;
}

Widget.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

小工具.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);
    }
}

好的,所以我找到了罪魁祸首:)

如果其他人也有同样的问题,我的错误出现在派生的QAbstractItemModel类的::flags()函数中:在定义中,我没有调用基类QAbstractItemModel::flags(index)函数,一旦我调用了它,而不是自己返回标志,一切都很顺利。

所以我认为,只要你的项有一个模型可以调用的Qt::flags flags()函数,你就不必重新实现QAbstractItemModel::flags(。

模型似乎无论如何都在通过QModelIndex::flags()函数查询标志。

感谢"Ezee"answers"Kuba Ober"愿意提供帮助。

我的问题是没有调用继承类的flags函数。

def flags(self, index):
        super_flags = super().flags(index)
        flags = Qt.ItemIsEnabled
        node = index.internalPointer()
        if index.column() == 0:
            flags |= Qt.ItemIsUserCheckable
            if node.parent == self._rootNode:
                flags |= Qt.ItemIsAutoTristate
        
        return flags|super_flags