[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Commit-gnuradio] r9036 - in grc/trunk: . notes src/grc src/grc/elements
From: |
jblum |
Subject: |
[Commit-gnuradio] r9036 - in grc/trunk: . notes src/grc src/grc/elements src/grc/gui src/grc/gui/elements src/grc_gnuradio |
Date: |
Mon, 28 Jul 2008 20:06:09 -0600 (MDT) |
Author: jblum
Date: 2008-07-28 20:06:09 -0600 (Mon, 28 Jul 2008)
New Revision: 9036
Modified:
grc/trunk/Makefile
grc/trunk/notes/todo.txt
grc/trunk/src/grc/ActionHandler.py
grc/trunk/src/grc/elements/Block.py
grc/trunk/src/grc/elements/Element.py
grc/trunk/src/grc/elements/FlowGraph.py
grc/trunk/src/grc/gui/Dialogs.py
grc/trunk/src/grc/gui/elements/Block.py
grc/trunk/src/grc/gui/elements/Element.py
grc/trunk/src/grc/gui/elements/FlowGraph.py
grc/trunk/src/grc/gui/elements/Port.py
grc/trunk/src/grc_gnuradio/Block.py
Log:
multi selection
Modified: grc/trunk/Makefile
===================================================================
--- grc/trunk/Makefile 2008-07-28 23:34:17 UTC (rev 9035)
+++ grc/trunk/Makefile 2008-07-29 02:06:09 UTC (rev 9036)
@@ -21,13 +21,12 @@
DOCS_URL = http://www.ece.jhu.edu/~jblum/downloads/grc_gnuradio_docs.tar.gz
DOCS_DEST = /usr/local/share/doc/
-PREFIX = /usr/local
all:
@echo Options: install, uninstall, docs_install, docs_uninstall
install:
- python setup.py install --prefix=$(PREFIX)
+ python setup.py install
rm -rf build
uninstall:
Modified: grc/trunk/notes/todo.txt
===================================================================
--- grc/trunk/notes/todo.txt 2008-07-28 23:34:17 UTC (rev 9035)
+++ grc/trunk/notes/todo.txt 2008-07-29 02:06:09 UTC (rev 9036)
@@ -10,7 +10,6 @@
-command line option for additional block wrappers
-hotkeys in action descriptions
-log slider gui control
--multi block selection
############ Maybe: ####################
-icons for certain blocks, + for add
@@ -21,7 +20,7 @@
-auto generate hier library
-auto clean hier library
-add hier blocks to tree without restart?
--block looses highlight on nports grow
+-multi select, enable, disable, repeat, undo = error?
############ Suggestions: ####################
-simple usrp
Modified: grc/trunk/src/grc/ActionHandler.py
===================================================================
--- grc/trunk/src/grc/ActionHandler.py 2008-07-28 23:34:17 UTC (rev 9035)
+++ grc/trunk/src/grc/ActionHandler.py 2008-07-29 02:06:09 UTC (rev 9036)
@@ -205,10 +205,12 @@
##############################################################################################
elif state == 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 state == 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)
##############################################################################################
@@ -218,11 +220,13 @@
self.handle_states(BLOCK_COPY)
self.handle_states(ELEMENT_DELETE)
elif state == BLOCK_COPY:
- block = self.get_flow_graph().get_selected_block()
- if block: self.clipboard = block.export_data()
- elif state == BLOCK_PASTE:
+ self.clipboard =
self.get_flow_graph().copy_to_clipboard()
+ elif state == BLOCK_PASTE:
if self.clipboard:
- block =
self.get_flow_graph().add_new_block(key=self.clipboard['key'], n=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)
##############################################################################################
# Move/Rotate/Delete/Create
##############################################################################################
@@ -248,18 +252,24 @@
self.get_page().set_saved(False)
elif state == 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 state == 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 state == PORT_CONTROLLER_INC:
if
self.get_flow_graph().port_controller_modify_selected(1):
+ self.get_flow_graph().update()
+ self.get_flow_graph().update() #2 times
self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
self.get_page().set_saved(False)
elif state == PORT_CONTROLLER_DEC:
if
self.get_flow_graph().port_controller_modify_selected(-1):
+ self.get_flow_graph().update()
+ self.get_flow_graph().update() #2 times
self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data())
self.get_page().set_saved(False)
##############################################################################################
@@ -361,20 +371,24 @@
# Global Actions for all States
##############################################################################################
#update general buttons
-
get_action_from_name(ELEMENT_DELETE).set_sensitive(bool(self.get_flow_graph().get_selected_element()))
+
get_action_from_name(ELEMENT_DELETE).set_sensitive(bool(self.get_flow_graph().get_selected_elements()))
get_action_from_name(BLOCK_PARAM_MODIFY).set_sensitive(bool(self.get_flow_graph().get_selected_block()))
-
get_action_from_name(BLOCK_ROTATE_RIGHT).set_sensitive(bool(self.get_flow_graph().get_selected_block()))
-
get_action_from_name(BLOCK_ROTATE_LEFT).set_sensitive(bool(self.get_flow_graph().get_selected_block()))
+
get_action_from_name(BLOCK_ROTATE_RIGHT).set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
+
get_action_from_name(BLOCK_ROTATE_LEFT).set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
#update cut/copy/paste
-
get_action_from_name(BLOCK_CUT).set_sensitive(bool(self.get_flow_graph().get_selected_block()))
-
get_action_from_name(BLOCK_COPY).set_sensitive(bool(self.get_flow_graph().get_selected_block()))
+
get_action_from_name(BLOCK_CUT).set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
+
get_action_from_name(BLOCK_COPY).set_sensitive(bool(self.get_flow_graph().get_selected_blocks()))
get_action_from_name(BLOCK_PASTE).set_sensitive(bool(self.clipboard))
#update enable/disable
get_action_from_name(BLOCK_ENABLE).set_sensitive(bool(
- self.get_flow_graph().get_selected_block() and not
self.get_flow_graph().get_selected_block().get_enabled()
+ self.get_flow_graph().get_selected_blocks() or \
+ self.get_flow_graph().get_selected_block() and \
+ not
self.get_flow_graph().get_selected_block().get_enabled()
))
get_action_from_name(BLOCK_DISABLE).set_sensitive(bool(
- self.get_flow_graph().get_selected_block() and
self.get_flow_graph().get_selected_block().get_enabled()
+ self.get_flow_graph().get_selected_blocks() or \
+ self.get_flow_graph().get_selected_block() and \
+ self.get_flow_graph().get_selected_block().get_enabled()
))
#set the exec and stop buttons
self.update_exec_stop()
Modified: grc/trunk/src/grc/elements/Block.py
===================================================================
--- grc/trunk/src/grc/elements/Block.py 2008-07-28 23:34:17 UTC (rev 9035)
+++ grc/trunk/src/grc/elements/Block.py 2008-07-29 02:06:09 UTC (rev 9036)
@@ -171,6 +171,8 @@
def get_doc(self): return ''
+ def get_ports(self): return self.get_sources() + self.get_sinks()
+
##############################################
# Access Params
##############################################
@@ -193,7 +195,7 @@
def get_sources(self): return self._sources.values()
def get_connections(self):
- return sum([port.get_connections() for port in
self.get_sources() + self.get_sinks()], [])
+ return sum([port.get_connections() for port in
self.get_ports()], [])
def resolve_dependencies(self, tmpl):
"""
Modified: grc/trunk/src/grc/elements/Element.py
===================================================================
--- grc/trunk/src/grc/elements/Element.py 2008-07-28 23:34:17 UTC (rev
9035)
+++ grc/trunk/src/grc/elements/Element.py 2008-07-29 02:06:09 UTC (rev
9036)
@@ -85,19 +85,13 @@
##############################################
## 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
+
Modified: grc/trunk/src/grc/elements/FlowGraph.py
===================================================================
--- grc/trunk/src/grc/elements/FlowGraph.py 2008-07-28 23:34:17 UTC (rev
9035)
+++ grc/trunk/src/grc/elements/FlowGraph.py 2008-07-29 02:06:09 UTC (rev
9036)
@@ -129,11 +129,11 @@
self.flag()
if element not in self.get_elements(): return
#found a port, set to parent signal block
- if element.is_source() or element.is_sink():
+ if element.is_port():
element = element.get_parent()
#remove block, remove all involved connections
if element.is_block():
- for port in element.get_sources() + element.get_sinks():
+ for port in element.get_ports():
map(lambda c: self.remove_element(c),
port.get_connections())
#remove a connection
elif element.is_connection(): pass
Modified: grc/trunk/src/grc/gui/Dialogs.py
===================================================================
--- grc/trunk/src/grc/gui/Dialogs.py 2008-07-28 23:34:17 UTC (rev 9035)
+++ grc/trunk/src/grc/gui/Dialogs.py 2008-07-29 02:06:09 UTC (rev 9036)
@@ -24,6 +24,7 @@
pygtk.require('2.0')
import gtk
from grc.Constants import *
+from grc import Preferences
class TextDisplay(gtk.TextView):
"""A non editable gtk text view."""
@@ -48,7 +49,6 @@
def __init__(self):
"""PreferencesDialog constructor."""
- from grc import Preferences
gtk.Dialog.__init__(self, buttons=('gtk-close',
gtk.RESPONSE_CLOSE))
self.set_title("Preferences")
self.set_size_request(MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT)
Modified: grc/trunk/src/grc/gui/elements/Block.py
===================================================================
--- grc/trunk/src/grc/gui/elements/Block.py 2008-07-28 23:34:17 UTC (rev
9035)
+++ grc/trunk/src/grc/gui/elements/Block.py 2008-07-29 02:06:09 UTC (rev
9036)
@@ -20,6 +20,7 @@
#The graphical signal block.
address@hidden Josh Blum
+from grc import Preferences
from Element import Element
import Utils
import Colors
@@ -130,7 +131,6 @@
layout.set_font_description(desc)
self.label_width,self.label_height = layout.get_pixel_size()
#display the params (except for the special params id and
position)
- from grc import Preferences
if Preferences.show_params():
for param in filter(lambda p: not
(p.get_key().startswith('gui_') or p.get_key().startswith('_')),
self.get_params()):
if not Preferences.show_id() and
param.get_key() == 'id': continue
@@ -175,16 +175,17 @@
window.draw_image(gc, self.horizontal_label, 0, 0,
X+LABEL_PADDING_WIDTH, Y+(self.H-self.label_height)/2, -1, -1)
elif self.is_vertical():
window.draw_image(gc, self.vertical_label, 0, 0,
X+(self.H-self.label_height)/2, Y+LABEL_PADDING_WIDTH, -1, -1)
- map(lambda p: p.draw(window), self.get_sources() +
self.get_sinks())
+ map(lambda p: p.draw(window), self.get_ports())
- def what_is_selected(self, coor):
+ def what_is_selected(self, coor, coor_m=None):
"""!
Get the element that is selected.
@param coor the (x,y) tuple
- @return this signal block, a port, or None
+ @param coor_m the (x_m, y_m) tuple
+ @return this block, a port, or None
"""
- for port in self.get_sources() + self.get_sinks():
- if port.what_is_selected(coor):
- return port.what_is_selected(coor)
- return Element.what_is_selected(self, coor)
+ for port in self.get_ports():
+ port_selected = port.what_is_selected(coor, coor_m)
+ if port_selected: return port_selected
+ return Element.what_is_selected(self, coor, coor_m)
Modified: grc/trunk/src/grc/gui/elements/Element.py
===================================================================
--- grc/trunk/src/grc/gui/elements/Element.py 2008-07-28 23:34:17 UTC (rev
9035)
+++ grc/trunk/src/grc/gui/elements/Element.py 2008-07-29 02:06:09 UTC (rev
9036)
@@ -148,42 +148,72 @@
def add_area(self, rel_coor, area, rotation=None):
"""!
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. If rotation is not
specified, the element's current rotation is used.
+ 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.
+ If rotation is not specified, the element's current rotation is
used.
@param rel_coor (x,y) offset from this element's coordinate
@param area (width,height) tuple
@param rotation rotation in degrees
"""
- if rotation == None: rotation = self.get_rotation()
- self.areas_dict[rotation].append((rel_coor, area))
+ self.areas_dict[rotation or
self.get_rotation()].append((rel_coor, area))
def add_line(self, rel_coor1, rel_coor2, rotation=None):
"""!
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. If rotation is not
specified, the element's current rotation is used.
+ A line is defined by 2 relative coordinates.
+ Lines must be horizontal or vertical.
+ The line is associated with a rotation.
+ If rotation is not specified, the element's current rotation is
used.
@param rel_coor1 relative (x1,y1) tuple
@param rel_coor2 relative (x2,y2) tuple
@param rotation rotation in degrees
"""
- if rotation == None: rotation = self.get_rotation()
- self.lines_dict[rotation].append((rel_coor1, rel_coor2))
+ self.lines_dict[rotation or
self.get_rotation()].append((rel_coor1, rel_coor2))
- def what_is_selected(self, coor):
+ def what_is_selected(self, coor, coor_m=None):
"""!
- Is this Element selected at given coordinate/is the coordinate
encompassed by one of the areas or lines?
+ 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?
+ @param coor the selection coordinate, tuple x, y
+ @param coor_m an additional selection coordinate.
@return 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)
- x,y = [a-b for a,b in zip(coor, self.get_coordinate())]
- for (x1,y1), (w,h) in self.areas_dict[self.get_rotation()]:
- if in_between(x, x1, x1+w) and in_between(y, y1, y1+h):
return self
- for (x1, y1), (x2, y2) in self.lines_dict[self.get_rotation()]:
#assume horizontal or vertical lines
- if x1 == x2: x1, x2 = x1-CONNECTION_SELECT_SENSITIVITY,
x2+CONNECTION_SELECT_SENSITIVITY
- if y1 == y2: y1, y2 = y1-CONNECTION_SELECT_SENSITIVITY,
y2+CONNECTION_SELECT_SENSITIVITY
- if in_between(x, x1, x2) and in_between(y, y1, y2):
return self
- return None
+ #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_dict[self.get_rotation()]:
+ 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_dict[self.get_rotation()]:
+ 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_dict[self.get_rotation()]:
+ 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_dict[self.get_rotation()]:
+ if x1 == x2: x1, x2 =
x1-CONNECTION_SELECT_SENSITIVITY, x2+CONNECTION_SELECT_SENSITIVITY
+ if y1 == y2: y1, y2 =
y1-CONNECTION_SELECT_SENSITIVITY, y2+CONNECTION_SELECT_SENSITIVITY
+ if in_between(x, x1, x2) and in_between(y, y1,
y2): return self
+ return None
def get_rotation(self):
"""!
Modified: grc/trunk/src/grc/gui/elements/FlowGraph.py
===================================================================
--- grc/trunk/src/grc/gui/elements/FlowGraph.py 2008-07-28 23:34:17 UTC (rev
9035)
+++ grc/trunk/src/grc/gui/elements/FlowGraph.py 2008-07-29 02:06:09 UTC (rev
9036)
@@ -20,10 +20,11 @@
#A flow graph structure for storing signal blocks and their connections.
address@hidden Josh Blum
+from grc import Preferences
from grc import Utils
from grc.Constants import *
from grc.Actions import *
-from Colors import BACKGROUND_COLOR, TXT_COLOR
+import Colors
from grc.gui.ParamsDialog import ParamsDialog
from Element import Element
from grc.elements import FlowGraph as _FlowGraph
@@ -49,78 +50,143 @@
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.has_moved = False
+ self.element_moved = False
self.mouse_pressed = False
- self._selected_element = None
- self.is_selected = lambda: bool(self.get_selected_element())
+ self.unselect()
self.time = 0
+ self.press_coor = (0, 0)
+ def _get_unique_id(self, base_id=''):
+ """!
+ Get a unique id starting with the base id.
+ @param base_id the id starts with this and appends a count
+ @return a unique id
+ """
+ index = -1
+ while True:
+ id = (index < 0) and base_id or '%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
+
+###########################################################################
+# Access Drawing Area
+###########################################################################
def get_drawing_area(self): return self.drawing_area
-
def get_gc(self): return self.get_drawing_area().gc
-
def get_pixmap(self): return self.get_drawing_area().pixmap
-
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_window(self): return self.get_drawing_area().window
-
def get_scroll_pane(self): return self.drawing_area.get_parent()
-###########################################################################
-# Flow Graph Access Methods
-###########################################################################
-
- def add_new_block(self, key, n=None):
+ def add_new_block(self, key):
"""!
Add a block of the given key to this flow graph.
@param key the block key
- @param n nested data preloader for block
"""
- index = -1
- while True:
- id = (index < 0) and key or '%s%d'%(key, index)
- index = index + 1
- #make sure that the id is not used by another block
- if not filter(lambda e: e.is_block() and e.get_id() ==
id, self.get_elements()):
- v_val =
self.get_scroll_pane().get_vadjustment().get_value()
- h_val =
self.get_scroll_pane().get_hadjustment().get_value()
- x = random.randint(100,200) + int(h_val)
- y = random.randint(100,200) + int(v_val)
- #get the new block
- block = self.get_new_block(key)
- block.set_coordinate((x, y))
- block.set_rotation(0)
- block.get_param('id').set_value(id)
- #set params from n
- if n:
- params_n = Utils.listify(n, 'param')
- for param_n in params_n:
- key = param_n['key']
- value = param_n['value']
- if not (key.startswith('_') or
key.startswith('gui') or key == 'id'):
-
block.get_param(key).set_value(value)
- #handle new state
- self.handle_states(ELEMENT_CREATE)
- self.update()
- return
+ 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()
+ x = random.randint(h_adj.page_size/4, 3*h_adj.page_size/4) +
h_adj.get_value()
+ y = random.randint(v_adj.page_size/4, 3*v_adj.page_size/4) +
v_adj.get_value()
+ #get the new block
+ block = self.get_new_block(key)
+ block.set_coordinate((x, y))
+ block.set_rotation(0)
+ block.get_param('id').set_value(id)
+ self.handle_states(ELEMENT_CREATE)
+
###########################################################################
+ # Copy Paste
+
###########################################################################
+ def copy_to_clipboard(self):
+ """!
+ Copy the selected blocks and connections into the clipboard.
+ @return 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.
+ @param 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['key']
+ if block_key == 'options': continue
+ block_id = self._get_unique_id(block_key)
+ block = self.get_new_block(block_key)
+ selected.add(block)
+ #set params
+ params_n = Utils.listify(block_n, 'param')
+ for param_n in params_n:
+ param_key = param_n['key']
+ param_value = param_n['value']
+ #setup id parameter
+ if param_key == 'id':
+ old_id2block[param_value] = block
+ param_value = block_id
+ #set value to key
+
block.get_param(param_key).set_value(param_value)
+ #move block to offset coordinate
+ block.move((x_off, y_off))
+ #create connections
+ for connection_n in connections_n:
+ source =
old_id2block[connection_n['source_block_id']].get_source(connection_n['source_key'])
+ sink =
old_id2block[connection_n['sink_block_id']].get_sink(connection_n['sink_key'])
+ self.connect(source, sink)
+ #set all pasted elements selected
+ for block in selected: selected =
selected.union(set(block.get_connections()))
+ self._selected_group = list(selected)
+
+
###########################################################################
+ # Modify Selected
+
###########################################################################
def type_controller_modify_selected(self, direction):
"""
- !Change the registered type controller for the selected signal
block.
+ !Change the registered type controller for the selected signal
blocks.
@param direction +1 or -1
- @return true for success
+ @return true for change
"""
- if self.get_selected_block():
- for child in self.get_selected_block().get_params() + \
- self.get_selected_block().get_sources() + \
- self.get_selected_block().get_sinks():
+ changed = False
+ for selected_block in self.get_selected_blocks():
+ for child in selected_block.get_params() +
selected_block.get_ports():
#find a param that controls a type
type_param = None
- for param in
self.get_selected_block().get_params():
+ for param in selected_block.get_params():
if not type_param and param.is_enum():
type_param = param
if param.is_enum() and param.get_key()
in child._type: type_param = param
if type_param:
@@ -130,22 +196,22 @@
old_index =
keys.index(type_param.get_value())
new_index = (old_index +
direction + len(keys))%len(keys)
type_param.set_value(keys[new_index])
- self.update()
- return True
- except: return False
- return False
+ changed = True
+ except: pass
+ return changed
def port_controller_modify_selected(self, direction):
"""!
- Change port controller for the selected signal block.
+ Change port controller for the selected signal blocks.
@param direction +1 or -1
- @return true for success
+ @return true for changed
"""
- if self.get_selected_block():
- for ports in (self.get_selected_block().get_sources(),
self.get_selected_block().get_sinks()):
+ changed = False
+ for selected_block in self.get_selected_blocks():
+ for ports in (selected_block.get_sources(),
selected_block.get_sinks()):
if ports and hasattr(ports[0], 'get_nports')
and ports[0].get_nports():
#find the param that controls port0
- for param in
self.get_selected_block().get_params():
+ for param in
selected_block.get_params():
if param.get_key() in
ports[0]._nports:
#try to increment the
port controller by direction
try:
@@ -153,134 +219,104 @@
value = value +
direction
assert(0 <
value <= MAX_NUM_PORTS)
param.set_value(value)
- self.update()
- return True
- except: return False
- return False
+ changed = True
+ except: pass
+ return changed
def param_modify_selected(self):
"""!
- Create and show a param modification dialog for the selected
element (port and signal block only).
+ Create and show a param modification dialog for the selected
block.
@return true if parameters were changed
"""
if self.get_selected_block():
signal_block_params_dialog =
ParamsDialog(self.get_selected_block())
- changed = signal_block_params_dialog.run()
- return changed #changes were made?
+ return signal_block_params_dialog.run()
return False
def enable_selected(self, enable):
"""!
- Enable/disable the selected block.
+ Enable/disable the selected blocks.
@param enable true to enable
@return true if changed
"""
- if self.get_selected_block():
- if self.get_selected_block().get_enabled() == enable:
return False
- self.get_selected_block().set_enabled(enable)
- self.update()
- return True
- return False
+ changed = False
+ for selected_block in self.get_selected_blocks():
+ if selected_block.get_enabled() != enable: changed =
True
+ selected_block.set_enabled(enable)
+ changed = True
+ return changed
def move_selected(self, delta_coordinate):
"""!
Move the element and by the change in coordinates.
@param delta_coordinate the change in coordinates
"""
- if self.get_selected_element():
- self.get_selected_element().move(delta_coordinate)
- self.has_moved = True
- self.draw()
+ for selected_block in self.get_selected_blocks():
+ selected_block.move(delta_coordinate)
+ self.element_moved = True
def rotate_selected(self, direction):
"""!
- Rotate the selected element by 90 degrees. Only rotate
SignalBlocks and Sockets.
+ Rotate the selected blocks by 90 degrees.
@param direction DIR_LEFT or DIR_RIGHT
- @return true if rotated, otherwise false.
+ @return true if changed, otherwise false.
"""
- if self.get_selected_element() and
(self.get_selected_element().is_block() or \
- self.get_selected_element().is_source() or
self.get_selected_element().is_sink()):
- self.get_selected_element().rotate(direction)
- self.draw()
- return True
- return False
+ changed = False
+ for selected_block in self.get_selected_blocks():
+ selected_block.rotate(direction)
+ changed = True
+ return changed
def remove_selected(self):
"""!
- If an element is selected, remove it and its attached parts
from the element list.
- @return true if the element was deleted, otherwise false.
+ Remove selected elements
+ @return true if changed.
"""
- if self.get_selected_element():
- self.remove_element(self.get_selected_element())
- self.unselect()
- return True
- return False
+ changed = False
+ for selected_element in self.get_selected_blocks() +
self.get_selected_connections():
+ self.remove_element(selected_element)
+ changed = True
+ return changed
- def unselect(self):
- """Set selected element to None."""
- self._selected_element = None
-
- def what_is_selected(self, coor):
+ def draw(self):
"""!
- What is selected?
- At the given coordinate, return 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.
- @param coor the coordinate of the mouse click
- @return the selected element or None
+ 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.
"""
- #check the elements
- for element in reversed(self.get_elements()):
- if element.what_is_selected(coor):
- self.get_elements().remove(element)
- self.get_elements().append(element)
- return element.what_is_selected(coor)
- return None
-
- def get_selected_element(self):
- """!
- Get the selected element.
- @return a block, port, or connection or None
- """
- return self._selected_element
-
- def get_selected_block(self):
- """!
- Get the selected block when a block or port is selected.
- @return a block or None
- """
- #determine the selected block or None
- if self.get_selected_element() and
self.get_selected_element().is_block():
- return self.get_selected_element()
- elif self.get_selected_element() and
(self.get_selected_element().is_source() or
self.get_selected_element().is_sink()):
- return self.get_selected_element().get_parent()
- else: return None
-
- def draw(self):
- """Draw the background and then all of the Elements in this
FlowGraph on the pixmap,
- then draw the pixmap to the drawable window of this
FlowGraph."""
if self.get_gc():
W,H = self.get_size()
#draw the background
- self.get_gc().foreground = BACKGROUND_COLOR
+ self.get_gc().foreground = Colors.BACKGROUND_COLOR
self.get_pixmap().draw_rectangle(self.get_gc(), True,
0, 0, W, H)
#draw grid (depends on prefs)
- from grc import Preferences
if Preferences.show_grid():
grid_size = Preferences.get_grid_size()
points = list()
for i in range(W/grid_size):
for j in range(H/grid_size):
points.append((i*grid_size,
j*grid_size))
- self.get_gc().foreground = TXT_COLOR
+ self.get_gc().foreground = Colors.TXT_COLOR
self.get_pixmap().draw_points(self.get_gc(),
points)
+ #draw multi select rectangle
+ if self.mouse_pressed and not
self.get_selected_elements():
+ #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
+ self.get_gc().foreground = Colors.H_COLOR
+ self.get_pixmap().draw_rectangle(self.get_gc(),
True, x, y, w, h)
+ self.get_gc().foreground = Colors.TXT_COLOR
+ self.get_pixmap().draw_rectangle(self.get_gc(),
False, x, y, w, h)
#draw blocks on top of connections
for element in self.get_connections() +
self.get_blocks():
element.draw(self.get_pixmap())
#draw any selected element as the topmost
- if self.get_selected_element():
-
self.get_selected_element().draw(self.get_pixmap())
+ for selected_element in self.get_selected_blocks() +
self.get_selected_connections():
+ selected_element.draw(self.get_pixmap())
self.get_drawing_area().draw()
def update(self):
@@ -291,8 +327,8 @@
"""
#update highlighting
map(lambda e: e.set_highlighted(False), self.get_elements())
- if self.get_selected_element():
- self.get_selected_element().set_highlighted(True)
+ for selected_element in self.get_selected_elements():
+ selected_element.set_highlighted(True)
#update all elements
map(lambda e: e.update(), self.get_elements())
#set the size of the flow graph area
@@ -302,9 +338,81 @@
if new_x != old_x or new_y != old_y: self.set_size(new_x, new_y)
##########################################################################
- ## Handlers
+ ## Get Selected
##########################################################################
+ def unselect(self):
+ """Set selected element to None."""
+ self._selected_group = []
+ 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.
+ @param coor the coordinate of the mouse click
+ @param coor_m the coordinate for multi select
+ @return the selected elements or empty list
+ """
+ selected = set()
+ #check the elements
+ for element in reversed(self.get_elements()):
+ selected_element = element.what_is_selected(coor,
coor_m)
+ if selected_element:
+ if not coor_m:
+ self.get_elements().remove(element)
+ self.get_elements().append(element)
+ return [selected_element]
+ selected.add(selected_element)
+ return list(selected)
+
+ def get_selected_connections(self):
+ """!
+ Get a group of selected connections.
+ @return 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.
+ @return sub set of blocks in this flow graph
+ """
+ selected = set()
+ for selected_element in self.get_selected_elements():
+ if selected_element.is_port():
selected.add(selected_element.get_parent())
+ elif 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.
+ @return 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.
+ @return sub set of elements in this flow graph
+ """
+ return self._selected_group
+
+ def get_selected_element(self):
+ """!
+ Get the selected element.
+ @return a block, port, or connection or None
+ """
+ return self.get_selected_elements() and
self.get_selected_elements()[0] or None
+
+
##########################################################################
+ ## Event Handlers
+
##########################################################################
def handle_mouse_button_press(self, left_click, double_click,
coordinate):
"""!
A mouse button is pressed, only respond to left clicks.
@@ -312,17 +420,24 @@
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)
if left_click:
self.time = 0
self.mouse_pressed = True
old_selection = self.get_selected_element()
- self._selected_element =
self.what_is_selected(self.get_coordinate())
+ new_selection =
self.what_is_selected(self.get_coordinate())
+ #update the selections if the new selection is not in
the current blocks/connections
+ #allows us to move entire selected groups of
blocks/connections
+ if not (
+ new_selection and new_selection[0] in \
+ self.get_selected_blocks() +
self.get_selected_connections()
+ ): self._selected_group = new_selection
#this selection and the last were ports, try to connect
them
if old_selection and self.get_selected_element() and \
old_selection is not
self.get_selected_element() and \
- (self.get_selected_element().is_source() or
self.get_selected_element().is_sink()) and \
- (old_selection.is_source() or
old_selection.is_sink()):
+ self.get_selected_element().is_port() and \
+ old_selection.is_port():
try:
self.connect(old_selection,
self.get_selected_element())
self.handle_states(ELEMENT_CREATE)
@@ -342,8 +457,7 @@
if left_click:
self.time = 0
self.mouse_pressed = False
- if self.has_moved:
- from grc import Preferences
+ if self.element_moved:
if Preferences.snap_to_grid():
grid_size = Preferences.get_grid_size()
X,Y =
self.get_selected_element().get_coordinate()
@@ -355,7 +469,11 @@
else: deltaY = grid_size - deltaY
self.move_selected((deltaX, deltaY))
self.handle_states(BLOCK_MOVE)
- self.has_moved = False
+ self.element_moved = False
+ elif not self.element_moved and not
self.get_selected_elements():
+ self._selected_group =
self.what_is_selected(self.get_coordinate(), self.press_coor)
+ self.update()
+ self.draw()
def handle_mouse_motion(self, coordinate):
"""!
@@ -363,14 +481,13 @@
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, an element
selected, timediff large enough.
- if time.time() - self.time >=
MOTION_DETECT_REDRAWING_SENSITIVITY and \
- self.mouse_pressed and self.get_selected_element():
+ #to perform a movement, the mouse must be pressed, timediff
large enough.
+ if time.time() - self.time >=
MOTION_DETECT_REDRAWING_SENSITIVITY and self.mouse_pressed:
#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()
- h_adj = self.get_scroll_pane().get_hadjustment()
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),
@@ -385,6 +502,7 @@
#move the selected element and record the new coordinate
X, Y = self.get_coordinate()
self.move_selected((int(x - X), int(y - Y)))
+ self.draw()
self.set_coordinate((x, y))
#update time
self.time = time.time()
Modified: grc/trunk/src/grc/gui/elements/Port.py
===================================================================
--- grc/trunk/src/grc/gui/elements/Port.py 2008-07-28 23:34:17 UTC (rev
9035)
+++ grc/trunk/src/grc/gui/elements/Port.py 2008-07-29 02:06:09 UTC (rev
9036)
@@ -29,64 +29,64 @@
import pango
class Port(Element):
- """The graphical port."""
-
+ """The graphical port."""
+
def __init__(self, *args, **kwargs):
"""!
Port contructor.
Create list of connector coordinates.
- """
+ """
Element.__init__(self)
self.connector_coordinates = dict()
-
+
def update(self):
"""Create new areas and labels for the port."""
self.clear()
self.BG_color = Colors.get_color(self.get_color())
- self._create_labels()
+ self._create_labels()
#get current rotation
- rotation = self.get_rotation()
- #get all sibling ports
+ rotation = self.get_rotation()
+ #get all sibling ports
if self.is_source(): ports = self.get_parent().get_sources()
- elif self.is_sink(): ports = self.get_parent().get_sinks()
+ elif self.is_sink(): ports = self.get_parent().get_sinks()
#get a numeric index for this port relative to its sibling ports
index = ports.index(self)
- length = len(ports)
- #reverse the order of ports for these rotations
+ 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*PORT_HEIGHT -
(length-1)*PORT_SEPARATION)/2
+ offset = (self.get_parent().H - length*PORT_HEIGHT -
(length-1)*PORT_SEPARATION)/2
#create areas and connector coordinates
- if (self.is_sink() and rotation == 0) or (self.is_source() and
rotation == 180):
+ if (self.is_sink() and rotation == 0) or (self.is_source() and
rotation == 180):
x = -1*PORT_WIDTH
y = (PORT_SEPARATION+PORT_HEIGHT)*index+offset
self.add_area((x, y), (PORT_WIDTH, PORT_HEIGHT))
self._connector_coordinate = (x-1, y+PORT_HEIGHT/2)
- elif (self.is_source() and rotation == 0) or (self.is_sink()
and rotation == 180):
+ elif (self.is_source() and rotation == 0) or (self.is_sink()
and rotation == 180):
x = self.get_parent().W
y = (PORT_SEPARATION+PORT_HEIGHT)*index+offset
self.add_area((x, y), (PORT_WIDTH, PORT_HEIGHT))
self._connector_coordinate = (x+1+PORT_WIDTH,
y+PORT_HEIGHT/2)
- elif (self.is_source() and rotation == 90) or (self.is_sink()
and rotation == 270):
+ elif (self.is_source() and rotation == 90) or (self.is_sink()
and rotation == 270):
y = -1*PORT_WIDTH
x = (PORT_SEPARATION+PORT_HEIGHT)*index+offset
self.add_area((x, y), (PORT_HEIGHT, PORT_WIDTH))
self._connector_coordinate = (x+PORT_HEIGHT/2, y-1)
- elif (self.is_sink() and rotation == 90) or (self.is_source()
and rotation == 270):
+ elif (self.is_sink() and rotation == 90) or (self.is_source()
and rotation == 270):
y = self.get_parent().W
x = (PORT_SEPARATION+PORT_HEIGHT)*index+offset
self.add_area((x, y), (PORT_HEIGHT, PORT_WIDTH))
self._connector_coordinate = (x+PORT_HEIGHT/2,
y+1+PORT_WIDTH)
#the connector length
self._connector_length = CONNECTOR_EXTENSION_INITIAL_LENGTH +
CONNECTOR_EXTENSION_LENGTH*index
-
+
def _create_labels(self):
"""Create the labels for the socket."""
#create the layout
layout = gtk.DrawingArea().create_pango_layout(self.get_name())
desc = pango.FontDescription(PORT_FONT)
- layout.set_font_description(desc)
- w,h = self.w,self.h = layout.get_pixel_size()
- #create the pixmap
+ layout.set_font_description(desc)
+ w,h = self.w,self.h = layout.get_pixel_size()
+ #create the pixmap
pixmap =
gtk.gdk.Pixmap(self.get_parent().get_parent().get_window(), w, h, -1)
gc = pixmap.new_gc()
gc.foreground = self.BG_color
@@ -94,46 +94,46 @@
gc.foreground = Colors.TXT_COLOR
pixmap.draw_layout(gc, 0, 0, layout)
#create the images
- self.horizontal_label = image = pixmap.get_image(0, 0, w, h)
- if self.is_vertical():
+ self.horizontal_label = image = pixmap.get_image(0, 0, w, h)
+ if self.is_vertical():
self.vertical_label = vimage =
gtk.gdk.Image(gtk.gdk.IMAGE_NORMAL, pixmap.get_visual(), h, w)
for i in range(w):
for j in range(h): vimage.put_pixel(j, w-i-1,
image.get_pixel(i, j))
-
+
def draw(self, window):
"""!
Draw the socket with a label.
@param window the gtk window to draw on
"""
- Element.draw(self, window, BG_color=self.BG_color)
- gc = self.get_gc()
+ Element.draw(self, window, BG_color=self.BG_color)
+ gc = self.get_gc()
gc.foreground = Colors.TXT_COLOR
X,Y = self.get_coordinate()
(x,y),(w,h) = self.areas_dict[self.get_rotation()][0] #use the
first area's sizes to place the labels
- if self.is_horizontal():
+ if self.is_horizontal():
window.draw_image(gc, self.horizontal_label, 0, 0,
x+X+(PORT_WIDTH-self.w)/2, y+Y+(PORT_HEIGHT-self.h)/2, -1, -1)
- elif self.is_vertical():
+ elif self.is_vertical():
window.draw_image(gc, self.vertical_label, 0, 0,
x+X+(PORT_HEIGHT-self.h)/2, y+Y+(PORT_WIDTH-self.w)/2, -1, -1)
-
+
def get_connector_coordinate(self):
"""!
Get the coordinate where connections may attach to.
@return the connector coordinate (x, y) tuple
"""
x,y = self._connector_coordinate
- X,Y = self.get_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.
@return the direction in degrees
- """
+ """
if self.is_source(): return self.get_rotation()
- elif self.is_sink(): return (self.get_rotation() + 180)%360
-
+ elif self.is_sink(): return (self.get_rotation() + 180)%360
+
def get_connector_length(self):
"""!
Get the length of the connector.
@@ -141,45 +141,45 @@
@return the length in pixels
"""
return self._connector_length
-
+
def get_rotation(self):
"""!
Get the parent's rotation rather than self.
@return the parent's rotation
"""
return self.get_parent().get_rotation()
-
+
def move(self, delta_coor):
"""!
Move the parent rather than self.
@param delta_corr the (delta_x, delta_y) tuple
"""
self.get_parent().move(delta_coor)
-
+
def rotate(self, direction):
"""!
Rotate the parent rather than self.
@param direction degrees to rotate
"""
self.get_parent().rotate(direction)
-
+
def get_coordinate(self):
"""!
Get the parent's coordinate rather than self.
@return the parents coordinate
"""
return self.get_parent().get_coordinate()
-
+
def set_highlighted(self, highlight):
"""!
Set the parent highlight rather than self.
@param highlight true to enable highlighting
"""
self.get_parent().set_highlighted(highlight)
-
+
def is_highlighted(self):
"""!
Get the parent's is highlight rather than self.
@return the parent's highlighting status
"""
- return self.get_parent().is_highlighted()
+ return self.get_parent().is_highlighted()
Modified: grc/trunk/src/grc_gnuradio/Block.py
===================================================================
--- grc/trunk/src/grc_gnuradio/Block.py 2008-07-28 23:34:17 UTC (rev 9035)
+++ grc/trunk/src/grc_gnuradio/Block.py 2008-07-29 02:06:09 UTC (rev 9036)
@@ -97,7 +97,6 @@
self.get_parent().remove_element(connection)
#remove the ports
for key in map(str, range(nports, num_ports)):
ports.pop(key)
- self.get_parent().is_valid() #FIXME:
inappropriate solution
continue
#add more ports
if nports > num_ports:
@@ -106,7 +105,6 @@
n['key'] = key
port = Port(self, n)
ports[key] = port
- self.get_parent().is_valid() #FIXME:
inappropriate solution
continue
def get_doc(self):
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Commit-gnuradio] r9036 - in grc/trunk: . notes src/grc src/grc/elements src/grc/gui src/grc/gui/elements src/grc_gnuradio,
jblum <=