From 7f856d69ba683d32ebc1e7f624384fa419874156 Mon Sep 17 00:00:00 2001 From: maltheism Date: Tue, 16 Feb 2021 18:17:08 -0500 Subject: [PATCH] update --- package.json | 1 + public/electron.js | 1 + python/libs/iEEG.py | 2 +- src/App.js | 75 +++++++++++++++- src/context.js | 6 ++ src/css/App.css | 19 ++++ src/css/Menu.css | 0 src/css/SplashScreen.css | 27 ++++++ src/jsx/Converter.js | 136 +++++++++++++++++++++++++++++ src/jsx/SplashScreen.js | 30 +++++++ src/jsx/Welcome.js | 116 ++---------------------- src/jsx/elements/authentication.js | 24 +++++ src/jsx/elements/menu.js | 103 ++++++++++++++++++++++ 13 files changed, 426 insertions(+), 114 deletions(-) create mode 100644 src/context.js create mode 100644 src/css/Menu.css create mode 100644 src/css/SplashScreen.css create mode 100644 src/jsx/Converter.js create mode 100644 src/jsx/SplashScreen.js create mode 100644 src/jsx/elements/authentication.js create mode 100644 src/jsx/elements/menu.js diff --git a/package.json b/package.json index fa7a7a8..375eb77 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "version": "1.0.0", "dependencies": { "electron-log": "^4.3.1", + "electron-store": "^7.0.2", "prop-types": "^15.7.2", "react": "^17.0.1", "react-dom": "^17.0.1", diff --git a/public/electron.js b/public/electron.js index c73e287..e049a28 100644 --- a/public/electron.js +++ b/public/electron.js @@ -57,6 +57,7 @@ const createWindow = () => { height: 600, minWidth: 900, minHeight: 600, + backgroundColor: '#0A826E', }); // mainWindow.maximize(); mainWindow.show(); diff --git a/python/libs/iEEG.py b/python/libs/iEEG.py index 7d1b7a4..9371e86 100644 --- a/python/libs/iEEG.py +++ b/python/libs/iEEG.py @@ -7,7 +7,7 @@ class Converter: # data: { - # file_path: '', // whee file located. + # file_path: '', // where file located. # bids_directory: '', // where to output. # read_only: true/false // read without write or write. def __init__(self, data): diff --git a/src/App.js b/src/App.js index 8c45925..a1c5e81 100644 --- a/src/App.js +++ b/src/App.js @@ -1,4 +1,5 @@ -import React from 'react'; +import React, {useEffect, useState} from 'react'; +import {AppContext} from './context'; import './css/App.css'; // Socket.io @@ -10,15 +11,85 @@ const options = { // Components import Welcome from './jsx/Welcome'; +import SplashScreen from './jsx/SplashScreen'; +import Converter from './jsx/Converter'; +import Menu from './jsx/elements/menu'; +// import {Authentication} from './jsx/elements/authentication'; /** * App - the starting point. * @return {JSX.Element} */ const App = () => { + const [appMode, setAppMode] = useState('SplashScreen'); + const [activeMenuTab, setActiveMenuTab] = useState(0); + + /** + * Similar to componentDidMount and componentDidUpdate. + */ + useEffect(() => { + setTimeout( + () => { + setAppMode('Welcome'); + }, 1500); + }, []); + return ( - + { + setAppMode(appMode); + }, + }}> + <> + { + e.preventDefault(); + setActiveMenuTab(0); + setAppMode('Welcome'); + }, + }, + { + title: 'Anonymize data', + onClick: (e) => { + e.preventDefault(); + setActiveMenuTab(1); + setAppMode('Converter'); + }, + }, + { + title: 'iEEG Converter', + onClick: (e) => { + e.preventDefault(); + setActiveMenuTab(2); + setAppMode('Converter'); + }, + }, + { + title: 'Validator', + onClick: (e) => { + e.preventDefault(); + setActiveMenuTab(3); + setAppMode('Converter'); + }, + }, + ]} + activeTab={activeMenuTab} + /> +
+ +
+
+ +
+
+ +
+ + ); }; diff --git a/src/context.js b/src/context.js new file mode 100644 index 0000000..0689019 --- /dev/null +++ b/src/context.js @@ -0,0 +1,6 @@ +import React from 'react'; + +/** + * AppContext used as global state and init in App.js. + */ +export const AppContext = React.createContext({}); diff --git a/src/css/App.css b/src/css/App.css index e69de29..b38b5a2 100644 --- a/src/css/App.css +++ b/src/css/App.css @@ -0,0 +1,19 @@ +html { + height: 100%; + -webkit-app-region: drag; + color: rgb(255, 255, 255); + background-color: rgb(10, 130, 110); +} + +body { + margin: 0; + -webkit-app-region: drag; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; +} + +.hidden { + display: none; +} diff --git a/src/css/Menu.css b/src/css/Menu.css new file mode 100644 index 0000000..e69de29 diff --git a/src/css/SplashScreen.css b/src/css/SplashScreen.css new file mode 100644 index 0000000..440a88c --- /dev/null +++ b/src/css/SplashScreen.css @@ -0,0 +1,27 @@ +.loader { + border: 8px solid #ffffff; + border-top: 8px solid #073e34; + border-left: 8px solid #073e34; + border-radius: 50%; + animation: spin 0.8s linear infinite; +} +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} +.centered { + top: 50%; + left: 50%; + position: fixed; + margin-top: -30px; + margin-left: -45px; +} +.loader-font { + top: 50%; + left: 50%; + color: #ffffff; + font-size: 24px; + position: fixed; + margin: -120px 0 0 -110px; + font-family: "Bangla MN", serif; +} diff --git a/src/jsx/Converter.js b/src/jsx/Converter.js new file mode 100644 index 0000000..9bec2b7 --- /dev/null +++ b/src/jsx/Converter.js @@ -0,0 +1,136 @@ +import React, {useContext, useState} from 'react'; + +// Socket.io +import {Event, SocketContext} from './socket.io'; + +// Components +import {DirectoryInput, FileInput, TextInput} from './elements/inputs'; + +/** + * Converter - the iEEG to BIDS Converter component. + * @param {object} props + * @return {JSX.Element} + */ +const Converter = (props) => { + // React Context + const socketContext = useContext(SocketContext); + + console.log('Converter rnedered'); + + // React State + const [edfFile, setEdfFile] = useState({}); + const [bidsDirectory, setBidsDirectory] = useState(null); + const [siteID, setSiteID] = useState(''); + + const fireBidsConverter = () => { + socketContext.emit('ieeg_to_bids', { + file_path: edfFile.path, + bids_directory: bidsDirectory, + read_only: false, + }); + }; + + const fireModifyBidsTsv = () => { + socketContext.emit('modify_bids_tsv', { + bids_directory: bidsDirectory, + site_id: siteID, + }); + }; + + const onMessage = (message) => { + console.log(message); + }; + + const onUserInput = async (name, value) => { + if (name === 'edfFile') { + await setEdfFile(value); + } else if (name === 'bidsDirectory') { + await setBidsDirectory(value); + } else if (name === 'siteID') { + await setSiteID(value); + } + }; + + return ( + <> +
+ iEEG to BIDS Converter +
+
+
+ +
+ +
+ + 3. Convert file.edf to BIDS format: + + +
+
+
+ Finalize participants.tsv for LORIS +
+
+ +
+
+ + 5. Modify participants.tsv data: + + +
+ + + ); +}; + +export default Converter; diff --git a/src/jsx/SplashScreen.js b/src/jsx/SplashScreen.js new file mode 100644 index 0000000..34607ca --- /dev/null +++ b/src/jsx/SplashScreen.js @@ -0,0 +1,30 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import '../css/SplashScreen.css'; + +/** + * Display the splash screen animation. + * @param {object} props + * @return {JSX.Element} - Loader React component + */ +const SplashScreen = (props) => { + return ( + <> +

+ PyCat is loading ... +

+
+ + ); +}; +SplashScreen.propTypes = { + size: PropTypes.string, +}; +SplashScreen.defaultProps = { + size: '60', +}; + +export default SplashScreen; diff --git a/src/jsx/Welcome.js b/src/jsx/Welcome.js index 310fd81..fc2c9b1 100644 --- a/src/jsx/Welcome.js +++ b/src/jsx/Welcome.js @@ -1,10 +1,4 @@ -import React, {useContext, useState} from 'react'; - -// Socket.io -import {Event, SocketContext} from './socket.io'; - -// Components -import {DirectoryInput, FileInput, TextInput} from './elements/inputs'; +import React from 'react'; /** * Welcome - the welcome component. @@ -12,43 +6,6 @@ import {DirectoryInput, FileInput, TextInput} from './elements/inputs'; * @return {JSX.Element} */ const Welcome = (props) => { - // React Context - const socketContext = useContext(SocketContext); - - // React State - const [edfFile, setEdfFile] = useState({}); - const [bidsDirectory, setBidsDirectory] = useState(null); - const [siteID, setSiteID] = useState(''); - - const fireBidsConverter = () => { - socketContext.emit('ieeg_to_bids', { - file_path: edfFile.path, - bids_directory: bidsDirectory, - read_only: false, - }); - }; - - const fireModifyBidsTsv = () => { - socketContext.emit('modify_bids_tsv', { - bids_directory: bidsDirectory, - site_id: siteID, - }); - }; - - const onMessage = (message) => { - console.log(message); - }; - - const onUserInput = async (name, value) => { - if (name === 'edfFile') { - await setEdfFile(value); - } else if (name === 'bidsDirectory') { - await setBidsDirectory(value); - } else if (name === 'siteID') { - await setSiteID(value); - } - }; - return ( <>
{ cursor: 'default', padding: '20px', }}> - iEEG to BIDS Converter -
-
-
- -
- -
- - 3. Convert file.edf to BIDS format: - - -
-
-
- Finalize participants.tsv for LORIS -
-
- + Welcome to pyCat!
-
- - 5. Modify participants.tsv data: - - +
+

