python - 为什么当我单击提交按钮时,此 PyQt5 发票 GUI 应用程序的“Python 停止工作”?
问题描述
如果你能帮助我解决这个问题,我会很高兴。我正在练习 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()
解决方案
如果你在 shell/prompt 中运行你的代码,你会清楚地看到错误:
>>> Exception "unhandled AttributeError"
'InvoiceForm' object has no attribute 'setCentralWidget'
问题是这setCentralWidget
是 QMainWindow 的一个功能,而您使用的是简单的 QWidget。
虽然您可以尝试InvoiceForm
从 QMainWindow 子类化并相应地修改其布局和逻辑,但它不会很好地工作,因为您犯了一些概念错误:最重要的是尝试向自身添加一个实例,但您之前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()
推荐阅读
- swift - 如何在 SwiftUI 中的特定时间循环播放背景音乐
- python - 我需要什么范围和权限才能让别人从我的 Google 云端硬盘下载文件?
- python - 使用索引列表拆分列表
- python - 使用 xlrd 引擎在 xls 文件上调用 pands read_excel 时出现 AssertionError
- javascript - 时刻不计算天数正确,还是我错了?(我是但为什么)
- r - 对具有多列的两个数据框列表执行多个两个样本 t 检验
- microsoft-graph-api - 状态码:禁止;原因:该请求未对此用户或应用程序授权
- javascript - 通过内部值检测 TD 元素并将其移动到最后
- javascript - 获取人的确切年龄,取决于月份和日期,而不仅仅是年份
- amazon-web-services - Selenium 在远程服务器(AWS EC2)上无法正常工作