summaryrefslogtreecommitdiff
path: root/grc/gui/FlowGraph.py
diff options
context:
space:
mode:
Diffstat (limited to 'grc/gui/FlowGraph.py')
-rw-r--r--grc/gui/FlowGraph.py149
1 files changed, 112 insertions, 37 deletions
diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py
index 2053e86454..6eb05f9ac9 100644
--- a/grc/gui/FlowGraph.py
+++ b/grc/gui/FlowGraph.py
@@ -17,39 +17,43 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
-import random
import functools
-from itertools import chain
-from operator import methodcaller
+import random
from distutils.spawn import find_executable
+from itertools import chain, count
+from operator import methodcaller
import gobject
-from . import Actions, Colors, Constants, Utils, Messages, Bars, Dialogs
-from . Element import Element
-from . Constants import SCROLL_PROXIMITY_SENSITIVITY, SCROLL_DISTANCE
-from . external_editor import ExternalEditor
+from . import Actions, Colors, Constants, Utils, Bars, Dialogs
+from .Constants import SCROLL_PROXIMITY_SENSITIVITY, SCROLL_DISTANCE
+from .Element import Element
+from .external_editor import ExternalEditor
+
+from ..core.FlowGraph import FlowGraph as _Flowgraph
+from ..core import Messages
-class FlowGraph(Element):
+class FlowGraph(Element, _Flowgraph):
"""
FlowGraph is the data structure to store graphical signal blocks,
graphical inputs and outputs,
and the connections between inputs and outputs.
"""
- def __init__(self):
+ def __init__(self, **kwargs):
"""
FlowGraph constructor.
Create a list for signal blocks and connections. Connect mouse handlers.
"""
Element.__init__(self)
+ _Flowgraph.__init__(self, **kwargs)
#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._selected_elements = []
self.press_coor = (0, 0)
#selected ports
self._old_selected_port = None
@@ -62,14 +66,31 @@ class FlowGraph(Element):
self._external_updaters = {}
+ 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
+ """
+ for index in count():
+ block_id = '{}_{}'.format(base_id, index)
+ if block_id not in (b.get_id() for b in self.blocks):
+ break
+ return block_id
+
def install_external_editor(self, param):
target = (param.get_parent().get_id(), param.get_key())
if target in self._external_updaters:
editor = self._external_updaters[target]
else:
- editor = (find_executable(Constants.EDITOR) or
- Dialogs.ChooseEditorDialog())
+ config = self.get_parent().config
+ editor = (find_executable(config.editor) or
+ Dialogs.ChooseEditorDialog(config))
if not editor:
return
updater = functools.partial(
@@ -86,9 +107,7 @@ class FlowGraph(Element):
# Problem launching the editor. Need to select a new editor.
Messages.send('>>> Error opening an external editor. Please select a different editor.\n')
# Reset the editor to force the user to select a new one.
- Constants.prefs.set_string('grc', 'editor', '')
- Constants.prefs.save()
- Constants.EDITOR = ""
+ self.get_parent().config.editor = ''
def handle_external_editor_change(self, new_value, target):
try:
@@ -131,7 +150,7 @@ class FlowGraph(Element):
int(random.uniform(.25, .75)*v_adj.page_size + v_adj.get_value()),
)
#get the new block
- block = self.get_new_block(key)
+ block = self.new_block(key)
block.set_coordinate(coor)
block.set_rotation(0)
block.get_param('id').set_value(id)
@@ -160,7 +179,7 @@ class FlowGraph(Element):
#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(),
+ self.connections,
)
clipboard = (
(x_min, y_min),
@@ -190,7 +209,7 @@ class FlowGraph(Element):
for block_n in blocks_n:
block_key = block_n.find('key')
if block_key == 'options': continue
- block = self.get_new_block(block_key)
+ block = self.new_block(block_key)
if not block:
continue # unknown block was pasted (e.g. dummy block)
selected.add(block)
@@ -206,7 +225,7 @@ class FlowGraph(Element):
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 [blk.get_id() for blk in self.get_blocks()]:
+ if param_value in (blk.get_id() for blk in self.blocks):
param_value = self._get_unique_id(param_value)
#set value to key
block.get_param(param_key).set_value(param_value)
@@ -293,6 +312,47 @@ class FlowGraph(Element):
selected_block.move(delta_coordinate)
self.element_moved = True
+ def align_selected(self, calling_action=None):
+ """
+ Align the selected blocks.
+
+ Args:
+ calling_action: the action initiating the alignment
+
+ Returns:
+ True if changed, otherwise False
+ """
+ blocks = self.get_selected_blocks()
+ if calling_action is None or not blocks:
+ return False
+
+ # compute common boundary of selected objects
+ min_x, min_y = max_x, max_y = blocks[0].get_coordinate()
+ for selected_block in blocks:
+ x, y = selected_block.get_coordinate()
+ min_x, min_y = min(min_x, x), min(min_y, y)
+ x += selected_block.W
+ y += selected_block.H
+ max_x, max_y = max(max_x, x), max(max_y, y)
+ ctr_x, ctr_y = (max_x + min_x)/2, (max_y + min_y)/2
+
+ # align the blocks as requested
+ transform = {
+ Actions.BLOCK_VALIGN_TOP: lambda x, y, w, h: (x, min_y),
+ Actions.BLOCK_VALIGN_MIDDLE: lambda x, y, w, h: (x, ctr_y - h/2),
+ Actions.BLOCK_VALIGN_BOTTOM: lambda x, y, w, h: (x, max_y - h),
+ Actions.BLOCK_HALIGN_LEFT: lambda x, y, w, h: (min_x, y),
+ Actions.BLOCK_HALIGN_CENTER: lambda x, y, w, h: (ctr_x-w/2, y),
+ Actions.BLOCK_HALIGN_RIGHT: lambda x, y, w, h: (max_x - w, y),
+ }.get(calling_action, lambda *args: args)
+
+ for selected_block in blocks:
+ x, y = selected_block.get_coordinate()
+ w, h = selected_block.W, selected_block.H
+ selected_block.set_coordinate(transform(x, y, w, h))
+
+ return True
+
def rotate_selected(self, rotation):
"""
Rotate the selected blocks by multiples of 90 degrees.
@@ -303,7 +363,8 @@ class FlowGraph(Element):
Returns:
true if changed, otherwise false.
"""
- if not self.get_selected_blocks(): return 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()
@@ -344,13 +405,18 @@ class FlowGraph(Element):
"""
W,H = self.get_size()
+ hide_disabled_blocks = Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active()
+ hide_variables = Actions.TOGGLE_HIDE_VARIABLES.get_active()
+
#draw the background
gc.set_foreground(Colors.FLOWGRAPH_BACKGROUND_COLOR)
window.draw_rectangle(gc, True, 0, 0, W, H)
# draw comments first
if Actions.TOGGLE_SHOW_BLOCK_COMMENTS.get_active():
- for block in self.iter_blocks():
+ for block in self.blocks:
+ if hide_variables and (block.is_variable or block.is_import):
+ continue # skip hidden disabled blocks and connections
if block.get_enabled():
block.draw_comment(gc, window)
#draw multi select rectangle
@@ -367,11 +433,12 @@ class FlowGraph(Element):
gc.set_foreground(Colors.BORDER_COLOR)
window.draw_rectangle(gc, False, x, y, w, h)
#draw blocks on top of connections
- hide_disabled_blocks = Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active()
- blocks = sorted(self.iter_blocks(), key=methodcaller('get_enabled'))
- for element in chain(self.iter_connections(), blocks):
+ blocks = sorted(self.blocks, key=methodcaller('get_enabled'))
+ for element in chain(self.connections, blocks):
if hide_disabled_blocks and not element.get_enabled():
continue # skip hidden disabled blocks and connections
+ if hide_variables and (element.is_variable or element.is_import):
+ continue # skip hidden disabled blocks and connections
element.draw(gc, window)
#draw selected blocks on top of selected connections
for selected_element in self.get_selected_connections() + self.get_selected_blocks():
@@ -432,6 +499,10 @@ class FlowGraph(Element):
"""
self._selected_elements = []
+ def select_all(self):
+ """Select all blocks in the flow graph"""
+ self._selected_elements = list(self.get_elements())
+
def what_is_selected(self, coor, coor_m=None):
"""
What is selected?
@@ -451,18 +522,18 @@ class FlowGraph(Element):
selected_port = None
selected = set()
#check the elements
+ hide_disabled_blocks = Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active()
+ hide_variables = Actions.TOGGLE_HIDE_VARIABLES.get_active()
for element in reversed(self.get_elements()):
+ if hide_disabled_blocks and not element.get_enabled():
+ continue # skip hidden disabled blocks and connections
+ if hide_variables and (element.is_variable or element.is_import):
+ continue # skip hidden disabled blocks and connections
selected_element = element.what_is_selected(coor, coor_m)
- if not selected_element: continue
- # hidden disabled connections, blocks and their ports can not be selected
- if Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active() and (
- selected_element.is_block() and not selected_element.get_enabled() or
- selected_element.is_connection() and not selected_element.get_enabled() or
- selected_element.is_port() and not selected_element.get_parent().get_enabled()
- ):
+ if not selected_element:
continue
#update the selected port information
- if selected_element.is_port():
+ if selected_element.is_port:
if not coor_m: selected_port = selected_element
selected_element = selected_element.get_parent()
selected.add(selected_element)
@@ -486,7 +557,8 @@ class FlowGraph(Element):
"""
selected = set()
for selected_element in self.get_selected_elements():
- if selected_element.is_connection(): selected.add(selected_element)
+ if selected_element.is_connection:
+ selected.add(selected_element)
return list(selected)
def get_selected_blocks(self):
@@ -498,7 +570,8 @@ class FlowGraph(Element):
"""
selected = set()
for selected_element in self.get_selected_elements():
- if selected_element.is_block(): selected.add(selected_element)
+ if selected_element.is_block:
+ selected.add(selected_element)
return list(selected)
def get_selected_block(self):
@@ -508,7 +581,8 @@ class FlowGraph(Element):
Returns:
a block or None
"""
- return self.get_selected_blocks() and self.get_selected_blocks()[0] or None
+ selected_blocks = self.get_selected_blocks()
+ return selected_blocks[0] if selected_blocks else None
def get_selected_elements(self):
"""
@@ -526,7 +600,8 @@ class FlowGraph(Element):
Returns:
a block, port, or connection or None
"""
- return self.get_selected_elements() and self.get_selected_elements()[0] or None
+ selected_elements = self.get_selected_elements()
+ return selected_elements[0] if selected_elements else None
def update_selected_elements(self):
"""
@@ -673,7 +748,7 @@ class FlowGraph(Element):
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():
+ 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
if not self.get_ctrl_mask():