Hello, you may begin your task by following the menu above. + Please remember to backup your data!

- ); }; diff --git a/src/jsx/elements/authentication.js b/src/jsx/elements/authentication.js new file mode 100644 index 0000000..fce6657 --- /dev/null +++ b/src/jsx/elements/authentication.js @@ -0,0 +1,24 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +export const Authentication = (props) => { + const handleClick = () => { + // Send current file to parent component + }; + return ( + <> + + + ); +}; +Authentication.propTypes = { + onUserInput: PropTypes.func, +}; + +export default { + Authentication, +}; diff --git a/src/jsx/elements/menu.js b/src/jsx/elements/menu.js new file mode 100644 index 0000000..6c2944f --- /dev/null +++ b/src/jsx/elements/menu.js @@ -0,0 +1,103 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import '../../css/Menu.css'; + +/** + * MenuTab - the menu tab component. + * @param {object} props + * @return {JSX.Element} + */ +const MenuTab = (props) => { + const styles = { + step: { + width: props.width, + padding: '0 0 2px 0', + position: 'relative', + display: 'table-cell', + WebkitUserSelect: 'none', + userSelect: 'none', + }, + title: { + default: { + fontSize: 16, + color: 'black', + display: 'block', + fontWeight: '300', + cursor: 'pointer', + margin: '8px 0 0 0', + textAlign: 'center', + }, + active: { + color: 'white', + }, + }, + }; + const styleTitleText = { + ...styles.title.default, + ...(props.active ? + styles.title.active : + {}), + }; + return ( +
+
+ {props.title} +
+
+ ); +}; +MenuTab.propTypes = { + id: PropTypes.string, + title: PropTypes.string, + onClick: PropTypes.func, + active: PropTypes.bool, +}; + +/** + * Menu - the menu component. + * @param {object} props + * @return {JSX.Element} + */ +const Menu = (props) => { + const styles = { + root: { + padding: 0, + minHeight: 0, + width: '100%', + }, + menu: { + width: '100%', + margin: '0 auto', + display: 'table', + }, + }; + return props.visible ? ( +
+
+ { props.tabs.map((tab, index) => ( + + ))} +
+
+ ) : null; +}; +Menu.defaultProps = { + activeTab: 0, +}; +Menu.propTypes = { + visible: PropTypes.bool, + tabs: PropTypes.array, + activeTab: PropTypes.number, +}; + +export default Menu;