Skip to content

Commit

Permalink
Merge pull request #237 from qdev-dk/hotfix/limit-widget
Browse files Browse the repository at this point in the history
SubprocessWidget improvements
  • Loading branch information
alexcjohnson authored Jun 14, 2016
2 parents 2ccea6c + 8a3d00a commit 9d3a55a
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 85 deletions.
42 changes: 24 additions & 18 deletions qcodes/widgets/display.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
"""Helper for adding content stored in a file to a jupyter notebook."""
import os
from pkg_resources import resource_string
from IPython.display import display, Javascript, HTML


# Originally I implemented this using regular open() and read(), so it
# could use relative paths from the importing file.
#
# But for distributable packages, pkg_resources.resource_string is the
# best way to load data files, because it works even if the package is
# in an egg or zip file. See:
# http://pythonhosted.org/setuptools/setuptools.html#accessing-data-files-at-runtime

def display_auto(qcodes_path, file_type=None):
'''
Display some javascript, css, or html content in the notebook
from a package-relative file path. Will use the file extension
to determine file type unless overridden by file_type
qcodes_path: the path to the target file within the qcodes package
file_type: optionally override the file extension to determine
what type of file this is
'''

# Originally I implemented this using regular open() and read(), so it
# could use relative paths from the importing file.
#
# But for distributable packages, pkg_resources.resource_string is the
# best way to load data files, because it works even if the package is
# in an egg or zip file. See:
# http://pythonhosted.org/setuptools/setuptools.html#accessing-data-files-at-runtime
"""
Display some javascript, css, or html content in a jupyter notebook.
Content comes from a package-relative file path. Will use the file
extension to determine file type unless overridden by file_type
Args:
qcodes_path (str): the path to the target file within the qcodes
package, like 'widgets/widgets.js'
file_type (Optional[str]): Override the file extension to determine
what type of file this is. Case insensitive, supported values
are 'js', 'css', and 'html'
"""
contents = resource_string('qcodes', qcodes_path).decode('utf-8')

if file_type is None:
ext = os.path.splitext(qcodes_path)[1].lower()
elif 'js' in file_type.lower():
Expand Down
29 changes: 28 additions & 1 deletion qcodes/widgets/widgets.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,37 @@
box-shadow: 0 0 12px 1px rgba(87, 87, 87, 0.2);
}

