Мне нужно напечатать QGraphicsScene в реальном масштабе (дюйм / мм)
Я пытаюсь распечатать содержимое QGraphicsScene
, Целевым принтером может быть что угодно - от обычных принтеров до специальных принтеров нестандартных размеров. Он должен печатать вещи в реальном размере (дюймы, мм....).
в QGraphicsScene
Я использую предположение 72 ppi.
Я предположил, что:
1) рендеринг сцены на принтер будет делать это в зависимости от разрешения принтера, чтобы я получал элементы в реальном размере (дюймах / мм), аналогичном тому, что они показывают на экране.
2) Я могу установить размер бумаги для принтера на желаемый размер холста (который представляет собой прямоугольник на очень большой сцене), и ничего кроме этого не будет печатать
3) Я могу установить поля, и содержимое вне "фактического холста" не будет напечатано, включая то, что находится на полях.
Все мои предположения пока неверны:
1) для разных принтеров кажется, что рендеринг предназначен для максимальной подгонки (с использованием соотношения сторон), если я предлагаю нестандартный размер, близкий к размеру бумаги по умолчанию (или если я не установил размер бумаги);
Если я устанавливаю размер бумаги, который не является близким (например, 4x4 дюйма на принтере с размером по умолчанию "ПИСЬМО"), он просто печатает пустую страницу.
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 точек на дюйм.
Как я могу получить напечатанный вывод в реальном масштабе (чтобы я мог измерить дюймы / мм на бумаге и чтобы они были правильными)?
Также, как я могу получить вывод, который будет обрезан по фактическому прямоугольнику холста?
1 ответ
Это все действительно просто. render
Метод делает только две вещи:
- Он отображается от исходного прямоугольника в единицах сцены до целевого прямоугольника в единицах устройства.
- Он рисует только внутри целевого прямоугольника.
Ваша ошибка заключалась в передаче нулевого целевого прямоугольника: тогда эффективного отсечения нет (оно обрезается до размера устройства), и вы также печатаете с неправильным масштабированием, если только ваша сцена не имеет точно такой же размер, как размер устройства.
Отображение 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/stackrun/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();
}