首页 > 代码库 > 漫谈可视化Prefuse(三)---Prefuse API数据结构阅读有感
漫谈可视化Prefuse(三)---Prefuse API数据结构阅读有感
前篇回顾:上篇《漫谈可视化Prefuse(二)---一分钟学会Prefuse》主要通过一个Prefuse的具体实例了解了构建一个Prefuse application的具体步骤。一个Prefuse Application需要经过数据导入(文本数据、数据库)->Prefuse数据结构接收数据->注册各种效果的Actions->渲染Renderer->交互展现Display的流程。
摸清了Prefuse那些看似眼花缭乱的框架结构,剩下的就是抽丝剥茧,顺藤摸瓜,结合Manual和API掀开Prefuse的神秘面纱。首先从Prefuse的数据结构开始:
1.prefuse.data:该包主要包含了接口:Node(点)、Edge(边)、Tuple(元组)
类:CascadedTable(级联表)、Graph(图)、Schema(模式)、SpanningTree(生成树)、Table(表)、Tree(树)。每个结构都有自己的属性和方法,下面列举了一些接口和类的主要成员:
Edge:方法:Node getAdjacentNode(Node n)返回给定节点的相邻节点集合方法:Graph getGraph()返回改变所在图形对象graph方法:Node getSourceNode()/getTargetNode()返回源节点、目标节点方法:boolean isDirected()判断有向图还是无向图Node:Graph getGraph()返回节点所在的图形graphint getInDegree()返回节点入度个数int getOutDegree()返回节点出度个数int getDegree()返回节点度数java.util.Iterator inEdges()返回指向该节点的边的迭代器java.util.Iterator outEdges()返回从节点指出的边的迭代器java.util.Iterator edges()返回边的迭代器java.util.Iterator inNeighbors()返回所有链入该节点的迭代器java.util.Iterator outNeighbors()Get an iterator over all adjacent nodes connected to this node by an outgoing edge (i.e., all nodes "pointed" to by this one).返回所有链出该节点的迭代器java.util.Iterator neighbors()返回所有近邻节点的迭代器 Node getParent()返回父节点Edge getParentEdge()返回父边int getDepth()返回深度(根节点深度为0)int getChildCount()返回子节点个数int getChildIndex(Node child)返回节点索引值Node getChild(int idx)根据序号获得相应节点Node getFirstChild()获得第一个子节点Node getLastChild()获得最后一个子节点Node getPreviousSibling()得到上一个兄弟节点Node getNextSibling()得到下一个兄弟节点java.util.Iterator children()获得孩子节点的迭代器java.util.Iterator childEdges()获得子边集合的迭代器Tuple:Schema getSchema()返回tuple数据概要Table getTable()返回tuple所在图表int getRow()返回tuple所在行数Graph:构造方法:public Graph()创建一个空的无向图;public Graph(boolean directed)true为有向,false为无向;public Graph(Table nodes, Boolean directed)根据给定节点集合创建有向/无向图;public Graph(Table nodes, boolean directed, java.lang.String nodeKey, java.lang.String sourceKey,java.lang.String targetKey)nodeKey起到标示作用,如果为null,则默认使用row numbers(行号)public Graph(Table nodes, Table edges,Boolean directed)public Graph(Table nodes, Table edges, boolean directed, java.lang.String sourceKey, java.lang.String targetKey)还有一些常用方法如添加删除节点或边等。Schema:Schema类是表示一个表格的列,其属性包括列名、数据类型、默认值。其构造方法有:Schema() Schema(int ncols) Schema(java.lang.String[] names, java.lang.Class[] types) Schema(java.lang.String[] names, java.lang.Class[] types, java.lang.Object[] defaults)主要方法有添加删除列,获取列名,锁定Schema对象等。 Table:表格是由一系列行和列数组组成的,每一行即为一个数据记录,每一列是由指定数据域和数据类的数据组成。表格的数据可以直接通过使用行数和列名称进行访问。表格的行可以插入和删除。Table的构造方法:Table()Table(int nrows, int ncols) protected Table(int nrows, int ncols, java.lang.Class tupleType) 其主要包含添加删除行或列Tree:构造方法如下:Tree() Tree(Table nodes, Table edges) Tree(Table nodes, Table edges, java.lang.String sourceKey, java.lang.String targetKey) Tree(Table nodes, Table edges, java.lang.String nodeKey, java.lang.String sourceKey, java.lang.String targetKey) 其方法主要有获取删除子节点、父节点、根节点
2.prefuse.data下还有一些包如:
prefuse.data.column
该包中主要介绍了列属性中可以有不同类型的列值,如BooleanColumn、DateColumn表示列中存储布尔类型和日期类型的值。列类的抽象基类是AbstractColumn。
prefuse.data.event
该包主要包含一些监听类,比如ColumnListener、ExpressionListener等分别表示针对不同对象的监听。
3.Prefuse还至于一些表达式的应用以及Prefuse对于各类表达式的解析
prefuse.data.expression(表达式)
该包主要包含了Prefuse关于表达式用法的类,有AndPredicate、NotPredicate、OrPredicate、ArithmeticExpression、BinaryExpression等分别表示AndPredicate(与)、OrPredicate(或)、NotPredicate(非)、ArithmeticExpression(算术表达式)、BinaryExpression(二进制表达式)等。
prefuse.data.expression.parser(表达式解析)
该包包含如何解析表达式Expression的类。
Prefuse支持的表达式涵盖很全面:
支持操作符运算、流程控制如:”x+y“(加运算)、"x^y"(平方运算)、”x>y“(比较运算)、”IF test THEN x ELSE
y“(if-then-else流程)等等;
一般常用函数如:"ISNODE()"(判断当前Tuple是否是一个节点)、”DEGREE()“(如果当前Tuple图中节点,返回该节点的度数)、”TREEDEPTH()“(如果当前Tuple是图中节点,则返回该节点在书中的深度)等;
数学运算如:"ABS(x)"(绝对值运算)、”SIN(x)“(正弦运算)、"FLOOR(x)"(向下取整)、”SUM(a,b,c...)“(求和运算)等等;
常用字符串运算如:”CAP(str)“(首字母大写)、”REPEAT(str,count)“(字符串替换操作)、”REVERSE(str)“(反转字符串)等等;
颜色控制函数如:”RGB(r,g,b)“(RGB模式颜色赋值)、”GRAY(v)“(灰度值模式颜色赋值)等;
综上相关表达式的支持,大大增强了Prefuse的灵活性,丰富了Prefuse的一些操作功能。
4.有关包prefuse.data.io以及prefuse.data.io.sql已经在《漫谈可视化Prefuse(一)---从SQL Server数据库读取数据》通过离子阐述过。
但是为了提高用户可操作性,也为了熟悉Java图形编程如何进行界面之间的传值,对上面文章中的例子进行了改进,将部分参数如端口号、数据库用户名、密码等信息开放给用户填写,提高程序的可操作性和灵活性,后期实际开发还会能够让用户在多中数据库之间切换。
我的界面传值思路:首先创建一个接收和读取数据的对象config,在填写参数的界面中画出文本框供用户填写参数,在完成按钮中添加监听事件,将所填信息赋给对象config,并执行在父界面中画出读取数据构成的图形,代码如下:
ConnectionToDB.java
public class ConnectionToDB{ public static Visualization vis = new Visualization(); public static Config config = new Config();//存储、获取参数对象 public static JFrame jf = new JFrame(); //标签显示 public static JLabel nodeLabel = new JLabel("节点查询:"); public static JLabel edgeLabel = new JLabel("边查询:"); public static JLabel strConfigLabel = new JLabel("配置字符串:"); public static JLabel databaseNameLabel = new JLabel("数据库名称:"); public static JLabel usernameLabel = new JLabel("用户名:"); public static JLabel passwordLabel = new JLabel("密码:"); public static JLabel portNumberLabel = new JLabel("端口号:"); //文本框 public static JTextField nodeText = new JTextField(); public static JTextField edgeText = new JTextField(); public static JTextField strConfigText = new JTextField(); public static JTextField databaseNameText = new JTextField(); public static JTextField usernameText = new JTextField(); public static JTextField passwordText = new JTextField(); public static JTextField portNumberText = new JTextField(); /** * @param args * @throws DataIOException */ public static void main(String[] args) throws DataIOException { //1.构建显示画面 2.填写参数配置 3.配置传值到主界面 4.主界面图形展示 //-----------1、构建主界面----------- JMenu dataMenu = new JMenu("数据导入");//添加菜单按钮 final JMenuItem dataItem = new JMenuItem("连接数据库"); dataItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { final JFrame second = new JFrame("second"); second.setSize(400,400); second.setLayout(null); strConfigLabel.setBounds(50, 20, 80, 30); strConfigText.setBounds(130, 20, 150, 30); second.add(strConfigLabel); second.add(strConfigText); databaseNameLabel.setBounds(50, 60, 80, 30); databaseNameText.setBounds(130, 60, 150, 30); second.add(databaseNameLabel); second.add(databaseNameText); usernameLabel.setBounds(50, 100, 80, 30); usernameText.setBounds(130, 100, 150, 30); second.add(usernameLabel); second.add(usernameText); passwordLabel.setBounds(50, 140, 80, 30); passwordText.setBounds(130, 140, 150, 30); second.add(passwordLabel); second.add(passwordText); portNumberLabel.setBounds(50, 180, 80, 30); portNumberText.setBounds(130, 180, 150, 30); second.add(portNumberLabel); second.add(portNumberText); nodeLabel.setBounds(50, 220, 80, 30); nodeText.setBounds(130, 220, 150, 30); second.add(nodeLabel); second.add(nodeText); edgeLabel.setBounds(50, 260, 80, 30); edgeText.setBounds(130, 260, 150, 30); second.add(edgeLabel); second.add(edgeText); JButton ok = new JButton("完成配置"); ok.setBounds(70, 300, 100, 30); second.add(ok); second.setVisible(true); ok.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if(nodeText.getText()!=null && edgeText.getText()!=null){ configPass(); System.out.println("node:"+ config.getNodeSql()); try { actionEvent();// second.dispose();这种关闭方式也可以 second.dispatchEvent(new WindowEvent(second,WindowEvent.WINDOW_CLOSING));//关闭子窗口 visual(); } catch (DataIOException e1) { e1.printStackTrace(); } } } }); } }); JMenuBar bar = new JMenuBar(); bar.add(dataMenu); dataMenu.add(dataItem); jf.setSize(new Dimension(800, 600)); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jf.setJMenuBar(bar);// jf.pack(); jf.setVisible(true); } /** * 存储配置界面用户输入的值 */ private static void configPass() { config.setNodeSql(nodeText.getText()); config.setEdgeSql(edgeText.getText()); config.setStrConfig(strConfigText.getText()); config.setDatabaseName(databaseNameText.getText()); config.setUsername(usernameText.getText()); config.setPassword(passwordText.getText()); config.setPortNumber(portNumberText.getText()); } /** * 连接数据库并添加相应效果渲染和动作 * @throws DataIOException */ public static void actionEvent() throws DataIOException{ String driver = config.getStrConfig().toString(); String url = "jdbc:sqlserver://localhost:"+config.getPortNumber().toString()+";DatabaseName="+config.getDatabaseName().toString()+""; String username = config.getUsername().toString(); String password = config.getPassword().toString(); DatabaseDataSource dbds = null; try { dbds = ConnectionFactory.getDatabaseConnection(driver, url, username, password); } catch (SQLException e1) { e1.printStackTrace(); } catch (ClassNotFoundException e1) { e1.printStackTrace(); } System.out.println(config.getEdgeSql()); Table nodes = dbds.getData(config.getNodeSql().toString()); Table edges = dbds.getData(config.getEdgeSql().toString()); Graph graph = new Graph(nodes, edges, false, "id", "sid", "tid"); vis.add("graph", graph); LabelRenderer label = new LabelRenderer("name"); label.setRoundedCorner(10, 10); vis.setRendererFactory(new DefaultRendererFactory(label)); int[] palette = new int[]{ColorLib.rgb(255, 180, 180),ColorLib.rgb(190, 190, 255)}; DataColorAction fill = new DataColorAction("graph.nodes" , "gender" , Constants.NOMINAL, VisualItem.FILLCOLOR,palette); ColorAction text = new ColorAction("graph.nodes", VisualItem.TEXTCOLOR, ColorLib.gray(0)); ColorAction edges1 = new ColorAction("graph.edges", VisualItem.STROKECOLOR, ColorLib.gray(200)); ActionList color = new ActionList(); color.add(fill); color.add(text); color.add(edges1); ActionList layout = new ActionList(Activity.INFINITY); layout.add(new ForceDirectedLayout("graph")); layout.add(new RepaintAction()); vis.putAction("color", color); vis.putAction("layout", layout); } /** * 添加控制器,显示图形 */ public static void visual(){ Display display = new Display(vis); display.setSize(700, 600); display.pan(250, 250); display.addControlListener(new DragControl()); display.addControlListener(new PanControl()); display.addControlListener(new ZoomControl()); display.addControlListener(new WheelZoomControl()); display.addControlListener(new FocusControl(1)); display.addControlListener(new ZoomToFitControl()); jf.add(display); vis.run("color"); vis.run("layout"); } }
Config.java:
public class Config { public String nodeSql; public String edgeSql; public String strConfig; public String databaseName; public String username; public String password; public String portNumber; public String getStrConfig() { return strConfig; } public void setStrConfig(String strConfig) { this.strConfig = strConfig; } public String getDatabaseName() { return databaseName; } public void setDatabaseName(String databaseName) { this.databaseName = databaseName; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getPortNumber() { return portNumber; } public void setPortNumber(String portNumber) { this.portNumber = portNumber; } public String getNodeSql() { return nodeSql; } public void setNodeSql(String nodeSql) { this.nodeSql = nodeSql; } public String getEdgeSql() { return edgeSql; } public void setEdgeSql(String edgeSql) { this.edgeSql = edgeSql; } }
具体图形如下:
(1)带有功能菜单的父界面:
(2)参数配置界面:
(3)填写配置参数界面:
(4)图形显示在父界面并关闭配置窗口:
因为之前对于Swing和AWT编程不是很熟悉,考虑在界面传值也可以做,但是对于多个字符串的传值可能逻辑比较复杂,这里采用一个类Config用来封装数据从而完成数据的存储和读取的工作。
本文链接《漫谈可视化Prefuse(三)---Prefuse API数据结构阅读有感》
http://www.cnblogs.com/bigdataZJ/p/VisualizationPrefuse3.html
后续将继续API之路,了解Prefuse使用的套路,先顺着它,依着它,摸清它的脾性后再一举拿下它^_^
漫谈可视化Prefuse(三)---Prefuse API数据结构阅读有感