2from ctypes
import c_double
4LAYOUT_ALGORITHM =
"neato"
5REPRESENT_CHANNELS_AS_NODES = 1
7DEFAULT_TRANSMISSIONS_MEMORY = (
14PRIORITY_UPDATE_MODEL = -100
15PRIORITY_UPDATE_VIEW = 200
20if platform.system() ==
"Windows":
21 SHELL_FONT =
"Lucida Console 9"
23 SHELL_FONT =
"Luxi Mono 10"
32 import dummy_threading
as threading
37 print(
"Pygraphviz is required by the visualizer module and could not be found")
43 print(
"Pycairo is required by the visualizer module and could not be found")
49 print(
"PyGObject is required by the visualizer module and could not be found")
58 gi.require_version(
"GooCanvas",
"2.0")
59 gi.require_version(
"Gtk",
"3.0")
60 gi.require_version(
"Gdk",
"3.0")
61 gi.require_foreign(
"cairo")
62 from gi.repository
import Gdk, GLib, GObject, GooCanvas, Gtk, Pango
65except ImportError
as e:
71 from .
import ipython_view
81 lookup_netdevice_traits,
84 transform_distance_canvas_to_simulation,
85 transform_distance_simulation_to_canvas,
86 transform_point_canvas_to_simulation,
87 transform_point_simulation_to_canvas,
90PI_OVER_2 = math.pi / 2
91PI_TIMES_2 = math.pi * 2
139 "query-extra-tooltip-info": (GObject.SignalFlags.RUN_LAST,
None, (object,)),
143 """! Initialize function.
144 @param self The object pointer.
145 @param visualizer visualizer object
146 @param node_index node index
171 def set_svg_icon(self, file_base_name, width=None, height=None, align_x=0.5, align_y=0.5):
173 Set a background SVG icon for the node.
175 @param file_base_name: base file name, including .svg
176 extension, of the svg file. Place the file
in the folder
177 src/contrib/visualizer/resource.
179 @param width: scale to the specified width,
in meters
180 @param height: scale to the specified height,
in meters
182 @param align_x: horizontal alignment of the icon relative to
183 the node position,
from 0 (icon fully to the left of the node)
184 to 1.0 (icon fully to the right of the node)
186 @param align_y: vertical alignment of the icon relative to the
187 node position,
from 0 (icon fully to the top of the node) to
188 1.0 (icon fully to the bottom of the node)
190 @return a ValueError exception
if invalid dimensions.
193 if width
is None and height
is None:
194 raise ValueError(
"either width or height must be given")
195 rsvg_handle = svgitem.rsvg_handle_factory(file_base_name)
200 self.
svg_item.props.pointer_events = GooCanvas.CanvasPointerEvents.NONE
202 self.
svg_item.props.visibility = GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD
203 if width
is not None:
204 self.
svg_item.props.width = transform_distance_simulation_to_canvas(width)
205 if height
is not None:
206 self.
svg_item.props.height = transform_distance_simulation_to_canvas(height)
219 Set a label for the node.
221 @param self:
class object.
222 @param label: label to set
224 @return: an exception
if invalid parameter.
226 assert isinstance(label, basestring)
234 @param self:
class object.
249 @param self:
class object.
250 @param tooltip: tooltip
255 ns3_node = ns.NodeList.GetNode(self.
node_index)
256 ipv4 = ns3_node.GetObject[ns.Ipv4]().__deref__()
257 ipv6 = ns3_node.GetObject[ns.Ipv6]().__deref__()
259 name =
"<b><u>Node %i</u></b>" % self.
node_index
260 node_name = ns.Names.FindName(ns3_node)
261 if len(node_name) != 0:
262 name +=
" <b>(" + node_name +
")</b>"
267 self.emit(
"query-extra-tooltip-info", lines)
269 mob = ns3_node.GetObject[ns.MobilityModel]()
271 mobility_model_name = mob.__deref__().GetInstanceTypeId().GetName()
272 lines.append(
" <b>Mobility Model</b>: %s" % mobility_model_name)
274 for devI
in range(ns3_node.GetNDevices()):
276 lines.append(
" <u>NetDevice %i:</u>" % devI)
277 dev = ns3_node.GetDevice(devI)
278 name = ns.Names.FindName(dev)
280 lines.append(
" <b>Name:</b> %s" % name)
281 devname = dev.GetInstanceTypeId().GetName()
282 lines.append(
" <b>Type:</b> %s" % devname)
285 ipv4_idx = ipv4.GetInterfaceForDevice(dev)
290 ipv4.GetAddress(ipv4_idx, i).GetLocal(),
291 ipv4.GetAddress(ipv4_idx, i).GetMask(),
293 for i
in range(ipv4.GetNAddresses(ipv4_idx))
295 lines.append(
" <b>IPv4 Addresses:</b> %s" %
"; ".join(addresses))
298 ipv6_idx = ipv6.GetInterfaceForDevice(dev)
303 ipv6.GetAddress(ipv6_idx, i).GetAddress(),
304 ipv6.GetAddress(ipv6_idx, i).GetPrefix(),
306 for i
in range(ipv6.GetNAddresses(ipv6_idx))
308 lines.append(
" <b>IPv6 Addresses:</b> %s" %
"; ".join(addresses))
310 lines.append(
" <b>MAC Address:</b> %s" % (dev.GetAddress(),))
312 tooltip.set_markup(
"\n".join(lines))
318 On Enter event handle.
320 @param self:
class object.
322 @param target: target
332 On Leave event handle.
334 @param self:
class object.
336 @param target: target
344 Set selected function.
346 @param self:
class object.
347 @param value: selected value
355 Get selected function.
357 @param self:
class object.
358 @return selected status
362 selected = property(_get_selected, _set_selected)
366 Set highlighted function.
368 @param self:
class object.
369 @param value: selected value
377 Get highlighted function.
379 @param self:
class object.
380 @return highlighted status
384 highlighted = property(_get_highlighted, _set_highlighted)
390 @param self:
class object.
391 @param size: selected size
399 Update the node aspect to reflect the selected/highlighted state
401 @param self:
class object.
405 size = transform_distance_simulation_to_canvas(self._size)
410 fill_color_rgba = (self.
_color & 0xFFFFFF00) | alpha
412 radius_x=size, radius_y=size, fill_color_rgba=fill_color_rgba
415 line_width = size * 0.3
417 line_width = size * 0.15
419 stroke_color =
"yellow"
421 stroke_color =
"black"
422 self.
canvas_item.set_properties(line_width=line_width, stroke_color=stroke_color)
424 if self.
_label is not None:
427 visibility_threshold=0.5,
428 font=
"Sans Serif 10",
429 fill_color_rgba=0x808080FF,
430 alignment=Pango.Alignment.CENTER,
431 anchor=GooCanvas.CanvasAnchorType.N,
432 parent=self.
visualizer.canvas.get_root_item(),
433 pointer_events=GooCanvas.CanvasPointerEvents.NONE,
438 visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD, text=self.
_label
444 Set position function.
446 @param self:
class object.
456 for link
in self.
links:
467 (min_x, min_y, max_x, max_y) = bounds
469 min_x = min(x, min_x)
470 min_y = min(y, min_y)
471 max_x = max(x, max_x)
472 max_y = max(y, max_y)
474 new_bounds = (min_x, min_y, max_x, max_y)
476 if new_bounds != bounds:
477 self.
visualizer.canvas.set_bounds(*new_bounds)
484 Get position function.
486 @param self:
class object.
487 @return x
and y position
496 Update position function.
498 @param self:
class object.
508 @param self:
class object.
509 @param color: color to set.
512 if isinstance(color, str):
513 color = Gdk.color_parse(color)
515 ((color.red >> 8) << 24)
516 | ((color.green >> 8) << 16)
517 | ((color.blue >> 8) << 8)
527 @param self:
class object.
528 @param link: link to add.
531 assert isinstance(link, Link)
532 self.
links.append(link)
536 Remove link function.
538 @param self:
class object.
539 @param link: link to add.
542 assert isinstance(link, Link)
543 self.
links.remove(link)
548 Has mobility function.
550 @param self:
class object.
551 @return modility option
570 Initializer function.
572 @param self:
class object.
573 @param channel: channel.
582 line_dash=GooCanvas.CanvasLineDash.newv([10.0, 10.0]),
583 visibility=GooCanvas.CanvasItemVisibility.VISIBLE,
590 Initializer function.
592 @param self:
class object.
593 @param x: x position.
594 @param y: y position.
600 for link
in self.
links:
605 Initializer function.
607 @param self:
class object.
608 @return x / y position.
627 Initializer function.
629 @param self:
class object.
630 @param node1:
class object.
631 @param node2:
class object.
633 assert isinstance(node1, Node)
634 assert isinstance(node2, (Node, Channel))
637 self.
canvas_item = GooCanvas.CanvasPath(line_width=1.0, stroke_color=
"black")
639 self.
node1.links.append(self)
640 self.
node2.links.append(self)
644 Update points function.
646 @param self:
class object.
649 pos1_x, pos1_y = self.node1.get_position()
650 pos2_x, pos2_y = self.node2.get_position()
651 self.canvas_item.set_property("data",
"M %r %r L %r %r" % (pos1_x, pos1_y, pos2_x, pos2_y))
672 Initializer function.
674 @param self:
class object.
677 super(SimulationThread, self).__init__()
678 assert isinstance(viz, Visualizer)
681 self.
go = threading.Event()
690 Set nodes of interest function.
692 @param self:
class object.
693 @param nodes:
class object.
704 Initializer function.
706 @param self:
class object.
721 self.
viz.play_button.set_sensitive(
False)
729 GLib.idle_add(self.
viz.update_model, priority=PRIORITY_UPDATE_MODEL)
760 if _import_error
is None:
763 "populate-node-menu": (
764 GObject.SignalFlags.RUN_LAST,
773 "simulation-periodic-update": (GObject.SignalFlags.RUN_LAST,
None, ()),
775 "topology-scanned": (GObject.SignalFlags.RUN_LAST,
None, ()),
777 "update-view": (GObject.SignalFlags.RUN_LAST,
None, ()),
782 Initializer function.
784 @param self:
class object.
787 assert Visualizer.INSTANCE
is None
788 Visualizer.INSTANCE = self
789 super(Visualizer, self).__init__()
794 self.time_label =
None
795 self.play_button =
None
797 self._scrolled_window =
None
799 self.links_group = GooCanvas.CanvasGroup()
800 self.channels_group = GooCanvas.CanvasGroup()
801 self.nodes_group = GooCanvas.CanvasGroup()
803 self._update_timeout_id =
None
805 self.selected_node =
None
807 self.information_windows = []
808 self._transmission_arrows = []
809 self._last_transmissions = []
810 self._drop_arrows = []
811 self._last_drops = []
812 self._show_transmissions_mode =
None
813 self.set_show_transmissions_mode(ShowTransmissionsMode.ALL)
814 self._panning_state =
None
815 self.node_size_adjustment =
None
816 self.transmissions_smoothing_adjustment =
None
817 self.sample_period = SAMPLE_PERIOD
818 self.node_drag_state =
None
819 self.follow_node =
None
820 self.shell_window =
None
824 for plugin
in plugins:
827 def set_show_transmissions_mode(self, mode):
829 Set show transmission mode.
831 @param self:
class object.
832 @param mode: mode to set.
835 assert isinstance(mode, ShowTransmissionsMode)
836 self._show_transmissions_mode = mode
837 if self._show_transmissions_mode == ShowTransmissionsMode.ALL:
838 self.simulation.set_nodes_of_interest(
list(range(ns.NodeList.GetNNodes())))
839 elif self._show_transmissions_mode == ShowTransmissionsMode.NONE:
840 self.simulation.set_nodes_of_interest([])
841 elif self._show_transmissions_mode == ShowTransmissionsMode.SELECTED:
842 if self.selected_node
is None:
843 self.simulation.set_nodes_of_interest([])
845 self.simulation.set_nodes_of_interest([self.selected_node.node_index])
847 def _create_advanced_controls(self):
849 Create advanced controls.
851 @param self:
class object.
854 expander = Gtk.Expander.new("Advanced")
857 main_vbox = GObject.new(Gtk.VBox, border_width=8, visible=
True)
858 expander.add(main_vbox)
860 main_hbox1 = GObject.new(Gtk.HBox, border_width=8, visible=
True)
861 main_vbox.pack_start(main_hbox1,
True,
True, 0)
863 show_transmissions_group = GObject.new(
864 Gtk.HeaderBar, title=
"Show transmissions", visible=
True
866 main_hbox1.pack_start(show_transmissions_group,
False,
False, 8)
868 vbox = Gtk.VBox(homogeneous=
True, spacing=4)
870 show_transmissions_group.add(vbox)
872 all_nodes = Gtk.RadioButton.new(
None)
873 all_nodes.set_label(
"All nodes")
874 all_nodes.set_active(
True)
878 selected_node = Gtk.RadioButton.new_from_widget(all_nodes)
880 selected_node.set_label(
"Selected node")
881 selected_node.set_active(
False)
882 vbox.add(selected_node)
884 no_node = Gtk.RadioButton.new_from_widget(all_nodes)
886 no_node.set_label(
"Disabled")
887 no_node.set_active(
False)
891 if radio.get_active():
892 self.set_show_transmissions_mode(ShowTransmissionsMode.ALL)
894 all_nodes.connect(
"toggled", toggled)
897 if radio.get_active():
898 self.set_show_transmissions_mode(ShowTransmissionsMode.NONE)
900 no_node.connect(
"toggled", toggled)
903 if radio.get_active():
904 self.set_show_transmissions_mode(ShowTransmissionsMode.SELECTED)
906 selected_node.connect(
"toggled", toggled)
909 misc_settings_group = GObject.new(Gtk.HeaderBar, title=
"Misc Settings", visible=
True)
910 main_hbox1.pack_start(misc_settings_group,
False,
False, 8)
911 settings_hbox = GObject.new(Gtk.HBox, border_width=8, visible=
True)
912 misc_settings_group.add(settings_hbox)
915 vbox = GObject.new(Gtk.VBox, border_width=0, visible=
True)
916 scale = GObject.new(Gtk.HScale, visible=
True, digits=2)
917 vbox.pack_start(scale,
True,
True, 0)
918 vbox.pack_start(GObject.new(Gtk.Label, label=
"Node Size", visible=
True),
True,
True, 0)
919 settings_hbox.pack_start(vbox,
False,
False, 6)
920 self.node_size_adjustment = scale.get_adjustment()
922 def node_size_changed(adj):
923 for node
in self.nodes.values():
924 node.set_size(adj.get_value())
926 self.node_size_adjustment.connect(
"value-changed", node_size_changed)
927 self.node_size_adjustment.set_lower(0.01)
928 self.node_size_adjustment.set_upper(20)
929 self.node_size_adjustment.set_step_increment(0.1)
930 self.node_size_adjustment.set_value(DEFAULT_NODE_SIZE)
933 vbox = GObject.new(Gtk.VBox, border_width=0, visible=
True)
934 scale = GObject.new(Gtk.HScale, visible=
True, digits=1)
935 vbox.pack_start(scale,
True,
True, 0)
937 GObject.new(Gtk.Label, label=
"Tx. Smooth Factor (s)", visible=
True),
True,
True, 0
939 settings_hbox.pack_start(vbox,
False,
False, 6)
940 self.transmissions_smoothing_adjustment = scale.get_adjustment()
941 adj = self.transmissions_smoothing_adjustment
944 adj.set_step_increment(0.1)
945 adj.set_value(DEFAULT_TRANSMISSIONS_MEMORY * 0.1)
950 class _PanningState(
object):
953 __slots__ = [
"initial_mouse_pos",
"initial_canvas_pos",
"motion_signal"]
955 def _begin_panning(self, widget, event):
957 Set show trnamission mode.
959 @param self:
class object.
960 @param mode: mode to set.
963 display = self.canvas.get_window().get_display()
964 cursor = Gdk.Cursor.new_for_display(display, Gdk.CursorType.FLEUR)
965 self.canvas.get_window().set_cursor(cursor)
966 self._panning_state = self._PanningState()
967 pos = widget.get_window().get_device_position(event.device)
968 self._panning_state.initial_mouse_pos = (pos.x, pos.y)
969 x = self._scrolled_window.get_hadjustment().get_value()
970 y = self._scrolled_window.get_vadjustment().get_value()
971 self._panning_state.initial_canvas_pos = (x, y)
972 self._panning_state.motion_signal = self.canvas.connect(
973 "motion-notify-event", self._panning_motion
976 def _end_panning(self, event):
978 End panning function.
980 @param self:
class object.
981 @param event: active event.
984 if self._panning_state
is None:
986 self.canvas.get_window().set_cursor(
None)
987 self.canvas.disconnect(self._panning_state.motion_signal)
988 self._panning_state =
None
990 def _panning_motion(self, widget, event):
992 Panning motion function.
994 @param self:
class object.
995 @param widget: widget.
997 @return true
if successful
999 assert self._panning_state
is not None
1001 pos = widget.get_window().get_device_position(event.device)
1004 x, y = event.x, event.y
1006 hadj = self._scrolled_window.get_hadjustment()
1007 vadj = self._scrolled_window.get_vadjustment()
1008 mx0, my0 = self._panning_state.initial_mouse_pos
1009 cx0, cy0 = self._panning_state.initial_canvas_pos
1013 hadj.set_value(cx0 - dx)
1014 vadj.set_value(cy0 - dy)
1017 def _canvas_button_press(self, widget, event):
1018 if event.button == 2:
1019 self._begin_panning(widget, event)
1023 def _canvas_button_release(self, dummy_widget, event):
1024 if event.button == 2:
1025 self._end_panning(event)
1029 def _canvas_scroll_event(self, dummy_widget, event):
1030 if event.direction == Gdk.ScrollDirection.UP:
1031 self.zoom.set_value(self.zoom.get_value() * 1.25)
1033 elif event.direction == Gdk.ScrollDirection.DOWN:
1034 self.zoom.set_value(self.zoom.get_value() / 1.25)
1038 def get_hadjustment(self):
1039 return self._scrolled_window.get_hadjustment()
1041 def get_vadjustment(self):
1042 return self._scrolled_window.get_vadjustment()
1044 def create_gui(self):
1045 self.window = Gtk.Window()
1048 self.window.add(vbox)
1051 self.canvas = GooCanvas.Canvas()
1052 self.canvas.connect_after(
"button-press-event", self._canvas_button_press)
1053 self.canvas.connect_after(
"button-release-event", self._canvas_button_release)
1054 self.canvas.connect(
"scroll-event", self._canvas_scroll_event)
1055 self.canvas.props.has_tooltip =
True
1056 self.canvas.connect(
"query-tooltip", self._canvas_tooltip_cb)
1058 sw = Gtk.ScrolledWindow()
1060 self._scrolled_window = sw
1062 vbox.pack_start(sw,
True,
True, 4)
1063 self.canvas.set_size_request(600, 450)
1064 self.canvas.set_bounds(-10000, -10000, 10000, 10000)
1065 self.canvas.scroll_to(0, 0)
1067 self.canvas.get_root_item().add_child(self.links_group, -1)
1068 self.links_group.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1070 self.canvas.get_root_item().add_child(self.channels_group, -1)
1071 self.channels_group.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1072 self.channels_group.raise_(self.links_group)
1074 self.canvas.get_root_item().add_child(self.nodes_group, -1)
1075 self.nodes_group.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1076 self.nodes_group.raise_(self.channels_group)
1082 vbox.pack_start(hbox,
False,
False, 4)
1085 zoom_adj = Gtk.Adjustment(
1089 step_increment=0.02,
1093 self.zoom = zoom_adj
1095 def _zoom_changed(adj):
1096 self.canvas.set_scale(adj.get_value())
1098 zoom_adj.connect(
"value-changed", _zoom_changed)
1099 zoom = Gtk.SpinButton.new(zoom_adj, 0.1, 1)
1102 hbox.pack_start(GObject.new(Gtk.Label, label=
" Zoom:", visible=
True),
False,
False, 4)
1103 hbox.pack_start(zoom,
False,
False, 4)
1104 _zoom_changed(zoom_adj)
1107 speed_adj = Gtk.Adjustment(
1108 value=1.0, lower=0.01, upper=10.0, step_increment=0.02, page_increment=1.0, page_size=0
1111 def _speed_changed(adj):
1112 self.speed = adj.get_value()
1113 self.sample_period = SAMPLE_PERIOD * adj.get_value()
1114 self._start_update_timer()
1116 speed_adj.connect(
"value-changed", _speed_changed)
1117 speed = Gtk.SpinButton.new(speed_adj, 1, 0)
1120 hbox.pack_start(GObject.new(Gtk.Label, label=
" Speed:", visible=
True),
False,
False, 4)
1121 hbox.pack_start(speed,
False,
False, 4)
1122 _speed_changed(speed_adj)
1125 self.time_label = GObject.new(Gtk.Label, label=
" Speed:", visible=
True)
1126 self.time_label.set_width_chars(20)
1127 hbox.pack_start(self.time_label,
False,
False, 4)
1130 screenshot_button = GObject.new(
1133 relief=Gtk.ReliefStyle.NONE,
1134 focus_on_click=
False,
1137 hbox.pack_start(screenshot_button,
False,
False, 4)
1139 def load_button_icon(button, icon_name):
1140 if not Gtk.IconTheme.get_default().has_icon(icon_name):
1141 print(f
"Could not load icon {icon_name}", file=sys.stderr)
1143 image = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.BUTTON)
1144 button.set_image(image)
1145 button.props.always_show_image =
True
1147 load_button_icon(screenshot_button,
"applets-screenshooter")
1148 screenshot_button.connect(
"clicked", self._take_screenshot)
1151 if ipython_view
is not None:
1152 shell_button = GObject.new(
1155 relief=Gtk.ReliefStyle.NONE,
1156 focus_on_click=
False,
1159 hbox.pack_start(shell_button,
False,
False, 4)
1160 load_button_icon(shell_button,
"gnome-terminal")
1161 shell_button.connect(
"clicked", self._start_shell)
1164 self.play_button = GObject.new(
1166 label=
"Simulate (F3)",
1167 relief=Gtk.ReliefStyle.NONE,
1168 focus_on_click=
False,
1171 load_button_icon(self.play_button,
"media-playback-start")
1172 accel_group = Gtk.AccelGroup()
1173 self.window.add_accel_group(accel_group)
1174 self.play_button.add_accelerator(
1175 "clicked", accel_group, Gdk.KEY_F3, 0, Gtk.AccelFlags.VISIBLE
1177 self.play_button.connect(
"toggled", self._on_play_button_toggled)
1178 hbox.pack_start(self.play_button,
False,
False, 4)
1180 self.canvas.get_root_item().connect(
"button-press-event", self.on_root_button_press_event)
1182 vbox.pack_start(self._create_advanced_controls(),
False,
False, 4)
1184 display = Gdk.Display.get_default()
1186 monitor = display.get_primary_monitor()
1187 geometry = monitor.get_geometry()
1188 scale_factor = monitor.get_scale_factor()
1189 except AttributeError:
1190 screen = display.get_default_screen()
1191 monitor_id = screen.get_primary_monitor()
1192 geometry = screen.get_monitor_geometry(monitor_id)
1193 scale_factor = screen.get_monitor_scale_factor(monitor_id)
1194 width = scale_factor * geometry.width
1195 height = scale_factor * geometry.height
1196 self.window.set_default_size(width * 2 / 3, height * 2 / 3)
1199 def scan_topology(self):
1200 print(
"scanning topology: %i nodes..." % (ns.NodeList.GetNNodes(),))
1201 graph = pygraphviz.AGraph()
1203 for nodeI
in range(ns.NodeList.GetNNodes()):
1205 if seen_nodes == 100:
1207 "scan topology... %i nodes visited (%.1f%%)"
1208 % (nodeI, 100 * nodeI / ns.NodeList.GetNNodes())
1211 node = ns.NodeList.GetNode(nodeI)
1212 node_name =
"Node %i" % nodeI
1213 node_view = self.get_node(nodeI)
1215 mobility = node.GetObject[ns.MobilityModel]()
1217 node_view.set_color(
"red")
1218 pos = node.GetObject[ns.MobilityModel]().__deref__().GetPosition()
1219 node_view.set_position(*transform_point_simulation_to_canvas(pos.x, pos.y))
1222 graph.add_node(node_name)
1224 for devI
in range(node.GetNDevices()):
1225 device = node.GetDevice(devI)
1226 device_traits = lookup_netdevice_traits(type(device.__deref__()))
1227 if device_traits.is_wireless:
1229 if device_traits.is_virtual:
1231 channel = device.GetChannel()
1232 if channel.GetNDevices() > 2:
1233 if REPRESENT_CHANNELS_AS_NODES:
1235 if mobility
is None:
1236 channel_name =
"Channel %s" % id(channel)
1237 graph.add_edge(node_name, channel_name)
1238 self.get_channel(channel)
1239 self.create_link(self.get_node(nodeI), self.get_channel(channel))
1242 for otherDevI
in range(channel.GetNDevices()):
1243 otherDev = channel.GetDevice(otherDevI)
1244 otherNode = otherDev.GetNode()
1245 otherNodeView = self.get_node(otherNode.GetId())
1246 if otherNode
is not node:
1247 if mobility
is None and not otherNodeView.has_mobility:
1248 other_node_name =
"Node %i" % otherNode.GetId()
1249 graph.add_edge(node_name, other_node_name)
1250 self.create_link(self.get_node(nodeI), otherNodeView)
1252 for otherDevI
in range(channel.GetNDevices()):
1253 otherDev = channel.GetDevice(otherDevI)
1254 otherNode = otherDev.GetNode()
1255 otherNodeView = self.get_node(otherNode.GetId())
1256 if otherNode
is not node:
1257 if mobility
is None and not otherNodeView.has_mobility:
1258 other_node_name =
"Node %i" % otherNode.GetId()
1259 graph.add_edge(node_name, other_node_name)
1260 self.create_link(self.get_node(nodeI), otherNodeView)
1262 print(
"scanning topology: calling graphviz layout")
1263 graph.layout(LAYOUT_ALGORITHM)
1264 for node
in graph.iternodes():
1266 node_type, node_id = node.split(
" ")
1267 pos_x, pos_y = [float(s)
for s
in node.attr[
"pos"].split(
",")]
1268 if node_type ==
"Node":
1269 obj = self.nodes[int(node_id)]
1270 elif node_type ==
"Channel":
1271 obj = self.channels[int(node_id)]
1272 obj.set_position(pos_x, pos_y)
1274 print(
"scanning topology: all done.")
1275 self.emit(
"topology-scanned")
1277 def get_node(self, index):
1279 return self.nodes[index]
1281 node =
Node(self, index)
1282 self.nodes[index] = node
1283 self.nodes_group.add_child(node.canvas_item, -1)
1284 node.canvas_item.connect(
"button-press-event", self.on_node_button_press_event, node)
1285 node.canvas_item.connect(
1286 "button-release-event", self.on_node_button_release_event, node
1290 def get_channel(self, ns3_channel):
1292 return self.channels[id(ns3_channel)]
1294 channel =
Channel(ns3_channel)
1295 self.channels[id(ns3_channel)] = channel
1296 self.channels_group.add_child(channel.canvas_item, -1)
1299 def create_link(self, node, node_or_channel):
1301 self.links_group.add_child(link.canvas_item, -1)
1302 link.canvas_item.lower(
None)
1304 def update_view(self):
1307 self.time_label.set_text(
"Time: %f s" % ns.Simulator.Now().GetSeconds())
1309 self._update_node_positions()
1312 for info_win
in self.information_windows:
1315 self._update_transmissions_view()
1316 self._update_drops_view()
1318 self.emit(
"update-view")
1320 def _update_node_positions(self):
1321 for node
in self.nodes.values():
1322 if node.has_mobility:
1323 ns3_node = ns.NodeList.GetNode(node.node_index)
1324 mobility = ns3_node.GetObject[ns.MobilityModel]()
1326 pos = ns3_node.GetObject[ns.MobilityModel]().__deref__().GetPosition()
1327 x, y = transform_point_simulation_to_canvas(pos.x, pos.y)
1328 node.set_position(x, y)
1329 if node
is self.follow_node:
1330 hadj = self._scrolled_window.get_hadjustment()
1331 vadj = self._scrolled_window.get_vadjustment()
1332 px, py = self.canvas.convert_to_pixels(x, y)
1333 hadj.set_value(px - hadj.get_page_size() / 2)
1334 vadj.set_value(py - vadj.get_page_size() / 2)
1336 def center_on_node(self, node):
1337 if isinstance(node, ns.Node):
1338 node = self.nodes[node.GetId()]
1339 elif isinstance(node, int):
1340 node = self.nodes[node]
1341 elif isinstance(node, Node):
1344 raise TypeError(
"expected int, viz.Node or ns.Node, not %r" % node)
1346 x, y = node.get_position()
1347 hadj = self._scrolled_window.get_hadjustment()
1348 vadj = self._scrolled_window.get_vadjustment()
1349 px, py = self.canvas.convert_to_pixels(x, y)
1350 hadj.set_value(px - hadj.get_page_size() / 2)
1351 vadj.set_value(py - vadj.get_page_size() / 2)
1353 def update_model(self):
1354 self.simulation.lock.acquire()
1356 self.emit(
"simulation-periodic-update")
1358 self.simulation.lock.release()
1360 def do_simulation_periodic_update(self):
1361 smooth_factor = int(self.transmissions_smoothing_adjustment.get_value() * 10)
1363 transmissions = self.simulation.sim_helper.GetTransmissionSamples()
1364 self._last_transmissions.append(transmissions)
1365 while len(self._last_transmissions) > smooth_factor:
1366 self._last_transmissions.pop(0)
1368 drops = self.simulation.sim_helper.GetPacketDropSamples()
1369 self._last_drops.append(drops)
1370 while len(self._last_drops) > smooth_factor:
1371 self._last_drops.pop(0)
1373 def _get_label_over_line_position(self, pos1_x, pos1_y, pos2_x, pos2_y):
1374 hadj = self._scrolled_window.get_hadjustment()
1375 vadj = self._scrolled_window.get_vadjustment()
1376 bounds_x1, bounds_y1 = self.canvas.convert_from_pixels(hadj.get_value(), vadj.get_value())
1377 bounds_x2, bounds_y2 = self.canvas.convert_from_pixels(
1378 hadj.get_value() + hadj.get_page_size(), vadj.get_value() + vadj.get_page_size()
1380 ns.PyViz.LineClipping(
1381 bounds_x1, bounds_y1, bounds_x2, bounds_y2, pos1_x, pos1_y, pos2_x, pos2_y
1383 return (pos1_x.value + pos2_x.value) / 2, (pos1_y.value + pos2_y.value) / 2
1385 def _update_transmissions_view(self):
1386 transmissions_average = {}
1387 for transmission_set
in self._last_transmissions:
1388 for transmission
in transmission_set:
1389 key = (transmission.transmitter.GetId(), transmission.receiver.GetId())
1390 rx_bytes, count = transmissions_average.get(key, (0, 0))
1391 rx_bytes += transmission.bytes
1393 transmissions_average[key] = rx_bytes, count
1395 old_arrows = self._transmission_arrows
1396 for arrow, label
in old_arrows:
1397 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1398 label.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1401 k = self.node_size_adjustment.get_value() / 5
1403 for (transmitter_id, receiver_id), (rx_bytes, rx_count)
in transmissions_average.items():
1404 transmitter = self.get_node(transmitter_id)
1405 receiver = self.get_node(receiver_id)
1407 arrow, label = old_arrows.pop()
1409 arrow = GooCanvas.CanvasPolyline(
1411 stroke_color_rgba=0x00C000C0,
1414 pointer_events=GooCanvas.CanvasPointerEvents.NONE,
1416 arrow.set_property(
"parent", self.canvas.get_root_item())
1419 label = GooCanvas.CanvasText(
1420 parent=self.canvas.get_root_item(),
1421 pointer_events=GooCanvas.CanvasPointerEvents.NONE,
1425 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1426 line_width = max(0.1, math.log(float(rx_bytes) / rx_count / self.sample_period) * k)
1427 arrow.set_property(
"line-width", line_width)
1429 pos1_x, pos1_y = transmitter.get_position()
1430 pos2_x, pos2_y = receiver.get_position()
1431 points = GooCanvas.CanvasPoints.new(2)
1432 points.set_point(0, pos1_x, pos1_y)
1433 points.set_point(1, pos2_x, pos2_y)
1434 arrow.set_property(
"points", points)
1436 kbps = float(rx_bytes * 8) / 1e3 / rx_count / self.sample_period
1437 label.set_properties(
1438 visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
1439 visibility_threshold=0.5,
1440 font=(
"Sans Serif %f" % int(1 + BITRATE_FONT_SIZE * k)),
1442 angle = math.atan2((pos2_y - pos1_y), (pos2_x - pos1_x))
1443 if -PI_OVER_2 <= angle <= PI_OVER_2:
1444 label.set_properties(
1445 text=(
"%.2f kbit/s →" % (kbps,)),
1446 alignment=Pango.Alignment.CENTER,
1447 anchor=GooCanvas.CanvasAnchorType.S,
1452 label.set_properties(
1453 text=(
"← %.2f kbit/s" % (kbps,)),
1454 alignment=Pango.Alignment.CENTER,
1455 anchor=GooCanvas.CanvasAnchorType.N,
1460 lx, ly = self._get_label_over_line_position(
1461 c_double(pos1_x), c_double(pos1_y), c_double(pos2_x), c_double(pos2_y)
1466 label.set_transform(M)
1470 "PyGobject bug causing label position error; "
1471 "should be fixed in PyGObject >= 3.29.1"
1473 label.set_properties(x=(lx + label.props.x), y=(ly + label.props.y))
1475 new_arrows.append((arrow, label))
1477 self._transmission_arrows = new_arrows + old_arrows
1479 def _update_drops_view(self):
1481 for drop_set
in self._last_drops:
1482 for drop
in drop_set:
1483 key = drop.transmitter.GetId()
1484 drop_bytes, count = drops_average.get(key, (0, 0))
1485 drop_bytes += drop.bytes
1487 drops_average[key] = drop_bytes, count
1489 old_arrows = self._drop_arrows
1490 for arrow, label
in old_arrows:
1491 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1492 label.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1496 vadjustment = self._scrolled_window.get_vadjustment()
1497 bottom_y = vadjustment.get_value() + vadjustment.get_page_size()
1498 dummy, edge_y = self.canvas.convert_from_pixels(0, bottom_y)
1500 k = self.node_size_adjustment.get_value() / 5
1502 for transmitter_id, (drop_bytes, drop_count)
in drops_average.items():
1503 transmitter = self.get_node(transmitter_id)
1505 arrow, label = old_arrows.pop()
1507 arrow = GooCanvas.CanvasPolyline(
1509 stroke_color_rgba=0xC00000C0,
1512 pointer_events=GooCanvas.CanvasPointerEvents.NONE,
1514 arrow.set_property(
"parent", self.canvas.get_root_item())
1517 label = GooCanvas.CanvasText(
1518 pointer_events=GooCanvas.CanvasPointerEvents.NONE
1520 label.set_property(
"parent", self.canvas.get_root_item())
1523 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1526 max(0.1, math.log(float(drop_bytes) / drop_count / self.sample_period) * k),
1528 pos1_x, pos1_y = transmitter.get_position()
1529 pos2_x, pos2_y = pos1_x, edge_y
1530 points = GooCanvas.CanvasPoints.new(2)
1531 points.set_point(0, pos1_x, pos1_y)
1532 points.set_point(1, pos2_x, pos2_y)
1533 arrow.set_property(
"points", points)
1535 label.set_properties(
1536 visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
1537 visibility_threshold=0.5,
1538 font=(
"Sans Serif %i" % int(1 + BITRATE_FONT_SIZE * k)),
1540 "%.2f kbit/s" % (float(drop_bytes * 8) / 1e3 / drop_count / self.sample_period,)
1542 alignment=Pango.Alignment.CENTER,
1543 x=(pos1_x + pos2_x) / 2,
1544 y=(pos1_y + pos2_y) / 2,
1547 new_arrows.append((arrow, label))
1549 self._drop_arrows = new_arrows + old_arrows
1551 def update_view_timeout(self):
1555 while not self.simulation.lock.acquire(
False):
1556 while Gtk.events_pending():
1557 Gtk.main_iteration()
1558 pause_messages = self.simulation.pause_messages
1559 self.simulation.pause_messages = []
1562 self.simulation.target_time = ns.Simulator.Now().GetSeconds() + self.sample_period
1565 self.simulation.lock.release()
1569 dialog = Gtk.MessageDialog(
1572 type=Gtk.MessageType.WARNING,
1573 buttons=Gtk.ButtonsType.OK,
1574 message_format=
"\n".join(pause_messages),
1576 dialog.connect(
"response",
lambda d, r: d.destroy())
1578 self.play_button.set_active(
False)
1581 if not self.play_button.get_active():
1582 self._update_timeout_id =
None
1586 self.simulation.go.set()
1590 def _start_update_timer(self):
1591 if self._update_timeout_id
is not None:
1592 GLib.source_remove(self._update_timeout_id)
1594 self._update_timeout_id = GLib.timeout_add(
1595 int(SAMPLE_PERIOD / min(self.speed, 1) * 1e3),
1596 self.update_view_timeout,
1597 priority=PRIORITY_UPDATE_VIEW,
1600 def _on_play_button_toggled(self, button):
1601 if button.get_active():
1602 self._start_update_timer()
1604 if self._update_timeout_id
is not None:
1605 GLib.source_remove(self._update_timeout_id)
1607 def _quit(self, *dummy_args):
1608 if self._update_timeout_id
is not None:
1609 GLib.source_remove(self._update_timeout_id)
1610 self._update_timeout_id =
None
1611 self.simulation.quit =
True
1612 self.simulation.go.set()
1613 self.simulation.join()
1616 def _monkey_patch_ipython(self):
1623 original_runcode = self.ipython.runcode
1625 def runcode(ip, *args):
1627 self.simulation.lock.acquire()
1629 return original_runcode(*args)
1632 self.simulation.lock.release()
1636 self.ipython.runcode = types.MethodType(runcode, self.ipython)
1638 def autoscale_view(self):
1641 self._update_node_positions()
1642 positions = [node.get_position()
for node
in self.nodes.values()]
1643 min_x, min_y = min(x
for (x, y)
in positions), min(y
for (x, y)
in positions)
1644 max_x, max_y = max(x
for (x, y)
in positions), max(y
for (x, y)
in positions)
1645 min_x_px, min_y_px = self.canvas.convert_to_pixels(min_x, min_y)
1646 max_x_px, max_y_px = self.canvas.convert_to_pixels(max_x, max_y)
1649 dx_px = max_x_px - min_x_px
1650 dy_px = max_y_px - min_y_px
1651 hadj = self._scrolled_window.get_hadjustment()
1652 vadj = self._scrolled_window.get_vadjustment()
1653 new_dx, new_dy = 1.5 * dx_px, 1.5 * dy_px
1655 if new_dx == 0
or new_dy == 0:
1658 self.zoom.set_value(min(hadj.get_page_size() / new_dx, vadj.get_page_size() / new_dy))
1660 x1, y1 = self.canvas.convert_from_pixels(hadj.get_value(), vadj.get_value())
1661 x2, y2 = self.canvas.convert_from_pixels(
1662 (hadj.get_value() + hadj.get_page_size()), (vadj.get_value() + vadj.get_page_size())
1666 center_x = (min_x + max_x) / 2
1667 center_y = (min_y + max_y) / 2
1669 self.canvas.scroll_to(center_x - width / 2, center_y - height / 2)
1674 self.scan_topology()
1675 self.window.connect(
"delete-event", self._quit)
1677 GLib.timeout_add(200, self.autoscale_view)
1678 self.simulation.
start()
1685 self._monkey_patch_ipython()
1689 def on_root_button_press_event(self, view, target, event):
1690 if event.button == 1:
1691 self.select_node(
None)
1694 def on_node_button_press_event(self, view, target, event, node):
1695 button = event.button
1697 self.select_node(node)
1700 self.popup_node_menu(node, event)
1703 self.begin_node_drag(node, event)
1707 def on_node_button_release_event(self, view, target, event, node):
1708 if event.button == 2:
1709 self.end_node_drag(node)
1713 class NodeDragState(
object):
1714 def __init__(self, canvas_x0, canvas_y0, sim_x0, sim_y0):
1715 self.canvas_x0 = canvas_x0
1716 self.canvas_y0 = canvas_y0
1717 self.sim_x0 = sim_x0
1718 self.sim_y0 = sim_y0
1719 self.motion_signal =
None
1721 def begin_node_drag(self, node, event):
1722 self.simulation.lock.acquire()
1724 ns3_node = ns.NodeList.GetNode(node.node_index)
1725 mob = ns3_node.GetObject[ns.MobilityModel]()
1728 if self.node_drag_state
is not None:
1730 pos = ns3_node.GetObject[ns.MobilityModel]().__deref__().GetPosition()
1732 self.simulation.lock.release()
1733 devpos = self.canvas.get_window().get_device_position(event.device)
1734 x0, y0 = self.canvas.convert_from_pixels(devpos.x, devpos.y)
1735 self.node_drag_state = self.NodeDragState(x0, y0, pos.x, pos.y)
1736 self.node_drag_state.motion_signal = node.canvas_item.connect(
1737 "motion-notify-event", self.node_drag_motion, node
1740 def node_drag_motion(self, item, targe_item, event, node):
1741 self.simulation.lock.acquire()
1743 ns3_node = ns.NodeList.GetNode(node.node_index)
1744 mob = ns3_node.GetObject[ns.MobilityModel]()
1747 if self.node_drag_state
is None:
1749 devpos = self.canvas.get_window().get_device_position(event.device)
1750 canvas_x, canvas_y = self.canvas.convert_from_pixels(devpos.x, devpos.y)
1751 dx = canvas_x - self.node_drag_state.canvas_x0
1752 dy = canvas_y - self.node_drag_state.canvas_y0
1753 pos = mob.GetPosition()
1754 pos.x = self.node_drag_state.sim_x0 + transform_distance_canvas_to_simulation(dx)
1755 pos.y = self.node_drag_state.sim_y0 + transform_distance_canvas_to_simulation(dy)
1757 mob.SetPosition(pos)
1758 node.set_position(*transform_point_simulation_to_canvas(pos.x, pos.y))
1760 self.simulation.lock.release()
1763 def end_node_drag(self, node):
1764 if self.node_drag_state
is None:
1766 node.canvas_item.disconnect(self.node_drag_state.motion_signal)
1767 self.node_drag_state =
None
1769 def popup_node_menu(self, node, event):
1771 self.emit(
"populate-node-menu", node, menu)
1772 menu.popup_at_pointer(event)
1774 def _update_ipython_selected_node(self):
1783 if self.selected_node
is None:
1786 self.simulation.lock.acquire()
1788 ns3_node = ns.NodeList.GetNode(self.selected_node.node_index)
1790 self.simulation.lock.release()
1791 self.ipython.updateNamespace({
"selected_node": ns3_node})
1793 def select_node(self, node):
1794 if isinstance(node, ns.Node):
1795 node = self.nodes[node.GetId()]
1796 elif isinstance(node, int):
1797 node = self.nodes[node]
1798 elif isinstance(node, Node):
1803 raise TypeError(
"expected None, int, viz.Node or ns.Node, not %r" % node)
1805 if node
is self.selected_node:
1808 if self.selected_node
is not None:
1809 self.selected_node.selected =
False
1810 self.selected_node = node
1811 if self.selected_node
is not None:
1812 self.selected_node.selected =
True
1814 if self._show_transmissions_mode == ShowTransmissionsMode.SELECTED:
1815 if self.selected_node
is None:
1816 self.simulation.set_nodes_of_interest([])
1818 self.simulation.set_nodes_of_interest([self.selected_node.node_index])
1820 self._update_ipython_selected_node()
1822 def add_information_window(self, info_win):
1823 self.information_windows.append(info_win)
1824 self.simulation.lock.acquire()
1828 self.simulation.lock.release()
1830 def remove_information_window(self, info_win):
1831 self.information_windows.remove(info_win)
1833 def _canvas_tooltip_cb(self, canvas, x, y, keyboard_mode, tooltip):
1835 hadj = self._scrolled_window.get_hadjustment()
1836 vadj = self._scrolled_window.get_vadjustment()
1837 x, y = self.canvas.convert_from_pixels(hadj.get_value() + x, vadj.get_value() + y)
1838 item = self.canvas.get_item_at(x, y,
True)
1842 while item
is not None:
1843 obj = getattr(item,
"pyviz_object",
None)
1845 obj.tooltip_query(tooltip)
1847 item = item.props.parent
1850 def _get_export_file_name(self):
1851 sel = Gtk.FileChooserNative.new(
1852 "Save...", self.canvas.get_toplevel(), Gtk.FileChooserAction.SAVE,
"_Save",
"_Cancel"
1854 sel.set_local_only(
True)
1855 sel.set_do_overwrite_confirmation(
True)
1856 sel.set_current_name(
"Unnamed.pdf")
1858 filter = Gtk.FileFilter()
1859 filter.set_name(
"Embedded PostScript")
1860 filter.add_mime_type(
"image/x-eps")
1861 sel.add_filter(filter)
1863 filter = Gtk.FileFilter()
1864 filter.set_name(
"Portable Document Graphics")
1865 filter.add_mime_type(
"application/pdf")
1866 sel.add_filter(filter)
1868 filter = Gtk.FileFilter()
1869 filter.set_name(
"Scalable Vector Graphics")
1870 filter.add_mime_type(
"image/svg+xml")
1871 sel.add_filter(filter)
1874 if resp != Gtk.ResponseType.ACCEPT:
1878 file_name = sel.get_filename()
1882 def _take_screenshot(self, dummy_button):
1884 file_name = self._get_export_file_name()
1885 if file_name
is None:
1889 x1 = self._scrolled_window.get_hadjustment().get_value()
1890 y1 = self._scrolled_window.get_vadjustment().get_value()
1891 x2 = x1 + self._scrolled_window.get_hadjustment().get_page_size()
1892 y2 = y1 + self._scrolled_window.get_vadjustment().get_page_size()
1893 bounds = GooCanvas.CanvasBounds()
1894 bounds.x1, bounds.y1 = self.canvas.convert_from_pixels(x1, y1)
1895 bounds.x2, bounds.y2 = self.canvas.convert_from_pixels(x2, y2)
1896 dest_width = bounds.x2 - bounds.x1
1897 dest_height = bounds.y2 - bounds.y1
1900 dummy, extension = os.path.splitext(file_name)
1901 extension = extension.lower()
1902 if extension ==
".eps":
1903 surface = cairo.PSSurface(file_name, dest_width, dest_height)
1904 elif extension ==
".pdf":
1905 surface = cairo.PDFSurface(file_name, dest_width, dest_height)
1906 elif extension ==
".svg":
1907 surface = cairo.SVGSurface(file_name, dest_width, dest_height)
1909 dialog = Gtk.MessageDialog(
1910 parent=self.canvas.get_toplevel(),
1911 flags=Gtk.DialogFlags.DESTROY_WITH_PARENT,
1912 type=Gtk.MessageType.ERROR,
1913 buttons=Gtk.ButtonsType.OK,
1914 message_format=
"Unknown extension '%s' (valid extensions are '.eps', '.svg', and '.pdf')"
1922 cr = cairo.Context(surface)
1923 cr.translate(-bounds.x1, -bounds.y1)
1924 self.canvas.render(cr, bounds, self.zoom.get_value())
1928 def set_follow_node(self, node):
1929 if isinstance(node, ns.Node):
1930 node = self.nodes[node.GetId()]
1931 self.follow_node = node
1933 def _start_shell(self, dummy_button):
1934 if self.shell_window
is not None:
1935 self.shell_window.present()
1938 self.shell_window = Gtk.Window()
1939 self.shell_window.set_size_request(750, 550)
1940 self.shell_window.set_resizable(
True)
1941 scrolled_window = Gtk.ScrolledWindow()
1942 scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
1944 self.ipython.modify_font(Pango.FontDescription(SHELL_FONT))
1945 self.ipython.set_wrap_mode(Gtk.WrapMode.CHAR)
1947 scrolled_window.add(self.ipython)
1948 scrolled_window.show()
1949 self.shell_window.add(scrolled_window)
1950 self.shell_window.show()
1951 self.shell_window.connect(
"destroy", self._on_shell_window_destroy)
1953 self._update_ipython_selected_node()
1954 self.ipython.updateNamespace({
"viz": self})
1956 def _on_shell_window_destroy(self, window):
1957 self.shell_window =
None
1960initialization_hooks = []
1963def add_initialization_hook(hook, *args):
1965 Adds a callback to be called after
1966 the visualizer is initialized, like this::
1967 initialization_hook(visualizer, *args)
1969 global initialization_hooks
1970 initialization_hooks.append((hook, args))
1973def set_bounds(x1, y1, x2, y2):
1978 cx1, cy1 = transform_point_simulation_to_canvas(x1, y1)
1979 cx2, cy2 = transform_point_simulation_to_canvas(x2, y2)
1980 viz.canvas.set_bounds(cx1, cy1, cx2, cy2)
1982 add_initialization_hook(hook)
1993 assert Visualizer.INSTANCE
is None
1994 if _import_error
is not None:
1997 print(
"No visualization support (%s)." % (str(_import_error),), file=sys.stderr)
2002 for hook, args
in initialization_hooks:
2003 GLib.idle_add(hook, viz, *args)
2004 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.
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
property highlighted
highlighted property
def remove_link(self, link)
Remove link function.
def on_leave_notify_event(self, view, target, event)
On Leave event handle.
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 on_enter_notify_event(self, view, target, event)
On Enter event handle.
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.