Qt QItemSelection :: indexes()返回错误

时间:2014-10-14 13:11:01

标签: c++ qt

我从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 build的文件的链接: https://drive.google.com/file/d/0Bz03DnXr46WXYXRCeExtaHZadUU/view?usp=sharing

那里发生了什么: 创建DerivedModel并在根项目(ROOT)下保存2个项目(A和B)。 按下按钮指示QItemSelectionModel选择/取消选择A或B. 如果在模型中找到该项目"找到项目:)"打印,显示该项目存在且可供模型使用。 QGraphicsView包含一个Scene(派生自QGraphicsScene)。 该场景为空,仅表示从选择模型接收selectionChange信号的对象。 当它收到该信号时,它打印"场景接收的项目选择改变"所以我们可以看到信号已经过去了。 然后是真实的东西:

  1. 我们得到了通过"选择"中有多少QItemRanges的计数。变量,这是正确的。
  2. 我们计算了传递的"选择的#34;中所有范围内的索引数量。变量(selected.indexes())返回0,这是我们很快就会看到的错误
  3. 我们手动访问"选择"中第一个范围内的第一个索引变量(selected.value(0).topLeft())并看到它确实包含一个指向正确项目的索引,显示问题。
  4. 如果有人知道某事,或者我的方法中有错误,请告诉我。 谢谢!

    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); 
    }
    

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

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

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

    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);
        }
    }
    

1 个答案:

答案 0 :(得分:0)

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

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

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

似乎模型正在通过QModelIndex :: flags()函数查询标志。

感谢“Ezee”和“Kuba Ober”愿意提供帮助。