Python Kivy using Matplotlib for plotting real tim

2019-07-09 02:07发布

问题:

I am trying to create a measurement visualization inside kivy using kivy garden matplotlib. The goal is to plot variables from a dictionary with their associated measurements.

At the moment the code (see below) consists of 3 parts. After the import section I define constants that I use inside the code. The next part is a dictionary that I use to programmatically plot different variables with different settings (line color, line width, ect.). Beginning after the dictionary I define a method slice array to splice the arrays to reduce memory usage and a class Plots to generate the plots. I also posted a similar question but by using the kivy garden graph.

I have two problems with the code. First, it is not possible for me to display the coordinate system without. The ax.spines command does not have any effect. The second problem is, that I actually want to have the possibility to plot an arbitrary number of variables that I want to display in a separate subplot. I want echt figure to have a constant height on the display. This should lead to the possibility to scroll the plots.

import time
import psutil
import matplotlib.pyplot as plt

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
from kivy.uix.scrollview import ScrollView
from kivy.uix.widget import Widget

from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg

# Chart Constants
MAX_MEASUREMENT_ARRAY_LENGTH = 500      # to trim measured values values
X_BASE_INTERVAL_LENGTH = 10             # time axis is displayed for an interval of 50 seconds
BASE_LINE_WIDTH = 0.5                   # line width of lines in plots
BASE_LINE_COLOR = "white"
COLUMNS_OF_PLOTS = 1                    # TODO implement more general layout possibilities, at the moment only 1-column

chart_variables = {
    "cpu_usage": {
        "order_number": 1,
        "title": "CPU Usage",
        "color": BASE_LINE_COLOR,
        "line_width": BASE_LINE_WIDTH,
        "x_interval_length": X_BASE_INTERVAL_LENGTH,
        "y_bottom": 0,
        "y_top": 100,
        "x_label": "Time in epochs",
        "y_label": "CPU usage in %",
        "x_values": [],
        "y_values": [],
        "anti_aliased": False,
    },

    "upc_usage": {
        "order_number": 2,
        "title": "UPC Usage",
        "color": BASE_LINE_COLOR,
        "line_width": BASE_LINE_WIDTH,
        "x_interval_length": X_BASE_INTERVAL_LENGTH,
        "y_bottom": 0,
        "y_top": 100,
        "x_label": "Time in epochs",
        "y_label": "UPC usage in %",
        "x_values": [],
        "y_values": [],
        "anti_aliased": True,
    },

    "test3": {
        "order_number": 3,
        "title": "test",
        "color": BASE_LINE_COLOR,
        "line_width": BASE_LINE_WIDTH,
        "x_interval_length": X_BASE_INTERVAL_LENGTH,
        "y_bottom": 0,
        "y_top": 100,
        "x_label": "Time in epochs",
        "y_label": "UPC usage in %",
        "x_values": [],
        "y_values": [],
        "anti_aliased": True,
    },

    "test4": {
        "order_number": 4,
        "title": "test",
        "color": BASE_LINE_COLOR,
        "line_width": BASE_LINE_WIDTH,
        "x_interval_length": X_BASE_INTERVAL_LENGTH,
        "y_bottom": 0,
        "y_top": 100,
        "x_label": "Time in epochs",
        "y_label": "UPC usage in %",
        "x_values": [],
        "y_values": [],
        "anti_aliased": True,
    },

    "test5": {
        "order_number": 5,
        "title": "test",
        "color": BASE_LINE_COLOR,
        "line_width": BASE_LINE_WIDTH,
        "x_interval_length": X_BASE_INTERVAL_LENGTH,
        "y_bottom": 0,
        "y_top": 100,
        "x_label": "Time in epochs",
        "y_label": "UPC usage in %",
        "x_values": [],
        "y_values": [],
        "anti_aliased": True,
    },

    "test6": {
        "order_number": 6,
        "title": "test",
        "color": BASE_LINE_COLOR,
        "line_width": BASE_LINE_WIDTH,
        "x_interval_length": X_BASE_INTERVAL_LENGTH,
        "y_bottom": 0,
        "y_top": 100,
        "x_label": "Time in epochs",
        "y_label": "UPC usage in %",
        "x_values": [],
        "y_values": [],
        "anti_aliased": True,
    },
}


def slice_arrays(max_array_length):
    if len(chart_variables["cpu_usage"]["x_values"]) > max_array_length:
        for key in chart_variables:
            chart_variables[key]["x_values"] = chart_variables[key]["x_values"][int(max_array_length / 2):]
            chart_variables[key]["y_values"] = chart_variables[key]["y_values"][int(max_array_length / 2):]


class Plots(Widget):
    def __init__(self):
        super(Plots, self).__init__()
        self.number_of_plots = len(chart_variables)
        self.t_start = time.time()
        self.chart, self.axes = plt.subplots(nrows=self.number_of_plots,
                                             ncols=COLUMNS_OF_PLOTS,
                                             facecolor=(0.9, 0.9, 0.9))
        plt.subplots_adjust(hspace=0.01)
        self.chart.patch.set_alpha(0)  # transparent plot background

    def initialize_plots(self):
        for ax, key in zip(self.axes.flat, chart_variables):
            ax.set_title(chart_variables[key]["title"], color=chart_variables[key]["color"])
            ax.tick_params(labelcolor=chart_variables[key]["color"])
            ax.set_xlabel(chart_variables[key]["x_label"], color=chart_variables[key]["color"])
            ax.set_ylabel(chart_variables[key]["y_label"], color=chart_variables[key]["color"])
            ax.set_ylim(bottom=chart_variables[key]["y_bottom"],
                        top=chart_variables[key]["y_top"])
            ax.set_frame_on(False)  # remove white background inside each subplot
            ax.spines['bottom'].set_color('white')
            ax.spines['left'].set_color('white')

    def plot_data(self):
        slice_arrays(MAX_MEASUREMENT_ARRAY_LENGTH)
        for ax, key in zip(self.axes.flat, chart_variables):
            x_axis_interval_length = chart_variables[key]["x_interval_length"]
            elapsed_time = time.time() - self.t_start
            number_of_intervals = elapsed_time // x_axis_interval_length  # index 0 to get div part only
            chart_variables[key]["x_values"].append(elapsed_time)
            chart_variables[key]["y_values"].append(psutil.cpu_percent())
            ax.plot(chart_variables[key]["x_values"],
                    chart_variables[key]["y_values"],
                    color=chart_variables[key]["color"],
                    linewidth=chart_variables[key]["line_width"],
                    antialiased=chart_variables[key]["anti_aliased"])
            self.chart.canvas.draw()
            x_left = x_axis_interval_length * number_of_intervals
            x_right = x_axis_interval_length * (number_of_intervals + 1)
            ax.set_xlim(left=x_left, right=x_right)

    def update_plots(self, _):
        self.plot_data()


class MyApp(App):

    def build(self):
        plot = Plots()
        plot.initialize_plots()
        Clock.schedule_interval(plot.update_plots, 0.2)
        scroll_view = ScrollView()
        box = BoxLayout()
        box.spacing = 24
        scroll_view.add_widget(box)
        box.add_widget(FigureCanvasKivyAgg(plt.gcf()))
        return scroll_view


if __name__ == "__main__":
    MyApp().run()