Ведение наследования проверенного статуса в QTreeView - PullRequest
3 голосов
/ 24 июля 2011

Я пытаюсь сделать что-то простое: у вас есть QTreeView.1-я глубина - только папки, 2-я глубина - только файлы.Я хочу иметь флажок с отмеченным статусом рядом с каждым элементом.Файлы либо отмечены, либо не отмечены, папки также могут быть частично проверены в зависимости от их файлов;в целом вполне естественно, я верю.

Я решил пойти путем использования QStandardItemModel и заполнить его пользовательским подклассом QStandardItem: DescriptionFileItem.Может быть, это была плохая идея, если есть более простой способ, пожалуйста, просветите меня.

Я попытался использовать сигналы и слоты, чтобы мой сигнал CheckStateChanged в файле был подключен к слоту UpdateCheckedStateOnChildStateChanged в его папке.Это потребовало, чтобы мой DescriptionFileItem также наследовал от QObject (кстати, я был удивлен, что QStandardItem не наследовал от QObject).Сначала я надеялся, что это сработает без проблем с предоставленными базовыми классами, но это не сработало: emitDataChanged(), похоже, не вызывал сигнал dataChanged () моей модели ...

Использование сигналов dataChanged модели напрямуютоже не работает: его вызов защищен, поэтому вы не можете использовать его без подклассов (я думаю, что это мой следующий шаг, если кто-то не сможет помочь мне сделать это правильно).

На данный момент у меня есть сигнал -> слот соединение, которое не будет работать, и я понятия не имею, почему;компиляция и компоновка работают нормально.Вот код;возможно, вы легко заметите мои ошибки.Я оставляю закомментированные строки, чтобы вы могли увидеть, что я сделал не так в предыдущей попытке.Спасибо за ваш вклад!

#ifndef DESCRIPTIONFILEITEM_H
#define DESCRIPTIONFILEITEM_H

#include <QStandardItem>
#include <Qt>

class DescriptionFileItem :  public QObject, public QStandardItem
{
    Q_OBJECT

public:
    explicit DescriptionFileItem(const QString & text, bool isFileName=false, QObject* parent = 0);

    void setData ( const QVariant & value, int role = Qt::UserRole + 1 );

    QVariant data( int role = Qt::UserRole + 1 ) const;

    QString text;
    Qt::CheckState checkedState;
    bool isFileName;

signals:
    void CheckStateChanged();

public slots:
    void UpdateCheckedStateOnChildStateChanged();

};

#endif // DESCRIPTIONFILEITEM_H   

Соответствующие .cpp:

#include "DescriptionFileItem.h"

DescriptionFileItem::DescriptionFileItem(const QString & text, bool isFileName, QObject* parent):
    QObject(parent),QStandardItem(text)
{
    this->isFileName = isFileName;
    checkedState = Qt::Checked;
}

void DescriptionFileItem::setData ( const QVariant & value, int role){

    if(role == Qt::CheckStateRole){
        Qt::CheckState newCheckState = (Qt::CheckState)value.toInt();
        checkedState = newCheckState;
        if(isFileName){
            if(newCheckState == Qt::Unchecked || newCheckState == Qt::Checked){
                for(int i = 0; i<rowCount(); i++){
                    DescriptionFileItem* child = (DescriptionFileItem*)QStandardItem::child(i);
                    QModelIndex childIndex = child->index();
                    child->model()->setData(childIndex,newCheckState, Qt::CheckStateRole);
                    //child->setCheckState(newCheckState);
                    //child->setData(newCheckState,Qt::CheckStateRole);
                }
                /*if(rowCount()>1){
                    emit this->model()->dataChanged(this->child(0)->index(),this->child(rowCount()-1)->index());
                }else{
                    emit this->model()->dataChanged(this->child(0)->index(),this->child(0)->index());
                }*/
            }
        }else{
            emit CheckStateChanged();
        }
        //emit this->model()->dataChanged(this->index(),this->index());
    }else{
        QStandardItem::setData(value,role);
    }
}

QVariant DescriptionFileItem::data( int role ) const{
     if (role == Qt::CheckStateRole){
            return checkedState;
     }
     return QStandardItem::data(role);
}

void DescriptionFileItem::UpdateCheckedStateOnChildStateChanged()
{
    Qt::CheckState min = Qt::Checked;
    Qt::CheckState max = Qt::Unchecked;
    Qt::CheckState childState;
    for(int i = 0; i<rowCount(); i++){
        DescriptionFileItem* child = (DescriptionFileItem*)QStandardItem::child(i);
        childState = (Qt::CheckState) child->data(Qt::CheckStateRole).toInt();
        min = min>childState ? childState: min;
        max = max<childState ? childState: max;
    }
    if(min >= max)
        setData(min, Qt::CheckStateRole);
    else
        setData(Qt::PartiallyChecked, Qt::CheckStateRole);
}  

И конструкция соединения / дерева:

        DescriptionFileItem* descFileStdItem = new DescriptionFileItem(descriptionFileName, true);
        descFileStdItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsTristate);
        descriptionFileSIModel.appendRow(descFileStdItem);
        typedef pair<string,int> indexType;
        foreach(indexType index,dataFile->indexes){
            DescriptionFileItem* key_xItem = new DescriptionFileItem(index.first.c_str());
            descFileStdItem->appendRow(key_xItem);
            key_xItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled);
            QObject::connect(key_xItem,SIGNAL(CheckStateChanged()),descFileStdItem,SLOT(UpdateCheckedStateOnModelDataChanged()));
        }

РЕДАКТИРОВАТЬ: окончательный ответ, спасибосту (см. ниже)

void DataLoadWidget::ModelItemChanged(QStandardItem *item)
{
    QStandardItem* parent = item->parent();
    if(parent == 0){
        //folder state changed--> update children if not partially selected
        Qt::CheckState newState = item->checkState();
        if(newState != Qt::PartiallyChecked){
            for (int i = 0; i < item->rowCount(); i++)
            {
                item->child(i)->setCheckState(newState);
            }
        }
    }
    else{//child item changed--> count parent's children that are checked
        int checkCount = 0;
        for (int i = 0; i < parent->rowCount(); i++)
        {
            if (parent->child(i)->checkState() == Qt::Checked)
                checkCount++;
        }

        if(checkCount == 0)
            parent->setCheckState(Qt::Unchecked);
        else if (checkCount ==  parent->rowCount())
            parent->setCheckState(Qt::Checked);
        else
            parent->setCheckState(Qt::PartiallyChecked);
    }
}

1 Ответ

1 голос
/ 24 июля 2011

Если я не понял вашего вопроса, кажется, что ваше решение слишком сложное.Вы должны быть в состоянии сделать это тривиально с реализацией QStandardItemModel по умолчанию.

Как насчет чего-то подобного (обработка ошибок пропущена)?

QObject::connect(model, SIGNAL(itemChanged(QStandardItem*)), someObject, SLOT(modelItemChanged(QStandardItem*)));

А затем в обработчике сигналов:

void modelItemChanged(QStandardItem* item)
{
    QStandardItem* parent = item->parent();
    int checkCount = 0;
    int rowCount = parent->rowCount();

    for (int i = 0; i < rowCount; i++)
    {
        if (parent->child(i)->checkState() == Qt::Checked)
            checkCount++;
    }

    switch (checkCount)
    {
    case 0:
        parent->setCheckState(Qt::Unchecked);
        break;
    case rowCount:
        parent->setCheckState(Qt::Checked);
        break;
    default:
        parent->setCheckState(Qt::PartiallyChecked);
    }
}

Это ни в коем случае не оптимально, но может быть достаточно для ваших целей.

...