Qt 树形插件navlistview使用中的坑

文章目录[隐藏]

前言

最近在写自己的软件界面时,想让界面更好看点,于是找到大佬(feiyangqingyun)开源的代码。其中在使用树形插件(navlistview)的时候遇到一坑。

坑描述: 我是采用源代码引入,然后提升控件的方式,但是运行的时候一直报错,报的是数组越界的错误,我又找不到哪里越界了。

Qt 树形插件navlistview使用中的坑

树形插件使用

1.  引入源代码
//  navlistview.h 文件

#ifndef NAVLISTVIEW_H
#define NAVLISTVIEW_H

/**
 * 树状导航栏控件 作者:feiyangqingyun(QQ:517216493) 2016-10-28
 * 本控件来源于网络(原作者:sssooonnnggg(http://www.qtcn.org/bbs/read-htm-tid-58274-ds-1-page-1.html#163557))
 * 1:合并成一个文件
 * 2:隐藏多余接口,只保留 readData 函数接口
 * 3:修正重复加载数据 BUG 及关闭时有时候崩溃的 BUG
 * 4:修正当范围值中最小值为负数时的 bug
 * 5:增加默认支持鼠标悬停
 * 6:增加一些完整性校验
 * 7:增加设置各种颜色
 * 8:+-伸缩图形改成绘制,同时支持+-图形及三角形图形
 * 9:增加 setData 数据接口,使之同时支持字符串链表作为数据源
 * 10:增加设置分割线条是否可见
 * 11:增加设置伸缩图片采用何种颜色
 * 12:改进分割线条高度
 * 13:将显示未读条数部分改成提示信息,可以是字符串
 */

#include <QStyledItemDelegate>
#include <QAbstractListModel>
#include <QListView>
#include <vector>

class NavListView;

class NavDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    NavDelegate(QObject *parent);
    ~NavDelegate();

protected:
    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const ;
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;

private:
    NavListView *nav;
    QFont iconFont;
};


class NavModel : public QAbstractListModel
{
    Q_OBJECT
public:
    NavModel(QObject *parent);
    ~NavModel();

public:
    struct TreeNode {
        QString label;
        int level;
        bool collapse;
        bool theLast;
        QString info;
        std::list<TreeNode *> children;
    };

    struct ListNode {
        QString label;
        TreeNode *treeNode;
    };

protected:
    int rowCount(const QModelIndex &parent) const;
    QVariant data(const QModelIndex &index, int role) const;

private:
    std::vector<TreeNode *> treeNode;
    std::vector<ListNode> listNode;

public slots:    
    void setData(const QStringList &listItem);
    void collapse(const QModelIndex &index);

private:
    void refreshList();
};

#ifdef quc
#if (QT_VERSION < QT_VERSION_CHECK(5,7,0))
#include <QtDesigner/QDesignerExportWidget>
#else
#include <QtUiPlugin/QDesignerExportWidget>
#endif

class QDESIGNER_WIDGET_EXPORT NavListView : public QListView
#else
class NavListView : public QListView
#endif

