我需要将 QGraphicsScene 打印到实际 (inch/mm) 比例
I need to print a QGraphicsScene to actual (inch/mm) scale
我正在尝试打印 QGraphicsScene
的内容。目标打印机可以是任何东西——从普通打印机到自定义尺寸的特殊打印机。它必须以实际尺寸(英寸、毫米......)打印东西。
在 QGraphicsScene
中,我使用 72 ppi 的假设。
我假设:
1) 将场景渲染到打印机会根据打印机分辨率进行,这样我就可以得到实际大小 (inches/mm) 的项目,类似于它们在屏幕上显示的内容。
2) 我可以将打印机的纸张尺寸设置为所需的 canvas 尺寸(这是一个非常大的场景中的矩形),超出它就不会打印
3)我可以设置边距,"actual canvas"以外的内容不会打印,包括边距上的内容。
到目前为止我所有的假设都是错误的:
1) 对于不同的打印机,如果我建议自定义尺寸接近其默认纸张尺寸(或者如果我不设置纸张尺寸),渲染似乎是最大适合(使用宽高比);
如果我设置了一个不接近的纸张尺寸(比如打印机上的默认 "LETTER" 尺寸为 4x4 英寸),它只会打印一张空白页。
2-3) 在有打印的情况下,打印机只是将 canvas 拉伸到整页,绘图区域外的任何项目仍会打印。
我尝试在 painter 上或通过在渲染器上设置目标矩形进行剪辑,结果是场景的一小部分非常奇怪的剪辑。
我试过 HP LaserJet、Adobe PDF 和一些特定尺寸(如 4x6 英寸)的自定义打印机。它们都根据我指定的纵向或横向将场景缩放到最大尺寸,而完全忽略我的纸张尺寸要求或实际尺寸。
这是一个小示例程序,可以重现我正在尝试做的事情。
代码中的注释显示了我尝试过的一些选项。
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QGraphicsEllipseItem>
#include <QPrinter>
#include <QPrintDialog>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGraphicsScene* s = new QGraphicsScene();
s->setSceneRect(-500, -500, 1500, 1500);
QGraphicsView* view = new QGraphicsView();
view->setScene(s);
view->show();
int canvasSize = 288; // 4 in
QRectF canvasRect(0, 0, canvasSize, canvasSize);
// this is to show actual scene
QGraphicsRectItem* sss = new QGraphicsRectItem(canvasRect);
sss->setBrush(Qt::blue);
s->addItem(sss);
// this item is partially outside top left
QGraphicsEllipseItem* e1 = new QGraphicsEllipseItem(-50, -75, 100, 150);
e1->setBrush(Qt::yellow);
s->addItem(e1);
// this item is partially outside center
QGraphicsEllipseItem* e2 = new QGraphicsEllipseItem(100, 150, 250, 50);
e2->setBrush(Qt::yellow);
s->addItem(e2);
// this item is partially outside right
QGraphicsEllipseItem* e3 = new QGraphicsEllipseItem(200, 200, 75, 125);
e3->setBrush(Qt::yellow);
s->addItem(e3);
QPrinter printer;
// QPrinter printer(QPrinter::HighResolution); // this makes no difference except it rotates the output, strange
// without this just to use default printer, if you like
QPrintDialog printDialog(&printer);
if (printDialog.exec() != QDialog::Accepted)
return 1;
printer.setFullPage(false); // I see no diference between true and false
// this results in empty page (or is ignored if my rect is 8 in)
//printer.setPaperSize(canvasRect, QPrinter::Point);
printer.setOrientation(QPrinter::Landscape);
printer.setPageMargins(0, 0, 0, 0, QPrinter::Point);
QPainter painter;
if (painter.begin(&printer))
{
// painter.setClipRect(canvasRect); // this creates a small clipping, only a tiny corner
s->render(&painter, QRectF(), canvasRect, Qt::KeepAspectRatio);
// doing this instead clips to a tiny rectangle also
// s->render(&painter, canvasRect, canvasRect, Qt::KeepAspectRatio);
painter.end();
}
return app.exec();
}
正在做:
QPrinter printer(QPrinter::HighResolution);
qreal resolutionFactor = printer.resolution() / 1200.;
...
painter.scale(resolutionFactor, resolutionFactor);
修复了 LaserJet 打印(缩放 - 不是实际页面外的绘画)- 但在分辨率为 300 dpi 的打印机上导致几乎看不见的打印。
如何使打印输出符合实际比例(以便我可以在纸上测量 inches/mm 并使它们正确)?
另外,我怎样才能将输出裁剪到实际的 canvas 矩形?
一切都很简单。 render
方法只做两件事 :
- 它从以 scene 为单位的源矩形映射到以 device 为单位的目标矩形。
- 它只在目标矩形内绘制。
你的错误是传递了一个空目标矩形:然后没有有效的裁剪(它裁剪到设备大小),并且你正在以错误的缩放比例打印,除非你的场景恰好与设备大小。
设备单位和英寸之间的 DPI 映射由 QPrinter::resolution
根据 DPI(每英寸设备单位)给出。
要在选定的页面矩形内以正确的比例打印 canvasRect
并裁剪到选定的页面矩形,请执行以下操作,其中 in
是场景单位中的 1 英寸(72.0f
你的情况):
auto source = canvasRect;
auto scale = printer.resolution()/in;
auto page = printer.pageRect(QPrinter::DevicePixel);
auto target = QRectF(page.topLeft(), source.size()*scale);
target &= page; // clip target rect to page
qDebug() << page << scale << source << target;
scene.render(&painter, target, source);
打印机设备单元在 Qt 中看起来是矩形的,但这也许是因为我没有在足够奇怪的设备上尝试过。如果它们不是矩形,您可以从 pageRect
:
的输出中推断出它们
qreal resolution(QPrinter & printer, Qt::Orientation orientation) {
auto in = printer.pageRect(QPrinter::Inch);
auto dev = printer.pageRect(QPrinter::DevicePixel);
return (orientation == Qt::Horizontal) ? dev.width()/in.width()
: dev.height()/in.height();
}
...
auto scaleX = resolution(printer, Qt::Horizontal);
auto scaleY = resolution(printer, Qt::Vertical);
auto target = QRectF(page.left(), page.top(),
source.width()*scaleX, source.height()*scaleY);
...
完整示例如下。无论 in
的值是多少,输出都是相同的,因为我们使用明确的非修饰笔来绘制形状的轮廓。没有理由将 in
设置为任何特定值,如果您的自然单位是英寸,则只需设置 in=1.0f
.
// https://github.com/KubaO/Whosebugn/tree/master/questions/scene-print-37708423
#include <QtWidgets>
#include <QtPrintSupport>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGraphicsScene scene;
QGraphicsView view(&scene);
auto in = 72.0f;
auto pen = QPen(Qt::black, 0.01*in);
QRectF canvasRect(0, 0, 4*in, 4*in);
// this is to show actual scene
QGraphicsRectItem sss(canvasRect);
sss.setPen(pen);
sss.setBrush(Qt::blue);
scene.addItem(&sss);
// this item is partially outside top left
QGraphicsEllipseItem e1(-0.5*in, -0.5*in, 1*in, 1*in);
e1.setPen(pen);
e1.setBrush(Qt::yellow);
scene.addItem(&e1);
// this item is partially outside center
QGraphicsEllipseItem e2(2*in, 2*in, 2.5*in, 1*in);
e2.setPen(pen);
e2.setBrush(Qt::yellow);
scene.addItem(&e2);
// this item is partially outside right
QGraphicsEllipseItem e3(3.5*in, 3.5*in, 1*in, 1*in);
e3.setPen(pen);
e3.setBrush(Qt::yellow);
scene.addItem(&e3);
view.fitInView(scene.sceneRect(), Qt::KeepAspectRatio);
view.show();
QPrinter printer;
QPrintDialog printDialog(&printer);
QObject::connect(&printDialog, &QDialog::accepted, [&]{
printer.setOrientation(QPrinter::Landscape);
QPainter painter(&printer);
auto source = canvasRect;
auto scale = printer.resolution()/in;
auto page = printer.pageRect(QPrinter::DevicePixel);
auto target = QRectF(page.topLeft(), source.size()*scale);
target &= page; // clip target rect to page
qDebug() << page << scale << source << target;
scene.render(&painter, target, source);
});
printDialog.show(); // modal on OS X thus must follow `connect` above
return app.exec();
}
我正在尝试打印 QGraphicsScene
的内容。目标打印机可以是任何东西——从普通打印机到自定义尺寸的特殊打印机。它必须以实际尺寸(英寸、毫米......)打印东西。
在 QGraphicsScene
中,我使用 72 ppi 的假设。
我假设:
1) 将场景渲染到打印机会根据打印机分辨率进行,这样我就可以得到实际大小 (inches/mm) 的项目,类似于它们在屏幕上显示的内容。
2) 我可以将打印机的纸张尺寸设置为所需的 canvas 尺寸(这是一个非常大的场景中的矩形),超出它就不会打印
3)我可以设置边距,"actual canvas"以外的内容不会打印,包括边距上的内容。
到目前为止我所有的假设都是错误的:
1) 对于不同的打印机,如果我建议自定义尺寸接近其默认纸张尺寸(或者如果我不设置纸张尺寸),渲染似乎是最大适合(使用宽高比);
如果我设置了一个不接近的纸张尺寸(比如打印机上的默认 "LETTER" 尺寸为 4x4 英寸),它只会打印一张空白页。
2-3) 在有打印的情况下,打印机只是将 canvas 拉伸到整页,绘图区域外的任何项目仍会打印。
我尝试在 painter 上或通过在渲染器上设置目标矩形进行剪辑,结果是场景的一小部分非常奇怪的剪辑。
我试过 HP LaserJet、Adobe PDF 和一些特定尺寸(如 4x6 英寸)的自定义打印机。它们都根据我指定的纵向或横向将场景缩放到最大尺寸,而完全忽略我的纸张尺寸要求或实际尺寸。
这是一个小示例程序,可以重现我正在尝试做的事情。
代码中的注释显示了我尝试过的一些选项。
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QGraphicsEllipseItem>
#include <QPrinter>
#include <QPrintDialog>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGraphicsScene* s = new QGraphicsScene();
s->setSceneRect(-500, -500, 1500, 1500);
QGraphicsView* view = new QGraphicsView();
view->setScene(s);
view->show();
int canvasSize = 288; // 4 in
QRectF canvasRect(0, 0, canvasSize, canvasSize);
// this is to show actual scene
QGraphicsRectItem* sss = new QGraphicsRectItem(canvasRect);
sss->setBrush(Qt::blue);
s->addItem(sss);
// this item is partially outside top left
QGraphicsEllipseItem* e1 = new QGraphicsEllipseItem(-50, -75, 100, 150);
e1->setBrush(Qt::yellow);
s->addItem(e1);
// this item is partially outside center
QGraphicsEllipseItem* e2 = new QGraphicsEllipseItem(100, 150, 250, 50);
e2->setBrush(Qt::yellow);
s->addItem(e2);
// this item is partially outside right
QGraphicsEllipseItem* e3 = new QGraphicsEllipseItem(200, 200, 75, 125);
e3->setBrush(Qt::yellow);
s->addItem(e3);
QPrinter printer;
// QPrinter printer(QPrinter::HighResolution); // this makes no difference except it rotates the output, strange
// without this just to use default printer, if you like
QPrintDialog printDialog(&printer);
if (printDialog.exec() != QDialog::Accepted)
return 1;
printer.setFullPage(false); // I see no diference between true and false
// this results in empty page (or is ignored if my rect is 8 in)
//printer.setPaperSize(canvasRect, QPrinter::Point);
printer.setOrientation(QPrinter::Landscape);
printer.setPageMargins(0, 0, 0, 0, QPrinter::Point);
QPainter painter;
if (painter.begin(&printer))
{
// painter.setClipRect(canvasRect); // this creates a small clipping, only a tiny corner
s->render(&painter, QRectF(), canvasRect, Qt::KeepAspectRatio);
// doing this instead clips to a tiny rectangle also
// s->render(&painter, canvasRect, canvasRect, Qt::KeepAspectRatio);
painter.end();
}
return app.exec();
}
正在做:
QPrinter printer(QPrinter::HighResolution);
qreal resolutionFactor = printer.resolution() / 1200.;
...
painter.scale(resolutionFactor, resolutionFactor);
修复了 LaserJet 打印(缩放 - 不是实际页面外的绘画)- 但在分辨率为 300 dpi 的打印机上导致几乎看不见的打印。
如何使打印输出符合实际比例(以便我可以在纸上测量 inches/mm 并使它们正确)?
另外,我怎样才能将输出裁剪到实际的 canvas 矩形?
一切都很简单。 render
方法只做两件事 :
- 它从以 scene 为单位的源矩形映射到以 device 为单位的目标矩形。
- 它只在目标矩形内绘制。
你的错误是传递了一个空目标矩形:然后没有有效的裁剪(它裁剪到设备大小),并且你正在以错误的缩放比例打印,除非你的场景恰好与设备大小。
设备单位和英寸之间的 DPI 映射由 QPrinter::resolution
根据 DPI(每英寸设备单位)给出。
要在选定的页面矩形内以正确的比例打印 canvasRect
并裁剪到选定的页面矩形,请执行以下操作,其中 in
是场景单位中的 1 英寸(72.0f
你的情况):
auto source = canvasRect;
auto scale = printer.resolution()/in;
auto page = printer.pageRect(QPrinter::DevicePixel);
auto target = QRectF(page.topLeft(), source.size()*scale);
target &= page; // clip target rect to page
qDebug() << page << scale << source << target;
scene.render(&painter, target, source);
打印机设备单元在 Qt 中看起来是矩形的,但这也许是因为我没有在足够奇怪的设备上尝试过。如果它们不是矩形,您可以从 pageRect
:
qreal resolution(QPrinter & printer, Qt::Orientation orientation) {
auto in = printer.pageRect(QPrinter::Inch);
auto dev = printer.pageRect(QPrinter::DevicePixel);
return (orientation == Qt::Horizontal) ? dev.width()/in.width()
: dev.height()/in.height();
}
...
auto scaleX = resolution(printer, Qt::Horizontal);
auto scaleY = resolution(printer, Qt::Vertical);
auto target = QRectF(page.left(), page.top(),
source.width()*scaleX, source.height()*scaleY);
...
完整示例如下。无论 in
的值是多少,输出都是相同的,因为我们使用明确的非修饰笔来绘制形状的轮廓。没有理由将 in
设置为任何特定值,如果您的自然单位是英寸,则只需设置 in=1.0f
.
// https://github.com/KubaO/Whosebugn/tree/master/questions/scene-print-37708423
#include <QtWidgets>
#include <QtPrintSupport>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGraphicsScene scene;
QGraphicsView view(&scene);
auto in = 72.0f;
auto pen = QPen(Qt::black, 0.01*in);
QRectF canvasRect(0, 0, 4*in, 4*in);
// this is to show actual scene
QGraphicsRectItem sss(canvasRect);
sss.setPen(pen);
sss.setBrush(Qt::blue);
scene.addItem(&sss);
// this item is partially outside top left
QGraphicsEllipseItem e1(-0.5*in, -0.5*in, 1*in, 1*in);
e1.setPen(pen);
e1.setBrush(Qt::yellow);
scene.addItem(&e1);
// this item is partially outside center
QGraphicsEllipseItem e2(2*in, 2*in, 2.5*in, 1*in);
e2.setPen(pen);
e2.setBrush(Qt::yellow);
scene.addItem(&e2);
// this item is partially outside right
QGraphicsEllipseItem e3(3.5*in, 3.5*in, 1*in, 1*in);
e3.setPen(pen);
e3.setBrush(Qt::yellow);
scene.addItem(&e3);
view.fitInView(scene.sceneRect(), Qt::KeepAspectRatio);
view.show();
QPrinter printer;
QPrintDialog printDialog(&printer);
QObject::connect(&printDialog, &QDialog::accepted, [&]{
printer.setOrientation(QPrinter::Landscape);
QPainter painter(&printer);
auto source = canvasRect;
auto scale = printer.resolution()/in;
auto page = printer.pageRect(QPrinter::DevicePixel);
auto target = QRectF(page.topLeft(), source.size()*scale);
target &= page; // clip target rect to page
qDebug() << page << scale << source << target;
scene.render(&painter, target, source);
});
printDialog.show(); // modal on OS X thus must follow `connect` above
return app.exec();
}