Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

qml tree view crashed with c++ model,when add big data or resize window



  • main.qml

    import QtQuick 2.12
    import QtQuick.Controls 2.0
    import QtQuick.Controls 1.4
    import QtQuick.Window 2.0
    import QtQuick.Layouts 1.0
    import CC 1.0 as CC
    import "qrc:/qml/"
    import QtGraphicalEffects 1.0
    
    
    
    Window {
        id:root
        visible: true
        minimumWidth: 600; minimumHeight: 800
        width: 600; height: 800
    
        CC.OpenProjectManager{
            id:openProject;
        }
        Button {
            x:0;y:0
            width: 200;height: 100
            text: "open"
            id:btn
            onClicked:openProject.open()
        }
        ResultTree{
            id:treeView
            x:200;y:0
            width:parent.width-200
            height: parent.height
        }
    
    }
    
    code_text
    

    ResultTree.qml

    import QtQuick 2.7
    import QtQuick.Controls 2.4
    import QtQuick.Controls 1.4
    import QtQuick.Controls.Styles 1.4
    import QtQuick.Layouts 1.0
    import QtGraphicalEffects 1.0
    
    TreeView {
        model: treeModel
    
        TableViewColumn {
            role: "iconFile"
            title: "文件名"
            delegate: iconTextDelegate
            width: parent.width * 0.3
        }
        TableViewColumn {
            role: "severityStr"
            title: "错误级别"
            width: parent.width * 0.1
        }
        TableViewColumn {
            role: "line"
            title: "行号"
            width: parent.width * 0.1
        }
        TableViewColumn {
            role: "id"
            title: "错误号"
            width: parent.width * 0.2
        }
        TableViewColumn {
            role: "summary"
            title: "概要"
            width: parent.width * 0.3
        }
    
        Component {
            id:iconTextDelegate
            Item {
                Image {
                    id:image
                    source: styleData.value["icon"]
                    height:parent.height * 0.9; width: height
                    asynchronous:true
                }
                Text {
                    text: styleData.value["file"]
                    anchors.left: image.right
                    anchors.leftMargin: 10
                }
            }
        }
    }
    
    

    register treeModel

    #define TREEMODEL_REGISTER \
    {   \
        auto treeModel = CC::TreeModel::instance();                                   \
        engine.rootContext()->setContextProperty("treeModel", treeModel);   \
    }
    

    int works,but crashed when big data or resize window,
    TreeItem.h

    
    #ifndef TREEITEM_H
    #define TREEITEM_H
    
    #include <QList>
    #include <QVariant>
    
    namespace  CC{
    //! [0]
    class TreeItem
    {
    public:
        explicit TreeItem(const QMap<QString,QVariant> &data,TreeItem *parentItem = nullptr);
        ~TreeItem();
        TreeItem(const TreeItem& other);
        TreeItem& operator = (const TreeItem & other);
        void appendChild(TreeItem *child);
    
        TreeItem *child(int row);
        int childCount() const;
        int columnCount() const;
        QVariant data(QString key) const;
        int row() const;
        TreeItem *parentItem();
        QList<TreeItem*> children();
    
    private:
        QList<TreeItem*> m_childItems;
        QMap<QString,QVariant> m_itemData;
        TreeItem *m_parentItem;
    };
    //! [0]
    }
    
    
    #endif // TREEITEM_H
    
    

    TreeItem.cpp

    
    #include <QStringList>
    
    #include "treeitem.h"
    
    using namespace CC;
    //! [0]
    TreeItem::TreeItem(const QMap<QString,QVariant> &data, TreeItem *parent)
    {
        m_parentItem = parent;
        m_itemData = data;
    }
    //! [0]
    
    //! [1]
    TreeItem::~TreeItem()
    {
        qDeleteAll(m_childItems);
    }
    
    TreeItem::TreeItem(const TreeItem &other)
    {
        this->m_childItems = other.m_childItems;
        this->m_itemData = other.m_itemData;
        this->m_parentItem = other.m_parentItem;
    }
    
    TreeItem &TreeItem::operator =(const TreeItem &other)
    {
        this->m_childItems = other.m_childItems;
        this->m_itemData = other.m_itemData;
        this->m_parentItem = other.m_parentItem;
        return *this;
    }
    //! [1]
    
    //! [2]
    void TreeItem::appendChild(TreeItem *item)
    {
        m_childItems.append(item);
    }
    //! [2]
    
    //! [3]
    TreeItem *TreeItem::child(int row)
    {
        return m_childItems.value(row);
    }
    //! [3]
    
    //! [4]
    int TreeItem::childCount() const
    {
        return m_childItems.count();
    }
    //! [4]
    
    //! [5]
    int TreeItem::columnCount() const
    {
        return m_itemData.count();
    }
    //! [5]
    
    //! [6]
    QVariant TreeItem::data(QString key) const
    {
        return m_itemData.value(key,QVariant());
    }
    //! [6]
    
    //! [7]
    TreeItem *TreeItem::parentItem()
    {
        return m_parentItem;
    }
    
    QList<TreeItem *> TreeItem::children()
    {
        return m_childItems;
    }
    //! [7]
    
    //! [8]
    int TreeItem::row() const
    {
        if (m_parentItem)
            return m_parentItem->m_childItems.indexOf(const_cast<TreeItem*>(this));
    
        return 0;
    }
    //! [8]
    
    

    TreeModel.h

    
    #ifndef TREEMODEL_H
    #define TREEMODEL_H
    
    #include <QAbstractItemModel>
    #include <QModelIndex>
    #include <QVariant>
    
    #define TREEMODEL_REGISTER \
    {   \
        auto treeModel = CC::TreeModel::instance();                                   \
        engine.rootContext()->setContextProperty("treeModel", treeModel);   \
    }
    
    const QString CONST_file("file");
    const QString CONST_iconFile("iconFile");
    const QString CONST_severity("severity");
    const QString CONST_severityStr("severityStr");
    const QString CONST_line("line");
    const QString CONST_id("id");
    const QString CONST_summary("summary");
    const QString CONST_hide("hide");
    const QString CONST_message("message");
    const QString CONST_column("column");
    const QString CONST_inconclusive("inconclusive");
    const QString CONST_file0("file0");
    const QString CONST_sinceDate("sinceDate");
    const QString CONST_tags("tags");
    
    class  ErrorItem;
    namespace CC {
    class TreeItem;
    //! [0]
    class TreeModel : public QAbstractItemModel
    {
        Q_OBJECT
        Q_DISABLE_COPY(TreeModel)
    
    public:
        enum class TreeModelRoles : int {
            file = Qt::UserRole + 1,
            iconFile,
            severity,
            severityStr,
            line,
            id,
            summary,
            hide,
            message,
            column,
            inconclusive,
            file0,
            sinceDate,
            tags,
        };
        enum class TreeModelColumn : int {
            iconFile,
            severityStr,
            line,
            id,
            summary,
        };
        ~TreeModel() Q_DECL_OVERRIDE;
        /* QAbstractItemModel interface */
        QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
        Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
        QVariant headerData(int section, Qt::Orientation orientation,
                            int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
        QModelIndex index(int row, int column,
                          const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
        QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
        int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
        int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
        QHash<int, QByteArray> roleNames() const override;
        static TreeModel* instance();
    private:
        explicit TreeModel(QObject *parent = nullptr);
    private:
        TreeItem *rootItem;
        QHash<int, QByteArray> m_roleNameMapping;
    
    
    public:
        void BeginResetModel();
        void EndResetModel();
        bool addErrorItem(const ErrorItem &item);
    private:
        bool addErrorItemExec(const ErrorItem &item);
        TreeItem * checkExistingItem(QList<TreeItem*> children, const QMap<QString,QVariant> &data);
    private:
        QStringList mHiddenMessageId;
        QString mFilter;
        bool mHasVisibleErrors = false;
    };
    //! [0]
    }
    
    
    #endif // TREEMODEL_H
    
    

    TreeModel.cpp

    
    #include "treemodel.h"
    #include <QStringList>
    #include <QJsonObject>
    #include <QDir>
    #include <algorithm>
    #include "treeitem.h"
    #include "erroritem.h"
    #include "helper.h"
    
    using namespace CC;
    //! [0]
    TreeModel::TreeModel(QObject *parent)
        : QAbstractItemModel(parent)
    {
        m_roleNameMapping[static_cast<int>(TreeModelRoles::file)] = CONST_file.toUtf8();
        m_roleNameMapping[static_cast<int>(TreeModelRoles::iconFile)] = CONST_iconFile.toUtf8();
        m_roleNameMapping[static_cast<int>(TreeModelRoles::severity)] = CONST_severity.toUtf8();
        m_roleNameMapping[static_cast<int>(TreeModelRoles::severityStr)] = CONST_severityStr.toUtf8();
        m_roleNameMapping[static_cast<int>(TreeModelRoles::line)] = CONST_line.toUtf8();
        m_roleNameMapping[static_cast<int>(TreeModelRoles::id)] = CONST_id.toUtf8();
        m_roleNameMapping[static_cast<int>(TreeModelRoles::summary)] = CONST_summary.toUtf8();
        m_roleNameMapping[static_cast<int>(TreeModelRoles::hide)] = CONST_hide.toUtf8();
        m_roleNameMapping[static_cast<int>(TreeModelRoles::message)] = CONST_message.toUtf8();
        m_roleNameMapping[static_cast<int>(TreeModelRoles::column)] = CONST_column.toUtf8();
        m_roleNameMapping[static_cast<int>(TreeModelRoles::inconclusive)] = CONST_inconclusive.toUtf8();
        m_roleNameMapping[static_cast<int>(TreeModelRoles::file0)] = CONST_file0.toUtf8();
        m_roleNameMapping[static_cast<int>(TreeModelRoles::sinceDate)] = CONST_sinceDate.toUtf8();
        m_roleNameMapping[static_cast<int>(TreeModelRoles::tags)] = CONST_tags.toUtf8();
    
        QMap<QString,QVariant> rootData;
        rootData.insert(CONST_file,CONST_file);
        rootData[CONST_iconFile] = QJsonObject({{"file","file1.cpp"},{"icon","qrc:/images/language-cpp.png"}});    rootData.insert(CONST_severity,CONST_severity);
        rootData.insert(CONST_severityStr,CONST_severityStr);
        rootData.insert(CONST_line,CONST_line);
        rootData.insert(CONST_id,CONST_id);
        rootData.insert(CONST_summary,CONST_summary);
        rootData.insert(CONST_hide,CONST_hide);
        rootData.insert(CONST_message,CONST_message);
        rootData.insert(CONST_column,CONST_column);
        rootData.insert(CONST_inconclusive,CONST_inconclusive);
        rootData.insert(CONST_file0,CONST_file0);
        rootData.insert(CONST_sinceDate,CONST_sinceDate);
        rootData.insert(CONST_tags,CONST_tags);
        rootItem = new TreeItem(rootData);
    }
    
    void TreeModel::BeginResetModel()
    {
        this->beginResetModel();
    }
    
    void TreeModel::EndResetModel()
    {
        this->endResetModel();
    
    }
    //! [0]
    
    //! [1]
    TreeModel::~TreeModel()
    {
        delete rootItem;
    }
    //! [1]
    
    //! [2]
    int TreeModel::columnCount(const QModelIndex &parent) const
    {
        if (parent.isValid())
            return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
        else
            return rootItem->columnCount();
    }
    //! [2]
    
    //! [3]
    QVariant TreeModel::data(const QModelIndex &index, int role) const
    {
        if (!index.isValid())
            return QVariant();
    
        if (!m_roleNameMapping.keys().contains(role)) {
            return QVariant();
        }
        TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
        return item->data(m_roleNameMapping.value(role));
    }
    //! [3]
    
    //! [4]
    Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
    {
        if (!index.isValid())
            return Qt::NoItemFlags;
    
        return QAbstractItemModel::flags(index);
    }
    //! [4]
    
    //! [5]
    QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
                                   int role) const
    {
        if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
            switch (section) {
            case static_cast<int>(TreeModelColumn::iconFile):
                return rootItem->data(CONST_file);
            case static_cast<int>(TreeModelColumn::severityStr):
                return rootItem->data(CONST_severityStr);
            case static_cast<int>(TreeModelColumn::line):
                return rootItem->data(CONST_line);
            case static_cast<int>(TreeModelColumn::id):
                return rootItem->data(CONST_id);
            case static_cast<int>(TreeModelColumn::summary):
                return rootItem->data(CONST_summary);
            }
        }
        return QVariant();
    }
    //! [5]
    
    //! [6]
    QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent)
                const
    {
        if (!hasIndex(row, column, parent))
            return QModelIndex();
    
        TreeItem *parentItem;
    
        if (!parent.isValid())
            parentItem = rootItem;
        else
            parentItem = static_cast<TreeItem*>(parent.internalPointer());
    
        TreeItem *childItem = parentItem->child(row);
        if (childItem)
            return createIndex(row, column, childItem);
        else
            return QModelIndex();
    }
    //! [6]
    
    //! [7]
    QModelIndex TreeModel::parent(const QModelIndex &index) const
    {
        if (!index.isValid())
            return QModelIndex();
    
        TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
        TreeItem *parentItem = childItem->parentItem();
    
        if (parentItem == rootItem)
            return QModelIndex();
    
        return createIndex(parentItem->row(), 0, parentItem);
    }
    //! [7]
    
    //! [8]
    int TreeModel::rowCount(const QModelIndex &parent) const
    {
        TreeItem *parentItem;
        if (parent.column() > 0)
            return 0;
    
        if (!parent.isValid())
            parentItem = rootItem;
        else
            parentItem = static_cast<TreeItem*>(parent.internalPointer());
    
        return parentItem->childCount();
    }
    //! [8]
    
    QHash<int, QByteArray> TreeModel::roleNames() const
    {
        return m_roleNameMapping;
    }
    
    TreeModel *TreeModel::instance()
    {
        static TreeModel model;
        return &model;
    }
    
    bool TreeModel::addErrorItem(const ErrorItem &item)
    {
        bool ret = addErrorItemExec(item);
        return ret;
    }
    
    bool TreeModel::addErrorItemExec(const ErrorItem &item)
    {
        if (item.errorPath.isEmpty()) {
            return false;
        }
        const QErrorPathItem &loc = item.errorId.startsWith("clang") ? item.errorPath.front() : item.errorPath.back();
        QString relativeFile = Helper::GetStripPath(loc.file);
        if (relativeFile.isEmpty()) {
            relativeFile = tr("Undefined file");
        }
        bool hide = false;
        if (mHiddenMessageId.contains(item.errorId)) {
            hide = true;
        }
        if (!hide && !mFilter.isEmpty()) {
            if (!item.summary.contains(mFilter, Qt::CaseInsensitive) &&
                !item.message.contains(mFilter, Qt::CaseInsensitive) &&
                !item.errorPath.back().file.contains(mFilter, Qt::CaseInsensitive) &&
                !item.errorId.contains(mFilter, Qt::CaseInsensitive)) {
                hide = true;
            }
        }
        if (!hide) {
            mHasVisibleErrors = true;
        }
        TreeItem *fileItem = nullptr;
        for(const auto & child : rootItem->children()) {
                if(child->data(CONST_file) == relativeFile){
                    fileItem =  child;
                }
        }
    
        if(!fileItem) {
            QMap<QString,QVariant> fileItemData;
            fileItemData.insert(CONST_iconFile, QJsonObject({{"file",relativeFile},{"icon","qrc:/images/language-cpp.png"}}));
            fileItemData.insert(CONST_severityStr,"");
            fileItemData.insert(CONST_line,"");
            fileItemData.insert(CONST_id,"");
            fileItemData.insert(CONST_summary,"");
            fileItem = new TreeItem(fileItemData,rootItem);
            rootItem->appendChild(fileItem);
        }
        QMap<QString,QVariant> errorItemData;
        errorItemData.insert(CONST_file,             relativeFile);
        errorItemData.insert(CONST_severity,     item.severity);
        errorItemData.insert(CONST_line,             loc.line);
        errorItemData.insert(CONST_id,               item.errorId);
        errorItemData.insert(CONST_summary,  item.summary);
        errorItemData.insert(CONST_hide,         hide);
        errorItemData.insert(CONST_message,  item.message);
        errorItemData.insert(CONST_column,       "");
        errorItemData.insert(CONST_inconclusive,     item.inconclusive);
        errorItemData.insert(CONST_file0,            item.file0);
        errorItemData.insert(CONST_sinceDate,    item.sinceDate);
        errorItemData.insert(CONST_tags,             item.tags);
        QString icon;
        QString severityStr;
        Helper::Severity2Icon(item.severity,icon,severityStr);
        errorItemData.insert(CONST_iconFile,QJsonObject({{"file",relativeFile},{"icon",icon}}));
        errorItemData.insert(CONST_severityStr,severityStr);
        TreeItem *errorItem = checkExistingItem(fileItem->children(),errorItemData);
        if(!errorItem) {
            errorItem = new TreeItem(errorItemData, fileItem);
            fileItem->appendChild(errorItem);
        }
        if (item.errorPath.size() > 1) {
            for (int i = 0; i < item.errorPath.size(); i++) {
                const QErrorPathItem &e = item.errorPath[i];
                QMap<QString,QVariant> pathItemData;
                pathItemData[CONST_file] = e.file;
                pathItemData[CONST_line] = e.line;
                pathItemData[CONST_column] = e.column;
                pathItemData[CONST_message] = e.info;
                pathItemData[CONST_summary] = e.info;
                pathItemData[CONST_severityStr] = "note";
                pathItemData.insert(CONST_id,"");
                pathItemData.insert(CONST_iconFile,QJsonObject({{"file",e.file},{"icon","qrc:/old/go-down.png"}}));
                TreeItem *pathItem = checkExistingItem(errorItem->children(),pathItemData);
                if (!pathItem) {
                    pathItem = new TreeItem(pathItemData,errorItem);
                    errorItem->appendChild(pathItem);
                }
            }
        }
        return true;
    }
    
    TreeItem *TreeModel::checkExistingItem(QList<TreeItem *> children, const QMap<QString, QVariant> &data)
    {
        TreeItem *item = nullptr;
    //    if(children.count()) {
    //        std::for_each(children.begin(),children.end(),[&item,data](TreeItem *child) {
    //           if((child->data(CONST_line) == data[CONST_line])
    //                   && (child->data(CONST_severity) == data[CONST_severity])
    //                   && (child->data(CONST_summary) == data[CONST_summary])) {
    //               item = child;
    //           }
    //        });
    //    }
        for(const auto & child : children) {
               if((child->data(CONST_line) == data[CONST_line])
                       && (child->data(CONST_severity) == data[CONST_severity])
                       && (child->data(CONST_summary) == data[CONST_summary])) {
                   item = child;
               }
        }
    
        return item;
    }
    
    

    crash callstack
    QQ图片20191112164723.png


Log in to reply