我需要将QGraphicsScene打印到实际(英寸/毫米)比例

时间:2016-06-08 16:49:02

标签: qt printing qgraphicsscene qprinter

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

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

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

我曾尝试使用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的打印机上产生几乎看不见的小字体。

如何将打印输出设置为实际比例(以便我可以在纸上测量英寸/毫米并让它们正确)?

另外,如何将输出剪切到实际的画布矩形?

1 个答案:

答案 0 :(得分:3)

一切都非常简单。 render方法仅执行两项

  1. 它从源矩形以场景为单位映射到目标矩形,以设备为单位。
  2. 仅在目标矩形内绘制。
  3. 你的错误是传递了一个空的目标矩形:那时没有有效的剪裁(它剪辑到设备大小),你也会以错误的缩放比例打印,除非你的场景恰好是与设备尺寸相同的尺寸。

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