Ejemplo n.º 1
0
class ViewerApp(tk.Tk):
    """Example simple GUI to plot a NetworkX Graph"""
    def __init__(self, graph, **kwargs):
        """Additional keyword arguments beyond graph are passed down to the
        GraphCanvas.  See it's docs for details"""
        tk.Tk.__init__(self)
        self.geometry('1000x600')
        self.title('Bridges Viewer')

        bottom_row = 10
        self.columnconfigure(0, weight=1)
        self.rowconfigure(bottom_row, weight=1)

        self.canvas = GraphCanvas(graph, width=400, height=400, **kwargs)
        self.canvas.grid(row=0, column=0, rowspan=bottom_row+2, sticky='NESW')
        self.canvas.onNodeSelected = self.onNodeSelected
        self.canvas.onEdgeSelected = self.onEdgeSelected

        r = 0   # Current row
        tk.Label(self, text='Nodes:').grid(row=r, column=1, sticky='W')
        self.node_entry = AutocompleteEntry(self.canvas.dataG.nodes)
        self.node_entry.bind('<Return>',self.add_node, add='+')
        self.node_entry.bind('<Control-Return>', self.buildNewShortcut, add='+')
        self.node_entry.grid(row=r, column=2, columnspan=2, sticky='NESW', pady=2)
        tk.Button(self, text='+', command=self.add_node, width=2).grid(
            row=r, column=4,sticky=tk.NW,padx=2,pady=2)

        r += 1
        nlsb = tk.Scrollbar(self, orient=tk.VERTICAL)
        self.node_list = tk.Listbox(self, yscrollcommand=nlsb.set, height=5)
        self.node_list.grid(row=r, column=1, columnspan=3, sticky='NESW')
        self.node_list.bind('<Delete>',lambda e: self.node_list.delete(tk.ANCHOR))
        nlsb.config(command=self.node_list.yview)
        nlsb.grid(row=r, column=4, sticky='NWS')

        r += 1
        tk.Label(self, text='Neighbors Levels:').grid(row=r, column=1,
                                                    columnspan=2, sticky=tk.NW)
        self.level_entry = tk.Entry(self, width=4)
        self.level_entry.insert(0,'1')
        self.level_entry.grid(row=r, column=3, sticky=tk.NW, padx=5)

        r += 1
        tk.Button(self, text='Build New', command=self.onBuildNew).grid(
            row=r, column=1)
        tk.Button(self, text='Add to Existing', command=self.onAddToExisting
                  ).grid(row=r, column=2, columnspan=2)

        r += 1
        line = tk.Canvas(self, height=15, width=200)
        line.create_line(0,13,250,13)
        line.create_line(0,15,250,15)
        line.grid(row=r, column=1, columnspan=4, sticky='NESW')

        r += 1
        tk.Label(self, text='Filters:').grid(row=r, column=1, sticky=tk.W)
        self.filter_entry = tk.Entry(self)
        self.filter_entry.bind('<Return>',self.add_filter, add='+')
        self.filter_entry.grid(row=r, column=2, columnspan=2, sticky='NESW', pady=2)
        tk.Button(self, text='+', command=self.add_filter, width=2).grid(
            row=r, column=4,sticky=tk.NW,padx=2,pady=2)

        r += 1
        flsb = tk.Scrollbar(self, orient=tk.VERTICAL)
        self.filter_list = tk.Listbox(self, yscrollcommand=flsb.set, height=5)
        self.filter_list.grid(row=r, column=1, columnspan=3, sticky='NESW')
        self.filter_list.bind('<Delete>',self.remove_filter)
        flsb.config(command=self.node_list.yview)
        flsb.grid(row=r, column=4, sticky='NWS')

        r += 1
        tk.Button(self, text='Clear',command=self.remove_filter).grid(
                    row=r, column=1, sticky='W')
        tk.Button(self, text='?', command=self.filter_help
                  ).grid(row=r, column=4, stick='NESW', padx=2)


        r += 1
        line2 = tk.Canvas(self, height=15, width=200)
        line2.create_line(0,13,250,13)
        line2.create_line(0,15,250,15)
        line2.grid(row=r, column=1, columnspan=4, sticky='NESW')

        r += 1
        self.lbl_attr = tk.Label(self, text='Attributes',
                                 wraplength=200, anchor=tk.SW, justify=tk.LEFT)
        self.lbl_attr.grid(row=r, column=1, columnspan=4, sticky='NW')

        r += 1
        self.tbl_attr = PropertyTable(self, {})
        self.tbl_attr.grid(row=r, column=1, columnspan=4, sticky='NESW')

        assert r == bottom_row, "Set bottom_row to %d" % r

        self._build_menu()

    def _build_menu(self):
        self.menubar = tk.Menu(self)
        self.config(menu=self.menubar)

        view = tk.Menu(self.menubar, tearoff=0)
        view.add_command(label='Undo', command=self.canvas.undo, accelerator="Ctrl+Z")
        self.bind_all("<Control-z>", lambda e: self.canvas.undo())  # Implement accelerator
        view.add_command(label='Redo', command=self.canvas.redo)
        view.add_separator()
        view.add_command(label='Center on node...', command=self.center_on_node)
        view.add_separator()
        view.add_command(label='Reset Node Marks', command=self.reset_node_markings)
        view.add_command(label='Reset Edge Marks', command=self.reset_edge_markings)
        view.add_command(label='Redraw Plot', command=self.canvas.replot)
        view.add_separator()
        view.add_command(label='Grow display one level...', command=self.grow_all)

        self.menubar.add_cascade(label='View', menu=view)

    def center_on_node(self):
        node = NodeDialog(self, "Name of node to center on:").result
        if node is None: return
        self.canvas.center_on_node(node)

    def reset_edge_markings(self):
        for u,v,k,d in self.canvas.dispG.edges_iter(data=True, keys=True):
            token = d['token']
            if token.is_marked:
                self.canvas.mark_edge(u,v,k)

    def reset_node_markings(self):
        for u,d in self.canvas.dispG.nodes_iter(data=True):
            token = d['token']
            if token.is_marked:
                self.canvas.mark_node(u)

    def add_node(self, event=None):
        node = self.node_entry.get()

        if node.isdigit() and self.canvas.dataG.has_node(int(node)):
                node = int(node)

        if self.canvas.dataG.has_node(node):
            self.node_list.insert(tk.END, node)
            self.node_entry.delete(0, tk.END)
        else:
            tkm.showerror("Node not found", "Node '%s' not in graph."%node)

    def add_filter(self, event=None, filter_lambda=None):
        if filter_lambda is None:
            filter_lambda = self.filter_entry.get()

        if self.canvas.add_filter(filter_lambda):
            # We successfully added the filter; add to list and clear entry
            self.filter_list.insert(tk.END, filter_lambda)
            self.filter_entry.delete(0, tk.END)

    def filter_help(self, event=None):
        msg = ("Enter a lambda function which returns True if you wish\n"
               "to show nodes with ONLY a given property.\n"
               "Parameters are:\n"
               "  - u, the node's name, and \n"
               "  - d, the data dictionary.\n\n"
               "Example: \n"
               " d.get('color',None)=='red'\n"
               "would show only red nodes.\n"
               "Example 2:\n"
               " str(u).is_digit()\n"
               "would show only nodes which have a numerical name.\n\n"
               "Multiple filters are ANDed together.")
        tkm.showinfo("Filter Condition", msg)
    def remove_filter(self, event=None):
        all_items = self.filter_list.get(0, tk.END)
        if event is None:
            # When no event passed, this function was called via the "clear"
            # button.
            items = all_items
        else:
            # Remove currently selected item
            items = (self.filter_list.get(tk.ANCHOR),)

        for item in items:
            self.canvas.remove_filter(item)
            idx = all_items.index(item)
            self.filter_list.delete(idx)
            all_items = self.filter_list.get(0, tk.END)


    def grow_all(self):
        """Grow all visible nodes one level"""
        for u, d in self.canvas.dispG.node.copy().items():
            if not d['token'].is_complete:
                self.canvas.grow_node(u)

    def get_node_list(self):
        """Get nodes in the node list and clear"""
        # See if we forgot to hit the plus sign
        if len(self.node_entry.get()) != 0:
            self.add_node()
        nodes = self.node_list.get(0, tk.END)
        self.node_list.delete(0, tk.END)
        return nodes


    def onBuildNew(self):
        nodes = self.get_node_list()

        if len(nodes) == 2:
            self.canvas.plot_path(nodes[0], nodes[1], levels=self.level)
        else:
            self.canvas.plot(nodes, levels=self.level)

    def onAddToExisting(self):
        """Add nodes to existing plot.  Prompt to include link to existing
        if possible"""
        home_nodes = set(self.get_node_list())
        self.canvas.plot_additional(home_nodes, levels=self.level)

    def buildNewShortcut(self, event=None):
        # Add node intelligently then doe a build new
        self.node_entry.event_generate('<Return>') # Resolve current
        self.onBuildNew()

    def goto_path(self, event):
        frm = self.node_entry.get()
        to = self.node_entry2.get()
        self.node_entry.delete(0, tk.END)
        self.node_entry2.delete(0, tk.END)

        if frm == '':
            tkm.showerror("No From Node", "Please enter a node in both "
                "boxes to plot a path.  Enter a node in only the first box "
                "to bring up nodes immediately adjacent.")
            return

        if frm.isdigit() and int(frm) in self.canvas.dataG.nodes():
            frm = int(frm)
        if to.isdigit() and int(to) in self.canvas.dataG.nodes():
            to = int(to)

        self.canvas.plot_path(frm, to, levels=self.level)

    def onNodeSelected(self, node_name, node_dict):
        self.tbl_attr.build(node_dict)
        self.lbl_attr.config(text="Attributes of node '%s'"%node_name)

    def onEdgeSelected(self, edge_name, edge_dict):
        self.tbl_attr.build(edge_dict)
        self.lbl_attr.config(text="Attributes of edge between '%s' and '%s'"%
                                    edge_name[:2])

    @property
    def level(self):
        try:
            l = int(self.level_entry.get())
        except ValueError:
            tkm.showerror("Invalid Level", "Please specify a level between "
                "greater than or equal to 0")
            raise
        return l
