2016-11-23 4 views
0

私は2つの異なるグラフがアニメーション化される必要があるプログラムに取り組んでいます。私は使用している構造でこれを行う方法を困惑させている。私はあなたがそれを試すことができるように私の下に自分のコードを貼り付けます。私はできるだけそれを取り除いていますが、コア機能はまだ残していますので、理解してもらえないことを願っています。現状ではアニメーションラインは何もしていないので、私が間違っている場所を教えてください。Tkinter/Matplotlibアニメーションアシスタンス

from Tkinter import *    #Used for GUI elements 
import time       #Used for timing elements 
import matplotlib     #Used for graphing 
matplotlib.use("TkAgg") 
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg 
from matplotlib.figure import Figure 
import matplotlib.animation as animation 
import numpy as np     #Used for arrays to find min/max of float array 
import random      #Only used for fake data input 


class tkgui: 
    def __init__(self, parent): 
     #--------------The following are variables that need to be accessed by other functions---------------------- 
     #Raw input values 
     self.x = 209500 
     self.y = 0 
     self.timeElapsed = 0 

     #State values 
     self.curFrame = 1 

     #List Values 
     self.timeList = np.array([]) 
     self.yList = np.array([]) 
     self.xList = np.array([]) 

     self.lastX = 0 
     #---------------------------------------------------------------------------------------------------------- 

     #Make Tkinter fullscreen 
     w, h = 320,240 #int(str(root.winfo_screenwidth())), int(str(root.winfo_screenheight())) #320, 240 is the RPiTFT 

     #The base layer of the GUI 
     topLevelContainer = Frame(parent) 
     topLevelContainer.pack() 

     #The two 'screens' to switch between. They contain everything on the GUI 
     self.buttonsFrame = Frame(topLevelContainer) 
     self.graphFrame = Frame(topLevelContainer) 

     #Stack the frames so that they are switchable 
     for frame in self.buttonsFrame, self.graphFrame: 
      frame.grid(row=0, column=0, sticky='news', padx=5, pady=(10, 10)) 

     buttonsFrameButtons = Frame(self.buttonsFrame) 
     buttonsFrameButtons.pack(side=LEFT, padx=(0, 50)) 

     #X button 
     self.xButton = Button(buttonsFrameButtons, command=self.xButtonClick) 
     self.xButton.configure(text="X", background="#C8C8C8", width=6, padx=35, pady=35) 
     self.xButton.pack(side=TOP, pady=10) 

     #Y button 
     self.yButton = Button(buttonsFrameButtons, command=self.yButtonClick) 
     self.yButton.configure(text="Y", background="#C8C8C8", width=6, padx=35, pady=35) 
     self.yButton.pack(side=TOP, pady=10) 

     #Bar graph 
     buttonsFrameBar = Frame(self.buttonsFrame) 
     buttonsFrameBar.pack(side=LEFT) 

     self.fBar = Figure(figsize=(2, 4), dpi=50) 
     aBar = self.fBar.add_subplot(111) 
     self.xBar = aBar.bar([0, 1], [0, 0], width=1) 

     lAxes = self.fBar.gca() 
     lAxes.axes.get_xaxis().set_ticklabels([]) 

     aBar.set_ylim([-30000, 30000]) 
     self.fBar.tight_layout() 

     self.buttonsFrame.tkraise()   

     #Setup the matplotlib graph 
     self.fGraph = Figure(figsize=(5, 3), dpi=50) 
     #Create the Y axis 
     aGraph = self.fGraph.add_subplot(111) 
     aGraph.set_xlabel("Time (s)") 
     aGraph.set_ylabel("Y") 
     self.yLine, = aGraph.plot([],[], "r-") 

     #Create the X axis 
     a2Graph = aGraph.twinx() 
     self.xLine, = a2Graph.plot([], []) 
     a2Graph.set_ylabel("X") 

     #Final matplotlib/Tkinter setup 
     self.canvasGraph = FigureCanvasTkAgg(self.fGraph, master=self.graphFrame) 
     self.canvasGraph.show() 
     self.canvasGraph.get_tk_widget().pack(side=LEFT, fill=BOTH, expand=1) 

     self.canvasBar = FigureCanvasTkAgg(self.fBar, master=buttonsFrameBar) 
     self.canvasBar.show() 
     self.canvasBar.get_tk_widget().pack(side=BOTTOM, fill=BOTH, expand=1) 

     #Resize the plot to fit all of the labels in 
     self.fGraph.subplots_adjust(bottom=0.13, left=0.15, right=0.87)  

    def refreshGraph(self, frameno):  #Redraw the graph with the updated arrays and resize it accordingly 
     #Update data 
     self.yLine.set_data(self.timeList, self.yList) 
     self.xLine.set_data(self.timeList, self.xList) 

     #Update y axis 
     ax = self.canvasGraph.figure.axes[0] 
     ax.set_xlim(self.timeList.min(), self.timeList.max()) 
     ax.set_ylim(self.yList.min(), self.yList.max()) 

     #Update x axis 
     ax2 = self.canvasGraph.figure.axes[1] 
     ax2.set_xlim(self.timeList.min(), self.timeList.max()) 
     ax2.set_ylim(self.xList.min(), self.xList.max()) 

     #Redraw 
     self.canvasGraph.draw() 


    def refreshBar(self, frameno): 
     curX = self.x 
     dif = curX - self.lastX 
     i = [dif] 
     for rect, h in zip(self.xBar, i): 
      rect.set_height(h) 
      if dif > 0: 
       rect.set_color('b') 
      else: 
       rect.set_color('r') 

     self.canvasBar.draw() 
     self.lastX=curX 

    def switchFrame(self):  #Switch the current screen. Either x/y buttons or graph 
     if self.curFrame: 
      self.graphFrame.tkraise() 
      self.curFrame = 0 
     else: 
      self.buttonsFrame.tkraise() 
      self.curFrame = 1 

    def xButtonClick(self): 
     self.switchFrame() 

    def yButtonClick(self): 
     self.close() 

    def close(e):    #Exit the program 
     sys.exit() 

