在 Matplotlib 中像 Google 财经图表一样制作动画?

发布于 2024-08-20 23:19:28 字数 5575 浏览 3 评论 0原文

我刚刚开始尝试使用 Matplotlib 的动画功能来生成 Google Finance 的图表。

我结合了在项目网站上找到的两个示例(可拖动矩形练习api 示例代码:date_demo.py)并对它们进行了一些调整以得出代码列在底部。

虽然看起来还不错,但我希望顶部图表(主)随着底部图表(从)选择的移动而动态更新,而不仅仅是在释放底部选择时。我该怎么做?我尝试将 self.rect.figure.canvas.draw() 位移至 on_motion 方法,但它似乎干扰了 blit 内容,因为底部选择获胜无法正确渲染。

因此,我认为解决方案是为底部图表(即 blit-ing 位)执行智能动画,而顶部图表则完全重新绘制。问题是,我可以重新绘制任何内容的唯一方法是重新绘制整个画布,这将包括底部图表。我确实找到了 matplotlib.axesdraw() 方法,但我无法让它工作。正如我上面所说,最好我只想重新绘制顶部图表,同时以巧妙的方式对底部图表进行位图传输。有谁知道该怎么做?

到目前为止,这是我的代码。请原谅代码,有点乱。

import datetime
import numpy as np
import sys
import time
import wx
import matplotlib
from matplotlib.figure import Figure 
import matplotlib.dates as mdates
import matplotlib.ticker as mtickers
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import matplotlib.patches as mpatches

class DraggableRectangle:
    lock = None
    def __init__(self, rect, master, xMin, xMax):       
        self.rect = rect        
        self.press = None
        self.background = None
        self.xMax = xMax
        self.xMin = xMin
        self.master = master
    def connect(self):      
        self.cidpress = self.rect.figure.canvas.mpl_connect('button_press_event', self.on_press)
        self.cidrelease = self.rect.figure.canvas.mpl_connect('button_release_event', self.on_release)
        self.cidmotion = self.rect.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)
    def on_press(self, event):      
        if event.inaxes != self.rect.axes: return
        if DraggableRectangle.lock is not None: return
        contains, attrd = self.rect.contains(event)
        if not contains: return     
        x0, y0 = self.rect.xy
        self.press = x0, y0, event.xdata, event.ydata
        DraggableRectangle.lock = self
        canvas = self.rect.figure.canvas
        axes = self.rect.axes
        self.rect.set_animated(True)
        canvas.draw()
        self.background = canvas.copy_from_bbox(self.rect.axes.bbox)
        axes.draw_artist(self.rect)
        canvas.blit(axes.bbox)
    def on_motion(self, event):
        if DraggableRectangle.lock is not self: return
        if event.inaxes != self.rect.axes: return
        x0, y0, xpress, ypress = self.press
        dx = event.xdata - xpress
        dy = 0
        if x0+dx > self.xMax:
            self.rect.set_x(self.xMax)
        elif x0+dx < self.xMin:
            self.rect.set_x(self.xMin)
        else:
            self.rect.set_x(x0+dx)
        self.rect.set_y(y0+dy)
        canvas = self.rect.figure.canvas
        axes = self.rect.axes
        canvas.restore_region(self.background)
        self.master.set_xlim(self.rect.get_x(), self.rect.get_x() + 92)
        axes.draw_artist(self.rect)
        canvas.blit(axes.bbox)
    def on_release(self, event):        
        if DraggableRectangle.lock is not self: return
        self.press = None
        DraggableRectangle.lock = None
        self.rect.set_animated(False)
        self.background = None
        self.rect.figure.canvas.draw()
    def disconnect(self):
        self.rect.figure.canvas.mpl_disconnect(self.cidpress)
        self.rect.figure.canvas.mpl_disconnect(self.cidrelease)
        self.rect.figure.canvas.mpl_disconnect(self.cidmotion)

