2014.10.15日以来的一个月,挤破了头、跑断了腿、伤透了心、吃够了全国最大餐饮连锁店——沙县小吃。其中酸甜苦辣,绝不是三言两语能够说得清道的明的。校招的兄弟姐妹们,你们懂得……

  体会最深的一句话还是:出来混,迟早要还的。

  一个月过去了,迷茫之际有太多无奈,无奈中又夹杂些许庆幸,岁月匆匆,人生不息,奋斗不止。

  遵守最初的诺言,继续走我可视化的道路:

  上集摘要:一个月博文中大概介绍了可视化的一些常用工具,从可操作性、实用性、交互性等各方面进行了简单的对比和总结,具体参见http://www.cnblogs.com/bigdataZJ/p/VisualizationSoloShow.html,结合自己的需求,挑出了Prefuse和Processing两员大将出来露了一手,详情请见http://www.cnblogs.com/bigdataZJ/p/VisualizationSoloShow2.html

  一番角逐之后,Prefuse工具集脱颖而出,其强大的展示效果、开发者友好的API说明文档、丰富的自带Demo无一不让我对其欲罢不能。下面我们来好好分析下Prefuse的强大之处:

1.Prefuse主要特征:

  (1)任意数据类型的表格、图和树形数据结构,数据索引、选择查询,有效的内存占用

  (2)具有布局、着色、大小、图形编码、扭曲、动画等多个组件

  (3)具有交互控制库

  (4)支持动画过渡,通过一系列的活动调度机制

  (5)支持平移、缩放等视图变换

  (6)对于交互过滤数据的动态查询

  (7)能够使用可用的搜索引擎进行文本检索

  (8)具有布局和动画的力导向模拟引擎

  (9)灵活的多视图展现,包括“概述+细节”和“小倍数”显示

  (10)内置类SQL语句查询,可以用于编写查询语句实现查询指定字段的数据

  (11)提供查询语句到Prefuse数据结构的数据映射的SQL查询

  (12)简单、开发者友好的APIs文档

2.Prefuse模型:

  (1)prefuse.data包提供了 Table, Graph, Tree等数据结构;提供了一个data tables,他的行使用一个类 Tuple来表示;这个包中,Node和Edge来表示图或者树的一些成员。
  作为一种高级特征的工具集,Prefuse提供了一种解释性的表达式语言,该语言可以用来请求Prefuse中的数据结构并根据已有的数据列创建衍生的列数据。表达式语言的功能实现类在prefuse.data.expression包中,文本表达式解析类在ExpressionParser类中。
  (2)prefuse.data.io包提供了文件的读写,包括表,图和树的结构,其中,表的格式:CSV和任意分割的文本文件,对于网络,有 GraphML和 TreeML(XML也能);prefuse.data.io.sql包提供了对SQL数据库的查询,并返回一个prefuse表
  (3)可视化抽象是通过将数据添加到Visulization实例中来得到的,它除了包含原始数据外,还建立了一套完整的可视化体系,包括x、y的坐标轴,颜色,大小字体等值,任意的Tuple, Node, 或者 Edge被添加到Visulization实例中时候,相关的VisualItems实例就建立好了,如NodeItem和 EdgeItem就是VisualItems的实例。(也就是说,可视化抽象实现了添加的数据元素与VisualItems之间的映射)
  (4)可视化映射工作由Action模块来完成,它是有一系列独立的处理模块组成的,这些模块来完成可视性、布局计算、颜色设定以及任何其他的可视化工作。prefuse.action包以及其子包会提供一系列布局,形变,动画以及可视化编码的工作。
  (5)Renderer模块决定了VisualItems的出现情况,Renderers模块负责计算显示区域,即如何将可视化图形绘制在屏幕上。RendererFactory用来对Renderer进行管理,体现在给VisualItems分配适当的Renderer上。
  (6)交互工作,Display组建负责完成交互方面的工作,起到一个类似于摄像机的功能,对显示的区域进行选取,缩放。它直接与用户相关。
