漫談可視化Prefuse(二)---一分鍾學會Prefuse


  前篇《漫談可視化Prefuse(一)---從SQL Server數據庫讀取數據》主要介紹了prefuse如何連接數據庫sql server並讀取數據進行可視化展現。

  回頭想想還是應該好好捋一捋prefuse框架中各個主要接口的職責以及如何有序使用它們構建一個prefuse應用。首先看圖:

 

  從圖中可以發現一個prefuse程序的編寫需要經歷以下步驟:

  1. 准備好原始數據(文件或是數據庫數據);
  2. 通過prefuse.data.io以及prefuse.data.io.sql包將數據讀入prefuse,實現原始數據與內置豐富的數據結構如Table、Graph、Tree等之間的映射;
  3. 為數據添加各種Action,執行布局、着色、設置形狀等操作;
  4. 將上面處理好的數據存入數據存儲中心Visuallization中;
  5. 為可視化組件添加渲染器Renderer,並將渲染器注冊到Visualization上;
  6. 通過Display將Visualization中的可視化組件顯示到屏幕上;
  7. 通過上圖發現是個閉環圖,當執行控制器以及過濾操作后又會得到更新后的Visualization,之后依次執行3、4、5、6步驟。

  下面我們通過一個例子具體了解如何創建一個prefuse應用。

1. 加載數據:

首先是加載圖數據到Prefuse的圖實例中,這里用的是socialnet.xml數據集。代碼中使用GraphMLReader類讀取數據集。

 

在方法readGraph中關於能讀取“/socialnet.xml”文件的說明:通過讀源碼發現首先會檢查這個字符串是不是一個URL鏈接,如果是的話則直接打開這個鏈接,如果不是則檢查其是否是一個Java Runtime 內的classpath資源鏈接,如果還不是,則會將該字符串視為一個文件或是文件系統。

 

Graph graph = null;

try {

    graph = new GraphMLReader().readGraph("/socialnet.xml");

} catch ( DataIOException e ) {

    e.printStackTrace();

    System.err.println("Error loading graph. Exiting...");

    System.exit(1);

}

 

2 .創建數據存儲中心Visualization實例:

  創建一個抽象的視覺圖形。在此之前,需要創建一個Visualization實例並添加圖形數據,同時在代碼中可以看到還要賦給一個名稱(group name)。當圖或樹的實例添加到Visualization對象上的時候,另外兩個子組名(subgroups)也會自動被創建:一個是節點(以“.nodes”為后綴),一個是邊(以“.edges”為后綴)

// add the graph to the visualization as the data group "graph"

// nodes and edges are accessible as "graph.nodes" and "graph.edges"

Visualization vis = new Visualization();

vis.add("graph", graph);

 

3.渲染和渲染工廠的創建:

  使用Renderers渲染器繪制Visualization對象中的VisualItems。默認情況下,Visualization類中包含了DefaultRendererFactory,其使用EdgeRenderer(默認繪制直連邊)、ShapeRenderer繪制圖形(比如正方形或三角形)。當希望看到節點上標簽值是,可以創建一個LabelRenderers並為標簽數據域賦值。

// draw the "name" label for NodeItems

LabelRenderer r = new LabelRenderer("name");

r.setRoundedCorner(8, 8); // round the corners

// create a new default renderer factory

// return our name label renderer as the default for all non-EdgeItems

// includes straight line edges for EdgeItems by default

vis.setRendererFactory(new DefaultRendererFactory(r));

 

4.添加處理的動作Actions:

  添加所需的視覺效果。通過創建不同的Action模塊處理Visualization上的VisualItems。比如可以創建一個顏色動作事件(ColorActions),該VisualItem默認支持三種顏色賦值:描邊顏色、填充顏色以及文本顏色。描邊的顏色是線條和輪廓的顏色;填充顏色是VisualItem的內部顏色;文本顏色是文本或是標簽的顏色。默認情況下,所有顏色都是純透明的。ColorLib類提供了很多顏色賦值的方法。

  通過以下代碼可以看出通過創建DataColorAction完成顏色分配工作。DataColorAction構造器的參數如下:

  (1)要運行的數據組名(這里是graph.nodes)

  (2)數據域的名稱(這里是gender)

  (3)數據域的數據類型,有三種:NOMINAL(適用於類別標簽集),ORDINAL(適用於有序集合),以及NUMERICAL(適用於數字)。

  (4)顏色域的設置,有描邊、填充和文本三種。

  (5)可選的調色板

  注意:這里DataColorAction對於賦值顏色的選取也是有排序的,一般是自然排序的順序,比如對於文本來說是按照字母表的先后順序來的。這就是為什么這里的粉紅色排在前面,因為按照字母順序,“F”排在“M”的前面。

