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)
在Qt Designer中,我可以轻松地将QWidget推广到这个领域,将头文件设置为“my_module/plot_widget.h”,名称设置为PlotWidget。然后,我可以通过动态加载构建一个小部件,例如:
example.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()
这很好用,但我还没有找到一种直接修改默认的初始化参数(在这种情况下是工具栏、画布大小和分辨率)的简单方法。
我知道有可能创建自定义插件来解决这个问题,但我想避免这样做,因为我认为这会给我简单的需求增加不必要的复杂性。另外,我希望我的代码尽可能与PySide/ PyQt4/ PyQt5兼容(为此,我实际上使用的是QtPy而不是直接使用PyQt5,尽管这有点离题),如果开始使用插件,我并不完全相信我能够实现这一点。
因此,我想到了以下两种方法:
方法1:以编程方式调用setupUi()
基本上,我可以简化我的构造函数,使它不在那里调用setupUi(),并在加载ui文件后进行该调用:
plot_widget.py:
class PlotWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(PlotWidget, self).__init__(parent)
...
example.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()
方法二:使用动态属性
不需要向setupUi()传递额外的参数,我可以在Qt Designer中添加任何需要的动态属性,然后像这样使用它们:
figsize = self.property('figsize')
如果它们没有被定义,它们将只是空值。问题在于 init 构造函数在这些动态属性添加到对象之前被调用(这似乎是合理的),因此 figsize 在这段代码中始终为 None: plot_widget.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)
这意味着在我的example.py文件中仍需要显式调用setupUi(),除非我可以想出其他方法。
结论
我更喜欢第二种方法的想法,因为它允许我直接从Qt Designer修改所有ui参数。然而,我想避免那个显式调用setupUi()。因此,是否有任何QWidget方法总是在动态属性分配给对象后被调用(因此我可以重写它来在那里调用setupUi())?
另外,您是否看到这种方法中的任何其他缺陷,或者您是否知道实现我所尝试实现的任何其他替代方法?请注意,我已经使用matplotlib画布作为示例(对于其可能已经存在一些特定方法),但我想将其用于我可能需要的任何自定义小部件。
pyqtProperty
方法在动态属性彼此独立或者我期望所有依赖的动态属性被定义在设计器中(在这种情况下,很容易检查哪个属性是最后一个被设置的,并在那时调用setupUi
),或调用setupUi
方法不太昂贵时似乎效果很好(在这种情况下,我不需要进行检查)。在许多情况下,这足以对自定义窗口小部件进行设计时调整。但对于PlotWidget
,我可能会坚持使用resizeEvent
方法。 - FernAndr