一个Visualization可以与多个Display实例关联,以实现多视图参数配置,比如“概述+详细”以及小倍数显示视图等。
  (7)每个Display实例都支持若干个Controls,他们负责处理Display上鼠标和键盘的action。prefuse.controls包提供了一个预处理的控制器可以用来完成旋转缩放Display的工作,通过prefuse.controls包的子类ControlAdapter可以实现对Display的控制。
  (8)最后,prefuse.data.query 包提供了动态查询绑定(?)的功能,这些绑定能够生成合适的用户界面组建,来直接操作这些查询。

 3.Prefuse自带Demo---GraphView.java详解

  下面是自己在研读Prefuse源码文件夹demos下的GraphView加的一些注释:

 //start of class GraphView

 public class GraphView extends JPanel {

     private static final String graph = "graph";

     private static final String nodes = "graph.nodes";

     private static final String edges = "graph.edges";

     private Visualization m_vis;

     public GraphView(Graph g, String label) {

          super(new BorderLayout());//在GraphView的构造函数中调用超类的构造方法,并创建布局BorderLayout对象。

         // create a new, empty visualization for our data

         m_vis = new Visualization();//创建Visualization对象,使用默认的渲染工厂(DefaultRendererFactory)。Visualization类负责管理源数据与可视化组件之间的映射。

         // --------------------------------------------------------------------

         // set up the renderers

         LabelRenderer tr = new LabelRenderer();

         tr.setRoundedCorner(8, 8);

         m_vis.setRendererFactory(new efaultRendererFactory(tr));//新建标签渲染器并注册到Visualization上,使用的还是DefaultRendererFactory。

         // --------------------------------------------------------------------
// register the data with a visualization // adds graph to visualization and sets renderer label field setGraph(g, label);// 向Visualization添加图形Graph并为标签域赋值。 // fix selected focus nodes 声明一个数据元组集合,并为该集合添加一个数据元组的监听器 TupleSet focusGroup = m_vis.getGroup(Visualization.FOCUS_ITEMS); focusGroup.addTupleSetListener(new TupleSetListener() { public void tupleSetChanged(TupleSet ts, Tuple[] add, Tuple[] rem) { for ( int i=0; i<rem.length; ++i ) ((VisualItem)rem[i]).setFixed(false); for ( int i=0; i<add.length; ++i ) { ((VisualItem)add[i]).setFixed(false); ((VisualItem)add[i]).setFixed(true); } if ( ts.getTupleCount() == 0 ) { ts.addTuple(rem[0]); ((VisualItem)rem[0]).setFixed(false); } m_vis.run("draw"); } });//声明一个数据元组集合,并通过匿名内部类的形式为该集合添加一个数据元组的监听器(TupleSetListener),其中ts:变化的数据元组;add:已经加入的元组数组集合;rem:移除的数据集合。 // -------------------------------------------------------------------- // create actions to process the visual data int hops = 30; final GraphDistanceFilter filter = new GraphDistanceFilter(graph, hops); ColorAction fill = new ColorAction(nodes, VisualItem.FILLCOLOR, ColorLib.rgb(200,200,255)); fill.add(VisualItem.FIXED, ColorLib.rgb(255,100,100)); fill.add(VisualItem.HIGHLIGHT, ColorLib.rgb(255,200,125)); ActionList draw = new ActionList(); draw.add(filter); draw.add(fill); draw.add(new ColorAction(nodes, VisualItem.STROKECOLOR, 0)); draw.add(new ColorAction(nodes, VisualItem.TEXTCOLOR, ColorLib.rgb(0,0,0))); draw.add(new ColorAction(edges, VisualItem.FILLCOLOR, ColorLib.gray(200))); draw.add(new ColorAction(edges, VisualItem.STROKECOLOR, ColorLib.gray(200)));// 根据设定距离hops新建一个图形距离过滤器类;针对nodes,采取完全填充颜色的方式(FILLCOLOR),并对聚焦点(fixed )、高亮点(与fixed node相邻的点即highlight)以及剩余点分别赋予不同的颜色表现.将GraphDistanceFilter和ColorAction都注册到声明的ActionList对象上,并同时添加点与边的描边颜色以及填充颜色的ColorAction。 ActionList animate = new ActionList(Activity.INFINITY); animate.add(new ForceDirectedLayout(graph)); animate.add(fill); animate.add(new RepaintAction());//声明一个ActionList的animate对象,在该对象上添加布局方式(这里采用力导向布局方法ForceDirectedLayout),并添加上面的ColorAction类的fill对象以及一个重绘图形Action。
// finally, we register our ActionList with the Visualization. // we can later execute our Actions by invoking a method on our // Visualization, using the name we've chosen below. m_vis.putAction("draw", draw); m_vis.putAction("layout", animate); m_vis.runAfter("draw", "layout");//将draw和animate注册到m_vis上,后面通过Visualization的方法触发执行每个注册的Action。 // -------------------------------------------------------------------- // set up a display to show the visualization
Display display = new Display(m_vis);
display.setSize(700,700);
display.pan(350, 350);
display.setForeground(Color.GRAY);
display.setBackground(Color.WHITE); // main display controls
display.addControlListener(new FocusControl(1));
display.addControlListener(new DragControl());
display.addControlListener(new PanControl());
display.addControlListener(new ZoomControl());
display.addControlListener(new WheelZoomControl());
display.addControlListener(new ZoomToFitControl());
display.addControlListener(new NeighborHighlightControl());//通过Display展现Visualization包括:设置画布大小,平移范围,前景背景颜色以及添加聚焦、拖拽、平移、缩放、滑轮、缩放至适合显示、紧邻高亮监听器。 // overview display // Display overview = new Display(vis); // overview.setSize(290,290); // overview.addItemBoundsListener(new FitOverviewListener()); display.setForeground(Color.GRAY); display.setBackground(Color.WHITE); // -------------------------------------------------------------------- // launch the visualization // create a panel for editing force values ForceSimulator fsim = ((ForceDirectedLayout)animate.get(0)).getForceSimulator(); JForcePanel fpanel = new JForcePanel(fsim); final JValueSlider slider = new JValueSlider("Distance", 0, hops, hops); slider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { filter.setDistance(slider.getValue().intValue());//只要调节面板上的值有变动就执行下面的run函数,重新布局界面 m_vis.run("draw"); } }); slider.setBackground(Color.WHITE); slider.setPreferredSize(new Dimension(300,30)); slider.setMaximumSize(new Dimension(300,30));//设置调节面板的背景颜色、大小 Box cf = new Box(BoxLayout.Y_AXIS); cf.add(slider); cf.setBorder(BorderFactory.createTitledBorder("Connectivity Filter")); fpanel.add(cf); //fpanel.add(opanel); fpanel.add(Box.createVerticalGlue()); // create a new JSplitPane to present the interface JSplitPane split = new JSplitPane(); split.setLeftComponent(display); split.setRightComponent(fpanel); split.setOneTouchExpandable(true); split.setContinuousLayout(false); split.setDividerLocation(700);//为整张画布布局,包括左边、右边应该呈现什么内容等 // now we run our action list //m_vis.run("draw"); add(split); } public void setGraph(Graph g, String label) { // update labeling DefaultRendererFactory drf = (DefaultRendererFactory) m_vis.getRendererFactory(); ((LabelRenderer)drf.getDefaultRenderer()).setTextField(label); // update graph m_vis.removeGroup(graph); VisualGraph vg = m_vis.addGraph(graph, g); m_vis.setValue(edges, null, VisualItem.INTERACTIVE, Boolean.FALSE); VisualItem f = (VisualItem)vg.getNode(0); m_vis.getGroup(Visualization.FOCUS_ITEMS).setTuple(f); f.setFixed(false); } // ------------------------------------------------------------------------ // Main and demo methods public static void main(String[] args) { UILib.setPlatformLookAndFeel(); // create graphview String datafile = null; String label = "label"; if ( args.length > 1 ) {//如果用户在运行时有参数传值则分别赋值给datafile和label datafile = args[0]; label = args[1]; } JFrame frame = demo(datafile, label); //通过调用demo函数完成整个界面的设计布局等,最终呈现一个JFrame frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 关闭按钮的动作为退出
} public static JFrame demo() { return demo((String)null, "label"); } public static JFrame demo(String datafile, String label) { Graph g = null; if ( datafile == null ) { g = GraphLib.getGrid(15,15);//如果datafile为空,则通过调用图形库GraphLib中的getGrid得到15*15的网状图形,如下图所示
label = "label"; } else { try { g = new GraphMLReader().readGraph(datafile);//否则通过指定路径读取datafile文件并转换为图形 } catch ( Exception e ) { e.printStackTrace(); System.exit(1); } } return demo(g, label); } public static JFrame demo(Graph g, String label) { final GraphView view = new GraphView(g, label); // set up menu JMenu dataMenu = new JMenu("Data");//新建菜单栏 dataMenu.add(new OpenGraphAction(view));//注册“打开文件”选项卡 dataMenu.add(new GraphMenuAction("Grid","ctrl 1",view) {//添加网状布局选项卡 protected Graph getGraph() { return GraphLib.getGrid(15,15); } }); dataMenu.add(new GraphMenuAction("Clique","ctrl 2",view) {//添加团状布局选项卡 protected Graph getGraph() { return GraphLib.getClique(10); } }); dataMenu.add(new GraphMenuAction("Honeycomb","ctrl 3",view) {//添加蜂窝状布局选项卡 protected Graph getGraph() { return GraphLib.getHoneycomb(5); } }); dataMenu.add(new GraphMenuAction("Balanced Tree","ctrl 4",view) {//添加平衡树布局选项卡 protected Graph getGraph() { return GraphLib.getBalancedTree(3,5); } }); dataMenu.add(new GraphMenuAction("Diamond Tree","ctrl 5",view) { protected Graph getGraph() { return GraphLib.getDiamondTree(3,3,3); //添加钻石树形图布局选项卡 } }); JMenuBar menubar = new JMenuBar(); menubar.add(dataMenu);//将以上菜单选项注册到menubar菜单栏上 // launch window JFrame frame = new JFrame("p r e f u s e | g r a p h v i e w"); frame.setJMenuBar(menubar); frame.setContentPane(view); frame.pack(); frame.setVisible(true);//添加菜单栏、图形等 frame.addWindowListener(new WindowAdapter() { public void windowActivated(WindowEvent e) { view.m_vis.run("layout"); } public void windowDeactivated(WindowEvent e) { view.m_vis.cancel("layout"); } }); return frame; } // ------------------------------------------------------------------------ /** * Swing menu action that loads a graph into the graph viewer. * 该类主要负责为每一种布局选项配置相应的快捷键 */ public abstract static class GraphMenuAction extends AbstractAction { private GraphView m_view; public GraphMenuAction(String name, String accel, GraphView view) { m_view = view; this.putValue(AbstractAction.NAME, name); this.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(accel)); } public void actionPerformed(ActionEvent e) { m_view.setGraph(getGraph(), "label"); } protected abstract Graph getGraph(); } //该类负责对菜单栏的选项卡的响应 public static class OpenGraphAction extends AbstractAction { private GraphView m_view; public OpenGraphAction(GraphView view) { m_view = view; this.putValue(AbstractAction.NAME, "Open File..."); this.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke("ctrl O")); } public void actionPerformed(ActionEvent e) { Graph g = IOLib.getGraphFile(m_view); if ( g == null ) return; String label = getLabel(m_view, g); if ( label != null ) { m_view.setGraph(g, label); } } public static String getLabel(Component c, Graph g) { // get the column names Table t = g.getNodeTable(); int cc = t.getColumnCount(); String[] names = new String[cc]; for ( int i=0; i<cc; ++i ) names[i] = t.getColumnName(i); // where to store the result final String[] label = new String[1]; // -- build the dialog ----- // we need to get the enclosing frame first while ( c != null && !(c instanceof JFrame) ) { c = c.getParent();
} final JDialog dialog = new JDialog( (JFrame)c, "Choose Label Field", true); // create the ok/cancel buttons final JButton ok = new JButton("OK"); ok.setEnabled(false); ok.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { dialog.setVisible(false); } }); JButton cancel = new JButton("Cancel"); cancel.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { label[0] = null; dialog.setVisible(false); } }); // build the selection list final JList list = new JList(names);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.getSelectionModel().addListSelectionListener( new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { int sel = list.getSelectedIndex(); if ( sel >= 0 ) { ok.setEnabled(true); label[0] = (String)list.getModel().getElementAt(sel); } else { ok.setEnabled(false); label[0] = null; } } }); JScrollPane scrollList = new JScrollPane(list); JLabel title = new JLabel("Choose a field to use for node labels:"); // layout the buttons Box bbox = new Box(BoxLayout.X_AXIS); bbox.add(Box.createHorizontalStrut(5)); bbox.add(Box.createHorizontalGlue()); bbox.add(ok); bbox.add(Box.createHorizontalStrut(5)); bbox.add(cancel); bbox.add(Box.createHorizontalStrut(5)); // put everything into a panel JPanel panel = new JPanel(new BorderLayout()); panel.add(title, BorderLayout.NORTH); panel.add(scrollList, BorderLayout.CENTER); panel.add(bbox, BorderLayout.SOUTH); panel.setBorder(BorderFactory.createEmptyBorder(5,2,2,2)); // show the dialog dialog.setContentPane(panel); dialog.pack(); dialog.setLocationRelativeTo(c); dialog.setVisible(true); dialog.dispose(); // return the label field selection return label[0]; } } //该类负责调整至适合屏幕显示 public static class FitOverviewListener implements ItemBoundsListener { private Rectangle2D m_bounds = new Rectangle2D.Double(); private Rectangle2D m_temp = new Rectangle2D.Double(); private double m_d = 15; public void itemBoundsChanged(Display d) { d.getItemBounds(m_temp); GraphicsLib.expand(m_temp, 25/d.getScale()); double dd = m_d/d.getScale(); double xd = Math.abs(m_temp.getMinX()-m_bounds.getMinX()); double yd = Math.abs(m_temp.getMinY()-m_bounds.getMinY()); double wd = Math.abs(m_temp.getWidth()-m_bounds.getWidth()); double hd = Math.abs(m_temp.getHeight()-m_bounds.getHeight()); if ( xd>dd || yd>dd || wd>dd || hd>dd ) { m_bounds.setFrame(m_temp); DisplayLib.fitViewToBounds(d, m_bounds, 0); } } } } // end of class GraphView

网格视图:

蜂窝状视图:

平衡树型视图:

以上介绍了Prefuse的一些特征,模型结构以及自带Demo GraphView.java的理解,后续会继续研究Prefuse的其他Demo以及主要接口。

本文链接http://www.cnblogs.com/bigdataZJ/p/VisualizationSoloShow3.html

友情赞助

如果你觉得博主的文章对你那么一点小帮助,恰巧你又有想打赏博主的小冲动,那么事不宜迟,赶紧扫一扫,小额地赞助下,攒个奶粉钱,也是让博主有动力继续努力,写出更好的文章^^。

    1. 支付宝                          2. 微信

                      

可视化工具solo show-----Prefuse自带例子GraphView讲解的更多相关文章

  1. 可视化工具solo show-----Processing Prefuse show

    继上篇<可视化工具solo show>罗列出一些主要基于Java开发的软件.插件之后,又仔细揣摩了下哪些可以为我所用. 一番端详之后,准备挑出其中Processing和Prefuse两位大 ...

  2. 可视化工具solo show

    辗转一圈还是回到了我魂牵梦绕的可视化上来了. 在Gephi+Netbeans上折腾了将近一个星期后,我深深的体会到个人对于代码的驾驭能力尚有提升的空间^_^,路很长,方向很重要,三思而行. 转载请标明 ...

  3. 漫谈可视化Prefuse(五)---一款属于我自己的可视化工具

    伴随着前期的基础积累,翻过API,读过一些Demo,总觉得自己已经摸透了Prefuse,小打小闹似乎已经无法满足内心膨胀的自己.还记得儿时看的<武状元苏乞儿>中降龙十八掌最后一张居然是空白 ...

  4. 可视化工具gephi源码探秘(二)---导入netbeans

    在上篇<可视化工具gephi源码探秘(一)>中主要介绍了如何将gephi的源码导入myeclipse中遇到的一些问题,此篇接着上篇而来,主要讲解当下通过myeclipse导入gephi源码 ...

  5. python 全栈开发,Day63(子查询,MySQl创建用户和授权,可视化工具Navicat的使用,pymysql模块的使用)

    昨日内容回顾 外键的变种三种关系: 多对一: 左表的多 对右表一 成立 左边的一 对右表多 不成立 foreign key(从表的id) refreences 主表的(id) 多对多 建立第三张表(f ...

  6. 可视化工具gephi源码探秘(二)

    在上篇<可视化工具gephi源码探秘(一)>中主要介绍了如何将gephi的源码导入myeclipse中遇到的一些问题,此篇接着上篇而来,主要讲解当下通过myeclipse导入gephi源码 ...

  7. 数据库——可视化工具Navicat、pymysql模块、sql注入问题

    数据库--可视化工具Navicat.pymysql模块.sql注入问题 Navicat可视化工具 Navicat是数据库的一个可视化工具,可直接在百度搜索下载安装,它可以通过鼠标"点点点&q ...

  8. mysql(单表查询,多表查询,MySQl创建用户和授权,可视化工具Navicat的使用)

    单表查询 语法: 一.单表查询的语法 SELECT 字段1,字段2... FROM 表名 WHERE 条件 GROUP BY field HAVING 筛选 ORDER BY field LIMIT ...

  9. MongoDB 安装和可视化工具

    MongoDB 是一款非常热门的NoSQL,面向文档的数据库管理系统,官方下载地址是:MongoDB,博主选择的是 Enterprise Server (MongoDB 3.2.9)版本,安装在Win ...

随机推荐

  1. Disabling default console handler in Java Logger by codes

    The open source packages usu. relies on log4j or Java Logger to print logs, by default the console h ...

  2. Android 事件拦截机制一种粗鄙的解释

    对于Android事件拦截机制,相信对于大多数Android初学者是一个抓耳挠腮难于理解的问题.其实理解这个问题并不困难. 首先,你的明白事件拦截机制到底是怎么一回事?这里说的事件拦截机制,指的是对触 ...

  3. SQL SERVER 2012 只能识别20个CPU的问题

    背景 最近在给客户优化时时候发现客户的虚拟机环境分配了32C 但是只能识别20个,物理机64C,64G内存,本来没什么,CPU利用率在40%左右,但是优化后同时增加了 CPU和内存的分配,CPU32增 ...

  4. 浅谈Excel开发:一 Excel 开发概述

        做Office相关的开发工作快一年多了,在这一年多里,在插件的开发中遇到了各种各样的问题和困难,还好同事们都很厉害,在和他们的交流讨论中学到了很多的知识.目前Office相关的开发资料是比较少 ...

  5. php操作xml

    最近计划写个人的小网站,一系列原因选择了用php来写,最大的问题就是虽然php很流行,但我从来没有接触过php,看了一个多星期的基本语法后做些小练习热热身,但是期间是各种问题啊,主要是对php不熟悉, ...

  6. ASP.NET集成模式下的管道事件

  7. NodeJS系列~第一个小例子,实现了request.querystring功能

    返回目录 百度百科上: Node.js是一套用来编写高性能网络服务器的JavaScript工具包,一系列的变化由此开始,在Node中,Http是首要的.Node为创建http服务器作了优化,所以在网上 ...

  8. ci框架里rewrite示例

    ci里新建应用app,入口文件app.php. Nginx 这里附上vhost配置 app.52fhy.com.conf server { listen 80; server_name app.52f ...

  9. Java final修饰符

    final的定义: 在英文层面上,final的意思是"最后的","最终的"意思,在Java中也同样表示出此种含义. final的运用对象: final适用于修饰 ...

  10. SQL中group by的用法

    group by即按照给定字段对结果集进行分组,从字面意义上理解就是根据"by"指定的规则对数据进行分组,所谓的分组就是将一个"数据集"划分成若干个" ...