我有一个程序,通过点击PyQT应用程序上的按钮,在另一个窗口中显示matplotlib图,当我点击按钮时,该窗口显示,但在图上没有任何事情发生,在控制台中我得到了这个。
QCoreApplication::exec: The event loop is already running.
我有一个3个类和文件与PyQT5 Gui.3类从TextEdits获取值,调用第二个类,计算和返回列表与x,y坐标显示在matplotlib图(第一类)。我找到了不同的方法来解决这个问题,但是有一些简单的例子,我不明白如何将它们应用到我的代码中。
from PyQt5 import QtCore, QtGui, QtWidgets
from pythongui4 import Ui_MainWindow
from scipy import integrate
from random import randint
from math import sin, cos, pi
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import sys
import time
# GLOBAL CONSTANTS:
g = 9.80665 # (Gravity acceleration on the Earth), m/s^2
class GraphTrajectoryAnimation(animation.FuncAnimation):
def __init__(self, x, y, color, xlim, ylim, parent=None, m=50, lw=3, width=6, height=4):
"""
Where x, y - NumPy lists with coordinates, color - color of line (default - choose by Random)
height, width, dpi - hw, dpi of the figure from PyLab.
"""
self.figure, self.ax = plt.subplots(figsize=(width, height))
self.ax.set_xlim([0, xlim])
self.ax.set_ylim([0, ylim])
self.graphfunction, = self.ax.plot([], [], color='blue', lw=2)
self.dot, = self.ax.plot([], [], 'o', color='red')
self.x = x
self.y = y
self.m = m
self.color = color
self.lw = lw
def animation(self, i, x, y, m, color):
""" Definition of a matplotlib animation. """
if i * m >= len(x) or i * m >= len(y):
self.graphfunction.set_data(x[:i*m], y[:i*m])
else:
self.graphfunction.set_data(x[:i*m], y[:i*m])
self.dot.set_data(x[i*m], y[i*m])
def graph(self, interval=50):
""" Definition of cycle FuncAnimation, which call animation. """
self.graph_id = animation.FuncAnimation(self.figure, self.animation,
fargs=(self.x, self.y, self.m, self.color),
repeat=False, interval=interval, frames=400)
def showfigure(self):
""" Call the graph function and displays it on the figure. """
self.graph()
plt.show()
class SolveSystemOfADifferentialEquations:
def __init__(self, k, angle, v0, m, ws, tlimit=20):
self.k = k
self.angle = angle
self.m = m
self.ws = ws
self.v0 = v0
self.v0_x = self.v0 * cos(self.angle)
self.v0_y = self.v0 * sin(self.angle)
self.kdivm = self.k / self.m
self.time = np.arange(0, tlimit, 0.0005)
def xmodel(self, X, t):
x = X[0]
dx = X[1]
zdot = [ [], [] ]
zdot[0] = dx
zdot[1] = -self.kdivm * dx + self.ws
return zdot
def ymodel(self, Y, t):
y = Y[0]
dy = Y[1]
zdot = [ [], [] ]
zdot[0] = dy
zdot[1] = -g - self.kdivm * dy
return zdot
def solveX(self):
x = integrate.odeint(self.xmodel, [0, self.v0_x], self.time)
return x
def solveY(self):
y = integrate.odeint(self.ymodel, [0, self.v0_y], self.time)
return y
class MainCommunicationWithGui:
def startbutton(self):
self.getvalues()
def getvalues(self):
ws = float(ui.textEdit_1.toPlainText())
m = float(ui.textEdit_2.toPlainText())
if m == 0:
return False
k = float(ui.textEdit_3.toPlainText())
angle = float(ui.textEdit_4.toPlainText()) * (pi / 180)
v0 = float(ui.textEdit_5.toPlainText())
xlim = float(ui.textEdit_7.toPlainText())
ylim = float(ui.textEdit_8.toPlainText())
x, y = self.tosystem(k, angle, v0, m, ws)
self.figureinit(x, y, xlim, ylim)
def tosystem(self, k, angle, v0, m, ws):
system = SolveSystemOfADifferentialEquations(k, angle, v0, m, ws)
Xi = np.array(system.solveY())
Yi = np.array(system.solveY())
x = []
y = []
for j in range(1, len(Yi)):
if Yi[j][0] > 0:
x.append(Yi[j][0])
y.append(Xi[j][0])
return x, y
def figureinit(self, x, y, xlim, ylim):
colors = ['blue', 'green', 'cyan', 'magenta', 'black']
figure = GraphTrajectoryAnimation(x, y, colors[randint(0, 4)] , xlim, ylim)
figure.showfigure()
def exitbutton(self):
sys.exit(1)
def initbuttons(self):
ui.pushButton_3.clicked.connect(self.startbutton)
ui.pushButton_1.clicked.connect(self.exitbutton)
def main():
global ui, app
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
communicate = MainCommunicationWithGui()
communicate.initbuttons()
MainWindow.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
如果你要使用GUI,那么你不应该使用pyplot,而应该使用各自后台的画布,这样就不会出现事件循环的冲突。
考虑到上述情况,解决方案是
from PyQt5 import QtCore, QtGui, QtWidgets
from scipy import integrate
import numpy as np
import matplotlib.animation as animation
from matplotlib.backends.backend_qt5agg import (
FigureCanvas,
NavigationToolbar2QT as NavigationToolbar,
)
from matplotlib.figure import Figure
from pythongui4 import Ui_MainWindow
g = 9.80665 # (Gravity acceleration on the Earth), m/s^2
class Canvas(QtWidgets.QMainWindow):
def __init__(
self, color, xlim=1, ylim=1, m=50, lw=3, width=6, height=4, parent=None
):
super().__init__(parent)
self.figure = Figure(figsize=(width, height))
self.canvas = FigureCanvas(self.figure)
self.setCentralWidget(self.canvas)
self.addToolBar(QtCore.Qt.TopToolBarArea, NavigationToolbar(self.canvas, self))
self.ax = self.figure.subplots()
self.ax.set_xlim([0, xlim])
self.ax.set_ylim([0, ylim])
(self.graphfunction,) = self.ax.plot([], [], color="blue", lw=2)
(self.dot,) = self.ax.plot([], [], "o", color="red")
self._color = color
self._xlim = xlim
self._ylim = ylim
self._m = m
self._lw = lw
@QtCore.pyqtSlot(list, list)
def update_values(self, x, y):
self.x = x[:]
self.y = y[:]
self.graph()
@property
def color(self):
return self._color
@color.setter
def color(self, color):
self._color = color
@property
def xlim(self):
return self._xlim
@xlim.setter
def xlim(self, xlim):
self._xlim = xlim
@property
def ylim(self):
return self._ylim
@color.setter
def ylim(self, ylim):
self._ylim = ylim
@property
def m(self):
return self._m
@m.setter
def m(self, m):
self._m = m
@property
def lw(self):
return self._lw
@lw.setter
def lw(self, lw):
self._lw = lw
def update_function(self, i, x, y, m, color):
""" Definition of a matplotlib animation. """
if i * m >= len(x) or i * m >= len(y):
self.graphfunction.set_data(x[: i * m], y[: i * m])
else:
self.graphfunction.set_data(x[: i * m], y[: i * m])
self.dot.set_data(x[i * m], y[i * m])
def graph(self, interval=50):
""" Definition of cycle FuncAnimation, which call animation. """
self.graph_id = animation.FuncAnimation(
self.figure,
self.update_function,
fargs=(self.x, self.y, self.m, self.color),
repeat=False,
interval=interval,
frames=400,
)
class SolveSystemOfADifferentialEquations:
def __init__(self, k, angle, v0, m, ws, tlimit=20):
self.k = k
self.angle = angle
self.m = m
self.ws = ws
self.v0 = v0
self.v0_x = self.v0 * np.cos(self.angle)
self.v0_y = self.v0 * np.sin(self.angle)
self.kdivm = self.k / self.m
self.time = np.arange(0, tlimit, 0.0005)
def xmodel(self, X, t):
x = X[0]
dx = X[1]
zdot = [[], []]
zdot[0] = dx
zdot[1] = -self.kdivm * dx + self.ws
return zdot
def ymodel(self, Y, t):
y = Y[0]
dy = Y[1]
zdot = [[], []]
zdot[0] = dy
zdot[1] = -g - self.kdivm * dy
return zdot
def solveX(self):
x = np.integrate.odeint(self.xmodel, [0, self.v0_x], self.time)
return x
def solveY(self):
y = np.integrate.odeint(self.ymodel, [0, self.v0_y], self.time)
return y
class MainCommunicationWithGui(QtCore.QObject):
dataChanged = QtCore.pyqtSignal(list, list)
@QtCore.pyqtSlot(float, float, float, float, float)
def update_values(self, ws, m, k, angle, v0):
x, y = self.tosystem(k, angle, v0, m, ws)
self.dataChanged.emit(x, y)
def tosystem(self, k, angle, v0, m, ws):
system = SolveSystemOfADifferentialEquations(k, angle, v0, m, ws)
Xi = np.array(system.solveY())
Yi = np.array(system.solveY())
x = []
y = []
for j in range(1, len(Yi)):
if Yi[j][0] > 0:
x.append(Yi[j][0])
y.append(Xi[j][0])
return x, y
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.communicate = MainCommunicationWithGui()
self.pushButton_3.clicked.connect(self.start)
self.pushButton_1.clicked.connect(QtCore.QCoreApplication.quit)
@QtCore.pyqtSlot()
def start(self):
try:
ws = float(self.textEdit_1.toPlainText())
m = float(self.textEdit_2.toPlainText())
if m == 0:
return
k = float(self.textEdit_3.toPlainText())
angle = float(self.textEdit_4.toPlainText()) * (np.pi / 180)
v0 = float(self.textEdit_5.toPlainText())
xlim = float(self.textEdit_7.toPlainText())
ylim = float(self.textEdit_8.toPlainText())
except ValueError as e:
print("error", e)
else:
self.communicate.xlim = xlim
self.communicate.ylim = ylim
self.communicate.update_values(ws, m, k, angle, v0)
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
canvas = Canvas(color=["blue", "green", "cyan", "magenta", "black"])
w.communicate.dataChanged.connect(canvas.update_values)
canvas.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()