如何从 .ui 文件向 Qt 按钮添加功能?
Posted
技术标签:
【中文标题】如何从 .ui 文件向 Qt 按钮添加功能?【英文标题】:How to add functions to Qt buttons from .ui file? 【发布时间】:2020-11-02 09:40:03 【问题描述】:我刚刚开始使用和发现 PyQt5 和 Qt。我安装了它
pip install pyqt5
。然后我打开pyqt-tools
自带的designer.exe
,做了我的设计。
之后,我使用 pyuic design.ui > dialog.py
将其从 .ui
转换为 .py
。
当我运行app.py
时,它运行良好并且用户界面运行良好。但现在我的问题是:如何添加按下按钮时调用的函数?
这是我的 app.py 代码:
import sys
from PyQt5.QtWidgets import QDialog, QApplication
from dialog import Ui_Form
class AppWindow(QDialog)
def __init__(self):
super().__init__()
self.ui = Ui_Form()
self.ui.setupUi(self)
self.show()
app = QApplication(sys.argv)
w = AppWindow()
w.show()
sys.exit(app.exec_())
【问题讨论】:
澄清一下:您所显示的app.py
显然不是 pyuic
命令的输出。那么,也许用 pyuic 创建的文件实际上是dialog.py
,对吧?
@musicamante 是的,你是对的,我显示了我输入的文件而不是生成的文件,因为我虽然这并不重要
重要的是,使用错误的名称或令人困惑的引用会使您的问题不清楚,并且需要像我们遇到的那样不必要的问题和确认,这可以避免:-)
【参考方案1】:
您的方法实际上是正确的(至少是正确的方法之一):除非您真的(REALLY)知道,否则应该永远修改pyuic
生成的文件你在做什么以及为什么。此外,绝不应模仿他们的行为,因为没有理由这样做。
作为(我会说是强制性的)参考,您可以在有关 using Designer 的官方 PyQt 指南中阅读有关此主题的更多信息。
让我对这个话题做一个重要的前提。
您正在做的事情被称为单一继承方法,这意味着您正在创建一个仅从您正在使用的 QWidget 子类(在本例中为 QDialog)继承的子类,并且您正在使用所谓的 form 类 在其之上构建 ui。表单类是一个标准的 Python object
类型,它负责在调用其 setupUi
函数时在主子类实例之上创建所有子小部件。
结果是子类实例将始终在其范围内引用self
,而其所有子实例都可通过self.ui
对象获得。
一种类似(并且更常见)的方法是多重继承方法。通过这种方式,您可以从 both 继承 QWidget 子类 (QDialog) 和 ui 对象。 UI 结果将是相同的,但使用 self.objectName
而不是 self.ui.objectName
可以更直接地使用小部件。
是否使用其中一个是一个选择问题,请记住,每当您使用多重继承方法时,setupUi
可能会覆盖任何先前设置的实例属性,并且如果您为已经存在的创建新属性对象名称,该对象将不再(直接)可访问。
最后,让我给你另一个建议:虽然没有完全错,但另一个答案中的 link posted 不仅给出了一个非常误导性的建议,而且甚至没有深入挖掘为什么它所指的以前的帖子是错误的。最重要的是,pyuic
创建的文件永远不应该被修改(也不应该尝试模仿他们的行为),原因有很多:不仅在您需要更改的情况下您的 ui 您需要将已经修改的代码与新的代码合并(希望您没有使用 pyuic
覆盖它),但这也会导致对整个对象结构的误解。
因此,在您的情况下(使用单一继承方法),在 Designer 中创建的所有对象实际上都可以使用 self.ui.objectName
访问,而 objectName
是 Designer 的对象检查器中显示的内容。
假设您创建了一个带有单个按钮的 UI,并且您希望在按下该按钮时打印一些内容。如果你没有对你的 ui 做太多的测试和修改,那个按钮的 object name 可能是“pushButton”。
然后,您的代码可能会如下所示:
import sys
from PyQt5.QtWidgets import QDialog, QApplication
from dialog import Ui_Form
class AppWindow(QDialog)
def __init__(self):
super().__init__()
self.ui = Ui_Form()
self.ui.setupUi(self)
self.ui.pushButton.clicked.connect(self.buttonClicked)
def buttonClicked(self):
print('button clicked!')
app = QApplication(sys.argv)
w = AppWindow()
w.show()
sys.exit(app.exec_())
请注意,Qt 对象名称不是唯一的。理论上,您可以为多个 QObject(或 QWidget)设置相同的对象名称,但这样做没有任何用处。从 python 的角度考虑:每个变量都应该有自己的名字,如果你覆盖了一个已经存在的变量名,前一个变量将变得不可访问(如果甚至没有被垃圾回收)。
设计器足够聪明(不是它需要这么多......)以防止创建共享相同对象名称的对象,这就是为什么如果您添加另一个按钮,它将被命名为pushButton_2
、pushButton_3
等。PyQt 需要无论您使用的是pyuic
文件还是uic.loadUi()
,都可以通过为所有对象名称创建唯一的实例属性来从中受益。
为了完整起见,以下是 多重继承 方法的外观,同时表现如上例:
import sys
from PyQt5.QtWidgets import QDialog, QApplication
from dialog import Ui_Form
class AppWindow(QDialog, Ui_Form)
def __init__(self):
super().__init__()
self.setupUi(self)
self.pushButton.clicked.connect(self.buttonClicked)
def buttonClicked(self):
print('button clicked!')
app = QApplication(sys.argv)
w = AppWindow()
w.show()
sys.exit(app.exec_())
根据上面的解释,考虑属性名问题:如果你在调用self.setupUi
之前创建了一个名为self.pushButton
的属性,那么之后调用self.pushButton
将是对按钮的引用和之前设置的值会迷路;如果您在调用self.setupUi
之后创建一个名为self.pushButton
的新属性,您将无法再访问QPushButton 实例。
现在,虽然对于正常使用,它们的行为方式通常相同,但使用pyuic
生成的文件或uic.loadUiType()
与更直接的uic.loadUi
之间存在细微但仍然重要的差异,不幸的是这些差异没有正式记录(坦率地说,我不记得所有这些)。
eyllanesc 之前在问题Size of verticalLayout is different in Qt Designer and PyQt program 中报告了一个示例。
考虑到这些是非常具体的例外情况(甚至可能在 PyQt 的未来版本中得到解决,尤其是现在 Qt6 即将推出)并且它们通常不会对标准编程造成重大问题.
最后(终于!),虽然设计师通常很聪明,但并不完美。例如,作为 QAction 分隔符的“技术上不重要”的对象被认为是“匿名的”,并且设计者甚至不应用它们的对象名称:如果您将分隔符添加到菜单,则无法直接访问它,即使您主动命名它。
【讨论】:
【参考方案2】:我建议您不要在转换后的 pythonscript 中编程。只是因为如果您想更改布局并再次转换它,您会覆盖那里编程的所有内容。
将此转换后的 ui 导入新的 pythonscript。有很多这样的介绍(谷歌他们) https://nitratine.net/blog/post/how-to-import-a-pyqt5-ui-file-in-a-python-gui/
然后你应该阅读槽和信号(你也可以在 qt 设计器中添加它们)或在 pythonscript 中添加函数: https://www.tutorialspoint.com/pyqt/pyqt_signals_and_slots.htm
这是按照教程了解它们的最简单方法...
【讨论】:
是的,谢谢,我查看了您提供的链接并按照指南进行操作,谢谢。 @user123 实际上,OP 在代码示例中显示的是 not 从 pyuic 转换的文件,并且在使用uic.loadUi
时非常好(我通常更喜欢方法,实际上),它没有正确回答问题,即如何使用 pyuic 生成的文件实现功能。
@musicamante 我实际上使用导入的 .ui 和生成的 pyuic 进行了测试,它们都可以工作,我缺少的主要部分是 self.findChild(QtWidgets.QPushButton, 'printButton').clicked.connect(self.function)
,它适用于您导入 ui 的任何方法,只需输入我刚才在self.show()
之前提到的那条线,它在这两种情况下都适用。
两种方法都得到几乎相同的结果,但我将在另一个答案中解决一些细微(但有时很重要)的差异。除此之外,该指南给出了一个非常bad 的建议,即使用findChild
来获取对象。这不仅具有误导性,而且完全没有必要,因为 pyuic 构建文件和 uic 模块都已经提供了对在 Designer 中创建的对象的直接访问。对于您的最后一条评论,您的“printButton”已经可以通过self.printButton
(或self.ui.printButton
,如果您使用单继承)访问。
@musicamante 我将如何实现单继承?既然您说它具有误导性,那么 findChild 的替代方法是什么?以上是关于如何从 .ui 文件向 Qt 按钮添加功能?的主要内容,如果未能解决你的问题,请参考以下文章