Skip to content

Commit

Permalink
Merge pull request #1460 from astrofrog/fix-references
Browse files Browse the repository at this point in the history
Fix bug that caused 'deleted C/C++ widget' errors
  • Loading branch information
astrofrog authored Oct 28, 2017
2 parents b1f00bc + 820272e commit ae8c1a3
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 43 deletions.
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ v0.13.0 (unreleased)

* No changes yet

v0.12.1 (unreleased)
--------------------

* Fix a bug that caused glue to crash when adding components to a dataset
after closing a viewer that had that data. [#1460]

v0.12.0 (2017-10-25)
--------------------

Expand Down
3 changes: 3 additions & 0 deletions glue/app/qt/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,9 @@ def choose_new_data_viewer(self, data=None):
label="Choose a new data viewer",
default=default, sort=True)

if client is None:
return

cmd = command.NewDataViewer(viewer=client, data=data)
return self.do(cmd)

Expand Down
7 changes: 5 additions & 2 deletions glue/core/command.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import absolute_import, division, print_function

import weakref
import logging
from abc import ABCMeta, abstractmethod

Expand Down Expand Up @@ -214,11 +215,13 @@ class NewDataViewer(Command):

def do(self, session):
v = session.application.new_data_viewer(self.viewer, self.data)
self.created = v
self.created = weakref.ref(v)
return v

def undo(self, session):
self.created.close(warn=False)
created = self.created()
if created is not None:
created.close(warn=False)


class AddLayer(Command):
Expand Down
11 changes: 5 additions & 6 deletions glue/core/data_combo_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
DataUpdateMessage,
ComponentReplacedMessage)
from glue.external.echo import delay_callback, ChoiceSeparator
from glue.utils import nonpartial

__all__ = ['ComponentIDComboHelper', 'ManualDataComboHelper',
'DataCollectionComboHelper']
Expand Down Expand Up @@ -365,7 +364,7 @@ def __init__(self, state, selection_property, data_collection=None):
else:
self.hub = None

def refresh(self):
def refresh(self, *args):
self.choices = [data for data in self._datasets]
self.refresh_component_ids()

Expand Down Expand Up @@ -460,7 +459,7 @@ def register_to_hub(self, hub):
super(ManualDataComboHelper, self).register_to_hub(hub)