Ejemplo n.º 2
0
class ViewerApp(tk.Tk):
    """Example simple GUI to plot a NetworkX Graph"""
    def __init__(self, graph, **kwargs):
        """Additional keyword arguments beyond graph are passed down to the
        GraphCanvas.  See it's docs for details"""
        tk.Tk.__init__(self)
        self.geometry('1000x600')
        self.title('NetworkX Viewer')

        bottom_row = 10
        self.columnconfigure(0, weight=1)
        self.rowconfigure(bottom_row, weight=1)

        self.canvas = GraphCanvas(graph, width=400, height=400, **kwargs)
        self.canvas.grid(row=0,
                         column=0,
                         rowspan=bottom_row + 2,
                         sticky='NESW')
        self.canvas.onNodeSelected = self.onNodeSelected
        self.canvas.onEdgeSelected = self.onEdgeSelected

        r = 0  # Current row
        tk.Label(self, text='Nodes:').grid(row=r, column=1, sticky='W')
        self.node_entry = AutocompleteEntry(self.canvas.dataG.nodes)
        self.node_entry.bind('<Return>', self.add_node, add='+')
        self.node_entry.bind('<Control-Return>',
                             self.buildNewShortcut,
                             add='+')
        self.node_entry.grid(row=r,
                             column=2,
                             columnspan=2,
                             sticky='NESW',
                             pady=2)
        tk.Button(self, text='+', command=self.add_node,
                  width=2).grid(row=r, column=4, sticky=tk.NW, padx=2, pady=2)

        r += 1
        nlsb = tk.Scrollbar(self, orient=tk.VERTICAL)
        self.node_list = tk.Listbox(self, yscrollcommand=nlsb.set, height=5)
        self.node_list.grid(row=r, column=1, columnspan=3, sticky='NESW')
        self.node_list.bind('<Delete>',
                            lambda e: self.node_list.delete(tk.ANCHOR))
        nlsb.config(command=self.node_list.yview)
        nlsb.grid(row=r, column=4, sticky='NWS')

        r += 1
        tk.Label(self, text='Neighbors Levels:').grid(row=r,
                                                      column=1,
                                                      columnspan=2,
                                                      sticky=tk.NW)
        self.level_entry = tk.Entry(self, width=4)
        self.level_entry.insert(0, '1')
        self.level_entry.grid(row=r, column=3, sticky=tk.NW, padx=5)

        r += 1
        tk.Button(self, text='Build New',
                  command=self.onBuildNew).grid(row=r, column=1)
        tk.Button(self, text='Add to Existing',
                  command=self.onAddToExisting).grid(row=r,
                                                     column=2,
                                                     columnspan=2)

        r += 1
        line = tk.Canvas(self, height=15, width=200)
        line.create_line(0, 13, 250, 13)
        line.create_line(0, 15, 250, 15)
        line.grid(row=r, column=1, columnspan=4, sticky='NESW')

        r += 1
        tk.Label(self, text='Filters:').grid(row=r, column=1, sticky=tk.W)
        self.filter_entry = tk.Entry(self)
        self.filter_entry.bind('<Return>', self.add_filter, add='+')
        self.filter_entry.grid(row=r,
                               column=2,
                               columnspan=2,
                               sticky='NESW',
                               pady=2)
        tk.Button(self, text='+', command=self.add_filter,
                  width=2).grid(row=r, column=4, sticky=tk.NW, padx=2, pady=2)

        r += 1
        flsb = tk.Scrollbar(self, orient=tk.VERTICAL)
        self.filter_list = tk.Listbox(self, yscrollcommand=flsb.set, height=5)
        self.filter_list.grid(row=r, column=1, columnspan=3, sticky='NESW')
        self.filter_list.bind('<Delete>', self.remove_filter)
        flsb.config(command=self.node_list.yview)
        flsb.grid(row=r, column=4, sticky='NWS')

        r += 1
        tk.Button(self, text='Clear',
                  command=self.remove_filter).grid(row=r, column=1, sticky='W')
        tk.Button(self, text='?', command=self.filter_help).grid(row=r,
                                                                 column=4,
                                                                 stick='NESW',
                                                                 padx=2)

        r += 1
        line2 = tk.Canvas(self, height=15, width=200)
        line2.create_line(0, 13, 250, 13)
        line2.create_line(0, 15, 250, 15)
        line2.grid(row=r, column=1, columnspan=4, sticky='NESW')

        r += 1
        self.lbl_attr = tk.Label(self,
                                 text='Attributes',
                                 wraplength=200,
                                 anchor=tk.SW,
                                 justify=tk.LEFT)
        self.lbl_attr.grid(row=r, column=1, columnspan=4, sticky='NW')

        r += 1
        self.tbl_attr = PropertyTable(self, {})
        self.tbl_attr.grid(row=r, column=1, columnspan=4, sticky='NESW')

        assert r == bottom_row, "Set bottom_row to %d" % r

        self._build_menu()

    def _build_menu(self):
        self.menubar = tk.Menu(self)
        self.config(menu=self.menubar)

        view = tk.Menu(self.menubar, tearoff=0)
        view.add_command(label='Undo',
                         command=self.canvas.undo,
                         accelerator="Ctrl+Z")
        self.bind_all("<Control-z>",
                      lambda e: self.canvas.undo())  # Implement accelerator
        view.add_command(label='Redo', command=self.canvas.redo)
        view.add_separator()
        view.add_command(label='Center on node...',
                         command=self.center_on_node)
        view.add_separator()
        view.add_command(label='Reset Node Marks',
                         command=self.reset_node_markings)
        view.add_command(label='Reset Edge Marks',
                         command=self.reset_edge_markings)
        view.add_command(label='Redraw Plot', command=self.canvas.replot)
        view.add_separator()
        view.add_command(label='Grow display one level...',
                         command=self.grow_all)

        self.menubar.add_cascade(label='View', menu=view)

    def center_on_node(self):
        node = NodeDialog(self, "Name of node to center on:").result
        if node is None: return
        self.canvas.center_on_node(node)

    def reset_edge_markings(self):
        for u, v, k, d in self.canvas.dispG.edges(data=True, keys=True):
            token = d['token']
            if token.is_marked:
                self.canvas.mark_edge(u, v, k)

    def reset_node_markings(self):
        for u, d in self.canvas.dispG.nodes(data=True):
            token = d['token']
            if token.is_marked:
                self.canvas.mark_node(u)

    def add_node(self, event=None):
        node = self.node_entry.get()

        if node.isdigit() and self.canvas.dataG.has_node(int(node)):
            node = int(node)

        if self.canvas.dataG.has_node(node):
            self.node_list.insert(tk.END, node)
            self.node_entry.delete(0, tk.END)
        else:
            tkm.showerror("Node not found", "Node '%s' not in graph." % node)

    def add_filter(self, event=None, filter_lambda=None):
        if filter_lambda is None:
            filter_lambda = self.filter_entry.get()

        if self.canvas.add_filter(filter_lambda):
            # We successfully added the filter; add to list and clear entry
            self.filter_list.insert(tk.END, filter_lambda)
            self.filter_entry.delete(0, tk.END)

    def filter_help(self, event=None):
        msg = ("Enter a lambda function which returns True if you wish\n"
               "to show nodes with ONLY a given property.\n"
               "Parameters are:\n"
               "  - u, the node's name, and \n"
               "  - d, the data dictionary.\n\n"
               "Example: \n"
               " d.get('color',None)=='red'\n"
               "would show only red nodes.\n"
               "Example 2:\n"
               " str(u).is_digit()\n"
               "would show only nodes which have a numerical name.\n\n"
               "Multiple filters are ANDed together.")
        tkm.showinfo("Filter Condition", msg)

    def remove_filter(self, event=None):
        all_items = self.filter_list.get(0, tk.END)
        if event is None:
            # When no event passed, this function was called via the "clear"
            # button.
            items = all_items
        else:
            # Remove currently selected item
            items = (self.filter_list.get(tk.ANCHOR), )

        for item in items:
            self.canvas.remove_filter(item)
            idx = all_items.index(item)
            self.filter_list.delete(idx)
            all_items = self.filter_list.get(0, tk.END)

    def grow_all(self):
        """Grow all visible nodes one level"""
        for u, d in self.canvas.dispG.copy().nodes.items():
            if not d['token'].is_complete:
                self.canvas.grow_node(u)

    def get_node_list(self):
        """Get nodes in the node list and clear"""
        # See if we forgot to hit the plus sign
        if len(self.node_entry.get()) != 0:
            self.add_node()
        nodes = self.node_list.get(0, tk.END)
        self.node_list.delete(0, tk.END)
        return nodes

    def onBuildNew(self):
        nodes = self.get_node_list()

        if len(nodes) == 2:
            self.canvas.plot_path(nodes[0], nodes[1], levels=self.level)
        else:
            self.canvas.plot(nodes, levels=self.level)

    def onAddToExisting(self):
        """Add nodes to existing plot.  Prompt to include link to existing
        if possible"""
        home_nodes = set(self.get_node_list())
        self.canvas.plot_additional(home_nodes, levels=self.level)

    def buildNewShortcut(self, event=None):
        # Add node intelligently then doe a build new
        self.node_entry.event_generate('<Return>')  # Resolve current
        self.onBuildNew()

    def goto_path(self, event):
        frm = self.node_entry.get()
        to = self.node_entry2.get()
        self.node_entry.delete(0, tk.END)
        self.node_entry2.delete(0, tk.END)

        if frm == '':
            tkm.showerror(
                "No From Node", "Please enter a node in both "
                "boxes to plot a path.  Enter a node in only the first box "
                "to bring up nodes immediately adjacent.")
            return

        if frm.isdigit() and int(frm) in self.canvas.dataG.nodes():
            frm = int(frm)
        if to.isdigit() and int(to) in self.canvas.dataG.nodes():
            to = int(to)

        self.canvas.plot_path(frm, to, levels=self.level)

    def onNodeSelected(self, node_name, node_dict):
        self.tbl_attr.build(node_dict)
        self.lbl_attr.config(text="Attributes of node '%s'" % node_name)

    def onEdgeSelected(self, edge_name, edge_dict):
        self.tbl_attr.build(edge_dict)
        self.lbl_attr.config(text="Attributes of edge between '%s' and '%s'" %
                             edge_name[:2])

    @property
    def level(self):
        try:
            l = int(self.level_entry.get())
        except ValueError:
            tkm.showerror(
                "Invalid Level", "Please specify a level between "
                "greater than or equal to 0")
            raise
        return l
