Begin switching study browser use react-viewerbase

This commit is contained in:
Erik Ziegler committed 2018-12-11 12:30:21 +01:00
1 parent 83a7271cd1
commit c3565cd687
13 files changed
+206 -311

No files matched your search

@@ -1,13 +0,0 @@
<template name="flexboxLayout">
<div class="viewerSection">
<div class="sidebarMenu sidebar-left {{#if leftSidebarOpen}}sidebar-open{{/if}}">
{{> studyBrowser (clone this)}}
</div>
<div class="mainContent {{#if leftSidebarOpen}}sidebar-left-open{{/if}} {{#if rightSidebarOpen}}sidebar-right-open{{/if}}">
{{> React component=ViewerMain studies=this.studies}}
</div>
<div class="sidebarMenu sidebar-right {{#if rightSidebarOpen}}sidebar-open{{/if}}">
{{> measurementLightTable (clone this)}}
</div>
</div>
</template>
@@ -1,15 +1,176 @@
import { Component } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import { Viewerbase } from 'meteor/ohif:viewerbase';
import { StudyBrowser } from 'react-viewerbase';
Template.flexboxLayout.helpers({
leftSidebarOpen() {
return Template.instance().data.state.get('leftSidebar');
},
/**
* Asynchronous wrapper around Cornerstone's renderToCanvas method.
*
* @param {HTMLElement} canvasElement An HTML <canvas> element
* @param {Image} image A Cornerstone Image
*
* @return {Promise} A promise tracking the progress of the rendering. Resolves empty.
*/
function renderAsync(canvasElement, image) {
return new Promise((resolve, reject) => {
try {
cornerstone.renderToCanvas(canvasElement, image);
resolve();
} catch(error) {
reject(error);
}
});
}
rightSidebarOpen() {
return Template.instance().data.state.get('rightSidebar');
},
// Define a handler for success on image load
const loadSuccess = function(image, studyInstanceUid, displaySetInstanceUid) {
// This is an off-screen canvas. It's used to get dataURL images by using
// cornerstone.renderToCanvas function.
const canvasElement = document.createElement('canvas');
canvasElement.width = 193;
canvasElement.height = 123;
ViewerMain() {
return Viewerbase.components.viewer.ViewerMain;
console.warn(this.state);
// Render the image to canvas to be able to get its dataURL
renderAsync(canvasElement, image).then(() => {
const studies = this.state.studiesForBrowser;
const study = studies.find(study => study.studyInstanceUid === studyInstanceUid);
const thumbnail = study.thumbnails.find(t => t.displaySetInstanceUid === displaySetInstanceUid);
thumbnail.imageSrc = canvasElement.toDataURL('image/jpeg', 1);
console.warn(this.state);
this.setState({
studiesForBrowser: studies
});
});
};
// Define a handler for error on image load
const loadError = function(error, studyInstanceUid, displaySetInstanceUid) {
const studies = this.state.studiesForBrowser;
const study = studies.find(study => study.studyInstanceUid === studyInstanceUid);
const thumbnail = study.thumbnails.find(t => t.displaySetInstanceUid === displaySetInstanceUid);
thumbnail.error = error;
this.setState({
studiesForBrowser: studies
});
};
const ViewerMain = Viewerbase.components.viewer.ViewerMain;
class FlexboxLayout extends Component {
constructor(props) {
super(props);
this.state = {
leftSidebarOpen: true,
rightSidebarOpen: false,
studiesForBrowser: this.getStudiesForBrowser(),
};
this.getStudiesForBrowser = this.getStudiesForBrowser.bind(this);
this.getThumbnailsFromImageIds = this.getThumbnailsFromImageIds.bind(this);
}
});
componentDidMount() {
this.getThumbnailsFromImageIds();
}
getThumbnailsFromImageIds() {
const studyThumbnails = this.state.studiesForBrowser.forEach(function (study) {
const { studyInstanceUid } = study;
study.thumbnails.forEach(function (displaySet) {
const imageId = displaySet.imageId;
const {displaySetInstanceUid, seriesDescription, seriesNumber, instanceNumber, numImageFrames} = displaySet;
cornerstone.loadAndCacheImage(imageId).then((image) => {
loadSuccess.call(this, image, studyInstanceUid, displaySetInstanceUid);
}, error => {
loadError.call(this, error, studyInstanceUid, displaySetInstanceUid);
});
}, this);
}, this);
}
getStudiesForBrowser() {
// @TypeSafeStudies
const studies = OHIF.viewer.Studies.findAllBy({
selected: true
});
// TODO[react]:
// - Add sorting of display sets
// - Pass in which display set is active from Redux
// - Pass in errors and stack loading progress from Redux
// - Add useMiddleSeriesInstanceAsThumbnail
// - Add showStackLoadingProgressBar option
return studies.map((study) => {
const { studyInstanceUid } = study;
const thumbnails = study.displaySets.map((displaySet) => {
const active = false;
const stackPercentComplete = 0;
const { displaySetInstanceUid, seriesDescription, seriesNumber, instanceNumber, numImageFrames } = displaySet;
const imageId = displaySet.images[0].getImageId();
return {
imageSrc: '',
imageId,
displaySetInstanceUid,
seriesDescription,
seriesNumber,
instanceNumber,
numImageFrames,
active,
stackPercentComplete
};
});
return {
studyInstanceUid,
thumbnails
};
});
}
render() {
let mainContentClassName = "mainContent"
if (this.state.leftSidebarOpen) {
mainContentClassName += ' sidebar-left-open';
}
if (this.state.rightSidebarOpen) {
mainContentClassName += ' sidebar-right-open';
}
// TODO[react]: Add measurementLightTable
return (
<div className="viewerSection">
<div className={this.state.leftSidebarOpen ? "sidebarMenu sidebar-left sidebar-open" : "sidebarMenu sidebar-left"}>
<StudyBrowser studies={this.state.studiesForBrowser}/>
</div>
<div className={mainContentClassName}>
<ViewerMain studies={this.props.studies}/>
</div>
<div className={this.state.rightSidebarOpen ? "sidebarMenu sidebar-right sidebar-open" : "sidebarMenu sidebar-right"}>
{/*{{> measurementLightTable (clone this)}}*/}
</div>
</div>
);
}
}
FlexboxLayout.propTypes = {
studies: PropTypes.array.isRequired
};
export default FlexboxLayout;
@@ -1,13 +1,19 @@
<template name="viewer">
{{#if and Template.subscriptionsReady}}
<div class="viewerDialogs-react">
{{> React component=CineDialog}}
</div>
<div class="viewerDialogs">
{{>cineDialog}}
{{>layoutChooser}}
{{>annotationDialogs}}
</div>
<div id="viewer">
{{>toolbarSection (clone this state=state)}}
{{>flexboxLayout (clone this state=state)}}
<div>
{{>toolbarSection (clone this state=state)}}
</div>
<div>
{{> React component=FlexboxLayout studies=this.studies}}
</div>
</div>
{{else}}
{{>loadingText}}
@@ -6,6 +6,9 @@ import { Tracker } from 'meteor/tracker';
import { OHIF } from 'meteor/ohif:core';
import { MeasurementTable } from 'meteor/ohif:measurement-table';
import { CineDialog } from 'react-viewerbase';
import FlexboxLayout from '../flexboxLayout/flexboxLayout.js';
import 'meteor/ohif:cornerstone';
import 'meteor/ohif:viewerbase';
import 'meteor/ohif:metadata';
@@ -180,5 +183,13 @@ Template.viewer.onDestroyed(function() {
Template.viewer.helpers({
state() {
return Template.instance().state;
},
CineDialog() {
return CineDialog
},
FlexboxLayout() {
return FlexboxLayout
}
});
+12
View File
@@ -49,6 +49,11 @@
"integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==",
"dev": true
},
"classnames": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
"integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -413,6 +418,13 @@
"warning": "^4.0.1"
}
},
"react-viewerbase": {
"version": "git://github.com/OHIF/react-viewerbase.git#af26417b7c1da75b713f70b38c9dc3ce29e5c359",
"from": "git://github.com/OHIF/react-viewerbase.git",
"requires": {
"classnames": "^2.2.6"
}
},
"readable-stream": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+2 -1
View File
@@ -12,7 +12,8 @@
"react": "^16.6.3",
"react-dom": "^16.6.3",
"react-router-dom": "^4.3.1",
"redux": "^4.0.1"
"redux": "^4.0.1",
"react-viewerbase": "git://github.com:OHIF/react-viewerbase.git"
},
"devDependencies": {
"puppeteer": "^1.5.0"