zoukankan      html  css  js  c++  java
  • [OHIF-Viewers]医疗数字阅片-医学影像-事件总线管理器

    [OHIF-Viewers]医疗数字阅片-医学影像-事件总线管理器

    添加按钮》调用命令》注册回调函数

    App.js

    import React, { Component } from 'react';
    import { OidcProvider } from 'redux-oidc';
    import { I18nextProvider } from 'react-i18next';
    import PropTypes from 'prop-types';
    import { Provider } from 'react-redux';
    import { BrowserRouter as Router } from 'react-router-dom';
    import { hot } from 'react-hot-loader/root';
    
    import OHIFCornerstoneExtension from '@ohif/extension-cornerstone';
    
    import {
        SnackbarProvider,
        ModalProvider,
        DialogProvider,
        OHIFModal,
      ErrorBoundary
    } from '@ohif/ui';
    
    import {
        CommandsManager,
        ExtensionManager,
        ServicesManager,
        HotkeysManager,
        UINotificationService,
        UIModalService,
        UIDialogService,
        MeasurementService,
        utils,
        redux as reduxOHIF,
    } from '@ohif/core';
    
    import i18n from '@ohif/i18n';
    
    // TODO: This should not be here
    //import './config';
    import { setConfiguration } from './config';
    
    /** Utils */
    import {
        getUserManagerForOpenIdConnectClient,
        initWebWorkers,
    } from './utils/index.js';
    
    /** Extensions */
    import { GenericViewerCommands, MeasurementsPanel } from './appExtensions';
    
    /** Viewer */
    import OHIFStandaloneViewer from './OHIFStandaloneViewer';
    
    /** Store */
    import { getActiveContexts } from './store/layout/selectors.js';
    import store from './store';
    
    /** Contexts */
    import WhiteLabelingContext from './context/WhiteLabelingContext';
    import UserManagerContext from './context/UserManagerContext';
    import { AppProvider, useAppContext, CONTEXTS } from './context/AppContext';
    
    /** ~~~~~~~~~~~~~ Application Setup */
    const commandsManagerConfig = {
        getAppState: () => store.getState(),
        getActiveContexts: () => getActiveContexts(store.getState()),
    };
    
    /** Managers */
    const commandsManager = new CommandsManager(commandsManagerConfig);
    const servicesManager = new ServicesManager();
    const hotkeysManager = new HotkeysManager(commandsManager, servicesManager);
    let extensionManager;
    /** ~~~~~~~~~~~~~ End Application Setup */
    
    // TODO[react] Use a provider when the whole tree is React
    window.store = store;
    
    window.ohif = window.ohif || {};
    window.ohif.app = {
      commandsManager,
      hotkeysManager,
      servicesManager,
      extensionManager,
    };
    
    class App extends Component {
        static propTypes = {
            config: PropTypes.oneOfType([
                PropTypes.func,
                PropTypes.shape({
                    routerBasename: PropTypes.string.isRequired,
                    oidc: PropTypes.array,
            whiteLabeling: PropTypes.shape({
              createLogoComponentFn: PropTypes.func,
            }),
                    extensions: PropTypes.array,
                }),
            ]).isRequired,
            defaultExtensions: PropTypes.array,
        };
    
        static defaultProps = {
            config: {
          showStudyList: true,
                oidc: [],
                extensions: [],
            },
            defaultExtensions: [],
        };
    
        _appConfig;
        _userManager;
    
        constructor(props) {
            super(props);
    
            const { config, defaultExtensions } = props;
    
            const appDefaultConfig = {
          showStudyList: true,
                cornerstoneExtensionConfig: {},
                extensions: [],
                routerBasename: '/',
            };
    
            this._appConfig = {
                ...appDefaultConfig,
                ...(typeof config === 'function' ? config({ servicesManager }) : config),
            };
    
            const {
                servers,
                hotkeys: appConfigHotkeys,
                cornerstoneExtensionConfig,
                extensions,
                oidc,
            } = this._appConfig;
    
        setConfiguration(this._appConfig);
    
            this.initUserManager(oidc);
            _initServices([
                UINotificationService,
                UIModalService,
                UIDialogService,
                MeasurementService,
            ]);
            _initExtensions(
                [...defaultExtensions, ...extensions],
                cornerstoneExtensionConfig,
                this._appConfig
            );
    
            /*
             * Must run after extension commands are registered
             * if there is no hotkeys from localStorage set up from config.
             */
            _initHotkeys(appConfigHotkeys);
            _initServers(servers);
            initWebWorkers();
        }
    
        render() {
        const { whiteLabeling, routerBasename } = this._appConfig;
            const {
                UINotificationService,
                UIDialogService,
                UIModalService,
                MeasurementService,
            } = servicesManager.services;
    
            //拥有 _userManager 模块才会走,  这个要对接OIDC模块,就是开放身份认证系统
            if (this._userManager) {
                return (
            <ErrorBoundary context='App'>
              <Provider store={store}>
                <AppProvider config={this._appConfig}>
                  <I18nextProvider i18n={i18n}>
                    <OidcProvider store={store} userManager={this._userManager}>
                      <UserManagerContext.Provider value={this._userManager}>
                        <Router basename={routerBasename}>
                          <WhiteLabelingContext.Provider value={whiteLabeling}>
                            <SnackbarProvider service={UINotificationService}>
                              <DialogProvider service={UIDialogService}>
                                <ModalProvider
                                  modal={OHIFModal}
                                  service={UIModalService}
                                >
                                  <OHIFStandaloneViewer
                                    userManager={this._userManager}
                                  />
                                </ModalProvider>
                              </DialogProvider>
                            </SnackbarProvider>
                          </WhiteLabelingContext.Provider>
                        </Router>
                      </UserManagerContext.Provider>
                    </OidcProvider>
                  </I18nextProvider>
                </AppProvider>
              </Provider>
             </ErrorBoundary>
           
                );
            }
            console.log("hit 初始页面");
    
            return (
          <ErrorBoundary context='App'>
            <Provider store={store}>
              <AppProvider config={this._appConfig}>
                <I18nextProvider i18n={i18n}>
                  <Router basename={routerBasename}>
                    <WhiteLabelingContext.Provider value={whiteLabeling}>
                      <SnackbarProvider service={UINotificationService}>
                        <DialogProvider service={UIDialogService}>
                          <ModalProvider modal={OHIFModal} service={UIModalService}>
                            <OHIFStandaloneViewer />
                          </ModalProvider>
                        </DialogProvider>
                      </SnackbarProvider>
                    </WhiteLabelingContext.Provider>
                  </Router>
                </I18nextProvider>
              </AppProvider>
            </Provider>
             </ErrorBoundary>
         
            );
        }
    
        initUserManager(oidc) {
            if (oidc && !!oidc.length) {
                const firstOpenIdClient = this._appConfig.oidc[0];
    
                const { protocol, host } = window.location;
                const { routerBasename } = this._appConfig;
                const baseUri = `${protocol}//${host}${routerBasename}`;
    
                const redirect_uri = firstOpenIdClient.redirect_uri || '/callback';
                const silent_redirect_uri =
                    firstOpenIdClient.silent_redirect_uri || '/silent-refresh.html';
                const post_logout_redirect_uri =
                    firstOpenIdClient.post_logout_redirect_uri || '/';
    
                const openIdConnectConfiguration = Object.assign({}, firstOpenIdClient, {
                    redirect_uri: _makeAbsoluteIfNecessary(redirect_uri, baseUri),
                    silent_redirect_uri: _makeAbsoluteIfNecessary(
                        silent_redirect_uri,
                        baseUri
                    ),
                    post_logout_redirect_uri: _makeAbsoluteIfNecessary(
                        post_logout_redirect_uri,
                        baseUri
                    ),
                });
    
                this._userManager = getUserManagerForOpenIdConnectClient(
                    store,
                    openIdConnectConfiguration
                );
            }
        }
    }
    
    function _initServices(services) {
        servicesManager.registerServices(services);
    }
    
    /**
     * @param
     */
    function _initExtensions(extensions, cornerstoneExtensionConfig, appConfig) {
        extensionManager = new ExtensionManager({
            commandsManager,
            servicesManager,
            appConfig,
        api: {
          contexts: CONTEXTS,
          hooks: {
            useAppContext
          }
        }
        });
    
        const requiredExtensions = [
            GenericViewerCommands,
            [OHIFCornerstoneExtension, cornerstoneExtensionConfig],
            /* WARNING: MUST BE REGISTERED _AFTER_ OHIFCornerstoneExtension */
            MeasurementsPanel,
        ];
        const mergedExtensions = requiredExtensions.concat(extensions);
        extensionManager.registerExtensions(mergedExtensions);
    }
    
    /**
     *
     * @param {Object} appConfigHotkeys - Default hotkeys, as defined by app config
     */
    function _initHotkeys(appConfigHotkeys) {
        // TODO: Use something more resilient
        // TODO: Mozilla has a special library for this
        const userPreferredHotkeys = JSON.parse(
            localStorage.getItem('hotkey-definitions') || '{}'
        );
    
        // TODO: hotkeysManager.isValidDefinitionObject(/* */)
        const hasUserPreferences =
            userPreferredHotkeys && Object.keys(userPreferredHotkeys).length > 0;
        if (hasUserPreferences) {
            hotkeysManager.setHotkeys(userPreferredHotkeys);
        } else {
            hotkeysManager.setHotkeys(appConfigHotkeys);
        }
    
        hotkeysManager.setDefaultHotKeys(appConfigHotkeys);
    }
    
    function _initServers(servers) {
        if (servers) {
            utils.addServers(servers, store);
        }
    }
    
    function _isAbsoluteUrl(url) {
        return url.includes('http://') || url.includes('https://');
    }
    
    function _makeAbsoluteIfNecessary(url, base_url) {
        if (_isAbsoluteUrl(url)) {
            return url;
        }
    
        /*
         * Make sure base_url and url are not duplicating slashes.
         */
        if (base_url[base_url.length - 1] === '/') {
            base_url = base_url.slice(0, base_url.length - 1);
        }
    
        return base_url + url;
    }
    
    /*
     * Only wrap/use hot if in dev.
     */
    const ExportedApp = process.env.NODE_ENV === 'development' ? hot(App) : App;
    
    export default ExportedApp;
    export { commandsManager, extensionManager, hotkeysManager, servicesManager };

    其中这里是进行定义管理器

    /** Managers */
    const commandsManager = new CommandsManager(commandsManagerConfig);
    const servicesManager = new ServicesManager();
    const hotkeysManager = new HotkeysManager(commandsManager, servicesManager);
    let extensionManager;
    /** ~~~~~~~~~~~~~ End Application Setup */
    
    // TODO[react] Use a provider when the whole tree is React
    window.store = store;
    
    window.ohif = window.ohif || {};
    window.ohif.app = {
      commandsManager,
      hotkeysManager,
      servicesManager,
      extensionManager,
    };

    commandsManager管理整个系统的命令 和 回调函数, 现有的头部所有按钮命令都是通过commandsManager分发的;

    CommandsManager.js定义

    import log from '../log.js';
    
    /**
     * The definition of a command
     *
     * @typedef {Object} CommandDefinition
     * @property {Function} commandFn - Command to call
     * @property {Array} storeContexts - Array of string of modules required from store
     * @property {Object} options - Object of params to pass action
     */
    
    /**
     * The Commands Manager tracks named commands (or functions) that are scoped to
     * a context. When we attempt to run a command with a given name, we look for it
     * in our active contexts. If found, we run the command, passing in any application
     * or call specific data specified in the command's definition.
     *
     * NOTE: A more robust version of the CommandsManager lives in v1. If you're looking
     * to extend this class, please check it's source before adding new methods.
     */
    export class CommandsManager {
      constructor({ getAppState, getActiveContexts } = {}) {
        this.contexts = {};
    
        if (!getAppState || !getActiveContexts) {
          log.warn(
            'CommandsManager was instantiated without getAppState() or getActiveContexts()'
          );
        }
    
        this._getAppState = getAppState;
        this._getActiveContexts = getActiveContexts;
      }
    
      /**
       * Allows us to create commands "per context". An example would be the "Cornerstone"
       * context having a `SaveImage` command, and the "VTK" context having a `SaveImage`
       * command. The distinction of a context allows us to call the command in either
       * context, and have faith that the correct command will be run.
       *
       * @method
       * @param {string} contextName - Namespace for commands
       * @returns {undefined}
       */
      createContext(contextName) {
        if (!contextName) {
          return;
        }
    
        if (this.contexts[contextName]) {
          return this.clearContext(contextName);
        }
    
        this.contexts[contextName] = {};
      }
    
      /**
       * Returns all command definitions for a given context
       *
       * @method
       * @param {string} contextName - Namespace for commands
       * @returns {Object} - the matched context
       */
      getContext(contextName) {
        const context = this.contexts[contextName];
    
        if (!context) {
          return;
        }
    
        return context;
      }
    
      /**
       * Clears all registered commands for a given context.
       *
       * @param {string} contextName - Namespace for commands
       * @returns {undefined}
       */
      clearContext(contextName) {
        if (!contextName) {
          return;
        }
    
        this.contexts[contextName] = {};
      }
    
      /**
       * Register a new command with the command manager. Scoped to a context, and
       * with a definition to assist command callers w/ providing the necessary params
       *
       * @method
       * @param {string} contextName - Namespace for command; often scoped to the extension that added it
       * @param {string} commandName - Unique name identifying the command
       * @param {CommandDefinition} definition - {@link CommandDefinition}
       */
      registerCommand(contextName, commandName, definition) {
        if (typeof definition !== 'object') {
          return;
        }
    
        const context = this.getContext(contextName);
        if (!context) {
          return;
        }
    
        context[commandName] = definition;
      }
    
      /**
       * Finds a command with the provided name if it exists in the specified context,
       * or a currently active context.
       *
       * @method
       * @param {String} commandName - Command to find
       * @param {String} [contextName] - Specific command to look in. Defaults to current activeContexts
       */
      getCommand(commandName, contextName) {
        let contexts = [];
        if (contextName) {
          const context = this.getContext(contextName);
          if (context) {
            contexts.push(context);
          }
        } else {
          const activeContexts = this._getActiveContexts();
          activeContexts.forEach(activeContext => {
            const context = this.getContext(activeContext);
            if (context) {
              contexts.push(context);
            }
          });
        }
    
        if (contexts.length === 0) {
          return;
        }
    
        let foundCommand;
        contexts.forEach(context => {
          if (context[commandName]) {
            foundCommand = context[commandName];
          }
        });
    
        return foundCommand;
      }
    
      /**
       *
       * @method
       * @param {String} commandName
       * @param {Object} [options={}] - Extra options to pass the command. Like a mousedown event
       * @param {String} [contextName]
       */
        runCommand(commandName, options = {}, contextName) {
    
        const definition = this.getCommand(commandName, contextName);
        if (!definition) {
          log.warn(`Command "${commandName}" not found in current context`);
          return;
        }
    
        const { commandFn, storeContexts = [] } = definition;
        const definitionOptions = definition.options;
    
        let commandParams = {};
        const appState = this._getAppState();
        storeContexts.forEach(context => {
          commandParams[context] = appState[context];
        });
    
        commandParams = Object.assign(
          {},
          commandParams, // Required store contexts
          definitionOptions, // "Command configuration"
          options // "Time of call" info
        );
    
        if (typeof commandFn !== 'function') {
          log.warn(`No commandFn was defined for command "${commandName}"`);
          return;
        } else {
          return commandFn(commandParams);
        }
      }
    }
    
    export default CommandsManager;

    toolbarModule.js 这里添加按钮结构

    // TODO: A way to add Icons that don't already exist?
    // - Register them and add
    // - Include SVG Source/Inline?
    // - By URL, or own component?
    
    // What KINDS of toolbar buttons do we have...
    // - One's that dispatch commands
    // - One's that set tool's active
    // - More custom, like CINE
    //    - Built in for one's like this, or custom components?
    
    // Visible?
    // Disabled?
    // Based on contexts or misc. criteria?
    //  -- ACTIVE_ROUTE::VIEWER
    //  -- ACTIVE_VIEWPORT::CORNERSTONE
    // setToolActive commands should receive the button event that triggered
    // so we can do the "bind to this button" magic
    
    const TOOLBAR_BUTTON_TYPES = {
        COMMAND: 'command',
        SET_TOOL_ACTIVE: 'setToolActive',
        BUILT_IN: 'builtIn',
    };
    
    const TOOLBAR_BUTTON_BEHAVIORS = {
        CINE: 'CINE',
        DOWNLOAD_SCREEN_SHOT: 'DOWNLOAD_SCREEN_SHOT',
    };
    
    /* TODO: Export enums through a extension manager. */
    const enums = {
        TOOLBAR_BUTTON_TYPES,
        TOOLBAR_BUTTON_BEHAVIORS,
    };
    
    const definitions = [
        {
            id: 'StackScroll',
            label: 'Stack Scroll',
            icon: 'bars',
            //
            type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
            commandName: 'setToolActive',
            commandOptions: { toolName: 'StackScroll' },
        },
        {
            id: 'Zoom',
            label: 'Zoom',
            icon: 'search-plus',
            //
            type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
            commandName: 'setToolActive',
            commandOptions: { toolName: 'Zoom' },
        },
        {
            id: 'Wwwc',
            label: 'Levels',
            icon: 'level',
            //
            type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
            commandName: 'setToolActive',
            commandOptions: { toolName: 'Wwwc' },
        },
        {
            id: 'Pan',
            label: 'Pan',
            icon: 'arrows',
            //
            type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
            commandName: 'setToolActive',
            commandOptions: { toolName: 'Pan' },
        },
        {
            id: 'Length',
            label: 'Length',
            icon: 'measure-temp',
            //
            type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
            commandName: 'setToolActive',
            commandOptions: { toolName: 'Length' },
        },
        {
            id: 'ArrowAnnotate',
            label: 'Annotate',
            icon: 'measure-non-target',
            //
            type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
            commandName: 'setToolActive',
            commandOptions: { toolName: 'ArrowAnnotate' },
        },
        {
            id: 'Angle',
            label: 'Angle',
            icon: 'angle-left',
            //
            type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
            commandName: 'setToolActive',
            commandOptions: { toolName: 'Angle' },
        },
        {
            id: 'Reset',
            label: 'Reset',
            icon: 'reset',
            //
            type: TOOLBAR_BUTTON_TYPES.COMMAND,
            commandName: 'resetViewport',
        },
        {
            id: 'KeyFrame',
            label: '关键帧',
            icon: 'star',
            //
            type: TOOLBAR_BUTTON_TYPES.BUILT_IN,
            options: {
                behavior: "SetKeyFrame",
            },
        },
        {
            id: 'Cine',
            label: 'CINE',
            icon: 'youtube',
            //
            type: TOOLBAR_BUTTON_TYPES.BUILT_IN,
            options: {
                behavior: TOOLBAR_BUTTON_BEHAVIORS.CINE,
            },
        },
        {
            id: 'More',
            label: 'More',
            icon: 'ellipse-circle',
            buttons: [
                {
                    id: 'Magnify',
                    label: 'Magnify',
                    icon: 'circle',
                    //
                    type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
                    commandName: 'setToolActive',
                    commandOptions: { toolName: 'Magnify' },
                },
                {
                    id: 'WwwcRegion',
                    label: 'ROI Window',
                    icon: 'stop',
                    //
                    type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
                    commandName: 'setToolActive',
                    commandOptions: { toolName: 'WwwcRegion' },
                },
                {
                    id: 'DragProbe',
                    label: 'Probe',
                    icon: 'dot-circle',
                    //
                    type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
                    commandName: 'setToolActive',
                    commandOptions: { toolName: 'DragProbe' },
                },
                {
                    id: 'EllipticalRoi',
                    label: 'Ellipse',
                    icon: 'circle-o',
                    //
                    type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
                    commandName: 'setToolActive',
                    commandOptions: { toolName: 'EllipticalRoi' },
                },
                {
                    id: 'RectangleRoi',
                    label: 'Rectangle',
                    icon: 'square-o',
                    //
                    type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
                    commandName: 'setToolActive',
                    commandOptions: { toolName: 'RectangleRoi' },
                },
                {
                    id: 'Invert',
                    label: 'Invert',
                    icon: 'adjust',
                    //
                    type: TOOLBAR_BUTTON_TYPES.COMMAND,
                    commandName: 'invertViewport',
                },
                {
                    id: 'RotateRight',
                    label: 'Rotate Right',
                    icon: 'rotate-right',
                    //
                    type: TOOLBAR_BUTTON_TYPES.COMMAND,
                    commandName: 'rotateViewportCW',
                },
                {
                    id: 'FlipH',
                    label: 'Flip H',
                    icon: 'ellipse-h',
                    //
                    type: TOOLBAR_BUTTON_TYPES.COMMAND,
                    commandName: 'flipViewportHorizontal',
                },
                {
                    id: 'FlipV',
                    label: 'Flip V',
                    icon: 'ellipse-v',
                    //
                    type: TOOLBAR_BUTTON_TYPES.COMMAND,
                    commandName: 'flipViewportVertical',
                },
                {
                    id: 'Clear',
                    label: 'Clear',
                    icon: 'trash',
                    //
                    type: TOOLBAR_BUTTON_TYPES.COMMAND,
                    commandName: 'clearAnnotations',
                },
                {
                    id: 'Bidirectional',
                    label: 'Bidirectional',
                    icon: 'measure-target',
                    //
                    type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
                    commandName: 'setToolActive',
                    commandOptions: { toolName: 'Bidirectional' },
                },
                {
                    id: 'Download',
                    label: 'Download',
                    icon: 'create-screen-capture',
                    //
                    type: TOOLBAR_BUTTON_TYPES.BUILT_IN,
                    options: {
                        behavior: TOOLBAR_BUTTON_BEHAVIORS.DOWNLOAD_SCREEN_SHOT,
                        togglable: true,
                    },
                },
            ],
        },
        {
            id: 'Exit2DMPR',
            label: 'Exit 2D MPR',
            icon: 'times',
            //
            type: TOOLBAR_BUTTON_TYPES.COMMAND,
            commandName: 'setCornerstoneLayout',
            context: 'ACTIVE_VIEWPORT::VTK',
        },
    ];
    
    export default {
        definitions,
        defaultContext: 'ACTIVE_VIEWPORT::CORNERSTONE',
    };

    ToolbarRow.js中调用命令

    import React, { Component } from 'react';
    import PropTypes from 'prop-types';
    import { withTranslation } from 'react-i18next';
    
    import { MODULE_TYPES } from '@ohif/core';
    import {
        ExpandableToolMenu,
        RoundedButtonGroup,
        ToolbarButton,
        withModal,
        withDialog,
    } from '@ohif/ui';
    
    import './ToolbarRow.css';
    import { commandsManager, extensionManager } from './../App.js';
    
    import ConnectedCineDialog from './ConnectedCineDialog';
    import ConnectedLayoutButton from './ConnectedLayoutButton';
    import { withAppContext } from '../context/AppContext';
    
    class ToolbarRow extends Component {
        // TODO: Simplify these? isOpen can be computed if we say "any" value for selected,
        // closed if selected is null/undefined
        static propTypes = {
            isLeftSidePanelOpen: PropTypes.bool.isRequired,
            isRightSidePanelOpen: PropTypes.bool.isRequired,
            selectedLeftSidePanel: PropTypes.string.isRequired,
            selectedRightSidePanel: PropTypes.string.isRequired,
            handleSidePanelChange: PropTypes.func.isRequired,
            activeContexts: PropTypes.arrayOf(PropTypes.string).isRequired,
            studies: PropTypes.array,
            t: PropTypes.func.isRequired,
            // NOTE: withDialog, withModal HOCs
            dialog: PropTypes.any,
            modal: PropTypes.any,
        };
    
        static defaultProps = {
            studies: [],
        };
    
        constructor(props) {
            super(props);
    
            const toolbarButtonDefinitions = _getVisibleToolbarButtons.call(this);
            // TODO:
            // If it's a tool that can be active... Mark it as active?
            // - Tools that are on/off?
            // - Tools that can be bound to multiple buttons?
    
            // Normal ToolbarButtons...
            // Just how high do we need to hoist this state?
            // Why ToolbarRow instead of just Toolbar? Do we have any others?
            this.state = {
                toolbarButtons: toolbarButtonDefinitions,
                activeButtons: [],
            };
    
            this.seriesPerStudyCount = [];
    
            this._handleBuiltIn = _handleBuiltIn.bind(this);
    
            this.updateButtonGroups();
        }
    
        updateButtonGroups() {
            //fanyinote panelModules.module.menuOptions.label �Dz˵�ʵ�ʵ� ���԰�
            const panelModules = extensionManager.modules[MODULE_TYPES.PANEL];
    
            this.buttonGroups = {
                left: [],
                right: [],
            };
    
            // ~ FIND MENU OPTIONS
            panelModules.forEach(panelExtension => {
                const panelModule = panelExtension.module;
                const defaultContexts = Array.from(panelModule.defaultContext);
    
                panelModule.menuOptions.forEach(menuOption => {
                    const contexts = Array.from(menuOption.context || defaultContexts);
                    const hasActiveContext = this.props.activeContexts.some(actx =>
                        contexts.includes(actx)
                    );
    
                    // It's a bit beefy to pass studies; probably only need to be reactive on `studyInstanceUIDs` and activeViewport?
                    // Note: This does not cleanly handle `studies` prop updating with panel open
                    const isDisabled =
                        typeof menuOption.isDisabled === 'function' &&
                        menuOption.isDisabled(this.props.studies);
    
                    if (hasActiveContext && !isDisabled) {
                        //�����ݽṹ���� ��ť���� menuOptionEntry "{"value":"measurement-panel","icon":"list","bottomLabel":"Measurements"}"
                        //bottomLabel ������ ui�ϵľ���textֵ
                        const menuOptionEntry = {
                            value: menuOption.target,
                            icon: menuOption.icon,
                            bottomLabel: menuOption.label,
                        };
                        const from = menuOption.from || 'right';
    
                        this.buttonGroups[from].push(menuOptionEntry);
                    }
                });
            });
    
            // TODO: This should come from extensions, instead of being baked in
            this.buttonGroups.left.unshift({
                value: 'studies',
                icon: 'th-large',
                bottomLabel: this.props.t('Series'),
            });
        }
    
        componentDidUpdate(prevProps) {
            const activeContextsChanged =
                prevProps.activeContexts !== this.props.activeContexts;
    
            const prevStudies = prevProps.studies;
            const studies = this.props.studies;
            const seriesPerStudyCount = this.seriesPerStudyCount;
    
            let studiesUpdated = false;
    
            if (prevStudies.length !== studies.length) {
                studiesUpdated = true;
            } else {
                for (let i = 0; i < studies.length; i++) {
                    if (studies[i].series.length !== seriesPerStudyCount[i]) {
                        seriesPerStudyCount[i] = studies[i].series.length;
    
                        studiesUpdated = true;
                        break;
                    }
                }
            }
    
            if (studiesUpdated) {
                this.updateButtonGroups();
            }
    
            if (activeContextsChanged) {
                this.setState(
                    {
                        toolbarButtons: _getVisibleToolbarButtons.call(this),
                    },
                    this.closeCineDialogIfNotApplicable
                );
            }
        }
    
        closeCineDialogIfNotApplicable = () => {
            const { dialog } = this.props;
            let { dialogId, activeButtons, toolbarButtons } = this.state;
            if (dialogId) {
                const cineButtonPresent = toolbarButtons.find(
                    button => button.options && button.options.behavior === 'CINE'
                );
                if (!cineButtonPresent) {
                    dialog.dismiss({ id: dialogId });
                    activeButtons = activeButtons.filter(
                        button => button.options && button.options.behavior !== 'CINE'
                    );
                    this.setState({ dialogId: null, activeButtons });
                }
            }
        };
    
        render() {
            const buttonComponents = _getButtonComponents.call(
                this,
                this.state.toolbarButtons,
                this.state.activeButtons
            );
    
            const onPress = (side, value) => {
                this.props.handleSidePanelChange(side, value);
            };
            const onPressLeft = onPress.bind(this, 'left');
            const onPressRight = onPress.bind(this, 'right');
            {/* fanyinote ���±ߵ�<>��һ����д�ı�ǩ��Ŀ����Ϊ�˰���һ���ڲ�Ԫ��*/ }
            return (
                <>
                    <div className="ToolbarRow">
                        <div className="pull-left m-t-1 p-y-1" style={{ padding: '10px' }}>
                            <RoundedButtonGroup
                                options={this.buttonGroups.left}
                                value={this.props.selectedLeftSidePanel || ''}
                                onValueChanged={onPressLeft}
                            />
                        </div>
                        {buttonComponents}
                        <ConnectedLayoutButton />
                        {/*fanyinote �±���Viewer���������Ͻǵ� ����������ļ�¼ֵ�������¼ֵ��Ҫapi��֧�ֲ��ܱ���������Ĭ�Ͻ����DZ���js�洢���� ��������Գ��� */}
                        <div
                            className="pull-right m-t-1 rm-x-1"
                            style={{ marginLeft: 'auto' }}
                        >
                            {this.buttonGroups.right.length && (
                                <RoundedButtonGroup
                                    options={this.buttonGroups.right}
                                    value={this.props.selectedRightSidePanel || ''}
                                    onValueChanged={onPressRight}
                                />
                            )}
                        </div>
                    </div>
                </>
            );
        }
    }
    
    function _getCustomButtonComponent(button, activeButtons) {
        const CustomComponent = button.CustomComponent;
        const isValidComponent = typeof CustomComponent === 'function';
    
        // Check if its a valid customComponent. Later on an CustomToolbarComponent interface could be implemented.
        if (isValidComponent) {
            const parentContext = this;
            const activeButtonsIds = activeButtons.map(button => button.id);
            const isActive = activeButtonsIds.includes(button.id);
    
            return (
                <CustomComponent
                    parentContext={parentContext}
                    toolbarClickCallback={_handleToolbarButtonClick.bind(this)}
                    button={button}
                    key={button.id}
                    activeButtons={activeButtonsIds}
                    isActive={isActive}
                />
            );
        }
    }
    
    function _getExpandableButtonComponent(button, activeButtons) {
        // Iterate over button definitions and update `onClick` behavior
        let activeCommand;
        const childButtons = button.buttons.map(childButton => {
            childButton.onClick = _handleToolbarButtonClick.bind(this, childButton);
    
            if (activeButtons.map(button => button.id).indexOf(childButton.id) > -1) {
                activeCommand = childButton.id;
            }
    
            return childButton;
        });
    
        return (
            <ExpandableToolMenu
                key={button.id}
                label={button.label}
                icon={button.icon}
                buttons={childButtons}
                activeCommand={activeCommand}
            />
        );
    }
    
    function _getDefaultButtonComponent(button, activeButtons) {
        return (
            <ToolbarButton
                key={button.id}
                label={button.label}
                icon={button.icon}
                onClick={_handleToolbarButtonClick.bind(this, button)}
                isActive={activeButtons.map(button => button.id).includes(button.id)}
            />
        );
    }
    /**
     * Determine which extension buttons should be showing, if they're
     * active, and what their onClick behavior should be.
     */
    function _getButtonComponents(toolbarButtons, activeButtons) {
        const _this = this;
        return toolbarButtons.map(button => {
            const hasCustomComponent = button.CustomComponent;
            const hasNestedButtonDefinitions = button.buttons && button.buttons.length;
    
            if (hasCustomComponent) {
                return _getCustomButtonComponent.call(_this, button, activeButtons);
            }
    
            if (hasNestedButtonDefinitions) {
                return _getExpandableButtonComponent.call(_this, button, activeButtons);
            }
    
            return _getDefaultButtonComponent.call(_this, button, activeButtons);
        });
    }
    
    /**
     * TODO: DEPRECATE
     * This is used exclusively in `extensions/cornerstone/src`
     * We have better ways with new UI Services to trigger "builtin" behaviors
     *
     * A handy way for us to handle different button types. IE. firing commands for
     * buttons, or initiation built in behavior.
     *
     * @param {*} button
     * @param {*} evt
     * @param {*} props
     */
    function _handleToolbarButtonClick(button, evt, props) {
        const { activeButtons } = this.state;
        console.log("_handleToolbarButtonClick");
        console.log(button, evt, props);
        if (button.commandName) {
            const options = Object.assign({ evt }, button.commandOptions);
            commandsManager.runCommand(button.commandName, options);
        }
    
        // TODO: Use Types ENUM
        // TODO: We can update this to be a `getter` on the extension to query
        //       For the active tools after we apply our updates?
        if (button.type === 'setToolActive') {
            const toggables = activeButtons.filter(
                ({ options }) => options && !options.togglable
            );
            this.setState({ activeButtons: [...toggables, button] });
        } else if (button.type === 'builtIn') {
            this._handleBuiltIn(button);
        }
    }
    
    /**
     *
     */
    function _getVisibleToolbarButtons() {
        const toolbarModules = extensionManager.modules[MODULE_TYPES.TOOLBAR];
        const toolbarButtonDefinitions = [];
    
        toolbarModules.forEach(extension => {
            const { definitions, defaultContext } = extension.module;
            definitions.forEach(definition => {
                const context = definition.context || defaultContext;
    
                if (this.props.activeContexts.includes(context)) {
                    toolbarButtonDefinitions.push(definition);
                }
            });
        });
    
        return toolbarButtonDefinitions;
    }
    
    function _handleBuiltIn(button) {
        /* TODO: Keep cine button active until its unselected. */
        const { dialog, t } = this.props;
        const { dialogId } = this.state;
        const { id, options } = button;
    
        if (options.behavior === 'CINE') {
            if (dialogId) {
                dialog.dismiss({ id: dialogId });
                this.setState(state => ({
                    dialogId: null,
                    activeButtons: [
                        ...state.activeButtons.filter(button => button.id !== id),
                    ],
                }));
            } else {
                const spacing = 20;
                const { x, y } = document
                    .querySelector(`.ViewerMain`)
                    .getBoundingClientRect();
                const newDialogId = dialog.create({
                    content: ConnectedCineDialog,
                    defaultPosition: {
                        x: x + spacing || 0,
                        y: y + spacing || 0,
                    },
                });
                this.setState(state => ({
                    dialogId: newDialogId,
                    activeButtons: [...state.activeButtons, button],
                }));
            }
        }
    
        if (options.behavior === 'DOWNLOAD_SCREEN_SHOT') {
            commandsManager.runCommand('showDownloadViewportModal', {
                title: t('Download High Quality Image'),
            });
        }
    
        if (options.behavior === 'SetKeyFrame') {
            commandsManager.runCommand('SetKeyFrame', {
                testData:"test data",
            });
        }
    }
    
    export default withTranslation(['Common', 'ViewportDownloadForm'])(
        withModal(withDialog(withAppContext(ToolbarRow)))
    );

    HeyFrameTag.js 中回调命令

    import React from 'react';
    import keyFrameTagStyle from "./HeyFrameTag.css";
    class HeyFrameTag extends React.Component {
        constructor(props) {
            super(props);
            //this.state = { b: this.props.imageIdIndex }
            this.state = { isShowKeyImageTag: false, currentFrameInfo: null, keyFrameData: [] }
            //最近一次更新时间 少于30秒 那么返回 不更新 lastKeyFrameListUpdateDate
            this.lastKeyFrameListUpdateDate = null;
            this.registerCommand();
        }
        registerCommand() {
            //注册 设置关键帧 handerSetKeyFrameCommand 命令
            let contextName = 'ACTIVE_VIEWPORT::CORNERSTONE';
    
            window.ohif.app.commandsManager.registerCommand(contextName, 'SetKeyFrame', {
                commandFn: this.handerSetKeyFrameCommand.bind(this),
                storeContexts: ['viewers'],
                options: { passMeToCommandFn: ':wave:' },
            });
    
            window.ohif.app.commandsManager.registerCommand(contextName, 'BindKeyFrame', {
                commandFn: this.handerBindKeyFrameCommand.bind(this),
                storeContexts: ['viewers'],
                options: { passMeToCommandFn: ':wave:' },
            });
        }
        addKeyFrameToKeyFrameData() {
            var sopInstanceUID = this.state.currentFrameInfo.SOPInstanceUID;
            var keyFrameData = this.state.keyFrameData.slice();
            var indexOf = keyFrameData.indexOf(sopInstanceUID);
            if (indexOf == -1) {
                keyFrameData.push(sopInstanceUID);
            }
            this.setState({ keyFrameData });
            return keyFrameData;
        }
        removeKeyFrameToKeyFrameData() {
            var sopInstanceUID = this.state.currentFrameInfo.SOPInstanceUID;
            var keyFrameData = this.state.keyFrameData.slice();
            var indexOf = keyFrameData.indexOf(sopInstanceUID);
            if (indexOf >= -1) {
                keyFrameData.splice(indexOf, 1);
            }
            this.setState({ keyFrameData });
            return keyFrameData;
        }
        handerSetKeyFrameCommand(cmdParam) {
            var studyInstanceUID = this.state.currentFrameInfo.StudyInstanceUID;
            console.log("handerSetKeyFrameCommand param=", cmdParam, "this.state.isShowKeyImageTag=", this.state.isShowKeyImageTag, "currentFrameInfo=", this.state.currentFrameInfo);
            var isAddKeyFrameToKeyFrameData = !this.state.isShowKeyImageTag;
    
            var keyFrameData = []
            if (isAddKeyFrameToKeyFrameData) keyFrameData = this.addKeyFrameToKeyFrameData();
            else keyFrameData = this.removeKeyFrameToKeyFrameData();
    
            var server = window.config.servers.dicomWeb[0];
            var paramUpdateKeyFrame = { StudyInstanceUID: studyInstanceUID, KeyFrameData: keyFrameData };
            fetch(server.wadoRoot + '/Measurement/UpdateKeyFrame',
                {
                    mode: 'cors',
                    method: 'POST',
                    body: JSON.stringify(paramUpdateKeyFrame)
                })
                .then(res => res.json())
                .then(data => {
                    console.log(data);
                })
                .catch(e => console.log('错误:', e));
    
            this.setState({ isShowKeyImageTag: isAddKeyFrameToKeyFrameData });
        }
        handerBindKeyFrameCommand(cmdParam) {
            console.log("handerBindKeyFrameCommand param=", cmdParam, "this.state.isShowKeyImageTag=", this.state.isShowKeyImageTag);
            this.setState({ currentFrameInfo: cmdParam });
            this.getKeyFrameData(cmdParam);
        }
        getKeyFrameData(param) {
            let self = this;
            if (!param) return;
            //最近一次更新时间 少于30秒 那么返回 不更新 lastKeyFrameListUpdateDate
            var nowTime = new Date().getTime();
            if (this.lastKeyFrameListUpdateDate && (nowTime - this.lastKeyFrameListUpdateDate < 30 * 1000)) {
                console.log("不更新", this.lastKeyFrameListUpdateDate);
                var keyFrameData = this.state.keyFrameData;
                if (keyFrameData.indexOf(this.state.currentFrameInfo.SOPInstanceUID) > -1) {
                    this.setState({ isShowKeyImageTag: true, keyFrameData: keyFrameData });
                } else {
                    this.setState({ isShowKeyImageTag: false, keyFrameData: keyFrameData });
                }
                return;
            }
            this.lastKeyFrameListUpdateDate = nowTime;
    
            var server = window.config.servers.dicomWeb[0];
            var paramGetKeyFrameList = { StudyInstanceUID: param.StudyInstanceUID };
            fetch(server.wadoRoot + '/Measurement/KeyFrameList',
                {
                    mode: 'cors',
                    method: 'POST',
                    body: JSON.stringify(paramGetKeyFrameList)
                })
                .then(res => res.json())
                .then(data => {
                    console.log(data);
                    if (data.Data.KeyFrame && data.Data.KeyFrame.KeyFrameData) {
                        var keyFrameData = data.Data.KeyFrame.KeyFrameData;
                        if (keyFrameData.indexOf(self.state.currentFrameInfo.SOPInstanceUID) > -1) {
                            this.setState({ isShowKeyImageTag: true, keyFrameData: keyFrameData });
                        } else {
                            this.setState({ isShowKeyImageTag: false, keyFrameData: keyFrameData });
                        }
                    }
                })
                .catch(e => console.log('错误:', e));
        }
        componentWillMount() {
        }
        componentDidMount() {
        }
        render() {
            return (
                <span className={"keyFrameTagStyle"} style={{ display: this.state.isShowKeyImageTag ? "block" : "none" }}>
                    <svg t="1594088509903" className="icon" viewBox="0 0 1042 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1149" data-spm-anchor-id="a313x.7781069.0.i0" width="100%" height="100%"><path d="M759.12762 345.375135l-142.179449-21.081101-62.849217-127.997264a38.316227 38.316227 0 0 0-34.298752-21.22949 38.316227 38.316227 0 0 0-34.298752 21.22949l-64.384202 128.767188-141.030035 20.311177a38.321092 38.321092 0 0 0-21.076235 65.52875l102.322156 101.177608-24.141339 140.26011a38.308929 38.308929 0 0 0 15.32795 37.321284 38.307712 38.307712 0 0 0 40.237998 2.916714l126.852714-65.528751 126.08279 66.298675a38.316227 38.316227 0 0 0 40.237998-2.922795 38.313794 38.313794 0 0 0 15.326734-37.320068l-23.375063-141.025169 101.941451-99.258269a38.33812 38.33812 0 0 0 9.901987-39.34036 38.325957 38.325957 0 0 0-30.983088-26.193256l0.384354-1.914473z" fill="#1296db" p-id="1150" data-spm-anchor-id="a313x.7781069.0.i3" className=""></path><path d="M624.996501 455.361266a76.634886 76.634886 0 0 0-22.22565 68.983074l10.727863 64.382985-55.565948-30.278844a76.654347 76.654347 0 0 0-73.196375 0l-57.485287 30.278844 11.113433-63.619142a76.644616 76.644616 0 0 0-22.61122-69.362563l-49.436957-45.606794 63.999848-9.197743a76.639751 76.639751 0 0 0 60.549173-42.153688l28.745076-59.019055 28.738995 59.019055a76.649482 76.649482 0 0 0 59.020271 42.538042l63.998632 9.197743-46.371854 44.838086z" fill="#1296db" p-id="1151" data-spm-anchor-id="a313x.7781069.0.i6" className=""></path><path d="M902.838404 13.878108H136.376429A114.965708 114.965708 0 0 0 21.407072 128.847465v766.461974c0 63.495079 51.474278 114.969357 114.969357 114.969358h7.279621l375.954407-153.292882 375.948326 153.292882h7.279621a114.974222 114.974222 0 0 0 81.298222-33.671136 114.974222 114.974222 0 0 0 33.671135-81.298222V128.847465a114.969357 114.969357 0 0 0-33.671135-81.297006 114.970573 114.970573 0 0 0-81.298222-33.672351z m38.323525 881.431331c0.246911 18.985397-13.446317 35.286396-32.189669 38.323525L519.610457 776.124349 130.242573 933.632964c-18.738486-3.037128-32.431714-19.338127-32.188452-38.323525V128.847465c0-21.166243 17.156066-38.322308 38.322308-38.322309h766.461975c21.166243 0 38.323524 17.156066 38.323525 38.322309v766.461974z m0 0" fill="#1296db" p-id="1152" data-spm-anchor-id="a313x.7781069.0.i1"></path></svg>
                </span>
            )
        }
    
    }
    export default HeyFrameTag;

    其中这里是回调注注册,正常情况下不用在这里注册,此处注册纯属偷懒.

    正常注册位置在这里/Viewers/extensions/cornerstone/src/commandsModule.js

            window.ohif.app.commandsManager.registerCommand(contextName, 'SetKeyFrame', {
                commandFn: this.handerSetKeyFrameCommand.bind(this),
                storeContexts: ['viewers'],
                options: { passMeToCommandFn: ':wave:' },
            });
     
  • 相关阅读:
    JS函数
    JS数据类型
    JavaScript HTML DOM
    JavaScript-HTML
    HTML基础知识
    数据库--事务:级联删除(学生教师信息表)为例
    javascript window.open
    设置DIV半透明CSS代码:
    css实现你的网页图片半透明效果
    html中,如何打开index.html后可以自动打开另一个页面?
  • 原文地址:https://www.cnblogs.com/landv/p/13266957.html
Copyright © 2011-2022 走看看