wxpython嵌入Figure动画实现——基于matplotlib.animation流数据动态监控可视化

1.思路:

(1)利用FigureCanvasWxAgg嵌入canvas

(2)利用 matplotlib.figure 放入画布中,在figure中画出自己想要的图像

(3)动画利用matplotlib.animation实现,动画停止可以用stop停止

self.ani = animation.FuncAnimation(self.figure,
                                           self.animate,
                                           interval=1000,
                                           blit=False,
                                           init_func=self.init)
self.ani.event_source.stop()

(4)动态数据通过队列和异步线程放入queue中,利用animation取数据,然后重画图像。

(5)可视化显示数据,利用figure.canvas.mpl_connect来相应鼠标事件

self.axes.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)
1.1.鼠标事件
# 当鼠标在子图范围内产生动作时,将触发鼠标事件,鼠标事件分为三种:
    botton_press_event:   鼠标按下时触发
    botton_release_event: 鼠标释放时触发
    motion_notify_event:  时间移动时触发
    
1.2.鼠标事件的相关信息可以通过event对象的属性获得:
    name:        事件名
    button:     鼠标按键,1,2,3表示左中右按键,None表示没有按键
    x,y:        表示鼠标在图表中的像素坐标
    xdata,ydata:鼠标在数据坐标系的坐标
 
