2 from ctypes
import c_double
4 LAYOUT_ALGORITHM =
"neato"
5 REPRESENT_CHANNELS_AS_NODES = 1
6 DEFAULT_NODE_SIZE = 1.0
7 DEFAULT_TRANSMISSIONS_MEMORY = (
10 BITRATE_FONT_SIZE = 10
14 PRIORITY_UPDATE_MODEL = -100
15 PRIORITY_UPDATE_VIEW = 200
20 if 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
65 except 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,
90 PI_OVER_2 = math.pi / 2
91 PI_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_itemsvg_item.props.pointer_events = GooCanvas.CanvasPointerEvents.NONE
202 self.
svg_itemsvg_item.props.visibility = GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD
203 if width
is not None:
205 if height
is not None:
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.
241 self.
svg_itemsvg_item.set_properties(
249 @param self: class object.
250 @param tooltip: tooltip
253 self.
visualizervisualizer.simulation.lock.acquire()
255 ns3_node = ns.NodeList.GetNode(self.
node_indexnode_index)
256 ipv4 = ns.cppyy.gbl.getNodeIpv4(ns3_node)
257 ipv6 = ns.cppyy.gbl.getNodeIpv6(ns3_node)
259 name =
"<b><u>Node %i</u></b>" % self.
node_indexnode_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 = ns.cppyy.gbl.hasMobilityModel(ns3_node)
271 mobility_model_name = ns.cppyy.gbl.getMobilityModelName(ns3_node)
273 " <b>Mobility Model</b>: %s" % ns.cppyy.gbl.getMobilityModelName(ns3_node)
276 for devI
in range(ns3_node.GetNDevices()):
278 lines.append(
" <u>NetDevice %i:</u>" % devI)
279 dev = ns3_node.GetDevice(devI)
280 name = ns.Names.FindName(dev)
282 lines.append(
" <b>Name:</b> %s" % name)
283 devname = dev.GetInstanceTypeId().GetName()
284 lines.append(
" <b>Type:</b> %s" % devname)
287 ipv4_idx = ipv4.GetInterfaceForDevice(dev)
292 ipv4.GetAddress(ipv4_idx, i).GetLocal(),
293 ipv4.GetAddress(ipv4_idx, i).GetMask(),
295 for i
in range(ipv4.GetNAddresses(ipv4_idx))
297 lines.append(
" <b>IPv4 Addresses:</b> %s" %
"; ".join(addresses))
300 ipv6_idx = ipv6.GetInterfaceForDevice(dev)
305 ipv6.GetAddress(ipv6_idx, i).GetAddress(),
306 ipv6.GetAddress(ipv6_idx, i).GetPrefix(),
308 for i
in range(ipv6.GetNAddresses(ipv6_idx))
310 lines.append(
" <b>IPv6 Addresses:</b> %s" %
"; ".join(addresses))
312 lines.append(
" <b>MAC Address:</b> %s" % (dev.GetAddress(),))
314 tooltip.set_markup(
"\n".join(lines))
316 self.
visualizervisualizer.simulation.lock.release()
320 On Enter event handle.
322 @param self: class object.
324 @param target: target
334 On Leave event handle.
336 @param self: class object.
338 @param target: target
346 Set selected function.
348 @param self: class object.
349 @param value: selected value
357 Get selected function.
359 @param self: class object.
360 @return selected status
364 selected = property(_get_selected, _set_selected)
368 Set highlighted function.
370 @param self: class object.
371 @param value: selected value
379 Get highlighted function.
381 @param self: class object.
382 @return highlighted status
386 highlighted = property(_get_highlighted, _set_highlighted)
392 @param self: class object.
393 @param size: selected size
396 self.
_size_size = size
401 Update the node aspect to reflect the selected/highlighted state
403 @param self: class object.
408 if self.
svg_itemsvg_item
is not None:
412 fill_color_rgba = (self.
_color_color & 0xFFFFFF00) | alpha
414 radius_x=size, radius_y=size, fill_color_rgba=fill_color_rgba
417 line_width = size * 0.3
419 line_width = size * 0.15
421 stroke_color =
"yellow"
423 stroke_color =
"black"
424 self.
canvas_itemcanvas_item.set_properties(line_width=line_width, stroke_color=stroke_color)
426 if self.
_label_label
is not None:
429 visibility_threshold=0.5,
430 font=
"Sans Serif 10",
431 fill_color_rgba=0x808080FF,
432 alignment=Pango.Alignment.CENTER,
433 anchor=GooCanvas.CanvasAnchorType.N,
434 parent=self.
visualizervisualizer.canvas.get_root_item(),
435 pointer_events=GooCanvas.CanvasPointerEvents.NONE,
440 visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD, text=self.
_label_label
446 Set position function.
448 @param self: class object.
453 self.
canvas_itemcanvas_item.set_property(
"center_x", x)
454 self.
canvas_itemcanvas_item.set_property(
"center_y", y)
455 if self.
svg_itemsvg_item
is not None:
458 for link
in self.
linkslinks:
467 bounds = self.
visualizervisualizer.canvas.get_bounds()
469 (min_x, min_y, max_x, max_y) = bounds
471 min_x =
min(x, min_x)
472 min_y =
min(y, min_y)
473 max_x =
max(x, max_x)
474 max_y =
max(y, max_y)
476 new_bounds = (min_x, min_y, max_x, max_y)
478 if new_bounds != bounds:
479 self.
visualizervisualizer.canvas.set_bounds(*new_bounds)
486 Get position function.
488 @param self: class object.
489 @return x and y position
492 self.
canvas_itemcanvas_item.get_property(
"center_x"),
493 self.
canvas_itemcanvas_item.get_property(
"center_y"),
498 Update position function.
500 @param self: class object.
510 @param self: class object.
511 @param color: color to set.
514 if isinstance(color, str):
515 color = Gdk.color_parse(color)
517 ((color.red >> 8) << 24)
518 | ((color.green >> 8) << 16)
519 | ((color.blue >> 8) << 8)
529 @param self: class object.
530 @param link: link to add.
533 assert isinstance(link, Link)
534 self.
linkslinks.append(link)
538 Remove link function.
540 @param self: class object.
541 @param link: link to add.
544 assert isinstance(link, Link)
545 self.
linkslinks.remove(link)
550 Has mobility function.
552 @param self: class object.
553 @return modility option
556 node = ns.NodeList.GetNode(self.
node_indexnode_index)
557 self.
_has_mobility_has_mobility = ns.cppyy.gbl.hasMobilityModel(node)
572 Initializer function.
574 @param self: class object.
575 @param channel: channel.
584 line_dash=GooCanvas.CanvasLineDash.newv([10.0, 10.0]),
585 visibility=GooCanvas.CanvasItemVisibility.VISIBLE,
592 Initializer function.
594 @param self: class object.
595 @param x: x position.
596 @param y: y position.
599 self.
canvas_itemcanvas_item.set_property(
"center_x", x)
600 self.
canvas_itemcanvas_item.set_property(
"center_y", y)
602 for link
in self.
linkslinks:
607 Initializer function.
609 @param self: class object.
610 @return x / y position.
613 self.
canvas_itemcanvas_item.get_property(
"center_x"),
614 self.
canvas_itemcanvas_item.get_property(
"center_y"),
629 Initializer function.
631 @param self: class object.
632 @param node1: class object.
633 @param node2: class object.
635 assert isinstance(node1, Node)
636 assert isinstance(node2, (Node, Channel))
639 self.
canvas_itemcanvas_item = GooCanvas.CanvasPath(line_width=1.0, stroke_color=
"black")
641 self.
node1node1.links.append(self)
642 self.
node2node2.links.append(self)
646 Update points function.
648 @param self: class object.
651 pos1_x, pos1_y = self.
node1node1.get_position()
652 pos2_x, pos2_y = self.
node2node2.get_position()
653 self.
canvas_itemcanvas_item.set_property(
"data",
"M %r %r L %r %r" % (pos1_x, pos1_y, pos2_x, pos2_y))
674 Initializer function.
676 @param self: class object.
677 @param viz: class object.
679 super(SimulationThread, self).
__init__()
680 assert isinstance(viz, Visualizer)
682 self.
locklock = threading.Lock()
683 self.
gogo = threading.Event()
692 Set nodes of interest function.
694 @param self: class object.
695 @param nodes: class object.
698 self.
locklock.acquire()
700 self.
sim_helpersim_helper.SetNodesOfInterest(nodes)
706 Initializer function.
708 @param self: class object.
711 while not self.
quitquit:
719 self.
locklock.acquire()
723 self.
vizviz.play_button.set_sensitive(
False)
731 GLib.idle_add(self.
vizviz.update_model, priority=PRIORITY_UPDATE_MODEL)
762 if _import_error
is None:
765 "populate-node-menu": (
766 GObject.SignalFlags.RUN_LAST,
775 "simulation-periodic-update": (GObject.SignalFlags.RUN_LAST,
None, ()),
777 "topology-scanned": (GObject.SignalFlags.RUN_LAST,
None, ()),
779 "update-view": (GObject.SignalFlags.RUN_LAST,
None, ()),
784 Initializer function.
786 @param self: class object.
789 assert Visualizer.INSTANCE
is None
790 Visualizer.INSTANCE = self
791 super(Visualizer, self).__init__()
796 self.time_label =
None
797 self.play_button =
None
799 self._scrolled_window =
None
801 self.links_group = GooCanvas.CanvasGroup()
802 self.channels_group = GooCanvas.CanvasGroup()
803 self.nodes_group = GooCanvas.CanvasGroup()
805 self._update_timeout_id =
None
807 self.selected_node =
None
809 self.information_windows = []
810 self._transmission_arrows = []
811 self._last_transmissions = []
812 self._drop_arrows = []
813 self._last_drops = []
814 self._show_transmissions_mode =
None
815 self.set_show_transmissions_mode(ShowTransmissionsMode.ALL)
816 self._panning_state =
None
817 self.node_size_adjustment =
None
818 self.transmissions_smoothing_adjustment =
None
819 self.sample_period = SAMPLE_PERIOD
820 self.node_drag_state =
None
821 self.follow_node =
None
822 self.shell_window =
None
826 for plugin
in plugins:
829 def set_show_transmissions_mode(self, mode):
831 Set show transmission mode.
833 @param self: class object.
834 @param mode: mode to set.
837 assert isinstance(mode, ShowTransmissionsMode)
838 self._show_transmissions_mode = mode
839 if self._show_transmissions_mode == ShowTransmissionsMode.ALL:
840 self.simulation.set_nodes_of_interest(
list(range(ns.NodeList.GetNNodes())))
841 elif self._show_transmissions_mode == ShowTransmissionsMode.NONE:
842 self.simulation.set_nodes_of_interest([])
843 elif self._show_transmissions_mode == ShowTransmissionsMode.SELECTED:
844 if self.selected_node
is None:
845 self.simulation.set_nodes_of_interest([])
847 self.simulation.set_nodes_of_interest([self.selected_node.node_index])
849 def _create_advanced_controls(self):
851 Create advanced controls.
853 @param self: class object.
856 expander = Gtk.Expander.new(
"Advanced")
859 main_vbox = GObject.new(Gtk.VBox, border_width=8, visible=
True)
860 expander.add(main_vbox)
862 main_hbox1 = GObject.new(Gtk.HBox, border_width=8, visible=
True)
863 main_vbox.pack_start(main_hbox1,
True,
True, 0)
865 show_transmissions_group = GObject.new(
866 Gtk.HeaderBar, title=
"Show transmissions", visible=
True
868 main_hbox1.pack_start(show_transmissions_group,
False,
False, 8)
870 vbox = Gtk.VBox(homogeneous=
True, spacing=4)
872 show_transmissions_group.add(vbox)
874 all_nodes = Gtk.RadioButton.new(
None)
875 all_nodes.set_label(
"All nodes")
876 all_nodes.set_active(
True)
880 selected_node = Gtk.RadioButton.new_from_widget(all_nodes)
882 selected_node.set_label(
"Selected node")
883 selected_node.set_active(
False)
884 vbox.add(selected_node)
886 no_node = Gtk.RadioButton.new_from_widget(all_nodes)
888 no_node.set_label(
"Disabled")
889 no_node.set_active(
False)
893 if radio.get_active():
894 self.set_show_transmissions_mode(ShowTransmissionsMode.ALL)
896 all_nodes.connect(
"toggled", toggled)
899 if radio.get_active():
900 self.set_show_transmissions_mode(ShowTransmissionsMode.NONE)
902 no_node.connect(
"toggled", toggled)
905 if radio.get_active():
906 self.set_show_transmissions_mode(ShowTransmissionsMode.SELECTED)
908 selected_node.connect(
"toggled", toggled)
911 misc_settings_group = GObject.new(Gtk.HeaderBar, title=
"Misc Settings", visible=
True)
912 main_hbox1.pack_start(misc_settings_group,
False,
False, 8)
913 settings_hbox = GObject.new(Gtk.HBox, border_width=8, visible=
True)
914 misc_settings_group.add(settings_hbox)
917 vbox = GObject.new(Gtk.VBox, border_width=0, visible=
True)
918 scale = GObject.new(Gtk.HScale, visible=
True, digits=2)
919 vbox.pack_start(scale,
True,
True, 0)
920 vbox.pack_start(GObject.new(Gtk.Label, label=
"Node Size", visible=
True),
True,
True, 0)
921 settings_hbox.pack_start(vbox,
False,
False, 6)
922 self.node_size_adjustment = scale.get_adjustment()
924 def node_size_changed(adj):
925 for node
in self.nodes.values():
926 node.set_size(adj.get_value())
928 self.node_size_adjustment.connect(
"value-changed", node_size_changed)
929 self.node_size_adjustment.set_lower(0.01)
930 self.node_size_adjustment.set_upper(20)
931 self.node_size_adjustment.set_step_increment(0.1)
932 self.node_size_adjustment.set_value(DEFAULT_NODE_SIZE)
935 vbox = GObject.new(Gtk.VBox, border_width=0, visible=
True)
936 scale = GObject.new(Gtk.HScale, visible=
True, digits=1)
937 vbox.pack_start(scale,
True,
True, 0)
939 GObject.new(Gtk.Label, label=
"Tx. Smooth Factor (s)", visible=
True),
True,
True, 0
941 settings_hbox.pack_start(vbox,
False,
False, 6)
942 self.transmissions_smoothing_adjustment = scale.get_adjustment()
943 adj = self.transmissions_smoothing_adjustment
946 adj.set_step_increment(0.1)
947 adj.set_value(DEFAULT_TRANSMISSIONS_MEMORY * 0.1)
952 class _PanningState(
object):
955 __slots__ = [
"initial_mouse_pos",
"initial_canvas_pos",
"motion_signal"]
957 def _begin_panning(self, widget, event):
959 Set show trnamission mode.
961 @param self: class object.
962 @param mode: mode to set.
965 display = self.canvas.get_window().get_display()
966 cursor = Gdk.Cursor.new_for_display(display, Gdk.CursorType.FLEUR)
967 self.canvas.get_window().set_cursor(cursor)
968 self._panning_state = self._PanningState()
969 pos = widget.get_window().get_device_position(event.device)
970 self._panning_state.initial_mouse_pos = (pos.x, pos.y)
971 x = self._scrolled_window.get_hadjustment().get_value()
972 y = self._scrolled_window.get_vadjustment().get_value()
973 self._panning_state.initial_canvas_pos = (x, y)
974 self._panning_state.motion_signal = self.canvas.connect(
975 "motion-notify-event", self._panning_motion
978 def _end_panning(self, event):
980 End panning function.
982 @param self: class object.
983 @param event: active event.
986 if self._panning_state
is None:
988 self.canvas.get_window().set_cursor(
None)
989 self.canvas.disconnect(self._panning_state.motion_signal)
990 self._panning_state =
None
992 def _panning_motion(self, widget, event):
994 Panning motion function.
996 @param self: class object.
997 @param widget: widget.
999 @return true if successful
1001 assert self._panning_state
is not None
1003 pos = widget.get_window().get_device_position(event.device)
1006 x, y = event.x, event.y
1008 hadj = self._scrolled_window.get_hadjustment()
1009 vadj = self._scrolled_window.get_vadjustment()
1010 mx0, my0 = self._panning_state.initial_mouse_pos
1011 cx0, cy0 = self._panning_state.initial_canvas_pos
1015 hadj.set_value(cx0 - dx)
1016 vadj.set_value(cy0 - dy)
1019 def _canvas_button_press(self, widget, event):
1020 if event.button == 2:
1021 self._begin_panning(widget, event)
1025 def _canvas_button_release(self, dummy_widget, event):
1026 if event.button == 2:
1027 self._end_panning(event)
1031 def _canvas_scroll_event(self, dummy_widget, event):
1032 if event.direction == Gdk.ScrollDirection.UP:
1033 self.zoom.set_value(self.zoom.get_value() * 1.25)
1035 elif event.direction == Gdk.ScrollDirection.DOWN:
1036 self.zoom.set_value(self.zoom.get_value() / 1.25)
1040 def get_hadjustment(self):
1041 return self._scrolled_window.get_hadjustment()
1043 def get_vadjustment(self):
1044 return self._scrolled_window.get_vadjustment()
1046 def create_gui(self):
1047 self.window = Gtk.Window()
1050 self.window.add(vbox)
1053 self.canvas = GooCanvas.Canvas()
1054 self.canvas.connect_after(
"button-press-event", self._canvas_button_press)
1055 self.canvas.connect_after(
"button-release-event", self._canvas_button_release)
1056 self.canvas.connect(
"scroll-event", self._canvas_scroll_event)
1057 self.canvas.props.has_tooltip =
True
1058 self.canvas.connect(
"query-tooltip", self._canvas_tooltip_cb)
1060 sw = Gtk.ScrolledWindow()
1062 self._scrolled_window = sw
1064 vbox.pack_start(sw,
True,
True, 4)
1065 self.canvas.set_size_request(600, 450)
1066 self.canvas.
set_bounds(-10000, -10000, 10000, 10000)
1067 self.canvas.scroll_to(0, 0)
1069 self.canvas.get_root_item().add_child(self.links_group, -1)
1070 self.links_group.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1072 self.canvas.get_root_item().add_child(self.channels_group, -1)
1073 self.channels_group.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1074 self.channels_group.raise_(self.links_group)
1076 self.canvas.get_root_item().add_child(self.nodes_group, -1)
1077 self.nodes_group.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1078 self.nodes_group.raise_(self.channels_group)
1084 vbox.pack_start(hbox,
False,
False, 4)
1087 zoom_adj = Gtk.Adjustment(
1091 step_increment=0.02,
1095 self.zoom = zoom_adj
1097 def _zoom_changed(adj):
1098 self.canvas.set_scale(adj.get_value())
1100 zoom_adj.connect(
"value-changed", _zoom_changed)
1101 zoom = Gtk.SpinButton.new(zoom_adj, 0.1, 1)
1104 hbox.pack_start(GObject.new(Gtk.Label, label=
" Zoom:", visible=
True),
False,
False, 4)
1105 hbox.pack_start(zoom,
False,
False, 4)
1106 _zoom_changed(zoom_adj)
1109 speed_adj = Gtk.Adjustment(
1110 value=1.0, lower=0.01, upper=10.0, step_increment=0.02, page_increment=1.0, page_size=0
1113 def _speed_changed(adj):
1114 self.speed = adj.get_value()
1115 self.sample_period = SAMPLE_PERIOD * adj.get_value()
1116 self._start_update_timer()
1118 speed_adj.connect(
"value-changed", _speed_changed)
1119 speed = Gtk.SpinButton.new(speed_adj, 1, 0)
1122 hbox.pack_start(GObject.new(Gtk.Label, label=
" Speed:", visible=
True),
False,
False, 4)
1123 hbox.pack_start(speed,
False,
False, 4)
1124 _speed_changed(speed_adj)
1127 self.time_label = GObject.new(Gtk.Label, label=
" Speed:", visible=
True)
1128 self.time_label.set_width_chars(20)
1129 hbox.pack_start(self.time_label,
False,
False, 4)
1132 screenshot_button = GObject.new(
1135 relief=Gtk.ReliefStyle.NONE,
1136 focus_on_click=
False,
1139 hbox.pack_start(screenshot_button,
False,
False, 4)
1141 def load_button_icon(button, icon_name):
1142 if not Gtk.IconTheme.get_default().has_icon(icon_name):
1143 print(f
"Could not load icon {icon_name}", file=sys.stderr)
1145 image = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.BUTTON)
1146 button.set_image(image)
1147 button.props.always_show_image =
True
1149 load_button_icon(screenshot_button,
"applets-screenshooter")
1150 screenshot_button.connect(
"clicked", self._take_screenshot)
1153 if ipython_view
is not None:
1154 shell_button = GObject.new(
1157 relief=Gtk.ReliefStyle.NONE,
1158 focus_on_click=
False,
1161 hbox.pack_start(shell_button,
False,
False, 4)
1162 load_button_icon(shell_button,
"gnome-terminal")
1163 shell_button.connect(
"clicked", self._start_shell)
1166 self.play_button = GObject.new(
1168 label=
"Simulate (F3)",
1169 relief=Gtk.ReliefStyle.NONE,
1170 focus_on_click=
False,
1173 load_button_icon(self.play_button,
"media-playback-start")
1174 accel_group = Gtk.AccelGroup()
1175 self.window.add_accel_group(accel_group)
1176 self.play_button.add_accelerator(
1177 "clicked", accel_group, Gdk.KEY_F3, 0, Gtk.AccelFlags.VISIBLE
1179 self.play_button.connect(
"toggled", self._on_play_button_toggled)
1180 hbox.pack_start(self.play_button,
False,
False, 4)
1182 self.canvas.get_root_item().connect(
"button-press-event", self.on_root_button_press_event)
1184 vbox.pack_start(self._create_advanced_controls(),
False,
False, 4)
1186 display = Gdk.Display.get_default()
1188 monitor = display.get_primary_monitor()
1189 geometry = monitor.get_geometry()
1190 scale_factor = monitor.get_scale_factor()
1191 except AttributeError:
1192 screen = display.get_default_screen()
1193 monitor_id = screen.get_primary_monitor()
1194 geometry = screen.get_monitor_geometry(monitor_id)
1195 scale_factor = screen.get_monitor_scale_factor(monitor_id)
1196 width = scale_factor * geometry.width
1197 height = scale_factor * geometry.height
1198 self.window.set_default_size(width * 2 / 3, height * 2 / 3)
1201 def scan_topology(self):
1202 print(
"scanning topology: %i nodes..." % (ns.NodeList.GetNNodes(),))
1203 graph = pygraphviz.AGraph()
1205 for nodeI
in range(ns.NodeList.GetNNodes()):
1207 if seen_nodes == 100:
1209 "scan topology... %i nodes visited (%.1f%%)"
1210 % (nodeI, 100 * nodeI / ns.NodeList.GetNNodes())
1213 node = ns.NodeList.GetNode(nodeI)
1214 node_name =
"Node %i" % nodeI
1215 node_view = self.get_node(nodeI)
1217 mobility = ns.cppyy.gbl.hasMobilityModel(node)
1219 node_view.set_color(
"red")
1220 pos = ns.cppyy.gbl.getNodePosition(node)
1224 graph.add_node(node_name)
1226 for devI
in range(node.GetNDevices()):
1227 device = node.GetDevice(devI)
1229 if device_traits.is_wireless:
1231 if device_traits.is_virtual:
1233 channel = device.GetChannel()
1234 if channel.GetNDevices() > 2:
1235 if REPRESENT_CHANNELS_AS_NODES:
1237 if mobility
is None:
1238 channel_name =
"Channel %s" % id(channel)
1239 graph.add_edge(node_name, channel_name)
1240 self.get_channel(channel)
1241 self.create_link(self.get_node(nodeI), self.get_channel(channel))
1244 for otherDevI
in range(channel.GetNDevices()):
1245 otherDev = channel.GetDevice(otherDevI)
1246 otherNode = otherDev.GetNode()
1247 otherNodeView = self.get_node(otherNode.GetId())
1248 if otherNode
is not node:
1249 if mobility
is None and not otherNodeView.has_mobility:
1250 other_node_name =
"Node %i" % otherNode.GetId()
1251 graph.add_edge(node_name, other_node_name)
1252 self.create_link(self.get_node(nodeI), otherNodeView)
1254 for otherDevI
in range(channel.GetNDevices()):
1255 otherDev = channel.GetDevice(otherDevI)
1256 otherNode = otherDev.GetNode()
1257 otherNodeView = self.get_node(otherNode.GetId())
1258 if otherNode
is not node:
1259 if mobility
is None and not otherNodeView.has_mobility:
1260 other_node_name =
"Node %i" % otherNode.GetId()
1261 graph.add_edge(node_name, other_node_name)
1262 self.create_link(self.get_node(nodeI), otherNodeView)
1264 print(
"scanning topology: calling graphviz layout")
1265 graph.layout(LAYOUT_ALGORITHM)
1266 for node
in graph.iternodes():
1268 node_type, node_id = node.split(
" ")
1269 pos_x, pos_y = [float(s)
for s
in node.attr[
"pos"].split(
",")]
1270 if node_type ==
"Node":
1271 obj = self.nodes[int(node_id)]
1272 elif node_type ==
"Channel":
1273 obj = self.channels[int(node_id)]
1274 obj.set_position(pos_x, pos_y)
1276 print(
"scanning topology: all done.")
1277 self.emit(
"topology-scanned")
1279 def get_node(self, index):
1281 return self.nodes[index]
1283 node =
Node(self, index)
1284 self.nodes[index] = node
1285 self.nodes_group.add_child(node.canvas_item, -1)
1286 node.canvas_item.connect(
"button-press-event", self.on_node_button_press_event, node)
1287 node.canvas_item.connect(
1288 "button-release-event", self.on_node_button_release_event, node
1292 def get_channel(self, ns3_channel):
1294 return self.channels[id(ns3_channel)]
1296 channel =
Channel(ns3_channel)
1297 self.channels[id(ns3_channel)] = channel
1298 self.channels_group.add_child(channel.canvas_item, -1)
1301 def create_link(self, node, node_or_channel):
1303 self.links_group.add_child(link.canvas_item, -1)
1304 link.canvas_item.lower(
None)
1306 def update_view(self):
1309 self.time_label.set_text(
"Time: %f s" % ns.Simulator.Now().GetSeconds())
1311 self._update_node_positions()
1314 for info_win
in self.information_windows:
1317 self._update_transmissions_view()
1318 self._update_drops_view()
1320 self.emit(
"update-view")
1322 def _update_node_positions(self):
1323 for node
in self.nodes.values():
1324 if node.has_mobility:
1325 ns3_node = ns.NodeList.GetNode(node.node_index)
1326 mobility = ns.cppyy.gbl.hasMobilityModel(ns3_node)
1328 pos = ns.cppyy.gbl.getNodePosition(ns3_node)
1330 node.set_position(x, y)
1331 if node
is self.follow_node:
1332 hadj = self._scrolled_window.get_hadjustment()
1333 vadj = self._scrolled_window.get_vadjustment()
1334 px, py = self.canvas.convert_to_pixels(x, y)
1335 hadj.set_value(px - hadj.get_page_size() / 2)
1336 vadj.set_value(py - vadj.get_page_size() / 2)
1338 def center_on_node(self, node):
1339 if isinstance(node, ns.Node):
1340 node = self.nodes[node.GetId()]
1341 elif isinstance(node, int):
1342 node = self.nodes[node]
1343 elif isinstance(node, Node):
1346 raise TypeError(
"expected int, viz.Node or ns.Node, not %r" % node)
1348 x, y = node.get_position()
1349 hadj = self._scrolled_window.get_hadjustment()
1350 vadj = self._scrolled_window.get_vadjustment()
1351 px, py = self.canvas.convert_to_pixels(x, y)
1352 hadj.set_value(px - hadj.get_page_size() / 2)
1353 vadj.set_value(py - vadj.get_page_size() / 2)
1355 def update_model(self):
1356 self.simulation.lock.acquire()
1358 self.emit(
"simulation-periodic-update")
1360 self.simulation.lock.release()
1362 def do_simulation_periodic_update(self):
1363 smooth_factor = int(self.transmissions_smoothing_adjustment.get_value() * 10)
1365 transmissions = self.simulation.sim_helper.GetTransmissionSamples()
1366 self._last_transmissions.append(transmissions)
1367 while len(self._last_transmissions) > smooth_factor:
1368 self._last_transmissions.pop(0)
1370 drops = self.simulation.sim_helper.GetPacketDropSamples()
1371 self._last_drops.append(drops)
1372 while len(self._last_drops) > smooth_factor:
1373 self._last_drops.pop(0)
1375 def _get_label_over_line_position(self, pos1_x, pos1_y, pos2_x, pos2_y):
1376 hadj = self._scrolled_window.get_hadjustment()
1377 vadj = self._scrolled_window.get_vadjustment()
1378 bounds_x1, bounds_y1 = self.canvas.convert_from_pixels(hadj.get_value(), vadj.get_value())
1379 bounds_x2, bounds_y2 = self.canvas.convert_from_pixels(
1380 hadj.get_value() + hadj.get_page_size(), vadj.get_value() + vadj.get_page_size()
1382 ns.PyViz.LineClipping(
1383 bounds_x1, bounds_y1, bounds_x2, bounds_y2, pos1_x, pos1_y, pos2_x, pos2_y
1385 return (pos1_x.value + pos2_x.value) / 2, (pos1_y.value + pos2_y.value) / 2
1387 def _update_transmissions_view(self):
1388 transmissions_average = {}
1389 for transmission_set
in self._last_transmissions:
1390 for transmission
in transmission_set:
1391 key = (transmission.transmitter.GetId(), transmission.receiver.GetId())
1392 rx_bytes, count = transmissions_average.get(key, (0, 0))
1393 rx_bytes += transmission.bytes
1395 transmissions_average[key] = rx_bytes, count
1397 old_arrows = self._transmission_arrows
1398 for arrow, label
in old_arrows:
1399 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1400 label.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1403 k = self.node_size_adjustment.get_value() / 5
1405 for (transmitter_id, receiver_id), (rx_bytes, rx_count)
in transmissions_average.items():
1406 transmitter = self.get_node(transmitter_id)
1407 receiver = self.get_node(receiver_id)
1409 arrow, label = old_arrows.pop()
1411 arrow = GooCanvas.CanvasPolyline(
1413 stroke_color_rgba=0x00C000C0,
1416 pointer_events=GooCanvas.CanvasPointerEvents.NONE,
1418 arrow.set_property(
"parent", self.canvas.get_root_item())
1421 label = GooCanvas.CanvasText(
1422 parent=self.canvas.get_root_item(),
1423 pointer_events=GooCanvas.CanvasPointerEvents.NONE,
1427 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1428 line_width =
max(0.1, math.log(float(rx_bytes) / rx_count / self.sample_period) * k)
1429 arrow.set_property(
"line-width", line_width)
1431 pos1_x, pos1_y = transmitter.get_position()
1432 pos2_x, pos2_y = receiver.get_position()
1433 points = GooCanvas.CanvasPoints.new(2)
1434 points.set_point(0, pos1_x, pos1_y)
1435 points.set_point(1, pos2_x, pos2_y)
1436 arrow.set_property(
"points", points)
1438 kbps = float(rx_bytes * 8) / 1e3 / rx_count / self.sample_period
1439 label.set_properties(
1440 visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
1441 visibility_threshold=0.5,
1442 font=(
"Sans Serif %f" % int(1 + BITRATE_FONT_SIZE * k)),
1444 angle = math.atan2((pos2_y - pos1_y), (pos2_x - pos1_x))
1445 if -PI_OVER_2 <= angle <= PI_OVER_2:
1446 label.set_properties(
1447 text=(
"%.2f kbit/s →" % (kbps,)),
1448 alignment=Pango.Alignment.CENTER,
1449 anchor=GooCanvas.CanvasAnchorType.S,
1454 label.set_properties(
1455 text=(
"← %.2f kbit/s" % (kbps,)),
1456 alignment=Pango.Alignment.CENTER,
1457 anchor=GooCanvas.CanvasAnchorType.N,
1462 lx, ly = self._get_label_over_line_position(
1463 c_double(pos1_x), c_double(pos1_y), c_double(pos2_x), c_double(pos2_y)
1468 label.set_transform(M)
1472 "PyGobject bug causing label position error; "
1473 "should be fixed in PyGObject >= 3.29.1"
1475 label.set_properties(x=(lx + label.props.x), y=(ly + label.props.y))
1477 new_arrows.append((arrow, label))
1479 self._transmission_arrows = new_arrows + old_arrows
1481 def _update_drops_view(self):
1483 for drop_set
in self._last_drops:
1484 for drop
in drop_set:
1485 key = drop.transmitter.GetId()
1486 drop_bytes, count = drops_average.get(key, (0, 0))
1487 drop_bytes += drop.bytes
1489 drops_average[key] = drop_bytes, count
1491 old_arrows = self._drop_arrows
1492 for arrow, label
in old_arrows:
1493 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1494 label.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1498 vadjustment = self._scrolled_window.get_vadjustment()
1499 bottom_y = vadjustment.get_value() + vadjustment.get_page_size()
1500 dummy, edge_y = self.canvas.convert_from_pixels(0, bottom_y)
1502 k = self.node_size_adjustment.get_value() / 5
1504 for transmitter_id, (drop_bytes, drop_count)
in drops_average.items():
1505 transmitter = self.get_node(transmitter_id)
1507 arrow, label = old_arrows.pop()
1509 arrow = GooCanvas.CanvasPolyline(
1511 stroke_color_rgba=0xC00000C0,
1514 pointer_events=GooCanvas.CanvasPointerEvents.NONE,
1516 arrow.set_property(
"parent", self.canvas.get_root_item())
1519 label = GooCanvas.CanvasText(
1520 pointer_events=GooCanvas.CanvasPointerEvents.NONE
1522 label.set_property(
"parent", self.canvas.get_root_item())
1525 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1528 max(0.1, math.log(float(drop_bytes) / drop_count / self.sample_period) * k),
1530 pos1_x, pos1_y = transmitter.get_position()
1531 pos2_x, pos2_y = pos1_x, edge_y
1532 points = GooCanvas.CanvasPoints.new(2)
1533 points.set_point(0, pos1_x, pos1_y)
1534 points.set_point(1, pos2_x, pos2_y)
1535 arrow.set_property(
"points", points)
1537 label.set_properties(
1538 visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
1539 visibility_threshold=0.5,
1540 font=(
"Sans Serif %i" % int(1 + BITRATE_FONT_SIZE * k)),
1542 "%.2f kbit/s" % (float(drop_bytes * 8) / 1e3 / drop_count / self.sample_period,)
1544 alignment=Pango.Alignment.CENTER,
1545 x=(pos1_x + pos2_x) / 2,
1546 y=(pos1_y + pos2_y) / 2,
1549 new_arrows.append((arrow, label))
1551 self._drop_arrows = new_arrows + old_arrows
1553 def update_view_timeout(self):
1557 while not self.simulation.lock.acquire(
False):
1558 while Gtk.events_pending():
1559 Gtk.main_iteration()
1560 pause_messages = self.simulation.pause_messages
1561 self.simulation.pause_messages = []
1564 self.simulation.target_time = ns.Simulator.Now().GetSeconds() + self.sample_period
1567 self.simulation.lock.release()
1571 dialog = Gtk.MessageDialog(
1574 type=Gtk.MessageType.WARNING,
1575 buttons=Gtk.ButtonsType.OK,
1576 message_format=
"\n".join(pause_messages),
1578 dialog.connect(
"response",
lambda d, r: d.destroy())
1580 self.play_button.set_active(
False)
1583 if not self.play_button.get_active():
1584 self._update_timeout_id =
None
1588 self.simulation.go.set()
1592 def _start_update_timer(self):
1593 if self._update_timeout_id
is not None:
1594 GLib.source_remove(self._update_timeout_id)
1596 self._update_timeout_id = GLib.timeout_add(
1597 int(SAMPLE_PERIOD /
min(self.speed, 1) * 1e3),
1598 self.update_view_timeout,
1599 priority=PRIORITY_UPDATE_VIEW,
1602 def _on_play_button_toggled(self, button):
1603 if button.get_active():
1604 self._start_update_timer()
1606 if self._update_timeout_id
is not None:
1607 GLib.source_remove(self._update_timeout_id)
1609 def _quit(self, *dummy_args):
1610 if self._update_timeout_id
is not None:
1611 GLib.source_remove(self._update_timeout_id)
1612 self._update_timeout_id =
None
1613 self.simulation.quit =
True
1614 self.simulation.go.set()
1615 self.simulation.join()
1618 def _monkey_patch_ipython(self):
1625 original_runcode = self.ipython.runcode
1627 def runcode(ip, *args):
1629 self.simulation.lock.acquire()
1631 return original_runcode(*args)
1634 self.simulation.lock.release()
1638 self.ipython.runcode = types.MethodType(runcode, self.ipython)
1640 def autoscale_view(self):
1643 self._update_node_positions()
1644 positions = [node.get_position()
for node
in self.nodes.values()]
1645 min_x, min_y =
min(x
for (x, y)
in positions),
min(y
for (x, y)
in positions)
1646 max_x, max_y =
max(x
for (x, y)
in positions),
max(y
for (x, y)
in positions)
1647 min_x_px, min_y_px = self.canvas.convert_to_pixels(min_x, min_y)
1648 max_x_px, max_y_px = self.canvas.convert_to_pixels(max_x, max_y)
1651 dx_px = max_x_px - min_x_px
1652 dy_px = max_y_px - min_y_px
1653 hadj = self._scrolled_window.get_hadjustment()
1654 vadj = self._scrolled_window.get_vadjustment()
1655 new_dx, new_dy = 1.5 * dx_px, 1.5 * dy_px
1657 if new_dx == 0
or new_dy == 0:
1660 self.zoom.set_value(
min(hadj.get_page_size() / new_dx, vadj.get_page_size() / new_dy))
1662 x1, y1 = self.canvas.convert_from_pixels(hadj.get_value(), vadj.get_value())
1663 x2, y2 = self.canvas.convert_from_pixels(
1664 (hadj.get_value() + hadj.get_page_size()), (vadj.get_value() + vadj.get_page_size())
1668 center_x = (min_x + max_x) / 2
1669 center_y = (min_y + max_y) / 2
1671 self.canvas.scroll_to(center_x - width / 2, center_y - height / 2)
1676 self.scan_topology()
1677 self.window.connect(
"delete-event", self._quit)
1679 GLib.timeout_add(200, self.autoscale_view)
1680 self.simulation.
start()
1687 self._monkey_patch_ipython()
1691 def on_root_button_press_event(self, view, target, event):
1692 if event.button == 1:
1693 self.select_node(
None)
1696 def on_node_button_press_event(self, view, target, event, node):
1697 button = event.button
1699 self.select_node(node)
1702 self.popup_node_menu(node, event)
1705 self.begin_node_drag(node, event)
1709 def on_node_button_release_event(self, view, target, event, node):
1710 if event.button == 2:
1711 self.end_node_drag(node)
1715 class NodeDragState(
object):
1716 def __init__(self, canvas_x0, canvas_y0, sim_x0, sim_y0):
1717 self.canvas_x0 = canvas_x0
1718 self.canvas_y0 = canvas_y0
1719 self.sim_x0 = sim_x0
1720 self.sim_y0 = sim_y0
1721 self.motion_signal =
None
1723 def begin_node_drag(self, node, event):
1724 self.simulation.lock.acquire()
1726 ns3_node = ns.NodeList.GetNode(node.node_index)
1727 mob = ns.cppyy.gbl.hasMobilityModel(ns3_node)
1730 if self.node_drag_state
is not None:
1732 pos = ns.cppyy.gbl.getNodePosition(ns3_node)
1734 self.simulation.lock.release()
1735 devpos = self.canvas.get_window().get_device_position(event.device)
1736 x0, y0 = self.canvas.convert_from_pixels(devpos.x, devpos.y)
1737 self.node_drag_state = self.NodeDragState(x0, y0, pos.x, pos.y)
1738 self.node_drag_state.motion_signal = node.canvas_item.connect(
1739 "motion-notify-event", self.node_drag_motion, node
1742 def node_drag_motion(self, item, targe_item, event, node):
1743 self.simulation.lock.acquire()
1745 ns3_node = ns.NodeList.GetNode(node.node_index)
1746 mob = ns.cppyy.gbl.hasMobilityModel(ns3_node)
1749 if self.node_drag_state
is None:
1751 devpos = self.canvas.get_window().get_device_position(event.device)
1752 canvas_x, canvas_y = self.canvas.convert_from_pixels(devpos.x, devpos.y)
1753 dx = canvas_x - self.node_drag_state.canvas_x0
1754 dy = canvas_y - self.node_drag_state.canvas_y0
1755 pos = mob.GetPosition()
1759 mob.SetPosition(pos)
1762 self.simulation.lock.release()
1765 def end_node_drag(self, node):
1766 if self.node_drag_state
is None:
1768 node.canvas_item.disconnect(self.node_drag_state.motion_signal)
1769 self.node_drag_state =
None
1771 def popup_node_menu(self, node, event):
1773 self.emit(
"populate-node-menu", node, menu)
1774 menu.popup_at_pointer(event)
1776 def _update_ipython_selected_node(self):
1785 if self.selected_node
is None:
1788 self.simulation.lock.acquire()
1790 ns3_node = ns.NodeList.GetNode(self.selected_node.node_index)
1792 self.simulation.lock.release()
1793 self.ipython.updateNamespace({
"selected_node": ns3_node})
1795 def select_node(self, node):
1796 if isinstance(node, ns.Node):
1797 node = self.nodes[node.GetId()]
1798 elif isinstance(node, int):
1799 node = self.nodes[node]
1800 elif isinstance(node, Node):
1805 raise TypeError(
"expected None, int, viz.Node or ns.Node, not %r" % node)
1807 if node
is self.selected_node:
1810 if self.selected_node
is not None:
1811 self.selected_node.selected =
False
1812 self.selected_node = node
1813 if self.selected_node
is not None:
1814 self.selected_node.selected =
True
1816 if self._show_transmissions_mode == ShowTransmissionsMode.SELECTED:
1817 if self.selected_node
is None:
1818 self.simulation.set_nodes_of_interest([])
1820 self.simulation.set_nodes_of_interest([self.selected_node.node_index])
1822 self._update_ipython_selected_node()
1824 def add_information_window(self, info_win):
1825 self.information_windows.append(info_win)
1826 self.simulation.lock.acquire()
1830 self.simulation.lock.release()
1832 def remove_information_window(self, info_win):
1833 self.information_windows.remove(info_win)
1835 def _canvas_tooltip_cb(self, canvas, x, y, keyboard_mode, tooltip):
1837 hadj = self._scrolled_window.get_hadjustment()
1838 vadj = self._scrolled_window.get_vadjustment()
1839 x, y = self.canvas.convert_from_pixels(hadj.get_value() + x, vadj.get_value() + y)
1840 item = self.canvas.get_item_at(x, y,
True)
1844 while item
is not None:
1845 obj = getattr(item,
"pyviz_object",
None)
1847 obj.tooltip_query(tooltip)
1849 item = item.props.parent
1852 def _get_export_file_name(self):
1853 sel = Gtk.FileChooserNative.new(
1854 "Save...", self.canvas.get_toplevel(), Gtk.FileChooserAction.SAVE,
"_Save",
"_Cancel"
1856 sel.set_local_only(
True)
1857 sel.set_do_overwrite_confirmation(
True)
1858 sel.set_current_name(
"Unnamed.pdf")
1860 filter = Gtk.FileFilter()
1861 filter.set_name(
"Embedded PostScript")
1862 filter.add_mime_type(
"image/x-eps")
1863 sel.add_filter(filter)
1865 filter = Gtk.FileFilter()
1866 filter.set_name(
"Portable Document Graphics")
1867 filter.add_mime_type(
"application/pdf")
1868 sel.add_filter(filter)
1870 filter = Gtk.FileFilter()
1871 filter.set_name(
"Scalable Vector Graphics")
1872 filter.add_mime_type(
"image/svg+xml")
1873 sel.add_filter(filter)
1876 if resp != Gtk.ResponseType.ACCEPT:
1880 file_name = sel.get_filename()
1884 def _take_screenshot(self, dummy_button):
1886 file_name = self._get_export_file_name()
1887 if file_name
is None:
1891 x1 = self._scrolled_window.get_hadjustment().get_value()
1892 y1 = self._scrolled_window.get_vadjustment().get_value()
1893 x2 = x1 + self._scrolled_window.get_hadjustment().get_page_size()
1894 y2 = y1 + self._scrolled_window.get_vadjustment().get_page_size()
1895 bounds = GooCanvas.CanvasBounds()
1896 bounds.x1, bounds.y1 = self.canvas.convert_from_pixels(x1, y1)
1897 bounds.x2, bounds.y2 = self.canvas.convert_from_pixels(x2, y2)
1898 dest_width = bounds.x2 - bounds.x1
1899 dest_height = bounds.y2 - bounds.y1
1902 dummy, extension = os.path.splitext(file_name)
1903 extension = extension.lower()
1904 if extension ==
".eps":
1905 surface = cairo.PSSurface(file_name, dest_width, dest_height)
1906 elif extension ==
".pdf":
1907 surface = cairo.PDFSurface(file_name, dest_width, dest_height)
1908 elif extension ==
".svg":
1909 surface = cairo.SVGSurface(file_name, dest_width, dest_height)
1911 dialog = Gtk.MessageDialog(
1912 parent=self.canvas.get_toplevel(),
1913 flags=Gtk.DialogFlags.DESTROY_WITH_PARENT,
1914 type=Gtk.MessageType.ERROR,
1915 buttons=Gtk.ButtonsType.OK,
1916 message_format=
"Unknown extension '%s' (valid extensions are '.eps', '.svg', and '.pdf')"
1924 cr = cairo.Context(surface)
1925 cr.translate(-bounds.x1, -bounds.y1)
1926 self.canvas.render(cr, bounds, self.zoom.get_value())
1930 def set_follow_node(self, node):
1931 if isinstance(node, ns.Node):
1932 node = self.nodes[node.GetId()]
1933 self.follow_node = node
1935 def _start_shell(self, dummy_button):
1936 if self.shell_window
is not None:
1937 self.shell_window.present()
1940 self.shell_window = Gtk.Window()
1941 self.shell_window.set_size_request(750, 550)
1942 self.shell_window.set_resizable(
True)
1943 scrolled_window = Gtk.ScrolledWindow()
1944 scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
1946 self.ipython.modify_font(Pango.FontDescription(SHELL_FONT))
1947 self.ipython.set_wrap_mode(Gtk.WrapMode.CHAR)
1949 scrolled_window.add(self.ipython)
1950 scrolled_window.show()
1951 self.shell_window.add(scrolled_window)
1952 self.shell_window.show()
1953 self.shell_window.connect(
"destroy", self._on_shell_window_destroy)
1955 self._update_ipython_selected_node()
1956 self.ipython.updateNamespace({
"viz": self})
1958 def _on_shell_window_destroy(self, window):
1959 self.shell_window =
None
1962 initialization_hooks = []
1967 Adds a callback to be called after
1968 the visualizer is initialized, like this::
1969 initialization_hook(visualizer, *args)
1971 global initialization_hooks
1972 initialization_hooks.append((hook, args))
1982 viz.canvas.set_bounds(cx1, cy1, cx2, cy2)
1988 assert Visualizer.INSTANCE
is None
1989 if _import_error
is not None:
1992 print(
"No visualization support (%s)." % (str(_import_error),), file=sys.stderr)
1997 for hook, args
in initialization_hooks:
1998 GLib.idle_add(hook, viz, *args)
1999 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
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.
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)