{
    Q_OBJECT
    Q_ENUMS(IcoStyle)

    Q_PROPERTY(bool infoVisible READ getInfoVisible WRITE setInfoVisible)
    Q_PROPERTY(bool lineVisible READ getLineVisible WRITE setLineVisible)
    Q_PROPERTY(bool icoColorBg READ getIcoColorBg WRITE setIcoColorBg)
    Q_PROPERTY(IcoStyle icoStyle READ getIcoStyle WRITE setIcoStyle)

    Q_PROPERTY(QColor colorLine READ getColorLine WRITE setColorLine)
    Q_PROPERTY(QColor colorBgNormal READ getColorBgNormal WRITE setColorBgNormal)
    Q_PROPERTY(QColor colorBgSelected READ getColorBgSelected WRITE setColorBgSelected)
    Q_PROPERTY(QColor colorBgHover READ getColorBgHover WRITE setColorBgHover)

    Q_PROPERTY(QColor colorTextNormal READ getColorTextNormal WRITE setColorTextNormal)
    Q_PROPERTY(QColor colorTextSelected READ getColorTextSelected WRITE setColorTextSelected)
    Q_PROPERTY(QColor colorTextHover READ getColorTextHover WRITE setColorTextHover)

public:
    enum IcoStyle {
        IcoStyle_Cross = 0,     //十字形状
        IcoStyle_Triangle = 1   //三角形状
    };

    NavListView(QWidget *parent = 0);
    ~NavListView();

private:
    NavModel *model;
    NavDelegate *delegate;

    bool infoVisible;               //是否显示提示信息
    bool lineVisible;               //是否显示分割线条
    bool icoColorBg;                //伸缩图片是否使用颜色
    IcoStyle icoStyle;              //图标样式

    QColor colorLine;               //线条颜色
    QColor colorBgNormal;           //正常背景色
    QColor colorBgSelected;         //选中背景色
    QColor colorBgHover;            //悬停背景色

    QColor colorTextNormal;         //正常文字颜色
    QColor colorTextSelected;       //选中文字颜色
    QColor colorTextHover;          //悬停文字颜色

public:
    bool getInfoVisible()           const;
    bool getLineVisible()           const;
    bool getIcoColorBg()            const;
    IcoStyle getIcoStyle()          const;

    QColor getColorLine()           const;
    QColor getColorBgNormal()       const;
    QColor getColorBgSelected()     const;
    QColor getColorBgHover()        const;

    QColor getColorTextNormal()     const;
    QColor getColorTextSelected()   const;
    QColor getColorTextHover()      const;

    QSize sizeHint()                const;
    QSize minimumSizeHint()         const;

public Q_SLOTS:    
    //设置数据集合
    void setData(const QStringList &listItem);

    //设置是否显示提示信息
    void setInfoVisible(bool infoVisible);

    //设置是否显示间隔线条
    void setLineVisible(bool lineVisible);

    //设置伸缩图片是否采用背景色
    void setIcoColorBg(bool icoColorBg);

    //设置伸缩图片样式
    void setIcoStyle(IcoStyle icoStyle);

    //设置各种前景色背景色选中色
    void setColorLine(const QColor &colorLine);

    void setColorBgNormal(const QColor &colorBgNormal);
    void setColorBgSelected(const QColor &colorBgSelected);
    void setColorBgHover(const QColor &colorBgHover);

    void setColorTextNormal(const QColor &colorTextNormal);
    void setColorTextSelected(const QColor &colorTextSelected);
    void setColorTextHover(const QColor &colorTextHover);
};

#endif // NAVLISTVIEW_H

// navlistview.cpp

#include "navlistview.h"
#include "qfontdatabase.h"
#include "qpainter.h"
#include "qfile.h"
#include "qdebug.h"

NavDelegate::NavDelegate(QObject *parent) : QStyledItemDelegate(parent)
{
    nav = (NavListView *)parent;

    //引入图形字体
    int fontId = QFontDatabase::addApplicationFont(":/image/fontawesome-webfont.ttf");
    QString fontName = QFontDatabase::applicationFontFamilies(fontId).at(0);
    iconFont = QFont(fontName);
}

NavDelegate::~NavDelegate()
{

}

QSize NavDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QSize size(50, 28);
    NavModel::TreeNode *node = (NavModel::TreeNode *)index.data(Qt::UserRole).toUInt();

    if (node->level == 1) {
        size = QSize(50, 35);
    } else {
        size = QSize(50, 28);
    }

    return size;
}

void NavDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    painter->setRenderHint(QPainter::Antialiasing);
    NavModel::TreeNode *node = (NavModel::TreeNode *)index.data(Qt::UserRole).toUInt();

    //绘制背景
    QColor colorBg;

    if (option.state & QStyle::State_Selected) {
        colorBg = nav->getColorBgSelected();
    } else if (option.state & QStyle::State_MouseOver) {
        colorBg = nav->getColorBgHover();
    } else {
        colorBg = nav->getColorBgNormal();
    }

    painter->fillRect(option.rect, colorBg);

    //绘制指示器
    if (node->children.size() != 0) {
        QPixmap pix(16, 16);
        pix.fill(Qt::transparent);
        QPainter p(&pix);
        p.setRenderHint(QPainter::Antialiasing);
        p.setFont(iconFont);

        //根据采用的背景色判断
        QColor icoColorSelected;
        QColor icoColorNormal;
        QColor icoColorHover;

        if (nav->getIcoColorBg()) {
            icoColorSelected = nav->getColorBgNormal();
            icoColorNormal = nav->getColorBgSelected();
            icoColorHover = nav->getColorBgNormal();
        } else {
            icoColorSelected = nav->getColorTextSelected();
            icoColorNormal = nav->getColorTextNormal();
            icoColorHover = nav->getColorTextHover();
        }

        if (option.state & QStyle::State_Selected) {
            p.setPen(icoColorSelected);
        } else if (option.state & QStyle::State_MouseOver) {
            p.setPen(icoColorHover);
        } else {
            p.setPen(icoColorNormal);
        }

        QFont font = p.font();

        if (nav->getIcoStyle() == NavListView::IcoStyle_Cross) {
            font.setPixelSize(13);
            p.setFont(font);

            //绘制 + - 指示器
            if (node->collapse) {
                p.drawText(pix.rect(), Qt::AlignCenter, QChar(0xf067));
            } else {
                p.drawText(pix.rect(), Qt::AlignCenter, QChar(0xf068));
            }
        } else if (nav->getIcoStyle() == NavListView::IcoStyle_Triangle) {
            font.setPixelSize(18);
            p.setFont(font);

            //绘制三角形指示器
            if (node->collapse) {
                p.drawText(pix.rect(), Qt::AlignCenter, QChar(0xf0da));
            } else {
                p.drawText(pix.rect(), Qt::AlignCenter, QChar(0xf0d7));
            }
        }

        QPixmap img(pix);
        QRect targetRect = option.rect;
        targetRect.setWidth(16);
        targetRect.setHeight(16);
        QPoint c = option.rect.center();
        c.setX(12);
        targetRect.moveCenter(c);
        painter->drawPixmap(targetRect, img, img.rect());
    }

    //绘制条目文字
    QColor colorText;

    if (option.state & QStyle::State_Selected) {
        colorText = nav->getColorTextSelected();
    } else if (option.state & QStyle::State_MouseOver) {
        colorText = nav->getColorTextHover();
    } else {
        colorText = nav->getColorTextNormal();
    }

    painter->setPen(QPen(colorText));

    //绘制文字离左边的距离
    int margin = 25;

    if (node->level == 2) {
        margin = 45;
    }

    QRect rect = option.rect;
    rect.setWidth(rect.width() - margin);
    rect.setX(rect.x() + margin);

    QFont normalFont("Microsoft Yahei", 9);
    painter->setFont(normalFont);
    painter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, index.data(Qt::DisplayRole).toString());

    //绘制分隔符线条
    if (nav->getLineVisible()) {
        painter->setPen(QPen(nav->getColorLine(), 1));

        if (node->level == 1 || (node->level == 2 && node->theLast)) {
            painter->drawLine(QPointF(option.rect.x(), option.rect.y() + option.rect.height()),
                              QPointF(option.rect.x() + option.rect.width(), option.rect.y() + option.rect.height()));
        }
    }

    //绘制提示信息
    QString recordInfo = node->info;

    //如果不需要显示提示信息或者提示信息为空则返回
    if (!nav->getInfoVisible() || recordInfo.isEmpty()) {
        return;
    }

    QPen decorationPen(option.state & QStyle::State_Selected ? nav->getColorBgSelected() : nav->getColorTextSelected());
    QBrush decorationBrush(option.state & QStyle::State_Selected ? nav->getColorTextSelected() : nav->getColorBgSelected());
    QFont decorationFont("Microsoft Yahei", 8);
    painter->setFont(decorationFont);

    //绘制提示信息背景
    QRect decoration = option.rect;
    decoration.setHeight(15);
    decoration.moveCenter(option.rect.center());
    decoration.setLeft(option.rect.right() - 45);
    decoration.setRight(option.rect.right() - 5);

    painter->setPen(decorationPen);
    QPainterPath path;
    path.addRoundedRect(decoration, 7, 7);
    painter->fillPath(path, decorationBrush);

    //如果是数字则将超过 999 的数字显示成 999+
    if (recordInfo.toInt() > 999) {
        recordInfo = "999+";
    }

    //如果显示的提示信息长度超过 4 则将多余显示成省略号..
    if (recordInfo.length() > 4) {
        recordInfo = recordInfo.mid(0, 4) + "..";
    }

    painter->drawText(decoration, Qt::AlignCenter, recordInfo);
}


