这是好久之前做的一个树控件,挺好玩的就拿出来分享一下.
代码的主要思路是是通过一个ListWidget和一个自定义的Widget进行组合成为一个节点,然后多个这样的控件进行组合,类似树控件的一个展示效果。
通过这个控件可以做成QQ好友列表的效果,之前有做过一版QQ列表,已经做完了,可惜代码找不到了…
不过只需要修改这两个控件就可以做成QQ好友列表的样子了,如果代码找到了,到时候可以在群里分享一下。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ITEM_HEIGHT 40 // item高度;
// 传感器数据结构;
struct SensorDataStruct
{
QColor alarmColor;
QString sensorName;
float sensorNumber;
};
// 节点数据结构;
struct NodeDataStruct
{
QString strNodeName;
int strNodeCount;
QColor numberColor;
QList<SensorDataStruct> sensorDataList;
};
class SensorItemWidget : public QWidget
{
public:
SensorItemWidget(QWidget* parent = NULL)
: QWidget(parent)
{
initWidget();
this->setFixedSize(QSize(250, ITEM_HEIGHT));
this->setStyleSheet(".QWidget:hover{background:rgba(200, 200, 200, 150);}");
}
// 设置传感器信息;
void setSensorInfo(QColor alarmColor, QString sensorName, float sensorNumber)
{
m_colorWidget->setStyleSheet(QString("QWidget{border-radius:6px;background:rgb(%1, %2, %3);}").arg(alarmColor.red()).arg(alarmColor.green()).arg(alarmColor.blue()));
m_sensorLabel->setText(sensorName);
m_sensorLabel->setScaledContents(true);
m_numberLabel->setText(QString::number(sensorNumber));
m_numberLabel->setScaledContents(true);
}
private:
void initWidget()
{
m_colorWidget = new QWidget;
m_colorWidget->setFixedSize(QSize(12, 12));
m_sensorLabel = new QLabel;
m_numberLabel = new QLabel;
QHBoxLayout* hLayout = new QHBoxLayout(this);
hLayout->addWidget(m_colorWidget);
hLayout->addWidget(m_sensorLabel);
hLayout->addWidget(m_numberLabel);
hLayout->addStretch();
hLayout->setSpacing(15);
hLayout->setContentsMargins(20, 0, 0, 0);
}
private:
QWidget* m_colorWidget;
QLabel* m_sensorLabel;
QLabel* m_numberLabel;
};
class SencorListWidget : public QWidget
{
Q_OBJECT
public:
SencorListWidget(QWidget* parent = NULL)
: QWidget(parent)
, m_isFolded(true)
{
initWidget();
this->setWindowFlags(Qt::FramelessWindowHint);
this->setStyleSheet("QListWidget{border:none;border-bottom:1px solid gray;}");
}
// 设置标题栏信息;
void setTitleInfo(QString strTitle, int count, QColor numberColor)
{
m_titleLabel->setText(strTitle);
m_numberLabel->setText(QString::number(count));
m_numberLabel->setStyleSheet(QString("color:white;border-radius:8px;background:rgb(%1, %2, %3);").arg(numberColor.red()).arg(numberColor.green()).arg(numberColor.blue()));
}
// 添加传感器子项;
void addSensorItem(QColor alarmColor, QString sensorName, float sensorNumber)
{
QListWidgetItem* item = new QListWidgetItem;
item->setSizeHint(QSize(250, ITEM_HEIGHT));
m_listWidget->addItem(item);
SensorItemWidget* itemWidget = new SensorItemWidget;
itemWidget->setSensorInfo(alarmColor, sensorName, sensorNumber);
m_listWidget->setItemWidget(item, itemWidget);
}
// 更新传感器某一项状态;
void updateSensorItem(int rowCount, QColor alarmColor, QString sensorName, float sensorNumber)
{
QListWidgetItem* item = m_listWidget->item(rowCount);
if (item != NULL)
{
SensorItemWidget* itemWidget = static_cast<SensorItemWidget*>(m_listWidget->itemWidget(item));
itemWidget->setSensorInfo(alarmColor, sensorName, sensorNumber);
}
}
private:
void initTitleBackWidget()
{
m_titleBackWidget = new QWidget;
m_titleBackWidget->setFixedSize(QSize(250, 40));
m_titleBackWidget->installEventFilter(this);
m_titleBackWidget->setStyleSheet(".QWidget{border-bottom:1px solid gray;}\
.QWidget:hover{background:rgba(200, 200, 200, 200);}");
m_foldStateLabel = new QLabel;
m_foldStateLabel->setFixedSize(QSize(20, 20));
m_foldStateLabel->setPixmap(QIcon(":/Resources/Folded.png").pixmap(m_foldStateLabel->size()));
m_titleLabel = new QLabel;
m_titleLabel->setStyleSheet("font-size:16px;font-weight:bold;");
m_numberLabel = new QLabel;
m_numberLabel->setFixedSize(QSize(16, 16));
m_numberLabel->setAlignment(Qt::AlignCenter);
QHBoxLayout* hButtonLayout = new QHBoxLayout;
hButtonLayout->addWidget(m_numberLabel);
hButtonLayout->setContentsMargins(0, 0, 0, 10);
m_foldTagLabel = new QLabel;
m_foldTagLabel->setFixedSize(QSize(20, 20));
m_foldTagLabel->setPixmap(QIcon(":/Resources/EnableFold.png").pixmap(m_foldTagLabel->size()));
QHBoxLayout* hTitleLayout = new QHBoxLayout(m_titleBackWidget);
hTitleLayout->addWidget(m_foldStateLabel);
hTitleLayout->addWidget(m_titleLabel);
hTitleLayout->addLayout(hButtonLayout);
hTitleLayout->addStretch();
hTitleLayout->addWidget(m_foldTagLabel);
hTitleLayout->setSpacing(5);
hTitleLayout->setContentsMargins(5, 0, 25, 0);
}
void initWidget()
{
initTitleBackWidget();
m_listWidget = new QListWidget;
m_listWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_listWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_listWidget->setVisible(false);
QVBoxLayout* vMainLayout = new QVBoxLayout(this);
vMainLayout->addWidget(m_titleBackWidget);
vMainLayout->addWidget(m_listWidget);
vMainLayout->addStretch();
vMainLayout->setSpacing(0);
vMainLayout->setMargin(0);
}
bool eventFilter(QObject *watched, QEvent *event)
{
if (watched == m_titleBackWidget)
{
if (event->type() == QEvent::MouseButtonRelease)
{
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
if (mouseEvent->button() == Qt::LeftButton)
{
// 节点点击进行展开收缩;
if (m_isFolded)
{
m_foldStateLabel->setPixmap(QIcon(":/Resources/UnFolded.png").pixmap(m_foldStateLabel->size()));
m_listWidget->setVisible(true);
m_listWidget->setFixedHeight(m_listWidget->count() * ITEM_HEIGHT + 10);
this->setFixedHeight(m_titleBackWidget->height() + m_listWidget->height());
emit signalNodeFoldChanged(m_titleBackWidget->height() + m_listWidget->height());
}
else
{
m_foldStateLabel->setPixmap(QIcon(":/Resources/Folded.png").pixmap(m_foldStateLabel->size()));
m_listWidget->setVisible(false);
this->setFixedHeight(m_titleBackWidget->height());
emit signalNodeFoldChanged(m_titleBackWidget->height());
}
m_isFolded = !m_isFolded;
}
}
}
return __super::eventFilter(watched, event);
}
signals:
void signalNodeFoldChanged(int itemHeight);
private:
QLabel* m_foldStateLabel;
QLabel* m_titleLabel;
QLabel* m_numberLabel;
QLabel* m_foldTagLabel;
QWidget* m_titleBackWidget;
QListWidget* m_listWidget;
bool m_isFolded;
};
class CustomTreeWidget : public QWidget
{
Q_OBJECT
public:
CustomTreeWidget(QWidget *parent = Q_NULLPTR);
private:
void initWidget();
private slots:
void onNodeFoldChanged();
void onUpdateData();
private:
QScrollArea* m_scrollArea;
QList<NodeDataStruct> m_nodeDataList;
QList<SencorListWidget*> m_nodeWidgetList;
QWidget* m_sensorBackWidget;
// 刷新数据时钟;
QTimer m_refreshTimer;
int m_refreshIndex;
};
#include "CustomTreeWidget.h"
CustomTreeWidget::CustomTreeWidget(QWidget *parent)
: QWidget(parent)
, m_refreshIndex(0)
{
initWidget();
this->setFixedSize(QSize(250, 600));
m_refreshTimer.setInterval(500);
connect(&m_refreshTimer, &QTimer::timeout, this, &CustomTreeWidget::onUpdateData);
m_refreshTimer.start();
this->setStyleSheet("*{font-family:Microsoft YaHei;}\
QScrollBar::vertical {\
background:rgb(226,222,221);\
border:none;\
width: 5px;\
margin:0px;\
}\
QScrollBar::handle:vertical {\
background: rgb(192,192,192);\
border-radius:1px;\
min-height: 20px;\
width:5px;\
}\
QScrollBar::add-line:vertical {\
height:0px;\
}\
QScrollBar::sub-line:vertical {\
height:0px;\
}\
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {\
background:transparent;\
}");
}
void CustomTreeWidget::initWidget()
{
m_scrollArea = new QScrollArea;
m_scrollArea->setFixedWidth(250);
m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_scrollArea->setStyleSheet(".QScrollArea{background:white;}");
for (int i = 0; i < 4; i++)
{
NodeDataStruct nodeData;
nodeData.numberColor = Qt::red;
nodeData.strNodeCount = 6;
nodeData.strNodeName = QString("Node-%1").arg(i);
for (int j = 0; j < 6; j ++)
{
SensorDataStruct sensorData;
sensorData.alarmColor = Qt::blue;
sensorData.sensorName = QString("Sensor-%1").arg(j);
sensorData.sensorNumber = j;
nodeData.sensorDataList.append(sensorData);
}
m_nodeDataList.append(nodeData);
}
m_sensorBackWidget = new QWidget;
m_sensorBackWidget->setFixedWidth(250);
m_sensorBackWidget->setStyleSheet(".QWidget{background:white;}");
QVBoxLayout* vBackWidget = new QVBoxLayout(m_sensorBackWidget);
vBackWidget->setSpacing(0);
vBackWidget->setMargin(0);
// 根据已经造好的数据布局界面;
for (int i = 0; i < m_nodeDataList.size(); i++)
{
NodeDataStruct nodeData = m_nodeDataList[i];
SencorListWidget* sencorListWidget = new SencorListWidget;
connect(sencorListWidget, &SencorListWidget::signalNodeFoldChanged, this, &CustomTreeWidget::onNodeFoldChanged);
// 先设置节点标题信息;
sencorListWidget->setTitleInfo(nodeData.strNodeName, nodeData.strNodeCount, nodeData.numberColor);
// 为每个节点添加数据;
for (int j = 0; j < nodeData.sensorDataList.count(); j++)
{
SensorDataStruct sensorData = nodeData.sensorDataList[j];
sencorListWidget->addSensorItem(sensorData.alarmColor, sensorData.sensorName, sensorData.sensorNumber);
}
vBackWidget->addWidget(sencorListWidget);
m_nodeWidgetList.append(sencorListWidget);
}
m_scrollArea->setWidget(m_sensorBackWidget);
QHBoxLayout* hMainLayout = new QHBoxLayout(this);
hMainLayout->addWidget(m_scrollArea);
hMainLayout->setMargin(0);
}
void CustomTreeWidget::onNodeFoldChanged()
{
int height = 0;
for (int i = 0; i < m_nodeWidgetList.count(); i++)
{
height += m_nodeWidgetList[i]->height();
}
m_sensorBackWidget->setFixedHeight(height);
}
// 刷新数据;
void CustomTreeWidget::onUpdateData()
{
if (m_refreshIndex >= m_nodeDataList.count())
{
m_refreshIndex = 0;
}
// 这里500ms更新一个节点的数据;
SencorListWidget* sencorListWidget = m_nodeWidgetList[m_refreshIndex];
NodeDataStruct nodeData = m_nodeDataList[m_refreshIndex];
nodeData.numberColor = QColor(rand() % 256, rand() % 256, rand() % 256);
sencorListWidget->setTitleInfo(nodeData.strNodeName, nodeData.strNodeCount, nodeData.numberColor);
for (int j = 0; j < 6; j++)
{
SensorDataStruct sensorData = nodeData.sensorDataList[j];
sensorData.alarmColor = nodeData.numberColor = QColor(rand() % 256, rand() % 256, rand() % 256);
sencorListWidget->updateSensorItem(j, sensorData.alarmColor, sensorData.sensorName, sensorData.sensorNumber);
}
m_refreshIndex++;
}