ээдд. Прохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен. Николай Прохоренок Владимир Дронов
Скачать 7.92 Mb.
|
ГЛ А В А 29 Печать документов PyQt 5 поддерживает ряд развитых средств, позволяющих выполнить печать документов, их предварительный просмотр и экспорт в формат PDF. Любой установленный в системе принтер представляется классом QPrinter . Поскольку он является подклассом класса QPagedPaintDevice , который, в свою очередь, наследует рас- смотренный в главе 24 класс QPaintDevice , мы можем для вывода документов на печать использовать методы последнего. Класс QPrintDialog обеспечивает функциональность диалогового окна выбора принтера, а класс QPageSetupDialog — диалогового окна установки параметров страницы. Для предва- рительного просмотра печатаемых документов задействуются классы QPrintPreviewDialog и QPrintPreviewWidget : первый выводит документ в отдельное диалоговое окно, а второй — в специализированный компонент, который мы можем использовать вместе с любыми дру- гими изученными нами компонентами. Класс QPrinterInfo позволяет выяснить, какие принтеры имеются в наличии, и узнать параметры любого из установленных принтеров. PyQt 5 поддерживает и экспорт документов в формат Adobe PDF исключительно встроен- ными средствами, без привлечения каких бы то ни было сторонних программ. Для выпол- нения этой задачи служит класс QPdfWriter Все описанные в этой главе классы определены в модуле QtPrintSupport , если не указано иное. 29.1. Основные средства печати Вывести документ на принтер в PyQt довольно просто. Почти все, что нам нужно для этого знать, было описано в главе 24. 29.1.1. Класс QPrinter Как уже говорилось, класс QPrinter представляет установленный в системе принтер. Его иерархия наследования такова: QPaintDevice – QPagedPaintDevice – QPrinter Конструктор класса QPrinter имеет следующие форматы вызова: <Объект> = QPrinter([mode=ScreenResolution]) <Объект> = QPrinter( 700 Часть II. Библиотека PyQt 5 Первый формат выбирает для печати принтер по умолчанию. В параметре mode может быть указано разрешение принтера, заданное в виде значения одного из следующих атрибутов класса QPrinter : ScreenResolution — 0 — разрешение экрана. Позволяет вывести документ максимально быстро, но в худшем качестве; HighResolution — 2 — разрешение принтера. Печать выполняется качественнее, но мед- леннее. Второй формат позволяет выбрать произвольный принтер из числа установленных в систе- ме. Этот принтер задается экземпляром класса QPrinterInfo , речь о котором пойдет далее. Класс QPrinter поддерживает очень много методов, из которых мы рассмотрим лишь наи- более полезные (полный их список можно найти на странице https://doc.qt.io/qt-5/ qprinter.html ). setPrinterName(<Имя принтера>) — выполняет подключение к принтеру с заданным в виде строки именем. Если передать пустую строку, будет выполнено подключение к встроенной в PyQt подсистеме вывода документов в формате PDF; printerName() — возвращает строку с именем принтера, к которому выполнено подклю- чение; setOutputFileName(<Путь к файлу>) — задает путь к файлу, в который будет выведен печатаемый документ (так называемая печать в файл). Если файл имеет расширение pdf , будет выполнен вывод в формате PDF. В противном случае файл будет сохранен в фор- мате, установленном в вызове метода setOutputFormat() . Чтобы отключить вывод доку- мента в файл, следует вызвать этот метод, передав ему в качестве параметра пустую строку; outputFileName() — возвращает путь к файлу, в который будет выведен документ вме- сто печати на бумаге; setOutputFormat(<Формат вывода>) — задает формат вывода документа при печати в файл. В качестве параметра передается значение одного из следующих атрибутов класса QPrinter : • NativeFormat — 0 — будет выполнен вывод во внутреннем формате принтера. Этот режим автоматически устанавливается при создании экземпляра класса QPrinter и вызове метода setPrinterName() с указанием имени существующего принтера. Если экземпляр класса подключен к подсистеме вывода в формате PDF, будет выполнено переподключение к принтеру по умолчанию; • PdfFormat — 1 — будет выполнен вывод в формате PDF. Этот режим автоматически устанавливается при вызове метода setPrinterName() с указанием пустой строки; outputFormat() — возвращает обозначение формата вывода документа; isValid() — возвращает True , если принтер действительно установлен в системе и готов к работе, и False — в противном случае. setPageSize( — задает размер страницы в виде экземпляра класса QPageSize из модуля QtGui . Возвращает True , если операция увенчалась успехом, и False — в противном случае. Вот пример задания размера бумаги А4: ps = QtGui.QPageSize(QtGui.QPageSize.A4) printer.setPageSize(ps) Глава 29. Печать документов 701 setPageOrientation(<Ориентация>) — задает ориентацию страницы в виде значения ат- рибута Portrait ( 0 , портретная) или Landscape ( 1 , ландшафтная) класса QPageLayout из модуля QtGui . Возвращает True , если операция увенчалась успехом, и False — в против- ном случае; setPageMargins(<Слева>, <Сверху>, <Справа>, <Снизу>, <Единица измерения>) — зада- ет отступы от краев страницы, соответственно, слева, сверху, справа и снизу в заданной единице измерения. Сами отступы указываются в виде вещественных чисел, а единица измерения — в виде значения одного из следующих атрибутов класса QPageLayout : • Millimeter — 0 — миллиметры; • Point — 1 — пункты; • Inch — 2 — дюймы; • Pica — 3 — пики; • Didot — 4 — дидо (0,375 мм); • Cicero — 5 — цицеро (4,5 мм). Возвращает True , если операция увенчалась успехом, и False — в противном случае. Вот пример задания отступов в 5 мм со всех сторон страницы: printer.setPageMargins(5, 5, 5, 5, QtGui.QPageLayout.Millimeter) setPageLayout( — задает сразу все параметры страницы (размеры, ори- ентацию и отступы от краев страницы в виде экземпляра класса QPageLayout . Возвраща- ет True , если операция увенчалась успехом, и False — в противном случае; pageLayout() — возвращает экземпляр класса QPageLayout , хранящий сведения о разме- ре бумаги, ориентации страницы и величинах отступов от краев страницы; setCopyCount(<Количество копий>) — задает количество копий печатаемого документа; copyCount() — возвращает количество копий печатаемого документа; setCollateCopies(<Флаг>) — если передано значение True , каждая копия документа бу- дет отпечатана полностью, прежде чем начнется печать следующей копии. Если пере- дать значение False , сначала будут отпечатаны все копии первой страницы, потом все копии второй и т. д.; collateCopies() — возвращает True , если каждая копия документа печатается пол- ностью, и False — в противном случае; setDuplex(<Режим двухсторонней печати>) — задает режим двусторонней печати в виде значения одного из следующих атрибутов класса QPrinter : • DuplexNone — 0 — односторонняя печать; • DuplexAuto — 1 — двусторонняя печать с автоматическим определением стороны листа, вокруг которой следует его перевернуть; • DuplexLongSide — 2 — двусторонняя печать с переворачиванием листа вокруг длин- ной стороны; • DuplexShortSide — 3 — двусторонняя печать с переворачиванием листа вокруг ко- роткой стороны; duplex() — возвращает обозначение заданного для принтера режима двусторонней печати; 702 Часть II. Библиотека PyQt 5 setPrintRange(<Диапазон печати>) — задает диапазон печати документа в виде значе- ния одного из следующих атрибутов класса QPrinter : • AllPages — 0 — печатать все страницы; • Selection — 1 — печатать только выделенный фрагмент; • PageRange — 2 — печатать только заданный диапазон страниц; • CurrentPage — 3 — печатать только текущую страницу; setFromTo(<Начальная страница>, <Конечная страница>) — задает диапазон печатаемых страниц в документе; fromPage() — возвращает начальную страницу диапазона печати; toPage() — возвращает конечную страницу диапазона печати; setColorMode(<Цветовой режим>) — задает режим вывода цвета в виде значения атрибу- та Color ( 1 , цветной) или Grayscale ( 0 , черно-белый) класса QPrinter ; colorMode() — возвращает обозначение режима вывода цвета, заданного для принтера; setResolution(<Разрешение>) — задает разрешение для принтера в виде целого числа в точках на дюйм. Если указано неподдерживаемое значение разрешения, будет выстав- лено разрешение, наиболее близкое к заданному; resolution() — возвращает заданное для принтера разрешение; setPaperSource(<Источник бумаги>) — задает источник бумаги для принтера в виде зна- чения одного из следующих атрибутов класса QPrinter (здесь приведены лишь наиболее часто используемые источники — полный их список можно найти на странице https://doc.qt.io/qt-5/qprinter.html#PaperSource-enum ): • OnlyOne — 0 — единственный лоток принтера или лоток, используемый по умолча- нию; • Lower — 1 — нижний лоток; • Middle — 2 — средний лоток; • Manual — 3 — лоток для ручной подачи бумаги; • Envelope — 4 — лоток для конвертов; • EnvelopeManual — 5 — лоток для ручной подачи конвертов; • Auto — 6 — автоматический выбор источника; paperSource() — возвращает обозначение источника бумаги; setFontEmbeddingEnabled(<Флаг>) — если передано значение True , в создаваемый доку- мент PDF будут внедрены все использованные в его тексте шрифты, если передано зна- чение False — не будут; supportsMultipleCopies() — возвращает True , если принтер сам способен напечатать несколько копий документа, и False — в противном случае; supportedResolutions() — возвращает список разрешений, поддерживаемых принтером и измеряемых в точках на дюйм: printer = QtPrintSupport.QPrinter() for f in printer.supportedResolutions(): print(f, end = " ") У авторов вывел: 600 Глава 29. Печать документов 703 Как видим, принтер авторов, установленный по умолчанию, поддерживает лишь разре- шение 600 точек на дюйм. В НИМАНИЕ ! Для некоторых принтеров метод supportResolutions() по какой-то причине выводит пус- той список. 29.1.2. Вывод на печать Процесс вывода документа на печать средствами PyQt можно разбить на следующие этапы: 1. Создание принтера (экземпляра класса QPrinter ) и задание его параметров: printer = QtPrintSupport.QPrinter() 2. Создание экземпляра класса QPainter : painter = QtGui.QPainter() 3. Вызов метода begin( класса QPainter и передача ему в качестве пара- метров только что созданного принтера: painter.begin(printer) Метод begin() инициирует процесс вывода графики на принтер. Он возвращает True , если инициализация прошла успешно, и False — в противном случае. 4. Выполнение вывода необходимой графики, составляющей собственно содержимое документа, средствами класса QPainter (см. главу 24). 5. В случае необходимости начать вывод новой страницы — вызов метода newPage() клас- са QPrinter : printer.newPage() Метод newPage() подготавливает принтер к выводу новой страницы и возвращает True , если подготовка увенчалась успехом, и False — в противном случае. 6. По окончании вывода документа — вызов метода end() класса QPainter : painter.end() Этот метод завершает рисование графики и возвращает True , если вывод графики был закончен успешно, и False — в противном случае. После его вызова выполняется собст- венно печать документа. Перед началом вывода графики нам понадобится определить размеры всей страницы прин- тера, размеры области на ней, доступной для рисования, и некоторые другие параметры, касающиеся этих размеров. Для этого мы воспользуемся следующими методами класса QPrinter (полный список их можно найти на страницах https://doc.qt.io/qt-5/qprinter.html и https://doc.qt.io/qt-5/qpaintdevice.html): width() — возвращает ширину области, доступной для вывода графики, в пикселах; height() — возвращает высоту области, доступной для вывода графики, в пикселах; widthMM() — возвращает ширину области, доступной для вывода графики, в милли- метрах; heightMM() — возвращает высоту области, доступной для вывода графики, в миллимет- рах; 704 Часть II. Библиотека PyQt 5 colorCount() — возвращает количество цветов, которые способен выводить принтер; pageRect(<Единица измерения>) — возвращает размеры области на странице, доступной для вывода графики, в виде экземпляра класса QRectF . Единица измерения задается в виде значения одного из следующих атрибутов класса QPrinter : • Millimeter — 0 — миллиметры; • Point — 1 — пункты; • Inch — 2 — дюймы; • Pica — 3 — пики; • Didot — 4 — дидо (0,375 мм); • Cicero — 5 — цицеро (4,5 мм); • DevicePixel — 6 — пикселы; paperRect([<Единица измерения>]) — возвращает размеры страницы целиком в виде экземпляра класса QRectF . Единица измерения задается в виде значения одного из атри- бутов класса QPrinter , приведенных ранее в описании метода pageRect() Начало координат находится в левом верхнем углу области, отведенной под вывод графи- ки, — лишь в этой области мы можем выводить графику. Горизонтальная координатная ось направлена направо, а вертикальная — вниз. Для примера напишем код, который будет выводить на установленный по умолчанию принтер документ из двух страниц (листинг 29.1). Первую страницу мы обведем точечной синей рамкой, а по ее центру расположим надпись «QPrinter». Вторая страница будет отпе- чатана в ландшафтной ориентации, и ее полностью займет графическое изображение. Листинг 29.1. Использование класса QPrinter from PyQt5 import QtCore, QtWidgets, QtGui, QtPrintSupport import sys app = QtWidgets.QApplication(sys.argv) # Создаем принтер printer = QtPrintSupport.QPrinter() # Для целей отладки лучше выводить документ не на принтер, # а в файл в формате PDF. Чтобы сделать это, достаточно # раскомментировать следующую строчку кода: # printer.setOutputFileName("output.pdf") # Создаем поверхность рисования и привязываем ее к принтеру painter = QtGui.QPainter() painter.begin(printer) # Рисуем рамку вокруг страницы pen = QtGui.QPen(QtGui.QColor(QtCore.Qt.blue), 5, style = QtCore.Qt.DotLine) painter.setPen(pen) painter.setBrush(QtCore.Qt.NoBrush) painter.drawRect(0, 0, printer.width(), printer.height()) # Выводим надпись color = QtGui.QColor(QtCore.Qt.black) painter.setPen(QtGui.QPen(color)) painter.setBrush(QtGui.QBrush(color)) Глава 29. Печать документов 705 font = QtGui.QFont("Verdana", pointSize = 42) painter.setFont(font) painter.drawText(10, printer.height() // 2 - 100, printer.width() - 20, 50, QtCore.Qt.AlignCenter | QtCore.Qt.TextDontClip, "QPrinter") # Изменяем ориентацию страницы. Сделать это нужно перед вызовом # метода newPage() printer.setPageOrientation(QtGui.QPageLayout.Landscape) # Переходим на новую страницу printer.newPage() # Выводим изображение pixmap = QtGui.QPixmap("img.jpg") pixmap = pixmap.scaled(printer.width(), printer.height(), aspectRatioMode = QtCore.Qt.KeepAspectRatio) painter.drawPixmap(0, 0, pixmap) painter.end() В листинге 29.2 приведен код класса PrintList , реализующий печать списков или содержи- мого таблиц баз данных в виде полноценного табличного отчета. Этот класс можно исполь- зовать для разработки бизнес-приложений. Листинг 29.2. Класс PrintList, выводящий на печать табличные данные from PyQt5 import QtCore, QtGui, QtPrintSupport class PrintList: def __init__(self): self.printer = QtPrintSupport.QPrinter() self.headerFont = QtGui.QFont("Arial", pointSize = 10, weight = QtGui.QFont.Bold) self.bodyFont = QtGui.QFont("Arial", pointSize = 10) self.footerFont = QtGui.QFont("Arial", pointSize = 9, italic = True) self.headerFlags = QtCore.Qt.AlignHCenter | QtCore.Qt.TextWordWrap self.bodyFlags = QtCore.Qt.TextWordWrap self.footerFlags = QtCore.Qt.AlignHCenter | QtCore.Qt.TextWordWrap color = QtGui.QColor(QtCore.Qt.black) self.headerPen = QtGui.QPen(color, 2) self.bodyPen = QtGui.QPen(color, 1) self.margin = 5 self._resetData() def _resetData(self): self.headers = None self.columnWidths = None self.data = None self._brush = QtCore.Qt.NoBrush self._currentRowHeight = 0 self._currentPageHeight = 0 self._headerRowHeight = 0 self._footerRowHeight = 0 706 Часть II. Библиотека PyQt 5 self._currentPageNumber = 1 self._painter = None def printData(self): self._painter = QtGui.QPainter() self._painter.begin(self.printer) self._painter.setBrush(self._brush) if self._headerRowHeight == 0: self._painter.setFont(self.headerFont) self._headerRowHeight = self._calculateRowHeight( self.columnWidths, self.headers) if self._footerRowHeight == 0: self._painter.setFont(self.footerFont) self._footerRowHeight = self._calculateRowHeight( [self.printer.width()], "Страница") for i in range(len(self.data)): height = self._calculateRowHeight(self.columnWidths, self.data[i]) if self._currentPageHeight + height > self.printer.height() - self._footerRowHeight - 2 * self.margin: self._printFooterRow() self._currentPageHeight = 0 self._currentPageNumber += 1 self.printer.newPage() if self._currentPageHeight == 0: self._painter.setPen(self.headerPen) self._painter.setFont(self.headerFont) self.printRow(self.columnWidths, self.headers, self._headerRowHeight, self.headerFlags) self._painter.setPen(self.bodyPen) self._painter.setFont(self.bodyFont) self.printRow(self.columnWidths, self.data[i], height, self.bodyFlags) self._printFooterRow() self._painter.end() self._resetData() def _calculateRowHeight(self, widths, cellData): height = 0 for i in range(len(widths)): r = self._painter.boundingRect(0, 0, widths[i] - 2 * self.margin, 50, QtCore.Qt.TextWordWrap, str(cellData[i])) h = r.height() + 2 * self.margin if height < h: height = h return height def printRow(self, widths, cellData, height, flags): x = 0 for i in range(len(widths)): self._painter.drawText(x + self.margin, self._currentPageHeight + self.margin, |