weka.classifiers.bayes.net.GUI Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of weka-stable Show documentation
Show all versions of weka-stable Show documentation
The Waikato Environment for Knowledge Analysis (WEKA), a machine
learning workbench. This is the stable version. Apart from bugfixes, this version
does not receive any other updates.
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/*
* GUI.java
* Copyright (C) 2007-2012 University of Waikato, Hamilton, New Zealand
*
*/
package weka.classifiers.bayes.net;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.beans.PropertyEditor;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.table.AbstractTableModel;
import weka.classifiers.bayes.net.MarginCalculator.JunctionTreeNode;
import weka.core.Instances;
import weka.core.OptionHandler;
import weka.core.SerializedObject;
import weka.core.Utils;
import weka.core.converters.AbstractFileLoader;
import weka.core.converters.AbstractFileSaver;
import weka.core.converters.ArffSaver;
import weka.core.converters.ConverterUtils;
import weka.gui.ConverterFileChooser;
import weka.gui.ExtensionFileFilter;
import weka.gui.GenericObjectEditor;
import weka.gui.LookAndFeel;
import weka.gui.PropertyDialog;
import weka.gui.graphvisualizer.BIFFormatException;
import weka.gui.graphvisualizer.BIFParser;
import weka.gui.graphvisualizer.GraphEdge;
import weka.gui.graphvisualizer.GraphNode;
import weka.gui.graphvisualizer.HierarchicalBCEngine;
import weka.gui.graphvisualizer.LayoutCompleteEvent;
import weka.gui.graphvisualizer.LayoutCompleteEventListener;
import weka.gui.graphvisualizer.LayoutEngine;
import weka.gui.visualize.PrintablePanel;
/**
* GUI interface to Bayesian Networks. Allows editing Bayesian networks on
* screen and provides GUI interface to various Bayesian network facilities in
* Weka, including random network generation, data set generation and Bayesion
* network inference.
*
* @author Remco Bouckaert ([email protected])
* @version $Revision: 10153 $
*/
public class GUI extends JPanel implements LayoutCompleteEventListener {
/** for serialization */
private static final long serialVersionUID = -2038911085935515624L;
/** The current LayoutEngine */
protected LayoutEngine m_layoutEngine;
/** Panel actually displaying the graph */
protected GraphPanel m_GraphPanel;
/** Container of Bayesian network */
EditableBayesNet m_BayesNet = new EditableBayesNet(true);
/** String containing file name storing current network */
protected String m_sFileName = "";
/** used for calculating marginals in Bayesian netwowrks */
MarginCalculator m_marginCalculator = null;
/**
* used for calculating marginals in Bayesian netwowrks when evidence is
* present
*/
MarginCalculator m_marginCalculatorWithEvidence = null;
/**
* flag indicating whether marginal distributions of each of the nodes should
* be shown in display.
*/
boolean m_bViewMargins = false;
boolean m_bViewCliques = false;
/** The menu bar */
private JMenuBar m_menuBar;
/** data selected from file. Used to train a Bayesian network on */
Instances m_Instances = null;
/** Text field for specifying zoom */
final JTextField m_jTfZoom;
/** toolbar containing buttons at top of window */
final JToolBar m_jTbTools;
/** status bar at bottom of window */
final JLabel m_jStatusBar;
/** TextField for node's width */
private final JTextField m_jTfNodeWidth = new JTextField(3);
/** TextField for nodes height */
private final JTextField m_jTfNodeHeight = new JTextField(3);
/** this contains the m_GraphPanel GraphPanel */
JScrollPane m_jScrollPane;
/** path for icons */
private final String ICONPATH = "weka/classifiers/bayes/net/icons/";
/** current zoom value */
private double m_fScale = 1;
/** standard width of node */
private int m_nNodeHeight = 2 * getFontMetrics(getFont()).getHeight();
/** standard height of node */
final static int DEFAULT_NODE_WIDTH = 50;
private int m_nNodeWidth = DEFAULT_NODE_WIDTH;
/** width of node, allowing for some padding */
final static int PADDING = 10;
private int m_nPaddedNodeWidth = DEFAULT_NODE_WIDTH + PADDING;
/** used when using zoomIn and zoomOut buttons */
private final int[] m_nZoomPercents = { 10, 25, 50, 75, 100, 125, 150, 175,
200, 225, 250, 275, 300, 350, 400, 450, 500, 550, 600, 650, 700, 800, 900,
999 };
/** actions triggered by GUI events */
Action a_new = new ActionNew();
Action a_quit = new ActionQuit();
Action a_save = new ActionSave();
ActionExport a_export = new ActionExport();
ActionPrint a_print = new ActionPrint();
Action a_load = new ActionLoad();
Action a_zoomin = new ActionZoomIn();
Action a_zoomout = new ActionZoomOut();
Action a_layout = new ActionLayout();
Action a_saveas = new ActionSaveAs();
Action a_viewtoolbar = new ActionViewToolbar();
Action a_viewstatusbar = new ActionViewStatusbar();
Action a_networkgenerator = new ActionGenerateNetwork();
Action a_datagenerator = new ActionGenerateData();
Action a_datasetter = new ActionSetData();
Action a_learn = new ActionLearn();
Action a_learnCPT = new ActionLearnCPT();
Action a_help = new ActionHelp();
Action a_about = new ActionAbout();
ActionAddNode a_addnode = new ActionAddNode();
Action a_delnode = new ActionDeleteNode();
Action a_cutnode = new ActionCutNode();
Action a_copynode = new ActionCopyNode();
Action a_pastenode = new ActionPasteNode();
Action a_selectall = new ActionSelectAll();
Action a_addarc = new ActionAddArc();
Action a_delarc = new ActionDeleteArc();
Action a_undo = new ActionUndo();
Action a_redo = new ActionRedo();
Action a_alignleft = new ActionAlignLeft();
Action a_alignright = new ActionAlignRight();
Action a_aligntop = new ActionAlignTop();
Action a_alignbottom = new ActionAlignBottom();
Action a_centerhorizontal = new ActionCenterHorizontal();
Action a_centervertical = new ActionCenterVertical();
Action a_spacehorizontal = new ActionSpaceHorizontal();
Action a_spacevertical = new ActionSpaceVertical();
/** node currently selected through right clicking */
int m_nCurrentNode = -1;
/** selection of nodes */
Selection m_Selection = new Selection();
/** selection rectangle drawn through dragging with left mouse button */
Rectangle m_nSelectedRect = null;
class Selection {
ArrayList m_selected;
public Selection() {
m_selected = new ArrayList();
} // c'tor
public ArrayList getSelected() {
return m_selected;
}
void updateGUI() {
if (m_selected.size() > 0) {
a_cutnode.setEnabled(true);
a_copynode.setEnabled(true);
} else {
a_cutnode.setEnabled(false);
a_copynode.setEnabled(false);
}
if (m_selected.size() > 1) {
a_alignleft.setEnabled(true);
a_alignright.setEnabled(true);
a_aligntop.setEnabled(true);
a_alignbottom.setEnabled(true);
a_centerhorizontal.setEnabled(true);
a_centervertical.setEnabled(true);
a_spacehorizontal.setEnabled(true);
a_spacevertical.setEnabled(true);
} else {
a_alignleft.setEnabled(false);
a_alignright.setEnabled(false);
a_aligntop.setEnabled(false);
a_alignbottom.setEnabled(false);
a_centerhorizontal.setEnabled(false);
a_centervertical.setEnabled(false);
a_spacehorizontal.setEnabled(false);
a_spacevertical.setEnabled(false);
}
} // updateGUI
public void addToSelection(int nNode) {
for (int iNode = 0; iNode < m_selected.size(); iNode++) {
if (nNode == m_selected.get(iNode)) {
return;
}
}
m_selected.add(nNode);
updateGUI();
} // addToSelection
public void addToSelection(int[] iNodes) {
for (int iNode2 : iNodes) {
addToSelection(iNode2);
}
updateGUI();
} // addToSelection
public void addToSelection(Rectangle selectedRect) {
for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
if (contains(selectedRect, iNode)) {
addToSelection(iNode);
}
}
} // addToSelection
public void selectAll() {
m_selected.clear();
for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
m_selected.add(iNode);
}
updateGUI();
} // selectAll
boolean contains(Rectangle rect, int iNode) {
return rect.intersects((m_BayesNet.getPositionX(iNode)) * m_fScale,
(m_BayesNet.getPositionY(iNode)) * m_fScale, m_nPaddedNodeWidth
* m_fScale, m_nNodeHeight * m_fScale);
} // contains
public void removeFromSelection(int nNode) {
for (int iNode = 0; iNode < m_selected.size(); iNode++) {
if (nNode == m_selected.get(iNode)) {
m_selected.remove(iNode);
}
}
updateGUI();
} // removeFromSelection
public void toggleSelection(int nNode) {
for (int iNode = 0; iNode < m_selected.size(); iNode++) {
if (nNode == m_selected.get(iNode)) {
m_selected.remove(iNode);
updateGUI();
return;
}
}
addToSelection(nNode);
} // toggleSelection
public void toggleSelection(Rectangle selectedRect) {
for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
if (contains(selectedRect, iNode)) {
toggleSelection(iNode);
}
}
} // toggleSelection
public void clear() {
m_selected.clear();
updateGUI();
}
public void draw(Graphics g) {
if (m_selected.size() == 0) {
return;
}
for (int iNode = 0; iNode < m_selected.size(); iNode++) {
int nNode = m_selected.get(iNode);
int nPosX = m_BayesNet.getPositionX(nNode);
int nPosY = m_BayesNet.getPositionY(nNode);
g.setColor(Color.BLACK);
int nXRC = nPosX + m_nPaddedNodeWidth - m_nNodeWidth
- (m_nPaddedNodeWidth - m_nNodeWidth) / 2;
int nYRC = nPosY;
int d = 5;
g.fillRect(nXRC, nYRC, d, d);
g.fillRect(nXRC, nYRC + m_nNodeHeight, d, d);
g.fillRect(nXRC + m_nNodeWidth, nYRC, d, d);
g.fillRect(nXRC + m_nNodeWidth, nYRC + m_nNodeHeight, d, d);
}
} // draw
} // Selection
ClipBoard m_clipboard = new ClipBoard();
class ClipBoard {
String m_sText = null;
public ClipBoard() {
if (a_pastenode != null) {
a_pastenode.setEnabled(false);
}
}
public boolean hasText() {
return m_sText != null;
}
public String getText() {
return m_sText;
}
public void setText(String sText) {
m_sText = sText;
a_pastenode.setEnabled(true);
}
} // class ClipBoard
/**
* Base class used for definining actions with a name, tool tip text, possibly
* an icon and accelerator key.
* */
class MyAction extends AbstractAction {
/** for serialization */
private static final long serialVersionUID = -2038911111935517L;
public MyAction(String sName, String sToolTipText, String sIcon,
String sAcceleratorKey) {
super(sName);
// setToolTipText(sToolTipText);
putValue(Action.SHORT_DESCRIPTION, sToolTipText);
putValue(Action.LONG_DESCRIPTION, sToolTipText);
if (sAcceleratorKey.length() > 0) {
KeyStroke keyStroke = KeyStroke.getKeyStroke(sAcceleratorKey);
putValue(Action.ACCELERATOR_KEY, keyStroke);
}
putValue(Action.MNEMONIC_KEY, (int) sName.charAt(0));
java.net.URL tempURL = ClassLoader.getSystemResource(ICONPATH + sIcon
+ ".png");
if (tempURL != null) {
putValue(Action.SMALL_ICON, new ImageIcon(tempURL));
} else {
putValue(Action.SMALL_ICON, new ImageIcon(new BufferedImage(20, 20,
BufferedImage.TYPE_4BYTE_ABGR)));
// System.err.println(ICONPATH + sIcon +
// ".png not found for weka.gui.graphvisualizer.Graph");
}
} // c'tor
/*
* Place holder. Should be implemented by derived classes. (non-Javadoc)
*
* @see
* java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
@Override
public void actionPerformed(ActionEvent ae) {
}
} // class MyAction
class ActionGenerateNetwork extends MyAction {
/** for serialization */
private static final long serialVersionUID = -2038911085935517L;
public ActionGenerateNetwork() {
super("Generate Network", "Generate Random Bayesian Network",
"generate.network", "ctrl N");
} // c'tor
int m_nNrOfNodes = 10;
int m_nNrOfArcs = 15;
int m_nCardinality = 2;
int m_nSeed = 123;
JDialog dlg = null;
@Override
public void actionPerformed(ActionEvent ae) {
if (dlg == null) {
dlg = new JDialog();
dlg.setTitle("Generate Random Bayesian Network Options");
final JLabel jLbNrOfNodes = new JLabel("Nr of nodes");
final JTextField jTfNrOfNodes = new JTextField(3);
jTfNrOfNodes.setHorizontalAlignment(JTextField.CENTER);
jTfNrOfNodes.setText("" + m_nNrOfNodes);
final JLabel jLbNrOfArcs = new JLabel("Nr of arcs");
final JTextField jTfNrOfArcs = new JTextField(3);
jTfNrOfArcs.setHorizontalAlignment(JTextField.CENTER);
jTfNrOfArcs.setText("" + m_nNrOfArcs);
final JLabel jLbCardinality = new JLabel("Cardinality");
final JTextField jTfCardinality = new JTextField(3);
jTfCardinality.setHorizontalAlignment(JTextField.CENTER);
jTfCardinality.setText("" + m_nCardinality);
final JLabel jLbSeed = new JLabel("Random seed");
final JTextField jTfSeed = new JTextField(3);
jTfSeed.setHorizontalAlignment(JTextField.CENTER);
jTfSeed.setText("" + m_nSeed);
JButton jBtGo;
jBtGo = new JButton("Generate Network");
jBtGo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
try {
BayesNetGenerator generator = new BayesNetGenerator();
m_BayesNet = generator;
m_BayesNet.clearUndoStack();
String[] options = new String[8];
options[0] = "-N";
options[1] = "" + jTfNrOfNodes.getText();
options[2] = "-A";
options[3] = "" + jTfNrOfArcs.getText();
options[4] = "-C";
options[5] = "" + jTfCardinality.getText();
options[6] = "-S";
options[7] = "" + jTfSeed.getText();
generator.setOptions(options);
generator.generateRandomNetwork();
// Convert to EditableBayesNet
// This ensures the getOptions() called by GenericObjectEditor to
// get the correct result.
BIFReader bifReader = new BIFReader();
bifReader.processString(m_BayesNet.toXMLBIF03());
m_BayesNet = new EditableBayesNet(bifReader);
updateStatus();
layoutGraph();
a_datagenerator.setEnabled(true);
m_Instances = null;
;
a_learn.setEnabled(false);
a_learnCPT.setEnabled(false);
dlg.setVisible(false);
} catch (Exception e) {
e.printStackTrace();
}
}
});
JButton jBtCancel;
jBtCancel = new JButton("Cancel");
jBtCancel.setMnemonic('C');
jBtCancel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
dlg.setVisible(false);
}
});
GridBagConstraints gbc = new GridBagConstraints();
dlg.setLayout(new GridBagLayout());
Container c = new Container();
c.setLayout(new GridBagLayout());
gbc.gridwidth = 2;
gbc.insets = new Insets(8, 0, 0, 0);
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.gridwidth = GridBagConstraints.RELATIVE;
gbc.fill = GridBagConstraints.HORIZONTAL;
c.add(jLbNrOfNodes, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
c.add(jTfNrOfNodes, gbc);
gbc.gridwidth = GridBagConstraints.RELATIVE;
c.add(jLbNrOfArcs, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
c.add(jTfNrOfArcs, gbc);
gbc.gridwidth = GridBagConstraints.RELATIVE;
c.add(jLbCardinality, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
c.add(jTfCardinality, gbc);
gbc.gridwidth = GridBagConstraints.RELATIVE;
c.add(jLbSeed, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
c.add(jTfSeed, gbc);
gbc.fill = GridBagConstraints.HORIZONTAL;
dlg.add(c, gbc);
dlg.add(jBtGo);
gbc.gridwidth = GridBagConstraints.REMAINDER;
dlg.add(jBtCancel);
}
dlg.pack();
dlg.setLocation(100, 100);
dlg.setVisible(true);
dlg.setSize(dlg.getPreferredSize());
dlg.setVisible(false);
dlg.setVisible(true);
dlg.repaint();
} // actionPerformed
} // class ActionGenerate
class ActionGenerateData extends MyAction {
/** for serialization */
private static final long serialVersionUID = -2038911085935516L;
public ActionGenerateData() {
super("Generate Data", "Generate Random Instances from Network",
"generate.data", "ctrl D");
} // c'tor
int m_nNrOfInstances = 100;
int m_nSeed = 1234;
String m_sFile = "";
JDialog dlg = null;
@Override
public void actionPerformed(ActionEvent ae) {
if (dlg == null) {
dlg = new JDialog();
dlg.setTitle("Generate Random Data Options");
final JLabel jLbNrOfInstances = new JLabel("Nr of instances");
final JTextField jTfNrOfInstances = new JTextField(3);
jTfNrOfInstances.setHorizontalAlignment(JTextField.CENTER);
jTfNrOfInstances.setText("" + m_nNrOfInstances);
final JLabel jLbSeed = new JLabel("Random seed");
final JTextField jTfSeed = new JTextField(3);
jTfSeed.setHorizontalAlignment(JTextField.CENTER);
jTfSeed.setText("" + m_nSeed);
final JLabel jLbFile = new JLabel("Output file (optional)");
final JTextField jTfFile = new JTextField(12);
jTfFile.setHorizontalAlignment(JTextField.CENTER);
jTfFile.setText(m_sFile);
JButton jBtGo;
jBtGo = new JButton("Generate Data");
jBtGo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
try {
String tmpfilename = "tmp.bif.file.xml";
BayesNetGenerator generator = new BayesNetGenerator();
String[] options = new String[4];
options[0] = "-M";
options[1] = "" + jTfNrOfInstances.getText();
options[2] = "-F";
options[3] = tmpfilename;
FileWriter outfile = new FileWriter(tmpfilename);
StringBuffer text = new StringBuffer();
if (m_marginCalculator == null) {
m_marginCalculator = new MarginCalculator();
m_marginCalculator.calcMargins(m_BayesNet);
}
text.append(m_marginCalculator.toXMLBIF03());
outfile.write(text.toString());
outfile.close();
generator.setOptions(options);
generator.generateRandomNetwork();
generator.generateInstances();
m_Instances = generator.m_Instances;
a_learn.setEnabled(true);
a_learnCPT.setEnabled(true);
m_sFile = jTfFile.getText();
if (m_sFile != null && !m_sFile.equals("")) {
AbstractFileSaver saver = ConverterUtils
.getSaverForFile(m_sFile);
// no idea what the format is, so let's save it as ARFF file
if (saver == null) {
saver = new ArffSaver();
}
saver.setFile(new File(m_sFile));
saver.setInstances(m_Instances);
saver.writeBatch();
}
} catch (Exception e) {
e.printStackTrace();
}
dlg.setVisible(false);
}
});
JButton jBtFile = new JButton("Browse");
jBtFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
ConverterFileChooser fc = new ConverterFileChooser(System
.getProperty("user.dir"));
fc.setDialogTitle("Save Instances As");
int rval = fc.showSaveDialog(GUI.this);
if (rval == JFileChooser.APPROVE_OPTION) {
String filename = fc.getSelectedFile().toString();
jTfFile.setText(filename);
}
dlg.setVisible(true);
}
});
JButton jBtCancel;
jBtCancel = new JButton("Cancel");
jBtCancel.setMnemonic('C');
jBtCancel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
dlg.setVisible(false);
}
});
GridBagConstraints gbc = new GridBagConstraints();
dlg.setLayout(new GridBagLayout());
Container c = new Container();
c.setLayout(new GridBagLayout());
gbc.gridwidth = 2;
gbc.insets = new Insets(8, 0, 0, 0);
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.gridwidth = GridBagConstraints.RELATIVE;
gbc.fill = GridBagConstraints.HORIZONTAL;
c.add(jLbNrOfInstances, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
c.add(jTfNrOfInstances, gbc);
gbc.gridwidth = GridBagConstraints.RELATIVE;
c.add(jLbSeed, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
c.add(jTfSeed, gbc);
gbc.gridwidth = GridBagConstraints.RELATIVE;
c.add(jLbFile, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
c.add(jTfFile, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
c.add(jBtFile, gbc);
gbc.fill = GridBagConstraints.HORIZONTAL;
dlg.add(c, gbc);
dlg.add(jBtGo);
gbc.gridwidth = GridBagConstraints.REMAINDER;
dlg.add(jBtCancel);
}
dlg.setLocation(100, 100);
dlg.setVisible(true);
dlg.setSize(dlg.getPreferredSize());
dlg.setVisible(false);
dlg.setVisible(true);
dlg.repaint();
} // actionPerformed
} // class ActionGenerateData
class ActionLearn extends MyAction {
/** for serialization */
private static final long serialVersionUID = -2038911085935516L;
public ActionLearn() {
super("Learn Network", "Learn Bayesian Network", "learn", "ctrl L");
setEnabled(false);
} // c'tor
JDialog dlg = null;
@Override
public void actionPerformed(ActionEvent ae) {
if (dlg == null) {
dlg = new JDialog();
dlg.setTitle("Learn Bayesian Network");
final JButton jBtOptions = new JButton("Options");
jBtOptions.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
// m_BayesNet = new EditableBayesNet();
try {
GenericObjectEditor.registerEditors();
GenericObjectEditor ce = new GenericObjectEditor(true);
ce.setClassType(weka.classifiers.Classifier.class);
ce.setValue(m_BayesNet);
PropertyDialog pd;
if (PropertyDialog.getParentDialog(GUI.this) != null) {
pd = new PropertyDialog(PropertyDialog
.getParentDialog(GUI.this), ce, 100, 100);
} else {
pd = new PropertyDialog(
PropertyDialog.getParentFrame(GUI.this), ce, 100, 100);
}
pd.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
PropertyEditor pe = ((PropertyDialog) e.getSource())
.getEditor();
Object c = pe.getValue();
String options = "";
if (c instanceof OptionHandler) {
options = Utils.joinOptions(((OptionHandler) c)
.getOptions());
try {
m_BayesNet.setOptions(((OptionHandler) c).getOptions());
} catch (Exception e2) {
e2.printStackTrace();
}
}
System.out.println(c.getClass().getName() + " " + options);
System.exit(0);
}
});
pd.setVisible(true);
} catch (Exception ex) {
ex.printStackTrace();
System.err.println(ex.getMessage());
}
m_BayesNet.clearUndoStack();
a_undo.setEnabled(false);
a_redo.setEnabled(false);
}
});
final JTextField jTfOptions = new JTextField(40);
jTfOptions.setHorizontalAlignment(JTextField.CENTER);
jTfOptions.setText("" + Utils.joinOptions(m_BayesNet.getOptions()));
JButton jBtGo;
jBtGo = new JButton("Learn");
jBtGo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
try {
m_BayesNet.buildClassifier(m_Instances);
layoutGraph();
updateStatus();
m_BayesNet.clearUndoStack();
dlg.setVisible(false);
} catch (Exception e) {
e.printStackTrace();
}
dlg.setVisible(false);
}
});
JButton jBtCancel;
jBtCancel = new JButton("Cancel");
jBtCancel.setMnemonic('C');
jBtCancel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
dlg.setVisible(false);
}
});
GridBagConstraints gbc = new GridBagConstraints();
dlg.setLayout(new GridBagLayout());
Container c = new Container();
c.setLayout(new GridBagLayout());
gbc.gridwidth = 2;
gbc.insets = new Insets(8, 0, 0, 0);
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.gridwidth = GridBagConstraints.RELATIVE;
gbc.fill = GridBagConstraints.HORIZONTAL;
c.add(jBtOptions, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
c.add(jTfOptions, gbc);
gbc.fill = GridBagConstraints.HORIZONTAL;
dlg.add(c, gbc);
dlg.add(jBtGo);
gbc.gridwidth = GridBagConstraints.REMAINDER;
dlg.add(jBtCancel);
}
dlg.setLocation(100, 100);
dlg.setVisible(true);
dlg.setSize(dlg.getPreferredSize());
dlg.setVisible(false);
dlg.setVisible(true);
dlg.repaint();
} // actionPerformed
} // class ActionLearn
class ActionLearnCPT extends MyAction {
/** for serialization */
private static final long serialVersionUID = -2022211085935516L;
public ActionLearnCPT() {
super("Learn CPT", "Learn conditional probability tables", "learncpt", "");
setEnabled(false);
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
if (m_Instances == null) {
JOptionPane.showMessageDialog(null,
"Select instances to learn from first (menu Tools/Set Data)");
return;
}
try {
m_BayesNet.setData(m_Instances);
} catch (Exception e) {
JOptionPane.showMessageDialog(null,
"Data set is not compatible with network.\n" + e.getMessage()
+ "\nChoose other instances (menu Tools/Set Data)");
return;
}
try {
m_BayesNet.estimateCPTs();
m_BayesNet.clearUndoStack();
} catch (Exception e) {
e.printStackTrace();
}
updateStatus();
} // actionPerformed
} // class ActionLearnCPT
class ActionSetData extends MyAction {
/** for serialization */
private static final long serialVersionUID = -2038911085935519L;
public ActionSetData() {
super("Set Data", "Set Data File", "setdata", "ctrl A");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
ConverterFileChooser fc = new ConverterFileChooser(
System.getProperty("user.dir"));
fc.setDialogTitle("Set Data File");
int rval = fc.showOpenDialog(GUI.this);
if (rval == JFileChooser.APPROVE_OPTION) {
AbstractFileLoader loader = fc.getLoader();
try {
if (loader != null) {
m_Instances = loader.getDataSet();
}
if (m_Instances.classIndex() == -1) {
m_Instances.setClassIndex(m_Instances.numAttributes() - 1);
}
a_learn.setEnabled(true);
a_learnCPT.setEnabled(true);
repaint();
} catch (Exception e) {
e.printStackTrace();
}
}
}
} // class ActionSetData
class ActionUndo extends MyAction {
/** for serialization */
private static final long serialVersionUID = -3038910085935519L;
public ActionUndo() {
super("Undo", "Undo", "undo", "ctrl Z");
setEnabled(false);
} // c'tor
@Override
public boolean isEnabled() {
return m_BayesNet.canUndo();
}
@Override
public void actionPerformed(ActionEvent ae) {
String sMsg = m_BayesNet.undo();
m_jStatusBar.setText("Undo action performed: " + sMsg);
// if (!sMsg.equals("")) {
// JOptionPane.showMessageDialog(null, sMsg, "Undo action successful",
// JOptionPane.INFORMATION_MESSAGE);
// }
a_redo.setEnabled(m_BayesNet.canRedo());
a_undo.setEnabled(m_BayesNet.canUndo());
m_Selection.clear();
updateStatus();
repaint();
}
} // ActionUndo
class ActionRedo extends MyAction {
/** for serialization */
private static final long serialVersionUID = -4038910085935519L;
public ActionRedo() {
super("Redo", "Redo", "redo", "ctrl Y");
setEnabled(false);
} // c'tor
@Override
public boolean isEnabled() {
return m_BayesNet.canRedo();
}
@Override
public void actionPerformed(ActionEvent ae) {
String sMsg = m_BayesNet.redo();
m_jStatusBar.setText("Redo action performed: " + sMsg);
// if (!sMsg.equals("")) {
// JOptionPane.showMessageDialog(null, sMsg, "Redo action successful",
// JOptionPane.INFORMATION_MESSAGE);
// }
m_Selection.clear();
updateStatus();
repaint();
}
} // ActionRedo
class ActionAddNode extends MyAction {
/** for serialization */
private static final long serialVersionUID = -2038910085935519L;
public ActionAddNode() {
super("Add Node", "Add Node", "addnode", "");
} // c'tor
JDialog dlg = null;
JTextField jTfName = new JTextField(20);
JTextField jTfCard = new JTextField(3);
int m_X = Integer.MAX_VALUE;
int m_Y;
public void addNode(int nX, int nY) {
m_X = nX;
m_Y = nY;
addNode();
} // addNode
void addNode() {
if (dlg == null) {
dlg = new JDialog();
dlg.setTitle("Add node");
JLabel jLbName = new JLabel("Name");
jTfName.setHorizontalAlignment(JTextField.CENTER);
JLabel jLbCard = new JLabel("Cardinality");
jTfCard.setHorizontalAlignment(JTextField.CENTER);
jTfCard.setText("2");
JButton jBtCancel;
jBtCancel = new JButton("Cancel");
jBtCancel.setMnemonic('C');
jBtCancel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
dlg.setVisible(false);
}
});
JButton jBtOk = new JButton("Ok");
jBtOk.setMnemonic('O');
jBtOk.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
String sName = jTfName.getText();
if (sName.length() <= 0) {
JOptionPane.showMessageDialog(null,
"Name should have at least one character");
return;
}
int nCard = new Integer(jTfCard.getText()).intValue();
if (nCard <= 1) {
JOptionPane.showMessageDialog(null,
"Cardinality should be larger than 1");
return;
}
try {
if (m_X < Integer.MAX_VALUE) {
m_BayesNet.addNode(sName, nCard, m_X, m_Y);
} else {
m_BayesNet.addNode(sName, nCard);
}
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
a_undo.setEnabled(true);
a_redo.setEnabled(false);
// GraphNode n = new GraphNode("id" + m_nodes.size(), sName);
// n.probs = m_BayesNet.getDistribution(sName);
// n.outcomes = m_BayesNet.getValues(sName);
// n.x = 100 + m_nodes.size() * 10;
// n.y = 100 + m_nodes.size() * 10;
// m_nodes.addElement(n);
} catch (Exception e) {
e.printStackTrace();
}
repaint();
dlg.setVisible(false);
}
});
dlg.setLayout(new GridLayout(3, 2, 10, 10));
dlg.add(jLbName);
dlg.add(jTfName);
dlg.add(jLbCard);
dlg.add(jTfCard);
dlg.add(jBtOk);
dlg.add(jBtCancel);
dlg.setSize(dlg.getPreferredSize());
}
jTfName.setText("Node" + (m_BayesNet.getNrOfNodes() + 1));
dlg.setVisible(true);
} // addNode
@Override
public void actionPerformed(ActionEvent ae) {
m_X = Integer.MAX_VALUE;
addNode();
}
} // class ActionAddNode
class ActionDeleteNode extends MyAction {
/** for serialization */
private static final long serialVersionUID = -2038912085935519L;
public ActionDeleteNode() {
super("Delete Node", "Delete Node", "delnode", "DELETE");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
if (m_Selection.getSelected().size() > 0) {
m_BayesNet.deleteSelection(m_Selection.getSelected());
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
m_Selection.clear();
updateStatus();
repaint();
} else {
String[] options = new String[m_BayesNet.getNrOfNodes()];
for (int i = 0; i < options.length; i++) {
options[i] = m_BayesNet.getNodeName(i);
}
String sResult = (String) JOptionPane.showInputDialog(null,
"Select node to delete", "Nodes", 0, null, options, options[0]);
if (sResult != null && !sResult.equals("")) {
int iNode = m_BayesNet.getNode2(sResult);
deleteNode(iNode);
}
}
}
} // class ActionDeleteNode
class ActionCopyNode extends MyAction {
/** for serialization */
private static final long serialVersionUID = -2038732085935519L;
public ActionCopyNode() {
super("Copy", "Copy Nodes", "copy", "ctrl C");
} // c'tor
public ActionCopyNode(String sName, String sToolTipText, String sIcon,
String sAcceleratorKey) {
super(sName, sToolTipText, sIcon, sAcceleratorKey);
} // c'rot
@Override
public void actionPerformed(ActionEvent ae) {
copy();
}
public void copy() {
String sXML = m_BayesNet.toXMLBIF03(m_Selection.getSelected());
m_clipboard.setText(sXML);
} // copy
} // class ActionCopyNode
class ActionCutNode extends ActionCopyNode {
/** for serialization */
private static final long serialVersionUID = -2038822085935519L;
public ActionCutNode() {
super("Cut", "Cut Nodes", "cut", "ctrl X");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
copy();
m_BayesNet.deleteSelection(m_Selection.getSelected());
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
m_Selection.clear();
a_undo.setEnabled(true);
a_redo.setEnabled(false);
repaint();
}
} // class ActionCutNode
class ActionPasteNode extends MyAction {
/** for serialization */
private static final long serialVersionUID = -2038732085935519L;
public ActionPasteNode() {
super("Paste", "Paste Nodes", "paste", "ctrl V");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
try {
m_BayesNet.paste(m_clipboard.getText());
updateStatus();
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean isEnabled() {
return m_clipboard.hasText();
}
} // class ActionPasteNode
class ActionSelectAll extends MyAction {
/** for serialization */
private static final long serialVersionUID = -2038642085935519L;
public ActionSelectAll() {
super("Select All", "Select All Nodes", "selectall", "ctrl A");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
m_Selection.selectAll();
repaint();
}
} // class ActionSelectAll
class ActionExport extends MyAction {
boolean m_bIsExporting = false;
/** for serialization */
private static final long serialVersionUID = -3027642085935519L;
public ActionExport() {
super("Export", "Export to graphics file", "export", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
m_bIsExporting = true;
m_GraphPanel.saveComponent();
m_bIsExporting = false;
repaint();
}
public boolean isExporting() {
return m_bIsExporting;
}
} // class ActionExport
class ActionAlignLeft extends MyAction {
/** for serialization */
private static final long serialVersionUID = -3138642085935519L;
public ActionAlignLeft() {
super("Align Left", "Align Left", "alignleft", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
m_BayesNet.alignLeft(m_Selection.getSelected());
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
a_undo.setEnabled(true);
a_redo.setEnabled(false);
repaint();
}
} // class ActionAlignLeft
class ActionAlignRight extends MyAction {
/** for serialization */
private static final long serialVersionUID = -4238642085935519L;
public ActionAlignRight() {
super("Align Right", "Align Right", "alignright", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
m_BayesNet.alignRight(m_Selection.getSelected());
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
a_undo.setEnabled(true);
a_redo.setEnabled(false);
repaint();
}
} // class ActionAlignRight
class ActionAlignTop extends MyAction {
/** for serialization */
private static final long serialVersionUID = -5338642085935519L;
public ActionAlignTop() {
super("Align Top", "Align Top", "aligntop", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
m_BayesNet.alignTop(m_Selection.getSelected());
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
a_undo.setEnabled(true);
a_redo.setEnabled(false);
repaint();
}
} // class ActionAlignTop
class ActionAlignBottom extends MyAction {
/** for serialization */
private static final long serialVersionUID = -6438642085935519L;
public ActionAlignBottom() {
super("Align Bottom", "Align Bottom", "alignbottom", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
m_BayesNet.alignBottom(m_Selection.getSelected());
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
a_undo.setEnabled(true);
a_redo.setEnabled(false);
repaint();
}
} // class ActionAlignBottom
class ActionCenterHorizontal extends MyAction {
/** for serialization */
private static final long serialVersionUID = -7538642085935519L;
public ActionCenterHorizontal() {
super("Center Horizontal", "Center Horizontal", "centerhorizontal", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
m_BayesNet.centerHorizontal(m_Selection.getSelected());
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
a_undo.setEnabled(true);
a_redo.setEnabled(false);
repaint();
}
} // class ActionCenterHorizontal
class ActionCenterVertical extends MyAction {
/** for serialization */
private static final long serialVersionUID = -8638642085935519L;
public ActionCenterVertical() {
super("Center Vertical", "Center Vertical", "centervertical", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
m_BayesNet.centerVertical(m_Selection.getSelected());
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
a_undo.setEnabled(true);
a_redo.setEnabled(false);
repaint();
}
} // class ActionCenterVertical
class ActionSpaceHorizontal extends MyAction {
/** for serialization */
private static final long serialVersionUID = -9738642085935519L;
public ActionSpaceHorizontal() {
super("Space Horizontal", "Space Horizontal", "spacehorizontal", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
m_BayesNet.spaceHorizontal(m_Selection.getSelected());
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
a_undo.setEnabled(true);
a_redo.setEnabled(false);
repaint();
}
} // class ActionSpaceHorizontal
class ActionSpaceVertical extends MyAction {
/** for serialization */
private static final long serialVersionUID = -838642085935519L;
public ActionSpaceVertical() {
super("Space Vertical", "Space Vertical", "spacevertical", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
m_BayesNet.spaceVertical(m_Selection.getSelected());
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
a_undo.setEnabled(true);
a_redo.setEnabled(false);
repaint();
}
} // class ActionSpaceVertical
class ActionAddArc extends MyAction {
/** for serialization */
private static final long serialVersionUID = -2038913085935519L;
public ActionAddArc() {
super("Add Arc", "Add Arc", "addarc", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
try {
String[] options = new String[m_BayesNet.getNrOfNodes()];
for (int i = 0; i < options.length; i++) {
options[i] = m_BayesNet.getNodeName(i);
}
String sChild = (String) JOptionPane.showInputDialog(null,
"Select child node", "Nodes", 0, null, options, options[0]);
if (sChild == null || sChild.equals("")) {
return;
}
int iChild = m_BayesNet.getNode(sChild);
addArcInto(iChild);
} catch (Exception e) {
e.printStackTrace();
}
}
} // class ActionAddArc
class ActionDeleteArc extends MyAction {
/** for serialization */
private static final long serialVersionUID = -2038914085935519L;
public ActionDeleteArc() {
super("Delete Arc", "Delete Arc", "delarc", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
int nEdges = 0;
for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
nEdges += m_BayesNet.getNrOfParents(iNode);
}
String[] options = new String[nEdges];
int i = 0;
for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iNode); iParent++) {
int nParent = m_BayesNet.getParent(iNode, iParent);
String sEdge = m_BayesNet.getNodeName(nParent);
sEdge += " -> ";
sEdge += m_BayesNet.getNodeName(iNode);
options[i++] = sEdge;
}
}
deleteArc(options);
}
} // class ActionDeleteArc
class ActionNew extends MyAction {
/** for serialization */
private static final long serialVersionUID = -2038911085935515L;
public ActionNew() {
super("New", "New Network", "new", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
m_sFileName = "";
m_BayesNet = new EditableBayesNet(true);
updateStatus();
layoutGraph();
a_datagenerator.setEnabled(false);
m_BayesNet.clearUndoStack();
m_jStatusBar.setText("New Network");
m_Selection = new Selection();
repaint();
}
} // class ActionNew
class ActionLoad extends MyAction {
/** for serialization */
private static final long serialVersionUID = -2038911085935515L;
public ActionLoad() {
super("Load", "Load Graph", "open", "ctrl O");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
JFileChooser fc = new JFileChooser(System.getProperty("user.dir"));
ExtensionFileFilter ef1 = new ExtensionFileFilter(".arff", "ARFF files");
ExtensionFileFilter ef2 = new ExtensionFileFilter(".xml", "XML BIF files");
fc.addChoosableFileFilter(ef1);
fc.addChoosableFileFilter(ef2);
fc.setDialogTitle("Load Graph");
int rval = fc.showOpenDialog(GUI.this);
if (rval == JFileChooser.APPROVE_OPTION) {
String sFileName = fc.getSelectedFile().toString();
if (sFileName.endsWith(ef1.getExtensions()[0])) {
initFromArffFile(sFileName);
} else {
try {
readBIFFromFile(sFileName);
} catch (Exception e) {
e.printStackTrace();
}
}
m_jStatusBar.setText("Loaded " + sFileName);
updateStatus();
}
}
} // class ActionLoad
class ActionViewStatusbar extends MyAction {
/** for serialization */
private static final long serialVersionUID = -20389330812354L;
public ActionViewStatusbar() {
super("View statusbar", "View statusbar", "statusbar", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
m_jStatusBar.setVisible(!m_jStatusBar.isVisible());
} // actionPerformed
} // class ActionViewStatusbar
class ActionViewToolbar extends MyAction {
/** for serialization */
private static final long serialVersionUID = -20389110812354L;
public ActionViewToolbar() {
super("View toolbar", "View toolbar", "toolbar", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
m_jTbTools.setVisible(!m_jTbTools.isVisible());
} // actionPerformed
} // class ActionViewToolbar
class ActionSave extends MyAction {
/** for serialization */
private static final long serialVersionUID = -20389110859355156L;
public ActionSave() {
super("Save", "Save Graph", "save", "ctrl S");
} // c'tor
public ActionSave(String sName, String sToolTipText, String sIcon,
String sAcceleratorKey) {
super(sName, sToolTipText, sIcon, sAcceleratorKey);
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
if (!m_sFileName.equals("")) {
saveFile(m_sFileName);
m_BayesNet.isSaved();
m_jStatusBar.setText("Saved as " + m_sFileName);
} else {
if (saveAs()) {
m_BayesNet.isSaved();
m_jStatusBar.setText("Saved as " + m_sFileName);
}
}
} // actionPerformed
ExtensionFileFilter ef1 = new ExtensionFileFilter(".xml", "XML BIF files");
boolean saveAs() {
JFileChooser fc = new JFileChooser(System.getProperty("user.dir"));
fc.addChoosableFileFilter(ef1);
fc.setDialogTitle("Save Graph As");
if (!m_sFileName.equals("")) {
// can happen on actionQuit
fc.setSelectedFile(new File(m_sFileName));
}
int rval = fc.showSaveDialog(GUI.this);
if (rval == JFileChooser.APPROVE_OPTION) {
// System.out.println("Saving to file \""+
// f.getAbsoluteFile().toString()+"\"");
String sFileName = fc.getSelectedFile().toString();
if (!sFileName.endsWith(".xml")) {
sFileName = sFileName.concat(".xml");
}
saveFile(sFileName);
return true;
}
return false;
} // saveAs
protected void saveFile(String sFileName) {
try {
FileWriter outfile = new FileWriter(sFileName);
outfile.write(m_BayesNet.toXMLBIF03());
outfile.close();
m_sFileName = sFileName;
m_jStatusBar.setText("Saved as " + m_sFileName);
} catch (IOException e) {
e.printStackTrace();
}
} // saveFile
} // class ActionSave
class ActionSaveAs extends ActionSave {
/** for serialization */
private static final long serialVersionUID = -20389110859354L;
public ActionSaveAs() {
super("Save As", "Save Graph As", "saveas", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
saveAs();
} // actionPerformed
} // class ActionSaveAs
class ActionPrint extends ActionSave {
/** for serialization */
private static final long serialVersionUID = -20389001859354L;
boolean m_bIsPrinting = false;
public ActionPrint() {
super("Print", "Print Graph", "print", "ctrl P");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
PrinterJob printJob = PrinterJob.getPrinterJob();
printJob.setPrintable(m_GraphPanel);
if (printJob.printDialog()) {
try {
m_bIsPrinting = true;
printJob.print();
m_bIsPrinting = false;
} catch (PrinterException pe) {
m_jStatusBar.setText("Error printing: " + pe);
m_bIsPrinting = false;
}
}
m_jStatusBar.setText("Print");
} // actionPerformed
public boolean isPrinting() {
return m_bIsPrinting;
}
} // class ActionPrint
class ActionQuit extends ActionSave {
/** for serialization */
private static final long serialVersionUID = -2038911085935515L;
public ActionQuit() {
super("Exit", "Exit Program", "exit", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
if (m_BayesNet.isChanged()) {
int result = JOptionPane.showConfirmDialog(null,
"Network changed. Do you want to save it?", "Save before closing?",
JOptionPane.YES_NO_CANCEL_OPTION);
if (result == JOptionPane.CANCEL_OPTION) {
return;
}
if (result == JOptionPane.YES_OPTION) {
if (!saveAs()) {
return;
}
}
}
System.exit(0);
}
} // class ActionQuit
class ActionHelp extends MyAction {
/** for serialization */
private static final long serialVersionUID = -20389110859354L;
public ActionHelp() {
super("Help", "Bayesian Network Workbench Help", "help", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
JOptionPane.showMessageDialog(null,
"See Weka Homepage\nhttp://www.cs.waikato.ac.nz/ml", "Help Message",
JOptionPane.PLAIN_MESSAGE);
}
} // class ActionHelp
class ActionAbout extends MyAction {
/** for serialization */
private static final long serialVersionUID = -20389110859353L;
public ActionAbout() {
super("About", "Help about", "about", "");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
JOptionPane.showMessageDialog(null,
"Bayesian Network Workbench\nPart of Weka\n2007", "About Message",
JOptionPane.PLAIN_MESSAGE);
}
} // class ActionAbout
class ActionZoomIn extends MyAction {
/** for serialization */
private static final long serialVersionUID = -2038911085935515L;
public ActionZoomIn() {
super("Zoom in", "Zoom in", "zoomin", "+");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
int i = 0, s = (int) (m_fScale * 100);
if (s < 300) {
i = s / 25;
} else if (s < 700) {
i = 6 + s / 50;
} else {
i = 13 + s / 100;
}
if (s >= 999) {
setEnabled(false);
return;
} else if (s >= 10) {
if (i >= 22) {
setEnabled(false);
}
if (s == 10 && !a_zoomout.isEnabled()) {
a_zoomout.setEnabled(true);
}
m_jTfZoom.setText(m_nZoomPercents[i + 1] + "%");
m_fScale = m_nZoomPercents[i + 1] / 100D;
} else {
if (!a_zoomout.isEnabled()) {
a_zoomout.setEnabled(true);
}
m_jTfZoom.setText(m_nZoomPercents[0] + "%");
m_fScale = m_nZoomPercents[0] / 100D;
}
setAppropriateSize();
m_GraphPanel.repaint();
m_GraphPanel.invalidate();
m_jScrollPane.revalidate();
m_jStatusBar.setText("Zooming in");
}
} // class ActionZoomIn
class ActionZoomOut extends MyAction {
/** for serialization */
private static final long serialVersionUID = -203891108593551L;
public ActionZoomOut() {
super("Zoom out", "Zoom out", "zoomout", "-");
} // c'tor
@Override
public void actionPerformed(ActionEvent ae) {
int i = 0, s = (int) (m_fScale * 100);
if (s < 300) {
i = (int) Math.ceil(s / 25D);
} else if (s < 700) {
i = 6 + (int) Math.ceil(s / 50D);
} else {
i = 13 + (int) Math.ceil(s / 100D);
}
if (s <= 10) {
setEnabled(false);
} else if (s < 999) {
if (i <= 1) {
setEnabled(false);
}
m_jTfZoom.setText(m_nZoomPercents[i - 1] + "%");
m_fScale = m_nZoomPercents[i - 1] / 100D;
} else {
if (!a_zoomin.isEnabled()) {
a_zoomin.setEnabled(true);
}
m_jTfZoom.setText(m_nZoomPercents[22] + "%");
m_fScale = m_nZoomPercents[22] / 100D;
}
setAppropriateSize();
m_GraphPanel.repaint();
m_GraphPanel.invalidate();
m_jScrollPane.revalidate();
m_jStatusBar.setText("Zooming out");
}
} // class ActionZoomOut
class ActionLayout extends MyAction {
/** for serialization */
private static final long serialVersionUID = -203891108593551L;
public ActionLayout() {
super("Layout", "Layout Graph", "layout", "ctrl L");
} // c'tor
JDialog dlg = null;
@Override
public void actionPerformed(ActionEvent ae) {
if (dlg == null) {
dlg = new JDialog();
dlg.setTitle("Graph Layout Options");
final JCheckBox jCbCustomNodeSize = new JCheckBox("Custom Node Size");
final JLabel jLbNodeWidth = new JLabel("Width");
final JLabel jLbNodeHeight = new JLabel("Height");
m_jTfNodeWidth.setHorizontalAlignment(JTextField.CENTER);
m_jTfNodeWidth.setText("" + m_nNodeWidth);
m_jTfNodeHeight.setHorizontalAlignment(JTextField.CENTER);
m_jTfNodeHeight.setText("" + m_nNodeHeight);
jLbNodeWidth.setEnabled(false);
m_jTfNodeWidth.setEnabled(false);
jLbNodeHeight.setEnabled(false);
m_jTfNodeHeight.setEnabled(false);
jCbCustomNodeSize.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
if (((JCheckBox) ae.getSource()).isSelected()) {
jLbNodeWidth.setEnabled(true);
m_jTfNodeWidth.setEnabled(true);
jLbNodeHeight.setEnabled(true);
m_jTfNodeHeight.setEnabled(true);
} else {
jLbNodeWidth.setEnabled(false);
m_jTfNodeWidth.setEnabled(false);
jLbNodeHeight.setEnabled(false);
m_jTfNodeHeight.setEnabled(false);
setAppropriateSize();
setAppropriateNodeSize();
}
}
});
JButton jBtLayout;
jBtLayout = new JButton("Layout Graph");
jBtLayout.setMnemonic('L');
jBtLayout.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
int tmpW, tmpH;
if (jCbCustomNodeSize.isSelected()) {
try {
tmpW = Integer.parseInt(m_jTfNodeWidth.getText());
} catch (NumberFormatException ne) {
JOptionPane.showMessageDialog(GUI.this.getParent(),
"Invalid integer entered for node width.", "Error",
JOptionPane.ERROR_MESSAGE);
tmpW = m_nNodeWidth;
m_jTfNodeWidth.setText("" + m_nNodeWidth);
}
try {
tmpH = Integer.parseInt(m_jTfNodeHeight.getText());
} catch (NumberFormatException ne) {
JOptionPane.showMessageDialog(GUI.this.getParent(),
"Invalid integer entered for node height.", "Error",
JOptionPane.ERROR_MESSAGE);
tmpH = m_nNodeHeight;
m_jTfNodeWidth.setText("" + m_nNodeHeight);
}
if (tmpW != m_nNodeWidth || tmpH != m_nNodeHeight) {
m_nNodeWidth = tmpW;
m_nPaddedNodeWidth = m_nNodeWidth + PADDING;
m_nNodeHeight = tmpH;
}
}
// JButton bt = (JButton) ae.getSource();
// bt.setEnabled(false);
dlg.setVisible(false);
updateStatus();
layoutGraph();
m_jStatusBar.setText("Laying out Bayes net");
}
});
JButton jBtCancel;
jBtCancel = new JButton("Cancel");
jBtCancel.setMnemonic('C');
jBtCancel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
dlg.setVisible(false);
}
});
GridBagConstraints gbc = new GridBagConstraints();
dlg.setLayout(new GridBagLayout());
// dlg.add(m_le.getControlPanel());
Container c = new Container();
c.setLayout(new GridBagLayout());
gbc.gridwidth = 1;
gbc.insets = new Insets(8, 0, 0, 0);
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.gridwidth = GridBagConstraints.REMAINDER;
c.add(jCbCustomNodeSize, gbc);
gbc.gridwidth = GridBagConstraints.RELATIVE;
c.add(jLbNodeWidth, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
c.add(m_jTfNodeWidth, gbc);
gbc.gridwidth = GridBagConstraints.RELATIVE;
c.add(jLbNodeHeight, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
c.add(m_jTfNodeHeight, gbc);
gbc.fill = GridBagConstraints.HORIZONTAL;
dlg.add(c, gbc);
dlg.add(jBtLayout);
gbc.gridwidth = GridBagConstraints.REMAINDER;
dlg.add(jBtCancel);
}
dlg.setLocation(100, 100);
dlg.setVisible(true);
dlg.setSize(dlg.getPreferredSize());
dlg.setVisible(false);
dlg.setVisible(true);
dlg.repaint();
}
} // class ActionLayout
/**
* Constructor
* Sets up the gui and initializes all the other previously uninitialized
* variables.
*/
public GUI() {
m_GraphPanel = new GraphPanel();
m_jScrollPane = new JScrollPane(m_GraphPanel);
// creating a new layout engine and adding this class as its listener
// to receive layoutComplete events
m_jTfZoom = new JTextField("100%");
m_jTfZoom.setMinimumSize(m_jTfZoom.getPreferredSize());
m_jTfZoom.setHorizontalAlignment(JTextField.CENTER);
m_jTfZoom.setToolTipText("Zoom");
m_jTfZoom.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
JTextField jt = (JTextField) ae.getSource();
try {
int i = -1;
i = jt.getText().indexOf('%');
if (i == -1) {
i = Integer.parseInt(jt.getText());
} else {
i = Integer.parseInt(jt.getText().substring(0, i));
}
if (i <= 999) {
m_fScale = i / 100D;
}
jt.setText((int) (m_fScale * 100) + "%");
if (m_fScale > 0.1) {
if (!a_zoomout.isEnabled()) {
a_zoomout.setEnabled(true);
}
} else {
a_zoomout.setEnabled(false);
}
if (m_fScale < 9.99) {
if (!a_zoomin.isEnabled()) {
a_zoomin.setEnabled(true);
}
} else {
a_zoomin.setEnabled(false);
}
setAppropriateSize();
// m_GraphPanel.clearBuffer();
m_GraphPanel.repaint();
m_GraphPanel.invalidate();
m_jScrollPane.revalidate();
} catch (NumberFormatException ne) {
JOptionPane.showMessageDialog(GUI.this.getParent(),
"Invalid integer entered for zoom.", "Error",
JOptionPane.ERROR_MESSAGE);
jt.setText((m_fScale * 100) + "%");
}
}
});
GridBagConstraints gbc = new GridBagConstraints();
final JPanel p = new JPanel(new GridBagLayout());
p.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("ExtraControls"),
BorderFactory.createEmptyBorder(4, 4, 4, 4)));
p.setPreferredSize(new Dimension(0, 0));
m_jTbTools = new JToolBar();
m_jTbTools.setFloatable(false);
m_jTbTools.setLayout(new GridBagLayout());
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 0, 0, 0);
m_jTbTools.add(p, gbc);
gbc.gridwidth = 1;
m_jTbTools.add(a_new);
m_jTbTools.add(a_save);
m_jTbTools.add(a_load);
m_jTbTools.addSeparator(new Dimension(2, 2));
m_jTbTools.add(a_cutnode);
m_jTbTools.add(a_copynode);
m_jTbTools.add(a_pastenode);
m_jTbTools.addSeparator(new Dimension(2, 2));
m_jTbTools.add(a_undo);
m_jTbTools.add(a_redo);
m_jTbTools.addSeparator(new Dimension(2, 2));
m_jTbTools.add(a_alignleft);
m_jTbTools.add(a_alignright);
m_jTbTools.add(a_aligntop);
m_jTbTools.add(a_alignbottom);
m_jTbTools.add(a_centerhorizontal);
m_jTbTools.add(a_centervertical);
m_jTbTools.add(a_spacehorizontal);
m_jTbTools.add(a_spacevertical);
m_jTbTools.addSeparator(new Dimension(2, 2));
m_jTbTools.add(a_zoomin);
gbc.fill = GridBagConstraints.VERTICAL;
gbc.weighty = 1;
JPanel p2 = new JPanel(new BorderLayout());
p2.setPreferredSize(m_jTfZoom.getPreferredSize());
p2.setMinimumSize(m_jTfZoom.getPreferredSize());
p2.add(m_jTfZoom, BorderLayout.CENTER);
m_jTbTools.add(p2, gbc);
gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
m_jTbTools.add(a_zoomout);
m_jTbTools.addSeparator(new Dimension(2, 2));
// jTbTools.add(jBtExtraControls, gbc);
m_jTbTools.add(a_layout);
m_jTbTools.addSeparator(new Dimension(4, 2));
gbc.weightx = 1;
gbc.fill = GridBagConstraints.BOTH;
// jTbTools.add(m_layoutEngine.getProgressBar(), gbc);
m_jStatusBar = new JLabel("Status bar");
this.setLayout(new BorderLayout());
this.add(m_jTbTools, BorderLayout.NORTH);
this.add(m_jScrollPane, BorderLayout.CENTER);
this.add(m_jStatusBar, BorderLayout.SOUTH);
updateStatus();
a_datagenerator.setEnabled(false);
makeMenuBar();
}
/**
* Get the menu bar for this application.
*
* @return the menu bar
*/
public JMenuBar getMenuBar() {
return m_menuBar;
}
private void makeMenuBar() {
m_menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
fileMenu.setMnemonic('F');
m_menuBar.add(fileMenu);
fileMenu.add(a_new);
fileMenu.add(a_load);
fileMenu.add(a_save);
fileMenu.add(a_saveas);
fileMenu.addSeparator();
fileMenu.add(a_print);
fileMenu.add(a_export);
fileMenu.addSeparator();
fileMenu.add(a_quit);
JMenu editMenu = new JMenu("Edit");
editMenu.setMnemonic('E');
m_menuBar.add(editMenu);
editMenu.add(a_undo);
editMenu.add(a_redo);
editMenu.addSeparator();
editMenu.add(a_selectall);
editMenu.add(a_delnode);
editMenu.add(a_cutnode);
editMenu.add(a_copynode);
editMenu.add(a_pastenode);
editMenu.addSeparator();
editMenu.add(a_addnode);
editMenu.add(a_addarc);
editMenu.add(a_delarc);
editMenu.addSeparator();
editMenu.add(a_alignleft);
editMenu.add(a_alignright);
editMenu.add(a_aligntop);
editMenu.add(a_alignbottom);
editMenu.add(a_centerhorizontal);
editMenu.add(a_centervertical);
editMenu.add(a_spacehorizontal);
editMenu.add(a_spacevertical);
JMenu toolMenu = new JMenu("Tools");
toolMenu.setMnemonic('T');
toolMenu.add(a_networkgenerator);
toolMenu.add(a_datagenerator);
toolMenu.add(a_datasetter);
toolMenu.add(a_learn);
toolMenu.add(a_learnCPT);
toolMenu.addSeparator();
toolMenu.add(a_layout);
toolMenu.addSeparator();
final JCheckBoxMenuItem viewMargins = new JCheckBoxMenuItem("Show Margins",
false);
viewMargins.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
boolean bPrev = m_bViewMargins;
m_bViewMargins = viewMargins.getState();
if (bPrev == false && viewMargins.getState() == true) {
updateStatus();
}
repaint();
}
});
toolMenu.add(viewMargins);
final JCheckBoxMenuItem viewCliques = new JCheckBoxMenuItem("Show Cliques",
false);
viewCliques.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
boolean bPrev = m_bViewCliques;
m_bViewCliques = viewCliques.getState();
if (bPrev == false && viewCliques.getState() == true) {
updateStatus();
}
repaint();
}
});
toolMenu.add(viewCliques);
m_menuBar.add(toolMenu);
JMenu viewMenu = new JMenu("View");
viewMenu.setMnemonic('V');
m_menuBar.add(viewMenu);
viewMenu.add(a_zoomin);
viewMenu.add(a_zoomout);
viewMenu.addSeparator();
viewMenu.add(a_viewtoolbar);
viewMenu.add(a_viewstatusbar);
JMenu helpMenu = new JMenu("Help");
helpMenu.setMnemonic('H');
m_menuBar.add(helpMenu);
helpMenu.add(a_help);
helpMenu.add(a_about);
}
/**
* This method sets the node size that is appropriate considering the maximum
* label size that is present. It is used internally when custom node size
* checkbox is unchecked.
*/
protected void setAppropriateNodeSize() {
int strWidth;
FontMetrics fm = this.getFontMetrics(this.getFont());
int nMaxStringWidth = DEFAULT_NODE_WIDTH;
if (nMaxStringWidth == 0) {
for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
strWidth = fm.stringWidth(m_BayesNet.getNodeName(iNode));
if (strWidth > nMaxStringWidth) {
nMaxStringWidth = strWidth;
}
}
}
m_nNodeWidth = nMaxStringWidth + 4;
m_nPaddedNodeWidth = m_nNodeWidth + PADDING;
m_jTfNodeWidth.setText("" + m_nNodeWidth);
m_nNodeHeight = 2 * fm.getHeight();
m_jTfNodeHeight.setText("" + m_nNodeHeight);
}
/**
* Sets the preferred size for m_GraphPanel GraphPanel to the minimum size
* that is neccessary to display the graph.
*/
public void setAppropriateSize() {
int maxX = 0, maxY = 0;
m_GraphPanel.setScale(m_fScale, m_fScale);
for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
int nPosX = m_BayesNet.getPositionX(iNode);
int nPosY = m_BayesNet.getPositionY(iNode);
if (maxX < nPosX) {
maxX = nPosX + 100;
}
if (maxY < nPosY) {
maxY = nPosY;
}
}
m_GraphPanel.setPreferredSize(new Dimension((int) ((maxX
+ m_nPaddedNodeWidth + 2) * m_fScale),
(int) ((maxY + m_nNodeHeight + 2) * m_fScale)));
m_GraphPanel.revalidate();
} // setAppropriateSize
/**
* This method is an implementation for LayoutCompleteEventListener class. It
* sets the size appropriate for m_GraphPanel GraphPanel and and revalidates
* it's container JScrollPane once a LayoutCompleteEvent is received from the
* LayoutEngine. Also, it updates positions of the Bayesian network stored in
* m_BayesNet.
*/
@Override
public void layoutCompleted(LayoutCompleteEvent le) {
LayoutEngine layoutEngine = m_layoutEngine; // (LayoutEngine)
// le.getSource();
ArrayList nPosX = new ArrayList(m_BayesNet.getNrOfNodes());
ArrayList nPosY = new ArrayList(m_BayesNet.getNrOfNodes());
for (int iNode = 0; iNode < layoutEngine.getNodes().size(); iNode++) {
GraphNode gNode = layoutEngine.getNodes().get(iNode);
if (gNode.nodeType == GraphNode.NORMAL) {
nPosX.add(gNode.x);
nPosY.add(gNode.y);
}
}
m_BayesNet.layoutGraph(nPosX, nPosY);
m_jStatusBar.setText("Graph layed out");
a_undo.setEnabled(true);
a_redo.setEnabled(false);
setAppropriateSize();
m_GraphPanel.invalidate();
m_jScrollPane.revalidate();
m_GraphPanel.repaint();
} // layoutCompleted
/**
* BIF reader
* Reads a graph description in XMLBIF03 from an file with name sFileName
*/
public void readBIFFromFile(String sFileName) throws BIFFormatException,
IOException {
m_sFileName = sFileName;
try {
BIFReader bayesNet = new BIFReader();
bayesNet.processFile(sFileName);
m_BayesNet = new EditableBayesNet(bayesNet);
updateStatus();
a_datagenerator.setEnabled(m_BayesNet.getNrOfNodes() > 0);
m_BayesNet.clearUndoStack();
} catch (Exception ex) {
ex.printStackTrace();
return;
}
setAppropriateNodeSize();
setAppropriateSize();
} // readBIFFromFile
/*
* read arff file from file sFileName and start new Bayesian network with
* nodes representing attributes in data set.
*/
void initFromArffFile(String sFileName) {
try {
Instances instances = new Instances(new FileReader(sFileName));
m_BayesNet = new EditableBayesNet(instances);
m_Instances = instances;
a_learn.setEnabled(true);
a_learnCPT.setEnabled(true);
setAppropriateNodeSize();
setAppropriateSize();
} catch (Exception ex) {
ex.printStackTrace();
return;
}
} // initFromArffFile
/**
* The panel which contains the actual Bayeian network.
*/
private class GraphPanel extends PrintablePanel implements Printable {
/** for serialization */
private static final long serialVersionUID = -3562813603236753173L;
/** node drawing modes */
final static int HIGHLIGHTED = 1;
final static int NORMAL = 0;
public GraphPanel() {
super();
this.addMouseListener(new GraphVisualizerMouseListener());
this.addMouseMotionListener(new GraphVisualizerMouseMotionListener());
this.setToolTipText("");
} // c'tor
/*
* For showing instructions when hovering over a node (non-Javadoc)
*
* @see javax.swing.JComponent#getToolTipText(java.awt.event.MouseEvent)
*/
@Override
public String getToolTipText(MouseEvent me) {
int x, y;
Rectangle r;
x = y = 0;
r = new Rectangle(0, 0, (int) (m_nPaddedNodeWidth * m_fScale),
(int) (m_nNodeHeight * m_fScale));
x += me.getX();
y += me.getY();
for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
r.x = (int) (m_BayesNet.getPositionX(iNode) * m_fScale);
r.y = (int) (m_BayesNet.getPositionY(iNode) * m_fScale);
if (r.contains(x, y)) {
return m_BayesNet.getNodeName(iNode)
+ " (right click to manipulate this node)";
}
}
return null;
} // getToolTipText
/*
* Code for showing the graph in the panel. (non-Javadoc)
*
* @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
*/
@Override
public void paintComponent(Graphics gr) {
Graphics2D g = (Graphics2D) gr;
RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
g.setRenderingHints(rh);
g.scale(m_fScale, m_fScale);
Rectangle r = g.getClipBounds();
g.clearRect(r.x, r.y, r.width, r.height);
if (m_bViewCliques) {
m_nClique = 1;
viewCliques(g, m_marginCalculator.m_root);
}
for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
drawNode(g, iNode, NORMAL);
}
if (!a_export.isExporting() && !a_print.isPrinting()) {
m_Selection.draw(g);
}
if (m_nSelectedRect != null) {
g.drawRect((int) (m_nSelectedRect.x / m_fScale),
(int) (m_nSelectedRect.y / m_fScale),
(int) (m_nSelectedRect.width / m_fScale),
(int) (m_nSelectedRect.height / m_fScale));
}
} // paintComponent
/**
* number of the clique being drawn. Used for selecting the color of the
* clique
*/
int m_nClique = 1;
/*
* draws cliques in junction tree.
*/
void viewCliques(Graphics g, JunctionTreeNode node) {
int[] nodes = node.m_nNodes;
g.setColor(new Color(m_nClique % 7 * 256 / 7, (m_nClique % 2 * 256 / 2),
(m_nClique % 3 * 256 / 3)));
int dX = m_nPaddedNodeWidth / 2 + m_nClique;
int dY = m_nNodeHeight / 2;
int nPosX = 0;
int nPosY = 0;
String sStr = "";
for (int j = 0; j < nodes.length; j++) {
nPosX += m_BayesNet.getPositionX(nodes[j]);
nPosY += m_BayesNet.getPositionY(nodes[j]);
sStr += " " + nodes[j];
for (int k = j + 1; k < nodes.length; k++) {
g.drawLine(m_BayesNet.getPositionX(nodes[j]) + dX,
m_BayesNet.getPositionY(nodes[j]) + dY,
m_BayesNet.getPositionX(nodes[k]) + dX,
m_BayesNet.getPositionY(nodes[k]) + dY);
}
}
m_nClique++;
nPosX /= nodes.length;
nPosY /= nodes.length;
g.drawString("Clique " + m_nClique + "(" + sStr + ")", nPosX, nPosY);
for (int iChild = 0; iChild < node.m_children.size(); iChild++) {
viewCliques(g, (JunctionTreeNode) node.m_children.elementAt(iChild));
}
} // viewCliques
/*
* Draw a node with index iNode on Graphics g at position Drawing mode can
* be NORMAL or HIGHLIGHTED.
*/
protected void drawNode(Graphics g, int iNode, int mode) {
int nPosX = m_BayesNet.getPositionX(iNode);
int nPosY = m_BayesNet.getPositionY(iNode);
g.setColor(this.getBackground().darker().darker());
FontMetrics fm = getFontMetrics(getFont());
if (mode == HIGHLIGHTED) {
g.setXORMode(Color.green); // g.setColor(Color.green);
}
g.fillOval(nPosX + m_nPaddedNodeWidth - m_nNodeWidth
- (m_nPaddedNodeWidth - m_nNodeWidth) / 2, nPosY, m_nNodeWidth,
m_nNodeHeight);
g.setColor(Color.white);
if (mode == HIGHLIGHTED) {
g.setXORMode(Color.red);
}
// Draw the node's label if it can fit inside the node's
// current width otherwise just display its node nr
// if it can fit in node's current width
if (fm.stringWidth(m_BayesNet.getNodeName(iNode)) <= m_nNodeWidth) {
g.drawString(m_BayesNet.getNodeName(iNode), nPosX + m_nPaddedNodeWidth
/ 2 - fm.stringWidth(m_BayesNet.getNodeName(iNode)) / 2, nPosY
+ m_nNodeHeight / 2 + fm.getHeight() / 2 - 2);
} else if (fm.stringWidth("" + iNode) <= m_nNodeWidth) {
g.drawString("" + iNode,
nPosX + m_nPaddedNodeWidth / 2 - fm.stringWidth("" + iNode) / 2,
nPosY + m_nNodeHeight / 2 + fm.getHeight() / 2 - 2);
}
if (mode == HIGHLIGHTED) {
g.setXORMode(Color.green);
}
if (m_bViewMargins) {
if (m_BayesNet.getEvidence(iNode) < 0) {
g.setColor(new Color(0, 128, 0));
} else {
g.setColor(new Color(128, 0, 0));
}
double[] P = m_BayesNet.getMargin(iNode);
for (int iValue = 0; iValue < P.length; iValue++) {
String sP = P[iValue] + "";
if (sP.charAt(0) == '0') {
sP = sP.substring(1);
}
if (sP.length() > 5) {
sP = sP.substring(1, 5);
}
g.fillRect(nPosX + m_nPaddedNodeWidth, nPosY + iValue * 10 + 2,
(int) (P[iValue] * 100), 8);
g.drawString(m_BayesNet.getNodeValue(iNode, iValue) + " " + sP, nPosX
+ m_nPaddedNodeWidth + (int) (P[iValue] * 100), nPosY + iValue * 10
+ 10);
}
}
if (m_bViewCliques) {
return;
}
g.setColor(Color.black);
// Drawing all incoming edges into the node,
for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iNode); iParent++) {
int nParent = m_BayesNet.getParent(iNode, iParent);
int nPosX1 = nPosX + m_nPaddedNodeWidth / 2;
int nPosY1 = nPosY + m_nNodeHeight;
int nPosX2 = m_BayesNet.getPositionX(nParent);
int nPosY2 = m_BayesNet.getPositionY(nParent);
int nPosX2b = nPosX2 + m_nPaddedNodeWidth / 2;
int nPosY2b = nPosY2;
double phi = Math.atan2((nPosX2b - nPosX1 + 0.0) * m_nNodeHeight,
(nPosY2b - nPosY1 + 0.0) * m_nNodeWidth);
nPosX1 = (int) (nPosX + m_nPaddedNodeWidth / 2 + Math.sin(phi)
* m_nNodeWidth / 2);
nPosY1 = (int) (nPosY + m_nNodeHeight / 2 + Math.cos(phi)
* m_nNodeHeight / 2);
nPosX2b = (int) (nPosX2 + m_nPaddedNodeWidth / 2 - Math.sin(phi)
* m_nNodeWidth / 2);
nPosY2b = (int) (nPosY2 + m_nNodeHeight / 2 - Math.cos(phi)
* m_nNodeHeight / 2);
drawArrow(g, nPosX2b, nPosY2b, nPosX1, nPosY1);
}
if (mode == HIGHLIGHTED) {
ArrayList children = m_BayesNet.getChildren(iNode);
for (int iChild = 0; iChild < children.size(); iChild++) {
int nChild = children.get(iChild);
int nPosX1 = nPosX + m_nPaddedNodeWidth / 2;
int nPosY1 = nPosY;
int nPosX2 = m_BayesNet.getPositionX(nChild);
int nPosY2 = m_BayesNet.getPositionY(nChild);
int nPosX2b = nPosX2 + m_nPaddedNodeWidth / 2;
int nPosY2b = nPosY2 + m_nNodeHeight;
double phi = Math.atan2((nPosX2b - nPosX1 + 0.0) * m_nNodeHeight,
(nPosY2b - nPosY1 + 0.0) * m_nNodeWidth);
nPosX1 = (int) (nPosX + m_nPaddedNodeWidth / 2 + Math.sin(phi)
* m_nNodeWidth / 2);
nPosY1 = (int) (nPosY + m_nNodeHeight / 2 + Math.cos(phi)
* m_nNodeHeight / 2);
nPosX2b = (int) (nPosX2 + m_nPaddedNodeWidth / 2 - Math.sin(phi)
* m_nNodeWidth / 2);
nPosY2b = (int) (nPosY2 + m_nNodeHeight / 2 - Math.cos(phi)
* m_nNodeHeight / 2);
drawArrow(g, nPosX1, nPosY1, nPosX2b, nPosY2b);
}
}
} // drawNode
/**
* This method draws an arrow on a line from (x1,y1) to (x2,y2). The arrow
* head is seated on (x2,y2) and is in the direction of the line. If the
* arrow is needed to be drawn in the opposite direction then simply swap
* the order of (x1, y1) and (x2, y2) when calling this function.
*/
protected void drawArrow(Graphics g, int nPosX1, int nPosY1, int nPosX2,
int nPosY2) {
g.drawLine(nPosX1, nPosY1, nPosX2, nPosY2);
if (nPosX1 == nPosX2) {
if (nPosY1 < nPosY2) {
g.drawLine(nPosX2, nPosY2, nPosX2 + 4, nPosY2 - 8);
g.drawLine(nPosX2, nPosY2, nPosX2 - 4, nPosY2 - 8);
} else {
g.drawLine(nPosX2, nPosY2, nPosX2 + 4, nPosY2 + 8);
g.drawLine(nPosX2, nPosY2, nPosX2 - 4, nPosY2 + 8);
}
} else {
// theta=line's angle from base, beta=angle of arrow's side from
// line
double hyp = 0, base = 0, perp = 0, theta, beta;
int nPosX3 = 0, nPosY3 = 0;
if (nPosX2 < nPosX1) {
base = nPosX1 - nPosX2;
hyp = Math.sqrt((nPosX2 - nPosX1) * (nPosX2 - nPosX1)
+ (nPosY2 - nPosY1) * (nPosY2 - nPosY1));
theta = Math.acos(base / hyp);
} else { // x1>x2 as we already checked x1==x2 before
base = nPosX1 - nPosX2;
hyp = Math.sqrt((nPosX2 - nPosX1) * (nPosX2 - nPosX1)
+ (nPosY2 - nPosY1) * (nPosY2 - nPosY1));
theta = Math.acos(base / hyp);
}
beta = 30 * Math.PI / 180;
hyp = 8;
base = Math.cos(theta - beta) * hyp;
perp = Math.sin(theta - beta) * hyp;
nPosX3 = (int) (nPosX2 + base);
if (nPosY1 < nPosY2) {
nPosY3 = (int) (nPosY2 - perp);
} else {
nPosY3 = (int) (nPosY2 + perp);
}
g.drawLine(nPosX2, nPosY2, nPosX3, nPosY3);
base = Math.cos(theta + beta) * hyp;
perp = Math.sin(theta + beta) * hyp;
nPosX3 = (int) (nPosX2 + base);
if (nPosY1 < nPosY2) {
nPosY3 = (int) (nPosY2 - perp);
} else {
nPosY3 = (int) (nPosY2 + perp);
}
g.drawLine(nPosX2, nPosY2, nPosX3, nPosY3);
}
} // drawArrow
/**
* This method highlights a given node and all its incoming and outgoing
* arcs
*/
public void highLight(int iNode) {
Graphics2D g = (Graphics2D) this.getGraphics();
RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
g.setRenderingHints(rh);
g.setPaintMode();
g.scale(m_fScale, m_fScale);
drawNode(g, iNode, HIGHLIGHTED);
} // highlight
/**
* implementation of Printable, used for printing
*
* @see Printable
*/
@Override
public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
if (pageIndex > 0) {
return (NO_SUCH_PAGE);
} else {
Graphics2D g2d = (Graphics2D) g;
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
double fHeight = pageFormat.getImageableHeight();
double fWidth = pageFormat.getImageableWidth();
int xMax = 1;
int yMax = 1;
for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
if (xMax < m_BayesNet.getPositionX(iNode)) {
xMax = m_BayesNet.getPositionX(iNode);
}
if (yMax < m_BayesNet.getPositionY(iNode)) {
yMax = m_BayesNet.getPositionY(iNode);
}
}
double fCurrentScale = m_fScale;
xMax += m_nPaddedNodeWidth + 100;
if (fWidth / xMax < fHeight / yMax) {
m_fScale = fWidth / xMax;
} else {
m_fScale = fHeight / yMax;
}
// Turn off double buffering
paint(g2d);
m_fScale = fCurrentScale;
// Turn double buffering back on
return (PAGE_EXISTS);
}
} // print
} // class GraphPanel
/**
* Table Model for the Table for editing CPTs
*/
private class GraphVisualizerTableModel extends AbstractTableModel {
/** for serialization */
private static final long serialVersionUID = -4789813491347366596L;
/** labels for the columns */
final String[] m_sColumnNames;
/** probability table data **/
final double[][] m_fProbs;
public GraphVisualizerTableModel(int iNode) {
double[][] probs = m_BayesNet.getDistribution(iNode);
m_fProbs = new double[probs.length][probs[0].length];
for (int i = 0; i < probs.length; i++) {
for (int j = 0; j < probs[0].length; j++) {
m_fProbs[i][j] = probs[i][j];
}
}
m_sColumnNames = m_BayesNet.getValues(iNode);
} // c'tor
/**
* method that generates random CPTs
*/
public void randomize() {
int nProbs = m_fProbs[0].length;
Random random = new Random();
for (int i = 0; i < m_fProbs.length; i++) {
// get random nrs
for (int j = 0; j < nProbs - 1; j++) {
m_fProbs[i][j] = random.nextDouble();
}
// sort
for (int j = 0; j < nProbs - 1; j++) {
for (int k = j + 1; k < nProbs - 1; k++) {
if (m_fProbs[i][j] > m_fProbs[i][k]) {
double h = m_fProbs[i][j];
m_fProbs[i][j] = m_fProbs[i][k];
m_fProbs[i][k] = h;
}
}
}
double sum = m_fProbs[i][0];
for (int j = 1; j < nProbs - 1; j++) {
m_fProbs[i][j] = m_fProbs[i][j] - sum;
sum += m_fProbs[i][j];
}
m_fProbs[i][nProbs - 1] = 1.0 - sum;
}
} // randomize
public void setData() {
}
/** return nr of colums */
@Override
public int getColumnCount() {
return m_sColumnNames.length;
}
/** return nr of rows */
@Override
public int getRowCount() {
return m_fProbs.length;
}
/**
* return name of specified colum
*
* @param iCol index of the column
*/
@Override
public String getColumnName(int iCol) {
return m_sColumnNames[iCol];
}
/**
* return data point
*
* @param iRow index of row in table
* @param iCol index of column in table
*/
@Override
public Object getValueAt(int iRow, int iCol) {
return new Double(m_fProbs[iRow][iCol]);
}
/**
* Set data point, assigns value to CPT entry specified by row and column.
* The remainder of the CPT is normalized so that the values add up to 1. IF
* a value below zero of over 1 is given, no changes take place.
*
* @param oProb data point
* @param iRow index of row in table
* @param iCol index of column in table
*/
@Override
public void setValueAt(Object oProb, int iRow, int iCol) {
Double fProb = (Double) oProb;
if (fProb < 0 || fProb > 1) {
return;
}
m_fProbs[iRow][iCol] = fProb;
double sum = 0;
for (int i = 0; i < m_fProbs[iRow].length; i++) {
sum += m_fProbs[iRow][i];
}
if (sum > 1) {
// handle overflow
int i = m_fProbs[iRow].length - 1;
while (sum > 1) {
if (i != iCol) {
if (m_fProbs[iRow][i] > sum - 1) {
m_fProbs[iRow][i] -= sum - 1;
sum = 1;
} else {
sum -= m_fProbs[iRow][i];
m_fProbs[iRow][i] = 0;
}
}
i--;
}
} else {
// handle underflow
int i = m_fProbs[iRow].length - 1;
while (sum < 1) {
if (i != iCol) {
m_fProbs[iRow][i] += 1 - sum;
sum = 1;
}
i--;
}
}
validate();
} // setData
/*
* JTable uses this method to determine the default renderer/ editor for
* each cell.
*/
@Override
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
/*
* Implemented this to make sure the table is uneditable.
*/
@Override
public boolean isCellEditable(int row, int col) {
return true;
}
} // class GraphVisualizerTableModel
/**
* Listener class for processing mouseClicked
*/
private class GraphVisualizerMouseListener extends MouseAdapter {
/**
* A left mouseclick on a node adds node to selection (depending on shift
* and ctrl keys). A right mouseclick on a node pops up menu with actions to
* be performed on the node. A right mouseclick outside another node pops up
* menu.
*/
@Override
public void mouseClicked(MouseEvent me) {
int x, y;
Rectangle r = new Rectangle(0, 0, (int) (m_nPaddedNodeWidth * m_fScale),
(int) (m_nNodeHeight * m_fScale));
x = me.getX();
y = me.getY();
for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
r.x = (int) (m_BayesNet.getPositionX(iNode) * m_fScale);
r.y = (int) (m_BayesNet.getPositionY(iNode) * m_fScale);
if (r.contains(x, y)) {
m_nCurrentNode = iNode;
if (me.getButton() == MouseEvent.BUTTON3) {
handleRightNodeClick(me);
}
if (me.getButton() == MouseEvent.BUTTON1) {
if ((me.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0) {
m_Selection.toggleSelection(m_nCurrentNode);
} else if ((me.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) {
m_Selection.addToSelection(m_nCurrentNode);
} else {
m_Selection.clear();
m_Selection.addToSelection(m_nCurrentNode);
}
repaint();
}
return;
}
}
if (me.getButton() == MouseEvent.BUTTON3) {
handleRightClick(me, (int) (x / m_fScale), (int) (y / m_fScale));
}
} // mouseClicked
/*
* update selection (non-Javadoc)
*
* @see
* java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
*/
@Override
public void mouseReleased(MouseEvent me) {
if (m_nSelectedRect != null) {
if ((me.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0) {
m_Selection.toggleSelection(m_nSelectedRect);
} else if ((me.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) {
m_Selection.addToSelection(m_nSelectedRect);
} else {
m_Selection.clear();
m_Selection.addToSelection(m_nSelectedRect);
}
m_nSelectedRect = null;
repaint();
}
} // mouseReleased
/** position clicked on */
int m_nPosX = 0, m_nPosY = 0;
/*
* pop up menu with actions that apply in general or to selection (if any
* exists)
*/
void handleRightClick(MouseEvent me, int nPosX, int nPosY) {
ActionListener act = new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
if (ae.getActionCommand().equals("Add node")) {
a_addnode.addNode(m_nPosX, m_nPosY);
return;
}
repaint();
}
};
JPopupMenu popupMenu = new JPopupMenu("Choose a value");
JMenuItem addNodeItem = new JMenuItem("Add node");
addNodeItem.addActionListener(act);
popupMenu.add(addNodeItem);
ArrayList selected = m_Selection.getSelected();
JMenu addArcMenu = new JMenu("Add parent");
popupMenu.add(addArcMenu);
if (selected.size() == 0) {
addArcMenu.setEnabled(false);
} else {
int nNodes = m_BayesNet.getNrOfNodes();
boolean[] isNotAllowedAsParent = new boolean[nNodes];
// prevent it being a parent of itself
for (int iNode = 0; iNode < selected.size(); iNode++) {
isNotAllowedAsParent[selected.get(iNode)] = true;
}
// prevent a descendant being a parent, since it introduces cycles
for (int i = 0; i < nNodes; i++) {
for (int iNode = 0; iNode < nNodes; iNode++) {
for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iNode); iParent++) {
if (isNotAllowedAsParent[m_BayesNet.getParent(iNode, iParent)]) {
isNotAllowedAsParent[iNode] = true;
}
}
}
}
// prevent nodes that are already a parent
for (int iNode = 0; iNode < selected.size(); iNode++) {
int nNode = selected.get(iNode);
for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(nNode); iParent++) {
isNotAllowedAsParent[m_BayesNet.getParent(nNode, iParent)] = true;
}
}
ActionListener addParentAction = new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
try {
m_BayesNet.addArc(ae.getActionCommand(),
m_Selection.getSelected());
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
updateStatus();
} catch (Exception e) {
e.printStackTrace();
}
}
};
// count nr of remaining candidates
int nCandidates = 0;
for (int i = 0; i < nNodes; i++) {
if (!isNotAllowedAsParent[i]) {
JMenuItem item = new JMenuItem(m_BayesNet.getNodeName(i));
item.addActionListener(addParentAction);
addArcMenu.add(item);
nCandidates++;
}
}
if (nCandidates == 0) {
addArcMenu.setEnabled(false);
}
}
m_nPosX = nPosX;
m_nPosY = nPosY;
popupMenu.setLocation(me.getX(), me.getY());
popupMenu.show(m_GraphPanel, me.getX(), me.getY());
} // handleRightClick
/*
* pop up menu with actions that apply to node that was clicked on
*/
void handleRightNodeClick(MouseEvent me) {
m_Selection.clear();
repaint();
ActionListener renameValueAction = new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
renameValue(m_nCurrentNode, ae.getActionCommand());
}
};
ActionListener delValueAction = new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
delValue(m_nCurrentNode, ae.getActionCommand());
}
};
ActionListener addParentAction = new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
try {
m_BayesNet.addArc(ae.getActionCommand(),
m_BayesNet.getNodeName(m_nCurrentNode));
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
updateStatus();
} catch (Exception e) {
e.printStackTrace();
}
}
};
ActionListener delParentAction = new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
deleteArc(m_nCurrentNode, ae.getActionCommand());
}
};
ActionListener delChildAction = new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
deleteArc(ae.getActionCommand(), m_nCurrentNode);
}
};
ActionListener setAvidenceAction = new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
try {
String[] outcomes = m_BayesNet.getValues(m_nCurrentNode);
int iValue = 0;
while (iValue < outcomes.length
&& !outcomes[iValue].equals(ae.getActionCommand())) {
iValue++;
}
if (iValue == outcomes.length) {
iValue = -1;
}
if (iValue < outcomes.length) {
m_jStatusBar.setText("Set evidence for "
+ m_BayesNet.getNodeName(m_nCurrentNode));
if (m_BayesNet.getEvidence(m_nCurrentNode) < 0 && iValue >= 0) {
m_BayesNet.setEvidence(m_nCurrentNode, iValue);
m_marginCalculatorWithEvidence.setEvidence(m_nCurrentNode,
iValue);
} else {
m_BayesNet.setEvidence(m_nCurrentNode, iValue);
SerializedObject so = new SerializedObject(m_marginCalculator);
m_marginCalculatorWithEvidence = (MarginCalculator) so
.getObject();
for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
if (m_BayesNet.getEvidence(iNode) >= 0) {
m_marginCalculatorWithEvidence.setEvidence(iNode,
m_BayesNet.getEvidence(iNode));
}
}
}
for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
m_BayesNet.setMargin(iNode,
m_marginCalculatorWithEvidence.getMargin(iNode));
}
}
} catch (Exception e) {
e.printStackTrace();
}
repaint();
}
};
ActionListener act = new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
if (ae.getActionCommand().equals("Rename")) {
renameNode(m_nCurrentNode);
return;
}
if (ae.getActionCommand().equals("Add parent")) {
addArcInto(m_nCurrentNode);
return;
}
if (ae.getActionCommand().equals("Add value")) {
addValue();
return;
}
if (ae.getActionCommand().equals("Delete node")) {
deleteNode(m_nCurrentNode);
return;
}
if (ae.getActionCommand().equals("Edit CPT")) {
editCPT(m_nCurrentNode);
return;
}
repaint();
}
};
try {
JPopupMenu popupMenu = new JPopupMenu("Choose a value");
JMenu setEvidenceMenu = new JMenu("Set evidence");
String[] outcomes = m_BayesNet.getValues(m_nCurrentNode);
for (String outcome : outcomes) {
JMenuItem item = new JMenuItem(outcome);
item.addActionListener(setAvidenceAction);
setEvidenceMenu.add(item);
}
setEvidenceMenu.addSeparator();
JMenuItem item = new JMenuItem("Clear");
item.addActionListener(setAvidenceAction);
setEvidenceMenu.add(item);
popupMenu.add(setEvidenceMenu);
setEvidenceMenu.setEnabled(m_bViewMargins);
popupMenu.addSeparator();
JMenuItem renameItem = new JMenuItem("Rename");
renameItem.addActionListener(act);
popupMenu.add(renameItem);
JMenuItem delNodeItem = new JMenuItem("Delete node");
delNodeItem.addActionListener(act);
popupMenu.add(delNodeItem);
JMenuItem editCPTItem = new JMenuItem("Edit CPT");
editCPTItem.addActionListener(act);
popupMenu.add(editCPTItem);
popupMenu.addSeparator();
JMenu addArcMenu = new JMenu("Add parent");
popupMenu.add(addArcMenu);
int nNodes = m_BayesNet.getNrOfNodes();
boolean[] isNotAllowedAsParent = new boolean[nNodes];
// prevent it being a parent of itself
isNotAllowedAsParent[m_nCurrentNode] = true;
// prevent a descendant being a parent, since it introduces cycles
for (int i = 0; i < nNodes; i++) {
for (int iNode = 0; iNode < nNodes; iNode++) {
for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iNode); iParent++) {
if (isNotAllowedAsParent[m_BayesNet.getParent(iNode, iParent)]) {
isNotAllowedAsParent[iNode] = true;
}
}
}
}
// prevent nodes that are already a parent
for (int iParent = 0; iParent < m_BayesNet
.getNrOfParents(m_nCurrentNode); iParent++) {
isNotAllowedAsParent[m_BayesNet.getParent(m_nCurrentNode, iParent)] = true;
}
// count nr of remaining candidates
int nCandidates = 0;
for (int i = 0; i < nNodes; i++) {
if (!isNotAllowedAsParent[i]) {
item = new JMenuItem(m_BayesNet.getNodeName(i));
item.addActionListener(addParentAction);
addArcMenu.add(item);
nCandidates++;
}
}
if (nCandidates == 0) {
addArcMenu.setEnabled(false);
}
JMenu delArcMenu = new JMenu("Delete parent");
popupMenu.add(delArcMenu);
if (m_BayesNet.getNrOfParents(m_nCurrentNode) == 0) {
delArcMenu.setEnabled(false);
}
for (int iParent = 0; iParent < m_BayesNet
.getNrOfParents(m_nCurrentNode); iParent++) {
item = new JMenuItem(m_BayesNet.getNodeName(m_BayesNet.getParent(
m_nCurrentNode, iParent)));
item.addActionListener(delParentAction);
delArcMenu.add(item);
}
JMenu delChildMenu = new JMenu("Delete child");
popupMenu.add(delChildMenu);
ArrayList nChildren = m_BayesNet.getChildren(m_nCurrentNode);
if (nChildren.size() == 0) {
delChildMenu.setEnabled(false);
}
for (int iChild = 0; iChild < nChildren.size(); iChild++) {
item = new JMenuItem(m_BayesNet.getNodeName(nChildren.get(iChild)));
item.addActionListener(delChildAction);
delChildMenu.add(item);
}
popupMenu.addSeparator();
JMenuItem addValueItem = new JMenuItem("Add value");
addValueItem.addActionListener(act);
popupMenu.add(addValueItem);
JMenu renameValue = new JMenu("Rename value");
popupMenu.add(renameValue);
for (String outcome : outcomes) {
item = new JMenuItem(outcome);
item.addActionListener(renameValueAction);
renameValue.add(item);
}
JMenu delValue = new JMenu("Delete value");
popupMenu.add(delValue);
if (m_BayesNet.getCardinality(m_nCurrentNode) <= 2) {
delValue.setEnabled(false);
}
for (String outcome : outcomes) {
JMenuItem delValueItem = new JMenuItem(outcome);
delValueItem.addActionListener(delValueAction);
delValue.add(delValueItem);
}
popupMenu.setLocation(me.getX(), me.getY());
popupMenu.show(m_GraphPanel, me.getX(), me.getY());
} catch (Exception e) {
e.printStackTrace();
}
} // handleRightNodeClick
} // class GraphVisualizerMouseListener
/**
* private class for handling mouseMoved events to highlight nodes if the the
* mouse is moved on one, move it around or move selection around
*/
private class GraphVisualizerMouseMotionListener extends MouseMotionAdapter {
/* last node moved over. Used for turning highlight on and off */
int m_nLastNode = -1;
/* current mouse position clicked */
int m_nPosX, m_nPosY;
/*
* identify the node under the mouse
*
* @returns node index of node under mouse, or -1 if there is no such node
*/
int getGraphNode(MouseEvent me) {
m_nPosX = m_nPosY = 0;
Rectangle r = new Rectangle(0, 0, (int) (m_nPaddedNodeWidth * m_fScale),
(int) (m_nNodeHeight * m_fScale));
m_nPosX += me.getX();
m_nPosY += me.getY();
for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
r.x = (int) (m_BayesNet.getPositionX(iNode) * m_fScale);
r.y = (int) (m_BayesNet.getPositionY(iNode) * m_fScale);
if (r.contains(m_nPosX, m_nPosY)) {
return iNode;
}
}
return -1;
} // getGraphNode
/*
* handle mouse dragging event (non-Javadoc)
*
* @see
* java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent
* )
*/
@Override
public void mouseDragged(MouseEvent me) {
if (m_nSelectedRect != null) {
m_nSelectedRect.width = me.getPoint().x - m_nSelectedRect.x;
m_nSelectedRect.height = me.getPoint().y - m_nSelectedRect.y;
repaint();
return;
}
int iNode = getGraphNode(me);
if (iNode >= 0) {
if (m_Selection.getSelected().size() > 0) {
if (m_Selection.getSelected().contains(iNode)) {
m_BayesNet.setPosition(iNode,
(int) ((m_nPosX / m_fScale - m_nPaddedNodeWidth / 2)),
(int) ((m_nPosY / m_fScale - m_nNodeHeight / 2)),
m_Selection.getSelected());
} else {
m_Selection.clear();
m_BayesNet.setPosition(iNode,
(int) ((m_nPosX / m_fScale - m_nPaddedNodeWidth / 2)),
(int) ((m_nPosY / m_fScale - m_nNodeHeight / 2)));
}
repaint();
} else {
m_BayesNet.setPosition(iNode,
(int) ((m_nPosX / m_fScale - m_nPaddedNodeWidth / 2)),
(int) ((m_nPosY / m_fScale - m_nNodeHeight / 2)));
}
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
a_undo.setEnabled(true);
a_redo.setEnabled(false);
m_GraphPanel.highLight(iNode);
}
if (iNode < 0) {
if (m_nLastNode >= 0) {
m_GraphPanel.repaint();
m_nLastNode = -1;
} else {
m_nSelectedRect = new Rectangle(me.getPoint().x, me.getPoint().y, 1,
1);
m_GraphPanel.repaint();
}
}
} // mouseDragged
/*
* handles mouse move event (non-Javadoc)
*
* @see
* java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
*/
@Override
public void mouseMoved(MouseEvent me) {
int iNode = getGraphNode(me);
if (iNode >= 0) {
if (iNode != m_nLastNode) {
m_GraphPanel.highLight(iNode);
if (m_nLastNode >= 0) {
m_GraphPanel.highLight(m_nLastNode);
}
m_nLastNode = iNode;
}
}
if (iNode < 0 && m_nLastNode >= 0) {
m_GraphPanel.repaint();
m_nLastNode = -1;
}
} // mouseMoved
} // class GraphVisualizerMouseMotionListener
/*
* apply graph layout algorithm to Bayesian network
*/
void layoutGraph() {
if (m_BayesNet.getNrOfNodes() == 0) {
return;
}
try {
ArrayList m_nodes = new ArrayList();
ArrayList m_edges = new ArrayList();
BIFParser bp = new BIFParser(m_BayesNet.toXMLBIF03(), m_nodes, m_edges);
bp.parse();
updateStatus();
m_layoutEngine = new HierarchicalBCEngine(m_nodes, m_edges,
m_nPaddedNodeWidth, m_nNodeHeight);
m_layoutEngine.addLayoutCompleteEventListener(this);
m_layoutEngine.layoutGraph();
} catch (Exception e) {
e.printStackTrace();
}
} // layoutGraph
/*
* Update status of various items that need regular updating such as enabled
* status of some menu items, marginal distributions if shown, repainting of
* graph.
*/
void updateStatus() {
a_undo.setEnabled(m_BayesNet.canUndo());
a_redo.setEnabled(m_BayesNet.canRedo());
a_datagenerator.setEnabled(m_BayesNet.getNrOfNodes() > 0);
if (!m_bViewMargins && !m_bViewCliques) {
repaint();
return;
}
try {
m_marginCalculator = new MarginCalculator();
m_marginCalculator.calcMargins(m_BayesNet);
SerializedObject so = new SerializedObject(m_marginCalculator);
m_marginCalculatorWithEvidence = (MarginCalculator) so.getObject();
for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
if (m_BayesNet.getEvidence(iNode) >= 0) {
m_marginCalculatorWithEvidence.setEvidence(iNode,
m_BayesNet.getEvidence(iNode));
}
}
for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
m_BayesNet.setMargin(iNode,
m_marginCalculatorWithEvidence.getMargin(iNode));
}
} catch (Exception e) {
e.printStackTrace();
}
repaint();
} // updateStatus
/*
* add arc with node iChild as child. This pops up a selection list with
* potential parents for the child. All decendants and current parents are
* excluded from the list as is the child node itself.
*
* @param iChild index of the node for which to add an arc
*/
void addArcInto(int iChild) {
String sChild = m_BayesNet.getNodeName(iChild);
try {
int nNodes = m_BayesNet.getNrOfNodes();
boolean[] isNotAllowedAsParent = new boolean[nNodes];
// prevent it being a parent of itself
isNotAllowedAsParent[iChild] = true;
// prevent a descendant being a parent, since it introduces cycles
for (int i = 0; i < nNodes; i++) {
for (int iNode = 0; iNode < nNodes; iNode++) {
for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iNode); iParent++) {
if (isNotAllowedAsParent[m_BayesNet.getParent(iNode, iParent)]) {
isNotAllowedAsParent[iNode] = true;
}
}
}
}
// prevent nodes that are already a parent
for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iChild); iParent++) {
isNotAllowedAsParent[m_BayesNet.getParent(iChild, iParent)] = true;
}
// count nr of remaining candidates
int nCandidates = 0;
for (int i = 0; i < nNodes; i++) {
if (!isNotAllowedAsParent[i]) {
nCandidates++;
}
}
if (nCandidates == 0) {
JOptionPane.showMessageDialog(null,
"No potential parents available for this node (" + sChild
+ "). Choose another node as child node.");
return;
}
String[] options = new String[nCandidates];
int k = 0;
for (int i = 0; i < nNodes; i++) {
if (!isNotAllowedAsParent[i]) {
options[k++] = m_BayesNet.getNodeName(i);
}
}
String sParent = (String) JOptionPane.showInputDialog(null,
"Select parent node for " + sChild, "Nodes", 0, null, options,
options[0]);
if (sParent == null || sParent.equals("")) {
return;
}
// update all data structures
m_BayesNet.addArc(sParent, sChild);
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
updateStatus();
} catch (Exception e) {
e.printStackTrace();
}
} // addArcInto
/*
* deletes arc from node with name sParent into child with index iChild
*/
void deleteArc(int iChild, String sParent) {
try {
m_BayesNet.deleteArc(m_BayesNet.getNode(sParent), iChild);
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
} catch (Exception e) {
e.printStackTrace();
}
updateStatus();
} // deleteArc
/*
* deletes arc from node with index iParent into child with name sChild
*/
void deleteArc(String sChild, int iParent) {
try {
m_BayesNet.deleteArc(iParent, m_BayesNet.getNode(sChild));
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
} catch (Exception e) {
e.printStackTrace();
}
updateStatus();
} // deleteArc
/*
* deletes arc. Pops up list of arcs listed in 'options' as
* " -> ".
*/
void deleteArc(String[] options) {
String sResult = (String) JOptionPane.showInputDialog(null,
"Select arc to delete", "Arcs", 0, null, options, options[0]);
if (sResult != null && !sResult.equals("")) {
int nPos = sResult.indexOf(" -> ");
String sParent = sResult.substring(0, nPos);
String sChild = sResult.substring(nPos + 4);
try {
m_BayesNet.deleteArc(sParent, sChild);
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
} catch (Exception e) {
e.printStackTrace();
}
updateStatus();
}
} // deleteArc
/*
* Rename node with index nTargetNode. Pops up window that allwos for entering
* a new name.
*/
void renameNode(int nTargetNode) {
String sName = JOptionPane.showInputDialog(null,
m_BayesNet.getNodeName(nTargetNode), "New name for node",
JOptionPane.OK_CANCEL_OPTION);
if (sName == null || sName.equals("")) {
return;
}
try {
while (m_BayesNet.getNode2(sName) >= 0) {
sName = JOptionPane.showInputDialog(null, "Cannot rename to " + sName
+ ".\nNode with that name already exists.");
if (sName == null || sName.equals("")) {
return;
}
}
m_BayesNet.setNodeName(nTargetNode, sName);
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
} catch (Exception e) {
e.printStackTrace();
}
repaint();
} // renameNode
/*
* Rename value with name sValeu of a node with index nTargetNode. Pops up
* window that allows entering a new name.
*/
void renameValue(int nTargetNode, String sValue) {
String sNewValue = JOptionPane.showInputDialog(null, "New name for value "
+ sValue, "Node " + m_BayesNet.getNodeName(nTargetNode),
JOptionPane.OK_CANCEL_OPTION);
if (sNewValue == null || sNewValue.equals("")) {
return;
}
m_BayesNet.renameNodeValue(nTargetNode, sValue, sNewValue);
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
a_undo.setEnabled(true);
a_redo.setEnabled(false);
repaint();
} // renameValue
/* delete a single node with index iNode */
void deleteNode(int iNode) {
try {
m_BayesNet.deleteNode(iNode);
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
} catch (Exception e) {
e.printStackTrace();
}
updateStatus();
} // deleteNode
/*
* Add a value to currently selected node. Shows window that allows to enter
* the name of the value.
*/
void addValue() {
// GraphNode n = (GraphNode) m_nodes.elementAt(m_nCurrentNode);
String sValue = "Value" + (m_BayesNet.getCardinality(m_nCurrentNode) + 1);
String sNewValue = JOptionPane.showInputDialog(null, "New value " + sValue,
"Node " + m_BayesNet.getNodeName(m_nCurrentNode),
JOptionPane.OK_CANCEL_OPTION);
if (sNewValue == null || sNewValue.equals("")) {
return;
}
try {
m_BayesNet.addNodeValue(m_nCurrentNode, sNewValue);
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
// n.outcomes = m_BayesNet.getValues(m_nCurrentNode);
// for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
// n = (GraphNode) m_nodes.elementAt(iNode);
// n.probs = m_BayesNet.getDistribution(iNode);
// }
} catch (Exception e) {
e.printStackTrace();
}
updateStatus();
} // addValue
/*
* remove value with name sValue from the node with index nTargetNode
*/
void delValue(int nTargetNode, String sValue) {
try {
m_BayesNet.delNodeValue(nTargetNode, sValue);
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
} catch (Exception e) {
e.printStackTrace();
}
updateStatus();
} // delValue
/*
* Edits CPT of node with index nTargetNode. Pops up table with probability
* table that the user can change or just view.
*/
void editCPT(int nTargetNode) {
m_nCurrentNode = nTargetNode;
final GraphVisualizerTableModel tm = new GraphVisualizerTableModel(
nTargetNode);
JTable jTblProbs = new JTable(tm);
JScrollPane js = new JScrollPane(jTblProbs);
int nParents = m_BayesNet.getNrOfParents(nTargetNode);
if (nParents > 0) {
GridBagConstraints gbc = new GridBagConstraints();
JPanel jPlRowHeader = new JPanel(new GridBagLayout());
// indices of the parent nodes in the Vector
int[] idx = new int[nParents];
// max length of values of each parent
int[] lengths = new int[nParents];
// Adding labels for rows
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(0, 1, 0, 0);
int addNum = 0, temp = 0;
boolean dark = false;
while (true) {
gbc.gridwidth = 1;
for (int k = 0; k < nParents; k++) {
int iParent2 = m_BayesNet.getParent(nTargetNode, k);
JLabel lb = new JLabel(m_BayesNet.getValueName(iParent2, idx[k]));
lb.setFont(new Font("Dialog", Font.PLAIN, 12));
lb.setOpaque(true);
lb.setBorder(BorderFactory.createEmptyBorder(1, 2, 1, 1));
lb.setHorizontalAlignment(JLabel.CENTER);
if (dark) {
lb.setBackground(lb.getBackground().darker());
lb.setForeground(Color.white);
} else {
lb.setForeground(Color.black);
}
temp = lb.getPreferredSize().width;
lb.setPreferredSize(new Dimension(temp, jTblProbs.getRowHeight()));
if (lengths[k] < temp) {
lengths[k] = temp;
}
temp = 0;
if (k == nParents - 1) {
gbc.gridwidth = GridBagConstraints.REMAINDER;
dark = (dark == true) ? false : true;
}
jPlRowHeader.add(lb, gbc);
addNum++;
}
for (int k = nParents - 1; k >= 0; k--) {
int iParent2 = m_BayesNet.getParent(m_nCurrentNode, k);
if (idx[k] == m_BayesNet.getCardinality(iParent2) - 1 && k != 0) {
idx[k] = 0;
continue;
} else {
idx[k]++;
break;
}
}
int iParent2 = m_BayesNet.getParent(m_nCurrentNode, 0);
if (idx[0] == m_BayesNet.getCardinality(iParent2)) {
JLabel lb = (JLabel) jPlRowHeader.getComponent(addNum - 1);
jPlRowHeader.remove(addNum - 1);
lb.setPreferredSize(new Dimension(lb.getPreferredSize().width,
jTblProbs.getRowHeight()));
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weighty = 1;
jPlRowHeader.add(lb, gbc);
gbc.weighty = 0;
break;
}
}
gbc.gridwidth = 1;
// The following panel contains the names of the
// parents
// and is displayed above the row names to identify
// which value belongs to which parent
JPanel jPlRowNames = new JPanel(new GridBagLayout());
for (int j = 0; j < nParents; j++) {
JLabel lb2;
JLabel lb1 = new JLabel(m_BayesNet.getNodeName(m_BayesNet.getParent(
nTargetNode, j)));
lb1.setBorder(BorderFactory.createEmptyBorder(1, 2, 1, 1));
Dimension tempd = lb1.getPreferredSize();
if (tempd.width < lengths[j]) {
lb1.setPreferredSize(new Dimension(lengths[j], tempd.height));
lb1.setHorizontalAlignment(JLabel.CENTER);
lb1.setMinimumSize(new Dimension(lengths[j], tempd.height));
} else if (tempd.width > lengths[j]) {
lb2 = (JLabel) jPlRowHeader.getComponent(j);
lb2.setPreferredSize(new Dimension(tempd.width, lb2
.getPreferredSize().height));
}
jPlRowNames.add(lb1, gbc);
}
js.setRowHeaderView(jPlRowHeader);
js.setCorner(JScrollPane.UPPER_LEFT_CORNER, jPlRowNames);
}
final JDialog dlg = new JDialog((Frame) GUI.this.getTopLevelAncestor(),
"Probability Distribution Table For "
+ m_BayesNet.getNodeName(nTargetNode), true);
dlg.setSize(500, 400);
dlg.setLocation(GUI.this.getLocation().x + GUI.this.getWidth() / 2 - 250,
GUI.this.getLocation().y + GUI.this.getHeight() / 2 - 200);
dlg.getContentPane().setLayout(new BorderLayout());
dlg.getContentPane().add(js, BorderLayout.CENTER);
JButton jBtRandomize = new JButton("Randomize");
jBtRandomize.setMnemonic('R');
jBtRandomize.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
tm.randomize();
dlg.repaint();
}
});
JButton jBtOk = new JButton("Ok");
jBtOk.setMnemonic('O');
jBtOk.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
tm.setData();
try {
m_BayesNet.setDistribution(m_nCurrentNode, tm.m_fProbs);
m_jStatusBar.setText(m_BayesNet.lastActionMsg());
updateStatus();
} catch (Exception e) {
e.printStackTrace();
}
dlg.setVisible(false);
}
});
JButton jBtCancel = new JButton("Cancel");
jBtCancel.setMnemonic('C');
jBtCancel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
dlg.setVisible(false);
}
});
Container c = new Container();
c.setLayout(new GridBagLayout());
c.add(jBtRandomize);
c.add(jBtOk);
c.add(jBtCancel);
dlg.getContentPane().add(c, BorderLayout.SOUTH);
dlg.setVisible(true);
} // editCPT
/**
* Main method. Builds up menus and reads from file if one is specified.
*/
public static void main(String[] args) {
weka.core.logging.Logger.log(weka.core.logging.Logger.Level.INFO,
"Logging started");
LookAndFeel.setLookAndFeel();
JFrame jf = new JFrame("Bayes Network Editor");
final GUI g = new GUI();
JMenuBar menuBar = g.getMenuBar();
if (args.length > 0) {
try {
g.readBIFFromFile(args[0]);
} catch (IOException ex) {
ex.printStackTrace();
} catch (BIFFormatException bf) {
bf.printStackTrace();
System.exit(-1);
}
}
jf.setJMenuBar(menuBar);
jf.getContentPane().add(g);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(800, 600);
jf.setVisible(true);
g.m_Selection.updateGUI();
GenericObjectEditor.registerEditors();
} // main
} // end of class