Skip to content

Commit

Permalink
Merge branch 'release/2015.5.19'
Browse files Browse the repository at this point in the history
  • Loading branch information
tomka committed May 19, 2015
2 parents 00240e5 + 8ae996b commit 426bdff
Show file tree
Hide file tree
Showing 31 changed files with 432 additions and 201 deletions.
56 changes: 56 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,67 @@
## Under development

### Features and enhancements



### Bug fixes



## 2015.5.19

Contributors: Tom Kazimiers


### Features and enhancements

Key shortcuts / mouse operations:

- Cycling through open end nodes will now only visit the root node if it is an
actual leaf. That is, when it has only one child node and is untagged.


3D Viewer:

- A light background shading variant for connectors was added. It uses a darker
cyan color which provides more contrast if a white background is used.


Miscellaneous:

- The location of messages and notifications can be configured in the settings
widget. The default location is still the upper right corner.

- If the node display limit is hit while panning the field of view in tracing
mode, node refresh will be temporary disabled. Once the mouse button is
released again an no further panning happens within one second, node update is
reset to normal. This allows for smoother panning if many nodes are visible.


### Bug fixes

Review system:

- Review teams are now respected when Shift + W is used to jump to the next
unreviewed node.


3D viewer:

- Skeletons with other coloring than "Source", will now be visible when exported
as SVG in the 3D viewer.


Miscellaneous:

- Skeletons added to a selection table, will now honor the table's "global"
settings for pre, post, meta and text visibility.

- If an annotation is removed from a neuron, the annotation itself will be
deleted, too, if it is not used anywhere else. Now also meta annotations of
the deleted annotation will be removed (and their meta annotations...), if
they are not used anywhere else.


## 2015.5.11

Expand Down
51 changes: 39 additions & 12 deletions django/applications/catmaid/control/neuron_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,11 @@ def _update_neuron_annotations(project_id, user, neuron_id, annotation_map):
These annotations are expected to come as dictornary of annotation name
versus annotator ID.
"""
annotated_with = Relation.objects.get(project_id=project_id,
relation_name='annotated_with')

qs = ClassInstanceClassInstance.objects.filter(
class_instance_a__id=neuron_id,
relation__relation_name='annotated_with')
class_instance_a__id=neuron_id, relation=annotated_with)
qs = qs.select_related('class_instance_b').values_list(
'class_instance_b__name', 'class_instance_b__id')

Expand All @@ -315,11 +317,37 @@ def _update_neuron_annotations(project_id, user, neuron_id, annotation_map):
to_delete_ids = tuple(aid for name, aid in existing_annotations.iteritems() \
if name in to_delete)

ClassInstanceClassInstance.objects.filter(
class_instance_a_id=neuron_id,
relation__relation_name='annotated_with',
ClassInstanceClassInstance.objects.filter(project=project_id,
class_instance_a_id=neuron_id, relation=annotated_with,
class_instance_b_id__in=to_delete_ids).delete()

for aid in to_delete_ids:
delete_annotation_if_unused(project_id, aid, annotated_with)


def delete_annotation_if_unused(project, annotation, relation):
""" Delete the given annotation instance if it is not used anymore.
Returns a tuple where the first element states if
"""
num_annotation_links = ClassInstanceClassInstance.objects.filter(
project=project, class_instance_b=annotation, relation=relation).count()

if num_annotation_links:
return False, num_annotation_links
else:
# See if the annotation is annotated itself
meta_annotation_links = ClassInstanceClassInstance.objects.filter(
project=project, class_instance_a=annotation, relation=relation)
meta_annotation_ids = [cici.class_instance_b_id for cici in meta_annotation_links]

# Delete annotation
ClassInstance.objects.filter(project=project, id=annotation).delete()

# Delete also meta annotation instances, if they exist
for ma in meta_annotation_ids:
delete_annotation_if_unused(project, ma, relation)

return True, 0

def _annotate_entities(project_id, entity_ids, annotation_map):
""" Annotate the entities with the given <entity_ids> with the given
Expand Down Expand Up @@ -479,16 +507,15 @@ def remove_annotation(request, project_id=None, annotation_id=None):

