pyviz: more fixes to make it work with PyGObject3
authorGustavo Carneiro <gjcarneiro@gmail.com>
Wed, 15 Aug 2018 12:27:09 -0700
changeset 13742 ca72220e64e4
parent 13741 aa283979c400
child 13743 ce5cd3cf2874
pyviz: more fixes to make it work with PyGObject3
src/visualizer/visualizer/core.py
--- a/src/visualizer/visualizer/core.py	Wed Aug 15 12:27:05 2018 -0700
+++ b/src/visualizer/visualizer/core.py	Wed Aug 15 12:27:09 2018 -0700
@@ -1,5 +1,5 @@
 # -*- Mode: python; coding: utf-8 -*-
-from __future__ import division
+from __future__ import division, print_function
 #from __future__ import with_statement
 
 LAYOUT_ALGORITHM = 'neato' # ['neato'|'dot'|'twopi'|'circo'|'fdp'|'nop']
@@ -13,6 +13,7 @@
 PRIORITY_UPDATE_MODEL = -100
 PRIORITY_UPDATE_VIEW = 200
 
+import warnings
 import platform
 if platform.system() == "Windows":
     SHELL_FONT = "Lucida Console 9"
@@ -35,7 +36,7 @@
     gi.require_version('GooCanvas', '2.0')
     gi.require_version('Gtk', '3.0')
     from gi.repository import GObject
-    GObject.threads_init()
+    from gi.repository import GLib
     import cairo
     gi.require_foreign("cairo")
     import pygraphviz
@@ -50,7 +51,7 @@
         import svgitem
     except ImportError:
         svgitem = None
-except ImportError, _import_error:
+except ImportError as _import_error:
     import dummy_threading as threading
 else:
     _import_error = None
@@ -665,7 +666,7 @@
                 self.sim_helper.SimulatorRunUntil(ns.core.Seconds(self.target_time))
                 #print "sim: Run until ended at current time: ", ns3.Simulator.Now ().GetSeconds ()
                 self.pause_messages.extend(self.sim_helper.GetPauseMessages())
-                GObject.idle_add(self.viz.update_model, priority=PRIORITY_UPDATE_MODEL)
+                GLib.idle_add(self.viz.update_model, priority=PRIORITY_UPDATE_MODEL)
                 #print "sim: Run until: ", self.target_time, ": finished."
             finally:
                 self.lock.release()
@@ -798,7 +799,7 @@
                                                visible=True)
         main_hbox1.pack_start(show_transmissions_group, False, False, 8)
 
-        vbox = Gtk.VBox(True, 4)
+        vbox = Gtk.VBox(homogeneous=True, spacing=4)
         vbox.show()
         show_transmissions_group.add(vbox)
 
@@ -886,10 +887,12 @@
         @param mode: mode to set.
         @return none
         """
-        self.canvas.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.FLEUR))
+        display = self.canvas.get_window().get_display()
+        cursor = Gdk.Cursor.new_for_display(display, Gdk.CursorType.FLEUR)
+        self.canvas.get_window().set_cursor(cursor)
         self._panning_state = self._PanningState()
-        _, x, y, _ = widget.get_window().get_pointer()
-        self._panning_state.initial_mouse_pos = (x, y)
+        pos = widget.get_window().get_device_position(event.device)
+        self._panning_state.initial_mouse_pos = (pos.x, pos.y)
         x = self._scrolled_window.get_hadjustment().get_value()
         y = self._scrolled_window.get_vadjustment().get_value()
         self._panning_state.initial_canvas_pos = (x, y)
@@ -920,7 +923,8 @@
         """
         assert self._panning_state is not None
         if event.is_hint:
-            _, x, y, _ = widget.get_window().get_pointer()
+            pos = widget.get_window().get_device_position(event.device)
+            x, y = pos.x, pos.y
         else:
             x, y = event.x, event.y
 
@@ -1001,7 +1005,10 @@
         vbox.pack_start(hbox, False, False, 4)
 
         # zoom
-        zoom_adj = Gtk.Adjustment(1.0, 0.01, 10.0, 0.02, 1.0, 0)
+        zoom_adj = Gtk.Adjustment(value=1.0, lower=0.01, upper=10.0,
+                                  step_increment=0.02,
+                                  page_increment=1.0,
+                                  page_size=1.0)
         self.zoom = zoom_adj
         def _zoom_changed(adj):
             self.canvas.set_scale(adj.get_value())
@@ -1014,7 +1021,9 @@
         _zoom_changed(zoom_adj)
 
         # speed
