QML ListView 委托选择在滚动时未更新

问题描述 投票:0回答:1

我正在开发一个名为“配置管理器”的窗口,它允许用户:

  • 查看
    ListView
    组件中具有特定扩展名的文件夹中的所有文件,
  • 根据用户输入的文本过滤显示的项目
    TextEdit
    组件,
  • 按最后修改日期对显示的项目进行排序,
  • 仅选择一项来加载相关文件,
  • 选择一个或多个删除相关文件,
  • 输入名称以将数据保存在新文件中,

列表视图显示过滤/排序的项目。当用户单击列表视图的一项时,我会更改该项的颜色。如果他单击另一个项目,前一个项目的颜色会恢复为默认颜色,并且新项目的颜色也会发生变化。

数据存储在后端的一个继承自

QAsbtractListModel
的类中,我正在使用另一个继承自
QSortFilterProxyModel
的类来仅显示过滤和排序的元素。

列表视图最多可以显示 100 个元素,因此我添加了一个滚动条以便能够选择其上显示的任何委托。

如果我选择第一个项目,颜色会正常更新。如果我单击第一个项目旁边的另一个项目,这两个项目(前一个和新的)的颜色变化也可以。如果选择顶部的一个项目,滚动列表视图的底部并选择另一个项目,我会遇到问题。这样,前一项就不会更新,并且仍然具有所选的颜色。在后端,通过一些 qDebug,我可以确认上一个项目没有更新,因为 setData 方法没有像我选择一个项目时那样被调用(以更新 GUI 上的颜色和模型上该元素后端的布尔值) ).

我这里不是说用键盘的CTRL或SHIFT键进行多重选择,但问题是一样的。仅更新部分项目。

我认为我缺少列表视图的一些属性,因为似乎并非所有委托都在缓存中(因此在滚动并选择新项目时不会更新)。

我添加了下面的代码,我现在不能做得更短。预先感谢您的帮助。

主要文件

  • 主.cpp
#include <QGuiApplication>
#include <QQuickView>
#include <QQmlContext>
#include <QQmlApplicationEngine>

#include "FilenameListModel.h"
#include "FilterProxyModel.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;

    FilenameListModel model;
    FilterProxyModel filterModel;
    filterModel.setSourceModel(&model);

    engine.rootContext()->setContextProperty("_FilteredRunwayConfigurationFilesModel", &filterModel);

    engine.load(QUrl("qrc:/main.qml"));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}
  • main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
import QtQml.Models 2.12
import QtQuick.Layouts 1.12

