2014-10-14 5 views
1

Я реализовал производный класс из QAbstractItemModel и, похоже, работает нормально. Затем я создал QItemSelectionModel и назначил его упомянутой QAbstractItemModel.Qt QItemSelection :: indexes() return is wrong

My 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

Что там происходит: A DerivedModel создается и содержит 2 элемента (A и B) под корневым элементом (ROOT). Нажатие кнопки сигнализирует QItemSelectionModel, чтобы выбрать/отменить выбор A или B. Если элемент находится в модели «Найдено товар :)», печатается, показывая, что элемент существует и доступен модели. QGraphicsView содержит сцену (полученную из QGraphicsScene). Эта сцена пуста и представляет только объект, получающий сигнал выбора выбора из модели выбора. Когда он получает этот сигнал, он печатает «Изменение выбора принимаемого предмета», чтобы мы могли видеть, что сигнал прошел. затем приходит реальный материал:

  1. мы получаем счета, сколько QItemRanges в пройденному «выбранной» переменной, которая является правильным.
  2. мы получаем количество индексов внутри всех диапазонов в переданной «выбранной» переменной (selected.indexes()), которая возвращает 0, что неверно, поскольку мы можем вскоре увидеть
  3. мы вручную получаем доступ к 1-му индексу в 1-м диапазоне в «выбранной» переменной (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); 
} 

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 

Сцена.каст:

#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

Было бы полезно подготовить рабочий пример. Теперь я могу сказать, что алгоритм этого метода отличается от Qt 4.8 до 5.0. – Ezee

+0

Спасибо, сделав один и отредактировав мой вопрос. – Bazilikum

+1

Добавлен рабочий пример :) – Bazilikum

ответ

0

Хорошо, так что я нашел виновника :)

В случае, если кто-либо другой имеет то же самое проблема, моя ошибка была в функции :: flags() моего производного класса QAbstractItemModel: Внутри определения Я не называл функцию базового класса QAbstractItemModel :: flags (index), как только я назвал это, вместо того, чтобы возвращать флаги, все прошло хорошо.

Поэтому я думаю, что до тех пор, пока ваш элемент имеет функцию Qt :: flags flags(), которую может вызывать модель, вам не нужно переопределять функцию QAbstractItemModel :: flags().

Кажется, что модель все равно запрашивает флаги через функцию QModelIndex :: flags().

Спасибо, что вы "Ezee" и "Kuba Ober" за помощь.