class ViewerApp(tk.Tk):
    """Example simple GUI to plot a NetworkX Graph"""
    def __init__(self, graph, **kwargs):
        """Additional keyword arguments beyond graph are passed down to the
        GraphCanvas.  See it's docs for details"""
        tk.Tk.__init__(self)
        self.geometry('1000x600')
        self.title('NetworkX Viewer')

        bottom_row = 10
        self.columnconfigure(0, weight=1)
        self.rowconfigure(bottom_row, weight=1)

        self.canvas = GraphCanvas(graph, width=400, height=400, **kwargs)
        self.canvas.grid(row=0,
                         column=0,
                         rowspan=bottom_row + 2,
                         sticky='NESW')
        self.canvas.onNodeSelected = self.onNodeSelected
        self.canvas.onEdgeSelected = self.onEdgeSelected

        r = 0  # Current row
        tk.Label(self, text='Nodes:').grid(row=r, column=1, sticky='W')
        self.node_entry = AutocompleteEntry(self.canvas.dataG.nodes)
        self.node_entry.bind('<Return>', self.add_node, add='+')
        self.node_entry.grid(row=r,
                             column=2,
                             columnspan=2,
                             sticky='NESW',
                             pady=2)
        tk.Button(self, text='+', command=self.add_node,
                  width=2).grid(row=r, column=4, sticky=tk.NW, padx=2, pady=2)

        r += 1
        nlsb = tk.Scrollbar(self, orient=tk.VERTICAL)
        self.node_list = tk.Listbox(self, yscrollcommand=nlsb.set, height=5)
        self.node_list.grid(row=r, column=1, columnspan=3, sticky='NESW')
        self.node_list.bind('<Delete>',
                            lambda e: self.node_list.delete(tk.ANCHOR))
        nlsb.config(command=self.node_list.yview)
        nlsb.grid(row=r, column=4, sticky='NWS')

        r += 1
        tk.Label(self, text='Neighbors Levels:').grid(row=r,
                                                      column=1,
                                                      columnspan=2,
                                                      sticky=tk.NW)
        self.level_entry = tk.Entry(self, width=4)
        self.level_entry.insert(0, '1')
        self.level_entry.grid(row=r, column=3, sticky=tk.NW, padx=5)

        r += 1
        tk.Button(self, text='Build New',
                  command=self.onBuildNew).grid(row=r, column=1)
        tk.Button(self, text='Add to Existing',
                  command=self.onAddToExisting).grid(row=r,
                                                     column=2,
                                                     columnspan=2)

        r += 1
        line = tk.Canvas(self, height=15, width=200)
        line.create_line(0, 13, 250, 13)
        line.create_line(0, 15, 250, 15)
        line.grid(row=r, column=1, columnspan=4, sticky='NESW')

        r += 1
        tk.Label(self, text='Filters:').grid(row=r, column=1, sticky=tk.W)
        self.filter_entry = tk.Entry(self)
        self.filter_entry.bind('<Return>', self.add_filter, add='+')
        self.filter_entry.grid(row=r,
                               column=2,
                               columnspan=2,
                               sticky='NESW',
                               pady=2)
        tk.Button(self, text='+', command=self.add_filter,
                  width=2).grid(row=r, column=4, sticky=tk.NW, padx=2, pady=2)

        r += 1
        flsb = tk.Scrollbar(self, orient=tk.VERTICAL)
        self.filter_list = tk.Listbox(self, yscrollcommand=flsb.set, height=5)
        self.filter_list.grid(row=r, column=1, columnspan=3, sticky='NESW')
        self.filter_list.bind('<Delete>', self.remove_filter)
        flsb.config(command=self.node_list.yview)
        flsb.grid(row=r, column=4, sticky='NWS')

        r += 1
        tk.Button(self, text='Clear',
                  command=self.remove_filter).grid(row=r, column=1, sticky='W')
        tk.Button(self, text='?', command=self.filter_help).grid(row=r,
                                                                 column=4,
                                                                 stick='NESW',
                                                                 padx=2)

        r += 1
        line2 = tk.Canvas(self, height=15, width=200)
        line2.create_line(0, 13, 250, 13)
        line2.create_line(0, 15, 250, 15)
        line2.grid(row=r, column=1, columnspan=4, sticky='NESW')

        r += 1
        self.lbl_attr = tk.Label(self,
                                 text='Attributes',
                                 wraplength=200,
                                 anchor=tk.SW,
                                 justify=tk.LEFT)
        self.lbl_attr.grid(row=r, column=1, columnspan=4, sticky='NW')

        r += 1
        self.tbl_attr = PropertyTable(self, {})
        self.tbl_attr.grid(row=r, column=1, columnspan=4, sticky='NESW')

        assert r == bottom_row, "Set bottom_row to %d" % r

        self._build_menu()

    def _build_menu(self):
        self.menubar = tk.Menu(self)
        self.config(menu=self.menubar)

        view = tk.Menu(self.menubar, tearoff=0)
        view.add_command(label='Center on node...',
                         command=self.center_on_node)
        view.add_separator()
        view.add_command(label='Reset Node Marks',
                         command=self.reset_node_markings)
        view.add_command(label='Reset Edge Marks',
                         command=self.reset_edge_markings)
        view.add_command(label='Redraw Plot', command=self.canvas.replot)
        view.add_separator()
        view.add_command(label='Grow display one level...',
                         command=self.grow_all)

        self.menubar.add_cascade(label='View', menu=view)

        algorithms = tk.Menu(self.menubar, tearoff=0)
        self.menubar.add_cascade(label='Algorithms', menu=algorithms)
        algorithms.add_command(label='Clique', command=self.clique)
        algorithms.add_command(label='Clustering', command=self.clustering)
        algorithms.add_command(label='Centrality', command=self.centrality)
        algorithms.add_separator()

    def clique(self):
        import config
        graphs = config.graph
        file = open('clique.txt', 'w')
        clique_string = [
            "This txt file will show you the finding of cliques in your network.\n\n"
            +
            "Description : In complex network, a clique is a maximal subset of the vertices or nodes in an undirected network such that every member\n"
            +
            "of the set is connected by an edge or link to every other node." +
            "The meaning of 'maximal' here means there is no other vertex or node in the network that can be added to the subset while keeping or preserving\n"
            +
            "the property that every vertex or node is connected to every other.\n"
        ]
        file.write(clique_string[0])

        max_clique = list(nx.make_max_clique_graph(graphs))
        max_clique_str = str(max_clique)
        max_clique_string = [
            "Maximal Cliques:\n-The maximal cliques and treats these cliques as nodes.\n -These nodes in a [] are connected if they have common members in the original graph.\n"
            + "-" + max_clique_str + '\n'
        ]
        file.write(max_clique_string[0])

        all_maximal_cliques = str(list(nx.find_cliques(graphs)))
        all_maximal_cliques_string = [
            "Cliques:\n-The possible cliques in the network.\n" + "-" +
            all_maximal_cliques + '\n'
        ]
        file.write(all_maximal_cliques_string[0])

        number_of_maximum_clique = str(nx.graph_number_of_cliques(graphs))
        number_of_node_in_largest_clique = str(nx.graph_clique_number(graphs))
        clique_number_string = [
            "Basic statistic of cliques in network:\n-The (largest) number of cliques in the network:"
            + number_of_maximum_clique + "\n" +
            "-The number of nodes in the largest clique in the network:" +
            number_of_node_in_largest_clique
        ]
        file.write(clique_number_string[0])

        file.close()  # this must add or only display a empty txt
        import os
        os.system("notepad.exe clique.txt")

    def clustering(self):
        import config
        graphs = config.graph
        file = open('clustering.txt', 'w')

        cluster_string = [
            'This txt file will show you the finding of clusters in your network.\n\n'
            +
            "Description : In complex network, nodes tend to gather and cluster together, which is called 'small-world' phenomenon.\n"
            +
            "              Therefore, different clustering coefficient is a measure of the degree to which nodes in a graph tend to cluster together.\n"
        ]
        file.write(cluster_string[0])
        file.write("\n\n")

        cluster = nx.clustering(graphs)
        cluster_list = cluster.items()
        clustering_string = [
            "Clustering Coefficient:\n-Compute the clustering coefficient for nodes.\n-The clustering coefficient measures the average probability that two neighbors of a node are\n"
            +
            "themselves neighbors. In effect it measures the density of triangles in the network.\n"
            + "-node name  Clustering Coefficient\n"
        ]
        file.write(clustering_string[0])
        for i in cluster_list:
            file.write(str(i) + '\n')
        file.write("\n\n")

        average_cluster = nx.average_clustering(graphs)
        average_cluster_string = [
            "Average Clustering Coefficient:\n" +
            "-Compute the average clustering coefficient for the\n" +
            "-Average Clustering Coefficient:" + " " + str(average_cluster) +
            "\n\n"
        ]
        file.write(average_cluster_string[0])

        transitivity = nx.transitivity(graphs)
        transitivity_string = [
            "Transitivity:\n" +
            "-Compute graph transitivity, the fraction of all possible triangles present in the network.\n"
            + "-Transitivity:" + " " + str(transitivity)
        ]
        file.write(transitivity_string[0])
        file.write("\n\n")

        triangles = nx.triangles(graphs)
        triangles_string = [
            "Triangles:\n" + "-Compute the number of triangles.\n" +
            "-Triangles:\n"
        ]
        file.write(triangles_string[0])
        for i in triangles:
            file.write(str(i) + ':' + str(triangles[i]) + '\n')
        file.write("\n\n")

        file.close()  # this must add or only display a empty txt
        import os
        os.system("notepad.exe clustering.txt")

    def centrality(self):
        import config
        graphs = config.graph
        file = open('centrality.txt', 'w')

        degree_centralitys = nx.degree_centrality(graphs)
        closeness_centralitys = nx.closeness_centrality(graphs)
        betweenness_centralitys = nx.betweenness_centrality(graphs)
        degree_centralitys_string = [
            "Description:\n\n" +
            "-Compute the centrality measures for nodes.\n" +
            "-Degree centrality for a node v is the fraction of nodes it is connected to.\n"
            +
            "-Closeness centrality of a node i is the reciprocal of the sum of the shortest path distances from node i to all (n-i) other nodes.\n"
            +
            "-Between centrality betweenness centrality is a measure of centrality in a graph based on shortest paths. \n"
            +
            "-node name(nn):degree centrality(dc),closeness centrality(cc),betweenness centrality(bc):\n"
            + "-nn:dc,cc,bc\n\n"
        ]
        file.write(degree_centralitys_string[0])
        for i in degree_centralitys:
            file.write(
                str(i) + ' : ' + str(degree_centralitys[i]) + ' , ' +
                str(closeness_centralitys[i]) + ' , ' +
                str(betweenness_centralitys[i]) + '\n')
        file.write("\n\n")

        file.close()  # this must add or only display a empty txt
        import os
        os.system("notepad.exe centrality.txt")

    def center_on_node(self):
        node = NodeDialog(self, "Name of node to center on:").result
        if node is None: return
        self.canvas.center_on_node(node)

    def reset_edge_markings(self):
        for u, v, k, d in self.canvas.dispG.edges_iter(data=True, keys=True):
            token = d['token']
            if token.is_marked:
                self.canvas.mark_edge(u, v, k)

    def reset_node_markings(self):
        for u, d in self.canvas.dispG.nodes_iter(data=True):
            token = d['token']
            if token.is_marked:
                self.canvas.mark_node(u)

    def add_node(self, event=None):
        node = self.node_entry.get()

        if node.isdigit() and self.canvas.dataG.has_node(int(node)):
            node = int(node)

        if self.canvas.dataG.has_node(node):
            self.node_list.insert(tk.END, node)
            self.node_entry.delete(0, tk.END)
        else:
            tkm.showerror("Node not found", "Node '%s' not in graph." % node)

    def add_filter(self, event=None, filter_lambda=None):
        if filter_lambda is None:
            filter_lambda = self.filter_entry.get()

        if self.canvas.add_filter(filter_lambda):
            # We successfully added the filter; add to list and clear entry
            self.filter_list.insert(tk.END, filter_lambda)
            self.filter_entry.delete(0, tk.END)

    def filter_help(self, event=None):
        msg = ("Enter a lambda function which returns True if you wish\n"
               "to show nodes with ONLY a given property.\n"
               "Parameters are:\n"
               "  - u, the node's name, and \n"
               "  - d, the data dictionary.\n\n"
               "Example: \n"
               " d.get('color',None)=='red'\n"
               "would show only red nodes.\n"
               "Example 2:\n"
               " str(u).is_digit()\n"
               "would show only nodes which have a numerical name.\n\n"
               "Multiple filters are ANDed together.")
        tkm.showinfo("Filter Condition", msg)

    def remove_filter(self, event=None):
        all_items = self.filter_list.get(0, tk.END)
        if event is None:
            # When no event passed, this function was called via the "clear"
            # button.
            items = all_items
        else:
            # Remove currently selected item
            items = (self.filter_list.get(tk.ANCHOR), )

        for item in items:
            self.canvas.remove_filter(item)
            idx = all_items.index(item)
            self.filter_list.delete(idx)

    def grow_all(self):
        """Grow all visible nodes one level"""
        for u, d in self.canvas.dispG.node.copy().items():
            if not d['token'].is_complete:
                self.canvas.grow_node(u)

    def get_node_list(self):
        """Get nodes in the node list and clear"""
        # See if we forgot to hit the plus sign
        if len(self.node_entry.get()) != 0:
            self.add_node()
        nodes = self.node_list.get(0, tk.END)
        self.node_list.delete(0, tk.END)
        return nodes

    def onBuildNew(self):
        nodes = self.get_node_list()
        self.canvas.clear()

        if len(nodes) == 2:
            self.canvas.plot_path(nodes[0], nodes[1], levels=self.level)
        else:
            self.canvas.plot(nodes, levels=self.level)

    def onAddToExisting(self):
        """Add nodes to existing plot.  Prompt to include link to existing
        if possible"""
        home_nodes = set(self.get_node_list())
        self.canvas.plot_additional(home_nodes, levels=self.level)

    def goto_path(self, event):
        frm = self.node_entry.get()
        to = self.node_entry2.get()
        self.node_entry.delete(0, tk.END)
        self.node_entry2.delete(0, tk.END)

        if frm == '':
            tkm.showerror(
                "No From Node", "Please enter a node in both "
                "boxes to plot a path.  Enter a node in only the first box "
                "to bring up nodes immediately adjacent.")
            return

        if frm.isdigit() and int(frm) in self.canvas.dataG.nodes():
            frm = int(frm)
        if to.isdigit() and int(to) in self.canvas.dataG.nodes():
            to = int(to)

        self.canvas.plot_path(frm, to, levels=self.level)

    def onNodeSelected(self, node_name, node_dict):
        self.tbl_attr.build(node_dict)
        self.lbl_attr.config(text="Attributes of node '%s'" % node_name)

    def onEdgeSelected(self, edge_name, edge_dict):
        self.tbl_attr.build(edge_dict)
        self.lbl_attr.config(text="Attributes of edge between '%s' and '%s'" %
                             edge_name[:2])

    @property
    def level(self):
        try:
            l = int(self.level_entry.get())
        except ValueError:
            tkm.showerror(
                "Invalid Level", "Please specify a level between "
                "greater than or equal to 0")
            raise
        return l