一、前言
樹狀導航欄控件是所有控件中最牛逼最經典最厲害的一個,在很多購買者中,使用頻率也是最高,因為該導航控件集合了非常多的展示效果,比如左側圖標+右側箭頭+元素前面的圖標設置+各種顏色設置等,全部涵蓋了,代碼量也比較多,該控件前后完善了三年,還提供了角標展示文字信息,縱觀市面上web也好,cs架構的程序也好,這種導航條使用非常多,目前只提供了二級菜單,如果需要三級菜單需要自行更改源碼才行。
二、實現的功能
- 1:設置節點數據相當方便,按照對應格式填入即可,分隔符,
- 2:可設置提示信息 是否顯示+寬度
- 3:可設置行分隔符 是否顯示+高度+顏色
- 4:可設置選中節點線條突出顯示+顏色+左側右側位置
- 5:可設置選中節點三角形突出顯示+顏色+左側右側位置
- 6:可設置父節點的 選中顏色+懸停顏色+默認顏色
- 7:可設置子節點的 選中顏色+懸停顏色+默認顏色
- 8:可設置父節點文字的 圖標邊距+左側距離+字體大小+高度
- 9:可設置子節點文字的 圖標邊距+左側距離+字體大小+高度
- 10:可設置節點展開模式 單擊+雙擊+禁用
三、效果圖
四、頭文件代碼
#ifndef NAVLISTVIEW_H
#define NAVLISTVIEW_H
/**
* 樹狀導航欄控件 作者:feiyangqingyun(QQ:517216493) 2018-8-15
* 1:設置節點數據相當方便,按照對應格式填入即可,分隔符,
* 2:可設置提示信息 是否顯示+寬度
* 3:可設置行分隔符 是否顯示+高度+顏色
* 4:可設置選中節點線條突出顯示+顏色+左側右側位置
* 5:可設置選中節點三角形突出顯示+顏色+左側右側位置
* 6:可設置父節點的 選中顏色+懸停顏色+默認顏色
* 7:可設置子節點的 選中顏色+懸停顏色+默認顏色
* 8:可設置父節點文字的 圖標邊距+左側距離+字體大小+高度
* 9:可設置子節點文字的 圖標邊距+左側距離+字體大小+高度
* 10:可設置節點展開模式 單擊+雙擊+禁用
*/
#include <QStyledItemDelegate>
#include <QAbstractListModel>
#include <QListView>
#include <QStringList>
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 {
int level; //層級,父節點-1,子節點-2
bool expand; //是否打開子節點
bool last; //是否末尾元素
QChar icon; //左側圖標
QString text; //顯示的節點文字
QString tip; //右側描述文字
QString parentText; //父節點名稱
QList<TreeNode *> children; //子節點集合
};
struct ListNode {
QString text; //節點文字
TreeNode *treeNode; //節點指針
};
protected:
int rowCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
private:
QList<TreeNode *> treeNode;
QList<ListNode> listNode;
public Q_SLOTS:
void setItems(const QStringList &items);
void expand(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(ExpendMode)
Q_PROPERTY(QString items READ getItems WRITE setItems)
Q_PROPERTY(bool rightIconVisible READ getRightIconVisible WRITE setRightIconVisible)
Q_PROPERTY(bool tipVisible READ getTipVisible WRITE setTipVisible)
Q_PROPERTY(int tipWidth READ getTipWidth WRITE setTipWidth)
Q_PROPERTY(bool separateVisible READ getSeparateVisible WRITE setSeparateVisible)
Q_PROPERTY(int separateHeight READ getSeparateHeight WRITE setSeparateHeight)
Q_PROPERTY(QColor separateColor READ getSeparateColor WRITE setSeparateColor)
Q_PROPERTY(bool lineLeft READ getLineLeft WRITE setLineLeft)
Q_PROPERTY(bool lineVisible READ getLineVisible WRITE setLineVisible)
Q_PROPERTY(int lineWidth READ getLineWidth WRITE setLineWidth)
Q_PROPERTY(QColor lineColor READ getLineColor WRITE setLineColor)
Q_PROPERTY(bool triangleLeft READ getTriangleLeft WRITE setTriangleLeft)
Q_PROPERTY(bool triangleVisible READ getTriangleVisible WRITE setTriangleVisible)
Q_PROPERTY(int triangleWidth READ getTriangleWidth WRITE setTriangleWidth)
Q_PROPERTY(QColor triangleColor READ getTriangleColor WRITE setTriangleColor)
Q_PROPERTY(int parentIconMargin READ getParentIconMargin WRITE setParentIconMargin)
Q_PROPERTY(int parentMargin READ getParentMargin WRITE setParentMargin)
Q_PROPERTY(int parentFontSize READ getParentFontSize WRITE setParentFontSize)
Q_PROPERTY(int parentHeight READ getParentHeight WRITE setParentHeight)
Q_PROPERTY(QColor parentBgNormalColor READ getParentBgNormalColor WRITE setParentBgNormalColor)
Q_PROPERTY(QColor parentBgSelectedColor READ getParentBgSelectedColor WRITE setParentBgSelectedColor)
Q_PROPERTY(QColor parentBgHoverColor READ getParentBgHoverColor WRITE setParentBgHoverColor)
Q_PROPERTY(QColor parentTextNormalColor READ getParentTextNormalColor WRITE setParentTextNormalColor)
Q_PROPERTY(QColor parentTextSelectedColor READ getParentTextSelectedColor WRITE setParentTextSelectedColor)
Q_PROPERTY(QColor parentTextHoverColor READ getParentTextHoverColor WRITE setParentTextHoverColor)
Q_PROPERTY(int childIconMargin READ getChildIconMargin WRITE setChildIconMargin)
Q_PROPERTY(int childMargin READ getChildMargin WRITE setChildMargin)
Q_PROPERTY(int childFontSize READ getChildFontSize WRITE setChildFontSize)
Q_PROPERTY(int childHeight READ getChildHeight WRITE setChildHeight)
Q_PROPERTY(QColor childBgNormalColor READ getChildBgNormalColor WRITE setChildBgNormalColor)
Q_PROPERTY(QColor childBgSelectedColor READ getChildBgSelectedColor WRITE setChildBgSelectedColor)
Q_PROPERTY(QColor childBgHoverColor READ getChildBgHoverColor WRITE setChildBgHoverColor)
Q_PROPERTY(QColor childTextNormalColor READ getChildTextNormalColor WRITE setChildTextNormalColor)
Q_PROPERTY(QColor childTextSelectedColor READ getChildTextSelectedColor WRITE setChildTextSelectedColor)
Q_PROPERTY(QColor childTextHoverColor READ getChildTextHoverColor WRITE setChildTextHoverColor)
Q_PROPERTY(ExpendMode expendMode READ getExpendMode WRITE setExpendMode)
public:
//節點展開模式
enum ExpendMode {
ExpendMode_SingleClick = 0, //單擊模式
ExpendMode_DoubleClick = 1, //雙擊模式
ExpendMode_NoClick = 2, //不可單擊雙擊
};
NavListView(QWidget *parent = 0);
~NavListView();
private:
NavModel *model; //數據模型
NavDelegate *delegate; //數據委托
QStringList parentItem; //父節點數據集合
QList<QStringList> childItem; //子節點數據
QString items; //節點集合
bool rightIconVisible; //右側圖標是否顯示
bool tipVisible; //是否顯示提示信息
int tipWidth; //提示信息寬度
bool separateVisible; //是否顯示行分隔符
int separateHeight; //行分隔符高度
QColor separateColor; //行分隔符顏色
bool lineLeft; //是否顯示在左側
bool lineVisible; //是否顯示線條
int lineWidth; //線條寬度
QColor lineColor; //線條顏色
bool triangleLeft; //是否顯示在左側
bool triangleVisible; //是否顯示三角形
int triangleWidth; //三角形寬度
QColor triangleColor; //三角形顏色
int parentIconMargin; //父節點圖標邊距
int parentMargin; //父節點邊距
int parentFontSize; //父節點字體大小
int parentHeight; //父節點高度
QColor parentBgNormalColor; //父節點正常背景色
QColor parentBgSelectedColor; //父節點選中背景色
QColor parentBgHoverColor; //父節點懸停背景色
QColor parentTextNormalColor; //父節點正常文字顏色
QColor parentTextSelectedColor; //父節點選中文字顏色
QColor parentTextHoverColor; //父節點懸停文字顏色
int childIconMargin; //子節點圖標邊距
int childMargin; //子節點邊距
int childFontSize; //子節點字體大小
int childHeight; //子節點高度
QColor childBgNormalColor; //子節點正常背景色
QColor childBgSelectedColor; //子節點選中背景色
QColor childBgHoverColor; //子節點懸停背景色
QColor childTextNormalColor; //子節點正常文字顏色
QColor childTextSelectedColor; //子節點選中文字顏色
QColor childTextHoverColor; //子節點懸停文字顏色
ExpendMode expendMode; //節點展開模式 單擊/雙擊/禁用
private slots:
void pressed(const QModelIndex &data);
void setData(const QStringList &listItems);
public:
QString getItems() const;
bool getRightIconVisible() const;
bool getTipVisible() const;
int getTipWidth() const;
bool getSeparateVisible() const;
int getSeparateHeight() const;
QColor getSeparateColor() const;
bool getLineLeft() const;
bool getLineVisible() const;
int getLineWidth() const;
QColor getLineColor() const;
bool getTriangleLeft() const;
bool getTriangleVisible() const;
int getTriangleWidth() const;
QColor getTriangleColor() const;
int getParentIconMargin() const;
int getParentMargin() const;
int getParentFontSize() const;
int getParentHeight() const;
QColor getParentBgNormalColor() const;
QColor getParentBgSelectedColor()const;
QColor getParentBgHoverColor() const;
QColor getParentTextNormalColor()const;
QColor getParentTextSelectedColor()const;
QColor getParentTextHoverColor()const;
int getChildIconMargin() const;
int getChildMargin() const;
int getChildFontSize() const;
int getChildHeight() const;
QColor getChildBgNormalColor() const;
QColor getChildBgSelectedColor()const;
QColor getChildBgHoverColor() const;
QColor getChildTextNormalColor()const;
QColor getChildTextSelectedColor()const;
QColor getChildTextHoverColor() const;
ExpendMode getExpendMode() const;
QSize sizeHint() const;
QSize minimumSizeHint() const;
public Q_SLOTS:
//設置節點數據
void setItems(const QString &items);
//設置選中指定行
void setCurrentRow(int row);
//設置父節點右側圖標是否顯示
void setRightIconVisible(bool rightIconVisible);
//設置提示信息 是否顯示+寬度
void setTipVisible(bool tipVisible);
void setTipWidth(int tipWidth);
//設置行分隔符 是否顯示+高度+顏色
void setSeparateVisible(bool separateVisible);
void setSeparateHeight(int separateHeight);
void setSeparateColor(const QColor &separateColor);
//設置線條 位置+可見+寬度+顏色
void setLineLeft(bool lineLeft);
void setLineVisible(bool lineVisible);
void setLineWidth(int lineWidth);
void setLineColor(const QColor &lineColor);
//設置三角形 位置+可見+寬度+顏色
void setTriangleLeft(bool triangleLeft);
void setTriangleVisible(bool triangleVisible);
void setTriangleWidth(int triangleWidth);
void setTriangleColor(const QColor &triangleColor);
//設置父節點 圖標邊距+左側邊距+字體大小+節點高度+顏色集合
void setParentIconMargin(int parentIconMargin);
void setParentMargin(int parentMargin);
void setParentFontSize(int parentFontSize);
void setParentHeight(int parentHeight);
void setParentBgNormalColor(const QColor &parentBgNormalColor);
void setParentBgSelectedColor(const QColor &parentBgSelectedColor);
void setParentBgHoverColor(const QColor &parentBgHoverColor);
void setParentTextNormalColor(const QColor &parentTextNormalColor);
void setParentTextSelectedColor(const QColor &parentTextSelectedColor);
void setParentTextHoverColor(const QColor &parentTextHoverColor);
//設置子節點 圖標邊距+左側邊距+字體大小+節點高度+顏色集合
void setChildIconMargin(int childIconMargin);
void setChildMargin(int childMargin);
void setChildFontSize(int childFontSize);
void setChildHeight(int childHeight);
void setChildBgNormalColor(const QColor &childBgNormalColor);
void setChildBgSelectedColor(const QColor &childBgSelectedColor);
void setChildBgHoverColor(const QColor &childBgHoverColor);
void setChildTextNormalColor(const QColor &childTextNormalColor);
void setChildTextSelectedColor(const QColor &childTextSelectedColor);
void setChildTextHoverColor(const QColor &childTextHoverColor);
//設置節點展開模式
void setExpendMode(const ExpendMode &expendMode);
Q_SIGNALS:
void pressed(const QString &text, const QString &parentText);
void pressed(int index, int parentIndex);
void pressed(int childIndex);
};
#endif // NAVLISTVIEW_H
五、核心代碼
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).toULongLong();
//定義變量存儲區域
QRect optionRect = option.rect;
int x = optionRect.x();
int y = optionRect.y();
int width = optionRect.width();
int height = optionRect.height();
int fontSize = nav->getParentFontSize();
//父節點和子節點顏色分開設置
bool parent = (node->level == 1);
//根據不同的狀態設置不同的顏色 bgColor-主背景色 textColor-主文字顏色 tipBgColor-提示信息背景顏色 tipTextColor-提示信息文字顏色
QColor bgColor, textColor, tipBgColor, tipTextColor, iconColor;
if (option.state & QStyle::State_Selected) {
bgColor = parent ? nav->getParentBgSelectedColor() : nav->getChildBgSelectedColor();
textColor = parent ? nav->getParentTextSelectedColor() : nav->getChildTextSelectedColor();
tipBgColor = parent ? nav->getParentTextSelectedColor() : nav->getChildTextSelectedColor();
tipTextColor = parent ? nav->getParentBgSelectedColor() : nav->getChildBgSelectedColor();
iconColor = parent ? nav->getParentTextSelectedColor() : nav->getChildTextSelectedColor();
} else if (option.state & QStyle::State_MouseOver) {
bgColor = parent ? nav->getParentBgHoverColor() : nav->getChildBgHoverColor();
textColor = parent ? nav->getParentTextHoverColor() : nav->getChildTextHoverColor();
tipBgColor = parent ? nav->getParentTextSelectedColor() : nav->getChildTextSelectedColor();
tipTextColor = parent ? nav->getParentBgSelectedColor() : nav->getChildBgSelectedColor();
iconColor = parent ? nav->getParentTextHoverColor() : nav->getChildTextHoverColor();
} else {
bgColor = parent ? nav->getParentBgNormalColor() : nav->getChildBgNormalColor();
textColor = parent ? nav->getParentTextNormalColor() : nav->getChildTextNormalColor();
tipBgColor = parent ? nav->getParentBgSelectedColor() : nav->getChildBgSelectedColor();
tipTextColor = parent ? nav->getParentTextSelectedColor() : nav->getChildTextSelectedColor();
iconColor = parent ? nav->getParentTextNormalColor() : nav->getChildTextNormalColor();
}
//繪制背景
painter->fillRect(optionRect, bgColor);
//繪制線條,目前限定子節點繪制,如果需要父節點也繪制則取消parent判斷即可
int lineWidth = nav->getLineWidth();
if (!parent && nav->getLineVisible() && lineWidth > 0) {
if ((option.state & QStyle::State_Selected) || (option.state & QStyle::State_MouseOver)) {
//設置偏移量,不然上下部分會有點偏移
float offset = (float)lineWidth / 2;
//計算上下兩個坐標點
QPointF pointTop(x, y + offset);
QPointF pointBottom(x, height + y - offset);
if (!nav->getLineLeft()) {
pointTop.setX(width);
pointBottom.setX(width);
}
//設置線條顏色和寬度
painter->setPen(QPen(nav->getLineColor(), lineWidth));
painter->drawLine(pointTop, pointBottom);
}
}
//繪制三角形,目前限定子節點繪制,如果需要父節點也繪制則取消parent判斷即可
int triangleWidth = nav->getTriangleWidth();
if (!parent && nav->getTriangleVisible() && triangleWidth > 0) {
if ((option.state & QStyle::State_Selected) || (option.state & QStyle::State_MouseOver)) {
QFont font = iconFont;
font.setPixelSize(fontSize + triangleWidth);
painter->setFont(font);
painter->setPen(nav->getTriangleColor());
//采用圖形字體中的三角形繪制
if (nav->getTriangleLeft()) {
painter->drawText(optionRect, Qt::AlignLeft | Qt::AlignVCenter, QChar(0xf0da));
} else {
painter->drawText(optionRect, Qt::AlignRight | Qt::AlignVCenter, QChar(0xf0d9));
}
}
}
//繪制行分隔符
if (nav->getSeparateVisible()) {
if (node->level == 1 || (node->level == 2 && node->last)) {
painter->setPen(QPen(nav->getSeparateColor(), nav->getSeparateHeight()));
painter->drawLine(QPointF(x, y + height), QPointF(x + width, y + height));
}
}
//繪制文字,如果文字為空則不繪制
QString text = node->text;
if (!text.isEmpty()) {
//文字離左邊的距離+字體大小
int margin = nav->getParentMargin();
if (node->level == 2) {
margin = nav->getChildMargin();
fontSize = nav->getChildFontSize();
}
//計算文字區域
QRect textRect = optionRect;
textRect.setWidth(width - margin);
textRect.setX(x + margin);
QFont font;
font.setPixelSize(fontSize);
painter->setFont(font);
painter->setPen(textColor);
painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, text);
}
//繪制提示信息,如果不需要顯示提示信息或者提示信息為空則不繪制
QString tip = node->tip;
if (nav->getTipVisible() && !tip.isEmpty()) {
//如果是數字則將超過999的數字顯示成 999+
//如果顯示的提示信息長度過長則將多余顯示成省略號 .
if (tip.toInt() > 0) {
tip = tip.toInt() > 999 ? "999+" : tip;
} else if (tip.length() > 2) {
tip = tip.left(2) + " .";
}
//計算繪制區域,半徑取高度的四分之一
int radius = height / 4;
QRect tipRect = optionRect;
tipRect.setHeight(radius * 2);
tipRect.moveCenter(optionRect.center());
tipRect.setLeft(optionRect.right() - nav->getTipWidth() - 5);
tipRect.setRight(optionRect.right() - 5);
//設置字體大小
QFont font;
font.setPixelSize(fontSize - 2);
painter->setFont(font);
//繪制提示文字的背景
painter->setPen(Qt::NoPen);
painter->setBrush(tipBgColor);
painter->drawRoundedRect(tipRect, radius, radius);
//繪制提示文字
painter->setPen(tipTextColor);
painter->setBrush(Qt::NoBrush);
painter->drawText(tipRect, Qt::AlignCenter, tip);
}
//計算繪制圖標區域
QRect iconRect = optionRect;
iconRect.setLeft(parent ? nav->getParentIconMargin() : nav->getChildIconMargin());
//設置圖形字體和畫筆顏色
QFont font = iconFont;
font.setPixelSize(fontSize);
painter->setFont(font);
painter->setPen(textColor);
//繪制左側圖標,有圖標則繪制圖標,沒有的話父窗體取 + -
if (!node->icon.isNull()) {
painter->drawText(iconRect, Qt::AlignLeft | Qt::AlignVCenter, node->icon);
} else if (parent) {
if (node->expand) {
painter->drawText(iconRect, Qt::AlignLeft | Qt::AlignVCenter, QChar(0xf067));
} else {
painter->drawText(iconRect, Qt::AlignLeft | Qt::AlignVCenter, QChar(0xf068));
}
}
//繪制父節點右側圖標
iconRect.setRight(optionRect.width() - 10);
if (!(nav->getTipVisible() && !node->tip.isEmpty()) && nav->getRightIconVisible() && parent) {
if (node->expand) {
painter->drawText(iconRect, Qt::AlignRight | Qt::AlignVCenter, QChar(0xf054));
} else {
painter->drawText(iconRect, Qt::AlignRight | Qt::AlignVCenter, QChar(0xf078));
}
}
}
六、控件介紹
- 超過150個精美控件,涵蓋了各種儀表盤、進度條、進度球、指南針、曲線圖、標尺、溫度計、導航條、導航欄,flatui、高亮按鈕、滑動選擇器、農歷等。遠超qwt集成的控件數量。
- 每個類都可以獨立成一個單獨的控件,零耦合,每個控件一個頭文件和一個實現文件,不依賴其他文件,方便單個控件以源碼形式集成到項目中,較少代碼量。qwt的控件類環環相扣,高度耦合,想要使用其中一個控件,必須包含所有的代碼。
- 全部純Qt編寫,QWidget+QPainter繪制,支持Qt4.6到Qt5.12的任何Qt版本,支持mingw、msvc、gcc等編譯器,支持任意操作系統比如windows+linux+mac+嵌入式linux等,不亂碼,可直接集成到Qt Creator中,和自帶的控件一樣使用,大部分效果只要設置幾個屬性即可,極為方便。
- 每個控件都有一個對應的單獨的包含該控件源碼的DEMO,方便參考使用。同時還提供一個所有控件使用的集成的DEMO。
- 每個控件的源代碼都有詳細中文注釋,都按照統一設計規范編寫,方便學習自定義控件的編寫。
- 每個控件默認配色和demo對應的配色都非常精美。
- 超過130個可見控件,6個不可見控件。
- 部分控件提供多種樣式風格選擇,多種指示器樣式選擇。
- 所有控件自適應窗體拉伸變化。
- 集成自定義控件屬性設計器,支持拖曳設計,所見即所得,支持導入導出xml格式。
- 自帶activex控件demo,所有控件可以直接運行在ie瀏覽器中。
- 集成fontawesome圖形字體+阿里巴巴iconfont收藏的幾百個圖形字體,享受圖形字體帶來的樂趣。
- 所有控件最后生成一個dll動態庫文件,可以直接集成到qtcreator中拖曳設計使用。
- 目前已經有qml版本,后期會考慮出pyqt版本,如果用戶需求量很大的話。
七、SDK下載
- SDK下載鏈接:https://pan.baidu.com/s/1A5Gd77kExm8Co5ckT51vvQ 提取碼:877p
- 下載鏈接中包含了各個版本的動態庫文件,所有控件的頭文件,使用demo,自定義控件+屬性設計器。
- 自定義控件插件開放動態庫dll使用(永久免費),無任何后門和限制,請放心使用。
- 目前已提供26個版本的dll,其中包括了qt5.12.3 msvc2017 32+64 mingw 32+64 的。
- 不定期增加控件和完善控件,不定期更新SDK,歡迎各位提出建議,謝謝!
- widget版本(QQ:517216493)qml版本(QQ:373955953)三峰駝(QQ:278969898)。
- 濤哥的知乎專欄 Qt進階之路 https://zhuanlan.zhihu.com/TaoQt
- 歡迎關注微信公眾號【高效程序員】,C++/Python、學習方法、寫作技巧、熱門技術、職場發展等內容,干貨多多,福利多多!
- Qt入門書籍推薦霍亞飛的《Qt Creator快速入門》《Qt5編程入門》,Qt進階書籍推薦官方的《C++ GUI Qt4編程》。
- 強烈推薦程序員自我修養和規划系列書《大話程序員》《程序員的成長課》《解憂程序員》,受益匪淺,受益終生!