📄 graphvisualizer.java
字号:
/*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* GraphVisualizer.java
* Copyright (C) 2003 Ashraf M. Kibriya
*
*/
package weka.gui.graphvisualizer;
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.Insets;
import java.awt.LayoutManager;
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.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.table.AbstractTableModel;
import weka.core.FastVector;
import weka.gui.ExtensionFileFilter;
/**
* This class displays the graph we want to visualize. It should
* be sufficient to use only this class in weka.gui.graphvisulizer
* package to visualize a graph. The description of a graph should
* be provided as a string argument using readBIF or readDOT method
* in either XMLBIF03 or DOT format. Alternatively, an InputStream
* in XMLBIF03 can also be provided to another variation of readBIF.
* It would be necessary in case input is in DOT format to call the
* layoutGraph() method to display the graph correctly after the call
* to readDOT. It is also necessary to do so if readBIF is called and
* the graph description doesn't have x y positions for nodes.
* <p> The graph's data is held in two FastVectors, nodes are stored as
* objects of GraphNode class and edges as objects of GraphEdge class.
* <p> The graph is displayed by positioning and drawing each node
* according to its x y position and then drawing all the edges coming
* out of it give by its edges[][] array, the arrow heads are ofcourse
* marked in the opposite(ie original direction) or both directions if
* the edge is reversed or is in both directions. The graph is centered
* if it is smaller than it's display area. The edges are drawn from the
* bottom of the current node to the top of the node given by edges[][]
* array in GraphNode class, to avoid edges crossing over other nodes.
* This might need to be changed if another layout engine is added or
* the current Hierarchical engine is updated to avoid such crossings
* over nodes.
*
* @author Ashraf M. Kibriya (amk14@cs.waikato.ac.nz)
* @version 1.0 - 24 Apr 2003 - Initial version (Ashraf M. Kibriya)
*/
public class GraphVisualizer extends JPanel implements GraphConstants, LayoutCompleteEventListener {
/**
*
*/
private static final long serialVersionUID = -2986996763127957595L;
/** Vector containing nodes */
protected FastVector m_nodes=new FastVector();
/** Vector containing edges */
protected FastVector m_edges=new FastVector();
/** The current LayoutEngine */
protected LayoutEngine m_le;
/** Panel actually displaying the graph */
protected GraphPanel m_gp;
/** String containing graph's name */
protected String graphID;
/**
Save button to save the current graph in DOT or XMLBIF format.
The graph should be layed out again to get the original form
if reloaded from command line, as the formats do not allow
saving specific information for a properly layed out graph.
*/
protected JButton m_jBtSave;
/** path for icons */
private final String ICONPATH = "weka/gui/graphvisualizer/icons/";
private FontMetrics fm = this.getFontMetrics( this.getFont() );
private double scale = 1; //current zoom
private int nodeHeight = 2*fm.getHeight(), nodeWidth = 24, paddedNodeWidth = 24+8;
/** TextField for node's width */
private final JTextField jTfNodeWidth = new JTextField(3);
/** TextField for nodes height */
private final JTextField jTfNodeHeight = new JTextField(3);
/** Button for laying out the graph again, necessary after changing node's size or
some other property of the layout engine */
private final JButton jBtLayout;
/** used for setting appropriate node size */
private int maxStringWidth=0;
/** used when using zoomIn and zoomOut buttons */
private int [] zoomPercents = { 10, 25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300,
350, 400, 450, 500, 550, 600, 650, 700, 800, 900, 999 };
/** this contains the m_gp GraphPanel */
JScrollPane m_js;
/**
* Constructor<br>
* Sets up the gui and initializes all the other previously
* uninitialized variables.
*/
public GraphVisualizer() {
m_gp = new GraphPanel();
m_js = new JScrollPane(m_gp);
//creating a new layout engine and adding this class as its listener
// to receive layoutComplete events
m_le=new HierarchicalBCEngine(m_nodes, m_edges, paddedNodeWidth, nodeHeight);
m_le.addLayoutCompleteEventListener(this);
m_jBtSave = new JButton();
java.net.URL tempURL = ClassLoader.getSystemResource(ICONPATH+"save.gif");
if(tempURL!=null)
m_jBtSave.setIcon(new ImageIcon(tempURL) );
else
System.err.println(ICONPATH+"save.gif not found for weka.gui.graphvisualizer.Graph");
m_jBtSave.setToolTipText("Save Graph");
m_jBtSave.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent ae) {
JFileChooser fc = new JFileChooser(System.getProperty("user.dir"));
ExtensionFileFilter ef1 = new ExtensionFileFilter(".dot", "DOT files");
ExtensionFileFilter ef2 = new ExtensionFileFilter(".xml", "XML BIF files");
fc.addChoosableFileFilter(ef1);
fc.addChoosableFileFilter(ef2);
fc.setDialogTitle("Save Graph As");
int rval = fc.showSaveDialog(GraphVisualizer.this);
if (rval == JFileChooser.APPROVE_OPTION) {
//System.out.println("Saving to file \""+f.getAbsoluteFile().toString()+"\"");
if(fc.getFileFilter()==ef2) {
String filename = fc.getSelectedFile().toString();
if(!filename.endsWith(".xml"))
filename = filename.concat(".xml");
BIFParser.writeXMLBIF03(filename, graphID, m_nodes, m_edges);
}
else {
String filename = fc.getSelectedFile().toString();
if(!filename.endsWith(".dot"))
filename = filename.concat(".dot");
DotParser.writeDOT(filename, graphID, m_nodes, m_edges);
}
}
}
});
final JButton jBtZoomIn = new JButton();
tempURL = ClassLoader.getSystemResource(ICONPATH+"zoomin.gif");
if(tempURL!=null)
jBtZoomIn.setIcon(new ImageIcon(tempURL) );
else
System.err.println(ICONPATH+"zoomin.gif not found for weka.gui.graphvisualizer.Graph");
jBtZoomIn.setToolTipText("Zoom In");
final JButton jBtZoomOut = new JButton();
tempURL = ClassLoader.getSystemResource(ICONPATH+"zoomout.gif");
if(tempURL!=null)
jBtZoomOut.setIcon(new ImageIcon(tempURL) );
else
System.err.println(ICONPATH+"zoomout.gif not found for weka.gui.graphvisualizer.Graph");
jBtZoomOut.setToolTipText("Zoom Out");
final JTextField jTfZoom = new JTextField("100%");
jTfZoom.setMinimumSize( jTfZoom.getPreferredSize() );
jTfZoom.setHorizontalAlignment(JTextField.CENTER);
jTfZoom.setToolTipText("Zoom");
jTfZoom.addActionListener( new ActionListener() {
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)
scale = i/100D;
jt.setText((int)(scale*100)+"%");
if(scale>0.1){
if(!jBtZoomOut.isEnabled())
jBtZoomOut.setEnabled(true);
}
else
jBtZoomOut.setEnabled(false);
if(scale<9.99) {
if(!jBtZoomIn.isEnabled())
jBtZoomIn.setEnabled(true);
}
else
jBtZoomIn.setEnabled(false);
setAppropriateSize();
//m_gp.clearBuffer();
m_gp.repaint();
m_gp.invalidate();
m_js.revalidate();
} catch(NumberFormatException ne) {
JOptionPane.showMessageDialog(GraphVisualizer.this.getParent(),
"Invalid integer entered for zoom.",
"Error",
JOptionPane.ERROR_MESSAGE);
jt.setText((scale*100)+"%");
}
}
});
jBtZoomIn.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent ae) {
int i=0, s = (int)(scale*100);
if(s<300)
i = s/25;
else if(s<700)
i = 6 + s/50;
else
i = 13 +s/100;
if(s>=999) {
JButton b = (JButton)ae.getSource();
b.setEnabled(false);
return;
}
else if(s>=10){
if(i>=22) {
JButton b = (JButton)ae.getSource();
b.setEnabled(false);
}
if(s==10 && !jBtZoomOut.isEnabled())
jBtZoomOut.setEnabled(true);
//System.out.println("i: "+i+"Zoom is: "+zoomPercents[i+1]);
jTfZoom.setText(zoomPercents[i+1]+"%");
scale = zoomPercents[i+1]/100D;
}
else {
if(!jBtZoomOut.isEnabled())
jBtZoomOut.setEnabled(true);
//System.out.println("i: "+i+"Zoom is: "+zoomPercents[0]);
jTfZoom.setText(zoomPercents[0]+"%");
scale = zoomPercents[0]/100D;
}
setAppropriateSize();
m_gp.repaint();
m_gp.invalidate();
m_js.revalidate();
}
});
jBtZoomOut.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent ae) {
int i=0, s = (int)(scale*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) {
JButton b = (JButton)ae.getSource();
b.setEnabled(false);
}
else if(s<999) {
if(i<=1) {
JButton b = (JButton)ae.getSource();
b.setEnabled(false);
}
//System.out.println("i: "+i+"Zoom is: "+zoomPercents[i-1]);
jTfZoom.setText(zoomPercents[i-1]+"%");
scale = zoomPercents[i-1]/100D;
}
else{
if(!jBtZoomIn.isEnabled())
jBtZoomIn.setEnabled(true);
//System.out.println("i: "+i+"Zoom is: "+zoomPercents[22]);
jTfZoom.setText(zoomPercents[22]+"%");
scale = zoomPercents[22]/100D;
}
setAppropriateSize();
m_gp.repaint();
m_gp.invalidate();
m_js.revalidate();
}
});
//This button pops out the extra controls
JButton jBtExtraControls = new JButton();
tempURL = ClassLoader.getSystemResource(ICONPATH+"extra.gif");
if(tempURL!=null)
jBtExtraControls.setIcon(new ImageIcon(tempURL) );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -