Qt 有一个demo "Frozen Column Example"是QTableView固定第一列,我仿照着写了一个QTreeWidget固定第一列。
#ifndef QFROZENTREEWIDGET_H
#define QFROZENTREEWIDGET_H
#include
#include
#include
//定位结构体,用来存储 展合、勾选 的item位置
struct structIndex
{
int m_index;
structIndex *m_pChildIndex;
structIndex()
{
m_index = -1;
m_pChildIndex = NULL;
}
};
class QFrozenTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
QFrozenTreeWidget(QWidget *parent = 0);
~QFrozenTreeWidget();
//接口
public:
void SetFrozenColumnHeader(const QString &strHeader);
void CreateNewItem(QTreeWidgetItem *item);
protected:
void resizeEvent(QResizeEvent *event);
QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers);
void scrollTo(const QModelIndex &index, ScrollHint hint);
private:
void Init();
void UpdateFrozenTreeGeometry();
/*
* 用来 从 固定列和本树 找对应的item
* 比如 固定列某个item展开,要找到本树的对应位置item让其展开,实现同步
* 下面是递归方法
* 可能还有简单方法,比如:
* 可以根据 鼠标位置 找到对应item,用itemAt(QPoint);
* 可以根据 item 对应的QModelIndex 里row() 采用加法找到对应item,有兴趣的可以实现试试
*/
structIndex *GetItemIndex(QTreeWidgetItem *targetItem, QTreeWidgetItem *sourceItem);
QTreeWidgetItem *GetItemFromIndex(QTreeWidgetItem *item, structIndex *index);
QTreeWidgetItem *GetItemFromItem(QTreeWidget *targetTree, QTreeWidgetItem *souceItem);
/*
* 下面实现同步勾选
*/
void SetParentCheckState(QTreeWidgetItem *parentItem);
void SetChildCheckState(QTreeWidgetItem *selfItem);
void SetItemChecked(QTreeWidgetItem *item);
private slots:
void OnItemCollapsed(QTreeWidgetItem *item);
void OnItemExpanded(QTreeWidgetItem *item);
void OnItemChanged(QTreeWidgetItem *item, int column);
void UpdateSectionWidthSlot(int logicalIndex, int, int newSize);
private:
QTreeWidget *m_pFrozenTreeWidget; //固定的第一列,覆盖在本树第一列位置,实现第一列保持不动
};
#endif // QFROZENTREEWIDGET_H
cpp文件 "QFrozenTreeWidget.cpp"
#include
#include
#include "QFrozenTreeWidget.h"
QFrozenTreeWidget::QFrozenTreeWidget(QWidget *parent)
: QTreeWidget(parent)
{
Init();
}
QFrozenTreeWidget::~QFrozenTreeWidget()
{
}
void QFrozenTreeWidget::SetFrozenColumnHeader(const QString &strHeader)
{
m_pFrozenTreeWidget->setHeaderLabel(strHeader);
}
void QFrozenTreeWidget::resizeEvent(QResizeEvent *event)
{
QTreeWidget::resizeEvent(event);
UpdateFrozenTreeGeometry();
}
QModelIndex QFrozenTreeWidget::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
{
QModelIndex current = QTreeWidget::moveCursor(cursorAction, modifiers);
if (cursorAction == MoveLeft && current.column() > 0
&& visualRect(current).topLeft().x() < m_pFrozenTreeWidget->columnWidth(0))
{
const int newValue = horizontalScrollBar()->value() + visualRect(current).topLeft().x()
- m_pFrozenTreeWidget->columnWidth(0);
horizontalScrollBar()->setValue(newValue);
}
return current;
}
void QFrozenTreeWidget::scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint hint)
{
if (index.column() > 0)
{
QTreeWidget::scrollTo(index, hint);
}
}
void QFrozenTreeWidget::Init()
{
m_pFrozenTreeWidget = new QTreeWidget(this);
m_pFrozenTreeWidget->setFocusPolicy(Qt::NoFocus);
//让固定列宽度不能手动调整大小,而跟随本树第一列大小来改变
m_pFrozenTreeWidget->header()->setSectionResizeMode(QHeaderView::Fixed);
//让浮在本树上边而不是下边
viewport()->stackUnder(m_pFrozenTreeWidget);
//设置第一列背景颜色好显示效果
m_pFrozenTreeWidget->setStyleSheet("QTreeView { border: none;"
"background-color: #8ede21;"
"selection-background-color: #999 }");
//设置选择模式一样
m_pFrozenTreeWidget->setSelectionModel(selectionModel());
//设置第一列宽度一样
m_pFrozenTreeWidget->setColumnWidth(0, columnWidth(0));
//固定列不需要水平垂直滚动条,用本列的来控制
m_pFrozenTreeWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_pFrozenTreeWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_pFrozenTreeWidget->show();
//设置滚动条模式按像素为单位滚动
setHorizontalScrollMode(ScrollPerPixel);
setVerticalScrollMode(ScrollPerPixel);
m_pFrozenTreeWidget->setVerticalScrollMode(ScrollPerPixel);
connect(header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(UpdateSectionWidthSlot(int,int,int)));
//连接垂直滚动条的信号槽实现同步滚动
connect(m_pFrozenTreeWidget->verticalScrollBar(), SIGNAL(valueChanged(int)), verticalScrollBar(), SLOT(setValue(int)));
connect(verticalScrollBar(), SIGNAL(valueChanged(int)), m_pFrozenTreeWidget->verticalScrollBar(), SLOT(setValue(int)));
//item 展合
connect(m_pFrozenTreeWidget, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(OnItemCollapsed(QTreeWidgetItem*)));
connect(m_pFrozenTreeWidget, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(OnItemExpanded(QTreeWidgetItem*)));
//第一列item勾选
connect(m_pFrozenTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(OnItemChanged(QTreeWidgetItem*,int)));
}
void QFrozenTreeWidget::UpdateFrozenTreeGeometry()
{
m_pFrozenTreeWidget->setGeometry(frameWidth(), frameWidth(), columnWidth(0), viewport()->height() + header()->height());
}
structIndex *QFrozenTreeWidget::GetItemIndex(QTreeWidgetItem *targetItem, QTreeWidgetItem *sourceItem)
{
if (targetItem == sourceItem)
{
return new structIndex;
}
for (int i = 0; i < targetItem->childCount(); ++i)
{
structIndex *pChildStructIndex = GetItemIndex(targetItem->child(i), sourceItem);
if (pChildStructIndex != NULL)
{
structIndex *pStructIndex = new structIndex;
pStructIndex->m_pChildIndex = pChildStructIndex;
pStructIndex->m_index = i;
return pStructIndex;
}
}
return NULL;
}
QTreeWidgetItem *QFrozenTreeWidget::GetItemFromIndex(QTreeWidgetItem *item, structIndex *index)
{
if (index->m_index == -1)
{
return item;
}
QTreeWidgetItem *targetItem = GetItemFromIndex(item->child(index->m_index), index->m_pChildIndex);
if (index->m_pChildIndex != NULL)
{
delete index->m_pChildIndex;
index->m_pChildIndex = NULL;
}
return targetItem;
}
QTreeWidgetItem *QFrozenTreeWidget::GetItemFromItem(QTreeWidget *targetTree, QTreeWidgetItem *souceItem)
{
structIndex *pStructIndex = GetItemIndex(souceItem->treeWidget()->invisibleRootItem(), souceItem);
QTreeWidgetItem *item = GetItemFromIndex(targetTree->invisibleRootItem(), pStructIndex);
delete pStructIndex;
return item;
}
void QFrozenTreeWidget::SetParentCheckState(QTreeWidgetItem *parentItem)
{
if (parentItem == NULL)
{
return;
}
int count = 0;
for (int i = 0; i < parentItem->childCount(); ++i)
{
if (parentItem->child(i)->checkState(0) == Qt::Checked)
{
count++;
}
}
if (count == parentItem->childCount())
{
parentItem->setCheckState(0, Qt::Checked);
}
else
{
parentItem->setCheckState(0, Qt::Unchecked);
}
GetItemFromItem(this, parentItem)->setCheckState(0, parentItem->checkState(0));
SetParentCheckState(parentItem->parent());
}
void QFrozenTreeWidget::SetChildCheckState(QTreeWidgetItem *selfItem)
{
for (int i = 0; i < selfItem->childCount(); ++i)
{
selfItem->child(i)->setCheckState(0, selfItem->checkState(0));
GetItemFromItem(this, selfItem->child(i))->setCheckState(0, selfItem->checkState(0));
SetChildCheckState(selfItem->child(i));
}
}
void QFrozenTreeWidget::SetItemChecked(QTreeWidgetItem *item)
{
QTreeWidgetItem *targetItem = GetItemFromItem(m_pFrozenTreeWidget, item);
targetItem->setCheckState(0, item->checkState(0));
}
void QFrozenTreeWidget::CreateNewItem(QTreeWidgetItem *item)
{
QTreeWidgetItem *newItem = NULL;
if (item->parent() == NULL)
{
newItem = new QTreeWidgetItem(GetItemFromItem(m_pFrozenTreeWidget, m_pFrozenTreeWidget->invisibleRootItem()));
}
else
{
newItem = new QTreeWidgetItem(GetItemFromItem(m_pFrozenTreeWidget, item->parent()));
}
newItem->setCheckState(0, Qt::Unchecked);
newItem->setText(0, item->text(0));
}
void QFrozenTreeWidget::OnItemCollapsed(QTreeWidgetItem *item)
{
collapseItem(GetItemFromItem(this, item));
}
void QFrozenTreeWidget::OnItemExpanded(QTreeWidgetItem *item)
{
expandItem(GetItemFromItem(this, item));
}
void QFrozenTreeWidget::OnItemChanged(QTreeWidgetItem *item, int column)
{
GetItemFromItem(this, item)->setCheckState(column, item->checkState(column));
m_pFrozenTreeWidget->blockSignals(true);
SetChildCheckState(item);
SetParentCheckState(item->parent());
m_pFrozenTreeWidget->blockSignals(false);
}
void QFrozenTreeWidget::UpdateSectionWidthSlot(int logicalIndex, int, int newSize)
{
if (logicalIndex == 0)
{
m_pFrozenTreeWidget->setColumnWidth(0, newSize);
UpdateFrozenTreeGeometry();
}
}
"main.cpp"
#include "QFrozenTreeWidget.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QFrozenTreeWidget w;
w.setColumnCount(5);
w.setHeaderLabels(QStringList() << "A" << "B" << "C" << "D" << "E");
w.SetFrozenColumnHeader("A");
for (int i = 0; i < 5; ++i)
{
QTreeWidgetItem *parent = new QTreeWidgetItem(w.invisibleRootItem(), QStringList() << QString("%1a").arg(i) << QString("%1b").arg(i) << QString("%1c").arg(i) << QString("%1d").arg(i) << QString("%1e").arg(i));
w.CreateNewItem(parent);
for (int j = 0; j < 5; ++j)
{
QTreeWidgetItem *child = new QTreeWidgetItem(parent,
QStringList() << QString("a%1%2").arg(i).arg(j)
<< QString("b%1%2").arg(i).arg(j)
<< QString("c%1%2").arg(i).arg(j)
<< QString("d%1%2").arg(i).arg(j)
<< QString("e%1%2").arg(i).arg(j));
w.CreateNewItem(child);
}
}
w.resize(400, 300);
w.setColumnWidth(0, 100);
w.show();
return a.exec();
}