ApplicationWindow
{
    id: root

    property bool isSaving: false

    minimumWidth: 600
    minimumHeight: 400
    visible: true
    modality: Qt.ApplicationModal

    Component.onCompleted:
    {
        // Update the backend with current saving mode
        _FilteredRunwayConfigurationFilesModel.isSavingMode = isSaving
    }

    DelegateModel
    {
        id: delegateModel

        model: _FilteredRunwayConfigurationFilesModel

        delegate: Rectangle
        {
            id: item_delegate

            property bool checked: false

            width: ListView.view.width - 10
            height:      name.implicitHeight

            color: model.isChecked? "lightgreen" : "lightblue"
            radius: 10

            onCheckedChanged:
            {
                listViewFilter.checkChanged()
            }

            Connections
            {
                target: listViewFilter

                // Called after the slot onCheckOne of the listViewFilter component
                onCheckOne:
                {
                    model.isChecked = (idx === index)
                    checked = model.isChecked
                }

                onCheckMul:
                {
                    if (idx > listViewFilter.mulBegin)
                    {
                        model.isChecked = (index >= listViewFilter.mulBegin && index <= idx)
                        checked = model.isChecked
                    }
                    else
                    {
                        model.isChecked = (index <= listViewFilter.mulBegin && index >= idx)
                        checked = model.isChecked
                    }
                }
            }

            Connections
            {
                target: textFilterString

                // Called after this signal has been catched by the textFilterString component
                onTextChanged:
                {
                    // Reset all status
                    model.isChecked = false
                    checked = model.isChecked
                }
            }

            Rectangle
            {
                id: leftRectangle
                anchors
                {
                    top: parent.top
                    bottom: parent.bottom
                    left: parent.left
                    right: rightRectangle.left
                }
                color: "transparent"

                Text
                {
                    id: name
                    anchors.fill: parent
                    anchors.margins: 5
                    verticalAlignment: Qt.AlignVCenter
                    horizontalAlignment: Qt.AlignHCenter

                    text: "%1".arg(model.name)
                }
            }

            Rectangle
            {
                id: rightRectangle
                anchors
                {
                    top: parent.top
                    bottom: parent.bottom
                    right: parent.right
                }
                color: "transparent"
                width: parent.width * 0.3

                Text
                {
                    id: date
                    anchors.fill: parent
                    anchors.margins: 5
                    verticalAlignment: Qt.AlignVCenter
                    horizontalAlignment: Qt.AlignHCenter

                    text:
                    {
                        var text = "%1".arg(model.date)
                        return text
                    }
                }
            }

            MouseArea
            {
                anchors.fill: parent
                acceptedButtons: Qt.LeftButton

                onClicked:
                {
                    if (!isSaving)
                    {
                        switch(mouse.modifiers)
                        {
                        case Qt.ControlModifier:
                            model.isChecked = !model.isChecked;
                            checked = model.isChecked
                            break
                        case Qt.ShiftModifier:
                            listViewFilter.checkMul(index)
                            break
                        default:
                            listViewFilter.checkOne(index)
                            listViewFilter.mulBegin = index
                            break
                        }
                    }
                    else
                    {
                        textFilterString.text = model.name
                    }
                }
            }
        }
    }

    ColumnLayout
    {
        anchors.fill: parent
        anchors.margins: 5

        TextField
        {
            id: textFilterString
            Layout.fillWidth: true
            Layout.preferredHeight: implicitHeight
            horizontalAlignment: Qt.AlignHCenter
            verticalAlignment: Qt.AlignVCenter
            placeholderText: isSaving ? "Write the filename with the '.atols' extension" : "Write the begin of the filename"

            onTextChanged:
            {
                _FilteredRunwayConfigurationFilesModel.filenameBeginning = text;
                listViewFilter.checkChanged()
            }
        }

        RowLayout
        {
            Layout.fillWidth: true
            Layout.preferredHeight: textFilterString.height
            Layout.alignment: Qt.AlignHCenter

            Button
            {
                id: buttonLoad
                Layout.preferredWidth: 100
                Layout.preferredHeight: parent.height
                text: "LOAD"
                enabled: false
                visible: !isSaving

                onClicked:
                {
                    _FilteredRunwayConfigurationFilesModel.loadConfigurationFile(listViewFilter.selectedIndex)
                }
            }

            Button
            {
                id: buttonDelete
                Layout.preferredWidth: 100
                Layout.preferredHeight: parent.height
                text: "DELETE"
                enabled: false
                visible: !isSaving

                onClicked:
                {
                    _FilteredRunwayConfigurationFilesModel.deleteConfigurationFile(listViewFilter.selectedIndex)
                }
            }

            Button
            {
                id: buttonSave
                Layout.preferredWidth: 100
                Layout.preferredHeight: parent.height
                text: "SAVE"
                enabled: textFilterString.text
                visible: isSaving

                onClicked:
                {
                    // TODO
                }
            }
        }

        ListView
        {
            id: listViewFilter

            property int mulBegin: 0
            property var selectedIndex: []

            signal checkOne(int idx)
            signal checkMul(int idx)

            // Called first when executing from a delegate item
            onCheckOne:
            {
                listViewFilter.mulBegin = idx
            }

            function checkChanged()
            {
                listViewFilter.selectedIndex = []

                // Check how many items are selected in the list
                var nSelected = 0
                for (var i = 0; i < listViewFilter.count; i++)
                {
                    if (listViewFilter.contentItem.children[i] && listViewFilter.contentItem.children[i].checked)
                    {
                        nSelected++

                        listViewFilter.selectedIndex.push(i)
                    }
                }

                // Enable or disable buttons to load or delete the selected items
                var loadEnable = false
                var deleteEnable = false

                switch(nSelected)
                {
                case 1:     // One item selected => Load and Delete available
                    loadEnable = true
                    deleteEnable = true
                    break
                case 0:     // No items selected => Nothing available
                    loadEnable = false
                    deleteEnable = false
                    break
                default:    // Multiple items selected => Only Delete available
                    loadEnable = false
                    deleteEnable = true
                    break
                }

                buttonLoad.enabled = loadEnable
                buttonDelete.enabled = deleteEnable
            }

            Layout.fillWidth: true
            Layout.fillHeight: true

            model: delegateModel

            ScrollBar.vertical: ScrollBar
            {
                anchors.top: parent.top
                anchors.bottom: parent.bottom
                anchors.right: parent.right
                policy: listViewFilter.contentHeight > listViewFilter.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
                active: true
            }
        }

        TextField
        {
            Layout.fillWidth: true
            Layout.preferredHeight: textFilterString.height
            text: "No files with this filter"
            verticalAlignment: Qt.AlignTop
            horizontalAlignment: Qt.AlignHCenter
            visible: listViewFilter.count === 0
        }
    }
}

