Как я могу «Распечатать предварительный просмотр» документа, созданного QTextDocument в PyQt5?

#python #printing #pyqt5 #print-preview #qtextdocument

#python #печать #pyqt5 #предварительный просмотр печати #qtextdocument

Вопрос:

Привет, эксперты!!Надеюсь, у вас отличный день. Я новичок в программировании с графическим интерфейсом, особенно PyQt5. Я практикуюсь в простом приложении счета-фактуры с графическим интерфейсом. В этом приложении я успешно сгенерировал счет-фактуру с помощью QTextDocument. Теперь я хочу добавить диалог печати и опцию предварительного просмотра. У меня проблемы с кодом. Это говорит

Ошибка атрибута: объект ‘InvoiceForm’ не имеет атрибута ‘PrintPreviewDialog

Поскольку я новичок, я немного запутался в этом. Не могли бы вы исправить код? Это мне очень поможет в изучении. Большое, большое спасибо. Код приведен ниже:-

 import sys
from PyQt5.QtCore import pyqtSignal, QSize, QSizeF, QDate
from PyQt5.QtGui import QTextDocument, QTextCursor, QFont
from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog
from PyQt5.QtWidgets import QWidget, QFormLayout, QLineEdit, QPlainTextEdit, QSpinBox, QDateEdit, QTableWidget, 
    QHeaderView, QPushButton, QHBoxLayout, QTextEdit, QApplication, QMainWindow

font= QFont('Arial',16)

class InvoiceForm(QWidget):
    submitted = pyqtSignal(dict)
    def __init__(self):
        super().__init__()
        self.setLayout(QFormLayout())
        self.inputs = dict()
        self.inputs['Customer Name'] = QLineEdit()
        self.inputs['Customer Address'] = QPlainTextEdit()
        self.inputs['Invoice Date'] = QDateEdit(date=QDate.currentDate(), calendarPopup=True)
        self.inputs['Days until Due'] = QSpinBox()
        for label, widget in self.inputs.items():
            self.layout().addRow(label, widget)

        self.line_items = QTableWidget(rowCount=10, columnCount=3)
        self.line_items.setHorizontalHeaderLabels(['Job', 'Rate', 'Hours'])
        self.line_items.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.layout().addRow(self.line_items)
        for row in range(self.line_items.rowCount()):
            for col in range(self.line_items.columnCount()):
                if col > 0:
                    w = QSpinBox()
                    self.line_items.setCellWidget(row, col, w)

        submit = QPushButton('Create Invoice', clicked=self.on_submit)
        print = QPushButton('Print Invoice', clicked=self.printpreviewDialog)
        self.layout().addRow(submit,print)

    def on_submit(self):
        data = {'c_name': self.inputs['Customer Name'].text(),
                'c_addr': self.inputs['Customer Address'].toPlainText(),
                'i_date': self.inputs['Invoice Date'].date().toString(),
                'i_due': self.inputs['Invoice Date'].date().addDays(self.inputs['Days until Due'].value()).toString(),
                'i_terms': '{} days'.format(self.inputs['Days until Due'].value()),
                'line_items': list()}

        for row in range(self.line_items.rowCount()):
            if not self.line_items.item(row, 0):
                continue
            job = self.line_items.item(row, 0).text()
            rate = self.line_items.cellWidget(row, 1).value()
            hours = self.line_items.cellWidget(row, 2).value()
            total = rate * hours
            row_data = [job, rate, hours, total]
            if any(row_data):
                data['line_items'].append(row_data)

        data['total_due'] = sum(x[3] for x in data['line_items'])
        self.submitted.emit(data)
        # remove everything else in this function below this point

