All Downloads are FREE. Search and download functionalities are using the official Maven repository.

lphystudio.app.graphicalmodelpanel.GraphicalModelPanel Maven / Gradle / Ivy

The newest version!
package lphystudio.app.graphicalmodelpanel;

import jebl.evolution.sequences.SequenceType;
import lphy.core.codebuilder.CanonicalCodeBuilder;
import lphy.core.logger.LoggerUtils;
import lphy.core.model.*;
import lphy.core.parser.LPhyParserDictionary;
import lphy.core.parser.graphicalmodel.GraphicalModel;
import lphy.core.parser.graphicalmodel.GraphicalModelListener;
import lphy.core.simulator.Sampler;
import lphy.core.simulator.SimulatorListener;
import lphy.core.vectorization.VectorizedFunction;
import lphystudio.app.alignmentcomponent.AlignmentComponent;
import lphystudio.app.alignmentcomponent.SequenceTypePanel;
import lphystudio.app.graphicalmodelcomponent.GraphicalModelComponent;
import lphystudio.app.graphicalmodelcomponent.interactive.InteractiveGraphicalModelComponent;
import lphystudio.app.treecomponent.TimeTreeComponent;
import lphystudio.app.treecomponent.TimeTreeExtraPlotComponent;
import lphystudio.core.codecolorizer.LineCodeColorizer;
import lphystudio.core.editor.UndoManagerHelper;
import lphystudio.core.layeredgraph.Layering;
import lphystudio.core.swing.TidyComboBox;
import lphystudio.core.swing.TidyTextField;
import lphystudio.core.valueeditor.Abstract2DEditor;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * The main panel to include prob graphical model,
 * views {@link ViewerPane}, and command line console {@link StudioConsoleInterpreter}.
 */
public class GraphicalModelPanel extends JPanel {

    GraphicalModelComponent component;
    StudioConsoleInterpreter modelInterpreter;
    StudioConsoleInterpreter dataInterpreter;

    // default interpreter for console is model tab
    final String DEFAULT_INTERPRETER = GraphicalModel.Context.model.toString();

    JTabbedPane leftPane;

    ViewerPane rightPane;

    JToolBar toolbar;

    JLabel repsLabel = new JLabel("reps:");
    JTextField repsField = new TidyTextField("1", 4);
    JButton sampleButton = new JButton("Sample");
    JCheckBox showConstantNodes = new JCheckBox("Show constants");
    JComboBox layeringAlgorithm = new TidyComboBox<>(new Layering[]{
            new Layering.LongestPathFromSinks(), new Layering.LongestPathFromSources()
    });

    //TODO https://github.com/LinguaPhylo/linguaPhylo/issues/307
    //    JCheckBox editValues = new JCheckBox("Edit values");

    JSplitPane horizSplitPane;
    JSplitPane verticalSplitPane;

    Object displayedElement;

//    Sampler sampler;

    CanonicalCodeBuilder codeBuilder = new CanonicalCodeBuilder();


    public GraphicalModelPanel(GraphicalModelParserDictionary parser, UndoManagerHelper undoManagerHelper) {

        dataInterpreter = new StudioConsoleInterpreter(parser, LPhyParserDictionary.Context.data, undoManagerHelper);
        modelInterpreter = new StudioConsoleInterpreter(parser, LPhyParserDictionary.Context.model, undoManagerHelper);

        component = new GraphicalModelComponent(parser);

        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));

        layeringAlgorithm.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                component.setLayering((Layering) layeringAlgorithm.getSelectedItem());
            }
        });
        layeringAlgorithm.setPreferredSize(new Dimension(200, 20));

        buttonPanel.add(repsLabel);
        buttonPanel.add(repsField);
        buttonPanel.add(sampleButton);
        buttonPanel.add(new JLabel(" Layering:"));
        buttonPanel.add(layeringAlgorithm);
        buttonPanel.add(showConstantNodes);
//        buttonPanel.add(editValues);

        sampleButton.addActionListener(e -> sample(getReps()));

        showConstantNodes.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                component.setShowConstantNodes(showConstantNodes.isSelected());
//                editValues.setEnabled(showConstantNodes.isSelected());
            }
        });
        showConstantNodes.setSelected(component.getShowConstantNodes());