#Initialisation of global variables 
lastTime = 0  #Used for the 'last time' iterated 
yState = 0  

def updateNumbers():  #Used to generate fake input variables. Will be replaced by ADC values 
    global lastTime 
    global yState 

    curTime = time.time()           #Update the time each time the function is called 
    if curTime - lastTime > 0.5:         #Only update numbers every 0.5 seconds 
     gui.x = random.randrange(200000, 230000)     #Generates x 
     if yState: 
      gui.y = gui.y - 20        #Decrease y 
      if gui.y < 1: 
       yState = 0          #Until it gets to a minimum of 0 
     else: 
      gui.y = gui.y + 20        #Increase y 
      if gui.y > 1300: 
       yState = 1          #Until it reaches a maximum of 1300 
     gui.yList = np.append(gui.yList, gui.y)   #Add the new y values to the array 
     gui.xList = np.append(gui.xList, gui.x/10000.0)   #Add the new x values to the array 
     lastTime = time.time()          #Record the last time iterated for timing purposes 
     gui.timeElapsed += 0.5          
     gui.timeList = np.append(gui.timeList, gui.timeElapsed)  #Add the latest time to the array 
##  gui.refreshGraph()           #Call the function that will redraw the graph with the new figures 

if __name__ == "__main__": 
    root = Tk()   #Root Tkinter setup 
    gui = tkgui(root) #Setup the gui class 

    updateNumbers() 
    aniGraph = animation.FuncAnimation(gui.fGraph,gui.refreshGraph,interval=500,frames=100,repeat=True) 
    aniBar = animation.FuncAnimation(gui.fBar,gui.refreshBar,interval=500,frames=100,repeat=True) 

    while(1):     #Main loop 
     updateNumbers()   #Update fake values 

     root.update()   #Update the gui loop 

    root.mainloop()    #Tkinter main loop 

明確にするために、私はこのコードでアニメーションをどのように動作させるかを尋ねています。

+0

afterを使用することができます'そして 'root.mainloopを( ) 'は決して使用されません。 'updateNumber'の終わりに' root.after(500、updateNumber) 'を使うことができます。そして、' while(1) 'ループを必要としないように、500ms(0.5s)後に' updateNumber'を再度実行します。 – furas

+0

私はバーアニメーションを見て、Xボタンをクリックするとラインアニメーションが見えます。私は何が問題なのか分からない。 – furas

+0

これはコードのこのバージョンでは表示されませんが、他の理由で実行する定数ループが必要です。私はその中でroot.update()がその問題を修正したという印象を受けましたか?同じコードを実行するとアニメーションが表示されないので、何かを変更しましたか? –

答えて

1

すべてのアニメーションが表示されますが、While(1):(またはそれ以上のpythonic While True:)を使用しないで実行する場合は、root.after(milliseconds, function_name)を使用できます。 FuncAnimationの代わりにイベントを使用できます。

そして、それはあなたが機能を制御できるように - それを開始/停止します。

あなたがそれを起動(またはそれを再起動します)を使用
if self.run_bar: 
    root.after(100, self.refreshBar) 

self.run_bar = True 
self.refreshBar() 

、あなたは

self.run_bar = False 

それを止めることができ、コード内のすべての# <--を参照してください。

from Tkinter import *    #Used for GUI elements 
import time       #Used for timing elements 
import matplotlib     #Used for graphing 
matplotlib.use("TkAgg") 
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg 
from matplotlib.figure import Figure 
import matplotlib.animation as animation 
import numpy as np     #Used for arrays to find min/max of float array 
import random      #Only used for fake data input 


class TkGUI: # <-- CamelCase names for classes 
      # <-- empty line for readabelity 
    def __init__(self, parent): 
     #--- The following are variables that need to be accessed by other functions---------------------- 
     #Raw input values 
     self.x = 209500 
     self.y = 0 
     self.timeElapsed = 0 

     #State values 
     self.curFrame = 1 

     #List Values 
     self.timeList = np.array([]) 
     self.yList = np.array([]) 
     self.xList = np.array([]) 

     self.lastX = 0 
     #----------------------------------------------------------- 

     #Make Tkinter fullscreen 
     w, h = 320,240 #int(str(root.winfo_screenwidth())), int(str(root.winfo_screenheight())) #320, 240 is the RPiTFT 

     #The base layer of the GUI 
     topLevelContainer = Frame(parent) 
     topLevelContainer.pack() 

     #The two 'screens' to switch between. They contain everything on the GUI 
     self.buttonsFrame = Frame(topLevelContainer) 
     self.graphFrame = Frame(topLevelContainer) 

     #Stack the frames so that they are switchable 
     for frame in self.buttonsFrame, self.graphFrame: 
      frame.grid(row=0, column=0, sticky='news', padx=5, pady=(10, 10)) 

     buttonsFrameButtons = Frame(self.buttonsFrame) 
     buttonsFrameButtons.pack(side=LEFT, padx=(0, 50)) 

     #X button 
     self.xButton = Button(buttonsFrameButtons, command=self.xButtonClick) 
     self.xButton.configure(text="X", background="#C8C8C8", width=6, padx=35, pady=35) 
     self.xButton.pack(side=TOP, pady=10) 

     #Y button 
     self.yButton = Button(buttonsFrameButtons, command=self.yButtonClick) 
     self.yButton.configure(text="Y", background="#C8C8C8", width=6, padx=35, pady=35) 
     self.yButton.pack(side=TOP, pady=10) 

     #Bar graph 
     buttonsFrameBar = Frame(self.buttonsFrame) 
     buttonsFrameBar.pack(side=LEFT) 

     self.fBar = Figure(figsize=(2, 4), dpi=50) 
     aBar = self.fBar.add_subplot(111) 
     self.xBar = aBar.bar([0, 1], [0, 0], width=1) 

     lAxes = self.fBar.gca() 
     lAxes.axes.get_xaxis().set_ticklabels([]) 

     aBar.set_ylim([-30000, 30000]) 
     self.fBar.tight_layout() 

     self.buttonsFrame.tkraise()   

     #Setup the matplotlib graph 
     self.fGraph = Figure(figsize=(5, 3), dpi=50) 
     #Create the Y axis 
     aGraph = self.fGraph.add_subplot(111) 
     aGraph.set_xlabel("Time (s)") 
     aGraph.set_ylabel("Y") 
     self.yLine, = aGraph.plot([],[], "r-") 

     #Create the X axis 
     a2Graph = aGraph.twinx() 
     self.xLine, = a2Graph.plot([], []) 
     a2Graph.set_ylabel("X") 

     #Final matplotlib/Tkinter setup 
     self.canvasGraph = FigureCanvasTkAgg(self.fGraph, master=self.graphFrame) 
     self.canvasGraph.show() 
     self.canvasGraph.get_tk_widget().pack(side=LEFT, fill=BOTH, expand=1) 

     self.canvasBar = FigureCanvasTkAgg(self.fBar, master=buttonsFrameBar) 
     self.canvasBar.show() 
     self.canvasBar.get_tk_widget().pack(side=BOTTOM, fill=BOTH, expand=1) 

     #Resize the plot to fit all of the labels in 
     self.fGraph.subplots_adjust(bottom=0.13, left=0.15, right=0.87)  

    def refreshGraph(self): # <-- without argument 
     '''Redraw the graph with the updated arrays and resize it accordingly''' # <-- docstring used by documentation generator and IDE as help 

     #Update data 
     self.yLine.set_data(self.timeList, self.yList) 
     self.xLine.set_data(self.timeList, self.xList) 

     #Update y axis 
     ax = self.canvasGraph.figure.axes[0] 
     ax.set_xlim(self.timeList.min(), self.timeList.max()) 
     ax.set_ylim(self.yList.min(), self.yList.max()) 

     #Update x axis 
     ax2 = self.canvasGraph.figure.axes[1] 
     ax2.set_xlim(self.timeList.min(), self.timeList.max()) 
     ax2.set_ylim(self.xList.min(), self.xList.max()) 

     #Redraw 
     self.canvasGraph.draw() 

     # run again after 100ms (0.1s) 
     root.after(100, self.refreshGraph) # <-- run again like in loop 

    def refreshBar(self): # <-- without argument 
     curX = self.x 
     dif = curX - self.lastX 
     i = [dif] 
     for rect, h in zip(self.xBar, i): 
      rect.set_height(h) 
      if dif > 0: 
       rect.set_color('b') 
      else: 
       rect.set_color('r') 

     self.canvasBar.draw() 
     self.lastX=curX 
     # run again after 100ms (0.1s) 
     root.after(100, self.refreshBar) # <-- run again like in loop 

    def switchFrame(self):  #Switch the current screen. Either x/y buttons or graph 
     if self.curFrame: 
      self.graphFrame.tkraise() 
      self.curFrame = 0 
     else: 
      self.buttonsFrame.tkraise() 
      self.curFrame = 1 

    def xButtonClick(self): 
     self.switchFrame() 

    def yButtonClick(self): 
     self.close() 

    def close(e): # Exit the program 
     sys.exit() 

