通过 PyQt 中的连接按钮将更新的参数传递给函数

Passing updated parameters to the function via connected button in PyQt

我目前正在学习 Python 和 PyQt。我在理解某事时遇到问题。我的程序使用欧拉方法计算牛顿冷却定律的解。目前所有参数都是不变的,但用户以后可以更改它们。

我想这样做,在按下按钮 "Plot" 后,信号被发送到具有新数据(不是旧数据)的 Plot 函数。因此,如果我更改任何参数,按下 "Plot" 后的结果将有所不同。

例如在 main.py 中,我在开始时计算数据并使用 Window 的构造函数对其进行解析,因为函数 "Plot" 在 Window [=39 中=].因此,在按下 "Plot" 后,将绘制传递给 Window 对象的数据。之后我再次计算数据但使用不同的参数。再次按 "Plot" 后,我想用这些数据制作一个新图。怎么做?最好的方法是什么?这是我的代码。提前谢谢你

main.py:

from mechanical_system import Mechanical_system
from calculus import Calculus
from window import Window
from PyQt4 import QtGui
import sys

if __name__ == "__main__":

    system1 = Mechanical_system(200, 300, 100, 10, 40, 50)
    data = Calculus.euler(system1.ODE, 100, 0, 100, 2)

    print system1.get_params()

    app = QtGui.QApplication(sys.argv)

    main = Window(data)
    main.show()

    data = Calculus.euler(system1.ODE, 200, 0, 50, 0.5)

    sys.exit(app.exec_())

calculus.py:

import matplotlib.pyplot as plt
import numpy as np

class Calculus:
    def __init__(self):
        print "object created"

    @staticmethod
    def euler(f,y0,a,b,h):
        """y0 - initial temp, a-time = 0 , b-end of time gap, h - step"""
        t,y = a,y0
        time = [t]
        value = [y]

        while t <= b:
            print "%6.3f %6.3f" % (t,y)
            t += h
            y += h * f(t,y)
            time.append(t)
            value.append(y)

        data = {'time' : time, 'value' : value}
        return data

    @staticmethod
    def draw_chart(time, value):
        """time dicionary contains moments for which each value was calculated"""
        plt.axhline(20, 0, 100, color='r')
        plt.plot(time, value, 'bo')

        plt.axis([0, 100, 0, 100])
        plt.xlabel("czas")
        plt.ylabel("temperatura")
        plt.show()

mechanical_system.py

class Mechanical_system:
    #public variable holding system's parameters
    system_parameters = {}

    #Constructor
    def __init__(self, momentum1, momentum2, n1, n2, w1, w2):
        Mechanical_system.system_parameters = {'momentum1': momentum1, 'momentum2': momentum2, 'N1': n1, 'N2' : n2, 'w1' : w1, 'w2' : w2};

    def get_params(self):
        """returns a dictionary that contains all the system parameters"""
        return Mechanical_system.system_parameters

    def set_param(self, param_name, value):
        """
        sets a new value for specified parameter
        available parameters: momentum1, momentum2, N1, N2, w1, w2
        """
        Mechanical_system.system_parameters[param_name] = value

    def ODE(self, time, temp):
        """ODE - ordinary differential equation describing our system"""
        return -0.07 * (temp - 20)

window.py

from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt

class Window(QtGui.QDialog):
    def __init__(self, data, parent = None):
        super(Window, self).__init__(parent)

        # a figure instance to plot on
        self.figure = plt.figure()

        # this is the Canvas Widget that displays the `figure`
        # it takes the `figure` instance as a parameter to __init__
        self.canvas = FigureCanvas(self.figure)

        # this is the Navigation widget
        # it takes the Canvas widget and a parent
        self.toolbar = NavigationToolbar(self.canvas, self)

        # Just some button connected to `plot` method
        self.button = QtGui.QPushButton('Plot')

        self.button.clicked.connect(lambda: self.plot(data['time'], data['value']))

        self.lineEdit = QtGui.QLineEdit()
        self.lineEdit.resize(200, 30)
        self.lineEdit.setInputMethodHints((QtCore.Qt.ImhFormattedNumbersOnly))
        # set the layout
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.toolbar)
        layout.addWidget(self.canvas)
        layout.addWidget(self.button)
        layout.addWidget(self.lineEdit)
        self.setLayout(layout)

    def plot(self, time, value):
        """time dicionary contains moments for which each value was calculated"""

        # create an axis
        ax = self.figure.add_subplot(111)

        # discards the old graph
        ax.hold(False)

        plt.axhline(20, 0, 100, color='r')    
        plt.axis([0, 100, 0, 100])
        plt.xlabel("czas")
        plt.ylabel("temperatura")

        # plot data
        plt.plot(time, value, '*-')

        # refresh canvas
        self.canvas.draw()

您将需要添加更多 Qt 小部件(如 QLineEditQSpinBox)以便用户可以指定值。

然后,当单击绘图按钮时,您应该使用更新的参数实例化新的 Mechanical_systemCalculus 对象(从您将添加到 [=31 的相关 Qt 小部件中读取它们=]),然后绘制该数据。

请注意,如果构建这些对象需要花费大量时间,您的 UI 将变得无响应。您可能需要将计算卸载到线程,但这是完全不同的事情。


额外

根据您在 if __name__ == "__main__": 块中的代码,我怀疑您误解了 GUI 程序的工作原理。 GUI 在到达 app.exec_() 调用之前不会实际显示。此时,事件循环开始响应鼠标事件,如单击、window 调整大小、键盘按钮按下等。此循环用于在单击按钮时处理 运行 方法。

因此,一旦启动了GUI,你想做的一切最终都必须由事件循环调用(事件循环直到GUI main window 关闭)。因此,您需要将用于创建绘图数据的对象的实例化移动到响应用户输入的方法中。例如,上面的建议是您的对象是响应按钮按下而创建的。