// create our nominal color palette

// pink for females, baby blue for males

int[] palette = new int[] {

    ColorLib.rgb(255,180,180), ColorLib.rgb(190,190,255)

};

// map nominal data values to colors using our provided palette

DataColorAction fill = new DataColorAction("graph.nodes", "gender",  Constants.NOMINAL, VisualItem.FILLCOLOR, palette);

// use black for node text

ColorAction text = new ColorAction("graph.nodes", VisualItem.TEXTCOLOR, ColorLib.gray(0));

// use light grey for edges

ColorAction edges = new ColorAction("graph.edges",

    VisualItem.STROKECOLOR, ColorLib.gray(200));

// create an action list containing all color assignments

ActionList color = new ActionList();

color.add(fill);

color.add(text);

color.add(edges);

  接着再創建關於動畫布局的ActionList。所有的Action都可以進行參數控制的指定運行多少次(默認情況下是一次),也可以限定指定間隔時間運行,或者指定間隔時間無限制運行。代碼中通過添加ForceDirectedLayout布局來進行圖中節點的位置更新。添加RepaintAction()來實現當布局重新計算后就進行圖形重繪的功能。

// create an action list with an animated layout

// the INFINITY parameter tells the action list to run indefinitely

ActionList layout = new ActionList(Activity.INFINITY);

layout.add(new ForceDirectedLayout("graph"));

layout.add(new RepaintAction());

  然后將這兩個ActionLists添加到Visualization上,每個注冊的Action都有唯一標示以方便調用。

// add the actions to the visualization

vis.putAction("color", color);

vis.putAction("layout", layout);

5.展現和交互控制:

  創建Display實現數據可視化。這里創建一個Display實例將Visualization中的Visualitems進行參數配置。可以設置想要展現的Display的大小、像素。這里,我們添加三個交互控制器:

  (1)         拖拽控制

  (2)         平移控制

  (3)         縮放控制

// create a new Display that pull from our Visualization

Display display = new Display(vis);

display.setSize(720, 500); // set display size

display.addControlListener(new DragControl()); // drag items around

display.addControlListener(new PanControl());  // pan with background left-drag

display.addControlListener(new ZoomControl()); // zoom with vertical right-drag

 

6.可視化呈現:

  剩下要做的就是添加Display顯示到一個應用程序窗口並讓程序運行。該例中,創建Java Swing編程中的頂級類JFrame,設置標題。“pack”可以使得窗口合適的布局並確保窗口可見。

// create a new window to hold the visualization

JFrame frame = new JFrame("prefuse example");

// ensure application exits when window is closed

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.add(display);

frame.pack();           // layout components in window

frame.setVisible(true); // show the window

vis.run("color");  // assign the colors

vis.run("layout"); // start up the animated layout

執行結果如下:

  

  另外,在《可視化工具solo show-----Prefuse自帶例子GraphView講解》篇中,對GraphView.java進行了閱讀並注釋,這里再貼出RadialGraphView.java和TreeView.java的部分注釋。

RadialGraphView.java

public class RadialGraphView extends Display {

    public static final String DATA_FILE = "socialnet.xml";
    private static final String tree = "tree";
    private static final String treeNodes = "tree.nodes";
    private static final String treeEdges = "tree.edges";
    private static final String linear = "linear";
    
    private LabelRenderer m_nodeRenderer;
    private EdgeRenderer m_edgeRenderer;
    
    private String m_label = "label";
    
