3

我正在开发一个 Python Pyramid 应用程序,我打算在其中创建多个 SVG 图像来使用饼图绘制统计数据。在我的测试中,我发现一个 SVG 视图可以正常工作,并且只要我添加第二个 SVG 输出视图,并且加载第二个 SVG 图像(SVG 图像加载的顺序无关紧要),无论是直接通过其视图还是通过引用此视图的另一个视图,SVG 图像在任何其他进一步调用中“组合”以加载 SVG 文件。这似乎是 Python 堆栈中的一个错误,因为它似乎没有正确清除内存(主要是在多个 SVG 文件的情况下,请参阅下面的更多详细信息)。下面还要注意,在加载足够多的图像/页面后会遇到 TclError。

由于我在具有更多视图的更详细的应用程序中使用 SVG,因此我在最小化/简化的应用程序中复制它以表明它不是我正在做的额外事情,并且此代码是直接从 Pyramid alchemy模板和数据库生成的不涉及通话。该数据库在我的更多详细信息应用程序中得到了积极利用。此应用程序只有 3 个视图,其中第一个视图是原始模板的一部分。我还添加了 DEBUG 日志记录,以明确没有迹象表明存在对其他 SVG 视图的任何内部调用。

一些视图代码基于Matplotlib svg 作为字符串,而不是主要用于StringIO. 请注意,由于需要饼图,这就是我的代码与引用问题中的代码不同的主要原因。我发现无论我使用StringIO还是cStringIO. 在我的代码中,我正在使用cStringIO.

完整的应用程序代码位于:https ://github.com/danielpronych/pyramidapp

PyPlot 文档:: pyplot api

更新:来自第一个 SVG 视图的代码(图形句柄和关闭命令):

@view_config(route_name='view_test_svg')
def test_svg_view(request):
    # Full module import is not allowed by Pyramid
    #from pylab import *
    # Do individual required imports instead
    from pylab import close, figure, axes, pie, title, savefig
    # For clarity, note that the above, and below, function the same
    #from matplotlib.pyplot import close, figure, axes, pie, title, savefig
    log.debug('In test_svg_view')
    fig = figure(1, figsize=(6,6))
    ax = axes([0.1, 0.1, 0.8, 0.8])
    labels = ['Frogs', 'Hogs', 'Dogs', 'Logs']
    fracs = [15, 30, 45, 10]
    explode=(0, 0.05, 0, 0)
    pie(fracs, explode=explode, labels=labels,
                                autopct='%1.1f%%', shadow=True, startangle=90)
    title('Raining Hogs and Dogs', bbox={'facecolor':'0.8', 'pad':5})
    imgdata = cStringIO.StringIO()
    fig.savefig(imgdata, format='svg')
    imgdata.seek(0)
    svg_dta = imgdata.getvalue()
    # Close the StringIO buffer
    imgdata.close()
    close('all')
    return Response(svg_dta, content_type='image/svg+xml')

此代码的步骤

  1. 加载http://localhost:6543/test.svg
  2. 加载http://localhost:6543/test2.svg
  3. 再次加载 (1) 或 (2)。将pserve在命令窗口中没有任何迹象的情况下连续“旋转”为什么。

注意:使用此代码加载相同的 SVG 视图 3 次也具有与上述步骤相同的结果,而下面的代码不会发生。

另请注意,pyplot pie确实有回报;然而,它本身并不是一个“手柄”。

  • pserve即使等待超过一分钟(特别是在执行上述步骤之后),窗口也不响应 Control+C 命令。

来自第一个 SVG 视图的代码(原始,以下步骤来自此代码):

@view_config(route_name='view_test_svg')
def test_svg_view(request):
    # Full module import is not allowed by Pyramid
    #from pylab import *
    # Do individual required imports instead
    from pylab import figure, axes, pie, title, savefig
    # For clarity, note that the above, and below, function the same
    #from matplotlib.pyplot import figure, axes, pie, title, savefig
    log.debug('In test_svg_view')
    figure(1, figsize=(6,6))
    ax = axes([0.1, 0.1, 0.8, 0.8])
    labels = ['Frogs', 'Hogs', 'Dogs', 'Logs']
    fracs = [15, 30, 45, 10]
    explode=(0, 0.05, 0, 0)
    pie(fracs, explode=explode, labels=labels,
                                autopct='%1.1f%%', shadow=True, startangle=90)
    title('Raining Hogs and Dogs', bbox={'facecolor':'0.8', 'pad':5})
    imgdata = cStringIO.StringIO()
    savefig(imgdata, format='svg')
    imgdata.seek(0)
    svg_dta = imgdata.getvalue()
    # Close the StringIO buffer
    imgdata.close()
    return Response(svg_dta, content_type='image/svg+xml')

Python 版本: Python 2.7.5

Python 包配置(仅限主包)

  • 金字塔-1.6a1-py2.7
  • matplotlib-1.4.3-py2.7-win32

重现步骤:

  1. 保护金字塔应用程序。

命令:pserve development.ini --reload

Starting server in PID 4912.
serving on http://0.0.0.0:6543
  1. 加载http://localhost:6543/test.svg

请注意,这可以正常工作

DEBUG [pyramidapp.views:22][Dummy-2] In test_svg_view

第 2 步图片

  1. 加载http://localhost:6543/test2.svg

请注意,这会将两个 SVG 文件“组合”在一起

DEBUG [pyramidapp.views:45][Dummy-3] In test2_svg_view

第 3 步图像

  1. 加载http://localhost:6543/test.svg

请注意,这与 test2.svg 完全相同,具有正确的标题,因为它们的长度也相似,现在图像也在此视图中合并

DEBUG [pyramidapp.views:22][Dummy-4] In test_svg_view

第 4 步图像

  1. 重新托管应用程序并仅加载http://localhost:6543/test2.svg

请注意,这适用于首次加载,因为此视图是在 test.svg 这次之前加载的

DEBUG [pyramidapp.views:45][Dummy-2] In test2_svg_view

第 5 步图像

使用 Control+C 终止 pserve 进程时的跟踪日志

Error in sys.exitfunc:
Traceback (most recent call last):
  File "--python_path--\lib\atexit.py", line 24, in _run_exitfuncs
    func(*targs, **kargs)
  File "--python_path--\lib\site-packages\matplotlib-1.4.3-py2.7-win32.egg\ma
tplotlib\_pylab_helpers.py", line 89, in destroy_all
    manager.destroy()
  File "--python_path--\lib\site-packages\matplotlib-1.4.3-py2.7-win32.egg\ma
tplotlib\backends\backend_tkagg.py", line 588, in destroy
    self.window.destroy()
  File "--python_path--\lib\lib-tk\Tkinter.py", line 1789, in destroy
    for c in self.children.values(): c.destroy()
  File "--python_path--\lib\lib-tk\Tkinter.py", line 2042, in destroy
    self.tk.call('destroy', self._w)
_tkinter.TclError: out of stack space (infinite loop?)
^C caught in monitor process

重要提示:加载足够多的 SVG 图像后,会遇到以下情况:

目前解决此问题的唯一方法是重新启动pserve。还要注意,my_view只要 SVG 图像未被此类视图引用或使用,视图(例如加载)就可以正确加载。

另一个重要的注意事项,只要只加载一个 SVG 文件,即http://localhost:6543/test.svg,整个时间pserve似乎可以无限次重新加载/刷新图像而没有任何明显的问题,或遇到以下情况:

_tkinter 标头

_tkinter.TclError
TclError: out of stack space (infinite loop?)
Traceback (most recent call last)
File "--python_path--\lib\site-packages\pyramid_debugtoolbar-2.0.2-py2.7.egg\pyramid_debugtoolbar\panels

\performance.py", line 69, in noresource_timer_handler
Display the sourcecode for this frameOpen an interactive python shell in this frameresult = handler(request)
File "--python_path--\lib\site-packages\pyramid-1.6a1-py2.7.egg\pyramid\tweens.py", line 20, in excview_tween
Display the sourcecode for this frameOpen an interactive python shell in this frameresponse = handler(request)
File "--python_path--\lib\site-packages\pyramid_tm-0.11-py2.7.egg\pyramid_tm\__init__.py", line 94, in tm_tween
Display the sourcecode for this frameOpen an interactive python shell in this framereraise(*exc_info)
File "--python_path--\lib\site-packages\pyramid_tm-0.11-py2.7.egg\pyramid_tm\__init__.py", line 75, in tm_tween
Display the sourcecode for this frameOpen an interactive python shell in this frameresponse = handler(request)
File "--python_path--\lib\site-packages\pyramid-1.6a1-py2.7.egg\pyramid\router.py", line 145, in handle_request
Display the sourcecode for this frameOpen an interactive python shell in this frameview_name
File "--python_path--\lib\site-packages\pyramid-1.6a1-py2.7.egg\pyramid\view.py", line 527, in _call_view
Display the sourcecode for this frameOpen an interactive python shell in this frameresponse = view_callable

