SoFunction
Updated on 2024-11-15

Python Add Timeline for Dynamic Drawing Explained

timeline

Three dimensions are not the limit of human understanding; after all, we live in thinking time and space. So the next thing to do is a four-dimensional graphic - add a timeline.

In fact, this project has been created with motion graphics in mind, after all, the default plotting coordinates are txyz. However, if you want to implement motion graphics, you must first set up an additional thread for dynamically updating the image, or else the plotting loop will oppress the main process, which will result in the interface becoming incredibly laggy.

Next, it's time to start realizing the need to draw motion graphics. the first step is to draw the UI. first add the setFrmCtrl to the

# Animation control
frm = (frmCtrl, width=320)
(side=, fill=)
(frm)

Then implement the specific

def setAnimateFrame(self, frm):
    pDct = dict(side=, fill=, padx=2)
     = ()
    (100)
    (frm, text="Delay/milliseconds").pack(**pDct)
    (frm, width=5, textvariable=).pack(**pDct)
     = ()
    (100)
    (frm, text="Frames.").pack(**pDct)
    (frm, width=5, textvariable=).pack(**pDct)
     = 0   # Current frames
    (frm, width=3, text=  "⇦", 
        command=).pack(**pDct)
    (frm, width=3, text="▶", 
        command=).pack(**pDct)
    (frm, width=3, text="⇨", 
        command=).pack(**pDct)
def btnAniStart(self): pass
def btnPreFrame(self): pass
def btnNextFrame(self): pass

Delay indicates the time between two frames when the motion picture is drawn automatically; Frame Count indicates the total number of drawing times. Three buttons for frame forward, frame backward and motion playback.

single-frame jump

Coordinate t doesn't work the same way as xyz, after all, each time the time parameter is called, it only needs to be called for a certain point in time. So, the existing method of setting the coordinate data doesn't work anymore, and you need to change the readDatas function

# Read the data from the axes al
def readDatas(self, al):
    dct = {}
    data = {}
    for flag in ():
        data[flag] = (flag, **dct)
        if flag=='t': 
            dct['t'] = data['t'][]
        else:
            dct[flag] = data[flag]
    return data

Then update the plotting function a bit more: it's really just a matter of removing t as the plotting axis

def btnDrawImg(self):
    ()
     = {}
    for al in :
        ax = (al)
        data = (al)
        draw = [()]
        style = ()
        keys = ().replace('t',"")
        draw(ax, data, keys, style)
    .subplots_adjust(left=0.1, right=0.95, top=0.95, bottom=0.08)
    ()

Finally, implement btnNextFrame.

def btnNextFrame(self): 
    num = int(())
     = ( + 1) % num
    ()

The result is as follows. At this point, we are just one multithread away from automating dynamic drawing.

source code (computing)

This article only changes the source code in, for the rest of the code check out this blog:Customized style mapping system

import tkinter as tk
import  as ttk
from  import askopenfilename
import matplotlib
import matplotlib as mpl
('TkAgg')
import .backend_tkagg as mbb
from  import Figure
import numpy as np
from alist import AxisList
from base import DrawType
class DarwSystem():
    def __init__(self):
         = ()
        ("Data presentation tools")
         = {}
         = []
        ()
        ()
        frmFig = ()
        (side=,fill=,expand=)
        (frmFig)
        ()
    def initConst(self):
         = ("Dotted line diagram.", "Scatterplot.", "Bar graph.")
         = {
            "Dotted line diagram." : ,
            "Scatterplot." : ,
            "Bar graph." : 
        }
    # ! Setting
    def setFrmCtrl(self):
        frmCtrl = (,width=320)
        (side=, fill=)
        # Master Frame
        frm = (frmCtrl, width=320)
        (side=, fill=)
        (frm)
        # Animation control
        frm = (frmCtrl, width=320)
        (side=, fill=)
        (frm)
         = (frmCtrl)
        (side=, fill=)
        (None)
    # ! toolbar (in computer software)
    def setCtrlButtons(self, frm):
         = DrawType(frm)
        (side=)
        (frm, text="📈",width=3,
            command=).pack(side=)
        (frm, text="📂",width=3,
            command=).pack(side=)
        btn = (frm, text="+", width=3)
        (side=)
        ("<Button-1>", )
        btn = (frm, text="-", width=3)
        (side=)
        ("<Button-1>", )
    # ! Animation Controls
    def setAnimateFrame(self, frm):
        pDct = dict(side=, fill=, padx=2)
         = ()
        (100)
        (frm, text="Delay/milliseconds").pack(**pDct)
        (frm, width=5, textvariable=).pack(**pDct)
         = ()
        (100)
        (frm, text="Frames.").pack(**pDct)
        (frm, width=5, textvariable=).pack(**pDct)
         = 0   # Current frames
        (frm, width=3, text=  "⇦", 
            command=).pack(**pDct)
        (frm, width=3, text="▶", 
            command=).pack(**pDct)
        (frm, width=3, text="⇨", 
            command=).pack(**pDct)
    def btnAniStart(self): pass
    def btnPreFrame(self): pass
    def btnNextFrame(self): 
        num = ()
         = ( + 1) % num
        ()
    # Add a set of coordinate systems
    def addLast(self, evt):
        title = f"coordinate (geometry){len()}"
        al = AxisList(, title, 1, [5,10,30], 
            , ())
        (side=, fill=)
        (al)
    # Delete a set of coordinate systems
    def deleteLast(self, evt):
        [-1].pack_forget()
        del [-1]
    # Load data // Temporarily in the discarded state
    def btnLoadData(self):
        name = askopenfilename()
        data = (name)
        for i, flag in enumerate('xyz'):
            if i >= [1]:
                return
            (flag, "External import")
            [flag] = (flag, data = data[:,i])
    # Read the data from the axes al
    def readDatas(self, al):
        dct = {}
        data = {}
        for flag in ():
            data[flag] = (flag, **dct)
            if flag=='t': 
                dct['t'] = data['t'][]
            else:
                dct[flag] = data[flag]
        return data
    # Set the plotting coordinates
    def setDrawAxis(self, al):
        sub = int(())
        print(sub)
        if sub in :
            return [sub]
        p = () 
        if p == "None":
            [sub] = .add_subplot(sub)
        else:
            [sub] = .add_subplot(sub, projection=p)
        return [sub]
    # Single-frame plotting functions
    def btnDrawImg(self):
        ()
         = {}
        for al in :
            ax = (al)
            data = (al)
            draw = [()]
            style = ()
            keys = ().replace('t',"")
            draw(ax, data, keys, style)
        .subplots_adjust(left=0.1, right=0.95, top=0.95, bottom=0.08)
        ()
    def drawBar(self, ax, data, keys, style):
        (data['x'], data['y'])
    def drawPlot(self, ax, data, keys, style):
        (*[data[key] for key in keys], **style)
    def drawScatter(self, ax, data, keys, style):
        (*[data[key] for key in keys])
    def setFrmFig(self, frmFig):
         = Figure()
         = (,frmFig)
        .get_tk_widget().pack(
            side=,fill=,expand=)
         = mbb.NavigationToolbar2Tk(,frmFig,
            pack_toolbar=False)
        ()
        (side=)
if __name__ == "__main__":
    test = DarwSystem()

Above is Python add timeline to achieve dynamic drawing details, more information about Python dynamic drawing, please pay attention to my other related articles!