Могу ли я заставить мой контейнер enable перерисовываться из обработчика traitsui?

Я использовал traitsui.api.Handler для перехвата и обработки событий для traitsui.api.View, это представление включает кнопку, поведение которой заключается в удалении графика из контейнера, содержащего несколько графиков. Доступ к списку компонентов контейнера осуществляется при использовании кнопки remove, вызове метода pop() и удалении графика. Однако вид не перерисовывается, поэтому кажется, что график остается на месте. Изменение размера окна путем перетаскивания угла вызовет перерисовку, подтверждая pop()

Вопрос: как я могу принудительно перерисовать программно?

Мне кажется, правильное место для этого было бы в методе setattr обработчика сразу после pop() построения графика.

# Major library imports

from numpy import linspace
from scipy.special import jn

# Enthought library imports
from enable.api import Container, ComponentEditor
from traits.api import HasTraits, Instance, Button, Int, Str
from traitsui.api import Item, HGroup, View, VSplit, UItem, InstanceEditor, Handler

# Chaco imports
from chaco.api import ArrayPlotData, GridContainer, Plot

# ===============================================================================
# Attributes to use for the plot view.
size = (1000, 800)

COLOR_PALETTE = [
    (0.65098039, 0.80784314, 0.89019608, 1.0),
    (0.12156863, 0.47058824, 0.70588235, 1.0),
    (0.69803922, 0.8745098, 0.54117647, 1.0),
    (0.2, 0.62745098, 0.17254902, 1.0),
    (0.98431373, 0.60392157, 0.6, 1.0),
    (0.89019608, 0.10196078, 0.10980392, 1.0),
    (0.99215686, 0.74901961, 0.43529412, 1.0),
    (1., 0.49803922, 0., 1.0),
    (0.79215686, 0.69803922, 0.83921569, 1.0),
]


class InstanceUItem(UItem):
    """Convenience class for including an Instance in a View"""
    style = Str('custom')
    editor = Instance(InstanceEditor, ())



# ===============================================================================
# # ManagerHandler will be the View's handler
#===============================================================================
class ManagerHandler(Handler):
    def setattr(self, info, object, name, value):
        Handler.setattr(self, info, object, name, value)
        info.ui.context['pgrid'].plots_container.components.pop()
        #At this point, the container does not redraw, and so, while it no longer
        #contains the last plot in its components collection, that plot is still
        # visible

# ===============================================================================
# # PlotsGrid class that is used by the demo
#===============================================================================
class PlotsGrid(HasTraits):
    plots_container = Instance(Container)
    rows = Int(3)
    cols = Int(3)

    #===============================================================================
    # # Create the plots, this is adapted from the chaco GridContainer demo
    #===============================================================================
    def _plots_container_default(self):
        # Create a GridContainer to hold all of our plots
        container = GridContainer(padding=20, fill_padding=True,
                                  bgcolor="lightgray", use_backbuffer=True,
                                  shape=(self.rows, self.cols), spacing=(20, 20))

        # Create the initial series of data
        x = linspace(-5, 15.0, 100)
        pd = ArrayPlotData(index=x)

        # Plot some bessel functions and add the plots to our container
        for i in range(self.rows * self.cols):
            pd.set_data("y" + str(i), jn(i, x))
            plot = Plot(pd)
            plot.plot(("index", "y" + str(i)),
                      color=tuple(COLOR_PALETTE[i]), line_width=2.0,
                      bgcolor="white", border_visible=True)

            container.add(plot)
        return container



# ===============================================================================
# # Controls HasTraits provides a button used to wire in the desired behavior
#===============================================================================
class Controls(HasTraits):
    rem_plot = Button("remove ...")

    def _rem_plot_changed(self):
        print "rem plot changed"



# ===============================================================================
# # manager_view provides the View and defines its layout
#===============================================================================
manager_view = View(
    VSplit(
        HGroup(
            Item('controls.rem_plot', height=32)
        ),
        Item('pgrid.plots_container', editor=ComponentEditor(size=size), show_label=False),
        show_border=True
    ),
    handler=ManagerHandler(),
    resizable=True
)

grid = PlotsGrid()
ctrl = Controls()

if __name__ == "__main__":
    ctrl.configure_traits(view=manager_view, context={'pgrid': grid, 'controls': ctrl})

person OYRM    schedule 05.12.2014    source источник


Ответы (1)


Самый простой способ заставить это работать — вызвать invalidate_and_redraw в контейнере графика после извлечения графика. В этом случае вы можете изменить свой вызов pop, чтобы он выглядел примерно так:

plots_container = info.ui.context['pgrid'].plots_container
plots_container.components.pop()
plots_container.invalidate_and_redraw()

Длинное обсуждение:

В идеале этим должен заниматься Чако. Часть проблемы заключается в том, что контейнер не предназначен для прямого изменения списка components. Вместо этого (я предполагаю) намерение состояло в том, чтобы пользователь вызывал plots_container.remove для элемента в списке components.

Тем не менее, это тоже не работает. Получается, что remove делает недействительным текущее состояние, но не запрашивает перерисовку. (Я предполагаю, что chaco/enable не перерисовывается автоматически, поскольку в строке может быть много операций, аннулирующих кеш; например, если вы должны удалить все графики из этого контейнера, вы захотите перерисовывать только после всех вызовов remove, а не после каждого.)

Итак, чтобы использовать этот альтернативный метод, вы должны написать что-то вроде:

plot = plots_container.components[-1]
plots_container.remove(plot)
plots_container.request_redraw()
person Tony S Yu    schedule 07.12.2014
comment
Превосходно. Я рад отметить это как ответ, это работает как шарм. Не возражаете, если я спрошу, где вы берете документы по этому поводу? В Enable довольно мало документации, так что это немного утомительно, чтобы выяснить, как лучше всего работать с API. - person OYRM; 08.12.2014
comment
К сожалению, мой ответ основан на опыте и беглом взгляде на исходный код. Документации здесь действительно немного не хватает. - person Tony S Yu; 08.12.2014