class MplCanvasFrame(wx.Frame): 
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, title='First Chart', size=(800, 700))
        datafile = matplotlib.get_example_data('goog.npy')
        r = np.load(datafile).view(np.recarray)
        datesFloat = matplotlib.dates.date2num(r.date)
        figure = Figure()
        xMaxDatetime = r.date[len(r.date)-1]
        xMinDatetime = r.date[0]
        xMaxFloat = datesFloat[len(datesFloat)-1]
        xMinFloat = datesFloat[0]
        yMin = min(r.adj_close) // 5 * 5
        yMax = (1 + max(r.adj_close) // 5) * 5      
        master = figure.add_subplot(211) 
        master.plot(datesFloat, r.adj_close)
        master.xaxis.set_minor_locator(mdates.MonthLocator())
        master.xaxis.set_major_locator(mdates.MonthLocator(bymonth=(1,4,7,10)))
        master.xaxis.set_major_formatter(mdates.DateFormatter('%b-%y'))
        master.set_xlim(datesFloat[120], datesFloat[120]+92)
        master.yaxis.set_minor_locator(mtickers.MultipleLocator(50))
        master.yaxis.set_major_locator(mtickers.MultipleLocator(100))
        master.set_ylim(yMin, yMax)
        master.set_position([0.05,0.20,0.92,0.75])
        master.xaxis.grid(True, which='minor')
        master.yaxis.grid(True, which='minor')
        slave = figure.add_subplot(212, yticks=[]) 
        slave.plot(datesFloat, r.adj_close)
        slave.xaxis.set_minor_locator(mdates.MonthLocator())
        slave.xaxis.set_major_locator(mdates.YearLocator())
        slave.xaxis.set_major_formatter(mdates.DateFormatter('%b-%y'))
        slave.set_xlim(xMinDatetime, xMaxDatetime)
        slave.set_ylim(yMin, yMax)
        slave.set_position([0.05,0.05,0.92,0.10])
        rectangle = mpatches.Rectangle((datesFloat[120], yMin), 92, yMax-yMin, facecolor='yellow', alpha = 0.4)     
        slave.add_patch(rectangle)
        canvas = FigureCanvas(self, -1, figure)
        drag = DraggableRectangle(rectangle, master, xMinFloat, xMaxFloat - 92)
        drag.connect()

app = wx.PySimpleApp() 
frame = MplCanvasFrame()
frame.Show(True) 
app.MainLoop()

I just started toying around with Matplotlib's Animation capabilities in order to produce a Google Finance looking chart.

I combined two examples I found on the project website (Draggable rectangle exercise, api example code: date_demo.py) and tweaked them a bit to come up with the code listed at the bottom.

While it doesn't look too bad, I would like the top chart (master) update dynamically as the bottom chart (slave) selection is moved around, and not only when the bottom selection is released. How can I do this? I tried to move the self.rect.figure.canvas.draw() bit to the on_motion method, but it seems to interfere with the blit stuff as the bottom selection won't render properly.

So I would assume the solution would be to do the intelligent animation for the bottom chart, i.e., the blit-ing bit, while the top chart is just re-drawn altogether. The issue is that the only way I can redraw anything is through the re-drawing the whole canvas, and this would include the bottom chart. I did find the draw() method for matplotlib.axes, but I can't get it to work. As I said above, preferably I would like to just re-draw the top chart while the bottom one is blit-ed the clever way. Does anyone know how to do this?

Here is my code so far. Please excuse the code, it's a bit untidy.

import datetime
import numpy as np
import sys
import time
import wx
import matplotlib
from matplotlib.figure import Figure 
import matplotlib.dates as mdates
import matplotlib.ticker as mtickers
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import matplotlib.patches as mpatches

class DraggableRectangle:
    lock = None
    def __init__(self, rect, master, xMin, xMax):       
        self.rect = rect        
        self.press = None
        self.background = None
        self.xMax = xMax
        self.xMin = xMin
        self.master = master
    def connect(self):      
        self.cidpress = self.rect.figure.canvas.mpl_connect('button_press_event', self.on_press)
        self.cidrelease = self.rect.figure.canvas.mpl_connect('button_release_event', self.on_release)
        self.cidmotion = self.rect.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)
    def on_press(self, event):      
        if event.inaxes != self.rect.axes: return
        if DraggableRectangle.lock is not None: return
        contains, attrd = self.rect.contains(event)
        if not contains: return     
        x0, y0 = self.rect.xy
        self.press = x0, y0, event.xdata, event.ydata
        DraggableRectangle.lock = self
        canvas = self.rect.figure.canvas
        axes = self.rect.axes
        self.rect.set_animated(True)
        canvas.draw()
        self.background = canvas.copy_from_bbox(self.rect.axes.bbox)
        axes.draw_artist(self.rect)
        canvas.blit(axes.bbox)
    def on_motion(self, event):
        if DraggableRectangle.lock is not self: return
        if event.inaxes != self.rect.axes: return
        x0, y0, xpress, ypress = self.press
        dx = event.xdata - xpress
        dy = 0
        if x0+dx > self.xMax:
            self.rect.set_x(self.xMax)
        elif x0+dx < self.xMin:
            self.rect.set_x(self.xMin)
        else:
            self.rect.set_x(x0+dx)
        self.rect.set_y(y0+dy)
        canvas = self.rect.figure.canvas
        axes = self.rect.axes
        canvas.restore_region(self.background)
        self.master.set_xlim(self.rect.get_x(), self.rect.get_x() + 92)
        axes.draw_artist(self.rect)
        canvas.blit(axes.bbox)
    def on_release(self, event):        
        if DraggableRectangle.lock is not self: return
        self.press = None
        DraggableRectangle.lock = None
        self.rect.set_animated(False)
        self.background = None
        self.rect.figure.canvas.draw()
    def disconnect(self):
        self.rect.figure.canvas.mpl_disconnect(self.cidpress)
        self.rect.figure.canvas.mpl_disconnect(self.cidrelease)
        self.rect.figure.canvas.mpl_disconnect(self.cidmotion)