class InvoiceView(QTextEdit):
    dpi = 72
    doc_width = 8.5 * dpi
    doc_height = 6 * dpi

    def __init__(self):
        super().__init__(readOnly=True)
        self.setFixedSize(QSize(self.doc_width, self.doc_height))

    def build_invoice(self, data):
        document = QTextDocument()
        self.setDocument(document)
        document.setPageSize(QSizeF(self.doc_width, self.doc_height))
        document.setDefaultFont(font)
        cursor = QTextCursor(document)
        cursor.insertText(f"Customer Name: {data['c_name']}n")
        cursor.insertText(f"Customer Address: {data['c_addr']}n")
        cursor.insertText(f"Date: {data['i_date']}n")
        cursor.insertText(f"Total Due: {data['total_due']}n")

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        central = QWidget()
        self.setCentralWidget(central)
        layout = QHBoxLayout(central)

        self.invoiceForm = InvoiceForm()
        layout.addWidget(self.invoiceForm)

        self.invoiceView = InvoiceView()
        layout.addWidget(self.invoiceView)
        # hide the widget right now...
        self.invoiceView.setVisible(False)

        self.invoiceForm.submitted.connect(self.showPreview)

    def showPreview(self, data):
        self.invoiceView.setVisible(True)
        self.invoiceView.build_invoice(data)

    def printpreviewDialog(self):
        printer = QPrinter(QPrinter.HighResolution)
        previewDialog = QPrintPreviewDialog(printer, self)
        previewDialog.paintRequested.connect(self.printPreview)
        previewDialog.exec_()

    def printPreview(self, printer):
        self.invoiceView.build_invoice.print_(printer)

def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()

if __name__ == '__main__':
    main()
  

Комментарии:

1. Хотя мы ценим вашу вежливость, пожалуйста, избегайте приветствий в начале ваших вопросов, они занимают значительное пространство, которое должно отображать фактическое содержание вопроса, в основном потому, что они отображаются в сводке результатов поиска; помните, что вопросы и ответы полезны всем в любое время илюди, ищущие проблему, похожую на вашу, предпочитают просматривать десятки резюме вопросов, и вряд ли им интересно читать, насколько вы добры 🙂

2. @musicamante Насколько я помню, я добавил приветствия. Но почему-то он не публиковался. Хотя я спешил, поэтому у меня не было времени прочитать вопрос. Спасибо за ваш любезный ответ.

3. Нет. Пожалуйста, внимательно прочитайте то, что я написал, потому что это прямо противоположно тому, что я вам сказал. НЕ добавляйте приветствия в начале вашего вопроса.

Ответ №1:

Основная проблема заключается в том, что self.printpreviewDialog это член MainWindow , а не of InvoiceForm , поэтому вместо этого вы должны подключить clicked сигнал из главного окна.

Также обратите внимание, что вы пытались использовать self.invoiceView.build_invoice.print_() , но это не сработало бы, поскольку вы не вызываете build_invoice , и даже если бы вы это сделали, эта функция ничего не возвращает.

Вы должны использовать self.invoiceView.document() вместо этого, но вы должны убедиться, что данные были созданы ранее.

 class InvoiceForm(QWidget):
    submitted = pyqtSignal(dict)
    def __init__(self):
        # ...
        submit = QPushButton('Create Invoice', clicked=self.on_submit)
        # make the button a member of the instance instead of a local variable, 
        # so that we can connect from the main window instance
        self.printButton = QPushButton('Print Invoice')
        self.layout().addRow(submit, self.printButton)

# ...

class MainWindow(QMainWindow):
    def __init__(self):
        # ...
        self.invoiceForm.printButton.clicked.connect(self.printpreviewDialog)
    # ...

    def printPreview(self, printer):
        self.invoiceView.document().print_(printer)
  

Примечание: никогда, никогда не используйте встроенные функции и операторы для имен переменных, например print .

Ответ №2:

Попробуйте:

 import sys
from PyQt5.QtCore import pyqtSignal, QSize, QSizeF, QDate
from PyQt5.QtGui import QTextDocument, QTextCursor, QFont
from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog
from PyQt5.QtWidgets import (QWidget, QFormLayout, QLineEdit, QPlainTextEdit, 
    QSpinBox, QDateEdit, QTableWidget, QHeaderView, QPushButton, QHBoxLayout, 
    QTextEdit, QApplication, QMainWindow)


font = QFont('Arial',16)


class InvoiceForm(QWidget):
    submitted = pyqtSignal(dict)
    
    def __init__(self, parent=None):                                        #   parent=None
        super().__init__(parent)                                            #   parent
        self.setLayout(QFormLayout())
        self.inputs = dict()
        self.inputs['Customer Name'] = QLineEdit()
        self.inputs['Customer Address'] = QPlainTextEdit()
        self.inputs['Invoice Date'] = QDateEdit(date=QDate.currentDate(), calendarPopup=True)
        self.inputs['Days until Due'] = QSpinBox()
        for label, widget in self.inputs.items():
            self.layout().addRow(label, widget)

        self.line_items = QTableWidget(rowCount=10, columnCount=3)
        self.line_items.setHorizontalHeaderLabels(['Job', 'Rate', 'Hours'])
        self.line_items.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.layout().addRow(self.line_items)
        for row in range(self.line_items.rowCount()):
            for col in range(self.line_items.columnCount()):
                if col > 0:
                    w = QSpinBox()
                    self.line_items.setCellWidget(row, col, w)

        submit = QPushButton('Create Invoice', clicked=self.on_submit)
        
#       vvvvvv                                        vvvvvvvvvvvvv 
        _print = QPushButton('Print Invoice', clicked=self.window().printpreviewDialog)  #   _print,   self.window()
        self.layout().addRow(submit, _print)                                             #   _print

    def on_submit(self):
        data = {'c_name': self.inputs['Customer Name'].text(),
                'c_addr': self.inputs['Customer Address'].toPlainText(),
                'i_date': self.inputs['Invoice Date'].date().toString(),
                'i_due': self.inputs['Invoice Date'].date().addDays(self.inputs['Days until Due'].value()).toString(),
                'i_terms': '{} days'.format(self.inputs['Days until Due'].value()),
                'line_items': list()}

        for row in range(self.line_items.rowCount()):
            if not self.line_items.item(row, 0):
                continue
            job = self.line_items.item(row, 0).text()
            rate = self.line_items.cellWidget(row, 1).value()
            hours = self.line_items.cellWidget(row, 2).value()
            total = rate * hours
            row_data = [job, rate, hours, total]
            if any(row_data):
                data['line_items'].append(row_data)

        data['total_due'] = sum(x[3] for x in data['line_items'])
        self.submitted.emit(data)
        # remove everything else in this function below this point
#  
        return data                                                            #    
        

class InvoiceView(QTextEdit):
    dpi = 72
    doc_width = 8.5 * dpi
    doc_height = 6 * dpi

    def __init__(self):
        super().__init__(readOnly=True)
        self.setFixedSize(QSize(self.doc_width, self.doc_height))

    def build_invoice(self, data):
        document = QTextDocument()
        self.setDocument(document)
        document.setPageSize(QSizeF(self.doc_width, self.doc_height))
        document.setDefaultFont(font)
        cursor = QTextCursor(document)
        cursor.insertText(f"Customer Name: {data['c_name']}n")
        cursor.insertText(f"Customer Address: {data['c_addr']}n")
        cursor.insertText(f"Date: {data['i_date']}n")
        cursor.insertText(f"Total Due: {data['total_due']}n")
#          
        return document                                                         #    
        

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        central = QWidget()
        self.setCentralWidget(central)
        layout = QHBoxLayout(central)
#                                      vvvv
        self.invoiceForm = InvoiceForm(self)                                    #   self
        
        layout.addWidget(self.invoiceForm)
        self.invoiceView = InvoiceView()
        layout.addWidget(self.invoiceView)
        # hide the widget right now...
        self.invoiceView.setVisible(False)
        self.invoiceForm.submitted.connect(self.showPreview)

    def showPreview(self, data):
        self.invoiceView.setVisible(True)
        self.invoiceView.build_invoice(data)

#     vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
    def printpreviewDialog(self):
        previewDialog = QPrintPreviewDialog()
        previewDialog.paintRequested.connect(self.printPreview)  
        previewDialog.exec_()

    def printPreview(self, printer):
#        self.invoiceView.build_invoice.print_(printer)        
        data = self.invoiceForm.on_submit()
        document = self.invoiceView.build_invoice(data)
        document.print_(printer)
#     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()

if __name__ == '__main__':
    main()
  

введите описание изображения здесь