0

I am learning Python and PyQt at the moment. I'm having a problem understanding something. My program calculates the solusion of Newton's Cooling Law using Euler's Method. For now all the parameters are constant but user will be able to change them later.

I would like to make it this way that after button "Plot" is pressed, the signal is sent to function Plot with new data(not the old one). So that if I change any parameter the result after pressing "Plot" will be different.

For example here in main.py i am calculating data at the beginning and parsing it using Window's constructor because function "Plot" is inside Window class. So after pressing "Plot" the data passed to the Window object will be plotted. After that I calculate data once again but with different parameters. I would like to make a new plot with this data after pressing "Plot" once again. How to do it? What is the best approach to this? Here's my code. Thank you in advance

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()

1 Answer 1

1

You will need to add more Qt widgets (like QLineEdit or QSpinBox) so the user can specify the values.

Then, when the plot button is clicked, you should instantiate new Mechanical_system and Calculus objects with the updated parameters (read them from the relevant Qt widgets you will add to your UI), and then plot that data.

Note that, if the construction of those objects takes a lot of time, your UI will become unresponsive. You may need to offload calculations to a thread, but that is a whole different kettle of fish.


Additional

I suspect from your code in the if __name__ == "__main__": block that you misunderstand how GUI programs work. The GUI is not actually shown until it reaches the app.exec_() call. At this point, an event loop is started which responds to mouse events like clicking, window resizes, keyboard button presses, etc. This loop is what handles running methods when buttons are clicked.

As such, once the GUI is started, everything you want to do must be ultimately called by the event loop (the event loop does not end until the GUI main window is closed). So you need to move instantiation of your objects for creating the plot data into a method which responds to user input. For example, the above suggestion where your objects are created in response to a button press.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you @three_pineapples it was helpful :) If you have any tips regarding the way I arrange my code feel free to share them :) I would be eternally grateful.
@Zwierzak you might want to try codereview.stackexchange.com for help with improving code structure
Again thank you :) I wasn't aware that such webpage exist on this network :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.