# Remove the annotation class instance, regardless of the owner, if there
# are no more links to it
annotation_links = ClassInstanceClassInstance.objects.filter(project=p,
class_instance_b__id=annotation_id)
num_annotation_links = annotation_links.count()
if num_annotation_links == 0:
ClassInstance.objects.get(pk=annotation_id).delete()
annotated_with = Relation.objects.get(project_id=project_id,
relation_name='annotated_with')
deleted, num_left = delete_annotation_if_unused(project_id, annotation_id,
annotated_with)
if deleted:
message += " Also removed annotation instance, because it isn't used " \
"anywhere else."
else:
message += " There are %s links left to this annotation." \
% num_annotation_links
message += " There are %s links left to this annotation." % num_left

return HttpResponse(json.dumps({'message': message}), content_type='text/json')

Expand Down
4 changes: 1 addition & 3 deletions django/applications/catmaid/control/skeleton.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,13 @@ def last_openleaf(request, project_id=None, skeleton_id=None):

# Some entries repeated, when a node has more than one tag
# Create a graph with edges from parent to child, and accumulate parents
real_root = None
tree = nx.DiGraph()
for row in cursor.fetchall():
nodeID = row[0]
if row[1]:
# It is ok to add edges that already exist: DiGraph doesn't keep duplicates
tree.add_edge(row[1], nodeID)
else:
real_root = nodeID
tree.add_node(nodeID)
tree.node[nodeID]['loc'] = (row[2], row[3], row[4])
tree.node[nodeID]['ct'] = row[5]
Expand All @@ -95,7 +93,7 @@ def last_openleaf(request, project_id=None, skeleton_id=None):
end_regex = re.compile('(?:' + ')|(?:'.join(end_tags) + ')')

for nodeID, out_degree in tree.out_degree_iter():
if 0 == out_degree or nodeID == real_root:
if 0 == out_degree:
# Found an end node
props = tree.node[nodeID]
# Check if not tagged with a tag containing 'end'
Expand Down
69 changes: 51 additions & 18 deletions django/applications/catmaid/static/js/CATMAID.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,57 @@
return ui;
},
});

// Configuration of message position
var messagePosition = 'tr';
Object.defineProperty(CATMAID, 'messagePosition', {
get: function() { return messagePosition; },
set: function(newValue) {
var allowedValues = ['tl', 'tr', 'bl', 'br', 'tc', 'bc'];
if (-1 === allowedValues.indexOf(newValue)) {
throw new CATMAID.ValueError('Please use one of these values: ' +
allowedValues.join(','));
}
messagePosition = newValue;
}
});

/**
* Convenience function to show a growl message
*/
CATMAID.msg = function(title, message, options)
{
var settings = {
title: title,
message: message,
duration: 3000,
size: 'large',
location: messagePosition,
style: undefined // Gray background by default, alternatives are:
// 'error' = red, 'warning' = yellow, 'notice' = green
};

// If an alert style wasn't provided, guess from the alert title
if (!options || !options.style) {
if (title.match(/error/i)) settings.style = 'error';
else if (title.match(/warn|beware/i)) settings.style = 'warning';
else if (title.match(/done|success/i)) settings.style = 'notice';
}

$.extend(settings, options);
$.growl(settings);
};

/**
* Convenience function to show a growl info message.
*/
CATMAID.info = CATMAID.msg.bind(window, "Information");

/**
* Convenience function to show a growl warning message.
*/
CATMAID.warn = CATMAID.msg.bind(window, "Warning");

})(CATMAID);


Expand Down Expand Up @@ -213,21 +264,3 @@ CATMAID.error = function(msg, detail)
{
new CATMAID.ErrorDialog(msg, detail).show();
};

/**
* Convenience function to show a growl message
*/
CATMAID.msg = function(title, msg)
{
growlAlert(title, msg);
};

/**
* Convenience function to show a growl info message.
*/
CATMAID.info = CATMAID.msg.bind(window, "Information");

/**
* Convenience function to show a growl warning message.
*/
CATMAID.warn = CATMAID.msg.bind(window, "Warning");
1 change: 1 addition & 0 deletions django/applications/catmaid/static/js/WindowMaker.js
Original file line number Diff line number Diff line change
Expand Up @@ -1030,6 +1030,7 @@ var WindowMaker = new function()