基础型号

  • 文件名ListModel.h
#ifndef FILENAMELISTMODEL_H
#define FILENAMELISTMODEL_H

#include <QAbstractListModel>
#include <QDate>

class FilenameListModel : public QAbstractListModel
{
    Q_OBJECT

public:
    explicit FilenameListModel(QObject *parent = nullptr);

public:
    enum Roles
    {
        NameRole = Qt::UserRole,
        DateRole,
        CheckedRole
    };

    int rowCount(const QModelIndex& parent) const override;
    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
    virtual bool setData(const QModelIndex &index, const QVariant &value, int role) override;

    QHash<int, QByteArray> roleNames() const override;

private:
    struct Entry
    {
        QString name;
        QDate date;
        bool isChecked;
    };

    QVector<Entry> m_entries;
};

#endif // FILENAMELISTMODEL_H
  • 文件名ListModel.cpp
#include "FilenameListModel.h"

#include <QDebug>

FilenameListModel::FilenameListModel(QObject *parent)
    : QAbstractListModel (parent)
{
    m_entries = {
        Entry{"Tata", QDate(2022, 5, 14)},
        Entry{"Tititata", QDate(2020, 3, 12)},
        Entry{"Toto", QDate(2018, 1, 10)},
        Entry{"Tata", QDate(2019, 2, 11)},
        Entry{"Toto", QDate(2021, 4, 13)},
        Entry{"Titi", QDate(2023, 6, 15)},
        Entry{"Tititata", QDate(2020, 3, 12)},
        Entry{"Toto", QDate(2018, 1, 10)},
        Entry{"Tata", QDate(2019, 2, 11)},
        Entry{"Toto", QDate(2021, 4, 13)},
        Entry{"Titi", QDate(2023, 6, 15)},
        Entry{"Tititata", QDate(2020, 3, 12)},
        Entry{"Toto", QDate(2018, 1, 10)},
        Entry{"Tata", QDate(2019, 2, 11)},
        Entry{"Toto", QDate(2021, 4, 13)},
        Entry{"Titi", QDate(2023, 6, 15)},
        Entry{"Tititata", QDate(2020, 3, 12)},
        Entry{"Toto", QDate(2018, 1, 10)},
        Entry{"Tata", QDate(2019, 2, 11)},
        Entry{"Toto", QDate(2021, 4, 13)},
        Entry{"Titi", QDate(2023, 6, 15)},
        Entry{"Tititata", QDate(2020, 3, 12)},
        Entry{"Toto", QDate(2018, 1, 10)},
        Entry{"Tata", QDate(2019, 2, 11)},
        Entry{"Toto", QDate(2021, 4, 13)},
        Entry{"Titi", QDate(2023, 6, 15)},
        Entry{"Tititata", QDate(2020, 3, 12)},
        Entry{"Toto", QDate(2018, 1, 10)},
        Entry{"Tata", QDate(2019, 2, 11)},
        Entry{"Toto", QDate(2021, 4, 13)},
        Entry{"Titi", QDate(2023, 6, 15)},
        Entry{"Tititata", QDate(2020, 3, 12)},
        Entry{"Toto", QDate(2018, 1, 10)},
        Entry{"Tata", QDate(2019, 2, 11)},
        Entry{"Toto", QDate(2021, 4, 13)},
        Entry{"Titi", QDate(2023, 6, 15)},
        Entry{"Tititata", QDate(2020, 3, 12)},
        Entry{"Toto", QDate(2018, 1, 10)},
        Entry{"Tata", QDate(2019, 2, 11)},
        Entry{"Toto", QDate(2021, 4, 13)},
        Entry{"Titi", QDate(2023, 6, 15)},
        Entry{"Tititata", QDate(2020, 3, 12)},
        Entry{"Toto", QDate(2018, 1, 10)},
        Entry{"Tata", QDate(2019, 2, 11)},
        Entry{"Toto", QDate(2021, 4, 13)},
        Entry{"Titi", QDate(2023, 6, 15)},
        Entry{"Tititata", QDate(2020, 3, 12)},
        Entry{"Toto", QDate(2018, 1, 10)},
        Entry{"Tata", QDate(2019, 2, 11)},
        Entry{"Toto", QDate(2021, 4, 13)},
        Entry{"Titi", QDate(2023, 6, 15)},
        Entry{"Tititata", QDate(2020, 3, 12)},
        Entry{"Toto", QDate(2018, 1, 10)},
        Entry{"Tata", QDate(2019, 2, 11)},
        Entry{"Toto", QDate(2021, 4, 13)},
        Entry{"Titi", QDate(2023, 6, 15)}
    };
}