    public RadialGraphView(Graph g, String label) {
        super(new Visualization());
        m_label = label;

        // -- set up visualization --
        m_vis.add(tree, g);//將graph對象添加到m_vis對象上
        m_vis.setInteractive(treeEdges, null, false);
        
        // -- set up renderers --   設置點和邊的渲染器
        m_nodeRenderer = new LabelRenderer(m_label);
        m_nodeRenderer.setRenderType(AbstractShapeRenderer.RENDER_TYPE_FILL);
        m_nodeRenderer.setHorizontalAlignment(Constants.CENTER);
        m_nodeRenderer.setRoundedCorner(8,8);
        m_edgeRenderer = new EdgeRenderer();
        
        DefaultRendererFactory rf = new DefaultRendererFactory(m_nodeRenderer);
        rf.add(new InGroupPredicate(treeEdges), m_edgeRenderer);
        m_vis.setRendererFactory(rf);
               
        // -- set up processing actions --
        
        // colors   
        ItemAction nodeColor = new NodeColorAction(treeNodes);
        ItemAction textColor = new TextColorAction(treeNodes);
        m_vis.putAction("textColor", textColor);
        
        ItemAction edgeColor = new ColorAction(treeEdges,
                VisualItem.STROKECOLOR, ColorLib.rgb(200,200,200));//新建邊顏色Action
        
        FontAction fonts = new FontAction(treeNodes, 
                FontLib.getFont("Tahoma", 10));//設置節點大小以及字體
        fonts.add("ingroup('_focus_')", FontLib.getFont("Tahoma", 11));
        
        // recolor   新建重新着色ActionList,並添加上面的節點和邊Action
        ActionList recolor = new ActionList();
        recolor.add(nodeColor);
        recolor.add(textColor);
        m_vis.putAction("recolor", recolor);
        
        // repaint   新建重繪ActionList,並加入recolor以及RepaintAction
        ActionList repaint = new ActionList();
        repaint.add(recolor);
        repaint.add(new RepaintAction());
        m_vis.putAction("repaint", repaint);
        
        // animate paint change  
        ActionList animatePaint = new ActionList(400);
        animatePaint.add(new ColorAnimator(treeNodes));
        animatePaint.add(new RepaintAction());
        m_vis.putAction("animatePaint", animatePaint);
        
        // create the tree layout action   采用徑向樹布局
        RadialTreeLayout treeLayout = new RadialTreeLayout(tree);
        //treeLayout.setAngularBounds(-Math.PI/2, Math.PI);
        m_vis.putAction("treeLayout", treeLayout);
        
        CollapsedSubtreeLayout subLayout = new CollapsedSubtreeLayout(tree);//折疊子樹布局
        m_vis.putAction("subLayout", subLayout);
        
        // create the filtering and layout   創建過濾器和布局     在圖形呈現之前,做好過濾filter工作
        ActionList filter = new ActionList();
        filter.add(new TreeRootAction(tree));
        filter.add(fonts);
        filter.add(treeLayout);
        filter.add(subLayout);
        filter.add(textColor);
        filter.add(nodeColor);
        filter.add(edgeColor);
        m_vis.putAction("filter", filter);
        
        // animated transition  動畫過渡
        ActionList animate = new ActionList(1250);
        animate.setPacingFunction(new SlowInSlowOutPacer());//起搏功能,提供漸入漸出效果
        animate.add(new QualityControlAnimator());//可以平滑切換動畫的動畫控制器
        animate.add(new VisibilityAnimator(tree));
        animate.add(new PolarLocationAnimator(treeNodes, linear));
        animate.add(new ColorAnimator(treeNodes));
        animate.add(new RepaintAction());
        m_vis.putAction("animate", animate);
        m_vis.alwaysRunAfter("filter", "animate");
        
         // initialize the display
        setSize(600,600);//初始化JFrame大小      注意:這里沒有像GraphView一樣創建Display對象,是因為該類RadialGraphView繼承 了Display,可以直接調用方法
        setItemSorter(new TreeDepthItemSorter());//為樹狀深度設置排序分類器
        addControlListener(new DragControl());//一下都是設置監聽類,包括拖拽、縮放至適合顯示、平移、聚焦、懸停
        addControlListener(new ZoomToFitControl());
        addControlListener(new ZoomControl());
        addControlListener(new PanControl());
        addControlListener(new FocusControl(1, "filter"));
        addControlListener(new HoverActionControl("repaint"));
                
        // filter graph and perform layout
        m_vis.run("filter");
        
        // maintain a set of items that should be interpolated linearly
        // this isn't absolutely necessary, but makes the animations nicer
        // the PolarLocationAnimator should read this set and act accordingly
        m_vis.addFocusGroup(linear, new DefaultTupleSet());
        m_vis.getGroup(Visualization.FOCUS_ITEMS).addTupleSetListener(//添加數據集監聽器,監聽數據變化
            new TupleSetListener() {
                public void tupleSetChanged(TupleSet t, Tuple[] add, Tuple[] rem) {
                    TupleSet linearInterp = m_vis.getGroup(linear);
                    if ( add.length < 1 ) return; linearInterp.clear();
                    for ( Node n = (Node)add[0]; n!=null; n=n.getParent() )
                        linearInterp.addTuple(n);
                }
            }
        );
        
        SearchTupleSet search = new PrefixSearchTupleSet();//監聽搜索欄數據
        m_vis.addFocusGroup(Visualization.SEARCH_ITEMS, search);
        search.addTupleSetListener(new TupleSetListener() {
            public void tupleSetChanged(TupleSet t, Tuple[] add, Tuple[] rem) {
                m_vis.cancel("animatePaint");
                m_vis.run("recolor");
                m_vis.run("animatePaint");
            }
        });
    }
    
