QTableWidget表格控件的常用功能示例;
目录导读
使用QSS修改界面样式
设置表头样式示例
QHeaderView::section {
font: 75 12px "微软雅黑";
font-size: 12px;
color: #333333;
line-height: 34px;
background-color: rgba(245, 245, 245, 255);
height: 34px; /*设置每个节点的高度*/
border: 0px;
padding-left: 5px;
}
/* ---竖线--- */
QScrollBar:vertical
{
width: 10px;
height: 153px;
background: #EBEBEB;
margin:0px,0px,0px,0px;
padding-top:0px;
padding-bottom:0px;
border-radius:4px;
}
QScrollBar::handle:vertical
{
width:10px;
height: 153px;
background: #D6D6D6;
border-radius:4px;
min-height:20px;
}
QScrollBar::handle:vertical:hover
{
width:10px;
border-radius:4px;
min-height:20px;
}
QScrollBar::add-line:vertical
{
height:0px;
width:10px;
subcontrol-position:bottom;
}
QScrollBar::sub-line:vertical
{
height:0px;width:10px;
subcontrol-position:top;
}
QScrollBar::add-page:vertical,QScrollBar::sub-page:vertical
{
border-radius:4px;
}
/* ---横向--- */
QScrollBar:horizontal
{
width: 153px;
height: 10px;
background: #EBEBEB;
margin:0px,0px,0px,0px;
padding-top:0px;
padding-bottom:0px;
border-radius:4px;
}
QScrollBar::handle:horizontal
{
width:153px;
height: 10px;
background: #D6D6D6;
border-radius:4px;
min-width:20px;
}
QScrollBar::handle:horizontal:hover
{
height:10px;
border-radius:4px;
min-width:20px;
}
QScrollBar::add-line:horizontal
{
height:10px;
width:0px;
subcontrol-position:right;
}
QScrollBar::sub-line:horizontal
{
height:10px;width:0px;
subcontrol-position:left;
}
QScrollBar::add-page:horizontal,QScrollBar::sub-page:horizontal
{
border-radius:4px;
}
包括修改QTableWidget控件字体,单元格高度,移除边框等常用功能样式。
QTableWidget{
border:0px;
font: 75 12px "微软雅黑";
font-size: 12px;
background-color: rgb(255, 255, 255);
line-height: 19px;
}
QTableWidget::item {
height: 35px; /*设置每个节点的高度*/
border: none; /*去除边框*/
color: #333333;
font: 75 14px "微软雅黑";
line-height: 14px;
text-align: left;
padding-left: 5px;
}
QTableWidget::item:selected {
border-image: none; /*去除边框 ,QTreeWidget::branch:selected*/
border: none;
}
QTableWidget::indicator:checked {
width:18px;
height:18px;
border-image: none; /*去除边框 ,QTreeWidget::branch:selected*/
image: url(:/Image/cleanC/checked.png);
}
QTableWidget::indicator:unchecked {
width:18px;
height:18px;
border-image: none; /*去除边框 ,QTreeWidget::branch:selected*/
image: url(:/Image/cleanC/unchecked.png);
}
QTableWidget::item:hover{
background-color: #F5F5F5; /*设置选中节点的背景色 ,QTreeWidget::branch:hover*/
color: #333333;
border-image: none; /*去除边框*/
}
实现效果:
使用QScrollBar滚动条 - 动态加载数据
数据处理的时候,如果QTableWidget表格控件一次性加载太多数据,会造成界面上的卡顿,通过QScrollBar竖直滚动条信号动态加载数据,一次加载个几百条数据就能解决这个问题,
类似于网页的动态加载:
- 首先定义一个根据开始索引和加载条数加载数据的方法:
- 设置
setFlags(rowid->flags()^Qt::ItemIsEditable);
禁用单元格编辑 - 设置
setData(Qt::DisplayRole,QVariant::fromValue(Datas[i].ParentDirectoryRecord)
用于单元格排序
//! dataLists 是总数据源,
//! int start 从什么地方开始
//! int step 要加载多少条
void QTableWindow::LoadDataBy(int start,int step)
{
QList<NTFSMFT_DATA> Datas=dataLists.mid(start,step);
for(int i=0;i<Datas.count();i++)
{
//! 一条一条的插入
int row=ui->tableWidget->rowCount();
ui->tableWidget->insertRow(row);
QTableWidgetItem* rowid=new QTableWidgetItem();
rowid->setFlags(rowid->flags()^Qt::ItemIsEditable);
rowid->setData(Qt::DisplayRole,QVariant::fromValue(Datas[i].Row));
ui->tableWidget->setItem(row,0,rowid);
QTableWidgetItem* path=new QTableWidgetItem(Datas[i].lpszFileName);
path->setFlags(path->flags()^Qt::ItemIsEditable);
path->setToolTip(Datas[i].lpszFileName);
ui->tableWidget->setItem(row,1,path);
QTableWidgetItem* IsDirectory=new QTableWidgetItem(Datas[i].IsDirectory?"DIR":"FILE");
IsDirectory->setFlags(IsDirectory->flags()^Qt::ItemIsEditable);
ui->tableWidget->setItem(row,2,IsDirectory);
QTableWidgetItem* ParentDirectoryRecord=new QTableWidgetItem();
ParentDirectoryRecord->setData(Qt::DisplayRole,QVariant::fromValue(Datas[i].ParentDirectoryRecord));
ParentDirectoryRecord->setFlags(ParentDirectoryRecord->flags()^Qt::ItemIsEditable);
ui->tableWidget->setItem(row,3,ParentDirectoryRecord);
QTableWidgetItem* MftRecordIndex=new QTableWidgetItem();
MftRecordIndex->setData(Qt::DisplayRole,QVariant::fromValue(Datas[i].MftRecordIndex));
MftRecordIndex->setFlags(MftRecordIndex->flags()^Qt::ItemIsEditable);
ui->tableWidget->setItem(row,4,MftRecordIndex);
QTableWidgetItem* AllocatedFileSize=new QTableWidgetItem();
AllocatedFileSize->setFlags(AllocatedFileSize->flags()^Qt::ItemIsEditable);
AllocatedFileSize->setData(Qt::DisplayRole,QVariant::fromValue(Datas[i].AllocatedFileSize.toLongLong()));
ui->tableWidget->setItem(row,5,AllocatedFileSize);
QTableWidgetItem* selectPath=new QTableWidgetItem("查看");
selectPath->setFlags(selectPath->flags()^Qt::ItemIsEditable);
selectPath->setData(Qt::UserRole,Datas[i].lpszFileName);
ui->tableWidget->setItem(row,6,selectPath);
}
}
- 再绑定QScrollBar竖直滚动条的
QScrollBar::valueChanged
信号加载数据
//! 加载步长
#define LOADSTEP 500
//! 动态加载数据
connect(ui->tableWidget->verticalScrollBar(),&QScrollBar::valueChanged,this,[&](int value){
QScrollBar* bar=(QScrollBar*)sender();
//! 加载数据
if(value==bar->maximum() //! 已经滑动到最后一条数据
&& dataLists.count()>0 //! 数据源不为空
&& ui->tableWidget->rowCount()!=dataLists.count() //! 加载的数据总条数与数据源不相等
){
LoadDataBy(ui->tableWidget->rowCount(),LOADSTEP);
}
});
简单实现动态加载数据。
使用QHeaderView标题和sortItems方法排序
对数据源的排序建议直接使用qSort()函数进行排序
这里指的是使用的是QTableWidget表格控件和QHeaderView标题控件对已经加载的表格数据进行排序,
- 需要在加载数据前先启用排序
ui->tableWidget->horizontalHeader()->setSortIndicatorShown(true);
- 再绑定
QHeaderView::sortIndicatorChanged
信号
//! 标题排序
connect(ui->tableWidget->horizontalHeader(),&QHeaderView::sortIndicatorChanged,this,[&](int logicalIndex, Qt::SortOrder order){
ui->tableWidget->sortItems(logicalIndex,order);
});
实现点击标题排序,像这样:
值得注意的是:
想要单元格按数据类型排序而是不按字符串排序:
必须设置setData(Qt::DisplayRole,QVariant::fromValue(T))
而不是在创建单元格时直接赋值:
QTableWidgetItem* ParentDirectoryRecord=new QTableWidgetItem(QString::number(T));
T:是数据类型,如果需要单元格结构体排序,尝试运算符重载 (operator)
参考:
QTableWidgetItem* FileSize=new QTableWidgetItem();
FileSize->setFlags(FileSize->flags()^Qt::ItemIsEditable);
FileSize->setData(Qt::DisplayRole,QVariant::fromValue(Datas[i].FileSize.toLongLong()));
ui->tableWidget->setItem(row,5,FileSize);
使用QStyledItemDelegate实现QPushButton控件简单样式
一开始在实现 <查看 >这个单元格样式,以及鼠标移上去显示下划线这个效果时
我是直接在QTableWidget表格控件中插入的QPushButton 控件然后设置QSS样式,像这样:
QPushButton* pBut_Select = new QPushButton(ui->tableWidget);
pBut_Select->setObjectName(QString::fromUtf8("pBut_Select"));
pBut_Select->setMaximumSize(QSize(28, 35));
pBut_Select->setCursor(QCursor(Qt::PointingHandCursor));
pBut_Select->setStyleSheet(QString::fromUtf8("QPushButton\n"
"{background-color:transparent;\n"
"font: 75 14px \"\345\276\256\350\275\257\351\233\205\351\273\221\";\n"
"font-size: 14px;\n"
"color: #079F41;\n"
"line-height: 19px;\n"
"border:0px;\n"
"}\n"
"QPushButton:hover\n"
"{\n"
" font-weight: bold;\n"
" text-decoration:underline;"
"}"));
pBut_Select->setFlat(true);
pBut_Select->setText("查看");
pBut_Select->setProperty("filepath",Datas[i].lpszFileName);
connect(pBut_Select,&QPushButton::clicked,this,[&](bool check){
// QPushButton* button=(QPushButton*)sender();
// if(ISNOTNULL(button))
// Explorer_Select(button->property("filepath").toString());
});
ui->tableWidget->setCellWidget(row,6,pBut_Select);
结果发现在QTableWidget控件中动态添加QPushButton控件或者QLabel控件时加载速度非常慢,于是考虑使用QStyledItemDelegate直接绘制单元格样式。
QStyledItemDelegate类主要用于自定义表格单元格的编辑器小部件,也可以直接重写
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
事件绘制单元格。
重写QStyledItemDelegate类
要实现上面QPushButton控件的效果只需要重写
//! 事件监控
bool eventFilter(QObject *object, QEvent *event) override;
//! 重新绘制
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
/*不需要重写createEditor(),setEditorData()和setModelData()函数实现编译器功能*/
两个函数。
如果只是要在 单元格获取到焦点时 -> 绘制下划线,只需要在
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
事件中使用
if(option.state & QStyle::State_MouseOver){}
判断 鼠标是否在单元格范围内,
但是我希望在鼠标移动到 <查看 >这两个字上面的时候添加下滑线,
就需要通过重写bool eventFilter(QObject *object, QEvent *event) override;
事件,
获取鼠标的移动坐标:
bool eventFilter(QObject *object, QEvent *event)
{
/*此处获取不到 MouseButtonPress 事件*/
if (event->type() == QEvent::HoverMove && object == this->parent())
{
//! 鼠标在单元格上移动
QHoverEvent *hoverEvent = static_cast<QHoverEvent *>(event);
QTableWidget* parentW=(QTableWidget*)(this->parent());
if(parentW!=NULL && parentW!=nullptr){
QModelIndex index = parentW->indexAt(hoverEvent->pos());
if(index.isValid()){
//! 计算坐标时需要减去表格标题的高度
mouseLocal=hoverEvent->pos()-QPoint(0,parentW->horizontalHeader()->height());
if(index.column()==6)
parentW->repaint();
}
}
}
return QStyledItemDelegate::eventFilter(object, event);
}
再在void paint(QPainter *painter,const QStyleOptionViewItem &option, const QModelIndex &index) const
方法中绘制下划线
void paint(QPainter *painter,const QStyleOptionViewItem &option, const QModelIndex &index) const
{
painter->save();
painter->setPen(QColor("#079F41"));
const QRect rectangle = option.rect;
QRect boundingRect;
//! 绘制< 查看 >文本内容,并返回文本框
painter->drawText(rectangle, Qt::AlignCenter, index.data().toString(), &boundingRect);
//! 如果鼠标在文字上方 添加下划线 否则只绘制文本内容
if(boundingRect.contains(mouseLocal))
{
QPen pen = painter->pen();
pen.setStyle(Qt::SolidLine);
painter->setPen(pen);
//! 在文本框内,添加下划线
painter->drawLine(boundingRect.bottomLeft()+QPoint(0,-1),boundingRect.bottomRight()+QPoint(0,-1));
}
/* 示例 --如果对坐标边框这些不了解可以取消注释看看,一看就懂
//! 绘制文本表格
QPen pen = painter->pen();
pen.setStyle(Qt::DotLine);
painter->setPen(pen);
painter->drawRect(boundingRect.adjusted(0, 0, -painter->pen().width(), -painter->pen().width()));
//! 绘制实际单元格边框
pen.setStyle(Qt::DashLine);
painter->setPen(pen);
painter->drawRect(rectangle.adjusted(0, 0, -pen.width(), -pen.width()));
//! 绘制鼠标所选的单元格边框
pen.setStyle(Qt::SolidLine);
painter->setPen(pen);
painter->drawRect(visualRect.adjusted(0, 0, -pen.width(), -pen.width()));
//! 绘制文本表格右下点
QPen pen2 = painter->pen();
pen2.setColor(Qt::red);
painter->setPen(pen2);
painter->drawPoint(boundingRect.bottomLeft());
//绘制鼠标点 跟随鼠标移动
QPen pen3 = painter->pen();
pen3.setColor(Qt::red);
pen3.setWidth(3);
painter->setPen(pen3);
painter->drawPoint(mouseLocal);
*/
painter->restore();
再将重写后的QStyledItemDelegate类绑定到QTableWidget表格控件中,实现QPushButton控件简单样式。
TQLabelDelegate* delegate=new TQLabelDelegate(ui->tableWidget);
//! 启用事件监控
ui->tableWidget->installEventFilter(delegate);
ui->tableWidget->setItemDelegateForColumn(6,delegate);
本来还打算在实现 点击文本的时候触发点击事件,
但是QStyledItemDelegate类的
bool eventFilter(QObject *object, QEvent *event) override
获取不到 QEvent::MouseButtonPress 事件,
就只能使用QTableWidget控件自带的单元格点击事件。
QHeaderView::ResizeMode 调整单元格列宽度小技巧
QHeaderView::ResizeMode 是 Qt 框架中的一个枚举类型,它用来定义表头视图(QHeaderView)中的单元格是否可以通过用户交互来改变大小,以及如何改变大小(出至文言一心)
这个枚举有以下几个值:
- QHeaderView::Interactive:表头可以通过用户交互来调整大小。
- QHeaderView::Fixed:表头的大小是固定的,不能通过用户交互来调整。
- QHeaderView::Stretch:所有表头的空间会被拉伸以填满可用空间。
- QHeaderView::ResizeToContents:表头的大小会调整到内容的大小。
QTableWidget表格控件不存在按比例调整单元格列宽度的方法,
使用setColumnWidth修改列后,
拖拽标题单元格列宽,依旧会出现横线的滚动条。
为了横线的滚动条不出现,影响界面布局
可以只设置某一列被拉伸以填满可用空间,而其他列设置一个固定宽度:
像这样,就能实现近乎按比例缩放的效果,拉伸的那行除外。
//!固定查看栏高度
ui->tableWidget->setColumnWidth(0,96);
ui->tableWidget->horizontalHeader()->setSectionResizeMode(1,QHeaderView::Stretch);
ui->tableWidget->setColumnWidth(2,96);
ui->tableWidget->setColumnWidth(3,96);
ui->tableWidget->setColumnWidth(4,96);
ui->tableWidget->setColumnWidth(5,96);
ui->tableWidget->setColumnWidth(6,40);