class MplCanvasFrame(wx.Frame): 
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, title='First Chart', size=(800, 700))
        datafile = matplotlib.get_example_data('goog.npy')
        r = np.load(datafile).view(np.recarray)
        datesFloat = matplotlib.dates.date2num(r.date)
        figure = Figure()
        xMaxDatetime = r.date[len(r.date)-1]
        xMinDatetime = r.date[0]
        xMaxFloat = datesFloat[len(datesFloat)-1]
        xMinFloat = datesFloat[0]
        yMin = min(r.adj_close) // 5 * 5
        yMax = (1 + max(r.adj_close) // 5) * 5      
        master = figure.add_subplot(211) 
        master.plot(datesFloat, r.adj_close)
        master.xaxis.set_minor_locator(mdates.MonthLocator())
        master.xaxis.set_major_locator(mdates.MonthLocator(bymonth=(1,4,7,10)))
        master.xaxis.set_major_formatter(mdates.DateFormatter('%b-%y'))
        master.set_xlim(datesFloat[120], datesFloat[120]+92)
        master.yaxis.set_minor_locator(mtickers.MultipleLocator(50))
        master.yaxis.set_major_locator(mtickers.MultipleLocator(100))
        master.set_ylim(yMin, yMax)
        master.set_position([0.05,0.20,0.92,0.75])
        master.xaxis.grid(True, which='minor')
        master.yaxis.grid(True, which='minor')
        slave = figure.add_subplot(212, yticks=[]) 
        slave.plot(datesFloat, r.adj_close)
        slave.xaxis.set_minor_locator(mdates.MonthLocator())
        slave.xaxis.set_major_locator(mdates.YearLocator())
        slave.xaxis.set_major_formatter(mdates.DateFormatter('%b-%y'))
        slave.set_xlim(xMinDatetime, xMaxDatetime)
        slave.set_ylim(yMin, yMax)
        slave.set_position([0.05,0.05,0.92,0.10])
        rectangle = mpatches.Rectangle((datesFloat[120], yMin), 92, yMax-yMin, facecolor='yellow', alpha = 0.4)     
        slave.add_patch(rectangle)
        canvas = FigureCanvas(self, -1, figure)
        drag = DraggableRectangle(rectangle, master, xMinFloat, xMaxFloat - 92)
        drag.connect()

app = wx.PySimpleApp() 
frame = MplCanvasFrame()
frame.Show(True) 
app.MainLoop()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦在夏天 2024-08-27 23:19:28

今天早上我有机会处理这个问题(过去三天我们遭遇了第二场暴风雪)。你是对的,如果你尝试在 on_motion 中重绘整个图形,它会弄乱黄色矩形的动画。关键是还要在主子​​图上 blit 线。

试试这个代码:

import datetime
import numpy as np
import sys
import time
import wx
import matplotlib
from matplotlib.figure import Figure 
import matplotlib.dates as mdates
import matplotlib.ticker as mtickers
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import matplotlib.patches as mpatches

class DraggableRectangle:
    lock = None
    def __init__(self, rect, master, xMin, xMax):       
        self.rect = rect        
        self.press = None
        self.slave_background = None
        self.master_background = None
        self.xMax = xMax
        self.xMin = xMin
        self.master = master
        self.master_line, = self.master.get_lines()

    def connect(self):      
        self.cidpress = self.rect.figure.canvas.mpl_connect('button_press_event', self.on_press)
        self.cidrelease = self.rect.figure.canvas.mpl_connect('button_release_event', self.on_release)
        self.cidmotion = self.rect.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)

    def on_press(self, event):      
        if event.inaxes != self.rect.axes: return
        if DraggableRectangle.lock is not None: return
        contains, attrd = self.rect.contains(event)
        if not contains: return     
        x0, y0 = self.rect.xy
        self.press = x0, y0, event.xdata, event.ydata
        DraggableRectangle.lock = self
        canvas = self.rect.figure.canvas
        axes = self.rect.axes

        # set up our animated elements
        self.rect.set_animated(True)
        self.master_line.set_animated(True) 
        self.master.xaxis.set_visible(False) #we are not animating this

        canvas.draw()

        # backgrounds for restoring on animation
        self.slave_background = canvas.copy_from_bbox(self.rect.axes.bbox)
        self.master_background = canvas.copy_from_bbox(self.master.axes.bbox)

        axes.draw_artist(self.rect)
        canvas.blit(axes.bbox)

    def on_motion(self, event):
        if DraggableRectangle.lock is not self: return
        if event.inaxes != self.rect.axes: return
        x0, y0, xpress, ypress = self.press
        dx = event.xdata - xpress
        dy = 0
        if x0+dx > self.xMax:
            self.rect.set_x(self.xMax)
        elif x0+dx < self.xMin:
            self.rect.set_x(self.xMin)
        else:
            self.rect.set_x(x0+dx)
        self.rect.set_y(y0+dy)
        canvas = self.rect.figure.canvas
        axes = self.rect.axes

        # restore backgrounds
        canvas.restore_region(self.slave_background)
        canvas.restore_region(self.master_background)

        # set our limits for animated line
        self.master.set_xlim(self.rect.get_x(), self.rect.get_x() + 92)

        # draw yellow box
        axes.draw_artist(self.rect)
        canvas.blit(axes.bbox)

        #draw line
        self.master.axes.draw_artist(self.master_line)
        canvas.blit(self.master.axes.bbox)  

    def on_release(self, event):        
        if DraggableRectangle.lock is not self: return
        self.press = None
        DraggableRectangle.lock = None

        # unanimate rect and lines
        self.rect.set_animated(False)
        self.master_line.set_animated(False)

        self.slave_background = None
        self.master_background = None

        # redraw whole figure
        self.master.xaxis.set_visible(True)
        self.rect.figure.canvas.draw()

    def disconnect(self):
        self.rect.figure.canvas.mpl_disconnect(self.cidpress)
        self.rect.figure.canvas.mpl_disconnect(self.cidrelease)
        self.rect.figure.canvas.mpl_disconnect(self.cidmotion)