int FilenameListModel::rowCount( const QModelIndex& parent) const
{
    if (parent.isValid())
        return 0;

    return m_entries.count();
}

QVariant FilenameListModel::data(const QModelIndex &index, int role) const
{
    if ( !index.isValid() )
        return QVariant();

    const Entry &entry = m_entries.at(index.row());

    switch(role)
    {
    case NameRole:
        return entry.name;
    case DateRole:
        return entry.date.toString("yyyy-MM-dd - hh::mm::ss");
    case CheckedRole:
    {
        return entry.isChecked;
    }
    default:
        return QVariant();
    }
}

bool FilenameListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    // Method called when QML change a parameter of the model
    if (!hasIndex(index.row(), index.column(), index.parent()) || !value.isValid())
        return false;

    if (m_entries[index.row()].isChecked != value.toBool())
        qDebug() << "backend:" << index.row() << "/" << value.toBool();

    m_entries[index.row()].isChecked = value.toBool();

    emit dataChanged(index, index, { role });
    return true;
}

QHash<int, QByteArray> FilenameListModel::roleNames() const
{
    static QHash<int, QByteArray> mapping {
        {NameRole, "name"},
        {DateRole, "date"},
        {CheckedRole, "isChecked"}
    };
    return mapping;
}

代理模型

  • FilterProxyModel.h
#ifndef FILTERPROXYMODEL_H
#define FILTERPROXYMODEL_H

#include <QSortFilterProxyModel>

class FilterProxyModel : public QSortFilterProxyModel
{
    Q_OBJECT

    Q_PROPERTY(QString filenameBeginning WRITE setFilenameBeginning READ getFilenameBeginnning NOTIFY filenameBeginningChanged)
    Q_PROPERTY(bool isSavingMode WRITE setIsSavingMode NOTIFY isSavingModeChanged)

public:
    explicit FilterProxyModel(QObject *parent = nullptr);

    void setFilenameBeginning(QString newStr);
    QString getFilenameBeginnning();
    void setIsSavingMode(bool newMode);

public slots:
    Q_INVOKABLE void deleteConfigurationFile(QVector<int> indexToDelete);
    Q_INVOKABLE void loadConfigurationFile(QVector<int> indexToLoad);

signals:
    void filenameBeginningChanged();
    void isSavingModeChanged();

protected:
    bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override;
    bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;

private:
    QString m_filenameBeginning;
    bool m_isSavingMode;
};

#endif // FILTERPROXYMODEL_H
  • FilterProxyModel.cpp
#include "FilterProxyModel.h"

#include "FilenameListModel.h"
#include <QDebug>

FilterProxyModel::FilterProxyModel(QObject *parent)
    : QSortFilterProxyModel (parent)
{
    m_isSavingMode = false;
    sort(0, Qt::SortOrder::DescendingOrder);
}

bool FilterProxyModel::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const
{
    const QDate leftDate = sourceLeft.data(FilenameListModel::DateRole).toDate();
    const QDate rightDate = sourceRight.data(FilenameListModel::DateRole).toDate();

    return leftDate < rightDate;
}

