summaryrefslogtreecommitdiff
path: root/grc/gui
diff options
context:
space:
mode:
authorJohnathan Corgan <johnathan@corganlabs.com>2014-08-20 10:34:08 -0700
committerJohnathan Corgan <johnathan@corganlabs.com>2014-08-20 10:34:08 -0700
commit9e9699b4c1e76d6d699d7c199775f3d8e134d5e5 (patch)
tree4989c9dc49800642ae57ed72c9670ad72c79c843 /grc/gui
parent84ce73af3dca41b8acec6d59fca65901e1fe0045 (diff)
parent00709699b217bc6b6b866ffad6d9a8c1e9497ae8 (diff)
Merge remote-tracking branch 'gnuradio-wg-grc/grc_snap_blocks_to_grid'
Diffstat (limited to 'grc/gui')
-rw-r--r--grc/gui/ActionHandler.py35
-rw-r--r--grc/gui/Actions.py22
-rw-r--r--grc/gui/Bars.py1
-rw-r--r--grc/gui/Block.py18
-rw-r--r--grc/gui/Constants.py5
-rw-r--r--grc/gui/DrawingArea.py4
-rw-r--r--grc/gui/FlowGraph.py13
-rw-r--r--grc/gui/Port.py10
-rw-r--r--grc/gui/Preferences.py29
-rw-r--r--grc/gui/Utils.py10
10 files changed, 94 insertions, 53 deletions
diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py
index 18b7c9aee0..dcc3c846eb 100644
--- a/grc/gui/ActionHandler.py
+++ b/grc/gui/ActionHandler.py
@@ -118,7 +118,7 @@ class ActionHandler:
Actions.TYPES_WINDOW_DISPLAY, Actions.TOGGLE_BLOCKS_WINDOW,
Actions.TOGGLE_REPORTS_WINDOW, Actions.TOGGLE_HIDE_DISABLED_BLOCKS,
Actions.TOOLS_RUN_FDESIGN, Actions.TOGGLE_SCROLL_LOCK, Actions.CLEAR_REPORTS,
- Actions.TOGGLE_AUTO_HIDE_PORT_LABELS
+ Actions.TOGGLE_AUTO_HIDE_PORT_LABELS, Actions.TOGGLE_SNAP_TO_GRID
): action.set_sensitive(True)
if ParseXML.xml_failures:
Messages.send_xml_errors_if_any(ParseXML.xml_failures)
@@ -134,10 +134,13 @@ class ActionHandler:
if not self.get_page(): self.main_window.new_page() #ensure that at least a blank page exists
self.main_window.btwin.search_entry.hide()
- Actions.TOGGLE_REPORTS_WINDOW.set_active(Preferences.reports_window_visibility())
- Actions.TOGGLE_BLOCKS_WINDOW.set_active(Preferences.blocks_window_visibility())
- Actions.TOGGLE_SCROLL_LOCK.set_active(Preferences.scroll_lock())
- Actions.TOGGLE_AUTO_HIDE_PORT_LABELS.set_active(Preferences.auto_hide_port_labels())
+ for action in (
+ Actions.TOGGLE_REPORTS_WINDOW,
+ Actions.TOGGLE_BLOCKS_WINDOW,
+ Actions.TOGGLE_AUTO_HIDE_PORT_LABELS,
+ Actions.TOGGLE_SCROLL_LOCK,
+ Actions.TOGGLE_SNAP_TO_GRID
+ ): action.load_from_preferences()
elif action == Actions.APPLICATION_QUIT:
if self.main_window.close_pages():
gtk.main_quit()
@@ -363,31 +366,32 @@ class ActionHandler:
elif action == Actions.ERRORS_WINDOW_DISPLAY:
Dialogs.ErrorsDialog(self.get_flow_graph())
elif action == Actions.TOGGLE_REPORTS_WINDOW:
- visible = action.get_active()
- if visible:
+ if action.get_active():
self.main_window.reports_scrolled_window.show()
else:
self.main_window.reports_scrolled_window.hide()
- Preferences.reports_window_visibility(visible)
+ action.save_to_preferences()
elif action == Actions.TOGGLE_BLOCKS_WINDOW:
- visible = action.get_active()
- if visible:
+ if action.get_active():
self.main_window.btwin.show()
else:
self.main_window.btwin.hide()
- Preferences.blocks_window_visibility(visible)
+ action.save_to_preferences()
elif action == Actions.TOGGLE_SCROLL_LOCK:
- visible = action.get_active()
- self.main_window.text_display.scroll_lock = visible
- if visible:
+ active = action.get_active()
+ self.main_window.text_display.scroll_lock = active
+ if active:
self.main_window.text_display.scroll_to_end()
+ action.save_to_preferences()
elif action == Actions.CLEAR_REPORTS:
self.main_window.text_display.clear()
elif action == Actions.TOGGLE_HIDE_DISABLED_BLOCKS:
Actions.NOTHING_SELECT()
elif action == Actions.TOGGLE_AUTO_HIDE_PORT_LABELS:
- Preferences.auto_hide_port_labels(action.get_active())
+ action.save_to_preferences()
self.main_window.get_flow_graph().create_shapes()
+ elif action == Actions.TOGGLE_SNAP_TO_GRID:
+ action.save_to_preferences()
##################################################
# Param Modifications
##################################################
@@ -497,7 +501,6 @@ class ActionHandler:
self.main_window.btwin.search_entry.show()
self.main_window.btwin.search_entry.grab_focus()
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)
diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py
index cbd41332ab..c41798aab8 100644
--- a/grc/gui/Actions.py
+++ b/grc/gui/Actions.py
@@ -21,6 +21,8 @@ import pygtk
pygtk.require('2.0')
import gtk
+import Preferences
+
NO_MODS_MASK = 0
########################################################################
@@ -127,7 +129,7 @@ class ToggleAction(gtk.ToggleAction, _ActionBase):
Pass additional arguments such as keypresses.
"""
- def __init__(self, keypresses=(), name=None, label=None, tooltip=None, stock_id=None):
+ def __init__(self, keypresses=(), name=None, label=None, tooltip=None, stock_id=None, preference_name=None):
"""
Create a new ToggleAction instance.
@@ -142,6 +144,15 @@ class ToggleAction(gtk.ToggleAction, _ActionBase):
)
#register this action
_ActionBase.__init__(self, label, keypresses)
+ self.preference_name = preference_name
+
+ def load_from_preferences(self):
+ if self.preference_name is not None:
+ self.set_active(Preferences.bool_entry(self.preference_name))
+
+ def save_to_preferences(self):
+ if self.preference_name is not None:
+ Preferences.bool_entry(self.preference_name, self.get_active())
########################################################################
# Actions
@@ -236,6 +247,11 @@ BLOCK_DISABLE = Action(
stock_id=gtk.STOCK_DISCONNECT,
keypresses=(gtk.keysyms.d, NO_MODS_MASK),
)
+TOGGLE_SNAP_TO_GRID = ToggleAction(
+ label='_Snap to grid',
+ tooltip='Snap blocks to a grid for an easier connection alignment',
+ preference_name='snap_to_grid'
+)
TOGGLE_HIDE_DISABLED_BLOCKS = ToggleAction(
label='Hide _disabled blocks',
tooltip='Toggle visibility of disabled blocks and connections',
@@ -245,6 +261,7 @@ TOGGLE_HIDE_DISABLED_BLOCKS = ToggleAction(
TOGGLE_AUTO_HIDE_PORT_LABELS = ToggleAction(
label='Auto-hide port _labels',
tooltip='Automatically hide port labels',
+ preference_name='auto_hide_port_labels'
)
BLOCK_CREATE_HIER = Action(
label='C_reate Hier',
@@ -279,15 +296,18 @@ TOGGLE_REPORTS_WINDOW = ToggleAction(
label='Show _Reports',
tooltip='Toggle visibility of the Report widget',
keypresses=(gtk.keysyms.r, gtk.gdk.CONTROL_MASK),
+ preference_name='reports_window_visible'
)
TOGGLE_BLOCKS_WINDOW = ToggleAction(
label='Show _Block Tree',
tooltip='Toggle visibility of the block tree widget',
keypresses=(gtk.keysyms.b, gtk.gdk.CONTROL_MASK),
+ preference_name='blocks_window_visible'
)
TOGGLE_SCROLL_LOCK = ToggleAction(
label='_Reports Scroll Lock',
tooltip='Toggle scroll lock for the report window',
+ preference_name='scroll_lock'
)
ABOUT_WINDOW_DISPLAY = Action(
label='_About',
diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py
index c3a2839c5c..743345200e 100644
--- a/grc/gui/Bars.py
+++ b/grc/gui/Bars.py
@@ -99,6 +99,7 @@ MENU_BAR_LIST = (
None,
Actions.TOGGLE_HIDE_DISABLED_BLOCKS,
Actions.TOGGLE_AUTO_HIDE_PORT_LABELS,
+ Actions.TOGGLE_SNAP_TO_GRID,
None,
Actions.ERRORS_WINDOW_DISPLAY,
Actions.FIND_BLOCKS,
diff --git a/grc/gui/Block.py b/grc/gui/Block.py
index 0afb351647..0ae624f94f 100644
--- a/grc/gui/Block.py
+++ b/grc/gui/Block.py
@@ -26,6 +26,7 @@ from Constants import \
BLOCK_LABEL_PADDING, \
PORT_SEPARATION, LABEL_SEPARATION, \
PORT_BORDER_SEPARATION, POSSIBLE_ROTATIONS
+import Actions
import pygtk
pygtk.require('2.0')
import gtk
@@ -43,6 +44,8 @@ class Block(Element):
Block contructor.
Add graphics related params to the block.
"""
+ self.W = 0
+ self.H = 0
#add the position param
self.get_params().append(self.get_parent().get_parent().Param(
block=self,
@@ -97,6 +100,12 @@ class Block(Element):
Args:
coor: the coordinate tuple (x, y)
"""
+ if Actions.TOGGLE_SNAP_TO_GRID.get_active():
+ offset_x, offset_y = (0, self.H/2) if self.is_horizontal() else (self.H/2, 0)
+ coor = (
+ Utils.align_to_grid(coor[0] + offset_x) - offset_x,
+ Utils.align_to_grid(coor[1] + offset_y) - offset_y
+ )
self.get_param('_coordinate').set_value(str(coor))
def get_rotation(self):
@@ -174,14 +183,17 @@ class Block(Element):
Utils.rotate_pixmap(gc, self.horizontal_label, self.vertical_label)
#calculate width and height needed
self.W = self.label_width + 2*BLOCK_LABEL_PADDING
+ def get_min_height_for_ports():
+ visible_ports = filter(lambda p: not p.get_hide(), ports)
+ H = 2*PORT_BORDER_SEPARATION + len(visible_ports) * PORT_SEPARATION
+ if visible_ports: H -= ports[0].H
+ return H
self.H = max(*(
[ # labels
self.label_height + 2 * BLOCK_LABEL_PADDING
] +
[ # ports
- 2 * PORT_BORDER_SEPARATION +
- sum([port.H + PORT_SEPARATION for port in ports if not port.get_hide()]) - PORT_SEPARATION
- for ports in (self.get_sources_gui(), self.get_sinks_gui())
+ get_min_height_for_ports() for ports in (self.get_sources_gui(), self.get_sinks_gui())
] +
[ # bus ports only
4 * PORT_BORDER_SEPARATION +
diff --git a/grc/gui/Constants.py b/grc/gui/Constants.py
index 7fabcfc0a0..c82449b6b6 100644
--- a/grc/gui/Constants.py
+++ b/grc/gui/Constants.py
@@ -53,7 +53,7 @@ BLOCK_LABEL_PADDING = 7
PORT_LABEL_PADDING = 2
#port constraint dimensions
-PORT_SEPARATION = 17
+PORT_SEPARATION = 32
PORT_BORDER_SEPARATION = 9
PORT_MIN_WIDTH = 20
@@ -81,3 +81,6 @@ SCROLL_DISTANCE = 15
#How close the mouse click can be to a line and register a connection select.
LINE_SELECT_SENSITIVITY = 5
+
+# canvas grid size
+CANVAS_GRID_SIZE = 8 \ No newline at end of file
diff --git a/grc/gui/DrawingArea.py b/grc/gui/DrawingArea.py
index 448948e7f4..d22a2c6d5f 100644
--- a/grc/gui/DrawingArea.py
+++ b/grc/gui/DrawingArea.py
@@ -37,6 +37,7 @@ class DrawingArea(gtk.DrawingArea):
main_window: the main_window containing all flow graphs
"""
self.ctrl_mask = False
+ self.mod1_mask = False
self._flow_graph = flow_graph
gtk.DrawingArea.__init__(self)
self.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
@@ -88,6 +89,7 @@ class DrawingArea(gtk.DrawingArea):
"""
self.grab_focus()
self.ctrl_mask = event.state & gtk.gdk.CONTROL_MASK
+ self.mod1_mask = event.state & gtk.gdk.MOD1_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),
@@ -102,6 +104,7 @@ class DrawingArea(gtk.DrawingArea):
Forward button release information to the flow graph.
"""
self.ctrl_mask = event.state & gtk.gdk.CONTROL_MASK
+ self.mod1_mask = event.state & gtk.gdk.MOD1_MASK
if event.button == 1: self._flow_graph.handle_mouse_selector_release(
coordinate=(event.x, event.y),
)
@@ -111,6 +114,7 @@ class DrawingArea(gtk.DrawingArea):
Forward mouse motion information to the flow graph.
"""
self.ctrl_mask = event.state & gtk.gdk.CONTROL_MASK
+ self.mod1_mask = event.state & gtk.gdk.MOD1_MASK
self._flow_graph.handle_mouse_motion(
coordinate=(event.x, event.y),
)
diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py
index bfe8fbfa4f..31017a9923 100644
--- a/grc/gui/FlowGraph.py
+++ b/grc/gui/FlowGraph.py
@@ -84,6 +84,7 @@ class FlowGraph(Element):
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 get_mod1_mask(self): return self.drawing_area.mod1_mask
def new_pixmap(self, *args): return self.get_drawing_area().new_pixmap(*args)
def add_new_block(self, key, coor=None):
@@ -574,7 +575,7 @@ class FlowGraph(Element):
self.create_shapes()
self.queue_draw()
else:
- #perform autoscrolling
+ #perform auto-scrolling
width, height = self.get_size()
x, y = coordinate
h_adj = self.get_scroll_pane().get_hadjustment()
@@ -594,8 +595,12 @@ class FlowGraph(Element):
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))
+ if not self.get_ctrl_mask():
+ X, Y = self.get_coordinate()
+ dX, dY = int(x - X), int(y - Y)
+ active = Actions.TOGGLE_SNAP_TO_GRID.get_active() or self.get_mod1_mask()
+ if not active or abs(dX) >= Utils.CANVAS_GRID_SIZE or abs(dY) >= Utils.CANVAS_GRID_SIZE:
+ self.move_selected((dX, dY))
+ self.set_coordinate((x, y))
#queue draw for animation
self.queue_draw()
diff --git a/grc/gui/Port.py b/grc/gui/Port.py
index b81b162f6e..c56432d2b5 100644
--- a/grc/gui/Port.py
+++ b/grc/gui/Port.py
@@ -70,26 +70,26 @@ class Port(Element):
length = len(filter(lambda p: not p.get_hide(), 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
+ offset = (self.get_parent().H - (length-1)*PORT_SEPARATION - self.H)/2
#create areas and connector coordinates
if (self.is_sink() and rotation == 0) or (self.is_source() and rotation == 180):
x = -1*W
- y = (PORT_SEPARATION+self.H)*index+offset
+ y = PORT_SEPARATION*index+offset
self.add_area((x, y), (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
+ y = PORT_SEPARATION*index+offset
self.add_area((x, y), (W, self.H))
self._connector_coordinate = (x+1+W, y+self.H/2)
elif (self.is_source() and rotation == 90) or (self.is_sink() and rotation == 270):
y = -1*W
- x = (PORT_SEPARATION+self.H)*index+offset
+ x = PORT_SEPARATION*index+offset
self.add_area((x, y), (self.H, 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
+ x = PORT_SEPARATION*index+offset
self.add_area((x, y), (self.H, W))
self._connector_coordinate = (x+self.H/2, y+1+W)
#the connector length
diff --git a/grc/gui/Preferences.py b/grc/gui/Preferences.py
index d2ffc71410..1d6675da32 100644
--- a/grc/gui/Preferences.py
+++ b/grc/gui/Preferences.py
@@ -84,26 +84,11 @@ def blocks_window_position(pos=None):
try: return _config_parser.getint('main', 'blocks_window_position') or 1 #greater than 0
except: return -1
-def reports_window_visibility(visible=None):
- if visible is not None: _config_parser.set('main', 'reports_window_visible', visible)
+def bool_entry(key, active=None, default=True):
+ if active is not None:
+ _config_parser.set('main', key, active)
else:
- try: return _config_parser.getboolean('main', 'reports_window_visible')
- except: return True
-
-def blocks_window_visibility(visible=None):
- if visible is not None: _config_parser.set('main', 'blocks_window_visible', visible)
- else:
- try: return _config_parser.getboolean('main', 'blocks_window_visible')
- except: return True
-
-def scroll_lock(visible=None):
- if visible is not None: _config_parser.set('main', 'scroll_lock', visible)
- else:
- try: return _config_parser.getboolean('main', 'scroll_lock')
- except: return True
-
-def auto_hide_port_labels(hide=None):
- if hide is not None: _config_parser.set('main', 'auto_hide_port_labels', hide)
- else:
- try: return _config_parser.getboolean('main', 'auto_hide_port_labels')
- except: return True
+ try:
+ return _config_parser.getboolean('main', key)
+ except:
+ return default \ No newline at end of file
diff --git a/grc/gui/Utils.py b/grc/gui/Utils.py
index ebd5aefca7..9a0a59cda7 100644
--- a/grc/gui/Utils.py
+++ b/grc/gui/Utils.py
@@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
-from Constants import POSSIBLE_ROTATIONS
+from Constants import POSSIBLE_ROTATIONS, CANVAS_GRID_SIZE
from Cheetah.Template import Template
import pygtk
pygtk.require('2.0')
@@ -108,3 +108,11 @@ def parse_template(tmpl_str, **kwargs):
# print tmpl_str
# print str(kwargs['param'].get_error_messages())
return str(Template(tmpl_str, kwargs))
+
+def align_to_grid(coor):
+ _align = lambda: int(round(x / (1.0 * CANVAS_GRID_SIZE)) * CANVAS_GRID_SIZE)
+ try:
+ return [_align() for x in coor]
+ except TypeError:
+ x = coor
+ return _align() \ No newline at end of file