搜档网
当前位置:搜档网 › QT表格打印

QT表格打印

最简单的,使用Qt支持的HTML标记写文档即QTextEdit所支持的格式来直接打印。
也可以考虑使用第三方报表设计软件。
比如我自己用的是XSL-FO方式来设计报表然后转换成PDF来打印。
XSL-FO实际上是一个XML文件,将XSL-FO再使用Apache FOP工具进行转换,转换成PDF文件就能实现浏览和打印了。当然了,如果有钱的话可以购买商业版的一些转换、开发、打印工具,使之能直接浏览、打印XSL-FO文件而不必转换为PDF。
本质上讲,将内容画在屏幕上 和 画在打印机上 是一样的,
但 打印机上的坐标 是 实实在在的 厘米,英寸,
还有 需要换页,
这就需要你在画的时候 计算好大小。
有几个有用的函数
setOrientation() 打印机定向
setPaperSize() 设置纸张大小
setResolution() 设置 DPI
newPage() 新的一页
setNumCopies() 打印多少份
Qt里面转换成html格式方便打印,可以参见<>第8章打印部分
楼上说的 第八章 是个很好的参考。
还有一个 有用的链接 https://www.sodocs.net/doc/863481129.html,/4.5/qprinter.html
qt中简单的打印过程
1、新建一个qprintviewdialog对象p。
2、connect对象p的printrequisition 信号到自己的一个函数如doprint(*printer)
3、实现doprint函数,就是在里面使用qpainter(print)新建一个painter后直接在里面画就好了。
4、p.exec()
这种方式是显示qt提供的打印预览对话框,该对话框需要你提供printrequisition信号的实际实现。当p需要预览或打印时就会发出该信号,你只要实现好doprint函数即可,别的qt都为你做好了。
需要注意的是:
1、在第三步中当调用painter.begin后默认就会创建一个空白页,所以一开始不需要newpage,直接画就是了;
2、在一页画完以后直接调用print.newpage创建新页面
3、直到所有页面画完后才可以调用painter.end().
4、qt提供的qprintviewdialog对话框已经包含了所有有关打印设置的功能,如需定制可以在新建p之前调用printdialog对话框来获取打印机,将其传给p的构造函数即可。
5、printer对象的pagerect返回的是去掉页边距后的矩形;paperrect返回的是纸张大小(单位可以设置)
6、这种方式不能适合多种打印机,也就是说你设计好了能工作了可能换到针式打印机就不一样了。因为不同打印机支持的纸张及打印分辨率不同。
解决方法推荐:
建议首先在A4纸张和300或600分辨率下设计,程序中设置一个变量deltaW=程序运行时打印机中的纸张宽度*程序运行时打印机分辨率/(A4纸宽度*300或600),然后在所有表示坐标.x或大小.w的数字上乘以deltaW即可。同样的也可以设置垂直缩放比例。
注意是所有的,包括字体大小、及绘图中的各种坐标。这样不管用什么纸张在什么打印机下都可以完全显示了(打出来会是整体放大或缩

小)。如果你能预先知道客户机上的打印环境的话,就不必这一步了。
Qt QTableView另类打印解决方案
https://www.sodocs.net/doc/863481129.html,/woshixingaaa/article/details/5789582
分类: Qt GUI 2010-08-05 08:47 247人阅读 评论(2) 收藏 举报
Qt QTableView另类打印解决方案
上回书说道Qt的model/view,我就做了个demo用于显示数据库中的内容。没想到tableview的打印竟然成了问题。我困惑了,难道Qt不应该提供一个print函数给tableview吗?这是最最常用的功能啊。
Google了半天,也没什么有用的结果。看到Qt labs有一篇blog,叫“All You Need is a little Polish”,里面给出了最新的spreadsheet demo中的tableview print代码。还挺高兴,原来已经可以解决了。后来试了一下,发现根本不是那么回事,这个例子只能打印tableview当前显示的区域,对于分页、页眉、页脚、等等都不支持。原来写这个demo的并不是真正的开发人员,只是support team的。
作为10年的老MFCer,我有着厚脸皮的DIY精神(没办法,被逼的太多次了,每次只能自己来)。相信以我在MFC中写Doc/View printing的经验,搞定Qt的打印还是没问题的。但是在跳进代码海之前,我花了几个小时仔细研究了一下QTableView的代码,发现我可以选下面几个实现方案。
1. 鸵鸟方案
学过操作系统的Tx都知道这意味着什么样的方案。上面提的那篇Blog里的方法可以算一种喽。如果你打印的table每次都是在1页内的话,可以用这个方案。但是作为10年英明神武的老MFCer,实在没脸这样做。
2. model方案
这个方案是从model中取得所有table的数据,然后使用2个for循环将这些数据以行和列打印出来。在qt-app网站上有人给出了这个解决方案(https://www.sodocs.net/doc/863481129.html,/content/show.php/TableView+Printer?content=76616)。我看了代码,作者在打印每页时从model中取数据,然后在内存中生成一个graphicview,然后用graphicview打印。也实现分页等功能。但打印的效果不好,增加的边框感觉也很别扭。
还有人建议将model中的数据导入至QTextDocument中,然后利用QTextDocument稍微强大一点打印功能打印。
总之,所有这些方案实现起来比较麻烦,而且只从model中取数据,忽略了view(delegate)中显示相关的因素(如文字render的格式,是否被选中等等)。还有一点就是分页总是成问题。即使纵向分页对了,横向的分页还是没看到相应的实现。(大家都注意到在Excel中一页横向打不下时会将未打印的内容打印至下一页吧)
3. delegate方案
在QTableView代码中,真正render每个item时,还是使用delegate的。所以有人建议使用delegate在2个for循环中将这些数据以行和列打印出来。这个貌似是最好的解决方案了。如果我是Qt开发人员的话,我会首

选这个方案的。
4. 我的方案
可惜我不是Qt的开发人员(不知Nokia这种企业氛围如何)。个人感觉Qt的风格是沉稳,外加一丝灵动。所以我的方案可能不是Qt开发人员喜欢的。即使我个人也觉得这个方案难登大雅之堂。只是在时间和精力都有限的情况下,借助了Qt的一点Q,想出来的一个非常tricky的办法。
其实我的方案就是:鸵鸟方案的升级版。当当当。。。
我的灵感来源于上面Bolg中鸵鸟方案的4句代码:
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
resize(printer->width(), printer->height());
render(printer);
前面2具隐藏scrollbars,第3句,让view与print的纸张大小相同,最关键的第4句,直接将view render到printer中。
作为一个10年的老MFCer,我坚信Qt在代码复用性方面应该更胜一筹。既然view可以直接render到printer中(感谢Qt中painter和printer的设计),那么为什么还需要去写delegate的paint呢?所以,我的方案出发点就是从怎么复用render函数去解决print问题。
方案成功了吗?心急的Tx请先看下面的图片。

打印预览、横向分页、纵向分页、背景色、选中标记、页眉、页脚。。。。。。该有的都有了。从单页到上百页我都测试过。那么我的鸵鸟升级方案是怎么实现的呢?
其实非常简单,还是基于view的render函数,既然view当前显示的内容可以直接render到printer中,那么我就模拟给它翻页,然后 render每页不就行了。最初的办法是使用QScrollBar的setValue对横向和纵向进行翻页。就是打完第1页之后,翻到第2页,再打印,以此类推。但最后一页很难处理,如果最后一页的内容只有几行的话,是没法继续向下翻得,造成的结果就是倒数第2页很多打印过的行会再次被打印出来。难道没办法了。
一筹莫展之际,只能继续翻Qt的文档,当看到tableview中setRowHidden和setColumnHidden这2个函数时,我一刹那被Carmack大仙(我的偶像)灵魂附体。这不就解决了吗?
什么?你还不明白?
打印完第1页时,将这页打印过的row隐藏,然后打印下一页。打印完纵向分页后,将横向打印过的colum隐藏。再次打印纵向分页(从而实现横向分页)。
剩下的就是数学了。。。
大部分问题都解决了。我写了个QTableView的派生类MyTableView,里面一些打印相关的代码用到了David Johnson david@https://www.sodocs.net/doc/863481129.html,的textprinter中某些代码,感谢开源社区。我的MyTableView类可以实现下面的功能:
1. WYSIWYG打印/打印预览,这意味着每个item的背景颜色、文字颜色、显示属性、选择状态、边框、表头。。。。。。所有你看到tableview显示的,都可以原封不动直接打印出来;
2. 纵向分页,当在一页末

尾某行没打印全时,会自动在下一页再次打印;
3. 横向分页,当在一页右侧某列没打印全时,会自动在下一次横向分页时再次打印;
4. 打印页眉
5. 打印页脚
6. 打印边距
7. 打印成PDF文件
关于QTableWidget表格打印的问题:
小弟目前正在做QTableWidget表格打印,万事俱备,但出了点小问题:
画出来的QTableWidget比页面要大,导致有些数据跑到外面去了,不知道怎么办才好?
小弟也用过printTable->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);之类的东西,但是达不到想要的那种效果啊
void QCurveQueryTool::printview() //printFlag =2 , 打印预览
{
QPrinter printer(QPrinter::ScreenResolution);
QPrintPreviewDialog preview(&printer, this);
preview.setMinimumSize(1000,900);
connect(&preview, SIGNAL(paintRequested(QPrinter *)),this, SLOT(printTable(QPrinter *)));
preview.exec();
}
void QCurveQueryTool::printTable(QPrinter *printer)
{
QString title = QString("曲线打印");
printTableWidget(table,title,printer);
}
void QCurveQueryTool::printTableWidget(const QTableWidget* view,QString stitile,QPrinter *printer) //printFlag =1,打印表格
{
double totalWidth = 0.0;
double totalHeight = 0.0;
double totalPageHeight=0.0;
int rows = view->model()->rowCount(); //行总数
const int cols = view->model()->columnCount(); //列总数
for (int c = 0; c < cols; ++c) //求出列宽
{
totalWidth += view->columnWidth(c);
}
for (int r = 0; r < rows; ++r) //求出行宽
{
totalHeight += view->rowHeight(r);
}
QPainter painter(printer);
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
painter.begin(printer);
QRect area = printer->paperRect();// paperRect();
QVector startRow;
QVector endRow;
int iCount = 0;
qreal left,top,right,bottom;
printer->getPageMargins(&left,&top,&right,&bottom,QPrinter::Point);
cout << "left = " << left << ", right = " << right << ", top = " << top << ", bottom = " << bottom << endl;
area.setHeight( int(area.height() - top - bottom) );
area.setWidth( int(area.width() - left - right) );
for (int p=0; p < rows; p++) //rows中保存了所有的行数 , 找出每次打印的开始行和结束行
{
totalPageHeight += view->rowHeight(p); //求出所有的列高度
int pageFooter = 50 ; //页脚的高度
int pageHeader = 50; //页眉的高度
if (startRow.size() == 0)
{
pageFooter = 50 ;
pageHeader = 500;
}
if ((totalPageHeight >= (area.height() - pageFooter - pageHeader)) || (p == rows -1)) //如果目前累加列的高度大于或者等于可用页面高度 || 到达最后一行
{
totalPageHeight = view->rowHeight(p);
if (p == rows -1)
{
if (p - iCount - 2 < 0)

{
startRow.push_back(0);
}
else
{
startRow.push_back(p - iCount - 2);
}
endRow.push_back(p);
//cout << startRow.at(startRow.size() - 1) << " " << endRow.at(endRow.size() - 1) << endl;
}
else
{
if (p - iCount - 2 < 0)
{
startRow.push_back(0);
endRow.push_back(p - 2);
}
else
{
startRow.push_back(p - iCount - 1);
endRow.push_back(p - 3);
}
//cout << startRow.at(startRow.size() - 1) << " " << endRow.at(endRow.size() - 1) << endl;
}
iCount = 0;
}
iCount++;
}
QTableWidget *printTable = new QTableWidget();
//printTable->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
//printTable->horizontalHeader()->setDefaultAlignment(Qt::AlignCenter);
//printTable->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
//printTable->verticalHeader()->setDefaultAlignment(Qt::AlignCenter);
const double xscale = area.width() / totalWidth;
const double yscale = area.height() / totalHeight;
//const double pscale = area.height() / totalPageHeight;
//painter.scale(xscale , yscale); // With the scaling 3 It is Printing all
//painter.translate(area.x() + xscale, area.y() + pscale); //This is original
painter.translate(area.x() + xscale, area.y() + yscale);
//painter.save(); //commented
//int y=0 ;// This is introduced for the columns
//view->paintEvent(&event);
//QPainter paint(this);
//paint.setPen(Qt::red);
//paint.drawRect(0, 0, 0, 0);
QStyleOptionViewItem option;
QPen pen;
pen.setColor(Qt::black);
pen.setWidth(1);
painter.setPen(pen);
int irowcount = -1;
float rate = 1;
for(int ipage = 0; ipage < startRow.size();ipage++) //startRow有大,就有多少页需要打印
{
printTable->setColumnCount(view->columnCount() + 1); //打印的列数为view的列数
printTable->setRowCount(endRow[ipage] - startRow[ipage] + 2); //设置当前打印页的行数
for(int c = 1;c < cols + 1;c++)
{
printTable->setColumnWidth( c,(view->columnWidth(c - 1)) );
QTableWidgetItem *newItem = printTable->item(0,c);
if (!newItem) //如果printTable的首行不存在,添加文字或者序号
{
if (view->horizontalHeaderItem(c - 1)) //Returns the horizontal(水平的) header item for column
newItem = new QTableWidgetItem(view->horizontalHeaderItem(c - 1)->text());
else
newItem = new QTableWidgetItem(QString::number(c));
printTable->setItem(0,c ,newItem);
}
else //否

则,直接设置文字或者序号
{
if (view->horizontalHeaderItem(c - 1))
newItem->setText(view->horizontalHeaderItem(c - 1)->text());
else
newItem->setText(QString::number(c));
}
}
/////////////////////////求出合适的比率,用来完美表格的显示
QRect firstTable , lastTable;
QModelIndex firstIndex = printTable->model()->index(0,0);
QModelIndex lastIndex = printTable->model()->index(0,cols);
firstTable = printTable->visualRect(firstIndex);
lastTable = printTable->visualRect(lastIndex);
float totalTableWidth = lastTable.topRight().x() - firstTable.topLeft().x();
float pagerWidth = printer->paperRect().width() - 20 - 20;
rate = totalTableWidth/pagerWidth;
cout << "totalTableWidth = " << totalTableWidth << ", pagerWidth = " << pagerWidth << ", rate = " << rate << endl;
/////////////////////////求出合适的比率,用来完美表格的显示
for(int c = 1;c < cols + 1;c++) //重设宽度
{
printTable->setColumnWidth( c,int((view->columnWidth(c - 1))/rate) );
}
printTable->setColumnWidth(0,view->verticalHeader()->width()); //Returns the table view's vertical header.
int iRow = 0;
for(int ir = startRow[ipage]; ir <= endRow[ipage];ir++) //ir变量用来标识当前打印的是哪些行,设置当前打印页的首行
{
printTable->setRowHeight(iRow,view->rowHeight(ir)); //Sets the height of the given row to be height.
for(int ic = 0; ic < view->columnCount(); ic++) //设置每个单元格的内容
{
QTableWidgetItem *newItem = printTable->item(iRow + 1,ic + 1);
if (!newItem)
{
if (view->item(ir,ic))
newItem = new QTableWidgetItem(view->item(ir,ic)->text());
else
newItem = new QTableWidgetItem("");
printTable->setItem(iRow + 1,ic + 1,newItem);
}
else
{
if (view->item(ir,ic))
newItem->setText(view->item(ir,ic)->text());
else
newItem->setText("");
}
}
iRow++;
}
qreal ioffx = (area.width() - totalWidth)/2;
qreal ioffy = 50;
if (ipage == 0)
{
ioffy = 500;
QwtPlotPrintFilter filter;
int options = QwtPlotPrintFilter::PrintAll;
options &= ~QwtPlotPrintFilter::PrintBackground;
options |= QwtPlotPrintFilter::PrintFrameWithScales;
filter.setOptions(options);
d_plot->print(&painter,QRect(50,65,650,400),filter);
}
rows = printTable->rowCount();
iRow = 1;
for(int ir = startRow

[ipage] ; ir <= endRow[ipage] ; ir++) //ir变量用来标识当前打印的是哪些行,设置当前打印页的首列序号
{
QTableWidgetItem *newItem = printTable->item(iRow,0);
if (!newItem)
{
if (view->verticalHeaderItem(ir))
newItem = new QTableWidgetItem(view->verticalHeaderItem(ir)->text());
else
newItem = new QTableWidgetItem(QString::number(iRow));
printTable->setItem(iRow,0,newItem);
}
else
{
if (view->verticalHeaderItem(ir))
newItem->setText(view->verticalHeaderItem(ir)->text());
else
newItem->setText(QString::number(iRow));
}
iRow ++;
}
QPointF offPt(20,ioffy); //20表示边框的空格
for (int r = 0; r < rows; r++)
{
QRect rt1,rt2;
QModelIndex idx1 = printTable->model()->index(r,0);
QModelIndex idx2 = printTable->model()->index(r,cols);
rt1 = printTable->visualRect(idx1);
rt2 = printTable->visualRect(idx2);
painter.drawLine( (rt1.bottomLeft() + offPt) , (rt2.bottomRight() + offPt) );
if (r == 0)
{
painter.drawLine( (rt1.topLeft() + offPt) , (rt2.topRight() + offPt) );
}
}
for (int c = 0; c < cols + 1; c++)
{
QRect rt1,rt2;
QModelIndex idx1 = printTable->model()->index(0,c);
QModelIndex idx2 = printTable->model()->index(rows - 1,c);
rt1 = printTable->visualRect(idx1);
rt2 = printTable->visualRect(idx2);
painter.drawLine( (rt1.topLeft() + offPt) , (rt2.bottomLeft() + offPt) );
if (c == cols)
{
painter.drawLine( (rt1.topRight() + offPt) , (rt2.bottomRight() + offPt) );
}
}
for (int r = 0; r < rows; r++)
{
irowcount ++;
for(int c = 0; c < cols + 1; c++)
{
QModelIndex idx = printTable->model()->index(r,c);
option.rect = printTable->visualRect(idx);
if (printTable->item(r,c))
{
QRectF rt(option.rect.left() + 3,option.rect.top(),option.rect.width(),option.rect.height());
rt = rt.translated(offPt);
if (r != 0 && c == 0)
{
painter.drawText(rt,Qt::AlignCenter,QString("%1").arg(irowcount));
}
else
{
if (r == 0)
{
painter.drawText(rt,Qt::AlignCenter,printTable->item(r,c)->text());
}
else
{
painter.drawText(rt,Qt::AlignVCente

r,printTable->item(r,c)->text());
}
}
}
}
}
//ipage
if (ipage == 0) //设置标题
{
QRectF rttt(area.left(),area.top(),area.width(),50);
painter.drawText(rttt,Qt::AlignCenter,stitile);
}
QRectF rttt(area.left(),area.bottom() - 65,area.width(),25);
painter.drawText(rttt,Qt::AlignCenter,QString::number(ipage + 1));
if (ipage < startRow.size() - 1)
{
printer->newPage();
irowcount--;
}
}
painter.end();
}

相关主题