    // ------------------------------------------------------------------------
    
    public static void main(String argv[]) {
        String infile = DATA_FILE;
        String label = "name";
        
        if ( argv.length > 1 ) {
            infile = argv[0];
            label = argv[1];
        }
        
        UILib.setPlatformLookAndFeel();
        
        JFrame frame = new JFrame("p r e f u s e  |  r a d i a l g r a p h v i e w");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(demo(infile, label));
        frame.pack();
        frame.setVisible(true);
    }
    
    public static JPanel demo() {
        return demo(DATA_FILE, "name");
    }
    
    public static JPanel demo(String datafile, final String label) {
        Graph g = null;
        try {
            g = new GraphMLReader().readGraph(datafile);
        } catch ( Exception e ) {
            e.printStackTrace();
            System.exit(1);
        }
        return demo(g, label);
    }
    
    public static JPanel demo(Graph g, final String label) {        
        // create a new radial tree view    //創建一個徑向樹形布局
        final RadialGraphView gview = new RadialGraphView(g, label);
        Visualization vis = gview.getVisualization();
        
        // create a search panel for the tree map
        SearchQueryBinding sq = new SearchQueryBinding(
             (Table)vis.getGroup(treeNodes), label,
             (SearchTupleSet)vis.getGroup(Visualization.SEARCH_ITEMS));
        JSearchPanel search = sq.createSearchPanel();
        search.setShowResultCount(true);
        search.setBorder(BorderFactory.createEmptyBorder(5,5,4,0));
        search.setFont(FontLib.getFont("Tahoma", Font.PLAIN, 11));
        
        final JFastLabel title = new JFastLabel("                 ");
        title.setPreferredSize(new Dimension(350, 20));
        title.setVerticalAlignment(SwingConstants.BOTTOM);
        title.setBorder(BorderFactory.createEmptyBorder(3,0,0,0));
        title.setFont(FontLib.getFont("Tahoma", Font.PLAIN, 16));
        
        gview.addControlListener(new ControlAdapter() {//為搜索框添加監聽事件
            public void itemEntered(VisualItem item, MouseEvent e) {
                if ( item.canGetString(label) )//如果搜索框文本發生變化,則將最新文本值賦給搜索文本框
                    title.setText(item.getString(label));
            }
            public void itemExited(VisualItem item, MouseEvent e) {//如果搜索框失去焦點,則置搜索文本框值為空
                title.setText(null);
            }
        });
        
        Box box = new Box(BoxLayout.X_AXIS);//創建盒子容器,裝在上面的搜索面板和搜索文本框(安裝水平方向布局)
        box.add(Box.createHorizontalStrut(10));
        box.add(title);
        box.add(Box.createHorizontalGlue());
        box.add(search);
        box.add(Box.createHorizontalStrut(3));
        
        JPanel panel = new JPanel(new BorderLayout());//按照組件級別從小到大,先創建JPanel對象,並將上面的RadialGraphView和Box注冊到JPanel上
        panel.add(gview, BorderLayout.CENTER);//RadialGraphView對象放到面板中間位置
        panel.add(box, BorderLayout.SOUTH);//Box對象放到面板南邊位置
        
        Color BACKGROUND = Color.WHITE;
        Color FOREGROUND = Color.DARK_GRAY;
        UILib.setColor(panel, BACKGROUND, FOREGROUND);//設置整個panel面板的前景和背景顏色
        
        return panel;
    }

    
    /**
     * Switch the root of the tree by requesting a new spanning tree
     * at the desired root
     */
    public static class TreeRootAction extends GroupAction {
        public TreeRootAction(String graphGroup) {
            super(graphGroup);
        }
        public void run(double frac) {
            TupleSet focus = m_vis.getGroup(Visualization.FOCUS_ITEMS);//如果沒有點被選中或選中為空則跳出run函數
            if ( focus==null || focus.getTupleCount() == 0 ) return;
            
            Graph g = (Graph)m_vis.getGroup(m_group);
            Node f = null;
            Iterator tuples = focus.tuples();
            while (tuples.hasNext() && !g.containsTuple(f=(Node)tuples.next()))//迭代選中的焦點,如果選中焦點不是上次的焦點,則置f為空,並跳出run函數
            {
                f = null;
            }
            if ( f == null ) return;
            g.getSpanningTree(f);//返回一個生成樹
        }
    }
    
