commit-gnuradio
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Commit-gnuradio] [gnuradio] 08/25: grc: Added the variable editor panel


From: git
Subject: [Commit-gnuradio] [gnuradio] 08/25: grc: Added the variable editor panel
Date: Fri, 27 May 2016 19:14:58 +0000 (UTC)

This is an automated email from the git hooks/post-receive script.

jcorgan pushed a commit to branch master
in repository gnuradio.

commit a682562051c75e3a17f0c246dc3a57cea4fab20d
Author: Seth Hitefield <address@hidden>
Date:   Tue May 10 15:06:28 2016 -0400

    grc: Added the variable editor panel
---
 grc/gui/ActionHandler.py  | 108 ++++++++++++------
 grc/gui/Actions.py        |  15 +++
 grc/gui/Bars.py           |   4 +
 grc/gui/MainWindow.py     | 120 ++++++++++++++++----
 grc/gui/Param.py          |  19 +++-
 grc/gui/Preferences.py    |  17 +++
 grc/gui/VariableEditor.py | 283 ++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 507 insertions(+), 59 deletions(-)

diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py
index e5f14bc..d0410e0 100644
--- a/grc/gui/ActionHandler.py
+++ b/grc/gui/ActionHandler.py
@@ -67,6 +67,7 @@ class ActionHandler:
         Messages.send_init(platform)
         #initialize
         self.init_file_paths = file_paths
+        self.init = False
         Actions.APPLICATION_INITIALIZE()
 
     def _handle_key_press(self, widget, event):
@@ -103,10 +104,13 @@ class ActionHandler:
 
     def _handle_action(self, action, *args):
         #print action
-        page = self.main_window.get_page()
+        main = self.main_window
+        page = main.get_page()
         flow_graph = page.get_flow_graph() if page else None
 
+
         def flow_graph_update(fg=flow_graph):
+            main.vars.update_gui()
             fg.update()
 
         ##################################################
@@ -117,13 +121,13 @@ class ActionHandler:
                 self.init_file_paths = 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: self.main_window.new_page(file_path) #load pages 
from file paths
+                if file_path: main.new_page(file_path) #load pages from file 
paths
             if Preferences.file_open() in self.init_file_paths:
-                self.main_window.new_page(Preferences.file_open(), show=True)
+                main.new_page(Preferences.file_open(), show=True)
             if not self.get_page():
-                self.main_window.new_page()  # ensure that at least a blank 
page exists
+                main.new_page()  # ensure that at least a blank page exists
 
-            self.main_window.btwin.search_entry.hide()
+            main.btwin.search_entry.hide()
 
             # Disable all actions, then re-enable a few
             for action in Actions.get_all_actions():
@@ -142,6 +146,8 @@ class ActionHandler:
                 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,
             ):
@@ -151,9 +157,9 @@ class ActionHandler:
             if ParseXML.xml_failures:
                 Messages.send_xml_errors_if_any(ParseXML.xml_failures)
                 Actions.XML_PARSER_ERRORS_DISPLAY.set_sensitive(True)
-
+            self.init = True
         elif action == Actions.APPLICATION_QUIT:
-            if self.main_window.close_pages():
+            if main.close_pages():
                 gtk.main_quit()
                 exit(0)
         ##################################################
@@ -250,7 +256,7 @@ class ActionHandler:
                         # Copy the selected blocks and paste them into a new 
page
                         #   then move the flowgraph to a reasonable position
                         Actions.BLOCK_COPY()
-                        self.main_window.new_page()
+                        main.new_page()
                         Actions.BLOCK_PASTE()
                         coords = (x_min,y_min)
                         flow_graph.move_selected(coords)
@@ -390,33 +396,33 @@ class ActionHandler:
             Dialogs.ErrorsDialog(flow_graph)
         elif action == Actions.TOGGLE_REPORTS_WINDOW:
             if action.get_active():