.qcodes-output-header {
.qcodes-output-header {
float: right;
}

.qcodes-highlight {
animation: pulse 1s linear;
background-color: #fa4;
}

@keyframes pulse {
0% {
background-color: #f00;
}
100% {
background-color: #fa4;
}
}

.qcodes-process-list {
float: left;
max-width: 780px;
margin: 3px 5px 3px 10px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}

.qcodes-output-view[qcodes-state=minimized] .qcodes-process-list {
max-width: 300px;
}

.qcodes-output-view span {
padding: 2px 6px 3px 12px;
}
Expand Down
128 changes: 94 additions & 34 deletions qcodes/widgets/widgets.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,14 @@ require([

var SubprocessView = UpdateView.extend({
render: function() {
var me = window.SPVIEW = this;
var me = this;
me._interval = 0;
me._minimize = '<i class="fa-minus fa"></i>';
me._restore = '<i class="fa-plus fa"></i>';

// max lines of output to show
me.maxOutputLength = 500;

// in case there is already an outputView present,
// like from before restarting the kernel
$('.qcodes-output-view').not(me.$el).remove();
Expand All @@ -70,7 +73,8 @@ require([
.attr('qcodes-state', 'docked')
.html(
'<div class="qcodes-output-header toolbar">' +
'<span></span>' +
'<div class="qcodes-process-list"></div>' +
'<button class="btn qcodes-processlines"><i class="fa-list fa"></i></button>' +
'<button class="btn qcodes-abort-loop disabled">Abort</button>' +
'<button class="btn qcodes-clear-output disabled qcodes-content">Clear</button>' +
'<button class="btn js-state qcodes-minimized"><i class="fa-minus fa"></i></button>' +
Expand All @@ -83,8 +87,11 @@ require([
me.clearButton = me.$el.find('.qcodes-clear-output');
me.minButton = me.$el.find('.qcodes-minimize');
me.outputArea = me.$el.find('pre');
me.subprocessList = me.$el.find('span');
me.subprocessList = me.$el.find('.qcodes-process-list');
me.abortButton = me.$el.find('.qcodes-abort-loop');
me.processLinesButton = me.$el.find('.qcodes-processlines')

me.outputLines = [];

me.clearButton.click(function() {
me.outputArea.html('');
Expand All @@ -95,6 +102,12 @@ require([
me.send({abort: true});
});

me.processLinesButton.click(function() {
// toggle multiline process list display
me.subprocessesMultiline = !me.subprocessesMultiline;
me.showSubprocesses();
});

me.$el.find('.js-state').click(function() {
var oldState = me.$el.attr('qcodes-state'),
state = this.className.substr(this.className.indexOf('qcodes'))
Expand All @@ -112,53 +125,100 @@ require([
me.$el.attr('qcodes-state', state);

if(state === 'floated') {
me.$el.draggable().css({
left: window.innerWidth - me.$el.width() - 15,
top: window.innerHeight - me.$el.height() - 10
});
me.$el
.draggable({stop: function() { me.clipBounds(); }})
.css({
left: window.innerWidth - me.$el.width() - 15,
top: window.innerHeight - me.$el.height() - 10
});
}
});

$(window).resize(function() {
if(me.$el.attr('qcodes-state') === 'floated') {
var position = me.$el.position(),
minVis = 20,
maxLeft = window.innerWidth - minVis,
maxTop = window.innerHeight - minVis;

if(position.left > maxLeft) me.$el.css('left', maxLeft);
if(position.top > maxTop) me.$el.css('top', maxTop);
}
// any previous highlighting is
me.$el.removeClass('qcodes-highlight');
});

$(window)
.off('resize.qcodes')
.on('resize.qcodes', function() {me.clipBounds();});

me.update();
},

clipBounds: function() {
var me = this;
if(me.$el.attr('qcodes-state') === 'floated') {
var bounds = me.$el[0].getBoundingClientRect(),
minVis = 40,
maxLeft = window.innerWidth - minVis,
minLeft = minVis - bounds.width,
maxTop = window.innerHeight - minVis;

if(bounds.left > maxLeft) me.$el.css('left', maxLeft);
else if(bounds.left < minLeft) me.$el.css('left', minLeft);

if(bounds.top > maxTop) me.$el.css('top', maxTop);
else if(bounds.top < 0) me.$el.css('top', 0);
console.log(bounds);
}
},

display: function(message) {
var me = this;
if(message) {
var initialScroll = this.outputArea.scrollTop();
this.outputArea.scrollTop(this.outputArea.prop('scrollHeight'));
var scrollBottom = this.outputArea.scrollTop();

if(this.$el.attr('qcodes-state') === 'minimized') {
this.$el.find('.qcodes-docked').click();
// always scroll to the bottom if we're restoring
// because of a new message
initialScroll = scrollBottom;
var initialScroll = me.outputArea.scrollTop();
me.outputArea.scrollTop(me.outputArea.prop('scrollHeight'));
var scrollBottom = me.outputArea.scrollTop();

if(me.$el.attr('qcodes-state') === 'minimized') {
// if we add text and the box is minimized, highlight the
// title bar to alert the user that there are new messages.
// remove then add the class, so we get the animation again
// if it's already highlighted
me.$el.removeClass('qcodes-highlight');
setTimeout(function(){
me.$el.addClass('qcodes-highlight');
}, 0);
}

var newLines = message.split('\n'),
out = me.outputLines,
outLen = out.length;
if(outLen) out[outLen - 1] += newLines[0];
else out.push(newLines[0]);

for(var i = 1; i < newLines.length; i++) {
out.push(newLines[i]);
}

this.outputArea.append(message);
this.clearButton.removeClass('disabled');
if(out.length > me.maxOutputLength) {
out.splice(0, out.length - me.maxOutputLength + 1,
'<<< Output clipped >>>');
}

me.outputArea.text(out.join('\n'));
me.clearButton.removeClass('disabled');

// if we were scrolled to the bottom initially, make sure
// we stay that way.
this.outputArea.scrollTop(initialScroll === scrollBottom ?
this.outputArea.prop('scrollHeight') : initialScroll);
me.outputArea.scrollTop(initialScroll === scrollBottom ?
me.outputArea.prop('scrollHeight') : initialScroll);
}

var processes = this.model.get('_processes') || 'No subprocesses';
this.abortButton.toggleClass('disabled', processes.indexOf('Measurement')===-1);
this.subprocessList.text(processes);
var processes = me.model.get('_processes');
me.abortButton.toggleClass('disabled', processes.indexOf('Measurement')===-1);
me._processes = processes;
me.showSubprocesses();
},

showSubprocesses: function() {
var me = this,
replacer = me.subprocessesMultiline ? '<br>' : ', ',
processes = (me._processes || '').replace(/\n/g, '&gt;' + replacer + '&lt;');

if(processes) processes = '&lt;' + processes + '&gt;';
else processes = 'No subprocesses';

me.subprocessList.html(processes);
}
});
manager.WidgetManager.register_widget_view('SubprocessView', SubprocessView);
Expand Down
Loading

0 comments on commit 9d3a55a

Please sign in to comment.