-        speed_adj = Gtk.Adjustment(1.0, 0.01, 10.0, 0.02, 1.0, 0)
+        speed_adj = Gtk.Adjustment(value=1.0, lower=0.01, upper=10.0,
+                                   step_increment=0.02,
+                                   page_increment=1.0, page_size=0)
         def _speed_changed(adj):
             self.speed = adj.get_value()
             self.sample_period = SAMPLE_PERIOD*adj.get_value()
@@ -1079,18 +1088,23 @@
 
         vbox.pack_start(self._create_advanced_controls(), False, False, 4)
 
-        self.window.set_default_size(Gdk.Screen.width()*2/3,
-                                     Gdk.Screen.height()*2/3)
+        display = Gdk.Display.get_default()
+        monitor = display.get_primary_monitor()
+        geometry = monitor.get_geometry()
+        scale_factor = monitor.get_scale_factor()
+        width = scale_factor * geometry.width
+        height = scale_factor * geometry.height
+        self.window.set_default_size(width * 2 / 3, height * 2 / 3)
         self.window.show()
 
     def scan_topology(self):
-        print "scanning topology: %i nodes..." % (ns.network.NodeList.GetNNodes(),)
+        print("scanning topology: %i nodes..." % (ns.network.NodeList.GetNNodes(),))
         graph = pygraphviz.AGraph()
         seen_nodes = 0
         for nodeI in range(ns.network.NodeList.GetNNodes()):
             seen_nodes += 1
             if seen_nodes == 100:
-                print "scan topology... %i nodes visited (%.1f%%)" % (nodeI, 100*nodeI/ns.network.NodeList.GetNNodes())
+                print("scan topology... %i nodes visited (%.1f%%)" % (nodeI, 100*nodeI/ns.network.NodeList.GetNNodes()))
                 seen_nodes = 0
             node = ns.network.NodeList.GetNode(nodeI)
             node_name = "Node %i" % nodeI
@@ -1143,7 +1157,7 @@
                                 graph.add_edge(node_name, other_node_name)
                             self.create_link(self.get_node(nodeI), otherNodeView)
 
-        print "scanning topology: calling graphviz layout"
+        print("scanning topology: calling graphviz layout")
         graph.layout(LAYOUT_ALGORITHM)
         for node in graph.iternodes():
             #print node, "=>", node.attr['pos']
@@ -1155,7 +1169,7 @@
                 obj = self.channels[int(node_id)]
             obj.set_position(pos_x, pos_y)
 
-        print "scanning topology: all done."
+        print("scanning topology: all done.")
         self.emit("topology-scanned")
 
     def get_node(self, index):
@@ -1316,22 +1330,24 @@
                                      alignment=Pango.Alignment.CENTER,
                                      anchor=GooCanvas.CanvasAnchorType.S,
                                      x=0, y=-line_width/2)
-                M = cairo.Matrix()
-                M.translate(*self._get_label_over_line_position(pos1_x, pos1_y, pos2_x, pos2_y))
-                M.rotate(angle)
-                # FIXME: KeyError: 'could not find foreign type Matrix'
-                # label.set_transform(M)
             else:
                 label.set_properties(text=("← %.2f kbit/s" % (kbps,)),
                                      alignment=Pango.Alignment.CENTER,
                                      anchor=GooCanvas.CanvasAnchorType.N,
                                      x=0, y=line_width/2)
-                M = cairo.Matrix()
-                M.translate(*self._get_label_over_line_position(pos1_x, pos1_y, pos2_x, pos2_y))
-                M.rotate(angle)
-                M.scale(-1, -1)
-                # FIXME: KeyError: 'could not find foreign type Matrix'
-                # label.set_transform(M)
+            M = cairo.Matrix()
+            lx, ly = self._get_label_over_line_position(pos1_x, pos1_y,
+                                                        pos2_x, pos2_y)
+            M.translate(lx, ly)
+            M.rotate(angle)
+            try:
+                label.set_transform(M)
+            except KeyError:
+                # https://gitlab.gnome.org/GNOME/pygobject/issues/16
+                warnings.warn("PyGobject bug causing label position error; "
+                              "should be fixed in PyGObject >= 3.29.1")
+                label.set_properties(x=(lx + label.props.x),
+                                     y=(ly + label.props.y))
 
             new_arrows.append((arrow, label))
 
@@ -1432,22 +1448,22 @@
 
     def _start_update_timer(self):
         if self._update_timeout_id is not None:
-            GObject.source_remove(self._update_timeout_id)
+            GLib.source_remove(self._update_timeout_id)
         #print "start_update_timer"