(context, request)
File "--python_path--\lib\site-packages\pyramid-1.6a1-py2.7.egg\pyramid\config\views.py", line 384, in 

viewresult_to_response
Display the sourcecode for this frameOpen an interactive python shell in this frameresult = view(context, 

request)
File "--python_path--\lib\site-packages\pyramid-1.6a1-py2.7.egg\pyramid\config\views.py", line 506, in 

_requestonly_view
Display the sourcecode for this frameOpen an interactive python shell in this frameresponse = view(request)
File "c:\projects\python\pyramid\pyramidapp\pyramidapp\views.py", line 55, in test2_svg_view
Display the sourcecode for this frameOpen an interactive python shell in this framesavefig(imgdata, 

format='svg')
File "--python_path--\lib\site-packages\matplotlib-1.4.3-py2.7-win32.egg\matplotlib\pyplot.py", line 578, in 

savefig
Display the sourcecode for this frameOpen an interactive python shell in this framedraw()   # need this if 

'transparent=True' to reset colors
File "--python_path--\lib\site-packages\matplotlib-1.4.3-py2.7-win32.egg\matplotlib\pyplot.py", line 571, in 

draw
Display the sourcecode for this frameOpen an interactive python shell in this frameget_current_fig_manager

().canvas.draw()
File "--python_path--\lib\site-packages\matplotlib-1.4.3-py2.7-win32.egg\matplotlib\backends\backend_tkagg.py", 

line 350, in draw
Display the sourcecode for this frameOpen an interactive python shell in this frametkagg.blit(self._tkphoto, 

self.renderer._renderer, colormode=2)
File "--python_path--\lib\site-packages\matplotlib-1.4.3-py2.7-win32.egg\matplotlib\backends\tkagg.py", line 

24, in blit
Display the sourcecode for this frameOpen an interactive python shell in this frametk.call("PyAggImagePhoto", 

photoimage, id(aggimage), colormode, id(bbox_array))
TclError: out of stack space (infinite loop?)

更新(2015 年 9 月):我希望看到 Sergey 的进一步更新,因为他建议的解决方案一开始似乎确实有所帮助;但是,他的解决方案并没有解决这个问题,因为即使我等待了相当长的时间也没有发生任何事情,我发现只有我的解决方案才能真正解决这个问题。最后,需要明确的是,我的解决方案适用于基于 Web 的实现和批处理,因为这个问题也出现在批处理中。

4

2 回答 2

3
于 2015-05-29T12:08:15.703 回答
1

谢尔盖提供的答案绝对有帮助。我能够发现,如果基于此示例的部分内容(包括语句)在代码中添加更多部分matplotlib.use,内存问题似乎已得到解决。我在我的测试应用程序(生成 SVG 文件的 2 个视图,在上面发布)和我的主应用程序(目前有 3 个 SVG 视图,我将扩展到更多创建 SVG 文件的视图)对此进行了测试。

使用我的主应用程序,我可以在所有 3 个现有视图之间多次刷新,而无需将我发现的SVG 图像与此代码的先前版本结合起来。该设置似乎对于更多并发使用是必要的,这可能是其他 SO 问题似乎没有反映此问题的原因,除非我能够找到导致此问题的其他内容。我不确定这是否是最佳答案;但是,目前看来这确实可以正常工作,我计划监视以查看是否发现任何其他问题。

@view_config(route_name='view_test_svg')
def test_svg_view(request):
    from matplotlib import use as matplotlib_use
    matplotlib_use("Svg")
    from matplotlib.pyplot import close, figure, axes, pie, title, savefig
    log.debug('In test_svg_view')
    fig = figure(1, figsize=(6,6))
    ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
    labels = ['Frogs', 'Hogs', 'Dogs', 'Logs']
    fracs = [15, 30, 45, 10]
    explode=(0, 0.05, 0, 0)
    pie(fracs, explode=explode, labels=labels,
                                autopct='%1.1f%%', shadow=True, startangle=90)
    title('Raining Hogs and Dogs', bbox={'facecolor':'0.8', 'pad':5})
    imgdata = cStringIO.StringIO()
    savefig(imgdata, format='svg')
    imgdata.seek(0)
    svg_dta = imgdata.getvalue()
    # Close the StringIO buffer
    imgdata.close()
    close('all')
    return Response(svg_dta, content_type='image/svg+xml')
于 2015-05-30T01:07:47.910 回答