bool FilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
    // In saving mode, keep all files on the list
    if (m_isSavingMode)
        return true;

    // Otherwise, filter the rows
    const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);

    const QString name = index.data(FilenameListModel::NameRole).toString();

    return strncmp(name.toLower().toStdString().c_str(), m_filenameBeginning.toLower().toStdString().c_str(), m_filenameBeginning.size()) == 0;
}

void FilterProxyModel::setFilenameBeginning(QString newStr)
{
    m_filenameBeginning = newStr;

    emit filenameBeginningChanged();
    invalidateFilter();
}

QString FilterProxyModel::getFilenameBeginnning()
{
    return m_filenameBeginning;
}

void FilterProxyModel::deleteConfigurationFile(QVector<int> indexToDelete)
{
    for (int i = 0; i < indexToDelete.size(); i++)
    {
        qDebug() << this->data(this->index(indexToDelete[i], 0), FilenameListModel::Roles::NameRole);
    }
}

void FilterProxyModel::loadConfigurationFile(QVector<int> indexToLoad)
{
    // The input should be with only one element on it
    qDebug() << this->data(this->index(indexToLoad[0], 0), FilenameListModel::Roles::DateRole);
}

void FilterProxyModel::setIsSavingMode(bool newMode)
{
    m_isSavingMode = newMode;
}
c++ listview qml qsortfilterproxymodel
1个回答
0
投票

感谢@JarMan的建议,我简化了QML部分,列表视图的委托上不再有算法部分。现在后端会自行更新我想要的模型上每个项目的角色

CheckedRole
。而且滚动不再是问题。

我把代码的修改放在这里:

主要文件

  • main.cpp(无变化)

  • main.qml(简化委托并将逻辑放在列表视图上)

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
import QtQml.Models 2.12
import QtQuick.Layouts 1.12

