2 from __future__
import division
5 LAYOUT_ALGORITHM =
'neato'
6 REPRESENT_CHANNELS_AS_NODES = 1
7 DEFAULT_NODE_SIZE = 3.0
8 DEFAULT_TRANSMISSIONS_MEMORY = 5
13 PRIORITY_UPDATE_MODEL = -100
14 PRIORITY_UPDATE_VIEW = 200
17 if platform.system() ==
"Windows":
18 SHELL_FONT =
"Lucida Console 9"
20 SHELL_FONT =
"Luxi Mono 10"
45 from higcontainer
import HIGContainer
46 gobject.threads_init()
51 except ImportError, _import_error:
52 import dummy_threading
as threading
61 from base
import InformationWindow, PyVizObject, Link, lookup_netdevice_traits, PIXELS_PER_METER
62 from base
import transform_distance_simulation_to_canvas, transform_point_simulation_to_canvas
63 from base
import transform_distance_canvas_to_simulation, transform_point_canvas_to_simulation
64 from base
import load_plugins, register_plugin, plugins
67 PI_TIMES_2 = math.pi*2
75 'query-extra-tooltip-info': (gobject.SIGNAL_RUN_LAST,
None, (object,)),
85 self.canvas_item.set_data(
"pyviz-object", self)
103 def set_svg_icon(self, file_base_name, width=None, height=None, align_x=0.5, align_y=0.5):
105 Set a background SVG icon for the node.
107 @param file_base_name: base file name, including .svg
108 extension, of the svg file. Place the file in the folder
109 src/contrib/visualizer/resource.
111 @param width: scale to the specified width, in meters
112 @param width: scale to the specified height, in meters
114 @param align_x: horizontal alignment of the icon relative to
115 the node position, from 0 (icon fully to the left of the node)
116 to 1.0 (icon fully to the right of the node)
118 @param align_y: vertical alignment of the icon relative to the
119 node position, from 0 (icon fully to the top of the node) to
120 1.0 (icon fully to the bottom of the node)
123 if width
is None and height
is None:
124 raise ValueError(
"either width or height must be given")
125 rsvg_handle = svgitem.rsvg_handle_factory(file_base_name)
126 x = self.canvas_item.props.center_x
127 y = self.canvas_item.props.center_y
129 self.svg_item.props.parent = self.visualizer.canvas.get_root_item()
130 self.svg_item.props.pointer_events = 0
131 self.svg_item.lower(
None)
132 self.svg_item.props.visibility = goocanvas.ITEM_VISIBLE_ABOVE_THRESHOLD
133 if width
is not None:
135 if height
is not None:
148 assert isinstance(label, basestring)
153 w = self.svg_item.width
154 h = self.svg_item.height
155 self.svg_item.set_properties(x=(x - (1-self.
svg_align_x)*w),
160 self.visualizer.simulation.lock.acquire()
162 ns3_node = ns.network.NodeList.GetNode(self.
node_index)
163 ipv4 = ns3_node.GetObject(ns.internet.Ipv4.GetTypeId())
164 ipv6 = ns3_node.GetObject(ns.internet.Ipv6.GetTypeId())
166 name =
'<b><u>Node %i</u></b>' % self.
node_index
167 node_name = ns.core.Names.FindName (ns3_node)
168 if len(node_name)!=0:
169 name +=
' <b>(' + node_name +
')</b>'
174 self.emit(
"query-extra-tooltip-info", lines)
176 mob = ns3_node.GetObject(ns.mobility.MobilityModel.GetTypeId())
178 lines.append(
' <b>Mobility Model</b>: %s' % mob.GetInstanceTypeId().GetName())
180 for devI
in range(ns3_node.GetNDevices()):
182 lines.append(
' <u>NetDevice %i:</u>' % devI)
183 dev = ns3_node.GetDevice(devI)
184 name = ns.core.Names.FindName(dev)
186 lines.append(
' <b>Name:</b> %s' % name)
187 devname = dev.GetInstanceTypeId().GetName()
188 lines.append(
' <b>Type:</b> %s' % devname)
191 ipv4_idx = ipv4.GetInterfaceForDevice(dev)
194 '%s/%s' % (ipv4.GetAddress(ipv4_idx, i).GetLocal(),
195 ipv4.GetAddress(ipv4_idx, i).GetMask())
196 for i
in range(ipv4.GetNAddresses(ipv4_idx))]
197 lines.append(
' <b>IPv4 Addresses:</b> %s' %
'; '.join(addresses))
200 ipv6_idx = ipv6.GetInterfaceForDevice(dev)
203 '%s/%s' % (ipv6.GetAddress(ipv6_idx, i).GetAddress(),
204 ipv6.GetAddress(ipv6_idx, i).GetPrefix())
205 for i
in range(ipv6.GetNAddresses(ipv6_idx))]
206 lines.append(
' <b>IPv6 Addresses:</b> %s' %
'; '.join(addresses))
208 lines.append(
' <b>MAC Address:</b> %s' % (dev.GetAddress(),))
210 tooltip.set_markup(
'\n'.join(lines))
212 self.visualizer.simulation.lock.release()
224 selected = property(_get_selected, _set_selected)
231 highlighted = property(_get_highlighted, _set_highlighted)
238 """Update the node aspect to reflect the selected/highlighted state"""
245 fill_color_rgba = (self.
_color & 0xffffff00) | alpha
246 self.canvas_item.set_properties(radius_x=size, radius_y=size,
247 fill_color_rgba=fill_color_rgba)
251 line_width = size*.15
253 stroke_color =
'yellow'
255 stroke_color =
'black'
256 self.canvas_item.set_properties(line_width=line_width, stroke_color=stroke_color)
258 if self.
_label is not None:
261 font=
"Sans Serif 10",
262 fill_color_rgba=0x808080ff,
263 alignment=pango.ALIGN_CENTER,
265 parent=self.visualizer.canvas.get_root_item(),
267 self._label_canvas_item.lower(
None)
269 self._label_canvas_item.set_properties(visibility=goocanvas.ITEM_VISIBLE_ABOVE_THRESHOLD,
274 self.canvas_item.set_property(
"center_x", x)
275 self.canvas_item.set_property(
"center_y", y)
279 for link
in self.
links:
283 self._label_canvas_item.set_properties(x=x, y=(y+self.
_size*3))
286 return (self.canvas_item.get_property(
"center_x"), self.canvas_item.get_property(
"center_y"))
293 if isinstance(color, str):
294 color = gtk.gdk.color_parse(color)
295 color = ((color.red>>8) << 24) | ((color.green>>8) << 16) | ((color.blue>>8) << 8) | 0xff
300 assert isinstance(link, Link)
301 self.links.append(link)
304 assert isinstance(link, Link)
305 self.links.remove(link)
310 node = ns.network.NodeList.GetNode(self.
node_index)
311 mobility = node.GetObject(ns.mobility.MobilityModel.GetTypeId())
321 stroke_color=
"grey", line_width=2.0,
322 line_dash=goocanvas.LineDash([10.0, 10.0 ]),
323 visibility=goocanvas.ITEM_VISIBLE)
324 self.canvas_item.set_data(
"pyviz-object", self)
328 self.canvas_item.set_property(
"center_x", x)
329 self.canvas_item.set_property(
"center_y", y)
331 for link
in self.
links:
335 return (self.canvas_item.get_property(
"center_x"), self.canvas_item.get_property(
"center_y"))
340 assert isinstance(node1, Node)
341 assert isinstance(node2, (Node, Channel))
344 self.
canvas_item = goocanvas.Path(line_width=1.0, stroke_color=
"black")
345 self.canvas_item.set_data(
"pyviz-object", self)
346 self.node1.links.append(self)
347 self.node2.links.append(self)
350 pos1_x, pos1_y = self.node1.get_position()
351 pos2_x, pos2_y = self.node2.get_position()
352 self.canvas_item.set_property(
"data",
"M %r %r L %r %r" % (pos1_x, pos1_y, pos2_x, pos2_y))
358 super(SimulationThread, self).
__init__()
359 assert isinstance(viz, Visualizer)
362 self.
go = threading.Event()
372 self.sim_helper.SetNodesOfInterest(nodes)
388 if ns3.core.Simulator.IsFinished():
389 self.viz.play_button.set_sensitive(
False)
394 self.sim_helper.SimulatorRunUntil(ns.core.Seconds(self.
target_time))
396 self.pause_messages.extend(self.sim_helper.GetPauseMessages())
397 gobject.idle_add(self.viz.update_model, priority=PRIORITY_UPDATE_MODEL)
413 if _import_error
is None:
417 'populate-node-menu': (gobject.SIGNAL_RUN_LAST,
None, (object, gtk.Menu,)),
421 'simulation-periodic-update': (gobject.SIGNAL_RUN_LAST,
None, ()),
424 'topology-scanned': (gobject.SIGNAL_RUN_LAST,
None, ()),
427 'update-view': (gobject.SIGNAL_RUN_LAST,
None, ()),
432 assert Visualizer.INSTANCE
is None
433 Visualizer.INSTANCE = self
469 for plugin
in plugins:
473 assert isinstance(mode, ShowTransmissionsMode)
476 self.simulation.set_nodes_of_interest(range(ns.network.NodeList.GetNNodes()))
478 self.simulation.set_nodes_of_interest([])
481 self.simulation.set_nodes_of_interest([])
483 self.simulation.set_nodes_of_interest([self.selected_node.node_index])
486 expander = gtk.Expander(
"Advanced")
489 main_vbox = gobject.new(gtk.VBox, border_width=8, visible=
True)
490 expander.add(main_vbox)
492 main_hbox1 = gobject.new(gtk.HBox, border_width=8, visible=
True)
493 main_vbox.pack_start(main_hbox1)
495 show_transmissions_group = HIGContainer(
"Show transmissions")
496 show_transmissions_group.show()
497 main_hbox1.pack_start(show_transmissions_group,
False,
False, 8)
499 vbox = gtk.VBox(
True, 4)
501 show_transmissions_group.add(vbox)
503 all_nodes = gtk.RadioButton(
None)
504 all_nodes.set_label(
"All nodes")
505 all_nodes.set_active(
True)
509 selected_node = gtk.RadioButton(all_nodes)
511 selected_node.set_label(
"Selected node")
512 selected_node.set_active(
False)
513 vbox.add(selected_node)
515 no_node = gtk.RadioButton(all_nodes)
517 no_node.set_label(
"Disabled")
518 no_node.set_active(
False)
522 if radio.get_active():
524 all_nodes.connect(
"toggled", toggled)
527 if radio.get_active():
529 no_node.connect(
"toggled", toggled)
532 if radio.get_active():
534 selected_node.connect(
"toggled", toggled)
538 misc_settings_group = HIGContainer(
"Misc Settings")
539 misc_settings_group.show()
540 main_hbox1.pack_start(misc_settings_group,
False,
False, 8)
541 settings_hbox = gobject.new(gtk.HBox, border_width=8, visible=
True)
542 misc_settings_group.add(settings_hbox)
545 vbox = gobject.new(gtk.VBox, border_width=0, visible=
True)
546 scale = gobject.new(gtk.HScale, visible=
True, digits=2)
547 vbox.pack_start(scale,
True,
True, 0)
548 vbox.pack_start(gobject.new(gtk.Label, label=
"Node Size", visible=
True),
True,
True, 0)
549 settings_hbox.pack_start(vbox,
False,
False, 6)
551 def node_size_changed(adj):
552 for node
in self.nodes.itervalues():
553 node.set_size(adj.value)
554 self.node_size_adjustment.connect(
"value-changed", node_size_changed)
555 self.node_size_adjustment.set_all(DEFAULT_NODE_SIZE, 0.01, 20, 0.1)
558 vbox = gobject.new(gtk.VBox, border_width=0, visible=
True)
559 scale = gobject.new(gtk.HScale, visible=
True, digits=1)
560 vbox.pack_start(scale,
True,
True, 0)
561 vbox.pack_start(gobject.new(gtk.Label, label=
"Tx. Smooth Factor (s)", visible=
True),
True,
True, 0)
562 settings_hbox.pack_start(vbox,
False,
False, 6)
564 self.transmissions_smoothing_adjustment.set_all(DEFAULT_TRANSMISSIONS_MEMORY*0.1, 0.1, 10, 0.1)
569 __slots__ = [
'initial_mouse_pos',
'initial_canvas_pos',
'motion_signal']
572 self.canvas.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR))
574 x, y, dummy = widget.window.get_pointer()
575 self._panning_state.initial_mouse_pos = (x, y)
576 x = self._scrolled_window.get_hadjustment().value
577 y = self._scrolled_window.get_vadjustment().value
578 self._panning_state.initial_canvas_pos = (x, y)
579 self._panning_state.motion_signal = self.canvas.connect(
"motion-notify-event", self.
_panning_motion)
584 self.canvas.window.set_cursor(
None)
585 self.canvas.disconnect(self._panning_state.motion_signal)
591 x, y, dummy = widget.window.get_pointer()
593 x, y = event.x, event.y
595 hadj = self._scrolled_window.get_hadjustment()
596 vadj = self._scrolled_window.get_vadjustment()
597 mx0, my0 = self._panning_state.initial_mouse_pos
598 cx0, cy0 = self._panning_state.initial_canvas_pos
602 hadj.value = cx0 - dx
603 vadj.value = cy0 - dy
607 if event.button == 2:
613 if event.button == 2:
619 if event.direction == gtk.gdk.SCROLL_UP:
620 self.zoom.value *= 1.25
622 elif event.direction == gtk.gdk.SCROLL_DOWN:
623 self.zoom.value /= 1.25
628 return self._scrolled_window.get_hadjustment()
630 return self._scrolled_window.get_vadjustment()
633 self.
window = gtk.Window()
634 vbox = gtk.VBox(); vbox.show()
635 self.window.add(vbox)
638 self.
canvas = goocanvas.Canvas()
642 self.canvas.props.has_tooltip =
True
645 sw = gtk.ScrolledWindow(); sw.show()
648 vbox.pack_start(sw,
True,
True, 4)
649 self.canvas.set_size_request(600, 450)
650 self.canvas.set_bounds(-10000, -10000, 10000, 10000)
651 self.canvas.scroll_to(0, 0)
654 self.canvas.get_root_item().add_child(self.
links_group)
655 self.links_group.set_property(
"visibility", goocanvas.ITEM_VISIBLE)
658 self.channels_group.set_property(
"visibility", goocanvas.ITEM_VISIBLE)
661 self.canvas.get_root_item().add_child(self.
nodes_group)
662 self.nodes_group.set_property(
"visibility", goocanvas.ITEM_VISIBLE)
667 hbox = gtk.HBox(); hbox.show()
668 vbox.pack_start(hbox,
False,
False, 4)
671 zoom_adj = gtk.Adjustment(1.0, 0.01, 10.0, 0.02, 1.0, 0)
673 def _zoom_changed(adj):
674 self.canvas.set_scale(adj.value)
675 zoom_adj.connect(
"value-changed", _zoom_changed)
676 zoom = gtk.SpinButton(zoom_adj)
679 hbox.pack_start(gobject.new(gtk.Label, label=
" Zoom:", visible=
True),
False,
False, 4)
680 hbox.pack_start(zoom,
False,
False, 4)
681 _zoom_changed(zoom_adj)
684 speed_adj = gtk.Adjustment(1.0, 0.01, 10.0, 0.02, 1.0, 0)
685 def _speed_changed(adj):
686 self.
speed = adj.value
689 speed_adj.connect(
"value-changed", _speed_changed)
690 speed = gtk.SpinButton(speed_adj)
693 hbox.pack_start(gobject.new(gtk.Label, label=
" Speed:", visible=
True),
False,
False, 4)
694 hbox.pack_start(speed,
False,
False, 4)
695 _speed_changed(speed_adj)
698 self.
time_label = gobject.new(gtk.Label, label=
" Speed:", visible=
True)
699 self.time_label.set_width_chars(20)
700 hbox.pack_start(self.
time_label,
False,
False, 4)
703 screenshot_button = gobject.new(gtk.Button,
705 relief=gtk.RELIEF_NONE, focus_on_click=
False,
707 hbox.pack_start(screenshot_button,
False,
False, 4)
709 def load_button_icon(button, icon_name):
713 sys.stderr.write(
"Could not load icon %s due to missing gnomedesktop Python module\n" % icon_name)
715 icon = gnomedesktop.find_icon(gtk.icon_theme_get_default(), icon_name, 16, 0)
717 button.props.image = gobject.new(gtk.Image, file=icon, visible=
True)
719 load_button_icon(screenshot_button,
"applets-screenshooter")
723 if ipython_view
is not None:
724 shell_button = gobject.new(gtk.Button,
726 relief=gtk.RELIEF_NONE, focus_on_click=
False,
728 hbox.pack_start(shell_button,
False,
False, 4)
729 load_button_icon(shell_button,
"gnome-terminal")
734 image=gobject.new(gtk.Image, stock=gtk.STOCK_MEDIA_PLAY, visible=
True),
735 label=
"Simulate (F3)",
736 relief=gtk.RELIEF_NONE, focus_on_click=
False,
737 use_stock=
True, visible=
True)
738 accel_group = gtk.AccelGroup()
739 self.window.add_accel_group(accel_group)
740 self.play_button.add_accelerator(
"clicked", accel_group,
741 gtk.keysyms.F3, 0, gtk.ACCEL_VISIBLE)
752 print "scanning topology: %i nodes..." % (ns.network.NodeList.GetNNodes(),)
753 graph = pygraphviz.AGraph()
755 for nodeI
in range(ns.network.NodeList.GetNNodes()):
757 if seen_nodes == 100:
758 print "scan topology... %i nodes visited (%.1f%%)" % (nodeI, 100*nodeI/ns.network.NodeList.GetNNodes())
760 node = ns.network.NodeList.GetNode(nodeI)
761 node_name =
"Node %i" % nodeI
764 mobility = node.GetObject(ns.mobility.MobilityModel.GetTypeId())
765 if mobility
is not None:
766 node_view.set_color(
"red")
767 pos = mobility.GetPosition()
771 graph.add_node(node_name)
773 for devI
in range(node.GetNDevices()):
774 device = node.GetDevice(devI)
776 if device_traits.is_wireless:
778 if device_traits.is_virtual:
780 channel = device.GetChannel()
781 if channel.GetNDevices() > 2:
782 if REPRESENT_CHANNELS_AS_NODES:
785 channel_name =
"Channel %s" % id(channel)
786 graph.add_edge(node_name, channel_name)
791 for otherDevI
in range(channel.GetNDevices()):
792 otherDev = channel.GetDevice(otherDevI)
793 otherNode = otherDev.GetNode()
794 otherNodeView = self.
get_node(otherNode.GetId())
795 if otherNode
is not node:
796 if mobility
is None and not otherNodeView.has_mobility:
797 other_node_name =
"Node %i" % otherNode.GetId()
798 graph.add_edge(node_name, other_node_name)
801 for otherDevI
in range(channel.GetNDevices()):
802 otherDev = channel.GetDevice(otherDevI)
803 otherNode = otherDev.GetNode()
804 otherNodeView = self.
get_node(otherNode.GetId())
805 if otherNode
is not node:
806 if mobility
is None and not otherNodeView.has_mobility:
807 other_node_name =
"Node %i" % otherNode.GetId()
808 graph.add_edge(node_name, other_node_name)
811 print "scanning topology: calling graphviz layout"
812 graph.layout(LAYOUT_ALGORITHM)
813 for node
in graph.iternodes():
815 node_type, node_id = node.split(
' ')
816 pos_x, pos_y = [float(s)
for s
in node.attr[
'pos'].split(
',')]
817 if node_type ==
'Node':
818 obj = self.
nodes[int(node_id)]
819 elif node_type ==
'Channel':
821 obj.set_position(pos_x, pos_y)
823 print "scanning topology: all done."
824 self.emit(
"topology-scanned")
828 return self.
nodes[index]
830 node =
Node(self, index)
831 self.
nodes[index] = node
832 self.nodes_group.add_child(node.canvas_item)
839 return self.
channels[id(ns3_channel)]
842 self.
channels[id(ns3_channel)] = channel
843 self.channels_group.add_child(channel.canvas_item)
848 self.links_group.add_child(link.canvas_item)
849 link.canvas_item.lower(
None)
854 self.time_label.set_text(
"Time: %f s" % ns.core.Simulator.Now().GetSeconds())
865 self.emit(
"update-view")
868 for node
in self.nodes.itervalues():
869 if node.has_mobility:
870 ns3_node = ns.network.NodeList.GetNode(node.node_index)
871 mobility = ns3_node.GetObject(ns.mobility.MobilityModel.GetTypeId())
872 if mobility
is not None:
873 pos = mobility.GetPosition()
875 node.set_position(x, y)
877 hadj = self._scrolled_window.get_hadjustment()
878 vadj = self._scrolled_window.get_vadjustment()
879 px, py = self.canvas.convert_to_pixels(x, y)
880 hadj.value = px - hadj.page_size/2
881 vadj.value = py - vadj.page_size/2
884 if isinstance(node, ns.network.Node):
885 node = self.
nodes[node.GetId()]
886 elif isinstance(node, (int, long)):
887 node = self.
nodes[node]
888 elif isinstance(node, Node):
891 raise TypeError(
"expected int, viz.Node or ns.network.Node, not %r" % node)
893 x, y = node.get_position()
894 hadj = self._scrolled_window.get_hadjustment()
895 vadj = self._scrolled_window.get_vadjustment()
896 px, py = self.canvas.convert_to_pixels(x, y)
897 hadj.value = px - hadj.page_size/2
898 vadj.value = py - vadj.page_size/2
902 self.simulation.lock.acquire()
904 self.emit(
"simulation-periodic-update")
906 self.simulation.lock.release()
909 smooth_factor = int(self.transmissions_smoothing_adjustment.value*10)
911 transmissions = self.simulation.sim_helper.GetTransmissionSamples()
912 self._last_transmissions.append(transmissions)
914 self._last_transmissions.pop(0)
916 drops = self.simulation.sim_helper.GetPacketDropSamples()
917 self._last_drops.append(drops)
919 self._last_drops.pop(0)
922 hadj = self._scrolled_window.get_hadjustment()
923 vadj = self._scrolled_window.get_vadjustment()
924 bounds_x1, bounds_y1 = self.canvas.convert_from_pixels(hadj.value, vadj.value)
925 bounds_x2, bounds_y2 = self.canvas.convert_from_pixels(hadj.value + hadj.page_size,
926 vadj.value + vadj.page_size)
927 pos1_x, pos1_y, pos2_x, pos2_y = ns.visualizer.PyViz.LineClipping(bounds_x1, bounds_y1,
928 bounds_x2, bounds_y2,
931 return (pos1_x + pos2_x)/2, (pos1_y + pos2_y)/2
934 transmissions_average = {}
936 for transmission
in transmission_set:
937 key = (transmission.transmitter.GetId(), transmission.receiver.GetId())
938 rx_bytes, count = transmissions_average.get(key, (0, 0))
939 rx_bytes += transmission.bytes
941 transmissions_average[key] = rx_bytes, count
944 for arrow, label
in old_arrows:
945 arrow.set_property(
"visibility", goocanvas.ITEM_HIDDEN)
946 label.set_property(
"visibility", goocanvas.ITEM_HIDDEN)
949 k = self.node_size_adjustment.value/5
951 for (transmitter_id, receiver_id), (rx_bytes, rx_count)
in transmissions_average.iteritems():
952 transmitter = self.
get_node(transmitter_id)
953 receiver = self.
get_node(receiver_id)
955 arrow, label = old_arrows.pop()
957 arrow = goocanvas.Polyline(line_width=2.0, stroke_color_rgba=0x00C000C0, close_path=
False, end_arrow=
True)
958 arrow.set_property(
"parent", self.canvas.get_root_item())
959 arrow.props.pointer_events = 0
962 label = goocanvas.Text(parent=self.canvas.get_root_item(), pointer_events=0)
965 arrow.set_property(
"visibility", goocanvas.ITEM_VISIBLE)
966 line_width = max(0.1, math.log(float(rx_bytes)/rx_count/self.
sample_period)*k)
967 arrow.set_property(
"line-width", line_width)
969 pos1_x, pos1_y = transmitter.get_position()
970 pos2_x, pos2_y = receiver.get_position()
971 points = goocanvas.Points([(pos1_x, pos1_y), (pos2_x, pos2_y)])
972 arrow.set_property(
"points", points)
975 label.set_properties(visibility=goocanvas.ITEM_VISIBLE_ABOVE_THRESHOLD,
976 visibility_threshold=0.5,
977 font=(
"Sans Serif %f" % int(1+BITRATE_FONT_SIZE*k)))
978 angle = math.atan2((pos2_y - pos1_y), (pos2_x - pos1_x))
979 if -PI_OVER_2 <= angle <= PI_OVER_2:
980 label.set_properties(text=(
"%.2f kbit/s →" % (kbps,)),
981 alignment=pango.ALIGN_CENTER,
983 x=0, y=-line_width/2)
987 label.set_transform(M)
989 label.set_properties(text=(
"← %.2f kbit/s" % (kbps,)),
990 alignment=pango.ALIGN_CENTER,
997 label.set_transform(M)
999 new_arrows.append((arrow, label))
1007 for drop
in drop_set:
1008 key = drop.transmitter.GetId()
1009 drop_bytes, count = drops_average.get(key, (0, 0))
1010 drop_bytes += drop.bytes
1012 drops_average[key] = drop_bytes, count
1015 for arrow, label
in old_arrows:
1016 arrow.set_property(
"visibility", goocanvas.ITEM_HIDDEN)
1017 label.set_property(
"visibility", goocanvas.ITEM_HIDDEN)
1021 vadjustment = self._scrolled_window.get_vadjustment()
1022 bottom_y = vadjustment.value + vadjustment.page_size
1023 dummy, edge_y = self.canvas.convert_from_pixels(0, bottom_y)
1025 k = self.node_size_adjustment.value/5
1027 for transmitter_id, (drop_bytes, drop_count)
in drops_average.iteritems():
1028 transmitter = self.
get_node(transmitter_id)
1030 arrow, label = old_arrows.pop()
1032 arrow = goocanvas.Polyline(line_width=2.0, stroke_color_rgba=0xC00000C0, close_path=
False, end_arrow=
True)
1033 arrow.props.pointer_events = 0
1034 arrow.set_property(
"parent", self.canvas.get_root_item())
1037 label = goocanvas.Text()
1038 label.props.pointer_events = 0
1039 label.set_property(
"parent", self.canvas.get_root_item())
1042 arrow.set_property(
"visibility", goocanvas.ITEM_VISIBLE)
1043 arrow.set_property(
"line-width", max(0.1, math.log(float(drop_bytes)/drop_count/self.
sample_period)*k))
1044 pos1_x, pos1_y = transmitter.get_position()
1045 pos2_x, pos2_y = pos1_x, edge_y
1046 points = goocanvas.Points([(pos1_x, pos1_y), (pos2_x, pos2_y)])
1047 arrow.set_property(
"points", points)
1049 label.set_properties(visibility=goocanvas.ITEM_VISIBLE_ABOVE_THRESHOLD,
1050 visibility_threshold=0.5,
1051 font=(
"Sans Serif %i" % int(1+BITRATE_FONT_SIZE*k)),
1052 text=(
"%.2f kbit/s" % (float(drop_bytes*8)/1e3/drop_count/self.
sample_period,)),
1053 alignment=pango.ALIGN_CENTER,
1054 x=(pos1_x + pos2_x)/2,
1055 y=(pos1_y + pos2_y)/2)
1057 new_arrows.append((arrow, label))
1066 while not self.simulation.lock.acquire(
False):
1067 while gtk.events_pending():
1068 gtk.main_iteration()
1069 pause_messages = self.simulation.pause_messages
1070 self.simulation.pause_messages = []
1073 self.simulation.target_time = ns.core.Simulator.Now ().GetSeconds () + self.
sample_period
1076 self.simulation.lock.release()
1080 dialog = gtk.MessageDialog(parent=self.
window, flags=0, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_OK,
1081 message_format=
'\n'.join(pause_messages))
1082 dialog.connect(
"response",
lambda d, r: d.destroy())
1084 self.play_button.set_active(
False)
1087 if not self.play_button.get_active():
1092 self.simulation.go.set()
1102 priority=PRIORITY_UPDATE_VIEW)
1105 if button.get_active():
1115 self.simulation.quit =
True
1116 self.simulation.go.set()
1117 self.simulation.join()
1127 original_runcode = __IPYTHON__.runcode
1128 def runcode(ip, *args):
1130 self.simulation.lock.acquire()
1132 return original_runcode(*args)
1135 self.simulation.lock.release()
1137 __IPYTHON__.runcode = types.MethodType(runcode, __IPYTHON__)
1143 positions = [node.get_position()
for node
in self.nodes.itervalues()]
1144 min_x, min_y = min(x
for (x,y)
in positions), min(y
for (x,y)
in positions)
1145 max_x, max_y = max(x
for (x,y)
in positions), max(y
for (x,y)
in positions)
1146 min_x_px, min_y_px = self.canvas.convert_to_pixels(min_x, min_y)
1147 max_x_px, max_y_px = self.canvas.convert_to_pixels(max_x, max_y)
1150 dx_px = max_x_px - min_x_px
1151 dy_px = max_y_px - min_y_px
1152 hadj = self._scrolled_window.get_hadjustment()
1153 vadj = self._scrolled_window.get_vadjustment()
1154 new_dx, new_dy = 1.5*dx_px, 1.5*dy_px
1156 if new_dx == 0
or new_dy == 0:
1159 self.zoom.value = min(hadj.page_size/new_dx, vadj.page_size/new_dy)
1161 x1, y1 = self.canvas.convert_from_pixels(hadj.value, vadj.value)
1162 x2, y2 = self.canvas.convert_from_pixels(hadj.value+hadj.page_size, vadj.value+vadj.page_size)
1165 center_x = (min_x + max_x) / 2
1166 center_y = (min_y + max_y) / 2
1168 self.canvas.scroll_to(center_x - width/2, center_y - height/2)
1174 self.window.connect(
"delete-event", self.
_quit)
1177 self.simulation.start()
1190 if event.button == 1:
1195 if event.button == 1:
1198 elif event.button == 3:
1201 elif event.button == 2:
1207 if event.button == 2:
1213 def __init__(self, canvas_x0, canvas_y0, sim_x0, sim_y0):
1221 self.simulation.lock.acquire()
1223 ns3_node = ns.network.NodeList.GetNode(node.node_index)
1224 mob = ns3_node.GetObject(ns.mobility.MobilityModel.GetTypeId())
1229 pos = mob.GetPosition()
1231 self.simulation.lock.release()
1232 x, y, dummy = self.canvas.window.get_pointer()
1233 x0, y0 = self.canvas.convert_from_pixels(x, y)
1235 self.node_drag_state.motion_signal = node.canvas_item.connect(
"motion-notify-event", self.
node_drag_motion, node)
1238 self.simulation.lock.acquire()
1240 ns3_node = ns.network.NodeList.GetNode(node.node_index)
1241 mob = ns3_node.GetObject(ns.mobility.MobilityModel.GetTypeId())
1246 x, y, dummy = self.canvas.window.get_pointer()
1247 canvas_x, canvas_y = self.canvas.convert_from_pixels(x, y)
1248 dx = (canvas_x - self.node_drag_state.canvas_x0)
1249 dy = (canvas_y - self.node_drag_state.canvas_y0)
1250 pos = mob.GetPosition()
1254 mob.SetPosition(pos)
1257 self.simulation.lock.release()
1263 node.canvas_item.disconnect(self.node_drag_state.motion_signal)
1268 self.emit(
"populate-node-menu", node, menu)
1269 menu.popup(
None,
None,
None, event.button, event.time)
1283 self.simulation.lock.acquire()
1285 ns3_node = ns.network.NodeList.GetNode(self.selected_node.node_index)
1287 self.simulation.lock.release()
1288 __IPYTHON__.user_ns[
'selected_node'] = ns3_node
1292 if isinstance(node, ns.network.Node):
1293 node = self.
nodes[node.GetId()]
1294 elif isinstance(node, (int, long)):
1295 node = self.
nodes[node]
1296 elif isinstance(node, Node):
1301 raise TypeError(
"expected None, int, viz.Node or ns.network.Node, not %r" % node)
1307 self.selected_node.selected =
False
1310 self.selected_node.selected =
True
1314 self.simulation.set_nodes_of_interest([])
1316 self.simulation.set_nodes_of_interest([self.selected_node.node_index])
1322 self.information_windows.append(info_win)
1323 self.simulation.lock.acquire()
1327 self.simulation.lock.release()
1330 self.information_windows.remove(info_win)
1334 hadj = self._scrolled_window.get_hadjustment()
1335 vadj = self._scrolled_window.get_vadjustment()
1336 x, y = self.canvas.convert_from_pixels(hadj.value + x, vadj.value + y)
1337 item = self.canvas.get_item_at(x, y,
True)
1341 while item
is not None:
1342 obj = item.get_data(
"pyviz-object")
1344 obj.tooltip_query(tooltip)
1346 item = item.props.parent
1350 sel = gtk.FileChooserDialog(
"Save...", self.canvas.get_toplevel(),
1351 gtk.FILE_CHOOSER_ACTION_SAVE,
1352 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
1353 gtk.STOCK_SAVE, gtk.RESPONSE_OK))
1354 sel.set_default_response(gtk.RESPONSE_OK)
1355 sel.set_local_only(
True)
1356 sel.set_do_overwrite_confirmation(
True)
1357 sel.set_current_name(
"Unnamed.pdf")
1359 filter = gtk.FileFilter()
1360 filter.set_name(
"Embedded PostScript")
1361 filter.add_mime_type(
"image/x-eps")
1362 sel.add_filter(filter)
1364 filter = gtk.FileFilter()
1365 filter.set_name(
"Portable Document Graphics")
1366 filter.add_mime_type(
"application/pdf")
1367 sel.add_filter(filter)
1369 filter = gtk.FileFilter()
1370 filter.set_name(
"Scalable Vector Graphics")
1371 filter.add_mime_type(
"image/svg+xml")
1372 sel.add_filter(filter)
1375 if resp != gtk.RESPONSE_OK:
1379 file_name = sel.get_filename()
1386 if file_name
is None:
1390 x1 = self._scrolled_window.get_hadjustment().value
1391 y1 = self._scrolled_window.get_vadjustment().value
1392 x2 = x1 + self._scrolled_window.get_hadjustment().page_size
1393 y2 = y1 + self._scrolled_window.get_vadjustment().page_size
1394 bounds = goocanvas.Bounds()
1395 bounds.x1, bounds.y1 = self.canvas.convert_from_pixels(x1, y1)
1396 bounds.x2, bounds.y2 = self.canvas.convert_from_pixels(x2, y2)
1397 dest_width = bounds.x2 - bounds.x1
1398 dest_height = bounds.y2 - bounds.y1
1401 dummy, extension = os.path.splitext(file_name)
1402 extension = extension.lower()
1403 if extension ==
'.eps':
1404 surface = cairo.PSSurface(file_name, dest_width, dest_height)
1405 elif extension ==
'.pdf':
1406 surface = cairo.PDFSurface(file_name, dest_width, dest_height)
1407 elif extension ==
'.svg':
1408 surface = cairo.SVGSurface(file_name, dest_width, dest_height)
1410 dialog = gtk.MessageDialog(parent = self.canvas.get_toplevel(),
1411 flags = gtk.DIALOG_DESTROY_WITH_PARENT,
1412 type = gtk.MESSAGE_ERROR,
1413 buttons = gtk.BUTTONS_OK,
1414 message_format =
"Unknown extension '%s' (valid extensions are '.eps', '.svg', and '.pdf')"
1421 cr = cairo.Context(surface)
1422 cr.translate(-bounds.x1, -bounds.y1)
1423 self.canvas.render(cr, bounds, self.zoom.value)
1428 if isinstance(node, ns.network.Node):
1429 node = self.
nodes[node.GetId()]
1434 self.shell_window.present()
1438 self.shell_window.set_size_request(750,550)
1439 self.shell_window.set_resizable(
True)
1440 scrolled_window = gtk.ScrolledWindow()
1441 scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
1443 ipython.modify_font(pango.FontDescription(SHELL_FONT))
1444 ipython.set_wrap_mode(gtk.WRAP_CHAR)
1446 scrolled_window.add(ipython)
1447 scrolled_window.show()
1448 self.shell_window.add(scrolled_window)
1449 self.shell_window.show()
1453 __IPYTHON__.user_ns[
'viz'] = self
1460 initialization_hooks = []
1464 Adds a callback to be called after
1465 the visualizer is initialized, like this::
1466 initialization_hook(visualizer, *args)
1468 global initialization_hooks
1469 initialization_hooks.append((hook, args))
1478 viz.canvas.set_bounds(cx1, cy1, cx2, cy2)
1483 assert Visualizer.INSTANCE
is None
1484 if _import_error
is not None:
1486 print >> sys.stderr,
"No visualization support (%s)." % (str(_import_error),)
1487 ns.core.Simulator.Run()
1491 for hook, args
in initialization_hooks:
1492 gobject.idle_add(hook, viz, *args)
1493 ns.network.Packet.EnablePrinting()
def _update_node_positions
def _get_label_over_line_position
def add_initialization_hook
def _canvas_button_release
def on_enter_notify_event
def do_simulation_periodic_update
def on_root_button_press_event
def on_node_button_press_event
def set_nodes_of_interest
def _get_export_file_name
def on_leave_notify_event
def transform_point_simulation_to_canvas
def on_node_button_release_event
def transform_distance_simulation_to_canvas
def lookup_netdevice_traits
transmissions_smoothing_adjustment
def transform_distance_canvas_to_simulation
def add_information_window
def _update_transmissions_view
def _on_play_button_toggled
def remove_information_window
def set_show_transmissions_mode
def _monkey_patch_ipython
def _on_shell_window_destroy
def _create_advanced_controls
def _update_ipython_selected_node