var synColors = document.createElement('select');
synColors.options.add(new Option('Type: pre/red, post/cyan', 'cyan-red'));
synColors.options.add(new Option('Type: pre/red, post/cyan (light background)', 'cyan-red-dark'));
synColors.options.add(new Option('N with partner: pre[red > blue], post[yellow > cyan]', 'by-amount'));
synColors.options.add(new Option('Synapse clusters', 'synapse-clustering'));
synColors.options.add(new Option('Max. flow cut: axon (green) and dendrite (blue)', 'axon-and-dendrite'));
Expand Down
2 changes: 1 addition & 1 deletion django/applications/catmaid/static/js/tools/roitool.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function RoiTool()
this.button_roi_apply = document.getElementById( "button_roi_apply" );
this.button_roi_apply.onclick = this.createRoi.bind(this, function(result) {
if (result.status) {
growlAlert("Success", result.status);
CATMAID.msg("Success", result.status);
}
});

Expand Down
41 changes: 38 additions & 3 deletions django/applications/catmaid/static/js/tools/tracingtool.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ function TracingTool()
}
};

var disableLayerUpdate = function() {
CATMAID.warn("Temporary disabling node update until panning is over");
tracingLayer.svgOverlay.noUpdate = true;
};

var createTracingLayer = function( parentStack )
{
stack = parentStack;
Expand All @@ -69,6 +74,9 @@ function TracingTool()
// view is the mouseCatcher now
var view = tracingLayer.svgOverlay.view;

// A handle to a delayed update
var updateTimeout;

var proto_onmousedown = view.onmousedown;
view.onmousedown = function( e ) {
switch ( CATMAID.ui.getMouseButton( e ) )
Expand All @@ -77,15 +85,42 @@ function TracingTool()
tracingLayer.svgOverlay.whenclicked( e );
break;
case 2:
// Attach to the node limit hit event to disable node updates
// temporary if the limit was hit. This allows for smoother panning
// when many nodes are visible.
tracingLayer.svgOverlay.on(tracingLayer.svgOverlay.EVENT_HIT_NODE_DISPLAY_LIMIT,
disableLayerUpdate, tracingLayer);
// Cancel any existing update timeout, if there is one
if (updateTimeout) {
clearTimeout(updateTimeout);
updateTimeout = undefined;
}

proto_onmousedown( e );

CATMAID.ui.registerEvent( "onmousemove", updateStatusBar );
CATMAID.ui.registerEvent( "onmouseup",
function onmouseup (e) {
CATMAID.ui.releaseEvents();
CATMAID.ui.removeEvent( "onmousemove", updateStatusBar );
CATMAID.ui.removeEvent( "onmouseup", onmouseup );
// Recreate nodes by feching them from the database for the new field of view
tracingLayer.svgOverlay.updateNodes();
tracingLayer.svgOverlay.off(tracingLayer.svgOverlay.EVENT_HIT_NODE_DISPLAY_LIMIT,
disableLayerUpdate, tracingLayer);
if (tracingLayer.svgOverlay.noUpdate) {
// Wait a second before updating the view, just in case the user
// continues to pan to not hit the node limit again. Then make
// sure the next update is not stopped.
updateTimeout = setTimeout(function() {
tracingLayer.svgOverlay.noUpdate = false;
// Recreate nodes by feching them from the database for the
// new field of view
tracingLayer.svgOverlay.updateNodes();
}, 1000);
} else {
// Recreate nodes by feching them from the database for the new
// field of view
tracingLayer.svgOverlay.updateNodes();
}
});
break;
default:
Expand Down Expand Up @@ -336,7 +371,7 @@ function TracingTool()
} ) );

this.addAction( new Action({
helpText: "Go to nearest open leaf or untagged root node (subsequent shift+R: cycle through other open leaves; with alt: most recent rather than nearest)",
helpText: "Go to nearest open leaf node (subsequent shift+R: cycle through other open leaves; with alt: most recent rather than nearest)",
keyShortcuts: { "R": [ 82 ] },
run: function (e) {
if (!mayView())
Expand Down
Loading

0 comments on commit 426bdff

Please sign in to comment.