class MplCanvasFrame(wx.Frame): 
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, title='First Chart', size=(800, 700))
        datafile = matplotlib.get_example_data('goog.npy')
        r = np.load(datafile).view(np.recarray)
        datesFloat = matplotlib.dates.date2num(r.date)
        figure = Figure()
        xMaxDatetime = r.date[len(r.date)-1]
        xMinDatetime = r.date[0]
        xMaxFloat = datesFloat[len(datesFloat)-1]
        xMinFloat = datesFloat[0]
        yMin = min(r.adj_close) // 5 * 5
        yMax = (1 + max(r.adj_close) // 5) * 5      
        master = figure.add_subplot(211)
        master.plot(datesFloat, r.adj_close)
        master.xaxis.set_minor_locator(mdates.MonthLocator())
        master.xaxis.set_major_locator(mdates.MonthLocator(bymonth=(1,4,7,10)))
        master.xaxis.set_major_formatter(mdates.DateFormatter('%b-%y'))
        master.set_xlim(datesFloat[120], datesFloat[120]+92)
        master.yaxis.set_minor_locator(mtickers.MultipleLocator(50))
        master.yaxis.set_major_locator(mtickers.MultipleLocator(100))
        master.set_ylim(yMin, yMax)
        master.set_position([0.05,0.20,0.92,0.75])
        master.xaxis.grid(True, which='minor')
        master.yaxis.grid(True, which='minor')
        slave = figure.add_subplot(212, yticks=[]) 
        slave.plot(datesFloat, r.adj_close)
        slave.xaxis.set_minor_locator(mdates.MonthLocator())
        slave.xaxis.set_major_locator(mdates.YearLocator())
        slave.xaxis.set_major_formatter(mdates.DateFormatter('%b-%y'))
        slave.set_xlim(xMinDatetime, xMaxDatetime)
        slave.set_ylim(yMin, yMax)
        slave.set_position([0.05,0.05,0.92,0.10])
        rectangle = mpatches.Rectangle((datesFloat[120], yMin), 92, yMax-yMin, facecolor='yellow', alpha = 0.4)     
        slave.add_patch(rectangle)
        canvas = FigureCanvas(self, -1, figure)
        drag = DraggableRectangle(rectangle, master, xMinFloat, xMaxFloat - 92)
        drag.connect()

app = wx.PySimpleApp() 
frame = MplCanvasFrame()
frame.Show(True) 
app.MainLoop()

I had a chance to work on this this morning (we are having a 2nd blizzard for the last 3 days). You are right, if you try to redraw the entire figure in the on_motion, it messes up the animation of the yellow rectangle. The key is to also blit the line on the master sub plot.

Try this code out:

import datetime
import numpy as np
import sys
import time
import wx
import matplotlib
from matplotlib.figure import Figure 
import matplotlib.dates as mdates
import matplotlib.ticker as mtickers
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import matplotlib.patches as mpatches

class DraggableRectangle:
    lock = None
    def __init__(self, rect, master, xMin, xMax):       
        self.rect = rect        
        self.press = None
        self.slave_background = None
        self.master_background = None
        self.xMax = xMax
        self.xMin = xMin
        self.master = master
        self.master_line, = self.master.get_lines()

    def connect(self):      
        self.cidpress = self.rect.figure.canvas.mpl_connect('button_press_event', self.on_press)
        self.cidrelease = self.rect.figure.canvas.mpl_connect('button_release_event', self.on_release)
        self.cidmotion = self.rect.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)

    def on_press(self, event):      
        if event.inaxes != self.rect.axes: return
        if DraggableRectangle.lock is not None: return
        contains, attrd = self.rect.contains(event)
        if not contains: return     
        x0, y0 = self.rect.xy
        self.press = x0, y0, event.xdata, event.ydata
        DraggableRectangle.lock = self
        canvas = self.rect.figure.canvas
        axes = self.rect.axes

        # set up our animated elements
        self.rect.set_animated(True)
        self.master_line.set_animated(True) 
        self.master.xaxis.set_visible(False) #we are not animating this

        canvas.draw()

        # backgrounds for restoring on animation
        self.slave_background = canvas.copy_from_bbox(self.rect.axes.bbox)
        self.master_background = canvas.copy_from_bbox(self.master.axes.bbox)

        axes.draw_artist(self.rect)
        canvas.blit(axes.bbox)

    def on_motion(self, event):
        if DraggableRectangle.lock is not self: return
        if event.inaxes != self.rect.axes: return
        x0, y0, xpress, ypress = self.press
        dx = event.xdata - xpress
        dy = 0
        if x0+dx > self.xMax:
            self.rect.set_x(self.xMax)
        elif x0+dx < self.xMin:
            self.rect.set_x(self.xMin)
        else:
            self.rect.set_x(x0+dx)
        self.rect.set_y(y0+dy)
        canvas = self.rect.figure.canvas
        axes = self.rect.axes

        # restore backgrounds
        canvas.restore_region(self.slave_background)
        canvas.restore_region(self.master_background)

        # set our limits for animated line
        self.master.set_xlim(self.rect.get_x(), self.rect.get_x() + 92)

        # draw yellow box
        axes.draw_artist(self.rect)
        canvas.blit(axes.bbox)

        #draw line
        self.master.axes.draw_artist(self.master_line)
        canvas.blit(self.master.axes.bbox)  

    def on_release(self, event):        
        if DraggableRectangle.lock is not self: return
        self.press = None
        DraggableRectangle.lock = None

        # unanimate rect and lines
        self.rect.set_animated(False)
        self.master_line.set_animated(False)

        self.slave_background = None
        self.master_background = None

        # redraw whole figure
        self.master.xaxis.set_visible(True)
        self.rect.figure.canvas.draw()

    def disconnect(self):
        self.rect.figure.canvas.mpl_disconnect(self.cidpress)
        self.rect.figure.canvas.mpl_disconnect(self.cidrelease)
        self.rect.figure.canvas.mpl_disconnect(self.cidmotion)