ApplicationWindow
{
    id: root

    property bool isSaving: false

    minimumWidth: 600
    minimumHeight: 400
    visible: true
    modality: Qt.ApplicationModal

    Component.onCompleted:
    {
        // Update the backend with current saving mode
        _FilteredRunwayConfigurationFilesModel.isSavingMode = isSaving
    }

    DelegateModel
    {
        id: delegateModel

        model: _FilteredRunwayConfigurationFilesModel

        delegate: Rectangle
        {
            id: item_delegate

            width:  ListView.view.width - 10
            height: name.implicitHeight

            color: model.isChecked? "lightgreen" : "lightblue"
            radius: 10

            Rectangle
            {
                id: leftRectangle
                anchors
                {
                    top: parent.top
                    bottom: parent.bottom
                    left: parent.left
                    right: rightRectangle.left
                }
                color: "transparent"

                Text
                {
                    id: name
                    anchors.fill: parent
                    anchors.margins: 5
                    verticalAlignment: Qt.AlignVCenter
                    horizontalAlignment: Qt.AlignHCenter

                    text: "%1".arg(model.name)
                }
            }

            Rectangle
            {
                id: rightRectangle
                anchors
                {
                    top: parent.top
                    bottom: parent.bottom
                    right: parent.right
                }
                color: "transparent"
                width: parent.width * 0.3

                Text
                {
                    id: date
                    anchors.fill: parent
                    anchors.margins: 5
                    verticalAlignment: Qt.AlignVCenter
                    horizontalAlignment: Qt.AlignHCenter
                    text: model.date
                }
            }

            MouseArea
            {
                anchors.fill: parent
                acceptedButtons: Qt.LeftButton

                onClicked:
                {
                    if (!isSaving)
                    {
                        switch(mouse.modifiers)
                        {
                        case Qt.ControlModifier:
                            listViewFilter.selectCtrl(index)
                            break
                        case Qt.ShiftModifier:
                            listViewFilter.selectShift(index)
                            break
                        default:
                            listViewFilter.selectOne(index)
                            break
                        }
                    }
                    else
                    {
                        textFilterString.text = model.name
                    }
                }
            }
        }
    }

    ColumnLayout
    {
        anchors.fill: parent
        anchors.margins: 5

        TextField
        {
            id: textFilterString
            Layout.fillWidth: true
            Layout.preferredHeight: implicitHeight
            horizontalAlignment: Qt.AlignHCenter
            verticalAlignment: Qt.AlignVCenter
            placeholderText: isSaving ? "Write the filename with the '.atols' extension" : "Write the begin of the filename"

            onTextChanged:
            {
                _FilteredRunwayConfigurationFilesModel.filenameBeginning = text;
            }
        }

        RowLayout
        {
            Layout.fillWidth: true
            Layout.preferredHeight: textFilterString.height
            Layout.alignment: Qt.AlignHCenter

            Button
            {
                id: buttonLoad
                Layout.preferredWidth: 100
                Layout.preferredHeight: parent.height
                text: "LOAD"
                enabled: false
                visible: !isSaving

                onClicked:
                {
                    _FilteredRunwayConfigurationFilesModel.loadConfigurationFile(listViewFilter.selectedIndex)
                }
            }

            Button
            {
                id: buttonDelete
                Layout.preferredWidth: 100
                Layout.preferredHeight: parent.height
                text: "DELETE"
                enabled: false
                visible: !isSaving

                onClicked:
                {
                    _FilteredRunwayConfigurationFilesModel.deleteConfigurationFile(listViewFilter.selectedIndex)
                }
            }

            Button
            {
                id: buttonSave
                Layout.preferredWidth: 100
                Layout.preferredHeight: parent.height
                text: "SAVE"
                enabled: textFilterString.text
                visible: isSaving

                onClicked:
                {
                    // TODO
                }
            }
        }

        ListView
        {
            id: listViewFilter

            property int indexBegin: -1
            property int indexEnd: -1
            property var selectedIndex: []

            signal selectOne(int idx)
            signal selectCtrl(int idx)
            signal selectShift(int idx)

            // Called first when executing from a delegate item
            onSelectOne:
            {
                indexBegin = indexEnd = idx
                selectedIndex = [indexBegin]
                selectedIndexChanged()
            }

            onSelectShift:
            {
                // Add all index between begin and end on the list
                if (indexBegin === -1)
                {
                    indexBegin = indexEnd = idx
                }
                else
                {
                    indexEnd = idx
                }
                selectedIndex.length = 0
                for (var i = Math.min(indexBegin, indexEnd); i <= Math.max(indexBegin, indexEnd); i++)
                    selectedIndex.push(i)

                // Define the new begin with the last item selected
                indexBegin = idx
                selectedIndexChanged()

            }

            onSelectCtrl:
            {
                // Add the index to the list of not already on it
                indexBegin = idx
                if (selectedIndex.indexOf(idx) === -1)
                    selectedIndex.push(idx)
                selectedIndexChanged()
            }

            onSelectedIndexChanged:
            {
                _FilteredRunwayConfigurationFilesModel.setCheckedIndexes(selectedIndex)
            }

            function checkChanged()
            {
                listViewFilter.selectedIndex = []

                // Check how many items are selected in the list
                var nSelected = 0
                for (var i = 0; i < listViewFilter.count; i++)
                {
                    if (listViewFilter.contentItem.children[i] && listViewFilter.contentItem.children[i].checked)
                    {
                        nSelected++

                        listViewFilter.selectedIndex.push(i)
                    }
                }

                // Enable or disable buttons to load or delete the selected items
                var loadEnable = false
                var deleteEnable = false

                switch(nSelected)
                {
                case 1:     // One item selected => Load and Delete available
                    loadEnable = true
                    deleteEnable = true
                    break
                case 0:     // No items selected => Nothing available
                    loadEnable = false
                    deleteEnable = false
                    break
                default:    // Multiple items selected => Only Delete available
                    loadEnable = false
                    deleteEnable = true
                    break
                }

                buttonLoad.enabled = loadEnable
                buttonDelete.enabled = deleteEnable

                _FilteredRunwayConfigurationFilesModel.setCheckedIndexes(listViewFilter.selectedIndex)
            }

            Layout.fillWidth: true
            Layout.fillHeight: true

            model: delegateModel

            ScrollBar.vertical: ScrollBar
            {
                anchors.top: parent.top
                anchors.bottom: parent.bottom
                anchors.right: parent.right
                policy: listViewFilter.contentHeight > listViewFilter.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
                active: true
            }
        }

        TextField
        {
            Layout.fillWidth: true
            Layout.preferredHeight: textFilterString.height
            text: "No files with this filter"
            verticalAlignment: Qt.AlignTop
            horizontalAlignment: Qt.AlignHCenter
            visible: listViewFilter.count === 0
        }
    }
}

