Как сделать кликабельные CheckBox-ы в QTreeView с моделью QFileSystemModel

Суть вот в чем. Нужно сделать простой обзорник директории относительно места расположения файла, с выводом только файлов SQL. С этим справился. Но есть отдельный момент: необходимо иметь возможность выбирать эти файлы посредством проставки галки в checkbox-е. Также должна быть возможность выбора группы файликов, выбором галкой папки, и отсутствия галки, если в папке файлов нема... Запнулся на CheckBox-ах. Вот в упор не могу въехать. Даже пробовал дочерний класс лепить, не вышло ничерта (не выкладываю, ибо чушь).

Для ЛЛ: топики Модель для QListView с чекбоксами и текстом

и

QTreeView с QCheckBox

Изучил. Не понял. Мне бы код сразу рабочий, без обобщающих вставок. Ибо я человек туповатый и иносказания понимаю плохо...

Мой "код" (по сути заготовка):

main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QFileSystemModel>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    QFileSystemModel *model = new QFileSystemModel;

};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    model->setRootPath(QDir::currentPath());
    model->setNameFilters(QStringList () << "*.sql");
    model->setNameFilterDisables(false);

    model->setFilter(QDir :: Files | QDir :: AllDirs | QDir :: NoDotAndDotDot);
    ui->setupUi(this);

    ui->treeView->setModel(model);
    for (int i = 1; i < model->columnCount(); ++i)
    ui->treeView->hideColumn(i);

    ui->treeView->setRootIndex(model->index(QDir::currentPath()));

}

MainWindow::~MainWindow()
{
    delete ui;
}

В ui-шке только QTreeView и все.


Ответы (2 шт):

Автор решения: Sergey Tatarincev

Да в целом направление было правильное, и я бы все же старался разобраться в том что люди пишут ;) Вам надо реализовать свою модель с наследованием от QFileSystemModel и просто добавить в нее реализацию чекбоксов

.h

class MyFileSystemModel: public QFileSystemModel
{
    Q_OBJECT
 public:
     MyFileSystemModel();
     virtual bool setData( const QModelIndex& index, const QVariant& value, int role );
     virtual Qt::ItemFlags flags( const QModelIndex & index ) const;
     virtual QVariant data( const QModelIndex &index, int role )const;
 signals:
     void itemChecked(const QModelIndex&);
 protected slots:
     void onItemChecked(const QModelIndex& index);
 private:
     mutable QMap <qint64, Qt::CheckState> checkedItems;
};

.cpp

MyFileSystemModel::MyFileSystemModel()
{
    connect(this, SIGNAL(itemChecked(const QModelIndex&)), SLOT(onItemChecked(const QModelIndex&)));
}

bool MyFileSystemModel::setData( const QModelIndex& index, const QVariant& value, int role ) {

    if( role == Qt::CheckStateRole && index.column() == 0 ) {
        checkedItems[ index.internalId() ] = static_cast<Qt::CheckState>( value.toInt() );
        emit itemChecked(index);
        emit dataChanged(index, index.sibling(0,0));
        return true;
    }
    return QFileSystemModel::setData(index, value, role);
}

Qt::ItemFlags MyFileSystemModel::flags( const QModelIndex & index ) const {
    return QFileSystemModel::flags( index ) | Qt::ItemIsUserCheckable;
}

QVariant MyFileSystemModel::data( const QModelIndex& index, int role ) const {
    if( role == Qt::CheckStateRole && index.column()==0 ) {
        return QVariant( checkedItems[ index.internalId() ] );
    }
    return QFileSystemModel::data( index, role );
}

void MyFileSystemModel::onItemChecked(const QModelIndex &index)
{
    Qt::CheckState state = checkedItems [index.internalId()];
    fetchMore(index);
    if ( state == Qt::Checked || state == Qt::Unchecked ) {
        for ( int i = 0; i < rowCount(index); i++ ) {
            QModelIndex child = index.child(i, 0);
            if (checkedItems [child.internalId()] != state )
                setData(child, state, Qt::CheckStateRole);
        }
    }

    QModelIndex parent = index.parent();
    if (parent.isValid()) {
        state = checkedItems [parent.child(0, 0).internalId()];
        if (state == Qt::PartiallyChecked)
            checkedItems[parent.internalId()] = state;
        else {
            int i = 1;
            while (i < rowCount(parent) && checkedItems [parent.child(i, 0).internalId()] == state)
                i++;
            if (i != rowCount(index))
                state = Qt::PartiallyChecked;
            checkedItems [parent.internalId()] = state;
        }
    }

}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
//.......
MyFileSystemModel *model = new MyFileSystemModel;
    model->setRootPath(QDir::currentPath());
    model->setNameFilters(QStringList () << "*.*");
    model->setNameFilterDisables(false);
    model->setFilter(QDir :: Files | QDir :: AllDirs | QDir :: NoDotAndDotDot);

    ui->treeView->setModel(model);
    ui->treeView->setRootIndex(model->index(QDir::currentPath()));
}
→ Ссылка
Автор решения: Gangrantua

Чутка допилил предыдущий ответ. Чисто, чтобы было:

void MyFileSystemModel::onItemChecked(const QModelIndex &index)
{
    Qt::CheckState state = checkedItems [index.internalId()];
    fetchMore(index);
    if ( state == Qt::Checked || state == Qt::Unchecked ) {
        for ( int i = 0; i < rowCount(index); i++ )
        {
            QModelIndex child = index.child(i, 0);
            if (checkedItems [child.internalId()] != state )
                setData(child, state, Qt::CheckStateRole);
        }
    }
    int ch = 0;
    QModelIndex parent = index.parent();

    bool part = false;
    for ( int i = 0; i < rowCount(parent); i++ )
    {
        if (checkedItems [parent.child(i, 0).internalId()] != Qt::Unchecked) ch++;
        if (checkedItems [parent.child(i, 0).internalId()] == Qt::PartiallyChecked) part = true;
    }

    if (part || (ch>0 && ch<rowCount(parent)))
    {
        setData(parent, Qt::PartiallyChecked, Qt::CheckStateRole);
    }
    else if (ch==0)
    {
        if (checkedItems [parent.internalId()] != Qt::Unchecked)
            setData(parent, Qt::Unchecked, Qt::CheckStateRole);
    }
    else if (ch==rowCount(parent))
    {
        if (checkedItems [parent.internalId()] != Qt::Checked)
            setData(parent, Qt::Checked, Qt::CheckStateRole);
    }
}
→ Ссылка