龙卷风事件未触发的

龙卷风事件未触发的

本文介绍了龙卷风事件未触发的 Matplotlib Webagg的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图通过 Tornado 将 matplotlib 嵌入到网页中(matplotlib 后端需要).它正在绘制一些圆圈,但拖放功能不起作用,并且可能在 webagg 核心后端中不支持 blitting.我想出了问题,因为事件没有被触发.我还没有找到使它工作的解决方案.请看以下代码:

导入json导入 mimetypes从 pathlib 导入路径尝试:进口龙卷风除了 ImportError 作为错误:从 err 引发 RuntimeError("This example requires tornado.")导入 tornado.web导入 tornado.httpserver导入 tornado.ioloop导入 tornado.websocket将 matplotlib 导入为 mpl从 matplotlib.backends.backend_webagg_core 导入(FigureManagerWebAgg, new_figure_manager_given_figure)from matplotlib.figure 导入图将 numpy 导入为 np导入 matplotlib.pyplot 作为 plt导入 matplotlib.patches 作为补丁def create_figure():fig = plt.figure()ax = fig.add_subplot(111)drs = []圆圈 = [patches.Circle((0.32, 0.3), 0.03, fc='r', alpha=0.5),patch.Circle((0.2, 0.35), 0.03, fc='g', alpha=0.5),patch.Circle((0.1, 0.3), 0.03, fc='y', alpha=0.5),patch.Circle((0.4, 0.4), 0.03, fc='r', alpha=0.5),patch.Circle((0.3, 0.1), 0.03, fc='g', alpha=0.5)]对于圈子:ax.add_patch(circ)dr = DraggablePoint(circ)博士.connect()drs.append(博士)返回无花果# 以下是网页内容.你通常会# 使用您网络中的某种模板工具生成它# 框架,但这里我们只使用 Python 字符串格式.html_content = """<头><!-- TODO:应该有一种方法可以包含所有必需的 javascript和 CSS 所以 matplotlib 可以在将来添加到集合中,如果它需要.--><link rel="样式表";href="_static/css/page.css";类型=文本/CSS"><link rel="样式表";href="_static/css/boilerplate.css";类型=文本/css";/><link rel="样式表";href="_static/css/fbm.css";类型=文本/css";/><link rel="样式表";href="_static/css/mpl.css";类型=文本/CSS"><script src="mpl.js"></script><脚本>/* 这是用户保存时调用的回调(下载)一个文件.它的目的实际上是从一个图和文件格式转换为应用程序中的 url.*/功能 ondownload(图,格式){window.open('download.' + format, '_blank');};功能就绪(fn){如果(document.readyState!=加载"){fn();} 别的 {document.addEventListener(DOMContentLoaded", fn);}}准备好(功能() {/* 由应用程序提供一个 websocket,该图形将用于与服务器通信.这个 websocket 对象可以也是假的"在下面多路复用消息的 websocket如有必要,可以从多个数字中提取.*/var websocket_type = mpl.get_websocket_type();var websocket = new websocket_type("%(ws_uri)sws");//mpl.figure 在网页上创建一个新图形.var fig = new mpl.figure(//图形的唯一数字标识符%(fig_id)s,//一个 websocket 对象(或行为类似的东西)网络套接字,//选择文件类型进行下载时调用的函数下载,//放置图形的 HTML 元素document.getElementById(图"));});<title>matplotlib</title><身体><div id="数字">