#Initialisation of global variables 
lastTime = 0  #Used for the 'last time' iterated 
yState = 0  

def updateNumbers():  #Used to generate fake input variables. Will be replaced by ADC values 
    global lastTime 
    global yState 

    curTime = time.time()           #Update the time each time the function is called 
    if curTime - lastTime > 0.5:         #Only update numbers every 0.5 seconds 
     gui.x = random.randrange(200000, 230000)     #Generates x 
     if yState: 
      gui.y = gui.y - 20        #Decrease y 
      if gui.y < 1: 
       yState = 0          #Until it gets to a minimum of 0 
     else: 
      gui.y = gui.y + 20        #Increase y 
      if gui.y > 1300: 
       yState = 1          #Until it reaches a maximum of 1300 
     gui.yList = np.append(gui.yList, gui.y)   #Add the new y values to the array 
     gui.xList = np.append(gui.xList, gui.x/10000.0)   #Add the new x values to the array 
     lastTime = time.time()          #Record the last time iterated for timing purposes 
     gui.timeElapsed += 0.5          
     gui.timeList = np.append(gui.timeList, gui.timeElapsed)  #Add the latest time to the array 

    # run again after 100ms (0.1s) 
    root.after(100, updateNumbers) # <-- run again like in loop  

if __name__ == "__main__": 
    root = Tk() 
    gui = TkGUI(root) 

    # <-- vvv - without While and without FuncAnimation - vvv 

    updateNumbers()  # run first time 

    gui.refreshBar() # run first time 
    gui.refreshGraph() # run first time 

    # <-- ^^^ - without While and without FuncAnimation - ^^^ 

    root.mainloop()  # Tkinter main loop 

