[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
- [Commit-gnuradio] [gnuradio] 12/25: grc: add warning for block flagged deprecated, (continued)
- [Commit-gnuradio] [gnuradio] 12/25: grc: add warning for block flagged deprecated, git, 2016/05/27
- [Commit-gnuradio] [gnuradio] 20/25: grc: faulty callback code if setter call contained a variable block id, git, 2016/05/27
- [Commit-gnuradio] [gnuradio] 14/25: Revert "grc: fix callback evaluation", git, 2016/05/27
- [Commit-gnuradio] [gnuradio] 18/25: modtool: Added version support for OOTs, git, 2016/05/27
- [Commit-gnuradio] [gnuradio] 19/25: grc-refactor: minor clean-up of callback generator code, git, 2016/05/27
- [Commit-gnuradio] [gnuradio] 15/25: grc: fix flow graph execution (amends ActionHandler refactoring), git, 2016/05/27
- [Commit-gnuradio] [gnuradio] 10/25: grc: Variable editor tweaks., git, 2016/05/27
- [Commit-gnuradio] [gnuradio] 25/25: Merge remote-tracking branch 'dkozel/uhd_lo_api', git, 2016/05/27
- [Commit-gnuradio] [gnuradio] 16/25: Added controls for importing, exporting, and sharing LOs, git, 2016/05/27
- [Commit-gnuradio] [gnuradio] 17/25: grc-refactor: fix custom canvas font sizes, git, 2016/05/27
- [Commit-gnuradio] [gnuradio] 08/25: grc: Added the variable editor panel,
git <=
- [Commit-gnuradio] [gnuradio] 09/25: grc: Added context menu to variable editor, git, 2016/05/27
- [Commit-gnuradio] [gnuradio] 06/25: grc: remove some dead code in FlowGraph.what_is_selected(), git, 2016/05/27
- [Commit-gnuradio] [gnuradio] 23/25: Merge remote-tracking branch 'mbr0wn/modtool/versioning', git, 2016/05/27
- [Commit-gnuradio] [gnuradio] 02/25: grc: block alignment tools, git, 2016/05/27
- [Commit-gnuradio] [gnuradio] 03/25: grc: minor fixes/clean-ups, git, 2016/05/27
- [Commit-gnuradio] [gnuradio] 05/25: grc: some action handler refactoring, add flow_graph.update() wrapper, git, 2016/05/27