    /**
     * Set node fill colors    設置點的填充色
     */
    public static class NodeColorAction extends ColorAction {
        public NodeColorAction(String group) {
            super(group, VisualItem.FILLCOLOR, ColorLib.rgba(255,255,255,0));
            add("_hover", ColorLib.gray(220,230));
            add("ingroup('_search_')", ColorLib.rgb(255,190,190));
            add("ingroup('_focus_')", ColorLib.rgb(198,229,229));
        }
                
    } // end of inner class NodeColorAction
    
    /**
     * Set node text colors     設置點內文本顏色
     */
    public static class TextColorAction extends ColorAction {
        public TextColorAction(String group) {
            super(group, VisualItem.TEXTCOLOR, ColorLib.gray(0));
            add("_hover", ColorLib.rgb(255,0,0));
        }
    } // end of inner class TextColorAction
    
    
} // end of class RadialGraphView
View Code

TreeView.java

public class TreeView extends Display {

    public static final String TREE_CHI = "chi-ontology.xml.gz";
    private static final String tree = "tree";
    private static final String treeNodes = "tree.nodes";
    private static final String treeEdges = "tree.edges";
    
    private LabelRenderer m_nodeRenderer;
    private EdgeRenderer m_edgeRenderer;
    
    private String m_label = "label";
    private int m_orientation = Constants.ORIENT_LEFT_RIGHT;//設置樹的布局方式,有left->right,right->left,top->bottom,bottom->top共四種方式
    