EDIT:もちろんあなたはafterなしFuncAnimationを維持し、あなたが `しながら、(1)を使用する場合にのみupdateNumbers

from Tkinter import *    #Used for GUI elements 
import time       #Used for timing elements 
import matplotlib     #Used for graphing 
matplotlib.use("TkAgg") 
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg 
from matplotlib.figure import Figure 
import matplotlib.animation as animation 
import numpy as np     #Used for arrays to find min/max of float array 
import random      #Only used for fake data input 


class TkGUI: # <-- CamelCase names for classes 
      # <-- empty line for readabelity 
    def __init__(self, parent): 
     #--- The following are variables that need to be accessed by other functions---------------------- 
     #Raw input values 
     self.x = 209500 
     self.y = 0 
     self.timeElapsed = 0 

     #State values 
     self.curFrame = 1 

     #List Values 
     self.timeList = np.array([]) 
     self.yList = np.array([]) 
     self.xList = np.array([]) 

     self.lastX = 0 
     #----------------------------------------------------------- 

     #Make Tkinter fullscreen 
     w, h = 320,240 #int(str(root.winfo_screenwidth())), int(str(root.winfo_screenheight())) #320, 240 is the RPiTFT 

     #The base layer of the GUI 
     topLevelContainer = Frame(parent) 
     topLevelContainer.pack() 

     #The two 'screens' to switch between. They contain everything on the GUI 
     self.buttonsFrame = Frame(topLevelContainer) 
     self.graphFrame = Frame(topLevelContainer) 

     #Stack the frames so that they are switchable 
     for frame in self.buttonsFrame, self.graphFrame: 
      frame.grid(row=0, column=0, sticky='news', padx=5, pady=(10, 10)) 

     buttonsFrameButtons = Frame(self.buttonsFrame) 
     buttonsFrameButtons.pack(side=LEFT, padx=(0, 50)) 

     #X button 
     self.xButton = Button(buttonsFrameButtons, command=self.xButtonClick) 
     self.xButton.configure(text="X", background="#C8C8C8", width=6, padx=35, pady=35) 
     self.xButton.pack(side=TOP, pady=10) 

     #Y button 
     self.yButton = Button(buttonsFrameButtons, command=self.yButtonClick) 
     self.yButton.configure(text="Y", background="#C8C8C8", width=6, padx=35, pady=35) 
     self.yButton.pack(side=TOP, pady=10) 

     #Bar graph 
     buttonsFrameBar = Frame(self.buttonsFrame) 
     buttonsFrameBar.pack(side=LEFT) 

     self.fBar = Figure(figsize=(2, 4), dpi=50) 
     aBar = self.fBar.add_subplot(111) 
     self.xBar = aBar.bar([0, 1], [0, 0], width=1) 

     lAxes = self.fBar.gca() 
     lAxes.axes.get_xaxis().set_ticklabels([]) 

     aBar.set_ylim([-30000, 30000]) 
     self.fBar.tight_layout() 

     self.buttonsFrame.tkraise()   

     #Setup the matplotlib graph 
     self.fGraph = Figure(figsize=(5, 3), dpi=50) 
     #Create the Y axis 
     aGraph = self.fGraph.add_subplot(111) 
     aGraph.set_xlabel("Time (s)") 
     aGraph.set_ylabel("Y") 
     self.yLine, = aGraph.plot([],[], "r-") 

     #Create the X axis 
     a2Graph = aGraph.twinx() 
     self.xLine, = a2Graph.plot([], []) 
     a2Graph.set_ylabel("X") 

     #Final matplotlib/Tkinter setup 
     self.canvasGraph = FigureCanvasTkAgg(self.fGraph, master=self.graphFrame) 
     self.canvasGraph.show() 
     self.canvasGraph.get_tk_widget().pack(side=LEFT, fill=BOTH, expand=1) 

     self.canvasBar = FigureCanvasTkAgg(self.fBar, master=buttonsFrameBar) 
     self.canvasBar.show() 
     self.canvasBar.get_tk_widget().pack(side=BOTTOM, fill=BOTH, expand=1) 

     #Resize the plot to fit all of the labels in 
     self.fGraph.subplots_adjust(bottom=0.13, left=0.15, right=0.87)  

    def refreshGraph(self, i): 
     '''Redraw the graph with the updated arrays and resize it accordingly''' # <-- docstring used by documentation generator and IDE as help 

     #Update data 
     self.yLine.set_data(self.timeList, self.yList) 
     self.xLine.set_data(self.timeList, self.xList) 

     #Update y axis 
     ax = self.canvasGraph.figure.axes[0] 
     ax.set_xlim(self.timeList.min(), self.timeList.max()) 
     ax.set_ylim(self.yList.min(), self.yList.max()) 

     #Update x axis 
     ax2 = self.canvasGraph.figure.axes[1] 
     ax2.set_xlim(self.timeList.min(), self.timeList.max()) 
     ax2.set_ylim(self.xList.min(), self.xList.max()) 

     #Redraw 
     self.canvasGraph.draw() 

    def refreshBar(self, i): 
     curX = self.x 
     dif = curX - self.lastX 
     i = [dif] 
     for rect, h in zip(self.xBar, i): 
      rect.set_height(h) 
      if dif > 0: 
       rect.set_color('b') 
      else: 
       rect.set_color('r') 

     self.canvasBar.draw() 
     self.lastX=curX 

    def switchFrame(self): 
     '''Switch the current screen. Either x/y buttons or graph''' 

     if self.curFrame: 
      self.graphFrame.tkraise() 
      self.curFrame = 0 
     else: 
      self.buttonsFrame.tkraise() 
      self.curFrame = 1 

    def xButtonClick(self): 
     self.switchFrame() 

    def yButtonClick(self): 
     self.close() 

    def close(e): # Exit the program 
     sys.exit() 

#Initialisation of global variables 
lastTime = 0  #Used for the 'last time' iterated 
yState = 0  

def updateNumbers():  #Used to generate fake input variables. Will be replaced by ADC values 
    global lastTime 
    global yState 

    curTime = time.time()           #Update the time each time the function is called 
    if curTime - lastTime > 0.5:         #Only update numbers every 0.5 seconds 
     gui.x = random.randrange(200000, 230000)     #Generates x 
     if yState: 
      gui.y = gui.y - 20        #Decrease y 
      if gui.y < 1: 
       yState = 0          #Until it gets to a minimum of 0 
     else: 
      gui.y = gui.y + 20        #Increase y 
      if gui.y > 1300: 
       yState = 1          #Until it reaches a maximum of 1300 
     gui.yList = np.append(gui.yList, gui.y)   #Add the new y values to the array 
     gui.xList = np.append(gui.xList, gui.x/10000.0)   #Add the new x values to the array 
     lastTime = time.time()          #Record the last time iterated for timing purposes 
     gui.timeElapsed += 0.5          
     gui.timeList = np.append(gui.timeList, gui.timeElapsed)  #Add the latest time to the array 

    # run again after 100ms (0.1s) 
    root.after(100, updateNumbers) # <-- run again like in loop  

if __name__ == "__main__": 
    root = Tk() 
    gui = TkGUI(root) 

    aniGraph = animation.FuncAnimation(gui.fGraph,gui.refreshGraph,interval=500,frames=100,repeat=True) 
    aniBar = animation.FuncAnimation(gui.fBar,gui.refreshBar,interval=500,frames=100,repeat=True) 

    # <-- vvv - without While - vvv 

    updateNumbers()  # run first time 

    # <-- ^^^ - without While - ^^^ 

    root.mainloop()  # Tkinter main loop 
+0

ありがとう、これは動作します。しかし、私はmatplotlibアニメーションを使うのは、それを再描画するよりも最適化されているのだろうかと思った。私は主なコードで大規模なデータセットと長いグラフを取得し始めたとき、またはそれを速く更新すると状況が少し遅くなり始めるからです。 –

+0

私は両方のメソッドの速度を決してチェックしませんが、 'refreshBar'と' refreshGraph'では何も変更しませんでしたので、両方のメソッドが同じ速度で同じ方法で動作するはずです。たぶんFuncAnimationのソースコードをチェックした後、おそらく 'after()'も見えます:) 'tkinter'で動作する' matplotlib/FuncAnimation'は 'tkinter'関数を使用しなければなりません。 – furas

+0

ところで、 'after'を使わずに' FuncAnimation'を保ち、 'updateNumbers'の中で' after'だけ使うこともできます。 – furas