因工作需要,用QT写了一个类似Excel的程序,其中有一条需求“选中单元格右下角下拉数字内容递增,文字复制的功能“,本人查找资料有幸找到先行者的资料,通过查看代码做了一些修改,因为先行者使用了QTableWidget ::setItemDelegate委托,对单元格进行一些画线操作,我这边基础功能已经写完,画线是通过重写paintEvent(处理绘图事件),并没有使用ItemDelegate委托,所以对代码进行了一些调整 ,希望对大家有一些帮助
先上效果图:
![](https://i-blog.csdnimg.cn/blog_migrate/4f8dc9b292d1972f67c84194b5808b46.gif)
上图就是成平效果,数字可以自增,文本内容复制,如果文本开头有数字、或者末尾数字,也其中数字进行递增。
下面我们来分析实现该功能的具体实现方法:
1.选中单元格外观绘制:重写了QTableWidget类 ,对重写的QTableWidget类的paintEvent(处理绘图事件)函数进行改造
2.其次我们则是要对鼠标外观进行分析设置,要实现鼠标外观只有在移到选中框右下角矩形时才会变成特定的样式,则需要在鼠标点击时,获取鼠标点击的框的位置以及长宽用来计算出右下角矩形的位置,当鼠标移到计算位置的区域时,则鼠标样式改变。获取点击框的位置以及长宽通过选中单元格的QRect类进行获取。
3.当鼠标移到右下角矩形位置以及按下鼠标左键,进行移动则可将选中框内容进行自增、复制等操作
————————————————
QTableWidget类重写 ,重写paintEvent方法,进行单元格绘画操作
重点!重点!重点!
最后需要加上这行代码!this->viewport()->update();
我自己项目的paintEvent代码里面 有写了绘制单元格网格线,边框线,对角线等各种绘制操作,如果不添加这行代码,绘制的操作并不会事实刷新。想深入了解的可以翻译一下资料,QT窗口与视口的关系。这里就不深入讲了
废话不多说,上paintEvent代码!
void DBTableWidget::paintEvent(QPaintEvent *e)
{
QTableWidget::paintEvent(e);
//绘制网格线
QPainter painter(viewport());
//保存QPainter对象
painter.save();
QPen pen = painter.pen();
pen.setWidth(1);
for (int i=0;i<rowCount();i++)
{
for (int j =0 ;j < columnCount();j++)
{
if(this->item(i,j) == NULL)
return;
QModelIndex modelIndex = indexFromItem(this->item(i,j));
//QRect类提供各种矩形坐标,绘制线跟点的时候需要用到坐标
QRect rect = visualRect(modelIndex);
QRect tmpRect = rect;
//如果是选中状态 并且在选择公式状态
if (this->item(i,j)->isSelected())
{
//给选中单元格进行画线画点
QVector<qreal>dashes;
QPen penXu = painter.pen();
//设置画笔宽度
penXu.setWidth(2);
QColor color;
//设置画笔颜色
color.setRgb(31,187,125);
penXu.setColor(color);
painter.setPen(penXu);
//绘制单元格四周的线
painter.drawRect(tmpRect);
//绘制单元格右下角点
penXu.setWidth(6);
painter.setPen(penXu);
painter.drawPoint(tmpRect.x()+tmpRect.width() -3,tmpRect.y()+tmpRect.height()-3);
}
}
}
//恢复QPainter对象
painter.restore();
this->viewport()->update();
}
2.重写mouseMoveEvent鼠标移动捕获函数、mousePressEvent鼠标按下捕获函数、.mouseReleaseEvent鼠标按下后松开捕获函数
重点!重点!重点!mouseMoveEvent函数 需要在DBTableWidget 构造函数里面添加代码:
this->setMouseTracking(true);
在Qt中要捕捉鼠标移动事件需要重写MouseMoveEvent,但是MouseMoveEvent为了不太耗资源,默认状态下是要鼠标按下才能捕捉到。要想鼠标不按下时的移动也能捕捉到,需要setMouseTracking(true)。
1.重写鼠标移动事件
//鼠标移动事件
void DBTableWidget::mouseMoveEvent(QMouseEvent *event)
{
//获取鼠标位置信息
QPoint mousePos = event->pos();
//获取所有选中单元格
QList<QTableWidgetItem*> itemList = this->selectedItems();
//没有选中单元格 就退出
if (itemList.size() <= 0)
return;
QModelIndex modelIndex = indexFromItem(itemList[itemList.size()-1]);
//获取最后一个选中的单元格的QRect,用来判断是否鼠标位置是否在右下角区域
QRect rect = visualRect(modelIndex);
//判断是否在我们规定的区域,或者是按下模式,isClick是按下模式标志
if((mousePos.rx() >= (rect.x()+rect.width() -7) && mousePos.rx() <= (rect.x()+rect.width())
&& mousePos.ry() >= (rect.y()+rect.height()-7) && mousePos.ry() <= (rect.y()+rect.height()))
|| m_isCellBottomRightClick )
{
//设置鼠标在右下角区域样式
this->setCursor(Qt::SizeAllCursor);
//在右下角区域
m_isCellBottomRight = true;
//是否是按下拖拽模式
if (m_isCellBottomRightDrag)
{
//如果是按下拖拽模式,则对当前选中的item进行数据设置,本人此处ToAddString函数则是递增函数
if (itemList.size() > 1)
{
QString strText = itemList[itemList.size()-2]->text();
bool isNum = false;
double val = strText.toDouble(&isNum);
QString strTextLast;
//如果是数字加1 否则复制文本
if (isNum)
strTextLast = QString::number(val+1);
else
{
//查看是否末尾是数字
QRegExp rx("\\d+$");
rx.indexIn(strText, 0);
QString end_num = rx.cap(0);
//查看是否最前面是数字
QRegExp rx1("^\\d+");
rx1.indexIn(strText, 0);
QString start_num = rx1.cap(0);
if (!start_num.isEmpty())
strText.replace(start_num, QString::number(start_num.toInt()+1));
else if (!end_num.isEmpty())
{
int strIndex = strText.lastIndexOf(end_num);
QString strMid = strText.mid(0,strIndex);
strText = strMid + QString::number(end_num.toInt()+1);
}
strTextLast = strText;
}
//这边撤销恢复方法需要记录的修改过的单元格 你们可以直接掠过
//if (strTextLast != itemList[itemList.size()-1]->text())
// {
// CellText info;
// info.row = itemList[itemList.size()-1]->row();
// info.col = itemList[itemList.size()-1]->column();
// info.strStart = itemList[itemList.size()-1]->text();
// info.strEnd = strTextLast;
// m_cellCellBottomRightDragContainer.cellInfos.push_back(info);
//设置值
// itemList[itemList.size()-1]->setText(strTextLast);
}
}
}
}
else
{
//不在右下角矩形范围内
m_isCellBottomRight = false;
//范围之外变回原来形状
this->setCursor(Qt::ArrowCursor);
}
//提交鼠标给QTableWidget控件,否则QTableWidget控件监测不到鼠标后以上代码则做无用功
QTableWidget::mouseMoveEvent(event);
}
2.重写鼠标点击事件
//鼠标点击事件
void DBTableWidget::mousePressEvent(QMouseEvent *event)
{
//确保按下的是鼠标左键且是在选中框右下角
if(event->button()==Qt::LeftButton && m_isCellBottomRight)
{
//设置为鼠标样式,防止鼠标在拖动的过程中由于鼠标样式改变,导致数据设置失败
this->setCursor(Qt::SizeAllCursor);
//是左键按下模式
m_isCellBottomRightClick = true;
//确保当前按下鼠标左键选中的单元格有内容,否则拖拽自增数据无效
if(!this->currentIndex().data().isNull() && this->currentIndex().data().toString().size() > 0)
m_isCellBottomRightDrag = true;
}
else
{
//不是左键按下模式
m_isCellBottomRightClick = false;
this->setCursor(Qt::ArrowCursor);
}
QTableWidget::mousePressEvent(event);//提交鼠标给控件
}
3.重写鼠标释放事件
//鼠标释放触发
void DBTableWidget::mouseReleaseEvent(QMouseEvent *ev){
//格式刷功能 直接跳过
//if (m_isFormatting)
//{
//代表鼠标左键松下
// if(ev->button()==Qt::LeftButton){
// emit formattingCloseSignal();
// setFormatting(false);
// }
}
//松开之前是鼠标在单元格右下角区域按下模式,释放各种参数
if(m_isCellBottomRightClick)
{
m_isCellBottomRightClick = false;
m_isCellBottomRightDrag = false;
m_isCellBottomRight = false;
//设置鼠标样式
this->setCursor(Qt::ArrowCursor);
QList<QTableWidgetItem*> itemList = this->selectedItems();
//将当前选择的单元格还给拖拽前点击的单元格
if (itemList.size() > 0)
this->setCurrentItem(itemList[0]);
if (m_cellCellBottomRightDragContainer.cellInfos.size() > 0)
{
QVariant variant = QVariant::fromValue(m_cellCellBottomRightDragContainer);
emit eventSignal(variant);
m_cellCellBottomRightDragContainer.cellInfos.clear();
}
}
//提交鼠标给控件
QTableWidget::mouseReleaseEvent(ev);
}
至此功能就写完啦,希望可以帮助需要的人,有什么不懂可以评论区留言,我会及时回复大家!
参考文档:https://blog.csdn.net/REDPINGPING/article/details/118182278