    public TreeView(Tree t, String label) {
        super(new Visualization());
        m_label = label;
        m_vis.add(tree, t);//將tree注冊到m_vis對象上
        
        m_nodeRenderer = new LabelRenderer(m_label);//新建節點和邊的渲染器
        m_nodeRenderer.setRenderType(AbstractShapeRenderer.RENDER_TYPE_FILL);
        m_nodeRenderer.setHorizontalAlignment(Constants.LEFT);
        m_nodeRenderer.setRoundedCorner(8,8);
        m_edgeRenderer = new EdgeRenderer(Constants.EDGE_TYPE_CURVE);
        
        DefaultRendererFactory rf = new DefaultRendererFactory(m_nodeRenderer);
        rf.add(new InGroupPredicate(treeEdges), m_edgeRenderer);
        m_vis.setRendererFactory(rf);//將上面的點和邊的渲染器注冊到DefaultRendererFactory,再添加到m_vis對象上
               
        // colors
        ItemAction nodeColor = new NodeColorAction(treeNodes);
        ItemAction textColor = new ColorAction(treeNodes,
                VisualItem.TEXTCOLOR, ColorLib.rgb(0,0,0));
        m_vis.putAction("textColor", textColor);
        
        ItemAction edgeColor = new ColorAction(treeEdges,
                VisualItem.STROKECOLOR, ColorLib.rgb(200,200,200));
        
        // quick repaint
        ActionList repaint = new ActionList();
        repaint.add(nodeColor);
        repaint.add(new RepaintAction());
        m_vis.putAction("repaint", repaint);
        
        // full paint
        ActionList fullPaint = new ActionList();
        fullPaint.add(nodeColor);
        m_vis.putAction("fullPaint", fullPaint);
        
        // animate paint change
        ActionList animatePaint = new ActionList(400);
        animatePaint.add(new ColorAnimator(treeNodes));
        animatePaint.add(new RepaintAction());
        m_vis.putAction("animatePaint", animatePaint);
        
        // create the tree layout action
        NodeLinkTreeLayout treeLayout = new NodeLinkTreeLayout(tree,
                m_orientation, 50, 0, 8);//50代表樹深度之間的距離      0代表兄弟節點之間的距離    8代表相鄰子樹之間的距離
        treeLayout.setLayoutAnchor(new Point2D.Double(25,300));
        m_vis.putAction("treeLayout", treeLayout);
        
        CollapsedSubtreeLayout subLayout = 
            new CollapsedSubtreeLayout(tree, m_orientation);
        m_vis.putAction("subLayout", subLayout);
        
        AutoPanAction autoPan = new AutoPanAction();
        
        // create the filtering and layout    在圖形呈現之前,做好過濾filter工作,包括默認顯示幾層樹,采用什么布局,節點和邊如何展示等等
        ActionList filter = new ActionList();
        filter.add(new FisheyeTreeFilter(tree, 2));//添加一個魚眼樹形過濾器    設置初始界面顯示幾層節點
        filter.add(new FontAction(treeNodes, FontLib.getFont("Tahoma", 16)));
        filter.add(treeLayout);
        filter.add(subLayout);
        filter.add(textColor);
        filter.add(nodeColor);
        filter.add(edgeColor);
        m_vis.putAction("filter", filter);
        
        // animated transition          動畫過渡       
        ActionList animate = new ActionList(1000);
        animate.setPacingFunction(new SlowInSlowOutPacer());//使用SlowInSlowOutPacer可以實現一開始速度緩慢,中途加速,即將停止之前變慢的效果
        animate.add(autoPan);
        animate.add(new QualityControlAnimator());
        animate.add(new VisibilityAnimator(tree));
        animate.add(new LocationAnimator(treeNodes));
        animate.add(new ColorAnimator(treeNodes));
        animate.add(new RepaintAction());//從RepaintAction的構造函數可以看出每次執行RepaintAction時都會執行m_vis,即刷新執行一個上面的m_vis,所以就不需要add上面的sublayout、treelayout、filter等,因為這些已經被putAction進入m_vis了
        m_vis.putAction("animate", animate);
        m_vis.alwaysRunAfter("filter", "animate");//第一個參數:等待的Action名稱    第二個參數:第一個Action運行完再運行的Action
        
        // create animator for orientation changes   針對圖形布局方向改變時進行的操作     
        ActionList orient = new ActionList(2000);
        orient.setPacingFunction(new SlowInSlowOutPacer());
        orient.add(autoPan);
        orient.add(new QualityControlAnimator());
        orient.add(new LocationAnimator(treeNodes));
        orient.add(new RepaintAction());
        m_vis.putAction("orient", orient);
        
       
        // initialize the display
        setSize(700,600);
        setItemSorter(new TreeDepthItemSorter());
        addControlListener(new ZoomToFitControl());
        addControlListener(new ZoomControl());
        addControlListener(new WheelZoomControl());
        addControlListener(new PanControl());
        addControlListener(new FocusControl(1, "filter"));//其中1代表鼠標聚焦點擊的次數
        
        registerKeyboardAction(//注冊快捷鍵響應事件  ctrl+1、ctrl+2、ctrl+3、ctrl+4分別對應不同的布局方式
            new OrientAction(Constants.ORIENT_LEFT_RIGHT),
            "left-to-right", KeyStroke.getKeyStroke("ctrl 1"), WHEN_FOCUSED);
        registerKeyboardAction(
            new OrientAction(Constants.ORIENT_TOP_BOTTOM),
            "top-to-bottom", KeyStroke.getKeyStroke("ctrl 2"), WHEN_FOCUSED);
        registerKeyboardAction(
            new OrientAction(Constants.ORIENT_RIGHT_LEFT),
            "right-to-left", KeyStroke.getKeyStroke("ctrl 3"), WHEN_FOCUSED);
        registerKeyboardAction(
            new OrientAction(Constants.ORIENT_BOTTOM_TOP),
            "bottom-to-top", KeyStroke.getKeyStroke("ctrl 4"), WHEN_FOCUSED);
        
        // ------------------------------------------------
        
        // filter graph and perform layout
        setOrientation(m_orientation);
        m_vis.run("filter");
        
        TupleSet search = new PrefixSearchTupleSet(); 
        m_vis.addFocusGroup(Visualization.SEARCH_ITEMS, search);
        search.addTupleSetListener(new TupleSetListener() {
            public void tupleSetChanged(TupleSet t, Tuple[] add, Tuple[] rem) {
                m_vis.cancel("animatePaint");
                m_vis.run("fullPaint");
                m_vis.run("animatePaint");
            }
        });
    }

    
    public void setOrientation(int orientation) {
        NodeLinkTreeLayout rtl 
            = (NodeLinkTreeLayout)m_vis.getAction("treeLayout");
        CollapsedSubtreeLayout stl
            = (CollapsedSubtreeLayout)m_vis.getAction("subLayout");
        switch ( orientation ) {
        case Constants.ORIENT_LEFT_RIGHT:
            m_nodeRenderer.setHorizontalAlignment(Constants.LEFT);
            m_edgeRenderer.setHorizontalAlignment1(Constants.RIGHT);
            m_edgeRenderer.setHorizontalAlignment2(Constants.LEFT);
            m_edgeRenderer.setVerticalAlignment1(Constants.CENTER);
            m_edgeRenderer.setVerticalAlignment2(Constants.CENTER);
            break;
        case Constants.ORIENT_RIGHT_LEFT:
            m_nodeRenderer.setHorizontalAlignment(Constants.RIGHT);
            m_edgeRenderer.setHorizontalAlignment1(Constants.LEFT);
            m_edgeRenderer.setHorizontalAlignment2(Constants.RIGHT);
            m_edgeRenderer.setVerticalAlignment1(Constants.CENTER);
            m_edgeRenderer.setVerticalAlignment2(Constants.CENTER);
            break;
        case Constants.ORIENT_TOP_BOTTOM:
            m_nodeRenderer.setHorizontalAlignment(Constants.CENTER);
            m_edgeRenderer.setHorizontalAlignment1(Constants.CENTER);
            m_edgeRenderer.setHorizontalAlignment2(Constants.CENTER);
            m_edgeRenderer.setVerticalAlignment1(Constants.BOTTOM);
            m_edgeRenderer.setVerticalAlignment2(Constants.TOP);
            break;
        case Constants.ORIENT_BOTTOM_TOP:
            m_nodeRenderer.setHorizontalAlignment(Constants.CENTER);
            m_edgeRenderer.setHorizontalAlignment1(Constants.CENTER);
            m_edgeRenderer.setHorizontalAlignment2(Constants.CENTER);
            m_edgeRenderer.setVerticalAlignment1(Constants.TOP);
            m_edgeRenderer.setVerticalAlignment2(Constants.BOTTOM);
            break;
        default:
            throw new IllegalArgumentException(
                "Unrecognized orientation value: "+orientation);
        }
        m_orientation = orientation;
        rtl.setOrientation(orientation);
        stl.setOrientation(orientation);
    }
    