-                self.main_window.reports_scrolled_window.show()
+                main.update_panel_visibility(main.REPORTS, True)
             else:
-                self.main_window.reports_scrolled_window.hide()
+                main.update_panel_visibility(main.REPORTS, False)
             action.save_to_preferences()
         elif action == Actions.TOGGLE_BLOCKS_WINDOW:
             if action.get_active():
-                self.main_window.btwin.show()
+                main.update_panel_visibility(main.BLOCKS, True)
             else:
-                self.main_window.btwin.hide()
+                main.update_panel_visibility(main.BLOCKS, False)
             action.save_to_preferences()
         elif action == Actions.TOGGLE_SCROLL_LOCK:
             active = action.get_active()
-            self.main_window.text_display.scroll_lock = active
+            main.text_display.scroll_lock = active
             if active:
-                self.main_window.text_display.scroll_to_end()
+                main.text_display.scroll_to_end()
             action.save_to_preferences()
         elif action == Actions.CLEAR_REPORTS:
-            self.main_window.text_display.clear()
+            main.text_display.clear()
         elif action == Actions.SAVE_REPORTS:
             file_path = SaveReportsFileDialog(page.get_file_path()).run()
             if file_path is not None:
-                self.main_window.text_display.save(file_path)
+                main.text_display.save(file_path)
         elif action == Actions.TOGGLE_HIDE_DISABLED_BLOCKS:
             Actions.NOTHING_SELECT()
         elif action == Actions.TOGGLE_AUTO_HIDE_PORT_LABELS:
             action.save_to_preferences()
