'''
This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de).
PM4Py is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PM4Py is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PM4Py. If not, see <https://www.gnu.org/licenses/>.
'''
import tempfile
from graphviz import Digraph
from pm4py.objects.petri_net.obj import Marking
from pm4py.util import exec_utils, constants
from pm4py.visualization.petrinet.parameters import Parameters
FORMAT = Parameters.FORMAT
DEBUG = Parameters.DEBUG
RANKDIR = Parameters.RANKDIR
[docs]def apply(net, initial_marking, final_marking, decorations=None, parameters=None):
"""
Apply method for Petri net visualization (it calls the
graphviz_visualization method)
Parameters
-----------
net
Petri net
initial_marking
Initial marking
final_marking
Final marking
decorations
Decorations for elements in the Petri net
parameters
Algorithm parameters
Returns
-----------
viz
Graph object
"""
if parameters is None:
parameters = {}
image_format = exec_utils.get_param_value(Parameters.FORMAT, parameters, "png")
debug = exec_utils.get_param_value(Parameters.DEBUG, parameters, False)
set_rankdir = exec_utils.get_param_value(Parameters.RANKDIR, parameters, None)
font_size = exec_utils.get_param_value(Parameters.FONT_SIZE, parameters, "12")
return graphviz_visualization(net, image_format=image_format, initial_marking=initial_marking,
final_marking=final_marking, decorations=decorations, debug=debug,
set_rankdir=set_rankdir, font_size=font_size)
[docs]def graphviz_visualization(net, image_format="png", initial_marking=None, final_marking=None, decorations=None,
debug=False, set_rankdir=None, font_size="12", bgcolor=constants.DEFAULT_BGCOLOR):
"""
Provides visualization for the petrinet
Parameters
----------
net: :class:`pm4py.entities.petri.petrinet.PetriNet`
Petri net
image_format
Format that should be associated to the image
initial_marking
Initial marking of the Petri net
final_marking
Final marking of the Petri net
decorations
Decorations of the Petri net (says how element must be presented)
debug
Enables debug mode
set_rankdir
Sets the rankdir to LR (horizontal layout)
Returns
-------
viz :
Returns a graph object
"""
if initial_marking is None:
initial_marking = Marking()
if final_marking is None:
final_marking = Marking()
if decorations is None:
decorations = {}
font_size = str(font_size)
filename = tempfile.NamedTemporaryFile(suffix='.gv')
viz = Digraph(net.name, filename=filename.name, engine='dot', graph_attr={'bgcolor': bgcolor})
if set_rankdir:
viz.graph_attr['rankdir'] = set_rankdir
else:
viz.graph_attr['rankdir'] = 'LR'
# transitions
viz.attr('node', shape='box')
for t in net.transitions:
if t.label is not None:
if t in decorations and "label" in decorations[t] and "color" in decorations[t]:
viz.node(str(id(t)), decorations[t]["label"], style='filled', fillcolor=decorations[t]["color"],
border='1', fontsize=font_size)
else:
viz.node(str(id(t)), str(t.label), fontsize=font_size)
else:
if debug:
viz.node(str(id(t)), str(t.name), fontsize=font_size)
elif t in decorations and "color" in decorations[t] and "label" in decorations[t]:
viz.node(str(id(t)), decorations[t]["label"], style='filled', fillcolor=decorations[t]["color"],
fontsize=font_size)
else:
viz.node(str(id(t)), "", style='filled', fillcolor="black", fontsize=font_size)
# places
viz.attr('node', shape='circle', fixedsize='true', width='0.75')
# add places, in order by their (unique) name, to avoid undeterminism in the visualization
places_sort_list_im = sorted([x for x in list(net.places) if x in initial_marking], key=lambda x: x.name)
places_sort_list_fm = sorted([x for x in list(net.places) if x in final_marking and not x in initial_marking],
key=lambda x: x.name)
places_sort_list_not_im_fm = sorted(
[x for x in list(net.places) if x not in initial_marking and x not in final_marking], key=lambda x: x.name)
# making the addition happen in this order:
# - first, the places belonging to the initial marking
# - after, the places not belonging neither to the initial marking and the final marking
# - at last, the places belonging to the final marking (but not to the initial marking)
# in this way, is more probable that the initial marking is on the left and the final on the right
places_sort_list = places_sort_list_im + places_sort_list_not_im_fm + places_sort_list_fm
for p in places_sort_list:
if p in initial_marking:
viz.node(str(id(p)), str(initial_marking[p]), style='filled', fillcolor="green", fontsize=font_size)
elif p in final_marking:
viz.node(str(id(p)), "", style='filled', fillcolor="orange", fontsize=font_size)
else:
if debug:
viz.node(str(id(p)), str(p.name), fontsize=font_size)
else:
if p in decorations and "color" in decorations[p] and "label" in decorations[p]:
viz.node(str(id(p)), decorations[p]["label"], style='filled', fillcolor=decorations[p]["color"],
fontsize=font_size)
else:
viz.node(str(id(p)), "")
# add arcs, in order by their source and target objects names, to avoid undeterminism in the visualization
arcs_sort_list = sorted(list(net.arcs), key=lambda x: (x.source.name, x.target.name))
for a in arcs_sort_list:
if a in decorations and "label" in decorations[a] and "penwidth" in decorations[a]:
viz.edge(str(id(a.source)), str(id(a.target)), label=decorations[a]["label"],
penwidth=decorations[a]["penwidth"], fontsize=font_size)
elif a in decorations and "color" in decorations[a]:
viz.edge(str(id(a.source)), str(id(a.target)), color=decorations[a]["color"], fontsize=font_size)
else:
viz.edge(str(id(a.source)), str(id(a.target)), fontsize=font_size)
viz.attr(overlap='false')
viz.format = image_format
return viz