    public int getOrientation() {
        return m_orientation;
    }
    
    // ------------------------------------------------------------------------
    
    public static void main(String argv[]) {
        String infile = TREE_CHI;
        String label = "name";
        if ( argv.length > 1 ) {
            infile = argv[0];
            label = argv[1];
        }
        JComponent treeview = demo(infile, label);
        
        JFrame frame = new JFrame("p r e f u s e  |  t r e e v i e w");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(treeview);
        frame.pack();
        frame.setVisible(true);
    }
    
    public static JComponent demo() {
        return demo(TREE_CHI, "name");
    }
    
    public static JComponent demo(String datafile, final String label) {
        Color BACKGROUND = Color.WHITE;
        Color FOREGROUND = Color.BLACK;
        
        Tree t = null;
        try {
            t = (Tree)new TreeMLReader().readGraph(datafile);
        } catch ( Exception e ) {
            e.printStackTrace();
            System.exit(1);
        }
        
        // create a new treemap    創建一個樹狀圖並設置前景背景顏色
        final TreeView tview = new TreeView(t, label);
        tview.setBackground(BACKGROUND);
        tview.setForeground(FOREGROUND);
        
        // create a search panel for the tree map    為樹狀圖添加一個搜索文本標簽
        JSearchPanel search = new JSearchPanel(tview.getVisualization(),
            treeNodes, Visualization.SEARCH_ITEMS, label, true, true);
        search.setShowResultCount(true);//顯示搜索符合條件的節點個數
        search.setBorder(BorderFactory.createEmptyBorder(5,5,4,0));
        search.setFont(FontLib.getFont("Tahoma", Font.PLAIN, 11));
        search.setBackground(BACKGROUND);
        search.setForeground(FOREGROUND);
        
        final JFastLabel title = new JFastLabel("                 ");//為樹狀圖添加一個搜索面板
        title.setPreferredSize(new Dimension(350, 20));//長350 寬20
        title.setVerticalAlignment(SwingConstants.BOTTOM);
        title.setBorder(BorderFactory.createEmptyBorder(3,0,0,0));
        title.setFont(FontLib.getFont("Tahoma", Font.PLAIN, 16));
        title.setBackground(BACKGROUND);
        title.setForeground(FOREGROUND);
        
        tview.addControlListener(new ControlAdapter() {
            public void itemEntered(VisualItem item, MouseEvent e) {
                if ( item.canGetString(label) )
                    title.setText(item.getString(label));
            }
            public void itemExited(VisualItem item, MouseEvent e) {
                title.setText(null);
            }
        });
        
        Box box = new Box(BoxLayout.X_AXIS);
        box.add(Box.createHorizontalStrut(10));
        box.add(title);
        box.add(Box.createHorizontalGlue());
        box.add(search);
        box.add(Box.createHorizontalStrut(3));
        box.setBackground(BACKGROUND);
        
        JPanel panel = new JPanel(new BorderLayout());
        panel.setBackground(BACKGROUND);
        panel.setForeground(FOREGROUND);
        panel.add(tview, BorderLayout.CENTER);
        panel.add(box, BorderLayout.SOUTH);
        return panel;
    }
    
   
    public class OrientAction extends AbstractAction {//方向Action,針對界面中變換布局方式所做的響應
        private int orientation;
        
