Как сбросить точки, выбранные с помощью плагина LinkedBrush для mpld3?

Я пытаюсь реализовать плагин, который позволяет пользователю выгружать соответствующую информацию о точках, выбранных плагином LinkedBrush. Я думаю, что мой вопрос как бы связан с этим примером. У меня есть метаинформация, привязанная к каждой точке через плагин HTMLTooltip. В идеале я мог бы как-то сбросить и это. В примере, на который я ссылался, информация выводится через подсказку. Я хочу иметь возможность сохранить эту информацию в какой-нибудь текстовый файл.

Сформулируем несколько иначе: как определить, какие точки на точечной диаграмме были выбраны инструментом LinkedBrush, чтобы можно было сохранить информацию?


person mattcart    schedule 28.10.2014    source источник
comment
Можете ли вы включить то, что вы пробовали до сих пор? Где ты застрял?   -  person Abraham D Flaxman    schedule 06.11.2014
comment
Смотрите мой ответ ниже. Я буду рад любым отзывам, чтобы сделать мое решение более элегантным.   -  person mattcart    schedule 06.11.2014
comment
Можно выполнять команды Python в коде JS — я посмотрю на это. Отличным трюком было бы автоматическое обновление других визуализаций при изменении этих переменных Python.   -  person mdurant    schedule 01.04.2015


Ответы (1)


Чтобы решить эту проблему, я просто отредактировал код плагина LinkedBrush. Я добавил кнопку, которая при нажатии выводит размер окна кисти с помощью brush.extent(). Это печатает минимальные и максимальные координаты x и y. В основном я буду использовать эти координаты, чтобы вернуться к набору входных данных и определить, какие точки попали в пределы кисти. Если у кого-то есть лучшее представление о том, как решить эту проблему, я был бы рад.

class LinkedBrush(plugins.PluginBase):
JAVASCRIPT="""
  mpld3.LinkedBrushPlugin = mpld3_LinkedBrushPlugin;
  mpld3.register_plugin("linkedbrush", mpld3_LinkedBrushPlugin);
  mpld3_LinkedBrushPlugin.prototype = Object.create(mpld3.Plugin.prototype);
  mpld3_LinkedBrushPlugin.prototype.constructor = mpld3_LinkedBrushPlugin;
  mpld3_LinkedBrushPlugin.prototype.requiredProps = [ "id" ];
  mpld3_LinkedBrushPlugin.prototype.defaultProps = {
    button: true,
    enabled: null
  };
  function mpld3_LinkedBrushPlugin(fig, props) {
    mpld3.Plugin.call(this, fig, props);
    if (this.props.enabled === null) {
      this.props.enabled = !this.props.button;
    }
    var enabled = this.props.enabled;
    if (this.props.button) {
      var BrushButton = mpld3.ButtonFactory({
        buttonID: "linkedbrush",
        sticky: true,
        actions: [ "drag" ],
        onActivate: this.activate.bind(this),
        onDeactivate: this.deactivate.bind(this),
        onDraw: function() {
          this.setState(enabled);
        },
        icon: function() {
          return mpld3.icons["brush"];
        }
      });
      this.fig.buttons.push(BrushButton);
      var my_icon = "data:image/png;base64,longstring_that_I_redacted";
      var SaveButton = mpld3.ButtonFactory({
            buttonID: "save",
            sticky: false,
            onActivate: this.get_selected.bind(this),
            icon: function(){return my_icon;},
        });
      this.fig.buttons.push(SaveButton);
    }
    this.extentClass = "linkedbrush";
  }
  mpld3_LinkedBrushPlugin.prototype.activate = function() {
    if (this.enable) this.enable();
  };
  mpld3_LinkedBrushPlugin.prototype.deactivate = function() {
    if (this.disable) this.disable();
  };
  mpld3_LinkedBrushPlugin.prototype.get_selected = function() {
    if (this.get_selected) this.get_selected();
  };
  mpld3_LinkedBrushPlugin.prototype.draw = function() {
    var obj = mpld3.get_element(this.props.id);
    if (obj === null) {
      throw "LinkedBrush: no object with id='" + this.props.id + "' was found";
    }
    var fig = this.fig;
    if (!("offsets" in obj.props)) {
      throw "Plot object with id='" + this.props.id + "' is not a scatter plot";
    }
    var dataKey = "offsets" in obj.props ? "offsets" : "data";
    mpld3.insert_css("#" + fig.figid + " rect.extent." + this.extentClass, {
      fill: "#000",
      "fill-opacity": .125,
      stroke: "#fff"
    });
    mpld3.insert_css("#" + fig.figid + " path.mpld3-hidden", {
      stroke: "#ccc !important",
      fill: "#ccc !important"
    });
    var dataClass = "mpld3data-" + obj.props[dataKey];
    var brush = fig.getBrush();
    var dataByAx = [];
    fig.axes.forEach(function(ax) {
      var axData = [];
      ax.elements.forEach(function(el) {
        if (el.props[dataKey] === obj.props[dataKey]) {
          el.group.classed(dataClass, true);
          axData.push(el);
        }
      });
      dataByAx.push(axData);
    });
    var allData = [];
    var dataToBrush = fig.canvas.selectAll("." + dataClass);
    var currentAxes;
    function brushstart(d) {
      if (currentAxes != this) {
        d3.select(currentAxes).call(brush.clear());
        currentAxes = this;
        brush.x(d.xdom).y(d.ydom);
      }
    }
    function brushmove(d) {
      var data = dataByAx[d.axnum];
      if (data.length > 0) {
        var ix = data[0].props.xindex;
        var iy = data[0].props.yindex;
        var e = brush.extent();
        if (brush.empty()) {
          dataToBrush.selectAll("path").classed("mpld3-hidden", false);
        } else {
          dataToBrush.selectAll("path").classed("mpld3-hidden", function(p) {
            return e[0][0] > p[ix] || e[1][0] < p[ix] || e[0][1] > p[iy] || e[1][1] < p[iy];
          });
        }
      }
    }
    function brushend(d) {
      if (brush.empty()) {
        dataToBrush.selectAll("path").classed("mpld3-hidden", false);
      }
    }
    this.get_selected = function(d) {
        var brush = fig.getBrush();
        var extent = brush.extent();
        alert(extent);
    }
    this.enable = function() {
      this.fig.showBrush(this.extentClass);
      brush.on("brushstart", brushstart).on("brush", brushmove).on("brushend", brushend);
      this.enabled = true;
    };
    this.disable = function() {
      d3.select(currentAxes).call(brush.clear());
      this.fig.hideBrush(this.extentClass);
      this.enabled = false;
    };
    this.disable();
  };
    """
    def __init__(self, points, button=True, enabled=True):
        if isinstance(points, mpl.lines.Line2D):
            suffix = "pts"
        else:
            suffix = None
        self.dict_ = {"type": "linkedbrush",
                      "button": button,
                      "enabled": False,
                      "id": utils.get_id(points, suffix)}
person mattcart    schedule 06.11.2014
comment
Великолепно! У меня сработало, можно ли каким-либо образом получить координаты в Python? - person applecider; 13.10.2015