//        editValues.addActionListener(new AbstractAction() {
//            @Override
//            public void actionPerformed(ActionEvent e) {
//                // avoid GraphicalModelComponent depends on GraphicalModelPanel
//                component.setEditValues(editValues.isSelected());
//                // update currentSelectionContainer
//                // take care of selectedNode here, and the rest will update by showObject(...)
//                LayeredNode selectedNode = component.getSelectedNode();
//                if (selectedNode instanceof LayeredGNode lnode) {
//                    if (lnode.value() instanceof Value value) {
//                        showValue(value, false);
//                    }
//                }
//                component.repaint();
//            }
//        });
//        editValues.setSelected(component.getEditValues());
//        editValues.setToolTipText("Please click Sample button to refresh variables after setting a new value.");

        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
        panel.add(component);
        panel.add(buttonPanel);

        setLayout(new BorderLayout());


        GraphicalModelListener listener = new GraphicalModelListener() {
            @Override
            public void valueSelected(Value value) {
                showValue(value);
            }

            @Override
            public void generativeDistributionSelected(GenerativeDistribution g) {
                showParameterized(g);
            }

            @Override
            public void functionSelected(DeterministicFunction f) {

                showParameterized(f);
            }

            @Override
            public void layout() {

            }

        };

        component.addGraphicalModelListener(listener);
        parser.addGraphicalModelChangeListener(component);

        //TODO need a new way to deal with this model listener interaction

        //        parser.addGraphicalModelListener(new GraphicalModelListener() {
//            @Override
//            public void valueSelected(Value value) {
//
//                showValue(value);
//            }
//
//            @Override
//            public void generativeDistributionSelected(GenerativeDistribution g) {
//
//            }
//
//            @Override
//            public void functionSelected(DeterministicFunction f) {
//
//            }
//        });

        rightPane = new ViewerPane(parser, component);
        //rightPane.addTab("New Random Variable", new NewRandomVariablePanel(modelInterpreter, ParserUtils.getGenerativeDistributions()));

        leftPane = new JTabbedPane();
        leftPane.addTab("AutoLayout", panel);
        leftPane.addTab("Interactive", new InteractiveGraphicalModelComponent(parser, component));

        horizSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPane, rightPane);
        horizSplitPane.setResizeWeight(0.5);
//        horizSplitPane.setOneTouchExpandable(true);
        horizSplitPane.setContinuousLayout(true);

        JTabbedPane interpreterPane = new JTabbedPane();
        interpreterPane.add("data", dataInterpreter);
        interpreterPane.add("model", modelInterpreter);

        // set default model or model interpreter
        if (DEFAULT_INTERPRETER.equals(GraphicalModel.Context.model.toString())) {
            interpreterPane.setSelectedComponent(modelInterpreter);
        } else {
            interpreterPane.setSelectedComponent(dataInterpreter);
        }

        verticalSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, horizSplitPane, interpreterPane);
        verticalSplitPane.setResizeWeight(0.75);
//        verticalSplitPane.setOneTouchExpandable(true);
        verticalSplitPane.setContinuousLayout(true);
        add(verticalSplitPane, BorderLayout.CENTER);

        if (parser.getModelSinks().size() > 0) {
            showValue(parser.getModelSinks().iterator().next());
        }
    }