-            for page in self.main_window.get_pages():
+            for page in main.get_pages():
                 page.get_flow_graph().create_shapes()
         elif action in (Actions.TOGGLE_SNAP_TO_GRID,
                         Actions.TOGGLE_SHOW_BLOCK_COMMENTS,
@@ -424,11 +430,40 @@ class ActionHandler:
             action.save_to_preferences()
         elif action == Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY:
             action.save_to_preferences()
-            for page in self.main_window.get_pages():
+            for page in main.get_pages():
                 flow_graph_update(page.get_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()
             Actions.NOTHING_SELECT()
             action.save_to_preferences()
+        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 not action.get_sensitive():
+                    # This is occurring after variables are un-hidden
+                    # Leave it enabled
+                    action.set_sensitive(True)
+                    action.set_active(True)
+                elif action.get_active():
+                    main.update_panel_visibility(main.VARIABLES, True)
+                else:
+                    main.update_panel_visibility(main.VARIABLES, False)
+            
Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR.set_sensitive(action.get_active())
+            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.run()
+                md.destroy()
+                action.save_to_preferences()
         ##################################################
         # Param Modifications
         ##################################################
@@ -461,6 +496,10 @@ class ActionHandler:
             if self.dialog is not None:
                 self.dialog.update_gui(force=True)
             page.set_saved(False)
+        elif action == Actions.VARIABLE_EDITOR_UPDATE:
+            page.get_state_cache().save_new_state(flow_graph.export_data())
+            flow_graph_update()
+            page.set_saved(False)
         ##################################################
         # View Parser Errors
         ##################################################
@@ -487,7 +526,7 @@ class ActionHandler:
         # New/Open/Save/Close
         ##################################################
         elif action == Actions.FLOW_GRAPH_NEW:
-            self.main_window.new_page()
+            main.new_page()
             if args:
                 
flow_graph._options_block.get_param('generate_options').set_value(args[0])
                 flow_graph_update()
@@ -495,10 +534,11 @@ class ActionHandler:
             file_paths = args if args else 
OpenFlowGraphFileDialog(page.get_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):
-                    self.main_window.new_page(file_path, show=(i==0))
+                    main.new_page(file_path, show=(i==0))
                     Preferences.add_recent_file(file_path)
-                    self.main_window.tool_bar.refresh_submenus()
-                    self.main_window.menu_bar.refresh_submenus()
+                    main.tool_bar.refresh_submenus()
+                    main.menu_bar.refresh_submenus()
+                    main.vars.update_gui()
 
         elif action == Actions.FLOW_GRAPH_OPEN_QSS_THEME:
             file_paths = OpenQSSFileDialog(self.platform.config.install_prefix 
+
@@ -511,7 +551,7 @@ class ActionHandler:
                 except Exception as e:
                     Messages.send("Failed to save QSS preference: " + str(e))
         elif action == Actions.FLOW_GRAPH_CLOSE:
-            self.main_window.close_page()
+            main.close_page()
         elif action == Actions.FLOW_GRAPH_SAVE:
             #read-only or undefined file path, do save-as
             if page.get_read_only() or not page.get_file_path():
@@ -531,8 +571,8 @@ class ActionHandler:
                 page.set_file_path(file_path)
                 Actions.FLOW_GRAPH_SAVE()
                 Preferences.add_recent_file(file_path)
-                self.main_window.tool_bar.refresh_submenus()
-                self.main_window.menu_bar.refresh_submenus()
+                main.tool_bar.refresh_submenus()
+                main.menu_bar.refresh_submenus()
         elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE:
             file_path, background_transparent = 
SaveScreenShotDialog(page.get_file_path()).run()
             if file_path is not None:
@@ -572,22 +612,22 @@ class ActionHandler:
             pass
         elif action == Actions.RELOAD_BLOCKS:
             self.platform.load_blocks()
-            self.main_window.btwin.clear()
-            self.platform.load_block_tree(self.main_window.btwin)
+            main.btwin.clear()
+            self.platform.load_block_tree(main.btwin)
             Actions.XML_PARSER_ERRORS_DISPLAY.set_sensitive(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
-            self.main_window.update_pages()
+            main.update_pages()
 
         elif action == Actions.FIND_BLOCKS:
-            self.main_window.btwin.show()
-            self.main_window.btwin.search_entry.show()
-            self.main_window.btwin.search_entry.grab_focus()
+            main.update_panel_visibility(main.BLOCKS, True)
+            main.btwin.search_entry.show()
+            main.btwin.search_entry.grab_focus()
         elif action == Actions.OPEN_HIER:
             for b in flow_graph.get_selected_blocks():
                 if b._grc_source:
-                    self.main_window.new_page(b._grc_source, show=True)
+                    main.new_page(b._grc_source, show=True)
         elif action == Actions.BUSSIFY_SOURCES:
             n = {'name':'bus', 'type':'bus'}
             for b in flow_graph.get_selected_blocks():
@@ -613,7 +653,7 @@ class ActionHandler:
         ##################################################
         # Global Actions for all States
         ##################################################
-        page = self.main_window.get_page()  # page and flowgraph might have 
changed
+        page = main.get_page()  # page and flowgraph might have changed
         flow_graph = page.get_flow_graph() if page else None
 
         selected_blocks = flow_graph.get_selected_blocks()
@@ -654,7 +694,7 @@ class ActionHandler:
         self.update_exec_stop()
         #saved status
         Actions.FLOW_GRAPH_SAVE.set_sensitive(not page.get_saved())
-        self.main_window.update()
+        main.update()
         try: #set the size of the flow graph area (if changed)
             new_size = (flow_graph.get_option('window_size') or
                         self.platform.config.default_canvas_size)
diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py
index e267029..d96e80c 100644
--- a/grc/gui/Actions.py
+++ b/grc/gui/Actions.py
@@ -173,6 +173,7 @@ class ToggleAction(gtk.ToggleAction, _ActionBase):
 ########################################################################
 PAGE_CHANGE = Action()
 EXTERNAL_UPDATE = Action()
+VARIABLE_EDITOR_UPDATE = Action()
 FLOW_GRAPH_NEW = Action(
     label='_New',
     tooltip='Create a new flow graph',
@@ -335,6 +336,20 @@ TOGGLE_HIDE_VARIABLES = ToggleAction(
     preference_name='hide_variables',
     default=False,
 )
+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,
+    default=True,
+    keypresses=(gtk.keysyms.e, gtk.gdk.CONTROL_MASK),
+    preference_name='variable_editor_visable',
+)
+TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR = ToggleAction(
+    label='Move 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(
     label='Auto-Hide _Port Labels',
     tooltip='Automatically hide port labels',
diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py
index c0c4bee..5bbbdc0 100644
--- a/grc/gui/Bars.py
+++ b/grc/gui/Bars.py
@@ -31,6 +31,7 @@ TOOLBAR_LIST = (
     Actions.FLOW_GRAPH_SAVE,
     Actions.FLOW_GRAPH_CLOSE,
     None,
+    Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR,
     Actions.FLOW_GRAPH_SCREEN_CAPTURE,
     None,
     Actions.BLOCK_CUT,
@@ -103,6 +104,9 @@ MENU_BAR_LIST = (
         Actions.CLEAR_REPORTS,
         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,
diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py
index ff77058..0b6bb74 100644
--- a/grc/gui/MainWindow.py
+++ b/grc/gui/MainWindow.py
@@ -23,6 +23,7 @@ import gtk
 
 from . import Bars, Actions, Preferences, Utils
 from .BlockTreeWindow import BlockTreeWindow
+from .VariableEditor import VariableEditor
 from .Constants import \
     NEW_FLOGRAPH_TITLE, DEFAULT_REPORTS_WINDOW_WIDTH
 from .Dialogs import TextDisplay, MessageDialogHelper
@@ -56,6 +57,7 @@ PAGE_TITLE_MARKUP_TMPL = """\
 #end if
 """
 
+
 ############################################################
 # Main window
 ############################################################
@@ -63,6 +65,11 @@ PAGE_TITLE_MARKUP_TMPL = """\
 class MainWindow(gtk.Window):
     """The topmost window with menus, the tool bar, and other major windows."""
 
+    # Constants the action handler can use to indicate which panel visibility 
to change.
+    BLOCKS = 0
+    REPORTS = 1
+    VARIABLES = 2
+
     def __init__(self, platform, action_handler_callback):
         """
         MainWindow contructor
@@ -76,48 +83,80 @@ class MainWindow(gtk.Window):
             (o.get_key(), o.get_name(), o.get_key() == generate_mode_default)
             for o in gen_opts.get_options()]
 
-        # load preferences
+        # Load preferences
         Preferences.load(platform)
-        #setup window
+
+        # Setup window
         gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
         vbox = gtk.VBox()
-        self.hpaned = gtk.HPaned()
         self.add(vbox)
-        #create the menu bar and toolbar
+
+        # Create the menu bar and toolbar
         self.add_accel_group(Actions.get_accel_group())
         self.menu_bar = Bars.MenuBar(generate_modes, action_handler_callback)
         vbox.pack_start(self.menu_bar, False)
-        self.tool_bar = Bars.Toolbar(generate_modes, action_handler_callback )
+        self.tool_bar = Bars.Toolbar(generate_modes, action_handler_callback)
         vbox.pack_start(self.tool_bar, False)
-        vbox.pack_start(self.hpaned)
-        #create the notebook
+
+        # Main parent container for the different panels
+        self.container = gtk.HPaned()
+        vbox.pack_start(self.container)
+
+        # Create the notebook
         self.notebook = gtk.Notebook()
         self.page_to_be_closed = None
         self.current_page = None
         self.notebook.set_show_border(False)
         self.notebook.set_scrollable(True) #scroll arrows for page tabs
         self.notebook.connect('switch-page', self._handle_page_change)
-        #setup containers
-        self.flow_graph_vpaned = gtk.VPaned()
-        #flow_graph_box.pack_start(self.scrolled_window)
-        self.flow_graph_vpaned.pack1(self.notebook)
-        self.hpaned.pack1(self.flow_graph_vpaned)
-        self.btwin = BlockTreeWindow(platform, self.get_flow_graph);
-        self.hpaned.pack2(self.btwin, False) #dont allow resize
-        #create the reports window
+
+        # Create the console window
         self.text_display = TextDisplay()
-        #house the reports in a scrolled window
         self.reports_scrolled_window = gtk.ScrolledWindow()
         self.reports_scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, 
gtk.POLICY_AUTOMATIC)
         self.reports_scrolled_window.add(self.text_display)
         self.reports_scrolled_window.set_size_request(-1, 
DEFAULT_REPORTS_WINDOW_WIDTH)
-        self.flow_graph_vpaned.pack2(self.reports_scrolled_window, False) 
#dont allow resize
+
+        # Create the block tree and variable panels
+        self.btwin = BlockTreeWindow(platform, self.get_flow_graph)
+        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.variable_panel_sidebar = Preferences.variable_editor_sidebar()
+        if self.variable_panel_sidebar:
+            self.left.pack1(self.notebook)
+            self.left.pack2(self.reports_scrolled_window, False)
+            self.right.pack1(self.btwin)
+            self.right.pack2(self.vars, False)
+        else:
+            # Put the variable editor in a panel with the reports
+            self.left.pack1(self.notebook)
+            self.left_subpanel.pack1(self.reports_scrolled_window, 
shrink=False)
+            self.left_subpanel.pack2(self.vars, resize=False, shrink=True)
+            self.left.pack2(self.left_subpanel, False)
+
+            # Create the right panel
+            self.right.pack1(self.btwin)
+
+        self.container.pack1(self.left)
+        self.container.pack2(self.right, False)
+
         #load preferences and show the main window
         self.resize(*Preferences.main_window_size())
-        
self.flow_graph_vpaned.set_position(Preferences.reports_window_position())
-        self.hpaned.set_position(Preferences.blocks_window_position())
+        self.container.set_position(Preferences.blocks_window_position())
+        self.left.set_position(Preferences.reports_window_position())
+        if self.variable_panel_sidebar:
+            
self.right.set_position(Preferences.variable_editor_position(sidebar=True))
+        else:
+            
self.left_subpanel.set_position(Preferences.variable_editor_position())
+
         self.show_all()
         self.reports_scrolled_window.hide()
+        self.vars.hide()
         self.btwin.hide()
 
     ############################################################
@@ -150,6 +189,38 @@ class MainWindow(gtk.Window):
         self.current_page = self.notebook.get_nth_page(page_num)
         Actions.PAGE_CHANGE()
 
+    def update_panel_visibility(self, panel, visibility=True):
+        """
+        Handles changing visibility of panels.
+        """
+        # Set the visibility for the requested panel, then update the 
containers if they need
+        #  to be hidden as well.
+
+        if panel == self.BLOCKS:
+            self.btwin.set_visible(visibility)
+        elif panel == self.REPORTS:
+            self.reports_scrolled_window.set_visible(visibility)
+        elif panel == self.VARIABLES:
+            self.vars.set_visible(visibility)
+        else:
+            return
+
+        if self.variable_panel_sidebar:
+            # If both the variable editor and block panels are hidden, hide 
the right container
+            if not self.btwin.get_visible() and not self.vars.get_visible():
+                self.right.hide()
+            else:
+                self.right.show()
+        else:
+            if not self.btwin.get_visible():
+                self.right.hide()
+            else:
+                self.right.show()
+            if not self.vars.get_visible() and not 
self.reports_scrolled_window.get_visible():
+                self.left_subpanel.hide()
+            else:
+                self.left_subpanel.show()
+
     ############################################################
     # Report Window
     ############################################################
@@ -226,8 +297,12 @@ class MainWindow(gtk.Window):
         Preferences.set_open_files(open_files)
         Preferences.file_open(open_file)
         Preferences.main_window_size(self.get_size())
-        
Preferences.reports_window_position(self.flow_graph_vpaned.get_position())
-        Preferences.blocks_window_position(self.hpaned.get_position())
+        Preferences.reports_window_position(self.left.get_position())
+        Preferences.blocks_window_position(self.container.get_position())
+        if self.variable_panel_sidebar:
+            Preferences.variable_editor_position(self.right.get_position(), 
sidebar=True)
+        else:
+            
Preferences.variable_editor_position(self.left_subpanel.get_position())
         Preferences.save()
         return True
 
@@ -297,6 +372,9 @@ class MainWindow(gtk.Window):
         #show/hide notebook tabs
         self.notebook.set_show_tabs(len(self.get_pages()) > 1)
 
+         # Need to update the variable window when changing
+        self.vars.update_gui()
+
     def update_pages(self):
         """
         Forces a reload of all the pages in this notebook.
diff --git a/grc/gui/Param.py b/grc/gui/Param.py
index bf0a59b..4b5a3c2 100644
--- a/grc/gui/Param.py
+++ b/grc/gui/Param.py
@@ -84,7 +84,7 @@ class InputParam(gtk.HBox):
         self._have_pending_changes = True
         self._update_gui()
         if self._editing_callback:
-            self._editing_callback()
+            self._editing_callback(self, None)
 
     def _apply_change(self, *args):
         """
@@ -95,7 +95,7 @@ class InputParam(gtk.HBox):
         self.param.set_value(self.get_text())
         #call the callback
         if self._changed_callback:
-            self._changed_callback(*args)
+            self._changed_callback(self, None)
         else:
             self.param.validate()
         #gui update
@@ -129,8 +129,19 @@ class EntryParam(InputParam):
         return self._input.get_text()
 
     def set_color(self, color):
-        self._input.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
-        self._input.modify_text(gtk.STATE_NORMAL, 
Colors.PARAM_ENTRY_TEXT_COLOR)
+        need_status_color = self.label not in self.get_children()
+        text_color = (
+            Colors.PARAM_ENTRY_TEXT_COLOR if not need_status_color else
+            gtk.gdk.color_parse('blue') if self._have_pending_changes else
+            gtk.gdk.color_parse('red') if not self.param.is_valid() else
+            Colors.PARAM_ENTRY_TEXT_COLOR)
+        base_color = (
+            Colors.BLOCK_DISABLED_COLOR
+            if need_status_color and not self.param.get_parent().get_enabled()
+            else gtk.gdk.color_parse(color)
+        )
+        self._input.modify_base(gtk.STATE_NORMAL, base_color)
+        self._input.modify_text(gtk.STATE_NORMAL, text_color)
 
     def set_tooltip_text(self, text):
         try:
diff --git a/grc/gui/Preferences.py b/grc/gui/Preferences.py
index 1a194fd..f74550c 100644
--- a/grc/gui/Preferences.py
+++ b/grc/gui/Preferences.py
@@ -148,6 +148,23 @@ 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')
 
diff --git a/grc/gui/VariableEditor.py b/grc/gui/VariableEditor.py
new file mode 100644
index 0000000..fcb1f4d
--- /dev/null
+++ b/grc/gui/VariableEditor.py
@@ -0,0 +1,283 @@
+"""
+Copyright 2015 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 itertools import chain
+from operator import attrgetter
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+import gobject
+
+from . import Actions
+from . import Preferences
+from .Constants import DEFAULT_BLOCKS_WINDOW_WIDTH
+
+BLOCK_INDEX = 0
+ID_INDEX = 1
+
+
+class VariableEditor(gtk.VBox):
+
+    # Actions that are handled by the editor
+    ADD_IMPORT = 0
+    ADD_VARIABLE = 1
+    OPEN_PROPERTIES = 2
+    DELETE_BLOCK = 3
+    DELETE_CONFIRM = 4
+    ENABLE_BLOCK = 5
+    DISABLE_BLOCK = 6
+
+    def __init__(self, platform, get_flow_graph):
+        gtk.VBox.__init__(self)
+        self.platform = platform
+        self.get_flow_graph = get_flow_graph
+        self._block = None
+
+        # 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.treeview.set_enable_search(True)
+        self.treeview.set_search_column(ID_INDEX)
+        self.treeview.get_selection().set_mode('single')
+        self.treeview.set_headers_visible(True)
+        self.treeview.connect('button-press-event', 
self._handle_mouse_button_press)
+        self.treeview.connect('key-press-event', self._handle_key_button_press)
+
+        # Block Name or Category
+        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.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_cell_data_func(self.id_cell, self.set_properties)
+        self.treeview.append_column(id_column)
+        self.treestore.set_sort_column_id(ID_INDEX, gtk.SORT_ASCENDING)
+
+        # Block Value
+        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.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_cell_data_func(self.value_cell, self.set_value)
+        self.treeview.append_column(value_column)
+
+        # Block Actions (Add, Remove)
+        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.add_with_viewport(self.treeview)
+        scrolled_window.set_size_request(DEFAULT_BLOCKS_WINDOW_WIDTH, -1)
+        self.pack_start(scrolled_window)
+
+        # Context menus
+        self._confirm_delete = Preferences.variable_editor_confirm_delete()
+
+    # Sets cell contents
+    def set_icon(self, col, cell, model, iter):
+        block = model.get_value(iter, BLOCK_INDEX)
+        if block:
+            pb = self.treeview.render_icon(gtk.STOCK_CLOSE, 
gtk.ICON_SIZE_MENU, None)
+        else:
+            pb = self.treeview.render_icon(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU, 
None)
+        cell.set_property('pixbuf', pb)
+
+    def set_value(self, col, cell, model, iter):
+        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)
+
+        # Set defaults
+        value = None
+        evaluated = None
+        self.set_tooltip_text(None)
+
+        # Block specific values
+        if block:
+            if block.get_key() == 'import':
+                value = block.get_param('import').get_value()
+            else:
+                if block.get_key() != "variable":
+                    value = "<Open Properties>"
+                    sp('editable', False)
+                else:
+                    value = block.get_param('value').get_value()
+
+            # Check if there are errors in the blocks.
+            # Show the block error as a tooltip
+            error_message = block.get_error_messages()
+            if len(error_message) > 0:
+                # Set the error message to the last error in the list.
+                # This should be the first message generated
+                self.set_tooltip_text(error_message[-1])
+            else:
+                # Evaluate and show the value (if it is a variable)
+                if block.get_key() == "variable":
+                    evaluated = str(block.get_param('value').evaluate())
+                    self.set_tooltip_text(evaluated)
+        # Always set the text value.
+        sp('text', value)
+
+    def set_properties(self, col, cell, model, iter):
+        sp = cell.set_property
+        block = model.get_value(iter, BLOCK_INDEX)
+        # Set defaults
+        sp('sensitive', True)
+        sp('editable', False)
+        sp('foreground', None)
+
+        # Block specific changes
+        if block:
+            if not block.get_enabled():
+                # Disabled block. But, this should still be editable
+                sp('editable', True)
+                sp('foreground', 'gray')
+            else:
+                sp('editable', True)
+            if block.get_error_messages():
+                sp('foreground', 'red')
+
+    def update_gui(self):
+        if not self.get_flow_graph():
+            return
+        self._update_blocks()
+        self._rebuild()
+        self.treeview.expand_all()
+
+    def _update_blocks(self):
+        self._imports = filter(attrgetter('is_import'),
+                               self.get_flow_graph().blocks)
+        self._variables = filter(attrgetter('is_variable'),
+                                 self.get_flow_graph().blocks)
+
+    def _rebuild(self, *args):
+        self.treestore.clear()
+        imports = self.treestore.append(None, [None, 'Imports'])
+        variables = self.treestore.append(None, [None, 'Variables'])
+        for block in self._imports:
+            self.treestore.append(imports, [block, 
block.get_param('id').get_value()])
+        for block in sorted(self._variables, key=lambda v: v.get_id()):
+            self.treestore.append(variables, [block, 
block.get_param('id').get_value()])
+
+    def _handle_name_edited_cb(self, cell, path, new_text):
+        block = self.treestore[path][BLOCK_INDEX]
+        block.get_param('id').set_value(new_text)
+        Actions.VARIABLE_EDITOR_UPDATE()
+
+    def _handle_value_edited_cb(self, cell, path, new_text):
+        block = self.treestore[path][BLOCK_INDEX]
+        if block.is_import:
+            block.get_param('import').set_value(new_text)
+        else:
+            block.get_param('value').set_value(new_text)
+        Actions.VARIABLE_EDITOR_UPDATE()
+
+    def _handle_action(self, item, key, event=None):
+        """
+        Single handler for the different actions that can be triggered by the 
context menu,
+        key presses or mouse clicks. Also triggers an update of the flowgraph 
and editor.
+        """
+        if key == self.ADD_IMPORT:
+            self.get_flow_graph().add_new_block('import')
+        elif key == self.ADD_VARIABLE:
+            self.get_flow_graph().add_new_block('variable')
+        elif key == self.OPEN_PROPERTIES:
+            Actions.BLOCK_PARAM_MODIFY(self._block)
+        elif key == self.DELETE_BLOCK:
+            self.get_flow_graph().remove_element(self._block)
+        elif key == self.DELETE_CONFIRM:
+            if self._confirm_delete:
+                # Create a context menu to confirm the delete operation
+                confirmation_menu = gtk.Menu()
+                block_id = 
self._block.get_param('id').get_value().replace("_", "__")
+                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)
+            else:
+                self._handle_action(None, self.DELETE_BLOCK, None)
+        elif key == self.ENABLE_BLOCK:
+            self._block.set_enabled(True)
+        elif key == self.DISABLE_BLOCK:
+            self._block.set_enabled(False)
+        Actions.VARIABLE_EDITOR_UPDATE()
+
+    def _handle_mouse_button_press(self, widget, event):
+        """
+        Handles mouse button for several different events:
+        - Doublc Click to open properties for advanced blocks
+        - Click to add/remove blocks
+        """
+        path = widget.get_path_at_pos(int(event.x), int(event.y))
+        if path:
+            # If there is a valid path, then get the row, column and block 
selected.
+            row = self.treestore[path[0]]
+            col = path[1]
+            self._block = row[BLOCK_INDEX]
+
+            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:
+                    # 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:
+                    # 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]:
+                        if row[1] == "Imports":
+                            # Add a new import block.
+                            self._handle_action(None, self.ADD_IMPORT, 
event=event)
+                        elif row[1] == "Variables":
+                            # Add a new variable block
+                            self._handle_action(None, self.ADD_VARIABLE, 
event=event)
+                        else:
+                            self._handle_action(None, self.DELETE_CONFIRM, 
event=event)
+                        return True
+        return False
+
+    def _handle_key_button_press(self, widget, event):
+        model, path = self.treeview.get_selection().get_selected_rows()
+        if path and self._block:
+            if self._block.get_enabled() and event.string == "d":
+                self._handle_action(None, self.DISABLE_BLOCK, None)
+                return True
+            elif not self._block.get_enabled() and event.string == "e":
+                self._handle_action(None, self.ENABLE_BLOCK, None)
+                return True
+        return False



reply via email to

[Prev in Thread] Current Thread [Next in Thread]