org.eclipse.eavp.viz.visit.VisitPythonDialog.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.eavp.viz.visit.VisitPythonDialog.java

Source

/*******************************************************************************
 * Copyright (c) 2014 UT-Battelle, LLC.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Initial API and implementation and/or initial documentation - Jay Jay Billings,
 *   Jordan H. Deyton, Dasha Gorin, Alexander J. McCaskey, Taylor Patterson,
 *   Claire Saunders, Matthew Wang, Anna Wojtowicz
 *******************************************************************************/
package org.eclipse.eavp.viz.visit;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.List;

import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.Bullet;
import org.eclipse.swt.custom.LineStyleEvent;
import org.eclipse.swt.custom.LineStyleListener;
import org.eclipse.swt.custom.PaintObjectEvent;
import org.eclipse.swt.custom.PaintObjectListener;
import org.eclipse.swt.custom.ST;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.GlyphMetrics;
import org.eclipse.swt.graphics.TextLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import gov.lbnl.visit.swt.VisItSwtWidget;
import visit.java.client.AttributeSubject;
import visit.java.client.AttributeSubject.AttributeSubjectCallback;

/**
 * This class provides a dialog for executing Python scripts on the active VisIt
 * plot.
 * 
 * @author Taylor Patterson
 * 
 */
public class VisitPythonDialog extends TitleAreaDialog {

    /**
     * Logger for handling event messages and other information.
     */
    private static final Logger logger = LoggerFactory.getLogger(VisitPythonDialog.class);

    /**
     * The active VisItSwtWidget
     */
    private VisItSwtWidget widget;

    /**
     * The button to load and execute a Python script from a file.
     */
    private Button loadButton;

    /**
     * The button to execute the Python command in the text box.
     */
    private Button execButton;

    /**
     * The AttributeSubjectCallback used by the VisIt widget.
     */
    private AttributeSubjectCallback attSubCallback;

    /**
     * The StyledText area that will act as the console to accept keyboard input
     * and display output
     */
    private StyledText console;

    /**
     * The constructor
     * 
     * @param parentShell
     *            The parent Shell for this dialog
     * @param inWidget
     */
    public VisitPythonDialog(Shell parentShell, VisItSwtWidget inWidget) {
        super(parentShell);

        // Remove the help button
        super.setHelpAvailable(false);

        // Get the active VisIt widget
        widget = inWidget;
    }

    /**
     * Set the title and message to display near the top of this dialog window.
     */
    @Override
    public void create() {
        super.create();

        // Set the title and help message for this TitleAreaDialog
        setTitle("VisIt Python Interface");
        setMessage("Use this console to input VisIt Python commands", IMessageProvider.INFORMATION);

        return;
    }

    /**
     * Create the buttons in the button bar area. Override the parent function
     * to add the 'Execute' and 'Load from File' buttons and remove the 'Ok'
     * button.
     */
    @Override
    public void createButtonsForButtonBar(Composite parent) {

        // Create the button to load a script from a file
        loadButton = createButton(parent, IDialogConstants.CLIENT_ID + 1, "Load from File", false);

        // Create the button to close the dialog
        createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);

        // Create the button to execute the last line in the text area
        execButton = createButton(parent, IDialogConstants.CLIENT_ID + 2, "Execute", true);

        // Add selection listeners to these buttons
        addButtonListeners();