hub.subscribe(self, DataUpdateMessage,
handler=nonpartial(self.refresh),
handler=self.refresh,
filter=lambda msg: msg.sender in self._datasets)
hub.subscribe(self, DataCollectionDeleteMessage,
handler=lambda msg: self.remove_data(msg.data),
Expand Down Expand Up @@ -494,11 +493,11 @@ def __init__(self, state, selection_property, data_collection):
def register_to_hub(self, hub):
super(DataCollectionComboHelper, self).register_to_hub(hub)
hub.subscribe(self, DataUpdateMessage,
handler=nonpartial(self.refresh),
handler=self.refresh,
filter=lambda msg: msg.sender in self._datasets)
hub.subscribe(self, DataCollectionAddMessage,
handler=nonpartial(self.refresh),
handler=self.refresh,
filter=lambda msg: msg.sender is self._datasets)
hub.subscribe(self, DataCollectionDeleteMessage,
handler=nonpartial(self.refresh),
handler=self.refresh,
filter=lambda msg: msg.sender is self._datasets)
10 changes: 5 additions & 5 deletions glue/viewers/common/qt/data_viewer_with_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from glue.core.state import lookup_class_with_patches
from glue.external import six
from glue.external.echo import delay_callback
from glue.utils import nonpartial, DeferDrawMeta, defer_draw
from glue.utils import DeferDrawMeta, defer_draw
from glue.utils.noconflict import classmaker
from glue.viewers.common.qt.data_viewer import DataViewer

Expand Down Expand Up @@ -38,25 +38,25 @@ def __init__(self, session, parent=None, wcs=None, state=None):
# When layer artists are removed from the layer artist container, we need
# to make sure we remove matching layer states in the viewer state
# layers attribute.
self._layer_artist_container.on_changed(nonpartial(self._sync_state_layers))
self._layer_artist_container.on_changed(self._sync_state_layers)

# And vice-versa when layer states are removed from the viewer state, we
# need to keep the layer_artist_container in sync
self.state.add_callback('layers', nonpartial(self._sync_layer_artist_container))
self.state.add_callback('layers', self._sync_layer_artist_container)

self.statusBar().setSizeGripEnabled(False)
self.setFocusPolicy(Qt.StrongFocus)

def redraw(self):
pass

def _sync_state_layers(self):
def _sync_state_layers(self, *args):
# Remove layer state objects that no longer have a matching layer
for layer_state in self.state.layers:
if layer_state.layer not in self._layer_artist_container:
self.state.layers.remove(layer_state)

def _sync_layer_artist_container(self):
def _sync_layer_artist_container(self, *args):
# Remove layer artists that no longer have a matching layer state
layer_states = set(layer_state.layer for layer_state in self.state.layers)
for layer_artist in self._layer_artist_container:
Expand Down
9 changes: 4 additions & 5 deletions glue/viewers/histogram/qt/data_viewer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import absolute_import, division, print_function

from glue.utils import nonpartial
from glue.viewers.matplotlib.qt.toolbar import MatplotlibViewerToolbar
from glue.core.edit_subset_mode import EditSubsetMode
from glue.core.util import update_ticks
Expand Down Expand Up @@ -31,11 +30,11 @@ class HistogramViewer(MatplotlibDataViewer):

def __init__(self, session, parent=None, state=None):
super(HistogramViewer, self).__init__(session, parent, state=state)
self.state.add_callback('x_att', nonpartial(self._update_axes))
self.state.add_callback('x_log', nonpartial(self._update_axes))
self.state.add_callback('normalize', nonpartial(self._update_axes))
self.state.add_callback('x_att', self._update_axes)
self.state.add_callback('x_log', self._update_axes)
self.state.add_callback('normalize', self._update_axes)

def _update_axes(self):
def _update_axes(self, *args):

if self.state.x_att is not None:

Expand Down
5 changes: 2 additions & 3 deletions glue/viewers/histogram/qt/options_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from qtpy import QtWidgets

from glue.external.echo.qt import autoconnect_callbacks_to_qt
from glue.utils import nonpartial
from glue.utils.qt import load_ui, fix_tab_widget_fontsize

__all__ = ['HistogramOptionsWidget']
Expand All @@ -26,9 +25,9 @@ def __init__(self, viewer_state, session, parent=None):

self.viewer_state = viewer_state

viewer_state.add_callback('x_att', nonpartial(self._update_attribute))
viewer_state.add_callback('x_att', self._update_attribute)

def _update_attribute(self):
def _update_attribute(self, *args):
# If at least one of the components is categorical, disable log button
log_enabled = not any(comp.categorical for comp in self.viewer_state._get_x_components())
self.ui.bool_x_log.setEnabled(log_enabled)
Expand Down
23 changes: 20 additions & 3 deletions glue/viewers/image/layer_artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,21 @@ def update(self):
class ImageSubsetArray(object):

def __init__(self, viewer_state, layer_artist):
self.viewer_state = weakref.proxy(viewer_state)
self.layer_artist = weakref.proxy(layer_artist)
self.layer_state = weakref.proxy(layer_artist.state)
self._viewer_state = weakref.ref(viewer_state)
self._layer_artist = weakref.ref(layer_artist)
self._layer_state = weakref.ref(layer_artist.state)

@property
def layer_artist(self):
return self._layer_artist()

@property
def layer_state(self):
return self._layer_state()

@property
def viewer_state(self):
return self._viewer_state()

@property
def shape(self):
Expand All @@ -267,6 +279,11 @@ def nan_array(self):

def __getitem__(self, view=None):

if (self.layer_artist is None or
self.layer_state is None or
self.viewer_state is None):
return self.nan_array

if not self.layer_artist._compatible_with_reference_data:
return self.nan_array

Expand Down
26 changes: 13 additions & 13 deletions glue/viewers/matplotlib/qt/data_viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from glue.viewers.matplotlib.qt.widget import MplWidget
from glue.viewers.common.viz_client import init_mpl, update_appearance_from_settings
from glue.external.echo import delay_callback
from glue.utils import nonpartial, defer_draw
from glue.utils import defer_draw
from glue.utils.decorators import avoid_circular
from glue.viewers.matplotlib.qt.toolbar import MatplotlibViewerToolbar
from glue.viewers.matplotlib.state import MatplotlibDataViewerState
Expand Down Expand Up @@ -34,20 +34,20 @@ def __init__(self, session, parent=None, wcs=None, state=None):

self.update_aspect()

self.state.add_callback('x_min', nonpartial(self.limits_to_mpl))
self.state.add_callback('x_max', nonpartial(self.limits_to_mpl))
self.state.add_callback('y_min', nonpartial(self.limits_to_mpl))
self.state.add_callback('y_max', nonpartial(self.limits_to_mpl))
self.state.add_callback('x_min', self.limits_to_mpl)
self.state.add_callback('x_max', self.limits_to_mpl)
self.state.add_callback('y_min', self.limits_to_mpl)
self.state.add_callback('y_max', self.limits_to_mpl)

self.limits_to_mpl()

self.state.add_callback('x_log', nonpartial(self.update_x_log), priority=1000)
self.state.add_callback('y_log', nonpartial(self.update_y_log), priority=1000)
self.state.add_callback('x_log', self.update_x_log, priority=1000)
self.state.add_callback('y_log', self.update_y_log, priority=1000)

self.update_x_log()

self.axes.callbacks.connect('xlim_changed', nonpartial(self.limits_from_mpl))
self.axes.callbacks.connect('ylim_changed', nonpartial(self.limits_from_mpl))
self.axes.callbacks.connect('xlim_changed', self.limits_from_mpl)
self.axes.callbacks.connect('ylim_changed', self.limits_from_mpl)

self.axes.set_autoscale_on(False)

Expand All @@ -58,26 +58,26 @@ def redraw(self):
self.figure.canvas.draw()

@defer_draw
def update_x_log(self):
def update_x_log(self, *args):
self.axes.set_xscale('log' if self.state.x_log else 'linear')
self.redraw()

@defer_draw
def update_y_log(self):
def update_y_log(self, *args):
self.axes.set_yscale('log' if self.state.y_log else 'linear')
self.redraw()

def update_aspect(self, aspect=None):
self.axes.set_aspect(self.state.aspect, adjustable='datalim')

@avoid_circular
def limits_from_mpl(self):
def limits_from_mpl(self, *args):
with delay_callback(self.state, 'x_min', 'x_max', 'y_min', 'y_max'):
self.state.x_min, self.state.x_max = self.axes.get_xlim()
self.state.y_min, self.state.y_max = self.axes.get_ylim()

@avoid_circular
def limits_to_mpl(self):
def limits_to_mpl(self, *args):
if self.state.x_min is not None and self.state.x_max is not None:
self.axes.set_xlim(self.state.x_min, self.state.x_max)
if self.state.y_min is not None and self.state.y_max is not None:
Expand Down
11 changes: 5 additions & 6 deletions glue/viewers/scatter/qt/data_viewer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from __future__ import absolute_import, division, print_function

from glue.core import command
from glue.utils import nonpartial
from glue.viewers.matplotlib.qt.toolbar import MatplotlibViewerToolbar
from glue.core.edit_subset_mode import EditSubsetMode
from glue.core.util import update_ticks
Expand Down Expand Up @@ -32,13 +31,13 @@ class ScatterViewer(MatplotlibDataViewer):

def __init__(self, session, parent=None, state=None):
super(ScatterViewer, self).__init__(session, parent, state=state)
self.state.add_callback('x_att', nonpartial(self._update_axes))
self.state.add_callback('y_att', nonpartial(self._update_axes))
self.state.add_callback('x_log', nonpartial(self._update_axes))
self.state.add_callback('y_log', nonpartial(self._update_axes))
self.state.add_callback('x_att', self._update_axes)
self.state.add_callback('y_att', self._update_axes)
self.state.add_callback('x_log', self._update_axes)
self.state.add_callback('y_log', self._update_axes)
self._update_axes()

def _update_axes(self):
def _update_axes(self, *args):

if self.state.x_att is not None:

Expand Down

0 comments on commit ae8c1a3

Please sign in to comment.