//    public Sampler getSampler() {
//        return this.sampler;
//    }

    /**
     * This is duplicated to {@link LPhyParserDictionary#source(BufferedReader, String[])},
     * but has extra code using {@link LineCodeColorizer}
     * and panel function.
     *
     * @param reader
     * @throws IOException
     */
    public void source(BufferedReader reader) throws IOException {

        LPhyParserDictionary metaData = component.getParserDictionary();
        metaData.source(reader, null);

        repaint();
    }

    private String consumeForLoop(String firstLine, BufferedReader reader) throws IOException {
        StringBuilder builder = new StringBuilder(firstLine);
        String line = reader.readLine();
        while (!line.trim().startsWith("}")) {
            builder.append(line);
            line = reader.readLine();
        }
        builder.append(line);
        return builder.toString();
    }

    private int getReps() {
        int reps = 1;
        try {
            reps = Integer.parseInt(repsField.getText());
        } catch (NumberFormatException nfe) {
            repsField.setText("1");
            reps = 1;
        }
        return reps;
    }

    // Key is the replicate index, value is the result of each replicate.
    Map> valuesAllRepsMap;

    public Map> getValuesAllRepsMap() {
        return valuesAllRepsMap;
    }

    public void sample(int reps) {
        sample(reps, new LinkedList<>());
    }

    public void sample(int reps, List loggers) {

        long start = System.currentTimeMillis();

        String id = null;
        if (displayedElement instanceof Value && !((Value) displayedElement).isAnonymous()) {
            id = ((Value) displayedElement).getId();
        }

        // add Loggers here, to trigger after click Sample button
        loggers.addAll(rightPane.getGUISimulatorListener());

        // These sync the consoles with GraphicalModelComponent containing the lphy code
        // the code may be changed by GUI, such as squared rectangles.
        dataInterpreter.clear();
        modelInterpreter.clear();
        // refresh data and model lines
        String text = codeBuilder.getCode(component.getParserDictionary());
        dataInterpreter.interpretInput(codeBuilder.getDataLines(), LPhyParserDictionary.Context.data);
        modelInterpreter.interpretInput(codeBuilder.getModelLines(), LPhyParserDictionary.Context.model);

        // Sample using the lphy code in component.getParser(), and output results to loggers
        Sampler sampler = new Sampler(component.getParserDictionary());
        // if null then use a random seed
        valuesAllRepsMap = sampler.sampleAll(reps, loggers, null);
//        this.sampler = sampler;

        if (id != null) {
            Value selectedValue = component.getParserDictionary().getValue(id, LPhyParserDictionary.Context.model);
            if (selectedValue != null) {
                showValue(selectedValue, false);
            }
        } else {
            List> sinks = component.getParserDictionary().getModelSinks();
            // TODO should not move to tab for all sinks?
//            if (sinks.size() > 0) showValue(sinks.get(0), false);
            for (Value value : sinks) {
                if (value instanceof RandomVariable) {
                    showValue(value, false);
                }
            }
        }
        long end = System.currentTimeMillis();
        LoggerUtils.log.info("sample(" + reps + ") took " + (end - start) + " ms.");

        rightPane.variableSummary.repaint();
        rightPane.repaint();
    }

    void showValue(Value value) {
        showValue(value, true);
    }

    void showValue(Value value, boolean moveToTab) {
        if (value != null) {
            String type = value.value().getClass().getSimpleName();
            String label = value.getLabel();

            showObject(type + " " + label, value, moveToTab);
        }
    }

    private void showParameterized(Generator g) {
        showObject(g.codeString(), g, true);
    }

    // TODO It looks ViewerRegister not store ValueEditor (JTextField),
    // so everytime viewer.getViewer(object) creates a new ValueEditor.
    private void showObject(String label, Object obj, boolean moveToTab) {
        displayedElement = obj;

        JComponent viewer = null;
        if (obj instanceof Value) {
            viewer = ViewerRegister.getJComponentForValue(obj);

            if (viewer instanceof TimeTreeComponent timeTreeComponent) {
//            if (timeTreeComponent.getTimeTree().isUltrametric()) {
                viewer = createTimeTreeSplitPane(timeTreeComponent);
//            }
            } else if (viewer instanceof AlignmentComponent alignmentComponent) {
                SequenceType sequenceType = alignmentComponent.getAlignment().getSequenceType();
                SequenceTypePanel sequenceTypePanel = new SequenceTypePanel(sequenceType);
                JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, alignmentComponent, sequenceTypePanel);
                splitPane.setResizeWeight(1.0);
                splitPane.setOneTouchExpandable(true);
                splitPane.setContinuousLayout(true);
                splitPane.setBorder(null);
                splitPane.setBackground(Color.white);
                final double ratio = 0.95;
//                if (SequenceTypePanel.isShowLegends())
                    splitPane.setDividerLocation(ratio);
//                else
//                    splitPane.setDividerLocation(1.0);

                splitPane.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent e) {
                        if (e.getButton() == MouseEvent.BUTTON1)
                            AlignmentComponent.setShowTreeInAlignmentViewerIfAvailable(
                                    !AlignmentComponent.getShowTreeInAlignmentViewerIfAvailable());
                        else if (e.getButton() == MouseEvent.BUTTON3) {
                            SequenceTypePanel.setShowLegends(!SequenceTypePanel.isShowLegends());
                            if (SequenceTypePanel.isShowLegends())
                                splitPane.setDividerLocation(ratio);
                            else
                                splitPane.setDividerLocation(1.0);
                        } else {
                            AlignmentComponent.showErrorsIfAvailable = !AlignmentComponent.showErrorsIfAvailable;
                        }
                        splitPane.repaint();
                    }
                });

                viewer = splitPane;
            }

        } else if (obj instanceof VectorizedFunction) {
            viewer = new JLabel(((VectorizedFunction) obj).getComponentFunction(0).getRichDescription(0));
        } else if (obj instanceof Generator) {
            viewer = new JLabel(((Generator) obj).getRichDescription(0));
        } else {
            LoggerUtils.log.severe("Trying to show an object that is neither Value nor Generator.");
        }
