Как обновить matplotlib реального времени в tkinter без блокировки основного цикла - PullRequest
0 голосов
/ 02 августа 2020

Привет, у меня есть график в реальном времени, который обновляется по подписке с использованием zmq. Я использую анимацию из matplotlib, чтобы отразить последнее обновление, но при каждом обновлении я получаю блок 1se c. Есть ли способ обновить сюжет без блокировки в пользовательском интерфейсе? Вы можете «почувствовать» блок, если перетащите окно UI.py по экрану с помощью мыши.

publi sh data: (сначала запустите: сохраните как Publisher.py и запустите: python Publisher.py)

import zmq
from time import sleep
import numpy as np

context = zmq.Context()
socket = context.socket(zmq.PUB)  # pylint: disable=no-member
socket.bind('tcp://127.0.0.1:2020')

# noise
dt = 0.01
t = np.arange(0, 30, dt)
nse1 = np.random.randn(len(t))
nse2 = np.random.randn(len(t))
nse3 = np.random.randn(len(t))

# sample data
s1 = np.sin(2 * np.pi * 10 * t) + nse1
s2 = np.sin(2 * np.pi * 10 * t) + nse2
s3 = np.sin(2 * np.pi * 10 * t) + nse3

index = 0
while(True):
    sleep(0.10)
    messages = [{"p1":{"key1":"100.01"},
         "p2":{"key1":s1[index],"key2":s2[index],"key3":s3[index]},
         "p3":{"key1":"100100.45","key2":"200200.56"}}]
    print( 'index=',index," {",messages[0],"}" )
    socket.send_pyobj(messages[0])
    if( index == len(s1)-1 ):
        index = 0
    else:
        index = index + 1

UI (сохраните его как UI.py и запустите python UI.py)

import sys
import tkinter as tk
from tkinter import *
from tkinter import messagebox
import tkinter.scrolledtext as tkst
import time
import zmq
import threading as t
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.backend_bases import key_press_handler
from matplotlib.ticker import (MultipleLocator, FormatStrFormatter,
                               AutoMinorLocator)
import numpy as np
from drawnow import *

key1=[]
key2=[]
key3=[]

PLOT_MAX=50
PLOT_FILL_COLOR='#8899b5ff'

class SIControllerUI(tk.Frame):

    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
        self.master = master
        self.master.title("SIControllerUI-UI v2020")
        self.grid()

        self.subscribing = True
        self.master.protocol("WM_DELETE_WINDOW", self.xit)

    def startup(self):
        self.create_widgets()
        self.tZmq = t.Thread(target=self.refreshState,args=[2020])
        self.tZmq.start()

    def isSubscribing(self):
        return self.subscribing

    def terminate(self):
        self.subscribing = False

    def xit(self):
        if messagebox.askokcancel("Quit", "Do you want to quit?"):
            #self.terminate()
            '''while self.tZmq.is_alive()==True:
                print('Subscription still running. Waiting...')
                time.sleep(.5)
            self.tZmq.join()'''
            self.master.destroy()

    def create_widgets(self):
        None

    def refreshState(self,port):
        #print('getting message...')
        context = zmq.Context()
        socket = context.socket(zmq.SUB) # pylint: disable=no-member
        subUrl = 'tcp://127.0.0.1:' + str(port)
        socket.connect(subUrl)
        try:
            socket.setsockopt(zmq.SUBSCRIBE, '') # pylint: disable=no-member
        except TypeError:
            socket.setsockopt_string(zmq.SUBSCRIBE, '') # pylint: disable=no-member
        #print('refreshState isSubscribing='+str(self.isSubscribing()))
        while(self.isSubscribing()):
            self.peripheralState = socket.recv_pyobj()
            #print( type(self.peripheralState))
            #print( self.peripheralState )
            periDict = self.peripheralState["p2"]
            index = 1
            for key in periDict:
                #print("key: %s , value: %s" % (key, periDict[key]))
                if(index == 1 and self.isSubscribing()):
                    key1.append( periDict[key] )
                    if( len(key1) > PLOT_MAX):
                        key1.pop(0)
                if(index == 2 and self.isSubscribing() ):
                    key2.append( periDict[key] )
                    if( len(key2) > PLOT_MAX):
                        key2.pop(0)
                if(index == 3 and self.isSubscribing()):
                    key3.append( periDict[key] )
                    if( len(key3) > PLOT_MAX):
                        key3.pop(0)

                index = index + 1


        print('refreshState is terminated!')

