为啥当我单击提交按钮时,此 PyQt5 发票 GUI 应用程序的“Python 停止工作”? [复制]

Posted

技术标签:

【中文标题】为啥当我单击提交按钮时,此 PyQt5 发票 GUI 应用程序的“Python 停止工作”? [复制]【英文标题】:Why does the "Python stop working" for this PyQt5 invoice GUI application when i clicked submit button? [duplicate]为什么当我单击提交按钮时,此 PyQt5 发票 GUI 应用程序的“Python 停止工作”? [复制] 【发布时间】:2020-09-17 13:38:40 【问题描述】:

如果您能帮我解决这个问题,我会很高兴。我正在练习 PyQt5 Gui 应用程序的编码。这是生成发票的简单 GUI 应用程序。但是每当我单击提交(创建发票)按钮时,它就会停止。我无法弄清楚问题所在。请帮我解决问题。

更新:任何人都可以无错误地修复代码吗? 错误信息是:

>>> Exception "unhandled AttributeError"
'InvoiceForm' object has no attribute 'setCentralWidget'

代码如下:-

import sys

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


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)
        self.layout().addRow(submit)

    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)
        main = QWidget()
        main.setLayout(QHBoxLayout())
        self.setCentralWidget(main)

        form = InvoiceForm()
        main.layout().addWidget(form)
        self.preview = InvoiceView()
        main.layout().addWidget(self.preview)
        form.submitted.connect(self.preview.build_invoice)

class InvoiceView(QTextEdit):
    dpi = 72
    doc_width = 8.5 * dpi
    doc_height = 11 * 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))
        cursor = QTextCursor(document)
        cursor.insertText("Invoice, woohoo!")

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

if __name__ == '__main__':
    main()

【问题讨论】:

首先您可以使用print() 来查看执行了哪部分代码以及您在变量中的内容。也许它运行长时间运行的代码,这会阻止 GUI,它不能工作 - 所以它会冻结。 如果程序崩溃,那么您应该在控制台中运行以查看错误消息 - 并将此错误消息(不是评论)添加为文本(不是图像) 粘贴代码时请多加小心,在问题预览中始终确保格式正确,否则我们将无法区分错误缩进导致的错误。在formatting code 上阅读更多信息。 谢谢大家。我刚刚更新了这个问题。我是***的新手。您的评论对我学习很有帮助。 【参考方案1】:

如果你在 shell/prompt 中运行你的代码,你会清楚地看到错误:

>>> Exception "unhandled AttributeError"
'InvoiceForm' object has no attribute 'setCentralWidget'

问题是setCentralWidget 是QMainWindow 的一个函数,而您使用的是一个简单的QWidget。

虽然您可以尝试从 QMainWindow 继承 InvoiceForm 并相应地修改其布局和逻辑,但它不会很好地工作,因为您犯了一些概念性错误:最重要的是尝试添加一个 InvoiceForm 实例到本身,但您也发出了submitted 信号 将其连接到build_invoice,因此它不会按预期工作。

最好的解决方案是创建一个 QMainWindow 子类并将这些小部件的实例添加到其中。

import sys

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


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)
        self.layout().addRow(submit)

    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):
    # ...

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 main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()

if __name__ == '__main__':
    main()

【讨论】:

您好,由于我是 GUI 新手,我无法理解您的意思。我已经从互联网上复制了代码用于学习目的。我没能跑。你能修复代码的特定部分吗?然后,我将研究错误。非常感谢。 @MinarMahmud 查看更新。

以上是关于为啥当我单击提交按钮时,此 PyQt5 发票 GUI 应用程序的“Python 停止工作”? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用jQuery提交功能时此表单不提交

为啥我必须单击提交按钮 TWICE 才能更新我的 useState() 挂钩?

为啥单击 1 次后此按钮无用?

当我使用线程时,PyQt5 中没有更新 GUI

为啥单击按钮时我的 reactstrap 模式没有打开?

PyQt5,单击按钮后如何打开新窗口