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 = 5
12 PRIORITY_UPDATE_MODEL = -100
13 PRIORITY_UPDATE_VIEW = 200
17 if 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
66 except ImportError
as e:
72 import ipython_viewxxxxxxxxxx
76 from .base
import InformationWindow, PyVizObject, Link, lookup_netdevice_traits, PIXELS_PER_METER
77 from .base
import transform_distance_simulation_to_canvas, transform_point_simulation_to_canvas
78 from .base
import transform_distance_canvas_to_simulation, transform_point_canvas_to_simulation
79 from .base
import load_plugins, register_plugin, plugins
82 PI_TIMES_2 = math.pi*2
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_itemsvg_item.props.pointer_events = GooCanvas.CanvasPointerEvents.NONE
188 self.
svg_itemsvg_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
239 self.
visualizervisualizer.simulation.lock.acquire()
241 ns3_node = ns.NodeList.GetNode(self.
node_indexnode_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_indexnode_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))
292 self.
visualizervisualizer.simulation.lock.release()
296 On Enter event handle.
298 @param self: class object.
300 @param target: target
310 On Leave event handle.
312 @param self: class object.
314 @param target: target
322 Set selected function.
324 @param self: class object.
325 @param value: selected value
332 Get selected function.
334 @param self: class object.
335 @return selected status
339 selected = property(_get_selected, _set_selected)
343 Set highlighted function.
345 @param self: class object.
346 @param value: selected value
353 Get highlighted function.
355 @param self: class object.
356 @return highlighted status
360 highlighted = property(_get_highlighted, _set_highlighted)
366 @param self: class object.
367 @param size: selected size
370 self.
_size_size = size
375 Update the node aspect to reflect the selected/highlighted state
377 @param self: class object.
382 if self.
svg_itemsvg_item
is not None:
386 fill_color_rgba = (self.
_color_color & 0xffffff00) | alpha
387 self.
canvas_itemcanvas_item.set_properties(radius_x=size, radius_y=size,
388 fill_color_rgba=fill_color_rgba)
392 line_width = size*.15
394 stroke_color =
'yellow'
396 stroke_color =
'black'
397 self.
canvas_itemcanvas_item.set_properties(line_width=line_width, stroke_color=stroke_color)
399 if self.
_label_label
is not None:
401 self.
_label_canvas_item_label_canvas_item = GooCanvas.CanvasText(visibility_threshold=0.5,
402 font=
"Sans Serif 10",
403 fill_color_rgba=0x808080ff,
404 alignment=Pango.Alignment.CENTER,
405 anchor=GooCanvas.CanvasAnchorType.N,
406 parent=self.
visualizervisualizer.canvas.get_root_item(),
407 pointer_events=GooCanvas.CanvasPointerEvents.NONE)
410 self.
_label_canvas_item_label_canvas_item.set_properties(visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
416 Set position function.
418 @param self: class object.
423 self.
canvas_itemcanvas_item.set_property(
"center_x", x)
424 self.
canvas_itemcanvas_item.set_property(
"center_y", y)
425 if self.
svg_itemsvg_item
is not None:
428 for link
in self.
linkslinks:
437 bounds = self.
visualizervisualizer.canvas.get_bounds()
439 (min_x, min_y, max_x, max_y) = bounds
441 min_x =
min(x, min_x)
442 min_y =
min(y, min_y)
443 max_x =
max(x, max_x)
444 max_y =
max(y, max_y)
446 new_bounds = (min_x, min_y, max_x, max_y)
448 if new_bounds != bounds:
449 self.
visualizervisualizer.canvas.set_bounds(*new_bounds)
456 Get position function.
458 @param self: class object.
459 @return x and y position
461 return (self.
canvas_itemcanvas_item.get_property(
"center_x"), self.
canvas_itemcanvas_item.get_property(
"center_y"))
465 Update position function.
467 @param self: class object.
477 @param self: class object.
478 @param color: color to set.
481 if isinstance(color, str):
482 color = Gdk.color_parse(color)
483 color = ((color.red>>8) << 24) | ((color.green>>8) << 16) | ((color.blue>>8) << 8) | 0xff
491 @param self: class object.
492 @param link: link to add.
495 assert isinstance(link, Link)
496 self.
linkslinks.append(link)
500 Remove link function.
502 @param self: class object.
503 @param link: link to add.
506 assert isinstance(link, Link)
507 self.
linkslinks.remove(link)
512 Has mobility function.
514 @param self: class object.
515 @return modility option
518 node = ns.NodeList.GetNode(self.
node_indexnode_index)
519 self.
_has_mobility_has_mobility = ns.cppyy.gbl.hasMobilityModel(node)
534 Initializer function.
536 @param self: class object.
537 @param channel: channel.
540 self.
canvas_itemcanvas_item = GooCanvas.CanvasEllipse(radius_x=30, radius_y=30,
542 stroke_color=
"grey", line_width=2.0,
543 line_dash=GooCanvas.CanvasLineDash.newv([10.0, 10.0 ]),
544 visibility=GooCanvas.CanvasItemVisibility.VISIBLE)
550 Initializer function.
552 @param self: class object.
553 @param x: x position.
554 @param y: y position.
557 self.
canvas_itemcanvas_item.set_property(
"center_x", x)
558 self.
canvas_itemcanvas_item.set_property(
"center_y", y)
560 for link
in self.
linkslinks:
565 Initializer function.
567 @param self: class object.
568 @return x / y position.
570 return (self.
canvas_itemcanvas_item.get_property(
"center_x"), self.
canvas_itemcanvas_item.get_property(
"center_y"))
584 Initializer function.
586 @param self: class object.
587 @param node1: class object.
588 @param node2: class object.
590 assert isinstance(node1, Node)
591 assert isinstance(node2, (Node, Channel))
594 self.
canvas_itemcanvas_item = GooCanvas.CanvasPath(line_width=1.0, stroke_color=
"black")
596 self.
node1node1.links.append(self)
597 self.
node2node2.links.append(self)
601 Update points function.
603 @param self: class object.
606 pos1_x, pos1_y = self.
node1node1.get_position()
607 pos2_x, pos2_y = self.
node2node2.get_position()
608 self.
canvas_itemcanvas_item.set_property(
"data",
"M %r %r L %r %r" % (pos1_x, pos1_y, pos2_x, pos2_y))
629 Initializer function.
631 @param self: class object.
632 @param viz: class object.
634 super(SimulationThread, self).
__init__()
635 assert isinstance(viz, Visualizer)
637 self.
locklock = threading.Lock()
638 self.
gogo = threading.Event()
647 Set nodes of interest function.
649 @param self: class object.
650 @param nodes: class object.
653 self.
locklock.acquire()
655 self.
sim_helpersim_helper.SetNodesOfInterest(nodes)
661 Initializer function.
663 @param self: class object.
666 while not self.
quitquit:
674 self.
locklock.acquire()
678 self.
vizviz.play_button.set_sensitive(
False)
686 GLib.idle_add(self.
vizviz.update_model, priority=PRIORITY_UPDATE_MODEL)
713 if _import_error
is None:
717 'populate-node-menu': (GObject.SignalFlags.RUN_LAST,
None, (object, Gtk.Menu,)),
721 'simulation-periodic-update': (GObject.SignalFlags.RUN_LAST,
None, ()),
724 'topology-scanned': (GObject.SignalFlags.RUN_LAST,
None, ()),
727 'update-view': (GObject.SignalFlags.RUN_LAST,
None, ()),
733 Initializer function.
735 @param self: class object.
738 assert Visualizer.INSTANCE
is None
739 Visualizer.INSTANCE = self
740 super(Visualizer, self).__init__()
745 self.time_label =
None
746 self.play_button =
None
748 self._scrolled_window =
None
750 self.links_group = GooCanvas.CanvasGroup()
751 self.channels_group = GooCanvas.CanvasGroup()
752 self.nodes_group = GooCanvas.CanvasGroup()
754 self._update_timeout_id =
None
756 self.selected_node =
None
758 self.information_windows = []
759 self._transmission_arrows = []
760 self._last_transmissions = []
761 self._drop_arrows = []
762 self._last_drops = []
763 self._show_transmissions_mode =
None
764 self.set_show_transmissions_mode(ShowTransmissionsMode.ALL)
765 self._panning_state =
None
766 self.node_size_adjustment =
None
767 self.transmissions_smoothing_adjustment =
None
768 self.sample_period = SAMPLE_PERIOD
769 self.node_drag_state =
None
770 self.follow_node =
None
771 self.shell_window =
None
775 for plugin
in plugins:
778 def set_show_transmissions_mode(self, mode):
780 Set show transmission mode.
782 @param self: class object.
783 @param mode: mode to set.
786 assert isinstance(mode, ShowTransmissionsMode)
787 self._show_transmissions_mode = mode
788 if self._show_transmissions_mode == ShowTransmissionsMode.ALL:
789 self.simulation.set_nodes_of_interest(
list(range(ns.NodeList.GetNNodes())))
790 elif self._show_transmissions_mode == ShowTransmissionsMode.NONE:
791 self.simulation.set_nodes_of_interest([])
792 elif self._show_transmissions_mode == ShowTransmissionsMode.SELECTED:
793 if self.selected_node
is None:
794 self.simulation.set_nodes_of_interest([])
796 self.simulation.set_nodes_of_interest([self.selected_node.node_index])
798 def _create_advanced_controls(self):
800 Create advanced controls.
802 @param self: class object.
805 expander = Gtk.Expander.new(
"Advanced")
808 main_vbox = GObject.new(Gtk.VBox, border_width=8, visible=
True)
809 expander.add(main_vbox)
811 main_hbox1 = GObject.new(Gtk.HBox, border_width=8, visible=
True)
812 main_vbox.pack_start(main_hbox1,
True,
True, 0)
814 show_transmissions_group = GObject.new(Gtk.HeaderBar,
815 title=
"Show transmissions",
817 main_hbox1.pack_start(show_transmissions_group,
False,
False, 8)
819 vbox = Gtk.VBox(homogeneous=
True, spacing=4)
821 show_transmissions_group.add(vbox)
823 all_nodes = Gtk.RadioButton.new(
None)
824 all_nodes.set_label(
"All nodes")
825 all_nodes.set_active(
True)
829 selected_node = Gtk.RadioButton.new_from_widget(all_nodes)
831 selected_node.set_label(
"Selected node")
832 selected_node.set_active(
False)
833 vbox.add(selected_node)
835 no_node = Gtk.RadioButton.new_from_widget(all_nodes)
837 no_node.set_label(
"Disabled")
838 no_node.set_active(
False)
842 if radio.get_active():
843 self.set_show_transmissions_mode(ShowTransmissionsMode.ALL)
844 all_nodes.connect(
"toggled", toggled)
847 if radio.get_active():
848 self.set_show_transmissions_mode(ShowTransmissionsMode.NONE)
849 no_node.connect(
"toggled", toggled)
852 if radio.get_active():
853 self.set_show_transmissions_mode(ShowTransmissionsMode.SELECTED)
854 selected_node.connect(
"toggled", toggled)
857 misc_settings_group = GObject.new(Gtk.HeaderBar, title=
"Misc Settings", visible=
True)
858 main_hbox1.pack_start(misc_settings_group,
False,
False, 8)
859 settings_hbox = GObject.new(Gtk.HBox, border_width=8, visible=
True)
860 misc_settings_group.add(settings_hbox)
863 vbox = GObject.new(Gtk.VBox, border_width=0, visible=
True)
864 scale = GObject.new(Gtk.HScale, visible=
True, digits=2)
865 vbox.pack_start(scale,
True,
True, 0)
866 vbox.pack_start(GObject.new(Gtk.Label, label=
"Node Size", visible=
True),
True,
True, 0)
867 settings_hbox.pack_start(vbox,
False,
False, 6)
868 self.node_size_adjustment = scale.get_adjustment()
869 def node_size_changed(adj):
870 for node
in self.nodes.values():
871 node.set_size(adj.get_value())
872 self.node_size_adjustment.connect(
"value-changed", node_size_changed)
873 self.node_size_adjustment.set_lower(0.01)
874 self.node_size_adjustment.set_upper(20)
875 self.node_size_adjustment.set_step_increment(0.1)
876 self.node_size_adjustment.set_value(DEFAULT_NODE_SIZE)
879 vbox = GObject.new(Gtk.VBox, border_width=0, visible=
True)
880 scale = GObject.new(Gtk.HScale, visible=
True, digits=1)
881 vbox.pack_start(scale,
True,
True, 0)
882 vbox.pack_start(GObject.new(Gtk.Label, label=
"Tx. Smooth Factor (s)", visible=
True),
True,
True, 0)
883 settings_hbox.pack_start(vbox,
False,
False, 6)
884 self.transmissions_smoothing_adjustment = scale.get_adjustment()
885 adj = self.transmissions_smoothing_adjustment
888 adj.set_step_increment(0.1)
889 adj.set_value(DEFAULT_TRANSMISSIONS_MEMORY*0.1)
894 class _PanningState(
object):
897 __slots__ = [
'initial_mouse_pos',
'initial_canvas_pos',
'motion_signal']
899 def _begin_panning(self, widget, event):
901 Set show trnamission mode.
903 @param self: class object.
904 @param mode: mode to set.
907 display = self.canvas.get_window().get_display()
908 cursor = Gdk.Cursor.new_for_display(display, Gdk.CursorType.FLEUR)
909 self.canvas.get_window().set_cursor(cursor)
910 self._panning_state = self._PanningState()
911 pos = widget.get_window().get_device_position(event.device)
912 self._panning_state.initial_mouse_pos = (pos.x, pos.y)
913 x = self._scrolled_window.get_hadjustment().get_value()
914 y = self._scrolled_window.get_vadjustment().get_value()
915 self._panning_state.initial_canvas_pos = (x, y)
916 self._panning_state.motion_signal = self.canvas.connect(
"motion-notify-event", self._panning_motion)
918 def _end_panning(self, event):
920 End panning function.
922 @param self: class object.
923 @param event: active event.
926 if self._panning_state
is None:
928 self.canvas.get_window().set_cursor(
None)
929 self.canvas.disconnect(self._panning_state.motion_signal)
930 self._panning_state =
None
932 def _panning_motion(self, widget, event):
934 Panning motion function.
936 @param self: class object.
937 @param widget: widget.
939 @return true if successful
941 assert self._panning_state
is not None
943 pos = widget.get_window().get_device_position(event.device)
946 x, y = event.x, event.y
948 hadj = self._scrolled_window.get_hadjustment()
949 vadj = self._scrolled_window.get_vadjustment()
950 mx0, my0 = self._panning_state.initial_mouse_pos
951 cx0, cy0 = self._panning_state.initial_canvas_pos
955 hadj.set_value(cx0 - dx)
956 vadj.set_value(cy0 - dy)
959 def _canvas_button_press(self, widget, event):
960 if event.button == 2:
961 self._begin_panning(widget, event)
965 def _canvas_button_release(self, dummy_widget, event):
966 if event.button == 2:
967 self._end_panning(event)
971 def _canvas_scroll_event(self, dummy_widget, event):
972 if event.direction == Gdk.ScrollDirection.UP:
973 self.zoom.set_value(self.zoom.get_value() * 1.25)
975 elif event.direction == Gdk.ScrollDirection.DOWN:
976 self.zoom.set_value(self.zoom.get_value() / 1.25)
980 def get_hadjustment(self):
981 return self._scrolled_window.get_hadjustment()
982 def get_vadjustment(self):
983 return self._scrolled_window.get_vadjustment()
985 def create_gui(self):
986 self.window = Gtk.Window()
989 self.window.add(vbox)
992 self.canvas = GooCanvas.Canvas()
993 self.canvas.connect_after(
"button-press-event", self._canvas_button_press)
994 self.canvas.connect_after(
"button-release-event", self._canvas_button_release)
995 self.canvas.connect(
"scroll-event", self._canvas_scroll_event)
996 self.canvas.props.has_tooltip =
True
997 self.canvas.connect(
"query-tooltip", self._canvas_tooltip_cb)
999 sw = Gtk.ScrolledWindow(); sw.show()
1000 self._scrolled_window = sw
1002 vbox.pack_start(sw,
True,
True, 4)
1003 self.canvas.set_size_request(600, 450)
1004 self.canvas.
set_bounds(-10000, -10000, 10000, 10000)
1005 self.canvas.scroll_to(0, 0)
1008 self.canvas.get_root_item().add_child(self.links_group, -1)
1009 self.links_group.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1011 self.canvas.get_root_item().add_child(self.channels_group, -1)
1012 self.channels_group.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1013 self.channels_group.raise_(self.links_group)
1015 self.canvas.get_root_item().add_child(self.nodes_group, -1)
1016 self.nodes_group.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1017 self.nodes_group.raise_(self.channels_group)
1021 hbox = Gtk.HBox(); hbox.show()
1022 vbox.pack_start(hbox,
False,
False, 4)
1025 zoom_adj = Gtk.Adjustment(value=1.0, lower=0.01, upper=10.0,
1026 step_increment=0.02,
1029 self.zoom = zoom_adj
1030 def _zoom_changed(adj):
1031 self.canvas.set_scale(adj.get_value())
1032 zoom_adj.connect(
"value-changed", _zoom_changed)
1033 zoom = Gtk.SpinButton.new(zoom_adj, 0.1, 1)
1036 hbox.pack_start(GObject.new(Gtk.Label, label=
" Zoom:", visible=
True),
False,
False, 4)
1037 hbox.pack_start(zoom,
False,
False, 4)
1038 _zoom_changed(zoom_adj)
1041 speed_adj = Gtk.Adjustment(value=1.0, lower=0.01, upper=10.0,
1042 step_increment=0.02,
1043 page_increment=1.0, page_size=0)
1044 def _speed_changed(adj):
1045 self.speed = adj.get_value()
1046 self.sample_period = SAMPLE_PERIOD*adj.get_value()
1047 self._start_update_timer()
1048 speed_adj.connect(
"value-changed", _speed_changed)
1049 speed = Gtk.SpinButton.new(speed_adj, 1, 0)
1052 hbox.pack_start(GObject.new(Gtk.Label, label=
" Speed:", visible=
True),
False,
False, 4)
1053 hbox.pack_start(speed,
False,
False, 4)
1054 _speed_changed(speed_adj)
1057 self.time_label = GObject.new(Gtk.Label, label=
" Speed:", visible=
True)
1058 self.time_label.set_width_chars(20)
1059 hbox.pack_start(self.time_label,
False,
False, 4)
1062 screenshot_button = GObject.new(Gtk.Button,
1064 relief=Gtk.ReliefStyle.NONE, focus_on_click=
False,
1066 hbox.pack_start(screenshot_button,
False,
False, 4)
1068 def load_button_icon(button, icon_name):
1069 if not Gtk.IconTheme.get_default().has_icon(icon_name):
1070 print(f
"Could not load icon {icon_name}", file=sys.stderr)
1072 image = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.BUTTON)
1073 button.set_image(image)
1074 button.props.always_show_image =
True
1076 load_button_icon(screenshot_button,
"applets-screenshooter")
1077 screenshot_button.connect(
"clicked", self._take_screenshot)
1080 if ipython_view
is not None:
1081 shell_button = GObject.new(Gtk.Button,
1083 relief=Gtk.ReliefStyle.NONE, focus_on_click=
False,
1085 hbox.pack_start(shell_button,
False,
False, 4)
1086 load_button_icon(shell_button,
"gnome-terminal")
1087 shell_button.connect(
"clicked", self._start_shell)
1090 self.play_button = GObject.new(Gtk.ToggleButton,
1091 label=
"Simulate (F3)",
1092 relief=Gtk.ReliefStyle.NONE, focus_on_click=
False,
1094 load_button_icon(self.play_button,
"media-playback-start")
1095 accel_group = Gtk.AccelGroup()
1096 self.window.add_accel_group(accel_group)
1097 self.play_button.add_accelerator(
"clicked", accel_group,
1098 Gdk.KEY_F3, 0, Gtk.AccelFlags.VISIBLE)
1099 self.play_button.connect(
"toggled", self._on_play_button_toggled)
1100 hbox.pack_start(self.play_button,
False,
False, 4)
1102 self.canvas.get_root_item().connect(
"button-press-event", self.on_root_button_press_event)
1104 vbox.pack_start(self._create_advanced_controls(),
False,
False, 4)
1106 display = Gdk.Display.get_default()
1108 monitor = display.get_primary_monitor()
1109 geometry = monitor.get_geometry()
1110 scale_factor = monitor.get_scale_factor()
1111 except AttributeError:
1112 screen = display.get_default_screen()
1113 monitor_id = screen.get_primary_monitor()
1114 geometry = screen.get_monitor_geometry(monitor_id)
1115 scale_factor = screen.get_monitor_scale_factor(monitor_id)
1116 width = scale_factor * geometry.width
1117 height = scale_factor * geometry.height
1118 self.window.set_default_size(width * 2 / 3, height * 2 / 3)
1121 def scan_topology(self):
1122 print(
"scanning topology: %i nodes..." % (ns.NodeList.GetNNodes(),))
1123 graph = pygraphviz.AGraph()
1125 for nodeI
in range(ns.NodeList.GetNNodes()):
1127 if seen_nodes == 100:
1128 print(
"scan topology... %i nodes visited (%.1f%%)" % (nodeI, 100*nodeI/ns.NodeList.GetNNodes()))
1130 node = ns.NodeList.GetNode(nodeI)
1131 node_name =
"Node %i" % nodeI
1132 node_view = self.get_node(nodeI)
1134 mobility = ns.cppyy.gbl.hasMobilityModel(node)
1136 node_view.set_color(
"red")
1137 pos = ns.cppyy.gbl.getNodePosition(node)
1141 graph.add_node(node_name)
1143 for devI
in range(node.GetNDevices()):
1144 device = node.GetDevice(devI)
1146 if device_traits.is_wireless:
1148 if device_traits.is_virtual:
1150 channel = device.GetChannel()
1151 if channel.GetNDevices() > 2:
1152 if REPRESENT_CHANNELS_AS_NODES:
1154 if mobility
is None:
1155 channel_name =
"Channel %s" % id(channel)
1156 graph.add_edge(node_name, channel_name)
1157 self.get_channel(channel)
1158 self.create_link(self.get_node(nodeI), self.get_channel(channel))
1161 for otherDevI
in range(channel.GetNDevices()):
1162 otherDev = channel.GetDevice(otherDevI)
1163 otherNode = otherDev.GetNode()
1164 otherNodeView = self.get_node(otherNode.GetId())
1165 if otherNode
is not node:
1166 if mobility
is None and not otherNodeView.has_mobility:
1167 other_node_name =
"Node %i" % otherNode.GetId()
1168 graph.add_edge(node_name, other_node_name)
1169 self.create_link(self.get_node(nodeI), otherNodeView)
1171 for otherDevI
in range(channel.GetNDevices()):
1172 otherDev = channel.GetDevice(otherDevI)
1173 otherNode = otherDev.GetNode()
1174 otherNodeView = self.get_node(otherNode.GetId())
1175 if otherNode
is not node:
1176 if mobility
is None and not otherNodeView.has_mobility:
1177 other_node_name =
"Node %i" % otherNode.GetId()
1178 graph.add_edge(node_name, other_node_name)
1179 self.create_link(self.get_node(nodeI), otherNodeView)
1181 print(
"scanning topology: calling graphviz layout")
1182 graph.layout(LAYOUT_ALGORITHM)
1183 for node
in graph.iternodes():
1185 node_type, node_id = node.split(
' ')
1186 pos_x, pos_y = [float(s)
for s
in node.attr[
'pos'].split(
',')]
1187 if node_type ==
'Node':
1188 obj = self.nodes[
int(node_id)]
1189 elif node_type ==
'Channel':
1190 obj = self.channels[
int(node_id)]
1191 obj.set_position(pos_x, pos_y)
1193 print(
"scanning topology: all done.")
1194 self.emit(
"topology-scanned")
1196 def get_node(self, index):
1198 return self.nodes[index]
1200 node =
Node(self, index)
1201 self.nodes[index] = node
1202 self.nodes_group.add_child(node.canvas_item, -1)
1203 node.canvas_item.connect(
"button-press-event", self.on_node_button_press_event, node)
1204 node.canvas_item.connect(
"button-release-event", self.on_node_button_release_event, node)
1207 def get_channel(self, ns3_channel):
1209 return self.channels[id(ns3_channel)]
1211 channel =
Channel(ns3_channel)
1212 self.channels[id(ns3_channel)] = channel
1213 self.channels_group.add_child(channel.canvas_item, -1)
1216 def create_link(self, node, node_or_channel):
1218 self.links_group.add_child(link.canvas_item, -1)
1219 link.canvas_item.lower(
None)
1221 def update_view(self):
1224 self.time_label.set_text(
"Time: %f s" % ns.Simulator.Now().GetSeconds())
1226 self._update_node_positions()
1229 for info_win
in self.information_windows:
1232 self._update_transmissions_view()
1233 self._update_drops_view()
1235 self.emit(
"update-view")
1237 def _update_node_positions(self):
1238 for node
in self.nodes.values():
1239 if node.has_mobility:
1240 ns3_node = ns.NodeList.GetNode(node.node_index)
1241 mobility = ns.cppyy.gbl.hasMobilityModel(ns3_node)
1243 pos = ns.cppyy.gbl.getNodePosition(ns3_node)
1245 node.set_position(x, y)
1246 if node
is self.follow_node:
1247 hadj = self._scrolled_window.get_hadjustment()
1248 vadj = self._scrolled_window.get_vadjustment()
1249 px, py = self.canvas.convert_to_pixels(x, y)
1250 hadj.set_value(px - hadj.get_page_size() / 2)
1251 vadj.set_value(py - vadj.get_page_size() / 2)
1253 def center_on_node(self, node):
1254 if isinstance(node, ns.Node):
1255 node = self.nodes[node.GetId()]
1256 elif isinstance(node, int):
1257 node = self.nodes[node]
1258 elif isinstance(node, Node):
1261 raise TypeError(
"expected int, viz.Node or ns.Node, not %r" % node)
1263 x, y = node.get_position()
1264 hadj = self._scrolled_window.get_hadjustment()
1265 vadj = self._scrolled_window.get_vadjustment()
1266 px, py = self.canvas.convert_to_pixels(x, y)
1267 hadj.set_value(px - hadj.get_page_size() / 2)
1268 vadj.set_value(py - vadj.get_page_size() / 2)
1270 def update_model(self):
1271 self.simulation.lock.acquire()
1273 self.emit(
"simulation-periodic-update")
1275 self.simulation.lock.release()
1277 def do_simulation_periodic_update(self):
1278 smooth_factor =
int(self.transmissions_smoothing_adjustment.get_value()*10)
1280 transmissions = self.simulation.sim_helper.GetTransmissionSamples()
1281 self._last_transmissions.append(transmissions)
1282 while len(self._last_transmissions) > smooth_factor:
1283 self._last_transmissions.pop(0)
1285 drops = self.simulation.sim_helper.GetPacketDropSamples()
1286 self._last_drops.append(drops)
1287 while len(self._last_drops) > smooth_factor:
1288 self._last_drops.pop(0)
1290 def _get_label_over_line_position(self, pos1_x, pos1_y, pos2_x, pos2_y):
1291 hadj = self._scrolled_window.get_hadjustment()
1292 vadj = self._scrolled_window.get_vadjustment()
1293 bounds_x1, bounds_y1 = self.canvas.convert_from_pixels(hadj.get_value(), vadj.get_value())
1294 bounds_x2, bounds_y2 = self.canvas.convert_from_pixels(hadj.get_value() + hadj.get_page_size(),
1295 vadj.get_value() + vadj.get_page_size())
1297 pos1_x, pos1_y, pos2_x, pos2_y = ns.PyViz.LineClipping(bounds_x1, bounds_y1,
1298 bounds_x2, bounds_y2,
1303 pos1_x, pos1_y, pos2_x, pos2_y = res
1304 return (pos1_x + pos2_x)/2, (pos1_y + pos2_y)/2
1306 def _update_transmissions_view(self):
1307 transmissions_average = {}
1308 for transmission_set
in self._last_transmissions:
1309 for transmission
in transmission_set:
1310 key = (transmission.transmitter.GetId(), transmission.receiver.GetId())
1311 rx_bytes, count = transmissions_average.get(key, (0, 0))
1312 rx_bytes += transmission.bytes
1314 transmissions_average[key] = rx_bytes, count
1316 old_arrows = self._transmission_arrows
1317 for arrow, label
in old_arrows:
1318 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1319 label.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1322 k = self.node_size_adjustment.get_value()/5
1324 for (transmitter_id, receiver_id), (rx_bytes, rx_count)
in transmissions_average.items():
1325 transmitter = self.get_node(transmitter_id)
1326 receiver = self.get_node(receiver_id)
1328 arrow, label = old_arrows.pop()
1330 arrow = GooCanvas.CanvasPolyline(line_width=2.0, stroke_color_rgba=0x00C000C0, close_path=
False, end_arrow=
True, pointer_events=GooCanvas.CanvasPointerEvents.NONE)
1331 arrow.set_property(
"parent", self.canvas.get_root_item())
1334 label = GooCanvas.CanvasText(parent=self.canvas.get_root_item(), pointer_events=GooCanvas.CanvasPointerEvents.NONE)
1337 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1338 line_width =
max(0.1, math.log(float(rx_bytes)/rx_count/self.sample_period)*k)
1339 arrow.set_property(
"line-width", line_width)
1341 pos1_x, pos1_y = transmitter.get_position()
1342 pos2_x, pos2_y = receiver.get_position()
1343 points = GooCanvas.CanvasPoints.new(2)
1344 points.set_point(0, pos1_x, pos1_y)
1345 points.set_point(1, pos2_x, pos2_y)
1346 arrow.set_property(
"points", points)
1348 kbps = float(rx_bytes*8)/1e3/rx_count/self.sample_period
1349 label.set_properties(visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
1350 visibility_threshold=0.5,
1351 font=(
"Sans Serif %f" %
int(1+BITRATE_FONT_SIZE*k)))
1352 angle = math.atan2((pos2_y - pos1_y), (pos2_x - pos1_x))
1353 if -PI_OVER_2 <= angle <= PI_OVER_2:
1354 label.set_properties(text=(
"%.2f kbit/s →" % (kbps,)),
1355 alignment=Pango.Alignment.CENTER,
1356 anchor=GooCanvas.CanvasAnchorType.S,
1357 x=0, y=-line_width/2)
1359 label.set_properties(text=(
"← %.2f kbit/s" % (kbps,)),
1360 alignment=Pango.Alignment.CENTER,
1361 anchor=GooCanvas.CanvasAnchorType.N,
1362 x=0, y=line_width/2)
1364 lx, ly = self._get_label_over_line_position(c_double(pos1_x), c_double(pos1_y),
1365 c_double(pos2_x), c_double(pos2_y))
1369 label.set_transform(M)
1372 warnings.warn(
"PyGobject bug causing label position error; "
1373 "should be fixed in PyGObject >= 3.29.1")
1374 label.set_properties(x=(lx + label.props.x),
1375 y=(ly + label.props.y))
1377 new_arrows.append((arrow, label))
1379 self._transmission_arrows = new_arrows + old_arrows
1382 def _update_drops_view(self):
1384 for drop_set
in self._last_drops:
1385 for drop
in drop_set:
1386 key = drop.transmitter.GetId()
1387 drop_bytes, count = drops_average.get(key, (0, 0))
1388 drop_bytes += drop.bytes
1390 drops_average[key] = drop_bytes, count
1392 old_arrows = self._drop_arrows
1393 for arrow, label
in old_arrows:
1394 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1395 label.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1399 vadjustment = self._scrolled_window.get_vadjustment()
1400 bottom_y = vadjustment.get_value() + vadjustment.get_page_size()
1401 dummy, edge_y = self.canvas.convert_from_pixels(0, bottom_y)
1403 k = self.node_size_adjustment.get_value()/5
1405 for transmitter_id, (drop_bytes, drop_count)
in drops_average.items():
1406 transmitter = self.get_node(transmitter_id)
1408 arrow, label = old_arrows.pop()
1410 arrow = GooCanvas.CanvasPolyline(line_width=2.0, stroke_color_rgba=0xC00000C0, close_path=
False, end_arrow=
True, pointer_events=GooCanvas.CanvasPointerEvents.NONE)
1411 arrow.set_property(
"parent", self.canvas.get_root_item())
1414 label = GooCanvas.CanvasText(pointer_events=GooCanvas.CanvasPointerEvents.NONE)
1415 label.set_property(
"parent", self.canvas.get_root_item())
1418 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1419 arrow.set_property(
"line-width",
max(0.1, math.log(float(drop_bytes)/drop_count/self.sample_period)*k))
1420 pos1_x, pos1_y = transmitter.get_position()
1421 pos2_x, pos2_y = pos1_x, edge_y
1422 points = GooCanvas.CanvasPoints.new(2)
1423 points.set_point(0, pos1_x, pos1_y)
1424 points.set_point(1, pos2_x, pos2_y)
1425 arrow.set_property(
"points", points)
1427 label.set_properties(visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
1428 visibility_threshold=0.5,
1429 font=(
"Sans Serif %i" %
int(1+BITRATE_FONT_SIZE*k)),
1430 text=(
"%.2f kbit/s" % (float(drop_bytes*8)/1e3/drop_count/self.sample_period,)),
1431 alignment=Pango.Alignment.CENTER,
1432 x=(pos1_x + pos2_x)/2,
1433 y=(pos1_y + pos2_y)/2)
1435 new_arrows.append((arrow, label))
1437 self._drop_arrows = new_arrows + old_arrows
1440 def update_view_timeout(self):
1444 while not self.simulation.lock.acquire(
False):
1445 while Gtk.events_pending():
1446 Gtk.main_iteration()
1447 pause_messages = self.simulation.pause_messages
1448 self.simulation.pause_messages = []
1451 self.simulation.target_time = ns.Simulator.Now ().GetSeconds () + self.sample_period
1454 self.simulation.lock.release()
1458 dialog = Gtk.MessageDialog(parent=self.window, flags=0, type=Gtk.MessageType.WARNING, buttons=Gtk.ButtonsType.OK,
1459 message_format=
'\n'.join(pause_messages))
1460 dialog.connect(
"response",
lambda d, r: d.destroy())
1462 self.play_button.set_active(
False)
1465 if not self.play_button.get_active():
1466 self._update_timeout_id =
None
1470 self.simulation.go.set()
1474 def _start_update_timer(self):
1475 if self._update_timeout_id
is not None:
1476 GLib.source_remove(self._update_timeout_id)
1478 self._update_timeout_id = GLib.timeout_add(
int(SAMPLE_PERIOD/
min(self.speed, 1)*1e3),
1479 self.update_view_timeout,
1480 priority=PRIORITY_UPDATE_VIEW)
1482 def _on_play_button_toggled(self, button):
1483 if button.get_active():
1484 self._start_update_timer()
1486 if self._update_timeout_id
is not None:
1487 GLib.source_remove(self._update_timeout_id)
1489 def _quit(self, *dummy_args):
1490 if self._update_timeout_id
is not None:
1491 GLib.source_remove(self._update_timeout_id)
1492 self._update_timeout_id =
None
1493 self.simulation.quit =
True
1494 self.simulation.go.set()
1495 self.simulation.join()
1498 def _monkey_patch_ipython(self):
1505 original_runcode = self.ipython.runcode
1506 def runcode(ip, *args):
1508 self.simulation.lock.acquire()
1510 return original_runcode(*args)
1513 self.simulation.lock.release()
1515 self.ipython.runcode = types.MethodType(runcode, self.ipython)
1517 def autoscale_view(self):
1520 self._update_node_positions()
1521 positions = [node.get_position()
for node
in self.nodes.values()]
1522 min_x, min_y =
min(x
for (x,y)
in positions),
min(y
for (x,y)
in positions)
1523 max_x, max_y =
max(x
for (x,y)
in positions),
max(y
for (x,y)
in positions)
1524 min_x_px, min_y_px = self.canvas.convert_to_pixels(min_x, min_y)
1525 max_x_px, max_y_px = self.canvas.convert_to_pixels(max_x, max_y)
1528 dx_px = max_x_px - min_x_px
1529 dy_px = max_y_px - min_y_px
1530 hadj = self._scrolled_window.get_hadjustment()
1531 vadj = self._scrolled_window.get_vadjustment()
1532 new_dx, new_dy = 1.5*dx_px, 1.5*dy_px
1534 if new_dx == 0
or new_dy == 0:
1537 self.zoom.set_value(
min(hadj.get_page_size()/new_dx, vadj.get_page_size()/new_dy))
1539 x1, y1 = self.canvas.convert_from_pixels(hadj.get_value(), vadj.get_value())
1540 x2, y2 = self.canvas.convert_from_pixels((hadj.get_value() +
1541 hadj.get_page_size()),
1543 vadj.get_page_size()))
1546 center_x = (min_x + max_x) / 2
1547 center_y = (min_y + max_y) / 2
1549 self.canvas.scroll_to(center_x - width/2, center_y - height/2)
1554 self.scan_topology()
1555 self.window.connect(
"delete-event", self._quit)
1557 GLib.timeout_add(200, self.autoscale_view)
1558 self.simulation.
start()
1565 self._monkey_patch_ipython()
1570 def on_root_button_press_event(self, view, target, event):
1571 if event.button == 1:
1572 self.select_node(
None)
1575 def on_node_button_press_event(self, view, target, event, node):
1576 button = event.button
1578 self.select_node(node)
1581 self.popup_node_menu(node, event)
1584 self.begin_node_drag(node, event)
1588 def on_node_button_release_event(self, view, target, event, node):
1589 if event.button == 2:
1590 self.end_node_drag(node)
1594 class NodeDragState(
object):
1595 def __init__(self, canvas_x0, canvas_y0, sim_x0, sim_y0):
1596 self.canvas_x0 = canvas_x0
1597 self.canvas_y0 = canvas_y0
1598 self.sim_x0 = sim_x0
1599 self.sim_y0 = sim_y0
1600 self.motion_signal =
None
1602 def begin_node_drag(self, node, event):
1603 self.simulation.lock.acquire()
1605 ns3_node = ns.NodeList.GetNode(node.node_index)
1606 mob = ns.cppyy.gbl.hasMobilityModel(ns3_node)
1609 if self.node_drag_state
is not None:
1611 pos = ns.cppyy.gbl.getNodePosition(ns3_node)
1613 self.simulation.lock.release()
1614 devpos = self.canvas.get_window().get_device_position(event.device)
1615 x0, y0 = self.canvas.convert_from_pixels(devpos.x, devpos.y)
1616 self.node_drag_state = self.NodeDragState(x0, y0, pos.x, pos.y)
1617 self.node_drag_state.motion_signal = node.canvas_item.connect(
"motion-notify-event", self.node_drag_motion, node)
1619 def node_drag_motion(self, item, targe_item, event, node):
1620 self.simulation.lock.acquire()
1622 ns3_node = ns.NodeList.GetNode(node.node_index)
1623 mob = ns.cppyy.gbl.hasMobilityModel(ns3_node)
1626 if self.node_drag_state
is None:
1628 devpos = self.canvas.get_window().get_device_position(event.device)
1629 canvas_x, canvas_y = self.canvas.convert_from_pixels(devpos.x, devpos.y)
1630 dx = (canvas_x - self.node_drag_state.canvas_x0)
1631 dy = (canvas_y - self.node_drag_state.canvas_y0)
1632 pos = mob.GetPosition()
1636 mob.SetPosition(pos)
1639 self.simulation.lock.release()
1642 def end_node_drag(self, node):
1643 if self.node_drag_state
is None:
1645 node.canvas_item.disconnect(self.node_drag_state.motion_signal)
1646 self.node_drag_state =
None
1648 def popup_node_menu(self, node, event):
1650 self.emit(
"populate-node-menu", node, menu)
1651 menu.popup_at_pointer(event)
1653 def _update_ipython_selected_node(self):
1662 if self.selected_node
is None:
1665 self.simulation.lock.acquire()
1667 ns3_node = ns.NodeList.GetNode(self.selected_node.node_index)
1669 self.simulation.lock.release()
1670 self.ipython.updateNamespace({
'selected_node': ns3_node})
1673 def select_node(self, node):
1674 if isinstance(node, ns.Node):
1675 node = self.nodes[node.GetId()]
1676 elif isinstance(node, int):
1677 node = self.nodes[node]
1678 elif isinstance(node, Node):
1683 raise TypeError(
"expected None, int, viz.Node or ns.Node, not %r" % node)
1685 if node
is self.selected_node:
1688 if self.selected_node
is not None:
1689 self.selected_node.selected =
False
1690 self.selected_node = node
1691 if self.selected_node
is not None:
1692 self.selected_node.selected =
True
1694 if self._show_transmissions_mode == ShowTransmissionsMode.SELECTED:
1695 if self.selected_node
is None:
1696 self.simulation.set_nodes_of_interest([])
1698 self.simulation.set_nodes_of_interest([self.selected_node.node_index])
1700 self._update_ipython_selected_node()
1703 def add_information_window(self, info_win):
1704 self.information_windows.append(info_win)
1705 self.simulation.lock.acquire()
1709 self.simulation.lock.release()
1711 def remove_information_window(self, info_win):
1712 self.information_windows.remove(info_win)
1714 def _canvas_tooltip_cb(self, canvas, x, y, keyboard_mode, tooltip):
1716 hadj = self._scrolled_window.get_hadjustment()
1717 vadj = self._scrolled_window.get_vadjustment()
1718 x, y = self.canvas.convert_from_pixels(hadj.get_value() + x, vadj.get_value() + y)
1719 item = self.canvas.get_item_at(x, y,
True)
1723 while item
is not None:
1724 obj = getattr(item,
"pyviz_object",
None)
1726 obj.tooltip_query(tooltip)
1728 item = item.props.parent
1731 def _get_export_file_name(self):
1732 sel = Gtk.FileChooserNative.new(
"Save...", self.canvas.get_toplevel(),
1733 Gtk.FileChooserAction.SAVE,
1736 sel.set_local_only(
True)
1737 sel.set_do_overwrite_confirmation(
True)
1738 sel.set_current_name(
"Unnamed.pdf")
1740 filter = Gtk.FileFilter()
1741 filter.set_name(
"Embedded PostScript")
1742 filter.add_mime_type(
"image/x-eps")
1743 sel.add_filter(filter)
1745 filter = Gtk.FileFilter()
1746 filter.set_name(
"Portable Document Graphics")
1747 filter.add_mime_type(
"application/pdf")
1748 sel.add_filter(filter)
1750 filter = Gtk.FileFilter()
1751 filter.set_name(
"Scalable Vector Graphics")
1752 filter.add_mime_type(
"image/svg+xml")
1753 sel.add_filter(filter)
1756 if resp != Gtk.ResponseType.ACCEPT:
1760 file_name = sel.get_filename()
1764 def _take_screenshot(self, dummy_button):
1766 file_name = self._get_export_file_name()
1767 if file_name
is None:
1771 x1 = self._scrolled_window.get_hadjustment().get_value()
1772 y1 = self._scrolled_window.get_vadjustment().get_value()
1773 x2 = x1 + self._scrolled_window.get_hadjustment().get_page_size()
1774 y2 = y1 + self._scrolled_window.get_vadjustment().get_page_size()
1775 bounds = GooCanvas.CanvasBounds()
1776 bounds.x1, bounds.y1 = self.canvas.convert_from_pixels(x1, y1)
1777 bounds.x2, bounds.y2 = self.canvas.convert_from_pixels(x2, y2)
1778 dest_width = bounds.x2 - bounds.x1
1779 dest_height = bounds.y2 - bounds.y1
1782 dummy, extension = os.path.splitext(file_name)
1783 extension = extension.lower()
1784 if extension ==
'.eps':
1785 surface = cairo.PSSurface(file_name, dest_width, dest_height)
1786 elif extension ==
'.pdf':
1787 surface = cairo.PDFSurface(file_name, dest_width, dest_height)
1788 elif extension ==
'.svg':
1789 surface = cairo.SVGSurface(file_name, dest_width, dest_height)
1791 dialog = Gtk.MessageDialog(parent = self.canvas.get_toplevel(),
1792 flags = Gtk.DialogFlags.DESTROY_WITH_PARENT,
1793 type = Gtk.MessageType.ERROR,
1794 buttons = Gtk.ButtonsType.OK,
1795 message_format =
"Unknown extension '%s' (valid extensions are '.eps', '.svg', and '.pdf')"
1802 cr = cairo.Context(surface)
1803 cr.translate(-bounds.x1, -bounds.y1)
1804 self.canvas.render(cr, bounds, self.zoom.get_value())
1808 def set_follow_node(self, node):
1809 if isinstance(node, ns.Node):
1810 node = self.nodes[node.GetId()]
1811 self.follow_node = node
1813 def _start_shell(self, dummy_button):
1814 if self.shell_window
is not None:
1815 self.shell_window.present()
1818 self.shell_window = Gtk.Window()
1819 self.shell_window.set_size_request(750,550)
1820 self.shell_window.set_resizable(
True)
1821 scrolled_window = Gtk.ScrolledWindow()
1822 scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
1823 Gtk.PolicyType.AUTOMATIC)
1825 self.ipython.modify_font(Pango.FontDescription(SHELL_FONT))
1826 self.ipython.set_wrap_mode(Gtk.WrapMode.CHAR)
1828 scrolled_window.add(self.ipython)
1829 scrolled_window.show()
1830 self.shell_window.add(scrolled_window)
1831 self.shell_window.show()
1832 self.shell_window.connect(
'destroy', self._on_shell_window_destroy)
1834 self._update_ipython_selected_node()
1835 self.ipython.updateNamespace({
'viz': self})
1838 def _on_shell_window_destroy(self, window):
1839 self.shell_window =
None
1842 initialization_hooks = []
1846 Adds a callback to be called after
1847 the visualizer is initialized, like this::
1848 initialization_hook(visualizer, *args)
1850 global initialization_hooks
1851 initialization_hooks.append((hook, args))
1860 viz.canvas.set_bounds(cx1, cy1, cx2, cy2)
1865 assert Visualizer.INSTANCE
is None
1866 if _import_error
is not None:
1868 print(
"No visualization support (%s)." % (str(_import_error),),
1874 for hook, args
in initialization_hooks:
1875 GLib.idle_add(hook, viz, *args)
1876 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)