enthought / chaco

Chaco is a Python package for building interactive and custom 2-D plots.
http://docs.enthought.com/chaco/
Other
292 stars 99 forks source link

issue with RectangularSelection #800

Open fred4ets opened 3 years ago

fred4ets commented 3 years ago

Hello,

I have 2 issues using RectangularSelection.

See CME below showing the 2 issues.

CME is based on chaco/examples/demo/basic/scatter_rect_select.py.

I have added FooTool class, which displays "Foo!" when key "p" is pressed.

And added enable_rect_selection trait to enable/disable RectangularSelection.

First, if you set enable_rect_selection to False, you get:

Now, if you set enable_rect_selection to True, you get:

So, what I would like to have as behaviour, when enable_rect_selection is set to True, is:

How could I achieve this?

Thanks in advance.

Regards.

The CME:

import sys

# Major library imports
from numpy import sort, compress, arange
from numpy.random import random

# Enthought library imports
from enable.api import Component, ComponentEditor
from traits.api import HasTraits, Instance
from traitsui.api import Item, Group, View

# Chaco imports
from chaco.api import (
    ArrayPlotData,
    Plot,
    LassoOverlay,
    ScatterInspectorOverlay,
)
from chaco.tools.api import RectangularSelection, ScatterInspector
from enable.api import BaseTool, KeySpec

class FooTool(BaseTool):

    key = KeySpec

    def normal_key_pressed(self, event):
        if self.key.match(event):
            print('Foo!')

# ===============================================================================
# # Create the Chaco plot.
# ===============================================================================
def _create_plot_component():
    # Create some data
    npts = 200
    x = sort(random(npts))
    y = random(npts)

    # Create a plot data obect and give it this data
    pd = ArrayPlotData()
    pd.set_data("index", x)
    pd.set_data("value", y)

    # Create the plot
    plot = Plot(pd)
    plot.plot(
        ("index", "value"),
        type="scatter",
        name="my_plot",
        marker="circle",
        index_sort="ascending",
        color="red",
        marker_size=4,
        bgcolor="white",
    )

    # Tweak some of the plot properties
    plot.title = "Scatter Plot With Rectangular Selection"
    plot.line_width = 1
    plot.padding = 50

    # Right now, some of the tools are a little invasive, and we need the
    # actual ScatterPlot object to give to them
    my_plot = plot.plots["my_plot"][0]

    enable_rect_selection = True

    # Attach some tools to the plot
    if enable_rect_selection:
        rect_selection = RectangularSelection(
            component=my_plot,
            selection_datasource=my_plot.index,
            drag_button="left",
            metadata_name="selections",
        )
        my_plot.tools.append(rect_selection)
        # my_plot.active_tool = rect_selection

        lasso_overlay = LassoOverlay(
            lasso_selection=rect_selection, component=my_plot
        )
        my_plot.overlays.append(lasso_overlay)

    my_plot.tools.append(ScatterInspector(my_plot, selection_mode="toggle"))

    plot.tools.append(FooTool(key=KeySpec('p')))

    scatter_overlay = ScatterInspectorOverlay(
        component=my_plot,
        selection_color="cornflowerblue",
        selection_marker_size=int(my_plot.marker_size) + 3,
        selection_marker="circle",
    )
    my_plot.overlays.append(scatter_overlay)

    return plot

# ===============================================================================
# Attributes to use for the plot view.
size = (650, 650)
title = "Scatter plot with selection"
bgcolor = "lightgray"

# ===============================================================================
# # Demo class that is used by the demo.py application.
# ===============================================================================
class Demo(HasTraits):
    plot = Instance(Component)

    traits_view = View(
        Group(
            Item(
                "plot",
                editor=ComponentEditor(size=size, bgcolor=bgcolor),
                show_label=False
            ),
            orientation="vertical",
        ),
        resizable=True,
        title=title,
    )

    def _selection_changed(self, event):
        mask = self.index_datasource.metadata["selections"]
        print("New selection: ")
        print(compress(mask, arange(len(mask))))
        # Ensure that the points are printed immediately:
        sys.stdout.flush()

    def _plot_default(self):
        plot = _create_plot_component()

        # Retrieve the plot hooked to the RectangularSelection tool.
        my_plot = plot.plots["my_plot"][0]
        # rect_selection = my_plot.active_tool

        # Set up the trait handler for the selection
        self.index_datasource = my_plot.index
        # rect_selection.observe(self._selection_changed, "selection_changed")

        return plot

demo = Demo()

if __name__ == "__main__":
    demo.configure_traits()

Debian x86_64, Python 3.7.3, ETS source from git

fred4ets commented 2 years ago

Hello,

Anybody has any clue to fix this issue?

Thanks in advance.

Regards

corranwebster commented 1 year ago

Sorry for the long delay in looking at this. It looks like there are a couple of things going on here.

It looks like part of the problem is the way that key events are handled. If you run the sample code, you will see that the FooTool actually works when the mouse pointer is in the padding region outside the plot area. This is because what is happening is that:

The LassoSelection/RectangleSelection tools on the renderers have key interactions, so they will go through that codepath, which means the key event never gets to the FooTool on the Plot object.

Probably the easiest work-around this is to put the FooTool on the renderer but before the RectangleSelection in the list of tools, so it gets the event first.

There may be some thought needed about the LassoSelection tool and whether it should be auto-handling events. Some of the issue may be because LassoSelection doesn't inherit from BaseTool, which doesn't have the auto-handling behaviour.

I'm not 100% sure what the issue is with the selection turning off. I only see this when you click (which the selection tool likely things of as the start of a new selection, and so deselecting things makes sense). The selection tools do have optional selection modes that extent or invert selections, so it may be that one of those makes more sense to use?