基础型号

  • FilenameListModel.h(无变化)

  • 文件名ListModel.cpp(无变化)

代理模型

  • FilterProxyModel.h:添加一个新的可调用公共槽
#ifndef FILTERPROXYMODEL_H
#define FILTERPROXYMODEL_H

#include <QSortFilterProxyModel>

class FilterProxyModel : public QSortFilterProxyModel
{
    Q_OBJECT

    Q_PROPERTY(QString filenameBeginning WRITE setFilenameBeginning READ getFilenameBeginnning NOTIFY filenameBeginningChanged)
    Q_PROPERTY(bool isSavingMode WRITE setIsSavingMode NOTIFY isSavingModeChanged)

public:
    explicit FilterProxyModel(QObject *parent = nullptr);

    void setFilenameBeginning(QString newStr);
    QString getFilenameBeginnning();
    void setIsSavingMode(bool newMode);

public slots:
    Q_INVOKABLE void deleteConfigurationFile(QVector<int> indexToDelete);
    Q_INVOKABLE void loadConfigurationFile(QVector<int> indexToLoad);
    Q_INVOKABLE void setCheckedIndexes(QVector<int> listIndex); // ------> New slot <------

signals:
    void filenameBeginningChanged();
    void isSavingModeChanged();

protected:
    bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override;
    bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;

private:
    QString m_filenameBeginning;
    bool m_isSavingMode;
};

#endif // FILTERPROXYMODEL_H
  • FilterProxyModel.cpp(通过QML部分实现新的slot调用)
#include "FilterProxyModel.h"

#include "FilenameListModel.h"
#include <QDebug>

FilterProxyModel::FilterProxyModel(QObject *parent)
    : QSortFilterProxyModel (parent)
{
    m_isSavingMode = false;
    sort(0, Qt::SortOrder::DescendingOrder);
}

bool FilterProxyModel::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const
{
    const QDate leftDate = sourceLeft.data(FilenameListModel::DateRole).toDate();
    const QDate rightDate = sourceRight.data(FilenameListModel::DateRole).toDate();

    return leftDate < rightDate;
}

bool FilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
    // In saving mode, keep all files on the list
    if (m_isSavingMode)
        return true;

    // Otherwise, filter the rows
    const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);

    const QString name = index.data(FilenameListModel::NameRole).toString();

    return strncmp(name.toLower().toStdString().c_str(), m_filenameBeginning.toLower().toStdString().c_str(), m_filenameBeginning.size()) == 0;
}

void FilterProxyModel::setFilenameBeginning(QString newStr)
{
    m_filenameBeginning = newStr;

    emit filenameBeginningChanged();
    invalidateFilter();
}

QString FilterProxyModel::getFilenameBeginnning()
{
    return m_filenameBeginning;
}

void FilterProxyModel::deleteConfigurationFile(QVector<int> indexToDelete)
{
    for (int i = 0; i < indexToDelete.size(); i++)
    {
        qDebug() << this->data(this->index(indexToDelete[i], 0), FilenameListModel::Roles::NameRole);
    }
}

void FilterProxyModel::loadConfigurationFile(QVector<int> indexToLoad)
{
    // The input should be with only one element on it
    qDebug() << this->data(this->index(indexToLoad[0], 0), FilenameListModel::Roles::DateRole);
}

void FilterProxyModel::setIsSavingMode(bool newMode)
{
    m_isSavingMode = newMode;
}
// ------> NEW CODE BELOW <------
void FilterProxyModel::setCheckedIndexes(QVector<int> listIndex)
{
    // First reset all checked value already to true
    for (int i = 0; i < sourceModel()->rowCount(); i++)
    {
        if (data(this->index(i, 0), FilenameListModel::Roles::CheckedRole).toBool())
            setData(this->index(i, 0), false, FilenameListModel::Roles::CheckedRole);
    }

    // Then set to true checked value only for selected delegate
    for (int i = 0; i < listIndex.size(); i++)
    {
        setData(this->index(listIndex[i], 0), true, FilenameListModel::Roles::CheckedRole);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.