-        self._update_timeout_id = GObject.timeout_add(int(SAMPLE_PERIOD/min(self.speed, 1)*1e3),
-                                                      self.update_view_timeout,
-                                                      priority=PRIORITY_UPDATE_VIEW)
+        self._update_timeout_id = GLib.timeout_add(int(SAMPLE_PERIOD/min(self.speed, 1)*1e3),
+                                                   self.update_view_timeout,
+                                                   priority=PRIORITY_UPDATE_VIEW)
 
     def _on_play_button_toggled(self, button):
         if button.get_active():
             self._start_update_timer()
         else:
             if self._update_timeout_id is not None:
-                GObject.source_remove(self._update_timeout_id)
+                GLib.source_remove(self._update_timeout_id)
 
     def _quit(self, *dummy_args):
         if self._update_timeout_id is not None:
-            GObject.source_remove(self._update_timeout_id)
+            GLib.source_remove(self._update_timeout_id)
             self._update_timeout_id = None
         self.simulation.quit = True
         self.simulation.go.set()
@@ -1513,7 +1529,7 @@
         self.scan_topology()
         self.window.connect("delete-event", self._quit)
         #self._start_update_timer()
-        GObject.timeout_add(200, self.autoscale_view)
+        GLib.timeout_add(200, self.autoscale_view)
         self.simulation.start()
 
         try:
@@ -1527,12 +1543,12 @@
 
 
     def on_root_button_press_event(self, view, target, event):
-        if event.button.button == 1:
+        if event.button == 1:
             self.select_node(None)
             return True
 
     def on_node_button_press_event(self, view, target, event, node):
-        button = event.button.button
+        button = event.button
         if button == 1:
             self.select_node(node)
             return True
@@ -1540,12 +1556,12 @@
             self.popup_node_menu(node, event)
             return True
         elif button == 2:
-            self.begin_node_drag(node)
+            self.begin_node_drag(node, event)
             return True
         return False
 
     def on_node_button_release_event(self, view, target, event, node):
-        if event.button.button == 2:
+        if event.button == 2:
             self.end_node_drag(node)
             return True
         return False
@@ -1558,7 +1574,7 @@
             self.sim_y0 = sim_y0
             self.motion_signal = None
 
-    def begin_node_drag(self, node):
+    def begin_node_drag(self, node, event):
         self.simulation.lock.acquire()
         try:
             ns3_node = ns.network.NodeList.GetNode(node.node_index)
@@ -1570,8 +1586,8 @@
             pos = mob.GetPosition()
         finally:
             self.simulation.lock.release()
-        _, x, y, _ = self.canvas.get_window().get_pointer()
-        x0, y0 = self.canvas.convert_from_pixels(x, y)
+        devpos = self.canvas.get_window().get_device_position(event.device)
+        x0, y0 = self.canvas.convert_from_pixels(devpos.x, devpos.y)
         self.node_drag_state = self.NodeDragState(x0, y0, pos.x, pos.y)
         self.node_drag_state.motion_signal = node.canvas_item.connect("motion-notify-event", self.node_drag_motion, node)
 
@@ -1584,8 +1600,8 @@
                 return False
             if self.node_drag_state is None:
                 return False
-            x, y, _, _ = self.canvas.get_window().get_pointer()
-            canvas_x, canvas_y = self.canvas.convert_from_pixels(x, y)
+            devpos = self.canvas.get_window().get_device_position(event.device)
+            canvas_x, canvas_y = self.canvas.convert_from_pixels(devpos.x, devpos.y)
             dx = (canvas_x - self.node_drag_state.canvas_x0)
             dy = (canvas_y - self.node_drag_state.canvas_y0)
             pos = mob.GetPosition()
@@ -1607,7 +1623,7 @@
     def popup_node_menu(self, node, event):
         menu = Gtk.Menu()
         self.emit("populate-node-menu", node, menu)
-        menu.popup(None, None, None, None, event.button.button, event.time)
+        menu.popup(None, None, None, None, event.button, event.time)
 
     def _update_ipython_selected_node(self):
         # If we are running under ipython -gthread, make this new
@@ -1825,12 +1841,13 @@
     assert Visualizer.INSTANCE is None
     if _import_error is not None:
         import sys
-        print >> sys.stderr, "No visualization support (%s)." % (str(_import_error),)
+        print("No visualization support (%s)." % (str(_import_error),),
+              file=sys.stderr)
         ns.core.Simulator.Run()
         return
     load_plugins()
     viz = Visualizer()
     for hook, args in initialization_hooks:
-        GObject.idle_add(hook, viz, *args)
+        GLib.idle_add(hook, viz, *args)
     ns.network.Packet.EnablePrinting()
     viz.start()