在 Python 中对小部件进行子类化以与 Qt Designer 一起使用的最简单方法 [英] Easiest way to subclass a widget in Python for use with Qt Designer

查看:73
本文介绍了在 Python 中对小部件进行子类化以与 Qt Designer 一起使用的最简单方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 PyQt5 构建一个应用程序,(大部分)GUI 是使用 Qt 设计器构建的.我有一些 UI 元素的功能大致相同,将它们的代码移动到从基类继承的类中似乎是明智的(基类又从 QTableWidget 继承).

这看起来非常简单,只是我不确定如何让 Qt Designer 在生成其 UI 文件时使用这些类.我找到了


现在,为了能够从 Designer 添加该小部件,您需要提升一个小部件.提升小部件是 Qt 扩展添加到 UI 的小部件的特性和功能的一种方式.
这非常有用,因为您可以以自己的方式扩展任何标准小部件类:例如,如果您将 QTableView 添加到 UI 并提升它,它允许您从Designer 界面,然后从您的代码中实现其他功能.

这个过程非常简单,只要你知道它是如何工作的.

  1. 创建 UI 并根据要扩展的类添加小部件


在本例中,我使用 QFrame 来展示如何扩展该类属性.只需创建您的界面,并在布局中添加一个 QFrame.

如您所见,Object Inspector 报告的类仍然是 QFrame.

  1. 推广小部件


右键单击要用于课程的小部件,然后选择Promote to...,然后设置Promoted class name";到之前创建的小部件类名,以及头文件"到包含该类的模块名称.我将 python 文件保存为 promoted.py,因此字段值将是 promoted.考虑头文件路径是相对于加载 UI 文件的路径.

然后点击添加"创建新提升的小部件class",最后Promote"正式推广它.之后,您可以将任何小部件提升到先前设置的提升类,只要基类兼容:如果您的界面中有另一个 QFrame,您可以右键单击它并从提升到"中选择提升的类;子菜单.

现在对象检查器中显示的类是TimeFrame.

  1. 设置对象属性、保存、创建代码并运行


由于我们使用的是 QFrame,我们可以设置它的框架(这是因为我们之前也在 paintEvent(event) 方法中调用了基类实现).

现在您只需要实现主小部件的基类.请记住,每次加载提升的小部件时,它的头文件"都会被加载.将被加载,这意味着它的代码将始终运行.这就是为什么将 if __name__ == '__main__': 代码块放在文件中很重要的原因,只要它包含主程序"和程序".和提升的小部件类.

promoted.py 代码:

类测试(QtWidgets.QWidget):def __init__(self):super().__init__()uic.loadUi('test.ui', self)self.timeEdit.timeChanged.connect(self.updateTimeFrame)self.timeEdit.setTime(QtCore.QTime.currentTime())def updateTimeFrame(self):self.timeFrame.time = self.timeEdit.time()如果 __name__ == '__main__':导入系统app = QtWidgets.QApplication(sys.argv)w = 测试()w.show()sys.exit(app.exec())

现在,显然无论如何都不会在 Designer 中显示任何内容,但是只要我们运行我们的代码,提升的小部件就会按预期显示!

现在你也可以知道我创建这个答案所花费的实际时间,很酷;-)

I'm building an app using PyQt5, with (most of) the GUI being built using Qt Designer. I have a few UI elements that do mostly the same thing and it seems sensible to move their code into classes that inherit from a base class (which in turn inherits from QTableWidget).

This seems really straightforward except that I'm not sure how to get Qt Designer to use these classes when generating its UI files. I've found a guide for doing this in PyQt4 and another one that's for Qt 5 but with native C++ code and yet another one that's for PyQt5 but doesn't deal with the (most complicated) aspect of making the subclasses available in Qt Designer.

How do I make a subclass of a widget created using PyQt5 available for use in Qt Designer?

Alternatively, is there an easier way to do this? I don't need to change the appearance or functionality of the widgets—I'm mostly just looking for an organized place to store some code that makes their data easier to set and access.

解决方案

