2from ctypes
import c_double
4LAYOUT_ALGORITHM =
'neato'
5REPRESENT_CHANNELS_AS_NODES = 1
7DEFAULT_TRANSMISSIONS_MEMORY = 5
12PRIORITY_UPDATE_MODEL = -100
13PRIORITY_UPDATE_VIEW = 200
17if platform.system() ==
"Windows":
18 SHELL_FONT =
"Lucida Console 9"
20 SHELL_FONT =
"Luxi Mono 10"
29 import dummy_threading
as threading
34 print(
"Pygraphviz is required by the visualizer module and could not be found")
40 print(
"Pycairo is required by the visualizer module and could not be found")
46 print(
"PyGObject is required by the visualizer module and could not be found")
55 gi.require_version(
'GooCanvas',
'2.0')
56 gi.require_version(
'Gtk',
'3.0')
57 gi.require_version(
'Gdk',
'3.0')
58 gi.require_foreign(
"cairo")
59 from gi.repository
import GObject
60 from gi.repository
import GLib
61 from gi.repository
import Gtk
62 from gi.repository
import Gdk
63 from gi.repository
import Pango
64 from gi.repository
import GooCanvas
66except ImportError
as e:
72 import ipython_viewxxxxxxxxxx
76from .base
import InformationWindow, PyVizObject, Link, lookup_netdevice_traits, PIXELS_PER_METER
77from .base
import transform_distance_simulation_to_canvas, transform_point_simulation_to_canvas
78from .base
import transform_distance_canvas_to_simulation, transform_point_canvas_to_simulation
79from .base
import load_plugins, register_plugin, plugins
125 'query-extra-tooltip-info': (GObject.SignalFlags.RUN_LAST,
None, (object,)),
129 """! Initialize function.
130 @param self The object pointer.
131 @param visualizer visualizer object
132 @param node_index node index
157 def set_svg_icon(self, file_base_name, width=None, height=None, align_x=0.5, align_y=0.5):
159 Set a background SVG icon for the node.
161 @param file_base_name: base file name, including .svg
162 extension, of the svg file. Place the file
in the folder
163 src/contrib/visualizer/resource.
165 @param width: scale to the specified width,
in meters
166 @param height: scale to the specified height,
in meters
168 @param align_x: horizontal alignment of the icon relative to
169 the node position,
from 0 (icon fully to the left of the node)
170 to 1.0 (icon fully to the right of the node)
172 @param align_y: vertical alignment of the icon relative to the
173 node position,
from 0 (icon fully to the top of the node) to
174 1.0 (icon fully to the bottom of the node)
176 @return a ValueError exception
if invalid dimensions.
179 if width
is None and height
is None:
180 raise ValueError(
"either width or height must be given")
181 rsvg_handle = svgitem.rsvg_handle_factory(file_base_name)
186 self.
svg_item.props.pointer_events = GooCanvas.CanvasPointerEvents.NONE
188 self.
svg_item.props.visibility = GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD
189 if width
is not None:
191 if height
is not None:
205 Set a label for the node.
207 @param self:
class object.
208 @param label: label to set
210 @return: an exception
if invalid parameter.
212 assert isinstance(label, basestring)
220 @param self:
class object.
235 @param self:
class object.
236 @param tooltip: tooltip
241 ns3_node = ns.NodeList.GetNode(self.
node_index)
242 ipv4 = ns.cppyy.gbl.getNodeIpv4(ns3_node)
243 ipv6 = ns.cppyy.gbl.getNodeIpv6(ns3_node)
245 name =
'<b><u>Node %i</u></b>' % self.
node_index
246 node_name = ns.Names.FindName (ns3_node)
247 if len(node_name)!=0:
248 name +=
' <b>(' + node_name +
')</b>'
253 self.emit(
"query-extra-tooltip-info", lines)
255 mob = ns.cppyy.gbl.hasMobilityModel(ns3_node)
257 mobility_model_name = ns.cppyy.gbl.getMobilityModelName(ns3_node)
258 lines.append(
' <b>Mobility Model</b>: %s' % ns.cppyy.gbl.getMobilityModelName(ns3_node))
260 for devI
in range(ns3_node.GetNDevices()):
262 lines.append(
' <u>NetDevice %i:</u>' % devI)
263 dev = ns3_node.GetDevice(devI)
264 name = ns.Names.FindName(dev)
266 lines.append(
' <b>Name:</b> %s' % name)
267 devname = dev.GetInstanceTypeId().GetName()
268 lines.append(
' <b>Type:</b> %s' % devname)
271 ipv4_idx = ipv4.GetInterfaceForDevice(dev)
274 '%s/%s' % (ipv4.GetAddress(ipv4_idx, i).GetLocal(),
275 ipv4.GetAddress(ipv4_idx, i).GetMask())
276 for i
in range(ipv4.GetNAddresses(ipv4_idx))]
277 lines.append(
' <b>IPv4 Addresses:</b> %s' %
'; '.join(addresses))
280 ipv6_idx = ipv6.GetInterfaceForDevice(dev)
283 '%s/%s' % (ipv6.GetAddress(ipv6_idx, i).GetAddress(),
284 ipv6.GetAddress(ipv6_idx, i).GetPrefix())
285 for i
in range(ipv6.GetNAddresses(ipv6_idx))]
286 lines.append(
' <b>IPv6 Addresses:</b> %s' %
'; '.join(addresses))
288 lines.append(
' <b>MAC Address:</b> %s' % (dev.GetAddress(),))
290 tooltip.set_markup(
'\n'.join(lines))
296 On Enter event handle.
298 @param self:
class object.
300 @param target: target
307 On Leave event handle.
309 @param self:
class object.
311 @param target: target
319 Set selected function.
321 @param self:
class object.
322 @param value: selected value
329 Get selected function.
331 @param self:
class object.
332 @return selected status
336 selected = property(_get_selected, _set_selected)
340 Set highlighted function.
342 @param self:
class object.
343 @param value: selected value
350 Get highlighted function.
352 @param self:
class object.
353 @return highlighted status
357 highlighted = property(_get_highlighted, _set_highlighted)
363 @param self:
class object.
364 @param size: selected size
372 Update the node aspect to reflect the selected/highlighted state
374 @param self:
class object.
383 fill_color_rgba = (self.
_color & 0xffffff00) | alpha
384 self.
canvas_item.set_properties(radius_x=size, radius_y=size,
385 fill_color_rgba=fill_color_rgba)
389 line_width = size*.15
391 stroke_color =
'yellow'
393 stroke_color =
'black'
394 self.
canvas_item.set_properties(line_width=line_width, stroke_color=stroke_color)
396 if self.
_label is not None:
399 font=
"Sans Serif 10",
400 fill_color_rgba=0x808080ff,
401 alignment=Pango.Alignment.CENTER,
402 anchor=GooCanvas.CanvasAnchorType.N,
403 parent=self.
visualizer.canvas.get_root_item(),
404 pointer_events=GooCanvas.CanvasPointerEvents.NONE)
407 self.
_label_canvas_item.set_properties(visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
413 Set position function.
415 @param self:
class object.
425 for link
in self.
links:
436 (min_x, min_y, max_x, max_y) = bounds
438 min_x =
min(x, min_x)
439 min_y =
min(y, min_y)
440 max_x =
max(x, max_x)
441 max_y =
max(y, max_y)
443 new_bounds = (min_x, min_y, max_x, max_y)
445 if new_bounds != bounds:
446 self.
visualizer.canvas.set_bounds(*new_bounds)
453 Get position function.
455 @param self:
class object.
456 @return x
and y position
462 Update position function.
464 @param self:
class object.
474 @param self:
class object.
475 @param color: color to set.
478 if isinstance(color, str):
479 color = Gdk.color_parse(color)
480 color = ((color.red>>8) << 24) | ((color.green>>8) << 16) | ((color.blue>>8) << 8) | 0xff
488 @param self:
class object.
489 @param link: link to add.
492 assert isinstance(link, Link)
493 self.
links.append(link)
497 Remove link function.
499 @param self:
class object.
500 @param link: link to add.
503 assert isinstance(link, Link)
504 self.
links.remove(link)
509 Has mobility function.
511 @param self:
class object.
512 @return modility option
531 Initializer function.
533 @param self:
class object.
534 @param channel: channel.
537 self.canvas_item = GooCanvas.CanvasEllipse(radius_x=30, radius_y=30,
539 stroke_color=
"grey", line_width=2.0,
540 line_dash=GooCanvas.CanvasLineDash.newv([10.0, 10.0 ]),
541 visibility=GooCanvas.CanvasItemVisibility.VISIBLE)
547 Initializer function.
549 @param self:
class object.
550 @param x: x position.
551 @param y: y position.
557 for link
in self.
links:
562 Initializer function.
564 @param self:
class object.
565 @return x / y position.
581 Initializer function.
583 @param self:
class object.
584 @param node1:
class object.
585 @param node2:
class object.
587 assert isinstance(node1, Node)
588 assert isinstance(node2, (Node, Channel))
591 self.
canvas_item = GooCanvas.CanvasPath(line_width=1.0, stroke_color=
"black")
593 self.
node1.links.append(self)
594 self.
node2.links.append(self)
598 Update points function.
600 @param self:
class object.
603 pos1_x, pos1_y = self.node1.get_position()
604 pos2_x, pos2_y = self.node2.get_position()
605 self.canvas_item.set_property("data",
"M %r %r L %r %r" % (pos1_x, pos1_y, pos2_x, pos2_y))
626 Initializer function.
628 @param self:
class object.
631 super(SimulationThread, self).__init__()
632 assert isinstance(viz, Visualizer)
635 self.
go = threading.Event()
644 Set nodes of interest function.
646 @param self:
class object.
647 @param nodes:
class object.
658 Initializer function.
660 @param self:
class object.
675 self.
viz.play_button.set_sensitive(
False)
683 GLib.idle_add(self.
viz.update_model, priority=PRIORITY_UPDATE_MODEL)
710 if _import_error
is None:
714 'populate-node-menu': (GObject.SignalFlags.RUN_LAST,
None, (object, Gtk.Menu,)),
718 'simulation-periodic-update': (GObject.SignalFlags.RUN_LAST,
None, ()),
721 'topology-scanned': (GObject.SignalFlags.RUN_LAST,
None, ()),
724 'update-view': (GObject.SignalFlags.RUN_LAST,
None, ()),
730 Initializer function.
732 @param self:
class object.
735 assert Visualizer.INSTANCE
is None
736 Visualizer.INSTANCE = self
737 super(Visualizer, self).__init__()
742 self.time_label =
None
743 self.play_button =
None
745 self._scrolled_window =
None
747 self.links_group = GooCanvas.CanvasGroup()
748 self.channels_group = GooCanvas.CanvasGroup()
749 self.nodes_group = GooCanvas.CanvasGroup()
751 self._update_timeout_id =
None
753 self.selected_node =
None
755 self.information_windows = []
756 self._transmission_arrows = []
757 self._last_transmissions = []
758 self._drop_arrows = []
759 self._last_drops = []
760 self._show_transmissions_mode =
None
761 self.set_show_transmissions_mode(ShowTransmissionsMode.ALL)
762 self._panning_state =
None
763 self.node_size_adjustment =
None
764 self.transmissions_smoothing_adjustment =
None
765 self.sample_period = SAMPLE_PERIOD
766 self.node_drag_state =
None
767 self.follow_node =
None
768 self.shell_window =
None
772 for plugin
in plugins:
775 def set_show_transmissions_mode(self, mode):
777 Set show transmission mode.
779 @param self:
class object.
780 @param mode: mode to set.
783 assert isinstance(mode, ShowTransmissionsMode)
784 self._show_transmissions_mode = mode
785 if self._show_transmissions_mode == ShowTransmissionsMode.ALL:
786 self.simulation.set_nodes_of_interest(
list(range(ns.NodeList.GetNNodes())))
787 elif self._show_transmissions_mode == ShowTransmissionsMode.NONE:
788 self.simulation.set_nodes_of_interest([])
789 elif self._show_transmissions_mode == ShowTransmissionsMode.SELECTED:
790 if self.selected_node
is None:
791 self.simulation.set_nodes_of_interest([])
793 self.simulation.set_nodes_of_interest([self.selected_node.node_index])
795 def _create_advanced_controls(self):
797 Create advanced controls.
799 @param self:
class object.
802 expander = Gtk.Expander.new("Advanced")
805 main_vbox = GObject.new(Gtk.VBox, border_width=8, visible=
True)
806 expander.add(main_vbox)
808 main_hbox1 = GObject.new(Gtk.HBox, border_width=8, visible=
True)
809 main_vbox.pack_start(main_hbox1,
True,
True, 0)
811 show_transmissions_group = GObject.new(Gtk.HeaderBar,
812 title=
"Show transmissions",
814 main_hbox1.pack_start(show_transmissions_group,
False,
False, 8)
816 vbox = Gtk.VBox(homogeneous=
True, spacing=4)
818 show_transmissions_group.add(vbox)
820 all_nodes = Gtk.RadioButton.new(
None)
821 all_nodes.set_label(
"All nodes")
822 all_nodes.set_active(
True)
826 selected_node = Gtk.RadioButton.new_from_widget(all_nodes)
828 selected_node.set_label(
"Selected node")
829 selected_node.set_active(
False)
830 vbox.add(selected_node)
832 no_node = Gtk.RadioButton.new_from_widget(all_nodes)
834 no_node.set_label(
"Disabled")
835 no_node.set_active(
False)
839 if radio.get_active():
840 self.set_show_transmissions_mode(ShowTransmissionsMode.ALL)
841 all_nodes.connect(
"toggled", toggled)
844 if radio.get_active():
845 self.set_show_transmissions_mode(ShowTransmissionsMode.NONE)
846 no_node.connect(
"toggled", toggled)
849 if radio.get_active():
850 self.set_show_transmissions_mode(ShowTransmissionsMode.SELECTED)
851 selected_node.connect(
"toggled", toggled)
854 misc_settings_group = GObject.new(Gtk.HeaderBar, title=
"Misc Settings", visible=
True)
855 main_hbox1.pack_start(misc_settings_group,
False,
False, 8)
856 settings_hbox = GObject.new(Gtk.HBox, border_width=8, visible=
True)
857 misc_settings_group.add(settings_hbox)
860 vbox = GObject.new(Gtk.VBox, border_width=0, visible=
True)
861 scale = GObject.new(Gtk.HScale, visible=
True, digits=2)
862 vbox.pack_start(scale,
True,
True, 0)
863 vbox.pack_start(GObject.new(Gtk.Label, label=
"Node Size", visible=
True),
True,
True, 0)
864 settings_hbox.pack_start(vbox,
False,
False, 6)
865 self.node_size_adjustment = scale.get_adjustment()
866 def node_size_changed(adj):
867 for node
in self.nodes.values():
868 node.set_size(adj.get_value())
869 self.node_size_adjustment.connect(
"value-changed", node_size_changed)
870 self.node_size_adjustment.set_lower(0.01)
871 self.node_size_adjustment.set_upper(20)
872 self.node_size_adjustment.set_step_increment(0.1)
873 self.node_size_adjustment.set_value(DEFAULT_NODE_SIZE)
876 vbox = GObject.new(Gtk.VBox, border_width=0, visible=
True)
877 scale = GObject.new(Gtk.HScale, visible=
True, digits=1)
878 vbox.pack_start(scale,
True,
True, 0)
879 vbox.pack_start(GObject.new(Gtk.Label, label=
"Tx. Smooth Factor (s)", visible=
True),
True,
True, 0)
880 settings_hbox.pack_start(vbox,
False,
False, 6)
881 self.transmissions_smoothing_adjustment = scale.get_adjustment()
882 adj = self.transmissions_smoothing_adjustment
885 adj.set_step_increment(0.1)
886 adj.set_value(DEFAULT_TRANSMISSIONS_MEMORY*0.1)
891 class _PanningState(
object):
894 __slots__ = [
'initial_mouse_pos',
'initial_canvas_pos',
'motion_signal']
896 def _begin_panning(self, widget, event):
898 Set show trnamission mode.
900 @param self:
class object.
901 @param mode: mode to set.
904 display = self.canvas.get_window().get_display()
905 cursor = Gdk.Cursor.new_for_display(display, Gdk.CursorType.FLEUR)
906 self.canvas.get_window().set_cursor(cursor)
907 self._panning_state = self._PanningState()
908 pos = widget.get_window().get_device_position(event.device)
909 self._panning_state.initial_mouse_pos = (pos.x, pos.y)
910 x = self._scrolled_window.get_hadjustment().get_value()
911 y = self._scrolled_window.get_vadjustment().get_value()
912 self._panning_state.initial_canvas_pos = (x, y)
913 self._panning_state.motion_signal = self.canvas.connect("motion-notify-event", self._panning_motion)
915 def _end_panning(self, event):
917 End panning function.
919 @param self:
class object.
920 @param event: active event.
923 if self._panning_state
is None:
925 self.canvas.get_window().set_cursor(
None)
926 self.canvas.disconnect(self._panning_state.motion_signal)
927 self._panning_state =
None
929 def _panning_motion(self, widget, event):
931 Panning motion function.
933 @param self:
class object.
934 @param widget: widget.
936 @return true
if successful
938 assert self._panning_state
is not None
940 pos = widget.get_window().get_device_position(event.device)
943 x, y = event.x, event.y
945 hadj = self._scrolled_window.get_hadjustment()
946 vadj = self._scrolled_window.get_vadjustment()
947 mx0, my0 = self._panning_state.initial_mouse_pos
948 cx0, cy0 = self._panning_state.initial_canvas_pos
952 hadj.set_value(cx0 - dx)
953 vadj.set_value(cy0 - dy)
956 def _canvas_button_press(self, widget, event):
957 if event.button == 2:
958 self._begin_panning(widget, event)
962 def _canvas_button_release(self, dummy_widget, event):
963 if event.button == 2:
964 self._end_panning(event)
968 def _canvas_scroll_event(self, dummy_widget, event):
969 if event.direction == Gdk.ScrollDirection.UP:
970 self.zoom.set_value(self.zoom.get_value() * 1.25)
972 elif event.direction == Gdk.ScrollDirection.DOWN:
973 self.zoom.set_value(self.zoom.get_value() / 1.25)
977 def get_hadjustment(self):
978 return self._scrolled_window.get_hadjustment()
979 def get_vadjustment(self):
980 return self._scrolled_window.get_vadjustment()
982 def create_gui(self):
983 self.window = Gtk.Window()
986 self.window.add(vbox)
989 self.canvas = GooCanvas.Canvas()
990 self.canvas.connect_after(
"button-press-event", self._canvas_button_press)
991 self.canvas.connect_after(
"button-release-event", self._canvas_button_release)
992 self.canvas.connect(
"scroll-event", self._canvas_scroll_event)
993 self.canvas.props.has_tooltip =
True
994 self.canvas.connect(
"query-tooltip", self._canvas_tooltip_cb)
996 sw = Gtk.ScrolledWindow(); sw.show()
997 self._scrolled_window = sw
999 vbox.pack_start(sw,
True,
True, 4)
1000 self.canvas.set_size_request(600, 450)
1001 self.canvas.
set_bounds(-10000, -10000, 10000, 10000)
1002 self.canvas.scroll_to(0, 0)
1005 self.canvas.get_root_item().add_child(self.links_group, -1)
1006 self.links_group.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1008 self.canvas.get_root_item().add_child(self.channels_group, -1)
1009 self.channels_group.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1010 self.channels_group.raise_(self.links_group)
1012 self.canvas.get_root_item().add_child(self.nodes_group, -1)
1013 self.nodes_group.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1014 self.nodes_group.raise_(self.channels_group)
1018 hbox = Gtk.HBox(); hbox.show()
1019 vbox.pack_start(hbox,
False,
False, 4)
1022 zoom_adj = Gtk.Adjustment(value=1.0, lower=0.01, upper=10.0,
1023 step_increment=0.02,
1026 self.zoom = zoom_adj
1027 def _zoom_changed(adj):
1028 self.canvas.set_scale(adj.get_value())
1029 zoom_adj.connect(
"value-changed", _zoom_changed)
1030 zoom = Gtk.SpinButton.new(zoom_adj, 0.1, 1)
1033 hbox.pack_start(GObject.new(Gtk.Label, label=
" Zoom:", visible=
True),
False,
False, 4)
1034 hbox.pack_start(zoom,
False,
False, 4)
1035 _zoom_changed(zoom_adj)
1038 speed_adj = Gtk.Adjustment(value=1.0, lower=0.01, upper=10.0,
1039 step_increment=0.02,
1040 page_increment=1.0, page_size=0)
1041 def _speed_changed(adj):
1042 self.speed = adj.get_value()
1043 self.sample_period = SAMPLE_PERIOD*adj.get_value()
1044 self._start_update_timer()
1045 speed_adj.connect(
"value-changed", _speed_changed)
1046 speed = Gtk.SpinButton.new(speed_adj, 1, 0)
1049 hbox.pack_start(GObject.new(Gtk.Label, label=
" Speed:", visible=
True),
False,
False, 4)
1050 hbox.pack_start(speed,
False,
False, 4)
1051 _speed_changed(speed_adj)
1054 self.time_label = GObject.new(Gtk.Label, label=
" Speed:", visible=
True)
1055 self.time_label.set_width_chars(20)
1056 hbox.pack_start(self.time_label,
False,
False, 4)
1059 screenshot_button = GObject.new(Gtk.Button,
1061 relief=Gtk.ReliefStyle.NONE, focus_on_click=
False,
1063 hbox.pack_start(screenshot_button,
False,
False, 4)
1065 def load_button_icon(button, icon_name):
1066 if not Gtk.IconTheme.get_default().has_icon(icon_name):
1067 print(f
"Could not load icon {icon_name}", file=sys.stderr)
1069 image = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.BUTTON)
1070 button.set_image(image)
1071 button.props.always_show_image =
True
1073 load_button_icon(screenshot_button,
"applets-screenshooter")
1074 screenshot_button.connect(
"clicked", self._take_screenshot)
1077 if ipython_view
is not None:
1078 shell_button = GObject.new(Gtk.Button,
1080 relief=Gtk.ReliefStyle.NONE, focus_on_click=
False,
1082 hbox.pack_start(shell_button,
False,
False, 4)
1083 load_button_icon(shell_button,
"gnome-terminal")
1084 shell_button.connect(
"clicked", self._start_shell)
1087 self.play_button = GObject.new(Gtk.ToggleButton,
1088 label=
"Simulate (F3)",
1089 relief=Gtk.ReliefStyle.NONE, focus_on_click=
False,
1091 load_button_icon(self.play_button,
"media-playback-start")
1092 accel_group = Gtk.AccelGroup()
1093 self.window.add_accel_group(accel_group)
1094 self.play_button.add_accelerator(
"clicked", accel_group,
1095 Gdk.KEY_F3, 0, Gtk.AccelFlags.VISIBLE)
1096 self.play_button.connect(
"toggled", self._on_play_button_toggled)
1097 hbox.pack_start(self.play_button,
False,
False, 4)
1099 self.canvas.get_root_item().connect(
"button-press-event", self.on_root_button_press_event)
1101 vbox.pack_start(self._create_advanced_controls(),
False,
False, 4)
1103 display = Gdk.Display.get_default()
1105 monitor = display.get_primary_monitor()
1106 geometry = monitor.get_geometry()
1107 scale_factor = monitor.get_scale_factor()
1108 except AttributeError:
1109 screen = display.get_default_screen()
1110 monitor_id = screen.get_primary_monitor()
1111 geometry = screen.get_monitor_geometry(monitor_id)
1112 scale_factor = screen.get_monitor_scale_factor(monitor_id)
1113 width = scale_factor * geometry.width
1114 height = scale_factor * geometry.height
1115 self.window.set_default_size(width * 2 / 3, height * 2 / 3)
1118 def scan_topology(self):
1119 print(
"scanning topology: %i nodes..." % (ns.NodeList.GetNNodes(),))
1120 graph = pygraphviz.AGraph()
1122 for nodeI
in range(ns.NodeList.GetNNodes()):
1124 if seen_nodes == 100:
1125 print(
"scan topology... %i nodes visited (%.1f%%)" % (nodeI, 100*nodeI/ns.NodeList.GetNNodes()))
1127 node = ns.NodeList.GetNode(nodeI)
1128 node_name =
"Node %i" % nodeI
1129 node_view = self.get_node(nodeI)
1131 mobility = ns.cppyy.gbl.hasMobilityModel(node)
1133 node_view.set_color(
"red")
1134 pos = ns.cppyy.gbl.getNodePosition(node)
1138 graph.add_node(node_name)
1140 for devI
in range(node.GetNDevices()):
1141 device = node.GetDevice(devI)
1143 if device_traits.is_wireless:
1145 if device_traits.is_virtual:
1147 channel = device.GetChannel()
1148 if channel.GetNDevices() > 2:
1149 if REPRESENT_CHANNELS_AS_NODES:
1151 if mobility
is None:
1152 channel_name =
"Channel %s" % id(channel)
1153 graph.add_edge(node_name, channel_name)
1154 self.get_channel(channel)
1155 self.create_link(self.get_node(nodeI), self.get_channel(channel))
1158 for otherDevI
in range(channel.GetNDevices()):
1159 otherDev = channel.GetDevice(otherDevI)
1160 otherNode = otherDev.GetNode()
1161 otherNodeView = self.get_node(otherNode.GetId())
1162 if otherNode
is not node:
1163 if mobility
is None and not otherNodeView.has_mobility:
1164 other_node_name =
"Node %i" % otherNode.GetId()
1165 graph.add_edge(node_name, other_node_name)
1166 self.create_link(self.get_node(nodeI), otherNodeView)
1168 for otherDevI
in range(channel.GetNDevices()):
1169 otherDev = channel.GetDevice(otherDevI)
1170 otherNode = otherDev.GetNode()
1171 otherNodeView = self.get_node(otherNode.GetId())
1172 if otherNode
is not node:
1173 if mobility
is None and not otherNodeView.has_mobility:
1174 other_node_name =
"Node %i" % otherNode.GetId()
1175 graph.add_edge(node_name, other_node_name)
1176 self.create_link(self.get_node(nodeI), otherNodeView)
1178 print(
"scanning topology: calling graphviz layout")
1179 graph.layout(LAYOUT_ALGORITHM)
1180 for node
in graph.iternodes():
1182 node_type, node_id = node.split(
' ')
1183 pos_x, pos_y = [float(s)
for s
in node.attr[
'pos'].split(
',')]
1184 if node_type ==
'Node':
1185 obj = self.nodes[
int(node_id)]
1186 elif node_type ==
'Channel':
1187 obj = self.channels[
int(node_id)]
1188 obj.set_position(pos_x, pos_y)
1190 print(
"scanning topology: all done.")
1191 self.emit(
"topology-scanned")
1193 def get_node(self, index):
1195 return self.nodes[index]
1197 node =
Node(self, index)
1198 self.nodes[index] = node
1199 self.nodes_group.add_child(node.canvas_item, -1)
1200 node.canvas_item.connect(
"button-press-event", self.on_node_button_press_event, node)
1201 node.canvas_item.connect(
"button-release-event", self.on_node_button_release_event, node)
1204 def get_channel(self, ns3_channel):
1206 return self.channels[id(ns3_channel)]
1208 channel =
Channel(ns3_channel)
1209 self.channels[id(ns3_channel)] = channel
1210 self.channels_group.add_child(channel.canvas_item, -1)
1213 def create_link(self, node, node_or_channel):
1215 self.links_group.add_child(link.canvas_item, -1)
1216 link.canvas_item.lower(
None)
1218 def update_view(self):
1221 self.time_label.set_text(
"Time: %f s" % ns.Simulator.Now().GetSeconds())
1223 self._update_node_positions()
1226 for info_win
in self.information_windows:
1229 self._update_transmissions_view()
1230 self._update_drops_view()
1232 self.emit(
"update-view")
1234 def _update_node_positions(self):
1235 for node
in self.nodes.values():
1236 if node.has_mobility:
1237 ns3_node = ns.NodeList.GetNode(node.node_index)
1238 mobility = ns.cppyy.gbl.hasMobilityModel(ns3_node)
1240 pos = ns.cppyy.gbl.getNodePosition(ns3_node)
1242 node.set_position(x, y)
1243 if node
is self.follow_node:
1244 hadj = self._scrolled_window.get_hadjustment()
1245 vadj = self._scrolled_window.get_vadjustment()
1246 px, py = self.canvas.convert_to_pixels(x, y)
1247 hadj.set_value(px - hadj.get_page_size() / 2)
1248 vadj.set_value(py - vadj.get_page_size() / 2)
1250 def center_on_node(self, node):
1251 if isinstance(node, ns.Node):
1252 node = self.nodes[node.GetId()]
1253 elif isinstance(node, int):
1254 node = self.nodes[node]
1255 elif isinstance(node, Node):
1258 raise TypeError(
"expected int, viz.Node or ns.Node, not %r" % node)
1260 x, y = node.get_position()
1261 hadj = self._scrolled_window.get_hadjustment()
1262 vadj = self._scrolled_window.get_vadjustment()
1263 px, py = self.canvas.convert_to_pixels(x, y)
1264 hadj.set_value(px - hadj.get_page_size() / 2)
1265 vadj.set_value(py - vadj.get_page_size() / 2)
1267 def update_model(self):
1268 self.simulation.lock.acquire()
1270 self.emit(
"simulation-periodic-update")
1272 self.simulation.lock.release()
1274 def do_simulation_periodic_update(self):
1275 smooth_factor =
int(self.transmissions_smoothing_adjustment.get_value()*10)
1277 transmissions = self.simulation.sim_helper.GetTransmissionSamples()
1278 self._last_transmissions.append(transmissions)
1279 while len(self._last_transmissions) > smooth_factor:
1280 self._last_transmissions.pop(0)
1282 drops = self.simulation.sim_helper.GetPacketDropSamples()
1283 self._last_drops.append(drops)
1284 while len(self._last_drops) > smooth_factor:
1285 self._last_drops.pop(0)
1287 def _get_label_over_line_position(self, pos1_x, pos1_y, pos2_x, pos2_y):
1288 hadj = self._scrolled_window.get_hadjustment()
1289 vadj = self._scrolled_window.get_vadjustment()
1290 bounds_x1, bounds_y1 = self.canvas.convert_from_pixels(hadj.get_value(), vadj.get_value())
1291 bounds_x2, bounds_y2 = self.canvas.convert_from_pixels(hadj.get_value() + hadj.get_page_size(),
1292 vadj.get_value() + vadj.get_page_size())
1294 pos1_x, pos1_y, pos2_x, pos2_y = ns.PyViz.LineClipping(bounds_x1, bounds_y1,
1295 bounds_x2, bounds_y2,
1300 pos1_x, pos1_y, pos2_x, pos2_y = res
1301 return (pos1_x + pos2_x)/2, (pos1_y + pos2_y)/2
1303 def _update_transmissions_view(self):
1304 transmissions_average = {}
1305 for transmission_set
in self._last_transmissions:
1306 for transmission
in transmission_set:
1307 key = (transmission.transmitter.GetId(), transmission.receiver.GetId())
1308 rx_bytes, count = transmissions_average.get(key, (0, 0))
1309 rx_bytes += transmission.bytes
1311 transmissions_average[key] = rx_bytes, count
1313 old_arrows = self._transmission_arrows
1314 for arrow, label
in old_arrows:
1315 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1316 label.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1319 k = self.node_size_adjustment.get_value()/5
1321 for (transmitter_id, receiver_id), (rx_bytes, rx_count)
in transmissions_average.items():
1322 transmitter = self.get_node(transmitter_id)
1323 receiver = self.get_node(receiver_id)
1325 arrow, label = old_arrows.pop()
1327 arrow = GooCanvas.CanvasPolyline(line_width=2.0, stroke_color_rgba=0x00C000C0, close_path=
False, end_arrow=
True, pointer_events=GooCanvas.CanvasPointerEvents.NONE)
1328 arrow.set_property(
"parent", self.canvas.get_root_item())
1331 label = GooCanvas.CanvasText(parent=self.canvas.get_root_item(), pointer_events=GooCanvas.CanvasPointerEvents.NONE)
1334 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1335 line_width =
max(0.1, math.log(float(rx_bytes)/rx_count/self.sample_period)*k)
1336 arrow.set_property(
"line-width", line_width)
1338 pos1_x, pos1_y = transmitter.get_position()
1339 pos2_x, pos2_y = receiver.get_position()
1340 points = GooCanvas.CanvasPoints.new(2)
1341 points.set_point(0, pos1_x, pos1_y)
1342 points.set_point(1, pos2_x, pos2_y)
1343 arrow.set_property(
"points", points)
1345 kbps = float(rx_bytes*8)/1e3/rx_count/self.sample_period
1346 label.set_properties(visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
1347 visibility_threshold=0.5,
1348 font=(
"Sans Serif %f" %
int(1+BITRATE_FONT_SIZE*k)))
1349 angle = math.atan2((pos2_y - pos1_y), (pos2_x - pos1_x))
1350 if -PI_OVER_2 <= angle <= PI_OVER_2:
1351 label.set_properties(text=(
"%.2f kbit/s →" % (kbps,)),
1352 alignment=Pango.Alignment.CENTER,
1353 anchor=GooCanvas.CanvasAnchorType.S,
1354 x=0, y=-line_width/2)
1356 label.set_properties(text=(
"← %.2f kbit/s" % (kbps,)),
1357 alignment=Pango.Alignment.CENTER,
1358 anchor=GooCanvas.CanvasAnchorType.N,
1359 x=0, y=line_width/2)
1361 lx, ly = self._get_label_over_line_position(c_double(pos1_x), c_double(pos1_y),
1362 c_double(pos2_x), c_double(pos2_y))
1366 label.set_transform(M)
1369 warnings.warn(
"PyGobject bug causing label position error; "
1370 "should be fixed in PyGObject >= 3.29.1")
1371 label.set_properties(x=(lx + label.props.x),
1372 y=(ly + label.props.y))
1374 new_arrows.append((arrow, label))
1376 self._transmission_arrows = new_arrows + old_arrows
1379 def _update_drops_view(self):
1381 for drop_set
in self._last_drops:
1382 for drop
in drop_set:
1383 key = drop.transmitter.GetId()
1384 drop_bytes, count = drops_average.get(key, (0, 0))
1385 drop_bytes += drop.bytes
1387 drops_average[key] = drop_bytes, count
1389 old_arrows = self._drop_arrows
1390 for arrow, label
in old_arrows:
1391 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1392 label.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1396 vadjustment = self._scrolled_window.get_vadjustment()
1397 bottom_y = vadjustment.get_value() + vadjustment.get_page_size()
1398 dummy, edge_y = self.canvas.convert_from_pixels(0, bottom_y)
1400 k = self.node_size_adjustment.get_value()/5
1402 for transmitter_id, (drop_bytes, drop_count)
in drops_average.items():
1403 transmitter = self.get_node(transmitter_id)
1405 arrow, label = old_arrows.pop()
1407 arrow = GooCanvas.CanvasPolyline(line_width=2.0, stroke_color_rgba=0xC00000C0, close_path=
False, end_arrow=
True, pointer_events=GooCanvas.CanvasPointerEvents.NONE)
1408 arrow.set_property(
"parent", self.canvas.get_root_item())
1411 label = GooCanvas.CanvasText(pointer_events=GooCanvas.CanvasPointerEvents.NONE)
1412 label.set_property(
"parent", self.canvas.get_root_item())
1415 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1416 arrow.set_property(
"line-width",
max(0.1, math.log(float(drop_bytes)/drop_count/self.sample_period)*k))
1417 pos1_x, pos1_y = transmitter.get_position()
1418 pos2_x, pos2_y = pos1_x, edge_y
1419 points = GooCanvas.CanvasPoints.new(2)
1420 points.set_point(0, pos1_x, pos1_y)
1421 points.set_point(1, pos2_x, pos2_y)
1422 arrow.set_property(
"points", points)
1424 label.set_properties(visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
1425 visibility_threshold=0.5,
1426 font=(
"Sans Serif %i" %
int(1+BITRATE_FONT_SIZE*k)),
1427 text=(
"%.2f kbit/s" % (float(drop_bytes*8)/1e3/drop_count/self.sample_period,)),
1428 alignment=Pango.Alignment.CENTER,
1429 x=(pos1_x + pos2_x)/2,
1430 y=(pos1_y + pos2_y)/2)
1432 new_arrows.append((arrow, label))
1434 self._drop_arrows = new_arrows + old_arrows
1437 def update_view_timeout(self):
1441 while not self.simulation.lock.acquire(
False):
1442 while Gtk.events_pending():
1443 Gtk.main_iteration()
1444 pause_messages = self.simulation.pause_messages
1445 self.simulation.pause_messages = []
1448 self.simulation.target_time = ns.Simulator.Now ().GetSeconds () + self.sample_period
1451 self.simulation.lock.release()
1455 dialog = Gtk.MessageDialog(parent=self.window, flags=0, type=Gtk.MessageType.WARNING, buttons=Gtk.ButtonsType.OK,
1456 message_format=
'\n'.join(pause_messages))
1457 dialog.connect(
"response",
lambda d, r: d.destroy())
1459 self.play_button.set_active(
False)
1462 if not self.play_button.get_active():
1463 self._update_timeout_id =
None
1467 self.simulation.go.set()
1471 def _start_update_timer(self):
1472 if self._update_timeout_id
is not None:
1473 GLib.source_remove(self._update_timeout_id)
1475 self._update_timeout_id = GLib.timeout_add(
int(SAMPLE_PERIOD/
min(self.speed, 1)*1e3),
1476 self.update_view_timeout,
1477 priority=PRIORITY_UPDATE_VIEW)
1479 def _on_play_button_toggled(self, button):
1480 if button.get_active():
1481 self._start_update_timer()
1483 if self._update_timeout_id
is not None:
1484 GLib.source_remove(self._update_timeout_id)
1486 def _quit(self, *dummy_args):
1487 if self._update_timeout_id
is not None:
1488 GLib.source_remove(self._update_timeout_id)
1489 self._update_timeout_id =
None
1490 self.simulation.quit =
True
1491 self.simulation.go.set()
1492 self.simulation.join()
1495 def _monkey_patch_ipython(self):
1502 original_runcode = self.ipython.runcode
1503 def runcode(ip, *args):
1505 self.simulation.lock.acquire()
1507 return original_runcode(*args)
1510 self.simulation.lock.release()
1512 self.ipython.runcode = types.MethodType(runcode, self.ipython)
1514 def autoscale_view(self):
1517 self._update_node_positions()
1518 positions = [node.get_position()
for node
in self.nodes.values()]
1519 min_x, min_y =
min(x
for (x,y)
in positions),
min(y
for (x,y)
in positions)
1520 max_x, max_y =
max(x
for (x,y)
in positions),
max(y
for (x,y)
in positions)
1521 min_x_px, min_y_px = self.canvas.convert_to_pixels(min_x, min_y)
1522 max_x_px, max_y_px = self.canvas.convert_to_pixels(max_x, max_y)
1525 dx_px = max_x_px - min_x_px
1526 dy_px = max_y_px - min_y_px
1527 hadj = self._scrolled_window.get_hadjustment()
1528 vadj = self._scrolled_window.get_vadjustment()
1529 new_dx, new_dy = 1.5*dx_px, 1.5*dy_px
1531 if new_dx == 0
or new_dy == 0:
1534 self.zoom.set_value(
min(hadj.get_page_size()/new_dx, vadj.get_page_size()/new_dy))
1536 x1, y1 = self.canvas.convert_from_pixels(hadj.get_value(), vadj.get_value())
1537 x2, y2 = self.canvas.convert_from_pixels((hadj.get_value() +
1538 hadj.get_page_size()),
1540 vadj.get_page_size()))
1543 center_x = (min_x + max_x) / 2
1544 center_y = (min_y + max_y) / 2
1546 self.canvas.scroll_to(center_x - width/2, center_y - height/2)
1551 self.scan_topology()
1552 self.window.connect(
"delete-event", self._quit)
1554 GLib.timeout_add(200, self.autoscale_view)
1555 self.simulation.
start()
1562 self._monkey_patch_ipython()
1567 def on_root_button_press_event(self, view, target, event):
1568 if event.button == 1:
1569 self.select_node(
None)
1572 def on_node_button_press_event(self, view, target, event, node):
1573 button = event.button
1575 self.select_node(node)
1578 self.popup_node_menu(node, event)
1581 self.begin_node_drag(node, event)
1585 def on_node_button_release_event(self, view, target, event, node):
1586 if event.button == 2:
1587 self.end_node_drag(node)
1591 class NodeDragState(
object):
1592 def __init__(self, canvas_x0, canvas_y0, sim_x0, sim_y0):
1593 self.canvas_x0 = canvas_x0
1594 self.canvas_y0 = canvas_y0
1595 self.sim_x0 = sim_x0
1596 self.sim_y0 = sim_y0
1597 self.motion_signal =
None
1599 def begin_node_drag(self, node, event):
1600 self.simulation.lock.acquire()
1602 ns3_node = ns.NodeList.GetNode(node.node_index)
1603 mob = ns.cppyy.gbl.hasMobilityModel(ns3_node)
1606 if self.node_drag_state
is not None:
1608 pos = ns.cppyy.gbl.getNodePosition(ns3_node)
1610 self.simulation.lock.release()
1611 devpos = self.canvas.get_window().get_device_position(event.device)
1612 x0, y0 = self.canvas.convert_from_pixels(devpos.x, devpos.y)
1613 self.node_drag_state = self.NodeDragState(x0, y0, pos.x, pos.y)
1614 self.node_drag_state.motion_signal = node.canvas_item.connect(
"motion-notify-event", self.node_drag_motion, node)
1616 def node_drag_motion(self, item, targe_item, event, node):
1617 self.simulation.lock.acquire()
1619 ns3_node = ns.NodeList.GetNode(node.node_index)
1620 mob = ns.cppyy.gbl.hasMobilityModel(ns3_node)
1623 if self.node_drag_state
is None:
1625 devpos = self.canvas.get_window().get_device_position(event.device)
1626 canvas_x, canvas_y = self.canvas.convert_from_pixels(devpos.x, devpos.y)
1627 dx = (canvas_x - self.node_drag_state.canvas_x0)
1628 dy = (canvas_y - self.node_drag_state.canvas_y0)
1629 pos = mob.GetPosition()
1633 mob.SetPosition(pos)
1636 self.simulation.lock.release()
1639 def end_node_drag(self, node):
1640 if self.node_drag_state
is None:
1642 node.canvas_item.disconnect(self.node_drag_state.motion_signal)
1643 self.node_drag_state =
None
1645 def popup_node_menu(self, node, event):
1647 self.emit(
"populate-node-menu", node, menu)
1648 menu.popup_at_pointer(event)
1650 def _update_ipython_selected_node(self):
1659 if self.selected_node
is None:
1662 self.simulation.lock.acquire()
1664 ns3_node = ns.NodeList.GetNode(self.selected_node.node_index)
1666 self.simulation.lock.release()
1667 self.ipython.updateNamespace({
'selected_node': ns3_node})
1670 def select_node(self, node):
1671 if isinstance(node, ns.Node):
1672 node = self.nodes[node.GetId()]
1673 elif isinstance(node, int):
1674 node = self.nodes[node]
1675 elif isinstance(node, Node):
1680 raise TypeError(
"expected None, int, viz.Node or ns.Node, not %r" % node)
1682 if node
is self.selected_node:
1685 if self.selected_node
is not None:
1686 self.selected_node.selected =
False
1687 self.selected_node = node
1688 if self.selected_node
is not None:
1689 self.selected_node.selected =
True
1691 if self._show_transmissions_mode == ShowTransmissionsMode.SELECTED:
1692 if self.selected_node
is None:
1693 self.simulation.set_nodes_of_interest([])
1695 self.simulation.set_nodes_of_interest([self.selected_node.node_index])
1697 self._update_ipython_selected_node()
1700 def add_information_window(self, info_win):
1701 self.information_windows.append(info_win)
1702 self.simulation.lock.acquire()
1706 self.simulation.lock.release()
1708 def remove_information_window(self, info_win):
1709 self.information_windows.remove(info_win)
1711 def _canvas_tooltip_cb(self, canvas, x, y, keyboard_mode, tooltip):
1713 hadj = self._scrolled_window.get_hadjustment()
1714 vadj = self._scrolled_window.get_vadjustment()
1715 x, y = self.canvas.convert_from_pixels(hadj.get_value() + x, vadj.get_value() + y)
1716 item = self.canvas.get_item_at(x, y,
True)
1720 while item
is not None:
1721 obj = getattr(item,
"pyviz_object",
None)
1723 obj.tooltip_query(tooltip)
1725 item = item.props.parent
1728 def _get_export_file_name(self):
1729 sel = Gtk.FileChooserNative.new(
"Save...", self.canvas.get_toplevel(),
1730 Gtk.FileChooserAction.SAVE,
1733 sel.set_local_only(
True)
1734 sel.set_do_overwrite_confirmation(
True)
1735 sel.set_current_name(
"Unnamed.pdf")
1737 filter = Gtk.FileFilter()
1738 filter.set_name(
"Embedded PostScript")
1739 filter.add_mime_type(
"image/x-eps")
1740 sel.add_filter(filter)
1742 filter = Gtk.FileFilter()
1743 filter.set_name(
"Portable Document Graphics")
1744 filter.add_mime_type(
"application/pdf")
1745 sel.add_filter(filter)
1747 filter = Gtk.FileFilter()
1748 filter.set_name(
"Scalable Vector Graphics")
1749 filter.add_mime_type(
"image/svg+xml")
1750 sel.add_filter(filter)
1753 if resp != Gtk.ResponseType.ACCEPT:
1757 file_name = sel.get_filename()
1761 def _take_screenshot(self, dummy_button):
1763 file_name = self._get_export_file_name()
1764 if file_name
is None:
1768 x1 = self._scrolled_window.get_hadjustment().get_value()
1769 y1 = self._scrolled_window.get_vadjustment().get_value()
1770 x2 = x1 + self._scrolled_window.get_hadjustment().get_page_size()
1771 y2 = y1 + self._scrolled_window.get_vadjustment().get_page_size()
1772 bounds = GooCanvas.CanvasBounds()
1773 bounds.x1, bounds.y1 = self.canvas.convert_from_pixels(x1, y1)
1774 bounds.x2, bounds.y2 = self.canvas.convert_from_pixels(x2, y2)
1775 dest_width = bounds.x2 - bounds.x1
1776 dest_height = bounds.y2 - bounds.y1
1779 dummy, extension = os.path.splitext(file_name)
1780 extension = extension.lower()
1781 if extension ==
'.eps':
1782 surface = cairo.PSSurface(file_name, dest_width, dest_height)
1783 elif extension ==
'.pdf':
1784 surface = cairo.PDFSurface(file_name, dest_width, dest_height)
1785 elif extension ==
'.svg':
1786 surface = cairo.SVGSurface(file_name, dest_width, dest_height)
1788 dialog = Gtk.MessageDialog(parent = self.canvas.get_toplevel(),
1789 flags = Gtk.DialogFlags.DESTROY_WITH_PARENT,
1790 type = Gtk.MessageType.ERROR,
1791 buttons = Gtk.ButtonsType.OK,
1792 message_format =
"Unknown extension '%s' (valid extensions are '.eps', '.svg', and '.pdf')"
1799 cr = cairo.Context(surface)
1800 cr.translate(-bounds.x1, -bounds.y1)
1801 self.canvas.render(cr, bounds, self.zoom.get_value())
1805 def set_follow_node(self, node):
1806 if isinstance(node, ns.Node):
1807 node = self.nodes[node.GetId()]
1808 self.follow_node = node
1810 def _start_shell(self, dummy_button):
1811 if self.shell_window
is not None:
1812 self.shell_window.present()
1815 self.shell_window = Gtk.Window()
1816 self.shell_window.set_size_request(750,550)
1817 self.shell_window.set_resizable(
True)
1818 scrolled_window = Gtk.ScrolledWindow()
1819 scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
1820 Gtk.PolicyType.AUTOMATIC)
1822 self.ipython.modify_font(Pango.FontDescription(SHELL_FONT))
1823 self.ipython.set_wrap_mode(Gtk.WrapMode.CHAR)
1825 scrolled_window.add(self.ipython)
1826 scrolled_window.show()
1827 self.shell_window.add(scrolled_window)
1828 self.shell_window.show()
1829 self.shell_window.connect(
'destroy', self._on_shell_window_destroy)
1831 self._update_ipython_selected_node()
1832 self.ipython.updateNamespace({
'viz': self})
1835 def _on_shell_window_destroy(self, window):
1836 self.shell_window =
None
1839initialization_hooks = []
1843 Adds a callback to be called after
1844 the visualizer is initialized, like this::
1845 initialization_hook(visualizer, *args)
1847 global initialization_hooks
1848 initialization_hooks.append((hook, args))
1857 viz.canvas.set_bounds(cx1, cy1, cx2, cy2)
1862 assert Visualizer.INSTANCE
is None
1863 if _import_error
is not None:
1865 print(
"No visualization support (%s)." % (str(_import_error),),
1871 for hook, args
in initialization_hooks:
1872 GLib.idle_add(hook, viz, *args)
1873 ns.Packet.EnablePrinting()
static bool IsFinished()
Check if the simulation should finish.
def set_position(self, x, y)
Initializer function.
def __init__(self, channel)
Initializer function.
def get_position(self)
Initializer function.
def on_enter_notify_event(self, view, target, event)
On Enter event handle.
visualizer
visualier object
def set_label(self, label)
Set a label for the node.
def add_link(self, link)
Add link function.
def set_svg_icon(self, file_base_name, width=None, height=None, align_x=0.5, align_y=0.5)
Set a background SVG icon for the node.
def get_position(self)
Get position function.
_highlighted
is highlighted
def _set_selected(self, value)
Set selected function.
_label_canvas_item
label canvas
highlighted
highlighted property
def on_leave_notify_event(self, view, target, event)
On Leave event handle.
def remove_link(self, link)
Remove link function.
def _update_svg_position(self, x, y)
Update svg position.
def __init__(self, visualizer, node_index)
Initialize function.
_has_mobility
has mobility model
def _get_selected(self)
Get selected function.
def set_color(self, color)
Set color function.
def _get_highlighted(self)
Get highlighted function.
def _update_position(self)
Update position function.
def has_mobility(self)
Has mobility function.
def set_position(self, x, y)
Set position function.
def tooltip_query(self, tooltip)
Query tooltip.
def set_size(self, size)
Set size function.
def _update_appearance(self)
Update the node aspect to reflect the selected/highlighted state.
def _set_highlighted(self, value)
Set highlighted function.
pause_messages
pause messages
def run(self)
Initializer function.
def set_nodes_of_interest(self, nodes)
Set nodes of interest function.
def __init__(self, viz)
Initializer function.
sim_helper
helper function
def __init__(self, node1, node2)
Initializer function.
def update_points(self)
Update points function.
def transform_distance_simulation_to_canvas(d)
def transform_distance_canvas_to_simulation(d)
def lookup_netdevice_traits(class_type)
def transform_point_simulation_to_canvas(x, y)
def add_initialization_hook(hook, *args)
def set_bounds(x1, y1, x2, y2)