【问题标题】:I need to print a QGraphicsScene to actual (inch/mm) scale我需要将 QGraphicsScene 打印到实际(英寸/毫米)比例
【发布时间】:2016-10-09 01:42:45
【问题描述】:

我正在尝试打印QGraphicsScene 的内容。目标打印机可以是任何东西——从普通打印机到定制尺寸的特殊打印机。它必须以实际尺寸(英寸,毫米......)打印东西。
QGraphicsScene 中我使用的是 72 ppi 的假设。

我认为:
1) 将场景渲染到打印机将根据打印机分辨率进行,这样我就可以得到与它们在屏幕上显示的实际尺寸(英寸/毫米)相似的项目。
2) 我可以将打印机的纸张尺寸设置为所需的画布尺寸(在非常大的场景中是一个矩形),除此之外什么都不会打印
3)我可以设置边距,“实际画布”之外的内容不会打印,包括边距上的内容。

到目前为止,我所有的假设都是错误的:
1) 对于不同的打印机,如果我建议使用接近其默认纸张尺寸的自定义尺寸(或者如果我没有设置纸张尺寸),则渲染似乎是为了最大适合(使用纵横比);
如果我设置的纸张尺寸不接近(例如默认“LETTER”尺寸的打印机上的 4x4 英寸),它只会打印一个空白页。
2-3) 在有打印的情况下,打印机只是将画布拉伸到整页,绘图区域之外的任何项目仍会打印。
我尝试在 Painter 上或通过在渲染上设置目标矩形进行剪辑,结果是场景的一小部分非常奇怪的剪辑。

我尝试过使用 HP LaserJet、Adobe PDF 和一些具有特定尺寸(如 4x6 英寸)的定制打印机。它们都根据我指定的是纵向还是横向将场景缩放到最大尺寸,并且完全忽略了我的纸张尺寸请求或实际尺寸。

这是一个小示例程序,用于重现我正在尝试做的事情。
代码中的 cmets 显示了我尝试过的一些选项。

#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 分辨率的打印机上打印出几乎不可见的微小打印。

我怎样才能使打印输出达到实际比例(以便我可以在纸上测量英寸/毫米并确保它们是正确的)?

另外,我怎样才能将输出剪辑到实际的画布矩形?

【问题讨论】:

  • dpi 是一个转换因子。这不是物理测量。这就像问“开车到纽约要走多远”,而你回答“60 英里/小时”。
  • @MarcB 我的意思是 PPI(每英寸像素 - 或每英寸点数?) - 我正在绘制 72* 英寸大小的场景项目。 QPrinter 似乎同意,如果我这样做 printer.setPaperSize(canvasRect, QPrinter::Point);qDebug() &lt;&lt; printer.paperSize(QPrinter::Inch); 它会给我预期的大小。
  • 场景中的单位不是像素,所以说PPI是没有意义的。您可以简单地说您的场景单位是 1/72 英寸。
  • 我很欣赏你的独立、中肯的测试用例。荣誉和非常感谢!

标签: qt printing qgraphicsscene qprinter


【解决方案1】:

这一切都很简单。 render 方法只做两件事

  1. 它从以场景为单位的源矩形映射到以设备为单位的目标矩形。
  2. 它只在目标矩形内绘制。

您的错误是传递了一个空目标矩形:然后没有有效的剪辑(它剪辑到设备大小),并且您也在以错误的缩放比例打印,除非您的场景恰好与设备大小完全相同.

设备单位和英寸之间的 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/stackoverflown/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();
}

【讨论】:

  • 这很棒,包括不同的水平和垂直分辨率!有没有办法告诉打印机使用什么纸张尺寸,而不会有它无法识别并打印空白页的风险?
  • @Thalia 当printer.setPageSize(QPageSize::A4))(例如)返回真时,你就知道它成功了。您可以设置默认页面大小,然后显示打印对话框,但希望它可能会更改,因为用户应该能够更改它并且不应忽略更改。您不关心页面大小是多少,它是用户设置的任何内容,pageRect 反映了选择的页面大小。
  • 这假设我可以从枚举中设置纸张尺寸 - 但对于自定义打印机,情况并非如此......尤其是对于连续卷筒纸。我试图告诉打印机纸张尺寸是多少(或至少向打印对话框建议)。
  • @Thalia 您也可以设置自定义纸张尺寸。 QPageSize 接受自定义尺寸。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-05
  • 2020-08-19
  • 2011-09-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多