NavModel::NavModel(QObject *parent)	: QAbstractListModel(parent)
{

}

NavModel::~NavModel()
{
    for (std::vector<TreeNode *>::iterator it = treeNode.begin(); it != treeNode.end();) {
        for (std::list<TreeNode *>::iterator child = (*it)->children.begin(); child != (*it)->children.end();) {
            delete(*child);
            child = (*it)->children.erase(child);
        }

        delete(*it);
        it = treeNode.erase(it);
    }
}

void NavModel::setData(const QStringList &listItem)
{
    int count = listItem.count();

    if (count == 0) {
        return;
    }

    treeNode.clear();
    listNode.clear();

    //listItem 格式: 标题|父节点标题(父节点为空)|是否展开|提示信息
    for (int i = 0; i < count; i++) {
        QString item = listItem.at(i);
        QStringList list = item.split("|");

        if (list.count() < 4) {
            continue;
        }

        //首先先将父节点即父节点标题为空的元素加载完毕
        QString title = list.at(0);
        QString fatherTitle = list.at(1);
        QString collapse = list.at(2);
        QString info = list.at(3);

        if (fatherTitle.isEmpty()) {
            TreeNode *node = new TreeNode;
            node->label = title;
            node->collapse = collapse.toInt();
            node->info = info;
            node->level = 1;

            //查找该父节点是否有对应子节点,有则加载
            for (int j = 0; j < count; j++) {
                QString secItem = listItem.at(j);
                QStringList secList = secItem.split("|");

                if (secList.count() < 4) {
                    continue;
                }

                QString secTitle = secList.at(0);
                QString secFatherTitle = secList.at(1);
                QString secInfo = secList.at(3);

                if (secFatherTitle == title) {
                    TreeNode *secNode = new TreeNode;
                    secNode->label = secTitle;
                    secNode->info = secInfo;
                    secNode->collapse = false;
                    secNode->level = 2;
                    secNode->theLast = (j == count - 1);
                    node->children.push_back(secNode);
                }
            }

            treeNode.push_back(node);
        }
    }

    refreshList();
    beginResetModel();
    endResetModel();
}

int NavModel::rowCount(const QModelIndex &parent) const
{
    return listNode.size();
}

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

    if (index.row() >= listNode.size() || index.row() < 0) {
        return QVariant();
    }

    if (role == Qt::DisplayRole) {
        return listNode[index.row()].label;
    } else if (role == Qt::UserRole) {
        return (quint64)(listNode[index.row()].treeNode);
    }

    return QVariant();
}

void NavModel::refreshList()
{
    listNode.clear();

    for (std::vector<TreeNode *>::iterator it = treeNode.begin(); it != treeNode.end(); ++it) {
        ListNode node;
        node.label = (*it)->label;
        node.treeNode = *it;

        listNode.push_back(node);

        if ((*it)->collapse) {
            continue;
        }

        for (std::list<TreeNode *>::iterator child = (*it)->children.begin(); child != (*it)->children.end(); ++child) {
            ListNode node;
            node.label = (*child)->label;
            node.treeNode = *child;
            node.treeNode->theLast = false;
            listNode.push_back(node);
        }

        if (!listNode.empty()) {
            listNode.back().treeNode->theLast = true;
        }
    }
}

void NavModel::collapse(const QModelIndex &index)
{
    TreeNode *node = listNode[index.row()].treeNode;

    if (node->children.size() == 0) {
        return;
    }

    node->collapse = !node->collapse;
    refreshList();

    if (!node->collapse) {
        beginInsertRows(QModelIndex(), index.row() + 1, index.row() + node->children.size());
        endInsertRows();
    } else {
        beginRemoveRows(QModelIndex(), index.row() + 1, index.row() + node->children.size());
        endRemoveRows();
    }
}

