【问题标题】:How can I "Print Preview" of document created by QTextDocument in PyQt5?如何在 PyQt5 中“打印预览”由 QTextDocument 创建的文档?
【发布时间】:2021-01-05 12:12:09
【问题描述】:

专家您好!!希望您今天过得愉快。我是 GUI 编程的新手,特别是 PyQt5。我正在练习简单的 GUI 发票应用程序。在这个应用程序中,我成功地生成了 Invoice By QTextDocument。现在我想添加打印对话框和打印预览选项。我在代码中遇到问题。这是说

AttributeError: 'InvoiceForm' 对象没有属性 '打印预览对话框

因为我是新人,所以我在那里有点困惑。你能修复代码吗?这对我学习有很大帮助。非常感谢。 代码如下:-

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

【问题讨论】:

  • 虽然我们感谢您的礼貌,但请避免在问题开始时打招呼,因为它们会占用相当大的空间来显示问题的实际内容,主要是因为它们出现在摘要中搜索结果;请记住,问题和答案在任何时候对每个人都有用,寻找与您类似的问题的人更喜欢滚动浏览数十个问题摘要,并且对阅读您的友善程度几乎没有兴趣:-)
  • @musicamante 我记得,我已经添加了问候语。但不知何故,它没有发布。虽然我很着急,所以我没有时间阅读这个问题。感谢您的友好回复。
  • 没有。请仔细阅读我写的内容,因为这与我告诉你的完全相反。请在问题的开头添加问候语。

标签: python printing pyqt5 print-preview qtextdocument


【解决方案1】:

主要问题是self.printpreviewDialogMainWindow 的成员,而不是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()
    

    【讨论】:

      猜你喜欢
      • 2020-04-13
      • 1970-01-01
      • 2022-01-04
      • 2020-06-10
      • 1970-01-01
      • 1970-01-01
      • 2016-01-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多