主要功能概述:
最近实习在学QT,做了一个小练习,实现如下图所示的功能,在tableWidget实现表格格式设计后,使用QPainter和QPrinter实现表格的打印,功能如下图ui所示:
一、tableWidget控件功能实现
控件功能实现csdn上有很多人写过,这里做一下整合:
1、新增与删除行列
1.1新增行列:
用insertRow(int),和insertColumn(int)实现
//获取到当前有多少行
int rowCount = ui->tableWidget->rowCount();
//插入新的行
ui->tableWidget->insertRow(rowCount);
//获取当前的列数
int colCount = ui->tableWidget->columnCount();
//插入新的列
ui->tableWidget->insertColumn(colCount);
1.2删除行列:
//获取当前选中的行
int curRow = ui->tableWidget->currentRow();
//删除当前选中的行
ui->tableWidget->removeRow(curRow);
//获取当前选中的列
int curCol = ui->tableWidget->currentColumn();
//删除选中的列
ui->tableWidget->removeColumn(curCol);
2、合并与拆分单元格
2.1合并单元格
使用selectedRanges()来获得在tableWidget上多选单元格的数据,selectRanges会返回选中区域中所包含的矩形局域数组,数组中包含每个矩形区域左上顶点和右下顶点所在的行列。
值得注意的是,如果选中区域内包含了已经被合并的单元格,那每个单元格会被当成单独的矩形区域计算(原因应该是合并是一种隐藏,selectedRanges()操作的是逻辑单元格而不是可视单元格),如果要得到大的矩形区域左上顶点和右下顶点所在的行列,需要遍历Qlist获得最大最小值。具体代码如下:
QModelIndexList list = ui->tableWidget->selectionModel()->selectedIndexes();
if (list.size() < 2)
{
QMessageBox::warning(this, "单元格合并", "所选中单元格中为单个单元格,无法合并", "确定");
return;
}
int topRow = 0;
int leftCol = 0;
int bottomRow = 0;
int rightCol = 0;
QList<QTableWidgetSelectionRange> selectRanges = ui->tableWidget->selectedRanges();
if (selectRanges.size() > 0)
{
topRow = selectRanges[0].topRow();
leftCol = selectRanges[0].leftColumn();
bottomRow = selectRanges[0].bottomRow();
rightCol = selectRanges[0].rightColumn();
}
for(auto range:selectRanges)
{
if(range.topRow()<topRow)
topRow=range.topRow();
if(range.leftColumn()<leftCol)
leftCol=range.leftColumn();
if(range.bottomRow()> bottomRow)
bottomRow=range.bottomRow();
if(range.rightColumn()>rightCol)
rightCol=range.rightColumn();
}
int rowSpan = (bottomRow - topRow) + 1;
int colSpan = (rightCol - leftCol) + 1;
ui->tableWidget->setSpan(topRow, leftCol, rowSpan, colSpan);
得到要合并的区域后使用setSpan()合并,setSpan函数原型 void setSpan(int row, int column, int rowSpan, int columnSpan);其中row表示行,column表示列,rowSpan 为行跨度,columnSpan 为列跨度。
2.2拆分单元格
setSpan跨度设为1可实现拆分单元格,使用selectedItems()可以直接获得合并区域中第一个单元格所在的行列。
int row,col;
QList<QTableWidgetSelectionRange> selectRanges = ui->tableWidget->selectedRanges();
if (selectRanges.size() < 2)
{
QMessageBox::warning(this, QString::fromLocal8Bit("拆分表格失败"), "单元格已是最小单位,不能再进行拆分", "确定");
return;
}
QList<QTableWidgetItem*> selectItems = this->ui->tableWidget->selectedItems();
if(selectItems.size()==0)
{
QMessageBox::warning(this, QString::fromLocal8Bit("拆分表格失败"), "请先为表格设置元素item", "确定");
return;
}
if(selectItems.size()>1)
{
QMessageBox::warning(this, QString::fromLocal8Bit("拆分表格失败"), "非法选择", "确定");
return;
}
for(auto item:selectItems)
{
row= item->row();
col=item->column();
}
ui->tableWidget->setSpan(row, col, 1, 1); // 设置跨度为1
3、行列表头设置
将行列表头设为可见:
ui->tableWidget->verticalHeader()->setVisible(true);//行头数字列是否可见
ui->tableWidget->horizontalHeader()->setVisible(true);//列头数字列是否可见
列表头设为所有单元格可拉伸,且最后一格会自动填满tableWidget剩余区域
ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
4、最终效果
二、将上述格式的表格打印到PDF
1、打印设置:
打印用了QPrinter,用了调用打印预览窗打印和直接打印,在QPrinter里面可以进行一些打印设置,不如打印分辨率,纸张大小,纸张朝向等:
//调打印预览窗
void Dialog::printWithPreview()
{
QPrinter printer(QPrinter::ScreenResolution);
printer.setPageSize(QPrinter::A4);
printer.setOrientation(QPrinter::Portrait);
QPrintPreviewDialog preview(&printer);
connect(&preview,SIGNAL(paintRequested(QPrinter*)),this,SLOT(printDocument(QPrinter*)));
preview.setWindowState(Qt::WindowMaximized);
preview.exec();
}
//直接打印
void Dialog::printDirect()
{
QPrinter printer(QPrinter::ScreenResolution);
printer.setPageSize(QPrinter::A4);
QPrintDialog printDialog(&printer);
printer.setOrientation(QPrinter::Portrait);
if(printDialog.exec()==QDialog::Accepted)
{
printDocument(&printer);
}
}
槽函数printDocument(QPrinter *printer)里设置画笔颜色与打印纸张页数,并调用画图函数
void Dialog::printDocument(QPrinter *printer)
{
QPainter painter;
painter.begin(printer);
painter.setPen(Qt::black);
for(int i=0;i<pageNum;i++)
{
drawTable(&painter);
qDebug()<<"正在打印"<<endl;
if(i!=pageNum-1)
{
printer->newPage();
painter.setPen(Qt::black);
}
}
drawTable(&painter);
painter.end();
}
2、绘表:
在绘表函数drawTable里我们要对上面我们设置过格式的单元格进行绘制,格式设置包括合并单元格,单元格长宽比的变化等。
2.1获得打印单元格的长宽
根据控件上的单元格长宽比获得打印时每个单元格的大小,并分别用 QList<int> m_rowHeight; QList<int> m_colWidth;保存。
//此函数根据控件计算单元格长宽比
void Dialog::countRC()
{
int m_w;
int m_h;
int temp_cout=0;
int num=1;
m_w=ui->tableWidget->horizontalHeader()->size().rwidth();
m_h=ui->tableWidget->verticalHeader()->size().rheight();
//qDebug()<<m_w;
//qDebug()<<m_h;
for(int y=0;y<m_h;y++)
{
temp_cout=ui->tableWidget->rowAt(y);
if(temp_cout==num)
{
m_rowHeight.append(y);
num++;
}
if(temp_cout==-1)
{
m_rowHeight.append(y);
break;
}
}
//m_rowHeight.append(m_h);
temp_cout=0;
num=1;
for(int x=0;x<m_w;x++)
{
temp_cout=ui->tableWidget->columnAt(x);
if(temp_cout==num)
{
m_colWidth.append(x);
num++;
}
}
m_colWidth.append(m_w);
//计算每一格在tabWidget的长宽
for(int i=m_rowHeight.size()-1;i>0;i--)
{
temp_cout=m_rowHeight.at(i)-m_rowHeight.at(i-1);
m_rowHeight[i]=temp_cout;
}
for(int i=m_colWidth.size()-1;i>0;i--)
{
temp_cout=m_colWidth.at(i)-m_colWidth.at(i-1);
m_colWidth[i]=temp_cout;
}
//归一化,换算为在m_maxWeight下的宽与高
double m_rate=double(m_maxWeight)/m_w;
for(int i=0;i<m_rowHeight.size();i++)
{
m_rowHeight[i]=m_rowHeight.at(i)*m_rate;
}
for(int i=0;i<m_colWidth.size();i++)
{
m_colWidth[i]=m_colWidth.at(i)*m_rate;
}
}
2.2绘制
使用矩形画图来绘制每个单元格,由于合并,一些单元格被隐藏,我们首先要获得需要打印的单元格,将单元格是否要打印存在Qlist<int>draw中。对于要打印的单元格,根据其跨度确定他的长宽,最终打印函数如下:
其中,m_startx,m_starty为表格起始绘制坐标,m_maxWeight是表格的最大宽。
void Dialog::drawTable(QPainter *painter)
{
m_colNum=ui->tableWidget->columnCount();
m_rowNum=ui->tableWidget->rowCount();
m_colWidth.clear();
m_rowHeight.clear();
countRC();
//根据当前表生成绘图数组
QList<bool> draw;
for (int i=0;i<m_rowNum*m_colNum;i++)
{
draw.append(true);
}
for(int i=0;i<m_rowNum;i++)
{
for(int j=0;j<m_colNum;j++)
{
if(draw[i*m_colNum+j]==false) continue;
else
{
for(int ii=0;ii<ui->tableWidget->rowSpan(i,j);ii++)
for(int jj=0;jj<ui->tableWidget->columnSpan(i,j);jj++)
{
draw[(i+ii)*m_colNum+j+jj]=false;
}
draw[i*m_colNum+j]=true;
}
}
}
painter->setRenderHint(QPainter::Antialiasing, true);
int startx=m_startx;
int starty=m_starty;
for(int i=0;i<m_rowNum;i++)
{
for(int j=0;j<m_colNum;j++)
{
if(draw[i*m_colNum+j]==true){
//qDebug()<<"hai";
int m_rowSpan=0;
int m_colSpan=0;
for(int k=0;k<ui->tableWidget->rowSpan(i,j);k++)
{
m_rowSpan+=m_rowHeight.at(i+k);
}
for(int k=0;k<ui->tableWidget->columnSpan(i,j);k++)
{
m_colSpan+=m_colWidth.at(j+k);
}
painter->drawRect(startx,starty,m_colSpan,m_rowSpan);
}
startx+=m_colWidth.at(j);
}
starty+=m_rowHeight.at(i);
startx=m_startx;
}
}