diff options
Diffstat (limited to 'grc')
53 files changed, 6804 insertions, 6931 deletions
diff --git a/grc/base/Block.py b/grc/base/Block.py index 1fb0db9ad8..b0b96422a7 100644 --- a/grc/base/Block.py +++ b/grc/base/Block.py @@ -25,376 +25,346 @@ from UserDict import UserDict from .. gui import Actions class TemplateArg(UserDict): - """ - A cheetah template argument created from a param. - The str of this class evaluates to the param's to code method. - The use of this class as a dictionary (enum only) will reveal the enum opts. - The __call__ or () method can return the param evaluated to a raw python data type. - """ - - def __init__(self, param): - UserDict.__init__(self) - self._param = param - if param.is_enum(): - for key in param.get_opt_keys(): - self[key] = str(param.get_opt(key)) - - def __str__(self): - return str(self._param.to_code()) - - def __call__(self): - return self._param.get_evaluated() + """ + A cheetah template argument created from a param. + The str of this class evaluates to the param's to code method. + The use of this class as a dictionary (enum only) will reveal the enum opts. + The __call__ or () method can return the param evaluated to a raw python data type. + """ + + def __init__(self, param): + UserDict.__init__(self) + self._param = param + if param.is_enum(): + for key in param.get_opt_keys(): + self[key] = str(param.get_opt(key)) + + def __str__(self): + return str(self._param.to_code()) + + def __call__(self): + return self._param.get_evaluated() def _get_keys(lst): return [elem.get_key() for elem in lst] def _get_elem(lst, key): - try: return lst[_get_keys(lst).index(key)] - except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst)) + try: return lst[_get_keys(lst).index(key)] + except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst)) class Block(Element): - def __init__(self, flow_graph, n): - """ - Make a new block from nested data. - - Args: - flow: graph the parent element - n: the nested odict - - Returns: - block a new block - """ - #build the block - - Element.__init__(self, flow_graph) - #grab the data - params = n.findall('param') - sources = n.findall('source') - sinks = n.findall('sink') - self._name = n.find('name') - self._key = n.find('key') - self._category = n.find('category') or '' - self._grc_source = n.find('grc_source') or '' - self._block_wrapper_path = n.find('block_wrapper_path') - self._bussify_sink = n.find('bus_sink') - self._bussify_source = n.find('bus_source') - - - #create the param objects - self._params = list() - #add the id param - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({ - 'name': 'ID', - 'key': 'id', - 'type': 'id', - }) - )) - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({ - 'name': 'Enabled', - 'key': '_enabled', - 'type': 'raw', - 'value': 'True', - 'hide': 'all', - }) - )) - for param in map(lambda n: self.get_parent().get_parent().Param(block=self, n=n), params): - key = param.get_key() - #test against repeated keys - if key in self.get_param_keys(): - raise Exception, 'Key "%s" already exists in params'%key - #store the param - self.get_params().append(param) - #create the source objects - self._sources = list() - for source in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='source'), sources): - key = source.get_key() - #test against repeated keys - if key in self.get_source_keys(): - raise Exception, 'Key "%s" already exists in sources'%key - #store the port - self.get_sources().append(source) - self.back_ofthe_bus(self.get_sources()) - #create the sink objects - self._sinks = list() - for sink in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='sink'), sinks): - key = sink.get_key() - #test against repeated keys - if key in self.get_sink_keys(): - raise Exception, 'Key "%s" already exists in sinks'%key - #store the port - self.get_sinks().append(sink) - self.back_ofthe_bus(self.get_sinks()) - self.current_bus_structure = {'source':'','sink':''}; - - - def back_ofthe_bus(self, portlist): - portlist.sort(key=lambda a: a.get_type() == 'bus'); - - - def filter_bus_port(self, ports): - buslist = [i for i in ports if i.get_type() == 'bus']; - if len(buslist) == 0: - return ports; - else: - return buslist; - - def get_enabled(self): - """ - Get the enabled state of the block. - - Returns: - true for enabled - """ - try: return eval(self.get_param('_enabled').get_value()) - except: return True - - def set_enabled(self, enabled): - """ - Set the enabled state of the block. - - Args: - enabled: true for enabled - """ - self.get_param('_enabled').set_value(str(enabled)) - - def __str__(self): return 'Block - %s - %s(%s)'%(self.get_id(), self.get_name(), self.get_key()) - - def get_id(self): return self.get_param('id').get_value() - def is_block(self): return True - def get_name(self): return self._name - def get_key(self): return self._key - def get_category(self): return self._category - def get_doc(self): return '' - def get_ports(self): return self.get_sources() + self.get_sinks() - def get_ports_gui(self): return self.filter_bus_port(self.get_sources()) + self.filter_bus_port(self.get_sinks()); - - - - def get_children(self): return self.get_ports() + self.get_params() - def get_children_gui(self): return self.get_ports_gui() + self.get_params() - def get_block_wrapper_path(self): return self._block_wrapper_path - - ############################################## - # Access Params - ############################################## - def get_param_keys(self): return _get_keys(self._params) - def get_param(self, key): return _get_elem(self._params, key) - def get_params(self): return self._params - - ############################################## - # Access Sinks - ############################################## - def get_sink_keys(self): return _get_keys(self._sinks) - def get_sink(self, key): return _get_elem(self._sinks, key) - def get_sinks(self): return self._sinks - def get_sinks_gui(self): return self.filter_bus_port(self.get_sinks()) - - ############################################## - # Access Sources - ############################################## - def get_source_keys(self): return _get_keys(self._sources) - def get_source(self, key): return _get_elem(self._sources, key) - def get_sources(self): return self._sources - def get_sources_gui(self): return self.filter_bus_port(self.get_sources()); - - - - def get_connections(self): - return sum([port.get_connections() for port in self.get_ports()], []) - - def resolve_dependencies(self, tmpl): - """ - Resolve a paramater dependency with cheetah templates. - - Args: - tmpl: the string with dependencies - - Returns: - the resolved value - """ - tmpl = str(tmpl) - if '$' not in tmpl: return tmpl - n = dict((p.get_key(), TemplateArg(p)) for p in self.get_params()) - - try: return str(Template(tmpl, n)) - except Exception, e: return "-------->\n%s: %s\n<--------"%(e, tmpl) - - ############################################## - # Controller Modify - ############################################## - def type_controller_modify(self, direction): - """ - Change the type controller. - - Args: - direction: +1 or -1 - - Returns: - true for change - """ - changed = False - type_param = None - for param in filter(lambda p: p.is_enum(), self.get_params()): - children = self.get_ports() + self.get_params() - #priority to the type controller - if param.get_key() in ' '.join(map(lambda p: p._type, children)): type_param = param - #use param if type param is unset - if not type_param: type_param = param - if type_param: - #try to increment the enum by direction - try: - keys = type_param.get_option_keys() - old_index = keys.index(type_param.get_value()) - new_index = (old_index + direction + len(keys))%len(keys) - type_param.set_value(keys[new_index]) - changed = True - except: pass - return changed - - def port_controller_modify(self, direction): - """ - Change the port controller. - - Args: - direction: +1 or -1 - - Returns: - true for change - """ - return False - - - def form_bus_structure(self, direc): - if direc == 'source': - get_p = self.get_sources; - get_p_gui = self.get_sources_gui; - bus_structure = self.get_bus_structure('source'); - else: - get_p = self.get_sinks; - get_p_gui = self.get_sinks_gui - bus_structure = self.get_bus_structure('sink'); - - struct = [range(len(get_p()))]; - if True in map(lambda a: isinstance(a.get_nports(), int), get_p()): - - - structlet = []; - last = 0; - for j in [i.get_nports() for i in get_p() if isinstance(i.get_nports(), int)]: - structlet.extend(map(lambda a: a+last, range(j))); - last = structlet[-1] + 1; - struct = [structlet]; - if bus_structure: - - struct = bus_structure - - self.current_bus_structure[direc] = struct; - return struct - - def bussify(self, n, direc): - if direc == 'source': - get_p = self.get_sources; - get_p_gui = self.get_sources_gui; - bus_structure = self.get_bus_structure('source'); - else: - get_p = self.get_sinks; - get_p_gui = self.get_sinks_gui - bus_structure = self.get_bus_structure('sink'); - - - for elt in get_p(): - for connect in elt.get_connections(): - self.get_parent().remove_element(connect); - - - - - - - if (not 'bus' in map(lambda a: a.get_type(), get_p())) and len(get_p()) > 0: - - struct = self.form_bus_structure(direc); - self.current_bus_structure[direc] = struct; - if get_p()[0].get_nports(): - n['nports'] = str(1); - - for i in range(len(struct)): - n['key'] = str(len(get_p())); - n = odict(n); - port = self.get_parent().get_parent().Port(block=self, n=n, dir=direc); - get_p().append(port); - - - - - elif 'bus' in map(lambda a: a.get_type(), get_p()): - for elt in get_p_gui(): - get_p().remove(elt); - self.current_bus_structure[direc] = '' - - - - ############################################## - ## Import/Export Methods - ############################################## - def export_data(self): - """ - Export this block's params to nested data. - - Returns: - a nested data odict - """ - n = odict() - - - n['key'] = self.get_key() - n['param'] = map(lambda p: p.export_data(), self.get_params()) - if 'bus' in map(lambda a: a.get_type(), self.get_sinks()): - n['bus_sink'] = str(1); - if 'bus' in map(lambda a: a.get_type(), self.get_sources()): - n['bus_source'] = str(1); - return n - - def import_data(self, n): - """ - Import this block's params from nested data. - Any param keys that do not exist will be ignored. - Since params can be dynamically created based another param, - call rewrite, and repeat the load until the params stick. - This call to rewrite will also create any dynamic ports - that are needed for the connections creation phase. - - Args: - n: the nested data odict - """ - - get_hash = lambda: hash(tuple(map(hash, self.get_params()))) - my_hash = 0 - while get_hash() != my_hash: - params_n = n.findall('param') - for param_n in params_n: - key = param_n.find('key') - value = param_n.find('value') - #the key must exist in this block's params - if key in self.get_param_keys(): - self.get_param(key).set_value(value) - #store hash and call rewrite - my_hash = get_hash() - self.rewrite() - bussinks = n.findall('bus_sink'); - if len(bussinks) > 0 and not self._bussify_sink: - self.bussify({'name':'bus','type':'bus'}, 'sink') - elif len(bussinks) > 0: - self.bussify({'name':'bus','type':'bus'}, 'sink') - self.bussify({'name':'bus','type':'bus'}, 'sink') - - bussrcs = n.findall('bus_source'); - if len(bussrcs) > 0 and not self._bussify_source: - self.bussify({'name':'bus','type':'bus'}, 'source') - elif len(bussrcs) > 0: - self.bussify({'name':'bus','type':'bus'}, 'source') - self.bussify({'name':'bus','type':'bus'}, 'source') - + def __init__(self, flow_graph, n): + """ + Make a new block from nested data. + + Args: + flow: graph the parent element + n: the nested odict + + Returns: + block a new block + """ + #build the block + Element.__init__(self, flow_graph) + #grab the data + params = n.findall('param') + sources = n.findall('source') + sinks = n.findall('sink') + self._name = n.find('name') + self._key = n.find('key') + self._category = n.find('category') or '' + self._grc_source = n.find('grc_source') or '' + self._block_wrapper_path = n.find('block_wrapper_path') + self._bussify_sink = n.find('bus_sink') + self._bussify_source = n.find('bus_source') + #create the param objects + self._params = list() + #add the id param + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({ + 'name': 'ID', + 'key': 'id', + 'type': 'id', + }) + )) + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({ + 'name': 'Enabled', + 'key': '_enabled', + 'type': 'raw', + 'value': 'True', + 'hide': 'all', + }) + )) + for param in map(lambda n: self.get_parent().get_parent().Param(block=self, n=n), params): + key = param.get_key() + #test against repeated keys + if key in self.get_param_keys(): + raise Exception, 'Key "%s" already exists in params'%key + #store the param + self.get_params().append(param) + #create the source objects + self._sources = list() + for source in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='source'), sources): + key = source.get_key() + #test against repeated keys + if key in self.get_source_keys(): + raise Exception, 'Key "%s" already exists in sources'%key + #store the port + self.get_sources().append(source) + self.back_ofthe_bus(self.get_sources()) + #create the sink objects + self._sinks = list() + for sink in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='sink'), sinks): + key = sink.get_key() + #test against repeated keys + if key in self.get_sink_keys(): + raise Exception, 'Key "%s" already exists in sinks'%key + #store the port + self.get_sinks().append(sink) + self.back_ofthe_bus(self.get_sinks()) + self.current_bus_structure = {'source':'','sink':''}; + + + def back_ofthe_bus(self, portlist): + portlist.sort(key=lambda a: a.get_type() == 'bus'); + + + def filter_bus_port(self, ports): + buslist = [i for i in ports if i.get_type() == 'bus']; + if len(buslist) == 0: + return ports; + else: + return buslist; + + def get_enabled(self): + """ + Get the enabled state of the block. + + Returns: + true for enabled + """ + try: return eval(self.get_param('_enabled').get_value()) + except: return True + + def set_enabled(self, enabled): + """ + Set the enabled state of the block. + + Args: + enabled: true for enabled + """ + self.get_param('_enabled').set_value(str(enabled)) + + def __str__(self): return 'Block - %s - %s(%s)'%(self.get_id(), self.get_name(), self.get_key()) + + def get_id(self): return self.get_param('id').get_value() + def is_block(self): return True + def get_name(self): return self._name + def get_key(self): return self._key + def get_category(self): return self._category + def get_doc(self): return '' + def get_ports(self): return self.get_sources() + self.get_sinks() + def get_ports_gui(self): return self.filter_bus_port(self.get_sources()) + self.filter_bus_port(self.get_sinks()); + def get_children(self): return self.get_ports() + self.get_params() + def get_children_gui(self): return self.get_ports_gui() + self.get_params() + def get_block_wrapper_path(self): return self._block_wrapper_path + + ############################################## + # Access Params + ############################################## + def get_param_keys(self): return _get_keys(self._params) + def get_param(self, key): return _get_elem(self._params, key) + def get_params(self): return self._params + + ############################################## + # Access Sinks + ############################################## + def get_sink_keys(self): return _get_keys(self._sinks) + def get_sink(self, key): return _get_elem(self._sinks, key) + def get_sinks(self): return self._sinks + def get_sinks_gui(self): return self.filter_bus_port(self.get_sinks()) + + ############################################## + # Access Sources + ############################################## + def get_source_keys(self): return _get_keys(self._sources) + def get_source(self, key): return _get_elem(self._sources, key) + def get_sources(self): return self._sources + def get_sources_gui(self): return self.filter_bus_port(self.get_sources()); + + def get_connections(self): + return sum([port.get_connections() for port in self.get_ports()], []) + + def resolve_dependencies(self, tmpl): + """ + Resolve a paramater dependency with cheetah templates. + + Args: + tmpl: the string with dependencies + + Returns: + the resolved value + """ + tmpl = str(tmpl) + if '$' not in tmpl: return tmpl + n = dict((p.get_key(), TemplateArg(p)) for p in self.get_params()) + try: return str(Template(tmpl, n)) + except Exception, e: return "-------->\n%s: %s\n<--------"%(e, tmpl) + + ############################################## + # Controller Modify + ############################################## + def type_controller_modify(self, direction): + """ + Change the type controller. + + Args: + direction: +1 or -1 + + Returns: + true for change + """ + changed = False + type_param = None + for param in filter(lambda p: p.is_enum(), self.get_params()): + children = self.get_ports() + self.get_params() + #priority to the type controller + if param.get_key() in ' '.join(map(lambda p: p._type, children)): type_param = param + #use param if type param is unset + if not type_param: type_param = param + if type_param: + #try to increment the enum by direction + try: + keys = type_param.get_option_keys() + old_index = keys.index(type_param.get_value()) + new_index = (old_index + direction + len(keys))%len(keys) + type_param.set_value(keys[new_index]) + changed = True + except: pass + return changed + + def port_controller_modify(self, direction): + """ + Change the port controller. + + Args: + direction: +1 or -1 + + Returns: + true for change + """ + return False + + def form_bus_structure(self, direc): + if direc == 'source': + get_p = self.get_sources; + get_p_gui = self.get_sources_gui; + bus_structure = self.get_bus_structure('source'); + else: + get_p = self.get_sinks; + get_p_gui = self.get_sinks_gui + bus_structure = self.get_bus_structure('sink'); + + struct = [range(len(get_p()))]; + if True in map(lambda a: isinstance(a.get_nports(), int), get_p()): + + + structlet = []; + last = 0; + for j in [i.get_nports() for i in get_p() if isinstance(i.get_nports(), int)]: + structlet.extend(map(lambda a: a+last, range(j))); + last = structlet[-1] + 1; + struct = [structlet]; + if bus_structure: + + struct = bus_structure + + self.current_bus_structure[direc] = struct; + return struct + + def bussify(self, n, direc): + if direc == 'source': + get_p = self.get_sources; + get_p_gui = self.get_sources_gui; + bus_structure = self.get_bus_structure('source'); + else: + get_p = self.get_sinks; + get_p_gui = self.get_sinks_gui + bus_structure = self.get_bus_structure('sink'); + + + for elt in get_p(): + for connect in elt.get_connections(): + self.get_parent().remove_element(connect); + + + + + + + if (not 'bus' in map(lambda a: a.get_type(), get_p())) and len(get_p()) > 0: + + struct = self.form_bus_structure(direc); + self.current_bus_structure[direc] = struct; + if get_p()[0].get_nports(): + n['nports'] = str(1); + + for i in range(len(struct)): + n['key'] = str(len(get_p())); + n = odict(n); + port = self.get_parent().get_parent().Port(block=self, n=n, dir=direc); + get_p().append(port); + + + + + elif 'bus' in map(lambda a: a.get_type(), get_p()): + for elt in get_p_gui(): + get_p().remove(elt); + self.current_bus_structure[direc] = '' + ############################################## + ## Import/Export Methods + ############################################## + def export_data(self): + """ + Export this block's params to nested data. + + Returns: + a nested data odict + """ + n = odict() + n['key'] = self.get_key() + n['param'] = map(lambda p: p.export_data(), self.get_params()) + if 'bus' in map(lambda a: a.get_type(), self.get_sinks()): + n['bus_sink'] = str(1); + if 'bus' in map(lambda a: a.get_type(), self.get_sources()): + n['bus_source'] = str(1); + return n + + def import_data(self, n): + """ + Import this block's params from nested data. + Any param keys that do not exist will be ignored. + Since params can be dynamically created based another param, + call rewrite, and repeat the load until the params stick. + This call to rewrite will also create any dynamic ports + that are needed for the connections creation phase. + + Args: + n: the nested data odict + """ + get_hash = lambda: hash(tuple(map(hash, self.get_params()))) + my_hash = 0 + while get_hash() != my_hash: + params_n = n.findall('param') + for param_n in params_n: + key = param_n.find('key') + value = param_n.find('value') + #the key must exist in this block's params + if key in self.get_param_keys(): + self.get_param(key).set_value(value) + #store hash and call rewrite + my_hash = get_hash() + self.rewrite() diff --git a/grc/base/Connection.py b/grc/base/Connection.py index 654eedb357..252b7deac1 100644 --- a/grc/base/Connection.py +++ b/grc/base/Connection.py @@ -22,104 +22,99 @@ from . import odict class Connection(Element): - def __init__(self, flow_graph, porta, portb): - """ - Make a new connection given the parent and 2 ports. - - Args: - flow_graph: the parent of this element - porta: a port (any direction) - portb: a port (any direction) - @throws Error cannot make connection - - Returns: - a new connection - """ - Element.__init__(self, flow_graph) - source = sink = None - #separate the source and sink - for port in (porta, portb): - if port.is_source(): source = port - if port.is_sink(): sink = port - if not source: raise ValueError('Connection could not isolate source') - if not sink: raise ValueError('Connection could not isolate sink') + def __init__(self, flow_graph, porta, portb): + """ + Make a new connection given the parent and 2 ports. + + Args: + flow_graph: the parent of this element + porta: a port (any direction) + portb: a port (any direction) + @throws Error cannot make connection + + Returns: + a new connection + """ + Element.__init__(self, flow_graph) + source = sink = None + #separate the source and sink + for port in (porta, portb): + if port.is_source(): source = port + if port.is_sink(): sink = port + if not source: raise ValueError('Connection could not isolate source') + if not sink: raise ValueError('Connection could not isolate sink') + busses = len(filter(lambda a: a.get_type() == 'bus', [source, sink]))%2 + if not busses == 0: raise ValueError('busses must get with busses') - busses = len(filter(lambda a: a.get_type() == 'bus', [source, sink]))%2 - if not busses == 0: raise ValueError('busses must get with busses') + if not len(source.get_associated_ports()) == len(sink.get_associated_ports()): + raise ValueError('port connections must have same cardinality'); + #ensure that this connection (source -> sink) is unique + for connection in self.get_parent().get_connections(): + if connection.get_source() is source and connection.get_sink() is sink: + raise Exception('This connection between source and sink is not unique.') + self._source = source + self._sink = sink + if source.get_type() == 'bus': + + sources = source.get_associated_ports(); + sinks = sink.get_associated_ports(); + + for i in range(len(sources)): + try: + flow_graph.connect(sources[i], sinks[i]); + except: + pass - if not len(source.get_associated_ports()) == len(sink.get_associated_ports()): - raise ValueError('port connections must have same cardinality'); - #ensure that this connection (source -> sink) is unique - for connection in self.get_parent().get_connections(): - if connection.get_source() is source and connection.get_sink() is sink: - raise Exception('This connection between source and sink is not unique.') - self._source = source - self._sink = sink - - if source.get_type() == 'bus': - - sources = source.get_associated_ports(); - sinks = sink.get_associated_ports(); - - for i in range(len(sources)): - try: - flow_graph.connect(sources[i], sinks[i]); - except: - pass - - - def __str__(self): - return 'Connection (\n\t%s\n\t\t%s\n\t%s\n\t\t%s\n)'%( - self.get_source().get_parent(), - self.get_source(), - self.get_sink().get_parent(), - self.get_sink(), - ) + def __str__(self): + return 'Connection (\n\t%s\n\t\t%s\n\t%s\n\t\t%s\n)'%( + self.get_source().get_parent(), + self.get_source(), + self.get_sink().get_parent(), + self.get_sink(), + ) - def is_connection(self): return True + def is_connection(self): return True - def validate(self): - """ - Validate the connections. - The ports must match in type. - """ - Element.validate(self) - source_type = self.get_source().get_type() - sink_type = self.get_sink().get_type() - if source_type != sink_type: - self.add_error_message('Source type "%s" does not match sink type "%s".'%(source_type, sink_type)) + def validate(self): + """ + Validate the connections. + The ports must match in type. + """ + Element.validate(self) + source_type = self.get_source().get_type() + sink_type = self.get_sink().get_type() + if source_type != sink_type: + self.add_error_message('Source type "%s" does not match sink type "%s".'%(source_type, sink_type)) - + def get_enabled(self): + """ + Get the enabled state of this connection. + + Returns: + true if source and sink blocks are enabled + """ + return self.get_source().get_parent().get_enabled() and \ + self.get_sink().get_parent().get_enabled() - def get_enabled(self): - """ - Get the enabled state of this connection. - - Returns: - true if source and sink blocks are enabled - """ - return self.get_source().get_parent().get_enabled() and \ - self.get_sink().get_parent().get_enabled() + ############################# + # Access Ports + ############################# + def get_sink(self): return self._sink + def get_source(self): return self._source - ############################# - # Access Ports - ############################# - def get_sink(self): return self._sink - def get_source(self): return self._source - - ############################################## - ## Import/Export Methods - ############################################## - def export_data(self): - """ - Export this connection's info. - - Returns: - a nested data odict - """ - n = odict() - n['source_block_id'] = self.get_source().get_parent().get_id() - n['sink_block_id'] = self.get_sink().get_parent().get_id() - n['source_key'] = self.get_source().get_key() - n['sink_key'] = self.get_sink().get_key() - return n + ############################################## + ## Import/Export Methods + ############################################## + def export_data(self): + """ + Export this connection's info. + + Returns: + a nested data odict + """ + n = odict() + n['source_block_id'] = self.get_source().get_parent().get_id() + n['sink_block_id'] = self.get_sink().get_parent().get_id() + n['source_key'] = self.get_source().get_key() + n['sink_key'] = self.get_sink().get_key() + return n diff --git a/grc/base/Element.py b/grc/base/Element.py index 74097eea9a..17b2234a8c 100644 --- a/grc/base/Element.py +++ b/grc/base/Element.py @@ -19,77 +19,77 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA class Element(object): - def __init__(self, parent=None): - self._parent = parent + def __init__(self, parent=None): + self._parent = parent - ################################################## - # Element Validation API - ################################################## - def validate(self): - """ - Validate this element and call validate on all children. - Call this base method before adding error messages in the subclass. - """ - self._error_messages = list() - for child in self.get_children(): child.validate() + ################################################## + # Element Validation API + ################################################## + def validate(self): + """ + Validate this element and call validate on all children. + Call this base method before adding error messages in the subclass. + """ + self._error_messages = list() + for child in self.get_children(): child.validate() - def is_valid(self): - """ - Is this element valid? - - Returns: - true when the element is enabled and has no error messages - """ - return not self.get_error_messages() or not self.get_enabled() + def is_valid(self): + """ + Is this element valid? + + Returns: + true when the element is enabled and has no error messages + """ + return not self.get_error_messages() or not self.get_enabled() - def add_error_message(self, msg): - """ - Add an error message to the list of errors. - - Args: - msg: the error message string - """ - self._error_messages.append(msg) + def add_error_message(self, msg): + """ + Add an error message to the list of errors. + + Args: + msg: the error message string + """ + self._error_messages.append(msg) - def get_error_messages(self): - """ - Get the list of error messages from this element and all of its children. - Do not include the error messages from disabled children. - Cleverly indent the children error messages for printing purposes. - - Returns: - a list of error message strings - """ - error_messages = list(self._error_messages) #make a copy - for child in filter(lambda c: c.get_enabled(), self.get_children()): - for msg in child.get_error_messages(): - error_messages.append("%s:\n\t%s"%(child, msg.replace("\n", "\n\t"))) - return error_messages + def get_error_messages(self): + """ + Get the list of error messages from this element and all of its children. + Do not include the error messages from disabled children. + Cleverly indent the children error messages for printing purposes. + + Returns: + a list of error message strings + """ + error_messages = list(self._error_messages) #make a copy + for child in filter(lambda c: c.get_enabled(), self.get_children()): + for msg in child.get_error_messages(): + error_messages.append("%s:\n\t%s"%(child, msg.replace("\n", "\n\t"))) + return error_messages - def rewrite(self): - """ - Rewrite this element and call rewrite on all children. - Call this base method before rewriting the element. - """ - for child in self.get_children(): child.rewrite() + def rewrite(self): + """ + Rewrite this element and call rewrite on all children. + Call this base method before rewriting the element. + """ + for child in self.get_children(): child.rewrite() - def get_enabled(self): return True + def get_enabled(self): return True - ############################################## - ## Tree-like API - ############################################## - def get_parent(self): return self._parent - def get_children(self): return list() + ############################################## + ## Tree-like API + ############################################## + def get_parent(self): return self._parent + def get_children(self): return list() - ############################################## - ## Type testing methods - ############################################## - def is_element(self): return True - def is_platform(self): return False - def is_flow_graph(self): return False - def is_connection(self): return False - def is_block(self): return False - def is_source(self): return False - def is_sink(self): return False - def is_port(self): return False - def is_param(self): return False + ############################################## + ## Type testing methods + ############################################## + def is_element(self): return True + def is_platform(self): return False + def is_flow_graph(self): return False + def is_connection(self): return False + def is_block(self): return False + def is_source(self): return False + def is_sink(self): return False + def is_port(self): return False + def is_param(self): return False diff --git a/grc/base/FlowGraph.py b/grc/base/FlowGraph.py index a35afa363b..2f2d8c65e1 100644 --- a/grc/base/FlowGraph.py +++ b/grc/base/FlowGraph.py @@ -23,296 +23,288 @@ from .. gui import Messages class FlowGraph(Element): - def __init__(self, platform): - """ - Make a flow graph from the arguments. - - Args: - platform: a platforms with blocks and contrcutors - - Returns: - the flow graph object - """ - #initialize - Element.__init__(self, platform) - #inital blank import - self.import_data() + def __init__(self, platform): + """ + Make a flow graph from the arguments. + + Args: + platform: a platforms with blocks and contrcutors + + Returns: + the flow graph object + """ + #initialize + Element.__init__(self, platform) + #inital blank import + self.import_data() - def _get_unique_id(self, base_id=''): - """ - Get a unique id starting with the base id. - - Args: - base_id: the id starts with this and appends a count - - Returns: - a unique id - """ - index = 0 - while True: - id = '%s_%d'%(base_id, index) - index = index + 1 - #make sure that the id is not used by another block - if not filter(lambda b: b.get_id() == id, self.get_blocks()): return id + def _get_unique_id(self, base_id=''): + """ + Get a unique id starting with the base id. + + Args: + base_id: the id starts with this and appends a count + + Returns: + a unique id + """ + index = 0 + while True: + id = '%s_%d'%(base_id, index) + index = index + 1 + #make sure that the id is not used by another block + if not filter(lambda b: b.get_id() == id, self.get_blocks()): return id - def __str__(self): return 'FlowGraph - %s(%s)'%(self.get_option('title'), self.get_option('id')) - def rewrite(self): - def refactor_bus_structure(): - - for block in self.get_blocks(): - for direc in ['source', 'sink']: - if direc == 'source': - get_p = block.get_sources; - get_p_gui = block.get_sources_gui; - bus_structure = block.form_bus_structure('source'); - else: - get_p = block.get_sinks; - get_p_gui = block.get_sinks_gui - bus_structure = block.form_bus_structure('sink'); - - if 'bus' in map(lambda a: a.get_type(), get_p_gui()): - - - - if len(get_p_gui()) > len(bus_structure): - times = range(len(bus_structure), len(get_p_gui())); - for i in times: - for connect in get_p_gui()[-1].get_connections(): - block.get_parent().remove_element(connect); - get_p().remove(get_p_gui()[-1]); - elif len(get_p_gui()) < len(bus_structure): - n = {'name':'bus','type':'bus'}; - if True in map(lambda a: isinstance(a.get_nports(), int), get_p()): - n['nports'] = str(1); - - times = range(len(get_p_gui()), len(bus_structure)); - - for i in times: - n['key'] = str(len(get_p())); - n = odict(n); - port = block.get_parent().get_parent().Port(block=block, n=n, dir=direc); - get_p().append(port); - - - - for child in self.get_children(): child.rewrite() - refactor_bus_structure(); - + def __str__(self): return 'FlowGraph - %s(%s)'%(self.get_option('title'), self.get_option('id')) + def rewrite(self): + def refactor_bus_structure(): + + for block in self.get_blocks(): + for direc in ['source', 'sink']: + if direc == 'source': + get_p = block.get_sources; + get_p_gui = block.get_sources_gui; + bus_structure = block.form_bus_structure('source'); + else: + get_p = block.get_sinks; + get_p_gui = block.get_sinks_gui + bus_structure = block.form_bus_structure('sink'); + + if 'bus' in map(lambda a: a.get_type(), get_p_gui()): + + + + if len(get_p_gui()) > len(bus_structure): + times = range(len(bus_structure), len(get_p_gui())); + for i in times: + for connect in get_p_gui()[-1].get_connections(): + block.get_parent().remove_element(connect); + get_p().remove(get_p_gui()[-1]); + elif len(get_p_gui()) < len(bus_structure): + n = {'name':'bus','type':'bus'}; + if True in map(lambda a: isinstance(a.get_nports(), int), get_p()): + n['nports'] = str(1); + + times = range(len(get_p_gui()), len(bus_structure)); + + for i in times: + n['key'] = str(len(get_p())); + n = odict(n); + port = block.get_parent().get_parent().Port(block=block, n=n, dir=direc); + get_p().append(port); + + + + for child in self.get_children(): child.rewrite() + refactor_bus_structure(); - def get_option(self, key): - """ - Get the option for a given key. - The option comes from the special options block. - - Args: - key: the param key for the options block - - Returns: - the value held by that param - """ - return self._options_block.get_param(key).get_evaluated() + def get_option(self, key): + """ + Get the option for a given key. + The option comes from the special options block. + + Args: + key: the param key for the options block + + Returns: + the value held by that param + """ + return self._options_block.get_param(key).get_evaluated() - def is_flow_graph(self): return True + def is_flow_graph(self): return True - ############################################## - ## Access Elements - ############################################## - def get_block(self, id): return filter(lambda b: b.get_id() == id, self.get_blocks())[0] - def get_blocks_unordered(self): return filter(lambda e: e.is_block(), self.get_elements()) - def get_blocks(self): - blocks = self.get_blocks_unordered(); - for i in range(len(blocks)): - if blocks[i].get_key() == 'variable': - blk = blocks[i]; - blocks.remove(blk); - blocks.insert(1, blk); - return blocks; - def get_connections(self): return filter(lambda e: e.is_connection(), self.get_elements()) - def get_children(self): return self.get_elements() - def get_elements(self): - """ - Get a list of all the elements. - Always ensure that the options block is in the list (only once). - - Returns: - the element list - """ - options_block_count = self._elements.count(self._options_block) - if not options_block_count: - self._elements.append(self._options_block) - for i in range(options_block_count-1): - self._elements.remove(self._options_block) - return self._elements + ############################################## + ## Access Elements + ############################################## + def get_block(self, id): return filter(lambda b: b.get_id() == id, self.get_blocks())[0] + def get_blocks_unordered(self): return filter(lambda e: e.is_block(), self.get_elements()) + def get_blocks(self): + blocks = self.get_blocks_unordered(); + for i in range(len(blocks)): + if blocks[i].get_key() == 'variable': + blk = blocks[i]; + blocks.remove(blk); + blocks.insert(1, blk); + return blocks; + def get_connections(self): return filter(lambda e: e.is_connection(), self.get_elements()) + def get_children(self): return self.get_elements() + def get_elements(self): + """ + Get a list of all the elements. + Always ensure that the options block is in the list (only once). + + Returns: + the element list + """ + options_block_count = self._elements.count(self._options_block) + if not options_block_count: + self._elements.append(self._options_block) + for i in range(options_block_count-1): + self._elements.remove(self._options_block) + return self._elements - def get_enabled_blocks(self): - """ - Get a list of all blocks that are enabled. - - Returns: - a list of blocks - """ - return filter(lambda b: b.get_enabled(), self.get_blocks()) + def get_enabled_blocks(self): + """ + Get a list of all blocks that are enabled. + + Returns: + a list of blocks + """ + return filter(lambda b: b.get_enabled(), self.get_blocks()) - def get_enabled_connections(self): - """ - Get a list of all connections that are enabled. - - Returns: - a list of connections - """ - return filter(lambda c: c.get_enabled(), self.get_connections()) + def get_enabled_connections(self): + """ + Get a list of all connections that are enabled. + + Returns: + a list of connections + """ + return filter(lambda c: c.get_enabled(), self.get_connections()) - def get_new_block(self, key): - """ - Get a new block of the specified key. - Add the block to the list of elements. - - Args: - key: the block key - - Returns: - the new block or None if not found - """ - - if key not in self.get_parent().get_block_keys(): return None - block = self.get_parent().get_new_block(self, key) - self.get_elements().append(block); - if block._bussify_sink: - block.bussify({'name':'bus','type':'bus'}, 'sink') - if block._bussify_source: - block.bussify({'name':'bus','type':'bus'}, 'source') - return block; + def get_new_block(self, key): + """ + Get a new block of the specified key. + Add the block to the list of elements. + + Args: + key: the block key + + Returns: + the new block or None if not found + """ + if key not in self.get_parent().get_block_keys(): return None + block = self.get_parent().get_new_block(self, key) + self.get_elements().append(block); + if block._bussify_sink: + block.bussify({'name':'bus','type':'bus'}, 'sink') + if block._bussify_source: + block.bussify({'name':'bus','type':'bus'}, 'source') + return block; - def connect(self, porta, portb): - """ - Create a connection between porta and portb. - - Args: - porta: a port - portb: another port - @throw Exception bad connection - - Returns: - the new connection - """ - - connection = self.get_parent().Connection(flow_graph=self, porta=porta, portb=portb) - - - self.get_elements().append(connection) - return connection + def connect(self, porta, portb): + """ + Create a connection between porta and portb. + + Args: + porta: a port + portb: another port + @throw Exception bad connection + + Returns: + the new connection + """ + connection = self.get_parent().Connection(flow_graph=self, porta=porta, portb=portb) + self.get_elements().append(connection) + return connection - def remove_element(self, element): - """ - Remove the element from the list of elements. - If the element is a port, remove the whole block. - If the element is a block, remove its connections. - If the element is a connection, just remove the connection. - """ + def remove_element(self, element): + """ + Remove the element from the list of elements. + If the element is a port, remove the whole block. + If the element is a block, remove its connections. + If the element is a connection, just remove the connection. + """ + if element not in self.get_elements(): return + #found a port, set to parent signal block + if element.is_port(): + element = element.get_parent() + #remove block, remove all involved connections + if element.is_block(): + for port in element.get_ports(): + map(self.remove_element, port.get_connections()) + if element.is_connection(): + if element.is_bus(): + cons_list = [] + for i in map(lambda a: a.get_connections(), element.get_source().get_associated_ports()): + cons_list.extend(i); + map(self.remove_element, cons_list); + self.get_elements().remove(element) - if element not in self.get_elements(): return - #found a port, set to parent signal block - if element.is_port(): - element = element.get_parent() - #remove block, remove all involved connections - if element.is_block(): - for port in element.get_ports(): - map(self.remove_element, port.get_connections()) - if element.is_connection(): - if element.is_bus(): - cons_list = [] - for i in map(lambda a: a.get_connections(), element.get_source().get_associated_ports()): - cons_list.extend(i); - map(self.remove_element, cons_list); - self.get_elements().remove(element) - + def evaluate(self, expr): + """ + Evaluate the expression. + + Args: + expr: the string expression + @throw NotImplementedError + """ + raise NotImplementedError - def evaluate(self, expr): - """ - Evaluate the expression. - - Args: - expr: the string expression - @throw NotImplementedError - """ - raise NotImplementedError + ############################################## + ## Import/Export Methods + ############################################## + def export_data(self): + """ + Export this flow graph to nested data. + Export all block and connection data. + + Returns: + a nested data odict + """ + import time + n = odict() + n['timestamp'] = time.ctime() + n['block'] = [block.export_data() for block in self.get_blocks()] + n['connection'] = [connection.export_data() for connection in self.get_connections()] + return odict({'flow_graph': n}) - ############################################## - ## Import/Export Methods - ############################################## - def export_data(self): - """ - Export this flow graph to nested data. - Export all block and connection data. - - Returns: - a nested data odict - """ - import time - n = odict() - n['timestamp'] = time.ctime() - n['block'] = [block.export_data() for block in self.get_blocks()] - n['connection'] = [connection.export_data() for connection in self.get_connections()] - return odict({'flow_graph': n}) - - def import_data(self, n=None): - """ - Import blocks and connections into this flow graph. - Clear this flowgraph of all previous blocks and connections. - Any blocks or connections in error will be ignored. - - Args: - n: the nested data odict - """ - #remove previous elements - self._elements = list() - #use blank data if none provided - fg_n = n and n.find('flow_graph') or odict() - blocks_n = fg_n.findall('block') - connections_n = fg_n.findall('connection') - #create option block - self._options_block = self.get_parent().get_new_block(self, 'options') - #build the blocks - for block_n in blocks_n: - key = block_n.find('key') - if key == 'options': block = self._options_block - else: block = self.get_new_block(key) - #only load the block when the block key was valid - if block: block.import_data(block_n) - else: Messages.send_error_load('Block key "%s" not found in %s'%(key, self.get_parent())) - - #build the connections - for connection_n in connections_n: - #try to make the connection - try: - #get the block ids - source_block_id = connection_n.find('source_block_id') - sink_block_id = connection_n.find('sink_block_id') - #get the port keys - source_key = connection_n.find('source_key') - sink_key = connection_n.find('sink_key') - #verify the blocks - block_ids = map(lambda b: b.get_id(), self.get_blocks()) - if source_block_id not in block_ids: - raise LookupError('source block id "%s" not in block ids'%source_block_id) - if sink_block_id not in block_ids: - raise LookupError('sink block id "%s" not in block ids'%sink_block_id) - #get the blocks - source_block = self.get_block(source_block_id) - sink_block = self.get_block(sink_block_id) - #verify the ports - if source_key not in source_block.get_source_keys(): - raise LookupError('source key "%s" not in source block keys'%source_key) - if sink_key not in sink_block.get_sink_keys(): - raise LookupError('sink key "%s" not in sink block keys'%sink_key) - #get the ports - source = source_block.get_source(source_key) - sink = sink_block.get_sink(sink_key) - #build the connection - self.connect(source, sink) - except LookupError, e: Messages.send_error_load( - 'Connection between %s(%s) and %s(%s) could not be made.\n\t%s'%( - source_block_id, source_key, sink_block_id, sink_key, e - ) - ) - self.rewrite() #global rewrite + def import_data(self, n=None): + """ + Import blocks and connections into this flow graph. + Clear this flowgraph of all previous blocks and connections. + Any blocks or connections in error will be ignored. + + Args: + n: the nested data odict + """ + #remove previous elements + self._elements = list() + #use blank data if none provided + fg_n = n and n.find('flow_graph') or odict() + blocks_n = fg_n.findall('block') + connections_n = fg_n.findall('connection') + #create option block + self._options_block = self.get_parent().get_new_block(self, 'options') + #build the blocks + for block_n in blocks_n: + key = block_n.find('key') + if key == 'options': block = self._options_block + else: block = self.get_new_block(key) + #only load the block when the block key was valid + if block: block.import_data(block_n) + else: Messages.send_error_load('Block key "%s" not found in %s'%(key, self.get_parent())) + #build the connections + for connection_n in connections_n: + #try to make the connection + try: + #get the block ids + source_block_id = connection_n.find('source_block_id') + sink_block_id = connection_n.find('sink_block_id') + #get the port keys + source_key = connection_n.find('source_key') + sink_key = connection_n.find('sink_key') + #verify the blocks + block_ids = map(lambda b: b.get_id(), self.get_blocks()) + if source_block_id not in block_ids: + raise LookupError('source block id "%s" not in block ids'%source_block_id) + if sink_block_id not in block_ids: + raise LookupError('sink block id "%s" not in block ids'%sink_block_id) + #get the blocks + source_block = self.get_block(source_block_id) + sink_block = self.get_block(sink_block_id) + #verify the ports + if source_key not in source_block.get_source_keys(): + raise LookupError('source key "%s" not in source block keys'%source_key) + if sink_key not in sink_block.get_sink_keys(): + raise LookupError('sink key "%s" not in sink block keys'%sink_key) + #get the ports + source = source_block.get_source(source_key) + sink = sink_block.get_sink(sink_key) + #build the connection + self.connect(source, sink) + except LookupError, e: Messages.send_error_load( + 'Connection between %s(%s) and %s(%s) could not be made.\n\t%s'%( + source_block_id, source_key, sink_block_id, sink_key, e + ) + ) + self.rewrite() #global rewrite diff --git a/grc/base/Param.py b/grc/base/Param.py index 76a74d3ed7..8b8362ac1a 100644 --- a/grc/base/Param.py +++ b/grc/base/Param.py @@ -22,166 +22,166 @@ from Element import Element def _get_keys(lst): return [elem.get_key() for elem in lst] def _get_elem(lst, key): - try: return lst[_get_keys(lst).index(key)] - except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst)) + try: return lst[_get_keys(lst).index(key)] + except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst)) class Option(Element): - def __init__(self, param, n): - Element.__init__(self, param) - self._name = n.find('name') - self._key = n.find('key') - self._opts = dict() - opts = n.findall('opt') - #test against opts when non enum - if not self.get_parent().is_enum() and opts: - raise Exception, 'Options for non-enum types cannot have sub-options' - #extract opts - for opt in opts: - #separate the key:value - try: key, value = opt.split(':') - except: raise Exception, 'Error separating "%s" into key:value'%opt - #test against repeated keys - if self._opts.has_key(key): - raise Exception, 'Key "%s" already exists in option'%key - #store the option - self._opts[key] = value - - def __str__(self): return 'Option %s(%s)'%(self.get_name(), self.get_key()) - def get_name(self): return self._name - def get_key(self): return self._key - - ############################################## - # Access Opts - ############################################## - def get_opt_keys(self): return self._opts.keys() - def get_opt(self, key): return self._opts[key] - def get_opts(self): return self._opts.values() + def __init__(self, param, n): + Element.__init__(self, param) + self._name = n.find('name') + self._key = n.find('key') + self._opts = dict() + opts = n.findall('opt') + #test against opts when non enum + if not self.get_parent().is_enum() and opts: + raise Exception, 'Options for non-enum types cannot have sub-options' + #extract opts + for opt in opts: + #separate the key:value + try: key, value = opt.split(':') + except: raise Exception, 'Error separating "%s" into key:value'%opt + #test against repeated keys + if self._opts.has_key(key): + raise Exception, 'Key "%s" already exists in option'%key + #store the option + self._opts[key] = value + + def __str__(self): return 'Option %s(%s)'%(self.get_name(), self.get_key()) + def get_name(self): return self._name + def get_key(self): return self._key + + ############################################## + # Access Opts + ############################################## + def get_opt_keys(self): return self._opts.keys() + def get_opt(self, key): return self._opts[key] + def get_opts(self): return self._opts.values() class Param(Element): - def __init__(self, block, n): - """ - Make a new param from nested data. - - Args: - block: the parent element - n: the nested odict - """ - #grab the data - self._name = n.find('name') - self._key = n.find('key') - value = n.find('value') or '' - self._type = n.find('type') - self._hide = n.find('hide') or '' - #build the param - Element.__init__(self, block) - #create the Option objects from the n data - self._options = list() - for option in map(lambda o: Option(param=self, n=o), n.findall('option')): - key = option.get_key() - #test against repeated keys - if key in self.get_option_keys(): - raise Exception, 'Key "%s" already exists in options'%key - #store the option - self.get_options().append(option) - #test the enum options - if self.is_enum(): - #test against options with identical keys - if len(set(self.get_option_keys())) != len(self.get_options()): - raise Exception, 'Options keys "%s" are not unique.'%self.get_option_keys() - #test against inconsistent keys in options - opt_keys = self.get_options()[0].get_opt_keys() - for option in self.get_options(): - if set(opt_keys) != set(option.get_opt_keys()): - raise Exception, 'Opt keys "%s" are not identical across all options.'%opt_keys - #if a value is specified, it must be in the options keys - self._value = value if value or value in self.get_option_keys() else self.get_option_keys()[0] - if self.get_value() not in self.get_option_keys(): - raise Exception, 'The value "%s" is not in the possible values of "%s".'%(self.get_value(), self.get_option_keys()) - else: self._value = value or '' - - def validate(self): - """ - Validate the param. - The value must be evaluated and type must a possible type. - """ - Element.validate(self) - if self.get_type() not in self.get_types(): - self.add_error_message('Type "%s" is not a possible type.'%self.get_type()) - - def get_evaluated(self): raise NotImplementedError - - def to_code(self): - """ - Convert the value to code. - @throw NotImplementedError - """ - raise NotImplementedError - - def get_types(self): - """ - Get a list of all possible param types. - @throw NotImplementedError - """ - raise NotImplementedError - - def get_color(self): return '#FFFFFF' - def __str__(self): return 'Param - %s(%s)'%(self.get_name(), self.get_key()) - def is_param(self): return True - def get_name(self): return self._name - def get_key(self): return self._key - def get_hide(self): return self.get_parent().resolve_dependencies(self._hide).strip() - - def get_value(self): - value = self._value - if self.is_enum() and value not in self.get_option_keys(): - value = self.get_option_keys()[0] - self.set_value(value) - return value - - def set_value(self, value): self._value = str(value) #must be a string - - def get_type(self): return self.get_parent().resolve_dependencies(self._type) - def is_enum(self): return self._type == 'enum' - - def __repr__(self): - """ - Get the repr (nice string format) for this param. - Just return the value (special case enum). - Derived classes can handle complex formatting. - - Returns: - the string representation - """ - if self.is_enum(): return self.get_option(self.get_value()).get_name() - return self.get_value() - - ############################################## - # Access Options - ############################################## - def get_option_keys(self): return _get_keys(self.get_options()) - def get_option(self, key): return _get_elem(self.get_options(), key) - def get_options(self): return self._options - - ############################################## - # Access Opts - ############################################## - def get_opt_keys(self): return self.get_option(self.get_value()).get_opt_keys() - def get_opt(self, key): return self.get_option(self.get_value()).get_opt(key) - def get_opts(self): return self.get_option(self.get_value()).get_opts() - - ############################################## - ## Import/Export Methods - ############################################## - def export_data(self): - """ - Export this param's key/value. - - Returns: - a nested data odict - """ - n = odict() - n['key'] = self.get_key() - n['value'] = self.get_value() - return n + def __init__(self, block, n): + """ + Make a new param from nested data. + + Args: + block: the parent element + n: the nested odict + """ + #grab the data + self._name = n.find('name') + self._key = n.find('key') + value = n.find('value') or '' + self._type = n.find('type') + self._hide = n.find('hide') or '' + #build the param + Element.__init__(self, block) + #create the Option objects from the n data + self._options = list() + for option in map(lambda o: Option(param=self, n=o), n.findall('option')): + key = option.get_key() + #test against repeated keys + if key in self.get_option_keys(): + raise Exception, 'Key "%s" already exists in options'%key + #store the option + self.get_options().append(option) + #test the enum options + if self.is_enum(): + #test against options with identical keys + if len(set(self.get_option_keys())) != len(self.get_options()): + raise Exception, 'Options keys "%s" are not unique.'%self.get_option_keys() + #test against inconsistent keys in options + opt_keys = self.get_options()[0].get_opt_keys() + for option in self.get_options(): + if set(opt_keys) != set(option.get_opt_keys()): + raise Exception, 'Opt keys "%s" are not identical across all options.'%opt_keys + #if a value is specified, it must be in the options keys + self._value = value if value or value in self.get_option_keys() else self.get_option_keys()[0] + if self.get_value() not in self.get_option_keys(): + raise Exception, 'The value "%s" is not in the possible values of "%s".'%(self.get_value(), self.get_option_keys()) + else: self._value = value or '' + + def validate(self): + """ + Validate the param. + The value must be evaluated and type must a possible type. + """ + Element.validate(self) + if self.get_type() not in self.get_types(): + self.add_error_message('Type "%s" is not a possible type.'%self.get_type()) + + def get_evaluated(self): raise NotImplementedError + + def to_code(self): + """ + Convert the value to code. + @throw NotImplementedError + """ + raise NotImplementedError + + def get_types(self): + """ + Get a list of all possible param types. + @throw NotImplementedError + """ + raise NotImplementedError + + def get_color(self): return '#FFFFFF' + def __str__(self): return 'Param - %s(%s)'%(self.get_name(), self.get_key()) + def is_param(self): return True + def get_name(self): return self._name + def get_key(self): return self._key + def get_hide(self): return self.get_parent().resolve_dependencies(self._hide).strip() + + def get_value(self): + value = self._value + if self.is_enum() and value not in self.get_option_keys(): + value = self.get_option_keys()[0] + self.set_value(value) + return value + + def set_value(self, value): self._value = str(value) #must be a string + + def get_type(self): return self.get_parent().resolve_dependencies(self._type) + def is_enum(self): return self._type == 'enum' + + def __repr__(self): + """ + Get the repr (nice string format) for this param. + Just return the value (special case enum). + Derived classes can handle complex formatting. + + Returns: + the string representation + """ + if self.is_enum(): return self.get_option(self.get_value()).get_name() + return self.get_value() + + ############################################## + # Access Options + ############################################## + def get_option_keys(self): return _get_keys(self.get_options()) + def get_option(self, key): return _get_elem(self.get_options(), key) + def get_options(self): return self._options + + ############################################## + # Access Opts + ############################################## + def get_opt_keys(self): return self.get_option(self.get_value()).get_opt_keys() + def get_opt(self, key): return self.get_option(self.get_value()).get_opt(key) + def get_opts(self): return self.get_option(self.get_value()).get_opts() + + ############################################## + ## Import/Export Methods + ############################################## + def export_data(self): + """ + Export this param's key/value. + + Returns: + a nested data odict + """ + n = odict() + n['key'] = self.get_key() + n['value'] = self.get_value() + return n diff --git a/grc/base/ParseXML.py b/grc/base/ParseXML.py index e8f9b0583e..56097395dd 100644 --- a/grc/base/ParseXML.py +++ b/grc/base/ParseXML.py @@ -21,99 +21,99 @@ from lxml import etree from . import odict class XMLSyntaxError(Exception): - def __init__(self, error_log): - self._error_log = error_log - def __str__(self): - return '\n'.join(map(str, self._error_log.filter_from_errors())) + def __init__(self, error_log): + self._error_log = error_log + def __str__(self): + return '\n'.join(map(str, self._error_log.filter_from_errors())) def validate_dtd(xml_file, dtd_file=None): - """ - Validate an xml file against its dtd. - - Args: - xml_file: the xml file - dtd_file: the optional dtd file - @throws Exception validation fails - """ - #perform parsing, use dtd validation if dtd file is not specified - parser = etree.XMLParser(dtd_validation=not dtd_file) - xml = etree.parse(xml_file, parser=parser) - if parser.error_log: raise XMLSyntaxError(parser.error_log) - #perform dtd validation if the dtd file is specified - if not dtd_file: return - dtd = etree.DTD(dtd_file) - if not dtd.validate(xml.getroot()): raise XMLSyntaxError(dtd.error_log) + """ + Validate an xml file against its dtd. + + Args: + xml_file: the xml file + dtd_file: the optional dtd file + @throws Exception validation fails + """ + #perform parsing, use dtd validation if dtd file is not specified + parser = etree.XMLParser(dtd_validation=not dtd_file) + xml = etree.parse(xml_file, parser=parser) + if parser.error_log: raise XMLSyntaxError(parser.error_log) + #perform dtd validation if the dtd file is specified + if not dtd_file: return + dtd = etree.DTD(dtd_file) + if not dtd.validate(xml.getroot()): raise XMLSyntaxError(dtd.error_log) def from_file(xml_file): - """ - Create nested data from an xml file using the from xml helper. - - Args: - xml_file: the xml file path - - Returns: - the nested data - """ - xml = etree.parse(xml_file).getroot() - return _from_file(xml) + """ + Create nested data from an xml file using the from xml helper. + + Args: + xml_file: the xml file path + + Returns: + the nested data + """ + xml = etree.parse(xml_file).getroot() + return _from_file(xml) def _from_file(xml): - """ - Recursivly parse the xml tree into nested data format. - - Args: - xml: the xml tree - - Returns: - the nested data - """ - tag = xml.tag - if not len(xml): - return odict({tag: xml.text or ''}) #store empty tags (text is None) as empty string - nested_data = odict() - for elem in xml: - key, value = _from_file(elem).items()[0] - if nested_data.has_key(key): nested_data[key].append(value) - else: nested_data[key] = [value] - #delistify if the length of values is 1 - for key, values in nested_data.iteritems(): - if len(values) == 1: nested_data[key] = values[0] + """ + Recursivly parse the xml tree into nested data format. + + Args: + xml: the xml tree + + Returns: + the nested data + """ + tag = xml.tag + if not len(xml): + return odict({tag: xml.text or ''}) #store empty tags (text is None) as empty string + nested_data = odict() + for elem in xml: + key, value = _from_file(elem).items()[0] + if nested_data.has_key(key): nested_data[key].append(value) + else: nested_data[key] = [value] + #delistify if the length of values is 1 + for key, values in nested_data.iteritems(): + if len(values) == 1: nested_data[key] = values[0] - return odict({tag: nested_data}) + return odict({tag: nested_data}) def to_file(nested_data, xml_file): - """ - Write an xml file and use the to xml helper method to load it. - - Args: - nested_data: the nested data - xml_file: the xml file path - """ - xml = _to_file(nested_data)[0] - open(xml_file, 'w').write(etree.tostring(xml, xml_declaration=True, pretty_print=True)) + """ + Write an xml file and use the to xml helper method to load it. + + Args: + nested_data: the nested data + xml_file: the xml file path + """ + xml = _to_file(nested_data)[0] + open(xml_file, 'w').write(etree.tostring(xml, xml_declaration=True, pretty_print=True)) def _to_file(nested_data): - """ - Recursivly parse the nested data into xml tree format. - - Args: - nested_data: the nested data - - Returns: - the xml tree filled with child nodes - """ - nodes = list() - for key, values in nested_data.iteritems(): - #listify the values if not a list - if not isinstance(values, (list, set, tuple)): - values = [values] - for value in values: - node = etree.Element(key) - if isinstance(value, (str, unicode)): node.text = value - else: node.extend(_to_file(value)) - nodes.append(node) - return nodes + """ + Recursivly parse the nested data into xml tree format. + + Args: + nested_data: the nested data + + Returns: + the xml tree filled with child nodes + """ + nodes = list() + for key, values in nested_data.iteritems(): + #listify the values if not a list + if not isinstance(values, (list, set, tuple)): + values = [values] + for value in values: + node = etree.Element(key) + if isinstance(value, (str, unicode)): node.text = value + else: node.extend(_to_file(value)) + nodes.append(node) + return nodes if __name__ == '__main__': - """Use the main method to test parse xml's functions.""" - pass + """Use the main method to test parse xml's functions.""" + pass diff --git a/grc/base/Platform.py b/grc/base/Platform.py index f0f24a48a7..bb80e29552 100644 --- a/grc/base/Platform.py +++ b/grc/base/Platform.py @@ -30,159 +30,159 @@ from Constants import BLOCK_TREE_DTD, FLOW_GRAPH_DTD class Platform(_Element): - def __init__(self, name, version, key, - block_paths, block_dtd, default_flow_graph, generator, - license='', website=None, colors=[]): - """ - Make a platform from the arguments. - - Args: - name: the platform name - version: the version string - key: the unique platform key - block_paths: the file paths to blocks in this platform - block_dtd: the dtd validator for xml block wrappers - default_flow_graph: the default flow graph file path - generator: the generator class for this platform - colors: a list of title, color_spec tuples - license: a multi-line license (first line is copyright) - website: the website url for this platform - - Returns: - a platform object - """ - _Element.__init__(self) - self._name = name - self._version = version - self._key = key - self._license = license - self._website = website - self._block_paths = block_paths - self._block_dtd = block_dtd - self._default_flow_graph = default_flow_graph - self._generator = generator - self._colors = colors - #create a dummy flow graph for the blocks - self._flow_graph = _Element(self) - #search for *.xml files in the given search path - - self.loadblocks(); + def __init__(self, name, version, key, + block_paths, block_dtd, default_flow_graph, generator, + license='', website=None, colors=[]): + """ + Make a platform from the arguments. + + Args: + name: the platform name + version: the version string + key: the unique platform key + block_paths: the file paths to blocks in this platform + block_dtd: the dtd validator for xml block wrappers + default_flow_graph: the default flow graph file path + generator: the generator class for this platform + colors: a list of title, color_spec tuples + license: a multi-line license (first line is copyright) + website: the website url for this platform + + Returns: + a platform object + """ + _Element.__init__(self) + self._name = name + self._version = version + self._key = key + self._license = license + self._website = website + self._block_paths = block_paths + self._block_dtd = block_dtd + self._default_flow_graph = default_flow_graph + self._generator = generator + self._colors = colors + #create a dummy flow graph for the blocks + self._flow_graph = _Element(self) + #search for *.xml files in the given search path + + self.loadblocks(); - def loadblocks(self): - xml_files = list() - for block_path in self._block_paths: - if os.path.isfile(block_path): xml_files.append(block_path) - elif os.path.isdir(block_path): - for dirpath, dirnames, filenames in os.walk(block_path): - for filename in sorted(filter(lambda f: f.endswith('.xml'), filenames)): - xml_files.append(os.path.join(dirpath, filename)) - #load the blocks - self._blocks = odict() - self._blocks_n = odict() - self._block_tree_files = list() - for xml_file in xml_files: - try: #try to add the xml file as a block wrapper - ParseXML.validate_dtd(xml_file, self._block_dtd) - n = ParseXML.from_file(xml_file).find('block') - #inject block wrapper path - n['block_wrapper_path'] = xml_file - block = self.Block(self._flow_graph, n) - key = block.get_key() - #test against repeated keys - if key in self.get_block_keys(): - print >> sys.stderr, 'Warning: Block with key "%s" already exists.\n\tIgnoring: %s'%(key, xml_file) - #store the block - else: - self._blocks[key] = block - self._blocks_n[key] = n - except ParseXML.XMLSyntaxError, e: - try: #try to add the xml file as a block tree - ParseXML.validate_dtd(xml_file, BLOCK_TREE_DTD) - self._block_tree_files.append(xml_file) - except ParseXML.XMLSyntaxError, e: - print >> sys.stderr, 'Warning: Block validation failed:\n\t%s\n\tIgnoring: %s'%(e, xml_file) - except Exception, e: - print >> sys.stderr, 'Warning: Block loading failed:\n\t%s\n\tIgnoring: %s'%(e, xml_file) - - def parse_flow_graph(self, flow_graph_file): - """ - Parse a saved flow graph file. - Ensure that the file exists, and passes the dtd check. - - Args: - flow_graph_file: the flow graph file - - Returns: - nested data - @throws exception if the validation fails - """ - flow_graph_file = flow_graph_file or self._default_flow_graph - open(flow_graph_file, 'r') #test open - ParseXML.validate_dtd(flow_graph_file, FLOW_GRAPH_DTD) - return ParseXML.from_file(flow_graph_file) - - def load_block_tree(self, block_tree): - """ - Load a block tree with categories and blocks. - Step 1: Load all blocks from the xml specification. - Step 2: Load blocks with builtin category specifications. - - Args: - block_tree: the block tree object - """ - #recursive function to load categories and blocks - def load_category(cat_n, parent=[]): - #add this category - parent = parent + [cat_n.find('name')] - block_tree.add_block(parent) - #recursive call to load sub categories - map(lambda c: load_category(c, parent), cat_n.findall('cat')) - #add blocks in this category - for block_key in cat_n.findall('block'): - if block_key not in self.get_block_keys(): - print >> sys.stderr, 'Warning: Block key "%s" not found when loading category tree.'%(block_key) - continue - block = self.get_block(block_key) - #if it exists, the block's category overrides the block tree - if not block.get_category(): block_tree.add_block(parent, block) - #load the block tree - for block_tree_file in self._block_tree_files: - #recursivly add all blocks in the tree - load_category(ParseXML.from_file(block_tree_file).find('cat')) - #add all other blocks, use the catgory tag - for block in self.get_blocks(): - #blocks with empty categories are in the xml block tree or hidden - if not block.get_category(): continue - block_tree.add_block(block.get_category(), block) - - def __str__(self): return 'Platform - %s(%s)'%(self.get_key(), self.get_name()) - - def is_platform(self): return True - - def get_new_flow_graph(self): return self.FlowGraph(platform=self) - - def get_generator(self): return self._generator - - ############################################## - # Access Blocks - ############################################## - def get_block_keys(self): return self._blocks.keys() - def get_block(self, key): return self._blocks[key] - def get_blocks(self): return self._blocks.values() - def get_new_block(self, flow_graph, key): return self.Block(flow_graph, n=self._blocks_n[key]) - - def get_name(self): return self._name - def get_version(self): return self._version - def get_key(self): return self._key - def get_license(self): return self._license - def get_website(self): return self._website - def get_colors(self): return self._colors - - ############################################## - # Constructors - ############################################## - FlowGraph = _FlowGraph - Connection = _Connection - Block = _Block - Port = _Port - Param = _Param + def loadblocks(self): + xml_files = list() + for block_path in self._block_paths: + if os.path.isfile(block_path): xml_files.append(block_path) + elif os.path.isdir(block_path): + for dirpath, dirnames, filenames in os.walk(block_path): + for filename in sorted(filter(lambda f: f.endswith('.xml'), filenames)): + xml_files.append(os.path.join(dirpath, filename)) + #load the blocks + self._blocks = odict() + self._blocks_n = odict() + self._block_tree_files = list() + for xml_file in xml_files: + try: #try to add the xml file as a block wrapper + ParseXML.validate_dtd(xml_file, self._block_dtd) + n = ParseXML.from_file(xml_file).find('block') + #inject block wrapper path + n['block_wrapper_path'] = xml_file + block = self.Block(self._flow_graph, n) + key = block.get_key() + #test against repeated keys + if key in self.get_block_keys(): + print >> sys.stderr, 'Warning: Block with key "%s" already exists.\n\tIgnoring: %s'%(key, xml_file) + #store the block + else: + self._blocks[key] = block + self._blocks_n[key] = n + except ParseXML.XMLSyntaxError, e: + try: #try to add the xml file as a block tree + ParseXML.validate_dtd(xml_file, BLOCK_TREE_DTD) + self._block_tree_files.append(xml_file) + except ParseXML.XMLSyntaxError, e: + print >> sys.stderr, 'Warning: Block validation failed:\n\t%s\n\tIgnoring: %s'%(e, xml_file) + except Exception, e: + print >> sys.stderr, 'Warning: Block loading failed:\n\t%s\n\tIgnoring: %s'%(e, xml_file) + + def parse_flow_graph(self, flow_graph_file): + """ + Parse a saved flow graph file. + Ensure that the file exists, and passes the dtd check. + + Args: + flow_graph_file: the flow graph file + + Returns: + nested data + @throws exception if the validation fails + """ + flow_graph_file = flow_graph_file or self._default_flow_graph + open(flow_graph_file, 'r') #test open + ParseXML.validate_dtd(flow_graph_file, FLOW_GRAPH_DTD) + return ParseXML.from_file(flow_graph_file) + + def load_block_tree(self, block_tree): + """ + Load a block tree with categories and blocks. + Step 1: Load all blocks from the xml specification. + Step 2: Load blocks with builtin category specifications. + + Args: + block_tree: the block tree object + """ + #recursive function to load categories and blocks + def load_category(cat_n, parent=[]): + #add this category + parent = parent + [cat_n.find('name')] + block_tree.add_block(parent) + #recursive call to load sub categories + map(lambda c: load_category(c, parent), cat_n.findall('cat')) + #add blocks in this category + for block_key in cat_n.findall('block'): + if block_key not in self.get_block_keys(): + print >> sys.stderr, 'Warning: Block key "%s" not found when loading category tree.'%(block_key) + continue + block = self.get_block(block_key) + #if it exists, the block's category overrides the block tree + if not block.get_category(): block_tree.add_block(parent, block) + #load the block tree + for block_tree_file in self._block_tree_files: + #recursivly add all blocks in the tree + load_category(ParseXML.from_file(block_tree_file).find('cat')) + #add all other blocks, use the catgory tag + for block in self.get_blocks(): + #blocks with empty categories are in the xml block tree or hidden + if not block.get_category(): continue + block_tree.add_block(block.get_category(), block) + + def __str__(self): return 'Platform - %s(%s)'%(self.get_key(), self.get_name()) + + def is_platform(self): return True + + def get_new_flow_graph(self): return self.FlowGraph(platform=self) + + def get_generator(self): return self._generator + + ############################################## + # Access Blocks + ############################################## + def get_block_keys(self): return self._blocks.keys() + def get_block(self, key): return self._blocks[key] + def get_blocks(self): return self._blocks.values() + def get_new_block(self, flow_graph, key): return self.Block(flow_graph, n=self._blocks_n[key]) + + def get_name(self): return self._name + def get_version(self): return self._version + def get_key(self): return self._key + def get_license(self): return self._license + def get_website(self): return self._website + def get_colors(self): return self._colors + + ############################################## + # Constructors + ############################################## + FlowGraph = _FlowGraph + Connection = _Connection + Block = _Block + Port = _Port + Param = _Param diff --git a/grc/base/Port.py b/grc/base/Port.py index 1b2acb0582..b7de5301f1 100644 --- a/grc/base/Port.py +++ b/grc/base/Port.py @@ -21,97 +21,96 @@ from Element import Element class Port(Element): - def __init__(self, block, n, dir): - """ - Make a new port from nested data. - - Args: - block: the parent element - n: the nested odict - dir: the direction source or sink - """ - #build the port - Element.__init__(self, block) - #grab the data - self._name = n['name'] - self._key = n['key'] - self._type = n['type'] - self._dir = dir + def __init__(self, block, n, dir): + """ + Make a new port from nested data. + + Args: + block: the parent element + n: the nested odict + dir: the direction source or sink + """ + #build the port + Element.__init__(self, block) + #grab the data + self._name = n['name'] + self._key = n['key'] + self._type = n['type'] + self._dir = dir - - def validate(self): - """ - Validate the port. - The port must be non-empty and type must a possible type. - """ - Element.validate(self) - if self.get_type() not in self.get_types(): - self.add_error_message('Type "%s" is not a possible type.'%self.get_type()) + def validate(self): + """ + Validate the port. + The port must be non-empty and type must a possible type. + """ + Element.validate(self) + if self.get_type() not in self.get_types(): + self.add_error_message('Type "%s" is not a possible type.'%self.get_type()) - def __str__(self): - if self.is_source(): - return 'Source - %s(%s)'%(self.get_name(), self.get_key()) - if self.is_sink(): - return 'Sink - %s(%s)'%(self.get_name(), self.get_key()) + def __str__(self): + if self.is_source(): + return 'Source - %s(%s)'%(self.get_name(), self.get_key()) + if self.is_sink(): + return 'Sink - %s(%s)'%(self.get_name(), self.get_key()) - def get_types(self): - """ - Get a list of all possible port types. - @throw NotImplementedError - """ - raise NotImplementedError + def get_types(self): + """ + Get a list of all possible port types. + @throw NotImplementedError + """ + raise NotImplementedError - def is_port(self): return True - def get_color(self): return '#FFFFFF' - def get_name(self): - number = '' - if self.get_type() == 'bus': - busses = filter(lambda a: a._dir == self._dir, self.get_parent().get_ports_gui()); - - number = str(busses.index(self)) + '#' + str(len(self.get_associated_ports())); - return self._name + number + def is_port(self): return True + def get_color(self): return '#FFFFFF' + def get_name(self): + number = '' + if self.get_type() == 'bus': + busses = filter(lambda a: a._dir == self._dir, self.get_parent().get_ports_gui()); + + number = str(busses.index(self)) + '#' + str(len(self.get_associated_ports())); + return self._name + number - def get_key(self): return self._key - def is_sink(self): return self._dir == 'sink' - def is_source(self): return self._dir == 'source' - def get_type(self): return self.get_parent().resolve_dependencies(self._type) + def get_key(self): return self._key + def is_sink(self): return self._dir == 'sink' + def is_source(self): return self._dir == 'source' + def get_type(self): return self.get_parent().resolve_dependencies(self._type) - def get_connections(self): - """ - Get all connections that use this port. - - Returns: - a list of connection objects - """ - connections = self.get_parent().get_parent().get_connections() - connections = filter(lambda c: c.get_source() is self or c.get_sink() is self, connections) - return connections + def get_connections(self): + """ + Get all connections that use this port. + + Returns: + a list of connection objects + """ + connections = self.get_parent().get_parent().get_connections() + connections = filter(lambda c: c.get_source() is self or c.get_sink() is self, connections) + return connections - def get_enabled_connections(self): - """ - Get all enabled connections that use this port. - - Returns: - a list of connection objects - """ - return filter(lambda c: c.get_enabled(), self.get_connections()) - - def get_associated_ports(self): - if not self.get_type() == 'bus': - return [self]; - else: - if self.is_source(): - get_p = self.get_parent().get_sources; - bus_structure = self.get_parent().current_bus_structure['source']; - direc = 'source' - else: - get_p = self.get_parent().get_sinks; - bus_structure = self.get_parent().current_bus_structure['sink']; - direc = 'sink' - - ports = [i for i in get_p() if not i.get_type() == 'bus']; - if bus_structure: - busses = [i for i in get_p() if i.get_type() == 'bus']; - bus_index = busses.index(self); - ports = filter(lambda a: ports.index(a) in bus_structure[bus_index], ports); - return ports; + def get_enabled_connections(self): + """ + Get all enabled connections that use this port. + + Returns: + a list of connection objects + """ + return filter(lambda c: c.get_enabled(), self.get_connections()) + + def get_associated_ports(self): + if not self.get_type() == 'bus': + return [self]; + else: + if self.is_source(): + get_p = self.get_parent().get_sources; + bus_structure = self.get_parent().current_bus_structure['source']; + direc = 'source' + else: + get_p = self.get_parent().get_sinks; + bus_structure = self.get_parent().current_bus_structure['sink']; + direc = 'sink' + + ports = [i for i in get_p() if not i.get_type() == 'bus']; + if bus_structure: + busses = [i for i in get_p() if i.get_type() == 'bus']; + bus_index = busses.index(self); + ports = filter(lambda a: ports.index(a) in bus_structure[bus_index], ports); + return ports; diff --git a/grc/base/block_tree.dtd b/grc/base/block_tree.dtd index 7d4a13ccc3..9e23576477 100644 --- a/grc/base/block_tree.dtd +++ b/grc/base/block_tree.dtd @@ -17,9 +17,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA --> <!-- - block_tree.dtd - Josh Blum - The document type definition for a block tree category listing. + block_tree.dtd + Josh Blum + The document type definition for a block tree category listing. --> <!ELEMENT cat (name, cat*, block*)> <!ELEMENT name (#PCDATA)> diff --git a/grc/base/flow_graph.dtd b/grc/base/flow_graph.dtd index becfc21e9b..bdfe1dc059 100644 --- a/grc/base/flow_graph.dtd +++ b/grc/base/flow_graph.dtd @@ -17,9 +17,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA --> <!-- - flow_graph.dtd - Josh Blum - The document type definition for flow graph xml files. + flow_graph.dtd + Josh Blum + The document type definition for flow graph xml files. --> <!ELEMENT flow_graph (timestamp?, block*, connection*)> <!-- optional timestamp --> <!ELEMENT timestamp (#PCDATA)> diff --git a/grc/base/odict.py b/grc/base/odict.py index 302583163b..0c03d753f6 100644 --- a/grc/base/odict.py +++ b/grc/base/odict.py @@ -21,85 +21,85 @@ from UserDict import DictMixin class odict(DictMixin): - def __init__(self, d={}): - self._keys = list(d.keys()) - self._data = dict(d.copy()) + def __init__(self, d={}): + self._keys = list(d.keys()) + self._data = dict(d.copy()) - def __setitem__(self, key, value): - if key not in self._data: - self._keys.append(key) - self._data[key] = value + def __setitem__(self, key, value): + if key not in self._data: + self._keys.append(key) + self._data[key] = value - def __getitem__(self, key): - return self._data[key] + def __getitem__(self, key): + return self._data[key] - def __delitem__(self, key): - del self._data[key] - self._keys.remove(key) + def __delitem__(self, key): + del self._data[key] + self._keys.remove(key) - def keys(self): - return list(self._keys) + def keys(self): + return list(self._keys) - def copy(self): - copy_dict = odict() - copy_dict._data = self._data.copy() - copy_dict._keys = list(self._keys) - return copy_dict + def copy(self): + copy_dict = odict() + copy_dict._data = self._data.copy() + copy_dict._keys = list(self._keys) + return copy_dict - def insert_after(self, pos_key, key, val): - """ - Insert the new key, value entry after the entry given by the position key. - If the positional key is None, insert at the end. - - Args: - pos_key: the positional key - key: the key for the new entry - val: the value for the new entry - """ - index = (pos_key is None) and len(self._keys) or self._keys.index(pos_key) - if key in self._keys: raise KeyError('Cannot insert, key "%s" already exists'%str(key)) - self._keys.insert(index+1, key) - self._data[key] = val + def insert_after(self, pos_key, key, val): + """ + Insert the new key, value entry after the entry given by the position key. + If the positional key is None, insert at the end. + + Args: + pos_key: the positional key + key: the key for the new entry + val: the value for the new entry + """ + index = (pos_key is None) and len(self._keys) or self._keys.index(pos_key) + if key in self._keys: raise KeyError('Cannot insert, key "%s" already exists'%str(key)) + self._keys.insert(index+1, key) + self._data[key] = val - def insert_before(self, pos_key, key, val): - """ - Insert the new key, value entry before the entry given by the position key. - If the positional key is None, insert at the begining. - - Args: - pos_key: the positional key - key: the key for the new entry - val: the value for the new entry - """ - index = (pos_key is not None) and self._keys.index(pos_key) or 0 - if key in self._keys: raise KeyError('Cannot insert, key "%s" already exists'%str(key)) - self._keys.insert(index, key) - self._data[key] = val + def insert_before(self, pos_key, key, val): + """ + Insert the new key, value entry before the entry given by the position key. + If the positional key is None, insert at the begining. + + Args: + pos_key: the positional key + key: the key for the new entry + val: the value for the new entry + """ + index = (pos_key is not None) and self._keys.index(pos_key) or 0 + if key in self._keys: raise KeyError('Cannot insert, key "%s" already exists'%str(key)) + self._keys.insert(index, key) + self._data[key] = val - def find(self, key): - """ - Get the value for this key if exists. - - Args: - key: the key to search for - - Returns: - the value or None - """ - if self.has_key(key): return self[key] - return None + def find(self, key): + """ + Get the value for this key if exists. + + Args: + key: the key to search for + + Returns: + the value or None + """ + if self.has_key(key): return self[key] + return None - def findall(self, key): - """ - Get a list of values for this key. - - Args: - key: the key to search for - - Returns: - a list of values or empty list - """ - obj = self.find(key) - if obj is None: obj = list() - if isinstance(obj, list): return obj - return [obj] + def findall(self, key): + """ + Get a list of values for this key. + + Args: + key: the key to search for + + Returns: + a list of values or empty list + """ + obj = self.find(key) + if obj is None: obj = list() + if isinstance(obj, list): return obj + return [obj] diff --git a/grc/examples/xmlrpc/xmlrpc_client_script.py b/grc/examples/xmlrpc/xmlrpc_client_script.py index 8f00fa55d8..e96c4cbf83 100644 --- a/grc/examples/xmlrpc/xmlrpc_client_script.py +++ b/grc/examples/xmlrpc/xmlrpc_client_script.py @@ -9,15 +9,15 @@ s = xmlrpclib.Server("http://localhost:1234") #randomly change parameters of the sinusoid for i in range(10): - #generate random values - new_freq = random.uniform(0, 5000) - new_ampl = random.uniform(0, 2) - new_offset = random.uniform(-1, 1) - #set new values - time.sleep(1) - s.set_freq(new_freq) - time.sleep(1) - s.set_ampl(new_ampl) - time.sleep(1) - s.set_offset(new_offset) + #generate random values + new_freq = random.uniform(0, 5000) + new_ampl = random.uniform(0, 2) + new_offset = random.uniform(-1, 1) + #set new values + time.sleep(1) + s.set_freq(new_freq) + time.sleep(1) + s.set_ampl(new_ampl) + time.sleep(1) + s.set_offset(new_offset) diff --git a/grc/grc_gnuradio/blks2/__init__.py b/grc/grc_gnuradio/blks2/__init__.py index fde76f2563..e6941ab91b 100644 --- a/grc/grc_gnuradio/blks2/__init__.py +++ b/grc/grc_gnuradio/blks2/__init__.py @@ -20,7 +20,7 @@ from selector import selector, valve from packet import options, packet_encoder, packet_decoder, \ - packet_mod_b, packet_mod_s, packet_mod_i, packet_mod_f, packet_mod_c, \ - packet_demod_b, packet_demod_s, packet_demod_i, packet_demod_f, packet_demod_c + packet_mod_b, packet_mod_s, packet_mod_i, packet_mod_f, packet_mod_c, \ + packet_demod_b, packet_demod_s, packet_demod_i, packet_demod_f, packet_demod_c from error_rate import error_rate from tcp import tcp_source, tcp_sink diff --git a/grc/grc_gnuradio/blks2/error_rate.py b/grc/grc_gnuradio/blks2/error_rate.py index 7970f29bae..95ded7dadc 100644 --- a/grc/grc_gnuradio/blks2/error_rate.py +++ b/grc/grc_gnuradio/blks2/error_rate.py @@ -29,112 +29,112 @@ import numpy _1s_counts = [sum([1&(i>>j) for j in range(8)]) for i in range(2**8)] class input_watcher(_threading.Thread): - """ - Read samples from the message queue and hand them to the callback. - """ + """ + Read samples from the message queue and hand them to the callback. + """ - def __init__(self, msgq, callback): - self._msgq = msgq - self._callback = callback - _threading.Thread.__init__(self) - self.setDaemon(1) - self.keep_running = True - self.start() + def __init__(self, msgq, callback): + self._msgq = msgq + self._callback = callback + _threading.Thread.__init__(self) + self.setDaemon(1) + self.keep_running = True + self.start() - def run(self): - r = '' - while True: - msg = self._msgq.delete_head() - itemsize = int(msg.arg1()) - nitems = int(msg.arg2()) - s = r + msg.to_string() - i = (nitems-nitems%2)*itemsize - r = s[i:] - s = s[:i] - samples = numpy.fromstring(s, numpy.int8) - self._callback(samples) + def run(self): + r = '' + while True: + msg = self._msgq.delete_head() + itemsize = int(msg.arg1()) + nitems = int(msg.arg2()) + s = r + msg.to_string() + i = (nitems-nitems%2)*itemsize + r = s[i:] + s = s[:i] + samples = numpy.fromstring(s, numpy.int8) + self._callback(samples) class error_rate(gr.hier_block2): - """ - Sample the incoming data streams (byte) and calculate the bit or symbol error rate. - Write the running rate to the output data stream (float). - """ + """ + Sample the incoming data streams (byte) and calculate the bit or symbol error rate. + Write the running rate to the output data stream (float). + """ - def __init__(self, type='BER', win_size=default_win_size, bits_per_symbol=2): - """ - Error rate constructor. - - Args: - type: a string 'BER' or 'SER' - win_size: the number of samples to calculate over - bits_per_symbol: the number of information bits per symbol (BER only) - """ - #init - gr.hier_block2.__init__( - self, 'error_rate', - gr.io_signature(2, 2, gr.sizeof_char), - gr.io_signature(1, 1, gr.sizeof_float), - ) - assert type in ('BER', 'SER') - self._max_samples = win_size - self._bits_per_symbol = bits_per_symbol - #setup message queue - msg_source = blocks.message_source(gr.sizeof_float, 1) - self._msgq_source = msg_source.msgq() - msgq_sink = gr.msg_queue(2) - msg_sink = blocks.message_sink(gr.sizeof_char, msgq_sink, False) #False -> blocking - inter = blocks.interleave(gr.sizeof_char) - #start thread - self._num_errs = 0 - self._err_index = 0 - self._num_samps = 0 - self._err_array = numpy.zeros(self._max_samples, numpy.int8) - if type == 'BER': - input_watcher(msgq_sink, self._handler_ber) - elif type == 'SER': - input_watcher(msgq_sink, self._handler_ser) - #connect - self.connect(msg_source, self) - self.connect((self, 0), (inter, 0)) - self.connect((self, 1), (inter, 1)) - self.connect(inter, msg_sink) + def __init__(self, type='BER', win_size=default_win_size, bits_per_symbol=2): + """ + Error rate constructor. + + Args: + type: a string 'BER' or 'SER' + win_size: the number of samples to calculate over + bits_per_symbol: the number of information bits per symbol (BER only) + """ + #init + gr.hier_block2.__init__( + self, 'error_rate', + gr.io_signature(2, 2, gr.sizeof_char), + gr.io_signature(1, 1, gr.sizeof_float), + ) + assert type in ('BER', 'SER') + self._max_samples = win_size + self._bits_per_symbol = bits_per_symbol + #setup message queue + msg_source = blocks.message_source(gr.sizeof_float, 1) + self._msgq_source = msg_source.msgq() + msgq_sink = gr.msg_queue(2) + msg_sink = blocks.message_sink(gr.sizeof_char, msgq_sink, False) #False -> blocking + inter = blocks.interleave(gr.sizeof_char) + #start thread + self._num_errs = 0 + self._err_index = 0 + self._num_samps = 0 + self._err_array = numpy.zeros(self._max_samples, numpy.int8) + if type == 'BER': + input_watcher(msgq_sink, self._handler_ber) + elif type == 'SER': + input_watcher(msgq_sink, self._handler_ser) + #connect + self.connect(msg_source, self) + self.connect((self, 0), (inter, 0)) + self.connect((self, 1), (inter, 1)) + self.connect(inter, msg_sink) - def _handler_ber(self, samples): - num = len(samples)/2 - arr = numpy.zeros(num, numpy.float32) - for i in range(num): - old_err = self._err_array[self._err_index] - #record error - self._err_array[self._err_index] = _1s_counts[samples[i*2] ^ samples[i*2 + 1]] - self._num_errs = self._num_errs + self._err_array[self._err_index] - old_err - #increment index - self._err_index = (self._err_index + 1)%self._max_samples - self._num_samps = min(self._num_samps + 1, self._max_samples) - #write sample - arr[i] = float(self._num_errs)/float(self._num_samps*self._bits_per_symbol) - #write message - msg = blocks.message_from_string(arr.tostring(), 0, gr.sizeof_float, num) - self._msgq_source.insert_tail(msg) + def _handler_ber(self, samples): + num = len(samples)/2 + arr = numpy.zeros(num, numpy.float32) + for i in range(num): + old_err = self._err_array[self._err_index] + #record error + self._err_array[self._err_index] = _1s_counts[samples[i*2] ^ samples[i*2 + 1]] + self._num_errs = self._num_errs + self._err_array[self._err_index] - old_err + #increment index + self._err_index = (self._err_index + 1)%self._max_samples + self._num_samps = min(self._num_samps + 1, self._max_samples) + #write sample + arr[i] = float(self._num_errs)/float(self._num_samps*self._bits_per_symbol) + #write message + msg = blocks.message_from_string(arr.tostring(), 0, gr.sizeof_float, num) + self._msgq_source.insert_tail(msg) - def _handler_ser(self, samples): - num = len(samples)/2 - arr = numpy.zeros(num, numpy.float32) - for i in range(num): - old_err = self._err_array[self._err_index] - #record error - ref = samples[i*2] - res = samples[i*2 + 1] - if ref == res: - self._err_array[self._err_index] = 0 - else: - self._err_array[self._err_index] = 1 - #update number of errors - self._num_errs = self._num_errs + self._err_array[self._err_index] - old_err - #increment index - self._err_index = (self._err_index + 1)%self._max_samples - self._num_samps = min(self._num_samps + 1, self._max_samples) - #write sample - arr[i] = float(self._num_errs)/float(self._num_samps) - #write message - msg = blocks.message_from_string(arr.tostring(), 0, gr.sizeof_float, num) - self._msgq_source.insert_tail(msg) + def _handler_ser(self, samples): + num = len(samples)/2 + arr = numpy.zeros(num, numpy.float32) + for i in range(num): + old_err = self._err_array[self._err_index] + #record error + ref = samples[i*2] + res = samples[i*2 + 1] + if ref == res: + self._err_array[self._err_index] = 0 + else: + self._err_array[self._err_index] = 1 + #update number of errors + self._num_errs = self._num_errs + self._err_array[self._err_index] - old_err + #increment index + self._err_index = (self._err_index + 1)%self._max_samples + self._num_samps = min(self._num_samps + 1, self._max_samples) + #write sample + arr[i] = float(self._num_errs)/float(self._num_samps) + #write message + msg = blocks.message_from_string(arr.tostring(), 0, gr.sizeof_float, num) + self._msgq_source.insert_tail(msg) diff --git a/grc/grc_gnuradio/blks2/packet.py b/grc/grc_gnuradio/blks2/packet.py index 4c7bd235dd..0cf89fbad0 100644 --- a/grc/grc_gnuradio/blks2/packet.py +++ b/grc/grc_gnuradio/blks2/packet.py @@ -36,176 +36,176 @@ DEFAULT_THRESHOLD = 12 ## Options Class for OFDM ################################################## class options(object): - def __init__(self, **kwargs): - for key, value in kwargs.iteritems(): setattr(self, key, value) + def __init__(self, **kwargs): + for key, value in kwargs.iteritems(): setattr(self, key, value) ################################################## ## Packet Encoder ################################################## class _packet_encoder_thread(_threading.Thread): - def __init__(self, msgq, payload_length, send): - self._msgq = msgq - self._payload_length = payload_length - self._send = send - _threading.Thread.__init__(self) - self.setDaemon(1) - self.keep_running = True - self.start() - - def run(self): - sample = '' #residual sample - while self.keep_running: - msg = self._msgq.delete_head() #blocking read of message queue - sample = sample + msg.to_string() #get the body of the msg as a string - while len(sample) >= self._payload_length: - payload = sample[:self._payload_length] - sample = sample[self._payload_length:] - self._send(payload) + def __init__(self, msgq, payload_length, send): + self._msgq = msgq + self._payload_length = payload_length + self._send = send + _threading.Thread.__init__(self) + self.setDaemon(1) + self.keep_running = True + self.start() + + def run(self): + sample = '' #residual sample + while self.keep_running: + msg = self._msgq.delete_head() #blocking read of message queue + sample = sample + msg.to_string() #get the body of the msg as a string + while len(sample) >= self._payload_length: + payload = sample[:self._payload_length] + sample = sample[self._payload_length:] + self._send(payload) class packet_encoder(gr.hier_block2): - """ - Hierarchical block for wrapping packet-based modulators. - """ - - def __init__(self, samples_per_symbol, bits_per_symbol, access_code='', pad_for_usrp=True): - """ - packet_mod constructor. - - Args: - samples_per_symbol: number of samples per symbol - bits_per_symbol: number of bits per symbol - access_code: AKA sync vector - pad_for_usrp: If true, packets are padded such that they end up a multiple of 128 samples - payload_length: number of bytes in a data-stream slice - """ - #setup parameters - self._samples_per_symbol = samples_per_symbol - self._bits_per_symbol = bits_per_symbol - self._pad_for_usrp = pad_for_usrp - if not access_code: #get access code - access_code = packet_utils.default_access_code - if not packet_utils.is_1_0_string(access_code): - raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) - self._access_code = access_code - self._pad_for_usrp = pad_for_usrp - #create blocks - msg_source = blocks.message_source(gr.sizeof_char, DEFAULT_MSGQ_LIMIT) - self._msgq_out = msg_source.msgq() - #initialize hier2 - gr.hier_block2.__init__( - self, - "packet_encoder", - gr.io_signature(0, 0, 0), # Input signature - gr.io_signature(1, 1, gr.sizeof_char) # Output signature - ) - #connect - self.connect(msg_source, self) - - def send_pkt(self, payload): - """ - Wrap the payload in a packet and push onto the message queue. - - Args: - payload: string, data to send - """ - packet = packet_utils.make_packet( - payload, - self._samples_per_symbol, - self._bits_per_symbol, - self._access_code, - self._pad_for_usrp - ) - msg = gr.message_from_string(packet) - self._msgq_out.insert_tail(msg) + """ + Hierarchical block for wrapping packet-based modulators. + """ + + def __init__(self, samples_per_symbol, bits_per_symbol, access_code='', pad_for_usrp=True): + """ + packet_mod constructor. + + Args: + samples_per_symbol: number of samples per symbol + bits_per_symbol: number of bits per symbol + access_code: AKA sync vector + pad_for_usrp: If true, packets are padded such that they end up a multiple of 128 samples + payload_length: number of bytes in a data-stream slice + """ + #setup parameters + self._samples_per_symbol = samples_per_symbol + self._bits_per_symbol = bits_per_symbol + self._pad_for_usrp = pad_for_usrp + if not access_code: #get access code + access_code = packet_utils.default_access_code + if not packet_utils.is_1_0_string(access_code): + raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) + self._access_code = access_code + self._pad_for_usrp = pad_for_usrp + #create blocks + msg_source = blocks.message_source(gr.sizeof_char, DEFAULT_MSGQ_LIMIT) + self._msgq_out = msg_source.msgq() + #initialize hier2 + gr.hier_block2.__init__( + self, + "packet_encoder", + gr.io_signature(0, 0, 0), # Input signature + gr.io_signature(1, 1, gr.sizeof_char) # Output signature + ) + #connect + self.connect(msg_source, self) + + def send_pkt(self, payload): + """ + Wrap the payload in a packet and push onto the message queue. + + Args: + payload: string, data to send + """ + packet = packet_utils.make_packet( + payload, + self._samples_per_symbol, + self._bits_per_symbol, + self._access_code, + self._pad_for_usrp + ) + msg = gr.message_from_string(packet) + self._msgq_out.insert_tail(msg) ################################################## ## Packet Decoder ################################################## class _packet_decoder_thread(_threading.Thread): - def __init__(self, msgq, callback): - _threading.Thread.__init__(self) - self.setDaemon(1) - self._msgq = msgq - self.callback = callback - self.keep_running = True - self.start() - - def run(self): - while self.keep_running: - msg = self._msgq.delete_head() - ok, payload = packet_utils.unmake_packet(msg.to_string(), int(msg.arg1())) - if self.callback: - self.callback(ok, payload) + def __init__(self, msgq, callback): + _threading.Thread.__init__(self) + self.setDaemon(1) + self._msgq = msgq + self.callback = callback + self.keep_running = True + self.start() + + def run(self): + while self.keep_running: + msg = self._msgq.delete_head() + ok, payload = packet_utils.unmake_packet(msg.to_string(), int(msg.arg1())) + if self.callback: + self.callback(ok, payload) class packet_decoder(gr.hier_block2): - """ - Hierarchical block for wrapping packet-based demodulators. - """ - - def __init__(self, access_code='', threshold=-1, callback=None): - """ - packet_demod constructor. - - Args: - access_code: AKA sync vector - threshold: detect access_code with up to threshold bits wrong (0 -> use default) - callback: a function of args: ok, payload - """ - #access code - if not access_code: #get access code - access_code = packet_utils.default_access_code - if not packet_utils.is_1_0_string(access_code): - raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) - self._access_code = access_code - #threshold - if threshold < 0: threshold = DEFAULT_THRESHOLD - self._threshold = threshold - #blocks - msgq = gr.msg_queue(DEFAULT_MSGQ_LIMIT) #holds packets from the PHY - correlator = digital.correlate_access_code_bb(self._access_code, self._threshold) - framer_sink = digital.framer_sink_1(msgq) - #initialize hier2 - gr.hier_block2.__init__( - self, - "packet_decoder", - gr.io_signature(1, 1, gr.sizeof_char), # Input signature - gr.io_signature(0, 0, 0) # Output signature - ) - #connect - self.connect(self, correlator, framer_sink) - #start thread - _packet_decoder_thread(msgq, callback) + """ + Hierarchical block for wrapping packet-based demodulators. + """ + + def __init__(self, access_code='', threshold=-1, callback=None): + """ + packet_demod constructor. + + Args: + access_code: AKA sync vector + threshold: detect access_code with up to threshold bits wrong (0 -> use default) + callback: a function of args: ok, payload + """ + #access code + if not access_code: #get access code + access_code = packet_utils.default_access_code + if not packet_utils.is_1_0_string(access_code): + raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) + self._access_code = access_code + #threshold + if threshold < 0: threshold = DEFAULT_THRESHOLD + self._threshold = threshold + #blocks + msgq = gr.msg_queue(DEFAULT_MSGQ_LIMIT) #holds packets from the PHY + correlator = digital.correlate_access_code_bb(self._access_code, self._threshold) + framer_sink = digital.framer_sink_1(msgq) + #initialize hier2 + gr.hier_block2.__init__( + self, + "packet_decoder", + gr.io_signature(1, 1, gr.sizeof_char), # Input signature + gr.io_signature(0, 0, 0) # Output signature + ) + #connect + self.connect(self, correlator, framer_sink) + #start thread + _packet_decoder_thread(msgq, callback) ################################################## ## Packet Mod for OFDM Mod and Packet Encoder ################################################## class packet_mod_base(gr.hier_block2): - """ - Hierarchical block for wrapping packet source block. - """ - - def __init__(self, packet_source=None, payload_length=0): - if not payload_length: #get payload length - payload_length = DEFAULT_PAYLOAD_LEN - if payload_length%self._item_size_in != 0: #verify that packet length is a multiple of the stream size - raise ValueError, 'The payload length: "%d" is not a mutiple of the stream size: "%d".'%(payload_length, self._item_size_in) - #initialize hier2 - gr.hier_block2.__init__( - self, - "ofdm_mod", - gr.io_signature(1, 1, self._item_size_in), # Input signature - gr.io_signature(1, 1, packet_source._hb.output_signature().sizeof_stream_item(0)) # Output signature - ) - #create blocks - msgq = gr.msg_queue(DEFAULT_MSGQ_LIMIT) - msg_sink = blocks.message_sink(self._item_size_in, msgq, False) #False -> blocking - #connect - self.connect(self, msg_sink) - self.connect(packet_source, self) - #start thread - _packet_encoder_thread(msgq, payload_length, packet_source.send_pkt) + """ + Hierarchical block for wrapping packet source block. + """ + + def __init__(self, packet_source=None, payload_length=0): + if not payload_length: #get payload length + payload_length = DEFAULT_PAYLOAD_LEN + if payload_length%self._item_size_in != 0: #verify that packet length is a multiple of the stream size + raise ValueError, 'The payload length: "%d" is not a mutiple of the stream size: "%d".'%(payload_length, self._item_size_in) + #initialize hier2 + gr.hier_block2.__init__( + self, + "ofdm_mod", + gr.io_signature(1, 1, self._item_size_in), # Input signature + gr.io_signature(1, 1, packet_source._hb.output_signature().sizeof_stream_item(0)) # Output signature + ) + #create blocks + msgq = gr.msg_queue(DEFAULT_MSGQ_LIMIT) + msg_sink = blocks.message_sink(self._item_size_in, msgq, False) #False -> blocking + #connect + self.connect(self, msg_sink) + self.connect(packet_source, self) + #start thread + _packet_encoder_thread(msgq, payload_length, packet_source.send_pkt) class packet_mod_b(packet_mod_base): _item_size_in = gr.sizeof_char class packet_mod_s(packet_mod_base): _item_size_in = gr.sizeof_short @@ -217,32 +217,32 @@ class packet_mod_c(packet_mod_base): _item_size_in = gr.sizeof_gr_complex ## Packet Demod for OFDM Demod and Packet Decoder ################################################## class packet_demod_base(gr.hier_block2): - """ - Hierarchical block for wrapping packet sink block. - """ - - def __init__(self, packet_sink=None): - #initialize hier2 - gr.hier_block2.__init__( - self, - "ofdm_mod", - gr.io_signature(1, 1, packet_sink._hb.input_signature().sizeof_stream_item(0)), # Input signature - gr.io_signature(1, 1, self._item_size_out) # Output signature - ) - #create blocks - msg_source = blocks.message_source(self._item_size_out, DEFAULT_MSGQ_LIMIT) - self._msgq_out = msg_source.msgq() - #connect - self.connect(self, packet_sink) - self.connect(msg_source, self) - if packet_sink._hb.output_signature().sizeof_stream_item(0): - self.connect(packet_sink, + """ + Hierarchical block for wrapping packet sink block. + """ + + def __init__(self, packet_sink=None): + #initialize hier2 + gr.hier_block2.__init__( + self, + "ofdm_mod", + gr.io_signature(1, 1, packet_sink._hb.input_signature().sizeof_stream_item(0)), # Input signature + gr.io_signature(1, 1, self._item_size_out) # Output signature + ) + #create blocks + msg_source = blocks.message_source(self._item_size_out, DEFAULT_MSGQ_LIMIT) + self._msgq_out = msg_source.msgq() + #connect + self.connect(self, packet_sink) + self.connect(msg_source, self) + if packet_sink._hb.output_signature().sizeof_stream_item(0): + self.connect(packet_sink, blocks.null_sink(packet_sink._hb.output_signature().sizeof_stream_item(0))) - def recv_pkt(self, ok, payload): - msg = blocks.message_from_string(payload, 0, self._item_size_out, + def recv_pkt(self, ok, payload): + msg = blocks.message_from_string(payload, 0, self._item_size_out, len(payload)/self._item_size_out) - if ok: self._msgq_out.insert_tail(msg) + if ok: self._msgq_out.insert_tail(msg) class packet_demod_b(packet_demod_base): _item_size_out = gr.sizeof_char class packet_demod_s(packet_demod_base): _item_size_out = gr.sizeof_short diff --git a/grc/grc_gnuradio/blks2/selector.py b/grc/grc_gnuradio/blks2/selector.py index bc393f84b6..7ae664823b 100644 --- a/grc/grc_gnuradio/blks2/selector.py +++ b/grc/grc_gnuradio/blks2/selector.py @@ -23,120 +23,120 @@ from gnuradio import gr from gnuradio import blocks class selector(gr.hier_block2): - """A hier2 block with N inputs and M outputs, where data is only forwarded through input n to output m.""" - def __init__(self, item_size, num_inputs, num_outputs, input_index, output_index): - """ - Selector constructor. - - Args: - item_size: the size of the gr data stream in bytes - num_inputs: the number of inputs (integer) - num_outputs: the number of outputs (integer) - input_index: the index for the source data - output_index: the index for the destination data - """ - gr.hier_block2.__init__( - self, 'selector', - gr.io_signature(num_inputs, num_inputs, item_size), - gr.io_signature(num_outputs, num_outputs, item_size), - ) - #terminator blocks for unused inputs and outputs - self.input_terminators = [blocks.null_sink(item_size) for i in range(num_inputs)] - self.output_terminators = [blocks.head(item_size, 0) for i in range(num_outputs)] - self.copy = blocks.copy(item_size) - #connections - for i in range(num_inputs): self.connect((self, i), self.input_terminators[i]) - for i in range(num_outputs): self.connect(blocks.null_source(item_size), + """A hier2 block with N inputs and M outputs, where data is only forwarded through input n to output m.""" + def __init__(self, item_size, num_inputs, num_outputs, input_index, output_index): + """ + Selector constructor. + + Args: + item_size: the size of the gr data stream in bytes + num_inputs: the number of inputs (integer) + num_outputs: the number of outputs (integer) + input_index: the index for the source data + output_index: the index for the destination data + """ + gr.hier_block2.__init__( + self, 'selector', + gr.io_signature(num_inputs, num_inputs, item_size), + gr.io_signature(num_outputs, num_outputs, item_size), + ) + #terminator blocks for unused inputs and outputs + self.input_terminators = [blocks.null_sink(item_size) for i in range(num_inputs)] + self.output_terminators = [blocks.head(item_size, 0) for i in range(num_outputs)] + self.copy = blocks.copy(item_size) + #connections + for i in range(num_inputs): self.connect((self, i), self.input_terminators[i]) + for i in range(num_outputs): self.connect(blocks.null_source(item_size), self.output_terminators[i], (self, i)) - self.item_size = item_size - self.input_index = input_index - self.output_index = output_index - self.num_inputs = num_inputs - self.num_outputs = num_outputs - self._connect_current() + self.item_size = item_size + self.input_index = input_index + self.output_index = output_index + self.num_inputs = num_inputs + self.num_outputs = num_outputs + self._connect_current() - def _indexes_valid(self): - """ - Are the input and output indexes within range of the number of inputs and outputs? - - Returns: - true if input index and output index are in range - """ - return self.input_index in range(self.num_inputs) and self.output_index in range(self.num_outputs) + def _indexes_valid(self): + """ + Are the input and output indexes within range of the number of inputs and outputs? + + Returns: + true if input index and output index are in range + """ + return self.input_index in range(self.num_inputs) and self.output_index in range(self.num_outputs) - def _connect_current(self): - """If the input and output indexes are valid: - disconnect the blocks at the input and output index from their terminators, - and connect them to one another. Then connect the terminators to one another.""" - if self._indexes_valid(): - self.disconnect((self, self.input_index), self.input_terminators[self.input_index]) - self.disconnect(self.output_terminators[self.output_index], (self, self.output_index)) - self.connect((self, self.input_index), self.copy) - self.connect(self.copy, (self, self.output_index)) - self.connect(self.output_terminators[self.output_index], self.input_terminators[self.input_index]) + def _connect_current(self): + """If the input and output indexes are valid: + disconnect the blocks at the input and output index from their terminators, + and connect them to one another. Then connect the terminators to one another.""" + if self._indexes_valid(): + self.disconnect((self, self.input_index), self.input_terminators[self.input_index]) + self.disconnect(self.output_terminators[self.output_index], (self, self.output_index)) + self.connect((self, self.input_index), self.copy) + self.connect(self.copy, (self, self.output_index)) + self.connect(self.output_terminators[self.output_index], self.input_terminators[self.input_index]) - def _disconnect_current(self): - """If the input and output indexes are valid: - disconnect the blocks at the input and output index from one another, - and the terminators at the input and output index from one another. - Reconnect the blocks to the terminators.""" - if self._indexes_valid(): - self.disconnect((self, self.input_index), self.copy) - self.disconnect(self.copy, (self, self.output_index)) - self.disconnect(self.output_terminators[self.output_index], self.input_terminators[self.input_index]) - self.connect((self, self.input_index), self.input_terminators[self.input_index]) - self.connect(self.output_terminators[self.output_index], (self, self.output_index)) + def _disconnect_current(self): + """If the input and output indexes are valid: + disconnect the blocks at the input and output index from one another, + and the terminators at the input and output index from one another. + Reconnect the blocks to the terminators.""" + if self._indexes_valid(): + self.disconnect((self, self.input_index), self.copy) + self.disconnect(self.copy, (self, self.output_index)) + self.disconnect(self.output_terminators[self.output_index], self.input_terminators[self.input_index]) + self.connect((self, self.input_index), self.input_terminators[self.input_index]) + self.connect(self.output_terminators[self.output_index], (self, self.output_index)) - def set_input_index(self, input_index): - """ - Change the block to the new input index if the index changed. - - Args: - input_index: the new input index - """ - if self.input_index != input_index: - self.lock() - self._disconnect_current() - self.input_index = input_index - self._connect_current() - self.unlock() + def set_input_index(self, input_index): + """ + Change the block to the new input index if the index changed. + + Args: + input_index: the new input index + """ + if self.input_index != input_index: + self.lock() + self._disconnect_current() + self.input_index = input_index + self._connect_current() + self.unlock() - def set_output_index(self, output_index): - """ - Change the block to the new output index if the index changed. - - Args: - output_index: the new output index - """ - if self.output_index != output_index: - self.lock() - self._disconnect_current() - self.output_index = output_index - self._connect_current() - self.unlock() + def set_output_index(self, output_index): + """ + Change the block to the new output index if the index changed. + + Args: + output_index: the new output index + """ + if self.output_index != output_index: + self.lock() + self._disconnect_current() + self.output_index = output_index + self._connect_current() + self.unlock() class valve(selector): - """Wrapper for selector with 1 input and 1 output.""" + """Wrapper for selector with 1 input and 1 output.""" - def __init__(self, item_size, open): - """ - Constructor for valve. - - Args: - item_size: the size of the gr data stream in bytes - open: true if initial valve state is open - """ - if open: output_index = -1 - else: output_index = 0 - selector.__init__(self, item_size, 1, 1, 0, output_index) + def __init__(self, item_size, open): + """ + Constructor for valve. + + Args: + item_size: the size of the gr data stream in bytes + open: true if initial valve state is open + """ + if open: output_index = -1 + else: output_index = 0 + selector.__init__(self, item_size, 1, 1, 0, output_index) - def set_open(self, open): - """ - Callback to set open state. - - Args: - open: true to set valve state to open - """ - if open: output_index = -1 - else: output_index = 0 - self.set_output_index(output_index) + def set_open(self, open): + """ + Callback to set open state. + + Args: + open: true to set valve state to open + """ + if open: output_index = -1 + else: output_index = 0 + self.set_output_index(output_index) diff --git a/grc/grc_gnuradio/blks2/tcp.py b/grc/grc_gnuradio/blks2/tcp.py index 33160020cb..8613c02a17 100644 --- a/grc/grc_gnuradio/blks2/tcp.py +++ b/grc/grc_gnuradio/blks2/tcp.py @@ -24,47 +24,47 @@ import socket import os def _get_sock_fd(addr, port, server): - """ - Get the file descriptor for the socket. - As a client, block on connect, dup the socket descriptor. - As a server, block on accept, dup the client descriptor. - - Args: - addr: the ip address string - port: the tcp port number - server: true for server mode, false for client mode - - Returns: - the file descriptor number - """ - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - if server: - sock.bind((addr, port)) - sock.listen(1) - clientsock, address = sock.accept() - return os.dup(clientsock.fileno()) - else: - sock.connect((addr, port)) - return os.dup(sock.fileno()) + """ + Get the file descriptor for the socket. + As a client, block on connect, dup the socket descriptor. + As a server, block on accept, dup the client descriptor. + + Args: + addr: the ip address string + port: the tcp port number + server: true for server mode, false for client mode + + Returns: + the file descriptor number + """ + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + if server: + sock.bind((addr, port)) + sock.listen(1) + clientsock, address = sock.accept() + return os.dup(clientsock.fileno()) + else: + sock.connect((addr, port)) + return os.dup(sock.fileno()) class tcp_source(gr.hier_block2): - def __init__(self, itemsize, addr, port, server=True): - #init hier block - gr.hier_block2.__init__( - self, 'tcp_source', - gr.io_signature(0, 0, 0), - gr.io_signature(1, 1, itemsize), - ) - fd = _get_sock_fd(addr, port, server) - self.connect(blocks.file_descriptor_source(itemsize, fd), self) + def __init__(self, itemsize, addr, port, server=True): + #init hier block + gr.hier_block2.__init__( + self, 'tcp_source', + gr.io_signature(0, 0, 0), + gr.io_signature(1, 1, itemsize), + ) + fd = _get_sock_fd(addr, port, server) + self.connect(blocks.file_descriptor_source(itemsize, fd), self) class tcp_sink(gr.hier_block2): - def __init__(self, itemsize, addr, port, server=False): - #init hier block - gr.hier_block2.__init__( - self, 'tcp_sink', - gr.io_signature(1, 1, itemsize), - gr.io_signature(0, 0, 0), - ) - fd = _get_sock_fd(addr, port, server) - self.connect(self, blocks.file_descriptor_sink(itemsize, fd)) + def __init__(self, itemsize, addr, port, server=False): + #init hier block + gr.hier_block2.__init__( + self, 'tcp_sink', + gr.io_signature(1, 1, itemsize), + gr.io_signature(0, 0, 0), + ) + fd = _get_sock_fd(addr, port, server) + self.connect(self, blocks.file_descriptor_sink(itemsize, fd)) diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py index fa3e960d26..71d62fc8f5 100644 --- a/grc/gui/ActionHandler.py +++ b/grc/gui/ActionHandler.py @@ -37,132 +37,132 @@ from FileDialogs import OpenFlowGraphFileDialog, SaveFlowGraphFileDialog, SaveIm gobject.threads_init() class ActionHandler: - """ - The action handler will setup all the major window components, - and handle button presses and flow graph operations from the GUI. - """ - - def __init__(self, file_paths, platform): - """ - ActionHandler constructor. - Create the main window, setup the message handler, import the preferences, - and connect all of the action handlers. Finally, enter the gtk main loop and block. - - Args: - file_paths: a list of flow graph file passed from command line - platform: platform module - """ - self.clipboard = None - 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.connect('delete-event', self._quit) - self.main_window.connect('key-press-event', self._handle_key_press) - self.get_page = self.main_window.get_page - self.get_flow_graph = self.main_window.get_flow_graph - self.get_focus_flag = self.main_window.get_focus_flag - #setup the messages - Messages.register_messenger(self.main_window.add_report_line) - Messages.send_init(platform) - #initialize - self.init_file_paths = file_paths - Actions.APPLICATION_INITIALIZE() - #enter the mainloop - gtk.main() - - def _handle_key_press(self, widget, event): - """ - Handle key presses from the keyboard and translate key combinations into actions. - This key press handler is called prior to the gtk key press handler. - This handler bypasses built in accelerator key handling when in focus because - * some keys are ignored by the accelerators like the direction keys, - * some keys are not registered to any accelerators but are still used. - When not in focus, gtk and the accelerators handle the the key press. - - Returns: - false to let gtk handle the key action - """ - if not self.get_focus_flag(): return False - return Actions.handle_key_press(event) - - def _quit(self, window, event): - """ - Handle the delete event from the main window. - Generated by pressing X to close, alt+f4, or right click+close. - This method in turns calls the state handler to quit. - - Returns: - true - """ - Actions.APPLICATION_QUIT() - return True - - def _handle_action(self, action): - #print action - ################################################## - # Initalize/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, - ): action.set_sensitive(True) - if not self.init_file_paths: - self.init_file_paths = Preferences.files_open() - 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 Preferences.file_open() in self.init_file_paths: - self.main_window.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 - elif action == Actions.APPLICATION_QUIT: - if self.main_window.close_pages(): - gtk.main_quit() - exit(0) - ################################################## - # Selections - ################################################## - elif action == Actions.ELEMENT_SELECT: - pass #do nothing, update routines below - elif action == Actions.NOTHING_SELECT: - self.get_flow_graph().unselect() - ################################################## - # Enable/Disable - ################################################## - elif action == Actions.BLOCK_ENABLE: - if self.get_flow_graph().enable_selected(True): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - elif action == Actions.BLOCK_DISABLE: - if self.get_flow_graph().enable_selected(False): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - ################################################## - # Cut/Copy/Paste - ################################################## - elif action == Actions.BLOCK_CUT: - Actions.BLOCK_COPY() - Actions.ELEMENT_DELETE() - elif action == Actions.BLOCK_COPY: - self.clipboard = self.get_flow_graph().copy_to_clipboard() - elif action == Actions.BLOCK_PASTE: - if self.clipboard: - self.get_flow_graph().paste_from_clipboard(self.clipboard) - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) + """ + The action handler will setup all the major window components, + and handle button presses and flow graph operations from the GUI. + """ + + def __init__(self, file_paths, platform): + """ + ActionHandler constructor. + Create the main window, setup the message handler, import the preferences, + and connect all of the action handlers. Finally, enter the gtk main loop and block. + + Args: + file_paths: a list of flow graph file passed from command line + platform: platform module + """ + self.clipboard = None + 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.connect('delete-event', self._quit) + self.main_window.connect('key-press-event', self._handle_key_press) + self.get_page = self.main_window.get_page + self.get_flow_graph = self.main_window.get_flow_graph + self.get_focus_flag = self.main_window.get_focus_flag + #setup the messages + Messages.register_messenger(self.main_window.add_report_line) + Messages.send_init(platform) + #initialize + self.init_file_paths = file_paths + Actions.APPLICATION_INITIALIZE() + #enter the mainloop + gtk.main() + + def _handle_key_press(self, widget, event): + """ + Handle key presses from the keyboard and translate key combinations into actions. + This key press handler is called prior to the gtk key press handler. + This handler bypasses built in accelerator key handling when in focus because + * some keys are ignored by the accelerators like the direction keys, + * some keys are not registered to any accelerators but are still used. + When not in focus, gtk and the accelerators handle the the key press. + + Returns: + false to let gtk handle the key action + """ + if not self.get_focus_flag(): return False + return Actions.handle_key_press(event) + + def _quit(self, window, event): + """ + Handle the delete event from the main window. + Generated by pressing X to close, alt+f4, or right click+close. + This method in turns calls the state handler to quit. + + Returns: + true + """ + Actions.APPLICATION_QUIT() + return True + + def _handle_action(self, action): + #print action + ################################################## + # Initalize/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, + ): action.set_sensitive(True) + if not self.init_file_paths: + self.init_file_paths = Preferences.files_open() + 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 Preferences.file_open() in self.init_file_paths: + self.main_window.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 + elif action == Actions.APPLICATION_QUIT: + if self.main_window.close_pages(): + gtk.main_quit() + exit(0) + ################################################## + # Selections + ################################################## + elif action == Actions.ELEMENT_SELECT: + pass #do nothing, update routines below + elif action == Actions.NOTHING_SELECT: + self.get_flow_graph().unselect() + ################################################## + # Enable/Disable + ################################################## + elif action == Actions.BLOCK_ENABLE: + if self.get_flow_graph().enable_selected(True): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + elif action == Actions.BLOCK_DISABLE: + if self.get_flow_graph().enable_selected(False): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + ################################################## + # Cut/Copy/Paste + ################################################## + elif action == Actions.BLOCK_CUT: + Actions.BLOCK_COPY() + Actions.ELEMENT_DELETE() + elif action == Actions.BLOCK_COPY: + self.clipboard = self.get_flow_graph().copy_to_clipboard() + elif action == Actions.BLOCK_PASTE: + if self.clipboard: + self.get_flow_graph().paste_from_clipboard(self.clipboard) + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) ################################################## # Create heir block ################################################## - elif action == Actions.BLOCK_CREATE_HIER: + elif action == Actions.BLOCK_CREATE_HIER: # keeping track of coordinates for pasting later coords = self.get_flow_graph().get_selected_blocks()[0].get_coordinate() @@ -211,8 +211,8 @@ 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() + Actions.BLOCK_COPY() + self.main_window.new_page() Actions.BLOCK_PASTE() coords = (x_min,y_min) self.get_flow_graph().move_selected(coords) @@ -284,274 +284,272 @@ class ActionHandler: new_connection = self.get_flow_graph().connect(pad_source,sink) # update the new heir block flow graph - self.get_flow_graph().update() + self.get_flow_graph().update() - ################################################## - # Move/Rotate/Delete/Create - ################################################## - elif action == Actions.BLOCK_MOVE: - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - elif action == Actions.BLOCK_ROTATE_CCW: - if self.get_flow_graph().rotate_selected(90): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - elif action == Actions.BLOCK_ROTATE_CW: - if self.get_flow_graph().rotate_selected(-90): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - elif action == Actions.ELEMENT_DELETE: - if self.get_flow_graph().remove_selected(): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - Actions.NOTHING_SELECT() - self.get_page().set_saved(False) - elif action == Actions.ELEMENT_CREATE: - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - Actions.NOTHING_SELECT() - self.get_page().set_saved(False) - elif action == Actions.BLOCK_INC_TYPE: - if self.get_flow_graph().type_controller_modify_selected(1): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - elif action == Actions.BLOCK_DEC_TYPE: - if self.get_flow_graph().type_controller_modify_selected(-1): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - elif action == Actions.PORT_CONTROLLER_INC: - if self.get_flow_graph().port_controller_modify_selected(1): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - elif action == Actions.PORT_CONTROLLER_DEC: - if self.get_flow_graph().port_controller_modify_selected(-1): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - ################################################## - # Window stuff - ################################################## - elif action == Actions.ABOUT_WINDOW_DISPLAY: - Dialogs.AboutDialog(self.get_flow_graph().get_parent()) - elif action == Actions.HELP_WINDOW_DISPLAY: - Dialogs.HelpDialog() - elif action == Actions.TYPES_WINDOW_DISPLAY: - Dialogs.TypesDialog(self.get_flow_graph().get_parent()) - elif action == Actions.ERRORS_WINDOW_DISPLAY: - Dialogs.ErrorsDialog(self.get_flow_graph()) - ################################################## - # Param Modifications - ################################################## - elif action == Actions.BLOCK_PARAM_MODIFY: - selected_block = self.get_flow_graph().get_selected_block() - if selected_block: - if PropsDialog(selected_block).run(): - #save the new state - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - else: - #restore the current state - n = self.get_page().get_state_cache().get_current_state() - self.get_flow_graph().import_data(n) - self.get_flow_graph().update() - ################################################## - # Undo/Redo - ################################################## - elif action == Actions.FLOW_GRAPH_UNDO: - n = self.get_page().get_state_cache().get_prev_state() - if n: - self.get_flow_graph().unselect() - self.get_flow_graph().import_data(n) - self.get_flow_graph().update() - self.get_page().set_saved(False) - elif action == Actions.FLOW_GRAPH_REDO: - n = self.get_page().get_state_cache().get_next_state() - if n: - self.get_flow_graph().unselect() - self.get_flow_graph().import_data(n) - self.get_flow_graph().update() - self.get_page().set_saved(False) - ################################################## - # New/Open/Save/Close - ################################################## - elif action == Actions.FLOW_GRAPH_NEW: - self.main_window.new_page() - 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_CLOSE: - self.main_window.close_page() - elif action == Actions.FLOW_GRAPH_SAVE: - #read-only or undefined file path, do save-as - if self.get_page().get_read_only() or not self.get_page().get_file_path(): - Actions.FLOW_GRAPH_SAVE_AS() - #otherwise try to save - else: - try: - ParseXML.to_file(self.get_flow_graph().export_data(), self.get_page().get_file_path()) - self.get_flow_graph().grc_file_path = self.get_page().get_file_path() - self.get_page().set_saved(True) - except IOError: - Messages.send_fail_save(self.get_page().get_file_path()) - self.get_page().set_saved(False) - elif action == Actions.FLOW_GRAPH_SAVE_AS: - file_path = SaveFlowGraphFileDialog(self.get_page().get_file_path()).run() - if file_path is not None: - self.get_page().set_file_path(file_path) - Actions.FLOW_GRAPH_SAVE() - elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE: - file_path = SaveImageFileDialog(self.get_page().get_file_path()).run() - if file_path is not None: - pixbuf = self.get_flow_graph().get_drawing_area().get_pixbuf() - pixbuf.save(file_path, IMAGE_FILE_EXTENSION[1:]) - ################################################## - # Gen/Exec/Stop - ################################################## - elif action == Actions.FLOW_GRAPH_GEN: - if not self.get_page().get_proc(): - if not self.get_page().get_saved() or not self.get_page().get_file_path(): - Actions.FLOW_GRAPH_SAVE() #only save if file path missing or not saved - if self.get_page().get_saved() and self.get_page().get_file_path(): - generator = self.get_page().get_generator() - try: - Messages.send_start_gen(generator.get_file_path()) - generator.write() - except Exception,e: Messages.send_fail_gen(e) - else: self.generator = None - elif action == Actions.FLOW_GRAPH_EXEC: - if not self.get_page().get_proc(): - Actions.FLOW_GRAPH_GEN() - if self.get_page().get_saved() and self.get_page().get_file_path(): - ExecFlowGraphThread(self) - elif action == Actions.FLOW_GRAPH_KILL: - if self.get_page().get_proc(): - try: self.get_page().get_proc().kill() - except: print "could not kill process: %d"%self.get_page().get_proc().pid - elif action == Actions.PAGE_CHANGE: #pass and run the global actions - pass - elif action == Actions.RELOAD_BLOCKS: - self.platform.loadblocks() - self.main_window.btwin.clear(); - self.platform.load_block_tree(self.main_window.btwin); - elif action == Actions.OPEN_HIER: - bn = []; - for b in self.get_flow_graph().get_selected_blocks(): - if b._grc_source: - self.main_window.new_page(b._grc_source, show=True); - elif action == Actions.BUSSIFY_SOURCES: - n = {'name':'bus', 'type':'bus'} - for b in self.get_flow_graph().get_selected_blocks(): - b.bussify(n, 'source'); - self.get_flow_graph()._old_selected_port = None; - self.get_flow_graph()._new_selected_port = None; - Actions.ELEMENT_CREATE(); - - elif action == Actions.BUSSIFY_SINKS: - n = {'name':'bus', 'type':'bus'} - for b in self.get_flow_graph().get_selected_blocks(): - b.bussify(n, 'sink') - self.get_flow_graph()._old_selected_port = None; - self.get_flow_graph()._new_selected_port = None; - Actions.ELEMENT_CREATE(); - - - else: print '!!! Action "%s" not handled !!!'%action - ################################################## - # Global Actions for all States - ################################################## - #update general buttons - Actions.ERRORS_WINDOW_DISPLAY.set_sensitive(not self.get_flow_graph().is_valid()) - Actions.ELEMENT_DELETE.set_sensitive(bool(self.get_flow_graph().get_selected_elements())) - Actions.BLOCK_PARAM_MODIFY.set_sensitive(bool(self.get_flow_graph().get_selected_block())) - Actions.BLOCK_ROTATE_CCW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_ROTATE_CW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - #update cut/copy/paste - Actions.BLOCK_CUT.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_COPY.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_PASTE.set_sensitive(bool(self.clipboard)) - #update enable/disable - Actions.BLOCK_ENABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_DISABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_CREATE_HIER.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.OPEN_HIER.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BUSSIFY_SOURCES.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BUSSIFY_SINKS.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.RELOAD_BLOCKS.set_sensitive(True) - #set the exec and stop buttons - self.update_exec_stop() - #saved status - Actions.FLOW_GRAPH_SAVE.set_sensitive(not self.get_page().get_saved()) - self.main_window.update() - try: #set the size of the flow graph area (if changed) - new_size = self.get_flow_graph().get_option('window_size') - if self.get_flow_graph().get_size() != tuple(new_size): - self.get_flow_graph().set_size(*new_size) - except: pass - #draw the flow graph - self.get_flow_graph().update_selected() - self.get_flow_graph().queue_draw() - return True #action was handled - - def update_exec_stop(self): - """ - Update the exec and stop buttons. - Lock and unlock the mutex for race conditions with exec flow graph threads. - """ - sensitive = self.get_flow_graph().is_valid() and not self.get_page().get_proc() - 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() != None) + ################################################## + # Move/Rotate/Delete/Create + ################################################## + elif action == Actions.BLOCK_MOVE: + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + elif action == Actions.BLOCK_ROTATE_CCW: + if self.get_flow_graph().rotate_selected(90): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + elif action == Actions.BLOCK_ROTATE_CW: + if self.get_flow_graph().rotate_selected(-90): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + elif action == Actions.ELEMENT_DELETE: + if self.get_flow_graph().remove_selected(): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + Actions.NOTHING_SELECT() + self.get_page().set_saved(False) + elif action == Actions.ELEMENT_CREATE: + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + Actions.NOTHING_SELECT() + self.get_page().set_saved(False) + elif action == Actions.BLOCK_INC_TYPE: + if self.get_flow_graph().type_controller_modify_selected(1): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + elif action == Actions.BLOCK_DEC_TYPE: + if self.get_flow_graph().type_controller_modify_selected(-1): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + elif action == Actions.PORT_CONTROLLER_INC: + if self.get_flow_graph().port_controller_modify_selected(1): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + elif action == Actions.PORT_CONTROLLER_DEC: + if self.get_flow_graph().port_controller_modify_selected(-1): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + ################################################## + # Window stuff + ################################################## + elif action == Actions.ABOUT_WINDOW_DISPLAY: + Dialogs.AboutDialog(self.get_flow_graph().get_parent()) + elif action == Actions.HELP_WINDOW_DISPLAY: + Dialogs.HelpDialog() + elif action == Actions.TYPES_WINDOW_DISPLAY: + Dialogs.TypesDialog(self.get_flow_graph().get_parent()) + elif action == Actions.ERRORS_WINDOW_DISPLAY: + Dialogs.ErrorsDialog(self.get_flow_graph()) + ################################################## + # Param Modifications + ################################################## + elif action == Actions.BLOCK_PARAM_MODIFY: + selected_block = self.get_flow_graph().get_selected_block() + if selected_block: + if PropsDialog(selected_block).run(): + #save the new state + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + else: + #restore the current state + n = self.get_page().get_state_cache().get_current_state() + self.get_flow_graph().import_data(n) + self.get_flow_graph().update() + ################################################## + # Undo/Redo + ################################################## + elif action == Actions.FLOW_GRAPH_UNDO: + n = self.get_page().get_state_cache().get_prev_state() + if n: + self.get_flow_graph().unselect() + self.get_flow_graph().import_data(n) + self.get_flow_graph().update() + self.get_page().set_saved(False) + elif action == Actions.FLOW_GRAPH_REDO: + n = self.get_page().get_state_cache().get_next_state() + if n: + self.get_flow_graph().unselect() + self.get_flow_graph().import_data(n) + self.get_flow_graph().update() + self.get_page().set_saved(False) + ################################################## + # New/Open/Save/Close + ################################################## + elif action == Actions.FLOW_GRAPH_NEW: + self.main_window.new_page() + 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_CLOSE: + self.main_window.close_page() + elif action == Actions.FLOW_GRAPH_SAVE: + #read-only or undefined file path, do save-as + if self.get_page().get_read_only() or not self.get_page().get_file_path(): + Actions.FLOW_GRAPH_SAVE_AS() + #otherwise try to save + else: + try: + ParseXML.to_file(self.get_flow_graph().export_data(), self.get_page().get_file_path()) + self.get_flow_graph().grc_file_path = self.get_page().get_file_path() + self.get_page().set_saved(True) + except IOError: + Messages.send_fail_save(self.get_page().get_file_path()) + self.get_page().set_saved(False) + elif action == Actions.FLOW_GRAPH_SAVE_AS: + file_path = SaveFlowGraphFileDialog(self.get_page().get_file_path()).run() + if file_path is not None: + self.get_page().set_file_path(file_path) + Actions.FLOW_GRAPH_SAVE() + elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE: + file_path = SaveImageFileDialog(self.get_page().get_file_path()).run() + if file_path is not None: + pixbuf = self.get_flow_graph().get_drawing_area().get_pixbuf() + pixbuf.save(file_path, IMAGE_FILE_EXTENSION[1:]) + ################################################## + # Gen/Exec/Stop + ################################################## + elif action == Actions.FLOW_GRAPH_GEN: + if not self.get_page().get_proc(): + if not self.get_page().get_saved() or not self.get_page().get_file_path(): + Actions.FLOW_GRAPH_SAVE() #only save if file path missing or not saved + if self.get_page().get_saved() and self.get_page().get_file_path(): + generator = self.get_page().get_generator() + try: + Messages.send_start_gen(generator.get_file_path()) + generator.write() + except Exception,e: Messages.send_fail_gen(e) + else: self.generator = None + elif action == Actions.FLOW_GRAPH_EXEC: + if not self.get_page().get_proc(): + Actions.FLOW_GRAPH_GEN() + if self.get_page().get_saved() and self.get_page().get_file_path(): + ExecFlowGraphThread(self) + elif action == Actions.FLOW_GRAPH_KILL: + if self.get_page().get_proc(): + try: self.get_page().get_proc().kill() + except: print "could not kill process: %d"%self.get_page().get_proc().pid + elif action == Actions.PAGE_CHANGE: #pass and run the global actions + pass + elif action == Actions.RELOAD_BLOCKS: + self.platform.loadblocks() + self.main_window.btwin.clear(); + self.platform.load_block_tree(self.main_window.btwin); + elif action == Actions.OPEN_HIER: + bn = []; + for b in self.get_flow_graph().get_selected_blocks(): + if b._grc_source: + self.main_window.new_page(b._grc_source, show=True); + elif action == Actions.BUSSIFY_SOURCES: + n = {'name':'bus', 'type':'bus'} + for b in self.get_flow_graph().get_selected_blocks(): + b.bussify(n, 'source'); + self.get_flow_graph()._old_selected_port = None; + self.get_flow_graph()._new_selected_port = None; + Actions.ELEMENT_CREATE(); + + elif action == Actions.BUSSIFY_SINKS: + n = {'name':'bus', 'type':'bus'} + for b in self.get_flow_graph().get_selected_blocks(): + b.bussify(n, 'sink') + self.get_flow_graph()._old_selected_port = None; + self.get_flow_graph()._new_selected_port = None; + Actions.ELEMENT_CREATE(); + else: print '!!! Action "%s" not handled !!!'%action + ################################################## + # Global Actions for all States + ################################################## + #update general buttons + Actions.ERRORS_WINDOW_DISPLAY.set_sensitive(not self.get_flow_graph().is_valid()) + Actions.ELEMENT_DELETE.set_sensitive(bool(self.get_flow_graph().get_selected_elements())) + Actions.BLOCK_PARAM_MODIFY.set_sensitive(bool(self.get_flow_graph().get_selected_block())) + Actions.BLOCK_ROTATE_CCW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_ROTATE_CW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + #update cut/copy/paste + Actions.BLOCK_CUT.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_COPY.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_PASTE.set_sensitive(bool(self.clipboard)) + #update enable/disable + Actions.BLOCK_ENABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_DISABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_CREATE_HIER.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.OPEN_HIER.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BUSSIFY_SOURCES.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BUSSIFY_SINKS.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.RELOAD_BLOCKS.set_sensitive(True) + #set the exec and stop buttons + self.update_exec_stop() + #saved status + Actions.FLOW_GRAPH_SAVE.set_sensitive(not self.get_page().get_saved()) + self.main_window.update() + try: #set the size of the flow graph area (if changed) + new_size = self.get_flow_graph().get_option('window_size') + if self.get_flow_graph().get_size() != tuple(new_size): + self.get_flow_graph().set_size(*new_size) + except: pass + #draw the flow graph + self.get_flow_graph().update_selected() + self.get_flow_graph().queue_draw() + return True #action was handled + + def update_exec_stop(self): + """ + Update the exec and stop buttons. + Lock and unlock the mutex for race conditions with exec flow graph threads. + """ + sensitive = self.get_flow_graph().is_valid() and not self.get_page().get_proc() + 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() != 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() - Messages.send_start_exec(self.page.get_generator().get_file_path()) - #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) - gobject.idle_add(self.done) - - def done(self): - """Perform end of execution tasks.""" - Messages.send_end_exec() - self.page.set_proc(None) - self.update_exec_stop() + """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() + Messages.send_start_exec(self.page.get_generator().get_file_path()) + #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) + gobject.idle_add(self.done) + + def done(self): + """Perform end of execution tasks.""" + Messages.send_end_exec() + self.page.set_proc(None) + self.update_exec_stop() diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py index 5832e08bf0..a70109c021 100644 --- a/grc/gui/Actions.py +++ b/grc/gui/Actions.py @@ -30,26 +30,26 @@ _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. - Both the key value and the mask must have a match. - - Args: - event: a gtk key press event - - Returns: - true if handled - """ - _used_mods_mask = reduce(lambda x, y: x | y, [mod_mask for keyval, mod_mask in _actions_keypress_dict], NO_MODS_MASK) - #extract the key value and the consumed modifiers - keyval, egroup, level, consumed = _keymap.translate_keyboard_state( - event.hardware_keycode, event.state, event.group) - #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 + """ + Call the action associated with the key press event. + Both the key value and the mask must have a match. + + Args: + event: a gtk key press event + + Returns: + true if handled + """ + _used_mods_mask = reduce(lambda x, y: x | y, [mod_mask for keyval, mod_mask in _actions_keypress_dict], NO_MODS_MASK) + #extract the key value and the consumed modifiers + keyval, egroup, level, consumed = _keymap.translate_keyboard_state( + event.hardware_keycode, event.state, event.group) + #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 _all_actions_list = list() def get_all_actions(): return _all_actions_list @@ -58,250 +58,250 @@ _accel_group = gtk.AccelGroup() def get_accel_group(): return _accel_group class Action(gtk.Action): - """ - A custom Action class based on gtk.Action. - Pass additional arguments such as keypresses. - Register actions and keypresses with this module. - """ + """ + A custom Action class based on gtk.Action. + Pass additional arguments such as keypresses. + Register actions and keypresses with this module. + """ - def __init__(self, keypresses=(), name=None, label=None, tooltip=None, stock_id=None): - """ - Create a new Action instance. - - Args: - key_presses: a tuple of (keyval1, mod_mask1, keyval2, mod_mask2, ...) - the: regular gtk.Action parameters (defaults to None) - """ - if name is None: name = label - gtk.Action.__init__(self, - name=name, label=label, - tooltip=tooltip, stock_id=stock_id, - ) - #register this action - _all_actions_list.append(self) - for i in range(len(keypresses)/2): - keyval, mod_mask = keypresses[i*2:(i+1)*2] - #register this keypress - if _actions_keypress_dict.has_key((keyval, mod_mask)): - 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() - self.set_accel_group(get_accel_group()) - self.set_accel_path(accel_path) - gtk.accel_map_add_entry(accel_path, keyval, mod_mask) + def __init__(self, keypresses=(), name=None, label=None, tooltip=None, stock_id=None): + """ + Create a new Action instance. + + Args: + key_presses: a tuple of (keyval1, mod_mask1, keyval2, mod_mask2, ...) + the: regular gtk.Action parameters (defaults to None) + """ + if name is None: name = label + gtk.Action.__init__(self, + name=name, label=label, + tooltip=tooltip, stock_id=stock_id, + ) + #register this action + _all_actions_list.append(self) + for i in range(len(keypresses)/2): + keyval, mod_mask = keypresses[i*2:(i+1)*2] + #register this keypress + if _actions_keypress_dict.has_key((keyval, mod_mask)): + 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() + self.set_accel_group(get_accel_group()) + self.set_accel_path(accel_path) + gtk.accel_map_add_entry(accel_path, keyval, mod_mask) - def __str__(self): - """ - The string representation should be the name of the action id. - Try to find the action id for this action by searching this module. - """ - try: - import Actions - return filter(lambda attr: getattr(Actions, attr) == self, dir(Actions))[0] - except: return self.get_name() + def __str__(self): + """ + The string representation should be the name of the action id. + Try to find the action id for this action by searching this module. + """ + try: + import Actions + return filter(lambda attr: getattr(Actions, attr) == self, dir(Actions))[0] + except: return self.get_name() - def __repr__(self): return str(self) + def __repr__(self): return str(self) - def __call__(self): - """ - Emit the activate signal when called with (). - """ - self.emit('activate') + def __call__(self): + """ + Emit the activate signal when called with (). + """ + self.emit('activate') ######################################################################## # Actions ######################################################################## PAGE_CHANGE = Action() FLOW_GRAPH_NEW = Action( - label='_New', - tooltip='Create a new flow graph', - stock_id=gtk.STOCK_NEW, - keypresses=(gtk.keysyms.n, gtk.gdk.CONTROL_MASK), + label='_New', + tooltip='Create a new flow graph', + stock_id=gtk.STOCK_NEW, + keypresses=(gtk.keysyms.n, gtk.gdk.CONTROL_MASK), ) FLOW_GRAPH_OPEN = Action( - label='_Open', - tooltip='Open an existing flow graph', - stock_id=gtk.STOCK_OPEN, - keypresses=(gtk.keysyms.o, gtk.gdk.CONTROL_MASK), + label='_Open', + tooltip='Open an existing flow graph', + stock_id=gtk.STOCK_OPEN, + keypresses=(gtk.keysyms.o, gtk.gdk.CONTROL_MASK), ) FLOW_GRAPH_SAVE = Action( - label='_Save', - tooltip='Save the current flow graph', - stock_id=gtk.STOCK_SAVE, - keypresses=(gtk.keysyms.s, gtk.gdk.CONTROL_MASK), + label='_Save', + tooltip='Save the current flow graph', + stock_id=gtk.STOCK_SAVE, + keypresses=(gtk.keysyms.s, gtk.gdk.CONTROL_MASK), ) FLOW_GRAPH_SAVE_AS = Action( - label='Save _As', - tooltip='Save the current flow graph as...', - stock_id=gtk.STOCK_SAVE_AS, - keypresses=(gtk.keysyms.s, gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK), + label='Save _As', + tooltip='Save the current flow graph as...', + stock_id=gtk.STOCK_SAVE_AS, + keypresses=(gtk.keysyms.s, gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK), ) FLOW_GRAPH_CLOSE = Action( - label='_Close', - tooltip='Close the current flow graph', - stock_id=gtk.STOCK_CLOSE, - keypresses=(gtk.keysyms.w, gtk.gdk.CONTROL_MASK), + label='_Close', + tooltip='Close the current flow graph', + stock_id=gtk.STOCK_CLOSE, + keypresses=(gtk.keysyms.w, gtk.gdk.CONTROL_MASK), ) APPLICATION_INITIALIZE = Action() APPLICATION_QUIT = Action( - label='_Quit', - tooltip='Quit program', - stock_id=gtk.STOCK_QUIT, - keypresses=(gtk.keysyms.q, gtk.gdk.CONTROL_MASK), + label='_Quit', + tooltip='Quit program', + stock_id=gtk.STOCK_QUIT, + keypresses=(gtk.keysyms.q, gtk.gdk.CONTROL_MASK), ) FLOW_GRAPH_UNDO = Action( - label='_Undo', - tooltip='Undo a change to the flow graph', - stock_id=gtk.STOCK_UNDO, - keypresses=(gtk.keysyms.z, gtk.gdk.CONTROL_MASK), + label='_Undo', + tooltip='Undo a change to the flow graph', + stock_id=gtk.STOCK_UNDO, + keypresses=(gtk.keysyms.z, gtk.gdk.CONTROL_MASK), ) FLOW_GRAPH_REDO = Action( - label='_Redo', - tooltip='Redo a change to the flow graph', - stock_id=gtk.STOCK_REDO, - keypresses=(gtk.keysyms.y, gtk.gdk.CONTROL_MASK), + label='_Redo', + tooltip='Redo a change to the flow graph', + stock_id=gtk.STOCK_REDO, + keypresses=(gtk.keysyms.y, gtk.gdk.CONTROL_MASK), ) NOTHING_SELECT = Action() ELEMENT_SELECT = Action() ELEMENT_CREATE = Action() ELEMENT_DELETE = Action( - label='_Delete', - tooltip='Delete the selected blocks', - stock_id=gtk.STOCK_DELETE, - keypresses=(gtk.keysyms.Delete, NO_MODS_MASK), + label='_Delete', + tooltip='Delete the selected blocks', + stock_id=gtk.STOCK_DELETE, + keypresses=(gtk.keysyms.Delete, NO_MODS_MASK), ) BLOCK_MOVE = Action() BLOCK_ROTATE_CCW = Action( - label='Rotate Counterclockwise', - tooltip='Rotate the selected blocks 90 degrees to the left', - stock_id=gtk.STOCK_GO_BACK, - keypresses=(gtk.keysyms.Left, NO_MODS_MASK), + label='Rotate Counterclockwise', + tooltip='Rotate the selected blocks 90 degrees to the left', + stock_id=gtk.STOCK_GO_BACK, + keypresses=(gtk.keysyms.Left, NO_MODS_MASK), ) BLOCK_ROTATE_CW = Action( - label='Rotate Clockwise', - tooltip='Rotate the selected blocks 90 degrees to the right', - stock_id=gtk.STOCK_GO_FORWARD, - keypresses=(gtk.keysyms.Right, NO_MODS_MASK), + label='Rotate Clockwise', + tooltip='Rotate the selected blocks 90 degrees to the right', + stock_id=gtk.STOCK_GO_FORWARD, + keypresses=(gtk.keysyms.Right, NO_MODS_MASK), ) BLOCK_PARAM_MODIFY = Action( - label='_Properties', - tooltip='Modify params for the selected block', - stock_id=gtk.STOCK_PROPERTIES, - keypresses=(gtk.keysyms.Return, NO_MODS_MASK), + label='_Properties', + tooltip='Modify params for the selected block', + stock_id=gtk.STOCK_PROPERTIES, + keypresses=(gtk.keysyms.Return, NO_MODS_MASK), ) BLOCK_ENABLE = Action( - label='E_nable', - tooltip='Enable the selected blocks', - stock_id=gtk.STOCK_CONNECT, - keypresses=(gtk.keysyms.e, NO_MODS_MASK), + label='E_nable', + tooltip='Enable the selected blocks', + stock_id=gtk.STOCK_CONNECT, + keypresses=(gtk.keysyms.e, NO_MODS_MASK), ) BLOCK_DISABLE = Action( - label='D_isable', - tooltip='Disable the selected blocks', - stock_id=gtk.STOCK_DISCONNECT, - keypresses=(gtk.keysyms.d, NO_MODS_MASK), + label='D_isable', + tooltip='Disable the selected blocks', + stock_id=gtk.STOCK_DISCONNECT, + keypresses=(gtk.keysyms.d, NO_MODS_MASK), ) BLOCK_CREATE_HIER = Action( - label='C_reate Hier', - tooltip='Create hier block from selected blocks', - stock_id=gtk.STOCK_CONNECT, -# keypresses=(gtk.keysyms.c, NO_MODS_MASK), + label='C_reate Hier', + tooltip='Create hier block from selected blocks', + stock_id=gtk.STOCK_CONNECT, +# keypresses=(gtk.keysyms.c, NO_MODS_MASK), ) BLOCK_CUT = Action( - label='Cu_t', - tooltip='Cut', - stock_id=gtk.STOCK_CUT, - keypresses=(gtk.keysyms.x, gtk.gdk.CONTROL_MASK), + label='Cu_t', + tooltip='Cut', + stock_id=gtk.STOCK_CUT, + keypresses=(gtk.keysyms.x, gtk.gdk.CONTROL_MASK), ) BLOCK_COPY = Action( - label='_Copy', - tooltip='Copy', - stock_id=gtk.STOCK_COPY, - keypresses=(gtk.keysyms.c, gtk.gdk.CONTROL_MASK), + label='_Copy', + tooltip='Copy', + stock_id=gtk.STOCK_COPY, + keypresses=(gtk.keysyms.c, gtk.gdk.CONTROL_MASK), ) BLOCK_PASTE = Action( - label='_Paste', - tooltip='Paste', - stock_id=gtk.STOCK_PASTE, - keypresses=(gtk.keysyms.v, gtk.gdk.CONTROL_MASK), + label='_Paste', + tooltip='Paste', + stock_id=gtk.STOCK_PASTE, + keypresses=(gtk.keysyms.v, gtk.gdk.CONTROL_MASK), ) ERRORS_WINDOW_DISPLAY = Action( - label='_Errors', - tooltip='View flow graph errors', - stock_id=gtk.STOCK_DIALOG_ERROR, + label='_Errors', + tooltip='View flow graph errors', + stock_id=gtk.STOCK_DIALOG_ERROR, ) ABOUT_WINDOW_DISPLAY = Action( - label='_About', - tooltip='About this program', - stock_id=gtk.STOCK_ABOUT, + label='_About', + tooltip='About this program', + stock_id=gtk.STOCK_ABOUT, ) HELP_WINDOW_DISPLAY = Action( - label='_Help', - tooltip='Usage tips', - stock_id=gtk.STOCK_HELP, - keypresses=(gtk.keysyms.F1, NO_MODS_MASK), + label='_Help', + tooltip='Usage tips', + stock_id=gtk.STOCK_HELP, + keypresses=(gtk.keysyms.F1, NO_MODS_MASK), ) TYPES_WINDOW_DISPLAY = Action( - label='_Types', - tooltip='Types color mapping', - stock_id=gtk.STOCK_DIALOG_INFO, + label='_Types', + tooltip='Types color mapping', + stock_id=gtk.STOCK_DIALOG_INFO, ) FLOW_GRAPH_GEN = Action( - label='_Generate', - tooltip='Generate the flow graph', - stock_id=gtk.STOCK_CONVERT, - keypresses=(gtk.keysyms.F5, NO_MODS_MASK), + label='_Generate', + tooltip='Generate the flow graph', + stock_id=gtk.STOCK_CONVERT, + keypresses=(gtk.keysyms.F5, NO_MODS_MASK), ) FLOW_GRAPH_EXEC = Action( - label='_Execute', - tooltip='Execute the flow graph', - stock_id=gtk.STOCK_EXECUTE, - keypresses=(gtk.keysyms.F6, NO_MODS_MASK), + label='_Execute', + tooltip='Execute the flow graph', + stock_id=gtk.STOCK_EXECUTE, + keypresses=(gtk.keysyms.F6, NO_MODS_MASK), ) FLOW_GRAPH_KILL = Action( - label='_Kill', - tooltip='Kill the flow graph', - stock_id=gtk.STOCK_STOP, - keypresses=(gtk.keysyms.F7, NO_MODS_MASK), + label='_Kill', + tooltip='Kill the flow graph', + stock_id=gtk.STOCK_STOP, + keypresses=(gtk.keysyms.F7, NO_MODS_MASK), ) FLOW_GRAPH_SCREEN_CAPTURE = Action( - label='S_creen Capture', - tooltip='Create a screen capture of the flow graph', - stock_id=gtk.STOCK_PRINT, - keypresses=(gtk.keysyms.Print, NO_MODS_MASK), + label='S_creen Capture', + tooltip='Create a screen capture of the flow graph', + stock_id=gtk.STOCK_PRINT, + keypresses=(gtk.keysyms.Print, NO_MODS_MASK), ) PORT_CONTROLLER_DEC = Action( - keypresses=(gtk.keysyms.minus, NO_MODS_MASK, gtk.keysyms.KP_Subtract, NO_MODS_MASK), + keypresses=(gtk.keysyms.minus, NO_MODS_MASK, gtk.keysyms.KP_Subtract, NO_MODS_MASK), ) PORT_CONTROLLER_INC = Action( - keypresses=(gtk.keysyms.plus, NO_MODS_MASK, gtk.keysyms.KP_Add, NO_MODS_MASK), + keypresses=(gtk.keysyms.plus, NO_MODS_MASK, gtk.keysyms.KP_Add, NO_MODS_MASK), ) BLOCK_INC_TYPE = Action( - keypresses=(gtk.keysyms.Down, NO_MODS_MASK), + keypresses=(gtk.keysyms.Down, NO_MODS_MASK), ) BLOCK_DEC_TYPE = Action( - keypresses=(gtk.keysyms.Up, NO_MODS_MASK), + keypresses=(gtk.keysyms.Up, NO_MODS_MASK), ) RELOAD_BLOCKS = Action( - label='Reload _Blocks', - tooltip='Reload Blocks', - stock_id=gtk.STOCK_REFRESH + label='Reload _Blocks', + tooltip='Reload Blocks', + stock_id=gtk.STOCK_REFRESH ) OPEN_HIER = Action( - label='Open H_ier', - tooltip='Open the source of the selected hierarchical block', - stock_id=gtk.STOCK_JUMP_TO, + label='Open H_ier', + tooltip='Open the source of the selected hierarchical block', + stock_id=gtk.STOCK_JUMP_TO, ) BUSSIFY_SOURCES = Action( - label='Toggle So_urce Bus', - tooltip='Gang source ports into a single bus port', - stock_id=gtk.STOCK_JUMP_TO, + label='Toggle So_urce Bus', + tooltip='Gang source ports into a single bus port', + stock_id=gtk.STOCK_JUMP_TO, ) BUSSIFY_SINKS = Action( - label='Toggle S_ink Bus', - tooltip='Gang sink ports into a single bus port', - stock_id=gtk.STOCK_JUMP_TO, + label='Toggle S_ink Bus', + tooltip='Gang sink ports into a single bus port', + stock_id=gtk.STOCK_JUMP_TO, ) diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py index 770e705ffc..e2b7f4f9bc 100644 --- a/grc/gui/Bars.py +++ b/grc/gui/Bars.py @@ -24,31 +24,31 @@ import gtk ##The list of actions for the toolbar. TOOLBAR_LIST = ( - Actions.FLOW_GRAPH_NEW, - Actions.FLOW_GRAPH_OPEN, - Actions.FLOW_GRAPH_SAVE, - Actions.FLOW_GRAPH_CLOSE, - None, - Actions.FLOW_GRAPH_SCREEN_CAPTURE, - None, - Actions.BLOCK_CUT, - Actions.BLOCK_COPY, - Actions.BLOCK_PASTE, - Actions.ELEMENT_DELETE, - None, - Actions.FLOW_GRAPH_UNDO, - Actions.FLOW_GRAPH_REDO, - None, - Actions.ERRORS_WINDOW_DISPLAY, - Actions.FLOW_GRAPH_GEN, - Actions.FLOW_GRAPH_EXEC, - Actions.FLOW_GRAPH_KILL, - None, - Actions.BLOCK_ROTATE_CCW, - Actions.BLOCK_ROTATE_CW, - None, - Actions.BLOCK_ENABLE, - Actions.BLOCK_DISABLE, + Actions.FLOW_GRAPH_NEW, + Actions.FLOW_GRAPH_OPEN, + Actions.FLOW_GRAPH_SAVE, + Actions.FLOW_GRAPH_CLOSE, + None, + Actions.FLOW_GRAPH_SCREEN_CAPTURE, + None, + Actions.BLOCK_CUT, + Actions.BLOCK_COPY, + Actions.BLOCK_PASTE, + Actions.ELEMENT_DELETE, + None, + Actions.FLOW_GRAPH_UNDO, + Actions.FLOW_GRAPH_REDO, + None, + Actions.ERRORS_WINDOW_DISPLAY, + Actions.FLOW_GRAPH_GEN, + Actions.FLOW_GRAPH_EXEC, + Actions.FLOW_GRAPH_KILL, + None, + Actions.BLOCK_ROTATE_CCW, + Actions.BLOCK_ROTATE_CW, + None, + Actions.BLOCK_ENABLE, + Actions.BLOCK_DISABLE, None, Actions.RELOAD_BLOCKS, Actions.OPEN_HIER, @@ -57,88 +57,88 @@ TOOLBAR_LIST = ( ##The list of actions and categories for the menu bar. MENU_BAR_LIST = ( - (gtk.Action('File', '_File', None, None), [ - Actions.FLOW_GRAPH_NEW, - Actions.FLOW_GRAPH_OPEN, - None, - Actions.FLOW_GRAPH_SAVE, - Actions.FLOW_GRAPH_SAVE_AS, - None, - Actions.FLOW_GRAPH_SCREEN_CAPTURE, - None, - Actions.FLOW_GRAPH_CLOSE, - Actions.APPLICATION_QUIT, - ]), - (gtk.Action('Edit', '_Edit', None, None), [ - Actions.FLOW_GRAPH_UNDO, - Actions.FLOW_GRAPH_REDO, - None, - Actions.BLOCK_CUT, - Actions.BLOCK_COPY, - Actions.BLOCK_PASTE, - Actions.ELEMENT_DELETE, - None, - Actions.BLOCK_ROTATE_CCW, - Actions.BLOCK_ROTATE_CW, - None, - Actions.BLOCK_ENABLE, - Actions.BLOCK_DISABLE, - None, - Actions.BLOCK_PARAM_MODIFY, - ]), - (gtk.Action('View', '_View', None, None), [ - Actions.ERRORS_WINDOW_DISPLAY, - ]), - (gtk.Action('Build', '_Build', None, None), [ - Actions.FLOW_GRAPH_GEN, - Actions.FLOW_GRAPH_EXEC, - Actions.FLOW_GRAPH_KILL, - ]), - (gtk.Action('Help', '_Help', None, None), [ - Actions.HELP_WINDOW_DISPLAY, - Actions.TYPES_WINDOW_DISPLAY, - None, - Actions.ABOUT_WINDOW_DISPLAY, - ]), + (gtk.Action('File', '_File', None, None), [ + Actions.FLOW_GRAPH_NEW, + Actions.FLOW_GRAPH_OPEN, + None, + Actions.FLOW_GRAPH_SAVE, + Actions.FLOW_GRAPH_SAVE_AS, + None, + Actions.FLOW_GRAPH_SCREEN_CAPTURE, + None, + Actions.FLOW_GRAPH_CLOSE, + Actions.APPLICATION_QUIT, + ]), + (gtk.Action('Edit', '_Edit', None, None), [ + Actions.FLOW_GRAPH_UNDO, + Actions.FLOW_GRAPH_REDO, + None, + Actions.BLOCK_CUT, + Actions.BLOCK_COPY, + Actions.BLOCK_PASTE, + Actions.ELEMENT_DELETE, + None, + Actions.BLOCK_ROTATE_CCW, + Actions.BLOCK_ROTATE_CW, + None, + Actions.BLOCK_ENABLE, + Actions.BLOCK_DISABLE, + None, + Actions.BLOCK_PARAM_MODIFY, + ]), + (gtk.Action('View', '_View', None, None), [ + Actions.ERRORS_WINDOW_DISPLAY, + ]), + (gtk.Action('Build', '_Build', None, None), [ + Actions.FLOW_GRAPH_GEN, + Actions.FLOW_GRAPH_EXEC, + Actions.FLOW_GRAPH_KILL, + ]), + (gtk.Action('Help', '_Help', None, None), [ + Actions.HELP_WINDOW_DISPLAY, + Actions.TYPES_WINDOW_DISPLAY, + None, + Actions.ABOUT_WINDOW_DISPLAY, + ]), ) class Toolbar(gtk.Toolbar): - """The gtk toolbar with actions added from the toolbar list.""" + """The gtk toolbar with actions added from the toolbar list.""" - def __init__(self): - """ - Parse the list of action names in the toolbar list. - Look up the action for each name in the action list and add it to the toolbar. - """ - 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 - action.set_property('tooltip', action.get_property('tooltip')) - else: self.add(gtk.SeparatorToolItem()) + def __init__(self): + """ + Parse the list of action names in the toolbar list. + Look up the action for each name in the action list and add it to the toolbar. + """ + 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 + action.set_property('tooltip', action.get_property('tooltip')) + else: self.add(gtk.SeparatorToolItem()) class MenuBar(gtk.MenuBar): - """The gtk menu bar with actions added from the menu bar list.""" + """The gtk menu bar with actions added from the menu bar list.""" - def __init__(self): - """ - Parse the list of submenus from the menubar list. - For each submenu, get a list of action names. - Look up the action for each name in the action list and add it to the submenu. - Add the submenu to the menu bar. - """ - gtk.MenuBar.__init__(self) - 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: - if action: #append a menu item - main_menu.append(action.create_menu_item()) - else: main_menu.append(gtk.SeparatorMenuItem()) - main_menu.show_all() #this show all is required for the separators to show + def __init__(self): + """ + Parse the list of submenus from the menubar list. + For each submenu, get a list of action names. + Look up the action for each name in the action list and add it to the submenu. + Add the submenu to the menu bar. + """ + gtk.MenuBar.__init__(self) + 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: + if action: #append a menu item + main_menu.append(action.create_menu_item()) + else: main_menu.append(gtk.SeparatorMenuItem()) + main_menu.show_all() #this show all is required for the separators to show diff --git a/grc/gui/Block.py b/grc/gui/Block.py index e69fe1a418..30031866c0 100644 --- a/grc/gui/Block.py +++ b/grc/gui/Block.py @@ -23,9 +23,9 @@ import Colors from .. base import odict from Constants import BORDER_PROXIMITY_SENSITIVITY from Constants import \ - BLOCK_LABEL_PADDING, \ - PORT_SEPARATION, LABEL_SEPARATION, \ - PORT_BORDER_SEPARATION, POSSIBLE_ROTATIONS + BLOCK_LABEL_PADDING, \ + PORT_SEPARATION, LABEL_SEPARATION, \ + PORT_BORDER_SEPARATION, POSSIBLE_ROTATIONS import pygtk pygtk.require('2.0') import gtk @@ -36,187 +36,184 @@ BLOCK_MARKUP_TMPL="""\ <span foreground="$foreground" font_desc="Sans 8"><b>$encode($block.get_name())</b></span>""" class Block(Element): - """The graphical signal block.""" + """The graphical signal block.""" - def __init__(self): - """ - Block contructor. - Add graphics related params to the block. - """ - #add the position param - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({ - 'name': 'GUI Coordinate', - 'key': '_coordinate', - 'type': 'raw', - 'value': '(0, 0)', - 'hide': 'all', - }) - )) - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({ - 'name': 'GUI Rotation', - 'key': '_rotation', - 'type': 'raw', - 'value': '0', - 'hide': 'all', - }) - )) - Element.__init__(self) + def __init__(self): + """ + Block contructor. + Add graphics related params to the block. + """ + #add the position param + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({ + 'name': 'GUI Coordinate', + 'key': '_coordinate', + 'type': 'raw', + 'value': '(0, 0)', + 'hide': 'all', + }) + )) + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({ + 'name': 'GUI Rotation', + 'key': '_rotation', + 'type': 'raw', + 'value': '0', + 'hide': 'all', + }) + )) + Element.__init__(self) - def get_coordinate(self): - """ - Get the coordinate from the position param. - - Returns: - the coordinate tuple (x, y) or (0, 0) if failure - """ - try: #should evaluate to tuple - coor = eval(self.get_param('_coordinate').get_value()) - x, y = map(int, coor) - fgW,fgH = self.get_parent().get_size() - if x <= 0: - x = 0 - elif x >= fgW - BORDER_PROXIMITY_SENSITIVITY: - x = fgW - BORDER_PROXIMITY_SENSITIVITY - if y <= 0: - y = 0 - elif y >= fgH - BORDER_PROXIMITY_SENSITIVITY: - y = fgH - BORDER_PROXIMITY_SENSITIVITY - return (x, y) - except: - self.set_coordinate((0, 0)) - return (0, 0) + def get_coordinate(self): + """ + Get the coordinate from the position param. + + Returns: + the coordinate tuple (x, y) or (0, 0) if failure + """ + try: #should evaluate to tuple + coor = eval(self.get_param('_coordinate').get_value()) + x, y = map(int, coor) + fgW,fgH = self.get_parent().get_size() + if x <= 0: + x = 0 + elif x >= fgW - BORDER_PROXIMITY_SENSITIVITY: + x = fgW - BORDER_PROXIMITY_SENSITIVITY + if y <= 0: + y = 0 + elif y >= fgH - BORDER_PROXIMITY_SENSITIVITY: + y = fgH - BORDER_PROXIMITY_SENSITIVITY + return (x, y) + except: + self.set_coordinate((0, 0)) + return (0, 0) - def set_coordinate(self, coor): - """ - Set the coordinate into the position param. - - Args: - coor: the coordinate tuple (x, y) - """ - self.get_param('_coordinate').set_value(str(coor)) + def set_coordinate(self, coor): + """ + Set the coordinate into the position param. + + Args: + coor: the coordinate tuple (x, y) + """ + self.get_param('_coordinate').set_value(str(coor)) - def get_rotation(self): - """ - Get the rotation from the position param. - - Returns: - the rotation in degrees or 0 if failure - """ - try: #should evaluate to dict - rotation = eval(self.get_param('_rotation').get_value()) - return int(rotation) - except: - self.set_rotation(POSSIBLE_ROTATIONS[0]) - return POSSIBLE_ROTATIONS[0] + def get_rotation(self): + """ + Get the rotation from the position param. + + Returns: + the rotation in degrees or 0 if failure + """ + try: #should evaluate to dict + rotation = eval(self.get_param('_rotation').get_value()) + return int(rotation) + except: + self.set_rotation(POSSIBLE_ROTATIONS[0]) + return POSSIBLE_ROTATIONS[0] - def set_rotation(self, rot): - """ - Set the rotation into the position param. - - Args: - rot: the rotation in degrees - """ - self.get_param('_rotation').set_value(str(rot)) + def set_rotation(self, rot): + """ + Set the rotation into the position param. + + Args: + rot: the rotation in degrees + """ + self.get_param('_rotation').set_value(str(rot)) - def create_shapes(self): - """Update the block, parameters, and ports when a change occurs.""" - - Element.create_shapes(self) - if self.is_horizontal(): self.add_area((0, 0), (self.W, self.H)) - elif self.is_vertical(): self.add_area((0, 0), (self.H, self.W)) + def create_shapes(self): + """Update the block, parameters, and ports when a change occurs.""" + Element.create_shapes(self) + if self.is_horizontal(): self.add_area((0, 0), (self.W, self.H)) + elif self.is_vertical(): self.add_area((0, 0), (self.H, self.W)) - def create_labels(self): - """Create the labels for the signal block.""" - Element.create_labels(self) - self._bg_color = self.get_enabled() and Colors.BLOCK_ENABLED_COLOR or Colors.BLOCK_DISABLED_COLOR - layouts = list() - #create the main layout - layout = gtk.DrawingArea().create_pango_layout('') - layouts.append(layout) - layout.set_markup(Utils.parse_template(BLOCK_MARKUP_TMPL, block=self)) - self.label_width, self.label_height = layout.get_pixel_size() - #display the params - markups = [param.get_markup() for param in self.get_params() if param.get_hide() not in ('all', 'part')] - if markups: - layout = gtk.DrawingArea().create_pango_layout('') - layout.set_spacing(LABEL_SEPARATION*pango.SCALE) - layout.set_markup('\n'.join(markups)) - layouts.append(layout) - w,h = layout.get_pixel_size() - self.label_width = max(w, self.label_width) - self.label_height += h + LABEL_SEPARATION - width = self.label_width - height = self.label_height - #setup the pixmap - pixmap = self.get_parent().new_pixmap(width, height) - gc = pixmap.new_gc() - gc.set_foreground(self._bg_color) - pixmap.draw_rectangle(gc, True, 0, 0, width, height) - #draw the layouts - h_off = 0 - for i,layout in enumerate(layouts): - w,h = layout.get_pixel_size() - if i == 0: w_off = (width-w)/2 - else: w_off = 0 - pixmap.draw_layout(gc, w_off, h_off, layout) - h_off = h + h_off + LABEL_SEPARATION - #create vertical and horizontal pixmaps - self.horizontal_label = pixmap - if self.is_vertical(): - self.vertical_label = self.get_parent().new_pixmap(height, width) - Utils.rotate_pixmap(gc, self.horizontal_label, self.vertical_label) - #calculate width and height needed - self.W = self.label_width + 2*BLOCK_LABEL_PADDING - self.H = max(*( - [self.label_height+2*BLOCK_LABEL_PADDING] + [2*PORT_BORDER_SEPARATION + \ - sum([port.H + PORT_SEPARATION for port in ports]) - PORT_SEPARATION - for ports in (self.get_sources_gui(), self.get_sinks_gui())] + - [4*PORT_BORDER_SEPARATION + \ - sum([(port.H) + PORT_SEPARATION for port in ports]) - PORT_SEPARATION - for ports in ([i for i in self.get_sources_gui() if i.get_type() == 'bus'], [i for i in self.get_sinks_gui() if i.get_type() == 'bus'])] - )) + def create_labels(self): + """Create the labels for the signal block.""" + Element.create_labels(self) + self._bg_color = self.get_enabled() and Colors.BLOCK_ENABLED_COLOR or Colors.BLOCK_DISABLED_COLOR + layouts = list() + #create the main layout + layout = gtk.DrawingArea().create_pango_layout('') + layouts.append(layout) + layout.set_markup(Utils.parse_template(BLOCK_MARKUP_TMPL, block=self)) + self.label_width, self.label_height = layout.get_pixel_size() + #display the params + markups = [param.get_markup() for param in self.get_params() if param.get_hide() not in ('all', 'part')] + if markups: + layout = gtk.DrawingArea().create_pango_layout('') + layout.set_spacing(LABEL_SEPARATION*pango.SCALE) + layout.set_markup('\n'.join(markups)) + layouts.append(layout) + w,h = layout.get_pixel_size() + self.label_width = max(w, self.label_width) + self.label_height += h + LABEL_SEPARATION + width = self.label_width + height = self.label_height + #setup the pixmap + pixmap = self.get_parent().new_pixmap(width, height) + gc = pixmap.new_gc() + gc.set_foreground(self._bg_color) + pixmap.draw_rectangle(gc, True, 0, 0, width, height) + #draw the layouts + h_off = 0 + for i,layout in enumerate(layouts): + w,h = layout.get_pixel_size() + if i == 0: w_off = (width-w)/2 + else: w_off = 0 + pixmap.draw_layout(gc, w_off, h_off, layout) + h_off = h + h_off + LABEL_SEPARATION + #create vertical and horizontal pixmaps + self.horizontal_label = pixmap + if self.is_vertical(): + self.vertical_label = self.get_parent().new_pixmap(height, width) + Utils.rotate_pixmap(gc, self.horizontal_label, self.vertical_label) + #calculate width and height needed + self.W = self.label_width + 2*BLOCK_LABEL_PADDING + self.H = max(*( + [self.label_height+2*BLOCK_LABEL_PADDING] + [2*PORT_BORDER_SEPARATION + \ + sum([port.H + PORT_SEPARATION for port in ports]) - PORT_SEPARATION + for ports in (self.get_sources_gui(), self.get_sinks_gui())] + + [4*PORT_BORDER_SEPARATION + \ + sum([(port.H) + PORT_SEPARATION for port in ports]) - PORT_SEPARATION + for ports in ([i for i in self.get_sources_gui() if i.get_type() == 'bus'], [i for i in self.get_sinks_gui() if i.get_type() == 'bus'])] + )) - def draw(self, gc, window): - """ - Draw the signal block with label and inputs/outputs. - - Args: - gc: the graphics context - window: the gtk window to draw on - """ - x, y = self.get_coordinate() - #draw main block - Element.draw( - self, gc, window, bg_color=self._bg_color, - border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or Colors.BORDER_COLOR, - ) - #draw label image - if self.is_horizontal(): - window.draw_drawable(gc, self.horizontal_label, 0, 0, x+BLOCK_LABEL_PADDING, y+(self.H-self.label_height)/2, -1, -1) - elif self.is_vertical(): - window.draw_drawable(gc, self.vertical_label, 0, 0, x+(self.H-self.label_height)/2, y+BLOCK_LABEL_PADDING, -1, -1) - #draw ports - - - for port in self.get_ports_gui(): - port.draw(gc, window) + def draw(self, gc, window): + """ + Draw the signal block with label and inputs/outputs. + + Args: + gc: the graphics context + window: the gtk window to draw on + """ + x, y = self.get_coordinate() + #draw main block + Element.draw( + self, gc, window, bg_color=self._bg_color, + border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or Colors.BORDER_COLOR, + ) + #draw label image + if self.is_horizontal(): + window.draw_drawable(gc, self.horizontal_label, 0, 0, x+BLOCK_LABEL_PADDING, y+(self.H-self.label_height)/2, -1, -1) + elif self.is_vertical(): + window.draw_drawable(gc, self.vertical_label, 0, 0, x+(self.H-self.label_height)/2, y+BLOCK_LABEL_PADDING, -1, -1) + #draw ports + for port in self.get_ports_gui(): + port.draw(gc, window) - def what_is_selected(self, coor, coor_m=None): - """ - Get the element that is selected. - - Args: - coor: the (x,y) tuple - coor_m: the (x_m, y_m) tuple - - Returns: - this block, a port, or None - """ - for port in self.get_ports_gui(): - port_selected = port.what_is_selected(coor, coor_m) - if port_selected: return port_selected - return Element.what_is_selected(self, coor, coor_m) + def what_is_selected(self, coor, coor_m=None): + """ + Get the element that is selected. + + Args: + coor: the (x,y) tuple + coor_m: the (x_m, y_m) tuple + + Returns: + this block, a port, or None + """ + for port in self.get_ports_gui(): + port_selected = port.what_is_selected(coor, coor_m) + if port_selected: return port_selected + return Element.what_is_selected(self, coor, coor_m) diff --git a/grc/gui/BlockTreeWindow.py b/grc/gui/BlockTreeWindow.py index 28867dce7c..7d52f730ed 100644 --- a/grc/gui/BlockTreeWindow.py +++ b/grc/gui/BlockTreeWindow.py @@ -38,172 +38,172 @@ undocumented#slurp CAT_MARKUP_TMPL="""Category: $cat""" class BlockTreeWindow(gtk.VBox): - """The block selection panel.""" - - def __init__(self, platform, get_flow_graph): - """ - BlockTreeWindow constructor. - Create a tree view of the possible blocks in the platform. - The tree view nodes will be category names, the leaves will be block names. - A mouse double click or button press action will trigger the add block event. - - Args: - platform: the particular platform will all block prototypes - get_flow_graph: get the selected flow graph - """ - gtk.VBox.__init__(self) - self.platform = platform - self.get_flow_graph = get_flow_graph - #make the tree model for holding blocks - self.treestore = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING) - self.treeview = gtk.TreeView(self.treestore) - self.treeview.set_enable_search(False) #disable pop up search box - self.treeview.add_events(gtk.gdk.BUTTON_PRESS_MASK) - self.treeview.connect('button-press-event', self._handle_mouse_button_press) - selection = self.treeview.get_selection() - selection.set_mode('single') - selection.connect('changed', self._handle_selection_change) - renderer = gtk.CellRendererText() - column = gtk.TreeViewColumn('Blocks', renderer, text=NAME_INDEX) - self.treeview.append_column(column) - #setup the search - self.treeview.set_enable_search(True) - self.treeview.set_search_equal_func(self._handle_search) - #try to enable the tooltips (available in pygtk 2.12 and above) - try: self.treeview.set_tooltip_column(DOC_INDEX) - except: pass + """The block selection panel.""" + + def __init__(self, platform, get_flow_graph): + """ + BlockTreeWindow constructor. + Create a tree view of the possible blocks in the platform. + The tree view nodes will be category names, the leaves will be block names. + A mouse double click or button press action will trigger the add block event. + + Args: + platform: the particular platform will all block prototypes + get_flow_graph: get the selected flow graph + """ + gtk.VBox.__init__(self) + self.platform = platform + self.get_flow_graph = get_flow_graph + #make the tree model for holding blocks + self.treestore = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING) + self.treeview = gtk.TreeView(self.treestore) + self.treeview.set_enable_search(False) #disable pop up search box + self.treeview.add_events(gtk.gdk.BUTTON_PRESS_MASK) + self.treeview.connect('button-press-event', self._handle_mouse_button_press) + selection = self.treeview.get_selection() + selection.set_mode('single') + selection.connect('changed', self._handle_selection_change) + renderer = gtk.CellRendererText() + column = gtk.TreeViewColumn('Blocks', renderer, text=NAME_INDEX) + self.treeview.append_column(column) + #setup the search + self.treeview.set_enable_search(True) + self.treeview.set_search_equal_func(self._handle_search) + #try to enable the tooltips (available in pygtk 2.12 and above) + try: self.treeview.set_tooltip_column(DOC_INDEX) + except: pass #setup sort order - column.set_sort_column_id(0) - #setup drag and drop - self.treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, DND_TARGETS, gtk.gdk.ACTION_COPY) - self.treeview.connect('drag-data-get', self._handle_drag_get_data) - #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) - #add button - self.add_button = gtk.Button(None, gtk.STOCK_ADD) - self.add_button.connect('clicked', self._handle_add_button) - self.pack_start(self.add_button, False) - #map categories to iters, automatic mapping for root - self._categories = {tuple(): None} - #add blocks and categories - self.platform.load_block_tree(self) - #initialize - self._update_add_button() - - def clear(self): - self.treestore.clear(); - self._categories = {tuple(): None} - - - ############################################################ - ## Block Tree Methods - ############################################################ - def add_block(self, category, block=None): - """ - Add a block with category to this selection window. - Add only the category when block is None. - - Args: - category: the category list or path string - block: the block object or None - """ - if isinstance(category, str): category = category.split('/') - category = tuple(filter(lambda x: x, category)) #tuple is hashable - #add category and all sub categories - for i, cat_name in enumerate(category): - sub_category = category[:i+1] - if sub_category not in self._categories: - iter = self.treestore.insert_before(self._categories[sub_category[:-1]], None) - self.treestore.set_value(iter, NAME_INDEX, '[ %s ]'%cat_name) - self.treestore.set_value(iter, KEY_INDEX, '') - self.treestore.set_value(iter, DOC_INDEX, Utils.parse_template(CAT_MARKUP_TMPL, cat=cat_name)) - self._categories[sub_category] = iter - #add block - if block is None: return - iter = self.treestore.insert_before(self._categories[category], None) - self.treestore.set_value(iter, NAME_INDEX, block.get_name()) - self.treestore.set_value(iter, KEY_INDEX, block.get_key()) - self.treestore.set_value(iter, DOC_INDEX, Utils.parse_template(DOC_MARKUP_TMPL, doc=block.get_doc())) - - ############################################################ - ## Helper Methods - ############################################################ - def _get_selected_block_key(self): - """ - Get the currently selected block key. - - Returns: - the key of the selected block or a empty string - """ - selection = self.treeview.get_selection() - treestore, iter = selection.get_selected() - return iter and treestore.get_value(iter, KEY_INDEX) or '' - - def _update_add_button(self): - """ - Update the add button's sensitivity. - The button should be active only if a block is selected. - """ - key = self._get_selected_block_key() - self.add_button.set_sensitive(bool(key)) - - def _add_selected_block(self): - """ - Add the selected block with the given key to the flow graph. - """ - key = self._get_selected_block_key() - if key: self.get_flow_graph().add_new_block(key) - - ############################################################ - ## Event Handlers - ############################################################ - def _handle_search(self, model, column, key, iter): - #determine which blocks match the search key - blocks = self.get_flow_graph().get_parent().get_blocks() - matching_blocks = filter(lambda b: key in b.get_key() or key in b.get_name().lower(), blocks) - #remove the old search category - try: self.treestore.remove(self._categories.pop((self._search_category, ))) - except (KeyError, AttributeError): pass #nothing to remove - #create a search category - if not matching_blocks: return - self._search_category = 'Search: %s'%key - for block in matching_blocks: self.add_block(self._search_category, block) - #expand the search category - path = self.treestore.get_path(self._categories[(self._search_category, )]) - self.treeview.collapse_all() - self.treeview.expand_row(path, open_all=False) - - def _handle_drag_get_data(self, widget, drag_context, selection_data, info, time): - """ - Handle a drag and drop by setting the key to the selection object. - This will call the destination handler for drag and drop. - Only call set when the key is valid to ignore DND from categories. - """ - key = self._get_selected_block_key() - if key: selection_data.set(selection_data.target, 8, key) - - def _handle_mouse_button_press(self, widget, event): - """ - Handle the mouse button press. - If a left double click is detected, call add selected block. - """ - if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS: - self._add_selected_block() - - def _handle_selection_change(self, selection): - """ - Handle a selection change in the tree view. - If a selection changes, set the add button sensitive. - """ - self._update_add_button() - - def _handle_add_button(self, widget): - """ - Handle the add button clicked signal. - Call add selected block. - """ - self._add_selected_block() + column.set_sort_column_id(0) + #setup drag and drop + self.treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, DND_TARGETS, gtk.gdk.ACTION_COPY) + self.treeview.connect('drag-data-get', self._handle_drag_get_data) + #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) + #add button + self.add_button = gtk.Button(None, gtk.STOCK_ADD) + self.add_button.connect('clicked', self._handle_add_button) + self.pack_start(self.add_button, False) + #map categories to iters, automatic mapping for root + self._categories = {tuple(): None} + #add blocks and categories + self.platform.load_block_tree(self) + #initialize + self._update_add_button() + + def clear(self): + self.treestore.clear(); + self._categories = {tuple(): None} + + + ############################################################ + ## Block Tree Methods + ############################################################ + def add_block(self, category, block=None): + """ + Add a block with category to this selection window. + Add only the category when block is None. + + Args: + category: the category list or path string + block: the block object or None + """ + if isinstance(category, str): category = category.split('/') + category = tuple(filter(lambda x: x, category)) #tuple is hashable + #add category and all sub categories + for i, cat_name in enumerate(category): + sub_category = category[:i+1] + if sub_category not in self._categories: + iter = self.treestore.insert_before(self._categories[sub_category[:-1]], None) + self.treestore.set_value(iter, NAME_INDEX, '[ %s ]'%cat_name) + self.treestore.set_value(iter, KEY_INDEX, '') + self.treestore.set_value(iter, DOC_INDEX, Utils.parse_template(CAT_MARKUP_TMPL, cat=cat_name)) + self._categories[sub_category] = iter + #add block + if block is None: return + iter = self.treestore.insert_before(self._categories[category], None) + self.treestore.set_value(iter, NAME_INDEX, block.get_name()) + self.treestore.set_value(iter, KEY_INDEX, block.get_key()) + self.treestore.set_value(iter, DOC_INDEX, Utils.parse_template(DOC_MARKUP_TMPL, doc=block.get_doc())) + + ############################################################ + ## Helper Methods + ############################################################ + def _get_selected_block_key(self): + """ + Get the currently selected block key. + + Returns: + the key of the selected block or a empty string + """ + selection = self.treeview.get_selection() + treestore, iter = selection.get_selected() + return iter and treestore.get_value(iter, KEY_INDEX) or '' + + def _update_add_button(self): + """ + Update the add button's sensitivity. + The button should be active only if a block is selected. + """ + key = self._get_selected_block_key() + self.add_button.set_sensitive(bool(key)) + + def _add_selected_block(self): + """ + Add the selected block with the given key to the flow graph. + """ + key = self._get_selected_block_key() + if key: self.get_flow_graph().add_new_block(key) + + ############################################################ + ## Event Handlers + ############################################################ + def _handle_search(self, model, column, key, iter): + #determine which blocks match the search key + blocks = self.get_flow_graph().get_parent().get_blocks() + matching_blocks = filter(lambda b: key in b.get_key() or key in b.get_name().lower(), blocks) + #remove the old search category + try: self.treestore.remove(self._categories.pop((self._search_category, ))) + except (KeyError, AttributeError): pass #nothing to remove + #create a search category + if not matching_blocks: return + self._search_category = 'Search: %s'%key + for block in matching_blocks: self.add_block(self._search_category, block) + #expand the search category + path = self.treestore.get_path(self._categories[(self._search_category, )]) + self.treeview.collapse_all() + self.treeview.expand_row(path, open_all=False) + + def _handle_drag_get_data(self, widget, drag_context, selection_data, info, time): + """ + Handle a drag and drop by setting the key to the selection object. + This will call the destination handler for drag and drop. + Only call set when the key is valid to ignore DND from categories. + """ + key = self._get_selected_block_key() + if key: selection_data.set(selection_data.target, 8, key) + + def _handle_mouse_button_press(self, widget, event): + """ + Handle the mouse button press. + If a left double click is detected, call add selected block. + """ + if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS: + self._add_selected_block() + + def _handle_selection_change(self, selection): + """ + Handle a selection change in the tree view. + If a selection changes, set the add button sensitive. + """ + self._update_add_button() + + def _handle_add_button(self, widget): + """ + Handle the add button clicked signal. + Call add selected block. + """ + self._add_selected_block() diff --git a/grc/gui/Connection.py b/grc/gui/Connection.py index 4f46e73ea9..0f631791db 100644 --- a/grc/gui/Connection.py +++ b/grc/gui/Connection.py @@ -23,137 +23,137 @@ import Colors from Constants import CONNECTOR_ARROW_BASE, CONNECTOR_ARROW_HEIGHT class Connection(Element): - """ - A graphical connection for ports. - The connection has 2 parts, the arrow and the wire. - The coloring of the arrow and wire exposes the status of 3 states: - enabled/disabled, valid/invalid, highlighted/non-highlighted. - The wire coloring exposes the enabled and highlighted states. - The arrow coloring exposes the enabled and valid states. - """ + """ + A graphical connection for ports. + The connection has 2 parts, the arrow and the wire. + The coloring of the arrow and wire exposes the status of 3 states: + enabled/disabled, valid/invalid, highlighted/non-highlighted. + The wire coloring exposes the enabled and highlighted states. + The arrow coloring exposes the enabled and valid states. + """ - def __init__(self): Element.__init__(self) + def __init__(self): Element.__init__(self) - def get_coordinate(self): - """ - Get the 0,0 coordinate. - Coordinates are irrelevant in connection. - - Returns: - 0, 0 - """ - return (0, 0) + def get_coordinate(self): + """ + Get the 0,0 coordinate. + Coordinates are irrelevant in connection. + + Returns: + 0, 0 + """ + return (0, 0) - def get_rotation(self): - """ - Get the 0 degree rotation. - Rotations are irrelevant in connection. - - Returns: - 0 - """ - return 0 + def get_rotation(self): + """ + Get the 0 degree rotation. + Rotations are irrelevant in connection. + + Returns: + 0 + """ + return 0 - def create_shapes(self): - """Precalculate relative coordinates.""" - Element.create_shapes(self) - self._sink_rot = None - self._source_rot = None - self._sink_coor = None - self._source_coor = None - #get the source coordinate - try: - connector_length = self.get_source().get_connector_length() - except: - return - self.x1, self.y1 = Utils.get_rotated_coordinate((connector_length, 0), self.get_source().get_rotation()) - #get the sink coordinate - connector_length = self.get_sink().get_connector_length() + CONNECTOR_ARROW_HEIGHT - self.x2, self.y2 = Utils.get_rotated_coordinate((-connector_length, 0), self.get_sink().get_rotation()) - #build the arrow - self.arrow = [(0, 0), - Utils.get_rotated_coordinate((-CONNECTOR_ARROW_HEIGHT, -CONNECTOR_ARROW_BASE/2), self.get_sink().get_rotation()), - Utils.get_rotated_coordinate((-CONNECTOR_ARROW_HEIGHT, CONNECTOR_ARROW_BASE/2), self.get_sink().get_rotation()), - ] - self._update_after_move() - if not self.get_enabled(): self._arrow_color = Colors.CONNECTION_DISABLED_COLOR - elif not self.is_valid(): self._arrow_color = Colors.CONNECTION_ERROR_COLOR - else: self._arrow_color = Colors.CONNECTION_ENABLED_COLOR + def create_shapes(self): + """Precalculate relative coordinates.""" + Element.create_shapes(self) + self._sink_rot = None + self._source_rot = None + self._sink_coor = None + self._source_coor = None + #get the source coordinate + try: + connector_length = self.get_source().get_connector_length() + except: + return + self.x1, self.y1 = Utils.get_rotated_coordinate((connector_length, 0), self.get_source().get_rotation()) + #get the sink coordinate + connector_length = self.get_sink().get_connector_length() + CONNECTOR_ARROW_HEIGHT + self.x2, self.y2 = Utils.get_rotated_coordinate((-connector_length, 0), self.get_sink().get_rotation()) + #build the arrow + self.arrow = [(0, 0), + Utils.get_rotated_coordinate((-CONNECTOR_ARROW_HEIGHT, -CONNECTOR_ARROW_BASE/2), self.get_sink().get_rotation()), + Utils.get_rotated_coordinate((-CONNECTOR_ARROW_HEIGHT, CONNECTOR_ARROW_BASE/2), self.get_sink().get_rotation()), + ] + self._update_after_move() + if not self.get_enabled(): self._arrow_color = Colors.CONNECTION_DISABLED_COLOR + elif not self.is_valid(): self._arrow_color = Colors.CONNECTION_ERROR_COLOR + else: self._arrow_color = Colors.CONNECTION_ENABLED_COLOR - def _update_after_move(self): - """Calculate coordinates.""" - self.clear() #FIXME do i want this here? - #source connector - source = self.get_source() - X, Y = source.get_connector_coordinate() - x1, y1 = self.x1 + X, self.y1 + Y - self.add_line((x1, y1), (X, Y)) - #sink connector - sink = self.get_sink() - X, Y = sink.get_connector_coordinate() - x2, y2 = self.x2 + X, self.y2 + Y - self.add_line((x2, y2), (X, Y)) - #adjust arrow - self._arrow = [(x+X, y+Y) for x,y in self.arrow] - #add the horizontal and vertical lines in this connection - if abs(source.get_connector_direction() - sink.get_connector_direction()) == 180: - #2 possible point sets to create a 3-line connector - mid_x, mid_y = (x1 + x2)/2.0, (y1 + y2)/2.0 - points = [((mid_x, y1), (mid_x, y2)), ((x1, mid_y), (x2, mid_y))] - #source connector -> points[0][0] should be in the direction of source (if possible) - if Utils.get_angle_from_coordinates((x1, y1), points[0][0]) != source.get_connector_direction(): points.reverse() - #points[0][0] -> sink connector should not be in the direction of sink - if Utils.get_angle_from_coordinates(points[0][0], (x2, y2)) == sink.get_connector_direction(): points.reverse() - #points[0][0] -> source connector should not be in the direction of source - if Utils.get_angle_from_coordinates(points[0][0], (x1, y1)) == source.get_connector_direction(): points.reverse() - #create 3-line connector - p1, p2 = map(int, points[0][0]), map(int, points[0][1]) - self.add_line((x1, y1), p1) - self.add_line(p1, p2) - self.add_line((x2, y2), p2) - else: - #2 possible points to create a right-angled connector - points = [(x1, y2), (x2, y1)] - #source connector -> points[0] should be in the direction of source (if possible) - if Utils.get_angle_from_coordinates((x1, y1), points[0]) != source.get_connector_direction(): points.reverse() - #points[0] -> sink connector should not be in the direction of sink - if Utils.get_angle_from_coordinates(points[0], (x2, y2)) == sink.get_connector_direction(): points.reverse() - #points[0] -> source connector should not be in the direction of source - if Utils.get_angle_from_coordinates(points[0], (x1, y1)) == source.get_connector_direction(): points.reverse() - #create right-angled connector - self.add_line((x1, y1), points[0]) - self.add_line((x2, y2), points[0]) + def _update_after_move(self): + """Calculate coordinates.""" + self.clear() #FIXME do i want this here? + #source connector + source = self.get_source() + X, Y = source.get_connector_coordinate() + x1, y1 = self.x1 + X, self.y1 + Y + self.add_line((x1, y1), (X, Y)) + #sink connector + sink = self.get_sink() + X, Y = sink.get_connector_coordinate() + x2, y2 = self.x2 + X, self.y2 + Y + self.add_line((x2, y2), (X, Y)) + #adjust arrow + self._arrow = [(x+X, y+Y) for x,y in self.arrow] + #add the horizontal and vertical lines in this connection + if abs(source.get_connector_direction() - sink.get_connector_direction()) == 180: + #2 possible point sets to create a 3-line connector + mid_x, mid_y = (x1 + x2)/2.0, (y1 + y2)/2.0 + points = [((mid_x, y1), (mid_x, y2)), ((x1, mid_y), (x2, mid_y))] + #source connector -> points[0][0] should be in the direction of source (if possible) + if Utils.get_angle_from_coordinates((x1, y1), points[0][0]) != source.get_connector_direction(): points.reverse() + #points[0][0] -> sink connector should not be in the direction of sink + if Utils.get_angle_from_coordinates(points[0][0], (x2, y2)) == sink.get_connector_direction(): points.reverse() + #points[0][0] -> source connector should not be in the direction of source + if Utils.get_angle_from_coordinates(points[0][0], (x1, y1)) == source.get_connector_direction(): points.reverse() + #create 3-line connector + p1, p2 = map(int, points[0][0]), map(int, points[0][1]) + self.add_line((x1, y1), p1) + self.add_line(p1, p2) + self.add_line((x2, y2), p2) + else: + #2 possible points to create a right-angled connector + points = [(x1, y2), (x2, y1)] + #source connector -> points[0] should be in the direction of source (if possible) + if Utils.get_angle_from_coordinates((x1, y1), points[0]) != source.get_connector_direction(): points.reverse() + #points[0] -> sink connector should not be in the direction of sink + if Utils.get_angle_from_coordinates(points[0], (x2, y2)) == sink.get_connector_direction(): points.reverse() + #points[0] -> source connector should not be in the direction of source + if Utils.get_angle_from_coordinates(points[0], (x1, y1)) == source.get_connector_direction(): points.reverse() + #create right-angled connector + self.add_line((x1, y1), points[0]) + self.add_line((x2, y2), points[0]) - def draw(self, gc, window): - """ - Draw the connection. - - Args: - gc: the graphics context - window: the gtk window to draw on - """ - sink = self.get_sink() - source = self.get_source() - #check for changes - if self._sink_rot != sink.get_rotation() or self._source_rot != source.get_rotation(): self.create_shapes() - elif self._sink_coor != sink.get_coordinate() or self._source_coor != source.get_coordinate(): - try: - self._update_after_move() - except: - return - #cache values - self._sink_rot = sink.get_rotation() - self._source_rot = source.get_rotation() - self._sink_coor = sink.get_coordinate() - self._source_coor = source.get_coordinate() - #draw - if self.is_highlighted(): border_color = Colors.HIGHLIGHT_COLOR - elif self.get_enabled(): border_color = Colors.CONNECTION_ENABLED_COLOR - else: border_color = Colors.CONNECTION_DISABLED_COLOR - Element.draw(self, gc, window, bg_color=None, border_color=border_color) - #draw arrow on sink port - try: - gc.set_foreground(self._arrow_color) - window.draw_polygon(gc, True, self._arrow) - except: - return + def draw(self, gc, window): + """ + Draw the connection. + + Args: + gc: the graphics context + window: the gtk window to draw on + """ + sink = self.get_sink() + source = self.get_source() + #check for changes + if self._sink_rot != sink.get_rotation() or self._source_rot != source.get_rotation(): self.create_shapes() + elif self._sink_coor != sink.get_coordinate() or self._source_coor != source.get_coordinate(): + try: + self._update_after_move() + except: + return + #cache values + self._sink_rot = sink.get_rotation() + self._source_rot = source.get_rotation() + self._sink_coor = sink.get_coordinate() + self._source_coor = source.get_coordinate() + #draw + if self.is_highlighted(): border_color = Colors.HIGHLIGHT_COLOR + elif self.get_enabled(): border_color = Colors.CONNECTION_ENABLED_COLOR + else: border_color = Colors.CONNECTION_DISABLED_COLOR + Element.draw(self, gc, window, bg_color=None, border_color=border_color) + #draw arrow on sink port + try: + gc.set_foreground(self._arrow_color) + window.draw_polygon(gc, True, self._arrow) + except: + return diff --git a/grc/gui/Dialogs.py b/grc/gui/Dialogs.py index df424750b9..5b3b420d3b 100644 --- a/grc/gui/Dialogs.py +++ b/grc/gui/Dialogs.py @@ -23,46 +23,46 @@ import gtk import Utils class TextDisplay(gtk.TextView): - """A non editable gtk text view.""" - - def __init__(self, text=''): - """ - TextDisplay constructor. - - Args: - text: the text to display (string) - """ - text_buffer = gtk.TextBuffer() - text_buffer.set_text(text) - self.set_text = text_buffer.set_text - self.insert = lambda line: text_buffer.insert(text_buffer.get_end_iter(), line) - gtk.TextView.__init__(self, text_buffer) - self.set_editable(False) - self.set_cursor_visible(False) - self.set_wrap_mode(gtk.WRAP_WORD_CHAR) + """A non editable gtk text view.""" + + def __init__(self, text=''): + """ + TextDisplay constructor. + + Args: + text: the text to display (string) + """ + text_buffer = gtk.TextBuffer() + text_buffer.set_text(text) + self.set_text = text_buffer.set_text + self.insert = lambda line: text_buffer.insert(text_buffer.get_end_iter(), line) + gtk.TextView.__init__(self, text_buffer) + self.set_editable(False) + self.set_cursor_visible(False) + self.set_wrap_mode(gtk.WRAP_WORD_CHAR) def MessageDialogHelper(type, buttons, title=None, markup=None): - """ - Create a modal message dialog and run it. - - Args: - type: the type of message: gtk.MESSAGE_INFO, gtk.MESSAGE_WARNING, gtk.MESSAGE_QUESTION or gtk.MESSAGE_ERROR - buttons: the predefined set of buttons to use: - gtk.BUTTONS_NONE, gtk.BUTTONS_OK, gtk.BUTTONS_CLOSE, gtk.BUTTONS_CANCEL, gtk.BUTTONS_YES_NO, gtk.BUTTONS_OK_CANCEL - - Args: - tittle: the title of the window (string) - markup: the message text with pango markup - - Returns: - the gtk response from run() - """ - message_dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, type, buttons) - if title: message_dialog.set_title(title) - if markup: message_dialog.set_markup(markup) - response = message_dialog.run() - message_dialog.destroy() - return response + """ + Create a modal message dialog and run it. + + Args: + type: the type of message: gtk.MESSAGE_INFO, gtk.MESSAGE_WARNING, gtk.MESSAGE_QUESTION or gtk.MESSAGE_ERROR + buttons: the predefined set of buttons to use: + gtk.BUTTONS_NONE, gtk.BUTTONS_OK, gtk.BUTTONS_CLOSE, gtk.BUTTONS_CANCEL, gtk.BUTTONS_YES_NO, gtk.BUTTONS_OK_CANCEL + + Args: + tittle: the title of the window (string) + markup: the message text with pango markup + + Returns: + the gtk response from run() + """ + message_dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, type, buttons) + if title: message_dialog.set_title(title) + if markup: message_dialog.set_markup(markup) + response = message_dialog.run() + message_dialog.destroy() + return response ERRORS_MARKUP_TMPL="""\ @@ -72,31 +72,31 @@ $encode($err_msg.replace('\t', ' ')) #end for""" def ErrorsDialog(flowgraph): MessageDialogHelper( - type=gtk.MESSAGE_ERROR, - buttons=gtk.BUTTONS_CLOSE, - title='Flow Graph Errors', - markup=Utils.parse_template(ERRORS_MARKUP_TMPL, errors=flowgraph.get_error_messages()), + type=gtk.MESSAGE_ERROR, + buttons=gtk.BUTTONS_CLOSE, + title='Flow Graph Errors', + markup=Utils.parse_template(ERRORS_MARKUP_TMPL, errors=flowgraph.get_error_messages()), ) class AboutDialog(gtk.AboutDialog): - """A cute little about dialog.""" - - def __init__(self, platform): - """AboutDialog constructor.""" - gtk.AboutDialog.__init__(self) - self.set_name(platform.get_name()) - self.set_version(platform.get_version()) - self.set_license(platform.get_license()) - self.set_copyright(platform.get_license().splitlines()[0]) - self.set_website(platform.get_website()) - self.run() - self.destroy() + """A cute little about dialog.""" + + def __init__(self, platform): + """AboutDialog constructor.""" + gtk.AboutDialog.__init__(self) + self.set_name(platform.get_name()) + self.set_version(platform.get_version()) + self.set_license(platform.get_license()) + self.set_copyright(platform.get_license().splitlines()[0]) + self.set_website(platform.get_website()) + self.run() + self.destroy() def HelpDialog(): MessageDialogHelper( - type=gtk.MESSAGE_INFO, - buttons=gtk.BUTTONS_CLOSE, - title='Help', - markup="""\ + type=gtk.MESSAGE_INFO, + buttons=gtk.BUTTONS_CLOSE, + title='Help', + markup="""\ <b>Usage Tips</b> <u>Add block</u>: drag and drop or double click a block in the block selection window. @@ -112,15 +112,15 @@ COLORS_DIALOG_MARKUP_TMPL = """\ <b>Color Mapping</b> #if $colors - #set $max_len = max([len(color[0]) for color in $colors]) + 10 - #for $title, $color_spec in $colors + #set $max_len = max([len(color[0]) for color in $colors]) + 10 + #for $title, $color_spec in $colors <span background="$color_spec"><tt>$($encode($title).center($max_len))</tt></span> - #end for + #end for #end if """ def TypesDialog(platform): MessageDialogHelper( - type=gtk.MESSAGE_INFO, - buttons=gtk.BUTTONS_CLOSE, - title='Types', - markup=Utils.parse_template(COLORS_DIALOG_MARKUP_TMPL, colors=platform.get_colors())) + type=gtk.MESSAGE_INFO, + buttons=gtk.BUTTONS_CLOSE, + title='Types', + markup=Utils.parse_template(COLORS_DIALOG_MARKUP_TMPL, colors=platform.get_colors())) diff --git a/grc/gui/DrawingArea.py b/grc/gui/DrawingArea.py index da16eb7cf2..64be4d3ea6 100644 --- a/grc/gui/DrawingArea.py +++ b/grc/gui/DrawingArea.py @@ -23,113 +23,113 @@ import gtk from Constants import MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT, DND_TARGETS class DrawingArea(gtk.DrawingArea): - """ - DrawingArea is the gtk pixel map that graphical elements may draw themselves on. - The drawing area also responds to mouse and key events. - """ + """ + DrawingArea is the gtk pixel map that graphical elements may draw themselves on. + The drawing area also responds to mouse and key events. + """ - def __init__(self, flow_graph): - """ - DrawingArea contructor. - Connect event handlers. - - Args: - main_window: the main_window containing all flow graphs - """ - self.ctrl_mask = False - self._flow_graph = flow_graph - gtk.DrawingArea.__init__(self) - self.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT) - self.connect('realize', self._handle_window_realize) - self.connect('configure-event', self._handle_window_configure) - self.connect('expose-event', self._handle_window_expose) - self.connect('motion-notify-event', self._handle_mouse_motion) - self.connect('button-press-event', self._handle_mouse_button_press) - self.connect('button-release-event', self._handle_mouse_button_release) - self.add_events( - gtk.gdk.BUTTON_PRESS_MASK | \ - gtk.gdk.POINTER_MOTION_MASK | \ - gtk.gdk.BUTTON_RELEASE_MASK | \ - gtk.gdk.LEAVE_NOTIFY_MASK | \ - gtk.gdk.ENTER_NOTIFY_MASK - ) - #setup drag and drop - self.drag_dest_set(gtk.DEST_DEFAULT_ALL, DND_TARGETS, gtk.gdk.ACTION_COPY) - self.connect('drag-data-received', self._handle_drag_data_received) - #setup the focus flag - self._focus_flag = False - self.get_focus_flag = lambda: self._focus_flag - def _handle_focus_event(widget, event, focus_flag): self._focus_flag = focus_flag - self.connect('leave-notify-event', _handle_focus_event, False) - self.connect('enter-notify-event', _handle_focus_event, True) + def __init__(self, flow_graph): + """ + DrawingArea contructor. + Connect event handlers. + + Args: + main_window: the main_window containing all flow graphs + """ + self.ctrl_mask = False + self._flow_graph = flow_graph + gtk.DrawingArea.__init__(self) + self.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT) + self.connect('realize', self._handle_window_realize) + self.connect('configure-event', self._handle_window_configure) + self.connect('expose-event', self._handle_window_expose) + self.connect('motion-notify-event', self._handle_mouse_motion) + self.connect('button-press-event', self._handle_mouse_button_press) + self.connect('button-release-event', self._handle_mouse_button_release) + self.add_events( + gtk.gdk.BUTTON_PRESS_MASK | \ + gtk.gdk.POINTER_MOTION_MASK | \ + gtk.gdk.BUTTON_RELEASE_MASK | \ + gtk.gdk.LEAVE_NOTIFY_MASK | \ + gtk.gdk.ENTER_NOTIFY_MASK + ) + #setup drag and drop + self.drag_dest_set(gtk.DEST_DEFAULT_ALL, DND_TARGETS, gtk.gdk.ACTION_COPY) + self.connect('drag-data-received', self._handle_drag_data_received) + #setup the focus flag + self._focus_flag = False + self.get_focus_flag = lambda: self._focus_flag + def _handle_focus_event(widget, event, focus_flag): self._focus_flag = focus_flag + self.connect('leave-notify-event', _handle_focus_event, False) + self.connect('enter-notify-event', _handle_focus_event, True) - def new_pixmap(self, width, height): return gtk.gdk.Pixmap(self.window, width, height, -1) - def get_pixbuf(self): - width, height = self._pixmap.get_size() - pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, 0, 8, width, height) - pixbuf.get_from_drawable(self._pixmap, self._pixmap.get_colormap(), 0, 0, 0, 0, width, height) - return pixbuf + def new_pixmap(self, width, height): return gtk.gdk.Pixmap(self.window, width, height, -1) + def get_pixbuf(self): + width, height = self._pixmap.get_size() + pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, 0, 8, width, height) + pixbuf.get_from_drawable(self._pixmap, self._pixmap.get_colormap(), 0, 0, 0, 0, width, height) + return pixbuf - ########################################################################## - ## Handlers - ########################################################################## - def _handle_drag_data_received(self, widget, drag_context, x, y, selection_data, info, time): - """ - Handle a drag and drop by adding a block at the given coordinate. - """ - self._flow_graph.add_new_block(selection_data.data, (x, y)) + ########################################################################## + ## Handlers + ########################################################################## + def _handle_drag_data_received(self, widget, drag_context, x, y, selection_data, info, time): + """ + Handle a drag and drop by adding a block at the given coordinate. + """ + self._flow_graph.add_new_block(selection_data.data, (x, y)) - def _handle_mouse_button_press(self, widget, event): - """ - Forward button click information to the flow graph. - """ - self.ctrl_mask = event.state & gtk.gdk.CONTROL_MASK - if event.button == 1: self._flow_graph.handle_mouse_selector_press( - double_click=(event.type == gtk.gdk._2BUTTON_PRESS), - coordinate=(event.x, event.y), - ) - if event.button == 3: self._flow_graph.handle_mouse_context_press( - coordinate=(event.x, event.y), - event=event, - ) + def _handle_mouse_button_press(self, widget, event): + """ + Forward button click information to the flow graph. + """ + self.ctrl_mask = event.state & gtk.gdk.CONTROL_MASK + if event.button == 1: self._flow_graph.handle_mouse_selector_press( + double_click=(event.type == gtk.gdk._2BUTTON_PRESS), + coordinate=(event.x, event.y), + ) + if event.button == 3: self._flow_graph.handle_mouse_context_press( + coordinate=(event.x, event.y), + event=event, + ) - def _handle_mouse_button_release(self, widget, event): - """ - Forward button release information to the flow graph. - """ - self.ctrl_mask = event.state & gtk.gdk.CONTROL_MASK - if event.button == 1: self._flow_graph.handle_mouse_selector_release( - coordinate=(event.x, event.y), - ) + def _handle_mouse_button_release(self, widget, event): + """ + Forward button release information to the flow graph. + """ + self.ctrl_mask = event.state & gtk.gdk.CONTROL_MASK + if event.button == 1: self._flow_graph.handle_mouse_selector_release( + coordinate=(event.x, event.y), + ) - def _handle_mouse_motion(self, widget, event): - """ - Forward mouse motion information to the flow graph. - """ - self.ctrl_mask = event.state & gtk.gdk.CONTROL_MASK - self._flow_graph.handle_mouse_motion( - coordinate=(event.x, event.y), - ) + def _handle_mouse_motion(self, widget, event): + """ + Forward mouse motion information to the flow graph. + """ + self.ctrl_mask = event.state & gtk.gdk.CONTROL_MASK + self._flow_graph.handle_mouse_motion( + coordinate=(event.x, event.y), + ) - def _handle_window_realize(self, widget): - """ - Called when the window is realized. - Update the flowgraph, which calls new pixmap. - """ - self._flow_graph.update() + def _handle_window_realize(self, widget): + """ + Called when the window is realized. + Update the flowgraph, which calls new pixmap. + """ + self._flow_graph.update() - def _handle_window_configure(self, widget, event): - """ - Called when the window is resized. - Create a new pixmap for background buffer. - """ - self._pixmap = self.new_pixmap(*self.get_size_request()) + def _handle_window_configure(self, widget, event): + """ + Called when the window is resized. + Create a new pixmap for background buffer. + """ + self._pixmap = self.new_pixmap(*self.get_size_request()) - def _handle_window_expose(self, widget, event): - """ - Called when window is exposed, or queue_draw is called. - Double buffering: draw to pixmap, then draw pixmap to window. - """ - gc = self.window.new_gc() - self._flow_graph.draw(gc, self._pixmap) - self.window.draw_drawable(gc, self._pixmap, 0, 0, 0, 0, -1, -1) + def _handle_window_expose(self, widget, event): + """ + Called when window is exposed, or queue_draw is called. + Double buffering: draw to pixmap, then draw pixmap to window. + """ + gc = self.window.new_gc() + self._flow_graph.draw(gc, self._pixmap) + self.window.draw_drawable(gc, self._pixmap, 0, 0, 0, 0, -1, -1) diff --git a/grc/gui/Element.py b/grc/gui/Element.py index cd97a3fb21..915bdfb915 100644 --- a/grc/gui/Element.py +++ b/grc/gui/Element.py @@ -21,245 +21,244 @@ from Constants import LINE_SELECT_SENSITIVITY from Constants import POSSIBLE_ROTATIONS class Element(object): - """ - GraphicalElement is the base class for all graphical elements. - It contains an X,Y coordinate, a list of rectangular areas that the element occupies, - and methods to detect selection of those areas. - """ + """ + GraphicalElement is the base class for all graphical elements. + It contains an X,Y coordinate, a list of rectangular areas that the element occupies, + and methods to detect selection of those areas. + """ - def __init__(self): - """ - Make a new list of rectangular areas and lines, and set the coordinate and the rotation. - """ - self.set_rotation(POSSIBLE_ROTATIONS[0]) - self.set_coordinate((0, 0)) - self.clear() - self.set_highlighted(False) + def __init__(self): + """ + Make a new list of rectangular areas and lines, and set the coordinate and the rotation. + """ + self.set_rotation(POSSIBLE_ROTATIONS[0]) + self.set_coordinate((0, 0)) + self.clear() + self.set_highlighted(False) - def is_horizontal(self, rotation=None): - """ - Is this element horizontal? - If rotation is None, use this element's rotation. - - Args: - rotation: the optional rotation - - Returns: - true if rotation is horizontal - """ - rotation = rotation or self.get_rotation() - return rotation in (0, 180) + def is_horizontal(self, rotation=None): + """ + Is this element horizontal? + If rotation is None, use this element's rotation. + + Args: + rotation: the optional rotation + + Returns: + true if rotation is horizontal + """ + rotation = rotation or self.get_rotation() + return rotation in (0, 180) - def is_vertical(self, rotation=None): - """ - Is this element vertical? - If rotation is None, use this element's rotation. - - Args: - rotation: the optional rotation - - Returns: - true if rotation is vertical - """ - rotation = rotation or self.get_rotation() - return rotation in (90, 270) + def is_vertical(self, rotation=None): + """ + Is this element vertical? + If rotation is None, use this element's rotation. + + Args: + rotation: the optional rotation + + Returns: + true if rotation is vertical + """ + rotation = rotation or self.get_rotation() + return rotation in (90, 270) - def create_labels(self): - """ - Create labels (if applicable) and call on all children. - Call this base method before creating labels in the element. - """ - for child in self.get_children():child.create_labels() + def create_labels(self): + """ + Create labels (if applicable) and call on all children. + Call this base method before creating labels in the element. + """ + for child in self.get_children():child.create_labels() - def create_shapes(self): - """ - Create shapes (if applicable) and call on all children. - Call this base method before creating shapes in the element. - """ - self.clear() - for child in self.get_children(): child.create_shapes() + def create_shapes(self): + """ + Create shapes (if applicable) and call on all children. + Call this base method before creating shapes in the element. + """ + self.clear() + for child in self.get_children(): child.create_shapes() - def draw(self, gc, window, border_color, bg_color): - """ - Draw in the given window. - - Args: - gc: the graphics context - window: the gtk window to draw on - border_color: the color for lines and rectangle borders - bg_color: the color for the inside of the rectangle - """ - - X,Y = self.get_coordinate() - for (rX,rY),(W,H) in self._areas_list: - aX = X + rX - aY = Y + rY - gc.set_foreground(bg_color) - window.draw_rectangle(gc, True, aX, aY, W, H) - gc.set_foreground(border_color) - window.draw_rectangle(gc, False, aX, aY, W, H) - for (x1, y1),(x2, y2) in self._lines_list: - gc.set_foreground(border_color) - window.draw_line(gc, X+x1, Y+y1, X+x2, Y+y2) + def draw(self, gc, window, border_color, bg_color): + """ + Draw in the given window. + + Args: + gc: the graphics context + window: the gtk window to draw on + border_color: the color for lines and rectangle borders + bg_color: the color for the inside of the rectangle + """ + X,Y = self.get_coordinate() + for (rX,rY),(W,H) in self._areas_list: + aX = X + rX + aY = Y + rY + gc.set_foreground(bg_color) + window.draw_rectangle(gc, True, aX, aY, W, H) + gc.set_foreground(border_color) + window.draw_rectangle(gc, False, aX, aY, W, H) + for (x1, y1),(x2, y2) in self._lines_list: + gc.set_foreground(border_color) + window.draw_line(gc, X+x1, Y+y1, X+x2, Y+y2) - def rotate(self, rotation): - """ - Rotate all of the areas by 90 degrees. - - Args: - rotation: multiple of 90 degrees - """ - self.set_rotation((self.get_rotation() + rotation)%360) + def rotate(self, rotation): + """ + Rotate all of the areas by 90 degrees. + + Args: + rotation: multiple of 90 degrees + """ + self.set_rotation((self.get_rotation() + rotation)%360) - def clear(self): - """Empty the lines and areas.""" - self._areas_list = list() - self._lines_list = list() + def clear(self): + """Empty the lines and areas.""" + self._areas_list = list() + self._lines_list = list() - def set_coordinate(self, coor): - """ - Set the reference coordinate. - - Args: - coor: the coordinate tuple (x,y) - """ - self.coor = coor + def set_coordinate(self, coor): + """ + Set the reference coordinate. + + Args: + coor: the coordinate tuple (x,y) + """ + self.coor = coor - def get_parent(self): - """ - Get the parent of this element. - - Returns: - the parent - """ - return self.parent + def get_parent(self): + """ + Get the parent of this element. + + Returns: + the parent + """ + return self.parent - def set_highlighted(self, highlighted): - """ - Set the highlight status. - - Args: - highlighted: true to enable highlighting - """ - self.highlighted = highlighted + def set_highlighted(self, highlighted): + """ + Set the highlight status. + + Args: + highlighted: true to enable highlighting + """ + self.highlighted = highlighted - def is_highlighted(self): - """ - Get the highlight status. - - Returns: - true if highlighted - """ - return self.highlighted + def is_highlighted(self): + """ + Get the highlight status. + + Returns: + true if highlighted + """ + return self.highlighted - def get_coordinate(self): - """Get the coordinate. - - Returns: - the coordinate tuple (x,y) - """ - return self.coor + def get_coordinate(self): + """Get the coordinate. + + Returns: + the coordinate tuple (x,y) + """ + return self.coor - def move(self, delta_coor): - """ - Move the element by adding the delta_coor to the current coordinate. - - Args: - delta_coor: (delta_x,delta_y) tuple - """ - deltaX, deltaY = delta_coor - X, Y = self.get_coordinate() - self.set_coordinate((X+deltaX, Y+deltaY)) + def move(self, delta_coor): + """ + Move the element by adding the delta_coor to the current coordinate. + + Args: + delta_coor: (delta_x,delta_y) tuple + """ + deltaX, deltaY = delta_coor + X, Y = self.get_coordinate() + self.set_coordinate((X+deltaX, Y+deltaY)) - def add_area(self, rel_coor, area): - """ - Add an area to the area list. - An area is actually a coordinate relative to the main coordinate - with a width/height pair relative to the area coordinate. - A positive width is to the right of the coordinate. - A positive height is above the coordinate. - The area is associated with a rotation. - - Args: - rel_coor: (x,y) offset from this element's coordinate - area: (width,height) tuple - """ - self._areas_list.append((rel_coor, area)) + def add_area(self, rel_coor, area): + """ + Add an area to the area list. + An area is actually a coordinate relative to the main coordinate + with a width/height pair relative to the area coordinate. + A positive width is to the right of the coordinate. + A positive height is above the coordinate. + The area is associated with a rotation. + + Args: + rel_coor: (x,y) offset from this element's coordinate + area: (width,height) tuple + """ + self._areas_list.append((rel_coor, area)) - def add_line(self, rel_coor1, rel_coor2): - """ - Add a line to the line list. - A line is defined by 2 relative coordinates. - Lines must be horizontal or vertical. - The line is associated with a rotation. - - Args: - rel_coor1: relative (x1,y1) tuple - rel_coor2: relative (x2,y2) tuple - """ - self._lines_list.append((rel_coor1, rel_coor2)) + def add_line(self, rel_coor1, rel_coor2): + """ + Add a line to the line list. + A line is defined by 2 relative coordinates. + Lines must be horizontal or vertical. + The line is associated with a rotation. + + Args: + rel_coor1: relative (x1,y1) tuple + rel_coor2: relative (x2,y2) tuple + """ + self._lines_list.append((rel_coor1, rel_coor2)) - def what_is_selected(self, coor, coor_m=None): - """ - One coordinate specified: - Is this element selected at given coordinate? - ie: is the coordinate encompassed by one of the areas or lines? - Both coordinates specified: - Is this element within the rectangular region defined by both coordinates? - ie: do any area corners or line endpoints fall within the region? - - Args: - coor: the selection coordinate, tuple x, y - coor_m: an additional selection coordinate. - - Returns: - self if one of the areas/lines encompasses coor, else None. - """ - #function to test if p is between a and b (inclusive) - in_between = lambda p, a, b: p >= min(a, b) and p <= max(a, b) - #relative coordinate - x, y = [a-b for a,b in zip(coor, self.get_coordinate())] - if coor_m: - x_m, y_m = [a-b for a,b in zip(coor_m, self.get_coordinate())] - #handle rectangular areas - for (x1,y1), (w,h) in self._areas_list: - if in_between(x1, x, x_m) and in_between(y1, y, y_m) or \ - in_between(x1+w, x, x_m) and in_between(y1, y, y_m) or \ - in_between(x1, x, x_m) and in_between(y1+h, y, y_m) or \ - in_between(x1+w, x, x_m) and in_between(y1+h, y, y_m): - return self - #handle horizontal or vertical lines - for (x1, y1), (x2, y2) in self._lines_list: - if in_between(x1, x, x_m) and in_between(y1, y, y_m) or \ - in_between(x2, x, x_m) and in_between(y2, y, y_m): - return self - return None - else: - #handle rectangular areas - for (x1,y1), (w,h) in self._areas_list: - if in_between(x, x1, x1+w) and in_between(y, y1, y1+h): return self - #handle horizontal or vertical lines - for (x1, y1), (x2, y2) in self._lines_list: - if x1 == x2: x1, x2 = x1-LINE_SELECT_SENSITIVITY, x2+LINE_SELECT_SENSITIVITY - if y1 == y2: y1, y2 = y1-LINE_SELECT_SENSITIVITY, y2+LINE_SELECT_SENSITIVITY - if in_between(x, x1, x2) and in_between(y, y1, y2): return self - return None + def what_is_selected(self, coor, coor_m=None): + """ + One coordinate specified: + Is this element selected at given coordinate? + ie: is the coordinate encompassed by one of the areas or lines? + Both coordinates specified: + Is this element within the rectangular region defined by both coordinates? + ie: do any area corners or line endpoints fall within the region? + + Args: + coor: the selection coordinate, tuple x, y + coor_m: an additional selection coordinate. + + Returns: + self if one of the areas/lines encompasses coor, else None. + """ + #function to test if p is between a and b (inclusive) + in_between = lambda p, a, b: p >= min(a, b) and p <= max(a, b) + #relative coordinate + x, y = [a-b for a,b in zip(coor, self.get_coordinate())] + if coor_m: + x_m, y_m = [a-b for a,b in zip(coor_m, self.get_coordinate())] + #handle rectangular areas + for (x1,y1), (w,h) in self._areas_list: + if in_between(x1, x, x_m) and in_between(y1, y, y_m) or \ + in_between(x1+w, x, x_m) and in_between(y1, y, y_m) or \ + in_between(x1, x, x_m) and in_between(y1+h, y, y_m) or \ + in_between(x1+w, x, x_m) and in_between(y1+h, y, y_m): + return self + #handle horizontal or vertical lines + for (x1, y1), (x2, y2) in self._lines_list: + if in_between(x1, x, x_m) and in_between(y1, y, y_m) or \ + in_between(x2, x, x_m) and in_between(y2, y, y_m): + return self + return None + else: + #handle rectangular areas + for (x1,y1), (w,h) in self._areas_list: + if in_between(x, x1, x1+w) and in_between(y, y1, y1+h): return self + #handle horizontal or vertical lines + for (x1, y1), (x2, y2) in self._lines_list: + if x1 == x2: x1, x2 = x1-LINE_SELECT_SENSITIVITY, x2+LINE_SELECT_SENSITIVITY + if y1 == y2: y1, y2 = y1-LINE_SELECT_SENSITIVITY, y2+LINE_SELECT_SENSITIVITY + if in_between(x, x1, x2) and in_between(y, y1, y2): return self + return None - def get_rotation(self): - """ - Get the rotation in degrees. - - Returns: - the rotation - """ - return self.rotation + def get_rotation(self): + """ + Get the rotation in degrees. + + Returns: + the rotation + """ + return self.rotation - def set_rotation(self, rotation): - """ - Set the rotation in degrees. - - Args: - rotation: the rotation""" - if rotation not in POSSIBLE_ROTATIONS: - raise Exception('"%s" is not one of the possible rotations: (%s)'%(rotation, POSSIBLE_ROTATIONS)) - self.rotation = rotation + def set_rotation(self, rotation): + """ + Set the rotation in degrees. + + Args: + rotation: the rotation""" + if rotation not in POSSIBLE_ROTATIONS: + raise Exception('"%s" is not one of the possible rotations: (%s)'%(rotation, POSSIBLE_ROTATIONS)) + self.rotation = rotation diff --git a/grc/gui/FileDialogs.py b/grc/gui/FileDialogs.py index 20172a7418..e8e859dc60 100644 --- a/grc/gui/FileDialogs.py +++ b/grc/gui/FileDialogs.py @@ -22,8 +22,8 @@ pygtk.require('2.0') import gtk from Dialogs import MessageDialogHelper from Constants import \ - DEFAULT_FILE_PATH, IMAGE_FILE_EXTENSION, \ - NEW_FLOGRAPH_TITLE + DEFAULT_FILE_PATH, IMAGE_FILE_EXTENSION, \ + NEW_FLOGRAPH_TITLE import Preferences from os import path import Utils @@ -46,139 +46,139 @@ File <b>$encode($filename)</b> Does not Exist!""" ################################################## ##the filter for flow graph files def get_flow_graph_files_filter(): - filter = gtk.FileFilter() - filter.set_name('Flow Graph Files') - filter.add_pattern('*'+Preferences.file_extension()) - return filter + filter = gtk.FileFilter() + filter.set_name('Flow Graph Files') + filter.add_pattern('*'+Preferences.file_extension()) + return filter ##the filter for image files def get_image_files_filter(): - filter = gtk.FileFilter() - filter.set_name('Image Files') - filter.add_pattern('*'+IMAGE_FILE_EXTENSION) - return filter + filter = gtk.FileFilter() + filter.set_name('Image Files') + filter.add_pattern('*'+IMAGE_FILE_EXTENSION) + return filter ##the filter for all files def get_all_files_filter(): - filter = gtk.FileFilter() - filter.set_name('All Files') - filter.add_pattern('*') - return filter + filter = gtk.FileFilter() + filter.set_name('All Files') + filter.add_pattern('*') + return filter ################################################## # File Dialogs ################################################## class FileDialogHelper(gtk.FileChooserDialog): - """ - A wrapper class for the gtk file chooser dialog. - Implement a file chooser dialog with only necessary parameters. - """ - - def __init__(self, action, title): - """ - FileDialogHelper contructor. - Create a save or open dialog with cancel and ok buttons. - Use standard settings: no multiple selection, local files only, and the * filter. - - Args: - action: gtk.FILE_CHOOSER_ACTION_OPEN or gtk.FILE_CHOOSER_ACTION_SAVE - title: the title of the dialog (string) - """ - ok_stock = {gtk.FILE_CHOOSER_ACTION_OPEN : 'gtk-open', gtk.FILE_CHOOSER_ACTION_SAVE : 'gtk-save'}[action] - gtk.FileChooserDialog.__init__(self, title, None, action, ('gtk-cancel', gtk.RESPONSE_CANCEL, ok_stock, gtk.RESPONSE_OK)) - self.set_select_multiple(False) - self.set_local_only(True) - self.add_filter(get_all_files_filter()) + """ + A wrapper class for the gtk file chooser dialog. + Implement a file chooser dialog with only necessary parameters. + """ + + def __init__(self, action, title): + """ + FileDialogHelper contructor. + Create a save or open dialog with cancel and ok buttons. + Use standard settings: no multiple selection, local files only, and the * filter. + + Args: + action: gtk.FILE_CHOOSER_ACTION_OPEN or gtk.FILE_CHOOSER_ACTION_SAVE + title: the title of the dialog (string) + """ + ok_stock = {gtk.FILE_CHOOSER_ACTION_OPEN : 'gtk-open', gtk.FILE_CHOOSER_ACTION_SAVE : 'gtk-save'}[action] + gtk.FileChooserDialog.__init__(self, title, None, action, ('gtk-cancel', gtk.RESPONSE_CANCEL, ok_stock, gtk.RESPONSE_OK)) + self.set_select_multiple(False) + self.set_local_only(True) + self.add_filter(get_all_files_filter()) class FileDialog(FileDialogHelper): - """A dialog box to save or open flow graph files. This is a base class, do not use.""" - - def __init__(self, current_file_path=''): - """ - FileDialog constructor. - - Args: - current_file_path: the current directory or path to the open flow graph - """ - if not current_file_path: current_file_path = path.join(DEFAULT_FILE_PATH, NEW_FLOGRAPH_TITLE + Preferences.file_extension()) - if self.type == OPEN_FLOW_GRAPH: - FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_OPEN, 'Open a Flow Graph from a File...') - self.add_and_set_filter(get_flow_graph_files_filter()) - self.set_select_multiple(True) - elif self.type == SAVE_FLOW_GRAPH: - FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_SAVE, 'Save a Flow Graph to a File...') - self.add_and_set_filter(get_flow_graph_files_filter()) - self.set_current_name(path.basename(current_file_path)) #show the current filename - elif self.type == SAVE_IMAGE: - FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_SAVE, 'Save a Flow Graph Screen Shot...') - 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 - self.set_current_folder(path.dirname(current_file_path)) #current directory - - def add_and_set_filter(self, filter): - """ - Add the gtk file filter to the list of filters and set it as the default file filter. - - Args: - filter: a gtk file filter. - """ - self.add_filter(filter) - self.set_filter(filter) - - def get_rectified_filename(self): - """ - Run the dialog and get the filename. - If this is a save dialog and the file name is missing the extension, append the file extension. - If the file name with the extension already exists, show a overwrite dialog. - If this is an open dialog, return a list of filenames. - - Returns: - the complete file path - """ - if gtk.FileChooserDialog.run(self) != gtk.RESPONSE_OK: return None #response was cancel - ############################################# - # Handle Save Dialogs - ############################################# - if self.type in (SAVE_FLOW_GRAPH, SAVE_IMAGE): - filename = self.get_filename() - extension = { - SAVE_FLOW_GRAPH: Preferences.file_extension(), - SAVE_IMAGE: IMAGE_FILE_EXTENSION, - }[self.type] - #append the missing file extension if the filter matches - if path.splitext(filename)[1].lower() != extension: filename += extension - self.set_current_name(path.basename(filename)) #show the filename with extension - if path.exists(filename): #ask the user to confirm overwrite - if MessageDialogHelper( - gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, 'Confirm Overwrite!', - Utils.parse_template(FILE_OVERWRITE_MARKUP_TMPL, filename=filename), - ) == gtk.RESPONSE_NO: return self.get_rectified_filename() - return filename - ############################################# - # Handle Open Dialogs - ############################################# - elif self.type in (OPEN_FLOW_GRAPH,): - filenames = self.get_filenames() - for filename in filenames: - if not path.exists(filename): #show a warning and re-run - MessageDialogHelper( - gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, 'Cannot Open!', - Utils.parse_template(FILE_DNE_MARKUP_TMPL, filename=filename), - ) - return self.get_rectified_filename() - return filenames - - def run(self): - """ - Get the filename and destroy the dialog. - - Returns: - the filename or None if a close/cancel occured. - """ - filename = self.get_rectified_filename() - self.destroy() - return filename + """A dialog box to save or open flow graph files. This is a base class, do not use.""" + + def __init__(self, current_file_path=''): + """ + FileDialog constructor. + + Args: + current_file_path: the current directory or path to the open flow graph + """ + if not current_file_path: current_file_path = path.join(DEFAULT_FILE_PATH, NEW_FLOGRAPH_TITLE + Preferences.file_extension()) + if self.type == OPEN_FLOW_GRAPH: + FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_OPEN, 'Open a Flow Graph from a File...') + self.add_and_set_filter(get_flow_graph_files_filter()) + self.set_select_multiple(True) + elif self.type == SAVE_FLOW_GRAPH: + FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_SAVE, 'Save a Flow Graph to a File...') + self.add_and_set_filter(get_flow_graph_files_filter()) + self.set_current_name(path.basename(current_file_path)) #show the current filename + elif self.type == SAVE_IMAGE: + FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_SAVE, 'Save a Flow Graph Screen Shot...') + 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 + self.set_current_folder(path.dirname(current_file_path)) #current directory + + def add_and_set_filter(self, filter): + """ + Add the gtk file filter to the list of filters and set it as the default file filter. + + Args: + filter: a gtk file filter. + """ + self.add_filter(filter) + self.set_filter(filter) + + def get_rectified_filename(self): + """ + Run the dialog and get the filename. + If this is a save dialog and the file name is missing the extension, append the file extension. + If the file name with the extension already exists, show a overwrite dialog. + If this is an open dialog, return a list of filenames. + + Returns: + the complete file path + """ + if gtk.FileChooserDialog.run(self) != gtk.RESPONSE_OK: return None #response was cancel + ############################################# + # Handle Save Dialogs + ############################################# + if self.type in (SAVE_FLOW_GRAPH, SAVE_IMAGE): + filename = self.get_filename() + extension = { + SAVE_FLOW_GRAPH: Preferences.file_extension(), + SAVE_IMAGE: IMAGE_FILE_EXTENSION, + }[self.type] + #append the missing file extension if the filter matches + if path.splitext(filename)[1].lower() != extension: filename += extension + self.set_current_name(path.basename(filename)) #show the filename with extension + if path.exists(filename): #ask the user to confirm overwrite + if MessageDialogHelper( + gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, 'Confirm Overwrite!', + Utils.parse_template(FILE_OVERWRITE_MARKUP_TMPL, filename=filename), + ) == gtk.RESPONSE_NO: return self.get_rectified_filename() + return filename + ############################################# + # Handle Open Dialogs + ############################################# + elif self.type in (OPEN_FLOW_GRAPH,): + filenames = self.get_filenames() + for filename in filenames: + if not path.exists(filename): #show a warning and re-run + MessageDialogHelper( + gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, 'Cannot Open!', + Utils.parse_template(FILE_DNE_MARKUP_TMPL, filename=filename), + ) + return self.get_rectified_filename() + return filenames + + def run(self): + """ + Get the filename and destroy the dialog. + + Returns: + the filename or None if a close/cancel occured. + """ + filename = self.get_rectified_filename() + self.destroy() + return filename class OpenFlowGraphFileDialog(FileDialog): type = OPEN_FLOW_GRAPH class SaveFlowGraphFileDialog(FileDialog): type = SAVE_FLOW_GRAPH diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index a238ed166a..4dff675afb 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -29,548 +29,537 @@ import random import Messages class FlowGraph(Element): - """ - FlowGraph is the data structure to store graphical signal blocks, - graphical inputs and outputs, - and the connections between inputs and outputs. - """ - - def __init__(self): - """ - FlowGraph contructor. - Create a list for signal blocks and connections. Connect mouse handlers. - """ - Element.__init__(self) - #when is the flow graph selected? (used by keyboard event handler) - self.is_selected = lambda: bool(self.get_selected_elements()) - #important vars dealing with mouse event tracking - self.element_moved = False - self.mouse_pressed = False - self.unselect() - self.press_coor = (0, 0) - #selected ports - self._old_selected_port = None - self._new_selected_port = None - #context menu - self._context_menu = gtk.Menu() - for action in [ - Actions.BLOCK_CUT, - Actions.BLOCK_COPY, - Actions.BLOCK_PASTE, - Actions.ELEMENT_DELETE, - Actions.BLOCK_ROTATE_CCW, - Actions.BLOCK_ROTATE_CW, - Actions.BLOCK_ENABLE, - Actions.BLOCK_DISABLE, - Actions.BLOCK_PARAM_MODIFY, - Actions.BLOCK_CREATE_HIER, - Actions.OPEN_HIER, - Actions.BUSSIFY_SOURCES, - Actions.BUSSIFY_SINKS, - ]: self._context_menu.append(action.create_menu_item()) - - ########################################################################### - # Access Drawing Area - ########################################################################### - def get_drawing_area(self): return self.drawing_area - def queue_draw(self): self.get_drawing_area().queue_draw() - def get_size(self): return self.get_drawing_area().get_size_request() - def set_size(self, *args): self.get_drawing_area().set_size_request(*args) - def get_scroll_pane(self): return self.drawing_area.get_parent() - def get_ctrl_mask(self): return self.drawing_area.ctrl_mask - def new_pixmap(self, *args): return self.get_drawing_area().new_pixmap(*args) - - def add_new_block(self, key, coor=None): - """ - Add a block of the given key to this flow graph. - - Args: - key: the block key - coor: an optional coordinate or None for random - """ - id = self._get_unique_id(key) - #calculate the position coordinate - h_adj = self.get_scroll_pane().get_hadjustment() - v_adj = self.get_scroll_pane().get_vadjustment() - if coor is None: coor = ( - int(random.uniform(.25, .75)*h_adj.page_size + h_adj.get_value()), - int(random.uniform(.25, .75)*v_adj.page_size + v_adj.get_value()), - ) - #get the new block - block = self.get_new_block(key) - block.set_coordinate(coor) - block.set_rotation(0) - block.get_param('id').set_value(id) - Actions.ELEMENT_CREATE() - - return id - - ########################################################################### - # Copy Paste - ########################################################################### - def copy_to_clipboard(self): - """ - Copy the selected blocks and connections into the clipboard. - - Returns: - the clipboard - """ - #get selected blocks - blocks = self.get_selected_blocks() - if not blocks: return None - #calc x and y min - x_min, y_min = blocks[0].get_coordinate() - for block in blocks: - x, y = block.get_coordinate() - x_min = min(x, x_min) - y_min = min(y, y_min) - #get connections between selected blocks - connections = filter( - lambda c: c.get_source().get_parent() in blocks and c.get_sink().get_parent() in blocks, - self.get_connections(), - ) - clipboard = ( - (x_min, y_min), - [block.export_data() for block in blocks], - [connection.export_data() for connection in connections], - ) - return clipboard - - def paste_from_clipboard(self, clipboard): - """ - Paste the blocks and connections from the clipboard. - - Args: - clipboard: the nested data of blocks, connections - """ - - selected = set() - (x_min, y_min), blocks_n, connections_n = clipboard - old_id2block = dict() - #recalc the position - h_adj = self.get_scroll_pane().get_hadjustment() - v_adj = self.get_scroll_pane().get_vadjustment() - x_off = h_adj.get_value() - x_min + h_adj.page_size/4 - y_off = v_adj.get_value() - y_min + v_adj.page_size/4 - #create blocks - for block_n in blocks_n: - block_key = block_n.find('key') - if block_key == 'options': continue - block = self.get_new_block(block_key) - selected.add(block) - #set params - params_n = block_n.findall('param') - for param_n in params_n: - - param_key = param_n.find('key') - param_value = param_n.find('value') - #setup id parameter - if param_key == 'id': - old_id2block[param_value] = block - #if the block id is not unique, get a new block id - if param_value in [bluck.get_id() for bluck in self.get_blocks()]: - param_value = self._get_unique_id(param_value) - - - #set value to key - - block.get_param(param_key).set_value(param_value) - #move block to offset coordinate - block.move((x_off, y_off)) - - #update before creating connections - self.update() - #create connections - for connection_n in connections_n: - source = old_id2block[connection_n.find('source_block_id')].get_source(connection_n.find('source_key')) - sink = old_id2block[connection_n.find('sink_block_id')].get_sink(connection_n.find('sink_key')) - self.connect(source, sink) - #set all pasted elements selected - for block in selected: selected = selected.union(set(block.get_connections())) - self._selected_elements = list(selected) - - ########################################################################### - # Modify Selected - ########################################################################### - def type_controller_modify_selected(self, direction): - """ - Change the registered type controller for the selected signal blocks. - - Args: - direction: +1 or -1 - - Returns: - true for change - """ - return any([sb.type_controller_modify(direction) for sb in self.get_selected_blocks()]) - - def port_controller_modify_selected(self, direction): - """ - Change port controller for the selected signal blocks. - - Args: - direction: +1 or -1 - - Returns: - true for changed - """ - return any([sb.port_controller_modify(direction) for sb in self.get_selected_blocks()]) - - def enable_selected(self, enable): - """ - Enable/disable the selected blocks. - - Args: - enable: true to enable - - Returns: - true if changed - """ - changed = False - for selected_block in self.get_selected_blocks(): - if selected_block.get_enabled() != enable: - selected_block.set_enabled(enable) - changed = True - return changed - - def move_selected(self, delta_coordinate): - """ - Move the element and by the change in coordinates. - - Args: - delta_coordinate: the change in coordinates - """ - for selected_block in self.get_selected_blocks(): - selected_block.move(delta_coordinate) - self.element_moved = True - - def rotate_selected(self, rotation): - """ - Rotate the selected blocks by multiples of 90 degrees. - - Args: - rotation: the rotation in degrees - - Returns: - true if changed, otherwise false. - """ - if not self.get_selected_blocks(): return False - #initialize min and max coordinates - min_x, min_y = self.get_selected_block().get_coordinate() - max_x, max_y = self.get_selected_block().get_coordinate() - #rotate each selected block, and find min/max coordinate - for selected_block in self.get_selected_blocks(): - selected_block.rotate(rotation) - #update the min/max coordinate - x, y = selected_block.get_coordinate() - min_x, min_y = min(min_x, x), min(min_y, y) - max_x, max_y = max(max_x, x), max(max_y, y) - #calculate center point of slected blocks - ctr_x, ctr_y = (max_x + min_x)/2, (max_y + min_y)/2 - #rotate the blocks around the center point - for selected_block in self.get_selected_blocks(): - x, y = selected_block.get_coordinate() - x, y = Utils.get_rotated_coordinate((x - ctr_x, y - ctr_y), rotation) - selected_block.set_coordinate((x + ctr_x, y + ctr_y)) - return True - - def remove_selected(self): - """ - Remove selected elements - - Returns: - true if changed. - """ - changed = False - for selected_element in self.get_selected_elements(): - self.remove_element(selected_element) - changed = True - return changed - - def draw(self, gc, window): - """ - Draw the background and grid if enabled. - Draw all of the elements in this flow graph onto the pixmap. - Draw the pixmap to the drawable window of this flow graph. - """ - W,H = self.get_size() - #draw the background - gc.set_foreground(Colors.FLOWGRAPH_BACKGROUND_COLOR) - window.draw_rectangle(gc, True, 0, 0, W, H) - #draw multi select rectangle - if self.mouse_pressed and (not self.get_selected_elements() or self.get_ctrl_mask()): - #coordinates - x1, y1 = self.press_coor - x2, y2 = self.get_coordinate() - #calculate top-left coordinate and width/height - x, y = int(min(x1, x2)), int(min(y1, y2)) - w, h = int(abs(x1 - x2)), int(abs(y1 - y2)) - #draw - gc.set_foreground(Colors.HIGHLIGHT_COLOR) - window.draw_rectangle(gc, True, x, y, w, h) - gc.set_foreground(Colors.BORDER_COLOR) - window.draw_rectangle(gc, False, x, y, w, h) - #draw blocks on top of connections - for element in self.get_connections() + self.get_blocks(): - element.draw(gc, window) - #draw selected blocks on top of selected connections - for selected_element in self.get_selected_connections() + self.get_selected_blocks(): - selected_element.draw(gc, window) - - def update_selected(self): - """ - Remove deleted elements from the selected elements list. - Update highlighting so only the selected are highlighted. - """ - selected_elements = self.get_selected_elements() - elements = self.get_elements() - #remove deleted elements - for selected in selected_elements: - if selected in elements: continue - selected_elements.remove(selected) - if self._old_selected_port and self._old_selected_port.get_parent() not in elements: - self._old_selected_port = None - if self._new_selected_port and self._new_selected_port.get_parent() not in elements: - self._new_selected_port = None - #update highlighting - for element in elements: - element.set_highlighted(element in selected_elements) - - def update(self): - """ - Call the top level rewrite and validate. - Call the top level create labels and shapes. - """ - - self.rewrite() - - self.validate() - - self.create_labels() - - self.create_shapes() - - ########################################################################## - ## Get Selected - ########################################################################## - def unselect(self): - """ - Set selected elements to an empty set. - """ - self._selected_elements = [] - - def what_is_selected(self, coor, coor_m=None): - """ - What is selected? - At the given coordinate, return the elements found to be selected. - If coor_m is unspecified, return a list of only the first element found to be selected: - Iterate though the elements backwards since top elements are at the end of the list. - If an element is selected, place it at the end of the list so that is is drawn last, - and hence on top. Update the selected port information. - - Args: - coor: the coordinate of the mouse click - coor_m: the coordinate for multi select - - Returns: - the selected blocks and connections or an empty list - """ - selected_port = None - selected = set() - #check the elements - for element in reversed(self.get_elements()): - selected_element = element.what_is_selected(coor, coor_m) - if not selected_element: continue - #update the selected port information - if selected_element.is_port(): - if not coor_m: selected_port = selected_element - selected_element = selected_element.get_parent() - selected.add(selected_element) - #place at the end of the list - self.get_elements().remove(element) - self.get_elements().append(element) - #single select mode, break - if not coor_m: break - #update selected ports - self._old_selected_port = self._new_selected_port - self._new_selected_port = selected_port - return list(selected) - - def get_selected_connections(self): - """ - Get a group of selected connections. - - Returns: - sub set of connections in this flow graph - """ - selected = set() - for selected_element in self.get_selected_elements(): - if selected_element.is_connection(): selected.add(selected_element) - return list(selected) - - def get_selected_blocks(self): - """ - Get a group of selected blocks. - - Returns: - sub set of blocks in this flow graph - """ - selected = set() - for selected_element in self.get_selected_elements(): - if selected_element.is_block(): selected.add(selected_element) - return list(selected) - - def get_selected_block(self): - """ - Get the selected block when a block or port is selected. - - Returns: - a block or None - """ - return self.get_selected_blocks() and self.get_selected_blocks()[0] or None - - def get_selected_elements(self): - """ - Get the group of selected elements. - - Returns: - sub set of elements in this flow graph - """ - return self._selected_elements - - def get_selected_element(self): - """ - Get the selected element. - - Returns: - a block, port, or connection or None - """ - return self.get_selected_elements() and self.get_selected_elements()[0] or None - - def update_selected_elements(self): - """ - Update the selected elements. - The update behavior depends on the state of the mouse button. - When the mouse button pressed the selection will change when - the control mask is set or the new selection is not in the current group. - When the mouse button is released the selection will change when - the mouse has moved and the control mask is set or the current group is empty. - Attempt to make a new connection if the old and ports are filled. - If the control mask is set, merge with the current elements. - """ - selected_elements = None - if self.mouse_pressed: - new_selections = self.what_is_selected(self.get_coordinate()) - #update the selections if the new selection is not in the current selections - #allows us to move entire selected groups of elements - if self.get_ctrl_mask() or not ( - new_selections and new_selections[0] in self.get_selected_elements() - ): selected_elements = new_selections - else: #called from a mouse release - if not self.element_moved and (not self.get_selected_elements() or self.get_ctrl_mask()): - selected_elements = self.what_is_selected(self.get_coordinate(), self.press_coor) - #this selection and the last were ports, try to connect them - if self._old_selected_port and self._new_selected_port and \ - self._old_selected_port is not self._new_selected_port: - try: - self.connect(self._old_selected_port, self._new_selected_port) - Actions.ELEMENT_CREATE() - except: Messages.send_fail_connection() - self._old_selected_port = None - self._new_selected_port = None - return - #update selected elements - if selected_elements is None: return - old_elements = set(self.get_selected_elements()) - self._selected_elements = list(set(selected_elements)) - new_elements = set(self.get_selected_elements()) - #if ctrl, set the selected elements to the union - intersection of old and new - if self.get_ctrl_mask(): - self._selected_elements = list( - set.union(old_elements, new_elements) - set.intersection(old_elements, new_elements) - ) - Actions.ELEMENT_SELECT() - - ########################################################################## - ## Event Handlers - ########################################################################## - def handle_mouse_context_press(self, coordinate, event): - """ - The context mouse button was pressed: - If no elements were selected, perform re-selection at this coordinate. - Then, show the context menu at the mouse click location. - """ - selections = self.what_is_selected(coordinate) - if not set(selections).intersection(self.get_selected_elements()): - self.set_coordinate(coordinate) - self.mouse_pressed = True - self.update_selected_elements() - self.mouse_pressed = False - self._context_menu.popup(None, None, None, event.button, event.time) - - def handle_mouse_selector_press(self, double_click, coordinate): - """ - The selector mouse button was pressed: - Find the selected element. Attempt a new connection if possible. - Open the block params window on a double click. - Update the selection state of the flow graph. - """ - self.press_coor = coordinate - self.set_coordinate(coordinate) - self.time = 0 - self.mouse_pressed = True - if double_click: self.unselect() - self.update_selected_elements() - #double click detected, bring up params dialog if possible - if double_click and self.get_selected_block(): - self.mouse_pressed = False - Actions.BLOCK_PARAM_MODIFY() - - def handle_mouse_selector_release(self, coordinate): - """ - The selector mouse button was released: - Update the state, handle motion (dragging). - And update the selected flowgraph elements. - """ - self.set_coordinate(coordinate) - self.time = 0 - self.mouse_pressed = False - if self.element_moved: - Actions.BLOCK_MOVE() - self.element_moved = False - self.update_selected_elements() - - def handle_mouse_motion(self, coordinate): - """ - The mouse has moved, respond to mouse dragging. - Move a selected element to the new coordinate. - Auto-scroll the scroll bars at the boundaries. - """ - #to perform a movement, the mouse must be pressed - # (no longer checking pending events via gtk.events_pending() - always true in Windows) - if not self.mouse_pressed: return - #perform autoscrolling - width, height = self.get_size() - x, y = coordinate - h_adj = self.get_scroll_pane().get_hadjustment() - v_adj = self.get_scroll_pane().get_vadjustment() - for pos, length, adj, adj_val, adj_len in ( - (x, width, h_adj, h_adj.get_value(), h_adj.page_size), - (y, height, v_adj, v_adj.get_value(), v_adj.page_size), - ): - #scroll if we moved near the border - if pos-adj_val > adj_len-SCROLL_PROXIMITY_SENSITIVITY and adj_val+SCROLL_DISTANCE < length-adj_len: - adj.set_value(adj_val+SCROLL_DISTANCE) - adj.emit('changed') - elif pos-adj_val < SCROLL_PROXIMITY_SENSITIVITY: - adj.set_value(adj_val-SCROLL_DISTANCE) - adj.emit('changed') - #remove the connection if selected in drag event - if len(self.get_selected_elements()) == 1 and self.get_selected_element().is_connection(): - Actions.ELEMENT_DELETE() - #move the selected elements and record the new coordinate - X, Y = self.get_coordinate() - if not self.get_ctrl_mask(): self.move_selected((int(x - X), int(y - Y))) - self.set_coordinate((x, y)) - #queue draw for animation - self.queue_draw() + """ + FlowGraph is the data structure to store graphical signal blocks, + graphical inputs and outputs, + and the connections between inputs and outputs. + """ + + def __init__(self): + """ + FlowGraph contructor. + Create a list for signal blocks and connections. Connect mouse handlers. + """ + Element.__init__(self) + #when is the flow graph selected? (used by keyboard event handler) + self.is_selected = lambda: bool(self.get_selected_elements()) + #important vars dealing with mouse event tracking + self.element_moved = False + self.mouse_pressed = False + self.unselect() + self.press_coor = (0, 0) + #selected ports + self._old_selected_port = None + self._new_selected_port = None + #context menu + self._context_menu = gtk.Menu() + for action in [ + Actions.BLOCK_CUT, + Actions.BLOCK_COPY, + Actions.BLOCK_PASTE, + Actions.ELEMENT_DELETE, + Actions.BLOCK_ROTATE_CCW, + Actions.BLOCK_ROTATE_CW, + Actions.BLOCK_ENABLE, + Actions.BLOCK_DISABLE, + Actions.BLOCK_PARAM_MODIFY, + Actions.BLOCK_CREATE_HIER, + Actions.OPEN_HIER, + Actions.BUSSIFY_SOURCES, + Actions.BUSSIFY_SINKS, + ]: self._context_menu.append(action.create_menu_item()) + + ########################################################################### + # Access Drawing Area + ########################################################################### + def get_drawing_area(self): return self.drawing_area + def queue_draw(self): self.get_drawing_area().queue_draw() + def get_size(self): return self.get_drawing_area().get_size_request() + def set_size(self, *args): self.get_drawing_area().set_size_request(*args) + def get_scroll_pane(self): return self.drawing_area.get_parent() + def get_ctrl_mask(self): return self.drawing_area.ctrl_mask + def new_pixmap(self, *args): return self.get_drawing_area().new_pixmap(*args) + + def add_new_block(self, key, coor=None): + """ + Add a block of the given key to this flow graph. + + Args: + key: the block key + coor: an optional coordinate or None for random + """ + id = self._get_unique_id(key) + #calculate the position coordinate + h_adj = self.get_scroll_pane().get_hadjustment() + v_adj = self.get_scroll_pane().get_vadjustment() + if coor is None: coor = ( + int(random.uniform(.25, .75)*h_adj.page_size + h_adj.get_value()), + int(random.uniform(.25, .75)*v_adj.page_size + v_adj.get_value()), + ) + #get the new block + block = self.get_new_block(key) + block.set_coordinate(coor) + block.set_rotation(0) + block.get_param('id').set_value(id) + Actions.ELEMENT_CREATE() + return id + + ########################################################################### + # Copy Paste + ########################################################################### + def copy_to_clipboard(self): + """ + Copy the selected blocks and connections into the clipboard. + + Returns: + the clipboard + """ + #get selected blocks + blocks = self.get_selected_blocks() + if not blocks: return None + #calc x and y min + x_min, y_min = blocks[0].get_coordinate() + for block in blocks: + x, y = block.get_coordinate() + x_min = min(x, x_min) + y_min = min(y, y_min) + #get connections between selected blocks + connections = filter( + lambda c: c.get_source().get_parent() in blocks and c.get_sink().get_parent() in blocks, + self.get_connections(), + ) + clipboard = ( + (x_min, y_min), + [block.export_data() for block in blocks], + [connection.export_data() for connection in connections], + ) + return clipboard + + def paste_from_clipboard(self, clipboard): + """ + Paste the blocks and connections from the clipboard. + + Args: + clipboard: the nested data of blocks, connections + """ + selected = set() + (x_min, y_min), blocks_n, connections_n = clipboard + old_id2block = dict() + #recalc the position + h_adj = self.get_scroll_pane().get_hadjustment() + v_adj = self.get_scroll_pane().get_vadjustment() + x_off = h_adj.get_value() - x_min + h_adj.page_size/4 + y_off = v_adj.get_value() - y_min + v_adj.page_size/4 + #create blocks + for block_n in blocks_n: + block_key = block_n.find('key') + if block_key == 'options': continue + block = self.get_new_block(block_key) + selected.add(block) + #set params + params_n = block_n.findall('param') + for param_n in params_n: + param_key = param_n.find('key') + param_value = param_n.find('value') + #setup id parameter + if param_key == 'id': + old_id2block[param_value] = block + #if the block id is not unique, get a new block id + if param_value in [bluck.get_id() for bluck in self.get_blocks()]: + param_value = self._get_unique_id(param_value) + #set value to key + block.get_param(param_key).set_value(param_value) + #move block to offset coordinate + block.move((x_off, y_off)) + #update before creating connections + self.update() + #create connections + for connection_n in connections_n: + source = old_id2block[connection_n.find('source_block_id')].get_source(connection_n.find('source_key')) + sink = old_id2block[connection_n.find('sink_block_id')].get_sink(connection_n.find('sink_key')) + self.connect(source, sink) + #set all pasted elements selected + for block in selected: selected = selected.union(set(block.get_connections())) + self._selected_elements = list(selected) + + ########################################################################### + # Modify Selected + ########################################################################### + def type_controller_modify_selected(self, direction): + """ + Change the registered type controller for the selected signal blocks. + + Args: + direction: +1 or -1 + + Returns: + true for change + """ + return any([sb.type_controller_modify(direction) for sb in self.get_selected_blocks()]) + + def port_controller_modify_selected(self, direction): + """ + Change port controller for the selected signal blocks. + + Args: + direction: +1 or -1 + + Returns: + true for changed + """ + return any([sb.port_controller_modify(direction) for sb in self.get_selected_blocks()]) + + def enable_selected(self, enable): + """ + Enable/disable the selected blocks. + + Args: + enable: true to enable + + Returns: + true if changed + """ + changed = False + for selected_block in self.get_selected_blocks(): + if selected_block.get_enabled() != enable: + selected_block.set_enabled(enable) + changed = True + return changed + + def move_selected(self, delta_coordinate): + """ + Move the element and by the change in coordinates. + + Args: + delta_coordinate: the change in coordinates + """ + for selected_block in self.get_selected_blocks(): + selected_block.move(delta_coordinate) + self.element_moved = True + + def rotate_selected(self, rotation): + """ + Rotate the selected blocks by multiples of 90 degrees. + + Args: + rotation: the rotation in degrees + + Returns: + true if changed, otherwise false. + """ + if not self.get_selected_blocks(): return False + #initialize min and max coordinates + min_x, min_y = self.get_selected_block().get_coordinate() + max_x, max_y = self.get_selected_block().get_coordinate() + #rotate each selected block, and find min/max coordinate + for selected_block in self.get_selected_blocks(): + selected_block.rotate(rotation) + #update the min/max coordinate + x, y = selected_block.get_coordinate() + min_x, min_y = min(min_x, x), min(min_y, y) + max_x, max_y = max(max_x, x), max(max_y, y) + #calculate center point of slected blocks + ctr_x, ctr_y = (max_x + min_x)/2, (max_y + min_y)/2 + #rotate the blocks around the center point + for selected_block in self.get_selected_blocks(): + x, y = selected_block.get_coordinate() + x, y = Utils.get_rotated_coordinate((x - ctr_x, y - ctr_y), rotation) + selected_block.set_coordinate((x + ctr_x, y + ctr_y)) + return True + + def remove_selected(self): + """ + Remove selected elements + + Returns: + true if changed. + """ + changed = False + for selected_element in self.get_selected_elements(): + self.remove_element(selected_element) + changed = True + return changed + + def draw(self, gc, window): + """ + Draw the background and grid if enabled. + Draw all of the elements in this flow graph onto the pixmap. + Draw the pixmap to the drawable window of this flow graph. + """ + W,H = self.get_size() + #draw the background + gc.set_foreground(Colors.FLOWGRAPH_BACKGROUND_COLOR) + window.draw_rectangle(gc, True, 0, 0, W, H) + #draw multi select rectangle + if self.mouse_pressed and (not self.get_selected_elements() or self.get_ctrl_mask()): + #coordinates + x1, y1 = self.press_coor + x2, y2 = self.get_coordinate() + #calculate top-left coordinate and width/height + x, y = int(min(x1, x2)), int(min(y1, y2)) + w, h = int(abs(x1 - x2)), int(abs(y1 - y2)) + #draw + gc.set_foreground(Colors.HIGHLIGHT_COLOR) + window.draw_rectangle(gc, True, x, y, w, h) + gc.set_foreground(Colors.BORDER_COLOR) + window.draw_rectangle(gc, False, x, y, w, h) + #draw blocks on top of connections + for element in self.get_connections() + self.get_blocks(): + element.draw(gc, window) + #draw selected blocks on top of selected connections + for selected_element in self.get_selected_connections() + self.get_selected_blocks(): + selected_element.draw(gc, window) + + def update_selected(self): + """ + Remove deleted elements from the selected elements list. + Update highlighting so only the selected are highlighted. + """ + selected_elements = self.get_selected_elements() + elements = self.get_elements() + #remove deleted elements + for selected in selected_elements: + if selected in elements: continue + selected_elements.remove(selected) + if self._old_selected_port and self._old_selected_port.get_parent() not in elements: + self._old_selected_port = None + if self._new_selected_port and self._new_selected_port.get_parent() not in elements: + self._new_selected_port = None + #update highlighting + for element in elements: + element.set_highlighted(element in selected_elements) + + def update(self): + """ + Call the top level rewrite and validate. + Call the top level create labels and shapes. + """ + self.rewrite() + self.validate() + self.create_labels() + self.create_shapes() + + ########################################################################## + ## Get Selected + ########################################################################## + def unselect(self): + """ + Set selected elements to an empty set. + """ + self._selected_elements = [] + + def what_is_selected(self, coor, coor_m=None): + """ + What is selected? + At the given coordinate, return the elements found to be selected. + If coor_m is unspecified, return a list of only the first element found to be selected: + Iterate though the elements backwards since top elements are at the end of the list. + If an element is selected, place it at the end of the list so that is is drawn last, + and hence on top. Update the selected port information. + + Args: + coor: the coordinate of the mouse click + coor_m: the coordinate for multi select + + Returns: + the selected blocks and connections or an empty list + """ + selected_port = None + selected = set() + #check the elements + for element in reversed(self.get_elements()): + selected_element = element.what_is_selected(coor, coor_m) + if not selected_element: continue + #update the selected port information + if selected_element.is_port(): + if not coor_m: selected_port = selected_element + selected_element = selected_element.get_parent() + selected.add(selected_element) + #place at the end of the list + self.get_elements().remove(element) + self.get_elements().append(element) + #single select mode, break + if not coor_m: break + #update selected ports + self._old_selected_port = self._new_selected_port + self._new_selected_port = selected_port + return list(selected) + + def get_selected_connections(self): + """ + Get a group of selected connections. + + Returns: + sub set of connections in this flow graph + """ + selected = set() + for selected_element in self.get_selected_elements(): + if selected_element.is_connection(): selected.add(selected_element) + return list(selected) + + def get_selected_blocks(self): + """ + Get a group of selected blocks. + + Returns: + sub set of blocks in this flow graph + """ + selected = set() + for selected_element in self.get_selected_elements(): + if selected_element.is_block(): selected.add(selected_element) + return list(selected) + + def get_selected_block(self): + """ + Get the selected block when a block or port is selected. + + Returns: + a block or None + """ + return self.get_selected_blocks() and self.get_selected_blocks()[0] or None + + def get_selected_elements(self): + """ + Get the group of selected elements. + + Returns: + sub set of elements in this flow graph + """ + return self._selected_elements + + def get_selected_element(self): + """ + Get the selected element. + + Returns: + a block, port, or connection or None + """ + return self.get_selected_elements() and self.get_selected_elements()[0] or None + + def update_selected_elements(self): + """ + Update the selected elements. + The update behavior depends on the state of the mouse button. + When the mouse button pressed the selection will change when + the control mask is set or the new selection is not in the current group. + When the mouse button is released the selection will change when + the mouse has moved and the control mask is set or the current group is empty. + Attempt to make a new connection if the old and ports are filled. + If the control mask is set, merge with the current elements. + """ + selected_elements = None + if self.mouse_pressed: + new_selections = self.what_is_selected(self.get_coordinate()) + #update the selections if the new selection is not in the current selections + #allows us to move entire selected groups of elements + if self.get_ctrl_mask() or not ( + new_selections and new_selections[0] in self.get_selected_elements() + ): selected_elements = new_selections + else: #called from a mouse release + if not self.element_moved and (not self.get_selected_elements() or self.get_ctrl_mask()): + selected_elements = self.what_is_selected(self.get_coordinate(), self.press_coor) + #this selection and the last were ports, try to connect them + if self._old_selected_port and self._new_selected_port and \ + self._old_selected_port is not self._new_selected_port: + try: + self.connect(self._old_selected_port, self._new_selected_port) + Actions.ELEMENT_CREATE() + except: Messages.send_fail_connection() + self._old_selected_port = None + self._new_selected_port = None + return + #update selected elements + if selected_elements is None: return + old_elements = set(self.get_selected_elements()) + self._selected_elements = list(set(selected_elements)) + new_elements = set(self.get_selected_elements()) + #if ctrl, set the selected elements to the union - intersection of old and new + if self.get_ctrl_mask(): + self._selected_elements = list( + set.union(old_elements, new_elements) - set.intersection(old_elements, new_elements) + ) + Actions.ELEMENT_SELECT() + + ########################################################################## + ## Event Handlers + ########################################################################## + def handle_mouse_context_press(self, coordinate, event): + """ + The context mouse button was pressed: + If no elements were selected, perform re-selection at this coordinate. + Then, show the context menu at the mouse click location. + """ + selections = self.what_is_selected(coordinate) + if not set(selections).intersection(self.get_selected_elements()): + self.set_coordinate(coordinate) + self.mouse_pressed = True + self.update_selected_elements() + self.mouse_pressed = False + self._context_menu.popup(None, None, None, event.button, event.time) + + def handle_mouse_selector_press(self, double_click, coordinate): + """ + The selector mouse button was pressed: + Find the selected element. Attempt a new connection if possible. + Open the block params window on a double click. + Update the selection state of the flow graph. + """ + self.press_coor = coordinate + self.set_coordinate(coordinate) + self.time = 0 + self.mouse_pressed = True + if double_click: self.unselect() + self.update_selected_elements() + #double click detected, bring up params dialog if possible + if double_click and self.get_selected_block(): + self.mouse_pressed = False + Actions.BLOCK_PARAM_MODIFY() + + def handle_mouse_selector_release(self, coordinate): + """ + The selector mouse button was released: + Update the state, handle motion (dragging). + And update the selected flowgraph elements. + """ + self.set_coordinate(coordinate) + self.time = 0 + self.mouse_pressed = False + if self.element_moved: + Actions.BLOCK_MOVE() + self.element_moved = False + self.update_selected_elements() + + def handle_mouse_motion(self, coordinate): + """ + The mouse has moved, respond to mouse dragging. + Move a selected element to the new coordinate. + Auto-scroll the scroll bars at the boundaries. + """ + #to perform a movement, the mouse must be pressed + # (no longer checking pending events via gtk.events_pending() - always true in Windows) + if not self.mouse_pressed: return + #perform autoscrolling + width, height = self.get_size() + x, y = coordinate + h_adj = self.get_scroll_pane().get_hadjustment() + v_adj = self.get_scroll_pane().get_vadjustment() + for pos, length, adj, adj_val, adj_len in ( + (x, width, h_adj, h_adj.get_value(), h_adj.page_size), + (y, height, v_adj, v_adj.get_value(), v_adj.page_size), + ): + #scroll if we moved near the border + if pos-adj_val > adj_len-SCROLL_PROXIMITY_SENSITIVITY and adj_val+SCROLL_DISTANCE < length-adj_len: + adj.set_value(adj_val+SCROLL_DISTANCE) + adj.emit('changed') + elif pos-adj_val < SCROLL_PROXIMITY_SENSITIVITY: + adj.set_value(adj_val-SCROLL_DISTANCE) + adj.emit('changed') + #remove the connection if selected in drag event + if len(self.get_selected_elements()) == 1 and self.get_selected_element().is_connection(): + Actions.ELEMENT_DELETE() + #move the selected elements and record the new coordinate + X, Y = self.get_coordinate() + if not self.get_ctrl_mask(): self.move_selected((int(x - X), int(y - Y))) + self.set_coordinate((x, y)) + #queue draw for animation + self.queue_draw() diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py index c2d661c668..677f202e1f 100644 --- a/grc/gui/MainWindow.py +++ b/grc/gui/MainWindow.py @@ -18,7 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ from Constants import \ - NEW_FLOGRAPH_TITLE, DEFAULT_REPORTS_WINDOW_WIDTH + NEW_FLOGRAPH_TITLE, DEFAULT_REPORTS_WINDOW_WIDTH import Actions import pygtk pygtk.require('2.0') @@ -63,293 +63,293 @@ PAGE_TITLE_MARKUP_TMPL = """\ ############################################################ class MainWindow(gtk.Window): - """The topmost window with menus, the tool bar, and other major windows.""" + """The topmost window with menus, the tool bar, and other major windows.""" - def __init__(self, platform): - """ - MainWindow contructor - Setup the menu, toolbar, flowgraph editor notebook, block selection window... - """ - self._platform = platform - #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 - self.add_accel_group(Actions.get_accel_group()) - vbox.pack_start(Bars.MenuBar(), False) - vbox.pack_start(Bars.Toolbar(), False) - vbox.pack_start(self.hpaned) - #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 - 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_with_viewport(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 - #load preferences and show the main window - Preferences.load(platform) - 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.show_all() + def __init__(self, platform): + """ + MainWindow contructor + Setup the menu, toolbar, flowgraph editor notebook, block selection window... + """ + self._platform = platform + #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 + self.add_accel_group(Actions.get_accel_group()) + vbox.pack_start(Bars.MenuBar(), False) + vbox.pack_start(Bars.Toolbar(), False) + vbox.pack_start(self.hpaned) + #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 + 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_with_viewport(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 + #load preferences and show the main window + Preferences.load(platform) + 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.show_all() - ############################################################ - # Event Handlers - ############################################################ + ############################################################ + # Event Handlers + ############################################################ - def _quit(self, window, event): - """ - Handle the delete event from the main window. - Generated by pressing X to close, alt+f4, or right click+close. - This method in turns calls the state handler to quit. - - Returns: - true - """ - Actions.APPLICATION_QUIT() - return True + def _quit(self, window, event): + """ + Handle the delete event from the main window. + Generated by pressing X to close, alt+f4, or right click+close. + This method in turns calls the state handler to quit. + + Returns: + true + """ + Actions.APPLICATION_QUIT() + return True - def _handle_page_change(self, notebook, page, page_num): - """ - Handle a page change. When the user clicks on a new tab, - reload the flow graph to update the vars window and - call handle states (select nothing) to update the buttons. - - Args: - notebook: the notebook - page: new page - page_num: new page number - """ - self.current_page = self.notebook.get_nth_page(page_num) - Messages.send_page_switch(self.current_page.get_file_path()) - Actions.PAGE_CHANGE() + def _handle_page_change(self, notebook, page, page_num): + """ + Handle a page change. When the user clicks on a new tab, + reload the flow graph to update the vars window and + call handle states (select nothing) to update the buttons. + + Args: + notebook: the notebook + page: new page + page_num: new page number + """ + self.current_page = self.notebook.get_nth_page(page_num) + Messages.send_page_switch(self.current_page.get_file_path()) + Actions.PAGE_CHANGE() - ############################################################ - # Report Window - ############################################################ + ############################################################ + # Report Window + ############################################################ - def add_report_line(self, line): - """ - Place line at the end of the text buffer, then scroll its window all the way down. - - Args: - line: the new text - """ - self.text_display.insert(line) - vadj = self.reports_scrolled_window.get_vadjustment() - vadj.set_value(vadj.upper) - vadj.emit('changed') + def add_report_line(self, line): + """ + Place line at the end of the text buffer, then scroll its window all the way down. + + Args: + line: the new text + """ + self.text_display.insert(line) + vadj = self.reports_scrolled_window.get_vadjustment() + vadj.set_value(vadj.upper) + vadj.emit('changed') - ############################################################ - # Pages: create and close - ############################################################ + ############################################################ + # Pages: create and close + ############################################################ - def new_page(self, file_path='', show=False): - """ - Create a new notebook page. - Set the tab to be selected. - - Args: - file_path: optional file to load into the flow graph - show: true if the page should be shown after loading - """ - #if the file is already open, show the open page and return - if file_path and file_path in self._get_files(): #already open - page = self.notebook.get_nth_page(self._get_files().index(file_path)) - self._set_page(page) - return - 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; - #print flow_graph - page = NotebookPage( - self, - flow_graph=flow_graph, - file_path=file_path, - ) - if file_path: Messages.send_end_load() - except Exception, e: #return on failure - Messages.send_fail_load(e) - if isinstance(e, KeyError) and str(e) == "'options'": - # This error is unrecoverable, so crash gracefully - exit(-1) - return - #add this page to the notebook - self.notebook.append_page(page, page.get_tab()) - try: self.notebook.set_tab_reorderable(page, True) - except: pass #gtk too old - self.notebook.set_tab_label_packing(page, False, False, gtk.PACK_START) - #only show if blank or manual - if not file_path or show: self._set_page(page) + def new_page(self, file_path='', show=False): + """ + Create a new notebook page. + Set the tab to be selected. + + Args: + file_path: optional file to load into the flow graph + show: true if the page should be shown after loading + """ + #if the file is already open, show the open page and return + if file_path and file_path in self._get_files(): #already open + page = self.notebook.get_nth_page(self._get_files().index(file_path)) + self._set_page(page) + return + 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; + #print flow_graph + page = NotebookPage( + self, + flow_graph=flow_graph, + file_path=file_path, + ) + if file_path: Messages.send_end_load() + except Exception, e: #return on failure + Messages.send_fail_load(e) + if isinstance(e, KeyError) and str(e) == "'options'": + # This error is unrecoverable, so crash gracefully + exit(-1) + return + #add this page to the notebook + self.notebook.append_page(page, page.get_tab()) + try: self.notebook.set_tab_reorderable(page, True) + except: pass #gtk too old + self.notebook.set_tab_label_packing(page, False, False, gtk.PACK_START) + #only show if blank or manual + if not file_path or show: self._set_page(page) - def close_pages(self): - """ - Close all the pages in this notebook. - - Returns: - true if all closed - """ - open_files = filter(lambda file: file, self._get_files()) #filter blank files - open_file = self.get_page().get_file_path() - #close each page - for page in self._get_pages(): - self.page_to_be_closed = page - self.close_page(False) - if self.notebook.get_n_pages(): return False - #save state before closing - Preferences.files_open(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.save() - return True + def close_pages(self): + """ + Close all the pages in this notebook. + + Returns: + true if all closed + """ + open_files = filter(lambda file: file, self._get_files()) #filter blank files + open_file = self.get_page().get_file_path() + #close each page + for page in self._get_pages(): + self.page_to_be_closed = page + self.close_page(False) + if self.notebook.get_n_pages(): return False + #save state before closing + Preferences.files_open(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.save() + return True - def close_page(self, ensure=True): - """ - Close the current page. - If the notebook becomes empty, and ensure is true, - call new page upon exit to ensure that at least one page exists. - - Args: - ensure: boolean - """ - if not self.page_to_be_closed: self.page_to_be_closed = self.get_page() - #show the page if it has an executing flow graph or is unsaved - if self.page_to_be_closed.get_proc() or not self.page_to_be_closed.get_saved(): - self._set_page(self.page_to_be_closed) - #unsaved? ask the user - if not self.page_to_be_closed.get_saved() and self._save_changes(): - Actions.FLOW_GRAPH_SAVE() #try to save - if not self.page_to_be_closed.get_saved(): #still unsaved? - self.page_to_be_closed = None #set the page to be closed back to None - return - #stop the flow graph if executing - if self.page_to_be_closed.get_proc(): Actions.FLOW_GRAPH_KILL() - #remove the page - self.notebook.remove_page(self.notebook.page_num(self.page_to_be_closed)) - if ensure and self.notebook.get_n_pages() == 0: self.new_page() #no pages, make a new one - self.page_to_be_closed = None #set the page to be closed back to None + def close_page(self, ensure=True): + """ + Close the current page. + If the notebook becomes empty, and ensure is true, + call new page upon exit to ensure that at least one page exists. + + Args: + ensure: boolean + """ + if not self.page_to_be_closed: self.page_to_be_closed = self.get_page() + #show the page if it has an executing flow graph or is unsaved + if self.page_to_be_closed.get_proc() or not self.page_to_be_closed.get_saved(): + self._set_page(self.page_to_be_closed) + #unsaved? ask the user + if not self.page_to_be_closed.get_saved() and self._save_changes(): + Actions.FLOW_GRAPH_SAVE() #try to save + if not self.page_to_be_closed.get_saved(): #still unsaved? + self.page_to_be_closed = None #set the page to be closed back to None + return + #stop the flow graph if executing + if self.page_to_be_closed.get_proc(): Actions.FLOW_GRAPH_KILL() + #remove the page + self.notebook.remove_page(self.notebook.page_num(self.page_to_be_closed)) + if ensure and self.notebook.get_n_pages() == 0: self.new_page() #no pages, make a new one + self.page_to_be_closed = None #set the page to be closed back to None - ############################################################ - # Misc - ############################################################ + ############################################################ + # Misc + ############################################################ - def update(self): - """ - Set the title of the main window. - Set the titles on the page tabs. - Show/hide the reports window. - - Args: - title: the window title - """ - gtk.Window.set_title(self, Utils.parse_template(MAIN_WINDOW_TITLE_TMPL, - basename=os.path.basename(self.get_page().get_file_path()), - dirname=os.path.dirname(self.get_page().get_file_path()), - new_flowgraph_title=NEW_FLOGRAPH_TITLE, - read_only=self.get_page().get_read_only(), - saved=self.get_page().get_saved(), - platform_name=self._platform.get_name(), - ) - ) - #set tab titles - for page in self._get_pages(): page.set_markup( - Utils.parse_template(PAGE_TITLE_MARKUP_TMPL, - #get filename and strip out file extension - title=os.path.splitext(os.path.basename(page.get_file_path()))[0], - read_only=page.get_read_only(), saved=page.get_saved(), - new_flowgraph_title=NEW_FLOGRAPH_TITLE, - ) - ) - #show/hide notebook tabs - self.notebook.set_show_tabs(len(self._get_pages()) > 1) + def update(self): + """ + Set the title of the main window. + Set the titles on the page tabs. + Show/hide the reports window. + + Args: + title: the window title + """ + gtk.Window.set_title(self, Utils.parse_template(MAIN_WINDOW_TITLE_TMPL, + basename=os.path.basename(self.get_page().get_file_path()), + dirname=os.path.dirname(self.get_page().get_file_path()), + new_flowgraph_title=NEW_FLOGRAPH_TITLE, + read_only=self.get_page().get_read_only(), + saved=self.get_page().get_saved(), + platform_name=self._platform.get_name(), + ) + ) + #set tab titles + for page in self._get_pages(): page.set_markup( + Utils.parse_template(PAGE_TITLE_MARKUP_TMPL, + #get filename and strip out file extension + title=os.path.splitext(os.path.basename(page.get_file_path()))[0], + read_only=page.get_read_only(), saved=page.get_saved(), + new_flowgraph_title=NEW_FLOGRAPH_TITLE, + ) + ) + #show/hide notebook tabs + self.notebook.set_show_tabs(len(self._get_pages()) > 1) - def get_page(self): - """ - Get the selected page. - - Returns: - the selected page - """ - return self.current_page + def get_page(self): + """ + Get the selected page. + + Returns: + the selected page + """ + return self.current_page - def get_flow_graph(self): - """ - Get the selected flow graph. - - Returns: - the selected flow graph - """ - return self.get_page().get_flow_graph() + def get_flow_graph(self): + """ + Get the selected flow graph. + + Returns: + the selected flow graph + """ + return self.get_page().get_flow_graph() - def get_focus_flag(self): - """ - Get the focus flag from the current page. - - Returns: - the focus flag - """ - return self.get_page().get_drawing_area().get_focus_flag() + def get_focus_flag(self): + """ + Get the focus flag from the current page. + + Returns: + the focus flag + """ + return self.get_page().get_drawing_area().get_focus_flag() - ############################################################ - # Helpers - ############################################################ + ############################################################ + # Helpers + ############################################################ - def _set_page(self, page): - """ - Set the current page. - - Args: - page: the page widget - """ - self.current_page = page - self.notebook.set_current_page(self.notebook.page_num(self.current_page)) + def _set_page(self, page): + """ + Set the current page. + + Args: + page: the page widget + """ + self.current_page = page + self.notebook.set_current_page(self.notebook.page_num(self.current_page)) - def _save_changes(self): - """ - Save changes to flow graph? - - Returns: - true if yes - """ - return MessageDialogHelper( - gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, 'Unsaved Changes!', - 'Would you like to save changes before closing?' - ) == gtk.RESPONSE_YES + def _save_changes(self): + """ + Save changes to flow graph? + + Returns: + true if yes + """ + return MessageDialogHelper( + gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, 'Unsaved Changes!', + 'Would you like to save changes before closing?' + ) == gtk.RESPONSE_YES - def _get_files(self): - """ - Get the file names for all the pages, in order. - - Returns: - list of file paths - """ - return map(lambda page: page.get_file_path(), self._get_pages()) + def _get_files(self): + """ + Get the file names for all the pages, in order. + + Returns: + list of file paths + """ + return map(lambda page: page.get_file_path(), self._get_pages()) - def _get_pages(self): - """ - Get a list of all pages in the notebook. - - Returns: - list of pages - """ - return [self.notebook.get_nth_page(page_num) for page_num in range(self.notebook.get_n_pages())] + def _get_pages(self): + """ + Get a list of all pages in the notebook. + + Returns: + list of pages + """ + return [self.notebook.get_nth_page(page_num) for page_num in range(self.notebook.get_n_pages())] diff --git a/grc/gui/Messages.py b/grc/gui/Messages.py index a9f0e36b85..d903e40a45 100644 --- a/grc/gui/Messages.py +++ b/grc/gui/Messages.py @@ -24,22 +24,22 @@ import sys MESSENGERS_LIST = list() def register_messenger(messenger): - """ - Append the given messenger to the list of messengers. - - Args: - messenger: a method thats takes a string - """ - MESSENGERS_LIST.append(messenger) + """ + Append the given messenger to the list of messengers. + + Args: + messenger: a method thats takes a string + """ + MESSENGERS_LIST.append(messenger) def send(message): - """ - Give the message to each of the messengers. - - Args: - message: a message string - """ - for messenger in MESSENGERS_LIST: messenger(message) + """ + Give the message to each of the messengers. + + Args: + message: a message string + """ + for messenger in MESSENGERS_LIST: messenger(message) #register stdout by default register_messenger(sys.stdout.write) @@ -48,61 +48,61 @@ register_messenger(sys.stdout.write) # Special functions for specific program functionalities ########################################################################### def send_init(platform): - send("""<<< Welcome to %s %s >>>\n"""%(platform.get_name(), platform.get_version())) + send("""<<< Welcome to %s %s >>>\n"""%(platform.get_name(), platform.get_version())) def send_page_switch(file_path): - send('\nShowing: "%s"\n'%file_path) + send('\nShowing: "%s"\n'%file_path) -################# functions for loading flow graphs ######################################## +################# functions for loading flow graphs ######################################## def send_start_load(file_path): - send('\nLoading: "%s"'%file_path + '\n') + send('\nLoading: "%s"'%file_path + '\n') def send_error_load(error): - send('>>> Error: %s\n'%error) - traceback.print_exc() + send('>>> Error: %s\n'%error) + traceback.print_exc() def send_end_load(): - send('>>> Done\n') + send('>>> Done\n') def send_fail_load(error): - send('Error: %s\n'%error) - send('>>> Failure\n') - traceback.print_exc() + send('Error: %s\n'%error) + send('>>> Failure\n') + traceback.print_exc() -################# functions for generating flow graphs ######################################## +################# functions for generating flow graphs ######################################## def send_start_gen(file_path): - send('\nGenerating: "%s"'%file_path + '\n') + send('\nGenerating: "%s"'%file_path + '\n') def send_fail_gen(error): - send('Generate Error: %s\n'%error) - send('>>> Failure\n') - traceback.print_exc() + send('Generate Error: %s\n'%error) + send('>>> Failure\n') + traceback.print_exc() -################# functions for executing flow graphs ######################################## +################# functions for executing flow graphs ######################################## def send_start_exec(file_path): - send('\nExecuting: "%s"'%file_path + '\n') + send('\nExecuting: "%s"'%file_path + '\n') def send_verbose_exec(verbose): - send(verbose) + send(verbose) def send_end_exec(): - send('\n>>> Done\n') + send('\n>>> Done\n') -################# functions for saving flow graphs ######################################## +################# functions for saving flow graphs ######################################## def send_fail_save(file_path): - send('>>> Error: Cannot save: %s\n'%file_path) + send('>>> Error: Cannot save: %s\n'%file_path) -################# functions for connections ######################################## +################# functions for connections ######################################## def send_fail_connection(): - send('>>> Error: Cannot create connection.\n') + send('>>> Error: Cannot create connection.\n') -################# functions for preferences ######################################## +################# functions for preferences ######################################## def send_fail_load_preferences(prefs_file_path): - send('>>> Error: Cannot load preferences file: "%s"\n'%prefs_file_path) + send('>>> Error: Cannot load preferences file: "%s"\n'%prefs_file_path) def send_fail_save_preferences(prefs_file_path): - send('>>> Error: Cannot save preferences file: "%s"\n'%prefs_file_path) + send('>>> Error: Cannot save preferences file: "%s"\n'%prefs_file_path) -################# functions for warning ######################################## +################# functions for warning ######################################## def send_warning(warning): - send('>>> Warning: %s\n'%warning) + send('>>> Warning: %s\n'%warning) diff --git a/grc/gui/NotebookPage.py b/grc/gui/NotebookPage.py index 095c045a66..10c5dde00d 100644 --- a/grc/gui/NotebookPage.py +++ b/grc/gui/NotebookPage.py @@ -31,185 +31,185 @@ import os ############################################################ class NotebookPage(gtk.HBox): - """A page in the notebook.""" - - def __init__(self, main_window, flow_graph, file_path=''): - """ - Page constructor. - - Args: - main_window: main window - file_path: path to a flow graph file - """ - self._flow_graph = flow_graph - self.set_proc(None) - #import the file - self.main_window = main_window - self.set_file_path(file_path) - initial_state = flow_graph.get_parent().parse_flow_graph(file_path) - self.state_cache = StateCache(initial_state) - self.set_saved(True) - #import the data to the flow graph - self.get_flow_graph().import_data(initial_state) - #initialize page gui - gtk.HBox.__init__(self, False, 0) - self.show() - #tab box to hold label and close button - self.tab = gtk.HBox(False, 0) - #setup tab label - self.label = gtk.Label() - self.tab.pack_start(self.label, False) - #setup button image - image = gtk.Image() - image.set_from_stock('gtk-close', gtk.ICON_SIZE_MENU) - #setup image box - image_box = gtk.HBox(False, 0) - image_box.pack_start(image, True, False, 0) - #setup the button - button = gtk.Button() - button.connect("clicked", self._handle_button) - button.set_relief(gtk.RELIEF_NONE) - button.add(image_box) - #button size - w, h = gtk.icon_size_lookup_for_settings(button.get_settings(), gtk.ICON_SIZE_MENU) - button.set_size_request(w+6, h+6) - self.tab.pack_start(button, False) - self.tab.show_all() - #setup scroll window and drawing area - self.scrolled_window = gtk.ScrolledWindow() - self.scrolled_window.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT) - self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - self.drawing_area = DrawingArea(self.get_flow_graph()) - self.scrolled_window.add_with_viewport(self.get_drawing_area()) - self.pack_start(self.scrolled_window) - #inject drawing area into flow graph - self.get_flow_graph().drawing_area = self.get_drawing_area() - self.show_all() - - def get_drawing_area(self): return self.drawing_area - - def get_generator(self): - """ - Get the generator object for this flow graph. - - Returns: - generator - """ - return self.get_flow_graph().get_parent().get_generator()( - self.get_flow_graph(), - self.get_file_path(), - ) - - def _handle_button(self, button): - """ - The button was clicked. - Make the current page selected, then close. - - Args: - the: button - """ - self.main_window.page_to_be_closed = self - Actions.FLOW_GRAPH_CLOSE() - - def set_markup(self, markup): - """ - Set the markup in this label. - - Args: - markup: the new markup text - """ - self.label.set_markup(markup) - - def get_tab(self): - """ - Get the gtk widget for this page's tab. - - Returns: - gtk widget - """ - return self.tab - - def get_proc(self): - """ - Get the subprocess for the flow graph. - - Returns: - the subprocess object - """ - return self.process - - def set_proc(self, process): - """ - Set the subprocess object. - - Args: - process: the new subprocess - """ - self.process = process - - def get_flow_graph(self): - """ - Get the flow graph. - - Returns: - the flow graph - """ - return self._flow_graph - - def get_read_only(self): - """ - Get the read-only state of the file. - Always false for empty path. - - Returns: - true for read-only - """ - if not self.get_file_path(): return False - return os.path.exists(self.get_file_path()) and \ - not os.access(self.get_file_path(), os.W_OK) - - def get_file_path(self): - """ - Get the file path for the flow graph. - - Returns: - the file path or '' - """ - return self.file_path - - def set_file_path(self, file_path=''): - """ - Set the file path, '' for no file path. - - Args: - file_path: file path string - """ - if file_path: self.file_path = os.path.abspath(file_path) - else: self.file_path = '' - - def get_saved(self): - """ - Get the saved status for the flow graph. - - Returns: - true if saved - """ - return self.saved - - def set_saved(self, saved=True): - """ - Set the saved status. - - Args: - saved: boolean status - """ - self.saved = saved - - def get_state_cache(self): - """ - Get the state cache for the flow graph. - - Returns: - the state cache - """ - return self.state_cache + """A page in the notebook.""" + + def __init__(self, main_window, flow_graph, file_path=''): + """ + Page constructor. + + Args: + main_window: main window + file_path: path to a flow graph file + """ + self._flow_graph = flow_graph + self.set_proc(None) + #import the file + self.main_window = main_window + self.set_file_path(file_path) + initial_state = flow_graph.get_parent().parse_flow_graph(file_path) + self.state_cache = StateCache(initial_state) + self.set_saved(True) + #import the data to the flow graph + self.get_flow_graph().import_data(initial_state) + #initialize page gui + gtk.HBox.__init__(self, False, 0) + self.show() + #tab box to hold label and close button + self.tab = gtk.HBox(False, 0) + #setup tab label + self.label = gtk.Label() + self.tab.pack_start(self.label, False) + #setup button image + image = gtk.Image() + image.set_from_stock('gtk-close', gtk.ICON_SIZE_MENU) + #setup image box + image_box = gtk.HBox(False, 0) + image_box.pack_start(image, True, False, 0) + #setup the button + button = gtk.Button() + button.connect("clicked", self._handle_button) + button.set_relief(gtk.RELIEF_NONE) + button.add(image_box) + #button size + w, h = gtk.icon_size_lookup_for_settings(button.get_settings(), gtk.ICON_SIZE_MENU) + button.set_size_request(w+6, h+6) + self.tab.pack_start(button, False) + self.tab.show_all() + #setup scroll window and drawing area + self.scrolled_window = gtk.ScrolledWindow() + self.scrolled_window.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT) + self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + self.drawing_area = DrawingArea(self.get_flow_graph()) + self.scrolled_window.add_with_viewport(self.get_drawing_area()) + self.pack_start(self.scrolled_window) + #inject drawing area into flow graph + self.get_flow_graph().drawing_area = self.get_drawing_area() + self.show_all() + + def get_drawing_area(self): return self.drawing_area + + def get_generator(self): + """ + Get the generator object for this flow graph. + + Returns: + generator + """ + return self.get_flow_graph().get_parent().get_generator()( + self.get_flow_graph(), + self.get_file_path(), + ) + + def _handle_button(self, button): + """ + The button was clicked. + Make the current page selected, then close. + + Args: + the: button + """ + self.main_window.page_to_be_closed = self + Actions.FLOW_GRAPH_CLOSE() + + def set_markup(self, markup): + """ + Set the markup in this label. + + Args: + markup: the new markup text + """ + self.label.set_markup(markup) + + def get_tab(self): + """ + Get the gtk widget for this page's tab. + + Returns: + gtk widget + """ + return self.tab + + def get_proc(self): + """ + Get the subprocess for the flow graph. + + Returns: + the subprocess object + """ + return self.process + + def set_proc(self, process): + """ + Set the subprocess object. + + Args: + process: the new subprocess + """ + self.process = process + + def get_flow_graph(self): + """ + Get the flow graph. + + Returns: + the flow graph + """ + return self._flow_graph + + def get_read_only(self): + """ + Get the read-only state of the file. + Always false for empty path. + + Returns: + true for read-only + """ + if not self.get_file_path(): return False + return os.path.exists(self.get_file_path()) and \ + not os.access(self.get_file_path(), os.W_OK) + + def get_file_path(self): + """ + Get the file path for the flow graph. + + Returns: + the file path or '' + """ + return self.file_path + + def set_file_path(self, file_path=''): + """ + Set the file path, '' for no file path. + + Args: + file_path: file path string + """ + if file_path: self.file_path = os.path.abspath(file_path) + else: self.file_path = '' + + def get_saved(self): + """ + Get the saved status for the flow graph. + + Returns: + true if saved + """ + return self.saved + + def set_saved(self, saved=True): + """ + Set the saved status. + + Args: + saved: boolean status + """ + self.saved = saved + + def get_state_cache(self): + """ + Get the state cache for the flow graph. + + Returns: + the state cache + """ + return self.state_cache diff --git a/grc/gui/Param.py b/grc/gui/Param.py index da76b6b82c..f0e5a2fcb2 100644 --- a/grc/gui/Param.py +++ b/grc/gui/Param.py @@ -25,110 +25,110 @@ import gtk import Colors class InputParam(gtk.HBox): - """The base class for an input parameter inside the input parameters dialog.""" - - def __init__(self, param, callback=None): - gtk.HBox.__init__(self) - self.param = param - self._callback = callback - self.label = gtk.Label() #no label, markup is added by set_markup - self.label.set_size_request(150, -1) - self.pack_start(self.label, False) - self.set_markup = lambda m: self.label.set_markup(m) - self.tp = None - #connect events - self.connect('show', self._update_gui) - def set_color(self, color): pass - def set_tooltip_text(self, text): pass - - def _update_gui(self, *args): - """ - Set the markup, color, tooltip, show/hide. - """ - #set the markup - has_cb = \ - hasattr(self.param.get_parent(), 'get_callbacks') and \ - filter(lambda c: self.param.get_key() in c, self.param.get_parent()._callbacks) - self.set_markup(Utils.parse_template(PARAM_LABEL_MARKUP_TMPL, param=self.param, has_cb=has_cb)) - #set the color - self.set_color(self.param.get_color()) - #set the tooltip - self.set_tooltip_text( - Utils.parse_template(TIP_MARKUP_TMPL, param=self.param).strip(), - ) - #show/hide - if self.param.get_hide() == 'all': self.hide_all() - else: self.show_all() - - def _handle_changed(self, *args): - """ - Handle a gui change by setting the new param value, - calling the callback (if applicable), and updating. - """ - #set the new value - self.param.set_value(self.get_text()) - #call the callback - if self._callback: self._callback(*args) - else: self.param.validate() - #gui update - self._update_gui() + """The base class for an input parameter inside the input parameters dialog.""" + + def __init__(self, param, callback=None): + gtk.HBox.__init__(self) + self.param = param + self._callback = callback + self.label = gtk.Label() #no label, markup is added by set_markup + self.label.set_size_request(150, -1) + self.pack_start(self.label, False) + self.set_markup = lambda m: self.label.set_markup(m) + self.tp = None + #connect events + self.connect('show', self._update_gui) + def set_color(self, color): pass + def set_tooltip_text(self, text): pass + + def _update_gui(self, *args): + """ + Set the markup, color, tooltip, show/hide. + """ + #set the markup + has_cb = \ + hasattr(self.param.get_parent(), 'get_callbacks') and \ + filter(lambda c: self.param.get_key() in c, self.param.get_parent()._callbacks) + self.set_markup(Utils.parse_template(PARAM_LABEL_MARKUP_TMPL, param=self.param, has_cb=has_cb)) + #set the color + self.set_color(self.param.get_color()) + #set the tooltip + self.set_tooltip_text( + Utils.parse_template(TIP_MARKUP_TMPL, param=self.param).strip(), + ) + #show/hide + if self.param.get_hide() == 'all': self.hide_all() + else: self.show_all() + + def _handle_changed(self, *args): + """ + Handle a gui change by setting the new param value, + calling the callback (if applicable), and updating. + """ + #set the new value + self.param.set_value(self.get_text()) + #call the callback + if self._callback: self._callback(*args) + else: self.param.validate() + #gui update + self._update_gui() class EntryParam(InputParam): - """Provide an entry box for strings and numbers.""" - - def __init__(self, *args, **kwargs): - InputParam.__init__(self, *args, **kwargs) - self._input = gtk.Entry() - self._input.set_text(self.param.get_value()) - self._input.connect('changed', self._handle_changed) - self.pack_start(self._input, True) - def get_text(self): 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) - def set_tooltip_text(self, text): self._input.set_tooltip_text(text) + """Provide an entry box for strings and numbers.""" + + def __init__(self, *args, **kwargs): + InputParam.__init__(self, *args, **kwargs) + self._input = gtk.Entry() + self._input.set_text(self.param.get_value()) + self._input.connect('changed', self._handle_changed) + self.pack_start(self._input, True) + def get_text(self): 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) + def set_tooltip_text(self, text): self._input.set_tooltip_text(text) class EnumParam(InputParam): - """Provide an entry box for Enum types with a drop down menu.""" - - def __init__(self, *args, **kwargs): - InputParam.__init__(self, *args, **kwargs) - self._input = gtk.combo_box_new_text() - for option in self.param.get_options(): self._input.append_text(option.get_name()) - self._input.set_active(self.param.get_option_keys().index(self.param.get_value())) - self._input.connect('changed', self._handle_changed) - self.pack_start(self._input, False) - def get_text(self): return self.param.get_option_keys()[self._input.get_active()] - def set_tooltip_text(self, text): self._input.set_tooltip_text(text) + """Provide an entry box for Enum types with a drop down menu.""" + + def __init__(self, *args, **kwargs): + InputParam.__init__(self, *args, **kwargs) + self._input = gtk.combo_box_new_text() + for option in self.param.get_options(): self._input.append_text(option.get_name()) + self._input.set_active(self.param.get_option_keys().index(self.param.get_value())) + self._input.connect('changed', self._handle_changed) + self.pack_start(self._input, False) + def get_text(self): return self.param.get_option_keys()[self._input.get_active()] + def set_tooltip_text(self, text): self._input.set_tooltip_text(text) class EnumEntryParam(InputParam): - """Provide an entry box and drop down menu for Raw Enum types.""" - - def __init__(self, *args, **kwargs): - InputParam.__init__(self, *args, **kwargs) - self._input = gtk.combo_box_entry_new_text() - for option in self.param.get_options(): self._input.append_text(option.get_name()) - try: self._input.set_active(self.param.get_option_keys().index(self.param.get_value())) - except: - self._input.set_active(-1) - self._input.get_child().set_text(self.param.get_value()) - self._input.connect('changed', self._handle_changed) - self._input.get_child().connect('changed', self._handle_changed) - self.pack_start(self._input, False) - def get_text(self): - if self._input.get_active() == -1: return self._input.get_child().get_text() - return self.param.get_option_keys()[self._input.get_active()] - def set_tooltip_text(self, text): - if self._input.get_active() == -1: #custom entry - self._input.get_child().set_tooltip_text(text) - else: self._input.set_tooltip_text(text) - def set_color(self, color): - if self._input.get_active() == -1: #custom entry, use color - self._input.get_child().modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color)) - self._input.get_child().modify_text(gtk.STATE_NORMAL, Colors.PARAM_ENTRY_TEXT_COLOR) - else: #from enum, make pale background - self._input.get_child().modify_base(gtk.STATE_NORMAL, Colors.ENTRYENUM_CUSTOM_COLOR) - self._input.get_child().modify_text(gtk.STATE_NORMAL, Colors.PARAM_ENTRY_TEXT_COLOR) + """Provide an entry box and drop down menu for Raw Enum types.""" + + def __init__(self, *args, **kwargs): + InputParam.__init__(self, *args, **kwargs) + self._input = gtk.combo_box_entry_new_text() + for option in self.param.get_options(): self._input.append_text(option.get_name()) + try: self._input.set_active(self.param.get_option_keys().index(self.param.get_value())) + except: + self._input.set_active(-1) + self._input.get_child().set_text(self.param.get_value()) + self._input.connect('changed', self._handle_changed) + self._input.get_child().connect('changed', self._handle_changed) + self.pack_start(self._input, False) + def get_text(self): + if self._input.get_active() == -1: return self._input.get_child().get_text() + return self.param.get_option_keys()[self._input.get_active()] + def set_tooltip_text(self, text): + if self._input.get_active() == -1: #custom entry + self._input.get_child().set_tooltip_text(text) + else: self._input.set_tooltip_text(text) + def set_color(self, color): + if self._input.get_active() == -1: #custom entry, use color + self._input.get_child().modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color)) + self._input.get_child().modify_text(gtk.STATE_NORMAL, Colors.PARAM_ENTRY_TEXT_COLOR) + else: #from enum, make pale background + self._input.get_child().modify_base(gtk.STATE_NORMAL, Colors.ENTRYENUM_CUSTOM_COLOR) + self._input.get_child().modify_text(gtk.STATE_NORMAL, Colors.PARAM_ENTRY_TEXT_COLOR) PARAM_MARKUP_TMPL="""\ #set $foreground = $param.is_valid() and 'black' or 'red' @@ -142,13 +142,13 @@ PARAM_LABEL_MARKUP_TMPL="""\ TIP_MARKUP_TMPL="""\ ######################################## #def truncate(string) - #set $max_len = 100 - #set $string = str($string) - #if len($string) > $max_len + #set $max_len = 100 + #set $string = str($string) + #if len($string) > $max_len $('%s...%s'%($string[:$max_len/2], $string[-$max_len/2:]))#slurp - #else + #else $string#slurp - #end if + #end if #end def ######################################## Key: $param.get_key() @@ -159,35 +159,35 @@ Value: $truncate($param.get_evaluated()) Error: $(param.get_error_messages()[0]) #else Error: - #for $error_msg in $param.get_error_messages() + #for $error_msg in $param.get_error_messages() * $error_msg - #end for + #end for #end if""" class Param(Element): - """The graphical parameter.""" - - def __init__(self): Element.__init__(self) - - def get_input(self, *args, **kwargs): - """ - Get the graphical gtk class to represent this parameter. - An enum requires and combo parameter. - A non-enum with options gets a combined entry/combo parameter. - All others get a standard entry parameter. - - Returns: - gtk input class - """ - if self.is_enum(): return EnumParam(self, *args, **kwargs) - if self.get_options(): return EnumEntryParam(self, *args, **kwargs) - return EntryParam(self, *args, **kwargs) - - def get_markup(self): - """ - Get the markup for this param. - - Returns: - a pango markup string - """ - return Utils.parse_template(PARAM_MARKUP_TMPL, param=self) + """The graphical parameter.""" + + def __init__(self): Element.__init__(self) + + def get_input(self, *args, **kwargs): + """ + Get the graphical gtk class to represent this parameter. + An enum requires and combo parameter. + A non-enum with options gets a combined entry/combo parameter. + All others get a standard entry parameter. + + Returns: + gtk input class + """ + if self.is_enum(): return EnumParam(self, *args, **kwargs) + if self.get_options(): return EnumEntryParam(self, *args, **kwargs) + return EntryParam(self, *args, **kwargs) + + def get_markup(self): + """ + Get the markup for this param. + + Returns: + a pango markup string + """ + return Utils.parse_template(PARAM_MARKUP_TMPL, param=self) diff --git a/grc/gui/Platform.py b/grc/gui/Platform.py index 8bbfaca232..6a8175b9fa 100644 --- a/grc/gui/Platform.py +++ b/grc/gui/Platform.py @@ -20,4 +20,4 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from Element import Element class Platform(Element): - def __init__(self): Element.__init__(self) + def __init__(self): Element.__init__(self) diff --git a/grc/gui/Port.py b/grc/gui/Port.py index 2ccc394971..fe1dc5070a 100644 --- a/grc/gui/Port.py +++ b/grc/gui/Port.py @@ -19,9 +19,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from Element import Element from Constants import \ - PORT_SEPARATION, CONNECTOR_EXTENSION_MINIMAL, \ - CONNECTOR_EXTENSION_INCREMENT, \ - PORT_LABEL_PADDING, PORT_MIN_WIDTH + PORT_SEPARATION, CONNECTOR_EXTENSION_MINIMAL, \ + CONNECTOR_EXTENSION_INCREMENT, \ + PORT_LABEL_PADDING, PORT_MIN_WIDTH import Utils import Colors import pygtk @@ -32,196 +32,192 @@ PORT_MARKUP_TMPL="""\ <span foreground="black" font_desc="Sans 7.5">$encode($port.get_name())</span>""" class Port(Element): - """The graphical port.""" - - def __init__(self): - """ - Port contructor. - Create list of connector coordinates. - """ - Element.__init__(self) - self.connector_coordinates = dict() - - def create_shapes(self): - """Create new areas and labels for the port.""" - Element.create_shapes(self) - - #get current rotation - rotation = self.get_rotation() - #get all sibling ports - if self.is_source(): ports = self.get_parent().get_sources_gui() - elif self.is_sink(): ports = self.get_parent().get_sinks_gui() - #get the max width - self.W = max([port.W for port in ports] + [PORT_MIN_WIDTH]) - #get a numeric index for this port relative to its sibling ports - try: - index = ports.index(self) - except: - if hasattr(self, '_connector_length'): - del self._connector_length; - return - - length = len(ports) - #reverse the order of ports for these rotations - if rotation in (180, 270): index = length-index-1 - offset = (self.get_parent().H - length*self.H - (length-1)*PORT_SEPARATION)/2 - #create areas and connector coordinates - if (self.is_sink() and rotation == 0) or (self.is_source() and rotation == 180): - x = -1*self.W - y = (PORT_SEPARATION+self.H)*index+offset - self.add_area((x, y), (self.W, self.H)) - self._connector_coordinate = (x-1, y+self.H/2) - elif (self.is_source() and rotation == 0) or (self.is_sink() and rotation == 180): - x = self.get_parent().W - y = (PORT_SEPARATION+self.H)*index+offset - self.add_area((x, y), (self.W, self.H)) - self._connector_coordinate = (x+1+self.W, y+self.H/2) - elif (self.is_source() and rotation == 90) or (self.is_sink() and rotation == 270): - y = -1*self.W - x = (PORT_SEPARATION+self.H)*index+offset - self.add_area((x, y), (self.H, self.W)) - self._connector_coordinate = (x+self.H/2, y-1) - elif (self.is_sink() and rotation == 90) or (self.is_source() and rotation == 270): - y = self.get_parent().W - x = (PORT_SEPARATION+self.H)*index+offset - self.add_area((x, y), (self.H, self.W)) - self._connector_coordinate = (x+self.H/2, y+1+self.W) - #the connector length - self._connector_length = CONNECTOR_EXTENSION_MINIMAL + CONNECTOR_EXTENSION_INCREMENT*index - - def modify_height(self, start_height): - type_dict = {'bus':(lambda a: a * 3)}; - - if self.get_type() in type_dict: - return type_dict[self.get_type()](start_height); - else: - return start_height; - - def create_labels(self): - """Create the labels for the socket.""" - Element.create_labels(self) - self._bg_color = Colors.get_color(self.get_color()) - #create the layout - layout = gtk.DrawingArea().create_pango_layout('') - layout.set_markup(Utils.parse_template(PORT_MARKUP_TMPL, port=self)) - self.w, self.h = layout.get_pixel_size() - self.W, self.H = 2*PORT_LABEL_PADDING+self.w, 2*PORT_LABEL_PADDING+self.h - self.H = self.modify_height(self.H); - #create the pixmap - pixmap = self.get_parent().get_parent().new_pixmap(self.w, self.h) - gc = pixmap.new_gc() - gc.set_foreground(self._bg_color) - pixmap.draw_rectangle(gc, True, 0, 0, self.w, self.h) - pixmap.draw_layout(gc, 0, 0, layout) - #create vertical and horizontal pixmaps - self.horizontal_label = pixmap - if self.is_vertical(): - self.vertical_label = self.get_parent().get_parent().new_pixmap(self.h, self.w) - Utils.rotate_pixmap(gc, self.horizontal_label, self.vertical_label) - - def draw(self, gc, window): - """ - Draw the socket with a label. - - Args: - gc: the graphics context - window: the gtk window to draw on - """ - - Element.draw( - self, gc, window, bg_color=self._bg_color, - border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or Colors.BORDER_COLOR, - ) - X,Y = self.get_coordinate() - (x,y),(w,h) = self._areas_list[0] #use the first area's sizes to place the labels - if self.is_horizontal(): - window.draw_drawable(gc, self.horizontal_label, 0, 0, x+X+(self.W-self.w)/2, y+Y+(self.H-self.h)/2, -1, -1) - elif self.is_vertical(): - window.draw_drawable(gc, self.vertical_label, 0, 0, x+X+(self.H-self.h)/2, y+Y+(self.W-self.w)/2, -1, -1) - - def get_connector_coordinate(self): - """ - Get the coordinate where connections may attach to. - - Returns: - the connector coordinate (x, y) tuple - """ - x,y = self._connector_coordinate - X,Y = self.get_coordinate() - return (x+X, y+Y) - - def get_connector_direction(self): - """ - Get the direction that the socket points: 0,90,180,270. - This is the rotation degree if the socket is an output or - the rotation degree + 180 if the socket is an input. - - Returns: - the direction in degrees - """ - if self.is_source(): return self.get_rotation() - elif self.is_sink(): return (self.get_rotation() + 180)%360 - - def get_connector_length(self): - """ - Get the length of the connector. - The connector length increases as the port index changes. - - Returns: - the length in pixels - """ - return self._connector_length - - def get_rotation(self): - """ - Get the parent's rotation rather than self. - - Returns: - the parent's rotation - """ - return self.get_parent().get_rotation() - - def move(self, delta_coor): - """ - Move the parent rather than self. - - Args: - delta_corr: the (delta_x, delta_y) tuple - """ - self.get_parent().move(delta_coor) - - def rotate(self, direction): - """ - Rotate the parent rather than self. - - Args: - direction: degrees to rotate - """ - self.get_parent().rotate(direction) - - def get_coordinate(self): - """ - Get the parent's coordinate rather than self. - - Returns: - the parents coordinate - """ - return self.get_parent().get_coordinate() - - def set_highlighted(self, highlight): - """ - Set the parent highlight rather than self. - - Args: - highlight: true to enable highlighting - """ - self.get_parent().set_highlighted(highlight) - - def is_highlighted(self): - """ - Get the parent's is highlight rather than self. - - Returns: - the parent's highlighting status - """ - return self.get_parent().is_highlighted() + """The graphical port.""" + + def __init__(self): + """ + Port contructor. + Create list of connector coordinates. + """ + Element.__init__(self) + self.connector_coordinates = dict() + + def create_shapes(self): + """Create new areas and labels for the port.""" + Element.create_shapes(self) + #get current rotation + rotation = self.get_rotation() + #get all sibling ports + if self.is_source(): ports = self.get_parent().get_sources_gui() + elif self.is_sink(): ports = self.get_parent().get_sinks_gui() + #get the max width + self.W = max([port.W for port in ports] + [PORT_MIN_WIDTH]) + #get a numeric index for this port relative to its sibling ports + try: + index = ports.index(self) + except: + if hasattr(self, '_connector_length'): + del self._connector_length; + return + length = len(ports) + #reverse the order of ports for these rotations + if rotation in (180, 270): index = length-index-1 + offset = (self.get_parent().H - length*self.H - (length-1)*PORT_SEPARATION)/2 + #create areas and connector coordinates + if (self.is_sink() and rotation == 0) or (self.is_source() and rotation == 180): + x = -1*self.W + y = (PORT_SEPARATION+self.H)*index+offset + self.add_area((x, y), (self.W, self.H)) + self._connector_coordinate = (x-1, y+self.H/2) + elif (self.is_source() and rotation == 0) or (self.is_sink() and rotation == 180): + x = self.get_parent().W + y = (PORT_SEPARATION+self.H)*index+offset + self.add_area((x, y), (self.W, self.H)) + self._connector_coordinate = (x+1+self.W, y+self.H/2) + elif (self.is_source() and rotation == 90) or (self.is_sink() and rotation == 270): + y = -1*self.W + x = (PORT_SEPARATION+self.H)*index+offset + self.add_area((x, y), (self.H, self.W)) + self._connector_coordinate = (x+self.H/2, y-1) + elif (self.is_sink() and rotation == 90) or (self.is_source() and rotation == 270): + y = self.get_parent().W + x = (PORT_SEPARATION+self.H)*index+offset + self.add_area((x, y), (self.H, self.W)) + self._connector_coordinate = (x+self.H/2, y+1+self.W) + #the connector length + self._connector_length = CONNECTOR_EXTENSION_MINIMAL + CONNECTOR_EXTENSION_INCREMENT*index + def modify_height(self, start_height): + type_dict = {'bus':(lambda a: a * 3)}; + + if self.get_type() in type_dict: + return type_dict[self.get_type()](start_height); + else: + return start_height; + + def create_labels(self): + """Create the labels for the socket.""" + Element.create_labels(self) + self._bg_color = Colors.get_color(self.get_color()) + #create the layout + layout = gtk.DrawingArea().create_pango_layout('') + layout.set_markup(Utils.parse_template(PORT_MARKUP_TMPL, port=self)) + self.w, self.h = layout.get_pixel_size() + self.W, self.H = 2*PORT_LABEL_PADDING+self.w, 2*PORT_LABEL_PADDING+self.h + self.H = self.modify_height(self.H); + #create the pixmap + pixmap = self.get_parent().get_parent().new_pixmap(self.w, self.h) + gc = pixmap.new_gc() + gc.set_foreground(self._bg_color) + pixmap.draw_rectangle(gc, True, 0, 0, self.w, self.h) + pixmap.draw_layout(gc, 0, 0, layout) + #create vertical and horizontal pixmaps + self.horizontal_label = pixmap + if self.is_vertical(): + self.vertical_label = self.get_parent().get_parent().new_pixmap(self.h, self.w) + Utils.rotate_pixmap(gc, self.horizontal_label, self.vertical_label) + + def draw(self, gc, window): + """ + Draw the socket with a label. + + Args: + gc: the graphics context + window: the gtk window to draw on + """ + Element.draw( + self, gc, window, bg_color=self._bg_color, + border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or Colors.BORDER_COLOR, + ) + X,Y = self.get_coordinate() + (x,y),(w,h) = self._areas_list[0] #use the first area's sizes to place the labels + if self.is_horizontal(): + window.draw_drawable(gc, self.horizontal_label, 0, 0, x+X+(self.W-self.w)/2, y+Y+(self.H-self.h)/2, -1, -1) + elif self.is_vertical(): + window.draw_drawable(gc, self.vertical_label, 0, 0, x+X+(self.H-self.h)/2, y+Y+(self.W-self.w)/2, -1, -1) + + def get_connector_coordinate(self): + """ + Get the coordinate where connections may attach to. + + Returns: + the connector coordinate (x, y) tuple + """ + x,y = self._connector_coordinate + X,Y = self.get_coordinate() + return (x+X, y+Y) + + def get_connector_direction(self): + """ + Get the direction that the socket points: 0,90,180,270. + This is the rotation degree if the socket is an output or + the rotation degree + 180 if the socket is an input. + + Returns: + the direction in degrees + """ + if self.is_source(): return self.get_rotation() + elif self.is_sink(): return (self.get_rotation() + 180)%360 + + def get_connector_length(self): + """ + Get the length of the connector. + The connector length increases as the port index changes. + + Returns: + the length in pixels + """ + return self._connector_length + + def get_rotation(self): + """ + Get the parent's rotation rather than self. + + Returns: + the parent's rotation + """ + return self.get_parent().get_rotation() + + def move(self, delta_coor): + """ + Move the parent rather than self. + + Args: + delta_corr: the (delta_x, delta_y) tuple + """ + self.get_parent().move(delta_coor) + + def rotate(self, direction): + """ + Rotate the parent rather than self. + + Args: + direction: degrees to rotate + """ + self.get_parent().rotate(direction) + + def get_coordinate(self): + """ + Get the parent's coordinate rather than self. + + Returns: + the parents coordinate + """ + return self.get_parent().get_coordinate() + + def set_highlighted(self, highlight): + """ + Set the parent highlight rather than self. + + Args: + highlight: true to enable highlighting + """ + self.get_parent().set_highlighted(highlight) + + def is_highlighted(self): + """ + Get the parent's is highlight rather than self. + + Returns: + the parent's highlighting status + """ + return self.get_parent().is_highlighted() diff --git a/grc/gui/Preferences.py b/grc/gui/Preferences.py index 1d89920dd5..ce545cab6a 100644 --- a/grc/gui/Preferences.py +++ b/grc/gui/Preferences.py @@ -27,60 +27,60 @@ def file_extension(): return '.'+_platform.get_key() def _prefs_file(): return os.path.join(os.path.expanduser('~'), file_extension()) def load(platform): - global _platform - _platform = platform - #create sections - _config_parser.add_section('main') - _config_parser.add_section('files_open') - try: _config_parser.read(_prefs_file()) - except: pass + global _platform + _platform = platform + #create sections + _config_parser.add_section('main') + _config_parser.add_section('files_open') + try: _config_parser.read(_prefs_file()) + except: pass def save(): - try: _config_parser.write(open(_prefs_file(), 'w')) - except: pass + try: _config_parser.write(open(_prefs_file(), 'w')) + except: pass ########################################################################### # Special methods for specific program functionalities ########################################################################### def main_window_size(size=None): - if size is not None: - _config_parser.set('main', 'main_window_width', size[0]) - _config_parser.set('main', 'main_window_height', size[1]) - else: - try: return ( - _config_parser.getint('main', 'main_window_width'), - _config_parser.getint('main', 'main_window_height'), - ) - except: return (1, 1) + if size is not None: + _config_parser.set('main', 'main_window_width', size[0]) + _config_parser.set('main', 'main_window_height', size[1]) + else: + try: return ( + _config_parser.getint('main', 'main_window_width'), + _config_parser.getint('main', 'main_window_height'), + ) + except: return (1, 1) def file_open(file=None): - if file is not None: _config_parser.set('main', 'file_open', file) - else: - try: return _config_parser.get('main', 'file_open') - except: return '' + if file is not None: _config_parser.set('main', 'file_open', file) + else: + try: return _config_parser.get('main', 'file_open') + except: return '' def files_open(files=None): - if files is not None: - _config_parser.remove_section('files_open') #clear section - _config_parser.add_section('files_open') - for i, file in enumerate(files): - _config_parser.set('files_open', 'file_open_%d'%i, file) - else: - files = list() - i = 0 - while True: - try: files.append(_config_parser.get('files_open', 'file_open_%d'%i)) - except: return files - i = i + 1 + if files is not None: + _config_parser.remove_section('files_open') #clear section + _config_parser.add_section('files_open') + for i, file in enumerate(files): + _config_parser.set('files_open', 'file_open_%d'%i, file) + else: + files = list() + i = 0 + while True: + try: files.append(_config_parser.get('files_open', 'file_open_%d'%i)) + except: return files + i = i + 1 def reports_window_position(pos=None): - if pos is not None: _config_parser.set('main', 'reports_window_position', pos) - else: - try: return _config_parser.getint('main', 'reports_window_position') or 1 #greater than 0 - except: return -1 + if pos is not None: _config_parser.set('main', 'reports_window_position', pos) + else: + try: return _config_parser.getint('main', 'reports_window_position') or 1 #greater than 0 + except: return -1 def blocks_window_position(pos=None): - if pos is not None: _config_parser.set('main', 'blocks_window_position', pos) - else: - try: return _config_parser.getint('main', 'blocks_window_position') or 1 #greater than 0 - except: return -1 + if pos is not None: _config_parser.set('main', 'blocks_window_position', pos) + else: + try: return _config_parser.getint('main', 'blocks_window_position') or 1 #greater than 0 + except: return -1 diff --git a/grc/gui/PropsDialog.py b/grc/gui/PropsDialog.py index 5264857fab..5c09f7cac1 100644 --- a/grc/gui/PropsDialog.py +++ b/grc/gui/PropsDialog.py @@ -25,158 +25,158 @@ from Dialogs import TextDisplay from Constants import MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT def get_title_label(title): - """ - Get a title label for the params window. - The title will be bold, underlined, and left justified. - - Args: - title: the text of the title - - Returns: - a gtk object - """ - label = gtk.Label() - label.set_markup('\n<b><span underline="low">%s</span>:</b>\n'%title) - hbox = gtk.HBox() - hbox.pack_start(label, False, False, padding=11) - return hbox + """ + Get a title label for the params window. + The title will be bold, underlined, and left justified. + + Args: + title: the text of the title + + Returns: + a gtk object + """ + label = gtk.Label() + label.set_markup('\n<b><span underline="low">%s</span>:</b>\n'%title) + hbox = gtk.HBox() + hbox.pack_start(label, False, False, padding=11) + return hbox class PropsDialog(gtk.Dialog): - """ - A dialog to set block parameters, view errors, and view documentation. - """ + """ + A dialog to set block parameters, view errors, and view documentation. + """ - def __init__(self, block): - """ - Properties dialog contructor. - - Args: - block: a block instance - """ - self._hash = 0 - LABEL_SPACING = 7 - gtk.Dialog.__init__(self, - title='Properties: %s'%block.get_name(), - buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT), - ) - self._block = block - self.set_size_request(MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT) - vbox = gtk.VBox() - #Create the scrolled window to hold all the parameters - scrolled_window = gtk.ScrolledWindow() - scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - scrolled_window.add_with_viewport(vbox) - self.vbox.pack_start(scrolled_window, True) - #Params box for block parameters - self._params_box = gtk.VBox() - self._params_box.pack_start(get_title_label('Parameters'), False) - self._input_object_params = list() - #Error Messages for the block - self._error_box = gtk.VBox() - self._error_messages_text_display = TextDisplay() - self._error_box.pack_start(gtk.Label(), False, False, LABEL_SPACING) - self._error_box.pack_start(get_title_label('Error Messages'), False) - self._error_box.pack_start(self._error_messages_text_display, False) - #Docs for the block - self._docs_box = err_box = gtk.VBox() - self._docs_text_display = TextDisplay() - self._docs_box.pack_start(gtk.Label(), False, False, LABEL_SPACING) - self._docs_box.pack_start(get_title_label('Documentation'), False) - self._docs_box.pack_start(self._docs_text_display, False) - #Add the boxes - vbox.pack_start(self._params_box, False) - vbox.pack_start(self._error_box, False) - vbox.pack_start(self._docs_box, False) - #connect events - self.connect('key-press-event', self._handle_key_press) - self.connect('show', self._update_gui) - #show all (performs initial gui update) - self.show_all() + def __init__(self, block): + """ + Properties dialog contructor. + + Args: + block: a block instance + """ + self._hash = 0 + LABEL_SPACING = 7 + gtk.Dialog.__init__(self, + title='Properties: %s'%block.get_name(), + buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT), + ) + self._block = block + self.set_size_request(MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT) + vbox = gtk.VBox() + #Create the scrolled window to hold all the parameters + scrolled_window = gtk.ScrolledWindow() + scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrolled_window.add_with_viewport(vbox) + self.vbox.pack_start(scrolled_window, True) + #Params box for block parameters + self._params_box = gtk.VBox() + self._params_box.pack_start(get_title_label('Parameters'), False) + self._input_object_params = list() + #Error Messages for the block + self._error_box = gtk.VBox() + self._error_messages_text_display = TextDisplay() + self._error_box.pack_start(gtk.Label(), False, False, LABEL_SPACING) + self._error_box.pack_start(get_title_label('Error Messages'), False) + self._error_box.pack_start(self._error_messages_text_display, False) + #Docs for the block + self._docs_box = err_box = gtk.VBox() + self._docs_text_display = TextDisplay() + self._docs_box.pack_start(gtk.Label(), False, False, LABEL_SPACING) + self._docs_box.pack_start(get_title_label('Documentation'), False) + self._docs_box.pack_start(self._docs_text_display, False) + #Add the boxes + vbox.pack_start(self._params_box, False) + vbox.pack_start(self._error_box, False) + vbox.pack_start(self._docs_box, False) + #connect events + self.connect('key-press-event', self._handle_key_press) + self.connect('show', self._update_gui) + #show all (performs initial gui update) + self.show_all() - def _params_changed(self): - """ - Have the params in this dialog changed? - Ex: Added, removed, type change, hide change... - To the props dialog, the hide setting of 'none' and 'part' are identical. - Therfore, the props dialog only cares if the hide setting is/not 'all'. - Make a hash that uniquely represents the params' state. - - Returns: - true if changed - """ - old_hash = self._hash - #create a tuple of things from each param that affects the params box - self._hash = hash(tuple([( - hash(param), param.get_type(), param.get_hide() == 'all', - ) for param in self._block.get_params()])) - return self._hash != old_hash + def _params_changed(self): + """ + Have the params in this dialog changed? + Ex: Added, removed, type change, hide change... + To the props dialog, the hide setting of 'none' and 'part' are identical. + Therfore, the props dialog only cares if the hide setting is/not 'all'. + Make a hash that uniquely represents the params' state. + + Returns: + true if changed + """ + old_hash = self._hash + #create a tuple of things from each param that affects the params box + self._hash = hash(tuple([( + hash(param), param.get_type(), param.get_hide() == 'all', + ) for param in self._block.get_params()])) + return self._hash != old_hash - def _handle_changed(self, *args): - """ - A change occured within a param: - Rewrite/validate the block and update the gui. - """ - #update for the block - self._block.rewrite() - self._block.validate() - self._update_gui() + def _handle_changed(self, *args): + """ + A change occured within a param: + Rewrite/validate the block and update the gui. + """ + #update for the block + self._block.rewrite() + self._block.validate() + self._update_gui() - def _update_gui(self, *args): - """ - Repopulate the parameters box (if changed). - Update all the input parameters. - Update the error messages box. - Hide the box if there are no errors. - Update the documentation block. - Hide the box if there are no docs. - """ - #update the params box - if self._params_changed(): - #hide params box before changing - self._params_box.hide_all() - #empty the params box - for io_param in list(self._input_object_params): - self._params_box.remove(io_param) - self._input_object_params.remove(io_param) - io_param.destroy() - #repopulate the params box - for param in self._block.get_params(): - if param.get_hide() == 'all': continue - io_param = param.get_input(self._handle_changed) - self._input_object_params.append(io_param) - self._params_box.pack_start(io_param, False) - #show params box with new params - self._params_box.show_all() - #update the errors box - if self._block.is_valid(): self._error_box.hide() - else: self._error_box.show() - messages = '\n\n'.join(self._block.get_error_messages()) - self._error_messages_text_display.set_text(messages) - #update the docs box - if self._block.get_doc(): self._docs_box.show() - else: self._docs_box.hide() - self._docs_text_display.set_text(self._block.get_doc()) + def _update_gui(self, *args): + """ + Repopulate the parameters box (if changed). + Update all the input parameters. + Update the error messages box. + Hide the box if there are no errors. + Update the documentation block. + Hide the box if there are no docs. + """ + #update the params box + if self._params_changed(): + #hide params box before changing + self._params_box.hide_all() + #empty the params box + for io_param in list(self._input_object_params): + self._params_box.remove(io_param) + self._input_object_params.remove(io_param) + io_param.destroy() + #repopulate the params box + for param in self._block.get_params(): + if param.get_hide() == 'all': continue + io_param = param.get_input(self._handle_changed) + self._input_object_params.append(io_param) + self._params_box.pack_start(io_param, False) + #show params box with new params + self._params_box.show_all() + #update the errors box + if self._block.is_valid(): self._error_box.hide() + else: self._error_box.show() + messages = '\n\n'.join(self._block.get_error_messages()) + self._error_messages_text_display.set_text(messages) + #update the docs box + if self._block.get_doc(): self._docs_box.show() + else: self._docs_box.hide() + self._docs_text_display.set_text(self._block.get_doc()) - def _handle_key_press(self, widget, event): - """ - Handle key presses from the keyboard. - Call the ok response when enter is pressed. - - Returns: - false to forward the keypress - """ - if event.keyval == gtk.keysyms.Return: - self.response(gtk.RESPONSE_ACCEPT) - return True #handled here - return False #forward the keypress + def _handle_key_press(self, widget, event): + """ + Handle key presses from the keyboard. + Call the ok response when enter is pressed. + + Returns: + false to forward the keypress + """ + if event.keyval == gtk.keysyms.Return: + self.response(gtk.RESPONSE_ACCEPT) + return True #handled here + return False #forward the keypress - def run(self): - """ - Run the dialog and get its response. - - Returns: - true if the response was accept - """ - response = gtk.Dialog.run(self) - self.destroy() - return response == gtk.RESPONSE_ACCEPT + def run(self): + """ + Run the dialog and get its response. + + Returns: + true if the response was accept + """ + response = gtk.Dialog.run(self) + self.destroy() + return response == gtk.RESPONSE_ACCEPT diff --git a/grc/gui/StateCache.py b/grc/gui/StateCache.py index 50d85bf960..558f507716 100644 --- a/grc/gui/StateCache.py +++ b/grc/gui/StateCache.py @@ -21,82 +21,82 @@ import Actions from Constants import STATE_CACHE_SIZE class StateCache(object): - """ - The state cache is an interface to a list to record data/states and to revert to previous states. - States are recorded into the list in a circular fassion by using an index for the current state, - and counters for the range where states are stored. - """ + """ + The state cache is an interface to a list to record data/states and to revert to previous states. + States are recorded into the list in a circular fassion by using an index for the current state, + and counters for the range where states are stored. + """ - def __init__(self, initial_state): - """ - StateCache constructor. - - Args: - initial_state: the intial state (nested data) - """ - self.states = [None] * STATE_CACHE_SIZE #fill states - self.current_state_index = 0 - self.num_prev_states = 0 - self.num_next_states = 0 - self.states[0] = initial_state - self.update_actions() + def __init__(self, initial_state): + """ + StateCache constructor. + + Args: + initial_state: the intial state (nested data) + """ + self.states = [None] * STATE_CACHE_SIZE #fill states + self.current_state_index = 0 + self.num_prev_states = 0 + self.num_next_states = 0 + self.states[0] = initial_state + self.update_actions() - def save_new_state(self, state): - """ - Save a new state. - Place the new state at the next index and add one to the number of previous states. - - Args: - state: the new state - """ - self.current_state_index = (self.current_state_index + 1)%STATE_CACHE_SIZE - self.states[self.current_state_index] = state - self.num_prev_states = self.num_prev_states + 1 - if self.num_prev_states == STATE_CACHE_SIZE: self.num_prev_states = STATE_CACHE_SIZE - 1 - self.num_next_states = 0 - self.update_actions() + def save_new_state(self, state): + """ + Save a new state. + Place the new state at the next index and add one to the number of previous states. + + Args: + state: the new state + """ + self.current_state_index = (self.current_state_index + 1)%STATE_CACHE_SIZE + self.states[self.current_state_index] = state + self.num_prev_states = self.num_prev_states + 1 + if self.num_prev_states == STATE_CACHE_SIZE: self.num_prev_states = STATE_CACHE_SIZE - 1 + self.num_next_states = 0 + self.update_actions() - def get_current_state(self): - """ - Get the state at the current index. - - Returns: - the current state (nested data) - """ - self.update_actions() - return self.states[self.current_state_index] + def get_current_state(self): + """ + Get the state at the current index. + + Returns: + the current state (nested data) + """ + self.update_actions() + return self.states[self.current_state_index] - def get_prev_state(self): - """ - Get the previous state and decrement the current index. - - Returns: - the previous state or None - """ - if self.num_prev_states > 0: - self.current_state_index = (self.current_state_index + STATE_CACHE_SIZE -1)%STATE_CACHE_SIZE - self.num_next_states = self.num_next_states + 1 - self.num_prev_states = self.num_prev_states - 1 - return self.get_current_state() - return None + def get_prev_state(self): + """ + Get the previous state and decrement the current index. + + Returns: + the previous state or None + """ + if self.num_prev_states > 0: + self.current_state_index = (self.current_state_index + STATE_CACHE_SIZE -1)%STATE_CACHE_SIZE + self.num_next_states = self.num_next_states + 1 + self.num_prev_states = self.num_prev_states - 1 + return self.get_current_state() + return None - def get_next_state(self): - """ - Get the nest state and increment the current index. - - Returns: - the next state or None - """ - if self.num_next_states > 0: - self.current_state_index = (self.current_state_index + 1)%STATE_CACHE_SIZE - self.num_next_states = self.num_next_states - 1 - self.num_prev_states = self.num_prev_states + 1 - return self.get_current_state() - return None + def get_next_state(self): + """ + Get the nest state and increment the current index. + + Returns: + the next state or None + """ + if self.num_next_states > 0: + self.current_state_index = (self.current_state_index + 1)%STATE_CACHE_SIZE + self.num_next_states = self.num_next_states - 1 + self.num_prev_states = self.num_prev_states + 1 + return self.get_current_state() + return None - def update_actions(self): - """ - Update the undo and redo actions based on the number of next and prev states. - """ - Actions.FLOW_GRAPH_REDO.set_sensitive(self.num_next_states != 0) - Actions.FLOW_GRAPH_UNDO.set_sensitive(self.num_prev_states != 0) + def update_actions(self): + """ + Update the undo and redo actions based on the number of next and prev states. + """ + Actions.FLOW_GRAPH_REDO.set_sensitive(self.num_next_states != 0) + Actions.FLOW_GRAPH_UNDO.set_sensitive(self.num_prev_states != 0) diff --git a/grc/gui/Utils.py b/grc/gui/Utils.py index 407c875a87..cc1f8ceb12 100644 --- a/grc/gui/Utils.py +++ b/grc/gui/Utils.py @@ -25,86 +25,86 @@ import gtk import gobject def rotate_pixmap(gc, src_pixmap, dst_pixmap, angle=gtk.gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE): - """ - Load the destination pixmap with a rotated version of the source pixmap. - The source pixmap will be loaded into a pixbuf, rotated, and drawn to the destination pixmap. - The pixbuf is a client-side drawable, where a pixmap is a server-side drawable. - - Args: - gc: the graphics context - src_pixmap: the source pixmap - dst_pixmap: the destination pixmap - angle: the angle to rotate by - """ - width, height = src_pixmap.get_size() - pixbuf = gtk.gdk.Pixbuf( - colorspace=gtk.gdk.COLORSPACE_RGB, - has_alpha=False, bits_per_sample=8, - width=width, height=height, - ) - pixbuf.get_from_drawable(src_pixmap, src_pixmap.get_colormap(), 0, 0, 0, 0, -1, -1) - pixbuf = pixbuf.rotate_simple(angle) - dst_pixmap.draw_pixbuf(gc, pixbuf, 0, 0, 0, 0) + """ + Load the destination pixmap with a rotated version of the source pixmap. + The source pixmap will be loaded into a pixbuf, rotated, and drawn to the destination pixmap. + The pixbuf is a client-side drawable, where a pixmap is a server-side drawable. + + Args: + gc: the graphics context + src_pixmap: the source pixmap + dst_pixmap: the destination pixmap + angle: the angle to rotate by + """ + width, height = src_pixmap.get_size() + pixbuf = gtk.gdk.Pixbuf( + colorspace=gtk.gdk.COLORSPACE_RGB, + has_alpha=False, bits_per_sample=8, + width=width, height=height, + ) + pixbuf.get_from_drawable(src_pixmap, src_pixmap.get_colormap(), 0, 0, 0, 0, -1, -1) + pixbuf = pixbuf.rotate_simple(angle) + dst_pixmap.draw_pixbuf(gc, pixbuf, 0, 0, 0, 0) def get_rotated_coordinate(coor, rotation): - """ - Rotate the coordinate by the given rotation. - - Args: - coor: the coordinate x, y tuple - rotation: the angle in degrees - - Returns: - the rotated coordinates - """ - #handles negative angles - rotation = (rotation + 360)%360 - if rotation not in POSSIBLE_ROTATIONS: - raise ValueError('unusable rotation angle "%s"'%str(rotation)) - #determine the number of degrees to rotate - cos_r, sin_r = { - 0: (1, 0), - 90: (0, 1), - 180: (-1, 0), - 270: (0, -1), - }[rotation] - x, y = coor - return (x*cos_r + y*sin_r, -x*sin_r + y*cos_r) + """ + Rotate the coordinate by the given rotation. + + Args: + coor: the coordinate x, y tuple + rotation: the angle in degrees + + Returns: + the rotated coordinates + """ + #handles negative angles + rotation = (rotation + 360)%360 + if rotation not in POSSIBLE_ROTATIONS: + raise ValueError('unusable rotation angle "%s"'%str(rotation)) + #determine the number of degrees to rotate + cos_r, sin_r = { + 0: (1, 0), + 90: (0, 1), + 180: (-1, 0), + 270: (0, -1), + }[rotation] + x, y = coor + return (x*cos_r + y*sin_r, -x*sin_r + y*cos_r) def get_angle_from_coordinates((x1,y1), (x2,y2)): - """ - Given two points, calculate the vector direction from point1 to point2, directions are multiples of 90 degrees. - - Args: - (x1,y1): the coordinate of point 1 - (x2,y2): the coordinate of point 2 - - Returns: - the direction in degrees - """ - if y1 == y2:#0 or 180 - if x2 > x1: return 0 - else: return 180 - else:#90 or 270 - if y2 > y1: return 270 - else: return 90 + """ + Given two points, calculate the vector direction from point1 to point2, directions are multiples of 90 degrees. + + Args: + (x1,y1): the coordinate of point 1 + (x2,y2): the coordinate of point 2 + + Returns: + the direction in degrees + """ + if y1 == y2:#0 or 180 + if x2 > x1: return 0 + else: return 180 + else:#90 or 270 + if y2 > y1: return 270 + else: return 90 def parse_template(tmpl_str, **kwargs): - """ - Parse the template string with the given args. - Pass in the xml encode method for pango escape chars. - - Args: - tmpl_str: the template as a string - - Returns: - a string of the parsed template - """ - kwargs['encode'] = gobject.markup_escape_text - #try: - # cat = str(Template(tmpl_str, kwargs)) - #except TypeError: - # print 'guppy' - # print tmpl_str - # print str(kwargs['param'].get_error_messages()) - return str(Template(tmpl_str, kwargs)) + """ + Parse the template string with the given args. + Pass in the xml encode method for pango escape chars. + + Args: + tmpl_str: the template as a string + + Returns: + a string of the parsed template + """ + kwargs['encode'] = gobject.markup_escape_text + #try: + # cat = str(Template(tmpl_str, kwargs)) + #except TypeError: + # print 'guppy' + # print tmpl_str + # print str(kwargs['param'].get_error_messages()) + return str(Template(tmpl_str, kwargs)) diff --git a/grc/python/Block.py b/grc/python/Block.py index d365c43319..e13b26c12f 100644 --- a/grc/python/Block.py +++ b/grc/python/Block.py @@ -23,195 +23,187 @@ import extract_docs class Block(_Block, _GUIBlock): - def is_virtual_sink(self): return self.get_key() == 'virtual_sink' - def is_virtual_source(self): return self.get_key() == 'virtual_source' - - ##for make source to keep track of indexes - _source_count = 0 - ##for make sink to keep track of indexes - _sink_count = 0 - - - - def __init__(self, flow_graph, n): - """ - Make a new block from nested data. - - Args: - flow: graph the parent element - n: the nested odict - - Returns: - block a new block - """ - #grab the data - self._doc = n.find('doc') or '' - self._imports = map(lambda i: i.strip(), n.findall('import')) - self._make = n.find('make') - self._var_make = n.find('var_make') - self._checks = n.findall('check') - self._callbacks = n.findall('callback') - self._throttle = n.find('throttle') or '' - self._bus_structure_source = n.find('bus_structure_source') or '' - self._bus_structure_sink = n.find('bus_structure_sink') or '' - #build the block - _Block.__init__( - self, - flow_graph=flow_graph, - n=n, - ) - _GUIBlock.__init__(self) - - def get_bus_structure(self, direction): - if direction == 'source': - bus_structure = self._bus_structure_source; - else: - bus_structure = self._bus_structure_sink; - - bus_structure = self.resolve_dependencies(bus_structure); - - if not bus_structure: return '' - try: - clean_bus_structure = self.get_parent().evaluate(bus_structure) - return clean_bus_structure - - except: return '' - - def throttle(self): return bool(self._throttle) - - def validate(self): - """ - Validate this block. - Call the base class validate. - Evaluate the checks: each check must evaluate to True. - """ - _Block.validate(self) - #evaluate the checks - for check in self._checks: - check_res = self.resolve_dependencies(check) - try: - if not self.get_parent().evaluate(check_res): - self.add_error_message('Check "%s" failed.'%check) - except: self.add_error_message('Check "%s" did not evaluate.'%check) - - def rewrite(self): - """ - Add and remove ports to adjust for the nports. - """ - _Block.rewrite(self) - - def rectify(ports): - #restore integer contiguity after insertion - #rectify the port names with the index - self.back_ofthe_bus(ports); - for i, port in enumerate(ports): - port._key = str(i) - port._name = port._n['name'] - if len(ports) > 1 and not port._type == 'bus': port._name += str(i) - - def insert_port(get_ports, get_port, key): - prev_port = get_port(str(int(key)-1)) - get_ports().insert( - get_ports().index(prev_port)+1, - prev_port.copy(new_key=key), - ) - rectify(get_ports()) - - def remove_port(get_ports, get_port, key): - port = get_port(key) - for connection in port.get_connections(): - self.get_parent().remove_element(connection) - get_ports().remove(port) - rectify(get_ports()) - - #adjust nports - for get_ports, get_port in ( - (self.get_sources, self.get_source), - (self.get_sinks, self.get_sink), - ): - master_ports = filter(lambda p: p.get_nports(), get_ports()) - for i, master_port in enumerate(master_ports): - nports = master_port.get_nports() - index_first = get_ports().index(master_port) - try: index_last = get_ports().index(master_ports[i+1]) - except IndexError: index_last = len(get_ports()) - num_ports = index_last - index_first - #do nothing if nports is already num ports - if nports == num_ports: continue - #remove excess ports and connections - if nports < num_ports: - for key in reversed(map(str, range(index_first+nports, index_first+num_ports))): - remove_port(get_ports, get_port, key); - - - continue - #add more ports - if nports > num_ports: - for key in map(str, range(index_first+num_ports, index_first+nports)): - insert_port(get_ports, get_port, key) - - - continue - - - def port_controller_modify(self, direction): - """ - Change the port controller. - - Args: - direction: +1 or -1 - - Returns: - true for change - """ - changed = False - #concat the nports string from the private nports settings of all ports - nports_str = ' '.join([port._nports for port in self.get_ports()]) - #modify all params whose keys appear in the nports string - for param in self.get_params(): - if param.is_enum() or param.get_key() not in nports_str: continue - #try to increment the port controller by direction - try: - value = param.get_evaluated() - value = value + direction - if 0 < value: - param.set_value(value) - changed = True - except: pass - return changed - - def get_doc(self): - doc = self._doc.strip('\n').replace('\\\n', '') - #merge custom doc with doxygen docs - return '\n'.join([doc, extract_docs.extract(self.get_key())]).strip('\n') - - def get_category(self): - return _Block.get_category(self) - - def get_imports(self): - """ - Resolve all import statements. - Split each import statement at newlines. - Combine all import statments into a list. - Filter empty imports. - - Returns: - a list of import statements - """ - return filter(lambda i: i, sum(map(lambda i: self.resolve_dependencies(i).split('\n'), self._imports), [])) - - def get_make(self): return self.resolve_dependencies(self._make) - def get_var_make(self): return self.resolve_dependencies(self._var_make) - - def get_callbacks(self): - """ - Get a list of function callbacks for this block. - - Returns: - a list of strings - """ - def make_callback(callback): - callback = self.resolve_dependencies(callback) - if 'self.' in callback: return callback - return 'self.%s.%s'%(self.get_id(), callback) - return map(make_callback, self._callbacks) + def is_virtual_sink(self): return self.get_key() == 'virtual_sink' + def is_virtual_source(self): return self.get_key() == 'virtual_source' + + ##for make source to keep track of indexes + _source_count = 0 + ##for make sink to keep track of indexes + _sink_count = 0 + + def __init__(self, flow_graph, n): + """ + Make a new block from nested data. + + Args: + flow: graph the parent element + n: the nested odict + + Returns: + block a new block + """ + #grab the data + self._doc = n.find('doc') or '' + self._imports = map(lambda i: i.strip(), n.findall('import')) + self._make = n.find('make') + self._var_make = n.find('var_make') + self._checks = n.findall('check') + self._callbacks = n.findall('callback') + self._throttle = n.find('throttle') or '' + self._bus_structure_source = n.find('bus_structure_source') or '' + self._bus_structure_sink = n.find('bus_structure_sink') or '' + #build the block + _Block.__init__( + self, + flow_graph=flow_graph, + n=n, + ) + _GUIBlock.__init__(self) + + def get_bus_structure(self, direction): + if direction == 'source': + bus_structure = self._bus_structure_source; + else: + bus_structure = self._bus_structure_sink; + + bus_structure = self.resolve_dependencies(bus_structure); + + if not bus_structure: return '' + try: + clean_bus_structure = self.get_parent().evaluate(bus_structure) + return clean_bus_structure + + except: return '' + def throttle(self): return bool(self._throttle) + + def validate(self): + """ + Validate this block. + Call the base class validate. + Evaluate the checks: each check must evaluate to True. + """ + _Block.validate(self) + #evaluate the checks + for check in self._checks: + check_res = self.resolve_dependencies(check) + try: + if not self.get_parent().evaluate(check_res): + self.add_error_message('Check "%s" failed.'%check) + except: self.add_error_message('Check "%s" did not evaluate.'%check) + + def rewrite(self): + """ + Add and remove ports to adjust for the nports. + """ + _Block.rewrite(self) + + def rectify(ports): + #restore integer contiguity after insertion + #rectify the port names with the index + self.back_ofthe_bus(ports); + for i, port in enumerate(ports): + port._key = str(i) + port._name = port._n['name'] + if len(ports) > 1 and not port._type == 'bus': port._name += str(i) + + def insert_port(get_ports, get_port, key): + prev_port = get_port(str(int(key)-1)) + get_ports().insert( + get_ports().index(prev_port)+1, + prev_port.copy(new_key=key), + ) + rectify(get_ports()) + + def remove_port(get_ports, get_port, key): + port = get_port(key) + for connection in port.get_connections(): + self.get_parent().remove_element(connection) + get_ports().remove(port) + rectify(get_ports()) + + #adjust nports + for get_ports, get_port in ( + (self.get_sources, self.get_source), + (self.get_sinks, self.get_sink), + ): + master_ports = filter(lambda p: p.get_nports(), get_ports()) + for i, master_port in enumerate(master_ports): + nports = master_port.get_nports() + index_first = get_ports().index(master_port) + try: index_last = get_ports().index(master_ports[i+1]) + except IndexError: index_last = len(get_ports()) + num_ports = index_last - index_first + #do nothing if nports is already num ports + if nports == num_ports: continue + #remove excess ports and connections + if nports < num_ports: + for key in reversed(map(str, range(index_first+nports, index_first+num_ports))): + remove_port(get_ports, get_port, key); + continue + #add more ports + if nports > num_ports: + for key in map(str, range(index_first+num_ports, index_first+nports)): + insert_port(get_ports, get_port, key) + continue + + def port_controller_modify(self, direction): + """ + Change the port controller. + + Args: + direction: +1 or -1 + + Returns: + true for change + """ + changed = False + #concat the nports string from the private nports settings of all ports + nports_str = ' '.join([port._nports for port in self.get_ports()]) + #modify all params whose keys appear in the nports string + for param in self.get_params(): + if param.is_enum() or param.get_key() not in nports_str: continue + #try to increment the port controller by direction + try: + value = param.get_evaluated() + value = value + direction + if 0 < value: + param.set_value(value) + changed = True + except: pass + return changed + + def get_doc(self): + doc = self._doc.strip('\n').replace('\\\n', '') + #merge custom doc with doxygen docs + return '\n'.join([doc, extract_docs.extract(self.get_key())]).strip('\n') + + def get_category(self): + return _Block.get_category(self) + + def get_imports(self): + """ + Resolve all import statements. + Split each import statement at newlines. + Combine all import statments into a list. + Filter empty imports. + + Returns: + a list of import statements + """ + return filter(lambda i: i, sum(map(lambda i: self.resolve_dependencies(i).split('\n'), self._imports), [])) + + def get_make(self): return self.resolve_dependencies(self._make) + def get_var_make(self): return self.resolve_dependencies(self._var_make) + + def get_callbacks(self): + """ + Get a list of function callbacks for this block. + + Returns: + a list of strings + """ + def make_callback(callback): + callback = self.resolve_dependencies(callback) + if 'self.' in callback: return callback + return 'self.%s.%s'%(self.get_id(), callback) + return map(make_callback, self._callbacks) diff --git a/grc/python/Connection.py b/grc/python/Connection.py index 97bf61d590..7f235b190b 100644 --- a/grc/python/Connection.py +++ b/grc/python/Connection.py @@ -24,27 +24,26 @@ from .. gui.Connection import Connection as _GUIConnection class Connection(_Connection, _GUIConnection): - def __init__(self, **kwargs): - _Connection.__init__(self, **kwargs) - _GUIConnection.__init__(self) - - def is_msg(self): - return self.get_source().get_type() == self.get_sink().get_type() == 'msg' - - def is_message(self): - return self.get_source().get_type() == self.get_sink().get_type() == 'message' - - def is_bus(self): - return self.get_source().get_type() == self.get_sink().get_type() == 'bus' - def validate(self): - """ - Validate the connections. - The ports must match in io size. - """ - Element.validate(self) - source_size = Constants.TYPE_TO_SIZEOF[self.get_source().get_type()] * self.get_source().get_vlen() - sink_size = Constants.TYPE_TO_SIZEOF[self.get_sink().get_type()] * self.get_sink().get_vlen() - if source_size != sink_size: - self.add_error_message('Source IO size "%s" does not match sink IO size "%s".'%(source_size, sink_size)) - - + def __init__(self, **kwargs): + _Connection.__init__(self, **kwargs) + _GUIConnection.__init__(self) + + def is_msg(self): + return self.get_source().get_type() == self.get_sink().get_type() == 'msg' + + def is_message(self): + return self.get_source().get_type() == self.get_sink().get_type() == 'message' + + def is_bus(self): + return self.get_source().get_type() == self.get_sink().get_type() == 'bus' + + def validate(self): + """ + Validate the connections. + The ports must match in io size. + """ + Element.validate(self) + source_size = Constants.TYPE_TO_SIZEOF[self.get_source().get_type()] * self.get_source().get_vlen() + sink_size = Constants.TYPE_TO_SIZEOF[self.get_sink().get_type()] * self.get_sink().get_vlen() + if source_size != sink_size: + self.add_error_message('Source IO size "%s" does not match sink IO size "%s".'%(source_size, sink_size)) diff --git a/grc/python/Constants.py b/grc/python/Constants.py index 5666a2378b..15cc203b43 100644 --- a/grc/python/Constants.py +++ b/grc/python/Constants.py @@ -27,11 +27,11 @@ _gr_prefs = gr.prefs() PATH_SEP = {'/':':', '\\':';'}[os.path.sep] HIER_BLOCKS_LIB_DIR = os.path.join(os.path.expanduser('~'), '.grc_gnuradio') 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), + 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] #file creation modes @@ -45,40 +45,40 @@ BLOCK_DTD = os.path.join(DATA_DIR, 'block.dtd') DEFAULT_FLOW_GRAPH = os.path.join(DATA_DIR, 'default_flow_graph.grc') CORE_TYPES = ( #name, key, sizeof, color - ('Complex Float 64', 'fc64', 16, '#CC8C69'), - ('Complex Float 32', 'fc32', 8, '#3399FF'), - ('Complex Integer 64', 'sc64', 16, '#66CC00'), - ('Complex Integer 32', 'sc32', 8, '#33cc66'), - ('Complex Integer 16', 'sc16', 4, '#cccc00'), - ('Complex Integer 8', 'sc8', 2, '#cc00cc'), - ('Float 64', 'f64', 8, '#66CCCC'), - ('Float 32', 'f32', 4, '#FF8C69'), - ('Integer 64', 's64', 8, '#99FF33'), - ('Integer 32', 's32', 4, '#00FF99'), - ('Integer 16', 's16', 2, '#FFFF66'), - ('Integer 8', 's8', 1, '#FF66FF'), - ('Message Queue', 'msg', 0, '#777777'), - ('Async Message', 'message', 0, '#C0C0C0'), - ('Bus Connection', 'bus', 0, '#FFFFFF'), - ('Wildcard', '', 0, '#FFFFFF'), + ('Complex Float 64', 'fc64', 16, '#CC8C69'), + ('Complex Float 32', 'fc32', 8, '#3399FF'), + ('Complex Integer 64', 'sc64', 16, '#66CC00'), + ('Complex Integer 32', 'sc32', 8, '#33cc66'), + ('Complex Integer 16', 'sc16', 4, '#cccc00'), + ('Complex Integer 8', 'sc8', 2, '#cc00cc'), + ('Float 64', 'f64', 8, '#66CCCC'), + ('Float 32', 'f32', 4, '#FF8C69'), + ('Integer 64', 's64', 8, '#99FF33'), + ('Integer 32', 's32', 4, '#00FF99'), + ('Integer 16', 's16', 2, '#FFFF66'), + ('Integer 8', 's8', 1, '#FF66FF'), + ('Message Queue', 'msg', 0, '#777777'), + ('Async Message', 'message', 0, '#C0C0C0'), + ('Bus Connection', 'bus', 0, '#FFFFFF'), + ('Wildcard', '', 0, '#FFFFFF'), ) ALIAS_TYPES = { - 'complex' : (8, '#3399FF'), - 'float' : (4, '#FF8C69'), - 'int' : (4, '#00FF99'), - 'short' : (2, '#FFFF66'), - 'byte' : (1, '#FF66FF'), + 'complex' : (8, '#3399FF'), + 'float' : (4, '#FF8C69'), + 'int' : (4, '#00FF99'), + 'short' : (2, '#FFFF66'), + 'byte' : (1, '#FF66FF'), } TYPE_TO_COLOR = dict() TYPE_TO_SIZEOF = dict() for name, key, sizeof, color in CORE_TYPES: - TYPE_TO_COLOR[key] = color - TYPE_TO_SIZEOF[key] = sizeof + TYPE_TO_COLOR[key] = color + TYPE_TO_SIZEOF[key] = sizeof for key, (sizeof, color) in ALIAS_TYPES.iteritems(): - TYPE_TO_COLOR[key] = color - TYPE_TO_SIZEOF[key] = sizeof + TYPE_TO_COLOR[key] = color + TYPE_TO_SIZEOF[key] = sizeof #coloring COMPLEX_COLOR_SPEC = '#3399FF' diff --git a/grc/python/FlowGraph.py b/grc/python/FlowGraph.py index 89acfd89ef..22ed4f76d6 100644 --- a/grc/python/FlowGraph.py +++ b/grc/python/FlowGraph.py @@ -31,239 +31,192 @@ _bussrc_searcher = re.compile('^(bus_source)$') _bus_struct_sink_searcher = re.compile('^(bus_structure_sink)$') _bus_struct_src_searcher = re.compile('^(bus_structure_source)$') - class FlowGraph(_FlowGraph, _GUIFlowGraph): - def __init__(self, **kwargs): - _FlowGraph.__init__(self, **kwargs) - _GUIFlowGraph.__init__(self) - self._eval_cache = dict() - - def _eval(self, code, namespace, namespace_hash): - """ - Evaluate the code with the given namespace. - - Args: - code: a string with python code - namespace: a dict representing the namespace - namespace_hash: a unique hash for the namespace - - Returns: - the resultant object - """ - if not code: raise Exception, 'Cannot evaluate empty statement.' - my_hash = hash(code) ^ namespace_hash - #cache if does not exist - - if not self._eval_cache.has_key(my_hash): - self._eval_cache[my_hash] = eval(code, namespace, namespace) - #return from cache - return self._eval_cache[my_hash] - - def get_io_signaturev(self, direction): - """ - Get a list of io signatures for this flow graph. - - Args: - direction: a string of 'in' or 'out' - - Returns: - a list of dicts with: type, label, vlen, size - """ - sorted_pads = { - 'in': self.get_pad_sources(), - 'out': self.get_pad_sinks(), - }[direction] + def __init__(self, **kwargs): + _FlowGraph.__init__(self, **kwargs) + _GUIFlowGraph.__init__(self) + self._eval_cache = dict() + + def _eval(self, code, namespace, namespace_hash): + """ + Evaluate the code with the given namespace. + + Args: + code: a string with python code + namespace: a dict representing the namespace + namespace_hash: a unique hash for the namespace + + Returns: + the resultant object + """ + if not code: raise Exception, 'Cannot evaluate empty statement.' + my_hash = hash(code) ^ namespace_hash + #cache if does not exist + if not self._eval_cache.has_key(my_hash): + self._eval_cache[my_hash] = eval(code, namespace, namespace) + #return from cache + return self._eval_cache[my_hash] + + def get_io_signaturev(self, direction): + """ + Get a list of io signatures for this flow graph. + + Args: + direction: a string of 'in' or 'out' + + Returns: + a list of dicts with: type, label, vlen, size + """ + sorted_pads = { + 'in': self.get_pad_sources(), + 'out': self.get_pad_sinks(), + }[direction] # we only want stream ports - sorted_pads = filter(lambda b: b.get_param('type').get_evaluated() != 'message', sorted_pads); - expanded_pads = []; - for i in sorted_pads: - for j in range(i.get_param('num_streams').get_evaluated()): - expanded_pads.append(i); - #load io signature - return [{ - 'label': str(pad.get_param('label').get_evaluated()), - 'type': str(pad.get_param('type').get_evaluated()), - 'vlen': str(pad.get_param('vlen').get_evaluated()), - 'size': pad.get_param('type').get_opt('size'), - 'optional': bool(pad.get_param('optional').get_evaluated()), - } for pad in expanded_pads] - - def get_pad_sources(self): - """ - Get a list of pad source blocks sorted by id order. - - Returns: - a list of pad source blocks in this flow graph - """ - pads = filter(lambda b: b.get_key() == 'pad_source', self.get_enabled_blocks()) - return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) - - def get_pad_sinks(self): - """ - Get a list of pad sink blocks sorted by id order. - - Returns: - a list of pad sink blocks in this flow graph - """ - pads = filter(lambda b: b.get_key() == 'pad_sink', self.get_enabled_blocks()) - return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) - - def get_msg_pad_sources(self): - ps = self.get_pad_sources(); - return filter(lambda b: b.get_param('type').get_evaluated() == 'message', ps); - - def get_msg_pad_sinks(self): - ps = self.get_pad_sinks(); - return filter(lambda b: b.get_param('type').get_evaluated() == 'message', ps); - - def get_imports(self): - """ - Get a set of all import statments in this flow graph namespace. - - Returns: - a set of import statements - """ - imports = sum([block.get_imports() for block in self.get_enabled_blocks()], []) - imports = sorted(set(imports)) - return imports - - def get_variables(self): - """ - Get a list of all variables in this flow graph namespace. - Exclude paramterized variables. - - Returns: - a sorted list of variable blocks in order of dependency (indep -> dep) - """ - variables = filter(lambda b: _variable_matcher.match(b.get_key()), self.get_enabled_blocks()) - return expr_utils.sort_objects(variables, lambda v: v.get_id(), lambda v: v.get_var_make()) - - def get_parameters(self): - """ - Get a list of all paramterized variables in this flow graph namespace. - - Returns: - a list of paramterized variables - """ - parameters = filter(lambda b: _parameter_matcher.match(b.get_key()), self.get_enabled_blocks()) - return parameters - - def get_monitors(self): - """ - Get a list of all ControlPort monitors - """ - monitors = filter(lambda b: _monitors_searcher.search(b.get_key()), self.get_enabled_blocks()) - return monitors - - def get_bussink(self): - bussink = filter(lambda b: _bussink_searcher.search(b.get_key()), self.get_enabled_blocks()) - - for i in bussink: - for j in i.get_params(): - if j.get_name() == 'On/Off' and j.get_value() == 'on': - return True; - - return False - - - - def get_bussrc(self): - bussrc = filter(lambda b: _bussrc_searcher.search(b.get_key()), self.get_enabled_blocks()) - - for i in bussrc: - for j in i.get_params(): - if j.get_name() == 'On/Off' and j.get_value() == 'on': - return True; - - return False - - def get_bus_structure_sink(self): - bussink = filter(lambda b: _bus_struct_sink_searcher.search(b.get_key()), self.get_enabled_blocks()) - - return bussink - - def get_bus_structure_src(self): - bussrc = filter(lambda b: _bus_struct_src_searcher.search(b.get_key()), self.get_enabled_blocks()) - - return bussrc - - - def rewrite(self): - """ - Flag the namespace to be renewed. - """ - - - - - def reconnect_bus_blocks(): - for block in self.get_blocks(): - - if 'bus' in map(lambda a: a.get_type(), block.get_sources_gui()): - - - for i in range(len(block.get_sources_gui())): - if len(block.get_sources_gui()[i].get_connections()) > 0: - source = block.get_sources_gui()[i] - sink = [] - - for j in range(len(source.get_connections())): - sink.append(source.get_connections()[j].get_sink()); - - - for elt in source.get_connections(): - self.remove_element(elt); - for j in sink: - self.connect(source, j); - self._renew_eval_ns = True - _FlowGraph.rewrite(self); - reconnect_bus_blocks(); - - - - - - def evaluate(self, expr): - """ - Evaluate the expression. - - Args: - expr: the string expression - @throw Exception bad expression - - Returns: - the evaluated data - """ - - - if self._renew_eval_ns: - self._renew_eval_ns = False - #reload namespace - n = dict() - #load imports - for imp in self.get_imports(): - try: exec imp in n - except: pass - #load parameters - np = dict() - for parameter in self.get_parameters(): - try: - e = eval(parameter.get_param('value').to_code(), n, n) - np[parameter.get_id()] = e - except: pass - n.update(np) #merge param namespace - #load variables - for variable in self.get_variables(): - try: - e = eval(variable.get_param('value').to_code(), n, n) - n[variable.get_id()] = e - except: pass - #make namespace public - self.n = n - self.n_hash = hash(str(n)) - - #evaluate - e = self._eval(expr, self.n, self.n_hash) - - return e + sorted_pads = filter(lambda b: b.get_param('type').get_evaluated() != 'message', sorted_pads); + expanded_pads = []; + for i in sorted_pads: + for j in range(i.get_param('num_streams').get_evaluated()): + expanded_pads.append(i); + #load io signature + return [{ + 'label': str(pad.get_param('label').get_evaluated()), + 'type': str(pad.get_param('type').get_evaluated()), + 'vlen': str(pad.get_param('vlen').get_evaluated()), + 'size': pad.get_param('type').get_opt('size'), + 'optional': bool(pad.get_param('optional').get_evaluated()), + } for pad in expanded_pads] + + def get_pad_sources(self): + """ + Get a list of pad source blocks sorted by id order. + + Returns: + a list of pad source blocks in this flow graph + """ + pads = filter(lambda b: b.get_key() == 'pad_source', self.get_enabled_blocks()) + return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) + + def get_pad_sinks(self): + """ + Get a list of pad sink blocks sorted by id order. + + Returns: + a list of pad sink blocks in this flow graph + """ + pads = filter(lambda b: b.get_key() == 'pad_sink', self.get_enabled_blocks()) + return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) + + def get_msg_pad_sources(self): + ps = self.get_pad_sources(); + return filter(lambda b: b.get_param('type').get_evaluated() == 'message', ps); + + def get_msg_pad_sinks(self): + ps = self.get_pad_sinks(); + return filter(lambda b: b.get_param('type').get_evaluated() == 'message', ps); + + def get_imports(self): + """ + Get a set of all import statments in this flow graph namespace. + + Returns: + a set of import statements + """ + imports = sum([block.get_imports() for block in self.get_enabled_blocks()], []) + imports = sorted(set(imports)) + return imports + + def get_variables(self): + """ + Get a list of all variables in this flow graph namespace. + Exclude paramterized variables. + + Returns: + a sorted list of variable blocks in order of dependency (indep -> dep) + """ + variables = filter(lambda b: _variable_matcher.match(b.get_key()), self.get_enabled_blocks()) + return expr_utils.sort_objects(variables, lambda v: v.get_id(), lambda v: v.get_var_make()) + + def get_parameters(self): + """ + Get a list of all paramterized variables in this flow graph namespace. + + Returns: + a list of paramterized variables + """ + parameters = filter(lambda b: _parameter_matcher.match(b.get_key()), self.get_enabled_blocks()) + return parameters + + def get_monitors(self): + """ + Get a list of all ControlPort monitors + """ + monitors = filter(lambda b: _monitors_searcher.search(b.get_key()), self.get_enabled_blocks()) + return monitors + + def rewrite(self): + """ + Flag the namespace to be renewed. + """ + def reconnect_bus_blocks(): + for block in self.get_blocks(): + + if 'bus' in map(lambda a: a.get_type(), block.get_sources_gui()): + + + for i in range(len(block.get_sources_gui())): + if len(block.get_sources_gui()[i].get_connections()) > 0: + source = block.get_sources_gui()[i] + sink = [] + + for j in range(len(source.get_connections())): + sink.append(source.get_connections()[j].get_sink()); + + + for elt in source.get_connections(): + self.remove_element(elt); + for j in sink: + self.connect(source, j); + self._renew_eval_ns = True + _FlowGraph.rewrite(self); + reconnect_bus_blocks(); + + def evaluate(self, expr): + """ + Evaluate the expression. + + Args: + expr: the string expression + @throw Exception bad expression + + Returns: + the evaluated data + """ + if self._renew_eval_ns: + self._renew_eval_ns = False + #reload namespace + n = dict() + #load imports + for imp in self.get_imports(): + try: exec imp in n + except: pass + #load parameters + np = dict() + for parameter in self.get_parameters(): + try: + e = eval(parameter.get_param('value').to_code(), n, n) + np[parameter.get_id()] = e + except: pass + n.update(np) #merge param namespace + #load variables + for variable in self.get_variables(): + try: + e = eval(variable.get_param('value').to_code(), n, n) + n[variable.get_id()] = e + except: pass + #make namespace public + self.n = n + self.n_hash = hash(str(n)) + #evaluate + e = self._eval(expr, self.n, self.n_hash) + return e diff --git a/grc/python/Generator.py b/grc/python/Generator.py index 47fe1d98fd..005ed4c2b3 100644 --- a/grc/python/Generator.py +++ b/grc/python/Generator.py @@ -24,137 +24,137 @@ import tempfile from Cheetah.Template import Template import expr_utils from Constants import \ - TOP_BLOCK_FILE_MODE, HIER_BLOCK_FILE_MODE, \ - HIER_BLOCKS_LIB_DIR, FLOW_GRAPH_TEMPLATE + TOP_BLOCK_FILE_MODE, HIER_BLOCK_FILE_MODE, \ + HIER_BLOCKS_LIB_DIR, FLOW_GRAPH_TEMPLATE import convert_hier from .. gui import Messages class Generator(object): - def __init__(self, flow_graph, file_path): - """ - Initialize the generator object. - Determine the file to generate. - - Args: - flow_graph: the flow graph object - file_path: the path to write the file to - """ - self._flow_graph = flow_graph - self._generate_options = self._flow_graph.get_option('generate_options') - if self._generate_options == 'hb': - self._mode = HIER_BLOCK_FILE_MODE - dirname = HIER_BLOCKS_LIB_DIR - else: - self._mode = TOP_BLOCK_FILE_MODE - dirname = os.path.dirname(file_path) - #handle the case where the directory is read-only - #in this case, use the system's temp directory - 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) + def __init__(self, flow_graph, file_path): + """ + Initialize the generator object. + Determine the file to generate. + + Args: + flow_graph: the flow graph object + file_path: the path to write the file to + """ + self._flow_graph = flow_graph + self._generate_options = self._flow_graph.get_option('generate_options') + if self._generate_options == 'hb': + self._mode = HIER_BLOCK_FILE_MODE + dirname = HIER_BLOCKS_LIB_DIR + else: + self._mode = TOP_BLOCK_FILE_MODE + dirname = os.path.dirname(file_path) + #handle the case where the directory is read-only + #in this case, use the system's temp directory + 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) - def get_file_path(self): return self._file_path + def get_file_path(self): return self._file_path - def write(self): - #do throttle warning - throttled = any(map(lambda b: b.throttle(), self._flow_graph.get_enabled_blocks())) - if not throttled and self._generate_options != 'hb': - Messages.send_warning('''\ + def write(self): + #do throttle warning + throttled = any(map(lambda b: b.throttle(), self._flow_graph.get_enabled_blocks())) + if not throttled and self._generate_options != 'hb': + Messages.send_warning('''\ This flow graph may not have flow control: no audio or usrp blocks found. \ Add a Misc->Throttle block to your flow graph to avoid CPU congestion.''') - #generate - open(self.get_file_path(), 'w').write(str(self)) - if self._generate_options == 'hb': - #convert hier block to xml wrapper - convert_hier.convert_hier(self._flow_graph, self.get_file_path()) - os.chmod(self.get_file_path(), self._mode) + #generate + open(self.get_file_path(), 'w').write(str(self)) + if self._generate_options == 'hb': + #convert hier block to xml wrapper + convert_hier.convert_hier(self._flow_graph, self.get_file_path()) + os.chmod(self.get_file_path(), self._mode) - def get_popen(self): - """ - Execute this python flow graph. - - Returns: - a popen object - """ - #extract the path to the python executable - python_exe = sys.executable + def get_popen(self): + """ + Execute this python flow graph. + + 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' + #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' - #setup the command args to run - cmds = [python_exe, '-u', self.get_file_path()] #-u is unbuffered stdio + #setup the command args to run + cmds = [python_exe, '-u', self.get_file_path()] #-u is unbuffered stdio - #when in no gui mode on linux, use an xterm (looks nice) - if self._generate_options == 'no_gui' and 'linux' in sys.platform.lower(): - cmds = ['xterm', '-e'] + cmds + #when in no gui mode on linux, use an xterm (looks nice) + if self._generate_options == 'no_gui' and 'linux' in sys.platform.lower(): + cmds = ['xterm', '-e'] + cmds - p = subprocess.Popen(args=cmds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False, universal_newlines=True) - return p + p = subprocess.Popen(args=cmds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False, universal_newlines=True) + return p - def __str__(self): - """ - Convert the flow graph to python code. - - Returns: - a string of python code - """ - title = self._flow_graph.get_option('title') or self._flow_graph.get_option('id').replace('_', ' ').title() - imports = self._flow_graph.get_imports() - variables = self._flow_graph.get_variables() - parameters = self._flow_graph.get_parameters() - monitors = self._flow_graph.get_monitors() - #list of blocks not including variables and imports and parameters and disabled - def _get_block_sort_text(block): - code = block.get_make().replace(block.get_id(), ' ') - try: code += block.get_param('notebook').get_value() #older gui markup w/ wxgui - except: pass - try: code += block.get_param('gui_hint').get_value() #newer gui markup w/ qtgui - except: pass - return code - blocks = expr_utils.sort_objects( - self._flow_graph.get_enabled_blocks(), - lambda b: b.get_id(), _get_block_sort_text - ) - #list of regular blocks (all blocks minus the special ones) - blocks = filter(lambda b: b not in (imports + parameters), blocks) - #list of connections where each endpoint is enabled - connections = filter(lambda c: not (c.is_bus() or c.is_msg() or c.is_message()), self._flow_graph.get_enabled_connections()) - messages = filter(lambda c: c.is_msg(), self._flow_graph.get_enabled_connections()) - messages2 = filter(lambda c: c.is_message(), self._flow_graph.get_enabled_connections()) - #list of variable names - var_ids = [var.get_id() for var in parameters + variables] - #prepend self. - replace_dict = dict([(var_id, 'self.%s'%var_id) for var_id in var_ids]) - #list of callbacks - callbacks = [ - expr_utils.expr_replace(cb, replace_dict) - for cb in sum([block.get_callbacks() for block in self._flow_graph.get_enabled_blocks()], []) - ] - #map var id to callbacks - var_id2cbs = dict( - [(var_id, filter(lambda c: expr_utils.get_variable_dependencies(c, [var_id]), callbacks)) - for var_id in var_ids] - ) - #load the namespace - namespace = { - 'title': title, - 'imports': imports, - 'flow_graph': self._flow_graph, - 'variables': variables, - 'parameters': parameters, + def __str__(self): + """ + Convert the flow graph to python code. + + Returns: + a string of python code + """ + title = self._flow_graph.get_option('title') or self._flow_graph.get_option('id').replace('_', ' ').title() + imports = self._flow_graph.get_imports() + variables = self._flow_graph.get_variables() + parameters = self._flow_graph.get_parameters() + monitors = self._flow_graph.get_monitors() + #list of blocks not including variables and imports and parameters and disabled + def _get_block_sort_text(block): + code = block.get_make().replace(block.get_id(), ' ') + try: code += block.get_param('notebook').get_value() #older gui markup w/ wxgui + except: pass + try: code += block.get_param('gui_hint').get_value() #newer gui markup w/ qtgui + except: pass + return code + blocks = expr_utils.sort_objects( + self._flow_graph.get_enabled_blocks(), + lambda b: b.get_id(), _get_block_sort_text + ) + #list of regular blocks (all blocks minus the special ones) + blocks = filter(lambda b: b not in (imports + parameters), blocks) + #list of connections where each endpoint is enabled + connections = filter(lambda c: not (c.is_bus() or c.is_msg() or c.is_message()), self._flow_graph.get_enabled_connections()) + messages = filter(lambda c: c.is_msg(), self._flow_graph.get_enabled_connections()) + messages2 = filter(lambda c: c.is_message(), self._flow_graph.get_enabled_connections()) + #list of variable names + var_ids = [var.get_id() for var in parameters + variables] + #prepend self. + replace_dict = dict([(var_id, 'self.%s'%var_id) for var_id in var_ids]) + #list of callbacks + callbacks = [ + expr_utils.expr_replace(cb, replace_dict) + for cb in sum([block.get_callbacks() for block in self._flow_graph.get_enabled_blocks()], []) + ] + #map var id to callbacks + var_id2cbs = dict( + [(var_id, filter(lambda c: expr_utils.get_variable_dependencies(c, [var_id]), callbacks)) + for var_id in var_ids] + ) + #load the namespace + namespace = { + 'title': title, + 'imports': imports, + 'flow_graph': self._flow_graph, + 'variables': variables, + 'parameters': parameters, 'monitors': monitors, - 'blocks': blocks, - 'connections': connections, - 'messages': messages, - 'messages2': messages2, - 'generate_options': self._generate_options, - 'var_id2cbs': var_id2cbs, - } - #build the template - t = Template(open(FLOW_GRAPH_TEMPLATE, 'r').read(), namespace) - return str(t) + 'blocks': blocks, + 'connections': connections, + 'messages': messages, + 'messages2': messages2, + 'generate_options': self._generate_options, + 'var_id2cbs': var_id2cbs, + } + #build the template + t = Template(open(FLOW_GRAPH_TEMPLATE, 'r').read(), namespace) + return str(t) diff --git a/grc/python/Param.py b/grc/python/Param.py index 696f16cc94..e603d6cdbe 100644 --- a/grc/python/Param.py +++ b/grc/python/Param.py @@ -34,51 +34,51 @@ _check_id_matcher = re.compile('^[a-z|A-Z]\w*$') _show_id_matcher = re.compile('^(variable\w*|parameter|options|notebook)$') class FileParam(EntryParam): - """Provide an entry box for filename and a button to browse for a file.""" + """Provide an entry box for filename and a button to browse for a file.""" - def __init__(self, *args, **kwargs): - EntryParam.__init__(self, *args, **kwargs) - input = gtk.Button('...') - input.connect('clicked', self._handle_clicked) - self.pack_start(input, False) + def __init__(self, *args, **kwargs): + EntryParam.__init__(self, *args, **kwargs) + input = gtk.Button('...') + input.connect('clicked', self._handle_clicked) + self.pack_start(input, False) - def _handle_clicked(self, widget=None): - """ - If the button was clicked, open a file dialog in open/save format. - Replace the text in the entry with the new filename from the file dialog. - """ - #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 - #build the dialog - if self.param.get_type() == 'file_open': - file_dialog = gtk.FileChooserDialog('Open a Data File...', None, - gtk.FILE_CHOOSER_ACTION_OPEN, ('gtk-cancel',gtk.RESPONSE_CANCEL,'gtk-open',gtk.RESPONSE_OK)) - elif self.param.get_type() == 'file_save': - file_dialog = gtk.FileChooserDialog('Save a Data File...', None, - gtk.FILE_CHOOSER_ACTION_SAVE, ('gtk-cancel',gtk.RESPONSE_CANCEL, 'gtk-save',gtk.RESPONSE_OK)) - file_dialog.set_do_overwrite_confirmation(True) - file_dialog.set_current_name(basename) #show the current filename - file_dialog.set_current_folder(dirname) #current directory - file_dialog.set_select_multiple(False) - file_dialog.set_local_only(True) - if gtk.RESPONSE_OK == file_dialog.run(): #run the dialog - file_path = file_dialog.get_filename() #get the file path - self._input.set_text(file_path) - self._handle_changed() - file_dialog.destroy() #destroy the dialog + def _handle_clicked(self, widget=None): + """ + If the button was clicked, open a file dialog in open/save format. + Replace the text in the entry with the new filename from the file dialog. + """ + #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 + #build the dialog + if self.param.get_type() == 'file_open': + file_dialog = gtk.FileChooserDialog('Open a Data File...', None, + gtk.FILE_CHOOSER_ACTION_OPEN, ('gtk-cancel',gtk.RESPONSE_CANCEL,'gtk-open',gtk.RESPONSE_OK)) + elif self.param.get_type() == 'file_save': + file_dialog = gtk.FileChooserDialog('Save a Data File...', None, + gtk.FILE_CHOOSER_ACTION_SAVE, ('gtk-cancel',gtk.RESPONSE_CANCEL, 'gtk-save',gtk.RESPONSE_OK)) + file_dialog.set_do_overwrite_confirmation(True) + file_dialog.set_current_name(basename) #show the current filename + file_dialog.set_current_folder(dirname) #current directory + file_dialog.set_select_multiple(False) + file_dialog.set_local_only(True) + if gtk.RESPONSE_OK == file_dialog.run(): #run the dialog + file_path = file_dialog.get_filename() #get the file path + self._input.set_text(file_path) + self._handle_changed() + file_dialog.destroy() #destroy the dialog #blacklist certain ids, its not complete, but should help 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__) + 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] + 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) @@ -86,389 +86,389 @@ INT_TYPES = tuple(INT_TYPES) class Param(_Param, _GUIParam): - def __init__(self, **kwargs): - _Param.__init__(self, **kwargs) - _GUIParam.__init__(self) - self._init = False - self._hostage_cells = list() + def __init__(self, **kwargs): + _Param.__init__(self, **kwargs) + _GUIParam.__init__(self) + self._init = False + self._hostage_cells = list() - def get_types(self): return ( - 'raw', 'enum', - 'complex', 'real', 'float', 'int', - 'complex_vector', 'real_vector', 'float_vector', 'int_vector', - 'hex', 'string', 'bool', - 'file_open', 'file_save', - 'id', 'stream_id', - 'grid_pos', 'notebook', 'gui_hint', - 'import', - ) + def get_types(self): return ( + 'raw', 'enum', + 'complex', 'real', 'float', 'int', + 'complex_vector', 'real_vector', 'float_vector', 'int_vector', + 'hex', 'string', 'bool', + 'file_open', 'file_save', + 'id', 'stream_id', + 'grid_pos', 'notebook', 'gui_hint', + 'import', + ) - def __repr__(self): - """ - Get the repr (nice string format) for this param. - - Returns: - the string representation - """ - ################################################## - # truncate helper method - ################################################## - def _truncate(string, style=0): - max_len = max(27 - len(self.get_name()), 3) - if len(string) > max_len: - if style < 0: #front truncate - string = '...' + string[3-max_len:] - elif style == 0: #center truncate - string = string[:max_len/2 -3] + '...' + string[-max_len/2:] - elif style > 0: #rear truncate - string = string[:max_len-3] + '...' - return string - ################################################## - # simple conditions - ################################################## - 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 - ################################################## - truncate = 0 #default center truncate - e = self.get_evaluated() - t = self.get_type() - if isinstance(e, bool): return str(e) - elif isinstance(e, COMPLEX_TYPES): dt_str = num_to_str(e) - elif isinstance(e, VECTOR_TYPES): #vector types - if len(e) > 8: - dt_str = self.get_value() #large vectors use code - truncate = 1 - else: dt_str = ', '.join(map(num_to_str, e)) #small vectors use eval - elif t in ('file_open', 'file_save'): - dt_str = self.get_value() - truncate = -1 - else: dt_str = str(e) #other types - ################################################## - # done - ################################################## - return _truncate(dt_str, truncate) + def __repr__(self): + """ + Get the repr (nice string format) for this param. + + Returns: + the string representation + """ + ################################################## + # truncate helper method + ################################################## + def _truncate(string, style=0): + max_len = max(27 - len(self.get_name()), 3) + if len(string) > max_len: + if style < 0: #front truncate + string = '...' + string[3-max_len:] + elif style == 0: #center truncate + string = string[:max_len/2 -3] + '...' + string[-max_len/2:] + elif style > 0: #rear truncate + string = string[:max_len-3] + '...' + return string + ################################################## + # simple conditions + ################################################## + 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 + ################################################## + truncate = 0 #default center truncate + e = self.get_evaluated() + t = self.get_type() + if isinstance(e, bool): return str(e) + elif isinstance(e, COMPLEX_TYPES): dt_str = num_to_str(e) + elif isinstance(e, VECTOR_TYPES): #vector types + if len(e) > 8: + dt_str = self.get_value() #large vectors use code + truncate = 1 + else: dt_str = ', '.join(map(num_to_str, e)) #small vectors use eval + elif t in ('file_open', 'file_save'): + dt_str = self.get_value() + truncate = -1 + else: dt_str = str(e) #other types + ################################################## + # done + ################################################## + return _truncate(dt_str, truncate) - def get_input(self, *args, **kwargs): - if self.get_type() in ('file_open', 'file_save'): return FileParam(self, *args, **kwargs) - return _GUIParam.get_input(self, *args, **kwargs) + def get_input(self, *args, **kwargs): + if self.get_type() in ('file_open', 'file_save'): return FileParam(self, *args, **kwargs) + return _GUIParam.get_input(self, *args, **kwargs) - def get_color(self): - """ - Get the color that represents this param's type. - - Returns: - a hex color code. - """ - try: - return { - #number types - 'complex': Constants.COMPLEX_COLOR_SPEC, - 'real': Constants.FLOAT_COLOR_SPEC, - 'float': Constants.FLOAT_COLOR_SPEC, - 'int': Constants.INT_COLOR_SPEC, - #vector types - 'complex_vector': Constants.COMPLEX_VECTOR_COLOR_SPEC, - 'real_vector': Constants.FLOAT_VECTOR_COLOR_SPEC, + def get_color(self): + """ + Get the color that represents this param's type. + + Returns: + a hex color code. + """ + try: + return { + #number types + 'complex': Constants.COMPLEX_COLOR_SPEC, + 'real': Constants.FLOAT_COLOR_SPEC, + 'float': Constants.FLOAT_COLOR_SPEC, + 'int': Constants.INT_COLOR_SPEC, + #vector types + 'complex_vector': Constants.COMPLEX_VECTOR_COLOR_SPEC, + 'real_vector': Constants.FLOAT_VECTOR_COLOR_SPEC, 'float_vector': Constants.FLOAT_VECTOR_COLOR_SPEC, - 'int_vector': Constants.INT_VECTOR_COLOR_SPEC, - #special - 'bool': Constants.INT_COLOR_SPEC, - 'hex': Constants.INT_COLOR_SPEC, - 'string': Constants.BYTE_VECTOR_COLOR_SPEC, - 'id': Constants.ID_COLOR_SPEC, - 'stream_id': Constants.ID_COLOR_SPEC, - 'grid_pos': Constants.INT_VECTOR_COLOR_SPEC, - 'notebook': Constants.INT_VECTOR_COLOR_SPEC, - 'raw': Constants.WILDCARD_COLOR_SPEC, - }[self.get_type()] - except: return _Param.get_color(self) + 'int_vector': Constants.INT_VECTOR_COLOR_SPEC, + #special + 'bool': Constants.INT_COLOR_SPEC, + 'hex': Constants.INT_COLOR_SPEC, + 'string': Constants.BYTE_VECTOR_COLOR_SPEC, + 'id': Constants.ID_COLOR_SPEC, + 'stream_id': Constants.ID_COLOR_SPEC, + 'grid_pos': Constants.INT_VECTOR_COLOR_SPEC, + 'notebook': Constants.INT_VECTOR_COLOR_SPEC, + 'raw': Constants.WILDCARD_COLOR_SPEC, + }[self.get_type()] + except: return _Param.get_color(self) - def get_hide(self): - """ - Get the hide value from the base class. - Hide the ID parameter for most blocks. Exceptions below. - If the parameter controls a port type, vlen, or nports, return part. - If the parameter is an empty grid position, return part. - These parameters are redundant to display in the flow graph view. - - Returns: - hide the hide property string - """ - hide = _Param.get_hide(self) - if hide: return hide - #hide ID in non variable blocks - if self.get_key() == 'id' and not _show_id_matcher.match(self.get_parent().get_key()): return 'part' - #hide port controllers for type and nports - if self.get_key() in ' '.join(map( - lambda p: ' '.join([p._type, p._nports]), self.get_parent().get_ports()) - ): return 'part' - #hide port controllers for vlen, when == 1 - if self.get_key() in ' '.join(map( - lambda p: p._vlen, self.get_parent().get_ports()) - ): - try: - if int(self.get_evaluated()) == 1: return 'part' - except: pass - #hide empty grid positions - if self.get_key() in ('grid_pos', 'notebook') and not self.get_value(): return 'part' - return hide + def get_hide(self): + """ + Get the hide value from the base class. + Hide the ID parameter for most blocks. Exceptions below. + If the parameter controls a port type, vlen, or nports, return part. + If the parameter is an empty grid position, return part. + These parameters are redundant to display in the flow graph view. + + Returns: + hide the hide property string + """ + hide = _Param.get_hide(self) + if hide: return hide + #hide ID in non variable blocks + if self.get_key() == 'id' and not _show_id_matcher.match(self.get_parent().get_key()): return 'part' + #hide port controllers for type and nports + if self.get_key() in ' '.join(map( + lambda p: ' '.join([p._type, p._nports]), self.get_parent().get_ports()) + ): return 'part' + #hide port controllers for vlen, when == 1 + if self.get_key() in ' '.join(map( + lambda p: p._vlen, self.get_parent().get_ports()) + ): + try: + if int(self.get_evaluated()) == 1: return 'part' + except: pass + #hide empty grid positions + if self.get_key() in ('grid_pos', 'notebook') and not self.get_value(): return 'part' + return hide - def validate(self): - """ - Validate the param. - A test evaluation is performed - """ - _Param.validate(self) #checks type - self._evaluated = None - try: self._evaluated = self.evaluate() - except Exception, e: self.add_error_message(str(e)) + def validate(self): + """ + Validate the param. + A test evaluation is performed + """ + _Param.validate(self) #checks type + self._evaluated = None + try: self._evaluated = self.evaluate() + except Exception, e: self.add_error_message(str(e)) - def get_evaluated(self): return self._evaluated + def get_evaluated(self): return self._evaluated - def evaluate(self): - """ - Evaluate the value. - - Returns: - evaluated type - """ - self._init = True - self._lisitify_flag = False - self._stringify_flag = False - self._hostage_cells = list() - def eval_string(v): - try: - e = self.get_parent().get_parent().evaluate(v) - if isinstance(e, str): return e - raise Exception #want to stringify - except: - self._stringify_flag = True - return v - t = self.get_type() - v = self.get_value() - ######################### - # Enum Type - ######################### - if self.is_enum(): return v - ######################### - # Numeric Types - ######################### - elif t in ('raw', 'complex', 'real', 'float', 'int', 'hex', 'bool'): - #raise exception if python cannot evaluate this value - try: e = self.get_parent().get_parent().evaluate(v) - except Exception, e: raise Exception, 'Value "%s" cannot be evaluated:\n%s'%(v, e) - #raise an exception if the data is invalid - if t == 'raw': return e - elif t == 'complex': - if not isinstance(e, COMPLEX_TYPES): - raise Exception, 'Expression "%s" is invalid for type complex.'%str(e) - return e - elif t == 'real' or t == 'float': - if not isinstance(e, REAL_TYPES): - raise Exception, 'Expression "%s" is invalid for type float.'%str(e) - return e - elif t == 'int': - if not isinstance(e, INT_TYPES): - raise Exception, 'Expression "%s" is invalid for type integer.'%str(e) - return e - elif t == 'hex': return hex(e) - elif t == 'bool': - if not isinstance(e, bool): - raise Exception, 'Expression "%s" is invalid for type bool.'%str(e) - return e - else: raise TypeError, 'Type "%s" not handled'%t - ######################### - # Numeric Vector Types - ######################### - elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'): - if not v: v = '()' #turn a blank string into an empty list, so it will eval - #raise exception if python cannot evaluate this value - try: e = self.get_parent().get_parent().evaluate(v) - except Exception, e: raise Exception, 'Value "%s" cannot be evaluated:\n%s'%(v, e) - #raise an exception if the data is invalid - if t == 'complex_vector': - if not isinstance(e, VECTOR_TYPES): - self._lisitify_flag = True - e = [e] - if not all([isinstance(ei, COMPLEX_TYPES) for ei in e]): - raise Exception, 'Expression "%s" is invalid for type complex vector.'%str(e) - return e - elif t == 'real_vector' or t == 'float_vector': - if not isinstance(e, VECTOR_TYPES): - self._lisitify_flag = True - e = [e] - if not all([isinstance(ei, REAL_TYPES) for ei in e]): - raise Exception, 'Expression "%s" is invalid for type float vector.'%str(e) - return e - elif t == 'int_vector': - if not isinstance(e, VECTOR_TYPES): - self._lisitify_flag = True - e = [e] - if not all([isinstance(ei, INT_TYPES) for ei in e]): - raise Exception, 'Expression "%s" is invalid for type integer vector.'%str(e) - return e - ######################### - # String Types - ######################### - elif t in ('string', 'file_open', 'file_save'): - #do not check if file/directory exists, that is a runtime issue - e = eval_string(v) - return str(e) - ######################### - # Unique ID Type - ######################### - elif t == 'id': - #can python use this as a variable? - if not _check_id_matcher.match(v): - raise Exception, 'ID "%s" must begin with a letter and may contain letters, numbers, and underscores.'%v - ids = [param.get_value() for param in self.get_all_params(t)] - if ids.count(v) > 1: #id should only appear once, or zero times if block is disabled - raise Exception, 'ID "%s" is not unique.'%v - if v in ID_BLACKLIST: - raise Exception, 'ID "%s" is blacklisted.'%v - return v - ######################### - # Stream ID Type - ######################### - elif t == 'stream_id': - #get a list of all stream ids used in the virtual sinks - ids = [param.get_value() for param in filter( - lambda p: p.get_parent().is_virtual_sink(), - self.get_all_params(t), - )] - #check that the virtual sink's stream id is unique - if self.get_parent().is_virtual_sink(): - if ids.count(v) > 1: #id should only appear once, or zero times if block is disabled - raise Exception, 'Stream ID "%s" is not unique.'%v - #check that the virtual source's steam id is found - if self.get_parent().is_virtual_source(): - if v not in ids: - raise Exception, 'Stream ID "%s" is not found.'%v - return v - ######################### - # GUI Position/Hint - ######################### - elif t == 'gui_hint': - if ':' in v: tab, pos = v.split(':') - elif '@' in v: tab, pos = v, '' - else: tab, pos = '', v + def evaluate(self): + """ + Evaluate the value. + + Returns: + evaluated type + """ + self._init = True + self._lisitify_flag = False + self._stringify_flag = False + self._hostage_cells = list() + def eval_string(v): + try: + e = self.get_parent().get_parent().evaluate(v) + if isinstance(e, str): return e + raise Exception #want to stringify + except: + self._stringify_flag = True + return v + t = self.get_type() + v = self.get_value() + ######################### + # Enum Type + ######################### + if self.is_enum(): return v + ######################### + # Numeric Types + ######################### + elif t in ('raw', 'complex', 'real', 'float', 'int', 'hex', 'bool'): + #raise exception if python cannot evaluate this value + try: e = self.get_parent().get_parent().evaluate(v) + except Exception, e: raise Exception, 'Value "%s" cannot be evaluated:\n%s'%(v, e) + #raise an exception if the data is invalid + if t == 'raw': return e + elif t == 'complex': + if not isinstance(e, COMPLEX_TYPES): + raise Exception, 'Expression "%s" is invalid for type complex.'%str(e) + return e + elif t == 'real' or t == 'float': + if not isinstance(e, REAL_TYPES): + raise Exception, 'Expression "%s" is invalid for type float.'%str(e) + return e + elif t == 'int': + if not isinstance(e, INT_TYPES): + raise Exception, 'Expression "%s" is invalid for type integer.'%str(e) + return e + elif t == 'hex': return hex(e) + elif t == 'bool': + if not isinstance(e, bool): + raise Exception, 'Expression "%s" is invalid for type bool.'%str(e) + return e + else: raise TypeError, 'Type "%s" not handled'%t + ######################### + # Numeric Vector Types + ######################### + elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'): + if not v: v = '()' #turn a blank string into an empty list, so it will eval + #raise exception if python cannot evaluate this value + try: e = self.get_parent().get_parent().evaluate(v) + except Exception, e: raise Exception, 'Value "%s" cannot be evaluated:\n%s'%(v, e) + #raise an exception if the data is invalid + if t == 'complex_vector': + if not isinstance(e, VECTOR_TYPES): + self._lisitify_flag = True + e = [e] + if not all([isinstance(ei, COMPLEX_TYPES) for ei in e]): + raise Exception, 'Expression "%s" is invalid for type complex vector.'%str(e) + return e + elif t == 'real_vector' or t == 'float_vector': + if not isinstance(e, VECTOR_TYPES): + self._lisitify_flag = True + e = [e] + if not all([isinstance(ei, REAL_TYPES) for ei in e]): + raise Exception, 'Expression "%s" is invalid for type float vector.'%str(e) + return e + elif t == 'int_vector': + if not isinstance(e, VECTOR_TYPES): + self._lisitify_flag = True + e = [e] + if not all([isinstance(ei, INT_TYPES) for ei in e]): + raise Exception, 'Expression "%s" is invalid for type integer vector.'%str(e) + return e + ######################### + # String Types + ######################### + elif t in ('string', 'file_open', 'file_save'): + #do not check if file/directory exists, that is a runtime issue + e = eval_string(v) + return str(e) + ######################### + # Unique ID Type + ######################### + elif t == 'id': + #can python use this as a variable? + if not _check_id_matcher.match(v): + raise Exception, 'ID "%s" must begin with a letter and may contain letters, numbers, and underscores.'%v + ids = [param.get_value() for param in self.get_all_params(t)] + if ids.count(v) > 1: #id should only appear once, or zero times if block is disabled + raise Exception, 'ID "%s" is not unique.'%v + if v in ID_BLACKLIST: + raise Exception, 'ID "%s" is blacklisted.'%v + return v + ######################### + # Stream ID Type + ######################### + elif t == 'stream_id': + #get a list of all stream ids used in the virtual sinks + ids = [param.get_value() for param in filter( + lambda p: p.get_parent().is_virtual_sink(), + self.get_all_params(t), + )] + #check that the virtual sink's stream id is unique + if self.get_parent().is_virtual_sink(): + if ids.count(v) > 1: #id should only appear once, or zero times if block is disabled + raise Exception, 'Stream ID "%s" is not unique.'%v + #check that the virtual source's steam id is found + if self.get_parent().is_virtual_source(): + if v not in ids: + raise Exception, 'Stream ID "%s" is not found.'%v + return v + ######################### + # GUI Position/Hint + ######################### + elif t == 'gui_hint': + if ':' in v: tab, pos = v.split(':') + elif '@' in v: tab, pos = v, '' + else: tab, pos = '', v - if '@' in tab: tab, index = tab.split('@') - else: index = '?' + if '@' in tab: tab, index = tab.split('@') + else: index = '?' - widget_str = ({ - (True, True): 'self.%(tab)s_grid_layout_%(index)s.addWidget(%(widget)s, %(pos)s)', - (True, False): 'self.%(tab)s_layout_%(index)s.addWidget(%(widget)s)', - (False, True): 'self.top_grid_layout.addWidget(%(widget)s, %(pos)s)', - (False, False): 'self.top_layout.addWidget(%(widget)s)', - }[bool(tab), bool(pos)])%{'tab': tab, 'index': index, 'widget': '%s', 'pos': pos} + widget_str = ({ + (True, True): 'self.%(tab)s_grid_layout_%(index)s.addWidget(%(widget)s, %(pos)s)', + (True, False): 'self.%(tab)s_layout_%(index)s.addWidget(%(widget)s)', + (False, True): 'self.top_grid_layout.addWidget(%(widget)s, %(pos)s)', + (False, False): 'self.top_layout.addWidget(%(widget)s)', + }[bool(tab), bool(pos)])%{'tab': tab, 'index': index, 'widget': '%s', 'pos': pos} - def gui_hint(ws, w): - if 'layout' in w: ws = ws.replace('addWidget', 'addLayout') - return ws%w + def gui_hint(ws, w): + if 'layout' in w: ws = ws.replace('addWidget', 'addLayout') + return ws%w - return lambda w: gui_hint(widget_str, w) - ######################### - # Grid Position Type - ######################### - elif t == 'grid_pos': - if not v: return '' #allow for empty grid pos - e = self.get_parent().get_parent().evaluate(v) - if not isinstance(e, (list, tuple)) or len(e) != 4 or not all([isinstance(ei, int) for ei in e]): - raise Exception, 'A grid position must be a list of 4 integers.' - row, col, row_span, col_span = e - #check row, col - if row < 0 or col < 0: - raise Exception, 'Row and column must be non-negative.' - #check row span, col span - if row_span <= 0 or col_span <= 0: - raise Exception, 'Row and column span must be greater than zero.' - #get hostage cell parent - try: my_parent = self.get_parent().get_param('notebook').evaluate() - except: my_parent = '' - #calculate hostage cells - for r in range(row_span): - for c in range(col_span): - self._hostage_cells.append((my_parent, (row+r, col+c))) - #avoid collisions - params = filter(lambda p: p is not self, self.get_all_params('grid_pos')) - for param in params: - for parent, cell in param._hostage_cells: - if (parent, cell) in self._hostage_cells: - raise Exception, 'Another graphical element is using parent "%s", cell "%s".'%(str(parent), str(cell)) - return e - ######################### - # Notebook Page Type - ######################### - elif t == 'notebook': - if not v: return '' #allow for empty notebook - #get a list of all notebooks - notebook_blocks = filter(lambda b: b.get_key() == 'notebook', self.get_parent().get_parent().get_enabled_blocks()) - #check for notebook param syntax - try: notebook_id, page_index = map(str.strip, v.split(',')) - except: raise Exception, 'Bad notebook page format.' - #check that the notebook id is valid - try: notebook_block = filter(lambda b: b.get_id() == notebook_id, notebook_blocks)[0] - except: raise Exception, 'Notebook id "%s" is not an existing notebook id.'%notebook_id - #check that page index exists - if int(page_index) not in range(len(notebook_block.get_param('labels').evaluate())): - raise Exception, 'Page index "%s" is not a valid index number.'%page_index - return notebook_id, page_index - ######################### - # Import Type - ######################### - elif t == 'import': - n = dict() #new namespace - try: exec v in n - except ImportError: raise Exception, 'Import "%s" failed.'%v - except Exception: raise Exception, 'Bad import syntax: "%s".'%v - return filter(lambda k: str(k) != '__builtins__', n.keys()) - ######################### - else: raise TypeError, 'Type "%s" not handled'%t + return lambda w: gui_hint(widget_str, w) + ######################### + # Grid Position Type + ######################### + elif t == 'grid_pos': + if not v: return '' #allow for empty grid pos + e = self.get_parent().get_parent().evaluate(v) + if not isinstance(e, (list, tuple)) or len(e) != 4 or not all([isinstance(ei, int) for ei in e]): + raise Exception, 'A grid position must be a list of 4 integers.' + row, col, row_span, col_span = e + #check row, col + if row < 0 or col < 0: + raise Exception, 'Row and column must be non-negative.' + #check row span, col span + if row_span <= 0 or col_span <= 0: + raise Exception, 'Row and column span must be greater than zero.' + #get hostage cell parent + try: my_parent = self.get_parent().get_param('notebook').evaluate() + except: my_parent = '' + #calculate hostage cells + for r in range(row_span): + for c in range(col_span): + self._hostage_cells.append((my_parent, (row+r, col+c))) + #avoid collisions + params = filter(lambda p: p is not self, self.get_all_params('grid_pos')) + for param in params: + for parent, cell in param._hostage_cells: + if (parent, cell) in self._hostage_cells: + raise Exception, 'Another graphical element is using parent "%s", cell "%s".'%(str(parent), str(cell)) + return e + ######################### + # Notebook Page Type + ######################### + elif t == 'notebook': + if not v: return '' #allow for empty notebook + #get a list of all notebooks + notebook_blocks = filter(lambda b: b.get_key() == 'notebook', self.get_parent().get_parent().get_enabled_blocks()) + #check for notebook param syntax + try: notebook_id, page_index = map(str.strip, v.split(',')) + except: raise Exception, 'Bad notebook page format.' + #check that the notebook id is valid + try: notebook_block = filter(lambda b: b.get_id() == notebook_id, notebook_blocks)[0] + except: raise Exception, 'Notebook id "%s" is not an existing notebook id.'%notebook_id + #check that page index exists + if int(page_index) not in range(len(notebook_block.get_param('labels').evaluate())): + raise Exception, 'Page index "%s" is not a valid index number.'%page_index + return notebook_id, page_index + ######################### + # Import Type + ######################### + elif t == 'import': + n = dict() #new namespace + try: exec v in n + except ImportError: raise Exception, 'Import "%s" failed.'%v + except Exception: raise Exception, 'Bad import syntax: "%s".'%v + return filter(lambda k: str(k) != '__builtins__', n.keys()) + ######################### + else: raise TypeError, 'Type "%s" not handled'%t - def to_code(self): - """ - Convert the value to code. - For string and list types, check the init flag, call evaluate(). - This ensures that evaluate() was called to set the xxxify_flags. - - Returns: - a string representing the code - """ - v = self.get_value() - t = self.get_type() - if t in ('string', 'file_open', 'file_save'): #string types - if not self._init: self.evaluate() - if self._stringify_flag: return '"%s"'%v.replace('"', '\"') - else: return v - elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'): #vector types - if not self._init: self.evaluate() - if self._lisitify_flag: return '(%s, )'%v - else: return '(%s)'%v - else: return v + def to_code(self): + """ + Convert the value to code. + For string and list types, check the init flag, call evaluate(). + This ensures that evaluate() was called to set the xxxify_flags. + + Returns: + a string representing the code + """ + v = self.get_value() + t = self.get_type() + if t in ('string', 'file_open', 'file_save'): #string types + if not self._init: self.evaluate() + if self._stringify_flag: return '"%s"'%v.replace('"', '\"') + else: return v + elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'): #vector types + if not self._init: self.evaluate() + if self._lisitify_flag: return '(%s, )'%v + else: return '(%s)'%v + else: return v - def get_all_params(self, type): - """ - Get all the params from the flowgraph that have the given type. - - Args: - type: the specified type - - Returns: - a list of params - """ - return sum([filter(lambda p: p.get_type() == type, block.get_params()) for block in self.get_parent().get_parent().get_enabled_blocks()], []) + def get_all_params(self, type): + """ + Get all the params from the flowgraph that have the given type. + + Args: + type: the specified type + + Returns: + a list of params + """ + return sum([filter(lambda p: p.get_type() == type, block.get_params()) for block in self.get_parent().get_parent().get_enabled_blocks()], []) diff --git a/grc/python/Platform.py b/grc/python/Platform.py index e036361ff0..f6adaf47a5 100644 --- a/grc/python/Platform.py +++ b/grc/python/Platform.py @@ -28,43 +28,43 @@ from Port import Port as _Port from Param import Param as _Param from Generator import Generator from Constants import \ - HIER_BLOCKS_LIB_DIR, BLOCK_DTD, \ - DEFAULT_FLOW_GRAPH, BLOCKS_DIRS + HIER_BLOCKS_LIB_DIR, BLOCK_DTD, \ + DEFAULT_FLOW_GRAPH, BLOCKS_DIRS import Constants COLORS = [(name, color) for name, key, sizeof, color in Constants.CORE_TYPES] class Platform(_Platform, _GUIPlatform): - def __init__(self): - """ - Make a platform for gnuradio. - """ - #ensure hier dir - if not os.path.exists(HIER_BLOCKS_LIB_DIR): os.mkdir(HIER_BLOCKS_LIB_DIR) - #convert block paths to absolute paths - block_paths = set(map(os.path.abspath, BLOCKS_DIRS)) - #init - _Platform.__init__( - self, - name='GNU Radio Companion', - version=gr.version(), - key='grc', - license=__doc__.strip(), - website='http://gnuradio.org/redmine/wiki/gnuradio/GNURadioCompanion', - block_paths=block_paths, - block_dtd=BLOCK_DTD, - default_flow_graph=DEFAULT_FLOW_GRAPH, - generator=Generator, - colors=COLORS, - ) - _GUIPlatform.__init__(self) + def __init__(self): + """ + Make a platform for gnuradio. + """ + #ensure hier dir + if not os.path.exists(HIER_BLOCKS_LIB_DIR): os.mkdir(HIER_BLOCKS_LIB_DIR) + #convert block paths to absolute paths + block_paths = set(map(os.path.abspath, BLOCKS_DIRS)) + #init + _Platform.__init__( + self, + name='GNU Radio Companion', + version=gr.version(), + key='grc', + license=__doc__.strip(), + website='http://gnuradio.org/redmine/wiki/gnuradio/GNURadioCompanion', + block_paths=block_paths, + block_dtd=BLOCK_DTD, + default_flow_graph=DEFAULT_FLOW_GRAPH, + generator=Generator, + colors=COLORS, + ) + _GUIPlatform.__init__(self) - ############################################## - # Constructors - ############################################## - FlowGraph = _FlowGraph - Connection = _Connection - Block = _Block - Port = _Port - Param = _Param + ############################################## + # Constructors + ############################################## + FlowGraph = _FlowGraph + Connection = _Connection + Block = _Block + Port = _Port + Param = _Param diff --git a/grc/python/Port.py b/grc/python/Port.py index 8ebf5c7b05..247dbed3e6 100644 --- a/grc/python/Port.py +++ b/grc/python/Port.py @@ -22,209 +22,203 @@ from .. gui.Port import Port as _GUIPort import Constants def _get_source_from_virtual_sink_port(vsp): - """ - Resolve the source port that is connected to the given virtual sink port. - Use the get source from virtual source to recursively resolve subsequent ports. - """ - try: return _get_source_from_virtual_source_port( - vsp.get_enabled_connections()[0].get_source()) - except: raise Exception, 'Could not resolve source for virtual sink port %s'%vsp + """ + Resolve the source port that is connected to the given virtual sink port. + Use the get source from virtual source to recursively resolve subsequent ports. + """ + try: return _get_source_from_virtual_source_port( + vsp.get_enabled_connections()[0].get_source()) + except: raise Exception, 'Could not resolve source for virtual sink port %s'%vsp def _get_source_from_virtual_source_port(vsp, traversed=[]): - """ - Recursively resolve source ports over the virtual connections. - Keep track of traversed sources to avoid recursive loops. - """ - if not vsp.get_parent().is_virtual_source(): return vsp - if vsp in traversed: raise Exception, 'Loop found when resolving virtual source %s'%vsp - try: return _get_source_from_virtual_source_port( - _get_source_from_virtual_sink_port( - filter(#get all virtual sinks with a matching stream id - lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(), - filter(#get all enabled blocks that are also virtual sinks - lambda b: b.is_virtual_sink(), - vsp.get_parent().get_parent().get_enabled_blocks(), - ), - )[0].get_sinks()[0] - ), traversed + [vsp], - ) - except: raise Exception, 'Could not resolve source for virtual source port %s'%vsp + """ + Recursively resolve source ports over the virtual connections. + Keep track of traversed sources to avoid recursive loops. + """ + if not vsp.get_parent().is_virtual_source(): return vsp + if vsp in traversed: raise Exception, 'Loop found when resolving virtual source %s'%vsp + try: return _get_source_from_virtual_source_port( + _get_source_from_virtual_sink_port( + filter(#get all virtual sinks with a matching stream id + lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(), + filter(#get all enabled blocks that are also virtual sinks + lambda b: b.is_virtual_sink(), + vsp.get_parent().get_parent().get_enabled_blocks(), + ), + )[0].get_sinks()[0] + ), traversed + [vsp], + ) + except: raise Exception, 'Could not resolve source for virtual source port %s'%vsp def _get_sink_from_virtual_source_port(vsp): - """ - Resolve the sink port that is connected to the given virtual source port. - Use the get sink from virtual sink to recursively resolve subsequent ports. - """ - try: return _get_sink_from_virtual_sink_port( - vsp.get_enabled_connections()[0].get_sink()) # Could have many connections, but use first - except: raise Exception, 'Could not resolve source for virtual source port %s'%vsp + """ + Resolve the sink port that is connected to the given virtual source port. + Use the get sink from virtual sink to recursively resolve subsequent ports. + """ + try: return _get_sink_from_virtual_sink_port( + vsp.get_enabled_connections()[0].get_sink()) # Could have many connections, but use first + except: raise Exception, 'Could not resolve source for virtual source port %s'%vsp def _get_sink_from_virtual_sink_port(vsp, traversed=[]): - """ - Recursively resolve sink ports over the virtual connections. - Keep track of traversed sinks to avoid recursive loops. - """ - if not vsp.get_parent().is_virtual_sink(): return vsp - if vsp in traversed: raise Exception, 'Loop found when resolving virtual sink %s'%vsp - try: return _get_sink_from_virtual_sink_port( - _get_sink_from_virtual_source_port( - filter(#get all virtual source with a matching stream id - lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(), - filter(#get all enabled blocks that are also virtual sinks - lambda b: b.is_virtual_source(), - vsp.get_parent().get_parent().get_enabled_blocks(), - ), - )[0].get_sources()[0] - ), traversed + [vsp], - ) - except: raise Exception, 'Could not resolve source for virtual sink port %s'%vsp + """ + Recursively resolve sink ports over the virtual connections. + Keep track of traversed sinks to avoid recursive loops. + """ + if not vsp.get_parent().is_virtual_sink(): return vsp + if vsp in traversed: raise Exception, 'Loop found when resolving virtual sink %s'%vsp + try: return _get_sink_from_virtual_sink_port( + _get_sink_from_virtual_source_port( + filter(#get all virtual source with a matching stream id + lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(), + filter(#get all enabled blocks that are also virtual sinks + lambda b: b.is_virtual_source(), + vsp.get_parent().get_parent().get_enabled_blocks(), + ), + )[0].get_sources()[0] + ), traversed + [vsp], + ) + except: raise Exception, 'Could not resolve source for virtual sink port %s'%vsp class Port(_Port, _GUIPort): - def __init__(self, block, n, dir): - """ - Make a new port from nested data. - - Args: - block: the parent element - n: the nested odict - dir: the direction - """ - self._n = n - if n['type'] == 'msg': n['key'] = 'msg' - if n['type'] == 'message': n['key'] = n['name'] - if dir == 'source' and not n.find('key'): - n['key'] = str(block._source_count) - block._source_count += 1 - if dir == 'sink' and not n.find('key'): - n['key'] = str(block._sink_count) - block._sink_count += 1 - #build the port - _Port.__init__( - self, - block=block, - n=n, - dir=dir, - ) - _GUIPort.__init__(self) - self._nports = n.find('nports') or '' - self._vlen = n.find('vlen') or '' - self._optional = bool(n.find('optional')) - - - def get_types(self): return Constants.TYPE_TO_SIZEOF.keys() - - def is_type_empty(self): return not self._n['type'] - - def validate(self): - _Port.validate(self) - if not self.get_enabled_connections() and not self.get_optional(): - self.add_error_message('Port is not connected.') - if not self.is_source() and (not self.get_type() == "message") and len(self.get_enabled_connections()) > 1: - self.add_error_message('Port has too many connections.') - #message port logic - if self.get_type() == 'msg': - if self.get_nports(): - self.add_error_message('A port of type "msg" cannot have "nports" set.') - if self.get_vlen() != 1: - self.add_error_message('A port of type "msg" must have a "vlen" of 1.') - - def rewrite(self): - """ - Handle the port cloning for virtual blocks. - """ - _Port.rewrite(self) - if self.is_type_empty(): - try: #clone type and vlen - source = self.resolve_empty_type() - self._type = str(source.get_type()) - self._vlen = str(source.get_vlen()) - except: #reset type and vlen - self._type = '' - self._vlen = '' - - def resolve_virtual_source(self): - if self.get_parent().is_virtual_sink(): return _get_source_from_virtual_sink_port(self) - if self.get_parent().is_virtual_source(): return _get_source_from_virtual_source_port(self) - - def resolve_empty_type(self): - if self.is_sink(): - try: - src = _get_source_from_virtual_sink_port(self) - if not src.is_type_empty(): return src - except: pass - sink = _get_sink_from_virtual_sink_port(self) - if not sink.is_type_empty(): return sink - if self.is_source(): - try: - src = _get_source_from_virtual_source_port(self) - if not src.is_type_empty(): return src - except: pass - sink = _get_sink_from_virtual_source_port(self) - if not sink.is_type_empty(): return sink - - def get_vlen(self): - """ - Get the vector length. - If the evaluation of vlen cannot be cast to an integer, return 1. - - Returns: - the vector length or 1 - """ - vlen = self.get_parent().resolve_dependencies(self._vlen) - try: return int(self.get_parent().get_parent().evaluate(vlen)) - except: return 1 - - - - - def get_nports(self): - """ - Get the number of ports. - If already blank, return a blank - If the evaluation of nports cannot be cast to an integer, return 1. - - Returns: - the number of ports or 1 - """ - nports = self.get_parent().resolve_dependencies(self._nports) - #return blank if nports is blank - if not nports: return '' - try: - nports = int(self.get_parent().get_parent().evaluate(nports)) - if 0 < nports: return nports - except: return 1 - - - - def get_optional(self): return bool(self._optional) - - def get_color(self): - """ - Get the color that represents this port's type. - Codes differ for ports where the vec length is 1 or greater than 1. - - Returns: - a hex color code. - """ - try: - color = Constants.TYPE_TO_COLOR[self.get_type()] - vlen = self.get_vlen() - if vlen == 1: return color - color_val = int(color[1:], 16) - r = (color_val >> 16) & 0xff - g = (color_val >> 8) & 0xff - b = (color_val >> 0) & 0xff - dark = (0, 0, 30, 50, 70)[min(4, vlen)] - r = max(r-dark, 0) - g = max(g-dark, 0) - b = max(b-dark, 0) - return '#%.2x%.2x%.2x'%(r, g, b) - except: return _Port.get_color(self) - - def copy(self, new_key=None): - n = self._n.copy() - #remove nports from the key so the copy cannot be a duplicator - if n.has_key('nports'): n.pop('nports') - if new_key: n['key'] = new_key - return self.__class__(self.get_parent(), n, self._dir) + def __init__(self, block, n, dir): + """ + Make a new port from nested data. + + Args: + block: the parent element + n: the nested odict + dir: the direction + """ + self._n = n + if n['type'] == 'msg': n['key'] = 'msg' + if n['type'] == 'message': n['key'] = n['name'] + if dir == 'source' and not n.find('key'): + n['key'] = str(block._source_count) + block._source_count += 1 + if dir == 'sink' and not n.find('key'): + n['key'] = str(block._sink_count) + block._sink_count += 1 + #build the port + _Port.__init__( + self, + block=block, + n=n, + dir=dir, + ) + _GUIPort.__init__(self) + self._nports = n.find('nports') or '' + self._vlen = n.find('vlen') or '' + self._optional = bool(n.find('optional')) + + def get_types(self): return Constants.TYPE_TO_SIZEOF.keys() + + def is_type_empty(self): return not self._n['type'] + + def validate(self): + _Port.validate(self) + if not self.get_enabled_connections() and not self.get_optional(): + self.add_error_message('Port is not connected.') + if not self.is_source() and (not self.get_type() == "message") and len(self.get_enabled_connections()) > 1: + self.add_error_message('Port has too many connections.') + #message port logic + if self.get_type() == 'msg': + if self.get_nports(): + self.add_error_message('A port of type "msg" cannot have "nports" set.') + if self.get_vlen() != 1: + self.add_error_message('A port of type "msg" must have a "vlen" of 1.') + + def rewrite(self): + """ + Handle the port cloning for virtual blocks. + """ + _Port.rewrite(self) + if self.is_type_empty(): + try: #clone type and vlen + source = self.resolve_empty_type() + self._type = str(source.get_type()) + self._vlen = str(source.get_vlen()) + except: #reset type and vlen + self._type = '' + self._vlen = '' + + def resolve_virtual_source(self): + if self.get_parent().is_virtual_sink(): return _get_source_from_virtual_sink_port(self) + if self.get_parent().is_virtual_source(): return _get_source_from_virtual_source_port(self) + + def resolve_empty_type(self): + if self.is_sink(): + try: + src = _get_source_from_virtual_sink_port(self) + if not src.is_type_empty(): return src + except: pass + sink = _get_sink_from_virtual_sink_port(self) + if not sink.is_type_empty(): return sink + if self.is_source(): + try: + src = _get_source_from_virtual_source_port(self) + if not src.is_type_empty(): return src + except: pass + sink = _get_sink_from_virtual_source_port(self) + if not sink.is_type_empty(): return sink + + def get_vlen(self): + """ + Get the vector length. + If the evaluation of vlen cannot be cast to an integer, return 1. + + Returns: + the vector length or 1 + """ + vlen = self.get_parent().resolve_dependencies(self._vlen) + try: return int(self.get_parent().get_parent().evaluate(vlen)) + except: return 1 + + def get_nports(self): + """ + Get the number of ports. + If already blank, return a blank + If the evaluation of nports cannot be cast to an integer, return 1. + + Returns: + the number of ports or 1 + """ + nports = self.get_parent().resolve_dependencies(self._nports) + #return blank if nports is blank + if not nports: return '' + try: + nports = int(self.get_parent().get_parent().evaluate(nports)) + if 0 < nports: return nports + except: return 1 + + def get_optional(self): return bool(self._optional) + + def get_color(self): + """ + Get the color that represents this port's type. + Codes differ for ports where the vec length is 1 or greater than 1. + + Returns: + a hex color code. + """ + try: + color = Constants.TYPE_TO_COLOR[self.get_type()] + vlen = self.get_vlen() + if vlen == 1: return color + color_val = int(color[1:], 16) + r = (color_val >> 16) & 0xff + g = (color_val >> 8) & 0xff + b = (color_val >> 0) & 0xff + dark = (0, 0, 30, 50, 70)[min(4, vlen)] + r = max(r-dark, 0) + g = max(g-dark, 0) + b = max(b-dark, 0) + return '#%.2x%.2x%.2x'%(r, g, b) + except: return _Port.get_color(self) + + def copy(self, new_key=None): + n = self._n.copy() + #remove nports from the key so the copy cannot be a duplicator + if n.has_key('nports'): n.pop('nports') + if new_key: n['key'] = new_key + return self.__class__(self.get_parent(), n, self._dir) diff --git a/grc/python/block.dtd b/grc/python/block.dtd index 99d38a0d46..21ffbe09af 100644 --- a/grc/python/block.dtd +++ b/grc/python/block.dtd @@ -17,25 +17,25 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA --> <!-- - gnuradio_python.blocks.dtd - Josh Blum - The document type definition for blocks. + gnuradio_python.blocks.dtd + Josh Blum + The document type definition for blocks. --> <!-- - Top level element. - A block contains a name, ...parameters list, and list of IO ports. + Top level element. + A block contains a name, ...parameters list, and list of IO ports. --> <!ELEMENT block (name, key, category?, throttle?, import*, var_make?, make, callback*, param*, bus_sink?, bus_source?, check*, sink*, source*, bus_structure_sink?, bus_structure_source?, doc?, grc_source?)> <!-- - Sub level elements. + Sub level elements. --> <!ELEMENT param (name, key, value?, type, hide?, option*)> <!ELEMENT option (name, key, opt*)> <!ELEMENT sink (name, type, vlen?, nports?, optional?)> <!ELEMENT source (name, type, vlen?, nports?, optional?)> <!-- - Bottom level elements. - Character data only. + Bottom level elements. + Character data only. --> <!ELEMENT category (#PCDATA)> <!ELEMENT import (#PCDATA)> diff --git a/grc/python/convert_hier.py b/grc/python/convert_hier.py index de76827541..dc86daa0ef 100644 --- a/grc/python/convert_hier.py +++ b/grc/python/convert_hier.py @@ -22,89 +22,89 @@ from .. base import ParseXML from .. base import odict def convert_hier(flow_graph, python_file): - #extract info from the flow graph - input_sigs = flow_graph.get_io_signaturev('in') - output_sigs = flow_graph.get_io_signaturev('out') - input_msgp = flow_graph.get_msg_pad_sources(); - output_msgp = flow_graph.get_msg_pad_sinks(); - parameters = flow_graph.get_parameters() - bussink = flow_graph.get_bussink() - bussrc = flow_graph.get_bussrc() - bus_struct_sink = flow_graph.get_bus_structure_sink() - bus_struct_src = flow_graph.get_bus_structure_src() - block_key = flow_graph.get_option('id') - block_name = flow_graph.get_option('title') or flow_graph.get_option('id').replace('_', ' ').title() - block_category = flow_graph.get_option('category') - block_desc = flow_graph.get_option('description') - block_author = flow_graph.get_option('author') - #build the nested data - block_n = odict() - block_n['name'] = block_name - block_n['key'] = block_key - block_n['category'] = block_category - block_n['import'] = 'execfile("%s")'%python_file - #make data - if parameters: block_n['make'] = '%s(\n\t%s,\n)'%( - block_key, - ',\n\t'.join(['%s=$%s'%(param.get_id(), param.get_id()) for param in parameters]), - ) - else: block_n['make'] = '%s()'%block_key - #callback data - block_n['callback'] = ['set_%s($%s)'%(param.get_id(), param.get_id()) for param in parameters] - #param data - params_n = list() - for param in parameters: - param_n = odict() - param_n['name'] = param.get_param('label').get_value() or param.get_id() - param_n['key'] = param.get_id() - param_n['value'] = param.get_param('value').get_value() - param_n['type'] = 'raw' - params_n.append(param_n) - block_n['param'] = params_n - #sink data stream ports - if bussink: - block_n['bus_sink'] = '1'; - if bussrc: - block_n['bus_source'] = '1'; - block_n['sink'] = list() - for input_sig in input_sigs: - sink_n = odict() - sink_n['name'] = input_sig['label'] - sink_n['type'] = input_sig['type'] - sink_n['vlen'] = input_sig['vlen'] - if input_sig['optional']: sink_n['optional'] = '1' - block_n['sink'].append(sink_n) - #sink data msg ports - for input_sig in input_msgp: - sink_n = odict() - sink_n['name'] = input_sig.get_param("label").get_value(); - sink_n['type'] = "message" - sink_n['optional'] = input_sig.get_param("optional").get_value(); - block_n['sink'].append(sink_n) - #source data stream ports - block_n['source'] = list() - if bus_struct_sink: - block_n['bus_structure_sink'] = bus_struct_sink[0].get_param('struct').get_value(); - if bus_struct_src: - block_n['bus_structure_source'] = bus_struct_src[0].get_param('struct').get_value(); - for output_sig in output_sigs: - source_n = odict() - source_n['name'] = output_sig['label'] - source_n['type'] = output_sig['type'] - source_n['vlen'] = output_sig['vlen'] - if output_sig['optional']: source_n['optional'] = '1' - block_n['source'].append(source_n) - #source data msg ports - for output_sig in output_msgp: - source_n = odict() - source_n['name'] = output_sig.get_param("label").get_value(); - source_n['type'] = "message" - source_n['optional'] = output_sig.get_param("optional").get_value(); - block_n['source'].append(source_n) - #doc data - block_n['doc'] = "%s\n%s\n%s"%(block_author, block_desc, python_file) - block_n['grc_source'] = "%s"%(flow_graph.grc_file_path) - #write the block_n to file - xml_file = python_file + '.xml' - ParseXML.to_file({'block': block_n}, xml_file) - ParseXML.validate_dtd(xml_file, BLOCK_DTD) + #extract info from the flow graph + input_sigs = flow_graph.get_io_signaturev('in') + output_sigs = flow_graph.get_io_signaturev('out') + input_msgp = flow_graph.get_msg_pad_sources(); + output_msgp = flow_graph.get_msg_pad_sinks(); + parameters = flow_graph.get_parameters() + bussink = flow_graph.get_bussink() + bussrc = flow_graph.get_bussrc() + bus_struct_sink = flow_graph.get_bus_structure_sink() + bus_struct_src = flow_graph.get_bus_structure_src() + block_key = flow_graph.get_option('id') + block_name = flow_graph.get_option('title') or flow_graph.get_option('id').replace('_', ' ').title() + block_category = flow_graph.get_option('category') + block_desc = flow_graph.get_option('description') + block_author = flow_graph.get_option('author') + #build the nested data + block_n = odict() + block_n['name'] = block_name + block_n['key'] = block_key + block_n['category'] = block_category + block_n['import'] = 'execfile("%s")'%python_file + #make data + if parameters: block_n['make'] = '%s(\n %s,\n)'%( + block_key, + ',\n '.join(['%s=$%s'%(param.get_id(), param.get_id()) for param in parameters]), + ) + else: block_n['make'] = '%s()'%block_key + #callback data + block_n['callback'] = ['set_%s($%s)'%(param.get_id(), param.get_id()) for param in parameters] + #param data + params_n = list() + for param in parameters: + param_n = odict() + param_n['name'] = param.get_param('label').get_value() or param.get_id() + param_n['key'] = param.get_id() + param_n['value'] = param.get_param('value').get_value() + param_n['type'] = 'raw' + params_n.append(param_n) + block_n['param'] = params_n + #sink data stream ports + if bussink: + block_n['bus_sink'] = '1'; + if bussrc: + block_n['bus_source'] = '1'; + block_n['sink'] = list() + for input_sig in input_sigs: + sink_n = odict() + sink_n['name'] = input_sig['label'] + sink_n['type'] = input_sig['type'] + sink_n['vlen'] = input_sig['vlen'] + if input_sig['optional']: sink_n['optional'] = '1' + block_n['sink'].append(sink_n) + #sink data msg ports + for input_sig in input_msgp: + sink_n = odict() + sink_n['name'] = input_sig.get_param("label").get_value(); + sink_n['type'] = "message" + sink_n['optional'] = input_sig.get_param("optional").get_value(); + block_n['sink'].append(sink_n) + #source data stream ports + block_n['source'] = list() + if bus_struct_sink: + block_n['bus_structure_sink'] = bus_struct_sink[0].get_param('struct').get_value(); + if bus_struct_src: + block_n['bus_structure_source'] = bus_struct_src[0].get_param('struct').get_value(); + for output_sig in output_sigs: + source_n = odict() + source_n['name'] = output_sig['label'] + source_n['type'] = output_sig['type'] + source_n['vlen'] = output_sig['vlen'] + if output_sig['optional']: source_n['optional'] = '1' + block_n['source'].append(source_n) + #source data msg ports + for output_sig in output_msgp: + source_n = odict() + source_n['name'] = output_sig.get_param("label").get_value(); + source_n['type'] = "message" + source_n['optional'] = output_sig.get_param("optional").get_value(); + block_n['source'].append(source_n) + #doc data + block_n['doc'] = "%s\n%s\n%s"%(block_author, block_desc, python_file) + block_n['grc_source'] = "%s"%(flow_graph.grc_file_path) + #write the block_n to file + xml_file = python_file + '.xml' + ParseXML.to_file({'block': block_n}, xml_file) + ParseXML.validate_dtd(xml_file, BLOCK_DTD) diff --git a/grc/python/default_flow_graph.grc b/grc/python/default_flow_graph.grc index dea26f3a5e..53d39e885a 100644 --- a/grc/python/default_flow_graph.grc +++ b/grc/python/default_flow_graph.grc @@ -2,42 +2,42 @@ <!-- ################################################### ##Default Flow Graph: -## include an options block and a variable for sample rate +## include an options block and a variable for sample rate ################################################### --> <flow_graph> - <block> - <key>options</key> - <param> - <key>id</key> - <value>top_block</value> - </param> - <param> - <key>_coordinate</key> - <value>(10, 10)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> - <block> - <key>variable</key> - <param> - <key>id</key> - <value>samp_rate</value> - </param> - <param> - <key>value</key> - <value>32000</value> - </param> - <param> - <key>_coordinate</key> - <value>(10, 170)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> + <block> + <key>options</key> + <param> + <key>id</key> + <value>top_block</value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>value</key> + <value>32000</value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 170)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> </flow_graph> diff --git a/grc/python/expr_utils.py b/grc/python/expr_utils.py index 67580f6ffc..85f420f04c 100644 --- a/grc/python/expr_utils.py +++ b/grc/python/expr_utils.py @@ -21,157 +21,157 @@ import string VAR_CHARS = string.letters + string.digits + '_' class graph(object): - """ - Simple graph structure held in a dictionary. - """ + """ + Simple graph structure held in a dictionary. + """ - def __init__(self): self._graph = dict() + def __init__(self): self._graph = dict() - def __str__(self): return str(self._graph) + def __str__(self): return str(self._graph) - def add_node(self, node_key): - if self._graph.has_key(node_key): return - self._graph[node_key] = set() + def add_node(self, node_key): + if self._graph.has_key(node_key): return + self._graph[node_key] = set() - def remove_node(self, node_key): - if not self._graph.has_key(node_key): return - for edges in self._graph.values(): - if node_key in edges: edges.remove(node_key) - self._graph.pop(node_key) + def remove_node(self, node_key): + if not self._graph.has_key(node_key): return + for edges in self._graph.values(): + if node_key in edges: edges.remove(node_key) + self._graph.pop(node_key) - def add_edge(self, src_node_key, dest_node_key): - self._graph[src_node_key].add(dest_node_key) + def add_edge(self, src_node_key, dest_node_key): + self._graph[src_node_key].add(dest_node_key) - def remove_edge(self, src_node_key, dest_node_key): - self._graph[src_node_key].remove(dest_node_key) + def remove_edge(self, src_node_key, dest_node_key): + self._graph[src_node_key].remove(dest_node_key) - def get_nodes(self): return self._graph.keys() + def get_nodes(self): return self._graph.keys() - def get_edges(self, node_key): return self._graph[node_key] + def get_edges(self, node_key): return self._graph[node_key] def expr_split(expr): - """ - Split up an expression by non alphanumeric characters, including underscore. - Leave strings in-tact. - #TODO ignore escaped quotes, use raw strings. - - Args: - expr: an expression string - - Returns: - a list of string tokens that form expr - """ - toks = list() - tok = '' - quote = '' - for char in expr: - if quote or char in VAR_CHARS: - if char == quote: quote = '' - tok += char - elif char in ("'", '"'): - toks.append(tok) - tok = char - quote = char - else: - toks.append(tok) - toks.append(char) - tok = '' - toks.append(tok) - return filter(lambda t: t, toks) + """ + Split up an expression by non alphanumeric characters, including underscore. + Leave strings in-tact. + #TODO ignore escaped quotes, use raw strings. + + Args: + expr: an expression string + + Returns: + a list of string tokens that form expr + """ + toks = list() + tok = '' + quote = '' + for char in expr: + if quote or char in VAR_CHARS: + if char == quote: quote = '' + tok += char + elif char in ("'", '"'): + toks.append(tok) + tok = char + quote = char + else: + toks.append(tok) + toks.append(char) + tok = '' + toks.append(tok) + return filter(lambda t: t, toks) def expr_replace(expr, replace_dict): - """ - Search for vars in the expression and add the prepend. - - Args: - expr: an expression string - replace_dict: a dict of find:replace - - Returns: - a new expression with the prepend - """ - expr_splits = expr_split(expr) - for i, es in enumerate(expr_splits): - if es in replace_dict.keys(): - expr_splits[i] = replace_dict[es] - return ''.join(expr_splits) + """ + Search for vars in the expression and add the prepend. + + Args: + expr: an expression string + replace_dict: a dict of find:replace + + Returns: + a new expression with the prepend + """ + expr_splits = expr_split(expr) + for i, es in enumerate(expr_splits): + if es in replace_dict.keys(): + expr_splits[i] = replace_dict[es] + return ''.join(expr_splits) def get_variable_dependencies(expr, vars): - """ - Return a set of variables used in this expression. - - Args: - expr: an expression string - vars: a list of variable names - - Returns: - a subset of vars used in the expression - """ - expr_toks = expr_split(expr) - return set(filter(lambda v: v in expr_toks, vars)) + """ + Return a set of variables used in this expression. + + Args: + expr: an expression string + vars: a list of variable names + + Returns: + a subset of vars used in the expression + """ + expr_toks = expr_split(expr) + return set(filter(lambda v: v in expr_toks, vars)) def get_graph(exprs): - """ - Get a graph representing the variable dependencies - - Args: - exprs: a mapping of variable name to expression - - Returns: - a graph of variable deps - """ - vars = exprs.keys() - #get dependencies for each expression, load into graph - var_graph = graph() - for var in vars: var_graph.add_node(var) - for var, expr in exprs.iteritems(): - for dep in get_variable_dependencies(expr, vars): - if dep != var: var_graph.add_edge(dep, var) - return var_graph + """ + Get a graph representing the variable dependencies + + Args: + exprs: a mapping of variable name to expression + + Returns: + a graph of variable deps + """ + vars = exprs.keys() + #get dependencies for each expression, load into graph + var_graph = graph() + for var in vars: var_graph.add_node(var) + for var, expr in exprs.iteritems(): + for dep in get_variable_dependencies(expr, vars): + if dep != var: var_graph.add_edge(dep, var) + return var_graph def sort_variables(exprs): - """ - Get a list of variables in order of dependencies. - - Args: - exprs: a mapping of variable name to expression - - Returns: - a list of variable names - @throws Exception circular dependencies - """ - var_graph = get_graph(exprs) - sorted_vars = list() - #determine dependency order - while var_graph.get_nodes(): - #get a list of nodes with no edges - indep_vars = filter(lambda var: not var_graph.get_edges(var), var_graph.get_nodes()) - if not indep_vars: raise Exception('circular dependency caught in sort_variables') - #add the indep vars to the end of the list - sorted_vars.extend(sorted(indep_vars)) - #remove each edge-less node from the graph - for var in indep_vars: var_graph.remove_node(var) - return reversed(sorted_vars) + """ + Get a list of variables in order of dependencies. + + Args: + exprs: a mapping of variable name to expression + + Returns: + a list of variable names + @throws Exception circular dependencies + """ + var_graph = get_graph(exprs) + sorted_vars = list() + #determine dependency order + while var_graph.get_nodes(): + #get a list of nodes with no edges + indep_vars = filter(lambda var: not var_graph.get_edges(var), var_graph.get_nodes()) + if not indep_vars: raise Exception('circular dependency caught in sort_variables') + #add the indep vars to the end of the list + sorted_vars.extend(sorted(indep_vars)) + #remove each edge-less node from the graph + for var in indep_vars: var_graph.remove_node(var) + return reversed(sorted_vars) def sort_objects(objects, get_id, get_expr): - """ - Sort a list of objects according to their expressions. - - Args: - objects: the list of objects to sort - get_id: the function to extract an id from the object - get_expr: the function to extract an expression from the object - - Returns: - a list of sorted objects - """ - id2obj = dict([(get_id(obj), obj) for obj in objects]) - #map obj id to expression code - id2expr = dict([(get_id(obj), get_expr(obj)) for obj in objects]) - #sort according to dependency - sorted_ids = sort_variables(id2expr) - #return list of sorted objects - return [id2obj[id] for id in sorted_ids] + """ + Sort a list of objects according to their expressions. + + Args: + objects: the list of objects to sort + get_id: the function to extract an id from the object + get_expr: the function to extract an expression from the object + + Returns: + a list of sorted objects + """ + id2obj = dict([(get_id(obj), obj) for obj in objects]) + #map obj id to expression code + id2expr = dict([(get_id(obj), get_expr(obj)) for obj in objects]) + #sort according to dependency + sorted_ids = sort_variables(id2expr) + #return list of sorted objects + return [id2obj[id] for id in sorted_ids] if __name__ == '__main__': - for i in sort_variables({'x':'1', 'y':'x+1', 'a':'x+y', 'b':'y+1', 'c':'a+b+x+y'}): print i + for i in sort_variables({'x':'1', 'y':'x+1', 'a':'x+y', 'b':'y+1', 'c':'a+b+x+y'}): print i diff --git a/grc/python/extract_docs.py b/grc/python/extract_docs.py index 1d1c738dcc..b3b87e64ca 100644 --- a/grc/python/extract_docs.py +++ b/grc/python/extract_docs.py @@ -20,55 +20,55 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import re def _extract(key): - """ - Extract the documentation from the python __doc__ strings. - If multiple modules match, combine the docs. - - Args: - key: the block key - - Returns: - a string with documentation - """ - #extract matches - try: - module_name, constructor_name = key.split('_', 1) - module = __import__('gnuradio.'+module_name) - module = getattr(module, module_name) - except ImportError: - try: - module_name, constructor_name = key.split('_', 1) - module = __import__(module_name) - except: return '' - except: - return '' - pattern = constructor_name.replace('_', '_*').replace('x', '\w') - pattern_matcher = re.compile('^%s\w*$'%pattern) - matches = filter(lambda x: pattern_matcher.match(x), dir(module)) - #combine all matches - doc_strs = list() - for match in matches: - try: - title = ' --- ' + match + ' --- ' - doc_strs.append('\n\n'.join([title, getattr(module, match).__doc__]).strip()) - except: pass - return '\n\n'.join(doc_strs) + """ + Extract the documentation from the python __doc__ strings. + If multiple modules match, combine the docs. + + Args: + key: the block key + + Returns: + a string with documentation + """ + #extract matches + try: + module_name, constructor_name = key.split('_', 1) + module = __import__('gnuradio.'+module_name) + module = getattr(module, module_name) + except ImportError: + try: + module_name, constructor_name = key.split('_', 1) + module = __import__(module_name) + except: return '' + except: + return '' + pattern = constructor_name.replace('_', '_*').replace('x', '\w') + pattern_matcher = re.compile('^%s\w*$'%pattern) + matches = filter(lambda x: pattern_matcher.match(x), dir(module)) + #combine all matches + doc_strs = list() + for match in matches: + try: + title = ' --- ' + match + ' --- ' + doc_strs.append('\n\n'.join([title, getattr(module, match).__doc__]).strip()) + except: pass + return '\n\n'.join(doc_strs) _docs_cache = dict() def extract(key): - """ - Call the private extract and cache the result. - - Args: - key: the block key - - Returns: - a string with documentation - """ - if not _docs_cache.has_key(key): - _docs_cache[key] = _extract(key) - return _docs_cache[key] + """ + Call the private extract and cache the result. + + Args: + key: the block key + + Returns: + a string with documentation + """ + if not _docs_cache.has_key(key): + _docs_cache[key] = _extract(key) + return _docs_cache[key] if __name__ == '__main__': - import sys - print extract(sys.argv[1]) + import sys + print extract(sys.argv[1]) diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl index d104d4913d..550ecd78b5 100644 --- a/grc/python/flow_graph.tmpl +++ b/grc/python/flow_graph.tmpl @@ -13,7 +13,7 @@ ##@param var_id2cbs variable id map to callback strings ######################################################## #def indent($code) -#set $code = '\n\t\t'.join(str($code).splitlines()) +#set $code = '\n '.join(str($code).splitlines()) $code#slurp #end def #import time @@ -39,304 +39,304 @@ $imp ######################################################## ##Create Class -## Write the class declaration for a top or hier block. -## The parameter names are the arguments to __init__. -## Determine the absolute icon path (wx gui only). -## Setup the IO signature (hier block only). +## Write the class declaration for a top or hier block. +## The parameter names are the arguments to __init__. +## Determine the absolute icon path (wx gui only). +## Setup the IO signature (hier block only). ######################################################## #set $class_name = $flow_graph.get_option('id') #set $param_str = ', '.join(['self'] + ['%s=%s'%(param.get_id(), param.get_make()) for param in $parameters]) #if $generate_options == 'wx_gui' - #import gtk - #set $icon = gtk.IconTheme().lookup_icon('gnuradio-grc', 32, 0) + #import gtk + #set $icon = gtk.IconTheme().lookup_icon('gnuradio-grc', 32, 0) class $(class_name)(grc_wxgui.top_block_gui): - def __init__($param_str): - grc_wxgui.top_block_gui.__init__(self, title="$title") - #if $icon - _icon_path = "$icon.get_filename()" - self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY)) - #end if + def __init__($param_str): + grc_wxgui.top_block_gui.__init__(self, title="$title") + #if $icon + _icon_path = "$icon.get_filename()" + self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY)) + #end if #elif $generate_options == 'qt_gui' class $(class_name)(gr.top_block, Qt.QWidget): - def __init__($param_str): - gr.top_block.__init__(self, "$title") - Qt.QWidget.__init__(self) - self.setWindowTitle("$title") - try: - self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc')) - except: - pass - self.top_scroll_layout = Qt.QVBoxLayout() - self.setLayout(self.top_scroll_layout) - self.top_scroll = Qt.QScrollArea() - self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame) - self.top_scroll_layout.addWidget(self.top_scroll) - self.top_scroll.setWidgetResizable(True) - self.top_widget = Qt.QWidget() - self.top_scroll.setWidget(self.top_widget) - self.top_layout = Qt.QVBoxLayout(self.top_widget) - self.top_grid_layout = Qt.QGridLayout() - self.top_layout.addLayout(self.top_grid_layout) + def __init__($param_str): + gr.top_block.__init__(self, "$title") + Qt.QWidget.__init__(self) + self.setWindowTitle("$title") + try: + self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc')) + except: + pass + self.top_scroll_layout = Qt.QVBoxLayout() + self.setLayout(self.top_scroll_layout) + self.top_scroll = Qt.QScrollArea() + self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame) + self.top_scroll_layout.addWidget(self.top_scroll) + self.top_scroll.setWidgetResizable(True) + self.top_widget = Qt.QWidget() + self.top_scroll.setWidget(self.top_widget) + self.top_layout = Qt.QVBoxLayout(self.top_widget) + self.top_grid_layout = Qt.QGridLayout() + self.top_layout.addLayout(self.top_grid_layout) - self.settings = Qt.QSettings("GNU Radio", "$class_name") - self.restoreGeometry(self.settings.value("geometry").toByteArray()) + self.settings = Qt.QSettings("GNU Radio", "$class_name") + self.restoreGeometry(self.settings.value("geometry").toByteArray()) #elif $generate_options == 'no_gui' class $(class_name)(gr.top_block): - def __init__($param_str): - gr.top_block.__init__(self, "$title") + def __init__($param_str): + gr.top_block.__init__(self, "$title") #elif $generate_options == 'hb' - #set $in_sigs = $flow_graph.get_io_signaturev('in') - #set $out_sigs = $flow_graph.get_io_signaturev('out') + #set $in_sigs = $flow_graph.get_io_signaturev('in') + #set $out_sigs = $flow_graph.get_io_signaturev('out') class $(class_name)(gr.hier_block2): #def make_io_sig($io_sigs) - #set $size_strs = ['%s*%s'%(io_sig['size'], io_sig['vlen']) for io_sig in $io_sigs] - #if len($io_sigs) == 0 + #set $size_strs = ['%s*%s'%(io_sig['size'], io_sig['vlen']) for io_sig in $io_sigs] + #if len($io_sigs) == 0 gr.io_signature(0, 0, 0)#slurp - #elif len($io_sigs) == 1 + #elif len($io_sigs) == 1 gr.io_signature(1, 1, $size_strs[0])#slurp - #else + #else gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))])#slurp - #end if + #end if #end def - def __init__($param_str): - gr.hier_block2.__init__( - self, "$title", - $make_io_sig($in_sigs), - $make_io_sig($out_sigs), - ) + def __init__($param_str): + gr.hier_block2.__init__( + self, "$title", + $make_io_sig($in_sigs), + $make_io_sig($out_sigs), + ) #end if ######################################################## ##Create Parameters -## Set the parameter to a property of self. +## Set the parameter to a property of self. ######################################################## #if $parameters - $DIVIDER - # Parameters - $DIVIDER + $DIVIDER + # Parameters + $DIVIDER #end if #for $param in $parameters - $indent($param.get_var_make()) + $indent($param.get_var_make()) #end for ######################################################## ##Create Variables ######################################################## #if $variables - $DIVIDER - # Variables - $DIVIDER + $DIVIDER + # Variables + $DIVIDER #end if #for $var in $variables - $indent($var.get_var_make()) + $indent($var.get_var_make()) #end for ######################################################## ##Create Message Queues ######################################################## #if $messages - $DIVIDER - # Message Queues - $DIVIDER + $DIVIDER + # Message Queues + $DIVIDER #end if #for $msg in $messages - $(msg.get_source().get_parent().get_id())_msgq_out = $(msg.get_sink().get_parent().get_id())_msgq_in = gr.msg_queue(2) + $(msg.get_source().get_parent().get_id())_msgq_out = $(msg.get_sink().get_parent().get_id())_msgq_in = gr.msg_queue(2) #end for ######################################################## ##Create Blocks ######################################################## #if $blocks - $DIVIDER - # Blocks - $DIVIDER + $DIVIDER + # Blocks + $DIVIDER #end if #for $blk in filter(lambda b: b.get_make(), $blocks) - #if $blk in $variables - $indent($blk.get_make()) - #else - self.$blk.get_id() = $indent($blk.get_make()) - #end if + #if $blk in $variables + $indent($blk.get_make()) + #else + self.$blk.get_id() = $indent($blk.get_make()) + #end if #end for ######################################################## ##Create Connections -## The port name should be the id of the parent block. -## However, port names for IO pads should be self. +## The port name should be the id of the parent block. +## However, port names for IO pads should be self. ######################################################## #def make_port_sig($port) - #if $port.get_parent().get_key() == 'pad_source' + #if $port.get_parent().get_key() == 'pad_source' (self, $flow_graph.get_pad_sources().index($port.get_parent()))#slurp - #elif $port.get_parent().get_key() == 'pad_sink' + #elif $port.get_parent().get_key() == 'pad_sink' (self, $flow_graph.get_pad_sinks().index($port.get_parent()))#slurp - #else + #else (self.$port.get_parent().get_id(), $port.get_key())#slurp - #end if + #end if #end def #if $connections - $DIVIDER - # Connections - $DIVIDER + $DIVIDER + # Connections + $DIVIDER #end if #for $con in $connections - #set $source = $con.get_source() - #set $sink = $con.get_sink() - ##resolve virtual sources to the actual sources - #if $source.get_parent().is_virtual_source() - #set $source = $source.resolve_virtual_source() - #end if - ##do not generate connections with virtual sinks - #if not $sink.get_parent().is_virtual_sink() - self.connect($make_port_sig($source), $make_port_sig($sink)) - #end if + #set $source = $con.get_source() + #set $sink = $con.get_sink() + ##resolve virtual sources to the actual sources + #if $source.get_parent().is_virtual_source() + #set $source = $source.resolve_virtual_source() + #end if + ##do not generate connections with virtual sinks + #if not $sink.get_parent().is_virtual_sink() + self.connect($make_port_sig($source), $make_port_sig($sink)) + #end if #end for ######################################################## ##Create Asynch Message Connections ######################################################## #if $messages2 - $DIVIDER - # Asynch Message Connections - $DIVIDER + $DIVIDER + # Asynch Message Connections + $DIVIDER #end if #for $msg in $messages2 - #set $sr = $msg.get_source() - #set $source = "self.%s"%($sr.get_parent().get_id()) - #set $source_port = $sr.get_name(); - #if $sr.get_parent().get_key() == "pad_source" - #set $source = "self" - #set $source_port = $sr.get_parent().get_param("label").get_value(); - #end if - #set $sk = $msg.get_sink() - #set $sink = "self.%s"%($sk.get_parent().get_id()) - #set $sink_port = $sk.get_name(); - #if $sk.get_parent().get_key() == "pad_sink" - #set $sink = "self" - #set $sink_port = $sk.get_parent().get_param("label").get_value(); - #end if - self.msg_connect($source, "$source_port", $sink, "$sink_port") + #set $sr = $msg.get_source() + #set $source = "self.%s"%($sr.get_parent().get_id()) + #set $source_port = $sr.get_name(); + #if $sr.get_parent().get_key() == "pad_source" + #set $source = "self" + #set $source_port = $sr.get_parent().get_param("label").get_value(); + #end if + #set $sk = $msg.get_sink() + #set $sink = "self.%s"%($sk.get_parent().get_id()) + #set $sink_port = $sk.get_name(); + #if $sk.get_parent().get_key() == "pad_sink" + #set $sink = "self" + #set $sink_port = $sk.get_parent().get_param("label").get_value(); + #end if + self.msg_connect($source, "$source_port", $sink, "$sink_port") #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() + def closeEvent(self, event): + self.settings = Qt.QSettings("GNU Radio", "$class_name") + self.settings.setValue("geometry", self.saveGeometry()) + event.accept() #end if ######################################################## ##Create Callbacks -## Write a set method for this variable that calls the 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 + #set $id = $var.get_id() + def get_$(id)(self): + return self.$id - def set_$(id)(self, $id): - self.$id = $id - #for $callback in $var_id2cbs[$id] - $indent($callback) - #end for + def set_$(id)(self, $id): + self.$id = $id + #for $callback in $var_id2cbs[$id] + $indent($callback) + #end for #end for ######################################################## ##Create Main -## For top block code, generate a main routine. -## Instantiate the top block and run as gui or cli. +## For top block code, generate a main routine. +## Instantiate the top block and run as gui or cli. ######################################################## #def make_default($type, $param) - #if $type == 'eng_float' + #if $type == 'eng_float' eng_notation.num_to_str($param.get_make())#slurp - #else + #else $param.get_make()#slurp - #end if + #end if #end def #def make_short_id($param) - #set $short_id = $param.get_param('short_id').get_evaluated() - #if $short_id - #set $short_id = '-' + $short_id - #end if + #set $short_id = $param.get_param('short_id').get_evaluated() + #if $short_id + #set $short_id = '-' + $short_id + #end if $short_id#slurp #end def #if $generate_options != 'hb' if __name__ == '__main__': - 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), - help="Set $($param.get_param('label').get_evaluated() or $param.get_id()) [default=%default]") - #end if - #end for - (options, args) = parser.parse_args() - #if $flow_graph.get_option('realtime_scheduling') - if gr.enable_realtime_scheduling() != gr.RT_OK: - print "Error: failed to enable realtime scheduling." - #end if - #if $generate_options == 'wx_gui' - tb = $(class_name)($(', '.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 - tb.Start($flow_graph.get_option('run')) + 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), + help="Set $($param.get_param('label').get_evaluated() or $param.get_id()) [default=%default]") + #end if + #end for + (options, args) = parser.parse_args() + #if $flow_graph.get_option('realtime_scheduling') + if gr.enable_realtime_scheduling() != gr.RT_OK: + print "Error: failed to enable realtime scheduling." + #end if + #if $generate_options == 'wx_gui' + tb = $(class_name)($(', '.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 + tb.Start($flow_graph.get_option('run')) #for $m in $monitors - (tb.$m.get_id()).start() + (tb.$m.get_id()).start() #end for - tb.Wait() - #end if - #elif $generate_options == 'qt_gui' - qapp = Qt.QApplication(sys.argv) - tb = $(class_name)($(', '.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')) - #else - tb.start() - #end if - #end if - tb.show() + tb.Wait() + #end if + #elif $generate_options == 'qt_gui' + qapp = Qt.QApplication(sys.argv) + tb = $(class_name)($(', '.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')) + #else + tb.start() + #end if + #end if + tb.show() #for $m in $monitors - (tb.$m.get_id()).start() + (tb.$m.get_id()).start() #end for - qapp.exec_() - tb.stop() - tb = None #to clean up Qt widgets - #elif $generate_options == 'no_gui' - tb = $(class_name)($(', '.join($params_eq_list))) - #set $run_options = $flow_graph.get_option('run_options') - #if $run_options == 'prompt' - #if $flow_graph.get_option('max_nouts') - tb.start($flow_graph.get_option('max_nouts')) - #else - tb.start() - #end if + qapp.exec_() + tb.stop() + tb = None #to clean up Qt widgets + #elif $generate_options == 'no_gui' + tb = $(class_name)($(', '.join($params_eq_list))) + #set $run_options = $flow_graph.get_option('run_options') + #if $run_options == 'prompt' + #if $flow_graph.get_option('max_nouts') + tb.start($flow_graph.get_option('max_nouts')) + #else + tb.start() + #end if #for $m in $monitors - (tb.$m.get_id()).start() + (tb.$m.get_id()).start() #end for - raw_input('Press Enter to quit: ') - tb.stop() - #elif $run_options == 'run' - #if $flow_graph.get_option('max_nouts') - tb.start($flow_graph.get_option('max_nouts')) - #else - tb.start() - #end if - #end if + raw_input('Press Enter to quit: ') + tb.stop() + #elif $run_options == 'run' + #if $flow_graph.get_option('max_nouts') + tb.start($flow_graph.get_option('max_nouts')) + #else + tb.start() + #end if + #end if #for $m in $monitors - (tb.$m.get_id()).start() + (tb.$m.get_id()).start() #end for - tb.wait() - #end if + tb.wait() + #end if #end if diff --git a/grc/scripts/gnuradio-companion b/grc/scripts/gnuradio-companion index dabca3028f..6d45ecc246 100755 --- a/grc/scripts/gnuradio-companion +++ b/grc/scripts/gnuradio-companion @@ -24,36 +24,36 @@ import gtk try: from gnuradio import gr except ImportError, e: - d = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_CLOSE, message_format=""" + d = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_CLOSE, message_format=""" Cannot import gnuradio. Is the python path environment variable set correctly? - All OS: PYTHONPATH + All OS: PYTHONPATH Is the library path environment variable set correctly? - Linux: LD_LIBRARY_PATH - Windows: PATH - MacOSX: DYLD_LIBRARY_PATH + Linux: LD_LIBRARY_PATH + Windows: PATH + MacOSX: DYLD_LIBRARY_PATH """) - d.set_title(str(e)) - d.run() - exit(-1) + d.set_title(str(e)) + d.run() + exit(-1) from optparse import OptionParser import os if __name__ == "__main__": - if ('GR_DONT_LOAD_PREFS' in os.environ.keys() and - (not 'GRC_BLOCKS_PATH' in os.environ.keys() or len(os.environ['GRC_BLOCKS_PATH']) == 0)): - d = gtk.MessageDialog( - type=gtk.MESSAGE_ERROR, - buttons=gtk.BUTTONS_CLOSE, - message_format="""Can't find block definitions. Use config.conf or GRC_BLOCKS_PATH. """) - d.set_title("No block definitions available.") - d.run() - exit(-1) - usage = 'usage: %prog [options] [saved flow graphs]' - version = """ + if ('GR_DONT_LOAD_PREFS' in os.environ.keys() and + (not 'GRC_BLOCKS_PATH' in os.environ.keys() or len(os.environ['GRC_BLOCKS_PATH']) == 0)): + d = gtk.MessageDialog( + type=gtk.MESSAGE_ERROR, + buttons=gtk.BUTTONS_CLOSE, + message_format="""Can't find block definitions. Use config.conf or GRC_BLOCKS_PATH. """) + d.set_title("No block definitions available.") + d.run() + exit(-1) + usage = 'usage: %prog [options] [saved flow graphs]' + version = """ GNU Radio Companion %s This program is part of GNU Radio @@ -61,12 +61,12 @@ GRC comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it. """%gr.version() - parser = OptionParser(usage=usage, version=version) - (options, args) = parser.parse_args() - from gnuradio.grc.python.Platform import Platform - from gnuradio.grc.gui.ActionHandler import ActionHandler - #setup icon using icon theme - try: gtk.window_set_default_icon(gtk.IconTheme().load_icon('gnuradio-grc', 256, 0)) - except: pass - ActionHandler(args, Platform()) + parser = OptionParser(usage=usage, version=version) + (options, args) = parser.parse_args() + from gnuradio.grc.python.Platform import Platform + from gnuradio.grc.gui.ActionHandler import ActionHandler + #setup icon using icon theme + try: gtk.window_set_default_icon(gtk.IconTheme().load_icon('gnuradio-grc', 256, 0)) + except: pass + ActionHandler(args, Platform()) |