From e66cfa31ff52b95a9c3df27c8a1f3b02bef6db3d Mon Sep 17 00:00:00 2001
From: Seth Hitefield <sdhitefield@gmail.com>
Date: Mon, 11 Apr 2016 22:09:16 -0400
Subject: grc: Main window opens with pygobject and gtk3. Still throws tons of
 errors.

---
 grc/gui/Bars.py | 63 ++++++++++++++++++++++++++++++---------------------------
 1 file changed, 33 insertions(+), 30 deletions(-)

(limited to 'grc/gui/Bars.py')

diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py
index a4819b973c..2d0709309c 100644
--- a/grc/gui/Bars.py
+++ b/grc/gui/Bars.py
@@ -17,9 +17,10 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
-import pygtk
-pygtk.require('2.0')
-import gtk
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+from gi.repository import GObject
 
 from . import Actions
 
@@ -62,7 +63,7 @@ TOOLBAR_LIST = (
 
 # The list of actions and categories for the menu bar.
 MENU_BAR_LIST = (
-    (gtk.Action('File', '_File', None, None), [
+    (Gtk.Action(name='File', label='_File'), [
         'flow_graph_new',
         Actions.FLOW_GRAPH_OPEN,
         'flow_graph_recent',
@@ -75,7 +76,7 @@ MENU_BAR_LIST = (
         Actions.FLOW_GRAPH_CLOSE,
         Actions.APPLICATION_QUIT,
     ]),
-    (gtk.Action('Edit', '_Edit', None, None), [
+    (Gtk.Action(name='Edit', label='_Edit'), [
         Actions.FLOW_GRAPH_UNDO,
         Actions.FLOW_GRAPH_REDO,
         None,
@@ -95,7 +96,7 @@ MENU_BAR_LIST = (
         None,
         Actions.BLOCK_PARAM_MODIFY,
     ]),
-    (gtk.Action('View', '_View', None, None), [
+    (Gtk.Action(name='View', label='_View'), [
         Actions.TOGGLE_BLOCKS_WINDOW,
         None,
         Actions.TOGGLE_CONSOLE_WINDOW,
@@ -117,12 +118,12 @@ MENU_BAR_LIST = (
         Actions.ERRORS_WINDOW_DISPLAY,
         Actions.FIND_BLOCKS,
     ]),
-    (gtk.Action('Run', '_Run', None, None), [
+    (Gtk.Action(name='Run', label='_Run'), [
         Actions.FLOW_GRAPH_GEN,
         Actions.FLOW_GRAPH_EXEC,
         Actions.FLOW_GRAPH_KILL,
     ]),
-    (gtk.Action('Tools', '_Tools', None, None), [
+    (Gtk.Action(name='Tools', label='_Tools'), [
         Actions.TOOLS_RUN_FDESIGN,
         Actions.FLOW_GRAPH_OPEN_QSS_THEME,
         None,
@@ -130,7 +131,7 @@ MENU_BAR_LIST = (
         None,
         Actions.TOOLS_MORE_TO_COME,
     ]),
-    (gtk.Action('Help', '_Help', None, None), [
+    (Gtk.Action(name='Help', label='_Help'), [
         Actions.HELP_WINDOW_DISPLAY,
         Actions.TYPES_WINDOW_DISPLAY,
         Actions.XML_PARSER_ERRORS_DISPLAY,
@@ -152,7 +153,7 @@ CONTEXT_MENU_LIST = [
     Actions.BLOCK_DISABLE,
     Actions.BLOCK_BYPASS,
     None,
-    (gtk.Action('More', '_More', None, None), [
+    (Gtk.Action(name='More', label='_More'), [
         Actions.BLOCK_CREATE_HIER,
         Actions.OPEN_HIER,
         None,
@@ -189,13 +190,13 @@ class SubMenuCreator(object):
 
     def _fill_flow_graph_new_submenu(self, action):
         """Sub menu to create flow-graph with pre-set generate mode"""
-        menu = gtk.Menu()
+        menu = Gtk.Menu()
         for key, name, default in self.generate_modes:
             if default:
                 item = Actions.FLOW_GRAPH_NEW.create_menu_item()
                 item.set_label(name)
             else:
-                item = gtk.MenuItem(name, use_underline=False)
+                item = Gtk.MenuItem(name=name, use_underline=False)
                 item.connect('activate', self.callback_adaptor, (action, key))
             menu.append(item)
         menu.show_all()
@@ -204,11 +205,11 @@ class SubMenuCreator(object):
     def _fill_flow_graph_recent_submenu(self, action):
         """menu showing recent flow-graphs"""
         import Preferences
-        menu = gtk.Menu()
+        menu = Gtk.Menu()
         recent_files = Preferences.get_recent_files()
         if len(recent_files) > 0:
             for i, file_name in enumerate(recent_files):
-                item = gtk.MenuItem("%d. %s" % (i+1, file_name), use_underline=False)
+                item = Gtk.MenuItem(name="%d. %s" % (i+1, file_name), use_underline=False)
                 item.connect('activate', self.callback_adaptor,
                              (action, file_name))
                 menu.append(item)
@@ -217,7 +218,7 @@ class SubMenuCreator(object):
         return None
 
 
-class Toolbar(gtk.Toolbar, SubMenuCreator):
+class Toolbar(Gtk.Toolbar, SubMenuCreator):
     """The gtk toolbar with actions added from the toolbar list."""
 
     def __init__(self, generate_modes, action_handler_callback):
@@ -226,23 +227,25 @@ class Toolbar(gtk.Toolbar, SubMenuCreator):
         Look up the action for each name in the action list and add it to the
         toolbar.
         """
-        gtk.Toolbar.__init__(self)
-        self.set_style(gtk.TOOLBAR_ICONS)
+        GObject.GObject.__init__(self)
+        self.set_style(Gtk.ToolbarStyle.ICONS)
         SubMenuCreator.__init__(self, generate_modes, action_handler_callback)
 
         for action in TOOLBAR_LIST:
             if isinstance(action, tuple) and isinstance(action[1], str):
                 # create a button with a sub-menu
-                action[0].set_tool_item_type(gtk.MenuToolButton)
+                # TODO: Fix later
+                #action[0].set_tool_item_type(Gtk.MenuToolButton)
                 item = action[0].create_tool_item()
-                self.create_submenu(action, item)
-                self.refresh_submenus()
+                #self.create_submenu(action, item)
+                #self.refresh_submenus()
 
             elif action is None:
-                item = gtk.SeparatorToolItem()
+                item = Gtk.SeparatorToolItem()
 
             else:
-                action.set_tool_item_type(gtk.ToolButton)
+                #TODO: Fix later
+                #action.set_tool_item_type(Gtk.ToolButton)
                 item = action.create_tool_item()
                 # this reset of the tooltip property is required
                 # (after creating the tool item) for the tooltip to show
@@ -255,14 +258,14 @@ class MenuHelperMixin(object):
 
     def _fill_menu(self, actions, menu=None):
         """Create a menu from list of actions"""
-        menu = menu or gtk.Menu()
+        menu = menu or Gtk.Menu()
         for item in actions:
             if isinstance(item, tuple):
                 menu_item = self._make_sub_menu(*item)
             elif isinstance(item, str):
                 menu_item = getattr(self, 'create_' + item)()
             elif item is None:
-                menu_item = gtk.SeparatorMenuItem()
+                menu_item = Gtk.SeparatorMenuItem()
             else:
                 menu_item = item.create_menu_item()
             menu.append(menu_item)
@@ -276,7 +279,7 @@ class MenuHelperMixin(object):
         return main
 
 
-class MenuBar(gtk.MenuBar, MenuHelperMixin, SubMenuCreator):
+class MenuBar(Gtk.MenuBar, MenuHelperMixin, SubMenuCreator):
     """The gtk menu bar with actions added from the menu bar list."""
 
     def __init__(self, generate_modes, action_handler_callback):
@@ -286,13 +289,13 @@ class MenuBar(gtk.MenuBar, MenuHelperMixin, SubMenuCreator):
         Look up the action for each name in the action list and add it to the
         submenu. Add the submenu to the menu bar.
         """
-        gtk.MenuBar.__init__(self)
+        GObject.GObject.__init__(self)
         SubMenuCreator.__init__(self, generate_modes, action_handler_callback)
         for main_action, actions in MENU_BAR_LIST:
             self.append(self._make_sub_menu(main_action, actions))
 
     def create_flow_graph_new(self):
-        main = gtk.ImageMenuItem(gtk.STOCK_NEW)
+        main = Gtk.ImageMenuItem(Gtk.STOCK_NEW)
         main.set_label(Actions.FLOW_GRAPH_NEW.get_label())
         func = self._fill_flow_graph_new_submenu
         self.submenus.append((Actions.FLOW_GRAPH_NEW, func, main))
@@ -300,7 +303,7 @@ class MenuBar(gtk.MenuBar, MenuHelperMixin, SubMenuCreator):
         return main
 
     def create_flow_graph_recent(self):
-        main = gtk.ImageMenuItem(gtk.STOCK_OPEN)
+        main = Gtk.ImageMenuItem(Gtk.STOCK_OPEN)
         main.set_label(Actions.FLOW_GRAPH_OPEN_RECENT.get_label())
         func = self._fill_flow_graph_recent_submenu
         self.submenus.append((Actions.FLOW_GRAPH_OPEN, func, main))
@@ -310,9 +313,9 @@ class MenuBar(gtk.MenuBar, MenuHelperMixin, SubMenuCreator):
         return main
 
 
-class ContextMenu(gtk.Menu, MenuHelperMixin):
+class ContextMenu(Gtk.Menu, MenuHelperMixin):
     """The gtk menu with actions added from the context menu list."""
 
     def __init__(self):
-        gtk.Menu.__init__(self)
+        GObject.GObject.__init__(self)
         self._fill_menu(CONTEXT_MENU_LIST, self)
-- 
cgit v1.2.3


From 5352dfd80fd238256da7bbd5efd15c154f3f5a14 Mon Sep 17 00:00:00 2001
From: Sebastian Koslowski <koslowski@kit.edu>
Date: Mon, 18 Apr 2016 18:11:52 +0200
Subject: gtk3: add flowgraph draw code and other gtk3 fixes (WIP)

---
 grc/core/Platform.py      |  7 ++--
 grc/gui/ActionHandler.py  |  9 ++---
 grc/gui/Actions.py        | 21 ++++++-----
 grc/gui/Bars.py           |  4 +-
 grc/gui/Block.py          | 91 ++++++++++++++++++++++++---------------------
 grc/gui/Colors.py         | 25 +++++++------
 grc/gui/Connection.py     | 29 +++++++--------
 grc/gui/DrawingArea.py    | 57 ++++++----------------------
 grc/gui/Element.py        | 26 +++++++------
 grc/gui/FlowGraph.py      | 29 ++++++++-------
 grc/gui/MainWindow.py     | 28 ++++++--------
 grc/gui/NotebookPage.py   | 16 ++++----
 grc/gui/Port.py           | 63 +++++++++++++------------------
 grc/gui/VariableEditor.py | 95 ++++++++++++++++++++++++-----------------------
 14 files changed, 230 insertions(+), 270 deletions(-)

(limited to 'grc/gui/Bars.py')

diff --git a/grc/core/Platform.py b/grc/core/Platform.py
index dfb60ee6a5..82d91ddb72 100644
--- a/grc/core/Platform.py
+++ b/grc/core/Platform.py
@@ -219,10 +219,9 @@ class Platform(Element):
 
         color = n.find('color') or ''
         try:
-            import gi # ugly but handy
-            from gi.repository import Gdk
-            Gdk.color_parse(color)
-        except (ValueError, ImportError):
+            chars_per_color = 2 if len(color) > 4 else 1
+            tuple(int(color[o:o + 2], 16) / 255.0 for o in range(1, 3 * chars_per_color, chars_per_color))
+        except ValueError:
             if color:  # no color is okay, default set in GUI
                 print >> sys.stderr, 'Warning: Can\'t parse color code "{}" for domain "{}" '.format(color, key)
                 color = None
diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py
index 96f7080c40..e25fa19030 100644
--- a/grc/gui/ActionHandler.py
+++ b/grc/gui/ActionHandler.py
@@ -84,9 +84,8 @@ class ActionHandler:
         # prevent key event stealing while the search box is active
         # .has_focus() only in newer versions 2.17+?
         # .is_focus() seems to work, but exactly the same
-        if self.main_window.btwin.search_entry.flags() & Gtk.HAS_FOCUS:
+        if self.main_window.btwin.search_entry.has_focus():
             return False
-        if not self.get_focus_flag(): return False
         return Actions.handle_key_press(event)
 
     def _quit(self, window, event):
@@ -447,9 +446,9 @@ class ActionHandler:
             action.save_to_preferences()
         elif action == Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR:
             if self.init:
-                md = gtk.MessageDialog(main,
-                    gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_INFO,
-                    gtk.BUTTONS_CLOSE, "Moving the variable editor requires a restart of GRC.")
+                md = Gtk.MessageDialog(main,
+                    Gtk.DIALOG_DESTROY_WITH_PARENT, Gtk.MESSAGE_INFO,
+                    Gtk.BUTTONS_CLOSE, "Moving the variable editor requires a restart of GRC.")
                 md.run()
                 md.destroy()
                 action.save_to_preferences()
diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py
index b2745c995a..d0e114293f 100644
--- a/grc/gui/Actions.py
+++ b/grc/gui/Actions.py
@@ -27,6 +27,7 @@ import Preferences
 
 NO_MODS_MASK = 0
 
+
 ########################################################################
 # Actions API
 ########################################################################
@@ -48,7 +49,7 @@ def handle_key_press(event):
     """
     _used_mods_mask = reduce(lambda x, y: x | y, [mod_mask for keyval, mod_mask in _actions_keypress_dict], NO_MODS_MASK)
     # extract the key value and the consumed modifiers
-    keyval, egroup, level, consumed = _keymap.translate_keyboard_state(
+    _unused, keyval, egroup, level, consumed = _keymap.translate_keyboard_state(
         event.hardware_keycode, event.get_state(), event.group)
     # get the modifier mask and ignore irrelevant modifiers
     mod_mask = event.get_state() & ~consumed & _used_mods_mask
@@ -261,32 +262,32 @@ BLOCK_ROTATE_CW = Action(
 BLOCK_VALIGN_TOP = Action(
     label='Vertical Align Top',
     tooltip='Align tops of selected blocks',
-    keypresses=(gtk.keysyms.t, gtk.gdk.SHIFT_MASK),
+    keypresses=(Gdk.KEY_t, Gdk.ModifierType.SHIFT_MASK),
 )
 BLOCK_VALIGN_MIDDLE = Action(
     label='Vertical Align Middle',
     tooltip='Align centers of selected blocks vertically',
-    keypresses=(gtk.keysyms.m, gtk.gdk.SHIFT_MASK),
+    keypresses=(Gdk.KEY_m, Gdk.ModifierType.SHIFT_MASK),
 )
 BLOCK_VALIGN_BOTTOM = Action(
     label='Vertical Align Bottom',
     tooltip='Align bottoms of selected blocks',
-    keypresses=(gtk.keysyms.b, gtk.gdk.SHIFT_MASK),
+    keypresses=(Gdk.KEY_b, Gdk.ModifierType.SHIFT_MASK),
 )
 BLOCK_HALIGN_LEFT = Action(
     label='Horizontal Align Left',
     tooltip='Align left edges of blocks selected blocks',
-    keypresses=(gtk.keysyms.l, gtk.gdk.SHIFT_MASK),
+    keypresses=(Gdk.KEY_l, Gdk.ModifierType.SHIFT_MASK),
 )
 BLOCK_HALIGN_CENTER = Action(
     label='Horizontal Align Center',
     tooltip='Align centers of selected blocks horizontally',
-    keypresses=(gtk.keysyms.c, gtk.gdk.SHIFT_MASK),
+    keypresses=(Gdk.KEY_c, Gdk.ModifierType.SHIFT_MASK),
 )
 BLOCK_HALIGN_RIGHT = Action(
     label='Horizontal Align Right',
     tooltip='Align right edges of selected blocks',
-    keypresses=(gtk.keysyms.r, gtk.gdk.SHIFT_MASK),
+    keypresses=(Gdk.KEY_r, Gdk.ModifierType.SHIFT_MASK),
 )
 BLOCK_ALIGNMENTS = [
     BLOCK_VALIGN_TOP,
@@ -341,9 +342,9 @@ TOGGLE_HIDE_VARIABLES = ToggleAction(
 TOGGLE_FLOW_GRAPH_VAR_EDITOR = ToggleAction(
     label='Show _Variable Editor',
     tooltip='Show the variable editor. Modify variables and imports in this flow graph',
-    stock_id=gtk.STOCK_EDIT,
+    stock_id=Gtk.STOCK_EDIT,
     default=True,
-    keypresses=(gtk.keysyms.e, gtk.gdk.CONTROL_MASK),
+    keypresses=(Gdk.KEY_e, Gdk.ModifierType.CONTROL_MASK),
     preference_name='variable_editor_visable',
 )
 TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR = ToggleAction(
@@ -407,7 +408,7 @@ ERRORS_WINDOW_DISPLAY = Action(
 TOGGLE_CONSOLE_WINDOW = ToggleAction(
     label='Show _Console Panel',
     tooltip='Toggle visibility of the console',
-    keypresses=(Gdk.KEY_c, Gdk.ModifierType.CONTROL_MASK),
+    keypresses=(Gdk.KEY_r, Gdk.ModifierType.CONTROL_MASK),
     preference_name='console_window_visible'
 )
 TOGGLE_BLOCKS_WINDOW = ToggleAction(
diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py
index 2d0709309c..c8631aa298 100644
--- a/grc/gui/Bars.py
+++ b/grc/gui/Bars.py
@@ -61,6 +61,7 @@ TOOLBAR_LIST = (
     Actions.OPEN_HIER,
 )
 
+
 # The list of actions and categories for the menu bar.
 MENU_BAR_LIST = (
     (Gtk.Action(name='File', label='_File'), [
@@ -88,7 +89,7 @@ MENU_BAR_LIST = (
         None,
         Actions.BLOCK_ROTATE_CCW,
         Actions.BLOCK_ROTATE_CW,
-        (gtk.Action('Align', '_Align', None, None), Actions.BLOCK_ALIGNMENTS),
+        (Gtk.Action('Align', '_Align', None, None), Actions.BLOCK_ALIGNMENTS),
         None,
         Actions.BLOCK_ENABLE,
         Actions.BLOCK_DISABLE,
@@ -140,6 +141,7 @@ MENU_BAR_LIST = (
     ]),
 )
 
+
 # The list of actions for the context menu.
 CONTEXT_MENU_LIST = [
     Actions.BLOCK_CUT,
diff --git a/grc/gui/Block.py b/grc/gui/Block.py
index 64c9e022b5..d16c9d01c6 100644
--- a/grc/gui/Block.py
+++ b/grc/gui/Block.py
@@ -17,10 +17,11 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+import math
 import gi
 gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk
-from gi.repository import Pango
+gi.require_version('PangoCairo', '1.0')
+from gi.repository import Gtk, Pango, PangoCairo
 
 from . import Actions, Colors, Utils
 
@@ -89,7 +90,9 @@ class Block(Element, _Block):
         ))
         Element.__init__(self)
         self._comment_pixmap = None
+        self._bg_color = Colors.BLOCK_ENABLED_COLOR
         self.has_busses = [False, False]  # source, sink
+        self.layouts = []
 
     def get_coordinate(self):
         """
@@ -196,14 +199,14 @@ class Block(Element, _Block):
     def create_labels(self):
         """Create the labels for the signal block."""
         Element.create_labels(self)
-        self._bg_color = self.is_dummy_block and Colors.MISSING_BLOCK_BACKGROUND_COLOR or \
-                         self.get_bypassed() and Colors.BLOCK_BYPASSED_COLOR or \
-                         self.get_enabled() and Colors.BLOCK_ENABLED_COLOR or Colors.BLOCK_DISABLED_COLOR
-
-        layouts = list()
+        self._bg_color = Colors.MISSING_BLOCK_BACKGROUND_COLOR if self.is_dummy_block else \
+                         Colors.BLOCK_BYPASSED_COLOR if self.get_bypassed() else \
+                         Colors.BLOCK_ENABLED_COLOR if self.get_enabled() else \
+                         Colors.BLOCK_DISABLED_COLOR
+        del self.layouts[:]
         #create the main layout
         layout = Gtk.DrawingArea().create_pango_layout('')
-        layouts.append(layout)
+        self.layouts.append(layout)
         layout.set_markup(Utils.parse_template(BLOCK_MARKUP_TMPL, block=self, font=BLOCK_FONT))
         self.label_width, self.label_height = layout.get_pixel_size()
         #display the params
@@ -217,30 +220,11 @@ class Block(Element, _Block):
             layout = Gtk.DrawingArea().create_pango_layout('')
             layout.set_spacing(LABEL_SEPARATION*Pango.SCALE)
             layout.set_markup('\n'.join(markups))
-            layouts.append(layout)
+            self.layouts.append(layout)
             w, h = layout.get_pixel_size()
             self.label_width = max(w, self.label_width)
             self.label_height += h + LABEL_SEPARATION
-        width = self.label_width
-        height = self.label_height
-        #setup the pixmap
-        pixmap = self.get_parent().new_pixmap(width, height)
-        gc = pixmap.new_gc()
-        gc.set_foreground(self._bg_color)
-        pixmap.draw_rectangle(gc, True, 0, 0, width, height)
-        #draw the layouts
-        h_off = 0
-        for i,layout in enumerate(layouts):
-            w,h = layout.get_pixel_size()
-            if i == 0: w_off = (width-w)/2
-            else: w_off = 0
-            pixmap.draw_layout(gc, w_off, h_off, layout)
-            h_off = h + h_off + LABEL_SEPARATION
-        #create vertical and horizontal pixmaps
-        self.horizontal_label = pixmap
-        if self.is_vertical():
-            self.vertical_label = self.get_parent().new_pixmap(height, width)
-            Utils.rotate_pixmap(gc, self.horizontal_label, self.vertical_label)
+
         #calculate width and height needed
         W = self.label_width + 2 * BLOCK_LABEL_PADDING
 
@@ -301,29 +285,50 @@ class Block(Element, _Block):
         else:
             self._comment_pixmap = None
 
-    def draw(self, gc, window):
+    def draw(self, widget, cr):
         """
         Draw the signal block with label and inputs/outputs.
-
-        Args:
-            gc: the graphics context
-            window: the gtk window to draw on
         """
         # draw ports
         for port in self.get_ports_gui():
-            port.draw(gc, window)
+            port.draw(widget, cr)
         # draw main block
-        x, y = self.get_coordinate()
-        Element.draw(
-            self, gc, window, bg_color=self._bg_color,
-            border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or
-                         self.is_dummy_block and Colors.MISSING_BLOCK_BORDER_COLOR or Colors.BORDER_COLOR,
+        border_color = (
+            Colors.HIGHLIGHT_COLOR if self.is_highlighted() else
+            Colors.MISSING_BLOCK_BORDER_COLOR if self.is_dummy_block else
+            Colors.BORDER_COLOR
         )
-        #draw label image
+        Element.draw(self, widget, cr, border_color, self._bg_color)
+        x, y = self.get_coordinate()
+        # create the image surface
+        width = self.label_width
+        height = self.label_height
+        cr.set_source_rgb(*self._bg_color)
+        cr.save()
         if self.is_horizontal():
-            window.draw_drawable(gc, self.horizontal_label, 0, 0, x+BLOCK_LABEL_PADDING, y+(self.H-self.label_height)/2, -1, -1)
+            cr.translate(x + BLOCK_LABEL_PADDING, y + (self.H - self.label_height) / 2)
         elif self.is_vertical():
-            window.draw_drawable(gc, self.vertical_label, 0, 0, x+(self.H-self.label_height)/2, y+BLOCK_LABEL_PADDING, -1, -1)
+            cr.translate(x + (self.H - self.label_height) / 2, y + BLOCK_LABEL_PADDING)
+            cr.rotate(-90 * math.pi / 180.)
+            cr.translate(-width, 0)
+
+        # cr.rectangle(0, 0, width, height)
+        # cr.fill()
+
+        # draw the layouts
+        h_off = 0
+        for i, layout in enumerate(self.layouts):
+            w, h = layout.get_pixel_size()
+            if i == 0:
+                w_off = (width - w) / 2
+            else:
+                w_off = 0
+            cr.translate(w_off, h_off)
+            PangoCairo.update_layout(cr, layout)
+            PangoCairo.show_layout(cr, layout)
+            cr.translate(-w_off, -h_off)
+            h_off = h + h_off + LABEL_SEPARATION
+        cr.restore()
 
     def what_is_selected(self, coor, coor_m=None):
         """
diff --git a/grc/gui/Colors.py b/grc/gui/Colors.py
index 2c5c73017e..686b378c38 100644
--- a/grc/gui/Colors.py
+++ b/grc/gui/Colors.py
@@ -16,26 +16,30 @@ You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
+
 try:
     import gi
     gi.require_version('Gtk', '3.0')
-    from gi.repository import Gtk
     from gi.repository import Gdk
 
+    # Not gtk3?
+    #COLORMAP = Gdk.colormap_get_system() #create all of the colors
+    #def get_color(color_code): return _COLORMAP.alloc_color(color_code, True, True)
+
     def get_color(color_code):
-        color = Gdk.RGBA()
-        color.parse(color_code)
-        return color
+        chars_per_color = 2 if len(color_code) > 4 else 1
+        offsets = range(1, 3 * chars_per_color + 1, chars_per_color)
+        return tuple(int(color_code[o:o + 2], 16) / 255.0 for o in offsets)
 
     HIGHLIGHT_COLOR = get_color('#00FFFF')
     BORDER_COLOR = get_color('#444444')
 
     # Missing blocks stuff
     MISSING_BLOCK_BACKGROUND_COLOR = get_color('#FFF2F2')
-    MISSING_BLOCK_BORDER_COLOR = get_color('red')
+    MISSING_BLOCK_BORDER_COLOR = get_color('#FF0000')
 
     # Param entry boxes
-    PARAM_ENTRY_TEXT_COLOR = get_color('black')
+    PARAM_ENTRY_TEXT_COLOR = get_color('#000000')
     ENTRYENUM_CUSTOM_COLOR = get_color('#EEEEEE')
 
     # Flow graph color constants
@@ -49,12 +53,9 @@ try:
     BLOCK_BYPASSED_COLOR = get_color('#F4FF81')
 
     # Connection color constants
-    CONNECTION_ENABLED_COLOR = get_color('black')
+    CONNECTION_ENABLED_COLOR = get_color('#000000')
     CONNECTION_DISABLED_COLOR = get_color('#BBBBBB')
-    CONNECTION_ERROR_COLOR = get_color('red')
+    CONNECTION_ERROR_COLOR = get_color('#FF0000')
 
 except Exception as e:
-    print 'Unable to import Colors'
-
-
-DEFAULT_DOMAIN_COLOR_CODE = '#777777'
+    print 'Unable to import Colors', e
diff --git a/grc/gui/Connection.py b/grc/gui/Connection.py
index 356a55d83b..46414c94c8 100644
--- a/grc/gui/Connection.py
+++ b/grc/gui/Connection.py
@@ -91,10 +91,10 @@ class Connection(Element, _Connection):
         ]
         source_domain = self.get_source().get_domain()
         sink_domain = self.get_sink().get_domain()
-        self.line_attributes[0] = 2 if source_domain != sink_domain else 0
-        self.line_attributes[1] = Gdk.LINE_DOUBLE_DASH \
-            if not source_domain == sink_domain == GR_MESSAGE_DOMAIN \
-            else Gdk.LINE_ON_OFF_DASH
+        # self.line_attributes[0] = 2 if source_domain != sink_domain else 0
+        # self.line_attributes[1] = Gdk.LINE_DOUBLE_DASH \
+        #     if not source_domain == sink_domain == GR_MESSAGE_DOMAIN \
+        #     else Gdk.LINE_ON_OFF_DASH
         get_domain_color = lambda d: Colors.get_color((
             self.get_parent().get_parent().domains.get(d, {})
         ).get('color') or Colors.DEFAULT_DOMAIN_COLOR_CODE)
@@ -147,13 +147,9 @@ class Connection(Element, _Connection):
             self.add_line((x1, y1), points[0])
             self.add_line((x2, y2), points[0])
 
-    def draw(self, gc, window):
+    def draw(self, widget, cr):
         """
         Draw the connection.
-
-        Args:
-            gc: the graphics context
-            window: the gtk window to draw on
         """
         sink = self.get_sink()
         source = self.get_source()
@@ -175,11 +171,12 @@ class Connection(Element, _Connection):
             Colors.CONNECTION_DISABLED_COLOR if not self.get_enabled() else
             color
         )
-        Element.draw(self, gc, window, mod_color(self._color), mod_color(self._bg_color))
+        Element.draw(self, widget, cr, mod_color(self._color), mod_color(self._bg_color))
         # draw arrow on sink port
-        try:
-            gc.set_foreground(mod_color(self._arrow_color))
-            gc.set_line_attributes(0, Gdk.LINE_SOLID, Gdk.CAP_BUTT, Gdk.JOIN_MITER)
-            window.draw_polygon(gc, True, self._arrow)
-        except:
-            pass
+        cr.set_source_rgb(*self._arrow_color)
+        # TODO: gc.set_line_attributes(0, Gdk.LINE_SOLID, Gdk.CAP_BUTT, Gdk.JOIN_MITER)
+        cr.move_to(*self._arrow[0])
+        cr.line_to(*self._arrow[1])
+        cr.line_to(*self._arrow[2])
+        cr.close_path()
+        cr.fill()
diff --git a/grc/gui/DrawingArea.py b/grc/gui/DrawingArea.py
index f91fb2a3de..2bce21fa6d 100644
--- a/grc/gui/DrawingArea.py
+++ b/grc/gui/DrawingArea.py
@@ -48,8 +48,7 @@ class DrawingArea(Gtk.DrawingArea):
         GObject.GObject.__init__(self)
         self.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
         self.connect('realize', self._handle_window_realize)
-        self.connect('configure-event', self._handle_window_configure)
-        self.connect('expose-event', self._handle_window_expose)
+        self.connect('draw', self.draw)
         self.connect('motion-notify-event', self._handle_mouse_motion)
         self.connect('button-press-event', self._handle_mouse_button_press)
         self.connect('button-release-event', self._handle_mouse_button_release)
@@ -59,35 +58,22 @@ class DrawingArea(Gtk.DrawingArea):
             Gdk.EventMask.POINTER_MOTION_MASK | \
             Gdk.EventMask.BUTTON_RELEASE_MASK | \
             Gdk.EventMask.LEAVE_NOTIFY_MASK | \
-            Gdk.EventMask.ENTER_NOTIFY_MASK | \
-            Gdk.EventMask.FOCUS_CHANGE_MASK
+            Gdk.EventMask.ENTER_NOTIFY_MASK #| \
+            #Gdk.EventMask.FOCUS_CHANGE_MASK
         )
         #setup drag and drop
-        self.drag_dest_set(Gtk.DestDefaults.ALL, DND_TARGETS, Gdk.DragAction.COPY)
+        self.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY)
         self.connect('drag-data-received', self._handle_drag_data_received)
+        self.drag_dest_set_target_list(None)
+        self.drag_dest_add_text_targets()
         #setup the focus flag
         self._focus_flag = False
         self.get_focus_flag = lambda: self._focus_flag
         def _handle_notify_event(widget, event, focus_flag): self._focus_flag = focus_flag
         self.connect('leave-notify-event', _handle_notify_event, False)
         self.connect('enter-notify-event', _handle_notify_event, True)
-        self.set_flags(Gtk.CAN_FOCUS)  # self.set_can_focus(True)
-        self.connect('focus-out-event', self._handle_focus_lost_event)
-
-    def new_pixmap(self, width, height):
-        return Gdk.Pixmap(self.window, width, height, -1)
-
-    def get_screenshot(self, transparent_bg=False):
-        pixmap = self._pixmap
-        W, H = pixmap.get_size()
-        pixbuf = GdkPixbuf.Pixbuf(GdkPixbuf.Colorspace.RGB, 0, 8, W, H)
-        pixbuf.fill(0xFF + Colors.FLOWGRAPH_BACKGROUND_COLOR.pixel << 8)
-        pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0, W-1, H-1)
-        if transparent_bg:
-            bgc = Colors.FLOWGRAPH_BACKGROUND_COLOR
-            pixbuf = pixbuf.add_alpha(True, bgc.red, bgc.green, bgc.blue)
-        return pixbuf
-
+#        self.set_flags(Gtk.CAN_FOCUS)  # self.set_can_focus(True)
+#        self.connect('focus-out-event', self._handle_focus_lost_event)
 
     ##########################################################################
     ## Handlers
@@ -96,7 +82,7 @@ class DrawingArea(Gtk.DrawingArea):
         """
         Handle a drag and drop by adding a block at the given coordinate.
         """
-        self._flow_graph.add_new_block(selection_data.data, (x, y))
+        self._flow_graph.add_new_block(selection_data.get_text(), (x, y))
 
     def _handle_mouse_scroll(self, widget, event):
         if event.get_state() & Gdk.ModifierType.SHIFT_MASK:
@@ -113,7 +99,7 @@ class DrawingArea(Gtk.DrawingArea):
         self.ctrl_mask = event.get_state() & Gdk.ModifierType.CONTROL_MASK
         self.mod1_mask = event.get_state() & Gdk.ModifierType.MOD1_MASK
         if event.button == 1: self._flow_graph.handle_mouse_selector_press(
-            double_click=(event.type == Gdk._2BUTTON_PRESS),
+            double_click=(event.type == Gdk.EventType._2BUTTON_PRESS),
             coordinate=(event.x, event.y),
         )
         if event.button == 3: self._flow_graph.handle_mouse_context_press(
@@ -148,27 +134,8 @@ class DrawingArea(Gtk.DrawingArea):
         """
         self._flow_graph.update()
 
-    def _handle_window_configure(self, widget, event):
-        """
-        Called when the window is resized.
-        Create a new pixmap for background buffer.
-        """
-        self._pixmap = self.new_pixmap(*self.get_size_request())
-
-    def _handle_window_expose(self, widget, event):
-        """
-        Called when window is exposed, or queue_draw is called.
-        Double buffering: draw to pixmap, then draw pixmap to window.
-        """
-        gc = self.window.new_gc()
-        self._flow_graph.draw(gc, self._pixmap)
-        self.window.draw_drawable(gc, self._pixmap, 0, 0, 0, 0, -1, -1)
-        # draw a light grey line on the bottom and right end of the canvas.
-        # this is useful when the theme uses the same panel bg color as the canvas
-        W, H = self._pixmap.get_size()
-        gc.set_foreground(Colors.FLOWGRAPH_EDGE_COLOR)
-        self.window.draw_line(gc, 0, H-1, W, H-1)
-        self.window.draw_line(gc, W-1, 0, W-1, H)
+    def draw(self, widget, cr):
+        self._flow_graph.draw(widget, cr)
 
     def _handle_focus_lost_event(self, widget, event):
         # don't clear selection while context menu is active
diff --git a/grc/gui/Element.py b/grc/gui/Element.py
index 3f6017def7..30c0f5dba7 100644
--- a/grc/gui/Element.py
+++ b/grc/gui/Element.py
@@ -90,29 +90,33 @@ class Element(object):
         self.clear()
         for child in self.get_children(): child.create_shapes()
 
-    def draw(self, gc, window, border_color, bg_color):
+    def draw(self, widget, cr, border_color, bg_color):
         """
         Draw in the given window.
 
         Args:
-            gc: the graphics context
-            window: the gtk window to draw on
+            widget:
+            cr:
             border_color: the color for lines and rectangle borders
             bg_color: the color for the inside of the rectangle
         """
         X, Y = self.get_coordinate()
-        gc.set_line_attributes(*self.line_attributes)
+        # TODO: gc.set_line_attributes(*self.line_attributes)
         for (rX, rY), (W, H) in self._areas_list:
             aX = X + rX
             aY = Y + rY
-            gc.set_foreground(bg_color)
-            window.draw_rectangle(gc, True, aX, aY, W, H)
-            gc.set_foreground(border_color)
-            window.draw_rectangle(gc, False, aX, aY, W, H)
+            cr.set_source_rgb(*bg_color)
+            cr.rectangle(aX, aY, W, H)
+            cr.fill()
+            cr.set_source_rgb(*border_color)
+            cr.rectangle(aX, aY, W, H)
+            cr.stroke()
+
         for (x1, y1), (x2, y2) in self._lines_list:
-            gc.set_foreground(border_color)
-            gc.set_background(bg_color)
-            window.draw_line(gc, X+x1, Y+y1, X+x2, Y+y2)
+            cr.set_source_rgb(*border_color)
+            cr.move_to(X + x1, Y + y1)
+            cr.line_to(X + x2, Y + y2)
+            cr.stroke()
 
     def rotate(self, rotation):
         """
diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py
index f7af93fe8e..c3ea6770c1 100644
--- a/grc/gui/FlowGraph.py
+++ b/grc/gui/FlowGraph.py
@@ -397,23 +397,21 @@ class FlowGraph(Element, _Flowgraph):
             changed = True
         return changed
 
-    def draw(self, gc, window):
+    def draw(self, widget, cr):
         """
         Draw the background and grid if enabled.
-        Draw all of the elements in this flow graph onto the pixmap.
-        Draw the pixmap to the drawable window of this flow graph.
         """
 
-        W,H = self.get_size()
-        #draw the background
-        gc.set_foreground(Colors.FLOWGRAPH_BACKGROUND_COLOR)
-        window.draw_rectangle(gc, True, 0, 0, W, H)
+        cr.set_source_rgb(*Colors.FLOWGRAPH_BACKGROUND_COLOR)
+        cr.rectangle(0, 0, *self.get_size())
+        cr.fill()
 
         # draw comments first
         if Actions.TOGGLE_SHOW_BLOCK_COMMENTS.get_active():
             for block in self.blocks:
                 if block.get_enabled():
-                    block.draw_comment(gc, window)
+                    # block.draw_comment(widget, cr)
+                    pass
         #draw multi select rectangle
         if self.mouse_pressed and (not self.get_selected_elements() or self.get_ctrl_mask()):
             #coordinates
@@ -423,10 +421,13 @@ class FlowGraph(Element, _Flowgraph):
             x, y = int(min(x1, x2)), int(min(y1, y2))
             w, h = int(abs(x1 - x2)), int(abs(y1 - y2))
             #draw
-            gc.set_foreground(Colors.HIGHLIGHT_COLOR)
-            window.draw_rectangle(gc, True, x, y, w, h)
-            gc.set_foreground(Colors.BORDER_COLOR)
-            window.draw_rectangle(gc, False, x, y, w, h)
+            cr.set_source_rgb(*Colors.HIGHLIGHT_COLOR)
+            cr.rectangle(x, y, w, h)
+            cr.fill()
+            cr.set_source_rgb(*Colors.BORDER_COLOR)
+            cr.rectangle(x, y, w, h)
+            cr.stroke()
+
         #draw blocks on top of connections
         hide_disabled_blocks = Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active()
         hide_variables = Actions.TOGGLE_HIDE_VARIABLES.get_active()
@@ -437,10 +438,10 @@ class FlowGraph(Element, _Flowgraph):
                 continue  # skip hidden disabled blocks and connections
             if hide_variables and (element.is_variable or element.is_import):
                 continue  # skip hidden disabled blocks and connections
-            element.draw(gc, window)
+            element.draw(widget, cr)
         #draw selected blocks on top of selected connections
         for selected_element in self.get_selected_connections() + self.get_selected_blocks():
-            selected_element.draw(gc, window)
+            selected_element.draw(widget, cr)
 
     def update_selected(self):
         """
diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py
index 649b8b29c5..95a5c89867 100644
--- a/grc/gui/MainWindow.py
+++ b/grc/gui/MainWindow.py
@@ -36,6 +36,7 @@ from .NotebookPage import NotebookPage
 
 from ..core import Messages
 
+
 MAIN_WINDOW_TITLE_TMPL = """\
 #if not $saved
 *#slurp
@@ -104,8 +105,8 @@ class MainWindow(Gtk.Window):
         vbox.pack_start(self.tool_bar, False, False, 0)
 
         # Main parent container for the different panels
-        self.container = Gtk.HPaned()
-        vbox.pack_start(self.container)
+        self.main = Gtk.HPaned() #(orientation=Gtk.Orientation.HORIZONTAL)
+        vbox.pack_start(self.main, True, True, 0)
 
         # Create the notebook
         self.notebook = Gtk.Notebook()
@@ -127,9 +128,9 @@ class MainWindow(Gtk.Window):
         self.vars = VariableEditor(platform, self.get_flow_graph)
 
         # Figure out which place to put the variable editor
-        self.left = Gtk.VPaned()
-        self.right = Gtk.VPaned()
-        self.left_subpanel = Gtk.HPaned()
+        self.left = Gtk.VPaned() #orientation=Gtk.Orientation.VERTICAL)
+        self.right = Gtk.VPaned() #orientation=Gtk.Orientation.VERTICAL)
+        self.left_subpanel = Gtk.HPaned() #orientation=Gtk.Orientation.HORIZONTAL)
 
         self.variable_panel_sidebar = Preferences.variable_editor_sidebar()
         if self.variable_panel_sidebar:
@@ -147,12 +148,12 @@ class MainWindow(Gtk.Window):
             # Create the right panel
             self.right.pack1(self.btwin)
 
-        self.container.pack1(self.left)
-        self.container.pack2(self.right, False)
+        self.main.pack1(self.left)
+        self.main.pack2(self.right, False)
 
         # Load preferences and show the main window
         self.resize(*Preferences.main_window_size())
-        self.container.set_position(Preferences.blocks_window_position())
+        self.main.set_position(Preferences.blocks_window_position())
         self.left.set_position(Preferences.console_window_position())
         if self.variable_panel_sidebar:
             self.right.set_position(Preferences.variable_editor_position(sidebar=True))
@@ -276,9 +277,7 @@ class MainWindow(Gtk.Window):
             return
         #add this page to the notebook
         self.notebook.append_page(page, page.get_tab())
-        try: self.notebook.set_tab_reorderable(page, True)
-        except: pass #gtk too old
-        self.notebook.set_tab_label_packing(page, False, False, Gtk.PACK_START)
+        self.notebook.set_tab_reorderable(page, True)
         #only show if blank or manual
         if not file_path or show: self._set_page(page)
 
@@ -303,7 +302,7 @@ class MainWindow(Gtk.Window):
         Preferences.file_open(open_file)
         Preferences.main_window_size(self.get_size())
         Preferences.console_window_position(self.left.get_position())
-        Preferences.blocks_window_position(self.container.get_position())
+        Preferences.blocks_window_position(self.main.get_position())
         if self.variable_panel_sidebar:
             Preferences.variable_editor_position(self.right.get_position(), sidebar=True)
         else:
@@ -405,14 +404,11 @@ class MainWindow(Gtk.Window):
         Returns:
             the selected flow graph
         """
-        return None
-        # TODO: Issues with flowgraphs
-        #return self.get_page().get_flow_graph()
+        return self.get_page().get_flow_graph()
 
     def get_focus_flag(self):
         """
         Get the focus flag from the current page.
-
         Returns:
             the focus flag
         """
diff --git a/grc/gui/NotebookPage.py b/grc/gui/NotebookPage.py
index 3f49cd0223..9a76897fe6 100644
--- a/grc/gui/NotebookPage.py
+++ b/grc/gui/NotebookPage.py
@@ -54,7 +54,7 @@ class NotebookPage(Gtk.HBox):
         GObject.GObject.__init__(self)
         self.show()
         #tab box to hold label and close button
-        self.tab = Gtk.HBox(False, 0)
+        self.tab = Gtk.HBox(homogeneous=False, spacing=0)
         #setup tab label
         self.label = Gtk.Label()
         self.tab.pack_start(self.label, False, False, 0)
@@ -62,7 +62,7 @@ class NotebookPage(Gtk.HBox):
         image = Gtk.Image()
         image.set_from_stock('gtk-close', Gtk.IconSize.MENU)
         #setup image box
-        image_box = Gtk.HBox(False, 0)
+        image_box = Gtk.HBox(homogeneous=False, spacing=0)
         image_box.pack_start(image, True, False, 0)
         #setup the button
         button = Gtk.Button()
@@ -79,20 +79,18 @@ class NotebookPage(Gtk.HBox):
         self.scrolled_window.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
         self.scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
         self.scrolled_window.connect('key-press-event', self._handle_scroll_window_key_press)
-        #self.drawing_area = DrawingArea(self.get_flow_graph())
-        #self.scrolled_window.add_with_viewport(self.get_drawing_area())
+        self.drawing_area = DrawingArea(self.get_flow_graph())
+        self.scrolled_window.add_with_viewport(self.drawing_area)
         self.pack_start(self.scrolled_window, True, True, 0)
         #inject drawing area into flow graph
-        #self.get_flow_graph().drawing_area = self.get_drawing_area()
+        self.get_flow_graph().drawing_area = self.drawing_area
         self.show_all()
 
-    def get_drawing_area(self): return self.drawing_area
-
     def _handle_scroll_window_key_press(self, widget, event):
         """forward Ctrl-PgUp/Down to NotebookPage (switch fg instead of horiz. scroll"""
         is_ctrl_pg = (
-            event.state & gtk.gdk.CONTROL_MASK and
-            event.keyval in (gtk.keysyms.Page_Up, gtk.keysyms.Page_Down)
+            event.state & Gdk.ModifierType.CONTROL_MASK and
+            event.keyval in (Gdk.KEY_Page_Up, Gdk.KEY_Page_Down)
         )
         if is_ctrl_pg:
             return self.get_parent().event(event)
diff --git a/grc/gui/Port.py b/grc/gui/Port.py
index 02cef5d04b..eb15f6c788 100644
--- a/grc/gui/Port.py
+++ b/grc/gui/Port.py
@@ -17,9 +17,10 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+import math
 import gi
 gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk
+from gi.repository import Gtk, PangoCairo
 
 from . import Actions, Colors, Utils
 from .Constants import (
@@ -45,15 +46,19 @@ class Port(_Port, Element):
         """
         _Port.__init__(self, block, n, dir)
         Element.__init__(self)
-        self.W = self.H = self.w = self.h = 0
+        self.W = self.w = self.h = 0
+        self.H = 20  # todo: fix
         self._connector_coordinate = (0, 0)
         self._connector_length = 0
         self._hovering = True
         self._force_label_unhidden = False
+        self.layout = Gtk.DrawingArea().create_pango_layout('')
+        self._bg_color = Colors.get_color(self.get_color())
 
     def create_shapes(self):
         """Create new areas and labels for the port."""
         Element.create_shapes(self)
+        self._bg_color = Colors.get_color(self.get_color())
         if self.get_hide():
             return  # this port is hidden, no need to create shapes
         if self.get_domain() == GR_MESSAGE_DOMAIN:
@@ -112,50 +117,34 @@ class Port(_Port, Element):
 
     def create_labels(self):
         """Create the labels for the socket."""
-        Element.create_labels(self)
-        self._bg_color = Colors.get_color(self.get_color())
-        # create the layout
-        layout = Gtk.DrawingArea().create_pango_layout('')
-        layout.set_markup(Utils.parse_template(PORT_MARKUP_TMPL, port=self, font=PORT_FONT))
-        self.w, self.h = layout.get_pixel_size()
-        self.W = 2 * PORT_LABEL_PADDING + self.w
-        self.H = 2 * PORT_LABEL_PADDING + self.h * (
-            3 if self.get_type() == 'bus' else 1)
-        self.H += self.H % 2
-        # create the pixmap
-        pixmap = self.get_parent().get_parent().new_pixmap(self.w, self.h)
-        gc = pixmap.new_gc()
-        gc.set_foreground(self._bg_color)
-        pixmap.draw_rectangle(gc, True, 0, 0, self.w, self.h)
-        pixmap.draw_layout(gc, 0, 0, layout)
-        # create vertical and horizontal pixmaps
-        self.horizontal_label = pixmap
-        if self.is_vertical():
-            self.vertical_label = self.get_parent().get_parent().new_pixmap(self.h, self.w)
-            Utils.rotate_pixmap(gc, self.horizontal_label, self.vertical_label)
-
-    def draw(self, gc, window):
+        self.layout.set_markup(Utils.parse_template(PORT_MARKUP_TMPL, port=self, font=PORT_FONT))
+
+    def draw(self, widget, cr):
         """
         Draw the socket with a label.
-
-        Args:
-            gc: the graphics context
-            window: the gtk window to draw on
         """
-        Element.draw(
-            self, gc, window, bg_color=self._bg_color,
-            border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or
-                         self.get_parent().is_dummy_block and Colors.MISSING_BLOCK_BORDER_COLOR or
-                         Colors.BORDER_COLOR,
+        border_color = (
+            Colors.HIGHLIGHT_COLOR if self.is_highlighted() else
+            Colors.MISSING_BLOCK_BORDER_COLOR if self.get_parent().is_dummy_block else
+            Colors.BORDER_COLOR
         )
+        Element.draw(self, widget, cr, border_color, self._bg_color)
+
         if not self._areas_list or self._label_hidden():
             return  # this port is either hidden (no areas) or folded (no label)
         X, Y = self.get_coordinate()
-        (x, y), (w, h) = self._areas_list[0]  # use the first area's sizes to place the labels
+        (x, y), _ = self._areas_list[0]
+        cr.set_source_rgb(*self._bg_color)
+        cr.save()
         if self.is_horizontal():
-            window.draw_drawable(gc, self.horizontal_label, 0, 0, x+X+(self.W-self.w)/2, y+Y+(self.H-self.h)/2, -1, -1)
+            cr.translate(x + X + (self.W - self.w) / 2, y + Y + (self.H - self.h) / 2)
         elif self.is_vertical():
-            window.draw_drawable(gc, self.vertical_label, 0, 0, x+X+(self.H-self.h)/2, y+Y+(self.W-self.w)/2, -1, -1)
+            cr.translate(x + X + (self.H - self.h) / 2, y + Y + (self.W - self.w) / 2)
+            cr.rotate(-90 * math.pi / 180.)
+            cr.translate(-self.w, 0)
+        PangoCairo.update_layout(cr, self.layout)
+        PangoCairo.show_layout(cr, self.layout)
+        cr.restore()
 
     def get_connector_coordinate(self):
         """
diff --git a/grc/gui/VariableEditor.py b/grc/gui/VariableEditor.py
index 7721f3bda6..8729762928 100644
--- a/grc/gui/VariableEditor.py
+++ b/grc/gui/VariableEditor.py
@@ -1,5 +1,5 @@
 """
-Copyright 2015 Free Software Foundation, Inc.
+Copyright 2015, 2016 Free Software Foundation, Inc.
 This file is part of GNU Radio
 
 GNU Radio Companion is free software; you can redistribute it and/or
@@ -19,10 +19,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 from operator import attrgetter
 
-import pygtk
-pygtk.require('2.0')
-import gtk
-import gobject
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+from gi.repository import Gdk
+from gi.repository import GObject
 
 from . import Actions
 from . import Preferences
@@ -32,34 +33,34 @@ BLOCK_INDEX = 0
 ID_INDEX = 1
 
 
-class VariableEditorContextMenu(gtk.Menu):
+class VariableEditorContextMenu(Gtk.Menu):
     """ A simple context menu for our variable editor """
     def __init__(self, var_edit):
-        gtk.Menu.__init__(self)
+        Gtk.Menu.__init__(self)
 
-        self.imports = gtk.MenuItem("Add _Import")
+        self.imports = Gtk.MenuItem("Add _Import")
         self.imports.connect('activate', var_edit.handle_action, var_edit.ADD_IMPORT)
         self.add(self.imports)
 
-        self.variables = gtk.MenuItem("Add _Variable")
+        self.variables = Gtk.MenuItem("Add _Variable")
         self.variables.connect('activate', var_edit.handle_action, var_edit.ADD_VARIABLE)
         self.add(self.variables)
-        self.add(gtk.SeparatorMenuItem())
+        self.add(Gtk.SeparatorMenuItem())
 
-        self.enable = gtk.MenuItem("_Enable")
+        self.enable = Gtk.MenuItem("_Enable")
         self.enable.connect('activate', var_edit.handle_action, var_edit.ENABLE_BLOCK)
-        self.disable = gtk.MenuItem("_Disable")
+        self.disable = Gtk.MenuItem("_Disable")
         self.disable.connect('activate', var_edit.handle_action, var_edit.DISABLE_BLOCK)
         self.add(self.enable)
         self.add(self.disable)
-        self.add(gtk.SeparatorMenuItem())
+        self.add(Gtk.SeparatorMenuItem())
 
-        self.delete = gtk.MenuItem("_Delete")
+        self.delete = Gtk.MenuItem("_Delete")
         self.delete.connect('activate', var_edit.handle_action, var_edit.DELETE_BLOCK)
         self.add(self.delete)
-        self.add(gtk.SeparatorMenuItem())
+        self.add(Gtk.SeparatorMenuItem())
 
-        self.properties = gtk.MenuItem("_Properties...")
+        self.properties = Gtk.MenuItem("_Properties...")
         self.properties.connect('activate', var_edit.handle_action, var_edit.OPEN_PROPERTIES)
         self.add(self.properties)
         self.show_all()
@@ -71,7 +72,7 @@ class VariableEditorContextMenu(gtk.Menu):
         self.disable.set_sensitive(selected and enabled)
 
 
-class VariableEditor(gtk.VBox):
+class VariableEditor(Gtk.VBox):
 
     # Actions that are handled by the editor
     ADD_IMPORT = 0
@@ -83,7 +84,7 @@ class VariableEditor(gtk.VBox):
     DISABLE_BLOCK = 6
 
     def __init__(self, platform, get_flow_graph):
-        gtk.VBox.__init__(self)
+        Gtk.VBox.__init__(self)
         self.platform = platform
         self.get_flow_graph = get_flow_graph
         self._block = None
@@ -91,14 +92,14 @@ class VariableEditor(gtk.VBox):
 
         # Only use the model to store the block reference and name.
         # Generate everything else dynamically
-        self.treestore = gtk.TreeStore(gobject.TYPE_PYOBJECT,  # Block reference
-                                       gobject.TYPE_STRING)    # Category and block name
-        self.treeview = gtk.TreeView(self.treestore)
+        self.treestore = Gtk.TreeStore(GObject.TYPE_PYOBJECT,  # Block reference
+                                       GObject.TYPE_STRING)    # Category and block name
+        self.treeview = Gtk.TreeView(self.treestore)
         self.treeview.set_enable_search(False)
         self.treeview.set_search_column(-1)
         #self.treeview.set_enable_search(True)
         #self.treeview.set_search_column(ID_INDEX)
-        self.treeview.get_selection().set_mode('single')
+        self.treeview.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
         self.treeview.set_headers_visible(True)
         self.treeview.connect('button-press-event', self._handle_mouse_button_press)
         self.treeview.connect('button-release-event', self._handle_mouse_button_release)
@@ -106,67 +107,67 @@ class VariableEditor(gtk.VBox):
         self.treeview.connect('key-press-event', self._handle_key_button_press)
 
         # Block Name or Category
-        self.id_cell = gtk.CellRendererText()
+        self.id_cell = Gtk.CellRendererText()
         self.id_cell.connect('edited', self._handle_name_edited_cb)
-        id_column = gtk.TreeViewColumn("Id", self.id_cell, text=ID_INDEX)
+        id_column = Gtk.TreeViewColumn("Id", self.id_cell, text=ID_INDEX)
         id_column.set_name("id")
         id_column.set_resizable(True)
         id_column.set_max_width(300)
         id_column.set_min_width(80)
         id_column.set_fixed_width(100)
-        id_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+        id_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
         id_column.set_cell_data_func(self.id_cell, self.set_properties)
         self.id_column = id_column
         self.treeview.append_column(id_column)
-        self.treestore.set_sort_column_id(ID_INDEX, gtk.SORT_ASCENDING)
+        self.treestore.set_sort_column_id(ID_INDEX, Gtk.SortType.ASCENDING)
         # For forcing resize
         self._col_width = 0
 
         # Block Value
-        self.value_cell = gtk.CellRendererText()
+        self.value_cell = Gtk.CellRendererText()
         self.value_cell.connect('edited', self._handle_value_edited_cb)
-        value_column = gtk.TreeViewColumn("Value", self.value_cell)
+        value_column = Gtk.TreeViewColumn("Value", self.value_cell)
         value_column.set_name("value")
         value_column.set_resizable(False)
         value_column.set_expand(True)
         value_column.set_min_width(100)
-        value_column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
+        value_column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
         value_column.set_cell_data_func(self.value_cell, self.set_value)
         self.value_column = value_column
         self.treeview.append_column(value_column)
 
         # Block Actions (Add, Remove)
-        self.action_cell = gtk.CellRendererPixbuf()
+        self.action_cell = Gtk.CellRendererPixbuf()
         value_column.pack_start(self.action_cell, False)
         value_column.set_cell_data_func(self.action_cell, self.set_icon)
 
         # Make the scrolled window to hold the tree view
-        scrolled_window = gtk.ScrolledWindow()
-        scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolled_window = Gtk.ScrolledWindow()
+        scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
         scrolled_window.add_with_viewport(self.treeview)
         scrolled_window.set_size_request(DEFAULT_BLOCKS_WINDOW_WIDTH, -1)
-        self.pack_start(scrolled_window)
+        self.pack_start(scrolled_window, True, True, 0)
 
         # Context menus
         self._context_menu = VariableEditorContextMenu(self)
         self._confirm_delete = Preferences.variable_editor_confirm_delete()
 
     # Sets cell contents
-    def set_icon(self, col, cell, model, iter):
+    def set_icon(self, col, cell, model, iter, data):
         block = model.get_value(iter, BLOCK_INDEX)
         if block:
-            pb = self.treeview.render_icon(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU, None)
+            pb = self.treeview.render_icon(Gtk.STOCK_CLOSE, 16, None)
         else:
-            pb = self.treeview.render_icon(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU, None)
+            pb = self.treeview.render_icon(Gtk.STOCK_ADD, 16, None)
         cell.set_property('pixbuf', pb)
 
-    def set_value(self, col, cell, model, iter):
+    def set_value(self, col, cell, model, iter, data):
         sp = cell.set_property
         block = model.get_value(iter, BLOCK_INDEX)
 
         # Set the default properties for this column first.
         # Some set in set_properties() may be overridden (editable for advanced variable blocks)
-        self.set_properties(col, cell, model, iter)
+        self.set_properties(col, cell, model, iter, data)
 
         # Set defaults
         value = None
@@ -198,7 +199,7 @@ class VariableEditor(gtk.VBox):
         # Always set the text value.
         sp('text', value)
 
-    def set_properties(self, col, cell, model, iter):
+    def set_properties(self, col, cell, model, iter, data):
         sp = cell.set_property
         block = model.get_value(iter, BLOCK_INDEX)
         # Set defaults
@@ -268,13 +269,13 @@ class VariableEditor(gtk.VBox):
         elif key == self.DELETE_CONFIRM:
             if self._confirm_delete:
                 # Create a context menu to confirm the delete operation
-                confirmation_menu = gtk.Menu()
+                confirmation_menu = Gtk.Menu()
                 block_id = self._block.get_param('id').get_value().replace("_", "__")
-                confirm = gtk.MenuItem("Delete {}".format(block_id))
+                confirm = Gtk.MenuItem("Delete {}".format(block_id))
                 confirm.connect('activate', self.handle_action, self.DELETE_BLOCK)
                 confirmation_menu.add(confirm)
                 confirmation_menu.show_all()
-                confirmation_menu.popup(None, None, None, event.button, event.time)
+                confirmation_menu.popup(None, None, None, None, event.button, event.time)
             else:
                 self.handle_action(None, self.DELETE_BLOCK, None)
         elif key == self.ENABLE_BLOCK:
@@ -302,12 +303,12 @@ class VariableEditor(gtk.VBox):
 
             if event.button == 1 and col.get_name() == "value":
                 # Make sure this has a block (not the import/variable rows)
-                if self._block and event.type == gtk.gdk._2BUTTON_PRESS:
+                if self._block and event.type == Gdk.EventType._2BUTTON_PRESS:
                     # Open the advanced dialog if it is a gui variable
                     if self._block.get_key() not in ("variable", "import"):
                         self.handle_action(None, self.OPEN_PROPERTIES, event=event)
                         return True
-                if event.type == gtk.gdk.BUTTON_PRESS:
+                if event.type == Gdk.EventType.BUTTON_PRESS:
                     # User is adding/removing blocks
                     # Make sure this is the action cell (Add/Remove Icons)
                     if path[2] > col.cell_get_position(self.action_cell)[0]:
@@ -320,15 +321,15 @@ class VariableEditor(gtk.VBox):
                         else:
                             self.handle_action(None, self.DELETE_CONFIRM, event=event)
                         return True
-            elif event.button == 3 and event.type == gtk.gdk.BUTTON_PRESS:
+            elif event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
                 if self._block:
                     self._context_menu.update_sensitive(True, enabled=self._block.get_enabled())
                 else:
                     self._context_menu.update_sensitive(False)
-                self._context_menu.popup(None, None, None, event.button, event.time)
+                self._context_menu.popup(None, None, None, None, event.button, event.time)
 
             # Null handler. Stops the treeview from handling double click events.
-            if event.type == gtk.gdk._2BUTTON_PRESS:
+            if event.type == Gdk.EventType._2BUTTON_PRESS:
                 return True
         return False
 
-- 
cgit v1.2.3


From 94c4606edd30dc8b1278580782f2809b69f04641 Mon Sep 17 00:00:00 2001
From: Sebastian Koslowski <koslowski@kit.edu>
Date: Fri, 3 Jun 2016 10:02:36 +0200
Subject: grc: py3k compat using python-modernize

---
 grc/checks.py                        |  1 +
 grc/core/Block.py                    | 33 +++++++++--------
 grc/core/Config.py                   |  1 +
 grc/core/Connection.py               |  8 +++--
 grc/core/Constants.py                | 10 ++++--
 grc/core/Element.py                  |  6 ++--
 grc/core/FlowGraph.py                | 57 +++++++++++++++--------------
 grc/core/Messages.py                 |  3 +-
 grc/core/Param.py                    | 69 ++++++++++++++++++------------------
 grc/core/ParseXML.py                 | 21 +++++++----
 grc/core/Platform.py                 | 34 ++++++++++--------
 grc/core/Port.py                     | 41 +++++++++++----------
 grc/core/generator/FlowGraphProxy.py | 16 +++++----
 grc/core/generator/Generator.py      | 30 +++++++++-------
 grc/core/generator/__init__.py       |  3 +-
 grc/core/utils/__init__.py           | 10 +++---
 grc/core/utils/complexity.py         | 13 +++----
 grc/core/utils/epy_block_io.py       | 11 ++++--
 grc/core/utils/expr_utils.py         | 21 ++++++-----
 grc/core/utils/extract_docs.py       | 28 ++++++++-------
 grc/core/utils/odict.py              |  2 ++
 grc/gui/ActionHandler.py             |  8 +++--
 grc/gui/Actions.py                   | 14 +++++---
 grc/gui/Bars.py                      |  3 +-
 grc/gui/Block.py                     |  3 +-
 grc/gui/BlockTreeWindow.py           | 14 +++++---
 grc/gui/Colors.py                    |  1 +
 grc/gui/Config.py                    |  7 ++--
 grc/gui/Connection.py                | 16 ++++-----
 grc/gui/Constants.py                 |  1 +
 grc/gui/Dialogs.py                   |  1 +
 grc/gui/DrawingArea.py               |  1 +
 grc/gui/Element.py                   |  6 ++--
 grc/gui/Executor.py                  |  1 +
 grc/gui/FileDialogs.py               |  9 ++---
 grc/gui/FlowGraph.py                 | 14 +++++---
 grc/gui/MainWindow.py                |  6 ++--
 grc/gui/NotebookPage.py              |  1 +
 grc/gui/Param.py                     |  1 +
 grc/gui/ParamWidgets.py              |  1 +
 grc/gui/ParserErrorsDialog.py        |  8 +++--
 grc/gui/Platform.py                  |  4 ++-
 grc/gui/Port.py                      |  8 ++---
 grc/gui/Preferences.py               | 21 ++++++-----
 grc/gui/PropsDialog.py               |  6 ++--
 grc/gui/StateCache.py                |  5 +--
 grc/gui/Utils.py                     |  8 +++--
 grc/gui/VariableEditor.py            |  6 ++--
 grc/gui/external_editor.py           |  6 ++--
 grc/main.py                          |  1 +
 50 files changed, 356 insertions(+), 243 deletions(-)

(limited to 'grc/gui/Bars.py')

diff --git a/grc/checks.py b/grc/checks.py
index 66c114d723..40a0b2b270 100755
--- a/grc/checks.py
+++ b/grc/checks.py
@@ -15,6 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
+from __future__ import absolute_import
 import os
 import warnings
 
diff --git a/grc/core/Block.py b/grc/core/Block.py
index aafc5db6f1..062598e9d1 100644
--- a/grc/core/Block.py
+++ b/grc/core/Block.py
@@ -17,9 +17,13 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
 import collections
 import itertools
 
+from six.moves import map, range
+
 from Cheetah.Template import Template
 
 from .utils import epy_block_io, odict
@@ -70,7 +74,7 @@ class Block(Element):
             self._flags += BLOCK_FLAG_THROTTLE
 
         self._doc = (n.find('doc') or '').strip('\n').replace('\\\n', '')
-        self._imports = map(lambda i: i.strip(), n.findall('import'))
+        self._imports = [i.strip() for i in n.findall('import')]
         self._make = n.find('make')
         self._var_make = n.find('var_make')
         self._var_value = n.find('var_value') or '$value'
@@ -250,7 +254,7 @@ class Block(Element):
             self.back_ofthe_bus(ports)
             # Renumber non-message/message ports
             domain_specific_port_index = collections.defaultdict(int)
-            for port in filter(lambda p: p.get_key().isdigit(), ports):
+            for port in [p for p in ports if p.get_key().isdigit()]:
                 domain = port.get_domain()
                 port._key = str(domain_specific_port_index[domain])
                 domain_specific_port_index[domain] += 1
@@ -275,7 +279,8 @@ class Block(Element):
         """
         if raw:
             return self._imports
-        return filter(lambda i: i, sum(map(lambda i: self.resolve_dependencies(i).split('\n'), self._imports), []))
+        return [i for i in sum([self.resolve_dependencies(i).split('\n')
+                                for i in self._imports], []) if i]
 
     def get_make(self, raw=False):
         if raw:
@@ -300,7 +305,7 @@ class Block(Element):
             if 'self.' in callback:
                 return callback
             return 'self.{}.{}'.format(self.get_id(), callback)
-        return map(make_callback, self._callbacks)
+        return [make_callback(c) for c in self._callbacks]
 
     def is_virtual_sink(self):
         return self.get_key() == 'virtual_sink'
@@ -622,10 +627,10 @@ class Block(Element):
         """
         changed = False
         type_param = None
-        for param in filter(lambda p: p.is_enum(), self.get_params()):
+        for param in [p for p in self.get_params() if p.is_enum()]:
             children = self.get_ports() + self.get_params()
             # Priority to the type controller
-            if param.get_key() in ' '.join(map(lambda p: p._type, children)): type_param = param
+            if param.get_key() in ' '.join([p._type for p in children]): type_param = param
             # Use param if type param is unset
             if not type_param:
                 type_param = param
@@ -681,10 +686,10 @@ class Block(Element):
         """
         n = odict()
         n['key'] = self.get_key()
-        n['param'] = map(lambda p: p.export_data(), sorted(self.get_params(), key=str))
-        if 'bus' in map(lambda a: a.get_type(), self.get_sinks()):
+        n['param'] = [p.export_data() for p in sorted(self.get_params(), key=str)]
+        if 'bus' in [a.get_type() for a in self.get_sinks()]:
             n['bus_sink'] = str(1)
-        if 'bus' in map(lambda a: a.get_type(), self.get_sources()):
+        if 'bus' in [a.get_type() for a in self.get_sources()]:
             n['bus_source'] = str(1)
         return n
 
@@ -773,12 +778,12 @@ class Block(Element):
             get_p_gui = self.get_sinks_gui
             bus_structure = self.get_bus_structure('sink')
 
-        struct = [range(len(get_p()))]
-        if True in map(lambda a: isinstance(a.get_nports(), int), get_p()):
+        struct = [list(range(len(get_p())))]
+        if True in [isinstance(a.get_nports(), int) for a in get_p()]:
             structlet = []
             last = 0
             for j in [i.get_nports() for i in get_p() if isinstance(i.get_nports(), int)]:
-                structlet.extend(map(lambda a: a+last, range(j)))
+                structlet.extend([a+last for a in range(j)])
                 last = structlet[-1] + 1
                 struct = [structlet]
         if bus_structure:
@@ -802,7 +807,7 @@ class Block(Element):
             for connect in elt.get_connections():
                 self.get_parent().remove_element(connect)
 
-        if ('bus' not in map(lambda a: a.get_type(), get_p())) and len(get_p()) > 0:
+        if ('bus' not in [a.get_type() for a in get_p()]) and len(get_p()) > 0:
             struct = self.form_bus_structure(direc)
             self.current_bus_structure[direc] = struct
             if get_p()[0].get_nports():
@@ -813,7 +818,7 @@ class Block(Element):
                 n = odict(n)
                 port = self.get_parent().get_parent().Port(block=self, n=n, dir=direc)
                 get_p().append(port)
-        elif 'bus' in map(lambda a: a.get_type(), get_p()):
+        elif 'bus' in [a.get_type() for a in get_p()]:
             for elt in get_p_gui():
                 get_p().remove(elt)
             self.current_bus_structure[direc] = ''
diff --git a/grc/core/Config.py b/grc/core/Config.py
index ac38d9978c..400d5d365f 100644
--- a/grc/core/Config.py
+++ b/grc/core/Config.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 import os
 from os.path import expanduser, normpath, expandvars, exists
 
diff --git a/grc/core/Connection.py b/grc/core/Connection.py
index 3aa32ef183..ddc6c0256f 100644
--- a/grc/core/Connection.py
+++ b/grc/core/Connection.py
@@ -17,6 +17,10 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
+from six.moves import range
+
 from . import Constants
 from .Element import Element
 from .utils import odict
@@ -51,8 +55,8 @@ class Connection(Element):
             raise ValueError('Connection could not isolate source')
         if not sink:
             raise ValueError('Connection could not isolate sink')
-        busses = len(filter(lambda a: a.get_type() == 'bus', [source, sink])) % 2
-        if not busses == 0:
+
+        if (source.get_type() == 'bus') != (sink.get_type() == 'bus'):
             raise ValueError('busses must get with busses')
 
         if not len(source.get_associated_ports()) == len(sink.get_associated_ports()):
diff --git a/grc/core/Constants.py b/grc/core/Constants.py
index 4f278bb22d..8a99f8b256 100644
--- a/grc/core/Constants.py
+++ b/grc/core/Constants.py
@@ -17,10 +17,14 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
 import os
-import numpy
 import stat
 
+import numpy
+import six
+
 # Data files
 DATA_DIR = os.path.dirname(__file__)
 FLOW_GRAPH_DTD = os.path.join(DATA_DIR, 'flow_graph.dtd')
@@ -63,7 +67,7 @@ HIER_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP
 VECTOR_TYPES = (tuple, list, set, numpy.ndarray)
 COMPLEX_TYPES = [complex, numpy.complex, numpy.complex64, numpy.complex128]
 REAL_TYPES = [float, numpy.float, numpy.float32, numpy.float64]
-INT_TYPES = [int, long, numpy.int, numpy.int8, numpy.int16, numpy.int32, numpy.uint64,
+INT_TYPES = [int, numpy.int, numpy.int8, numpy.int16, numpy.int32, numpy.uint64,
              numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64]
 # Cast to tuple for isinstance, concat subtypes
 COMPLEX_TYPES = tuple(COMPLEX_TYPES + REAL_TYPES + INT_TYPES)
@@ -129,6 +133,6 @@ for name, key, sizeof, color in CORE_TYPES:
     TYPE_TO_COLOR[key] = color
     TYPE_TO_SIZEOF[key] = sizeof
 
-for key, (sizeof, color) in ALIAS_TYPES.iteritems():
+for key, (sizeof, color) in six.iteritems(ALIAS_TYPES):
     TYPE_TO_COLOR[key] = color
     TYPE_TO_SIZEOF[key] = sizeof
diff --git a/grc/core/Element.py b/grc/core/Element.py
index 67c36e12b4..e697d293fb 100644
--- a/grc/core/Element.py
+++ b/grc/core/Element.py
@@ -22,7 +22,7 @@ class Element(object):
 
     def __init__(self, parent=None):
         self._parent = parent
-        self._error_messages = list()
+        self._error_messages = []
 
     ##################################################
     # Element Validation API
@@ -64,7 +64,9 @@ class Element(object):
             a list of error message strings
         """
         error_messages = list(self._error_messages)  # Make a copy
-        for child in filter(lambda c: c.get_enabled() and not c.get_bypassed(), self.get_children()):
+        for child in self.get_children():
+            if not child.get_enabled() or child.get_bypassed():
+                continue
             for msg in child.get_error_messages():
                 error_messages.append("{}:\n\t{}".format(child, msg.replace("\n", "\n\t")))
         return error_messages
diff --git a/grc/core/FlowGraph.py b/grc/core/FlowGraph.py
index 949eecaa71..9edd4f24d8 100644
--- a/grc/core/FlowGraph.py
+++ b/grc/core/FlowGraph.py
@@ -15,12 +15,15 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
+from __future__ import absolute_import, print_function
+
 import imp
 import time
-from itertools import ifilter, chain
+import re
+from itertools import chain
 from operator import methodcaller, attrgetter
 
-import re
+from six.moves import filter
 
 from . import Messages
 from .Constants import FLOW_GRAPH_FILE_FORMAT_VERSION
@@ -87,7 +90,7 @@ class FlowGraph(Element):
         Returns:
             a sorted list of variable blocks in order of dependency (indep -> dep)
         """
-        variables = filter(attrgetter('is_variable'), self.iter_enabled_blocks())
+        variables = list(filter(attrgetter('is_variable'), self.iter_enabled_blocks()))
         return expr_utils.sort_objects(variables, methodcaller('get_id'), methodcaller('get_var_make'))
 
     def get_parameters(self):
@@ -97,15 +100,14 @@ class FlowGraph(Element):
         Returns:
             a list of parameterized variables
         """
-        parameters = filter(lambda b: _parameter_matcher.match(b.get_key()), self.iter_enabled_blocks())
+        parameters = [b for b in self.iter_enabled_blocks() if _parameter_matcher.match(b.get_key())]
         return parameters
 
     def get_monitors(self):
         """
         Get a list of all ControlPort monitors
         """
-        monitors = filter(lambda b: _monitors_searcher.search(b.get_key()),
-                          self.iter_enabled_blocks())
+        monitors = [b for b in self.iter_enabled_blocks() if _monitors_searcher.search(b.get_key())]
         return monitors
 
     def get_python_modules(self):
@@ -115,7 +117,7 @@ class FlowGraph(Element):
                 yield block.get_id(), block.get_param('source_code').get_value()
 
     def get_bussink(self):
-        bussink = filter(lambda b: _bussink_searcher.search(b.get_key()), self.get_enabled_blocks())
+        bussink = [b for b in self.get_enabled_blocks() if _bussink_searcher.search(b.get_key())]
 
         for i in bussink:
             for j in i.get_params():
@@ -124,7 +126,7 @@ class FlowGraph(Element):
         return False
 
     def get_bussrc(self):
-        bussrc = filter(lambda b: _bussrc_searcher.search(b.get_key()), self.get_enabled_blocks())
+        bussrc = [b for b in self.get_enabled_blocks() if _bussrc_searcher.search(b.get_key())]
 
         for i in bussrc:
             for j in i.get_params():
@@ -133,18 +135,18 @@ class FlowGraph(Element):
         return False
 
     def get_bus_structure_sink(self):
-        bussink = filter(lambda b: _bus_struct_sink_searcher.search(b.get_key()), self.get_enabled_blocks())
+        bussink = [b for b in self.get_enabled_blocks() if _bus_struct_sink_searcher.search(b.get_key())]
         return bussink
 
     def get_bus_structure_src(self):
-        bussrc = filter(lambda b: _bus_struct_src_searcher.search(b.get_key()), self.get_enabled_blocks())
+        bussrc = [b for b in self.get_enabled_blocks() if _bus_struct_src_searcher.search(b.get_key())]
         return bussrc
 
     def iter_enabled_blocks(self):
         """
         Get an iterator of all blocks that are enabled and not bypassed.
         """
-        return ifilter(methodcaller('get_enabled'), self.blocks)
+        return filter(methodcaller('get_enabled'), self.blocks)
 
     def get_enabled_blocks(self):
         """
@@ -162,7 +164,7 @@ class FlowGraph(Element):
         Returns:
             a list of blocks
         """
-        return filter(methodcaller('get_bypassed'), self.blocks)
+        return list(filter(methodcaller('get_bypassed'), self.blocks))
 
     def get_enabled_connections(self):
         """
@@ -171,7 +173,7 @@ class FlowGraph(Element):
         Returns:
             a list of connections
         """
-        return filter(methodcaller('get_enabled'), self.connections)
+        return list(filter(methodcaller('get_enabled'), self.connections))
 
     def get_option(self, key):
         """
@@ -206,7 +208,7 @@ class FlowGraph(Element):
         options_block_count = self.blocks.count(self._options_block)
         if not options_block_count:
             self.blocks.append(self._options_block)
-        for i in range(options_block_count-1):
+        for _ in range(options_block_count-1):
             self.blocks.remove(self._options_block)
 
         return self.blocks + self.connections
@@ -229,14 +231,14 @@ class FlowGraph(Element):
         # Load imports
         for expr in self.get_imports():
             try:
-                exec expr in namespace
+                exec(expr, namespace)
             except:
                 pass
 
         for id, expr in self.get_python_modules():
             try:
                 module = imp.new_module(id)
-                exec expr in module.__dict__
+                exec(expr, module.__dict__)
                 namespace[id] = module
             except:
                 pass
@@ -333,15 +335,15 @@ class FlowGraph(Element):
         if element in self.blocks:
             # Remove block, remove all involved connections
             for port in element.get_ports():
-                map(self.remove_element, port.get_connections())
+                for connection in port.get_connections():
+                    self.remove_element(connection)
             self.blocks.remove(element)
 
         elif element in self.connections:
             if element.is_bus():
-                cons_list = []
-                for i in map(lambda a: a.get_connections(), element.get_source().get_associated_ports()):
-                    cons_list.extend(i)
-                map(self.remove_element, cons_list)
+                for port in element.get_source().get_associated_ports():
+                    for connection in port.get_connections():
+                        self.remove_element(connection)
             self.connections.remove(element)
 
     ##############################################
@@ -484,21 +486,19 @@ class FlowGraph(Element):
                     get_p_gui = block.get_sinks_gui
                     bus_structure = block.form_bus_structure('sink')
 
-                if 'bus' in map(lambda a: a.get_type(), get_p_gui()):
+                if 'bus' in [a.get_type() for a in get_p_gui()]:
                     if len(get_p_gui()) > len(bus_structure):
-                        times = range(len(bus_structure), len(get_p_gui()))
+                        times = list(range(len(bus_structure), len(get_p_gui())))
                         for i in times:
                             for connect in get_p_gui()[-1].get_connections():
                                 block.get_parent().remove_element(connect)
                             get_p().remove(get_p_gui()[-1])
                     elif len(get_p_gui()) < len(bus_structure):
                         n = {'name': 'bus', 'type': 'bus'}
-                        if True in map(
-                                lambda a: isinstance(a.get_nports(), int),
-                                get_p()):
+                        if any(isinstance(a.get_nports(), int) for a in get_p()):
                             n['nports'] = str(1)
 
-                        times = range(len(get_p_gui()), len(bus_structure))
+                        times = list(range(len(get_p_gui()), len(bus_structure)))
 
                         for i in times:
                             n['key'] = str(len(get_p()))
@@ -507,8 +507,7 @@ class FlowGraph(Element):
                                 block=block, n=n, dir=direc)
                             get_p().append(port)
 
-                if 'bus' in map(lambda a: a.get_type(),
-                                block.get_sources_gui()):
+                if 'bus' in [a.get_type() for a in block.get_sources_gui()]:
                     for i in range(len(block.get_sources_gui())):
                         if len(block.get_sources_gui()[
                                    i].get_connections()) > 0:
diff --git a/grc/core/Messages.py b/grc/core/Messages.py
index 8daa12c33f..596b6197d8 100644
--- a/grc/core/Messages.py
+++ b/grc/core/Messages.py
@@ -16,9 +16,10 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 
+from __future__ import absolute_import
+
 import traceback
 import sys
-import os
 
 #  A list of functions that can receive a message.
 MESSENGERS_LIST = list()
diff --git a/grc/core/Param.py b/grc/core/Param.py
index 73d54b6aff..45f0187d27 100644
--- a/grc/core/Param.py
+++ b/grc/core/Param.py
@@ -17,20 +17,20 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
 import ast
 import weakref
 import re
 
+from six.moves import builtins, filter, map, range, zip
+
 from . import Constants
-from .Constants import VECTOR_TYPES, COMPLEX_TYPES, REAL_TYPES, INT_TYPES
 from .Element import Element
 from .utils import odict
 
 # Blacklist certain ids, its not complete, but should help
-import __builtin__
-
-
-ID_BLACKLIST = ['self', 'options', 'gr', 'blks2', 'wxgui', 'wx', 'math', 'forms', 'firdes'] + dir(__builtin__)
+ID_BLACKLIST = ['self', 'options', 'gr', 'blks2', 'wxgui', 'wx', 'math', 'forms', 'firdes'] + dir(builtins)
 try:
     from gnuradio import gr
     ID_BLACKLIST.extend(attr for attr in dir(gr.top_block()) if not attr.startswith('_'))
@@ -64,7 +64,7 @@ def num_to_str(num):
                 return template.format(value / factor, symbol.strip())
         return template.format(value, '')
 
-    if isinstance(num, COMPLEX_TYPES):
+    if isinstance(num, Constants.COMPLEX_TYPES):
         num = complex(num)  # Cast to python complex
         if num == 0:
             return '0'
@@ -112,13 +112,13 @@ class Option(Element):
     # Access Opts
     ##############################################
     def get_opt_keys(self):
-        return self._opts.keys()
+        return list(self._opts.keys())
 
     def get_opt(self, key):
         return self._opts[key]
 
     def get_opts(self):
-        return self._opts.values()
+        return list(self._opts.values())
 
 
 class TemplateArg(object):
@@ -176,7 +176,8 @@ class Param(Element):
         # Create the Option objects from the n data
         self._options = list()
         self._evaluated = None
-        for option in map(lambda o: Option(param=self, n=o), n.findall('option')):
+        for o_n in n.findall('option'):
+            option = Option(param=self, n=o_n)
             key = option.get_key()
             # Test against repeated keys
             if key in self.get_option_keys():
@@ -257,9 +258,9 @@ class Param(Element):
         t = self.get_type()
         if isinstance(e, bool):
             return str(e)
-        elif isinstance(e, COMPLEX_TYPES):
+        elif isinstance(e, Constants.COMPLEX_TYPES):
             dt_str = num_to_str(e)
-        elif isinstance(e, VECTOR_TYPES):
+        elif isinstance(e, Constants.VECTOR_TYPES):
             # Vector types
             if len(e) > 8:
                 # Large vectors use code
@@ -310,13 +311,10 @@ class Param(Element):
         if self.get_key() == 'id' and not _show_id_matcher.match(self.get_parent().get_key()):
             return 'part'
         # Hide port controllers for type and nports
-        if self.get_key() in ' '.join(map(lambda p: ' '.join([p._type, p._nports]),
-                                          self.get_parent().get_ports())):
+        if self.get_key() in ' '.join([' '.join([p._type, p._nports]) for p in self.get_parent().get_ports()]):
             return 'part'
         # Hide port controllers for vlen, when == 1
-        if self.get_key() in ' '.join(map(
-            lambda p: p._vlen, self.get_parent().get_ports())
-        ):
+        if self.get_key() in ' '.join(p._vlen for p in self.get_parent().get_ports()):
             try:
                 if int(self.get_evaluated()) == 1:
                     return 'part'
@@ -339,7 +337,7 @@ class Param(Element):
         self._evaluated = None
         try:
             self._evaluated = self.evaluate()
-        except Exception, e:
+        except Exception as e:
             self.add_error_message(str(e))
 
     def get_evaluated(self):
@@ -372,21 +370,21 @@ class Param(Element):
             # Raise exception if python cannot evaluate this value
             try:
                 e = self.get_parent().get_parent().evaluate(v)
-            except Exception, e:
+            except Exception as e:
                 raise Exception('Value "{}" cannot be evaluated:\n{}'.format(v, e))
             # Raise an exception if the data is invalid
             if t == 'raw':
                 return e
             elif t == 'complex':
-                if not isinstance(e, COMPLEX_TYPES):
+                if not isinstance(e, Constants.COMPLEX_TYPES):
                     raise Exception('Expression "{}" is invalid for type complex.'.format(str(e)))
                 return e
             elif t == 'real' or t == 'float':
-                if not isinstance(e, REAL_TYPES):
+                if not isinstance(e, Constants.REAL_TYPES):
                     raise Exception('Expression "{}" is invalid for type float.'.format(str(e)))
                 return e
             elif t == 'int':
-                if not isinstance(e, INT_TYPES):
+                if not isinstance(e, Constants.INT_TYPES):
                     raise Exception('Expression "{}" is invalid for type integer.'.format(str(e)))
                 return e
             elif t == 'hex':
@@ -407,28 +405,28 @@ class Param(Element):
             # Raise exception if python cannot evaluate this value
             try:
                 e = self.get_parent().get_parent().evaluate(v)
-            except Exception, e:
+            except Exception as e:
                 raise Exception('Value "{}" cannot be evaluated:\n{}'.format(v, e))
             # Raise an exception if the data is invalid
             if t == 'complex_vector':
-                if not isinstance(e, VECTOR_TYPES):
+                if not isinstance(e, Constants.VECTOR_TYPES):
                     self._lisitify_flag = True
                     e = [e]
-                if not all([isinstance(ei, COMPLEX_TYPES) for ei in e]):
+                if not all([isinstance(ei, Constants.COMPLEX_TYPES) for ei in e]):
                     raise Exception('Expression "{}" is invalid for type complex vector.'.format(str(e)))
                 return e
             elif t == 'real_vector' or t == 'float_vector':
-                if not isinstance(e, VECTOR_TYPES):
+                if not isinstance(e, Constants.VECTOR_TYPES):
                     self._lisitify_flag = True
                     e = [e]
-                if not all([isinstance(ei, REAL_TYPES) for ei in e]):
+                if not all([isinstance(ei, Constants.REAL_TYPES) for ei in e]):
                     raise Exception('Expression "{}" is invalid for type float vector.'.format(str(e)))
                 return e
             elif t == 'int_vector':
-                if not isinstance(e, VECTOR_TYPES):
+                if not isinstance(e, Constants.VECTOR_TYPES):
                     self._lisitify_flag = True
                     e = [e]
-                if not all([isinstance(ei, INT_TYPES) for ei in e]):
+                if not all([isinstance(ei, Constants.INT_TYPES) for ei in e]):
                     raise Exception('Expression "{}" is invalid for type integer vector.'.format(str(e)))
                 return e
         #########################
@@ -545,7 +543,7 @@ class Param(Element):
                 for c in range(col_span):
                     self._hostage_cells.append((my_parent, (row+r, col+c)))
             # Avoid collisions
-            params = filter(lambda p: p is not self, self.get_all_params('grid_pos'))
+            params = [p for p in self.get_all_params('grid_pos') if p is not self]
             for param in params:
                 for parent, cell in param._hostage_cells:
                     if (parent, cell) in self._hostage_cells:
@@ -560,7 +558,7 @@ class Param(Element):
                 return ''
 
             # Get a list of all notebooks
-            notebook_blocks = filter(lambda b: b.get_key() == 'notebook', self.get_parent().get_parent().get_enabled_blocks())
+            notebook_blocks = [b for b in self.get_parent().get_parent().get_enabled_blocks() if b.get_key() == 'notebook']
             # Check for notebook param syntax
             try:
                 notebook_id, page_index = map(str.strip, v.split(','))
@@ -568,7 +566,7 @@ class Param(Element):
                 raise Exception('Bad notebook page format.')
             # Check that the notebook id is valid
             try:
-                notebook_block = filter(lambda b: b.get_id() == notebook_id, notebook_blocks)[0]
+                notebook_block = [b for b in notebook_blocks if b.get_id() == notebook_id][0]
             except:
                 raise Exception('Notebook id "{}" is not an existing notebook id.'.format(notebook_id))
 
@@ -584,12 +582,12 @@ class Param(Element):
             # New namespace
             n = dict()
             try:
-                exec v in n
+                exec(v, n)
             except ImportError:
                 raise Exception('Import "{}" failed.'.format(v))
             except Exception:
                 raise Exception('Bad import syntax: "{}".'.format(v))
-            return filter(lambda k: str(k) != '__builtins__', n.keys())
+            return [k for k in list(n.keys()) if str(k) != '__builtins__']
 
         #########################
         else:
@@ -635,7 +633,10 @@ class Param(Element):
         Returns:
             a list of params
         """
-        return sum([filter(lambda p: p.get_type() == type, block.get_params()) for block in self.get_parent().get_parent().get_enabled_blocks()], [])
+        params = []
+        for block in self.get_parent().get_parent().get_enabled_blocks():
+            params.extend(p for p in block.get_params() if p.get_type() == type)
+        return params
 
     def is_enum(self):
         return self._type == 'enum'
diff --git a/grc/core/ParseXML.py b/grc/core/ParseXML.py
index c9f6541ee7..d1306fcab4 100644
--- a/grc/core/ParseXML.py
+++ b/grc/core/ParseXML.py
@@ -17,8 +17,13 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
 from lxml import etree
 
+import six
+from six.moves import map
+
 from .utils import odict
 
 xml_failures = {}
@@ -80,7 +85,9 @@ def from_file(xml_file):
     # Get the embedded instructions and build a dictionary item
     nested_data['_instructions'] = {}
     xml_instructions = xml.xpath('/processing-instruction()')
-    for inst in filter(lambda i: i.target == 'grc', xml_instructions):
+    for inst in xml_instructions:
+        if inst.target != 'grc':
+            continue
         nested_data['_instructions'] = odict(inst.attrib)
     return nested_data
 
@@ -100,13 +107,13 @@ def _from_file(xml):
         return odict({tag: xml.text or ''})  # store empty tags (text is None) as empty string
     nested_data = odict()
     for elem in xml:
-        key, value = _from_file(elem).items()[0]
+        key, value = list(_from_file(elem).items())[0]
         if key in nested_data:
             nested_data[key].append(value)
         else:
             nested_data[key] = [value]
     # Delistify if the length of values is 1
-    for key, values in nested_data.iteritems():
+    for key, values in six.iteritems(nested_data):
         if len(values) == 1:
             nested_data[key] = values[0]
 
@@ -127,7 +134,7 @@ def to_file(nested_data, xml_file):
     if instructions:
         xml_data += etree.tostring(etree.ProcessingInstruction(
             'grc', ' '.join(
-                "{0}='{1}'".format(*item) for item in instructions.iteritems())
+                "{0}='{1}'".format(*item) for item in six.iteritems(instructions))
         ), xml_declaration=True, pretty_print=True, encoding='utf-8')
     xml_data += etree.tostring(_to_file(nested_data)[0],
                                pretty_print=True, encoding='utf-8')
@@ -146,14 +153,14 @@ def _to_file(nested_data):
         the xml tree filled with child nodes
     """
     nodes = list()
-    for key, values in nested_data.iteritems():
+    for key, values in six.iteritems(nested_data):
         # Listify the values if not a list
         if not isinstance(values, (list, set, tuple)):
             values = [values]
         for value in values:
             node = etree.Element(key)
-            if isinstance(value, (str, unicode)):
-                node.text = unicode(value)
+            if isinstance(value, (str, six.text_type)):
+                node.text = six.text_type(value)
             else:
                 node.extend(_to_file(value))
             nodes.append(node)
diff --git a/grc/core/Platform.py b/grc/core/Platform.py
index 02a625bbf4..25f415639a 100644
--- a/grc/core/Platform.py
+++ b/grc/core/Platform.py
@@ -17,6 +17,8 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+from __future__ import print_function
 import os
 import sys
 
@@ -32,6 +34,9 @@ from .Port import Port
 from .Param import Param
 
 from .utils import odict, extract_docs
+import six
+from six.moves import map
+from six.moves import range
 
 
 class Platform(Element):
@@ -156,7 +161,7 @@ class Platform(Element):
                 # print >> sys.stderr, 'Warning: Block validation failed:\n\t%s\n\tIgnoring: %s' % (e, xml_file)
                 pass
             except Exception as e:
-                print >> sys.stderr, 'Warning: XML parsing failed:\n\t%r\n\tIgnoring: %s' % (e, xml_file)
+                print('Warning: XML parsing failed:\n\t%r\n\tIgnoring: %s' % (e, xml_file), file=sys.stderr)
 
         self._docstring_extractor.finish()
         # self._docstring_extractor.wait()
@@ -168,7 +173,7 @@ class Platform(Element):
                 yield block_path
             elif os.path.isdir(block_path):
                 for dirpath, dirnames, filenames in os.walk(block_path):
-                    for filename in sorted(filter(lambda f: f.endswith('.xml'), filenames)):
+                    for filename in sorted(f for f in filenames if f.endswith('.xml')):
                         yield os.path.join(dirpath, filename)
 
     def load_block_xml(self, xml_file):
@@ -181,7 +186,7 @@ class Platform(Element):
         block = self.Block(self._flow_graph, n)
         key = block.get_key()
         if key in self.blocks:
-            print >> sys.stderr, 'Warning: Block with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file)
+            print('Warning: Block with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file), file=sys.stderr)
         else:  # Store the block
             self.blocks[key] = block
             self._blocks_n[key] = n
@@ -205,13 +210,13 @@ class Platform(Element):
 
         key = n.find('key')
         if not key:
-            print >> sys.stderr, 'Warning: Domain with emtpy key.\n\tIgnoring: {}'.format(xml_file)
+            print('Warning: Domain with emtpy key.\n\tIgnoring: {}'.format(xml_file), file=sys.stderr)
             return
         if key in self.domains:  # test against repeated keys
-            print >> sys.stderr, 'Warning: Domain with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file)
+            print('Warning: Domain with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file), file=sys.stderr)
             return
 
-        #to_bool = lambda s, d: d if s is None else s.lower() not in ('false', 'off', '0', '')
+        # to_bool = lambda s, d: d if s is None else s.lower() not in ('false', 'off', '0', '')
         def to_bool(s, d):
             if s is not None:
                 return s.lower() not in ('false', 'off', '0', '')
@@ -223,7 +228,7 @@ class Platform(Element):
             tuple(int(color[o:o + 2], 16) / 255.0 for o in range(1, 3 * chars_per_color, chars_per_color))
         except ValueError:
             if color:  # no color is okay, default set in GUI
-                print >> sys.stderr, 'Warning: Can\'t parse color code "{}" for domain "{}" '.format(color, key)
+                print('Warning: Can\'t parse color code "{}" for domain "{}" '.format(color, key), file=sys.stderr)
                 color = None
 
         self.domains[key] = dict(
@@ -235,9 +240,9 @@ class Platform(Element):
         for connection_n in n.findall('connection'):
             key = (connection_n.find('source_domain'), connection_n.find('sink_domain'))
             if not all(key):
-                print >> sys.stderr, 'Warning: Empty domain key(s) in connection template.\n\t{}'.format(xml_file)
+                print('Warning: Empty domain key(s) in connection template.\n\t{}'.format(xml_file), file=sys.stderr)
             elif key in self.connection_templates:
-                print >> sys.stderr, 'Warning: Connection template "{}" already exists.\n\t{}'.format(key, xml_file)
+                print('Warning: Connection template "{}" already exists.\n\t{}'.format(key, xml_file), file=sys.stderr)
             else:
                 self.connection_templates[key] = connection_n.find('make') or ''
 
@@ -256,11 +261,12 @@ class Platform(Element):
             parent = (parent or []) + [cat_n.find('name')]
             block_tree.add_block(parent)
             # Recursive call to load sub categories
-            map(lambda c: load_category(c, parent), cat_n.findall('cat'))
+            for cat in cat_n.findall('cat'):
+                load_category(cat, parent)
             # Add blocks in this category
             for block_key in cat_n.findall('block'):
                 if block_key not in self.blocks:
-                    print >> sys.stderr, 'Warning: Block key "{}" not found when loading category tree.'.format(block_key)
+                    print('Warning: Block key "{}" not found when loading category tree.'.format(block_key), file=sys.stderr)
                     continue
                 block = self.blocks[block_key]
                 # If it exists, the block's category shall not be overridden by the xml tree
@@ -272,7 +278,7 @@ class Platform(Element):
             load_category(category_tree_n)
 
         # Add blocks to block tree
-        for block in self.blocks.itervalues():
+        for block in six.itervalues(self.blocks):
             # Blocks with empty categories are hidden
             if not block.get_category():
                 continue
@@ -280,7 +286,7 @@ class Platform(Element):
 
     def _save_docstring_extraction_result(self, key, docstrings):
         docs = {}
-        for match, docstring in docstrings.iteritems():
+        for match, docstring in six.iteritems(docstrings):
             if not docstring or match.endswith('_sptr'):
                 continue
             docstring = docstring.replace('\n\n', '\n').strip()
@@ -312,7 +318,7 @@ class Platform(Element):
         return self.FlowGraph(platform=self)
 
     def get_blocks(self):
-        return self.blocks.values()
+        return list(self.blocks.values())
 
     def get_new_block(self, flow_graph, key):
         return self.Block(flow_graph, n=self._blocks_n[key])
diff --git a/grc/core/Port.py b/grc/core/Port.py
index 6a8f484082..a24262da6b 100644
--- a/grc/core/Port.py
+++ b/grc/core/Port.py
@@ -17,7 +17,10 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
-from .Constants import DEFAULT_DOMAIN, GR_STREAM_DOMAIN, GR_MESSAGE_DOMAIN
+from __future__ import absolute_import
+
+from six.moves import filter
+
 from .Element import Element
 
 from . import Constants
@@ -47,13 +50,13 @@ def _get_source_from_virtual_source_port(vsp, traversed=[]):
     try:
         return _get_source_from_virtual_source_port(
             _get_source_from_virtual_sink_port(
-                filter(  # Get all virtual sinks with a matching stream id
+                list(filter(  # Get all virtual sinks with a matching stream id
                     lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(),
-                    filter(  # Get all enabled blocks that are also virtual sinks
+                    list(filter(  # Get all enabled blocks that are also virtual sinks
                         lambda b: b.is_virtual_sink(),
                         vsp.get_parent().get_parent().get_enabled_blocks(),
-                    ),
-                )[0].get_sinks()[0]
+                    )),
+                ))[0].get_sinks()[0]
             ), traversed + [vsp],
         )
     except:
@@ -87,10 +90,10 @@ def _get_sink_from_virtual_sink_port(vsp, traversed=[]):
             _get_sink_from_virtual_source_port(
                 filter(  # Get all virtual source with a matching stream id
                     lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(),
-                    filter(  # Get all enabled blocks that are also virtual sinks
+                    list(filter(  # Get all enabled blocks that are also virtual sinks
                         lambda b: b.is_virtual_source(),
                         vsp.get_parent().get_parent().get_enabled_blocks(),
-                    ),
+                    )),
                 )[0].get_sources()[0]
             ), traversed + [vsp],
         )
@@ -113,10 +116,10 @@ class Port(Element):
         """
         self._n = n
         if n['type'] == 'message':
-            n['domain'] = GR_MESSAGE_DOMAIN
+            n['domain'] = Constants.GR_MESSAGE_DOMAIN
         if 'domain' not in n:
-            n['domain'] = DEFAULT_DOMAIN
-        elif n['domain'] == GR_MESSAGE_DOMAIN:
+            n['domain'] = Constants.DEFAULT_DOMAIN
+        elif n['domain'] == Constants.GR_MESSAGE_DOMAIN:
             n['key'] = n['name']
             n['type'] = 'message'  # For port color
         if n['type'] == 'msg':
@@ -147,7 +150,7 @@ class Port(Element):
             return 'Sink - {}({})'.format(self.get_name(), self.get_key())
 
     def get_types(self):
-        return Constants.TYPE_TO_SIZEOF.keys()
+        return list(Constants.TYPE_TO_SIZEOF.keys())
 
     def is_type_empty(self):
         return not self._n['type']
@@ -189,11 +192,11 @@ class Port(Element):
 
         # Update domain if was deduced from (dynamic) port type
         type_ = self.get_type()
-        if self._domain == GR_STREAM_DOMAIN and type_ == "message":
-            self._domain = GR_MESSAGE_DOMAIN
+        if self._domain == Constants.GR_STREAM_DOMAIN and type_ == "message":
+            self._domain = Constants.GR_MESSAGE_DOMAIN
             self._key = self._name
-        if self._domain == GR_MESSAGE_DOMAIN and type_ != "message":
-            self._domain = GR_STREAM_DOMAIN
+        if self._domain == Constants.GR_MESSAGE_DOMAIN and type_ != "message":
+            self._domain = Constants.GR_STREAM_DOMAIN
             self._key = '0'  # Is rectified in rewrite()
 
     def resolve_virtual_source(self):
@@ -341,7 +344,7 @@ class Port(Element):
     def get_name(self):
         number = ''
         if self.get_type() == 'bus':
-            busses = filter(lambda a: a._dir == self._dir, self.get_parent().get_ports_gui())
+            busses = [a for a in self.get_parent().get_ports_gui() if a._dir == self._dir]
             number = str(busses.index(self)) + '#' + str(len(self.get_associated_ports()))
         return self._name + number
 
@@ -373,7 +376,7 @@ class Port(Element):
             a list of connection objects
         """
         connections = self.get_parent().get_parent().connections
-        connections = filter(lambda c: c.get_source() is self or c.get_sink() is self, connections)
+        connections = [c for c in connections if c.get_source() is self or c.get_sink() is self]
         return connections
 
     def get_enabled_connections(self):
@@ -383,7 +386,7 @@ class Port(Element):
         Returns:
             a list of connection objects
         """
-        return filter(lambda c: c.get_enabled(), self.get_connections())
+        return [c for c in self.get_connections() if c.get_enabled()]
 
     def get_associated_ports(self):
         if not self.get_type() == 'bus':
@@ -400,5 +403,5 @@ class Port(Element):
             if bus_structure:
                 busses = [i for i in get_ports() if i.get_type() == 'bus']
                 bus_index = busses.index(self)
-                ports = filter(lambda a: ports.index(a) in bus_structure[bus_index], ports)
+                ports = [a for a in ports if ports.index(a) in bus_structure[bus_index]]
             return ports
diff --git a/grc/core/generator/FlowGraphProxy.py b/grc/core/generator/FlowGraphProxy.py
index 3723005576..c673c5b005 100644
--- a/grc/core/generator/FlowGraphProxy.py
+++ b/grc/core/generator/FlowGraphProxy.py
@@ -16,6 +16,10 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 
+from __future__ import absolute_import
+from six.moves import range
+
+
 class FlowGraphProxy(object):
 
     def __init__(self, fg):
@@ -34,7 +38,7 @@ class FlowGraphProxy(object):
         Returns:
             a list of dicts with: type, label, vlen, size, optional
         """
-        return filter(lambda p: p['type'] != "message", self.get_hier_block_io(direction))
+        return [p for p in self.get_hier_block_io(direction) if p['type'] != "message"]
 
     def get_hier_block_message_io(self, direction):
         """
@@ -46,7 +50,7 @@ class FlowGraphProxy(object):
         Returns:
             a list of dicts with: type, label, vlen, size, optional
         """
-        return filter(lambda p: p['type'] == "message", self.get_hier_block_io(direction))
+        return [p for p in self.get_hier_block_io(direction) if p['type'] == "message"]
 
     def get_hier_block_io(self, direction):
         """
@@ -71,7 +75,7 @@ class FlowGraphProxy(object):
             }
             num_ports = pad.get_param('num_streams').get_evaluated()
             if num_ports > 1:
-                for i in xrange(num_ports):
+                for i in range(num_ports):
                     clone = master.copy()
                     clone['label'] += str(i)
                     ports.append(clone)
@@ -86,7 +90,7 @@ class FlowGraphProxy(object):
         Returns:
             a list of pad source blocks in this flow graph
         """
-        pads = filter(lambda b: b.get_key() == 'pad_source', self.get_enabled_blocks())
+        pads = [b for b in self.get_enabled_blocks() if b.get_key() == 'pad_source']
         return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id()))
 
     def get_pad_sinks(self):
@@ -96,7 +100,7 @@ class FlowGraphProxy(object):
         Returns:
             a list of pad sink blocks in this flow graph
         """
-        pads = filter(lambda b: b.get_key() == 'pad_sink', self.get_enabled_blocks())
+        pads = [b for b in self.get_enabled_blocks() if b.get_key() == 'pad_sink']
         return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id()))
 
     def get_pad_port_global_key(self, port):
@@ -123,4 +127,4 @@ class FlowGraphProxy(object):
                 # assuming we have either only sources or sinks
                 if not is_message_pad:
                     key_offset += len(pad.get_ports())
-        return -1
\ No newline at end of file
+        return -1
diff --git a/grc/core/generator/Generator.py b/grc/core/generator/Generator.py
index 0d0ca6f55f..c9b065372d 100644
--- a/grc/core/generator/Generator.py
+++ b/grc/core/generator/Generator.py
@@ -16,9 +16,11 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 
+from __future__ import absolute_import
 import codecs
 import os
 import tempfile
+import operator
 
 from Cheetah.Template import Template
 
@@ -85,14 +87,15 @@ class TopBlockGenerator(object):
     def write(self):
         """generate output and write it to files"""
         # Do throttle warning
-        throttling_blocks = filter(lambda b: b.throtteling(), self._flow_graph.get_enabled_blocks())
+        throttling_blocks = [b for b in self._flow_graph.get_enabled_blocks()
+                             if b.throtteling()]
         if not throttling_blocks and not self._generate_options.startswith('hb'):
             Messages.send_warning("This flow graph may not have flow control: "
                                   "no audio or RF hardware blocks found. "
                                   "Add a Misc->Throttle block to your flow "
                                   "graph to avoid CPU congestion.")
         if len(throttling_blocks) > 1:
-            keys = set(map(lambda b: b.get_key(), throttling_blocks))
+            keys = set([b.get_key() for b in throttling_blocks])
             if len(keys) > 1 and 'blocks_throttle' in keys:
                 Messages.send_warning("This flow graph contains a throttle "
                                       "block and another rate limiting block, "
@@ -139,15 +142,15 @@ class TopBlockGenerator(object):
             return code
 
         blocks = expr_utils.sort_objects(
-            filter(lambda b: b.get_enabled() and not b.get_bypassed(), fg.blocks),
-            lambda b: b.get_id(), _get_block_sort_text
+            [b for b in fg.blocks if b.get_enabled() and not b.get_bypassed()],
+            operator.methodcaller('get_id'), _get_block_sort_text
         )
         deprecated_block_keys = set(block.get_name() for block in blocks if block.is_deprecated)
         for key in deprecated_block_keys:
             Messages.send_warning("The block {!r} is deprecated.".format(key))
 
         # List of regular blocks (all blocks minus the special ones)
-        blocks = filter(lambda b: b not in (imports + parameters), blocks)
+        blocks = [b for b in blocks if b not in imports and b not in parameters]
 
         for block in blocks:
             key = block.get_key()
@@ -162,10 +165,10 @@ class TopBlockGenerator(object):
         # Filter out virtual sink connections
         def cf(c):
             return not (c.is_bus() or c.is_msg() or c.get_sink().get_parent().is_virtual_sink())
-        connections = filter(cf, fg.get_enabled_connections())
+        connections = [con for con in fg.get_enabled_connections() if cf(con)]
 
         # Get the virtual blocks and resolve their connections
-        virtual = filter(lambda c: c.get_source().get_parent().is_virtual_source(), connections)
+        virtual = [c for c in connections if c.get_source().get_parent().is_virtual_source()]
         for connection in virtual:
             source = connection.get_source().resolve_virtual_source()
             sink = connection.get_sink()
@@ -183,7 +186,7 @@ class TopBlockGenerator(object):
         for block in bypassed_blocks:
             # Get the upstream connection (off of the sink ports)
             # Use *connections* not get_connections()
-            source_connection = filter(lambda c: c.get_sink() == block.get_sinks()[0], connections)
+            source_connection = [c for c in connections if c.get_sink() == block.get_sinks()[0]]
             # The source connection should never have more than one element.
             assert (len(source_connection) == 1)
 
@@ -191,7 +194,7 @@ class TopBlockGenerator(object):
             source_port = source_connection[0].get_source()
 
             # Loop through all the downstream connections
-            for sink in filter(lambda c: c.get_source() == block.get_sources()[0], connections):
+            for sink in (c for c in connections if c.get_source() == block.get_sources()[0]):
                 if not sink.get_enabled():
                     # Ignore disabled connections
                     continue
@@ -210,7 +213,8 @@ class TopBlockGenerator(object):
         ))
 
         connection_templates = fg.get_parent().connection_templates
-        msgs = filter(lambda c: c.is_msg(), fg.get_enabled_connections())
+        msgs = [c for c in fg.get_enabled_connections() if c.is_msg()]
+
         # List of variable names
         var_ids = [var.get_id() for var in parameters + variables]
         # Prepend self.
@@ -222,7 +226,7 @@ class TopBlockGenerator(object):
             ]
         # Map var id to callbacks
         var_id2cbs = dict([
-            (var_id, filter(lambda c: expr_utils.get_variable_dependencies(c, [var_id]), callbacks))
+            (var_id, [c for c in callbacks if expr_utils.get_variable_dependencies(c, [var_id])])
             for var_id in var_ids
         ])
         # Load the namespace
@@ -290,8 +294,8 @@ class HierBlockGenerator(TopBlockGenerator):
         parameters = self._flow_graph.get_parameters()
 
         def var_or_value(name):
-            if name in map(lambda p: p.get_id(), parameters):
-                return "$"+name
+            if name in (p.get_id() for p in parameters):
+                return "$" + name
             return name
 
         # Build the nested data
diff --git a/grc/core/generator/__init__.py b/grc/core/generator/__init__.py
index f44b94a85d..98f410c8d4 100644
--- a/grc/core/generator/__init__.py
+++ b/grc/core/generator/__init__.py
@@ -15,4 +15,5 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
-from Generator import Generator
+from __future__ import absolute_import
+from .Generator import Generator
diff --git a/grc/core/utils/__init__.py b/grc/core/utils/__init__.py
index 6b23da2723..0d84f7131d 100644
--- a/grc/core/utils/__init__.py
+++ b/grc/core/utils/__init__.py
@@ -15,8 +15,10 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
-import expr_utils
-import epy_block_io
-import extract_docs
+from __future__ import absolute_import
 
-from odict import odict
+from . import expr_utils
+from . import epy_block_io
+from . import extract_docs
+
+from .odict import odict
diff --git a/grc/core/utils/complexity.py b/grc/core/utils/complexity.py
index baa8040db4..d72db9b3dd 100644
--- a/grc/core/utils/complexity.py
+++ b/grc/core/utils/complexity.py
@@ -8,8 +8,8 @@ def calculate_flowgraph_complexity(flowgraph):
             continue
 
         # Don't worry about optional sinks?
-        sink_list = filter(lambda c: not c.get_optional(), block.get_sinks())
-        source_list = filter(lambda c: not c.get_optional(), block.get_sources())
+        sink_list = [c for c in block.get_sinks() if not c.get_optional()]
+        source_list = [c for c in block.get_sources() if not c.get_optional()]
         sinks = float(len(sink_list))
         sources = float(len(source_list))
         base = max(min(sinks, sources), 1)
@@ -22,14 +22,15 @@ def calculate_flowgraph_complexity(flowgraph):
             multi = 1
 
         # Connection ratio multiplier
-        sink_multi = max(float(sum(map(lambda c: len(c.get_connections()), sink_list)) / max(sinks, 1.0)), 1.0)
-        source_multi = max(float(sum(map(lambda c: len(c.get_connections()), source_list)) / max(sources, 1.0)), 1.0)
-        dbal = dbal + (base * multi * sink_multi * source_multi)
+        sink_multi = max(float(sum(len(c.get_connections()) for c in sink_list) / max(sinks, 1.0)), 1.0)
+        source_multi = max(float(sum(len(c.get_connections()) for c in source_list) / max(sources, 1.0)), 1.0)
+        dbal += base * multi * sink_multi * source_multi
 
     blocks = float(len(flowgraph.blocks))
     connections = float(len(flowgraph.connections))
     elements = blocks + connections
-    disabled_connections = len(filter(lambda c: not c.get_enabled(), flowgraph.connections))
+    disabled_connections = sum(not c.get_enabled() for c in flowgraph.connections)
+
     variables = elements - blocks - connections
     enabled = float(len(flowgraph.get_enabled_blocks()))
 
diff --git a/grc/core/utils/epy_block_io.py b/grc/core/utils/epy_block_io.py
index 76b50051db..7a2006a833 100644
--- a/grc/core/utils/epy_block_io.py
+++ b/grc/core/utils/epy_block_io.py
@@ -1,7 +1,12 @@
 
+from __future__ import absolute_import
+
 import inspect
 import collections
 
+import six
+from six.moves import zip
+
 
 TYPE_MAP = {
     'complex64': 'complex', 'complex': 'complex',
@@ -31,10 +36,10 @@ def _ports(sigs, msgs):
 def _find_block_class(source_code, cls):
     ns = {}
     try:
-        exec source_code in ns
+        exec(source_code, ns)
     except Exception as e:
         raise ValueError("Can't interpret source code: " + str(e))
-    for var in ns.itervalues():
+    for var in six.itervalues(ns):
         if inspect.isclass(var) and issubclass(var, cls):
             return var
     raise ValueError('No python block class found in code')
@@ -52,7 +57,7 @@ def extract(cls):
 
     spec = inspect.getargspec(cls.__init__)
     init_args = spec.args[1:]
-    defaults = map(repr, spec.defaults or ())
+    defaults = [repr(arg) for arg in (spec.defaults or ())]
     doc = cls.__doc__ or cls.__init__.__doc__ or ''
     cls_name = cls.__name__
 
diff --git a/grc/core/utils/expr_utils.py b/grc/core/utils/expr_utils.py
index 66911757d6..0577f06a75 100644
--- a/grc/core/utils/expr_utils.py
+++ b/grc/core/utils/expr_utils.py
@@ -17,7 +17,12 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import, print_function
+
 import string
+
+import six
+
 VAR_CHARS = string.letters + string.digits + '_'
 
 
@@ -50,7 +55,7 @@ class graph(object):
         self._graph[src_node_key].remove(dest_node_key)
 
     def get_nodes(self):
-        return self._graph.keys()
+        return list(self._graph.keys())
 
     def get_edges(self, node_key):
         return self._graph[node_key]
@@ -85,7 +90,7 @@ def expr_split(expr):
             toks.append(char)
             tok = ''
     toks.append(tok)
-    return filter(lambda t: t, toks)
+    return [t for t in toks if t]
 
 
 def expr_replace(expr, replace_dict):
@@ -101,7 +106,7 @@ def expr_replace(expr, replace_dict):
     """
     expr_splits = expr_split(expr)
     for i, es in enumerate(expr_splits):
-        if es in replace_dict.keys():
+        if es in list(replace_dict.keys()):
             expr_splits[i] = replace_dict[es]
     return ''.join(expr_splits)
 
@@ -118,7 +123,7 @@ def get_variable_dependencies(expr, vars):
         a subset of vars used in the expression
     """
     expr_toks = expr_split(expr)
-    return set(filter(lambda v: v in expr_toks, vars))
+    return set(v for v in vars if v in expr_toks)
 
 
 def get_graph(exprs):
@@ -131,12 +136,12 @@ def get_graph(exprs):
     Returns:
         a graph of variable deps
     """
-    vars = exprs.keys()
+    vars = list(exprs.keys())
     # Get dependencies for each expression, load into graph
     var_graph = graph()
     for var in vars:
         var_graph.add_node(var)
-    for var, expr in exprs.iteritems():
+    for var, expr in six.iteritems(exprs):
         for dep in get_variable_dependencies(expr, vars):
             if dep != var:
                 var_graph.add_edge(dep, var)
@@ -159,7 +164,7 @@ def sort_variables(exprs):
     # Determine dependency order
     while var_graph.get_nodes():
         # Get a list of nodes with no edges
-        indep_vars = filter(lambda var: not var_graph.get_edges(var), var_graph.get_nodes())
+        indep_vars = [var for var in var_graph.get_nodes() if not var_graph.get_edges(var)]
         if not indep_vars:
             raise Exception('circular dependency caught in sort_variables')
         # Add the indep vars to the end of the list
@@ -193,4 +198,4 @@ def sort_objects(objects, get_id, get_expr):
 
 if __name__ == '__main__':
     for i in sort_variables({'x': '1', 'y': 'x+1', 'a': 'x+y', 'b': 'y+1', 'c': 'a+b+x+y'}):
-        print i
+        print(i)
diff --git a/grc/core/utils/extract_docs.py b/grc/core/utils/extract_docs.py
index a6e0bc971e..cff8a81099 100644
--- a/grc/core/utils/extract_docs.py
+++ b/grc/core/utils/extract_docs.py
@@ -17,15 +17,19 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import, print_function
+
 import sys
 import re
 import subprocess
 import threading
 import json
-import Queue
 import random
 import itertools
 
+import six
+from six.moves import queue, filter, range
+
 
 ###############################################################################
 # The docstring extraction
@@ -124,7 +128,7 @@ class SubprocessLoader(object):
         self.callback_query_result = callback_query_result
         self.callback_finished = callback_finished or (lambda: None)
 
-        self._queue = Queue.Queue()
+        self._queue = queue.Queue()
         self._thread = None
         self._worker = None
         self._shutdown = threading.Event()
@@ -157,14 +161,14 @@ class SubprocessLoader(object):
                 cmd, args = self._last_cmd
                 if cmd == 'query':
                     msg += " (crashed while loading {0!r})".format(args[0])
-                print >> sys.stderr, msg
+                print(msg, file=sys.stderr)
                 continue  # restart
             else:
                 break  # normal termination, return
             finally:
                 self._worker.terminate()
         else:
-            print >> sys.stderr, "Warning: docstring loader crashed too often"
+            print("Warning: docstring loader crashed too often", file=sys.stderr)
         self._thread = None
         self._worker = None
         self.callback_finished()
@@ -203,9 +207,9 @@ class SubprocessLoader(object):
             key, docs = args
             self.callback_query_result(key, docs)
         elif cmd == 'error':
-            print args
+            print(args)
         else:
-            print >> sys.stderr, "Unknown response:", cmd, args
+            print("Unknown response:", cmd, args, file=sys.stderr)
 
     def query(self, key, imports=None, make=None):
         """ Request docstring extraction for a certain key """
@@ -270,12 +274,12 @@ if __name__ == '__worker__':
 
 elif __name__ == '__main__':
     def callback(key, docs):
-        print key
-        for match, doc in docs.iteritems():
-            print '-->', match
-            print doc.strip()
-            print
-        print
+        print(key)
+        for match, doc in six.iteritems(docs):
+            print('-->', match)
+            print(doc.strip())
+            print()
+        print()
 
     r = SubprocessLoader(callback)
 
diff --git a/grc/core/utils/odict.py b/grc/core/utils/odict.py
index 20970e947c..38f898a97f 100644
--- a/grc/core/utils/odict.py
+++ b/grc/core/utils/odict.py
@@ -17,6 +17,8 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
 from UserDict import DictMixin
 
 
diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py
index 492bf8de9c..9c3e9246d5 100644
--- a/grc/gui/ActionHandler.py
+++ b/grc/gui/ActionHandler.py
@@ -18,6 +18,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
 
+from __future__ import absolute_import, print_function
+
 import os
 import subprocess
 
@@ -114,7 +116,7 @@ class ActionHandler:
         ##################################################
         if action == Actions.APPLICATION_INITIALIZE:
             if not self.init_file_paths:
-                self.init_file_paths = filter(os.path.exists, Preferences.get_open_files())
+                self.init_file_paths = list(filter(os.path.exists, Preferences.get_open_files()))
             if not self.init_file_paths: self.init_file_paths = ['']
             for file_path in self.init_file_paths:
                 if file_path: main.new_page(file_path) #load pages from file paths
@@ -603,7 +605,7 @@ class ActionHandler:
                 try:
                     page.process.kill()
                 except:
-                    print "could not kill process: %d" % page.process.pid
+                    print("could not kill process: %d" % page.process.pid)
         elif action == Actions.PAGE_CHANGE:  # pass and run the global actions
             pass
         elif action == Actions.RELOAD_BLOCKS:
@@ -645,7 +647,7 @@ class ActionHandler:
                              shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
 
         else:
-            print '!!! Action "%s" not handled !!!' % action
+            print('!!! Action "%s" not handled !!!' % action)
         ##################################################
         # Global Actions for all States
         ##################################################
diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py
index d0e114293f..3a51e80918 100644
--- a/grc/gui/Actions.py
+++ b/grc/gui/Actions.py
@@ -17,13 +17,17 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
+import six
+
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk
 from gi.repository import Gdk
 from gi.repository import GObject
 
-import Preferences
+from . import Preferences
 
 NO_MODS_MASK = 0
 
@@ -47,7 +51,6 @@ def handle_key_press(event):
     Returns:
         true if handled
     """
-    _used_mods_mask = reduce(lambda x, y: x | y, [mod_mask for keyval, mod_mask in _actions_keypress_dict], NO_MODS_MASK)
     # extract the key value and the consumed modifiers
     _unused, keyval, egroup, level, consumed = _keymap.translate_keyboard_state(
         event.hardware_keycode, event.get_state(), event.group)
@@ -80,13 +83,16 @@ class _ActionBase(object):
     Register actions and keypresses with this module.
     """
     def __init__(self, label, keypresses):
+        global _used_mods_mask
+
         _all_actions_list.append(self)
         for i in range(len(keypresses)/2):
             keyval, mod_mask = keypresses[i*2:(i+1)*2]
             # register this keypress
-            if _actions_keypress_dict.has_key((keyval, mod_mask)):
+            if (keyval, mod_mask) in _actions_keypress_dict:
                 raise KeyError('keyval/mod_mask pair already registered "%s"' % str((keyval, mod_mask)))
             _actions_keypress_dict[(keyval, mod_mask)] = self
+            _used_mods_mask |= mod_mask
             # set the accelerator group, and accelerator path
             # register the key name and mod mask with the accelerator path
             if label is None:
@@ -102,7 +108,7 @@ class _ActionBase(object):
         The string representation should be the name of the action id.
         Try to find the action id for this action by searching this module.
         """
-        for name, value in globals().iteritems():
+        for name, value in six.iteritems(globals()):
             if value == self:
                 return name
         return self.get_name()
diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py
index c8631aa298..0c18836c4e 100644
--- a/grc/gui/Bars.py
+++ b/grc/gui/Bars.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk
@@ -206,7 +207,7 @@ class SubMenuCreator(object):
 
     def _fill_flow_graph_recent_submenu(self, action):
         """menu showing recent flow-graphs"""
-        import Preferences
+        from . import Preferences
         menu = Gtk.Menu()
         recent_files = Preferences.get_recent_files()
         if len(recent_files) > 0:
diff --git a/grc/gui/Block.py b/grc/gui/Block.py
index 1b90cf69a0..a6c31cd473 100644
--- a/grc/gui/Block.py
+++ b/grc/gui/Block.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 import math
 import gi
 gi.require_version('Gtk', '3.0')
@@ -150,7 +151,7 @@ class Block(Element, _Block):
         W = self.label_width + 2 * BLOCK_LABEL_PADDING
 
         def get_min_height_for_ports():
-            visible_ports = filter(lambda p: not p.get_hide(), ports)
+            visible_ports = [p for p in ports if not p.get_hide()]
             min_height = 2*PORT_BORDER_SEPARATION + len(visible_ports) * PORT_SEPARATION
             if visible_ports:
                 min_height -= ports[0].H
diff --git a/grc/gui/BlockTreeWindow.py b/grc/gui/BlockTreeWindow.py
index 829ddfed68..26086f58e9 100644
--- a/grc/gui/BlockTreeWindow.py
+++ b/grc/gui/BlockTreeWindow.py
@@ -17,6 +17,9 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+import six
+
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk
@@ -33,7 +36,7 @@ def _format_doc(doc):
     docs = []
     if doc.get(''):
         docs += doc.pop('').splitlines() + ['']
-    for block_name, docstring in doc.iteritems():
+    for block_name, docstring in six.iteritems(doc):
         docs.append('--- {0} ---'.format(block_name))
         docs += docstring.splitlines()
         docs.append('')
@@ -142,8 +145,9 @@ class BlockTreeWindow(Gtk.VBox):
         if categories is None:
             categories = self._categories
 
-        if isinstance(category, (str, unicode)): category = category.split('/')
-        category = tuple(filter(lambda x: x, category))  # tuple is hashable
+        if isinstance(category, (str, six.text_type)):
+            category = category.split('/')
+        category = tuple(x for x in category if x)  # tuple is hashable
         # add category and all sub categories
         for i, cat_name in enumerate(category):
             sub_category = category[:i+1]
@@ -210,8 +214,8 @@ class BlockTreeWindow(Gtk.VBox):
             self.treeview.set_model(self.treestore)
             self.treeview.collapse_all()
         else:
-            matching_blocks = filter(lambda b: key in b.get_key().lower() or key in b.get_name().lower(),
-                                     self.platform.blocks.values())
+            matching_blocks = [b for b in list(self.platform.blocks.values())
+                               if key in b.get_key().lower() or key in b.get_name().lower()]
 
             self.treestore_search.clear()
             self._categories_search = {tuple(): None}
diff --git a/grc/gui/Colors.py b/grc/gui/Colors.py
index a03a7bcade..b2ed55b711 100644
--- a/grc/gui/Colors.py
+++ b/grc/gui/Colors.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 
 
 def get_color(color_code):
diff --git a/grc/gui/Config.py b/grc/gui/Config.py
index 9b0c5d4afe..b6556ad724 100644
--- a/grc/gui/Config.py
+++ b/grc/gui/Config.py
@@ -17,8 +17,11 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import, print_function
+
 import sys
 import os
+
 from ..core.Config import Config as _Config
 from . import Constants
 
@@ -57,7 +60,7 @@ class Config(_Config):
                 raise Exception()
             return value
         except:
-            print >> sys.stderr, "Error: invalid 'canvas_default_size' setting."
+            print("Error: invalid 'canvas_default_size' setting.", file=sys.stderr)
             return Constants.DEFAULT_CANVAS_SIZE_DEFAULT
 
     @property
@@ -69,6 +72,6 @@ class Config(_Config):
                 raise Exception()
         except:
             font_size = Constants.DEFAULT_FONT_SIZE
-            print >> sys.stderr, "Error: invalid 'canvas_font_size' setting."
+            print("Error: invalid 'canvas_font_size' setting.", file=sys.stderr)
 
         return font_size
diff --git a/grc/gui/Connection.py b/grc/gui/Connection.py
index 46414c94c8..8953ca0183 100644
--- a/grc/gui/Connection.py
+++ b/grc/gui/Connection.py
@@ -17,16 +17,14 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
-import gi
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk
-from gi.repository import Gdk
+from __future__ import absolute_import
 
+from six.moves import map
 
-import Colors
-import Utils
-from Constants import CONNECTOR_ARROW_BASE, CONNECTOR_ARROW_HEIGHT
-from Element import Element
+from . import Colors
+from . import Utils
+from .Constants import CONNECTOR_ARROW_BASE, CONNECTOR_ARROW_HEIGHT
+from .Element import Element
 
 from ..core.Constants import GR_MESSAGE_DOMAIN
 from ..core.Connection import Connection as _Connection
@@ -130,7 +128,7 @@ class Connection(Element, _Connection):
             #points[0][0] -> source connector should not be in the direction of source
             if Utils.get_angle_from_coordinates(points[0][0], (x1, y1)) == source.get_connector_direction(): points.reverse()
             #create 3-line connector
-            p1, p2 = map(int, points[0][0]), map(int, points[0][1])
+            p1, p2 = list(map(int, points[0][0])), list(map(int, points[0][1]))
             self.add_line((x1, y1), p1)
             self.add_line(p1, p2)
             self.add_line((x2, y2), p2)
diff --git a/grc/gui/Constants.py b/grc/gui/Constants.py
index 55739a7c03..8bb15acc09 100644
--- a/grc/gui/Constants.py
+++ b/grc/gui/Constants.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk
diff --git a/grc/gui/Dialogs.py b/grc/gui/Dialogs.py
index 953373ee24..8f0f60d764 100644
--- a/grc/gui/Dialogs.py
+++ b/grc/gui/Dialogs.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk
diff --git a/grc/gui/DrawingArea.py b/grc/gui/DrawingArea.py
index d1e0e78634..33c669c99f 100644
--- a/grc/gui/DrawingArea.py
+++ b/grc/gui/DrawingArea.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 from gi.repository import Gtk, Gdk, GObject
 
 
diff --git a/grc/gui/Element.py b/grc/gui/Element.py
index 30c0f5dba7..4e88df375f 100644
--- a/grc/gui/Element.py
+++ b/grc/gui/Element.py
@@ -17,10 +17,12 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
-from Constants import LINE_SELECT_SENSITIVITY
-from Constants import POSSIBLE_ROTATIONS
+from __future__ import absolute_import
+from .Constants import LINE_SELECT_SENSITIVITY
+from .Constants import POSSIBLE_ROTATIONS
 
 import gi
+from six.moves import zip
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk
 from gi.repository import Gdk
diff --git a/grc/gui/Executor.py b/grc/gui/Executor.py
index 13a1cfd942..a8c67986e5 100644
--- a/grc/gui/Executor.py
+++ b/grc/gui/Executor.py
@@ -15,6 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
+from __future__ import absolute_import
 import os
 import threading
 import shlex
diff --git a/grc/gui/FileDialogs.py b/grc/gui/FileDialogs.py
index 63d4a397a8..3ee715dac6 100644
--- a/grc/gui/FileDialogs.py
+++ b/grc/gui/FileDialogs.py
@@ -17,18 +17,19 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk
 from gi.repository import GObject
 
-from Dialogs import MessageDialogHelper
-from Constants import \
+from .Dialogs import MessageDialogHelper
+from .Constants import \
     DEFAULT_FILE_PATH, IMAGE_FILE_EXTENSION, TEXT_FILE_EXTENSION, \
     NEW_FLOGRAPH_TITLE
-import Preferences
+from . import Preferences
 from os import path
-import Utils
+from . import Utils
 
 ##################################################
 # Constants
diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py
index 802c54f7a7..50e146b4db 100644
--- a/grc/gui/FlowGraph.py
+++ b/grc/gui/FlowGraph.py
@@ -17,16 +17,20 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
 import functools
 import random
 from distutils.spawn import find_executable
 from itertools import chain, count
 from operator import methodcaller
 
+import six
+from six.moves import filter
+
 from gi.repository import GObject
 
-from . import Actions, Colors, Constants, Utils, Bars, Dialogs
-from .Constants import SCROLL_PROXIMITY_SENSITIVITY, SCROLL_DISTANCE
+from . import Actions, Colors, Utils, Bars, Dialogs
 from .Element import Element
 from .external_editor import ExternalEditor
 
@@ -179,10 +183,10 @@ class FlowGraph(Element, _Flowgraph):
             x_min = min(x, x_min)
             y_min = min(y, y_min)
         #get connections between selected blocks
-        connections = filter(
+        connections = list(filter(
             lambda c: c.get_source().get_parent() in blocks and c.get_sink().get_parent() in blocks,
             self.connections,
-        )
+        ))
         clipboard = (
             (x_min, y_min),
             [block.export_data() for block in blocks],
@@ -222,7 +226,7 @@ class FlowGraph(Element, _Flowgraph):
                 block.get_param('_io_cache').set_value(params.pop('_io_cache'))
                 block.get_param('_source_code').set_value(params.pop('_source_code'))
                 block.rewrite()  # this creates the other params
-            for param_key, param_value in params.iteritems():
+            for param_key, param_value in six.iteritems(params):
                 #setup id parameter
                 if param_key == 'id':
                     old_id2block[param_value] = block
diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py
index ce16074c81..3236768969 100644
--- a/grc/gui/MainWindow.py
+++ b/grc/gui/MainWindow.py
@@ -17,6 +17,8 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
 import os
 
 import gi
@@ -270,7 +272,7 @@ class MainWindow(Gtk.Window):
         Returns:
             true if all closed
         """
-        open_files = filter(lambda file: file, self._get_files()) #filter blank files
+        open_files = [file for file in self._get_files() if file] #filter blank files
         open_file = self.current_page.file_path
         #close each page
         for page in sorted(self.get_pages(), key=lambda p: p.saved):
@@ -416,7 +418,7 @@ class MainWindow(Gtk.Window):
         Returns:
             list of file paths
         """
-        return map(lambda page: page.file_path, self.get_pages())
+        return [page.file_path for page in self.get_pages()]
 
     def get_pages(self):
         """
diff --git a/grc/gui/NotebookPage.py b/grc/gui/NotebookPage.py
index bcfb4d87fe..757dcbc0f8 100644
--- a/grc/gui/NotebookPage.py
+++ b/grc/gui/NotebookPage.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 import os
 
 from gi.repository import Gtk, Gdk, GObject
diff --git a/grc/gui/Param.py b/grc/gui/Param.py
index 0f88015256..137c5e057b 100644
--- a/grc/gui/Param.py
+++ b/grc/gui/Param.py
@@ -15,6 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
+from __future__ import absolute_import
 from . import Utils, Constants
 
 from . import ParamWidgets
diff --git a/grc/gui/ParamWidgets.py b/grc/gui/ParamWidgets.py
index 2fd6ccd1dc..e0979e19f3 100644
--- a/grc/gui/ParamWidgets.py
+++ b/grc/gui/ParamWidgets.py
@@ -15,6 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
+from __future__ import absolute_import
 import os
 
 from gi.repository import Gtk, Gdk
diff --git a/grc/gui/ParserErrorsDialog.py b/grc/gui/ParserErrorsDialog.py
index f49e6923e5..28cc8ece0c 100644
--- a/grc/gui/ParserErrorsDialog.py
+++ b/grc/gui/ParserErrorsDialog.py
@@ -17,12 +17,16 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
+import six
+
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk
 from gi.repository import GObject
 
-from Constants import MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT
+from .Constants import MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT
 
 
 class ParserErrorsDialog(Gtk.Dialog):
@@ -72,7 +76,7 @@ class ParserErrorsDialog(Gtk.Dialog):
         """set up data model"""
         self.tree_store.clear()
         self._error_logs = error_logs
-        for filename, errors in error_logs.iteritems():
+        for filename, errors in six.iteritems(error_logs):
             parent = self.tree_store.append(None, [str(filename)])
             try:
                 with open(filename, 'r') as fp:
diff --git a/grc/gui/Platform.py b/grc/gui/Platform.py
index 500df1cce4..997e96ab59 100644
--- a/grc/gui/Platform.py
+++ b/grc/gui/Platform.py
@@ -17,6 +17,8 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import, print_function
+
 import os
 import sys
 
@@ -58,7 +60,7 @@ class Platform(Element, _Platform):
                 import shutil
                 shutil.move(old_gui_prefs_file, gui_prefs_file)
             except Exception as e:
-                print >> sys.stderr, e
+                print(e, file=sys.stderr)
 
     ##############################################
     # Constructors
diff --git a/grc/gui/Port.py b/grc/gui/Port.py
index fb1cd678cd..0fa35573c1 100644
--- a/grc/gui/Port.py
+++ b/grc/gui/Port.py
@@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 import math
 import gi
 gi.require_version('Gtk', '3.0')
@@ -68,7 +69,7 @@ class Port(_Port, Element):
         #get all sibling ports
         ports = self.get_parent().get_sources_gui() \
             if self.is_source else self.get_parent().get_sinks_gui()
-        ports = filter(lambda p: not p.get_hide(), ports)
+        ports = [p for p in ports if not p.get_hide()]
         #get the max width
         self.W = max([port.W for port in ports] + [PORT_MIN_WIDTH])
         W = self.W if not self._label_hidden() else PORT_LABEL_HIDDEN_WIDTH
@@ -79,16 +80,15 @@ class Port(_Port, Element):
             if hasattr(self, '_connector_length'):
                 del self._connector_length
             return
-        length = len(filter(lambda p: not p.get_hide(), ports))
         #reverse the order of ports for these rotations
         if rotation in (180, 270):
-            index = length-index-1
+            index = len(ports)-index-1
 
         port_separation = PORT_SEPARATION \
             if not self.get_parent().has_busses[self.is_source] \
             else max([port.H for port in ports]) + PORT_SPACING
 
-        offset = (self.get_parent().H - (length-1)*port_separation - self.H)/2
+        offset = (self.get_parent().H - (len(ports)-1)*port_separation - self.H)/2
         #create areas and connector coordinates
         if (self.is_sink and rotation == 0) or (self.is_source and rotation == 180):
             x = -W
diff --git a/grc/gui/Preferences.py b/grc/gui/Preferences.py
index 5fbdfe927a..8756a7ab23 100644
--- a/grc/gui/Preferences.py
+++ b/grc/gui/Preferences.py
@@ -17,9 +17,12 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import, print_function
+
 import os
 import sys
-import ConfigParser
+
+from six.moves import configparser
 
 
 HEADER = """\
@@ -31,7 +34,7 @@ HEADER = """\
 """
 
 _platform = None
-_config_parser = ConfigParser.SafeConfigParser()
+_config_parser = configparser.SafeConfigParser()
 
 
 def file_extension():
@@ -45,12 +48,12 @@ def load(platform):
     for section in ['main', 'files_open', 'files_recent']:
         try:
             _config_parser.add_section(section)
-        except Exception, e:
-             print e
+        except Exception as e:
+            print(e)
     try:
         _config_parser.read(_platform.get_prefs_file())
     except Exception as err:
-        print >> sys.stderr, err
+        print(err, file=sys.stderr)
 
 
 def save():
@@ -59,7 +62,7 @@ def save():
             fp.write(HEADER)
             _config_parser.write(fp)
     except Exception as err:
-        print >> sys.stderr, err
+        print(err, file=sys.stderr)
 
 
 def entry(key, value=None, default=None):
@@ -74,7 +77,7 @@ def entry(key, value=None, default=None):
         }.get(_type, _config_parser.get)
         try:
             result = getter('main', key)
-        except ConfigParser.Error:
+        except configparser.Error:
             result = _type() if default is None else default
     return result
 
@@ -106,7 +109,7 @@ def get_file_list(key):
     try:
         files = [value for name, value in _config_parser.items(key)
                  if name.startswith('%s_' % key)]
-    except ConfigParser.Error:
+    except configparser.Error:
         files = []
     return files
 
@@ -121,7 +124,7 @@ def set_open_files(files):
 
 def get_recent_files():
     """ Gets recent files, removes any that do not exist and re-saves it """
-    files = filter(os.path.exists, get_file_list('files_recent'))
+    files = list(filter(os.path.exists, get_file_list('files_recent')))
     set_recent_files(files)
     return files
 
diff --git a/grc/gui/PropsDialog.py b/grc/gui/PropsDialog.py
index d6b64944cc..f87ca6e7c1 100644
--- a/grc/gui/PropsDialog.py
+++ b/grc/gui/PropsDialog.py
@@ -17,10 +17,12 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
 from gi.repository import Gtk, Gdk, GObject, Pango
 
 from . import Actions, Utils, Constants
 from .Dialogs import SimpleTextDisplay
+import six
 
 
 class PropsDialog(Gtk.Dialog):
@@ -160,7 +162,7 @@ class PropsDialog(Gtk.Dialog):
                     # child.destroy()   # disabled because it throw errors...
                 # repopulate the params box
                 box_all_valid = True
-                for param in filter(lambda p: p.get_tab_label() == tab, self._block.get_params()):
+                for param in [p for p in self._block.get_params() if p.get_tab_label() == tab]:
                     # fixme: why do we even rebuild instead of really hiding params?
                     if param.get_hide() == 'all':
                         continue
@@ -212,7 +214,7 @@ class PropsDialog(Gtk.Dialog):
             docstrings = {block_class: docstrings[block_class]}
 
         # show docstring(s) extracted from python sources
-        for cls_name, docstring in docstrings.iteritems():
+        for cls_name, docstring in six.iteritems(docstrings):
             buf.insert_with_tags_by_name(pos, cls_name + '\n', 'b')
             buf.insert(pos, docstring + '\n\n')
         pos.backward_chars(2)
diff --git a/grc/gui/StateCache.py b/grc/gui/StateCache.py
index 3cdb5f30ce..b109a1281b 100644
--- a/grc/gui/StateCache.py
+++ b/grc/gui/StateCache.py
@@ -17,8 +17,9 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
-import Actions
-from Constants import STATE_CACHE_SIZE
+from __future__ import absolute_import
+from . import Actions
+from .Constants import STATE_CACHE_SIZE
 
 class StateCache(object):
     """
diff --git a/grc/gui/Utils.py b/grc/gui/Utils.py
index 311b37f468..e5d4ccaa35 100644
--- a/grc/gui/Utils.py
+++ b/grc/gui/Utils.py
@@ -17,6 +17,8 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import
+
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import GLib
@@ -47,7 +49,7 @@ def get_rotated_coordinate(coor, rotation):
     return x * cos_r + y * sin_r, -x * sin_r + y * cos_r
 
 
-def get_angle_from_coordinates((x1, y1), (x2, y2)):
+def get_angle_from_coordinates(p1, p2):
     """
     Given two points, calculate the vector direction from point1 to point2, directions are multiples of 90 degrees.
 
@@ -58,6 +60,8 @@ def get_angle_from_coordinates((x1, y1), (x2, y2)):
     Returns:
         the direction in degrees
     """
+    (x1, y1) = p1
+    (x2, y2) = p2
     if y1 == y2:  # 0 or 180
         return 0 if x2 > x1 else 180
     else:  # 90 or 270
@@ -78,7 +82,7 @@ def align_to_grid(coor, mode=round):
     def align(value):
         return int(mode(value / (1.0 * CANVAS_GRID_SIZE)) * CANVAS_GRID_SIZE)
     try:
-        return map(align, coor)
+        return [align(c) for c in coor]
     except TypeError:
         x = coor
         return align(coor)
diff --git a/grc/gui/VariableEditor.py b/grc/gui/VariableEditor.py
index d34903e241..399e4ec475 100644
--- a/grc/gui/VariableEditor.py
+++ b/grc/gui/VariableEditor.py
@@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
-from operator import attrgetter
+from __future__ import absolute_import
 
 import gi
 gi.require_version('Gtk', '3.0')
@@ -221,8 +221,8 @@ class VariableEditor(Gtk.VBox):
                 sp('foreground', 'red')
 
     def update_gui(self, blocks):
-        self._imports = filter(attrgetter('is_import'), blocks)
-        self._variables = filter(attrgetter('is_variable'), blocks)
+        self._imports = [block for block in blocks if block.is_import]
+        self._variables = [block for block in blocks if block.is_variable]
         self._rebuild()
         self.treeview.expand_all()
 
diff --git a/grc/gui/external_editor.py b/grc/gui/external_editor.py
index 76f21412b0..11d6fd7ebb 100644
--- a/grc/gui/external_editor.py
+++ b/grc/gui/external_editor.py
@@ -17,6 +17,8 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
+from __future__ import absolute_import, print_function
+
 import os
 import sys
 import time
@@ -71,7 +73,7 @@ class ExternalEditor(threading.Thread):
                 time.sleep(1)
 
         except Exception as e:
-            print >> sys.stderr, "file monitor crashed:", str(e)
+            print("file monitor crashed:", str(e), file=sys.stderr)
         else:
             # print "file monitor: done with", filename
             pass
@@ -79,7 +81,7 @@ class ExternalEditor(threading.Thread):
 
 if __name__ == '__main__':
     def p(data):
-        print data
+        print(data)
 
     e = ExternalEditor('/usr/bin/gedit', "test", "content", p)
     e.open_editor()
diff --git a/grc/main.py b/grc/main.py
index cd9863739f..810ac7c66f 100755
--- a/grc/main.py
+++ b/grc/main.py
@@ -15,6 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
+from __future__ import absolute_import
 import optparse
 
 import gi
-- 
cgit v1.2.3


From 9b39ca3a9a2550c51ee934633ff9c655e1676a3b Mon Sep 17 00:00:00 2001
From: Sebastian Koslowski <koslowski@kit.edu>
Date: Wed, 3 Aug 2016 21:38:56 +0200
Subject: grc: gtk3: update various deprecated gtk calls

---
 grc/gui/Bars.py            |  6 +++---
 grc/gui/BlockTreeWindow.py | 15 +++++++--------
 grc/gui/NotebookPage.py    |  2 +-
 grc/gui/VariableEditor.py  | 32 +++++++++++++-------------------
 4 files changed, 24 insertions(+), 31 deletions(-)

(limited to 'grc/gui/Bars.py')

diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py
index 0c18836c4e..d8d57843d7 100644
--- a/grc/gui/Bars.py
+++ b/grc/gui/Bars.py
@@ -90,7 +90,7 @@ MENU_BAR_LIST = (
         None,
         Actions.BLOCK_ROTATE_CCW,
         Actions.BLOCK_ROTATE_CW,
-        (Gtk.Action('Align', '_Align', None, None), Actions.BLOCK_ALIGNMENTS),
+        (Gtk.Action(name='Align', label='_Align', tooltip=None, stock_id=None), Actions.BLOCK_ALIGNMENTS),
         None,
         Actions.BLOCK_ENABLE,
         Actions.BLOCK_DISABLE,
@@ -298,7 +298,7 @@ class MenuBar(Gtk.MenuBar, MenuHelperMixin, SubMenuCreator):
             self.append(self._make_sub_menu(main_action, actions))
 
     def create_flow_graph_new(self):
-        main = Gtk.ImageMenuItem(Gtk.STOCK_NEW)
+        main = Gtk.ImageMenuItem(label=Gtk.STOCK_NEW)
         main.set_label(Actions.FLOW_GRAPH_NEW.get_label())
         func = self._fill_flow_graph_new_submenu
         self.submenus.append((Actions.FLOW_GRAPH_NEW, func, main))
@@ -306,7 +306,7 @@ class MenuBar(Gtk.MenuBar, MenuHelperMixin, SubMenuCreator):
         return main
 
     def create_flow_graph_recent(self):
-        main = Gtk.ImageMenuItem(Gtk.STOCK_OPEN)
+        main = Gtk.ImageMenuItem(label=Gtk.STOCK_OPEN)
         main.set_label(Actions.FLOW_GRAPH_OPEN_RECENT.get_label())
         func = self._fill_flow_graph_recent_submenu
         self.submenus.append((Actions.FLOW_GRAPH_OPEN, func, main))
diff --git a/grc/gui/BlockTreeWindow.py b/grc/gui/BlockTreeWindow.py
index e29b6b4b7f..90d4b561b7 100644
--- a/grc/gui/BlockTreeWindow.py
+++ b/grc/gui/BlockTreeWindow.py
@@ -1,5 +1,5 @@
 """
-Copyright 2007, 2008, 2009 Free Software Foundation, Inc.
+Copyright 2007, 2008, 2009, 2016 Free Software Foundation, Inc.
 This file is part of GNU Radio
 
 GNU Radio Companion is free software; you can redistribute it and/or
@@ -22,8 +22,7 @@ import six
 
 from gi.repository import Gtk, Gdk, GObject
 
-from . import Actions, Utils
-from . import Constants
+from . import Actions, Utils, Constants
 
 
 NAME_INDEX, KEY_INDEX, DOC_INDEX = range(3)
@@ -64,7 +63,7 @@ class BlockTreeWindow(Gtk.VBox):
     """The block selection panel."""
 
     __gsignals__ = {
-        'create_new_block': (GObject.SIGNAL_RUN_FIRST, None, (str,))
+        'create_new_block': (GObject.SignalFlags.RUN_FIRST, None, (str,))
     }
 
     def __init__(self, platform):
@@ -83,9 +82,9 @@ class BlockTreeWindow(Gtk.VBox):
         # search entry
         self.search_entry = Gtk.Entry()
         try:
-            self.search_entry.set_icon_from_stock(Gtk.EntryIconPosition.PRIMARY, Gtk.STOCK_FIND)
+            self.search_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY, 'edit-find')
             self.search_entry.set_icon_activatable(Gtk.EntryIconPosition.PRIMARY, False)
-            self.search_entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, Gtk.STOCK_CLOSE)
+            self.search_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, 'window-close')
             self.search_entry.connect('icon-release', self._handle_icon_event)
         except AttributeError:
             pass  # no icon for old pygtk
@@ -97,7 +96,7 @@ class BlockTreeWindow(Gtk.VBox):
         self.treestore = Gtk.TreeStore(GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING)
         self.treestore_search = Gtk.TreeStore(GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING)
 
-        self.treeview = Gtk.TreeView(self.treestore)
+        self.treeview = Gtk.TreeView(model=self.treestore)
         self.treeview.set_enable_search(False)  # disable pop up search box
         self.treeview.set_search_column(-1)  # really disable search
         self.treeview.set_headers_visible(False)
@@ -119,7 +118,7 @@ class BlockTreeWindow(Gtk.VBox):
         # make the scrolled window to hold the tree view
         scrolled_window = Gtk.ScrolledWindow()
         scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
-        scrolled_window.add_with_viewport(self.treeview)
+        scrolled_window.add(self.treeview)
         scrolled_window.set_size_request(Constants.DEFAULT_BLOCKS_WINDOW_WIDTH, -1)
         self.pack_start(scrolled_window, True, True, 0)
         # map categories to iters, automatic mapping for root
diff --git a/grc/gui/NotebookPage.py b/grc/gui/NotebookPage.py
index 4745035aff..347be8eea0 100644
--- a/grc/gui/NotebookPage.py
+++ b/grc/gui/NotebookPage.py
@@ -76,7 +76,7 @@ class NotebookPage(Gtk.HBox):
         self.scrolled_window.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
         self.scrolled_window.set_policy(Gtk.PolicyType.ALWAYS, Gtk.PolicyType.ALWAYS)
         self.scrolled_window.connect('key-press-event', self._handle_scroll_window_key_press)
-        self.scrolled_window.add_with_viewport(self.drawing_area)
+        self.scrolled_window.add(self.drawing_area)
         self.pack_start(self.scrolled_window, True, True, 0)
 
         self.show_all()
diff --git a/grc/gui/VariableEditor.py b/grc/gui/VariableEditor.py
index 09fe629195..d97b9e9f24 100644
--- a/grc/gui/VariableEditor.py
+++ b/grc/gui/VariableEditor.py
@@ -19,15 +19,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 from __future__ import absolute_import
 
-import gi
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk
-from gi.repository import Gdk
-from gi.repository import GObject
+from gi.repository import Gtk, Gdk, GObject
 
-from . import Actions
-from . import Preferences
-from .Constants import DEFAULT_BLOCKS_WINDOW_WIDTH
+from . import Actions, Preferences, Constants
 
 BLOCK_INDEX = 0
 ID_INDEX = 1
@@ -39,29 +33,29 @@ class VariableEditorContextMenu(Gtk.Menu):
     def __init__(self, var_edit):
         Gtk.Menu.__init__(self)
 
-        self.imports = Gtk.MenuItem("Add _Import")
+        self.imports = Gtk.MenuItem(label="Add _Import")
         self.imports.connect('activate', var_edit.handle_action, var_edit.ADD_IMPORT)
         self.add(self.imports)
 
-        self.variables = Gtk.MenuItem("Add _Variable")
+        self.variables = Gtk.MenuItem(label="Add _Variable")
         self.variables.connect('activate', var_edit.handle_action, var_edit.ADD_VARIABLE)
         self.add(self.variables)
         self.add(Gtk.SeparatorMenuItem())
 
-        self.enable = Gtk.MenuItem("_Enable")
+        self.enable = Gtk.MenuItem(label="_Enable")
         self.enable.connect('activate', var_edit.handle_action, var_edit.ENABLE_BLOCK)
-        self.disable = Gtk.MenuItem("_Disable")
+        self.disable = Gtk.MenuItem(label="_Disable")
         self.disable.connect('activate', var_edit.handle_action, var_edit.DISABLE_BLOCK)
         self.add(self.enable)
         self.add(self.disable)
         self.add(Gtk.SeparatorMenuItem())
 
-        self.delete = Gtk.MenuItem("_Delete")
+        self.delete = Gtk.MenuItem(label="_Delete")
         self.delete.connect('activate', var_edit.handle_action, var_edit.DELETE_BLOCK)
         self.add(self.delete)
         self.add(Gtk.SeparatorMenuItem())
 
-        self.properties = Gtk.MenuItem("_Properties...")
+        self.properties = Gtk.MenuItem(label="_Properties...")
         self.properties.connect('activate', var_edit.handle_action, var_edit.OPEN_PROPERTIES)
         self.add(self.properties)
         self.show_all()
@@ -85,8 +79,8 @@ class VariableEditor(Gtk.VBox):
     DISABLE_BLOCK = 6
 
     __gsignals__ = {
-        'create_new_block': (GObject.SIGNAL_RUN_FIRST, None, (str,)),
-        'remove_block': (GObject.SIGNAL_RUN_FIRST, None, (str,))
+        'create_new_block': (GObject.SignalFlags.RUN_FIRST, None, (str,)),
+        'remove_block': (GObject.SignalFlags.RUN_FIRST, None, (str,))
     }
 
     def __init__(self):
@@ -100,7 +94,7 @@ class VariableEditor(Gtk.VBox):
         # Generate everything else dynamically
         self.treestore = Gtk.TreeStore(GObject.TYPE_PYOBJECT,  # Block reference
                                        GObject.TYPE_STRING)    # Category and block name
-        self.treeview = Gtk.TreeView(self.treestore)
+        self.treeview = Gtk.TreeView(model=self.treestore)
         self.treeview.set_enable_search(False)
         self.treeview.set_search_column(-1)
         #self.treeview.set_enable_search(True)
@@ -150,8 +144,8 @@ class VariableEditor(Gtk.VBox):
         # Make the scrolled window to hold the tree view
         scrolled_window = Gtk.ScrolledWindow()
         scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
-        scrolled_window.add_with_viewport(self.treeview)
-        scrolled_window.set_size_request(DEFAULT_BLOCKS_WINDOW_WIDTH, -1)
+        scrolled_window.add(self.treeview)
+        scrolled_window.set_size_request(Constants.DEFAULT_BLOCKS_WINDOW_WIDTH, -1)
         self.pack_start(scrolled_window, True, True, 0)
 
         # Context menus
-- 
cgit v1.2.3


From 5eb34fb72ffe46929fa2e99d84b4ead4f4083ca4 Mon Sep 17 00:00:00 2001
From: Seth Hitefield <sdhitefield@gmail.com>
Date: Thu, 22 Sep 2016 12:15:51 -0400
Subject: grc: refactor: Cleaned up imports

---
 grc/gui/Actions.py            | 6 +-----
 grc/gui/Bars.py               | 6 ++----
 grc/gui/Config.py             | 8 ++++----
 grc/gui/DrawingArea.py        | 2 +-
 grc/gui/MainWindow.py         | 7 +------
 grc/gui/ParserErrorsDialog.py | 5 +----
 grc/gui/Platform.py           | 1 -
 grc/gui/canvas/flowgraph.py   | 3 ++-
 8 files changed, 12 insertions(+), 26 deletions(-)

(limited to 'grc/gui/Bars.py')

diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py
index 4759e56294..601d0005fb 100644
--- a/grc/gui/Actions.py
+++ b/grc/gui/Actions.py
@@ -21,11 +21,7 @@ from __future__ import absolute_import
 
 import six
 
-import gi
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk
-from gi.repository import Gdk
-from gi.repository import GObject
+from gi.repository import Gtk, Gdk, GObject
 
 from . import Preferences
 
diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py
index d8d57843d7..1c63f01fa6 100644
--- a/grc/gui/Bars.py
+++ b/grc/gui/Bars.py
@@ -18,10 +18,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
 from __future__ import absolute_import
-import gi
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk
-from gi.repository import GObject
+
+from gi.repository import Gtk, GObject
 
 from . import Actions
 
diff --git a/grc/gui/Config.py b/grc/gui/Config.py
index 0f40e06fce..0558892e2f 100644
--- a/grc/gui/Config.py
+++ b/grc/gui/Config.py
@@ -22,11 +22,11 @@ from __future__ import absolute_import, print_function
 import sys
 import os
 
-from ..core.Config import Config as _Config
+from ..core.Config import Config as CoreConfig
 from . import Constants
 
 
-class Config(_Config):
+class Config(CoreConfig):
 
     name = 'GNU Radio Companion'
 
@@ -34,7 +34,7 @@ class Config(_Config):
         'GRC_PREFS_PATH', os.path.expanduser('~/.gnuradio/grc.conf'))
 
     def __init__(self, install_prefix, *args, **kwargs):
-        _Config.__init__(self, *args, **kwargs)
+        CoreConfig.__init__(self, *args, **kwargs)
         self.install_prefix = install_prefix
         Constants.update_font_size(self.font_size)
 
@@ -83,4 +83,4 @@ class Config(_Config):
     @default_qss_theme.setter
     def default_qss_theme(self, value):
         self._gr_prefs.set_string("qtgui", "qss", value)
-        self._gr_prefs.save()
\ No newline at end of file
+        self._gr_prefs.save()
diff --git a/grc/gui/DrawingArea.py b/grc/gui/DrawingArea.py
index f279d50557..cad813a876 100644
--- a/grc/gui/DrawingArea.py
+++ b/grc/gui/DrawingArea.py
@@ -18,8 +18,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
 from __future__ import absolute_import
-from gi.repository import Gtk, Gdk, GObject
 
+from gi.repository import Gtk, Gdk, GObject
 
 from . import Constants, Colors
 
diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py
index e86273e288..5eabf33f67 100644
--- a/grc/gui/MainWindow.py
+++ b/grc/gui/MainWindow.py
@@ -21,12 +21,7 @@ from __future__ import absolute_import
 
 import os
 
-import gi
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk
-from gi.repository import Gdk
-from gi.repository import GObject
-
+from gi.repository import Gtk, Gdk, GObject
 
 from . import Bars, Actions, Preferences, Utils
 from .BlockTreeWindow import BlockTreeWindow
diff --git a/grc/gui/ParserErrorsDialog.py b/grc/gui/ParserErrorsDialog.py
index 93a09287e8..050b9a4f98 100644
--- a/grc/gui/ParserErrorsDialog.py
+++ b/grc/gui/ParserErrorsDialog.py
@@ -21,10 +21,7 @@ from __future__ import absolute_import
 
 import six
 
-import gi
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk
-from gi.repository import GObject
+from gi.repository import Gtk, GObject
 
 from .Constants import MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT
 
diff --git a/grc/gui/Platform.py b/grc/gui/Platform.py
index 44380c579f..aeade75d78 100644
--- a/grc/gui/Platform.py
+++ b/grc/gui/Platform.py
@@ -20,7 +20,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 from __future__ import absolute_import, print_function
 
 import sys
-
 import os
 
 from .Config import Config
diff --git a/grc/gui/canvas/flowgraph.py b/grc/gui/canvas/flowgraph.py
index 5969e00a43..ba13588355 100644
--- a/grc/gui/canvas/flowgraph.py
+++ b/grc/gui/canvas/flowgraph.py
@@ -26,9 +26,10 @@ from distutils.spawn import find_executable
 from itertools import count
 
 import six
-from gi.repository import GLib
 from six.moves import filter
 
+from gi.repository import GLib
+
 from .drawable import Drawable
 
 from .. import Actions, Colors, Constants, Utils, Bars, Dialogs
-- 
cgit v1.2.3


From 59f88d3cc03ed0f0c01433252f0d607330c23321 Mon Sep 17 00:00:00 2001
From: Seth Hitefield <sdhitefield@gmail.com>
Date: Thu, 22 Sep 2016 13:10:57 -0400
Subject: grc: refactor: Moved preferences to Config.py

---
 grc/gui/ActionHandler.py  |  15 ++--
 grc/gui/Actions.py        |   8 +--
 grc/gui/Bars.py           |   4 +-
 grc/gui/Config.py         | 120 +++++++++++++++++++++++++++++++
 grc/gui/Constants.py      |   1 +
 grc/gui/FileDialogs.py    |  14 ++--
 grc/gui/MainWindow.py     |  32 ++++-----
 grc/gui/Preferences.py    | 176 ----------------------------------------------
 grc/gui/VariableEditor.py |   6 +-
 9 files changed, 163 insertions(+), 213 deletions(-)
 delete mode 100644 grc/gui/Preferences.py

(limited to 'grc/gui/Bars.py')

diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py
index ad4dc35073..0d7a900e80 100644
--- a/grc/gui/ActionHandler.py
+++ b/grc/gui/ActionHandler.py
@@ -25,7 +25,7 @@ import subprocess
 
 from gi.repository import Gtk, GObject
 
-from . import Dialogs, Preferences, Actions, Executor, FileDialogs, Utils
+from . import Dialogs, Actions, Executor, FileDialogs, Utils
 from .MainWindow import MainWindow
 from .ParserErrorsDialog import ParserErrorsDialog
 from .PropsDialog import PropsDialog
@@ -55,6 +55,7 @@ class ActionHandler(Gtk.Application):
         for action in Actions.get_all_actions(): action.connect('activate', self._handle_action)
         #setup the main window
         self.platform = platform
+        self.config = platform.config
 
         #initialize
         self.init_file_paths = [os.path.abspath(file_path) for file_path in file_paths]
@@ -121,8 +122,8 @@ class ActionHandler(Gtk.Application):
         # Initialize/Quit
         ##################################################
         if action == Actions.APPLICATION_INITIALIZE:
-            file_path_to_show = Preferences.file_open()
-            for file_path in (self.init_file_paths or Preferences.get_open_files()):
+            file_path_to_show = self.config.file_open()
+            for file_path in (self.init_file_paths or self.config.get_open_files()):
                 if os.path.exists(file_path):
                     main.new_page(file_path, show=file_path_to_show == file_path)
             if not main.current_page:
@@ -517,7 +518,7 @@ class ActionHandler(Gtk.Application):
             if file_paths: # Open a new page for each file, show only the first
                 for i,file_path in enumerate(file_paths):
                     main.new_page(file_path, show=(i==0))
-                    Preferences.add_recent_file(file_path)
+                    self.config.add_recent_file(file_path)
                     main.tool_bar.refresh_submenus()
                     main.menu_bar.refresh_submenus()
         elif action == Actions.FLOW_GRAPH_OPEN_QSS_THEME:
@@ -545,7 +546,7 @@ class ActionHandler(Gtk.Application):
             if file_path is not None:
                 page.file_path = os.path.abspath(file_path)
                 Actions.FLOW_GRAPH_SAVE()
-                Preferences.add_recent_file(file_path)
+                self.config.add_recent_file(file_path)
                 main.tool_bar.refresh_submenus()
                 main.menu_bar.refresh_submenus()
         elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE:
@@ -576,10 +577,10 @@ class ActionHandler(Gtk.Application):
                 Actions.FLOW_GRAPH_GEN()
                 xterm = self.platform.config.xterm_executable
                 Dialogs.show_missing_xterm(main, xterm)
-                if Preferences.xterm_missing() != xterm:
+                if self.config.xterm_missing() != xterm:
                     if not os.path.exists(xterm):
                         Dialogs.show_missing_xterm(main, xterm)
-                    Preferences.xterm_missing(xterm)
+                    self.config.xterm_missing(xterm)
                 if page.saved and page.file_path:
                     Executor.ExecFlowGraphThread(
                         flow_graph_page=page,
diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py
index 601d0005fb..ef043853a9 100644
--- a/grc/gui/Actions.py
+++ b/grc/gui/Actions.py
@@ -23,8 +23,6 @@ import six
 
 from gi.repository import Gtk, Gdk, GObject
 
-from . import Preferences
-
 NO_MODS_MASK = 0
 
 
@@ -166,12 +164,14 @@ class ToggleAction(Gtk.ToggleAction, _ActionBase):
 
     def load_from_preferences(self):
         if self.preference_name is not None:
-            self.set_active(Preferences.entry(
+            config = Gtk.Application.get_default().config
+            self.set_active(config.entry(
                 self.preference_name, default=bool(self.default)))
 
     def save_to_preferences(self):
         if self.preference_name is not None:
-            Preferences.entry(self.preference_name, value=self.get_active())
+            config = Gtk.Application.get_default().config
+            config.entry(self.preference_name, value=self.get_active())
 
 ########################################################################
 # Actions
diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py
index 1c63f01fa6..26fea20024 100644
--- a/grc/gui/Bars.py
+++ b/grc/gui/Bars.py
@@ -205,9 +205,9 @@ class SubMenuCreator(object):
 
     def _fill_flow_graph_recent_submenu(self, action):
         """menu showing recent flow-graphs"""
-        from . import Preferences
         menu = Gtk.Menu()
-        recent_files = Preferences.get_recent_files()
+        config = Gtk.Application.get_default().config
+        recent_files = config.get_recent_files()
         if len(recent_files) > 0:
             for i, file_name in enumerate(recent_files):
                 item = Gtk.MenuItem(name="%d. %s" % (i+1, file_name), use_underline=False)
diff --git a/grc/gui/Config.py b/grc/gui/Config.py
index 0558892e2f..c4d395c0b3 100644
--- a/grc/gui/Config.py
+++ b/grc/gui/Config.py
@@ -25,6 +25,16 @@ import os
 from ..core.Config import Config as CoreConfig
 from . import Constants
 
+from six.moves import configparser
+
+HEADER = """\
+# This contains only GUI settings for GRC and is not meant for users to edit.
+#
+# GRC settings not accessible through the GUI are in gnuradio.conf under
+# section [grc].
+
+"""
+
 
 class Config(CoreConfig):
 
@@ -38,6 +48,41 @@ class Config(CoreConfig):
         self.install_prefix = install_prefix
         Constants.update_font_size(self.font_size)
 
+        self.parser = configparser.SafeConfigParser()
+        for section in ['main', 'files_open', 'files_recent']:
+            try:
+                self.parser.add_section(section)
+            except Exception as e:
+                print(e)
+        try:
+            self.parser.read(self.gui_prefs_file)
+        except Exception as err:
+            print(err, file=sys.stderr)
+
+    def save(self):
+        try:
+            with open(self.gui_prefs_file, 'w') as fp:
+                fp.write(HEADER)
+                self.parser.write(fp)
+        except Exception as err:
+            print(err, file=sys.stderr)
+
+    def entry(self, key, value=None, default=None):
+        if value is not None:
+            self.parser.set('main', key, str(value))
+            result = value
+        else:
+            _type = type(default) if default is not None else str
+            getter = {
+                bool: self.parser.getboolean,
+                int: self.parser.getint,
+            }.get(_type, self.parser.get)
+            try:
+                result = getter('main', key)
+            except (AttributeError, configparser.Error):
+                result = _type() if default is None else default
+        return result
+
     @property
     def editor(self):
         return self._gr_prefs.get_string('grc', 'editor', '')
@@ -84,3 +129,78 @@ class Config(CoreConfig):
     def default_qss_theme(self, value):
         self._gr_prefs.set_string("qtgui", "qss", value)
         self._gr_prefs.save()
+
+    ##### Originally from Preferences.py #####
+    def main_window_size(self, size=None):
+        if size is None:
+            size = [None, None]
+        w = self.entry('main_window_width', size[0], default=1)
+        h = self.entry('main_window_height', size[1], default=1)
+        return w, h
+
+    def file_open(self, filename=None):
+        return self.entry('file_open', filename, default='')
+
+    def set_file_list(self, key, files):
+        self.parser.remove_section(key)  # clear section
+        self.parser.add_section(key)
+        for i, filename in enumerate(files):
+            self.parser.set(key, '%s_%d' % (key, i), filename)
+
+    def get_file_list(self, key):
+        try:
+            files = [value for name, value in self.parser.items(key)
+                     if name.startswith('%s_' % key)]
+        except (AttributeError, configparser.Error):
+            files = []
+        return files
+
+    def get_open_files(self):
+        return self.get_file_list('files_open')
+
+    def set_open_files(self, files):
+        return self.set_file_list('files_open', files)
+
+    def get_recent_files(self):
+        """ Gets recent files, removes any that do not exist and re-saves it """
+        files = list(filter(os.path.exists, self.get_file_list('files_recent')))
+        self.set_recent_files(files)
+        return files
+
+    def set_recent_files(self, files):
+        return self.set_file_list('files_recent', files)
+
+    def add_recent_file(self, file_name):
+        # double check file_name
+        if os.path.exists(file_name):
+            recent_files = self.get_recent_files()
+            if file_name in recent_files:
+                recent_files.remove(file_name)  # Attempt removal
+            recent_files.insert(0, file_name)  # Insert at start
+            self.set_recent_files(recent_files[:10])  # Keep up to 10 files
+
+    def console_window_position(self, pos=None):
+        return self.entry('console_window_position', pos, default=-1) or 1
+
+    def blocks_window_position(self, pos=None):
+        return self.entry('blocks_window_position', pos, default=-1) or 1
+
+    def variable_editor_position(self, pos=None, sidebar=False):
+        # Figure out default
+        if sidebar:
+            w, h = self.main_window_size()
+            return self.entry('variable_editor_sidebar_position', pos, default=int(h*0.7))
+        else:
+            return self.entry('variable_editor_position', pos, default=int(self.blocks_window_position()*0.5))
+
+    def variable_editor_sidebar(self, pos=None):
+        return self.entry('variable_editor_sidebar', pos, default=False)
+
+    def variable_editor_confirm_delete(self, pos=None):
+        return self.entry('variable_editor_confirm_delete', pos, default=True)
+
+    def xterm_missing(self, cmd=None):
+        return self.entry('xterm_missing', cmd, default='INVALID_XTERM_SETTING')
+
+    def screen_shot_background_transparent(self, transparent=None):
+        return self.entry('screen_shot_background_transparent', transparent, default=False)
diff --git a/grc/gui/Constants.py b/grc/gui/Constants.py
index 2f0f7794be..0a555b37c9 100644
--- a/grc/gui/Constants.py
+++ b/grc/gui/Constants.py
@@ -26,6 +26,7 @@ from ..core.Constants import *
 
 # default path for the open/save dialogs
 DEFAULT_FILE_PATH = os.getcwd()
+FILE_EXTENSION = '.grc'
 
 # name for new/unsaved flow graphs
 NEW_FLOGRAPH_TITLE = 'untitled'
diff --git a/grc/gui/FileDialogs.py b/grc/gui/FileDialogs.py
index cbb7bcaa69..dbcecf91ab 100644
--- a/grc/gui/FileDialogs.py
+++ b/grc/gui/FileDialogs.py
@@ -23,7 +23,7 @@ from os import path
 
 from gi.repository import Gtk
 
-from . import Constants, Preferences, Utils, Dialogs
+from . import Constants, Utils, Dialogs
 
 
 class FileDialogHelper(Gtk.FileChooserDialog, object):
@@ -59,7 +59,7 @@ class FileDialogHelper(Gtk.FileChooserDialog, object):
 
         self.parent = parent
         self.current_file_path = current_file_path or path.join(
-            Constants.DEFAULT_FILE_PATH, Constants.NEW_FLOGRAPH_TITLE + Preferences.file_extension())
+            Constants.DEFAULT_FILE_PATH, Constants.NEW_FLOGRAPH_TITLE + Constants.FILE_EXTENSION)
 
         self.set_current_folder(path.dirname(current_file_path))  # current directory
         self.setup_filters()
@@ -130,7 +130,7 @@ class OpenFileDialog(FileDialogHelper):
 class OpenFlowGraph(OpenFileDialog):
     title = 'Open a Flow Graph from a File...'
     filter_label = 'Flow Graph Files'
-    filter_ext = Preferences.file_extension()
+    filter_ext = Constants.FILE_EXTENSION
 
     def __init__(self, parent, current_file_path=''):
         super(OpenFlowGraph, self).__init__(parent, current_file_path)
@@ -146,7 +146,7 @@ class OpenQSS(OpenFileDialog):
 class SaveFlowGraph(SaveFileDialog):
     title = 'Save a Flow Graph to a File...'
     filter_label = 'Flow Graph Files'
-    filter_ext = Preferences.file_extension()
+    filter_ext = Constants.FILE_EXTENSION
 
 
 class SaveConsole(SaveFileDialog):
@@ -163,8 +163,10 @@ class SaveScreenShot(SaveFileDialog):
     def __init__(self, parent, current_file_path=''):
         super(SaveScreenShot, self).__init__(parent, current_file_path)
 
+        self.config = Gtk.Application.get_default().config
+
         self._button = button = Gtk.CheckButton(label='Background transparent')
-        self._button.set_active(Preferences.screen_shot_background_transparent())
+        self._button.set_active(self.config.screen_shot_background_transparent())
         self.set_extra_widget(button)
 
     def setup_filters(self, filters=None):
@@ -193,6 +195,6 @@ class SaveScreenShot(SaveFileDialog):
             self.show_missing_message(filename)
 
         bg_transparent = self._button.get_active()
-        Preferences.screen_shot_background_transparent(bg_transparent)
+        self.config.screen_shot_background_transparent(bg_transparent)
         self.destroy()
         return filename, bg_transparent
diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py
index 5eabf33f67..1caec28aee 100644
--- a/grc/gui/MainWindow.py
+++ b/grc/gui/MainWindow.py
@@ -23,7 +23,7 @@ import os
 
 from gi.repository import Gtk, Gdk, GObject
 
-from . import Bars, Actions, Preferences, Utils
+from . import Bars, Actions, Utils
 from .BlockTreeWindow import BlockTreeWindow
 from .VariableEditor import VariableEditor
 from .Constants import \
@@ -52,7 +52,7 @@ class MainWindow(Gtk.ApplicationWindow):
         Gtk.ApplicationWindow.__init__(self, title="GNU Radio Companion", application=app)
 
         self._platform = platform
-        Preferences.load(platform)
+        self.config = platform.config
 
         # Setup window
         vbox = Gtk.VBox()
@@ -97,7 +97,7 @@ class MainWindow(Gtk.ApplicationWindow):
         self.right = Gtk.VPaned() #orientation=Gtk.Orientation.VERTICAL)
         self.left_subpanel = Gtk.HPaned() #orientation=Gtk.Orientation.HORIZONTAL)
 
-        self.variable_panel_sidebar = Preferences.variable_editor_sidebar()
+        self.variable_panel_sidebar = self.config.variable_editor_sidebar()
         if self.variable_panel_sidebar:
             self.left.pack1(self.notebook)
             self.left.pack2(self.console_window, False)
@@ -117,13 +117,13 @@ class MainWindow(Gtk.ApplicationWindow):
         self.main.pack2(self.right, False)
 
         # Load preferences and show the main window
-        self.resize(*Preferences.main_window_size())
-        self.main.set_position(Preferences.blocks_window_position())
-        self.left.set_position(Preferences.console_window_position())
+        self.resize(*self.config.main_window_size())
+        self.main.set_position(self.config.blocks_window_position())
+        self.left.set_position(self.config.console_window_position())
         if self.variable_panel_sidebar:
-            self.right.set_position(Preferences.variable_editor_position(sidebar=True))
+            self.right.set_position(self.config.variable_editor_position(sidebar=True))
         else:
-            self.left_subpanel.set_position(Preferences.variable_editor_position())
+            self.left_subpanel.set_position(self.config.variable_editor_position())
 
         self.show_all()
         self.console_window.hide()
@@ -279,16 +279,16 @@ class MainWindow(Gtk.ApplicationWindow):
                 break
         if self.notebook.get_n_pages(): return False
         #save state before closing
-        Preferences.set_open_files(open_files)
-        Preferences.file_open(open_file)
-        Preferences.main_window_size(self.get_size())
-        Preferences.console_window_position(self.left.get_position())
-        Preferences.blocks_window_position(self.main.get_position())
+        self.config.set_open_files(open_files)
+        self.config.file_open(open_file)
+        self.config.main_window_size(self.get_size())
+        self.config.console_window_position(self.left.get_position())
+        self.config.blocks_window_position(self.main.get_position())
         if self.variable_panel_sidebar:
-            Preferences.variable_editor_position(self.right.get_position(), sidebar=True)
+            self.config.variable_editor_position(self.right.get_position(), sidebar=True)
         else:
-            Preferences.variable_editor_position(self.left_subpanel.get_position())
-        Preferences.save()
+            self.config.variable_editor_position(self.left_subpanel.get_position())
+        self.config.save()
         return True
 
     def close_page(self, ensure=True):
diff --git a/grc/gui/Preferences.py b/grc/gui/Preferences.py
deleted file mode 100644
index d917537971..0000000000
--- a/grc/gui/Preferences.py
+++ /dev/null
@@ -1,176 +0,0 @@
-"""
-Copyright 2008 Free Software Foundation, Inc.
-This file is part of GNU Radio
-
-GNU Radio Companion is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-GNU Radio Companion is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
-"""
-
-from __future__ import absolute_import, print_function
-
-import os
-import sys
-
-from six.moves import configparser
-
-
-HEADER = """\
-# This contains only GUI settings for GRC and is not meant for users to edit.
-#
-# GRC settings not accessible through the GUI are in gnuradio.conf under
-# section [grc].
-
-"""
-
-_platform = None
-_config_parser = configparser.SafeConfigParser()
-
-
-def file_extension():
-    return '.grc'
-
-
-def load(platform):
-    global _platform
-    _platform = platform
-    # create sections
-    for section in ['main', 'files_open', 'files_recent']:
-        try:
-            _config_parser.add_section(section)
-        except Exception as e:
-            print(e)
-    try:
-        _config_parser.read(_platform.get_prefs_file())
-    except Exception as err:
-        print(err, file=sys.stderr)
-
-
-def save():
-    try:
-        with open(_platform.get_prefs_file(), 'w') as fp:
-            fp.write(HEADER)
-            _config_parser.write(fp)
-    except Exception as err:
-        print(err, file=sys.stderr)
-
-
-def entry(key, value=None, default=None):
-    if value is not None:
-        _config_parser.set('main', key, str(value))
-        result = value
-    else:
-        _type = type(default) if default is not None else str
-        getter = {
-            bool: _config_parser.getboolean,
-            int: _config_parser.getint,
-        }.get(_type, _config_parser.get)
-        try:
-            result = getter('main', key)
-        except (AttributeError, configparser.Error):
-            result = _type() if default is None else default
-    return result
-
-
-###########################################################################
-# Special methods for specific program functionalities
-###########################################################################
-
-def main_window_size(size=None):
-    if size is None:
-        size = [None, None]
-    w = entry('main_window_width', size[0], default=1)
-    h = entry('main_window_height', size[1], default=1)
-    return w, h
-
-
-def file_open(filename=None):
-    return entry('file_open', filename, default='')
-
-
-def set_file_list(key, files):
-    _config_parser.remove_section(key)  # clear section
-    _config_parser.add_section(key)
-    for i, filename in enumerate(files):
-        _config_parser.set(key, '%s_%d' % (key, i), filename)
-
-
-def get_file_list(key):
-    try:
-        files = [value for name, value in _config_parser.items(key)
-                 if name.startswith('%s_' % key)]
-    except (AttributeError, configparser.Error):
-        files = []
-    return files
-
-
-def get_open_files():
-    return get_file_list('files_open')
-
-
-def set_open_files(files):
-    return set_file_list('files_open', files)
-
-
-def get_recent_files():
-    """ Gets recent files, removes any that do not exist and re-saves it """
-    files = list(filter(os.path.exists, get_file_list('files_recent')))
-    set_recent_files(files)
-    return files
-
-
-def set_recent_files(files):
-    return set_file_list('files_recent', files)
-
-
-def add_recent_file(file_name):
-    # double check file_name
-    if os.path.exists(file_name):
-        recent_files = get_recent_files()
-        if file_name in recent_files:
-            recent_files.remove(file_name)  # Attempt removal
-        recent_files.insert(0, file_name)  # Insert at start
-        set_recent_files(recent_files[:10])  # Keep up to 10 files
-
-
-def console_window_position(pos=None):
-    return entry('console_window_position', pos, default=-1) or 1
-
-
-def blocks_window_position(pos=None):
-    return entry('blocks_window_position', pos, default=-1) or 1
-
-
-def variable_editor_position(pos=None, sidebar=False):
-    # Figure out default
-    if sidebar:
-        w, h = main_window_size()
-        return entry('variable_editor_sidebar_position', pos, default=int(h*0.7))
-    else:
-        return entry('variable_editor_position', pos, default=int(blocks_window_position()*0.5))
-
-
-def variable_editor_sidebar(pos=None):
-    return entry('variable_editor_sidebar', pos, default=False)
-
-
-def variable_editor_confirm_delete(pos=None):
-    return entry('variable_editor_confirm_delete', pos, default=True)
-
-
-def xterm_missing(cmd=None):
-    return entry('xterm_missing', cmd, default='INVALID_XTERM_SETTING')
-
-
-def screen_shot_background_transparent(transparent=None):
-    return entry('screen_shot_background_transparent', transparent, default=False)
diff --git a/grc/gui/VariableEditor.py b/grc/gui/VariableEditor.py
index d97b9e9f24..44dd2923eb 100644
--- a/grc/gui/VariableEditor.py
+++ b/grc/gui/VariableEditor.py
@@ -21,7 +21,7 @@ from __future__ import absolute_import
 
 from gi.repository import Gtk, Gdk, GObject
 
-from . import Actions, Preferences, Constants
+from . import Actions, Constants
 
 BLOCK_INDEX = 0
 ID_INDEX = 1
@@ -85,6 +85,8 @@ class VariableEditor(Gtk.VBox):
 
     def __init__(self):
         Gtk.VBox.__init__(self)
+        config = Gtk.Application.get_default().config
+
         self._block = None
         self._mouse_button_pressed = False
         self._imports = []
@@ -150,7 +152,7 @@ class VariableEditor(Gtk.VBox):
 
         # Context menus
         self._context_menu = VariableEditorContextMenu(self)
-        self._confirm_delete = Preferences.variable_editor_confirm_delete()
+        self._confirm_delete = config.variable_editor_confirm_delete()
 
     # Sets cell contents
     def set_icon(self, col, cell, model, iter, data):
-- 
cgit v1.2.3


From 486e0a9d06e43f3b8669471bef13a5eeedbda4c6 Mon Sep 17 00:00:00 2001
From: Seth Hitefield <sdhitefield@gmail.com>
Date: Wed, 3 May 2017 07:06:54 -0700
Subject: grc: gtk3: Converted actions to Gio.Action instead of Gtk.Action

---
 grc/core/Block.py            |   2 +-
 grc/core/ParseXML.py         |   2 +-
 grc/core/utils/expr_utils.py |   3 +-
 grc/gui/Actions.py           | 633 ++++++++++++++++++++++---------------------
 grc/gui/Application.py       | 281 ++++++++++++-------
 grc/gui/Bars.py              | 519 +++++++++++++++++------------------
 grc/gui/Config.py            |   2 +-
 grc/gui/Console.py           |   6 +
 grc/gui/DrawingArea.py       |  44 +++
 grc/gui/MainWindow.py        |  25 +-
 grc/gui/Notebook.py          |  44 ++-
 grc/gui/ParamWidgets.py      |   2 +-
 grc/gui/StateCache.py        |   4 +-
 grc/gui/Utils.py             |   6 +-
 grc/gui/VariableEditor.py    |   4 +-
 grc/gui/canvas/param.py      |   2 +-
 grc/main.py                  |  16 +-
 17 files changed, 889 insertions(+), 706 deletions(-)

(limited to 'grc/gui/Bars.py')

diff --git a/grc/core/Block.py b/grc/core/Block.py
index 8350828092..087815b941 100644
--- a/grc/core/Block.py
+++ b/grc/core/Block.py
@@ -403,7 +403,7 @@ class Block(Element):
         return itertools.chain(self.active_sources, self.active_sinks)
 
     def get_children(self):
-        return self.get_ports() + self.params.values()
+        return self.get_ports() + list(self.params.values())
 
     def get_children_gui(self):
         return self.get_ports_gui() + self.params.values()
diff --git a/grc/core/ParseXML.py b/grc/core/ParseXML.py
index 163289ba06..430ba5b474 100644
--- a/grc/core/ParseXML.py
+++ b/grc/core/ParseXML.py
@@ -156,7 +156,7 @@ def to_file(nested_data, xml_file):
         ), xml_declaration=True, pretty_print=True, encoding='utf-8')
     xml_data += etree.tostring(_to_file(nested_data)[0],
                                pretty_print=True, encoding='utf-8')
-    with open(xml_file, 'w') as fp:
+    with open(xml_file, 'wb') as fp:
         fp.write(xml_data)
 
 
diff --git a/grc/core/utils/expr_utils.py b/grc/core/utils/expr_utils.py
index 555bd709b1..cc03e9cb1c 100644
--- a/grc/core/utils/expr_utils.py
+++ b/grc/core/utils/expr_utils.py
@@ -23,7 +23,7 @@ import string
 
 import six
 
-VAR_CHARS = string.letters + string.digits + '_'
+VAR_CHARS = string.ascii_letters + string.digits + '_'
 
 
 class graph(object):
@@ -194,4 +194,3 @@ def sort_objects(objects, get_id, get_expr):
     sorted_ids = sort_variables(id2expr)
     # Return list of sorted objects
     return [id2obj[id] for id in sorted_ids]
-
diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py
index 97162065a6..d214f28049 100644
--- a/grc/gui/Actions.py
+++ b/grc/gui/Actions.py
@@ -20,286 +20,323 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 from __future__ import absolute_import
 
 import six
+import logging
 
-from gi.repository import Gtk, Gdk, GObject
+from gi.repository import Gtk, Gdk, Gio, GLib, GObject
 
-NO_MODS_MASK = 0
 
+log = logging.getLogger(__name__)
 
-########################################################################
-# Actions API
-########################################################################
-_actions_keypress_dict = dict()
-_keymap = Gdk.Keymap.get_default()
-_used_mods_mask = NO_MODS_MASK
-
-
-def handle_key_press(event):
-    """
-    Call the action associated with the key press event.
-    Both the key value and the mask must have a match.
-
-    Args:
-        event: a gtk key press event
-
-    Returns:
-        true if handled
-    """
-    # extract the key value and the consumed modifiers
-    _unused, keyval, egroup, level, consumed = _keymap.translate_keyboard_state(
-        event.hardware_keycode, event.get_state(), event.group)
-    # get the modifier mask and ignore irrelevant modifiers
-    mod_mask = event.get_state() & ~consumed & _used_mods_mask
-    # look up the keypress and call the action
-    try:
-        _actions_keypress_dict[(keyval, mod_mask)]()
-    except KeyError:
-        return False  # not handled
-    else:
-        return True  # handled here
-
-_all_actions_list = list()
-
-
-def get_all_actions():
-    return _all_actions_list
-
-_accel_group = Gtk.AccelGroup()
-
-
-def get_accel_group():
-    return _accel_group
-
-
-class _ActionBase(object):
-    """
-    Base class for Action and ToggleAction
-    Register actions and keypresses with this module.
-    """
-    def __init__(self, label, keypresses):
-        global _used_mods_mask
-
-        _all_actions_list.append(self)
-        for i in range(len(keypresses)/2):
-            keyval, mod_mask = keypresses[i*2:(i+1)*2]
-            # register this keypress
-            if (keyval, mod_mask) in _actions_keypress_dict:
-                raise KeyError('keyval/mod_mask pair already registered "%s"' % str((keyval, mod_mask)))
-            _actions_keypress_dict[(keyval, mod_mask)] = self
-            _used_mods_mask |= mod_mask
-            # set the accelerator group, and accelerator path
-            # register the key name and mod mask with the accelerator path
-            if label is None:
-                continue  # dont register accel
-            accel_path = '<main>/' + self.get_name()
-            self.set_accel_group(get_accel_group())
-            self.set_accel_path(accel_path)
-            Gtk.AccelMap.add_entry(accel_path, keyval, mod_mask)
-        self.args = None
+
+def filter_from_dict(vars):
+    return filter(lambda x: isinstance(x[1], Action), vars.items())
+
+
+class Namespace(object):
+
+    def __init__(self):
+        self._actions = {}
+
+    def add(self, action):
+        key = action.props.name
+        self._actions[key] = action
+
+    def connect(self, name, handler):
+        #log.debug("Connecting action <{}> to handler <{}>".format(name, handler.__name__))
+        self._actions[name].connect('activate', handler)
+
+    def register(self, name, parameter=None, handler=None, label=None, tooltip=None,
+                 icon_name=None, keypresses=None, preference_name=None, default=None):
+        # Check types
+        if not isinstance(name, str):
+            raise TypeError("Cannot register fuction: 'name' must be a str")
+        if parameter and not isinstance(parameter, str):
+            raise TypeError("Cannot register fuction: 'parameter' must be a str")
+        if handler and not callable(handler):
+            raise TypeError("Cannot register fuction: 'handler' must be callable")
+
+        # Check if the name has a prefix.
+        prefix = None
+        if name.startswith("app.") or name.startswith("win."):
+            # Set a prefix for later and remove it
+            prefix = name[0:3]
+            name = name[4:]
+
+        if handler:
+            log.debug("Register action [{}, prefix={}, param={}, handler={}]".format(
+                  name, prefix, parameter, handler.__name__))
+        else:
+            log.debug("Register action [{}, prefix={}, param={}, handler=None]".format(
+                  name, prefix, parameter))
+
+        action = Action(name, parameter, label=label, tooltip=tooltip,
+                        icon_name=icon_name, keypresses=keypresses, prefix=prefix,
+                        preference_name=preference_name, default=default)
+        if handler:
+            action.connect('activate', handler)
+
+        key = name
+        if prefix:
+            key = "{}.{}".format(prefix, name)
+            if prefix == "app":
+                pass
+                #self.app.add_action(action)
+            elif prefix == "win":
+                pass
+                #self.win.add_action(action)
+
+        #log.debug("Registering action as '{}'".format(key))
+        self._actions[key] = action
+        return action
+
+
+    # If the actions namespace is called, trigger an action
+    def __call__(self, name):
+        # Try to parse the action string.
+        valid, action_name, target_value = Action.parse_detailed_name(name)
+        if not valid:
+            raise Exception("Invalid action string: '{}'".format(name))
+        if action_name not in self._actions:
+            raise Exception("Action '{}' is not registered!".format(action_name))
+
+        if target_value:
+            self._actions[action_name].activate(target_value)
+        else:
+            self._actions[action_name].activate()
+
+    def __getitem__(self, key):
+        return self._actions[key]
+
+    def __iter__(self):
+        return self._actions.itervalues()
+
+    def __repr__(self):
+        return str(self)
+
+    def get_actions(self):
+        return self._actions
 
     def __str__(self):
-        """
-        The string representation should be the name of the action id.
-        Try to find the action id for this action by searching this module.
-        """
-        for name, value in six.iteritems(globals()):
-            if value == self:
-                return name
-        return self.get_name()
-
-    def __repr__(self): return str(self)
-
-    def __call__(self, *args):
-        """
-        Emit the activate signal when called with ().
-        """
-        self.args = args
-        self.emit('activate')
-
-
-class Action(Gtk.Action, _ActionBase):
-    """
-    A custom Action class based on Gtk.Action.
-    Pass additional arguments such as keypresses.
-    """
-
-    def __init__(self, keypresses=(), name=None, label=None, tooltip=None,
-                 stock_id=None):
-        """
-        Create a new Action instance.
-
-        Args:
-            key_presses: a tuple of (keyval1, mod_mask1, keyval2, mod_mask2, ...)
-            the: regular Gtk.Action parameters (defaults to None)
-        """
-        if name is None:
-            name = label
-        GObject.GObject.__init__(self, name=name, label=label, tooltip=tooltip,
-                            stock_id=stock_id)
-        _ActionBase.__init__(self, label, keypresses)
-
-
-class ToggleAction(Gtk.ToggleAction, _ActionBase):
-    """
-    A custom Action class based on Gtk.ToggleAction.
-    Pass additional arguments such as keypresses.
-    """
-
-    def __init__(self, keypresses=(), name=None, label=None, tooltip=None,
-                 stock_id=None, preference_name=None, default=True):
-        """
-        Create a new ToggleAction instance.
-
-        Args:
-            key_presses: a tuple of (keyval1, mod_mask1, keyval2, mod_mask2, ...)
-            the: regular Gtk.Action parameters (defaults to None)
-        """
-        if name is None:
-            name = label
-        GObject.GObject.__init__(self, name=name, label=label,
-                                  tooltip=tooltip, stock_id=stock_id)
-        _ActionBase.__init__(self, label, keypresses)
+        s = "{Actions:"
+        for key in self._actions:
+            s += " {},".format(key)
+        s = s.rstrip(",") + "}"
+        return s
+
+
+class Action(Gio.SimpleAction):
+
+    # Change these to normal python properties.
+    #prefs_name
+
+    def __init__(self, name, parameter=None, label=None, tooltip=None,
+                 icon_name=None, keypresses=None, prefix=None,
+                 preference_name=None, default=None):
+        self.name = name
+        self.label = label
+        self.tooltip = tooltip
+        self.icon_name = icon_name
+        self.keypresses = keypresses
+        self.prefix = prefix
         self.preference_name = preference_name
         self.default = default
 
-    def load_from_preferences(self):
+        # Don't worry about checking types here, since it's done in register()
+        # Save the parameter type to use for converting in __call__
+        self.type = None
+
+        variant = None
+        state = None
+        if parameter:
+            variant = GLib.VariantType.new(parameter)
+        if preference_name:
+            state = GLib.Variant.new_boolean(True)
+        Gio.SimpleAction.__init__(self, name=name, parameter_type=variant, state=state)
+
+    def enable(self):
+        self.props.enabled = True
+
+    def disable(self):
+        self.props.enabled = False
+
+    def set_enabled(self, state):
+        if not isinstance(state, bool):
+            raise TypeError("State must be True/False.")
+        self.props.enabled = state
+
+    def __str__(self):
+        return self.props.name
+
+    def __repr__(self):
+        return str(self)
+
+    def get_active(self):
+        if self.props.state:
+            return self.props.state.get_boolean()
+        return False
+
+    def set_active(self, state):
+        if not isinstance(state, bool):
+            raise TypeError("State must be True/False.")
+        self.change_state(GLib.Variant.new_boolean(state))
+
+    # Allows actions to be directly called.
+    def __call__(self, parameter=None):
+        if self.type and parameter:
+            # Try to convert it to the correct type.
+            try:
+                param = GLib.Variant(self.type, parameter)
+                self.activate(param)
+            except TypeError:
+                raise TypeError("Invalid parameter type for action '{}'. Expected: '{}'".format(self.get_name(), self.type))
+        else:
+            self.activate()
+
+    def load_from_preferences(self, *args):
+        log.debug("load_from_preferences({})".format(args))
         if self.preference_name is not None:
             config = Gtk.Application.get_default().config
-            self.set_active(config.entry(
-                self.preference_name, default=bool(self.default)))
+            self.set_active(config.entry(self.preference_name, default=bool(self.default)))
 
-    def save_to_preferences(self):
+    def save_to_preferences(self, *args):
+        log.debug("save_to_preferences({})".format(args))
         if self.preference_name is not None:
             config = Gtk.Application.get_default().config
             config.entry(self.preference_name, value=self.get_active())
 
+
+actions = Namespace()
+
+
+def get_actions():
+    return actions.get_actions()
+
+
+def connect(action, handler=None):
+    return actions.connect(action, handler=handler)
+
+
 ########################################################################
-# Actions
+# Old Actions
 ########################################################################
-PAGE_CHANGE = Action()
-EXTERNAL_UPDATE = Action()
-VARIABLE_EDITOR_UPDATE = Action()
-FLOW_GRAPH_NEW = Action(
+PAGE_CHANGE = actions.register("win.page_change")
+EXTERNAL_UPDATE = actions.register("app.external_update")
+VARIABLE_EDITOR_UPDATE = actions.register("app.variable_editor_update")
+FLOW_GRAPH_NEW = actions.register("app.flowgraph.new",
     label='_New',
     tooltip='Create a new flow graph',
-    stock_id=Gtk.STOCK_NEW,
-    keypresses=(Gdk.KEY_n, Gdk.ModifierType.CONTROL_MASK),
+    icon_name='document-new',
+    keypresses=["<Ctrl>n"],
+    parameter="s",
 )
-FLOW_GRAPH_OPEN = Action(
+FLOW_GRAPH_OPEN = actions.register("app.flowgraph.open",
     label='_Open',
     tooltip='Open an existing flow graph',
-    stock_id=Gtk.STOCK_OPEN,
-    keypresses=(Gdk.KEY_o, Gdk.ModifierType.CONTROL_MASK),
+    icon_name='document-open',
+    keypresses=["<Ctrl>o"],
 )
-FLOW_GRAPH_OPEN_RECENT = Action(
+FLOW_GRAPH_OPEN_RECENT = actions.register("app.flowgraph.open_recent",
     label='Open _Recent',
     tooltip='Open a recently used flow graph',
-    stock_id=Gtk.STOCK_OPEN,
+    icon_name='document-open-recent',
+    parameter="s",
 )
-FLOW_GRAPH_SAVE = Action(
+FLOW_GRAPH_CLEAR_RECENT = actions.register("app.flowgraph.clear_recent")
+FLOW_GRAPH_SAVE = actions.register("app.flowgraph.save",
     label='_Save',
     tooltip='Save the current flow graph',
-    stock_id=Gtk.STOCK_SAVE,
-    keypresses=(Gdk.KEY_s, Gdk.ModifierType.CONTROL_MASK),
+    icon_name='document-save',
+    keypresses=["<Ctrl>s"],
 )
-FLOW_GRAPH_SAVE_AS = Action(
+FLOW_GRAPH_SAVE_AS = actions.register("app.flowgraph.save_as",
     label='Save _As',
     tooltip='Save the current flow graph as...',
-    stock_id=Gtk.STOCK_SAVE_AS,
-    keypresses=(Gdk.KEY_s, Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK),
+    icon_name='document-save-as',
+    keypresses=["<Ctrl><Shift>s"],
 )
-FLOW_GRAPH_SAVE_A_COPY = Action(
-    label='Save A Copy',
-    tooltip='Save the copy of current flow graph',
+FLOW_GRAPH_SAVE_COPY = actions.register("app.flowgraph.save_copy",
+    label='Save Copy',
+    tooltip='Save a copy of current flow graph',
 )
-FLOW_GRAPH_DUPLICATE = Action(
+FLOW_GRAPH_DUPLICATE = actions.register("app.flowgraph.duplicate",
     label='_Duplicate',
     tooltip='Create a duplicate of current flow graph',
-    stock_id=Gtk.STOCK_COPY,
-    keypresses=(Gdk.KEY_d, Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK),
+    #stock_id=Gtk.STOCK_COPY,
+    keypresses=["<Ctrl><Shift>d"],
 )
-FLOW_GRAPH_CLOSE = Action(
+FLOW_GRAPH_CLOSE = actions.register("app.flowgraph.close",
     label='_Close',
     tooltip='Close the current flow graph',
-    stock_id=Gtk.STOCK_CLOSE,
-    keypresses=(Gdk.KEY_w, Gdk.ModifierType.CONTROL_MASK),
+    icon_name='window-close',
+    keypresses=["<Ctrl>w"],
 )
-APPLICATION_INITIALIZE = Action()
-APPLICATION_QUIT = Action(
+APPLICATION_INITIALIZE = actions.register("app.initialize")
+APPLICATION_QUIT = actions.register("app.quit",
     label='_Quit',
     tooltip='Quit program',
-    stock_id=Gtk.STOCK_QUIT,
-    keypresses=(Gdk.KEY_q, Gdk.ModifierType.CONTROL_MASK),
+    icon_name='application-exit',
+    keypresses=["<Ctrl>q"],
 )
-FLOW_GRAPH_UNDO = Action(
+FLOW_GRAPH_UNDO = actions.register("win.undo",
     label='_Undo',
     tooltip='Undo a change to the flow graph',
-    stock_id=Gtk.STOCK_UNDO,
-    keypresses=(Gdk.KEY_z, Gdk.ModifierType.CONTROL_MASK),
+    icon_name='edit-undo',
+    keypresses=["<Ctrl>z"],
 )
-FLOW_GRAPH_REDO = Action(
+FLOW_GRAPH_REDO = actions.register("win.redo",
     label='_Redo',
     tooltip='Redo a change to the flow graph',
-    stock_id=Gtk.STOCK_REDO,
-    keypresses=(Gdk.KEY_y, Gdk.ModifierType.CONTROL_MASK),
+    icon_name='edit-redo',
+    keypresses=["<Ctrl>y"],
 )
-NOTHING_SELECT = Action()
-SELECT_ALL = Action(
+NOTHING_SELECT = actions.register("win.unselect")
+SELECT_ALL = actions.register("win.select_all",
     label='Select _All',
     tooltip='Select all blocks and connections in the flow graph',
-    stock_id=Gtk.STOCK_SELECT_ALL,
-    keypresses=(Gdk.KEY_a, Gdk.ModifierType.CONTROL_MASK),
+    icon_name='edit-select-all',
+    keypresses=["<Ctrl>a"],
 )
-ELEMENT_SELECT = Action()
-ELEMENT_CREATE = Action()
-ELEMENT_DELETE = Action(
+ELEMENT_SELECT = actions.register("win.select")
+ELEMENT_CREATE = actions.register("win.add")
+ELEMENT_DELETE = actions.register("win.delete",
     label='_Delete',
     tooltip='Delete the selected blocks',
-    stock_id=Gtk.STOCK_DELETE,
-    keypresses=(Gdk.KEY_Delete, NO_MODS_MASK),
+    icon_name='edit-delete',
 )
-BLOCK_MOVE = Action()
-BLOCK_ROTATE_CCW = Action(
+BLOCK_MOVE = actions.register("win.block_move")
+BLOCK_ROTATE_CCW = actions.register("win.block_rotate_ccw",
     label='Rotate Counterclockwise',
     tooltip='Rotate the selected blocks 90 degrees to the left',
-    stock_id=Gtk.STOCK_GO_BACK,
-    keypresses=(Gdk.KEY_Left, NO_MODS_MASK),
+    icon_name='object-rotate-left',
 )
-BLOCK_ROTATE_CW = Action(
+BLOCK_ROTATE_CW = actions.register("win.block_rotate",
     label='Rotate Clockwise',
     tooltip='Rotate the selected blocks 90 degrees to the right',
-    stock_id=Gtk.STOCK_GO_FORWARD,
-    keypresses=(Gdk.KEY_Right, NO_MODS_MASK),
+    icon_name='object-rotate-right',
 )
-BLOCK_VALIGN_TOP = Action(
+BLOCK_VALIGN_TOP = actions.register("win.block_align_top",
     label='Vertical Align Top',
     tooltip='Align tops of selected blocks',
-    keypresses=(Gdk.KEY_t, Gdk.ModifierType.SHIFT_MASK),
 )
-BLOCK_VALIGN_MIDDLE = Action(
+BLOCK_VALIGN_MIDDLE = actions.register("win.block_align_middle",
     label='Vertical Align Middle',
     tooltip='Align centers of selected blocks vertically',
-    keypresses=(Gdk.KEY_m, Gdk.ModifierType.SHIFT_MASK),
 )
-BLOCK_VALIGN_BOTTOM = Action(
+BLOCK_VALIGN_BOTTOM = actions.register("win.block_align_bottom",
     label='Vertical Align Bottom',
     tooltip='Align bottoms of selected blocks',
-    keypresses=(Gdk.KEY_b, Gdk.ModifierType.SHIFT_MASK),
 )
-BLOCK_HALIGN_LEFT = Action(
+BLOCK_HALIGN_LEFT = actions.register("win.block_align_left",
     label='Horizontal Align Left',
     tooltip='Align left edges of blocks selected blocks',
-    keypresses=(Gdk.KEY_l, Gdk.ModifierType.SHIFT_MASK),
 )
-BLOCK_HALIGN_CENTER = Action(
+BLOCK_HALIGN_CENTER = actions.register("win.block_align_center",
     label='Horizontal Align Center',
     tooltip='Align centers of selected blocks horizontally',
-    keypresses=(Gdk.KEY_c, Gdk.ModifierType.SHIFT_MASK),
 )
-BLOCK_HALIGN_RIGHT = Action(
+BLOCK_HALIGN_RIGHT = actions.register("win.block_align_right",
     label='Horizontal Align Right',
     tooltip='Align right edges of selected blocks',
-    keypresses=(Gdk.KEY_r, Gdk.ModifierType.SHIFT_MASK),
 )
 BLOCK_ALIGNMENTS = [
     BLOCK_VALIGN_TOP,
@@ -310,234 +347,222 @@ BLOCK_ALIGNMENTS = [
     BLOCK_HALIGN_CENTER,
     BLOCK_HALIGN_RIGHT,
 ]
-BLOCK_PARAM_MODIFY = Action(
+BLOCK_PARAM_MODIFY = actions.register("win.block_modify",
     label='_Properties',
     tooltip='Modify params for the selected block',
-    stock_id=Gtk.STOCK_PROPERTIES,
-    keypresses=(Gdk.KEY_Return, NO_MODS_MASK),
+    icon_name='document-properties',
 )
-BLOCK_ENABLE = Action(
+BLOCK_ENABLE = actions.register("win.block_enable",
     label='E_nable',
     tooltip='Enable the selected blocks',
-    stock_id=Gtk.STOCK_CONNECT,
-    keypresses=(Gdk.KEY_e, NO_MODS_MASK),
+    icon_name='network-wired',
 )
-BLOCK_DISABLE = Action(
+BLOCK_DISABLE = actions.register("win.block_disable",
     label='D_isable',
     tooltip='Disable the selected blocks',
-    stock_id=Gtk.STOCK_DISCONNECT,
-    keypresses=(Gdk.KEY_d, NO_MODS_MASK),
+    icon_name='network-wired-disconnected',
 )
-BLOCK_BYPASS = Action(
+BLOCK_BYPASS = actions.register("win.block_bypass",
     label='_Bypass',
     tooltip='Bypass the selected block',
-    stock_id=Gtk.STOCK_MEDIA_FORWARD,
-    keypresses=(Gdk.KEY_b, NO_MODS_MASK),
+    icon_name='media-seek-forward',
 )
-TOGGLE_SNAP_TO_GRID = ToggleAction(
+TOGGLE_SNAP_TO_GRID = actions.register("win.snap_to_grid",
     label='_Snap to grid',
     tooltip='Snap blocks to a grid for an easier connection alignment',
-    preference_name='snap_to_grid'
+    preference_name='snap_to_grid',
 )
-TOGGLE_HIDE_DISABLED_BLOCKS = ToggleAction(
+TOGGLE_HIDE_DISABLED_BLOCKS = actions.register("win.hide_disabled",
     label='Hide _Disabled Blocks',
     tooltip='Toggle visibility of disabled blocks and connections',
-    stock_id=Gtk.STOCK_MISSING_IMAGE,
-    keypresses=(Gdk.KEY_d, Gdk.ModifierType.CONTROL_MASK),
+    icon_name='image-missing',
+    keypresses=["<Ctrl>d"],
+    preference_name='hide_disabled',
 )
-TOGGLE_HIDE_VARIABLES = ToggleAction(
+TOGGLE_HIDE_VARIABLES = actions.register("win.hide_variables",
     label='Hide Variables',
     tooltip='Hide all variable blocks',
     preference_name='hide_variables',
     default=False,
 )
-TOGGLE_FLOW_GRAPH_VAR_EDITOR = ToggleAction(
+TOGGLE_FLOW_GRAPH_VAR_EDITOR = actions.register("win.toggle_variable_editor",
     label='Show _Variable Editor',
     tooltip='Show the variable editor. Modify variables and imports in this flow graph',
-    stock_id=Gtk.STOCK_EDIT,
+    icon_name='accessories-text-editor',
     default=True,
-    keypresses=(Gdk.KEY_e, Gdk.ModifierType.CONTROL_MASK),
+    keypresses=["<Ctrl>e"],
     preference_name='variable_editor_visable',
 )
-TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR = ToggleAction(
+TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR = actions.register("win.toggle_variable_editor_sidebar",
     label='Move the Variable Editor to the Sidebar',
     tooltip='Move the variable editor to the sidebar',
     default=False,
     preference_name='variable_editor_sidebar',
 )
-TOGGLE_AUTO_HIDE_PORT_LABELS = ToggleAction(
+TOGGLE_AUTO_HIDE_PORT_LABELS = actions.register("win.auto_hide_port_labels",
     label='Auto-Hide _Port Labels',
     tooltip='Automatically hide port labels',
     preference_name='auto_hide_port_labels'
 )
-TOGGLE_SHOW_BLOCK_COMMENTS = ToggleAction(
+TOGGLE_SHOW_BLOCK_COMMENTS = actions.register("win.show_block_comments",
     label='Show Block Comments',
     tooltip="Show comment beneath each block",
     preference_name='show_block_comments'
 )
-TOGGLE_SHOW_CODE_PREVIEW_TAB = ToggleAction(
+TOGGLE_SHOW_CODE_PREVIEW_TAB = actions.register("win.toggle_code_preview",
     label='Generated Code Preview',
     tooltip="Show a preview of the code generated for each Block in its "
             "Properties Dialog",
     preference_name='show_generated_code_tab',
     default=False,
 )
-TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY = ToggleAction(
+TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY = actions.register("win.show_flowgraph_complexity",
     label='Show Flowgraph Complexity',
     tooltip="How many Balints is the flowgraph...",
     preference_name='show_flowgraph_complexity',
     default=False,
 )
-BLOCK_CREATE_HIER = Action(
+BLOCK_CREATE_HIER = actions.register("win.block_create_hier",
     label='C_reate Hier',
     tooltip='Create hier block from selected blocks',
-    stock_id=Gtk.STOCK_CONNECT,
-#   keypresses=(Gdk.KEY_c, NO_MODS_MASK),
+    icon_name='document-new',
 )
-BLOCK_CUT = Action(
+BLOCK_CUT = actions.register("win.block_cut",
     label='Cu_t',
     tooltip='Cut',
-    stock_id=Gtk.STOCK_CUT,
-    keypresses=(Gdk.KEY_x, Gdk.ModifierType.CONTROL_MASK),
+    icon_name='edit-cut',
+    keypresses=["<Ctrl>x"],
 )
-BLOCK_COPY = Action(
+BLOCK_COPY = actions.register("win.block_copy",
     label='_Copy',
     tooltip='Copy',
-    stock_id=Gtk.STOCK_COPY,
-    keypresses=(Gdk.KEY_c, Gdk.ModifierType.CONTROL_MASK),
+    icon_name='edit-copy',
+    keypresses=["<Ctrl>c"],
 )
-BLOCK_PASTE = Action(
+BLOCK_PASTE = actions.register("win.block_paste",
     label='_Paste',
     tooltip='Paste',
-    stock_id=Gtk.STOCK_PASTE,
-    keypresses=(Gdk.KEY_v, Gdk.ModifierType.CONTROL_MASK),
+    icon_name='edit-paste',
+    keypresses=["<Ctrl>v"],
 )
-ERRORS_WINDOW_DISPLAY = Action(
+ERRORS_WINDOW_DISPLAY = actions.register("app.errors",
     label='Flowgraph _Errors',
     tooltip='View flow graph errors',
-    stock_id=Gtk.STOCK_DIALOG_ERROR,
+    icon_name='dialog-error',
 )
-TOGGLE_CONSOLE_WINDOW = ToggleAction(
+TOGGLE_CONSOLE_WINDOW = actions.register("win.toggle_console_window",
     label='Show _Console Panel',
     tooltip='Toggle visibility of the console',
-    keypresses=(Gdk.KEY_r, Gdk.ModifierType.CONTROL_MASK),
+    keypresses=["<Ctrl>r"],
     preference_name='console_window_visible'
 )
-TOGGLE_BLOCKS_WINDOW = ToggleAction(
+# TODO: Might be able to convert this to a Gio.PropertyAction eventually.
+#       actions would need to be defined in the correct class and not globally
+TOGGLE_BLOCKS_WINDOW = actions.register("win.toggle_blocks_window",
     label='Show _Block Tree Panel',
     tooltip='Toggle visibility of the block tree widget',
-    keypresses=(Gdk.KEY_b, Gdk.ModifierType.CONTROL_MASK),
+    keypresses=["<Ctrl>b"],
     preference_name='blocks_window_visible'
 )
-TOGGLE_SCROLL_LOCK = ToggleAction(
+TOGGLE_SCROLL_LOCK = actions.register("win.console.scroll_lock",
     label='Console Scroll _Lock',
     tooltip='Toggle scroll lock for the console window',
     preference_name='scroll_lock'
 )
-ABOUT_WINDOW_DISPLAY = Action(
+ABOUT_WINDOW_DISPLAY = actions.register("app.about",
     label='_About',
     tooltip='About this program',
-    stock_id=Gtk.STOCK_ABOUT,
+    icon_name='help-about',
 )
-HELP_WINDOW_DISPLAY = Action(
+HELP_WINDOW_DISPLAY = actions.register("app.help",
     label='_Help',
     tooltip='Usage tips',
-    stock_id=Gtk.STOCK_HELP,
-    keypresses=(Gdk.KEY_F1, NO_MODS_MASK),
+    icon_name='help-contents',
+    keypresses=["F1"],
 )
-TYPES_WINDOW_DISPLAY = Action(
+TYPES_WINDOW_DISPLAY = actions.register("app.types",
     label='_Types',
     tooltip='Types color mapping',
-    stock_id=Gtk.STOCK_DIALOG_INFO,
+    icon_name='dialog-information',
 )
-FLOW_GRAPH_GEN = Action(
+FLOW_GRAPH_GEN = actions.register("app.flowgraph.generate",
     label='_Generate',
     tooltip='Generate the flow graph',
-    stock_id=Gtk.STOCK_CONVERT,
-    keypresses=(Gdk.KEY_F5, NO_MODS_MASK),
+    icon_name='insert-object',
+    keypresses=["F5"],
 )
-FLOW_GRAPH_EXEC = Action(
+FLOW_GRAPH_EXEC = actions.register("app.flowgraph.execute",
     label='_Execute',
     tooltip='Execute the flow graph',
-    stock_id=Gtk.STOCK_MEDIA_PLAY,
-    keypresses=(Gdk.KEY_F6, NO_MODS_MASK),
+    icon_name='media-playback-start',
+    keypresses=["F6"],
 )
-FLOW_GRAPH_KILL = Action(
+FLOW_GRAPH_KILL = actions.register("app.flowgraph.kill",
     label='_Kill',
     tooltip='Kill the flow graph',
-    stock_id=Gtk.STOCK_STOP,
-    keypresses=(Gdk.KEY_F7, NO_MODS_MASK),
+    icon_name='media-playback-stop',
+    keypresses=["F7"],
 )
-FLOW_GRAPH_SCREEN_CAPTURE = Action(
+FLOW_GRAPH_SCREEN_CAPTURE = actions.register("app.flowgraph.screen_capture",
     label='Screen Ca_pture',
     tooltip='Create a screen capture of the flow graph',
-    stock_id=Gtk.STOCK_PRINT,
-    keypresses=(Gdk.KEY_p, Gdk.ModifierType.CONTROL_MASK),
-)
-PORT_CONTROLLER_DEC = Action(
-    keypresses=(Gdk.KEY_minus, NO_MODS_MASK, Gdk.KEY_KP_Subtract, NO_MODS_MASK),
-)
-PORT_CONTROLLER_INC = Action(
-    keypresses=(Gdk.KEY_plus, NO_MODS_MASK, Gdk.KEY_KP_Add, NO_MODS_MASK),
-)
-BLOCK_INC_TYPE = Action(
-    keypresses=(Gdk.KEY_Down, NO_MODS_MASK),
-)
-BLOCK_DEC_TYPE = Action(
-    keypresses=(Gdk.KEY_Up, NO_MODS_MASK),
-)
-RELOAD_BLOCKS = Action(
+    icon_name='printer',
+    keypresses=["<Ctrl>p"],
+)
+PORT_CONTROLLER_DEC = actions.register("win.port_controller_dec")
+PORT_CONTROLLER_INC = actions.register("win.port_controller_inc")
+BLOCK_INC_TYPE = actions.register("win.block_inc_type")
+BLOCK_DEC_TYPE = actions.register("win.block_dec_type")
+RELOAD_BLOCKS = actions.register("app.reload_blocks",
     label='Reload _Blocks',
     tooltip='Reload Blocks',
-    stock_id=Gtk.STOCK_REFRESH
+    icon_name='view-refresh'
 )
-FIND_BLOCKS = Action(
+FIND_BLOCKS = actions.register("win.find_blocks",
     label='_Find Blocks',
     tooltip='Search for a block by name (and key)',
-    stock_id=Gtk.STOCK_FIND,
-    keypresses=(Gdk.KEY_f, Gdk.ModifierType.CONTROL_MASK,
-                Gdk.KEY_slash, NO_MODS_MASK),
+    icon_name='edit-find',
+    keypresses=["<Ctrl>f", "slash"],
 )
-CLEAR_CONSOLE = Action(
+CLEAR_CONSOLE = actions.register("win.console.clear",
     label='_Clear Console',
     tooltip='Clear Console',
-    stock_id=Gtk.STOCK_CLEAR,
+    icon_name='edit-clear',
 )
-SAVE_CONSOLE = Action(
+SAVE_CONSOLE = actions.register("win.console.save",
     label='_Save Console',
     tooltip='Save Console',
-    stock_id=Gtk.STOCK_SAVE,
+    icon_name='edit-save',
 )
-OPEN_HIER = Action(
+OPEN_HIER = actions.register("win.open_hier",
     label='Open H_ier',
     tooltip='Open the source of the selected hierarchical block',
-    stock_id=Gtk.STOCK_JUMP_TO,
+    icon_name='go-jump',
 )
-BUSSIFY_SOURCES = Action(
+BUSSIFY_SOURCES = actions.register("win.bussify_sources",
     label='Toggle So_urce Bus',
     tooltip='Gang source ports into a single bus port',
-    stock_id=Gtk.STOCK_JUMP_TO,
+    icon_name='go-jump',
 )
-BUSSIFY_SINKS = Action(
+BUSSIFY_SINKS = actions.register("win.bussify_sinks",
     label='Toggle S_ink Bus',
     tooltip='Gang sink ports into a single bus port',
-    stock_id=Gtk.STOCK_JUMP_TO,
+    icon_name='go-jump',
 )
-XML_PARSER_ERRORS_DISPLAY = Action(
+XML_PARSER_ERRORS_DISPLAY = actions.register("app.xml_errors",
     label='_Parser Errors',
     tooltip='View errors that occurred while parsing XML files',
-    stock_id=Gtk.STOCK_DIALOG_ERROR,
+    icon_name='dialog-error',
 )
-FLOW_GRAPH_OPEN_QSS_THEME = Action(
+FLOW_GRAPH_OPEN_QSS_THEME = actions.register("app.open_qss_theme",
     label='Set Default QT GUI _Theme',
     tooltip='Set a default QT Style Sheet file to use for QT GUI',
-    stock_id=Gtk.STOCK_OPEN,
+    icon_name='document-open',
 )
-TOOLS_RUN_FDESIGN = Action(
+TOOLS_RUN_FDESIGN = actions.register("app.filter_design",
     label='Filter Design Tool',
     tooltip='Execute gr_filter_design',
-    stock_id=Gtk.STOCK_EXECUTE,
-)
-TOOLS_MORE_TO_COME = Action(
-    label='More to come',
+    icon_name='media-playback-start',
 )
+POST_HANDLER = actions.register("app.post_handler")
+READY = actions.register("app.ready")
diff --git a/grc/gui/Application.py b/grc/gui/Application.py
index 9e89009dc9..c1456c3a8d 100644
--- a/grc/gui/Application.py
+++ b/grc/gui/Application.py
@@ -24,9 +24,9 @@ import os
 import subprocess
 import logging
 
-from gi.repository import Gtk, GObject
+from gi.repository import Gtk, Gio, GLib, GObject
 
-from . import Dialogs, Actions, Executor, FileDialogs, Utils
+from . import Dialogs, Actions, Executor, FileDialogs, Utils, Bars
 from .MainWindow import MainWindow
 from .ParserErrorsDialog import ParserErrorsDialog
 from .PropsDialog import PropsDialog
@@ -56,54 +56,58 @@ class Application(Gtk.Application):
         """
         self.clipboard = None
         self.dialog = None
-        for action in Actions.get_all_actions(): action.connect('activate', self._handle_action)
-        #setup the main window
+
+        # Setup the main window
         self.platform = platform
         self.config = platform.config
 
-        log.debug("__init__()")
-
-        #initialize
+        log.debug("Application()")
+        # Connect all actions to _handle_action
+        for x in Actions.get_actions():
+            Actions.connect(x, handler=self._handle_action)
+            Actions.actions[x].enable()
+            if x.startswith("app."):
+                self.add_action(Actions.actions[x])
+            # Setup the shortcut keys
+            # These are the globally defined shortcuts
+            keypress = Actions.actions[x].keypresses
+            if keypress:
+                self.set_accels_for_action(x, keypress)
+
+        # Initialize
         self.init_file_paths = [os.path.abspath(file_path) for file_path in file_paths]
         self.init = False
 
     def do_startup(self):
         Gtk.Application.do_startup(self)
-        log.debug("do_startup()")
+        log.debug("Application.do_startup()")
+
+        # Setup the menu
+        log.debug("Creating menu")
+        '''
+        self.menu = Bars.Menu()
+        self.set_menu()
+        if self.prefers_app_menu():
+            self.set_app_menu(self.menu)
+        else:
+            self.set_menubar(self.menu)
+        '''
 
     def do_activate(self):
         Gtk.Application.do_activate(self)
-        log.debug("do_activate()")
+        log.debug("Application.do_activate()")
 
-        self.main_window = MainWindow(self, self.platform, self._handle_action)
+        self.main_window = MainWindow(self, self.platform)
         self.main_window.connect('delete-event', self._quit)
-        self.main_window.connect('key-press-event', self._handle_key_press)
         self.get_focus_flag = self.main_window.get_focus_flag
+
         #setup the messages
         Messages.register_messenger(self.main_window.add_console_line)
         Messages.send_init(self.platform)
 
+        log.debug("Calling Actions.APPLICATION_INITIALIZE")
         Actions.APPLICATION_INITIALIZE()
 
-    def _handle_key_press(self, widget, event):
-        """
-        Handle key presses from the keyboard and translate key combinations into actions.
-        This key press handler is called prior to the gtk key press handler.
-        This handler bypasses built in accelerator key handling when in focus because
-        * some keys are ignored by the accelerators like the direction keys,
-        * some keys are not registered to any accelerators but are still used.
-        When not in focus, gtk and the accelerators handle the the key press.
-
-        Returns:
-            false to let gtk handle the key action
-        """
-        # prevent key event stealing while the search box is active
-        # .has_focus() only in newer versions 2.17+?
-        # .is_focus() seems to work, but exactly the same
-        if self.main_window.btwin.search_entry.has_focus():
-            return False
-        return Actions.handle_key_press(event)
-
     def _quit(self, window, event):
         """
         Handle the delete event from the main window.
@@ -117,7 +121,7 @@ class Application(Gtk.Application):
         return True
 
     def _handle_action(self, action, *args):
-        #print action
+        log.debug("_handle_action({0}, {1})".format(action, args))
         main = self.main_window
         page = main.current_page
         flow_graph = page.flow_graph if page else None
@@ -130,6 +134,7 @@ class Application(Gtk.Application):
         # Initialize/Quit
         ##################################################
         if action == Actions.APPLICATION_INITIALIZE:
+            log.debug("APPLICATION_INITIALIZE")
             file_path_to_show = self.config.file_open()
             for file_path in (self.init_file_paths or self.config.get_open_files()):
                 if os.path.exists(file_path):
@@ -139,35 +144,84 @@ class Application(Gtk.Application):
 
             main.btwin.search_entry.hide()
 
-            # Disable all actions, then re-enable a few
-            for action in Actions.get_all_actions():
-                action.set_sensitive(False)  # set all actions disabled
+            """
+            Only disable certain actions on startup. Each of these actions are
+            conditionally enabled in _handle_action, so disable them first.
+             - FLOW_GRAPH_UNDO/REDO are set in gui/StateCache.py
+             - XML_PARSER_ERRORS_DISPLAY is set in RELOAD_BLOCKS
+
+            TODO: These 4 should probably be included, but they are not currently
+            enabled anywhere else:
+             - PORT_CONTROLLER_DEC, PORT_CONTROLLER_INC
+             - BLOCK_INC_TYPE, BLOCK_DEC_TYPE
+
+            TODO: These should be handled better. They are set in
+            update_exec_stop(), but not anywhere else
+             - FLOW_GRAPH_GEN, FLOW_GRAPH_EXEC, FLOW_GRAPH_KILL
+            """
+            for action in (
+                Actions.ERRORS_WINDOW_DISPLAY,
+                Actions.ELEMENT_DELETE,
+                Actions.BLOCK_PARAM_MODIFY,
+                Actions.BLOCK_ROTATE_CCW,
+                Actions.BLOCK_ROTATE_CW,
+                Actions.BLOCK_VALIGN_TOP,
+                Actions.BLOCK_VALIGN_MIDDLE,
+                Actions.BLOCK_VALIGN_BOTTOM,
+                Actions.BLOCK_HALIGN_LEFT,
+                Actions.BLOCK_HALIGN_CENTER,
+                Actions.BLOCK_HALIGN_RIGHT,
+                Actions.BLOCK_CUT,
+                Actions.BLOCK_COPY,
+                Actions.BLOCK_PASTE,
+                Actions.BLOCK_ENABLE,
+                Actions.BLOCK_DISABLE,
+                Actions.BLOCK_BYPASS,
+                Actions.BLOCK_CREATE_HIER,
+                Actions.OPEN_HIER,
+                Actions.BUSSIFY_SOURCES,
+                Actions.BUSSIFY_SINKS,
+                Actions.FLOW_GRAPH_SAVE,
+                Actions.FLOW_GRAPH_UNDO,
+                Actions.FLOW_GRAPH_REDO,
+                Actions.XML_PARSER_ERRORS_DISPLAY
+            ):
+                action.disable()
+
+            # Load preferences
             for action in (
-                Actions.APPLICATION_QUIT, Actions.FLOW_GRAPH_NEW,
-                Actions.FLOW_GRAPH_OPEN, Actions.FLOW_GRAPH_SAVE_AS,
-                Actions.FLOW_GRAPH_DUPLICATE, Actions.FLOW_GRAPH_SAVE_A_COPY,
-                Actions.FLOW_GRAPH_CLOSE, Actions.ABOUT_WINDOW_DISPLAY,
-                Actions.FLOW_GRAPH_SCREEN_CAPTURE, Actions.HELP_WINDOW_DISPLAY,
-                Actions.TYPES_WINDOW_DISPLAY, Actions.TOGGLE_BLOCKS_WINDOW,
-                Actions.TOGGLE_CONSOLE_WINDOW, Actions.TOGGLE_HIDE_DISABLED_BLOCKS,
-                Actions.TOOLS_RUN_FDESIGN, Actions.TOGGLE_SCROLL_LOCK,
-                Actions.CLEAR_CONSOLE, Actions.SAVE_CONSOLE,
-                Actions.TOGGLE_AUTO_HIDE_PORT_LABELS, Actions.TOGGLE_SNAP_TO_GRID,
+                Actions.TOGGLE_BLOCKS_WINDOW,
+                Actions.TOGGLE_CONSOLE_WINDOW,
+                Actions.TOGGLE_HIDE_DISABLED_BLOCKS,
+                Actions.TOGGLE_SCROLL_LOCK,
+                Actions.TOGGLE_AUTO_HIDE_PORT_LABELS,
+                Actions.TOGGLE_SNAP_TO_GRID,
                 Actions.TOGGLE_SHOW_BLOCK_COMMENTS,
                 Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB,
                 Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY,
-                Actions.FLOW_GRAPH_OPEN_QSS_THEME,
                 Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR,
                 Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR,
                 Actions.TOGGLE_HIDE_VARIABLES,
-                Actions.SELECT_ALL,
             ):
-                action.set_sensitive(True)
+                action.set_enabled(True)
                 if hasattr(action, 'load_from_preferences'):
                     action.load_from_preferences()
+
+            # Hide the panels *IF* it's saved in preferences
+            main.update_panel_visibility(main.BLOCKS, Actions.TOGGLE_BLOCKS_WINDOW.get_active())
+            main.update_panel_visibility(main.CONSOLE, Actions.TOGGLE_CONSOLE_WINDOW.get_active())
+            main.update_panel_visibility(main.VARIABLES, Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR.get_active())
+
             if ParseXML.xml_failures:
                 Messages.send_xml_errors_if_any(ParseXML.xml_failures)
-                Actions.XML_PARSER_ERRORS_DISPLAY.set_sensitive(True)
+                Actions.XML_PARSER_ERRORS_DISPLAY.set_enabled(True)
+
+            # Force an update on the current page to match loaded preferences.
+            # In the future, change the __init__ order to load preferences first
+            page = main.current_page
+            if page:
+                page.flow_graph.update()
+
             self.init = True
         elif action == Actions.APPLICATION_QUIT:
             if main.close_pages():
@@ -400,12 +454,17 @@ class Application(Gtk.Application):
         elif action == Actions.ERRORS_WINDOW_DISPLAY:
             Dialogs.ErrorsDialog(main, flow_graph).run_and_destroy()
         elif action == Actions.TOGGLE_CONSOLE_WINDOW:
+            action.set_active(not action.get_active())
             main.update_panel_visibility(main.CONSOLE, action.get_active())
             action.save_to_preferences()
         elif action == Actions.TOGGLE_BLOCKS_WINDOW:
+            # This would be better matched to a Gio.PropertyAction, but to do
+            # this, actions would have to be defined in the window not globally
+            action.set_active(not action.get_active())
             main.update_panel_visibility(main.BLOCKS, action.get_active())
             action.save_to_preferences()
         elif action == Actions.TOGGLE_SCROLL_LOCK:
+            action.set_active(not action.get_active())
             active = action.get_active()
             main.console.text_display.scroll_lock = active
             if active:
@@ -418,41 +477,53 @@ class Application(Gtk.Application):
             if file_path is not None:
                 main.console.text_display.save(file_path)
         elif action == Actions.TOGGLE_HIDE_DISABLED_BLOCKS:
+            action.set_active(not action.get_active())
             Actions.NOTHING_SELECT()
         elif action == Actions.TOGGLE_AUTO_HIDE_PORT_LABELS:
+            action.set_active(not action.get_active())
             action.save_to_preferences()
             for page in main.get_pages():
                 page.flow_graph.create_shapes()
         elif action in (Actions.TOGGLE_SNAP_TO_GRID,
                         Actions.TOGGLE_SHOW_BLOCK_COMMENTS,
                         Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB):
+            action.set_active(not action.get_active())
             action.save_to_preferences()
         elif action == Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY:
+            action.set_active(not action.get_active())
             action.save_to_preferences()
             for page in main.get_pages():
                 flow_graph_update(page.flow_graph)
         elif action == Actions.TOGGLE_HIDE_VARIABLES:
-            # Call the variable editor TOGGLE in case it needs to be showing
-            Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR()
+            action.set_active(not action.get_active())
+            active = action.get_active()
+            # Either way, triggering this should simply trigger the variable editor
+            # to be visible.
+            varedit = Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR
+            if active:
+                log.debug("Variables are hidden. Forcing the variable panel to be visible.")
+                varedit.disable()
+            else:
+                varedit.enable()
+            # Just force it to show.
+            varedit.set_active(True)
+            main.update_panel_visibility(main.VARIABLES)
             Actions.NOTHING_SELECT()
             action.save_to_preferences()
+            varedit.save_to_preferences()
+            flow_graph_update()
         elif action == Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR:
-            # See if the variables are hidden
-            if Actions.TOGGLE_HIDE_VARIABLES.get_active():
-                # Force this to be shown
-                main.update_panel_visibility(main.VARIABLES, True)
-                action.set_active(True)
-                action.set_sensitive(False)
-            else:
-                if action.get_sensitive():
-                    main.update_panel_visibility(main.VARIABLES, action.get_active())
-                else:  # This is occurring after variables are un-hidden
-                    # Leave it enabled
-                    action.set_sensitive(True)
-                    action.set_active(True)
-            # Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR.set_sensitive(action.get_active())
+            # TODO: There may be issues at startup since these aren't triggered
+            # the same was as Gtk.Actions when loading preferences.
+            action.set_active(not action.get_active())
+            # Just assume this was triggered because it was enabled.
+            main.update_panel_visibility(main.VARIABLES, action.get_active())
+            action.save_to_preferences()
+
+            # Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR.set_enabled(action.get_active())
             action.save_to_preferences()
         elif action == Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR:
+            action.set_active(not action.get_active())
             if self.init:
                 Dialogs.MessageDialogWrapper(
                     main, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE,
@@ -463,7 +534,7 @@ class Application(Gtk.Application):
         # Param Modifications
         ##################################################
         elif action == Actions.BLOCK_PARAM_MODIFY:
-            selected_block = action.args[0] if action.args else flow_graph.selected_block
+            selected_block = args[0] if args[0] else flow_graph.selected_block
             if selected_block:
                 self.dialog = PropsDialog(self.main_window, selected_block)
                 response = Gtk.ResponseType.APPLY
@@ -520,17 +591,17 @@ class Application(Gtk.Application):
         elif action == Actions.FLOW_GRAPH_NEW:
             main.new_page()
             if args:
-                flow_graph = main.get_flow_graph()
+                flow_graph = main.current_page.flow_graph
                 flow_graph._options_block.get_param('generate_options').set_value(args[0])
                 flow_graph_update(flow_graph)
         elif action == Actions.FLOW_GRAPH_OPEN:
-            file_paths = args if args else FileDialogs.OpenFlowGraph(main, page.file_path).run()
+            file_paths = args[0] if args[0] else FileDialogs.OpenFlowGraph(main, page.file_path).run()
             if file_paths: # Open a new page for each file, show only the first
                 for i,file_path in enumerate(file_paths):
                     main.new_page(file_path, show=(i==0))
                     self.config.add_recent_file(file_path)
                     main.tool_bar.refresh_submenus()
-                    main.menu_bar.refresh_submenus()
+                    #main.menu_bar.refresh_submenus()
         elif action == Actions.FLOW_GRAPH_OPEN_QSS_THEME:
             file_paths = FileDialogs.OpenQSS(main, self.platform.config.install_prefix +
                                              '/share/gnuradio/themes/').run()
@@ -558,33 +629,37 @@ class Application(Gtk.Application):
                 Actions.FLOW_GRAPH_SAVE()
                 self.config.add_recent_file(file_path)
                 main.tool_bar.refresh_submenus()
-                main.menu_bar.refresh_submenus()
-        elif action == Actions.FLOW_GRAPH_SAVE_A_COPY:
+                #TODO
+                #main.menu_bar.refresh_submenus()
+        elif action == Actions.FLOW_GRAPH_SAVE_COPY:
             try:
-                if not page.get_file_path():
+                if not page.file_path:
+                    # Make sure the current flowgraph has been saved
                     Actions.FLOW_GRAPH_SAVE_AS()
                 else:
-                    dup_file_path = page.get_file_path()
+                    dup_file_path = page.file_path
                     dup_file_name = '.'.join(dup_file_path.split('.')[:-1]) + "_copy" # Assuming .grc extension at the end of file_path
                     dup_file_path_temp = dup_file_name+'.grc'
                     count = 1
                     while os.path.exists(dup_file_path_temp):
                         dup_file_path_temp = dup_file_name+'('+str(count)+').grc'
                         count += 1
-                    dup_file_path_user = SaveFlowGraphFileDialog(dup_file_path_temp).run()
+                    dup_file_path_user = FileDialogs.SaveFlowGraph(main, dup_file_path_temp).run()
                     if dup_file_path_user is not None:
                         ParseXML.to_file(flow_graph.export_data(), dup_file_path_user)
                         Messages.send('Saved Copy to: "' + dup_file_path_user + '"\n')
             except IOError:
                 Messages.send_fail_save("Can not create a copy of the flowgraph\n")
         elif action == Actions.FLOW_GRAPH_DUPLICATE:
-            flow_graph = main.get_flow_graph()
+            previous = flow_graph
+            # Create a new page
             main.new_page()
-            curr_page = main.get_page()
-            new_flow_graph = main.get_flow_graph()
-            new_flow_graph.import_data(flow_graph.export_data())
+            page = main.current_page
+            new_flow_graph = page.flow_graph
+            # Import the old data and mark the current as not saved
+            new_flow_graph.import_data(previous.export_data())
             flow_graph_update(new_flow_graph)
-            curr_page.set_saved(False)
+            page.saved = False
         elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE:
             file_path, background_transparent = FileDialogs.SaveScreenShot(main, page.file_path).run()
             if file_path is not None:
@@ -634,7 +709,7 @@ class Application(Gtk.Application):
         elif action == Actions.RELOAD_BLOCKS:
             self.platform.build_block_library()
             main.btwin.repopulate()
-            Actions.XML_PARSER_ERRORS_DISPLAY.set_sensitive(bool(
+            Actions.XML_PARSER_ERRORS_DISPLAY.set_enabled(bool(
                 ParseXML.xml_failures))
             Messages.send_xml_errors_if_any(ParseXML.xml_failures)
             # Force a redraw of the graph, by getting the current state and re-importing it
@@ -667,7 +742,7 @@ class Application(Gtk.Application):
                              shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
 
         else:
-            print('!!! Action "%s" not handled !!!' % action)
+            log.warning('!!! Action "%s" not handled !!!' % action)
         ##################################################
         # Global Actions for all States
         ##################################################
@@ -678,19 +753,19 @@ class Application(Gtk.Application):
         selected_block = selected_blocks[0] if selected_blocks else None
 
         #update general buttons
-        Actions.ERRORS_WINDOW_DISPLAY.set_sensitive(not flow_graph.is_valid())
-        Actions.ELEMENT_DELETE.set_sensitive(bool(flow_graph.selected_elements))
-        Actions.BLOCK_PARAM_MODIFY.set_sensitive(bool(selected_block))
-        Actions.BLOCK_ROTATE_CCW.set_sensitive(bool(selected_blocks))
-        Actions.BLOCK_ROTATE_CW.set_sensitive(bool(selected_blocks))
+        Actions.ERRORS_WINDOW_DISPLAY.set_enabled(not flow_graph.is_valid())
+        Actions.ELEMENT_DELETE.set_enabled(bool(flow_graph.selected_elements))
+        Actions.BLOCK_PARAM_MODIFY.set_enabled(bool(selected_block))
+        Actions.BLOCK_ROTATE_CCW.set_enabled(bool(selected_blocks))
+        Actions.BLOCK_ROTATE_CW.set_enabled(bool(selected_blocks))
         #update alignment options
         for act in Actions.BLOCK_ALIGNMENTS:
             if act:
-                act.set_sensitive(len(selected_blocks) > 1)
+                act.set_enabled(len(selected_blocks) > 1)
         #update cut/copy/paste
-        Actions.BLOCK_CUT.set_sensitive(bool(selected_blocks))
-        Actions.BLOCK_COPY.set_sensitive(bool(selected_blocks))
-        Actions.BLOCK_PASTE.set_sensitive(bool(self.clipboard))
+        Actions.BLOCK_CUT.set_enabled(bool(selected_blocks))
+        Actions.BLOCK_COPY.set_enabled(bool(selected_blocks))
+        Actions.BLOCK_PASTE.set_enabled(bool(self.clipboard))
         #update enable/disable/bypass
         can_enable = any(block.state != 'enabled'
                          for block in selected_blocks)
@@ -700,26 +775,26 @@ class Application(Gtk.Application):
             all(block.can_bypass() for block in selected_blocks) and
             any(not block.get_bypassed() for block in selected_blocks)
         )
-        Actions.BLOCK_ENABLE.set_sensitive(can_enable)
-        Actions.BLOCK_DISABLE.set_sensitive(can_disable)
-        Actions.BLOCK_BYPASS.set_sensitive(can_bypass_all)
+        Actions.BLOCK_ENABLE.set_enabled(can_enable)
+        Actions.BLOCK_DISABLE.set_enabled(can_disable)
+        Actions.BLOCK_BYPASS.set_enabled(can_bypass_all)
 
-        Actions.BLOCK_CREATE_HIER.set_sensitive(bool(selected_blocks))
-        Actions.OPEN_HIER.set_sensitive(bool(selected_blocks))
-        Actions.BUSSIFY_SOURCES.set_sensitive(bool(selected_blocks))
-        Actions.BUSSIFY_SINKS.set_sensitive(bool(selected_blocks))
-        Actions.RELOAD_BLOCKS.set_sensitive(True)
-        Actions.FIND_BLOCKS.set_sensitive(True)
+        Actions.BLOCK_CREATE_HIER.set_enabled(bool(selected_blocks))
+        Actions.OPEN_HIER.set_enabled(bool(selected_blocks))
+        Actions.BUSSIFY_SOURCES.set_enabled(bool(selected_blocks))
+        Actions.BUSSIFY_SINKS.set_enabled(bool(selected_blocks))
+        Actions.RELOAD_BLOCKS.enable()
+        Actions.FIND_BLOCKS.enable()
 
         self.update_exec_stop()
 
-        Actions.FLOW_GRAPH_SAVE.set_sensitive(not page.saved)
+        Actions.FLOW_GRAPH_SAVE.set_enabled(not page.saved)
         main.update()
 
         flow_graph.update_selected()
         page.drawing_area.queue_draw()
 
-        return True  # action was handled
+        return True  # Action was handled
 
     def update_exec_stop(self):
         """
@@ -728,6 +803,6 @@ class Application(Gtk.Application):
         """
         page = self.main_window.current_page
         sensitive = page.flow_graph.is_valid() and not page.process
-        Actions.FLOW_GRAPH_GEN.set_sensitive(sensitive)
-        Actions.FLOW_GRAPH_EXEC.set_sensitive(sensitive)
-        Actions.FLOW_GRAPH_KILL.set_sensitive(page.process is not None)
+        Actions.FLOW_GRAPH_GEN.set_enabled(sensitive)
+        Actions.FLOW_GRAPH_EXEC.set_enabled(sensitive)
+        Actions.FLOW_GRAPH_KILL.set_enabled(page.process is not None)
diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py
index 1510e109d2..2a8040f5d5 100644
--- a/grc/gui/Bars.py
+++ b/grc/gui/Bars.py
@@ -1,5 +1,5 @@
 """
-Copyright 2007, 2008, 2009, 2015 Free Software Foundation, Inc.
+Copyright 2007, 2008, 2009, 2015, 2016 Free Software Foundation, Inc.
 This file is part of GNU Radio
 
 GNU Radio Companion is free software; you can redistribute it and/or
@@ -19,306 +19,299 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 from __future__ import absolute_import
 
-from gi.repository import Gtk, GObject
+import logging
+
+from gi.repository import Gtk, GObject, Gio, GLib
 
 from . import Actions
 
 
+log = logging.getLogger(__name__)
+
+
+'''
+# Menu/Toolbar Lists:
+#
+# Sub items can be 1 of 3 types
+#  - List    Creates a section within the current menu
+#  - Tuple   Creates a submenu using a string or action as the parent. The child
+#            can be another menu list or an identifier used to call a helper function.
+#  - Action  Appends a new menu item to the current menu
+#
+
+LIST_NAME = [
+    [Action1, Action2], # New section
+    (Action3, [Action4, Action5]), # Submenu with action as parent
+    ("Label", [Action6, Action7]), # Submenu with string as parent
+    ("Label2", "helper") # Submenu with helper function. Calls 'create_helper()'
+]
+'''
+
+
 # The list of actions for the toolbar.
-TOOLBAR_LIST = (
-    (Actions.FLOW_GRAPH_NEW, 'flow_graph_new'),
-    (Actions.FLOW_GRAPH_OPEN, 'flow_graph_recent'),
-    Actions.FLOW_GRAPH_SAVE,
-    Actions.FLOW_GRAPH_CLOSE,
-    None,
-    Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR,
-    Actions.FLOW_GRAPH_SCREEN_CAPTURE,
-    None,
-    Actions.BLOCK_CUT,
-    Actions.BLOCK_COPY,
-    Actions.BLOCK_PASTE,
-    Actions.ELEMENT_DELETE,
-    None,
-    Actions.FLOW_GRAPH_UNDO,
-    Actions.FLOW_GRAPH_REDO,
-    None,
-    Actions.ERRORS_WINDOW_DISPLAY,
-    Actions.FLOW_GRAPH_GEN,
-    Actions.FLOW_GRAPH_EXEC,
-    Actions.FLOW_GRAPH_KILL,
-    None,
-    Actions.BLOCK_ROTATE_CCW,
-    Actions.BLOCK_ROTATE_CW,
-    None,
-    Actions.BLOCK_ENABLE,
-    Actions.BLOCK_DISABLE,
-    Actions.BLOCK_BYPASS,
-    Actions.TOGGLE_HIDE_DISABLED_BLOCKS,
-    None,
-    Actions.FIND_BLOCKS,
-    Actions.RELOAD_BLOCKS,
-    Actions.OPEN_HIER,
-)
+TOOLBAR_LIST = [
+    [(Actions.FLOW_GRAPH_NEW, 'flow_graph_new'), Actions.FLOW_GRAPH_OPEN,
+    (Actions.FLOW_GRAPH_OPEN_RECENT, 'flow_graph_recent'), Actions.FLOW_GRAPH_SAVE, Actions.FLOW_GRAPH_CLOSE],
+    [Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR, Actions.FLOW_GRAPH_SCREEN_CAPTURE],
+    [Actions.BLOCK_CUT, Actions.BLOCK_COPY, Actions.BLOCK_PASTE, Actions.ELEMENT_DELETE],
+    [Actions.FLOW_GRAPH_UNDO, Actions.FLOW_GRAPH_REDO],
+    [Actions.ERRORS_WINDOW_DISPLAY, Actions.FLOW_GRAPH_GEN, Actions.FLOW_GRAPH_EXEC, Actions.FLOW_GRAPH_KILL],
+    [Actions.BLOCK_ROTATE_CCW, Actions.BLOCK_ROTATE_CW],
+    [Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, Actions.BLOCK_BYPASS, Actions.TOGGLE_HIDE_DISABLED_BLOCKS],
+    [Actions.FIND_BLOCKS, Actions.RELOAD_BLOCKS, Actions.OPEN_HIER]
+]
 
 
 # The list of actions and categories for the menu bar.
-MENU_BAR_LIST = (
-    (Gtk.Action(name='File', label='_File'), [
-        'flow_graph_new',
-        Actions.FLOW_GRAPH_DUPLICATE,
-        Actions.FLOW_GRAPH_OPEN,
-        'flow_graph_recent',
-        None,
-        Actions.FLOW_GRAPH_SAVE,
-        Actions.FLOW_GRAPH_SAVE_AS,
-        Actions.FLOW_GRAPH_SAVE_A_COPY,
-        None,
-        Actions.FLOW_GRAPH_SCREEN_CAPTURE,
-        None,
-        Actions.FLOW_GRAPH_CLOSE,
-        Actions.APPLICATION_QUIT,
-    ]),
-    (Gtk.Action(name='Edit', label='_Edit'), [
-        Actions.FLOW_GRAPH_UNDO,
-        Actions.FLOW_GRAPH_REDO,
-        None,
-        Actions.BLOCK_CUT,
-        Actions.BLOCK_COPY,
-        Actions.BLOCK_PASTE,
-        Actions.ELEMENT_DELETE,
-        Actions.SELECT_ALL,
-        None,
-        Actions.BLOCK_ROTATE_CCW,
-        Actions.BLOCK_ROTATE_CW,
-        (Gtk.Action(name='Align', label='_Align', tooltip=None, stock_id=None), Actions.BLOCK_ALIGNMENTS),
-        None,
-        Actions.BLOCK_ENABLE,
-        Actions.BLOCK_DISABLE,
-        Actions.BLOCK_BYPASS,
-        None,
-        Actions.BLOCK_PARAM_MODIFY,
-    ]),
-    (Gtk.Action(name='View', label='_View'), [
-        Actions.TOGGLE_BLOCKS_WINDOW,
-        None,
-        Actions.TOGGLE_CONSOLE_WINDOW,
-        Actions.TOGGLE_SCROLL_LOCK,
-        Actions.SAVE_CONSOLE,
-        Actions.CLEAR_CONSOLE,
-        None,
-        Actions.TOGGLE_HIDE_VARIABLES,
-        Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR,
-        Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR,
-        None,
-        Actions.TOGGLE_HIDE_DISABLED_BLOCKS,
-        Actions.TOGGLE_AUTO_HIDE_PORT_LABELS,
-        Actions.TOGGLE_SNAP_TO_GRID,
-        Actions.TOGGLE_SHOW_BLOCK_COMMENTS,
-        None,
-        Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB,
-        None,
-        Actions.ERRORS_WINDOW_DISPLAY,
-        Actions.FIND_BLOCKS,
-    ]),
-    (Gtk.Action(name='Run', label='_Run'), [
-        Actions.FLOW_GRAPH_GEN,
-        Actions.FLOW_GRAPH_EXEC,
-        Actions.FLOW_GRAPH_KILL,
-    ]),
-    (Gtk.Action(name='Tools', label='_Tools'), [
-        Actions.TOOLS_RUN_FDESIGN,
-        Actions.FLOW_GRAPH_OPEN_QSS_THEME,
-        None,
-        Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY,
-        None,
-        Actions.TOOLS_MORE_TO_COME,
-    ]),
-    (Gtk.Action(name='Help', label='_Help'), [
-        Actions.HELP_WINDOW_DISPLAY,
-        Actions.TYPES_WINDOW_DISPLAY,
-        Actions.XML_PARSER_ERRORS_DISPLAY,
-        None,
-        Actions.ABOUT_WINDOW_DISPLAY,
-    ]),
-)
+MENU_BAR_LIST = [
+  ('_File', [
+    [(Actions.FLOW_GRAPH_NEW, 'flow_graph_new'), Actions.FLOW_GRAPH_DUPLICATE,
+     Actions.FLOW_GRAPH_OPEN, (Actions.FLOW_GRAPH_OPEN_RECENT, 'flow_graph_recent')],
+    [Actions.FLOW_GRAPH_SAVE, Actions.FLOW_GRAPH_SAVE_AS, Actions.FLOW_GRAPH_SAVE_COPY],
+    [Actions.FLOW_GRAPH_SCREEN_CAPTURE],
+    [Actions.FLOW_GRAPH_CLOSE, Actions.APPLICATION_QUIT]
+  ]),
+  ('_Edit', [
+    [Actions.FLOW_GRAPH_UNDO, Actions.FLOW_GRAPH_REDO],
+    [Actions.BLOCK_CUT, Actions.BLOCK_COPY, Actions.BLOCK_PASTE, Actions.ELEMENT_DELETE, Actions.SELECT_ALL],
+    [Actions.BLOCK_ROTATE_CCW, Actions.BLOCK_ROTATE_CW, ('_Align', Actions.BLOCK_ALIGNMENTS)],
+    [Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, Actions.BLOCK_BYPASS],
+    [Actions.BLOCK_PARAM_MODIFY]
+  ]),
+  ('_View', [
+    [Actions.TOGGLE_BLOCKS_WINDOW],
+    [Actions.TOGGLE_CONSOLE_WINDOW, Actions.TOGGLE_SCROLL_LOCK, Actions.SAVE_CONSOLE, Actions.CLEAR_CONSOLE],
+    [Actions.TOGGLE_HIDE_VARIABLES, Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR, Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR],
+    [Actions.TOGGLE_HIDE_DISABLED_BLOCKS, Actions.TOGGLE_AUTO_HIDE_PORT_LABELS, Actions.TOGGLE_SNAP_TO_GRID, Actions.TOGGLE_SHOW_BLOCK_COMMENTS],
+    [Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB],
+    [Actions.ERRORS_WINDOW_DISPLAY, Actions.FIND_BLOCKS],
+  ]),
+  ('_Run', [
+    Actions.FLOW_GRAPH_GEN, Actions.FLOW_GRAPH_EXEC, Actions.FLOW_GRAPH_KILL
+  ]),
+  ('_Tools', [
+    [Actions.TOOLS_RUN_FDESIGN, Actions.FLOW_GRAPH_OPEN_QSS_THEME],
+    [Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY]
+  ]),
+  ('_Help', [
+      [Actions.HELP_WINDOW_DISPLAY, Actions.TYPES_WINDOW_DISPLAY, Actions.XML_PARSER_ERRORS_DISPLAY],
+      [Actions.ABOUT_WINDOW_DISPLAY]
+  ])]
 
 
 # The list of actions for the context menu.
 CONTEXT_MENU_LIST = [
-    Actions.BLOCK_CUT,
-    Actions.BLOCK_COPY,
-    Actions.BLOCK_PASTE,
-    Actions.ELEMENT_DELETE,
-    None,
-    Actions.BLOCK_ROTATE_CCW,
-    Actions.BLOCK_ROTATE_CW,
-    Actions.BLOCK_ENABLE,
-    Actions.BLOCK_DISABLE,
-    Actions.BLOCK_BYPASS,
-    None,
-    (Gtk.Action(name='More', label='_More'), [
-        Actions.BLOCK_CREATE_HIER,
-        Actions.OPEN_HIER,
-        None,
-        Actions.BUSSIFY_SOURCES,
-        Actions.BUSSIFY_SINKS,
-    ]),
-    Actions.BLOCK_PARAM_MODIFY
+    [Actions.BLOCK_CUT, Actions.BLOCK_COPY, Actions.BLOCK_PASTE, Actions.ELEMENT_DELETE],
+    [Actions.BLOCK_ROTATE_CCW, Actions.BLOCK_ROTATE_CW, Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, Actions.BLOCK_BYPASS],
+    [("_More", [
+        [Actions.BLOCK_CREATE_HIER, Actions.OPEN_HIER],
+        [Actions.BUSSIFY_SOURCES, Actions.BUSSIFY_SINKS]]
+    )],
+    [Actions.BLOCK_PARAM_MODIFY],
 ]
 
 
-class SubMenuCreator(object):
+class SubMenuHelper(object):
+    ''' Generates custom submenus for the main menu or toolbar. '''
 
-    def __init__(self, generate_modes, action_handler_callback):
-        self.generate_modes = generate_modes
-        self.action_handler_callback = action_handler_callback
-        self.submenus = []
+    def __init__(self):
+        self.submenus = {}
 
-    def create_submenu(self, action_tuple, item):
-        func = getattr(self, '_fill_' + action_tuple[1] + "_submenu")
-        self.submenus.append((action_tuple[0], func, item))
-        self.refresh_submenus()
+    def build_submenu(self, name, obj, set_func):
+        # Get the correct helper function
+        create_func = getattr(self, "create_{}".format(name))
+        # Save the helper functions for rebuilding the menu later
+        self.submenus[name] = (create_func, obj, set_func)
+        # Actually build the menu
+        set_func(obj, create_func())
 
     def refresh_submenus(self):
-        for action, func, item in self.submenus:
-            try:
-                item.set_property("menu", func(action))
-            except TypeError:
-                item.set_property("submenu", func(action))
-            item.set_property('sensitive', True)
-
-    def callback_adaptor(self, item, action_key):
-        action, key = action_key
-        self.action_handler_callback(action, key)
-
-    def _fill_flow_graph_new_submenu(self, action):
-        """Sub menu to create flow-graph with pre-set generate mode"""
-        menu = Gtk.Menu()
-        for key, name, default in self.generate_modes:
-            if default:
-                item = Actions.FLOW_GRAPH_NEW.create_menu_item()
-                item.set_label(name)
-            else:
-                item = Gtk.MenuItem(name=name, use_underline=False)
-                item.connect('activate', self.callback_adaptor, (action, key))
-            menu.append(item)
-        menu.show_all()
+        for name in self.submenus:
+            create_func, obj, set_func = self.submenus[name]
+            print ("refresh", create_func, obj, set_func)
+            set_func(obj, create_func())
+
+    def create_flow_graph_new(self):
+        """ Different flowgraph types """
+        menu = Gio.Menu()
+        platform = Gtk.Application.get_default().platform
+        generate_modes = platform.get_generate_options()
+        for key, name, default in generate_modes:
+            target = "app.flowgraph.new::{}".format(key)
+            menu.append(name, target)
         return menu
 
-    def _fill_flow_graph_recent_submenu(self, action):
-        """menu showing recent flow-graphs"""
-        menu = Gtk.Menu()
+    def create_flow_graph_recent(self):
+        """ Recent flow graphs """
+
         config = Gtk.Application.get_default().config
         recent_files = config.get_recent_files()
+        menu = Gio.Menu()
         if len(recent_files) > 0:
+            files = Gio.Menu()
             for i, file_name in enumerate(recent_files):
-                item = Gtk.MenuItem(name="%d. %s" % (i+1, file_name), use_underline=False)
-                item.connect('activate', self.callback_adaptor,
-                             (action, file_name))
-                menu.append(item)
-            menu.show_all()
-            return menu
-        return None
+                target = "app.flowgraph.open_recent::{}".format(file_name)
+                files.append(file_name, target)
+            menu.append_section(None, files)
+            #clear = Gio.Menu()
+            #clear.append("Clear recent files", "app.flowgraph.clear_recent")
+            #menu.append_section(None, clear)
+        else:
+            # Show an empty menu
+            menuitem = Gio.MenuItem.new("No items found", "app.none")
+            menu.append_item(menuitem)
+        return menu
 
 
-class Toolbar(Gtk.Toolbar, SubMenuCreator):
-    """The gtk toolbar with actions added from the toolbar list."""
+class MenuHelper(SubMenuHelper):
+    """
+    Recursively builds a menu from a given list of actions.
 
-    def __init__(self, generate_modes, action_handler_callback):
-        """
-        Parse the list of action names in the toolbar list.
-        Look up the action for each name in the action list and add it to the
-        toolbar.
-        """
-        GObject.GObject.__init__(self)
-        self.set_style(Gtk.ToolbarStyle.ICONS)
-        SubMenuCreator.__init__(self, generate_modes, action_handler_callback)
-
-        for action in TOOLBAR_LIST:
-            if isinstance(action, tuple) and isinstance(action[1], str):
-                # create a button with a sub-menu
-                # TODO: Fix later
-                #action[0].set_tool_item_type(Gtk.MenuToolButton)
-                item = action[0].create_tool_item()
-                #self.create_submenu(action, item)
-                #self.refresh_submenus()
-
-            elif action is None:
-                item = Gtk.SeparatorToolItem()
-
-            else:
-                #TODO: Fix later
-                #action.set_tool_item_type(Gtk.ToolButton)
-                item = action.create_tool_item()
-                # this reset of the tooltip property is required
-                # (after creating the tool item) for the tooltip to show
-                action.set_property('tooltip', action.get_property('tooltip'))
-            self.add(item)
-
-
-class MenuHelperMixin(object):
-    """Mixin class to help build menus from the above action lists"""
-
-    def _fill_menu(self, actions, menu=None):
-        """Create a menu from list of actions"""
-        menu = menu or Gtk.Menu()
+    Args:
+     - actions:  List of actions to build the menu
+     - menu:     Current menu being built
+
+    Notes:
+     - Tuple:  Create a new submenu from the parent (1st) and child (2nd) elements
+     - Action: Append to current menu
+     - List:   Start a new section
+    """
+
+    def __init__(self):
+        SubMenuHelper.__init__(self)
+
+    def build_menu(self, actions, menu):
         for item in actions:
             if isinstance(item, tuple):
-                menu_item = self._make_sub_menu(*item)
-            elif isinstance(item, str):
-                menu_item = getattr(self, 'create_' + item)()
-            elif item is None:
-                menu_item = Gtk.SeparatorMenuItem()
-            else:
-                menu_item = item.create_menu_item()
-            menu.append(menu_item)
-        menu.show_all()
-        return menu
-
-    def _make_sub_menu(self, main, actions):
-        """Create a submenu from a main action and a list of actions"""
-        main = main.create_menu_item()
-        main.set_submenu(self._fill_menu(actions))
-        return main
+                # Create a new submenu
+                parent, child = (item[0], item[1])
+
+                # Create the parent
+                label, target = (parent, None)
+                if isinstance(parent, Actions.Action):
+                    label = parent.label
+                    target = "{}.{}".format(parent.prefix, parent.name)
+                menuitem = Gio.MenuItem.new(label, None)
+                if hasattr(parent, "icon_name"):
+                    menuitem.set_icon(Gio.Icon.new_for_string(parent.icon_name))
+
+                # Create the new submenu
+                if isinstance(child, list):
+                    submenu = Gio.Menu()
+                    self.build_menu(child, submenu)
+                    menuitem.set_submenu(submenu)
+                elif isinstance(child, str):
+                    # Child is the name of the submenu to create
+                    def set_func(obj, menu):
+                        obj.set_submenu(menu)
+                    self.build_submenu(child, menuitem, set_func)
+                menu.append_item(menuitem)
+
+            elif isinstance(item, list):
+                # Create a new section
+                section = Gio.Menu()
+                self.build_menu(item, section)
+                menu.append_section(None, section)
+
+            elif isinstance(item, Actions.Action):
+                # Append a new menuitem
+                target = "{}.{}".format(item.prefix, item.name)
+                menuitem = Gio.MenuItem.new(item.label, target)
+                if item.icon_name:
+                    menuitem.set_icon(Gio.Icon.new_for_string(item.icon_name))
+                menu.append_item(menuitem)
+
+
+class ToolbarHelper(SubMenuHelper):
+    """
+     Builds a toolbar from a given list of actions.
+
+    Args:
+     - actions:  List of actions to build the menu
+     - item:     Current menu being built
+
+    Notes:
+     - Tuple:  Create a new submenu from the parent (1st) and child (2nd) elements
+     - Action: Append to current menu
+     - List:   Start a new section
+    """
 
+    def __init__(self):
+        SubMenuHelper.__init__(self)
 
-class MenuBar(Gtk.MenuBar, MenuHelperMixin, SubMenuCreator):
-    """The gtk menu bar with actions added from the menu bar list."""
+    def build_toolbar(self, actions, current):
+        for item in actions:
+            if isinstance(item, list):
+                # Toolbar's don't have sections like menus, so call this function
+                #  recursively with the "section" and just append a separator.
+                self.build_toolbar(item, self)
+                current.insert(Gtk.SeparatorToolItem.new(), -1)
+
+            elif isinstance(item, tuple):
+                parent, child = (item[0], item[1])
+                # Create an item with a submenu
+                # Generate the submenu and add to the item.
+                # Add the item to the toolbar
+                button = Gtk.MenuToolButton.new()
+                # The tuple should be made up of an Action and something.
+                button.set_label(parent.label)
+                button.set_tooltip_text(parent.tooltip)
+                button.set_icon_name(parent.icon_name)
+
+                target = "{}.{}".format(parent.prefix, parent.name)
+                button.set_action_name(target)
+
+                def set_func(obj, menu):
+                    obj.set_menu(Gtk.Menu.new_from_model(menu))
+
+                self.build_submenu(child, button, set_func)
+                current.insert(button, -1)
+
+            elif isinstance(item, Actions.Action):
+                button = Gtk.ToolButton.new()
+                button.set_label(item.label)
+                button.set_tooltip_text(item.tooltip)
+                button.set_icon_name(item.icon_name)
+                target = "{}.{}".format(item.prefix, item.name)
+                button.set_action_name(target)
+                current.insert(button, -1)
+
+
+class Menu(Gio.Menu, MenuHelper):
+    """ Main Menu """
 
-    def __init__(self, generate_modes, action_handler_callback):
-        """
-        Parse the list of submenus from the menubar list.
-        For each submenu, get a list of action names.
-        Look up the action for each name in the action list and add it to the
-        submenu. Add the submenu to the menu bar.
-        """
+    def __init__(self):
         GObject.GObject.__init__(self)
-        SubMenuCreator.__init__(self, generate_modes, action_handler_callback)
-        for main_action, actions in MENU_BAR_LIST:
-            self.append(self._make_sub_menu(main_action, actions))
+        MenuHelper.__init__(self)
 
-    def create_flow_graph_new(self):
-        main = Gtk.ImageMenuItem(label=Gtk.STOCK_NEW)
-        main.set_label(Actions.FLOW_GRAPH_NEW.get_label())
-        func = self._fill_flow_graph_new_submenu
-        self.submenus.append((Actions.FLOW_GRAPH_NEW, func, main))
-        self.refresh_submenus()
-        return main
+        log.debug("Building the main menu")
+        self.build_menu(MENU_BAR_LIST, self)
 
-    def create_flow_graph_recent(self):
-        main = Gtk.ImageMenuItem(label=Gtk.STOCK_OPEN)
-        main.set_label(Actions.FLOW_GRAPH_OPEN_RECENT.get_label())
-        func = self._fill_flow_graph_recent_submenu
-        self.submenus.append((Actions.FLOW_GRAPH_OPEN, func, main))
-        self.refresh_submenus()
-        if main.get_submenu() is None:
-            main.set_property('sensitive', False)
-        return main
 
+class ContextMenu(Gio.Menu, MenuHelper):
+    """ Context menu for the drawing area """
+
+    def __init__(self):
+        GObject.GObject.__init__(self)
+
+        log.debug("Building the context menu")
+        self.build_menu(CONTEXT_MENU_LIST, self)
 
-class ContextMenu(Gtk.Menu, MenuHelperMixin):
-    """The gtk menu with actions added from the context menu list."""
+
+class Toolbar(Gtk.Toolbar, ToolbarHelper):
+    """ The gtk toolbar with actions added from the toolbar list. """
 
     def __init__(self):
+        """
+        Parse the list of action names in the toolbar list.
+        Look up the action for each name in the action list and add it to the
+        toolbar.
+        """
         GObject.GObject.__init__(self)
-        self._fill_menu(CONTEXT_MENU_LIST, self)
+        ToolbarHelper.__init__(self)
+
+        self.set_style(Gtk.ToolbarStyle.ICONS)
+        #self.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
+
+        #SubMenuCreator.__init__(self)
+        self.build_toolbar(TOOLBAR_LIST, self)
diff --git a/grc/gui/Config.py b/grc/gui/Config.py
index c4d395c0b3..6135296660 100644
--- a/grc/gui/Config.py
+++ b/grc/gui/Config.py
@@ -48,7 +48,7 @@ class Config(CoreConfig):
         self.install_prefix = install_prefix
         Constants.update_font_size(self.font_size)
 
-        self.parser = configparser.SafeConfigParser()
+        self.parser = configparser.ConfigParser()
         for section in ['main', 'files_open', 'files_recent']:
             try:
                 self.parser.add_section(section)
diff --git a/grc/gui/Console.py b/grc/gui/Console.py
index d40f300a8a..0ae862493d 100644
--- a/grc/gui/Console.py
+++ b/grc/gui/Console.py
@@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 from __future__ import absolute_import
 
 import os
+import logging
 
 import gi
 gi.require_version('Gtk', '3.0')
@@ -31,9 +32,14 @@ from .Dialogs import TextDisplay, MessageDialogWrapper
 from ..core import Messages
 
 
+log = logging.getLogger(__name__)
+
+
 class Console(Gtk.ScrolledWindow):
     def __init__(self):
         Gtk.ScrolledWindow.__init__(self)
+        log.debug("console()")
+        self.app = Gtk.Application.get_default()
 
         self.text_display = TextDisplay()
 
diff --git a/grc/gui/DrawingArea.py b/grc/gui/DrawingArea.py
index 94dfcf1370..2403fa2844 100644
--- a/grc/gui/DrawingArea.py
+++ b/grc/gui/DrawingArea.py
@@ -23,6 +23,7 @@ from gi.repository import Gtk, Gdk
 
 from .canvas.colors import FLOWGRAPH_BACKGROUND_COLOR
 from . import Constants
+from . import Actions
 
 
 class DrawingArea(Gtk.DrawingArea):
@@ -42,6 +43,7 @@ class DrawingArea(Gtk.DrawingArea):
         Gtk.DrawingArea.__init__(self)
 
         self._flow_graph = flow_graph
+        self.set_property('can_focus', True)
 
         self.zoom_factor = 1.0
         self._update_after_zoom = False
@@ -66,6 +68,11 @@ class DrawingArea(Gtk.DrawingArea):
             # Gdk.EventMask.FOCUS_CHANGE_MASK
         )
 
+        # This may not be the correct place to be handling the user events
+        # Should this be in the page instead?
+        # Or should more of the page functionality move here?
+        self.connect('key_press_event', self._handle_key_press)
+
         # setup drag and drop
         self.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY)
         self.connect('drag-data-received', self._handle_drag_data_received)
@@ -78,12 +85,14 @@ class DrawingArea(Gtk.DrawingArea):
 
         def _handle_notify_event(widget, event, focus_flag):
             self._focus_flag = focus_flag
+
         self.connect('leave-notify-event', _handle_notify_event, False)
         self.connect('enter-notify-event', _handle_notify_event, True)
         # todo: fix
 #        self.set_flags(Gtk.CAN_FOCUS)  # self.set_can_focus(True)
 #        self.connect('focus-out-event', self._handle_focus_lost_event)
 
+
     ##########################################################################
     # Handlers
     ##########################################################################
@@ -155,6 +164,41 @@ class DrawingArea(Gtk.DrawingArea):
             coordinate=self._translate_event_coords(event),
         )
 
+    def _handle_key_press(self, widget, event):
+        """
+        Handle specific keypresses when the drawing area has focus that
+        triggers actions by the user.
+        """
+        key = event.keyval
+        mod = event.state
+
+        # Setup a map of the accelerator keys to the action to trigger
+        accels = {
+            Gtk.accelerator_parse('d'): Actions.BLOCK_DISABLE,
+            Gtk.accelerator_parse('e'): Actions.BLOCK_ENABLE,
+            Gtk.accelerator_parse('b'): Actions.BLOCK_BYPASS,
+            Gtk.accelerator_parse('c'): Actions.BLOCK_CREATE_HIER,
+            Gtk.accelerator_parse('Up'): Actions.BLOCK_DEC_TYPE,
+            Gtk.accelerator_parse('Down'): Actions.BLOCK_INC_TYPE,
+            Gtk.accelerator_parse('Left'): Actions.BLOCK_ROTATE_CCW,
+            Gtk.accelerator_parse('Right'): Actions.BLOCK_ROTATE_CW,
+            Gtk.accelerator_parse('minus'): Actions.PORT_CONTROLLER_DEC,
+            Gtk.accelerator_parse('plus'): Actions.PORT_CONTROLLER_INC,
+            Gtk.accelerator_parse('Add'): Actions.PORT_CONTROLLER_INC,
+            Gtk.accelerator_parse('Subtract'): Actions.PORT_CONTROLLER_DEC,
+            Gtk.accelerator_parse('Return'): Actions.BLOCK_PARAM_MODIFY,
+            Gtk.accelerator_parse('<Shift>t'): Actions.BLOCK_VALIGN_TOP,
+            Gtk.accelerator_parse('<Shift>m'): Actions.BLOCK_VALIGN_MIDDLE,
+            Gtk.accelerator_parse('<Shift>b'): Actions.BLOCK_VALIGN_BOTTOM,
+            Gtk.accelerator_parse('<Shift>l'): Actions.BLOCK_HALIGN_LEFT,
+            Gtk.accelerator_parse('<Shift>c'): Actions.BLOCK_HALIGN_CENTER,
+            Gtk.accelerator_parse('<Shift>r'): Actions.BLOCK_HALIGN_RIGHT,
+        }
+        # Not sold on this.
+        if (key, mod) in accels:
+            accels[(key, mod)]()
+            return True
+
     def _update_size(self):
         w, h = self._flow_graph.get_extents()[2:]
         self.set_size_request(w * self.zoom_factor + 100, h * self.zoom_factor + 100)
diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py
index 5bb4c52a07..c13a59a16d 100644
--- a/grc/gui/MainWindow.py
+++ b/grc/gui/MainWindow.py
@@ -35,6 +35,7 @@ from .Notebook import Notebook, Page
 
 from ..core import Messages
 
+
 log = logging.getLogger(__name__)
 
 
@@ -49,7 +50,7 @@ class MainWindow(Gtk.ApplicationWindow):
     CONSOLE = 1
     VARIABLES = 2
 
-    def __init__(self, app, platform, action_handler_callback):
+    def __init__(self, app, platform):
         """
         MainWindow constructor
         Setup the menu, toolbar, flow graph editor notebook, block selection window...
@@ -58,18 +59,29 @@ class MainWindow(Gtk.ApplicationWindow):
         log.debug("__init__()")
 
         self._platform = platform
+        self.app = app
         self.config = platform.config
 
+        # Add all "win" actions to the local
+        win_actions = filter(lambda x: x.startswith("win."), Actions.get_actions())
+        map(lambda x: self.add_action(Actions.actions[x]), win_actions)
+
         # Setup window
         vbox = Gtk.VBox()
         self.add(vbox)
 
         # Create the menu bar and toolbar
         generate_modes = platform.get_generate_options()
-        self.add_accel_group(Actions.get_accel_group())
-        self.menu_bar = Bars.MenuBar(generate_modes, action_handler_callback)
+
+        # This needs to be replaced
+        # Have an option for either the application menu or this menu
+        self.menu_bar = Gtk.MenuBar.new_from_model(Bars.Menu())
         vbox.pack_start(self.menu_bar, False, False, 0)
-        self.tool_bar = Bars.Toolbar(generate_modes, action_handler_callback)
+
+        self.tool_bar = Bars.Toolbar()
+        self.tool_bar.set_hexpand(True)
+        # Show the toolbar
+        self.tool_bar.show()
         vbox.pack_start(self.tool_bar, False, False, 0)
 
         # Main parent container for the different panels
@@ -126,9 +138,7 @@ class MainWindow(Gtk.ApplicationWindow):
             self.left_subpanel.set_position(self.config.variable_editor_position())
 
         self.show_all()
-        self.console.hide()
-        self.vars.hide()
-        self.btwin.hide()
+        log.debug("Main window ready")
 
     ############################################################
     # Event Handlers
@@ -206,7 +216,6 @@ class MainWindow(Gtk.ApplicationWindow):
     def current_page(self, page):
         self.notebook.current_page = page
 
-
     def add_console_line(self, line):
         """
         Place line at the end of the text buffer, then scroll its window all the way down.
diff --git a/grc/gui/Notebook.py b/grc/gui/Notebook.py
index e78b748326..ef08961036 100644
--- a/grc/gui/Notebook.py
+++ b/grc/gui/Notebook.py
@@ -19,6 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 from __future__ import absolute_import
 import os
+import logging
 
 from gi.repository import Gtk, Gdk, GObject
 
@@ -28,15 +29,24 @@ from .Constants import MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT
 from .DrawingArea import DrawingArea
 
 
+log = logging.getLogger(__name__)
+
+
 class Notebook(Gtk.Notebook):
     def __init__(self):
         Gtk.Notebook.__init__(self)
-
+        log.debug("notebook()")
+        self.app = Gtk.Application.get_default()
         self.current_page = None
+
         self.set_show_border(False)
         self.set_scrollable(True)
         self.connect('switch-page', self._handle_page_change)
 
+        self.add_events(Gdk.EventMask.SCROLL_MASK)
+        self.connect('scroll-event', self._handle_scroll)
+        self._ignore_consecutive_scrolls = 0
+
     def _handle_page_change(self, notebook, page, page_num):
         """
         Handle a page change. When the user clicks on a new tab,
@@ -51,6 +61,26 @@ class Notebook(Gtk.Notebook):
         self.current_page = self.get_nth_page(page_num)
         Actions.PAGE_CHANGE()
 
+    def _handle_scroll(self, widget, event):
+        # Not sure how to handle this at the moment.
+        natural = True
+        # Slow it down
+        if self._ignore_consecutive_scrolls == 0:
+            if event.direction in (Gdk.ScrollDirection.UP, Gdk.ScrollDirection.LEFT):
+                if natural:
+                    self.prev_page()
+                else:
+                    self.next_page()
+            elif event.direction in (Gdk.ScrollDirection.DOWN, Gdk.ScrollDirection.RIGHT):
+                if natural:
+                    self.next_page()
+                else:
+                    self.prev_page()
+            self._ignore_consecutive_scrolls = 3
+        else:
+            self._ignore_consecutive_scrolls -= 1
+        return False
+
 
 class Page(Gtk.HBox):
     """A page in the notebook."""
@@ -99,20 +129,12 @@ class Page(Gtk.HBox):
         self.scrolled_window = Gtk.ScrolledWindow()
         self.scrolled_window.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
         self.scrolled_window.set_policy(Gtk.PolicyType.ALWAYS, Gtk.PolicyType.ALWAYS)
-        self.scrolled_window.connect('key-press-event', self._handle_scroll_window_key_press)
+
         self.scrolled_window.add(self.drawing_area)
         self.pack_start(self.scrolled_window, True, True, 0)
-
         self.show_all()
 
-    def _handle_scroll_window_key_press(self, widget, event):
-        """forward Ctrl-PgUp/Down to NotebookPage (switch fg instead of horiz. scroll"""
-        is_ctrl_pg = (
-            event.state & Gdk.ModifierType.CONTROL_MASK and
-            event.keyval in (Gdk.KEY_Page_Up, Gdk.KEY_Page_Down)
-        )
-        if is_ctrl_pg:
-            return self.get_parent().event(event)
+
 
     def get_generator(self):
         """
diff --git a/grc/gui/ParamWidgets.py b/grc/gui/ParamWidgets.py
index b56eace402..71cb1b7a7d 100644
--- a/grc/gui/ParamWidgets.py
+++ b/grc/gui/ParamWidgets.py
@@ -25,7 +25,7 @@ from . import Utils
 
 style_provider = Gtk.CssProvider()
 
-style_provider.load_from_data("""
+style_provider.load_from_data(b"""
     #dtype_complex         { background-color: #3399FF; }
     #dtype_real            { background-color: #FF8C69; }
     #dtype_float           { background-color: #FF8C69; }
diff --git a/grc/gui/StateCache.py b/grc/gui/StateCache.py
index b109a1281b..ef260d6091 100644
--- a/grc/gui/StateCache.py
+++ b/grc/gui/StateCache.py
@@ -99,5 +99,5 @@ class StateCache(object):
         """
         Update the undo and redo actions based on the number of next and prev states.
         """
-        Actions.FLOW_GRAPH_REDO.set_sensitive(self.num_next_states != 0)
-        Actions.FLOW_GRAPH_UNDO.set_sensitive(self.num_prev_states != 0)
+        Actions.FLOW_GRAPH_REDO.set_enabled(self.num_next_states != 0)
+        Actions.FLOW_GRAPH_UNDO.set_enabled(self.num_prev_states != 0)
diff --git a/grc/gui/Utils.py b/grc/gui/Utils.py
index 3fffe6dd20..969f3759f2 100644
--- a/grc/gui/Utils.py
+++ b/grc/gui/Utils.py
@@ -21,6 +21,7 @@ from __future__ import absolute_import
 
 from gi.repository import GLib
 import cairo
+import six
 
 from .canvas.colors import FLOWGRAPH_BACKGROUND_COLOR
 from . import Constants
@@ -107,7 +108,10 @@ def encode(value):
     Older versions of glib seg fault if the last byte starts a multi-byte
     character.
     """
-    valid_utf8 = value.decode('utf-8', errors='replace').encode('utf-8')
+    if six.PY2:
+        valid_utf8 = value.decode('utf-8', errors='replace').encode('utf-8')
+    else:
+        valid_utf8 = value
     return GLib.markup_escape_text(valid_utf8)
 
 
diff --git a/grc/gui/VariableEditor.py b/grc/gui/VariableEditor.py
index 484395be8c..e310676420 100644
--- a/grc/gui/VariableEditor.py
+++ b/grc/gui/VariableEditor.py
@@ -254,7 +254,9 @@ class VariableEditor(Gtk.VBox):
         elif key == self.ADD_VARIABLE:
             self.emit('create_new_block', 'variable')
         elif key == self.OPEN_PROPERTIES:
-            Actions.BLOCK_PARAM_MODIFY(self._block)
+            # TODO: This probably isn't working because the action doesn't expect a parameter
+            #Actions.BLOCK_PARAM_MODIFY()
+            pass
         elif key == self.DELETE_BLOCK:
             self.emit('remove_block', self._block.get_id())
         elif key == self.DELETE_CONFIRM:
diff --git a/grc/gui/canvas/param.py b/grc/gui/canvas/param.py
index 2ec99e70d8..b027b7653a 100644
--- a/grc/gui/canvas/param.py
+++ b/grc/gui/canvas/param.py
@@ -105,7 +105,7 @@ class Param(CoreParam):
                 if style < 0:  # Front truncate
                     string = '...' + string[3-max_len:]
                 elif style == 0:  # Center truncate
-                    string = string[:max_len/2 - 3] + '...' + string[-max_len/2:]
+                    string = string[:max_len//2 - 3] + '...' + string[-max_len//2:]
                 elif style > 0:  # Rear truncate
                     string = string[:max_len-3] + '...'
             return string
diff --git a/grc/main.py b/grc/main.py
index 305e8b8f78..224a9b11e8 100755
--- a/grc/main.py
+++ b/grc/main.py
@@ -22,10 +22,6 @@ gi.require_version('Gtk', '3.0')
 gi.require_version('PangoCairo', '1.0')
 from gi.repository import Gtk
 
-from gnuradio import gr
-from .gui.Platform import Platform
-from .gui.Application import Application
-
 
 VERSION_AND_DISCLAIMER_TEMPLATE = """\
 GNU Radio Companion %s
@@ -45,6 +41,7 @@ LOG_LEVELS = {
 
 
 def main():
+    from gnuradio import gr
     parser = argparse.ArgumentParser(
         description=VERSION_AND_DISCLAIMER_TEMPLATE % gr.version())
     parser.add_argument('flow_graphs', nargs='*')
@@ -65,7 +62,8 @@ def main():
     console = logging.StreamHandler()
     console.setLevel(LOG_LEVELS[args.log])
 
-    msg_format = '[%(asctime)s - %(levelname)8s] --- %(message)s (%(filename)s:%(lineno)s)'
+    #msg_format = '[%(asctime)s - %(levelname)8s] --- %(message)s (%(filename)s:%(lineno)s)'
+    msg_format = '[%(levelname)s] %(message)s (%(filename)s:%(lineno)s)'
     date_format = '%I:%M'
     formatter = logging.Formatter(msg_format, datefmt=date_format)
 
@@ -73,8 +71,14 @@ def main():
     console.setFormatter(formatter)
     log.addHandler(console)
 
-    log.debug("Loading platform")
+    log.debug("Running main")
 
+    # Delay importing until the logging is setup
+    # Otherwise, the decorators could not use logging.
+    from .gui.Platform import Platform
+    from .gui.Application import Application
+
+    log.debug("Loading platform")
     platform = Platform(
         version=gr.version(),
         version_parts=(gr.major_version(), gr.api_version(), gr.minor_version()),
-- 
cgit v1.2.3