</html>"类 MyApplication(tornado.web.Application):类 MainPage(tornado.web.RequestHandler):"提供主 HTML 页面."定义获取(自我):经理 = self.application.managerws_uri = "ws://{req.host}/".format(req=self.request)内容 = html_content % {ws_uri":ws_uri,fig_id":manager.num}self.write(内容)类 MplJs(tornado.web.RequestHandler):"提供生成的 matplotlib javascript 文件.内容是根据工具栏的功能动态生成的用户已定义.调用 `FigureManagerWebAgg` 来获取它的内容."定义获取(自我):self.set_header('Content-Type', 'application/javascript')js_content = FigureManagerWebAgg.get_javascript()self.write(js_content)类下载(tornado.web.RequestHandler):"处理下载各种文件格式的图形."def get(self, fmt):经理 = self.application.managerself.set_header('内容类型', mimetypes.types_map.get(fmt, 'binary'))buff = io.BytesIO()manager.canvas.figure.savefig(buff, format=fmt)self.write(buff.getvalue())类 WebSocket(tornado.websocket.WebSocketHandler):"一个用于情节之间交互通信的 websocket浏览器和服务器.除了龙卷风需要的方法外,还需要有两个回调方法:- ``send_json(json_content)`` 被 matplotlib 调用它需要将json发送到浏览器.`json_content` 是一个 JSON 树(Python 字典),它是责任此实现将其编码为要发送的字符串插座.- 调用``send_binary(blob)`` 来发送二进制图像数据到浏览器."support_binary = 真定义打开(自我):# 向 FigureManager 注册 websocket.经理 = self.application.managermanager.add_web_socket(self)如果 hasattr(self, 'set_nodelay'):self.set_nodelay(True)def on_close(self):# 当socket关闭时,注销websocket# 图管理器.经理 = self.application.managermanager.remove_web_socket(self)def on_message(self, message):# 'supports_binary' 消息与# websocket 本身.其他消息被传递# 到 matplotlib 原样.# 每条消息都有一个类型"和figure_id".消息 = json.loads(消息)如果消息['type'] == 'supports_binary':self.supports_binary = message['value']别的:经理 = self.application.managermanager.handle_json(消息)def send_json(self, content):self.write_message(json.dumps(content))def send_binary(self, blob):如果 self.supports_binary:self.write_message(blob, binary=True)别的:data_uri = "data:image/png;base64,{0}".format(blob.encode('base64').replace('\n', ''))self.write_message(data_uri)def __init__(self, figure):self.figure = 数字self.manager = new_figure_manager_given_figure(id(figure), figure)super().__init__([# CSS 和 JS 的静态文件(r'/_static/(.*)',tornado.web.StaticFileHandler,{'path': FigureManagerWebAgg.get_static_file_path()}),# 工具栏的静态图片(r'/_images/(.*)',tornado.web.StaticFileHandler,{'path': Path(mpl.get_data_path(), 'images')}),# 包含所有碎片的页面('/', self.MainPage),('/mpl.js', self.MplJs),# 向浏览器发送图像和事件,并接收# 来自浏览器的事件('/ws', self.WebSocket),# 处理静态图像的下载(即保存)(r'/download.([a-z0-9.]+)', self.Download),])类可拖动点:lock = None #一次只能设置一个动画def __init__(self, point):self.point = 点self.press = 无cv = self.point.figure.canvasself.background = cv.copy_from_bbox(cv.figure.bbox)定义连接(自我):'连接到我们需要的所有事件'self.cidpress = self.point.figure.canvas.mpl_connect('button_press_event', self.on_press)self.cidrelease = self.point.figure.canvas.mpl_connect('button_release_event', self.on_release)self.cidmotion = self.point.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)def on_press(self, event):打印('按...')如果 event.inaxes != self.point.axes: 返回如果 DraggablePoint.lock 不是 None:返回包含,attrd = self.point.contains(event)如果不包含:返回self.press = (self.point.center), event.xdata, event.ydataDraggablePoint.lock = self# # 绘制除选定矩形之外的所有内容并存储像素缓冲区# canvas = self.point.figure.canvas# 轴 = self.point.axes# self.point.set_animated(True)# canvas.draw()# self.background = canvas.copy_from_bbox(self.point.axes.bbox)# # 现在只重绘矩形#axis.draw_artist(self.point)# # 只对重绘区域进行 blit# canvas.blit(axes.bbox)def on_motion(自我,事件):如果 DraggablePoint.lock 不是自己的:返回如果 event.inaxes != self.point.axes: 返回self.point.center, xpress, ypress = self.pressdx = event.xdata - xpressdy = event.ydata - ypressself.point.center = (self.point.center[0]+dx, self.point.center[1]+dy)画布 = self.point.figure.canvas轴 = self.point.axes# 恢复背景区域canvas.restore_region(self.background)# 只重绘当前矩形axis.draw_artist(self.point)# blit 只重绘区域canvas.blit(axes.bbox)def on_release(self, event):'在发布时我们重置新闻数据'如果 DraggablePoint.lock 不是自己的:返回self.press = 无DraggablePoint.lock = 无# 关闭 rect 动画属性并重置背景# self.point.set_animated(False)# self.background = 无# 重绘全图# self.point.figure.canvas.draw()def断开连接(自我):'断开所有存储的连接 ID'self.point.figure.canvas.mpl_disconnect(self.cidpress)self.point.figure.canvas.mpl_disconnect(self.cidrelease)self.point.figure.canvas.mpl_disconnect(self.cidmotion)如果 __name__ == __main__":图 = create_figure()应用程序 = 我的应用程序(图)http_server = tornado.httpserver.HTTPServer(application)http_server.listen(8080)打印(http://127.0.0.1:8080/")打印(按Ctrl + C退出")tornado.ioloop.IOLoop.instance().start()

解决方案

我通过添加调用方法来接收触发事件解决了我自己的问题.

def __call__(self, event):如果 event.name == 'button_press_event':self.press(事件)elif event.name == 'button_release_event':self.release(事件)elif event.name == 'motion_notify_event':self.onmove(事件)

你必须修改connect方法如下:

def connect(self):'连接到我们需要的所有事件'self.cidpress = self.point.figure.canvas.mpl_connect('button_press_event', self)self.cidrelease = self.point.figure.canvas.mpl_connect('button_release_event', self)self.cidmotion = self.point.figure.canvas.mpl_connect('motion_notify_event', self)

当事件触发时,```call`` 方法被调用并检查条件,然后调用回调.

I was trying to embed matplotlib into web page via Tornado (required by matplotlib backend). It was plotting some circles but drag and drop functionality was not working and maybe blitting doesn't support in webagg core backend. I figured it out the issue because of the events were not fired. I couldn't find the solution to make it work yet. Please see the following code:

import json
import mimetypes
from pathlib import Path

try:
    import tornado
except ImportError as err:
    raise RuntimeError("This example requires tornado.") from err
import tornado.web
import tornado.httpserver
import tornado.ioloop
import tornado.websocket

import matplotlib as mpl
from matplotlib.backends.backend_webagg_core import (
    FigureManagerWebAgg, new_figure_manager_given_figure)
from matplotlib.figure import Figure

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches


def create_figure():

    fig = plt.figure()
    ax = fig.add_subplot(111)
    drs = []
    circles = [patches.Circle((0.32, 0.3), 0.03, fc='r', alpha=0.5),
                patches.Circle((0.2, 0.35), 0.03, fc='g', alpha=0.5),
                patches.Circle((0.1, 0.3), 0.03, fc='y', alpha=0.5),
                patches.Circle((0.4, 0.4), 0.03, fc='r', alpha=0.5),
                patches.Circle((0.3, 0.1), 0.03, fc='g', alpha=0.5)]

    for circ in circles:
      ax.add_patch(circ)
      dr = DraggablePoint(circ)
      dr.connect()
      drs.append(dr)

    return fig


# The following is the content of the web page.  You would normally
# generate this using some sort of template facility in your web
# framework, but here we just use Python string formatting.
html_content = """
<html>
  <head>
    <!-- TODO: There should be a way to include all of the required javascript
               and CSS so matplotlib can add to the set in the future if it
               needs to. -->
    <link rel="stylesheet" href="_static/css/page.css" type="text/css">
    <link rel="stylesheet" href="_static/css/boilerplate.css"
          type="text/css" />
    <link rel="stylesheet" href="_static/css/fbm.css" type="text/css" />
    <link rel="stylesheet" href="_static/css/mpl.css" type="text/css">
    <script src="mpl.js"></script>

    <script>
      /* This is a callback that is called when the user saves
         (downloads) a file.  Its purpose is really to map from a
         figure and file format to a url in the application. */
      function ondownload(figure, format) {
        window.open('download.' + format, '_blank');
      };

      function ready(fn) {
        if (document.readyState != "loading") {
          fn();
        } else {
          document.addEventListener("DOMContentLoaded", fn);
        }
      }

      ready(
        function() {
          /* It is up to the application to provide a websocket that the figure
             will use to communicate to the server.  This websocket object can
             also be a "fake" websocket that underneath multiplexes messages
             from multiple figures, if necessary. */
          var websocket_type = mpl.get_websocket_type();
          var websocket = new websocket_type("%(ws_uri)sws");

          // mpl.figure creates a new figure on the webpage.
          var fig = new mpl.figure(
              // A unique numeric identifier for the figure
              %(fig_id)s,
              // A websocket object (or something that behaves like one)
              websocket,
              // A function called when a file type is selected for download
              ondownload,
              // The HTML element in which to place the figure
              document.getElementById("figure"));
        }
      );
    </script>

    <title>matplotlib</title>
  </head>

  <body>
    <div id="figure">
    </div>
  </body>
</html>
"""


class MyApplication(tornado.web.Application):
    class MainPage(tornado.web.RequestHandler):
        """
        Serves the main HTML page.
        """

        def get(self):
            manager = self.application.manager
            ws_uri = "ws://{req.host}/".format(req=self.request)
            content = html_content % {
                "ws_uri": ws_uri, "fig_id": manager.num}
            self.write(content)

    class MplJs(tornado.web.RequestHandler):
        """
        Serves the generated matplotlib javascript file.  The content
        is dynamically generated based on which toolbar functions the
        user has defined.  Call `FigureManagerWebAgg` to get its
        content.
        """

        def get(self):
            self.set_header('Content-Type', 'application/javascript')
            js_content = FigureManagerWebAgg.get_javascript()

            self.write(js_content)

    class Download(tornado.web.RequestHandler):
        """
        Handles downloading of the figure in various file formats.
        """

        def get(self, fmt):
            manager = self.application.manager
            self.set_header(
                'Content-Type', mimetypes.types_map.get(fmt, 'binary'))
            buff = io.BytesIO()
            manager.canvas.figure.savefig(buff, format=fmt)
            self.write(buff.getvalue())

    class WebSocket(tornado.websocket.WebSocketHandler):
        """
        A websocket for interactive communication between the plot in
        the browser and the server.

        In addition to the methods required by tornado, it is required to
        have two callback methods:

            - ``send_json(json_content)`` is called by matplotlib when
              it needs to send json to the browser.  `json_content` is
              a JSON tree (Python dictionary), and it is the responsibility
              of this implementation to encode it as a string to send over
              the socket.

            - ``send_binary(blob)`` is called to send binary image data
              to the browser.
        """
        supports_binary = True

        def open(self):
            # Register the websocket with the FigureManager.
            manager = self.application.manager
            manager.add_web_socket(self)
            if hasattr(self, 'set_nodelay'):
                self.set_nodelay(True)

        def on_close(self):
            # When the socket is closed, deregister the websocket with
            # the FigureManager.
            manager = self.application.manager
            manager.remove_web_socket(self)

        def on_message(self, message):
            # The 'supports_binary' message is relevant to the
            # websocket itself.  The other messages get passed along
            # to matplotlib as-is.

            # Every message has a "type" and a "figure_id".
            message = json.loads(message)
            if message['type'] == 'supports_binary':
                self.supports_binary = message['value']
            else:
                manager = self.application.manager
                manager.handle_json(message)

        def send_json(self, content):
            self.write_message(json.dumps(content))

        def send_binary(self, blob):
            if self.supports_binary:
                self.write_message(blob, binary=True)
            else:
                data_uri = "data:image/png;base64,{0}".format(
                    blob.encode('base64').replace('\n', ''))
                self.write_message(data_uri)

    def __init__(self, figure):
        self.figure  = figure
        self.manager = new_figure_manager_given_figure(id(figure), figure)

        super().__init__([
            # Static files for the CSS and JS
            (r'/_static/(.*)',
             tornado.web.StaticFileHandler,
             {'path': FigureManagerWebAgg.get_static_file_path()}),

            # Static images for the toolbar
            (r'/_images/(.*)',
             tornado.web.StaticFileHandler,
             {'path': Path(mpl.get_data_path(), 'images')}),

            # The page that contains all of the pieces
            ('/', self.MainPage),

            ('/mpl.js', self.MplJs),

            # Sends images and events to the browser, and receives
            # events from the browser
            ('/ws', self.WebSocket),

            # Handles the downloading (i.e., saving) of static images
            (r'/download.([a-z0-9.]+)', self.Download),
        ])


class DraggablePoint:
    lock = None #only one can be animated at a time
    def __init__(self, point):
        self.point = point
        self.press = None
        cv = self.point.figure.canvas
        self.background = cv.copy_from_bbox(cv.figure.bbox)

    def connect(self):
        'connect to all the events we need'
        self.cidpress = self.point.figure.canvas.mpl_connect('button_press_event', self.on_press)
        self.cidrelease = self.point.figure.canvas.mpl_connect('button_release_event', self.on_release)
        self.cidmotion = self.point.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)

    def on_press(self, event):
        print('on press...')
        if event.inaxes != self.point.axes: return
        if DraggablePoint.lock is not None: return
        contains, attrd = self.point.contains(event)
        if not contains: return
        self.press = (self.point.center), event.xdata, event.ydata
        DraggablePoint.lock = self

        # # draw everything but the selected rectangle and store the pixel buffer
        # canvas = self.point.figure.canvas
        # axes = self.point.axes
        # self.point.set_animated(True)
        # canvas.draw()
        # self.background = canvas.copy_from_bbox(self.point.axes.bbox)

        # # now redraw just the rectangle
        # axes.draw_artist(self.point)

        # # and blit just the redrawn area
        # canvas.blit(axes.bbox)

    def on_motion(self, event):
        if DraggablePoint.lock is not self:
            return
        if event.inaxes != self.point.axes: return
        self.point.center, xpress, ypress = self.press
        dx = event.xdata - xpress
        dy = event.ydata - ypress
        self.point.center = (self.point.center[0]+dx, self.point.center[1]+dy)

        canvas = self.point.figure.canvas
        axes = self.point.axes
        # restore the background region
        canvas.restore_region(self.background)

        # redraw just the current rectangle
        axes.draw_artist(self.point)

        # blit just the redrawn area
        canvas.blit(axes.bbox)

    def on_release(self, event):
        'on release we reset the press data'
        if DraggablePoint.lock is not self:
            return

        self.press = None
        DraggablePoint.lock = None

        # turn off the rect animation property and reset the background
        # self.point.set_animated(False)
        # self.background = None

        # redraw the full figure
        # self.point.figure.canvas.draw()

    def disconnect(self):
        'disconnect all the stored connection ids'
        self.point.figure.canvas.mpl_disconnect(self.cidpress)
        self.point.figure.canvas.mpl_disconnect(self.cidrelease)
        self.point.figure.canvas.mpl_disconnect(self.cidmotion)

if __name__ == "__main__":

    figure = create_figure()
    application = MyApplication(figure)

    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8080)

    print("http://127.0.0.1:8080/")
    print("Press Ctrl+C to quit")

    tornado.ioloop.IOLoop.instance().start()
解决方案

I solved my own issue by adding a call method to receive the triggered events.

def __call__(self, event):
  if event.name == 'button_press_event':
    self.press(event)
  elif event.name == 'button_release_event':
    self.release(event)
  elif event.name == 'motion_notify_event':
    self.onmove(event)

You have to modify the connect method as the following:

def connect(self):
'connect to all the events we need'
  self.cidpress = self.point.figure.canvas.mpl_connect('button_press_event', self)
  self.cidrelease = self.point.figure.canvas.mpl_connect('button_release_event', self)
  self.cidmotion = self.point.figure.canvas.mpl_connect('motion_notify_event', self)

When the event fired, the ```call`` method is called and check the conditions and then invoke the callback.

这篇关于龙卷风事件未触发的 Matplotlib Webagg的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-05 11:21