def updateStreamingUi(i):
    plt.ticklabel_format(useOffset=False)
    axs[0].clear()
    axs[0].set_ylim(min(key1), max(key1))
    axs[0].xaxis.set_major_locator(MultipleLocator(2))
    axs[0].xaxis.set_minor_locator(AutoMinorLocator(2))
    x1 = np.arange(-1,len(key1)-1,1)
    axs[0].fill_between(x1,min(key1),key1,color=PLOT_FILL_COLOR)

    axs[1].clear()
    axs[1].set_ylim(min(key2), max(key2))
    axs[1].xaxis.set_major_locator(MultipleLocator(2))
    axs[1].xaxis.set_minor_locator(AutoMinorLocator(2))
    x2 = np.arange(-1,len(key2)-1,1)
    axs[1].fill_between(x2,min(key2),key2,color=PLOT_FILL_COLOR)

    axs[2].clear()
    axs[2].set_ylim(min(key3), max(key3))
    axs[2].xaxis.set_major_locator(MultipleLocator(2))
    axs[2].xaxis.set_minor_locator(AutoMinorLocator(2))
    x3 = np.arange(-1,len(key3)-1,1)
    axs[2].fill_between(x3,min(key3),key3,color=PLOT_FILL_COLOR)

def refreshStateMain(port, app):
    print('getting message...')
    context = zmq.Context()
    socket = context.socket(zmq.SUB) # pylint: disable=no-member
    subUrl = 'tcp://127.0.0.1:' + str(port)
    socket.connect(subUrl)
    try:
        socket.setsockopt(zmq.SUBSCRIBE, '') # pylint: disable=no-member
    except TypeError:
        socket.setsockopt_string(zmq.SUBSCRIBE, '') # pylint: disable=no-member
    print('refreshState isSubscribing='+str(app.isSubscribing()))
    while(app.isSubscribing()):
        app.peripheralState = socket.recv_pyobj()
        print( type(app.peripheralState))
        print( app.peripheralState )
        # get p1 for example
        periDict = app.peripheralState["p2"]
        index = 1
        for key in periDict:
            print("key: %s , value: %s" % (key, periDict[key]))

            if(index == 1 and app.isSubscribing()):
                key1.append( periDict[key] )
            if( len(key1) > PLOT_MAX):
                key1.pop(0)
            if(index == 2 and app.isSubscribing() ):
                key2.append( periDict[key] )
            if( len(key2) > PLOT_MAX):
                key2.pop(0)
            if(index == 3 and app.isSubscribing()):
                key3.append( periDict[key] )
            if( len(key3) > PLOT_MAX):
                key3.pop(0)

            index = index + 1

        print('refreshState isSubscribing='+str(app.isSubscribing()))
 
        print('refreshState is terminated!')

if __name__ == '__main__':
    root = tk.Tk()
    fig, axs = plt.subplots(3, 1)
    plt.ion()
    plt.rcParams['axes.xmargin'] = 0
    canvas = FigureCanvasTkAgg(fig, master=root)  # A tk.DrawingArea.
    canvas.draw()
    canvas.get_tk_widget().grid(row=0, column=0, columnspan=4, rowspan=4,
        sticky=W+E+N+S, padx=5, pady=5)
    ani = animation.FuncAnimation(fig, updateStreamingUi, interval=1000, blit=False)
    app = SIControllerUI(master=root)
    app.startup()
    app.mainloop()

Спасибо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...