enable icon indicating copy to clipboard operation
enable copied to clipboard

Updating the aspect_ratio of a Component causes the bounds to shrink

Open jwiggins opened this issue 3 years ago • 2 comments

For the use case of drawing an image plot in Chaco with a fixed aspect ratio, setting a new aspect ratio when the image data changes can cause the bounds to shrink until the component's parent widget is resized.

The code which handles this is in Component._enforce_aspect_ratio, so technically this applies to all components which update their aspect ratio.

A potential solution to this might be to consult the size of the Component.window (AbstractWindow) instance. However, that will only work if the component in question completely fills its window. For smaller components, we need to remember the maximum size available to be filled.

(Also, ConstraintsContainer might be a better option here)

Example Code
import numpy as np

from chaco.api import ArrayPlotData, CMapImagePlot, Plot
from enable.api import ComponentEditor
from traits.api import (
    Array, cached_property, Enum, HasTraits, Instance, Property
)
from traitsui.api import HGroup, UItem, View

IMAGE_DATA = np.random.randint(0, 256, size=(20, 10), dtype=np.uint8)


class PatchedPlot(Plot):
    def draw(self, gc, **kwargs):
        print(self.bounds)
        # Uncomment to "fix"
        # self.position = [0, 0]
        # self.bounds = [gc.width(), gc.height()]
        super().draw(gc, **kwargs)


class Demo(HasTraits):

    plot = Instance(Plot)
    data = Instance(ArrayPlotData)
    img_plot = Instance(CMapImagePlot)

    choice = Enum('tall', 'wide')
    image_data = Property(Array, depends_on='choice')

    @cached_property
    def _get_image_data(self):
        if self.choice == 'tall':
            return IMAGE_DATA
        else:
            return IMAGE_DATA.T

    def _data_default(self):
        return ArrayPlotData(img=self.image_data)

    def _plot_default(self):
        plot = PatchedPlot(
            self.data,
            default_origin='top left',
            aspect_ratio=self.image_data.shape[1] / self.image_data.shape[0]
        )
        self.img_plot = plot.img_plot('img')[0]
        return plot

    def _image_data_changed(self):
        data = self.image_data

        # Update the plot data source and aspect ratio
        self.data.set_data('img', data)
        self.plot.aspect_ratio = data.shape[1] / data.shape[0]

        # Update the grid mapper
        xrange = np.arange(data.shape[1])
        yrange = np.arange(data.shape[0])
        self.img_plot.index.set_data(xrange, yrange)

        self.plot.request_redraw()

    view = View(
        UItem('plot', editor=ComponentEditor()),
        HGroup(
            UItem('choice', springy=True),
        ),
        resizable=True,
    )


if __name__ == '__main__':
    Demo().configure_traits()

jwiggins avatar Mar 23 '21 15:03 jwiggins