        return;
    }

    /**
     * Add listeners to the 'Execute' and 'Load from File' buttons. Add some
     * additional functionality to the 'Cancel' button.
     */
    private void addButtonListeners() {

        // Close the AttributeSubjectCallback when the 'Cancel' button in
        // pressed.
        getButton(IDialogConstants.CANCEL_ID).addSelectionListener(new SelectionListener() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                logger.info("unregistering...");
                widget.getViewerState().unregisterCallback("ClientMethod", attSubCallback);
            }

            @Override
            public void widgetDefaultSelected(SelectionEvent e) {
                // Not used
            }
        });

        // Implement the functionality for the 'Load from File' button. Present
        // a FileDialog to select a python script.
        loadButton.addSelectionListener(new SelectionListener() {

            @Override
            public void widgetSelected(SelectionEvent e) {

                // Create an SWT FileDialog with the option to select multiple
                // files.
                FileDialog dialog = new FileDialog(getShell(), SWT.OPEN | SWT.MULTI);

                // Create a filter to limit the displayed files to those with a
                // .py extension.
                String[] filterNames = new String[] { ".py Files" };
                String[] filterExtensions = new String[] { "*.py" };

                // Set the dialog's file filters.
                dialog.setFilterNames(filterNames);
                dialog.setFilterExtensions(filterExtensions);

                // Get the OS file separator character.
                String separator = System.getProperty("file.separator");

                // Set the default location.
                String filterPath = System.getProperty("user.home") + separator + "ICEFiles" + separator + "default"
                        + separator;
                dialog.setFilterPath(filterPath);

                // If a file was selected in the dialog, process it.
                if (dialog.open() != null) {

                    // Get the selected file(s)
                    String[] files = dialog.getFileNames();

                    if (files != null) {
                        // Loop through the selected files
                        for (String file : files) {
                            try {
                                // Get the full file path
                                String path = dialog.getFilterPath() + separator + file;
                                // Initialize the readers and buffers
                                FileReader fReader = new FileReader(path);
                                BufferedReader bReader = new BufferedReader(fReader);
                                StringBuffer strBuffer = new StringBuffer();
                                // Loop through the file (via the
                                // BufferedReader) to create a single String.
                                String line = bReader.readLine();
                                while (line != null) {
                                    strBuffer.append(line + "\n");
                                    line = bReader.readLine();
                                }

                                // Close the reader
                                bReader.close();
                                // Convert the StringBuffer to a String
                                String fullFileStr = strBuffer.toString().trim() + "\n";
                                // Format the String of commands
                                String text = ">>> " + fullFileStr;
                                text = text.replace("\n", "\n>>> ");
                                text = text.replace("\n>>>  ", "\n... ");
                                console.append(text.trim() + "\n");
                                // Call the VisIt widget method to process these
                                // commands
                                widget.getViewerMethods().processCommands(fullFileStr);
                            } catch (Exception e1) {
                                // Complain if needed
                                logger.error(getClass().getName() + " Exception!", e);
                            }
                        }
                    }
                }
            }

            @Override
            public void widgetDefaultSelected(SelectionEvent e) {
                // Not used
            }
        });

        // Implement the functionality of the 'Execute' button.
        execButton.addSelectionListener(new SelectionListener() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                // Call the method to execute the last command from the console
                executeCommand();
            }

            @Override
            public void widgetDefaultSelected(SelectionEvent e) {
                // Not used
            }
        });

        return;
    }

    /**
     * Create the dialog area above the button bar area. This will include the
     * text area to input VisIt Python commands and display previously entered
     * commands with their resulting output.
     */
    @Override
    protected Control createDialogArea(Composite parent) {

        // Get the Composite for this dialog
        Composite composite = (Composite) super.createDialogArea(parent);

        // Create the text area
        console = new StyledText(composite, SWT.MULTI | SWT.BORDER | SWT.WRAP | SWT.V_SCROLL);
        console.setLayoutData(new GridData(GridData.FILL_BOTH));

        // Reserve space for the '>>> ' at the start of input lines in the
        // console
        console.addLineStyleListener(new LineStyleListener() {

            @Override
            public void lineGetStyle(LineStyleEvent event) {
                // Get the line where '>>>' should be placed
                event.bulletIndex = console.getLineAtOffset(event.lineOffset);

                // Create the StyleRange to reserve space for the preceeding
                // '>>> '
                StyleRange styleRange = new StyleRange();
                styleRange.metrics = new GlyphMetrics(0, 0, ">>> ".length() * 12);

                // Create and set the reserved space
                event.bullet = new Bullet(ST.BULLET_CUSTOM, styleRange);
            }
        });

        // Actually generate the preceeding '>>> '
        console.addPaintObjectListener(new PaintObjectListener() {

            @Override
            public void paintObject(PaintObjectEvent event) {
                // Define the TextLayout parameters
                TextLayout textLayout = new TextLayout(event.display);
                textLayout.setAscent(event.ascent);
                textLayout.setDescent(event.descent);
                textLayout.setFont(event.style.font);
                // Create the String (">>> ") at the beginning of each input
                // line
                textLayout.setText(">>> ");
                // Draw the text
                textLayout.draw(event.gc, event.x, event.y);
                textLayout.dispose();
            }
        });

        // Keep track of the index of the top line displayed in the console
        console.addListener(SWT.Modify, new Listener() {

            @Override
            public void handleEvent(Event event) {
                console.setTopIndex(console.getLineCount() - 1);
            }
        });

        console.addListener(SWT.Traverse, new Listener() {

            @Override
            public void handleEvent(Event event) {
                if (event.detail == SWT.TRAVERSE_RETURN) {
                    executeCommand();
                }
            }
        });

        // Create the AttributeSubjectCallback
        attSubCallback = new AttributeSubjectCallback() {

            @Override
            public boolean update(AttributeSubject subject) {
                String name = subject.getAsString("methodName");

                if (!"AcceptRecordedMacro".equals(name)) {
                    return false;
                }

                final List<String> vec = subject.getAsStringVector("stringArgs");

                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < vec.size(); ++i) {
                            console.append(vec.get(i) + "\n");
                        }

                        // Move the cursor to the end of the console
                        console.setSelection(console.getText().length());
                    }
                });

                return true;
            }
        };

        // Register the AttributeSubjectCallback with the VisIt widget
        widget.getViewerState().registerCallback("ClientMethod", attSubCallback);

        return composite;
    }

    /**
     * Execute the most recent user input in the the {@link console}. Parse the
     * text in the console to grab the most recent command and send it off to
     * the VisIt client.
     */
    private void executeCommand() {

        // Get the last command from the console Text
        String command = console.getText().trim();
        int start = Math.max(0, command.lastIndexOf("\n"));
        command = command.substring(start);
        // Call the VisIt widget method to process the command(s)
        if (!command.isEmpty()) {
            widget.getViewerMethods().processCommands(command);
        }

        return;
    }

}