Saya mencoba mencetak konten QGraphicsScene. Printer target bisa apa saja - dari printer biasa hingga printer khusus ukuran khusus. Itu harus mencetak sesuatu dengan ukuran sebenarnya (inci, mm....).
Dalam QGraphicsScene saya menggunakan asumsi 72 ppi.

Saya berasumsi bahwa:
1) rendering adegan ke printer akan melakukannya berdasarkan resolusi printer, sehingga saya akan mendapatkan item pada ukuran sebenarnya (inci/mm) mirip dengan apa yang mereka tampilkan di layar.
2) Saya dapat mengatur ukuran kertas untuk printer ke ukuran kanvas yang diinginkan (yang merupakan persegi panjang pada pemandangan yang sangat besar) dan tidak ada di luar itu yang akan dicetak
3) Saya dapat mengatur margin, dan konten di luar "kanvas sebenarnya" tidak akan dicetak, termasuk apa yang ada di margin.

Semua asumsi saya sejauh ini salah:
1) untuk printer yang berbeda, tampaknya renderingnya adalah untuk kecocokan maksimal (menggunakan rasio aspek), jika saya menyarankan ukuran khusus yang mendekati ukuran kertas default (atau jika saya tidak menetapkan ukuran kertas);
Jika saya mengatur ukuran kertas yang tidak dekat (seperti 4x4 inci pada printer dengan ukuran default "LETTER") hanya mencetak halaman kosong.
2-3) Dalam kasus di mana ada cetakan, dan printer hanya merentangkan kanvas ke halaman penuhnya, item apa pun yang berada di luar area gambar tetap dicetak.
Saya mencoba untuk memotong, baik pada pelukis atau dengan mengatur persegi panjang target pada render, dan hasilnya adalah kliping yang sangat aneh dari bagian kecil dari pemandangan.

Saya sudah mencoba di HP LaserJet, Adobe PDF, dan beberapa custom printer dengan ukuran tertentu (seperti 4x6 inci). Mereka semua menskalakan pemandangan ke ukuran maksimal berdasarkan apakah saya menentukan Potret atau Lanskap, dan sepenuhnya mengabaikan permintaan ukuran kertas saya atau ukuran sebenarnya.

Berikut adalah contoh program kecil untuk mereproduksi apa yang saya coba lakukan.
Komentar dalam kode menunjukkan beberapa opsi yang saya coba.

#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();
}

Perbuatan:

QPrinter printer(QPrinter::HighResolution);
qreal resolutionFactor = printer.resolution() / 1200.;
...
painter.scale(resolutionFactor, resolutionFactor);

Memperbaiki cetakan LaserJet (penskalaan - bukan lukisan di luar halaman sebenarnya) - tetapi menghasilkan cetakan kecil yang hampir tidak terlihat pada printer dengan resolusi 300 dpi.

Bagaimana saya bisa mendapatkan hasil cetakan menjadi skala aktual (sehingga saya dapat mengukur inci/mm di atas kertas dan membuatnya benar)?

Juga bagaimana saya bisa mendapatkan output yang akan dipotong ke persegi panjang kanvas yang sebenarnya ?

4
Thalia 8 Juni 2016, 19:49
1
dpi adalah faktor konversi. Ini bukan pengukuran fisik. itu seperti bertanya "berapa jauh untuk berkendara ke New York" dan Anda menjawab "60 mph".
 – 
Marc B
8 Juni 2016, 19:52
Maksud saya PPI (Piksel per inci - atau Poin per inci?) - Saya menggambar item adegan berukuran 72*inci. QPrinter tampaknya setuju, jika saya melakukan printer.setPaperSize(canvasRect, QPrinter::Point); dan qDebug() << printer.paperSize(QPrinter::Inch); itu memberi saya ukuran yang diharapkan.
 – 
Thalia
8 Juni 2016, 20:02
Unit dalam adegan bukan piksel, jadi berbicara tentang PPI tidak masuk akal. Anda cukup mengatakan bahwa unit adegan Anda adalah 1/72 inci.
 – 
Kuba hasn't forgotten Monica
8 Juni 2016, 21:30
Saya menghargai kasus uji Anda yang mandiri dan langsung. Kudos dan terima kasih banyak!
 – 
Kuba hasn't forgotten Monica
8 Juni 2016, 23:22

1 menjawab

Jawaban Terbaik

Semuanya sangat sederhana. Metode render melakukan dua hal hanya:

  1. Ini memetakan dari persegi panjang sumber, dalam unit adegan, ke persegi panjang target, dalam unit perangkat.
  2. Itu menarik dalam persegi panjang target saja.

Kesalahan Anda adalah melewatkan persegi target nol: tidak ada kliping yang efektif saat itu (terpotong ke ukuran perangkat), dan Anda juga mencetak pada skala yang salah kecuali jika adegan Anda kebetulan berukuran persis sama dengan ukuran perangkat.

Pemetaan DPI antara unit perangkat dan inci diberikan oleh QPrinter::resolution, dalam bentuk DPI (unit perangkat per inci).

Untuk mencetak canvasRect pada skala yang benar, di dalam dan dipotong ke persegi panjang halaman yang dipilih, lakukan hal berikut, di mana in adalah 1 inci dalam unit adegan (72.0f dalam kasus Anda):

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);

Unit perangkat printer tampak persegi panjang di Qt, tapi mungkin itu karena saya belum mencoba perangkat yang cukup aneh. Jika bukan persegi panjang, Anda dapat menyimpulkannya dari output 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);
...

Contoh lengkap berikut. Outputnya tetap identik berapa pun nilai in, karena kami menggunakan pena non-kosmetik eksplisit untuk garis luar bentuk. Tidak ada alasan untuk menyetel in ke nilai tertentu, jika satuan alami Anda adalah inci maka cukup setel 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();
}
4
Kuba hasn't forgotten Monica 9 Juni 2016, 01:03
Ini bagus, termasuk resolusi horizontal dan vertikal yang berbeda! Apakah ada cara untuk memberi tahu printer ukuran kertas apa yang digunakan, tanpa mengambil risiko tidak mengenalinya dan mencetak halaman kosong?
 – 
Thalia
9 Juni 2016, 00:51
Ketika printer.setPageSize(QPageSize::A4)) (mis.) mengembalikan nilai true, Anda tahu bahwa itu berhasil. Anda dapat mengatur ukuran halaman default, lalu menampilkan dialog cetak tetapi berharap bahwa itu mungkin berubah karena pengguna harus dapat mengubahnya dan perubahan tersebut tidak boleh diabaikan. Anda tidak peduli berapa ukuran halamannya, apa pun yang telah ditetapkan pengguna, dan pageRect mencerminkan ukuran halaman yang dipilih.
 – 
Kuba hasn't forgotten Monica
9 Juni 2016, 01:01
Ini mengasumsikan bahwa saya dapat mengatur ukuran kertas dari enum - tetapi untuk printer khusus, bukan itu masalahnya ... Terutama untuk roll-fed terus menerus. Saya mencoba memberi tahu printer berapa ukuran kertasnya (atau setidaknya menyarankannya ke dialog cetak).
 – 
Thalia
9 Juni 2016, 01:28
Anda juga dapat mengatur ukuran kertas khusus. QPageSize menerima ukuran khusus.
 – 
Kuba hasn't forgotten Monica
9 Juni 2016, 06:08