NavListView::NavListView(QWidget *parent) : QListView(parent)
{
    infoVisible = true;
    lineVisible = true;
    icoColorBg = false;

    icoStyle = NavListView::IcoStyle_Cross;

    colorLine = QColor(214, 216, 224);

    colorBgNormal = QColor(239, 241, 250);
    colorBgSelected = QColor(133, 153, 216);
    colorBgHover = QColor(209, 216, 240);

    colorTextNormal = QColor(58, 58, 58);
    colorTextSelected = QColor(255, 255, 255);
    colorTextHover = QColor(59, 59, 59);

    this->setMouseTracking(true);
    model = new NavModel(this);
    delegate = new NavDelegate(this);
    connect(this, SIGNAL(doubleClicked(QModelIndex)), model, SLOT(collapse(QModelIndex)));
}

NavListView::~NavListView()
{
    delete model;
    delete delegate;
}

bool NavListView::getInfoVisible() const
{
    return this->infoVisible;
}

bool NavListView::getLineVisible() const
{
    return this->lineVisible;
}

bool NavListView::getIcoColorBg() const
{
    return this->icoColorBg;
}

NavListView::IcoStyle NavListView::getIcoStyle() const
{
    return this->icoStyle;
}

QColor NavListView::getColorLine() const
{
    return this->colorLine;
}

QColor NavListView::getColorBgNormal() const
{
    return this->colorBgNormal;
}

QColor NavListView::getColorBgSelected() const
{
    return this->colorBgSelected;
}

QColor NavListView::getColorBgHover() const
{
    return this->colorBgHover;
}

QColor NavListView::getColorTextNormal() const
{
    return this->colorTextNormal;
}

QColor NavListView::getColorTextSelected() const
{
    return this->colorTextSelected;
}

QColor NavListView::getColorTextHover() const
{
    return this->colorTextHover;
}

QSize NavListView::sizeHint() const
{
    return QSize(200, 300);
}

QSize NavListView::minimumSizeHint() const
{
    return QSize(20, 20);
}

void NavListView::setData(const QStringList &listItem)
{
    model->setData(listItem);
    this->setModel(model);
    this->setItemDelegate(delegate);
}

void NavListView::setInfoVisible(bool infoVisible)
{
    if (this->infoVisible != infoVisible) {
        this->infoVisible = infoVisible;
    }
}

void NavListView::setLineVisible(bool lineVisible)
{
    if (this->lineVisible != lineVisible) {
        this->lineVisible = lineVisible;
    }
}

void NavListView::setIcoColorBg(bool icoColorBg)
{
    if (this->icoColorBg != icoColorBg) {
        this->icoColorBg = icoColorBg;
    }
}

void NavListView::setIcoStyle(NavListView::IcoStyle icoStyle)
{
    if (this->icoStyle != icoStyle) {
        this->icoStyle = icoStyle;
    }
}

void NavListView::setColorLine(const QColor &colorLine)
{
    if (this->colorLine != colorLine) {
        this->colorLine = colorLine;
    }
}

void NavListView::setColorBgNormal(const QColor &colorBgNormal)
{
    if (this->colorBgNormal != colorBgNormal) {
        this->colorBgNormal = colorBgNormal;
    }
}

void NavListView::setColorBgSelected(const QColor &colorBgSelected)
{
    if (this->colorBgSelected != colorBgSelected) {
        this->colorBgSelected = colorBgSelected;
    }
}

void NavListView::setColorBgHover(const QColor &colorBgHover)
{
    if (this->colorBgHover != colorBgHover) {
        this->colorBgHover = colorBgHover;
    }
}

void NavListView::setColorTextNormal(const QColor &colorTextNormal)
{
    if (this->colorTextNormal != colorTextNormal) {
        this->colorTextNormal = colorTextNormal;
    }
}

void NavListView::setColorTextSelected(const QColor &colorTextSelected)
{
    if (this->colorTextSelected != colorTextSelected) {
        this->colorTextSelected = colorTextSelected;
    }
}

void NavListView::setColorTextHover(const QColor &colorTextHover)
{
    if (this->colorTextHover != colorTextHover) {
        this->colorTextHover = colorTextHover;
    }
}

2. 拖入一个 ListView 控件,右键提升成 NavListView

Qt 树形插件navlistview使用中的坑

错误解决方案

错误原因是 cpp 文件引入字体了,但是我没有导入字体文件。解决办法: 要么注释掉这句话,要么引入字体文件。

Qt 树形插件navlistview使用中的坑
posted @ 2020-05-09 20:07  不随。  阅读(24)  评论(0编辑  收藏  举报  来源