c++ - Qt QItemSelection::indexes() 返回错误

标签 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。 如果在模型中找到该项目,则会打印“Found Item :)”,表明该项目存在并且可供模型使用。 QGraphicsView 拥有一个场景(从 QGraphicsScene 派生)。 该场景是空的,仅表示从选择模型接收选择更改信号的对象。 当它收到该信号时,它会打印“场景收到项目选择更改”,这样我们就可以看到信号已经通过。 然后是真正的东西:

  1. 我们得到了传递的“selected”变量中有多少个 QItemRange 的计数,这是正确的。
  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”和“Kuba Ober”愿意提供帮助。

关于c++ - Qt QItemSelection::indexes() 返回错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26361727/

相关文章:

python - 如何在 Windows 10 中安装 PyQt4 for Python 3.6

c++ - 将 Qt 项目文件迁移到 CMake

c++ - Visual Studio 2013 在/OPT :ICF? 存在的情况下是否正确优化

c++ - 需要多平台存档库

c++ - 通过指针恢复对数组的引用。 UB 与否?

qt - 开始学习 Qt 的最佳地点是什么?

c++ - Qt 小部件全屏边距

c++ - Qt 使用 QNetworkAccessManager 执行多个 get

c++ - 在 openacc 中为每个帮派进行递归的私有(private)数组

c++ - 预处理器错误 C++