        public OrientAction(int orientation) {
            this.orientation = orientation;
        }
        public void actionPerformed(ActionEvent evt) {
            setOrientation(orientation);
            getVisualization().cancel("orient");
            getVisualization().run("treeLayout");
            getVisualization().run("orient");//通過進入getVisualization方法可以發現該方法返回的是m_vis對象,getVisualization().run("orient");等價於m_vis.run("orient");
        }
    }
    
    public class AutoPanAction extends Action {
        private Point2D m_start = new Point2D.Double();
        private Point2D m_end   = new Point2D.Double();
        private Point2D m_cur   = new Point2D.Double();
        private int     m_bias  = 150;
        
        public void run(double frac) {
            TupleSet ts = m_vis.getFocusGroup(Visualization.FOCUS_ITEMS);
            if ( ts.getTupleCount() == 0 )
                return;
            
            if ( frac == 0.0 ) {
                int xbias=0, ybias=0;
                switch ( m_orientation ) {
                case Constants.ORIENT_LEFT_RIGHT:
                    xbias = m_bias;
                    break;
                case Constants.ORIENT_RIGHT_LEFT:
                    xbias = -m_bias;
                    break;
                case Constants.ORIENT_TOP_BOTTOM:
                    ybias = m_bias;
                    break;
                case Constants.ORIENT_BOTTOM_TOP:
                    ybias = -m_bias;
                    break;
                }

                VisualItem vi = (VisualItem)ts.tuples().next();
                m_cur.setLocation(getWidth()/2, getHeight()/2);
                getAbsoluteCoordinate(m_cur, m_start);
                m_end.setLocation(vi.getX()+xbias, vi.getY()+ybias);
            } else {
                m_cur.setLocation(m_start.getX() + frac*(m_end.getX()-m_start.getX()),
                                  m_start.getY() + frac*(m_end.getY()-m_start.getY()));
                panToAbs(m_cur);
            }
        }
    }
    
    public static class NodeColorAction extends ColorAction {
        
        public NodeColorAction(String group) {
            super(group, VisualItem.FILLCOLOR);
        }
        
        public int getColor(VisualItem item) {
            if ( m_vis.isInGroup(item, Visualization.SEARCH_ITEMS) )
                return ColorLib.rgb(255,190,190);
            else if ( m_vis.isInGroup(item, Visualization.FOCUS_ITEMS) )
                return ColorLib.rgb(198,229,229);
            else if ( item.getDOI() > -1 )
                return ColorLib.rgb(164,193,193);
            else
                return ColorLib.rgba(255,255,255,0);
        }
        
    } // end of inner class TreeMapColorAction
    
    
} // end of class TreeMap
View Code

原文鏈接:http://www.cnblogs.com/bigdataZJ/p/VisualizationPrefuse2.html

畫外音:1.prefuse真的很全面、很強大,不知道為什么后面一直沒有更新,是轉為商業化運營了么???

    2.感覺研究可視化的小伙伴不是很多啊。。。

 

友情贊助

如果你覺得博主的文章對你那么一點小幫助,恰巧你又有想打賞博主的小沖動,那么事不宜遲,趕緊掃一掃,小額地贊助下,攢個奶粉錢,也是讓博主有動力繼續努力,寫出更好的文章^^。

    1. 支付寶                          2. 微信

                      


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM