python - إدارة التآمر الديناميكي في وحدة الرسوم المتحركة ماتلوتليب




animation matplotlib (2)

للحصول على إجابة العمل المناسبة مع وحدة الرسوم المتحركة انظر إجابة إمبورتانسوفبينجيرنيست

لدي قضايا متعددة مع وظيفة المقصود الخاص بك. كيف يعمل تقدم الرسوم المتحركة جنبا إلى جنب مع عكس؟ هل سيكون هناك فيديو، ولكن الضغط على زر يبدأ تشغيله مرة أخرى؟ أو ينبغي أن يكون هناك خطوات فردية من الإطارات؟ أنا لست متأكدا من أنني أفهم كيف يمكن أن يقترن الرسوم المتحركة لهذه الميزة عكس. أنا صور الرسوم المتحركة ماتلوتليب لتكون أساسا الأفلام.

قضايتي الأخرى هي واحدة فنية: أنا لست متأكدا من أن هذا يمكن القيام به مع الرسوم المتحركة ماتبلوتليب. توضح المستندات أن FuncAnimation ينفذ بشكل سطحي

for d in frames:
   artists = func(d, *fargs)
   fig.canvas.draw_idle()
   plt.pause(interval)

حيث frames هي في الأساس قابلة للتكرار . لا يبدو واضحا بالنسبة لي لضبط حيوي frames أثناء الرسوم المتحركة، لذلك هذا هو عقبة تقنية.

في الواقع، وظيفة وصفت يعمل أفضل بكثير في رأسي في النهج القائم على القطعة. يمكن للأزرار نشر "الرسوم المتحركة"، أو يمكن أن يكون لديك زر الاختيار الذي يعدل ما إذا كانت الخطوة التالية يذهب إلى الأمام أو الخلف. وهنا دليل بسيط على مفهوم ما أعنيه:

import matplotlib.pyplot as plt
from matplotlib.widgets import Button
import numpy as np # just for dummy data generation

# generate dummy data
ndat = 20
x = np.linspace(0,1,ndat)
phi = np.linspace(0,2*np.pi,100,endpoint=False)
dat = np.transpose([x[:,None]*np.cos(phi),x[:,None]*np.sin(phi)],(1,2,0))

# create figure and axes
fig = plt.figure()
ax_pl = plt.subplot2grid((5,5),(0,0),colspan=5,rowspan=3)  # axes_plot
ax_bl = plt.subplot2grid((5,5),(4,0),colspan=2,rowspan=1)  # axes_button_left
ax_br = plt.subplot2grid((5,5),(4,3),colspan=2,rowspan=1)  # axes_button_right

# create forward/backward buttons
butt_l = Button(ax_bl, '\N{leftwards arrow}') # or u'' on python 2
butt_r = Button(ax_br, '\N{rightwards arrow}') # or u'' on python 2

# create initial plot
# store index of data and handle to plot as axes property because why not
ax_pl.idat = 0
hplot = ax_pl.scatter(*dat[ax_pl.idat].T)
ax_pl.hpl = hplot
ax_pl.axis('scaled')
ax_pl.axis([dat[...,0].min(),dat[...,0].max(),
            dat[...,1].min(),dat[...,1].max()])
ax_pl.set_autoscale_on(False)
ax_pl.set_title('{}/{}'.format(ax_pl.idat,dat.shape[0]-1))

# define and hook callback for buttons
def replot_data(ax_pl,dat):
    '''replot data after button push, assumes constant data shape'''
    ax_pl.hpl.set_offsets(dat[ax_pl.idat])
    ax_pl.set_title('{}/{}'.format(ax_pl.idat,dat.shape[0]-1))
    ax_pl.get_figure().canvas.draw()

def left_onclicked(event,ax=ax_pl,dat=dat):
    '''try to decrement data index, replot if success'''
    if ax.idat > 0:
        ax.idat -= 1
        replot_data(ax,dat)

def right_onclicked(event,ax=ax_pl,dat=dat):
    '''try to increment data index, replot if success'''
    if ax.idat < dat.shape[0]-1:
        ax.idat += 1
        replot_data(ax,dat)

butt_l.on_clicked(left_onclicked)
butt_r.on_clicked(right_onclicked)

plt.show()

لاحظ أنني لست حقا من ذوي الخبرة مع الحاجيات ماتلوتليب أو واجهات المستخدم الرسومية بشكل عام، لذلك لا نتوقع أعلاه لتتوافق مع أفضل الممارسات في هذا الموضوع. كما أضفت بعض المعلمات الإضافية التي سيتم تمريرها هنا وهناك، لأن لدي نفور لاستخدام الأسماء العالمية، ولكن هذا قد يكون خرافي بعض الشيء في هذا السياق. أنا بصراحة لا يمكن أن أقول. أيضا، إذا كنت تحديد هذه الكائنات داخل فئة أو وظيفة، تأكد من الاحتفاظ مرجع إلى الحاجيات وإلا فإنها قد تصبح لا تستجيب عندما عن طريق الخطأ القمامة التي تم جمعها.

الرقم الناتج لديه محاور للتآمر مؤامرات مبعثر، وهناك نوعان من الأزرار لزيادة مؤشر تشريح. وتشكل البيانات (ndat,100,2) ، حيث تحدد المؤشرات (ndat,100,2) 100 نقطة في الفضاء 2d. حالة محددة:

(لا يجب أن يكون هذا القبيحة، وأنا فقط لا تريد أن كمان مع التصميم.)

حتى أنني يمكن أن نتصور الإعداد حيث الموقت تلقائيا بتحديث المؤامرة، واتجاه التحديث يمكن تعيين مع القطعة. أنا لست متأكدا من كيف يمكن القيام بذلك بشكل صحيح، ولكن سأحاول متابعة هذا الطريق لنوع من التصور يبدو أن بعد.

نلاحظ أيضا أن النهج أعلاه هو في عداد المفقودين تماما التحسينات FuncAnimation وغيرها من التحسينات التي FuncAnimation القيام به، ولكن هذا نأمل أن لا تتداخل مع التصور الخاص بك.

أود أن يكون لها رسم بياني تكراري التي تسمح للتخطي إلى الإطار التالي، ووقفه والعودة إلى الإطار السابق.

لقد نظرت في وحدة الرسوم المتحركة ماتلوتليب التي من شأنها أن تكون مثالية إذا كان هناك طريقة لتنفيذ وظيفة الإطار السابق (مثل تشغيل الرسوم المتحركة إلى الوراء لعدد قليل من الإطارات عند الضغط على مفتاح)

سيكون لطيفا إلى شيء من هذا القبيل:

def update_frame(i, data):
    fig.set_data(data[i])

ولكن بطريقة تمكنت من إدارة صريحة ما إذا كان i إيتراتور يزيد أو ينخفض.

هل هناك طريقة للقيام بذلك في ماتبلوتليب؟ يجب أن أبحث عن وحدة بيثون مختلفة؟


تسمح فئة FuncAnimation بتوريد وظيفة مولد إلى وسيطة frames . ومن المتوقع أن تؤدي هذه الدالة إلى قيمة يتم توفيرها لوظيفة التحديث لكل خطوة من خطوات أنيمانتيون.

تنص وثيقة FuncAnimation يلي:

frames : متكررة، إنت، وظيفة مولد، أو لا شيء، اختياري [..]
إذا كان وظيفة مولد، ثم يجب أن يكون التوقيع
def gen_function() -> obj:
في جميع هذه الحالات، يتم تمرير القيم في الإطارات ببساطة إلى فونك الموردة من قبل المستخدم وبالتالي يمكن أن يكون من أي نوع.

يمكننا الآن إنشاء وظيفة مولد الذي ينتج الأعداد الصحيحة إما في الأمام أو في الاتجاه الخلفي بحيث أن الرسوم المتحركة يمضي إلى الأمام أو إلى الوراء . لتوجيه الرسوم المتحركة، قد نستخدم matplotlib.widgets.Button s matplotlib.widgets.Button خطوة واحدة إلى الأمام أو الخلف وظائف. هذا يشبه إجابتي على السؤال حول حلقات من خلال مجموعة من الصور.

وفيما يلي فئة تسمى Player الفئات الفرعية FuncAnimation و إنكوربوراتس كل هذا، مما يسمح لبدء وإيقاف الرسوم المتحركة. ويمكن أن مثيل على غرار FuncAnimation ،

ani = Player(fig, update, mini=0, maxi=10)

حيث سيكون التحديث وظيفة تحديث، ويتوقع عددا صحيحا من المدخلات، و mini و maxi تدل على الحد الأدنى والحد الأقصى لعدد أن وظيفة يمكن أن تستخدم. هذه الفئة بتخزين قيمة الفهرس الحالي ( self.i )، بحيث إذا تم إيقاف الحركة أو عادت فإنه سيتم إعادة تشغيل في الإطار الحالي.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import mpl_toolkits.axes_grid1
import matplotlib.widgets

class Player(FuncAnimation):
    def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
                 save_count=None, mini=0, maxi=100, pos=(0.125, 0.92), **kwargs):
        self.i = 0
        self.min=mini
        self.max=maxi
        self.runs = True
        self.forwards = True
        self.fig = fig
        self.func = func
        self.setup(pos)
        FuncAnimation.__init__(self,self.fig, self.func, frames=self.play(), 
                                           init_func=init_func, fargs=fargs,
                                           save_count=save_count, **kwargs )    

    def play(self):
        while self.runs:
            self.i = self.i+self.forwards-(not self.forwards)
            if self.i > self.min and self.i < self.max:
                yield self.i
            else:
                self.stop()
                yield self.i

    def start(self):
        self.runs=True
        self.event_source.start()

    def stop(self, event=None):
        self.runs = False
        self.event_source.stop()

    def forward(self, event=None):
        self.forwards = True
        self.start()
    def backward(self, event=None):
        self.forwards = False
        self.start()
    def oneforward(self, event=None):
        self.forwards = True
        self.onestep()
    def onebackward(self, event=None):
        self.forwards = False
        self.onestep()

    def onestep(self):
        if self.i > self.min and self.i < self.max:
            self.i = self.i+self.forwards-(not self.forwards)
        elif self.i == self.min and self.forwards:
            self.i+=1
        elif self.i == self.max and not self.forwards:
            self.i-=1
        self.func(self.i)
        self.fig.canvas.draw_idle()

    def setup(self, pos):
        playerax = self.fig.add_axes([pos[0],pos[1], 0.22, 0.04])
        divider = mpl_toolkits.axes_grid1.make_axes_locatable(playerax)
        bax = divider.append_axes("right", size="80%", pad=0.05)
        sax = divider.append_axes("right", size="80%", pad=0.05)
        fax = divider.append_axes("right", size="80%", pad=0.05)
        ofax = divider.append_axes("right", size="100%", pad=0.05)
        self.button_oneback = matplotlib.widgets.Button(playerax, label=ur'$\u29CF$')
        self.button_back = matplotlib.widgets.Button(bax, label=ur'$\u25C0$')
        self.button_stop = matplotlib.widgets.Button(sax, label=ur'$\u25A0$')
        self.button_forward = matplotlib.widgets.Button(fax, label=ur'$\u25B6$')
        self.button_oneforward = matplotlib.widgets.Button(ofax, label=ur'$\u29D0$')
        self.button_oneback.on_clicked(self.onebackward)
        self.button_back.on_clicked(self.backward)
        self.button_stop.on_clicked(self.stop)
        self.button_forward.on_clicked(self.forward)
        self.button_oneforward.on_clicked(self.oneforward)

### using this class is as easy as using FuncAnimation:            

fig, ax = plt.subplots()
x = np.linspace(0,6*np.pi, num=100)
y = np.sin(x)

ax.plot(x,y)
point, = ax.plot([],[], marker="o", color="crimson", ms=15)

def update(i):
    point.set_data(x[i],y[i])

ani = Player(fig, update, maxi=len(y)-1)

plt.show()





matplotlib