class MplCanvasFrame(wx.Frame): 
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, title='First Chart', size=(800, 700))
        datafile = matplotlib.get_example_data('goog.npy')
        r = np.load(datafile).view(np.recarray)
        datesFloat = matplotlib.dates.date2num(r.date)
        figure = Figure()
        xMaxDatetime = r.date[len(r.date)-1]
        xMinDatetime = r.date[0]
        xMaxFloat = datesFloat[len(datesFloat)-1]
        xMinFloat = datesFloat[0]
        yMin = min(r.adj_close) // 5 * 5
        yMax = (1 + max(r.adj_close) // 5) * 5      
        master = figure.add_subplot(211)
        master.plot(datesFloat, r.adj_close)
        master.xaxis.set_minor_locator(mdates.MonthLocator())
        master.xaxis.set_major_locator(mdates.MonthLocator(bymonth=(1,4,7,10)))
        master.xaxis.set_major_formatter(mdates.DateFormatter('%b-%y'))
        master.set_xlim(datesFloat[120], datesFloat[120]+92)
        master.yaxis.set_minor_locator(mtickers.MultipleLocator(50))
        master.yaxis.set_major_locator(mtickers.MultipleLocator(100))
        master.set_ylim(yMin, yMax)
        master.set_position([0.05,0.20,0.92,0.75])
        master.xaxis.grid(True, which='minor')
        master.yaxis.grid(True, which='minor')
        slave = figure.add_subplot(212, yticks=[]) 
        slave.plot(datesFloat, r.adj_close)
        slave.xaxis.set_minor_locator(mdates.MonthLocator())
        slave.xaxis.set_major_locator(mdates.YearLocator())
        slave.xaxis.set_major_formatter(mdates.DateFormatter('%b-%y'))
        slave.set_xlim(xMinDatetime, xMaxDatetime)
        slave.set_ylim(yMin, yMax)
        slave.set_position([0.05,0.05,0.92,0.10])
        rectangle = mpatches.Rectangle((datesFloat[120], yMin), 92, yMax-yMin, facecolor='yellow', alpha = 0.4)     
        slave.add_patch(rectangle)
        canvas = FigureCanvas(self, -1, figure)
        drag = DraggableRectangle(rectangle, master, xMinFloat, xMaxFloat - 92)
        drag.connect()

app = wx.PySimpleApp() 
frame = MplCanvasFrame()
frame.Show(True) 
app.MainLoop()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文