diff --git a/Dockerfile b/Dockerfile
index 3a621207..9abdc2e8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,8 +2,7 @@ FROM frodriguez4600/jupyter-neuron:v7.8.0
ARG INSTALLATION_FOLDER=/home/jovyan/work/NetPyNE-UI
ARG NETPYNE_VERSION=development
ARG WORKSPACE_VERSION=nov2020
-ARG JUPYTER_GEPPETTO_VERSION=development
-ARG PYGEPPETTO_VERSION=development
+ARG GEPPETTO_VERSION=development
ARG BUILD_ARGS=""
USER $NB_USER
@@ -11,8 +10,7 @@ USER $NB_USER
ENV INSTALLATION_FOLDER=$INSTALLATION_FOLDER
ENV NETPYNE_VERSION=$NETPYNE_VERSION
ENV WORKSPACE_VERSION=$WORKSPACE_VERSION
-ENV JUPYTER_GEPPETTO_VERSION=$JUPYTER_GEPPETTO_VERSION
-ENV PYGEPPETTO_VERSION=$PYGEPPETTO_VERSION
+ENV GEPPETTO_VERSION=$GEPPETTO_VERSION
ENV BUILD_ARGS=$BUILD_ARGS
# Install openmpi for parallel simulations
@@ -30,10 +28,12 @@ RUN pip install -r requirements.txt
COPY --chown=1000:1000 . .
WORKDIR ${INSTALLATION_FOLDER}/utilities
-RUN python install.py ${BUILD_ARGS}
+RUN npm install --global yarn
+RUN npm install --global yalc
+RUN python install.py ${BUILD_ARGS} --geppetto ${GEPPETTO_VERSION}
WORKDIR ${INSTALLATION_FOLDER}
RUN pip install -r requirements-test.txt
-RUN pytest tests/backend
+# RUN pytest tests/backend
CMD /bin/bash -c "jupyter notebook --NotebookApp.default_url=/geppetto --NotebookApp.token='' --library=netpyne_ui --NotebookApp.disable_check_xsrf=True"
diff --git a/webapp/components/NetPyNE.js b/webapp/components/NetPyNE.js
index 79aa179b..b864c5cd 100644
--- a/webapp/components/NetPyNE.js
+++ b/webapp/components/NetPyNE.js
@@ -31,7 +31,7 @@ const styles = ({ zIndex }) => ({
},
topbar: {
position: 'relative',
- zIndex: zIndex.drawer + 1,
+ zIndex: zIndex.drawer,
},
content: {
flexGrow: 1,
@@ -46,6 +46,11 @@ const TIMEOUT = 10000;
const EXPERIMENT_POLL_INTERVAL = 1000;
class NetPyNE extends React.Component {
+ constructor (props) {
+ super(props);
+ this.openPythonCallDialog = this.openPythonCallDialog.bind(this);
+ }
+
componentDidMount () {
GEPPETTO.on(GEPPETTO.Events.Error_while_exec_python_command, this.openPythonCallDialog, this);
diff --git a/webapp/components/general/ControlPanelTreeItem.js b/webapp/components/general/ControlPanelTreeItem.js
index 182d87d3..4049ae64 100644
--- a/webapp/components/general/ControlPanelTreeItem.js
+++ b/webapp/components/general/ControlPanelTreeItem.js
@@ -12,7 +12,7 @@ import Shuffle from '@material-ui/icons/Shuffle';
import { ChromePicker } from 'react-color';
import { useDispatch, useSelector } from 'react-redux';
import { experimentLabelColor } from '../../theme';
-import { changeInstanceColor } from '../../redux/actions/general';
+import { changeInstanceColor, selectInstances } from '../../redux/actions/general';
const useStyles = makeStyles((theme) => ({
networkItem: {
@@ -62,26 +62,39 @@ const ControlPanelTreeItem = (props) => {
setColor(_color.rgb);
};
+ const getRandomColor = () => ({
+ r: parseFloat((Math.random() * 255).toFixed(2)),
+ g: parseFloat((Math.random() * 255).toFixed(2)),
+ b: parseFloat((Math.random() * 255).toFixed(2)),
+ a: 1,
+ });
+
const generateRandomColor = (event, nodeId) => {
- const newInstances = instances.filter((instance) => !(instance.instancePath.startsWith(nodeId)));
- const randomColor = {
- r: parseFloat((Math.random() * 255).toFixed(2)),
- g: parseFloat((Math.random() * 255).toFixed(2)),
- b: parseFloat((Math.random() * 255).toFixed(2)),
- a: 1,
- };
+ const children = window.Instances.getInstance(nodeId).getChildren().map((instance) => instance.getInstancePath());
+ // const newInstances = instances.filter((instance) => !(instance.instancePath.startsWith(nodeId)));
+ const newInstances = instances.filter((instance) => {
+ let condition = true;
+ children.forEach((child) => {
+ if (instance.instancePath.startsWith(child)) {
+ condition = false;
+ }
+ });
+ return condition;
+ });
- newInstances.push({
- instancePath: nodeId,
- color: {
- r: randomColor.r / 255,
- g: randomColor.g / 255,
- b: randomColor.b / 255,
- a: randomColor.a,
- },
+ children.forEach((child) => {
+ const randomColor = getRandomColor();
+ newInstances.push({
+ instancePath: child,
+ color: {
+ r: randomColor.r / 255,
+ g: randomColor.g / 255,
+ b: randomColor.b / 255,
+ a: randomColor.a,
+ },
+ });
});
dispatch(changeInstanceColor(newInstances));
- setColor(randomColor);
};
const changeVisibility = (event, nodeId) => {
@@ -126,6 +139,7 @@ const ControlPanelTreeItem = (props) => {
onNodeSelect,
onVisibilityClick,
children,
+ disableRandom,
...other
} = props;
@@ -153,7 +167,7 @@ const ControlPanelTreeItem = (props) => {
changeVisibility(event, nodeId)}>
{ visibility ? : }
- generateRandomColor(event, nodeId)}>
+ generateRandomColor(event, nodeId)}>
setShowColorPicker(true)}>
{
showColorPicker
diff --git a/webapp/components/general/ExperimentControlPanel.js b/webapp/components/general/ExperimentControlPanel.js
index ddd40764..0f051e96 100644
--- a/webapp/components/general/ExperimentControlPanel.js
+++ b/webapp/components/general/ExperimentControlPanel.js
@@ -1,17 +1,16 @@
/* eslint-disable no-nested-ternary */
import * as React from 'react';
+import { useDispatch, useSelector } from 'react-redux';
import { makeStyles } from '@material-ui/core/styles';
import Box from '@material-ui/core/Box';
import Typography from '@material-ui/core/Typography';
import TextField from '@material-ui/core/TextField';
import TreeView from '@material-ui/lab/TreeView';
-import TreeItem from '@material-ui/lab/TreeItem';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
import ControlPanelTreeItem from './ControlPanelTreeItem';
import { experimentLabelColor } from '../../theme';
-
-import { MODEL_STATE } from '../../constants';
+import { selectInstances } from '../../redux/actions/general';
const useStyles = makeStyles(() => ({
header: {
@@ -24,9 +23,11 @@ const useStyles = makeStyles(() => ({
const ExperimentControlPanel = (props) => {
const classes = useStyles();
+ const dispatch = useDispatch();
+ const instances = useSelector((state) => state.general.instances);
const [filter, setFilter] = React.useState('');
const onNodeSelect = (nodeId) => {
- console.log(`Node with id ${nodeId} clicked`);
+ dispatch(selectInstances(instances, [nodeId]));
};
const instancesMap = new Map();
@@ -71,7 +72,7 @@ const ExperimentControlPanel = (props) => {
};
const getTreeItemsFromData = (treeItems) => treeItems.map((treeItemData) => {
- let children;
+ let children = [];
if (treeItemData.getChildren() && treeItemData.getChildren().length > 0) {
children = getTreeItemsFromData(treeItemData.getChildren());
}
@@ -84,6 +85,7 @@ const ExperimentControlPanel = (props) => {
type={treeItemData.getType().getId()}
onNodeSelect={onNodeSelect}
onVisibilityClick={onVisibilityClick}
+ disableRandom={children.length === 0}
>
{children}
@@ -110,12 +112,9 @@ const ExperimentControlPanel = (props) => {
defaultCollapseIcon={}
defaultExpandIcon={}
>
-
- {filter === ''
- ? getTreeItemsFromData(window.Instances.network.getChildren())
- : getFlatFilteredList(window.Instances.network.getChildren())
- }
-
+ {filter === ''
+ ? getTreeItemsFromData([window.Instances.getInstance('network')])
+ : getFlatFilteredList([window.Instances.getInstance('network')])}
)
diff --git a/webapp/components/index.js b/webapp/components/index.js
index 0761e2f6..d9d56302 100644
--- a/webapp/components/index.js
+++ b/webapp/components/index.js
@@ -16,7 +16,7 @@ import { openBackendErrorDialog, closeBackendErrorDialog } from '../redux/action
import {
updateCards, editModel, simulateNetwork, createNetwork, closeDialog,
createAndSimulateNetwork, showNetwork, pythonCall, modelLoaded, deleteNetParamsObj, resetModel,
- setDefaultWidgets, changeInstanceColor, openConfirmationDialog, closeConfirmationDialog,
+ setDefaultWidgets, changeInstanceColor, openConfirmationDialog, closeConfirmationDialog, selectInstances,
} from '../redux/actions/general';
import {
@@ -250,7 +250,7 @@ export const NetPyNEInstantiated = connect(
data: state.general.instances,
}),
(dispatch) => ({
- selectInstances: (instances) => dispatch(changeInstanceColor(instances)),
+ selectInstances: (instances, selectedInstances) => dispatch(selectInstances(instances, selectedInstances)),
}),
)(_NetPyNEInstantiated);
diff --git a/webapp/components/instantiation/NetPyNEInstantiated.js b/webapp/components/instantiation/NetPyNEInstantiated.js
index d1451906..b11851e8 100644
--- a/webapp/components/instantiation/NetPyNEInstantiated.js
+++ b/webapp/components/instantiation/NetPyNEInstantiated.js
@@ -13,7 +13,7 @@ const SELECTION_COLOR = {
r: 0, g: 0.8, b: 0.8, a: 1,
};
const DEFAULT_COLOR = {
- g: 0.50, b: 0.60, r: 1, a: 0.80,
+ g: 0.50, b: 0.60, r: 1, a: 1,
};
const styles = () => ({
@@ -50,13 +50,12 @@ class NetPyNEInstantiated extends React.Component {
this.canvasRef = React.createRef();
this.onSelection = this.onSelection.bind(this);
- this.applySelection = this.applySelection.bind(this);
this.mapToCanvasData = this.mapToCanvasData.bind(this);
}
onSelection (selectedInstances) {
const { selectInstances, data } = this.props;
- selectInstances(this.applySelection(data, selectedInstances));
+ selectInstances(data, selectedInstances);
}
updateBtnsWithTheme = (removeClass, addClass) => {
@@ -78,38 +77,6 @@ class NetPyNEInstantiated extends React.Component {
));
}
- applySelection (data, selectedInstances) {
- const smap = new Map(selectedInstances.map((i) => [i, true]));
- const newData = data.map((item) => {
- if (smap.get(item.instancePath)) {
- return {
- ...item,
- selected: !item.selected,
- };
- }
- return { ...item };
- });
- const dmap = new Map(newData.map((i) => [i.instancePath, true]));
-
- smap.forEach((value, key) => {
- const item = dmap.get(key);
- if (!item) {
- newData.push({
- instancePath: key,
- color: undefined,
- selected: true,
- });
- }
- });
- const canvasData = newData.filter((item) => {
- if ((item?.selected !== undefined && item?.selected === false) && item?.color === undefined) {
- return false;
- }
- return true;
- });
- return canvasData;
- }
-
render () {
const { cameraOptions } = this.state;
const { data } = this.props;
@@ -135,8 +102,13 @@ class NetPyNEInstantiated extends React.Component {
ref={this.canvasRef}
key="CanvasContainer"
cameraOptions={camOptions}
- backgroundColor={bgRegular}
+ backgroundColor={
+ this.props.theme === THEMES.BLACK
+ ? canvasBgDark
+ : (this.props.theme === THEMES.LIGHT ? canvasBgLight : bgRegular)
+ }
onSelection={this.onSelection}
+ linesThreshold="10000"
/>
);
diff --git a/webapp/constants.js b/webapp/constants.js
index 49f87589..5fefeffa 100644
--- a/webapp/constants.js
+++ b/webapp/constants.js
@@ -21,7 +21,7 @@ export const MODEL_STATE = {
};
export const DEFAULT_COLOR = {
- g: 0.50, b: 0.60, r: 1, a: 0.80,
+ g: 0.50, b: 0.60, r: 1, a: 1,
};
export const NETPYNE_COMMANDS = {
diff --git a/webapp/css/netpyne.less b/webapp/css/netpyne.less
index d1d36630..ddc757db 100644
--- a/webapp/css/netpyne.less
+++ b/webapp/css/netpyne.less
@@ -589,6 +589,20 @@ body {
.MuiTable-root {
border-collapse: separate;
}
+
+ .MuiBackdrop-root {
+ .MuiGrid-root {
+ display: block;
+ width: auto;
+ margin: 0;
+ flex: none;
+ text-align: center;
+
+ .MuiCircularProgress-root {
+ color: @textColor;
+ }
+ }
+ }
}
.instantiatedContainer {
diff --git a/webapp/redux/actions/general.js b/webapp/redux/actions/general.js
index c6afc209..ef22f5dd 100644
--- a/webapp/redux/actions/general.js
+++ b/webapp/redux/actions/general.js
@@ -25,6 +25,7 @@ export const SET_THEME = 'SET_THEME';
export const ADD_CANVAS_INSTANCES = 'ADD_CANVAS_INSTANCES';
export const CHANGE_INSTANCE_COLOR = 'CHANGE_INSTANCE_COLOR';
export const REMOVE_CANVAS_INSTANCES = 'REMOVE_CANVAS_INSTANCES';
+export const SELECT_INSTANCE = 'SELECT_INSTANCE';
// Actions
export const updateCards = { type: UPDATE_CARDS };
@@ -112,3 +113,11 @@ export const removeInstancesFromCanvas = (instances) => ({
type: REMOVE_CANVAS_INSTANCES,
instances,
});
+
+export const selectInstances = (instance, selectedInstances) => ({
+ type: SELECT_INSTANCE,
+ data: {
+ instance,
+ selectedInstances,
+ },
+});
diff --git a/webapp/redux/reducers/general.js b/webapp/redux/reducers/general.js
index 1a1a56fb..5a626024 100644
--- a/webapp/redux/reducers/general.js
+++ b/webapp/redux/reducers/general.js
@@ -21,6 +21,33 @@ export const GENERAL_DEFAULT_STATE = {
instances: [],
};
+const applySelection = (data, selectedInstances) => {
+ const smap = new Map(selectedInstances.map((i) => [i, true]));
+ const newData = data.map((item) => ({
+ ...item,
+ selected: false,
+ }));
+ const dmap = new Map(newData.map((i) => [i.instancePath, true]));
+
+ smap.forEach((value, key) => {
+ const item = dmap.get(key);
+ if (!item) {
+ newData.push({
+ instancePath: key,
+ color: undefined,
+ selected: true,
+ });
+ }
+ });
+ const canvasData = newData.filter((item) => {
+ if ((item?.selected !== undefined && item?.selected === false) && item?.color === undefined) {
+ return false;
+ }
+ return true;
+ });
+ return canvasData;
+};
+
// reducer function
export default function reduceGeneral (state = GENERAL_DEFAULT_STATE, action) {
switch (action.type) {
@@ -76,6 +103,10 @@ export default function reduceGeneral (state = GENERAL_DEFAULT_STATE, action) {
case Actions.REMOVE_CANVAS_INSTANCES: {
return { ...state };
}
+ case Actions.SELECT_INSTANCE: {
+ const newData = applySelection(action.data.instance, action.data.selectedInstances);
+ return { ...state, instances: [...newData] };
+ }
default: {
return state;
}