"""

2实现效果:

3.完整代码如下,保证运行:

# -*- coding: UTF-8 -*-

import threading
import queue
import time
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import numpy as np
import wx
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure


class MyFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=wx.DefaultPosition,
                          size=wx.Size(600, 200), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL)
        self.SetBackgroundColour(wx.Colour('#EBEDEB'))
        # 用来正常显示中文标签
        plt.rcParams['font.sans-serif'] = ['SimHei']
        # 用来正常显示负号
        plt.rcParams['axes.unicode_minus'] = False

        bSizer_ct_bt = wx.BoxSizer(wx.VERTICAL)

        # bSizer
        self.bSizer_anim = wx.BoxSizer(wx.VERTICAL)
        self.figure = Figure(figsize=(5, 2), facecolor='#0B173B')
        self.figure.subplots_adjust(left=0.1, top=0.8, bottom=0.3, right=0.9)  # 调整边距和子图的间距
        self.axes = self.figure.add_subplot(111, xlim=(0, 101), ylim=(0, 1), facecolor='#0B173B')
        self.axes.set_aspect('auto')  # 控制长宽比
        self.axes.grid(color='#606061')  # 是否要格子背景
        self.axes.spines['left'].set_color('#9b9c9c')
        self.axes.spines['right'].set_color('#9b9c9c')
        self.axes.spines['top'].set_color('#9b9c9c')
        self.axes.spines['bottom'].set_color('#9b9c9c')
        self.axes.tick_params(axis='both', which='both', colors='#ffffff')
        # self.axes.axhline(0.5, linestyle='-', color='#1e326d', lw=1)
        # self.axes.axvline(20, linestyle='-', color='#239B3F', lw=1)
        # 设置图片标题
        self.axes.set_title('FigureCanvas横坐标动态更新的animation实现', color='#ffffff')
        # 设置横坐标名称
        # self.axes.set_xlabel('动态变化的横轴')
        # 设置刻度标签
        self.axes.set_yticks([0, 0.5, 1])
        self.axes.set_yticklabels([0, 0.5, 1], rotation=0)

        self.canvas1 = FigureCanvas(self, -1, self.figure)
        self.bSizer_anim.Add(self.canvas1, 1, wx.ALL, 0)
        bSizer_ct_bt.Add(self.bSizer_anim, 1, wx.ALL, 50)
        self.SetSizer(bSizer_ct_bt)
        self.Layout()
        bSizer_ct_bt.Fit(self)

        self.thisAy = []
        self.thisx = []
        self.thisy = []

        self.text0 = self.axes.text(22, 0.8, '22, 0.0000', ha="left", va="bottom", color='#00FF00')
        self.line0, = self.axes.plot([20, 20], [0, 1], '-', color='#239B3F', lw=1)
        self.line, = self.axes.plot([], [], '-', color='#239B3F', lw=1)
        self.ani = animation.FuncAnimation(self.figure,
                                           self.animate,
                                           interval=1000,
                                           blit=False,
                                           init_func=self.init)
        # ani.save('jieky_animation.gif',writer='imagemagick')
        # anim.event_source.stop()
        # 常量
        self.initCount = 0
        self.destroy_flag = False
        self.Show()

        # event
        self.axes.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)

        self.animQueue = queue.Queue()
        # 线程加载数据
        thread1 = threading.Thread(target=self.put_data_thread,
                                   args=(self.animQueue,))  # 添加线程
        thread1.start()

    def put_data_thread(self, animQueue):
        # 随机生成数组
        num_list = np.random.rand(300)
        for num in num_list:
            time.sleep(0.1)
            animQueue.put(num)

    def on_motion(self, event):
        print(self.thisAy)
        x_val = event.xdata
        print('9999999999999999999999999999999999999', x_val)
        if x_val:
            x_val = int(round(x_val))
            self.axes.figure.canvas.draw()
            self.line0.set_data([x_val, x_val], [0, 1])
            showText = x_val
            if len(self.thisAy) > x_val:
                y_differ = self.thisAy[x_val]
                showText = str(showText) + ' ' + str(y_differ)
            self.text0.remove()
            self.text0 = self.axes.text(x_val + 1, 0.8, showText, ha="left", va="bottom", color='#00FF00')
        else:
            pass

    def stop_anim(self):
        self.ani.event_source.stop()
        self.destroy_flag = True

    def init(self):
        self.thisAy = []
        self.thisx = [i for i in range(0, 101)]
        self.thisy = (np.zeros(101, dtype=int) - 1).tolist()
        self.line.set_data([], [])
        # 设置x轴的范围
        self.axes.set_xlim(min(self.thisx), max(self.thisx))
        # 更新刻度,刻度只要早x轴的范围内就可以
        self.axes.set_xticks([i for i in range(min(self.thisx), max(self.thisx) + 1, 20)])
        # 设置刻度标签
        self.axes.set_xticklabels(
            [i if i >= 0 else '' for i in range(min(self.thisx), max(self.thisx) + 1, 20)],
            rotation=0)
        return self.line

    def animate(self, *args):
        print('animate............')
        try:
            animQueue = self.animQueue
            while not animQueue.empty():
                if self.destroy_flag:
                    break
                num_y = animQueue.get()
                print('animate get:', num_y)
                # 清空重新绘制
                if num_y == -1:
                    self.initCount = 0
                    self.init()
                    continue
                else:
                    self.thisAy.append(num_y)
                    if self.initCount > 99:
                        del self.thisx[0]
                        del self.thisy[0]
                        self.thisx.append(max(self.thisx) + 1)
                        self.thisy.append(num_y)
                    else:
                        self.thisx[self.initCount] = self.initCount
                        self.thisy[self.initCount] = num_y
                        self.initCount += 1
        except:
            self.stop_anim()
            return
        # 设置x轴的范围
        self.axes.set_xlim(min(self.thisx), max(self.thisx))
        # 更新刻度,刻度只要早x轴的范围内就可以
        self.axes.set_xticks([i for i in range(min(self.thisx), max(self.thisx) + 1, 20)])
        # 设置刻度标签
        self.axes.set_xticklabels(
            [i if i >= 0 else '' for i in range(min(self.thisx),
                                                max(self.thisx) + 1, 20)],
            rotation=0)
        # 重新渲染子图
        try:
            self.axes.figure.canvas.draw()
        except:
            self.stop_anim()
            return
        self.line.set_data(self.thisx, self.thisy)
        return self.line


class MyApp(wx.App):
    def OnInit(self):
        frame = MyFrame(None)
        frame.Show(True)
        self.SetTopWindow(frame)
        return True


if __name__ == '__main__':
    app = MyApp()
    app.MainLoop()

4.错误:Failed to convert value(s) to axis units

动画中放入的数据需要保证数据类型一致,即放float就保证数组都是float类型。

5.参考文章:

基于matplotlib.animation和python的流数据动态监控原型

 

对你有帮助就点个赞吧,有疑问的可以留言评论!一起成长!

 

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
实现数据可视化GUI界面,可以使用Python中的许多GUI库,如Tkinter、PyQt、wxPython等。在这些库中,PyQt可能是最强大和最常用的GUI库之一,因为它提供了丰富的功能和易于使用的API。以下是一个简单的PyQt程序,可以用来实现一个简单的数据可视化GUI界面: ``` import sys from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout, QLabel, QLineEdit, QPushButton class MyWidget(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setWindowTitle('数据可视化GUI') grid = QGridLayout() self.setLayout(grid) self.label1 = QLabel('输入数据:') grid.addWidget(self.label1, 0, 0) self.input1 = QLineEdit() grid.addWidget(self.input1, 0, 1) self.label2 = QLabel('输出数据:') grid.addWidget(self.label2, 1, 0) self.output1 = QLineEdit() grid.addWidget(self.output1, 1, 1) self.button1 = QPushButton('计算') self.button1.clicked.connect(self.calculate) grid.addWidget(self.button1, 2, 0, 1, 2) def calculate(self): input_data = self.input1.text() # 处理输入数据 output_data = '结果' self.output1.setText(output_data) if __name__ == '__main__': app = QApplication(sys.argv) widget = MyWidget() widget.show() sys.exit(app.exec_()) ``` 这个程序创建了一个窗口,其中包含一个输入框、一个输出框和一个按钮。用户可以在输入框中输入数据,然后单击按钮以计算数据。计算结果将显示在输出框中。您可以根据需要扩展此程序以实现更复杂的数据可视化GUI界面。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值