2 from __future__
import division, print_function
5 LAYOUT_ALGORITHM =
'neato'
6 REPRESENT_CHANNELS_AS_NODES = 1
7 DEFAULT_NODE_SIZE = 1.0
8 DEFAULT_TRANSMISSIONS_MEMORY = 5
13 PRIORITY_UPDATE_MODEL = -100
14 PRIORITY_UPDATE_VIEW = 200
18 if platform.system() ==
"Windows":
19 SHELL_FONT =
"Lucida Console 9"
21 SHELL_FONT =
"Luxi Mono 10"
34 if sys.version_info > (3,):
39 gi.require_version(
'GooCanvas',
'2.0')
40 gi.require_version(
'Gtk',
'3.0')
41 gi.require_version(
'Gdk',
'3.0')
42 from gi.repository
import GObject
43 from gi.repository
import GLib
45 gi.require_foreign(
"cairo")
47 from gi.repository
import Gtk
48 from gi.repository
import Gdk
49 from gi.repository
import Pango
50 from gi.repository
import GooCanvas
58 except ImportError
as e:
60 import dummy_threading
as threading
65 import ipython_viewxxxxxxxxxx
69 from .base
import InformationWindow, PyVizObject, Link, lookup_netdevice_traits, PIXELS_PER_METER
70 from .base
import transform_distance_simulation_to_canvas, transform_point_simulation_to_canvas
71 from .base
import transform_distance_canvas_to_simulation, transform_point_canvas_to_simulation
72 from .base
import load_plugins, register_plugin, plugins
75 PI_TIMES_2 = math.pi*2
117 'query-extra-tooltip-info': (GObject.SignalFlags.RUN_LAST,
None, (object,)),
121 """ Initialize function.
122 @param self The object pointer.
123 @param visualizer: visualizer object
124 @param node_index: node index
149 def set_svg_icon(self, file_base_name, width=None, height=None, align_x=0.5, align_y=0.5):
151 Set a background SVG icon for the node.
153 @param file_base_name: base file name, including .svg
154 extension, of the svg file. Place the file in the folder
155 src/contrib/visualizer/resource.
157 @param width: scale to the specified width, in meters
158 @param height: scale to the specified height, in meters
160 @param align_x: horizontal alignment of the icon relative to
161 the node position, from 0 (icon fully to the left of the node)
162 to 1.0 (icon fully to the right of the node)
164 @param align_y: vertical alignment of the icon relative to the
165 node position, from 0 (icon fully to the top of the node) to
166 1.0 (icon fully to the bottom of the node)
168 @return a ValueError exception if invalid dimensions.
171 if width
is None and height
is None:
172 raise ValueError(
"either width or height must be given")
173 rsvg_handle = svgitem.rsvg_handle_factory(file_base_name)
178 self.
svg_itemsvg_item.props.pointer_events = GooCanvas.CanvasPointerEvents.NONE
180 self.
svg_itemsvg_item.props.visibility = GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD
181 if width
is not None:
183 if height
is not None:
197 Set a label for the node.
199 @param self: class object.
200 @param label: label to set
202 @return: an exception if invalid parameter.
204 assert isinstance(label, basestring)
212 @param self: class object.
227 @param self: class object.
228 @param tooltip: tooltip
231 self.
visualizervisualizer.simulation.lock.acquire()
233 ns3_node = ns.network.NodeList.GetNode(self.
node_indexnode_index)
234 ipv4 = ns3_node.GetObject(ns.internet.Ipv4.GetTypeId())
235 ipv6 = ns3_node.GetObject(ns.internet.Ipv6.GetTypeId())
237 name =
'<b><u>Node %i</u></b>' % self.
node_indexnode_index
238 node_name = ns.core.Names.FindName (ns3_node)
239 if len(node_name)!=0:
240 name +=
' <b>(' + node_name +
')</b>'
245 self.emit(
"query-extra-tooltip-info", lines)
247 mob = ns3_node.GetObject(ns.mobility.MobilityModel.GetTypeId())
249 lines.append(
' <b>Mobility Model</b>: %s' % mob.GetInstanceTypeId().GetName())
251 for devI
in range(ns3_node.GetNDevices()):
253 lines.append(
' <u>NetDevice %i:</u>' % devI)
254 dev = ns3_node.GetDevice(devI)
255 name = ns.core.Names.FindName(dev)
257 lines.append(
' <b>Name:</b> %s' % name)
258 devname = dev.GetInstanceTypeId().GetName()
259 lines.append(
' <b>Type:</b> %s' % devname)
262 ipv4_idx = ipv4.GetInterfaceForDevice(dev)
265 '%s/%s' % (ipv4.GetAddress(ipv4_idx, i).GetLocal(),
266 ipv4.GetAddress(ipv4_idx, i).GetMask())
267 for i
in range(ipv4.GetNAddresses(ipv4_idx))]
268 lines.append(
' <b>IPv4 Addresses:</b> %s' %
'; '.join(addresses))
271 ipv6_idx = ipv6.GetInterfaceForDevice(dev)
274 '%s/%s' % (ipv6.GetAddress(ipv6_idx, i).GetAddress(),
275 ipv6.GetAddress(ipv6_idx, i).GetPrefix())
276 for i
in range(ipv6.GetNAddresses(ipv6_idx))]
277 lines.append(
' <b>IPv6 Addresses:</b> %s' %
'; '.join(addresses))
279 lines.append(
' <b>MAC Address:</b> %s' % (dev.GetAddress(),))
281 tooltip.set_markup(
'\n'.join(lines))
283 self.
visualizervisualizer.simulation.lock.release()
287 On Enter event handle.
289 @param self: class object.
291 @param target: target
298 On Leave event handle.
300 @param self: class object.
302 @param target: target
310 Set selected function.
312 @param self: class object.
313 @param value: selected value
320 Get selected function.
322 @param self: class object.
323 @return selected status
327 selected = property(_get_selected, _set_selected)
331 Set highlighted function.
333 @param self: class object.
334 @param value: selected value
341 Get highlighted function.
343 @param self: class object.
344 @return highlighted status
348 highlighted = property(_get_highlighted, _set_highlighted)
354 @param self: class object.
355 @param size: selected size
358 self.
_size_size = size
363 Update the node aspect to reflect the selected/highlighted state
365 @param self: class object.
370 if self.
svg_itemsvg_item
is not None:
374 fill_color_rgba = (self.
_color_color & 0xffffff00) | alpha
375 self.
canvas_itemcanvas_item.set_properties(radius_x=size, radius_y=size,
376 fill_color_rgba=fill_color_rgba)
380 line_width = size*.15
382 stroke_color =
'yellow'
384 stroke_color =
'black'
385 self.
canvas_itemcanvas_item.set_properties(line_width=line_width, stroke_color=stroke_color)
387 if self.
_label_label
is not None:
389 self.
_label_canvas_item_label_canvas_item = GooCanvas.CanvasText(visibility_threshold=0.5,
390 font=
"Sans Serif 10",
391 fill_color_rgba=0x808080ff,
392 alignment=Pango.Alignment.CENTER,
393 anchor=GooCanvas.CanvasAnchorType.N,
394 parent=self.
visualizervisualizer.canvas.get_root_item(),
395 pointer_events=GooCanvas.CanvasPointerEvents.NONE)
398 self.
_label_canvas_item_label_canvas_item.set_properties(visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
404 Set position function.
406 @param self: class object.
411 self.
canvas_itemcanvas_item.set_property(
"center_x", x)
412 self.
canvas_itemcanvas_item.set_property(
"center_y", y)
413 if self.
svg_itemsvg_item
is not None:
416 for link
in self.
linkslinks:
425 bounds = self.
visualizervisualizer.canvas.get_bounds()
427 (min_x, min_y, max_x, max_y) = bounds
429 min_x =
min(x, min_x)
430 min_y =
min(y, min_y)
431 max_x =
max(x, max_x)
432 max_y =
max(y, max_y)
434 new_bounds = (min_x, min_y, max_x, max_y)
436 if new_bounds != bounds:
437 self.
visualizervisualizer.canvas.set_bounds(*new_bounds)
444 Get position function.
446 @param self: class object.
447 @return x and y position
449 return (self.
canvas_itemcanvas_item.get_property(
"center_x"), self.
canvas_itemcanvas_item.get_property(
"center_y"))
453 Update position function.
455 @param self: class object.
465 @param self: class object.
466 @param color: color to set.
469 if isinstance(color, str):
470 color = Gdk.color_parse(color)
471 color = ((color.red>>8) << 24) | ((color.green>>8) << 16) | ((color.blue>>8) << 8) | 0xff
479 @param self: class object.
480 @param link: link to add.
483 assert isinstance(link, Link)
484 self.
linkslinks.append(link)
488 Remove link function.
490 @param self: class object.
491 @param link: link to add.
494 assert isinstance(link, Link)
495 self.
linkslinks.remove(link)
500 Has mobility function.
502 @param self: class object.
503 @return modility option
506 node = ns.network.NodeList.GetNode(self.
node_indexnode_index)
507 mobility = node.GetObject(ns.mobility.MobilityModel.GetTypeId())
523 Initializer function.
525 @param self: class object.
526 @param channel: channel.
530 self.
canvas_itemcanvas_item = GooCanvas.CanvasEllipse(radius_x=30, radius_y=30,
532 stroke_color=
"grey", line_width=2.0,
533 line_dash=GooCanvas.CanvasLineDash.newv([10.0, 10.0 ]),
534 visibility=GooCanvas.CanvasItemVisibility.VISIBLE)
540 Initializer function.
542 @param self: class object.
543 @param x: x position.
544 @param y: y position.
547 self.
canvas_itemcanvas_item.set_property(
"center_x", x)
548 self.
canvas_itemcanvas_item.set_property(
"center_y", y)
550 for link
in self.
linkslinks:
555 Initializer function.
557 @param self: class object.
558 @return x / y position.
560 return (self.
canvas_itemcanvas_item.get_property(
"center_x"), self.
canvas_itemcanvas_item.get_property(
"center_y"))
574 Initializer function.
576 @param self: class object.
577 @param node1: class object.
578 @param node2: class object.
581 assert isinstance(node1, Node)
582 assert isinstance(node2, (Node, Channel))
585 self.
canvas_itemcanvas_item = GooCanvas.CanvasPath(line_width=1.0, stroke_color=
"black")
587 self.
node1node1.links.append(self)
588 self.
node2node2.links.append(self)
592 Update points function.
594 @param self: class object.
597 pos1_x, pos1_y = self.
node1node1.get_position()
598 pos2_x, pos2_y = self.
node2node2.get_position()
599 self.
canvas_itemcanvas_item.set_property(
"data",
"M %r %r L %r %r" % (pos1_x, pos1_y, pos2_x, pos2_y))
620 Initializer function.
622 @param self: class object.
623 @param viz: class object.
626 super(SimulationThread, self).
__init__()
627 assert isinstance(viz, Visualizer)
629 self.
locklock = threading.Lock()
630 self.
gogo = threading.Event()
639 Set nodes of interest function.
641 @param self: class object.
642 @param nodes: class object.
645 self.
locklock.acquire()
647 self.
sim_helpersim_helper.SetNodesOfInterest(nodes)
653 Initializer function.
655 @param self: class object.
658 while not self.
quitquit:
666 self.
locklock.acquire()
669 if ns3.core.Simulator.IsFinished():
670 self.
vizviz.play_button.set_sensitive(
False)
678 GLib.idle_add(self.
vizviz.update_model, priority=PRIORITY_UPDATE_MODEL)
705 if _import_error
is None:
709 'populate-node-menu': (GObject.SignalFlags.RUN_LAST,
None, (object, Gtk.Menu,)),
713 'simulation-periodic-update': (GObject.SignalFlags.RUN_LAST,
None, ()),
716 'topology-scanned': (GObject.SignalFlags.RUN_LAST,
None, ()),
719 'update-view': (GObject.SignalFlags.RUN_LAST,
None, ()),
725 Initializer function.
727 @param self: class object.
730 assert Visualizer.INSTANCE
is None
731 Visualizer.INSTANCE = self
732 super(Visualizer, self).__init__()
737 self.time_label =
None
738 self.play_button =
None
740 self._scrolled_window =
None
742 self.links_group = GooCanvas.CanvasGroup()
743 self.channels_group = GooCanvas.CanvasGroup()
744 self.nodes_group = GooCanvas.CanvasGroup()
746 self._update_timeout_id =
None
748 self.selected_node =
None
750 self.information_windows = []
751 self._transmission_arrows = []
752 self._last_transmissions = []
753 self._drop_arrows = []
754 self._last_drops = []
755 self._show_transmissions_mode =
None
756 self.set_show_transmissions_mode(ShowTransmissionsMode.ALL)
757 self._panning_state =
None
758 self.node_size_adjustment =
None
759 self.transmissions_smoothing_adjustment =
None
760 self.sample_period = SAMPLE_PERIOD
761 self.node_drag_state =
None
762 self.follow_node =
None
763 self.shell_window =
None
767 for plugin
in plugins:
770 def set_show_transmissions_mode(self, mode):
772 Set show transmission mode.
774 @param self: class object.
775 @param mode: mode to set.
778 assert isinstance(mode, ShowTransmissionsMode)
779 self._show_transmissions_mode = mode
780 if self._show_transmissions_mode == ShowTransmissionsMode.ALL:
781 self.simulation.set_nodes_of_interest(
list(range(ns.network.NodeList.GetNNodes())))
782 elif self._show_transmissions_mode == ShowTransmissionsMode.NONE:
783 self.simulation.set_nodes_of_interest([])
784 elif self._show_transmissions_mode == ShowTransmissionsMode.SELECTED:
785 if self.selected_node
is None:
786 self.simulation.set_nodes_of_interest([])
788 self.simulation.set_nodes_of_interest([self.selected_node.node_index])
790 def _create_advanced_controls(self):
792 Create advanced controls.
794 @param self: class object.
797 expander = Gtk.Expander.new(
"Advanced")
800 main_vbox = GObject.new(Gtk.VBox, border_width=8, visible=
True)
801 expander.add(main_vbox)
803 main_hbox1 = GObject.new(Gtk.HBox, border_width=8, visible=
True)
804 main_vbox.pack_start(main_hbox1,
True,
True, 0)
806 show_transmissions_group = GObject.new(Gtk.HeaderBar,
807 title=
"Show transmissions",
809 main_hbox1.pack_start(show_transmissions_group,
False,
False, 8)
811 vbox = Gtk.VBox(homogeneous=
True, spacing=4)
813 show_transmissions_group.add(vbox)
815 all_nodes = Gtk.RadioButton.new(
None)
816 all_nodes.set_label(
"All nodes")
817 all_nodes.set_active(
True)
821 selected_node = Gtk.RadioButton.new_from_widget(all_nodes)
823 selected_node.set_label(
"Selected node")
824 selected_node.set_active(
False)
825 vbox.add(selected_node)
827 no_node = Gtk.RadioButton.new_from_widget(all_nodes)
829 no_node.set_label(
"Disabled")
830 no_node.set_active(
False)
834 if radio.get_active():
835 self.set_show_transmissions_mode(ShowTransmissionsMode.ALL)
836 all_nodes.connect(
"toggled", toggled)
839 if radio.get_active():
840 self.set_show_transmissions_mode(ShowTransmissionsMode.NONE)
841 no_node.connect(
"toggled", toggled)
844 if radio.get_active():
845 self.set_show_transmissions_mode(ShowTransmissionsMode.SELECTED)
846 selected_node.connect(
"toggled", toggled)
849 misc_settings_group = GObject.new(Gtk.HeaderBar, title=
"Misc Settings", visible=
True)
850 main_hbox1.pack_start(misc_settings_group,
False,
False, 8)
851 settings_hbox = GObject.new(Gtk.HBox, border_width=8, visible=
True)
852 misc_settings_group.add(settings_hbox)
855 vbox = GObject.new(Gtk.VBox, border_width=0, visible=
True)
856 scale = GObject.new(Gtk.HScale, visible=
True, digits=2)
857 vbox.pack_start(scale,
True,
True, 0)
858 vbox.pack_start(GObject.new(Gtk.Label, label=
"Node Size", visible=
True),
True,
True, 0)
859 settings_hbox.pack_start(vbox,
False,
False, 6)
860 self.node_size_adjustment = scale.get_adjustment()
861 def node_size_changed(adj):
862 for node
in self.nodes.values():
863 node.set_size(adj.get_value())
864 self.node_size_adjustment.connect(
"value-changed", node_size_changed)
865 self.node_size_adjustment.set_lower(0.01)
866 self.node_size_adjustment.set_upper(20)
867 self.node_size_adjustment.set_step_increment(0.1)
868 self.node_size_adjustment.set_value(DEFAULT_NODE_SIZE)
871 vbox = GObject.new(Gtk.VBox, border_width=0, visible=
True)
872 scale = GObject.new(Gtk.HScale, visible=
True, digits=1)
873 vbox.pack_start(scale,
True,
True, 0)
874 vbox.pack_start(GObject.new(Gtk.Label, label=
"Tx. Smooth Factor (s)", visible=
True),
True,
True, 0)
875 settings_hbox.pack_start(vbox,
False,
False, 6)
876 self.transmissions_smoothing_adjustment = scale.get_adjustment()
877 adj = self.transmissions_smoothing_adjustment
880 adj.set_step_increment(0.1)
881 adj.set_value(DEFAULT_TRANSMISSIONS_MEMORY*0.1)
886 class _PanningState(object):
889 __slots__ = [
'initial_mouse_pos',
'initial_canvas_pos',
'motion_signal']
891 def _begin_panning(self, widget, event):
893 Set show trnamission mode.
895 @param self: class object.
896 @param mode: mode to set.
899 display = self.canvas.get_window().get_display()
900 cursor = Gdk.Cursor.new_for_display(display, Gdk.CursorType.FLEUR)
901 self.canvas.get_window().set_cursor(cursor)
902 self._panning_state = self._PanningState()
903 pos = widget.get_window().get_device_position(event.device)
904 self._panning_state.initial_mouse_pos = (pos.x, pos.y)
905 x = self._scrolled_window.get_hadjustment().get_value()
906 y = self._scrolled_window.get_vadjustment().get_value()
907 self._panning_state.initial_canvas_pos = (x, y)
908 self._panning_state.motion_signal = self.canvas.connect(
"motion-notify-event", self._panning_motion)
910 def _end_panning(self, event):
912 End panning function.
914 @param self: class object.
915 @param event: active event.
918 if self._panning_state
is None:
920 self.canvas.get_window().set_cursor(
None)
921 self.canvas.disconnect(self._panning_state.motion_signal)
922 self._panning_state =
None
924 def _panning_motion(self, widget, event):
926 Panning motion function.
928 @param self: class object.
929 @param widget: widget.
931 @return true if successful
933 assert self._panning_state
is not None
935 pos = widget.get_window().get_device_position(event.device)
938 x, y = event.x, event.y
940 hadj = self._scrolled_window.get_hadjustment()
941 vadj = self._scrolled_window.get_vadjustment()
942 mx0, my0 = self._panning_state.initial_mouse_pos
943 cx0, cy0 = self._panning_state.initial_canvas_pos
947 hadj.set_value(cx0 - dx)
948 vadj.set_value(cy0 - dy)
951 def _canvas_button_press(self, widget, event):
952 if event.button == 2:
953 self._begin_panning(widget, event)
957 def _canvas_button_release(self, dummy_widget, event):
958 if event.button == 2:
959 self._end_panning(event)
963 def _canvas_scroll_event(self, dummy_widget, event):
964 if event.direction == Gdk.ScrollDirection.UP:
965 self.zoom.set_value(self.zoom.get_value() * 1.25)
967 elif event.direction == Gdk.ScrollDirection.DOWN:
968 self.zoom.set_value(self.zoom.get_value() / 1.25)
972 def get_hadjustment(self):
973 return self._scrolled_window.get_hadjustment()
974 def get_vadjustment(self):
975 return self._scrolled_window.get_vadjustment()
977 def create_gui(self):
978 self.window = Gtk.Window()
981 self.window.add(vbox)
984 self.canvas = GooCanvas.Canvas()
985 self.canvas.connect_after(
"button-press-event", self._canvas_button_press)
986 self.canvas.connect_after(
"button-release-event", self._canvas_button_release)
987 self.canvas.connect(
"scroll-event", self._canvas_scroll_event)
988 self.canvas.props.has_tooltip =
True
989 self.canvas.connect(
"query-tooltip", self._canvas_tooltip_cb)
991 sw = Gtk.ScrolledWindow(); sw.show()
992 self._scrolled_window = sw
994 vbox.pack_start(sw,
True,
True, 4)
995 self.canvas.set_size_request(600, 450)
996 self.canvas.
set_bounds(-10000, -10000, 10000, 10000)
997 self.canvas.scroll_to(0, 0)
1000 self.canvas.get_root_item().add_child(self.links_group, -1)
1001 self.links_group.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1003 self.canvas.get_root_item().add_child(self.channels_group, -1)
1004 self.channels_group.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1005 self.channels_group.raise_(self.links_group)
1007 self.canvas.get_root_item().add_child(self.nodes_group, -1)
1008 self.nodes_group.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1009 self.nodes_group.raise_(self.channels_group)
1013 hbox = Gtk.HBox(); hbox.show()
1014 vbox.pack_start(hbox,
False,
False, 4)
1017 zoom_adj = Gtk.Adjustment(value=1.0, lower=0.01, upper=10.0,
1018 step_increment=0.02,
1021 self.zoom = zoom_adj
1022 def _zoom_changed(adj):
1023 self.canvas.set_scale(adj.get_value())
1024 zoom_adj.connect(
"value-changed", _zoom_changed)
1025 zoom = Gtk.SpinButton.new(zoom_adj, 0.1, 1)
1028 hbox.pack_start(GObject.new(Gtk.Label, label=
" Zoom:", visible=
True),
False,
False, 4)
1029 hbox.pack_start(zoom,
False,
False, 4)
1030 _zoom_changed(zoom_adj)
1033 speed_adj = Gtk.Adjustment(value=1.0, lower=0.01, upper=10.0,
1034 step_increment=0.02,
1035 page_increment=1.0, page_size=0)
1036 def _speed_changed(adj):
1037 self.speed = adj.get_value()
1038 self.sample_period = SAMPLE_PERIOD*adj.get_value()
1039 self._start_update_timer()
1040 speed_adj.connect(
"value-changed", _speed_changed)
1041 speed = Gtk.SpinButton.new(speed_adj, 1, 0)
1044 hbox.pack_start(GObject.new(Gtk.Label, label=
" Speed:", visible=
True),
False,
False, 4)
1045 hbox.pack_start(speed,
False,
False, 4)
1046 _speed_changed(speed_adj)
1049 self.time_label = GObject.new(Gtk.Label, label=
" Speed:", visible=
True)
1050 self.time_label.set_width_chars(20)
1051 hbox.pack_start(self.time_label,
False,
False, 4)
1054 screenshot_button = GObject.new(Gtk.Button,
1056 relief=Gtk.ReliefStyle.NONE, focus_on_click=
False,
1058 hbox.pack_start(screenshot_button,
False,
False, 4)
1060 def load_button_icon(button, icon_name):
1064 sys.stderr.write(
"Could not load icon %s due to missing gnomedesktop Python module\n" % icon_name)
1066 icon = gnomedesktop.find_icon(Gtk.IconTheme.get_default(), icon_name, 16, 0)
1067 if icon
is not None:
1068 button.props.image = GObject.new(Gtk.Image, file=icon, visible=
True)
1070 load_button_icon(screenshot_button,
"applets-screenshooter")
1071 screenshot_button.connect(
"clicked", self._take_screenshot)
1074 if ipython_view
is not None:
1075 shell_button = GObject.new(Gtk.Button,
1077 relief=Gtk.ReliefStyle.NONE, focus_on_click=
False,
1079 hbox.pack_start(shell_button,
False,
False, 4)
1080 load_button_icon(shell_button,
"gnome-terminal")
1081 shell_button.connect(
"clicked", self._start_shell)
1084 self.play_button = GObject.new(Gtk.ToggleButton,
1085 image=GObject.new(Gtk.Image, stock=Gtk.STOCK_MEDIA_PLAY, visible=
True),
1086 label=
"Simulate (F3)",
1087 relief=Gtk.ReliefStyle.NONE, focus_on_click=
False,
1088 use_stock=
True, visible=
True)
1089 accel_group = Gtk.AccelGroup()
1090 self.window.add_accel_group(accel_group)
1091 self.play_button.add_accelerator(
"clicked", accel_group,
1092 Gdk.KEY_F3, 0, Gtk.AccelFlags.VISIBLE)
1093 self.play_button.connect(
"toggled", self._on_play_button_toggled)
1094 hbox.pack_start(self.play_button,
False,
False, 4)
1096 self.canvas.get_root_item().connect(
"button-press-event", self.on_root_button_press_event)
1098 vbox.pack_start(self._create_advanced_controls(),
False,
False, 4)
1100 display = Gdk.Display.get_default()
1102 monitor = display.get_primary_monitor()
1103 geometry = monitor.get_geometry()
1104 scale_factor = monitor.get_scale_factor()
1105 except AttributeError:
1106 screen = display.get_default_screen()
1107 monitor_id = screen.get_primary_monitor()
1108 geometry = screen.get_monitor_geometry(monitor_id)
1109 scale_factor = screen.get_monitor_scale_factor(monitor_id)
1110 width = scale_factor * geometry.width
1111 height = scale_factor * geometry.height
1112 self.window.set_default_size(width * 2 / 3, height * 2 / 3)
1115 def scan_topology(self):
1116 print(
"scanning topology: %i nodes..." % (ns.network.NodeList.GetNNodes(),))
1117 graph = pygraphviz.AGraph()
1119 for nodeI
in range(ns.network.NodeList.GetNNodes()):
1121 if seen_nodes == 100:
1122 print(
"scan topology... %i nodes visited (%.1f%%)" % (nodeI, 100*nodeI/ns.network.NodeList.GetNNodes()))
1124 node = ns.network.NodeList.GetNode(nodeI)
1125 node_name =
"Node %i" % nodeI
1126 node_view = self.get_node(nodeI)
1128 mobility = node.GetObject(ns.mobility.MobilityModel.GetTypeId())
1129 if mobility
is not None:
1130 node_view.set_color(
"red")
1131 pos = mobility.GetPosition()
1135 graph.add_node(node_name)
1137 for devI
in range(node.GetNDevices()):
1138 device = node.GetDevice(devI)
1140 if device_traits.is_wireless:
1142 if device_traits.is_virtual:
1144 channel = device.GetChannel()
1145 if channel.GetNDevices() > 2:
1146 if REPRESENT_CHANNELS_AS_NODES:
1148 if mobility
is None:
1149 channel_name =
"Channel %s" % id(channel)
1150 graph.add_edge(node_name, channel_name)
1151 self.get_channel(channel)
1152 self.create_link(self.get_node(nodeI), self.get_channel(channel))
1155 for otherDevI
in range(channel.GetNDevices()):
1156 otherDev = channel.GetDevice(otherDevI)
1157 otherNode = otherDev.GetNode()
1158 otherNodeView = self.get_node(otherNode.GetId())
1159 if otherNode
is not node:
1160 if mobility
is None and not otherNodeView.has_mobility:
1161 other_node_name =
"Node %i" % otherNode.GetId()
1162 graph.add_edge(node_name, other_node_name)
1163 self.create_link(self.get_node(nodeI), otherNodeView)
1165 for otherDevI
in range(channel.GetNDevices()):
1166 otherDev = channel.GetDevice(otherDevI)
1167 otherNode = otherDev.GetNode()
1168 otherNodeView = self.get_node(otherNode.GetId())
1169 if otherNode
is not node:
1170 if mobility
is None and not otherNodeView.has_mobility:
1171 other_node_name =
"Node %i" % otherNode.GetId()
1172 graph.add_edge(node_name, other_node_name)
1173 self.create_link(self.get_node(nodeI), otherNodeView)
1175 print(
"scanning topology: calling graphviz layout")
1176 graph.layout(LAYOUT_ALGORITHM)
1177 for node
in graph.iternodes():
1179 node_type, node_id = node.split(
' ')
1180 pos_x, pos_y = [float(s)
for s
in node.attr[
'pos'].split(
',')]
1181 if node_type ==
'Node':
1182 obj = self.nodes[int(node_id)]
1183 elif node_type ==
'Channel':
1184 obj = self.channels[int(node_id)]
1185 obj.set_position(pos_x, pos_y)
1187 print(
"scanning topology: all done.")
1188 self.emit(
"topology-scanned")
1190 def get_node(self, index):
1192 return self.nodes[index]
1194 node =
Node(self, index)
1195 self.nodes[index] = node
1196 self.nodes_group.add_child(node.canvas_item, -1)
1197 node.canvas_item.connect(
"button-press-event", self.on_node_button_press_event, node)
1198 node.canvas_item.connect(
"button-release-event", self.on_node_button_release_event, node)
1201 def get_channel(self, ns3_channel):
1203 return self.channels[id(ns3_channel)]
1205 channel =
Channel(ns3_channel)
1206 self.channels[id(ns3_channel)] = channel
1207 self.channels_group.add_child(channel.canvas_item, -1)
1210 def create_link(self, node, node_or_channel):
1212 self.links_group.add_child(link.canvas_item, -1)
1213 link.canvas_item.lower(
None)
1215 def update_view(self):
1218 self.time_label.set_text(
"Time: %f s" % ns.core.Simulator.Now().GetSeconds())
1220 self._update_node_positions()
1223 for info_win
in self.information_windows:
1226 self._update_transmissions_view()
1227 self._update_drops_view()
1229 self.emit(
"update-view")
1231 def _update_node_positions(self):
1232 for node
in self.nodes.values():
1233 if node.has_mobility:
1234 ns3_node = ns.network.NodeList.GetNode(node.node_index)
1235 mobility = ns3_node.GetObject(ns.mobility.MobilityModel.GetTypeId())
1236 if mobility
is not None:
1237 pos = mobility.GetPosition()
1239 node.set_position(x, y)
1240 if node
is self.follow_node:
1241 hadj = self._scrolled_window.get_hadjustment()
1242 vadj = self._scrolled_window.get_vadjustment()
1243 px, py = self.canvas.convert_to_pixels(x, y)
1244 hadj.set_value(px - hadj.get_page_size() / 2)
1245 vadj.set_value(py - vadj.get_page_size() / 2)
1247 def center_on_node(self, node):
1248 if isinstance(node, ns.network.Node):
1249 node = self.nodes[node.GetId()]
1250 elif isinstance(node, (int, long)):
1251 node = self.nodes[node]
1252 elif isinstance(node, Node):
1255 raise TypeError(
"expected int, viz.Node or ns.network.Node, not %r" % node)
1257 x, y = node.get_position()
1258 hadj = self._scrolled_window.get_hadjustment()
1259 vadj = self._scrolled_window.get_vadjustment()
1260 px, py = self.canvas.convert_to_pixels(x, y)
1261 hadj.set_value(px - hadj.get_page_size() / 2)
1262 vadj.set_value(py - vadj.get_page_size() / 2)
1264 def update_model(self):
1265 self.simulation.lock.acquire()
1267 self.emit(
"simulation-periodic-update")
1269 self.simulation.lock.release()
1271 def do_simulation_periodic_update(self):
1272 smooth_factor = int(self.transmissions_smoothing_adjustment.get_value()*10)
1274 transmissions = self.simulation.sim_helper.GetTransmissionSamples()
1275 self._last_transmissions.append(transmissions)
1276 while len(self._last_transmissions) > smooth_factor:
1277 self._last_transmissions.pop(0)
1279 drops = self.simulation.sim_helper.GetPacketDropSamples()
1280 self._last_drops.append(drops)
1281 while len(self._last_drops) > smooth_factor:
1282 self._last_drops.pop(0)
1284 def _get_label_over_line_position(self, pos1_x, pos1_y, pos2_x, pos2_y):
1285 hadj = self._scrolled_window.get_hadjustment()
1286 vadj = self._scrolled_window.get_vadjustment()
1287 bounds_x1, bounds_y1 = self.canvas.convert_from_pixels(hadj.get_value(), vadj.get_value())
1288 bounds_x2, bounds_y2 = self.canvas.convert_from_pixels(hadj.get_value() + hadj.get_page_size(),
1289 vadj.get_value() + vadj.get_page_size())
1290 pos1_x, pos1_y, pos2_x, pos2_y = ns.visualizer.PyViz.LineClipping(bounds_x1, bounds_y1,
1291 bounds_x2, bounds_y2,
1294 return (pos1_x + pos2_x)/2, (pos1_y + pos2_y)/2
1296 def _update_transmissions_view(self):
1297 transmissions_average = {}
1298 for transmission_set
in self._last_transmissions:
1299 for transmission
in transmission_set:
1300 key = (transmission.transmitter.GetId(), transmission.receiver.GetId())
1301 rx_bytes, count = transmissions_average.get(key, (0, 0))
1302 rx_bytes += transmission.bytes
1304 transmissions_average[key] = rx_bytes, count
1306 old_arrows = self._transmission_arrows
1307 for arrow, label
in old_arrows:
1308 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1309 label.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1312 k = self.node_size_adjustment.get_value()/5
1314 for (transmitter_id, receiver_id), (rx_bytes, rx_count)
in transmissions_average.items():
1315 transmitter = self.get_node(transmitter_id)
1316 receiver = self.get_node(receiver_id)
1318 arrow, label = old_arrows.pop()
1320 arrow = GooCanvas.CanvasPolyline(line_width=2.0, stroke_color_rgba=0x00C000C0, close_path=
False, end_arrow=
True, pointer_events=GooCanvas.CanvasPointerEvents.NONE)
1321 arrow.set_property(
"parent", self.canvas.get_root_item())
1324 label = GooCanvas.CanvasText(parent=self.canvas.get_root_item(), pointer_events=GooCanvas.CanvasPointerEvents.NONE)
1327 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1328 line_width =
max(0.1, math.log(float(rx_bytes)/rx_count/self.sample_period)*k)
1329 arrow.set_property(
"line-width", line_width)
1331 pos1_x, pos1_y = transmitter.get_position()
1332 pos2_x, pos2_y = receiver.get_position()
1333 points = GooCanvas.CanvasPoints.new(2)
1334 points.set_point(0, pos1_x, pos1_y)
1335 points.set_point(1, pos2_x, pos2_y)
1336 arrow.set_property(
"points", points)
1338 kbps = float(rx_bytes*8)/1e3/rx_count/self.sample_period
1339 label.set_properties(visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
1340 visibility_threshold=0.5,
1341 font=(
"Sans Serif %f" % int(1+BITRATE_FONT_SIZE*k)))
1342 angle = math.atan2((pos2_y - pos1_y), (pos2_x - pos1_x))
1343 if -PI_OVER_2 <= angle <= PI_OVER_2:
1344 label.set_properties(text=(
"%.2f kbit/s →" % (kbps,)),
1345 alignment=Pango.Alignment.CENTER,
1346 anchor=GooCanvas.CanvasAnchorType.S,
1347 x=0, y=-line_width/2)
1349 label.set_properties(text=(
"← %.2f kbit/s" % (kbps,)),
1350 alignment=Pango.Alignment.CENTER,
1351 anchor=GooCanvas.CanvasAnchorType.N,
1352 x=0, y=line_width/2)
1354 lx, ly = self._get_label_over_line_position(pos1_x, pos1_y,
1359 label.set_transform(M)
1362 warnings.warn(
"PyGobject bug causing label position error; "
1363 "should be fixed in PyGObject >= 3.29.1")
1364 label.set_properties(x=(lx + label.props.x),
1365 y=(ly + label.props.y))
1367 new_arrows.append((arrow, label))
1369 self._transmission_arrows = new_arrows + old_arrows
1372 def _update_drops_view(self):
1374 for drop_set
in self._last_drops:
1375 for drop
in drop_set:
1376 key = drop.transmitter.GetId()
1377 drop_bytes, count = drops_average.get(key, (0, 0))
1378 drop_bytes += drop.bytes
1380 drops_average[key] = drop_bytes, count
1382 old_arrows = self._drop_arrows
1383 for arrow, label
in old_arrows:
1384 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1385 label.set_property(
"visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
1389 vadjustment = self._scrolled_window.get_vadjustment()
1390 bottom_y = vadjustment.get_value() + vadjustment.get_page_size()
1391 dummy, edge_y = self.canvas.convert_from_pixels(0, bottom_y)
1393 k = self.node_size_adjustment.get_value()/5
1395 for transmitter_id, (drop_bytes, drop_count)
in drops_average.items():
1396 transmitter = self.get_node(transmitter_id)
1398 arrow, label = old_arrows.pop()
1400 arrow = GooCanvas.CanvasPolyline(line_width=2.0, stroke_color_rgba=0xC00000C0, close_path=
False, end_arrow=
True, pointer_events=GooCanvas.CanvasPointerEvents.NONE)
1401 arrow.set_property(
"parent", self.canvas.get_root_item())
1404 label = GooCanvas.CanvasText(pointer_events=GooCanvas.CanvasPointerEvents.NONE)
1405 label.set_property(
"parent", self.canvas.get_root_item())
1408 arrow.set_property(
"visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
1409 arrow.set_property(
"line-width",
max(0.1, math.log(float(drop_bytes)/drop_count/self.sample_period)*k))
1410 pos1_x, pos1_y = transmitter.get_position()
1411 pos2_x, pos2_y = pos1_x, edge_y
1412 points = GooCanvas.CanvasPoints.new(2)
1413 points.set_point(0, pos1_x, pos1_y)
1414 points.set_point(1, pos2_x, pos2_y)
1415 arrow.set_property(
"points", points)
1417 label.set_properties(visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
1418 visibility_threshold=0.5,
1419 font=(
"Sans Serif %i" % int(1+BITRATE_FONT_SIZE*k)),
1420 text=(
"%.2f kbit/s" % (float(drop_bytes*8)/1e3/drop_count/self.sample_period,)),
1421 alignment=Pango.Alignment.CENTER,
1422 x=(pos1_x + pos2_x)/2,
1423 y=(pos1_y + pos2_y)/2)
1425 new_arrows.append((arrow, label))
1427 self._drop_arrows = new_arrows + old_arrows
1430 def update_view_timeout(self):
1434 while not self.simulation.lock.acquire(
False):
1435 while Gtk.events_pending():
1436 Gtk.main_iteration()
1437 pause_messages = self.simulation.pause_messages
1438 self.simulation.pause_messages = []
1441 self.simulation.target_time = ns.core.Simulator.Now ().GetSeconds () + self.sample_period
1444 self.simulation.lock.release()
1448 dialog = Gtk.MessageDialog(parent=self.window, flags=0, type=Gtk.MessageType.WARNING, buttons=Gtk.ButtonsType.OK,
1449 message_format=
'\n'.join(pause_messages))
1450 dialog.connect(
"response",
lambda d, r: d.destroy())
1452 self.play_button.set_active(
False)
1455 if not self.play_button.get_active():
1456 self._update_timeout_id =
None
1460 self.simulation.go.set()
1464 def _start_update_timer(self):
1465 if self._update_timeout_id
is not None:
1466 GLib.source_remove(self._update_timeout_id)
1468 self._update_timeout_id = GLib.timeout_add(int(SAMPLE_PERIOD/
min(self.speed, 1)*1e3),
1469 self.update_view_timeout,
1470 priority=PRIORITY_UPDATE_VIEW)
1472 def _on_play_button_toggled(self, button):
1473 if button.get_active():
1474 self._start_update_timer()
1476 if self._update_timeout_id
is not None:
1477 GLib.source_remove(self._update_timeout_id)
1479 def _quit(self, *dummy_args):
1480 if self._update_timeout_id
is not None:
1481 GLib.source_remove(self._update_timeout_id)
1482 self._update_timeout_id =
None
1483 self.simulation.quit =
True
1484 self.simulation.go.set()
1485 self.simulation.join()
1488 def _monkey_patch_ipython(self):
1495 original_runcode = self.ipython.runcode
1496 def runcode(ip, *args):
1498 self.simulation.lock.acquire()
1500 return original_runcode(*args)
1503 self.simulation.lock.release()
1505 self.ipython.runcode = types.MethodType(runcode, self.ipython)
1507 def autoscale_view(self):
1510 self._update_node_positions()
1511 positions = [node.get_position()
for node
in self.nodes.values()]
1512 min_x, min_y =
min(x
for (x,y)
in positions),
min(y
for (x,y)
in positions)
1513 max_x, max_y =
max(x
for (x,y)
in positions),
max(y
for (x,y)
in positions)
1514 min_x_px, min_y_px = self.canvas.convert_to_pixels(min_x, min_y)
1515 max_x_px, max_y_px = self.canvas.convert_to_pixels(max_x, max_y)
1518 dx_px = max_x_px - min_x_px
1519 dy_px = max_y_px - min_y_px
1520 hadj = self._scrolled_window.get_hadjustment()
1521 vadj = self._scrolled_window.get_vadjustment()
1522 new_dx, new_dy = 1.5*dx_px, 1.5*dy_px
1524 if new_dx == 0
or new_dy == 0:
1527 self.zoom.set_value(
min(hadj.get_page_size()/new_dx, vadj.get_page_size()/new_dy))
1529 x1, y1 = self.canvas.convert_from_pixels(hadj.get_value(), vadj.get_value())
1530 x2, y2 = self.canvas.convert_from_pixels((hadj.get_value() +
1531 hadj.get_page_size()),
1533 vadj.get_page_size()))
1536 center_x = (min_x + max_x) / 2
1537 center_y = (min_y + max_y) / 2
1539 self.canvas.scroll_to(center_x - width/2, center_y - height/2)
1544 self.scan_topology()
1545 self.window.connect(
"delete-event", self._quit)
1547 GLib.timeout_add(200, self.autoscale_view)
1548 self.simulation.
start()
1555 self._monkey_patch_ipython()
1560 def on_root_button_press_event(self, view, target, event):
1561 if event.button == 1:
1562 self.select_node(
None)
1565 def on_node_button_press_event(self, view, target, event, node):
1566 button = event.button
1568 self.select_node(node)
1571 self.popup_node_menu(node, event)
1574 self.begin_node_drag(node, event)
1578 def on_node_button_release_event(self, view, target, event, node):
1579 if event.button == 2:
1580 self.end_node_drag(node)
1584 class NodeDragState(object):
1585 def __init__(self, canvas_x0, canvas_y0, sim_x0, sim_y0):
1586 self.canvas_x0 = canvas_x0
1587 self.canvas_y0 = canvas_y0
1588 self.sim_x0 = sim_x0
1589 self.sim_y0 = sim_y0
1590 self.motion_signal =
None
1592 def begin_node_drag(self, node, event):
1593 self.simulation.lock.acquire()
1595 ns3_node = ns.network.NodeList.GetNode(node.node_index)
1596 mob = ns3_node.GetObject(ns.mobility.MobilityModel.GetTypeId())
1599 if self.node_drag_state
is not None:
1601 pos = mob.GetPosition()
1603 self.simulation.lock.release()
1604 devpos = self.canvas.get_window().get_device_position(event.device)
1605 x0, y0 = self.canvas.convert_from_pixels(devpos.x, devpos.y)
1606 self.node_drag_state = self.NodeDragState(x0, y0, pos.x, pos.y)
1607 self.node_drag_state.motion_signal = node.canvas_item.connect(
"motion-notify-event", self.node_drag_motion, node)
1609 def node_drag_motion(self, item, targe_item, event, node):
1610 self.simulation.lock.acquire()
1612 ns3_node = ns.network.NodeList.GetNode(node.node_index)
1613 mob = ns3_node.GetObject(ns.mobility.MobilityModel.GetTypeId())
1616 if self.node_drag_state
is None:
1618 devpos = self.canvas.get_window().get_device_position(event.device)
1619 canvas_x, canvas_y = self.canvas.convert_from_pixels(devpos.x, devpos.y)
1620 dx = (canvas_x - self.node_drag_state.canvas_x0)
1621 dy = (canvas_y - self.node_drag_state.canvas_y0)
1622 pos = mob.GetPosition()
1626 mob.SetPosition(pos)
1629 self.simulation.lock.release()
1632 def end_node_drag(self, node):
1633 if self.node_drag_state
is None:
1635 node.canvas_item.disconnect(self.node_drag_state.motion_signal)
1636 self.node_drag_state =
None
1638 def popup_node_menu(self, node, event):
1640 self.emit(
"populate-node-menu", node, menu)
1641 menu.popup(
None,
None,
None,
None, event.button, event.time)
1643 def _update_ipython_selected_node(self):
1652 if self.selected_node
is None:
1655 self.simulation.lock.acquire()
1657 ns3_node = ns.network.NodeList.GetNode(self.selected_node.node_index)
1659 self.simulation.lock.release()
1660 self.ipython.updateNamespace({
'selected_node': ns3_node})
1663 def select_node(self, node):
1664 if isinstance(node, ns.network.Node):
1665 node = self.nodes[node.GetId()]
1666 elif isinstance(node, (int, long)):
1667 node = self.nodes[node]
1668 elif isinstance(node, Node):
1673 raise TypeError(
"expected None, int, viz.Node or ns.network.Node, not %r" % node)
1675 if node
is self.selected_node:
1678 if self.selected_node
is not None:
1679 self.selected_node.selected =
False
1680 self.selected_node = node
1681 if self.selected_node
is not None:
1682 self.selected_node.selected =
True
1684 if self._show_transmissions_mode == ShowTransmissionsMode.SELECTED:
1685 if self.selected_node
is None:
1686 self.simulation.set_nodes_of_interest([])
1688 self.simulation.set_nodes_of_interest([self.selected_node.node_index])
1690 self._update_ipython_selected_node()
1693 def add_information_window(self, info_win):
1694 self.information_windows.append(info_win)
1695 self.simulation.lock.acquire()
1699 self.simulation.lock.release()
1701 def remove_information_window(self, info_win):
1702 self.information_windows.remove(info_win)
1704 def _canvas_tooltip_cb(self, canvas, x, y, keyboard_mode, tooltip):
1706 hadj = self._scrolled_window.get_hadjustment()
1707 vadj = self._scrolled_window.get_vadjustment()
1708 x, y = self.canvas.convert_from_pixels(hadj.get_value() + x, vadj.get_value() + y)
1709 item = self.canvas.get_item_at(x, y,
True)
1713 while item
is not None:
1714 obj = getattr(item,
"pyviz_object",
None)
1716 obj.tooltip_query(tooltip)
1718 item = item.props.parent
1721 def _get_export_file_name(self):
1722 sel = Gtk.FileChooserDialog(
"Save...", self.canvas.get_toplevel(),
1723 Gtk.FileChooserAction.SAVE,
1724 (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
1725 Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
1726 sel.set_default_response(Gtk.ResponseType.OK)
1727 sel.set_local_only(
True)
1728 sel.set_do_overwrite_confirmation(
True)
1729 sel.set_current_name(
"Unnamed.pdf")
1731 filter = Gtk.FileFilter()
1732 filter.set_name(
"Embedded PostScript")
1733 filter.add_mime_type(
"image/x-eps")
1734 sel.add_filter(filter)
1736 filter = Gtk.FileFilter()
1737 filter.set_name(
"Portable Document Graphics")
1738 filter.add_mime_type(
"application/pdf")
1739 sel.add_filter(filter)
1741 filter = Gtk.FileFilter()
1742 filter.set_name(
"Scalable Vector Graphics")
1743 filter.add_mime_type(
"image/svg+xml")
1744 sel.add_filter(filter)
1747 if resp != Gtk.ResponseType.OK:
1751 file_name = sel.get_filename()
1755 def _take_screenshot(self, dummy_button):
1757 file_name = self._get_export_file_name()
1758 if file_name
is None:
1762 x1 = self._scrolled_window.get_hadjustment().get_value()
1763 y1 = self._scrolled_window.get_vadjustment().get_value()
1764 x2 = x1 + self._scrolled_window.get_hadjustment().get_page_size()
1765 y2 = y1 + self._scrolled_window.get_vadjustment().get_page_size()
1766 bounds = GooCanvas.CanvasBounds()
1767 bounds.x1, bounds.y1 = self.canvas.convert_from_pixels(x1, y1)
1768 bounds.x2, bounds.y2 = self.canvas.convert_from_pixels(x2, y2)
1769 dest_width = bounds.x2 - bounds.x1
1770 dest_height = bounds.y2 - bounds.y1
1773 dummy, extension = os.path.splitext(file_name)
1774 extension = extension.lower()
1775 if extension ==
'.eps':
1776 surface = cairo.PSSurface(file_name, dest_width, dest_height)
1777 elif extension ==
'.pdf':
1778 surface = cairo.PDFSurface(file_name, dest_width, dest_height)
1779 elif extension ==
'.svg':
1780 surface = cairo.SVGSurface(file_name, dest_width, dest_height)
1782 dialog = Gtk.MessageDialog(parent = self.canvas.get_toplevel(),
1783 flags = Gtk.DialogFlags.DESTROY_WITH_PARENT,
1784 type = Gtk.MessageType.ERROR,
1785 buttons = Gtk.ButtonsType.OK,
1786 message_format =
"Unknown extension '%s' (valid extensions are '.eps', '.svg', and '.pdf')"
1793 cr = cairo.Context(surface)
1794 cr.translate(-bounds.x1, -bounds.y1)
1795 self.canvas.render(cr, bounds, self.zoom.get_value())
1799 def set_follow_node(self, node):
1800 if isinstance(node, ns.network.Node):
1801 node = self.nodes[node.GetId()]
1802 self.follow_node = node
1804 def _start_shell(self, dummy_button):
1805 if self.shell_window
is not None:
1806 self.shell_window.present()
1809 self.shell_window = Gtk.Window()
1810 self.shell_window.set_size_request(750,550)
1811 self.shell_window.set_resizable(
True)
1812 scrolled_window = Gtk.ScrolledWindow()
1813 scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
1814 Gtk.PolicyType.AUTOMATIC)
1816 self.ipython.modify_font(Pango.FontDescription(SHELL_FONT))
1817 self.ipython.set_wrap_mode(Gtk.WrapMode.CHAR)
1819 scrolled_window.add(self.ipython)
1820 scrolled_window.show()
1821 self.shell_window.add(scrolled_window)
1822 self.shell_window.show()
1823 self.shell_window.connect(
'destroy', self._on_shell_window_destroy)
1825 self._update_ipython_selected_node()
1826 self.ipython.updateNamespace({
'viz': self})
1829 def _on_shell_window_destroy(self, window):
1830 self.shell_window =
None
1833 initialization_hooks = []
1837 Adds a callback to be called after
1838 the visualizer is initialized, like this::
1839 initialization_hook(visualizer, *args)
1841 global initialization_hooks
1842 initialization_hooks.append((hook, args))
1851 viz.canvas.set_bounds(cx1, cy1, cx2, cy2)
1856 assert Visualizer.INSTANCE
is None
1857 if _import_error
is not None:
1859 print(
"No visualization support (%s)." % (str(_import_error),),
1861 ns.core.Simulator.Run()
1865 for hook, args
in initialization_hooks:
1866 GLib.idle_add(hook, viz, *args)
1867 ns.network.Packet.EnablePrinting()
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)
_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)