QWidget如何刷新显示当前信息?
How do I refresh QWidget to display the current information?
我有一个 PySide2 GUI,它在第一页上接受用户的数字,然后进行一些计算并在第二页上显示结果。每个页面都是 QStackedWidget 中的一个 QWidget。第二页(结果页)上有一个按钮,可让用户返回第一页以输入新号码。
我的问题是,当我输入一个新数字时,结果与第一个数字没有任何变化。我使用打印语句来确认结果页面上的标签正在更新,但显示保持不变。
# importing the module
import os
import sys
from PySide2 import QtWidgets
import PySide2.QtUiTools as QtUiTools
class IncomeScreen(QtWidgets.QMainWindow):
def __init__(self):
super(IncomeScreen, self).__init__()
# Load the IncomeScreen ui
loader = QtUiTools.QUiLoader()
path = os.path.join(os.path.dirname(__file__), "main.ui")
self.main = loader.load(path, self)
# Connect the signals with custom slots
self.main.calculate_pushButton.clicked.connect(self.calculate)
def calculate(self):
init_amount = self.main.income_lineEdit.text()
IncomeScreen.init_amount = float(init_amount)
# Create an instance of DistributionScreen class
self.distribution = DistributionScreen()
# Add DistributionScreen to the stacked widget
widget.addWidget(self.distribution)
# Change index to show DownloadPage
widget.setCurrentIndex(widget.currentIndex()+1)
class DistributionScreen(QtWidgets.QMainWindow):
def __init__(self):
super(DistributionScreen, self).__init__()
loader = QtUiTools.QUiLoader()
path = os.path.join(os.path.dirname(__file__), "dialog.ui")
self.dialog = loader.load(path, self)
# Set initial amount to label
self.dialog.initialAmount_label.setText(str(IncomeScreen.init_amount))
print("Initial Amount = {:0.2f}".format(IncomeScreen.init_amount))
# 10 Percent
ten = IncomeScreen.init_amount * 0.1
print("10% = {:0.2f}".format(ten))
self.dialog.label_10percent.setText("{:0.2f}".format(ten))
print(self.dialog.label_10percent.text())
# 20 percent
twenty = IncomeScreen.init_amount * 0.2
print("20% = {:0.2f}".format(twenty))
self.dialog.label_20percent.setText("{:0.2f}".format(twenty))
print(self.dialog.label_20percent.text())
# Update widget
self.dialog.update()
# Connect the signals with custom slots
self.dialog.reset_pushButton.clicked.connect(self.reset)
def reset(self):
print("reset")
# Change index to show IncomeScreen
widget.setCurrentIndex(widget.currentIndex()-1)
# main
# if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
income = IncomeScreen()
widget = QtWidgets.QStackedWidget()
widget.addWidget(income)
widget.show()
try:
sys.exit(app.exec_())
except:
print("Exiting")
我也在使用 Python 3.7.4
编辑:您可以下载 ui 个文件 here
您的代码存在各种问题,但最重要的是每次调用 calculate
时,都会 添加一个新的 DistributionScreen
堆叠小部件,但 widget.setCurrentIndex(widget.currentIndex()+1)
将始终转到堆叠小部件的 second 索引(这是您创建的 first 实例) .
一个可能的简单解决方法是使用 addWidget
返回的小部件的索引或使用 setCurrentWidget
:
def calculate(self):
init_amount = self.main.income_lineEdit.text()
IncomeScreen.init_amount = float(init_amount)
self.distribution = DistributionScreen()
index = widget.addWidget(self.distribution)
widget.setCurrentIndex(index)
# alternatively:
widget.setCurrentWidget(self.distribution)
不幸的是,虽然这会使您的代码正常工作,但这不是一个有效的解决方案,因为还有其他 重要 问题迟早会产生其他问题:
- 堆叠小部件的工作方式类似于选项卡小部件:它旨在允许小部件 可重用性;你不应该每次都不断地创建一个新实例,但可以使用现有的实例;
- 您应该不为依赖于实例的变量设置或使用class属性(就像您对
IncomeScreen.init_amount
所做的那样);
- 您正在将 QMainWindows 添加到堆叠小部件中,这是不鼓励的,因为主要 window 应该用作顶级 window(它具有依赖于该方面的功能);请注意,即使 QDialog 也不是有效的候选者,您应该选择基本的 QWidget 或 QFrame 或 QGroupBox 等容器;
- 您正在使用
QUiLoader
将小部件加载为主要 window 的子部件,但没有将其添加到布局(或设置为中央小部件),这将使它成为每当顶层 window 调整大小时无法自行调整大小:如果主 window 变得太小,一些内容将不可见,如果它太大,将会有很多未使用的 space;
- 您正在尝试从实例访问全局变量 (
widget
),但不能保证该变量有效;在任何情况下,它应该不是创建新小部件和设置堆叠小部件索引的实例,而是堆叠小部件本身(或其任何祖先);
- 最后一个 try/except 块非常危险,因为它会阻止您捕获异常(因为它是一个通用的
except:
)或在程序崩溃时知道您的程序出了什么问题;
这可能是对您的代码的修订(未经测试,因为您没有提供 ui
文件)。
import os
import sys
from PySide2 import QtWidgets, QtCore
import PySide2.QtUiTools as QtUiTools
class IncomeScreen(QtWidgets.QWidget):
# a custom signal to notify that we want to show the distribution page
# with the provided value
goToDistribution = QtCore.Signal(float)
def __init__(self):
super(IncomeScreen, self).__init__()
# Load the IncomeScreen ui
loader = QtUiTools.QUiLoader()
path = os.path.join(os.path.dirname(__file__), "main.ui")
self.main = loader.load(path, self)
# a proper layout that manages the contents loaded with QUiLoader
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.main)
# Connect the signals with custom slots
self.main.calculate_pushButton.clicked.connect(self.calculate)
def calculate(self):
init_amount = self.main.income_lineEdit.text()
self.goToDistribution.emit(float(init_amount))
class DistributionScreen(QtWidgets.QWidget):
reset = QtCore.Signal()
def __init__(self):
super(DistributionScreen, self).__init__()
loader = QtUiTools.QUiLoader()
path = os.path.join(os.path.dirname(__file__), "dialog.ui")
self.dialog = loader.load(path, self)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.dialog)
self.dialog.reset_pushButton.clicked.connect(self.reset)
def setIncome(self, value):
# Set initial amount to label
self.dialog.initialAmount_label.setText(str(value))
print("Initial Amount = {:0.2f}".format(value))
# 10 Percent
ten = value * 0.1
print("10% = {:0.2f}".format(ten))
self.dialog.label_10percent.setText("{:0.2f}".format(ten))
print(self.dialog.label_10percent.text())
# 20 percent
twenty = value * 0.2
print("20% = {:0.2f}".format(twenty))
self.dialog.label_20percent.setText("{:0.2f}".format(twenty))
print(self.dialog.label_20percent.text())
class MainWidget(QtWidgets.QStackedWidget):
def __init__(self):
super(MainWidget, self).__init__()
# create *both* the pages here
self.income = IncomeScreen()
self.addWidget(self.income)
self.distribution = DistributionScreen()
self.addWidget(self.distribution)
self.income.goToDistribution.connect(self.goToDistribution)
self.distribution.reset.connect(self.reset)
def goToDistribution(self, value):
# we received the notification signal, then we set the value and
# show the related page by switching to it
self.distribution.setIncome(value)
self.setCurrentWidget(self.distribution)
def reset(self):
self.setCurrentWidget(self.income)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mainWidget = MainWidget()
mainWidget.show()
sys.exit(app.exec_())
注意:
- 如果你想要一个数字控件,你应该使用QSpinBox or QDoubleSpinBox (for floating point numbers), or set a QIntValidator or QDoubleValidator,否则如果用户输入一个非数字值你的程序会崩溃(由于使用
float()
done without previously checking if该字符串实际上是一个有效数字);
- 虽然 QUiLoader 很有用,但它的缺点是总是创建一个小部件,所以你永远不能覆盖它的方法;唯一的解决方案是使用
pyside-uic
生成的文件并使用多重继承方法,或者切换到 PyQt 并使用它的 uic.loadUi
而允许在 当前 小部件;
- 您代码中的大部分问题是由于最近共享的一些教程(其中一些在 youtube 上)造成的:不幸的是,这些教程建议 很多 应该 不 完成的可怕事情,对于 PyQt 和 Python 都是如此;我强烈建议您寻找其他资源,最重要的是,请始终研究文档。
我有一个 PySide2 GUI,它在第一页上接受用户的数字,然后进行一些计算并在第二页上显示结果。每个页面都是 QStackedWidget 中的一个 QWidget。第二页(结果页)上有一个按钮,可让用户返回第一页以输入新号码。
我的问题是,当我输入一个新数字时,结果与第一个数字没有任何变化。我使用打印语句来确认结果页面上的标签正在更新,但显示保持不变。
# importing the module
import os
import sys
from PySide2 import QtWidgets
import PySide2.QtUiTools as QtUiTools
class IncomeScreen(QtWidgets.QMainWindow):
def __init__(self):
super(IncomeScreen, self).__init__()
# Load the IncomeScreen ui
loader = QtUiTools.QUiLoader()
path = os.path.join(os.path.dirname(__file__), "main.ui")
self.main = loader.load(path, self)
# Connect the signals with custom slots
self.main.calculate_pushButton.clicked.connect(self.calculate)
def calculate(self):
init_amount = self.main.income_lineEdit.text()
IncomeScreen.init_amount = float(init_amount)
# Create an instance of DistributionScreen class
self.distribution = DistributionScreen()
# Add DistributionScreen to the stacked widget
widget.addWidget(self.distribution)
# Change index to show DownloadPage
widget.setCurrentIndex(widget.currentIndex()+1)
class DistributionScreen(QtWidgets.QMainWindow):
def __init__(self):
super(DistributionScreen, self).__init__()
loader = QtUiTools.QUiLoader()
path = os.path.join(os.path.dirname(__file__), "dialog.ui")
self.dialog = loader.load(path, self)
# Set initial amount to label
self.dialog.initialAmount_label.setText(str(IncomeScreen.init_amount))
print("Initial Amount = {:0.2f}".format(IncomeScreen.init_amount))
# 10 Percent
ten = IncomeScreen.init_amount * 0.1
print("10% = {:0.2f}".format(ten))
self.dialog.label_10percent.setText("{:0.2f}".format(ten))
print(self.dialog.label_10percent.text())
# 20 percent
twenty = IncomeScreen.init_amount * 0.2
print("20% = {:0.2f}".format(twenty))
self.dialog.label_20percent.setText("{:0.2f}".format(twenty))
print(self.dialog.label_20percent.text())
# Update widget
self.dialog.update()
# Connect the signals with custom slots
self.dialog.reset_pushButton.clicked.connect(self.reset)
def reset(self):
print("reset")
# Change index to show IncomeScreen
widget.setCurrentIndex(widget.currentIndex()-1)
# main
# if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
income = IncomeScreen()
widget = QtWidgets.QStackedWidget()
widget.addWidget(income)
widget.show()
try:
sys.exit(app.exec_())
except:
print("Exiting")
我也在使用 Python 3.7.4
编辑:您可以下载 ui 个文件 here
您的代码存在各种问题,但最重要的是每次调用 calculate
时,都会 添加一个新的 DistributionScreen
堆叠小部件,但 widget.setCurrentIndex(widget.currentIndex()+1)
将始终转到堆叠小部件的 second 索引(这是您创建的 first 实例) .
一个可能的简单解决方法是使用 addWidget
返回的小部件的索引或使用 setCurrentWidget
:
def calculate(self):
init_amount = self.main.income_lineEdit.text()
IncomeScreen.init_amount = float(init_amount)
self.distribution = DistributionScreen()
index = widget.addWidget(self.distribution)
widget.setCurrentIndex(index)
# alternatively:
widget.setCurrentWidget(self.distribution)
不幸的是,虽然这会使您的代码正常工作,但这不是一个有效的解决方案,因为还有其他 重要 问题迟早会产生其他问题:
- 堆叠小部件的工作方式类似于选项卡小部件:它旨在允许小部件 可重用性;你不应该每次都不断地创建一个新实例,但可以使用现有的实例;
- 您应该不为依赖于实例的变量设置或使用class属性(就像您对
IncomeScreen.init_amount
所做的那样); - 您正在将 QMainWindows 添加到堆叠小部件中,这是不鼓励的,因为主要 window 应该用作顶级 window(它具有依赖于该方面的功能);请注意,即使 QDialog 也不是有效的候选者,您应该选择基本的 QWidget 或 QFrame 或 QGroupBox 等容器;
- 您正在使用
QUiLoader
将小部件加载为主要 window 的子部件,但没有将其添加到布局(或设置为中央小部件),这将使它成为每当顶层 window 调整大小时无法自行调整大小:如果主 window 变得太小,一些内容将不可见,如果它太大,将会有很多未使用的 space; - 您正在尝试从实例访问全局变量 (
widget
),但不能保证该变量有效;在任何情况下,它应该不是创建新小部件和设置堆叠小部件索引的实例,而是堆叠小部件本身(或其任何祖先); - 最后一个 try/except 块非常危险,因为它会阻止您捕获异常(因为它是一个通用的
except:
)或在程序崩溃时知道您的程序出了什么问题;
这可能是对您的代码的修订(未经测试,因为您没有提供 ui
文件)。
import os
import sys
from PySide2 import QtWidgets, QtCore
import PySide2.QtUiTools as QtUiTools
class IncomeScreen(QtWidgets.QWidget):
# a custom signal to notify that we want to show the distribution page
# with the provided value
goToDistribution = QtCore.Signal(float)
def __init__(self):
super(IncomeScreen, self).__init__()
# Load the IncomeScreen ui
loader = QtUiTools.QUiLoader()
path = os.path.join(os.path.dirname(__file__), "main.ui")
self.main = loader.load(path, self)
# a proper layout that manages the contents loaded with QUiLoader
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.main)
# Connect the signals with custom slots
self.main.calculate_pushButton.clicked.connect(self.calculate)
def calculate(self):
init_amount = self.main.income_lineEdit.text()
self.goToDistribution.emit(float(init_amount))
class DistributionScreen(QtWidgets.QWidget):
reset = QtCore.Signal()
def __init__(self):
super(DistributionScreen, self).__init__()
loader = QtUiTools.QUiLoader()
path = os.path.join(os.path.dirname(__file__), "dialog.ui")
self.dialog = loader.load(path, self)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.dialog)
self.dialog.reset_pushButton.clicked.connect(self.reset)
def setIncome(self, value):
# Set initial amount to label
self.dialog.initialAmount_label.setText(str(value))
print("Initial Amount = {:0.2f}".format(value))
# 10 Percent
ten = value * 0.1
print("10% = {:0.2f}".format(ten))
self.dialog.label_10percent.setText("{:0.2f}".format(ten))
print(self.dialog.label_10percent.text())
# 20 percent
twenty = value * 0.2
print("20% = {:0.2f}".format(twenty))
self.dialog.label_20percent.setText("{:0.2f}".format(twenty))
print(self.dialog.label_20percent.text())
class MainWidget(QtWidgets.QStackedWidget):
def __init__(self):
super(MainWidget, self).__init__()
# create *both* the pages here
self.income = IncomeScreen()
self.addWidget(self.income)
self.distribution = DistributionScreen()
self.addWidget(self.distribution)
self.income.goToDistribution.connect(self.goToDistribution)
self.distribution.reset.connect(self.reset)
def goToDistribution(self, value):
# we received the notification signal, then we set the value and
# show the related page by switching to it
self.distribution.setIncome(value)
self.setCurrentWidget(self.distribution)
def reset(self):
self.setCurrentWidget(self.income)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mainWidget = MainWidget()
mainWidget.show()
sys.exit(app.exec_())
注意:
- 如果你想要一个数字控件,你应该使用QSpinBox or QDoubleSpinBox (for floating point numbers), or set a QIntValidator or QDoubleValidator,否则如果用户输入一个非数字值你的程序会崩溃(由于使用
float()
done without previously checking if该字符串实际上是一个有效数字); - 虽然 QUiLoader 很有用,但它的缺点是总是创建一个小部件,所以你永远不能覆盖它的方法;唯一的解决方案是使用
pyside-uic
生成的文件并使用多重继承方法,或者切换到 PyQt 并使用它的uic.loadUi
而允许在 当前 小部件; - 您代码中的大部分问题是由于最近共享的一些教程(其中一些在 youtube 上)造成的:不幸的是,这些教程建议 很多 应该 不 完成的可怕事情,对于 PyQt 和 Python 都是如此;我强烈建议您寻找其他资源,最重要的是,请始终研究文档。