漫谈可视化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()
返回节点所在的图形graph
int 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用来封装数据从而完成数据的存储和读取的工作。

后续将继续API之路,了解Prefuse使用的套路,先顺着它,依着它,摸清它的脾性后再一举拿下它^_^

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏黑白安全

SQLMAP tamper WAF 绕过脚本列表注

sqlmap的tamper目录下有41个用于绕过waf的脚本,网上有文章简要介绍过使用方法,但是只是简单说了其中少数几个的作用。本人通过这41个脚本的文档注释,...

18730
来自专栏cmazxiaoma的架构师之路

使用Hibernate、JPA、Lombok遇到的有趣问题

37540
来自专栏Java成神之路

Java_脚本引擎_01_用法入门

最近有个需求,需要在js中调用java,这样能避免更新java,从而实现代码的热更新。

13740
来自专栏流媒体

resources.arsc解析

示例apk 示例代码 binary view二进制文件查看工具: android 6.0系统源码(网上搜索下载,这里暂不提供资源)

18620
来自专栏钟绍威的专栏

怎么实现关闭窗口

思路:在java源文件中查找源代码 首先,我们知道JFrame.setDefaultCloseOperation(int operation)中提供了四种方...

47970
来自专栏风口上的猪的文章

.NET面试题系列[13] - LINQ to Object

"C# 3.0所有特性的提出都是更好地为LINQ服务的" - Learning Hard

13520
来自专栏Golang语言社区

Golang中time包用法--转

time包中包括两类时间:时间点(某一时刻)和时常(某一段时间) 1时间常量(时间格式化) const ( ANSIC = "Mon Jan...

1.9K80
来自专栏JAVA后端开发

给mybatis添加自动建表,自动加字段的功能

以前项目用惯了hibernate,jpa,它有个自动建表功能,只要在PO里加上配置就可以了,感觉很爽. 但现在用mybatis,发现没有该功能,每次都加个字段...

62830
来自专栏菩提树下的杨过

mybatis的物理分页:mybatis-paginator

文件分割与合并是一个常见需求,比如:上传大文件时,可以先分割成小块,传到服务器后,再进行合并。很多高大上的分布式文件系统(比如:google的GFS、taoba...

24090
来自专栏pangguoming

JDBC上关于数据库中多表操作一对多关系和多对多关系的实现方法

我们知道,在设计一个Java bean的时候,要把这些BEAN 的数据存放在数据库中的表结构,然而这些数据库中的表直接又有些特殊的关系,例如员工与部门直接有一对...

89570

扫码关注云+社区

领取腾讯云代金券