While creating plugins for Qt Designer is possible, it's not an easy task: the official documentation is outdated and sometimes incomplete, and some functions have not been fully ported for a transparent PyQt usage.

The easiest way is to use promoted widgets.

Let's say you have a widget that displays the current time in a fancy way:

from PyQt5 import QtCore, QtGui, QtWidgets, uic

class TimeFrame(QtWidgets.QFrame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.updateTimer = QtCore.QTimer(interval=60000, timeout=self.update)
        self._time = None

    @QtCore.pyqtProperty(QtCore.QTime)
    def time(self):
        return self._time

    @time.setter
    def time(self, time):
        self._time = time
        self.update()

    def paintEvent(self, event):
        super().paintEvent(event)
        size = min(self.width(), self.height()) - 10
        rect = QtCore.QRect(0, 0, size, size)
        rect.moveCenter(self.rect().center())
        qp = QtGui.QPainter(self)
        qp.setRenderHints(qp.Antialiasing)
        qp.drawEllipse(rect)
        qp.setPen(QtCore.Qt.NoPen)
        qp.setBrush(QtCore.Qt.blue)
        time = self._time or QtCore.QTime.currentTime()
        minuteAngle = time.minute() * 6
        qp.drawPie(rect, 1440, -minuteAngle * 16)
        qp.setBrush(QtCore.Qt.red)
        hourAngle = (time.hour() % 12 + time.minute() / 60) * 30
        qp.drawPie(rect.adjusted(30, 30, -30, -30), 1440, -hourAngle * 16)

This is a simple clock widget that shows a pair of "pies", the bigger for minutes, the smaller for hours (in this screenshot, the time is 2:03):


Now, to be able to add that widget from Designer, you'll need to promote a widget. Promoting widgets is a way for Qt to expand the features and functionalities of a widget added to a UI.
This is very useful, because you can expand any standard widget class in your own way: for example, if you add a QTableView to the UI and promote it, it allows you to set the standard QTableView properties from the Designer interface, and then implement other features from your code.

The procedure is pretty simple, once you know how it works.

  1. Create the UI and add a widget based on the class you want to expand


In this case, I'm using a QFrame, to show how that class properties can be expanded. Just create your interface, and add a QFrame to the layout.

As you can see, the class reported from the Object Inspector is still a QFrame.

  1. Promote the widget


Right click on the widget you want to use for your class, and select Promote to..., then set the "Promoted class name" to the widget class name created before, and the "Header file" to the module name that contains that class. I saved the python file as promoted.py, so the field value will be promoted. Consider that header file paths are relative to the path from where the UI file will be loaded.

Then click "Add" to create the new promoted widget "class", and finally "Promote" to officially promote it. After that, you can promote any widget to a previously set promoted class, as long as the base class is compatible: if you have another QFrame in your interface, you can right click on it and select the promoted class from the "Promote to" submenu.

Now the class displayed in the Object Inspector is TimeFrame.

  1. Set object properties, save, create the code and run


Since we're using a QFrame, we can set its frame (this is because we also called the base class implementation in the paintEvent(event) method before).

Now you just have to implement the base class for the main widget. Remember that each time a promoted widget is loaded, its "header file" will be loaded, which means that its code will always be run. That's why it's important that an if __name__ == '__main__': code block is placed in the file as long as it contains both the main "program" and the promoted widget class.

promoted.py code:

class Test(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        uic.loadUi('test.ui', self)

        self.timeEdit.timeChanged.connect(self.updateTimeFrame)

        self.timeEdit.setTime(QtCore.QTime.currentTime())

    def updateTimeFrame(self):
        self.timeFrame.time = self.timeEdit.time()


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Test()
    w.show()
    sys.exit(app.exec())

Now, obviously there won't be anything shown in Designer anyway, but as soon as we run our code, the promoted widget will be displayed as expected!

And now you can also know the actual time it took me to create this answer, cool ;-)

这篇关于在 Python 中对小部件进行子类化以与 Qt Designer 一起使用的最简单方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