summaryrefslogtreecommitdiff
path: root/grc
diff options
context:
space:
mode:
authorJohnathan Corgan <johnathan@corganlabs.com>2015-10-03 12:01:29 -0700
committerJohnathan Corgan <johnathan@corganlabs.com>2015-10-03 12:01:29 -0700
commit73ae366a6a4d97a9d44a711a8f404c8a29bd31c9 (patch)
treeaba3858dea02bd0898d3efc66b4e47488d37edfe /grc
parent779fb8fdb5bb2950efcf2a8befcb6733cb6d839d (diff)
parent0f9b29ae4cbb2e3e46abea3ab2475b784d36dab8 (diff)
Merge branch 'master' into next
Diffstat (limited to 'grc')
-rw-r--r--grc/base/FlowGraph.py54
-rw-r--r--grc/blocks/options.xml31
-rw-r--r--grc/gui/ActionHandler.py65
-rw-r--r--grc/gui/Actions.py74
-rw-r--r--grc/gui/Bars.py115
-rw-r--r--grc/gui/Block.py44
-rw-r--r--grc/gui/Constants.py3
-rw-r--r--grc/gui/FileDialogs.py15
-rw-r--r--grc/gui/FlowGraph.py2
-rw-r--r--grc/gui/MainWindow.py12
-rw-r--r--grc/gui/Param.py11
-rw-r--r--grc/python/Constants.py11
-rw-r--r--grc/python/Generator.py14
-rw-r--r--grc/python/Param.py40
-rw-r--r--grc/python/flow_graph.tmpl66
15 files changed, 395 insertions, 162 deletions
diff --git a/grc/base/FlowGraph.py b/grc/base/FlowGraph.py
index 217c4d7302..f61542c712 100644
--- a/grc/base/FlowGraph.py
+++ b/grc/base/FlowGraph.py
@@ -67,6 +67,58 @@ class FlowGraph(Element):
def __str__(self):
return 'FlowGraph - %s(%s)' % (self.get_option('title'), self.get_option('id'))
+ def get_complexity(self):
+ """
+ Determines the complexity of a flowgraph
+ """
+ dbal = 0
+ block_list = self.get_blocks()
+ for block in block_list:
+ # Skip options block
+ if block.get_key() == 'options':
+ 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())
+ sinks = float(len(sink_list))
+ sources = float(len(source_list))
+ base = max(min(sinks, sources), 1)
+
+ # Port ratio multiplier
+ if min(sinks, sources) > 0:
+ multi = sinks / sources
+ multi = (1 / multi) if multi > 1 else multi
+ else:
+ 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)
+
+ elements = float(len(self.get_elements()))
+ connections = float(len(self.get_connections()))
+ disabled_connections = len(filter(lambda c: not c.get_enabled(), self.get_connections()))
+ blocks = float(len(block_list))
+ variables = elements - blocks - connections
+ enabled = float(len(self.get_enabled_blocks()))
+
+ # Disabled multiplier
+ if enabled > 0:
+ disabled_multi = 1 / (max(1 - ((blocks - enabled) / max(blocks, 1)), 0.05))
+ else:
+ disabled_multi = 1
+
+ # Connection multiplier (How many connections )
+ if (connections - disabled_connections) > 0:
+ conn_multi = 1 / (max(1 - (disabled_connections / max(connections, 1)), 0.05))
+ else:
+ conn_multi = 1
+
+ final = round(max((dbal - 1) * disabled_multi * conn_multi * connections, 0.0) / 1000000, 6)
+ return final
+
def rewrite(self):
def refactor_bus_structure():
@@ -102,7 +154,7 @@ class FlowGraph(Element):
get_p().append(port);
for child in self.get_children(): child.rewrite()
- refactor_bus_structure();
+ refactor_bus_structure()
def get_option(self, key):
"""
diff --git a/grc/blocks/options.xml b/grc/blocks/options.xml
index dc02b83c2a..09cc74d151 100644
--- a/grc/blocks/options.xml
+++ b/grc/blocks/options.xml
@@ -77,7 +77,7 @@ else: self.stop(); self.wait()</callback>
<key>hb</key>
</option>
<option>
- <name>QT GUI Hier Block</name>
+ <name>Hier Block (QT GUI)</name>
<key>hb_qt_gui</key>
</option>
</param>
@@ -163,6 +163,23 @@ part#slurp
</option>
</param>
<param>
+ <name>QSS Theme</name>
+ <key>qt_qss_theme</key>
+ <value></value>
+ <type>file_open</type>
+ <hide>
+#if $generate_options() in ('qt_gui',)
+ #if $qt_qss_theme()
+ none
+ #else
+ part
+ #end if
+#else
+ all
+#end if
+</hide>
+ </param>
+ <param>
<name>Thread-safe setters</name>
<key>thread_safe_setters</key>
<value></value>
@@ -178,6 +195,18 @@ part#slurp
</option>
<tab>Advanced</tab>
</param>
+ <param>
+ <name>Run Command</name>
+ <key>run_command</key>
+ <value>{python} -u {filename}</value>
+ <type>string</type>
+ <hide>#if $generate_options().startswith('hb')
+all#slurp
+#else
+part#slurp
+#end if</hide>
+ <tab>Advanced</tab>
+ </param>
<check>not $window_size or len($window_size) == 2</check>
<check>not $window_size or 300 &lt;= $(window_size)[0] &lt;= 4096</check>
<check>not $window_size or 300 &lt;= $(window_size)[1] &lt;= 4096</check>
diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py
index 19c6edc914..ee01595a33 100644
--- a/grc/gui/ActionHandler.py
+++ b/grc/gui/ActionHandler.py
@@ -34,8 +34,9 @@ from .ParserErrorsDialog import ParserErrorsDialog
from .MainWindow import MainWindow
from .PropsDialog import PropsDialog
from .FileDialogs import (OpenFlowGraphFileDialog, SaveFlowGraphFileDialog,
- SaveReportsFileDialog, SaveImageFileDialog)
-from .Constants import DEFAULT_CANVAS_SIZE, IMAGE_FILE_EXTENSION
+ SaveReportsFileDialog, SaveImageFileDialog,
+ OpenQSSFileDialog)
+from .Constants import DEFAULT_CANVAS_SIZE, IMAGE_FILE_EXTENSION, GR_PREFIX
gobject.threads_init()
@@ -60,7 +61,7 @@ class ActionHandler:
for action in Actions.get_all_actions(): action.connect('activate', self._handle_action)
#setup the main window
self.platform = platform;
- self.main_window = MainWindow(platform)
+ self.main_window = MainWindow(platform, self._handle_action)
self.main_window.connect('delete-event', self._quit)
self.main_window.connect('key-press-event', self._handle_key_press)
self.get_page = self.main_window.get_page
@@ -107,27 +108,12 @@ class ActionHandler:
Actions.APPLICATION_QUIT()
return True
- def _handle_action(self, action):
+ def _handle_action(self, action, *args):
#print action
##################################################
# Initialize/Quit
##################################################
if action == Actions.APPLICATION_INITIALIZE:
- for action in Actions.get_all_actions(): action.set_sensitive(False) #set all actions disabled
- #enable a select few actions
- for action in (
- Actions.APPLICATION_QUIT, Actions.FLOW_GRAPH_NEW,
- Actions.FLOW_GRAPH_OPEN, Actions.FLOW_GRAPH_SAVE_AS,
- 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_REPORTS_WINDOW, Actions.TOGGLE_HIDE_DISABLED_BLOCKS,
- Actions.TOOLS_RUN_FDESIGN, Actions.TOGGLE_SCROLL_LOCK,
- Actions.CLEAR_REPORTS, Actions.SAVE_REPORTS,
- Actions.TOGGLE_AUTO_HIDE_PORT_LABELS, Actions.TOGGLE_SNAP_TO_GRID,
- Actions.TOGGLE_SHOW_BLOCK_COMMENTS,
- Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB,
- ): action.set_sensitive(True)
if ParseXML.xml_failures:
Messages.send_xml_errors_if_any(ParseXML.xml_failures)
Actions.XML_PARSER_ERRORS_DISPLAY.set_sensitive(True)
@@ -142,15 +128,28 @@ class ActionHandler:
if not self.get_page(): self.main_window.new_page() #ensure that at least a blank page exists
self.main_window.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
for action in (
- Actions.TOGGLE_REPORTS_WINDOW,
- Actions.TOGGLE_BLOCKS_WINDOW,
- Actions.TOGGLE_AUTO_HIDE_PORT_LABELS,
- Actions.TOGGLE_SCROLL_LOCK,
- Actions.TOGGLE_SNAP_TO_GRID,
+ Actions.APPLICATION_QUIT, Actions.FLOW_GRAPH_NEW,
+ Actions.FLOW_GRAPH_OPEN, Actions.FLOW_GRAPH_SAVE_AS,
+ 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_REPORTS_WINDOW, Actions.TOGGLE_HIDE_DISABLED_BLOCKS,
+ Actions.TOOLS_RUN_FDESIGN, Actions.TOGGLE_SCROLL_LOCK,
+ Actions.CLEAR_REPORTS, Actions.SAVE_REPORTS,
+ Actions.TOGGLE_AUTO_HIDE_PORT_LABELS, Actions.TOGGLE_SNAP_TO_GRID,
Actions.TOGGLE_SHOW_BLOCK_COMMENTS,
Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB,
- ): action.load_from_preferences()
+ Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY,
+ Actions.FLOW_GRAPH_OPEN_QSS_THEME,
+ ):
+ action.set_sensitive(True)
+ if hasattr(action, 'load_from_preferences'):
+ action.load_from_preferences()
+
elif action == Actions.APPLICATION_QUIT:
if self.main_window.close_pages():
gtk.main_quit()
@@ -416,6 +415,10 @@ class ActionHandler:
action.save_to_preferences()
elif action == Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB:
action.save_to_preferences()
+ elif action == Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY:
+ action.save_to_preferences()
+ for page in self.main_window.get_pages():
+ page.get_flow_graph().update()
##################################################
# Param Modifications
##################################################
@@ -465,11 +468,23 @@ class ActionHandler:
##################################################
elif action == Actions.FLOW_GRAPH_NEW:
self.main_window.new_page()
+ if args:
+ self.get_flow_graph()._options_block.get_param('generate_options').set_value(args[0])
+ self.get_flow_graph().update()
elif action == Actions.FLOW_GRAPH_OPEN:
file_paths = OpenFlowGraphFileDialog(self.get_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))
+ elif action == Actions.FLOW_GRAPH_OPEN_QSS_THEME:
+ file_paths = OpenQSSFileDialog(GR_PREFIX + '/share/gnuradio/themes/').run()
+ if file_paths:
+ try:
+ from gnuradio import gr
+ gr.prefs().set_string("qtgui", "qss", file_paths[0])
+ gr.prefs().save()
+ except Exception as e:
+ Messages.send("Failed to save QSS preference: " + str(e))
elif action == Actions.FLOW_GRAPH_CLOSE:
self.main_window.close_page()
elif action == Actions.FLOW_GRAPH_SAVE:
diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py
index ce1f2cf873..20929344c0 100644
--- a/grc/gui/Actions.py
+++ b/grc/gui/Actions.py
@@ -31,6 +31,8 @@ NO_MODS_MASK = 0
_actions_keypress_dict = dict()
_keymap = gtk.gdk.keymap_get_default()
_used_mods_mask = NO_MODS_MASK
+
+
def handle_key_press(event):
"""
Call the action associated with the key press event.
@@ -43,21 +45,30 @@ def handle_key_press(event):
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
+ # extract the key value and the consumed modifiers
keyval, egroup, level, consumed = _keymap.translate_keyboard_state(
event.hardware_keycode, event.state, event.group)
- #get the modifier mask and ignore irrelevant modifiers
+ # get the modifier mask and ignore irrelevant modifiers
mod_mask = event.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
- return True #handled here
+ # 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
+
+
+def get_all_actions():
+ return _all_actions_list
_accel_group = gtk.AccelGroup()
-def get_accel_group(): return _accel_group
+
+
+def get_accel_group():
+ return _accel_group
class _ActionBase(object):
@@ -69,14 +80,15 @@ class _ActionBase(object):
_all_actions_list.append(self)
for i in range(len(keypresses)/2):
keyval, mod_mask = keypresses[i*2:(i+1)*2]
- #register this keypress
+ # register this keypress
if _actions_keypress_dict.has_key((keyval, mod_mask)):
- raise KeyError('keyval/mod_mask pair already registered "%s"'%str((keyval, mod_mask)))
+ raise KeyError('keyval/mod_mask pair already registered "%s"' % str((keyval, mod_mask)))
_actions_keypress_dict[(keyval, mod_mask)] = self
- #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()
+ # 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.accel_map_add_entry(accel_path, keyval, mod_mask)
@@ -86,10 +98,10 @@ 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.
"""
- try:
- import Actions
- return filter(lambda attr: getattr(Actions, attr) == self, dir(Actions))[0]
- except: return self.get_name()
+ for name, value in globals():
+ if value == self:
+ return value
+ return self.get_name()
def __repr__(self): return str(self)
@@ -115,12 +127,10 @@ class Action(gtk.Action, _ActionBase):
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
- gtk.Action.__init__(self,
- name=name, label=label,
- tooltip=tooltip, stock_id=stock_id,
- )
- #register this action
+ if name is None:
+ name = label
+ gtk.Action.__init__(self, name=name, label=label, tooltip=tooltip,
+ stock_id=stock_id)
_ActionBase.__init__(self, label, keypresses)
@@ -141,9 +151,8 @@ class ToggleAction(gtk.ToggleAction, _ActionBase):
"""
if name is None:
name = label
- gtk.ToggleAction.__init__(self,
- name=name, label=label, tooltip=tooltip, stock_id=stock_id,
- )
+ gtk.ToggleAction.__init__(self, name=name, label=label,
+ tooltip=tooltip, stock_id=stock_id)
_ActionBase.__init__(self, label, keypresses)
self.preference_name = preference_name
self.default = default
@@ -284,6 +293,12 @@ TOGGLE_SHOW_CODE_PREVIEW_TAB = ToggleAction(
preference_name='show_generated_code_tab',
default=False,
)
+TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY = ToggleAction(
+ label='Show Flowgraph Complexity',
+ tooltip="How many Balints is the flowgraph...",
+ preference_name='show_flowgraph_complexity',
+ default=False,
+)
BLOCK_CREATE_HIER = Action(
label='C_reate Hier',
tooltip='Create hier block from selected blocks',
@@ -424,6 +439,11 @@ XML_PARSER_ERRORS_DISPLAY = Action(
tooltip='View errors that occured while parsing XML files',
stock_id=gtk.STOCK_DIALOG_ERROR,
)
+FLOW_GRAPH_OPEN_QSS_THEME = Action(
+ label='Set Default QT GUI _Theme',
+ tooltip='Set a default QT Style Sheet file to use for QT GUI',
+ stock_id=gtk.STOCK_OPEN,
+)
TOOLS_RUN_FDESIGN = Action(
label='Filter Design Tool',
tooltip='Execute gr_filter_design',
diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py
index f0f8dac7fb..2ab5b2a712 100644
--- a/grc/gui/Bars.py
+++ b/grc/gui/Bars.py
@@ -17,12 +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 Actions
import pygtk
pygtk.require('2.0')
import gtk
-##The list of actions for the toolbar.
+from . import Actions
+
+
+# The list of actions for the toolbar.
TOOLBAR_LIST = (
Actions.FLOW_GRAPH_NEW,
Actions.FLOW_GRAPH_OPEN,
@@ -57,11 +59,10 @@ TOOLBAR_LIST = (
Actions.OPEN_HIER,
)
-##The list of actions and categories for the menu bar.
-
+# The list of actions and categories for the menu bar.
MENU_BAR_LIST = (
(gtk.Action('File', '_File', None, None), [
- Actions.FLOW_GRAPH_NEW,
+ 'flow_graph_new',
Actions.FLOW_GRAPH_OPEN,
None,
Actions.FLOW_GRAPH_SAVE,
@@ -115,6 +116,9 @@ MENU_BAR_LIST = (
]),
(gtk.Action('Tools', '_Tools', None, None), [
Actions.TOOLS_RUN_FDESIGN,
+ Actions.FLOW_GRAPH_OPEN_QSS_THEME,
+ None,
+ Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY,
None,
Actions.TOOLS_MORE_TO_COME,
]),
@@ -127,7 +131,7 @@ MENU_BAR_LIST = (
]),
)
-
+# The list of actions for the context menu.
CONTEXT_MENU_LIST = [
Actions.BLOCK_CUT,
Actions.BLOCK_COPY,
@@ -162,17 +166,46 @@ class Toolbar(gtk.Toolbar):
gtk.Toolbar.__init__(self)
self.set_style(gtk.TOOLBAR_ICONS)
for action in TOOLBAR_LIST:
- if action: #add a tool item
- self.add(action.create_tool_item())
- #this reset of the tooltip property is required (after creating the tool item) for the tooltip to show
+ if action: # add a tool item
+ 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'))
- else: self.add(gtk.SeparatorToolItem())
+ else:
+ item = gtk.SeparatorToolItem()
+ 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()
+ 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
-class MenuBar(gtk.MenuBar):
+
+class MenuBar(gtk.MenuBar, MenuHelperMixin):
"""The gtk menu bar with actions added from the menu bar list."""
- def __init__(self):
+ 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.
@@ -180,37 +213,37 @@ class MenuBar(gtk.MenuBar):
Add the submenu to the menu bar.
"""
gtk.MenuBar.__init__(self)
+ self.generate_modes = generate_modes
+ self.action_handler_callback = action_handler_callback
for main_action, actions in MENU_BAR_LIST:
- #create the main menu item
- main_menu_item = main_action.create_menu_item()
- self.append(main_menu_item)
- #create the menu
- main_menu = gtk.Menu()
- main_menu_item.set_submenu(main_menu)
- for action in actions:
- main_menu.append(action.create_menu_item() if action else
- gtk.SeparatorMenuItem())
- main_menu.show_all() #this show all is required for the separators to show
-
-
-class ContextMenu(gtk.Menu):
+ self.append(self._make_sub_menu(main_action, actions))
+
+ def create_flow_graph_new(self):
+ """Sub menu to create flow-graph with pre-set generate mode"""
+
+ def callback_adaptor(item, key):
+ """Sets original FLOW_GRAPH_NEW action as source"""
+ self.action_handler_callback(Actions.FLOW_GRAPH_NEW, key)
+
+ sub_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)
+ item.connect('activate', callback_adaptor, key)
+ sub_menu.append(item)
+ sub_menu.show_all()
+ main = gtk.ImageMenuItem(gtk.STOCK_NEW)
+ main.set_label(Actions.FLOW_GRAPH_NEW.get_label())
+ main.set_submenu(sub_menu)
+ return main
+
+
+class ContextMenu(gtk.Menu, MenuHelperMixin):
"""The gtk menu with actions added from the context menu list."""
def __init__(self):
gtk.Menu.__init__(self)
- for action in CONTEXT_MENU_LIST:
- if isinstance(action, tuple):
- action, sub_menu_action_list = action
- item = action.create_menu_item()
- self.append(item)
- sub_menu = gtk.Menu()
- item.set_submenu(sub_menu)
- for action in sub_menu_action_list:
- sub_menu.append(action.create_menu_item() if action else
- gtk.SeparatorMenuItem())
- sub_menu.show_all()
-
- else:
- self.append(action.create_menu_item() if action else
- gtk.SeparatorMenuItem())
- self.show_all()
+ self._fill_menu(CONTEXT_MENU_LIST, self)
diff --git a/grc/gui/Block.py b/grc/gui/Block.py
index 11273a537b..7350e4bdfe 100644
--- a/grc/gui/Block.py
+++ b/grc/gui/Block.py
@@ -21,6 +21,7 @@ from Element import Element
import Utils
import Colors
from .. base import odict
+from .. python.Param import num_to_str
from Constants import BORDER_PROXIMITY_SENSITIVITY
from Constants import (
BLOCK_LABEL_PADDING, PORT_SPACING, PORT_SEPARATION, LABEL_SEPARATION,
@@ -35,9 +36,17 @@ import pango
BLOCK_MARKUP_TMPL="""\
#set $foreground = $block.is_valid() and 'black' or 'red'
<span foreground="$foreground" font_desc="$font"><b>$encode($block.get_name())</b></span>"""
-COMMENT_MARKUP_TMPL="""\
+
+# Includes the additional complexity markup if enabled
+COMMENT_COMPLEXITY_MARKUP_TMPL="""\
#set $foreground = $block.get_enabled() and '#444' or '#888'
-<span foreground="$foreground" font_desc="$font">$encode($block.get_comment())</span>"""
+#if $complexity
+<span foreground="#444" size="medium" font_desc="$font"><b>$encode($complexity)</b></span>
+#end if
+#if $comment
+<span foreground="$foreground" font_desc="$font">$encode($comment)</span>#slurp
+#end if"""
+
class Block(Element):
"""The graphical signal block."""
@@ -218,16 +227,31 @@ class Block(Element):
self.create_comment_label()
def create_comment_label(self):
- comment = self.get_comment()
- if comment:
- layout = gtk.DrawingArea().create_pango_layout('')
- layout.set_markup(Utils.parse_template(COMMENT_MARKUP_TMPL, block=self, font=BLOCK_FONT))
- width, height = layout.get_pixel_size()
- pixmap = self.get_parent().new_pixmap(width, height)
+ comment = self.get_comment() # Returns None if there are no comments
+ complexity = None
+
+ # Show the flowgraph complexity on the top block if enabled
+ if Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY.get_active() and self.get_key() == "options":
+ complexity = "Complexity: {}bal".format(num_to_str(self.get_parent().get_complexity()))
+
+ layout = gtk.DrawingArea().create_pango_layout('')
+ layout.set_markup(Utils.parse_template(COMMENT_COMPLEXITY_MARKUP_TMPL,
+ block=self,
+ comment=comment,
+ complexity=complexity,
+ font=BLOCK_FONT))
+
+ # Setup the pixel map. Make sure that layout not empty
+ width, height = layout.get_pixel_size()
+ if width and height:
+ padding = BLOCK_LABEL_PADDING
+ pixmap = self.get_parent().new_pixmap(width + 2 * padding,
+ height + 2 * padding)
gc = pixmap.new_gc()
gc.set_foreground(Colors.COMMENT_BACKGROUND_COLOR)
- pixmap.draw_rectangle(gc, True, 0, 0, width, height)
- pixmap.draw_layout(gc, 0, 0, layout)
+ pixmap.draw_rectangle(
+ gc, True, 0, 0, width + 2 * padding, height + 2 * padding)
+ pixmap.draw_layout(gc, padding, padding, layout)
self._comment_pixmap = pixmap
else:
self._comment_pixmap = None
diff --git a/grc/gui/Constants.py b/grc/gui/Constants.py
index a8395f631e..980396f85d 100644
--- a/grc/gui/Constants.py
+++ b/grc/gui/Constants.py
@@ -26,7 +26,7 @@ import sys
from gnuradio import gr
_gr_prefs = gr.prefs()
-
+GR_PREFIX = gr.prefix()
# default path for the open/save dialogs
DEFAULT_FILE_PATH = os.getcwd()
@@ -118,4 +118,3 @@ SCROLL_DISTANCE = 15
# How close the mouse click can be to a line and register a connection select.
LINE_SELECT_SENSITIVITY = 5
-
diff --git a/grc/gui/FileDialogs.py b/grc/gui/FileDialogs.py
index 96cbd94f20..730ac6fba0 100644
--- a/grc/gui/FileDialogs.py
+++ b/grc/gui/FileDialogs.py
@@ -35,6 +35,7 @@ OPEN_FLOW_GRAPH = 'open flow graph'
SAVE_FLOW_GRAPH = 'save flow graph'
SAVE_REPORTS = 'save reports'
SAVE_IMAGE = 'save image'
+OPEN_QSS_THEME = 'open qss theme'
FILE_OVERWRITE_MARKUP_TMPL="""\
File <b>$encode($filename)</b> Exists!\nWould you like to overwrite the existing file?"""
@@ -72,6 +73,13 @@ def get_all_files_filter():
filter.add_pattern('*')
return filter
+##the filter for qss files
+def get_qss_themes_filter():
+ filter = gtk.FileFilter()
+ filter.set_name('QSS Themes')
+ filter.add_pattern('*.qss')
+ return filter
+
##################################################
# File Dialogs
##################################################
@@ -126,6 +134,10 @@ class FileDialog(FileDialogHelper):
self.add_and_set_filter(get_image_files_filter())
current_file_path = current_file_path + IMAGE_FILE_EXTENSION
self.set_current_name(path.basename(current_file_path)) #show the current filename
+ elif self.type == OPEN_QSS_THEME:
+ FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_OPEN, 'Open a QSS theme...')
+ self.add_and_set_filter(get_qss_themes_filter())
+ self.set_select_multiple(False)
self.set_current_folder(path.dirname(current_file_path)) #current directory
def add_and_set_filter(self, filter):
@@ -171,7 +183,7 @@ class FileDialog(FileDialogHelper):
#############################################
# Handle Open Dialogs
#############################################
- elif self.type in (OPEN_FLOW_GRAPH,):
+ elif self.type in (OPEN_FLOW_GRAPH, OPEN_QSS_THEME):
filenames = self.get_filenames()
for filename in filenames:
if not path.exists(filename): #show a warning and re-run
@@ -195,5 +207,6 @@ class FileDialog(FileDialogHelper):
class OpenFlowGraphFileDialog(FileDialog): type = OPEN_FLOW_GRAPH
class SaveFlowGraphFileDialog(FileDialog): type = SAVE_FLOW_GRAPH
+class OpenQSSFileDialog(FileDialog): type = OPEN_QSS_THEME
class SaveReportsFileDialog(FileDialog): type = SAVE_REPORTS
class SaveImageFileDialog(FileDialog): type = SAVE_IMAGE
diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py
index b1e88aae8e..fc6a711572 100644
--- a/grc/gui/FlowGraph.py
+++ b/grc/gui/FlowGraph.py
@@ -285,10 +285,12 @@ class FlowGraph(Element):
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)
+
# draw comments first
if Actions.TOGGLE_SHOW_BLOCK_COMMENTS.get_active():
for block in self.iter_blocks():
diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py
index 07d0661e94..f658a85062 100644
--- a/grc/gui/MainWindow.py
+++ b/grc/gui/MainWindow.py
@@ -65,12 +65,17 @@ PAGE_TITLE_MARKUP_TMPL = """\
class MainWindow(gtk.Window):
"""The topmost window with menus, the tool bar, and other major windows."""
- def __init__(self, platform):
+ def __init__(self, platform, action_handler_callback):
"""
MainWindow contructor
Setup the menu, toolbar, flowgraph editor notebook, block selection window...
"""
self._platform = platform
+ gen_opts = platform.get_block('options').get_param('generate_options')
+ generate_mode_default = gen_opts.get_value()
+ generate_modes = [
+ (o.get_key(), o.get_name(), o.get_key() == generate_mode_default)
+ for o in gen_opts.get_options()]
#setup window
gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
vbox = gtk.VBox()
@@ -78,7 +83,8 @@ class MainWindow(gtk.Window):
self.add(vbox)
#create the menu bar and toolbar
self.add_accel_group(Actions.get_accel_group())
- vbox.pack_start(Bars.MenuBar(), False)
+ menu_bar = Bars.MenuBar(generate_modes, action_handler_callback)
+ vbox.pack_start(menu_bar, False)
vbox.pack_start(Bars.Toolbar(), False)
vbox.pack_start(self.hpaned)
#create the notebook
@@ -177,7 +183,7 @@ class MainWindow(gtk.Window):
try: #try to load from file
if file_path: Messages.send_start_load(file_path)
flow_graph = self._platform.get_new_flow_graph()
- flow_graph.grc_file_path = file_path;
+ flow_graph.grc_file_path = file_path
#print flow_graph
page = NotebookPage(
self,
diff --git a/grc/gui/Param.py b/grc/gui/Param.py
index b9436ab06e..ca0a8c60e5 100644
--- a/grc/gui/Param.py
+++ b/grc/gui/Param.py
@@ -19,7 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
import Utils
from Element import Element
-from . Constants import PARAM_FONT
+from . Constants import PARAM_FONT, GR_PREFIX
import pygtk
pygtk.require('2.0')
import gtk
@@ -233,7 +233,14 @@ class FileParam(EntryParam):
#get the paths
file_path = self.param.is_valid() and self.param.get_evaluated() or ''
(dirname, basename) = os.path.isfile(file_path) and os.path.split(file_path) or (file_path, '')
- if not os.path.exists(dirname): dirname = os.getcwd() #fix bad paths
+ # check for qss theme default directory
+ if self.param.get_key() == 'qt_qss_theme':
+ dirname = os.path.dirname(dirname) # trim filename
+ if not os.path.exists(dirname):
+ dirname = os.path.join(GR_PREFIX, '/share/gnuradio/themes')
+ if not os.path.exists(dirname):
+ dirname = os.getcwd() # fix bad paths
+
#build the dialog
if self.param.get_type() == 'file_open':
file_dialog = gtk.FileChooserDialog('Open a Data File...', None,
diff --git a/grc/python/Constants.py b/grc/python/Constants.py
index 02be22a441..b7a370cad7 100644
--- a/grc/python/Constants.py
+++ b/grc/python/Constants.py
@@ -19,6 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
import os
from os.path import expanduser
+import numpy
import stat
from gnuradio import gr
@@ -53,6 +54,16 @@ FLOW_GRAPH_TEMPLATE = os.path.join(DATA_DIR, 'flow_graph.tmpl')
BLOCK_DTD = os.path.join(DATA_DIR, 'block.dtd')
DEFAULT_FLOW_GRAPH = os.path.join(DATA_DIR, 'default_flow_graph.grc')
+#define types, native python + numpy
+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,
+ 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)
+REAL_TYPES = tuple(REAL_TYPES + INT_TYPES)
+INT_TYPES = tuple(INT_TYPES)
# Updating colors. Using the standard color pallette from:
# http://www.google.com/design/spec/style/color.html#color-color-palette
diff --git a/grc/python/Generator.py b/grc/python/Generator.py
index 3c687a2d64..d48be2f00e 100644
--- a/grc/python/Generator.py
+++ b/grc/python/Generator.py
@@ -21,6 +21,7 @@ import os
import sys
import subprocess
import tempfile
+import shlex
from distutils.spawn import find_executable
from Cheetah.Template import Template
@@ -120,20 +121,13 @@ class TopBlockGenerator(object):
Returns:
a popen object
"""
- # extract the path to the python executable
- python_exe = sys.executable
-
- # when using wx gui on mac os, execute with pythonw
- # using pythonw is not necessary anymore, disabled below
- # if self._generate_options == 'wx_gui' and 'darwin' in sys.platform.lower():
- # python_exe = 'pythonw'
-
def args_to_string(args):
"""Accounts for spaces in args"""
return ' '.join(repr(arg) if ' ' in arg else arg for arg in args)
- # setup the command args to run
- cmds = [python_exe, '-u', self.get_file_path()] # -u is unbuffered stdio
+ run_command = self._flow_graph.get_option('run_command')
+ cmds = shlex.split(run_command.format(python=sys.executable,
+ filename=self.get_file_path()))
# when in no gui mode on linux, use a graphical terminal (looks nice)
xterm_executable = find_executable(XTERM_EXECUTABLE)
diff --git a/grc/python/Param.py b/grc/python/Param.py
index 0e72fcbfb2..27e5b76320 100644
--- a/grc/python/Param.py
+++ b/grc/python/Param.py
@@ -19,8 +19,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
from .. base.Param import Param as _Param
from .. gui.Param import Param as _GUIParam
+
import Constants
-import numpy
+from Constants import VECTOR_TYPES, COMPLEX_TYPES, REAL_TYPES, INT_TYPES
+
from gnuradio import eng_notation
import re
from gnuradio import gr
@@ -33,16 +35,19 @@ _show_id_matcher = re.compile('^(variable\w*|parameter|options|notebook)$')
import __builtin__
ID_BLACKLIST = ['self', 'options', 'gr', 'blks2', 'wxgui', 'wx', 'math', 'forms', 'firdes'] + \
filter(lambda x: not x.startswith('_'), dir(gr.top_block())) + dir(__builtin__)
-#define types, native python + numpy
-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,
- 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)
-REAL_TYPES = tuple(REAL_TYPES + INT_TYPES)
-INT_TYPES = tuple(INT_TYPES)
+
+
+def num_to_str(num):
+ """ Display logic for numbers """
+ if isinstance(num, COMPLEX_TYPES):
+ num = complex(num) #cast to python complex
+ if num == 0: return '0' #value is zero
+ elif num.imag == 0: return '%s'%eng_notation.num_to_str(num.real) #value is real
+ elif num.real == 0: return '%sj'%eng_notation.num_to_str(num.imag) #value is imaginary
+ elif num.imag < 0: return '%s-%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(abs(num.imag)))
+ else: return '%s+%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(num.imag))
+ else: return str(num)
+
class Param(_Param, _GUIParam):
@@ -88,18 +93,7 @@ class Param(_Param, _GUIParam):
##################################################
if not self.is_valid(): return _truncate(self.get_value())
if self.get_value() in self.get_option_keys(): return self.get_option(self.get_value()).get_name()
- ##################################################
- # display logic for numbers
- ##################################################
- def num_to_str(num):
- if isinstance(num, COMPLEX_TYPES):
- num = complex(num) #cast to python complex
- if num == 0: return '0' #value is zero
- elif num.imag == 0: return '%s'%eng_notation.num_to_str(num.real) #value is real
- elif num.real == 0: return '%sj'%eng_notation.num_to_str(num.imag) #value is imaginary
- elif num.imag < 0: return '%s-%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(abs(num.imag)))
- else: return '%s+%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(num.imag))
- else: return str(num)
+
##################################################
# split up formatting by type
##################################################
diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl
index 3cc16e7e14..72891b7af8 100644
--- a/grc/python/flow_graph.tmpl
+++ b/grc/python/flow_graph.tmpl
@@ -7,7 +7,7 @@
##@param imports the import statements
##@param flow_graph the flow_graph
##@param variables the variable blocks
-##@param parameters the paramater blocks
+##@param parameters the parameter blocks
##@param blocks the signal blocks
##@param connections the connections
##@param msgs the msg type connections
@@ -53,6 +53,9 @@ if __name__ == '__main__':
########################################################
##Create Imports
########################################################
+#if $flow_graph.get_option('qt_qss_theme')
+#set imports = $sorted(set($imports + ["import os", "import sys"]))
+#end if
#if any(imp.endswith("# grc-generated hier_block") for imp in $imports)
import os
import sys
@@ -95,9 +98,9 @@ class $(class_name)(gr.top_block, Qt.QWidget):
Qt.QWidget.__init__(self)
self.setWindowTitle("$title")
try:
- self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc'))
+ self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc'))
except:
- pass
+ pass
self.top_scroll_layout = Qt.QVBoxLayout()
self.setLayout(self.top_scroll_layout)
self.top_scroll = Qt.QScrollArea()
@@ -258,22 +261,34 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))])
#include source=$connection_templates[($source.get_domain(), $sink.get_domain())]
#end for
-
########################################################
## QT sink close method reimplementation
########################################################
#if $generate_options == 'qt_gui'
+
def closeEvent(self, event):
self.settings = Qt.QSettings("GNU Radio", "$class_name")
self.settings.setValue("geometry", self.saveGeometry())
event.accept()
-#end if
+ #if $flow_graph.get_option('qt_qss_theme')
+ def setStyleSheetFromFile(self, filename):
+ try:
+ if not os.path.exists(filename):
+ filename = os.path.join(
+ gr.prefix(), "share", "gnuradio", "themes", filename)
+ with open(filename) as ss:
+ tb.setStyleSheet('1' + ss.read())
+ except Exception as e:
+ print >> sys.stderr, e
+ #end if
+#end if
########################################################
##Create Callbacks
## Write a set method for this variable that calls the callbacks
########################################################
#for $var in $parameters + $variables
+
#set $id = $var.get_id()
def get_$(id)(self):
return self.$id
@@ -291,7 +306,6 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))])
$indent($callback)
#end for
#end if
-
#end for
########################################################
##Create Main
@@ -313,25 +327,37 @@ $param.get_make()#slurp
$short_id#slurp
#end def
#if not $generate_options.startswith('hb')
+#set $params_eq_list = list()
+#if $parameters
-if __name__ == '__main__':
+
+def argument_parser():
parser = OptionParser(option_class=eng_option, usage="%prog: [options]")
- #set $params_eq_list = list()
#for $param in $parameters
#set $type = $param.get_param('type').get_value()
#if $type
#silent $params_eq_list.append('%s=options.%s'%($param.get_id(), $param.get_id()))
- parser.add_option("$make_short_id($param)", "--$param.get_id().replace('_', '-')", dest="$param.get_id()", type="$type", default=$make_default($type, $param),
+ parser.add_option(
+ "$make_short_id($param)", "--$param.get_id().replace('_', '-')", dest="$param.get_id()", type="$type", default=$make_default($type, $param),
help="Set $($param.get_param('label').get_evaluated() or $param.get_id()) [default=%default]")
#end if
#end for
- (options, args) = parser.parse_args()
+ return parser
+#end if
+
+
+def main(top_block_cls=$(class_name), options=None):
+ #if $parameters
+ if options is None:
+ options, _ = argument_parser().parse_args()
+ #end if
#if $flow_graph.get_option('realtime_scheduling')
if gr.enable_realtime_scheduling() != gr.RT_OK:
- print "Error: failed to enable realtime scheduling."
+ print "Error: failed to enable real-time scheduling."
#end if
+
#if $generate_options == 'wx_gui'
- tb = $(class_name)($(', '.join($params_eq_list)))
+ tb = top_block_cls($(', '.join($params_eq_list)))
#if $flow_graph.get_option('max_nouts')
tb.Run($flow_graph.get_option('run'), $flow_graph.get_option('max_nouts'))
#else
@@ -344,9 +370,11 @@ if __name__ == '__main__':
#elif $generate_options == 'qt_gui'
from distutils.version import StrictVersion
if StrictVersion(Qt.qVersion()) >= StrictVersion("4.5.0"):
- Qt.QApplication.setGraphicsSystem(gr.prefs().get_string('qtgui','style','raster'))
+ style = gr.prefs().get_string('qtgui', 'style', 'raster')
+ Qt.QApplication.setGraphicsSystem(style)
qapp = Qt.QApplication(sys.argv)
- tb = $(class_name)($(', '.join($params_eq_list)))
+
+ tb = top_block_cls($(', '.join($params_eq_list)))
#if $flow_graph.get_option('run')
#if $flow_graph.get_option('max_nouts')
tb.start($flow_graph.get_option('max_nouts'))
@@ -354,6 +382,9 @@ if __name__ == '__main__':
tb.start()
#end if
#end if
+ #if $flow_graph.get_option('qt_qss_theme')
+ tb.setStyleSheetFromFile($repr($flow_graph.get_option('qt_qss_theme')))
+ #end if
tb.show()
def quitting():
@@ -368,9 +399,8 @@ if __name__ == '__main__':
sys.stderr.write("Monitor '{0}' does not have an enable ('en') parameter.".format("tb.$m.get_id()"))
#end for
qapp.exec_()
- tb = None # to clean up Qt widgets
#elif $generate_options == 'no_gui'
- tb = $(class_name)($(', '.join($params_eq_list)))
+ tb = top_block_cls($(', '.join($params_eq_list)))
#set $run_options = $flow_graph.get_option('run_options')
#if $run_options == 'prompt'
#if $flow_graph.get_option('max_nouts')
@@ -398,4 +428,8 @@ if __name__ == '__main__':
#end for
tb.wait()
#end if
+
+
+if __name__ == '__main__':
+ main()
#end if