我正在尝试在一个小项目中优化示例(https://matplotlib.org/stable/gallery/widgets/check_buttons.html)。 我有一个有效的例子:
from PyQt5.QtWidgets import QApplication
import numpy as np
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.widgets import CheckButtons
from ShortCircuitCalc.gui.windows import CustomGraphicView, MainWindow
from PyQt5 import QtWidgets
class Window(CustomGraphicView):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.canvas = FigureCanvas(Figure(figsize=(5, 3)))
t = np.arange(0.0, 2.0, 0.01)
s0 = np.sin(2 * np.pi * t)
s1 = np.sin(4 * np.pi * t)
s2 = np.sin(6 * np.pi * t)
ax = self.canvas.figure.subplots()
l0, = ax.plot(t, s0, visible=False, lw=2, color="k", label="2 Hz")
l1, = ax.plot(t, s1, lw=2, color="r", label="4 Hz")
l2, = ax.plot(t, s2, lw=2, color="g", label="6 Hz")
self.canvas.figure.subplots_adjust(left=0.2)
self.lines = [l0, l1, l2]
rax = self.canvas.figure.add_axes([0.05, 0.4, 0.1, 0.15])
self.labels = [str(line.get_label()) for line in self.lines]
visibility = [line.get_visible() for line in self.lines]
self.check = CheckButtons(rax, self.labels, visibility)
self.check.on_clicked(self.is_click)
def is_click(self, label):
index = self.labels.index(label)
self.lines[index].set_visible(not self.lines[index].get_visible())
self.canvas.draw()
def main():
import sys
app = QApplication(sys.argv)
w = MainWindow()
r = Window()
w.resultView.scene = QtWidgets.QGraphicsScene()
w.resultView.scene.addWidget(r.canvas)
w.resultView.setScene(w.resultView.scene)
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
我正确地得到了下一个工作模型:
但是我想为我图中的每个子图获得这样一个活跃的模型。 我尝试修改上面的示例,甚至得到了我需要的布局,但其上的按钮不是交互式的。我如何改进它以获得每个交互式子图。目前我有这段代码和这些结果:
from PyQt5.QtWidgets import QApplication
import numpy as np
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.widgets import CheckButtons
from ShortCircuitCalc.gui.windows import CustomGraphicView, MainWindow
from PyQt5 import QtWidgets
class Window(CustomGraphicView):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
ncols = 5
nrows = 8
self.canvas = FigureCanvas(Figure(figsize=(ncols * 5, nrows * 3)))
t = np.arange(0.0, 2.0, 0.01)
s0 = np.sin(2 * np.pi * t)
s1 = np.sin(4 * np.pi * t)
s2 = np.sin(6 * np.pi * t)
ax = self.canvas.figure.subplots(ncols=ncols, nrows=nrows)
for col in range(ncols):
for row in range(nrows):
(l0,) = ax[row, col].plot(t, s0, visible=False, lw=2, color="k", label="2 Hz")
(l1,) = ax[row, col].plot(t, s1, lw=2, color="r", label="4 Hz")
(l2,) = ax[row, col].plot(t, s2, lw=2, color="g", label="6 Hz")
lines = [l0, l1, l2]
rax = ax[row, col].inset_axes([0.05, 0.4, 0.1, 0.15])
labels = [str(line.get_label()) for line in lines]
visibility = [line.get_visible() for line in lines]
self.check = CheckButtons(rax, labels, visibility)
self.check.on_clicked(self.is_click)
self.canvas.figure.subplots_adjust(left=0.2)
def is_click(self, label):
# index = self.labels.index(label)
# self.lines[index].set_visible(not self.lines[index].get_visible())
# self.canvas.draw()
print('Button is pressed')
def main():
import sys
app = QApplication(sys.argv)
w = MainWindow()
r = Window()
w.resultView.scene = QtWidgets.QGraphicsScene()
w.resultView.scene.addWidget(r.canvas)
w.resultView.setScene(w.resultView.scene)
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
在我的代码中,CustomGraphicView 只是一个带有一些导航调整的 QGraphicsView。目前CustomGraphicView的实现及其用例如下:
import sys
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import (
FigureCanvasQTAgg as FigCanvas,
NavigationToolbar2QT as NavToolbar,
)
from PyQt5 import QtWidgets, QtCore, QtGui
class CustomGraphicView(QtWidgets.QGraphicsView):
def __init__(self,
parent: QtWidgets = None,
figure: matplotlib.figure.Figure = None,
title: str = 'Viewer') -> None:
super(CustomGraphicView, self).__init__(parent)
self._title = title
self._figure = figure
self._scene = QtWidgets.QGraphicsScene()
if parent is not None:
self._canvas = None
else:
self._canvas = FigCanvas(self._figure)
self._scene.addWidget(self._canvas)
self.setScene(self._scene)
self.setWindowTitle(self._title)
self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
# Start viewing position
self.horizontalScrollBar().setSliderPosition(1)
self.verticalScrollBar().setSliderPosition(1)
self._zoom = 0
self._mousePressed = False
self._drag_pos = None
self.save_model_action = QtWidgets.QAction('Save model as ...', self)
self.save_model_action.triggered.connect(self.save_model)
self.save_fragment_action = QtWidgets.QAction('Save fragment as ...', self)
self.save_fragment_action.triggered.connect(self.save_fragment)
def set_figure(self, figure):
self._figure = figure
self._canvas = FigCanvas(self._figure)
self._scene.addWidget(self._canvas)
self.setScene(self._scene)
def mousePressEvent(self, event: QtCore.Qt.MouseButton.LeftButton) -> None:
if event.button() == QtCore.Qt.MouseButton.LeftButton:
self._mousePressed = True
self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.OpenHandCursor))
self._drag_pos = event.pos()
event.accept()
else:
super(CustomGraphicView, self).mousePressEvent(event)
def mouseMoveEvent(self, event: QtCore.Qt.MouseButton.LeftButton) -> None:
if self._mousePressed:
new_pos = event.pos()
diff = new_pos - self._drag_pos
self._drag_pos = new_pos
self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() - diff.x())
self.verticalScrollBar().setValue(self.verticalScrollBar().value() - diff.y())
self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.OpenHandCursor))
else:
super(CustomGraphicView, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event: QtCore.Qt.MouseButton.LeftButton) -> None:
if event.button() == QtCore.Qt.MouseButton.LeftButton:
self._mousePressed = False
self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.ArrowCursor))
super(CustomGraphicView, self).mouseReleaseEvent(event)
def wheelEvent(self, event: QtCore.Qt.KeyboardModifier.ControlModifier) -> None:
modifiers = QtWidgets.QApplication.keyboardModifiers()
if modifiers == QtCore.Qt.KeyboardModifier.ControlModifier:
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > -1:
self.scale(factor, factor)
else:
self._zoom = 0
else:
super(CustomGraphicView, self).wheelEvent(event)
def contextMenuEvent(self, event: QtGui.QContextMenuEvent) -> None:
# Creating context menu
menu = QtWidgets.QMenu(self)
menu.addAction(self.save_model_action)
# Creating / adding separator
separator = QtWidgets.QAction(self)
separator.setSeparator(True)
menu.addAction(separator)
menu.addAction(self.save_fragment_action)
menu.exec(event.globalPos())
def save_model(self):
NavToolbar.save_figure(self._figure)
def save_fragment(self):
rect_region = QtCore.QRect(0, 0,
self.width() - self.verticalScrollBar().width(),
self.height() - self.horizontalScrollBar().height())
pixmap = self.grab(rect_region)
fname = QtWidgets.QFileDialog.getSaveFileName(self, 'Save fragment as ...', 'image.png',
'Portable Network Graphics (*.png);;'
'Joint Photographic Experts Group (*.jpeg *.jpg)')[0]
if fname:
pixmap.save(fname)
if __name__ == '__main__':
# Some figure
fig, ax = plt.subplots()
t = np.arange(0.0, 2.0, 0.01)
s0 = np.sin(2 * np.pi * t)
s1 = np.sin(4 * np.pi * t)
s2 = np.sin(6 * np.pi * t)
ax.plot(t, s0, lw=2, color="k", label="2 Hz")
ax.plot(t, s1, lw=2, color="r", label="4 Hz")
ax.plot(t, s2, lw=2, color="g", label="6 Hz")
app = QtWidgets.QApplication(sys.argv)
window = CustomGraphicView(parent=None, figure=fig)
window.show()
sys.exit(app.exec_())
# Or i may
# app = QtWidgets.QApplication(sys.argv)
# window = CustomGraphicView(parent=None, figure=None)
# window.set_figure(fig)
# window.show()
# sys.exit(app.exec_())
使用字典和以下方法解决了这个问题,现在我得到了我期望的模型的行为。
from collections import namedtuple
import numpy as np
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.widgets import CheckButtons
from ShortCircuitCalc.gui.windows import CustomGraphicView, MainWindow
from PyQt5.QtWidgets import QApplication
from PyQt5 import QtWidgets
class Window(CustomGraphicView):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
ncols = 2
nrows = 3
self.canvas = FigureCanvas(Figure(figsize=(ncols * 5, nrows * 3)))
t = np.arange(0.0, 2.0, 0.01)
s0 = np.sin(2 * np.pi * t)
s1 = np.sin(4 * np.pi * t)
s2 = np.sin(6 * np.pi * t)
self.ax = self.canvas.figure.subplots(ncols=ncols, nrows=nrows)
self.checks = dict()
for row in range(nrows):
for col in range(ncols):
ax = self.ax[row, col]
# Check buttons work correctly!
rax = self.canvas.figure.add_axes([ax.get_position().x0, ax.get_position().y0,
ax.get_position().width / 2, ax.get_position().height / 2],
frameon=False)
# Check buttons in inserted axes is not interactive!!!
# rax = ax.inset_axes([0.05, 0.4, 0.1, 0.15])
(l0,) = ax.plot(t, s0, visible=False, lw=2, color="k", label="2 Hz")
(l1,) = ax.plot(t, s1, lw=2, color="r", label="4 Hz")
(l2,) = ax.plot(t, s2, lw=2, color="g", label="6 Hz")
lines = [l0, l1, l2]
labels = [str(line.get_label()) for line in lines]
visibility = [line.get_visible() for line in lines]
check = CheckButtons(rax, labels, visibility)
check.on_clicked(lambda label, i=row, j=col: self.is_click(label, i, j))
DictButton = namedtuple("DictButton", ["check", "lines", "labels", "visibility"])
self.checks[row, col] = DictButton(check, lines, labels, visibility)
def is_click(self, label, i, j):
index = self.checks[i, j].labels.index(label)
self.checks[i, j].lines[index].set_visible(
not self.checks[i, j].lines[index].get_visible()
)
self.canvas.draw()
print('Button is pressed')
def main():
import sys
app = QApplication(sys.argv)
w = MainWindow()
r = Window()
w.resultView.scene = QtWidgets.QGraphicsScene()
w.resultView.scene.addWidget(r.canvas)
w.resultView.setScene(w.resultView.scene)
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
重要提示!!! 不要在插入轴中使用复选按钮。
我是 PyQt 和 matplotlib 的新手,这段代码只是一个测试版本,我自己测试了新功能,如果这些想法对其他人有帮助,我将很高兴。