diff options
Diffstat (limited to 'grc')
-rw-r--r-- | grc/core/Constants.py | 25 | ||||
-rw-r--r-- | grc/core/Messages.py | 20 | ||||
-rw-r--r-- | grc/core/Platform.py | 13 | ||||
-rw-r--r-- | grc/core/generator/Generator.py | 89 | ||||
-rw-r--r-- | grc/core/utils/epy_block_io.py | 2 | ||||
-rw-r--r-- | grc/gui/ActionHandler.py | 59 | ||||
-rw-r--r-- | grc/gui/CMakeLists.txt | 1 | ||||
-rw-r--r-- | grc/gui/Constants.py | 10 | ||||
-rw-r--r-- | grc/gui/Executor.py | 123 | ||||
-rw-r--r-- | grc/gui/Platform.py | 12 |
10 files changed, 176 insertions, 178 deletions
diff --git a/grc/core/Constants.py b/grc/core/Constants.py index b067886595..9fe805f854 100644 --- a/grc/core/Constants.py +++ b/grc/core/Constants.py @@ -18,28 +18,8 @@ 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 - -_gr_prefs = gr.prefs() - -# Setup paths -PATH_SEP = {'/': ':', '\\': ';'}[os.path.sep] - -HIER_BLOCKS_LIB_DIR = os.environ.get('GRC_HIER_PATH', expanduser('~/.grc_gnuradio')) - -PREFS_FILE = os.environ.get('GRC_PREFS_PATH', expanduser('~/.gnuradio/grc.conf')) -PREFS_FILE_OLD = os.environ.get('GRC_PREFS_PATH', expanduser('~/.grc')) - -BLOCKS_DIRS = filter( # filter blank strings - lambda x: x, PATH_SEP.join([ - os.environ.get('GRC_BLOCKS_PATH', ''), - _gr_prefs.get_string('grc', 'local_blocks_path', ''), - _gr_prefs.get_string('grc', 'global_blocks_path', ''), - ]).split(PATH_SEP), -) + [HIER_BLOCKS_LIB_DIR] # Data files DATA_DIR = os.path.dirname(__file__) @@ -47,6 +27,7 @@ FLOW_GRAPH_DTD = os.path.join(DATA_DIR, 'flow_graph.dtd') BLOCK_TREE_DTD = os.path.join(DATA_DIR, 'block_tree.dtd') BLOCK_DTD = os.path.join(DATA_DIR, 'block.dtd') DEFAULT_FLOW_GRAPH = os.path.join(DATA_DIR, 'default_flow_graph.grc') +DOMAIN_DTD = os.path.join(DATA_DIR, 'domain.dtd') # File format versions: # 0: undefined / legacy @@ -58,7 +39,6 @@ DEFAULT_PARAM_TAB = "General" ADVANCED_PARAM_TAB = "Advanced" # Port domains -DOMAIN_DTD = os.path.join(DATA_DIR, 'domain.dtd') GR_STREAM_DOMAIN = "gr_stream" GR_MESSAGE_DOMAIN = "gr_message" DEFAULT_DOMAIN = GR_STREAM_DOMAIN @@ -73,9 +53,6 @@ BLOCK_DISABLED = 0 BLOCK_ENABLED = 1 BLOCK_BYPASSED = 2 -# User settings -XTERM_EXECUTABLE = _gr_prefs.get_string('grc', 'xterm_executable', 'xterm') - # File creation modes TOP_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | \ stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH diff --git a/grc/core/Messages.py b/grc/core/Messages.py index f794fdbba5..da50487e5b 100644 --- a/grc/core/Messages.py +++ b/grc/core/Messages.py @@ -58,20 +58,12 @@ register_messenger(sys.stdout.write) # Special functions for specific program functionalities ########################################################################### def send_init(platform): - p = platform - - def get_paths(x): - return os.path.abspath(os.path.expanduser(x)), x - - send('\n'.join([ - "<<< Welcome to %s %s >>>" % (p.config.name, p.config.version), - "", - "Preferences file: " + p.get_prefs_file(), - "Block paths:" - ] + [ - "\t%s" % path + (" (%s)" % opath if opath != path else "") - for path, opath in map(get_paths, p.get_block_paths()) - ]) + "\n") + msg = "<<< Welcome to {config.name} {config.version} >>>\n\n" \ + "Block paths:\n\t{paths}\n" + send(msg.format( + config=platform.config, + paths="\n\t".join(platform.config.block_paths)) + ) def send_page_switch(file_path): diff --git a/grc/core/Platform.py b/grc/core/Platform.py index f6bb756b4e..97e2b38309 100644 --- a/grc/core/Platform.py +++ b/grc/core/Platform.py @@ -51,11 +51,8 @@ class Config(object): def __init__(self): self.prefs = self._get_prefs() - # Ensure hier and conf directories if not exists(self.hier_block_lib_dir): os.mkdir(self.hier_block_lib_dir) - if not exists(dirname(Constants.PREFS_FILE)): - os.mkdir(dirname(Constants.PREFS_FILE)) @staticmethod def _get_prefs(): @@ -113,7 +110,6 @@ class Platform(Element): callback_finished=lambda: self.block_docstrings_loaded_callback() ) - self._block_paths = list(set(Constants.BLOCKS_DIRS)) self._block_dtd = Constants.BLOCK_DTD self._default_flow_graph = Constants.DEFAULT_FLOW_GRAPH @@ -124,8 +120,8 @@ class Platform(Element): self.blocks = None self._blocks_n = None self._category_trees_n = None - self.domains = dict() - self.connection_templates = dict() + self.domains = {} + self.connection_templates = {} self._auto_hier_block_generate_chain = set() @@ -215,7 +211,7 @@ class Platform(Element): def iter_xml_files(self): """Iterator for block descriptions and category trees""" - for block_path in map(lambda x: os.path.abspath(os.path.expanduser(x)), self._block_paths): + for block_path in self.config.block_paths: if os.path.isfile(block_path): yield block_path elif os.path.isdir(block_path): @@ -371,6 +367,3 @@ class Platform(Element): def get_colors(self): return [(name, color) for name, key, sizeof, color in Constants.CORE_TYPES] - - def get_block_paths(self): - return self._block_paths diff --git a/grc/core/generator/Generator.py b/grc/core/generator/Generator.py index a303a6f4cd..5c2fa6e0d5 100644 --- a/grc/core/generator/Generator.py +++ b/grc/core/generator/Generator.py @@ -15,22 +15,18 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -import shlex -import subprocess -import sys -import tempfile -from distutils.spawn import find_executable import codecs import os -import re # for shlex_quote +import tempfile + from Cheetah.Template import Template from .FlowGraphProxy import FlowGraphProxy from .. import ParseXML, Messages from ..Constants import ( TOP_BLOCK_FILE_MODE, BLOCK_FLAG_NEED_QT_GUI, - XTERM_EXECUTABLE, HIER_BLOCK_FILE_MODE, HIER_BLOCKS_LIB_DIR, BLOCK_DTD + HIER_BLOCK_FILE_MODE, BLOCK_DTD ) from ..utils import expr_utils, odict @@ -50,19 +46,16 @@ class Generator(object): flow_graph: the flow graph object file_path: the path to the grc file """ - self._generate_options = flow_graph.get_option('generate_options') - if self._generate_options == 'hb': + self.generate_options = flow_graph.get_option('generate_options') + if self.generate_options == 'hb': generator_cls = HierBlockGenerator - elif self._generate_options == 'hb_qt_gui': + elif self.generate_options == 'hb_qt_gui': generator_cls = QtHierBlockGenerator else: generator_cls = TopBlockGenerator self._generator = generator_cls(flow_graph, file_path) - def get_generate_options(self): - return self._generate_options - def __getattr__(self, item): """get all other attrib from actual generator object""" return getattr(self._generator, item) @@ -87,10 +80,10 @@ class TopBlockGenerator(object): if not os.access(dirname, os.W_OK): dirname = tempfile.gettempdir() filename = self._flow_graph.get_option('id') + '.py' - self._file_path = os.path.join(dirname, filename) + self.file_path = os.path.join(dirname, filename) def get_file_path(self): - return self._file_path + return self.file_path def write(self): """generate output and write it to files""" @@ -113,44 +106,12 @@ class TopBlockGenerator(object): for filename, data in self._build_python_code_from_template(): with codecs.open(filename, 'w', encoding='utf-8') as fp: fp.write(data) - if filename == self.get_file_path(): + if filename == self.file_path: try: os.chmod(filename, self._mode) except: pass - def get_popen(self): - """ - Execute this python flow graph. - - Returns: - a popen object - """ - run_command = self._flow_graph.get_option('run_command') - try: - run_command = run_command.format( - python=shlex_quote(sys.executable), - filename=shlex_quote(self.get_file_path())) - run_command_args = shlex.split(run_command) - except Exception as e: - raise ValueError("Can't parse run command {!r}: {}".format(run_command, e)) - - # When in no gui mode on linux, use a graphical terminal (looks nice) - xterm_executable = find_executable(XTERM_EXECUTABLE) - if self._generate_options == 'no_gui' and xterm_executable: - run_command_args = [xterm_executable, '-e', run_command] - - # this does not reproduce a shell executable command string, if a graphical - # terminal is used. Passing run_command though shlex_quote would do it but - # it looks really ugly and confusing in the console panel. - Messages.send_start_exec(' '.join(run_command_args)) - - return subprocess.Popen( - args=run_command_args, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - shell=False, universal_newlines=True - ) - def _build_python_code_from_template(self): """ Convert the flow graph to python code. @@ -280,7 +241,7 @@ class TopBlockGenerator(object): } # Build the template t = Template(open(FLOW_GRAPH_TEMPLATE, 'r').read(), namespace) - output.append((self.get_file_path(), str(t))) + output.append((self.file_path, str(t))) return output @@ -296,10 +257,12 @@ class HierBlockGenerator(TopBlockGenerator): file_path: where to write the py file (the xml goes into HIER_BLOCK_LIB_DIR) """ TopBlockGenerator.__init__(self, flow_graph, file_path) + platform = flow_graph.get_parent() + self._mode = HIER_BLOCK_FILE_MODE - self._file_path = os.path.join(HIER_BLOCKS_LIB_DIR, - self._flow_graph.get_option('id') + '.py') - self._file_path_xml = self._file_path + '.xml' + self.file_path = os.path.join(platform.config.hier_block_lib_dir, + self._flow_graph.get_option('id') + '.py') + self._file_path_xml = self.file_path + '.xml' def get_file_path_xml(self): return self._file_path_xml @@ -394,7 +357,7 @@ class HierBlockGenerator(TopBlockGenerator): block_n['doc'] = "\n".join(field for field in ( self._flow_graph.get_option('author'), self._flow_graph.get_option('description'), - self.get_file_path() + self.file_path ) if field) block_n['grc_source'] = str(self._flow_graph.grc_file_path) @@ -425,22 +388,4 @@ class QtHierBlockGenerator(HierBlockGenerator): "\n#set $win = 'self.%s' % $id" "\n${gui_hint()($win)}" ) - return n - - -########################################################### -# back-port from python3 -########################################################### -_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search - - -def shlex_quote(s): - """Return a shell-escaped version of the string *s*.""" - if not s: - return "''" - if _find_unsafe(s) is None: - return s - - # use single quotes, and put single quotes into double quotes - # the string $'b is then quoted as '$'"'"'b' - return "'" + s.replace("'", "'\"'\"'") + "'" + return n
\ No newline at end of file diff --git a/grc/core/utils/epy_block_io.py b/grc/core/utils/epy_block_io.py index e089908a01..c070e1c3f3 100644 --- a/grc/core/utils/epy_block_io.py +++ b/grc/core/utils/epy_block_io.py @@ -38,7 +38,7 @@ def _blk_class(source_code): except Exception as e: raise ValueError("Can't interpret source code: " + str(e)) for var in ns.itervalues(): - if inspect.isclass(var)and issubclass(var, gr.gateway.gateway_block): + if inspect.isclass(var) and issubclass(var, gr.gateway.gateway_block): return var raise ValueError('No python block class found in code') diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py index 41eb6f12b6..139bbedffd 100644 --- a/grc/gui/ActionHandler.py +++ b/grc/gui/ActionHandler.py @@ -17,15 +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 os -import subprocess -from threading import Thread -import gtk import gobject +import gtk +import os +import subprocess -from . import Dialogs, Preferences, Actions -from .Constants import DEFAULT_CANVAS_SIZE, IMAGE_FILE_EXTENSION, GR_PREFIX +from . import Dialogs, Preferences, Actions, Executor +from .Constants import DEFAULT_CANVAS_SIZE, IMAGE_FILE_EXTENSION, GR_PREFIX, XTERM_EXECUTABLE from .FileDialogs import (OpenFlowGraphFileDialog, SaveFlowGraphFileDialog, SaveReportsFileDialog, SaveScreenShotDialog, OpenQSSFileDialog) @@ -34,7 +33,6 @@ from .ParserErrorsDialog import ParserErrorsDialog from .PropsDialog import PropsDialog from ..core import Constants, ParseXML -from ..core.Constants import XTERM_EXECUTABLE from ..core import Messages gobject.threads_init() @@ -547,7 +545,7 @@ class ActionHandler: Dialogs.MissingXTermDialog(XTERM_EXECUTABLE) Preferences.xterm_missing(XTERM_EXECUTABLE) if self.get_page().get_saved() and self.get_page().get_file_path(): - ExecFlowGraphThread(self) + Executor.ExecFlowGraphThread(self) elif action == Actions.FLOW_GRAPH_KILL: if self.get_page().get_proc(): try: @@ -654,48 +652,3 @@ class ActionHandler: Actions.FLOW_GRAPH_GEN.set_sensitive(sensitive) Actions.FLOW_GRAPH_EXEC.set_sensitive(sensitive) Actions.FLOW_GRAPH_KILL.set_sensitive(self.get_page().get_proc() is not None) - -class ExecFlowGraphThread(Thread): - """Execute the flow graph as a new process and wait on it to finish.""" - - def __init__ (self, action_handler): - """ - ExecFlowGraphThread constructor. - - Args: - action_handler: an instance of an ActionHandler - """ - Thread.__init__(self) - self.update_exec_stop = action_handler.update_exec_stop - self.flow_graph = action_handler.get_flow_graph() - #store page and dont use main window calls in run - self.page = action_handler.get_page() - #get the popen - try: - self.p = self.page.get_generator().get_popen() - self.page.set_proc(self.p) - #update - self.update_exec_stop() - self.start() - except Exception, e: - Messages.send_verbose_exec(str(e)) - Messages.send_end_exec() - - def run(self): - """ - Wait on the executing process by reading from its stdout. - Use gobject.idle_add when calling functions that modify gtk objects. - """ - #handle completion - r = "\n" - while r: - gobject.idle_add(Messages.send_verbose_exec, r) - r = os.read(self.p.stdout.fileno(), 1024) - self.p.poll() - gobject.idle_add(self.done) - - def done(self): - """Perform end of execution tasks.""" - Messages.send_end_exec(self.p.returncode) - self.page.set_proc(None) - self.update_exec_stop() diff --git a/grc/gui/CMakeLists.txt b/grc/gui/CMakeLists.txt index 99140df7c4..7e4355357b 100644 --- a/grc/gui/CMakeLists.txt +++ b/grc/gui/CMakeLists.txt @@ -25,6 +25,7 @@ GR_PYTHON_INSTALL(FILES Constants.py Connection.py Element.py + Executor.py FlowGraph.py Param.py Platform.py diff --git a/grc/gui/Constants.py b/grc/gui/Constants.py index 741c6fda95..b891c5b313 100644 --- a/grc/gui/Constants.py +++ b/grc/gui/Constants.py @@ -29,6 +29,16 @@ from gnuradio import gr prefs = gr.prefs() GR_PREFIX = gr.prefix() EDITOR = prefs.get_string('grc', 'editor', '') +XTERM_EXECUTABLE = prefs.get_string('grc', 'xterm_executable', 'xterm') + +PREFS_FILE = os.environ.get( + 'GRC_PREFS_PATH', + os.path.expanduser('~/.gnuradio/grc.conf') +) +PREFS_FILE_OLD = os.environ.get( + 'GRC_PREFS_PATH', + os.path.expanduser('~/.grc') +) # default path for the open/save dialogs DEFAULT_FILE_PATH = os.getcwd() diff --git a/grc/gui/Executor.py b/grc/gui/Executor.py new file mode 100644 index 0000000000..96b95f4c7a --- /dev/null +++ b/grc/gui/Executor.py @@ -0,0 +1,123 @@ +# Copyright 2016 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 + +import gobject +import os +import threading +import shlex +import subprocess +import sys +import re +from distutils.spawn import find_executable + +from ..core import Messages +from .Constants import XTERM_EXECUTABLE + + +class ExecFlowGraphThread(threading.Thread): + """Execute the flow graph as a new process and wait on it to finish.""" + + def __init__(self, action_handler): + """ + ExecFlowGraphThread constructor. + + Args: + action_handler: an instance of an ActionHandler + """ + threading.Thread.__init__(self) + self.update_exec_stop = action_handler.update_exec_stop + self.flow_graph = action_handler.get_flow_graph() + #store page and dont use main window calls in run + self.page = action_handler.get_page() + #get the popen + try: + self.p = self._popen() + self.page.set_proc(self.p) + #update + self.update_exec_stop() + self.start() + except Exception, e: + Messages.send_verbose_exec(str(e)) + Messages.send_end_exec() + + def _popen(self): + """ + Execute this python flow graph. + """ + run_command = self.flow_graph.get_option('run_command') + generator = self.page.get_generator() + + try: + run_command = run_command.format( + python=shlex_quote(sys.executable), + filename=shlex_quote(generator.file_path)) + run_command_args = shlex.split(run_command) + except Exception as e: + raise ValueError("Can't parse run command {!r}: {}".format(run_command, e)) + + # When in no gui mode on linux, use a graphical terminal (looks nice) + xterm_executable = find_executable(XTERM_EXECUTABLE) + if generator.generate_options == 'no_gui' and xterm_executable: + run_command_args = [xterm_executable, '-e', run_command] + + # this does not reproduce a shell executable command string, if a graphical + # terminal is used. Passing run_command though shlex_quote would do it but + # it looks really ugly and confusing in the console panel. + Messages.send_start_exec(' '.join(run_command_args)) + + return subprocess.Popen( + args=run_command_args, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + shell=False, universal_newlines=True + ) + + def run(self): + """ + Wait on the executing process by reading from its stdout. + Use gobject.idle_add when calling functions that modify gtk objects. + """ + #handle completion + r = "\n" + while r: + gobject.idle_add(Messages.send_verbose_exec, r) + r = os.read(self.p.stdout.fileno(), 1024) + self.p.poll() + gobject.idle_add(self.done) + + def done(self): + """Perform end of execution tasks.""" + Messages.send_end_exec(self.p.returncode) + self.page.set_proc(None) + self.update_exec_stop() + + +########################################################### +# back-port from python3 +########################################################### +_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search + + +def shlex_quote(s): + """Return a shell-escaped version of the string *s*.""" + if not s: + return "''" + if _find_unsafe(s) is None: + return s + + # use single quotes, and put single quotes into double quotes + # the string $'b is then quoted as '$'"'"'b' + return "'" + s.replace("'", "'\"'\"'") + "'" diff --git a/grc/gui/Platform.py b/grc/gui/Platform.py index 85a6872027..cfd5ae3e3d 100644 --- a/grc/gui/Platform.py +++ b/grc/gui/Platform.py @@ -20,16 +20,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import os import sys -from .Element import Element - from ..core.Platform import Platform as _Platform -from ..core.Constants import PREFS_FILE, PREFS_FILE_OLD from .Block import Block as _Block +from .Connection import Connection as _Connection +from .Constants import PREFS_FILE, PREFS_FILE_OLD +from .Element import Element from .FlowGraph import FlowGraph as _FlowGraph from .Param import Param as _Param from .Port import Port as _Port -from .Connection import Connection as _Connection class Platform(Element, _Platform): @@ -37,6 +36,11 @@ class Platform(Element, _Platform): def __init__(self): Element.__init__(self) _Platform.__init__(self) + + # Ensure conf directories + if not os.path.exists(os.path.dirname(PREFS_FILE)): + os.mkdir(os.path.dirname(PREFS_FILE)) + self._move_old_pref_file() self._prefs_file = PREFS_FILE |