//        if (viewer instanceof JTextField textField)
//            textField.setEditable(editValues.isSelected());

        if (viewer instanceof JTextField || viewer instanceof JLabel || viewer instanceof Abstract2DEditor) {
            JPanel viewerPanel = new JPanel();
            viewerPanel.setOpaque(false);
            viewerPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
            viewerPanel.add(viewer);
            viewer = viewerPanel;
        }

        rightPane.currentSelectionContainer.setViewportView(viewer);
        rightPane.currentSelectionContainer.setBorder(
                BorderFactory.createTitledBorder(
                        BorderFactory.createMatteBorder(0, 0, 0, 0, viewer.getBackground()),
                        "" + label + ""));

        if (moveToTab) rightPane.setSelectedComponent(rightPane.currentSelectionContainer);
        rightPane.repaint();
        repaint();
    }

    private JSplitPane createTimeTreeSplitPane(TimeTreeComponent timeTreeComponent) {
        TimeTreeExtraPlotComponent plotComponent = new TimeTreeExtraPlotComponent(timeTreeComponent);
        TimeTreeExtraPlotPanel timeTreePlotPanel = new TimeTreeExtraPlotPanel(plotComponent);

        JSplitPane treeSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, timeTreeComponent, timeTreePlotPanel);
        treeSplitPane.setResizeWeight(0.5);
        treeSplitPane.setOneTouchExpandable(true);
        treeSplitPane.setContinuousLayout(true);
        treeSplitPane.setBorder(null);
        treeSplitPane.setBackground(Color.white);
//        if (TimeTreeExtraPlotComponent.isShowExtraPlot())
//            treeSplitPane.setDividerLocation(0.5);
//        else
//            treeSplitPane.setDividerLocation(1.0);

        //TODO not working
//        if (TimeTreeExtraPlotComponent.isShowExtraPlot())
//            treeSplitPane.setDividerLocation(0.5);
//        else
//            treeSplitPane.setDividerLocation(1.0);
        //TODO not working
//        treeSplitPane.addPropertyChangeListener("dividerLocation", evt -> {
//            //  Get the new divider location of the split pane
//            int location = (Integer) evt.getNewValue();
//            // getMaximumDividerLocation() NOT return the maximum position of the divider bar when maximized using the arrow
//            TimeTreeExtraPlotComponent.setShowExtraPlot(location < treeSplitPane.getMaximumDividerLocation());
//        });

        treeSplitPane.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getButton() == MouseEvent.BUTTON3) {
                    TimeTreeExtraPlotComponent.setShowExtraPlot(!TimeTreeExtraPlotComponent.isShowExtraPlot());
                    if (TimeTreeExtraPlotComponent.isShowExtraPlot())
                        treeSplitPane.setDividerLocation(0.5);
                    else
                        treeSplitPane.setDividerLocation(1.0);
                }
                treeSplitPane.repaint();
            }
        });
        return treeSplitPane;
    }

    // IO should be in one place
    // use LinguaPhyloStudio readFile(File exampleFile)
    @Deprecated
    public void readScript(File scriptFile) {
        Path path = Paths.get(scriptFile.getAbsolutePath());
        try {
            String mimeType = Files.probeContentType(path);
            // bug: NullPointerException: Cannot invoke "String.equals(Object)"
            if (mimeType.equals("text/plain")) {
                // TODO need to find another way to do this
                //parser.clear();
                dataInterpreter.clear();
                modelInterpreter.clear();

                FileReader reader = new FileReader(scriptFile);
                BufferedReader bufferedReader = new BufferedReader(reader);
                String line = bufferedReader.readLine();
                while (line != null) {
                    dataInterpreter.clear();
                    modelInterpreter.clear();
                    line = bufferedReader.readLine();
                }
                bufferedReader.close();
                reader.close();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public GraphicalModelParserDictionary getParserDictionary() {
        return component.getParserDictionary();
    }

    public ViewerPane getRightPane() {
        return rightPane;
    }

    public JTextPane getCanonicalModelPane() {
        return rightPane.canonicalModelPanel.pane;
    }

    public GraphicalModelComponent getComponent() {
        return component;
    }

    /**
     * clear panel, parser, and interpreters
     */
    public void clear() {
        dataInterpreter.clear();
        modelInterpreter.clear();
        component.clear();
        rightPane.clear();
    }

    public JToolBar getToolbar() {
        return toolbar;
    }

    public void setToolbar(JToolBar toolbar) {
        this.toolbar = toolbar;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy