Fer*_*ndr 5 python pyqt qt-designer
假设我有一个自定义 PyQt 小部件,我想在 Qt Designer 中使用它,例如用于 matplotlib 画布:
情节小部件.py:
from PyQt5 import QtWidgets
import matplotlib
matplotlib.use('Qt5Agg')
from matplotlib.backends.backend_qt5agg import (
FigureCanvasQTAgg, NavigationToolbar2QT
)
class PlotWidget(QtWidgets.QWidget):
def __init__(self, parent=None, toolbar=True, figsize=None, dpi=100):
super(PlotWidget, self).__init__(parent)
self.setupUi(toolbar, figsize, dpi)
def setupUi(self, toolbar=True, figsize=None, dpi=100):
layout = QtWidgets.QVBoxLayout(self)
self.figure = Figure(figsize=figsize, dpi=dpi)
self.canvas = FigureCanvasQTAgg(self.figure)
if toolbar:
self.toolbar = NavigationToolbar2QT(self.canvas, self)
layout.addWidget(self.toolbar, 0)
layout.addWidget(self.canvas, 1)
Run Code Online (Sandbox Code Playgroud)
在 Qt Designer 中,我可以轻松地为此提升 QWidget,将头文件设置为“my_module/plot_widget.h”并将其名称设置为 PlotWidget。然后,我可以通过动态加载 ui 文件来构造一个小部件,如下所示:
示例.py:
from PyQt5 import QtWidgets, uic
class MyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
uic.loadUi('example.ui', self)
self.show()
Run Code Online (Sandbox Code Playgroud)
这工作得很好,除了我没有看到修改我拥有的默认初始化参数(在本例中为工具栏、figsize 和 dpi)的直接方法。
我知道可以为此创建自定义插件,但我想避免它,因为我认为这会给我的简单需求增加不必要的复杂性。另外,我希望我的代码尽可能与 PySide / PyQt4 / PyQt5 兼容(为此,我实际上直接使用 QtPy 而不是 PyQt5,尽管这有点题外话),而且我并不完全相信如果我开始使用插件,我就可以实现这一目标。
于是,我想到了以下两种办法:
基本上,我可以简化我的构造函数,以便在那里不调用 setupUi() ,并在加载 ui 文件后执行该调用:
情节小部件.py:
class PlotWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(PlotWidget, self).__init__(parent)
...
Run Code Online (Sandbox Code Playgroud)
示例.py:
from PyQt5 import QtWidgets, uic
class MyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
uic.loadUi('example.ui', self)
self.myPlotWidget.setupUi(figsize=(4, 4), dpi=200)
self.show()
Run Code Online (Sandbox Code Playgroud)
我可以在 Qt Designer 中添加所需的任何动态属性,然后使用它们,而不是向 setupUi() 传递额外的参数,如下所示:
figsize = self.property('figsize')
Run Code Online (Sandbox Code Playgroud)
如果没有定义它们,它们就只是 None。问题在于,在将这些动态属性添加到对象之前调用 init 构造函数(这似乎符合逻辑),因此在这段代码中,figsize 始终为 None:
情节小部件.py:
class PlotWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(PlotWidget, self).__init__(parent)
def setupUi(self):
layout = QtWidgets.QVBoxLayout(self)
toolbar = self.property('toolbar') # Always None
figsize = self.property('figsize') # Always None
dpi = self.property('dpi') # Always None
self.figure = Figure(figsize=figsize, dpi=dpi)
self.canvas = FigureCanvasQTAgg(self.figure)
if toolbar:
self.toolbar = NavigationToolbar2QT(self.canvas, self)
layout.addWidget(self.toolbar, 0)
layout.addWidget(self.canvas, 1)
Run Code Online (Sandbox Code Playgroud)
这意味着我仍然需要在 example.py 文件中显式调用 setupUi() ,除非我能找出其他东西。
我更喜欢方法 2 的想法,因为它允许我直接从 Qt Designer 修改所有 ui 参数。但是,我想避免显式调用 setupUi()。因此,是否有任何 QWidget 方法在将动态属性分配给对象后总是被调用(因此我可以重写它以在那里调用 setupUi() )?
另外,您是否发现这种方法有任何其他缺陷,或者您是否知道实现我想要实现的目标的任何其他替代方法?请注意,我使用了 matplotlib 画布作为示例(可能已经有一些特定的方法),但我想将其用于我可能需要的任何自定义小部件。
我知道距离您自己的回答已经过去了几个月,也许您已经找到了满足您需求的解决方案,也许我还没有完全理解您的情况。
据我所知,我会在设计器中为小部件设置动态属性,然后重载,QObject.setProperty()
或者更好的是pyqtProperty
在自定义小部件中使用,然后使用@property.setter
来设置布局。唯一的问题是必须以正确的顺序声明属性。您可以使用不同的方法来规避。
使用单个 QStringList 动态属性将所有参数设置为(键,值)元组的假字典。
正如您提到的,使用 resizeEvent (或 showEvent),设置类标志,以避免多次调用 setupUi:
class Widget(QtWidgets.QWidget):
shown = False
[...]
def showEvent(self, event):
if not self.shown:
self.shown = True
self.setupUi()
Run Code Online (Sandbox Code Playgroud)每当设置它们的属性时,使用 insertWidget 根据它们的位置将每个小部件添加到布局中(这只适用于 QBoxLayout 并且如果您知道小部件将具有固定的布局结构)
或者,您可以为自定义小部件使用另一个 ui,添加所有可能的升级的plotlib 小部件,然后在装饰器中使用 setVisible() @property.setter
;然后您决定是在init中隐藏它们并仅在设置了它们的属性时才显示它们,还是记住始终设置动态属性。