Commit 8ac78b79 authored by Francois Mares's avatar Francois Mares
Browse files

upgrade selection

parent 77f2ed72
from enum import Enum, unique
from Parameters import parameters
import networkx as nx
@unique
class LayoutAlgo(Enum):
Random=nx.random_layout
Graphviz=nx.nx_agraph.graphviz_layout
class GraphDrawer:
_layoutAlgo=LayoutAlgo.Random
_nodeSelector=None
_plot=None
_canvas=None
_graph=None
_pos=None
_node_color=None
def __init__(self, plot, canvas, *args, **kwargs):
self._plot=plot
self._canvas=canvas
self._init()
def _init(self):
# node selector
self._nodeSelector=NodeSelector()
self._canvas.mpl_connect('button_press_event', self._nodeSelector)
# random graph
G = nx.fast_gnp_random_graph(n=10, p=0.5,directed=True)
self.setGraph(G)
def setGraph(self, G:nx.Graph):
self._graph=G
# generate node positions
if callable(getattr(self._graph,"check_planarity",None)) and self._graph.check_planarity()[0]:
self._pos=nx.planar_layout(self._graph)
else:
self._pos=nx.random_layout(self._graph)
# update selector
self._nodeSelector.updateGraph(self._graph, self._pos)
# draw
self._plot.cla()
self._node_color=['blue' for n in self._graph.nodes]
nx.draw_networkx(self._graph, ax=self._plot, pos=self._pos, node_color=self._node_color)
self._canvas.draw()
class NodeSelector:
"""
À associer à un évènement (clic de la sourie)
"self._canvas.mpl_connect('button_press_event', self._nodeSelector)"
"""
_axis=None
_data=None
_graph=None
_pos=None
def __init__(self, G:nx.Graph=None, pos=None, axis=None):
self._axis= axis
if G != None and pos != None:
self.updateGraph(G, pos)
def updateGraph(self, G:nx.Graph, pos):
self._graph=G
self._pos=pos
x, y, annotes = [], [], []
for key in self._pos:
d = self._pos[key]
annotes.append(key)
x.append(d[0])
y.append(d[1])
self._data = list(zip(x, y, annotes))
nx.set_node_attributes(self._graph, name="selected", values=False)
def __call__(self, event):
if event.inaxes:
clickX = event.xdata
clickY = event.ydata
if self._axis is None or self._axis==event.inaxes:
distances = []
smallest_x_dist = float('inf')
smallest_y_dist = float('inf')
for x,y,node in self._data:
if abs(clickX-x)<=smallest_x_dist and abs(clickY-y)<=smallest_y_dist :
dx, dy = x - clickX, y - clickY
distances.append((dx*dx+dy*dy,x,y, node) )
smallest_x_dist=abs(clickX-x)
smallest_y_dist=abs(clickY-y)
if distances:
distances.sort() # to select the nearest node
distance, x, y, node = distances[0]
# self.drawAnnote(event.inaxes, x, y, annote)
self.select(event.inaxes, node)
def select(self, axis, node):
print(self._graph.nodes(data=True))
# mise à jour état du noeud (attribut 'selected')
if self._graph.nodes[node]['selected']==False:
nx.set_node_attributes(self._graph, name='selected', values={node:True})
else:
nx.set_node_attributes(self._graph, name='selected', values={node:False})
# mise à jour liste des couleurs
self._node_color=[]
for node in self._graph.nodes(data=True):
if node[1]['selected'] == False:
self._node_color.append('blue')
elif node[1]['selected'] == True:
self._node_color.append('red')
axis.cla()
nx.draw_networkx(self._graph, ax=axis, pos=self._pos, node_color=self._node_color)
axis.figure.canvas.draw()
# def drawAnnote(self, axis, x, y, annote):
# if (x, y) in self._drawnAnnotations:
# markers = self._drawnAnnotations[(x, y)]
# for m in markers:
# m.set_visible(not m.get_visible())
# axis.figure.canvas.draw()
# else:
# t = axis.text(x, y, "%s" % (annote), )
# m = axis.scatter([x], [y], marker='d', c='r', zorder=100)
# self._drawnAnnotations[(x, y)] = (t, m)
# axis.figure.canvas.draw()
\ No newline at end of file
from itertools import product
from Categorie import Categorie
from Parameters import parameters
import networkx as nx
class GraphGenerator:
"""
Static
Construit le nx.Graph associé à un objet par la fonction 'give(object)'
"""
def give(obj) -> nx.Graph:
if issubclass(type(obj), Categorie):
return GraphGenerator._genererGrapheCategorie(obj)
else:
print(f"Impossible de générer le graphe de l'objet: {obj}")
"""
GENERATEURS PAR CLASSE
"""
def _genererGrapheCategorie(cat,
afficher_identites:bool = True,
complet:bool = True,
limite_fleches:int = 30)-> nx.Graph:
"""
@param complet: - si True affiche toutes les flèches;
- si False affiche seulement les flèches élémentaires.
@param limite_fleches: nombre maximal de flèches entre deux objets.
"""
G = nx.MultiDiGraph()
G.add_nodes_from(cat.objets, color=parameters.NODE_COLOR)
#pos = nx.spring_layout(G)
# flesches
fleches_elem = set(cat[cat.objets,cat.objets])
if complet:
func = lambda x,y : cat({x},{y})
else:
func = lambda x,y : cat[{x},{y}]
for source,cible in product(cat.objets,repeat=2):
nb_fleches = 0
for fleche in func(source,cible):
if afficher_identites or not fleche.is_identite:
if fleche.source not in cat.objets:
raise Exception("Source d'une fleche pas dans les objets de la categorie.")
if fleche.cible not in cat.objets:
raise Exception("Source d'une fleche pas dans les objets de la categorie.")
if len({obj for obj in cat.objets if obj == fleche.source}) > 1:
raise Exception("Plus d'un objet de la catégorie est source de la fleche.")
if len({obj for obj in cat.objets if obj == fleche.cible}) > 1:
raise Exception("Plus d'un objet de la catégorie est cible de la fleche.")
#permet d'avoir toujours un objet de la catégorie comme source
source = {obj for obj in cat.objets if obj == fleche.source}.pop()
#permet d'avoir toujours un objet de la catégorie comme cible
cible = {obj for obj in cat.objets if obj == fleche.cible}.pop()
# descriptions
if fleche in fleches_elem:
G.add_edge(str(source),str(cible),
color=parameters.EDGE_ELEMENTARY_COLOR,
label=str(fleche))
else:
G.add_edge(str(source),str(cible),
color=parameters.EDGE_NO_ELEMENTARY_COLOR,
label=str(fleche))
nb_fleches += 1
if nb_fleches > limite_fleches:
if WARNING_LIMITE_FLECHES_ATTEINTE:
print("Warning : limite fleches entre "+str(source)+" et "+str(cible)+" atteinte.")
break
return G
\ No newline at end of file
"""entry point for Tkinter Window with OpenGL]
"""
from itertools import product
from enum import Enum, unique
from tkinter import TOP, X, YES, BOTH, \
Frame, Label
from Categorie import Categorie
from tk.Factory import *
from Parameters import parameters
from plot.GraphDrawer import GraphDrawer
import matplotlib.pyplot as plt
import matplotlib
......@@ -19,97 +13,18 @@ from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationTool
import networkx as nx
@unique
class LayoutAlgo(Enum):
Random=nx.random_layout
Graphviz=nx.nx_agraph.graphviz_layout
class NodeSelector:
class PlotFrame(Frame):
"""
À associer à un évènement (clic de la sourie)
__call__ est alors appelée
Frame capable d'afficher un graphe NetworkX
"""
_pos=None
_axis=None
_data=None
_drawnAnnotations = {}
_G=None
_selection=[]
def __init__(self, pos, G, axis=None):
self._G=G
self._pos=pos
self._axis= axis
x, y, annotes = [], [], []
for key in pos:
d = pos[key]
annotes.append(key)
x.append(d[0])
y.append(d[1])
self._data = list(zip(x, y, annotes))
def __call__(self, event):
if event.inaxes:
clickX = event.xdata
clickY = event.ydata
print("event:",dir(event),event.key)
if self._axis is None or self._axis==event.inaxes:
distances = []
smallest_x_dist = float('inf')
smallest_y_dist = float('inf')
for x,y,a in self._data:
if abs(clickX-x)<=smallest_x_dist and abs(clickY-y)<=smallest_y_dist :
dx, dy = x - clickX, y - clickY
distances.append((dx*dx+dy*dy,x,y, a) )
smallest_x_dist=abs(clickX-x)
smallest_y_dist=abs(clickY-y)
print(distances[-1])
if distances:
distances.sort() # to select the nearest node
distance, x, y, annote = distances[0]
# self.drawAnnote(event.inaxes, x, y, annote)
self.select(event.inaxes, annote)
def select(self, axis, node):
if node in self._selection:
self._selection.remove(node)
nx.set_node_attributes(self._G, {node: "red"}, name="fillcolor")
#self._G.nodes[int(node)]['color']='red'
else:
self._selection.append(node)
nx.set_node_attributes(self._G, {node: "blue"}, name="fillcolor")
#self._G.nodes[int(node)]['color']='blue'
axis.cla()
nx.draw_networkx(self._G, ax=axis, pos=self._pos)
axis.figure.canvas.draw()
# print(self._G.nodes(data=True))
def drawAnnote(self, axis, x, y, annote):
if (x, y) in self._drawnAnnotations:
markers = self._drawnAnnotations[(x, y)]
for m in markers:
m.set_visible(not m.get_visible())
axis.figure.canvas.draw()
else:
t = axis.text(x, y, "%s" % (annote), )
m = axis.scatter([x], [y], marker='d', c='r', zorder=100)
self._drawnAnnotations[(x, y)] = (t, m)
axis.figure.canvas.draw()
class PlotFrame(Frame):
_controller=None
_plot=None
_canvas=None
_layoutAlgo=LayoutAlgo.Random
_pos=None
_graphDrawer=None
def __init__(self, parent, controller, *args, **kwargs):
assert controller
......@@ -121,113 +36,27 @@ class PlotFrame(Frame):
self._initFrame()
def _initFrame(self):
# Section
factory.buildSectionLabel(self,"Visualisation NetworkX").grid(row=0, column=0, sticky="nwe")
G = nx.fast_gnp_random_graph(n=10, p=0.5,directed=True)
# draw
# pyplot
f = Figure(figsize=(5,5), dpi=100)
self._plot=f.add_subplot(111)
self._plot.cla()
self._pos=nx.random_layout(G)
nx.set_node_attributes(G, {n:"blue" for n in G.nodes(data=True)}, name="color")
nx.draw_networkx(G, ax=self._plot, pos=self._pos, edge_color='r')
self._canvas = FigureCanvasTkAgg(f, master=self)
self._canvas.mpl_connect('button_press_event', NodeSelector(self._pos, G))
self._canvas.draw()
# graph
self._canvas=FigureCanvasTkAgg(f, self)
self._canvas.get_tk_widget().grid(row=1, column=0, sticky="nswe")
self._graphDrawer=GraphDrawer(self._plot, self._canvas)
# toolbar
toolbar = NavigationToolbar2Tk(self._canvas, self, pack_toolbar=False)
toolbar.update()
toolbar.grid(row=2, column=0, sticky="nswe")
self._canvas.get_tk_widget().grid(row=1, column=0, sticky="nswe")
def setGraph(self, graph:nx.Graph):
self._plot.cla()
# generate node positions
if callable(getattr(graph,"check_planarity",None)) and graph.check_planarity()[0]:
self._pos=nx.planar_layout(graph)
else:
self._pos=nx.random_layout(graph)
print(self._pos)
# draw
nx.draw_networkx(graph, ax=self._plot, pos=self._pos)
self._canvas.draw()
def setGraph(self, G:nx.Graph):
self._graphDrawer.setGraph(G)
class GraphGenerator:
"""
Static
Construit le nx.Graph associé à un objet par la fonction 'give(object)'
"""
def give(obj) -> nx.Graph:
if issubclass(type(obj), Categorie):
return GraphGenerator._genererGrapheCategorie(obj)
else:
print(f"Impossible de générer le graphe de l'objet: {obj}")
"""
GENERATEURS PAR CLASSE
"""
def _genererGrapheCategorie(cat,
afficher_identites:bool = True,
complet:bool = True,
limite_fleches:int = 30)-> nx.Graph:
"""
@param complet: - si True affiche toutes les flèches;
- si False affiche seulement les flèches élémentaires.
@param limite_fleches: nombre maximal de flèches entre deux objets.
"""
G = nx.MultiDiGraph()
G.add_nodes_from(cat.objets, color=parameters.NODE_COLOR)
#pos = nx.spring_layout(G)
# flesches
fleches_elem = set(cat[cat.objets,cat.objets])
if complet:
func = lambda x,y : cat({x},{y})
else:
func = lambda x,y : cat[{x},{y}]
for source,cible in product(cat.objets,repeat=2):
nb_fleches = 0
for fleche in func(source,cible):
if afficher_identites or not fleche.is_identite:
if fleche.source not in cat.objets:
raise Exception("Source d'une fleche pas dans les objets de la categorie.")
if fleche.cible not in cat.objets:
raise Exception("Source d'une fleche pas dans les objets de la categorie.")
if len({obj for obj in cat.objets if obj == fleche.source}) > 1:
raise Exception("Plus d'un objet de la catégorie est source de la fleche.")
if len({obj for obj in cat.objets if obj == fleche.cible}) > 1:
raise Exception("Plus d'un objet de la catégorie est cible de la fleche.")
#permet d'avoir toujours un objet de la catégorie comme source
source = {obj for obj in cat.objets if obj == fleche.source}.pop()
#permet d'avoir toujours un objet de la catégorie comme cible
cible = {obj for obj in cat.objets if obj == fleche.cible}.pop()
# descriptions
if fleche in fleches_elem:
G.add_edge(str(source),str(cible),
color=parameters.EDGE_ELEMENTARY_COLOR,
label=str(fleche))
else:
G.add_edge(str(source),str(cible),
color=parameters.EDGE_NO_ELEMENTARY_COLOR,
label=str(fleche))
nb_fleches += 1
if nb_fleches > limite_fleches:
if WARNING_LIMITE_FLECHES_ATTEINTE:
print("Warning : limite fleches entre "+str(source)+" et "+str(cible)+" atteinte.")
break
return G
\ No newline at end of file
......@@ -12,7 +12,7 @@ from tk.InformFrame import InformFrame
from tk.Factory import *
from tk.ObjetsFrameCallable import ObjetsFrameCallable
from plot.PlotFrame import GraphGenerator
from plot.GraphGenerator import GraphGenerator
......
......@@ -13,7 +13,7 @@ from tk.InformFrame import InformFrame
from tk.Factory import *
from tk.ObjetsFrameCallable import ObjetsFrameCallable
from plot.PlotFrame import GraphGenerator
from plot.GraphGenerator import GraphGenerator
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment