From 9988664127b367fa8fee4409f8460673d6f265e1 Mon Sep 17 00:00:00 2001 From: jblum Date: Tue, 23 Jun 2009 20:38:18 +0000 Subject: Merging r11186:11273 from grc branch. Fixes, features, and reorganization for grc. Minor fixes and features for wxgui forms. git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@11274 221aa14e-8319-0410-a670-987f0aec2ac5 --- grc/gui/FlowGraph.py | 489 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 489 insertions(+) create mode 100644 grc/gui/FlowGraph.py (limited to 'grc/gui/FlowGraph.py') diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py new file mode 100644 index 000000000..26544faab --- /dev/null +++ b/grc/gui/FlowGraph.py @@ -0,0 +1,489 @@ +""" +Copyright 2007, 2008, 2009 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +GNU Radio Companion is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +""" + +from Constants import SCROLL_PROXIMITY_SENSITIVITY, SCROLL_DISTANCE +from Actions import \ + ELEMENT_CREATE, ELEMENT_SELECT, \ + BLOCK_PARAM_MODIFY, BLOCK_MOVE, \ + ELEMENT_DELETE +import Colors +import Utils +from Element import Element +from .. base import FlowGraph as _FlowGraph +import pygtk +pygtk.require('2.0') +import gtk +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, *args, **kwargs): + """ + 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 + + ########################################################################### + # 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. + @param key the block key + @param 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) + 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.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 [block.get_id() for block 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. + @param direction +1 or -1 + @return 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. + @param direction +1 or -1 + @return 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. + @param enable true to enable + @return 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. + @param 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. + @param rotation the rotation in degrees + @return 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 + @return 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) + #update highlighting + for element in elements: + element.set_highlighted(element in selected_elements) + + def update(self): + """ + Call update on all elements. + """ + self.validate() + for element in self.get_elements(): element.update() + + ########################################################################## + ## 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. + @param coor the coordinate of the mouse click + @param coor_m the coordinate for multi select + @return 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. + @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_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_elements + + 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 + + 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) + self.handle_states(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) + ) + self.handle_states(ELEMENT_SELECT) + + ########################################################################## + ## Event Handlers + ########################################################################## + def handle_mouse_button_press(self, left_click, double_click, coordinate): + """ + A mouse button is pressed, only respond to left clicks. + 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. + """ + if not left_click: return + 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 + self.handle_states(BLOCK_PARAM_MODIFY) + + def handle_mouse_button_release(self, left_click, coordinate): + """ + A mouse button is released, record the state. + """ + if not left_click: return + self.set_coordinate(coordinate) + self.time = 0 + self.mouse_pressed = False + if self.element_moved: + self.handle_states(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 pending events + if gtk.events_pending() or 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(): + self.handle_states(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() -- cgit From 636a8cae9b5fd9cdc1e45f7a630068b905713057 Mon Sep 17 00:00:00 2001 From: jblum Date: Thu, 25 Jun 2009 17:36:36 +0000 Subject: minor fix so deleted elements are not referenced in the selected ports git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@11282 221aa14e-8319-0410-a670-987f0aec2ac5 --- grc/gui/FlowGraph.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'grc/gui/FlowGraph.py') diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 26544faab..63f289027 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -281,6 +281,10 @@ class FlowGraph(Element): for selected in selected_elements: if selected in elements: continue selected_elements.remove(selected) + if self._old_selected_port not in elements: + self._old_selected_port = None + if self._new_selected_port not in elements: + self._new_selected_port = None #update highlighting for element in elements: element.set_highlighted(element in selected_elements) -- cgit From deb7e98d2c413869e6615fb011f24c2a9944c6d3 Mon Sep 17 00:00:00 2001 From: jblum Date: Thu, 25 Jun 2009 17:59:26 +0000 Subject: better fix for selected ports, added variable config example to examples git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@11283 221aa14e-8319-0410-a670-987f0aec2ac5 --- grc/gui/FlowGraph.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'grc/gui/FlowGraph.py') diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 63f289027..f8028f199 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -281,10 +281,10 @@ class FlowGraph(Element): for selected in selected_elements: if selected in elements: continue selected_elements.remove(selected) - if self._old_selected_port not in elements: - self._old_selected_port = None - if self._new_selected_port not in elements: - self._new_selected_port = None + try: assert self._old_selected_port.get_parent() in elements + except: self._old_selected_port = None + try: assert self._new_selected_port.get_parent() in elements + except: self._new_selected_port = None #update highlighting for element in elements: element.set_highlighted(element in selected_elements) -- cgit From 854bed10dfb61e9f9feab5259a75e809941089ab Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Wed, 26 Aug 2009 11:23:23 -0700 Subject: Added virtual sink and logic to clone port. Tweaks to the base validation routines. Validate twice in the update until rewrite functions are implemented. --- grc/gui/FlowGraph.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'grc/gui/FlowGraph.py') diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index f8028f199..007bb622c 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -292,8 +292,12 @@ class FlowGraph(Element): def update(self): """ Call update on all elements. + Validate twice: + 1) elements call special rewrite rules that may break validation + 2) elements should come up with the same results, validation can pass """ self.validate() + self.validate() for element in self.get_elements(): element.update() ########################################################################## -- cgit From dc9e9db16047ec589a7b0488fac04c5bb682903c Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Wed, 26 Aug 2009 13:29:28 -0700 Subject: added rewrite methods to element to separate from validation logic --- grc/gui/FlowGraph.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'grc/gui/FlowGraph.py') diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 007bb622c..5e645be72 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -291,12 +291,10 @@ class FlowGraph(Element): def update(self): """ + Do a global rewrite and validate. Call update on all elements. - Validate twice: - 1) elements call special rewrite rules that may break validation - 2) elements should come up with the same results, validation can pass """ - self.validate() + self.rewrite() self.validate() for element in self.get_elements(): element.update() -- cgit From 5f54b018b3a84ba4b68009a1c326ba73eaea8cfd Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sat, 5 Sep 2009 01:54:41 -0700 Subject: standardized the Element inheritance __init__ usage in gui --- grc/gui/FlowGraph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'grc/gui/FlowGraph.py') diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 5e645be72..8a908ff50 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -39,7 +39,7 @@ class FlowGraph(Element): and the connections between inputs and outputs. """ - def __init__(self, *args, **kwargs): + def __init__(self): """ FlowGraph contructor. Create a list for signal blocks and connections. Connect mouse handlers. -- cgit From fa465d160b0c53fae3ad7876cf429263157dd60a Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sat, 5 Sep 2009 02:21:37 -0700 Subject: Created recursive create labels and shapes method for gui element. Replaces update methods in the gui classes and simplifies calls. The master update method in flow graph calls create labels and shapes. --- grc/gui/FlowGraph.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'grc/gui/FlowGraph.py') diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 8a908ff50..35ccf5e27 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -291,12 +291,13 @@ class FlowGraph(Element): def update(self): """ - Do a global rewrite and validate. - Call update on all elements. + Call the top level rewrite and validate. + Call the top level create labels and shapes. """ self.rewrite() self.validate() - for element in self.get_elements(): element.update() + self.create_labels() + self.create_shapes() ########################################################################## ## Get Selected -- cgit From 91a83e6f1fda6483bfd4b449a1ef7903a00af0ab Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sun, 13 Sep 2009 02:14:27 -0700 Subject: Reworked actions api and actions objects: Created standardized Action object for all gui actions. Actions module constants are actual Action objects (not strings). Keypresses, labels, tooltips, stock icons, etc all associate in the Action constructor. Usage of the action's signaling call () eliminated the need for a reference to handle_states. --- grc/gui/FlowGraph.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'grc/gui/FlowGraph.py') diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 35ccf5e27..c90071f23 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -86,7 +86,7 @@ class FlowGraph(Element): block.set_coordinate(coor) block.set_rotation(0) block.get_param('id').set_value(id) - self.handle_states(ELEMENT_CREATE) + ELEMENT_CREATE() ########################################################################### # Copy Paste @@ -409,7 +409,7 @@ class FlowGraph(Element): self._old_selected_port is not self._new_selected_port: try: self.connect(self._old_selected_port, self._new_selected_port) - self.handle_states(ELEMENT_CREATE) + ELEMENT_CREATE() except: Messages.send_fail_connection() self._old_selected_port = None self._new_selected_port = None @@ -424,7 +424,7 @@ class FlowGraph(Element): self._selected_elements = list( set.union(old_elements, new_elements) - set.intersection(old_elements, new_elements) ) - self.handle_states(ELEMENT_SELECT) + ELEMENT_SELECT() ########################################################################## ## Event Handlers @@ -446,7 +446,7 @@ class FlowGraph(Element): #double click detected, bring up params dialog if possible if double_click and self.get_selected_block(): self.mouse_pressed = False - self.handle_states(BLOCK_PARAM_MODIFY) + BLOCK_PARAM_MODIFY() def handle_mouse_button_release(self, left_click, coordinate): """ @@ -457,7 +457,7 @@ class FlowGraph(Element): self.time = 0 self.mouse_pressed = False if self.element_moved: - self.handle_states(BLOCK_MOVE) + BLOCK_MOVE() self.element_moved = False self.update_selected_elements() @@ -487,7 +487,7 @@ class FlowGraph(Element): 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(): - self.handle_states(ELEMENT_DELETE) + 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))) -- cgit From 8442dfc877a89de00e5fd0fd1b4b1890a91af630 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sun, 13 Sep 2009 02:29:26 -0700 Subject: Simply Actions module imports, using module prefix. --- grc/gui/FlowGraph.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'grc/gui/FlowGraph.py') diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index c90071f23..8feb171f1 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -18,10 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ from Constants import SCROLL_PROXIMITY_SENSITIVITY, SCROLL_DISTANCE -from Actions import \ - ELEMENT_CREATE, ELEMENT_SELECT, \ - BLOCK_PARAM_MODIFY, BLOCK_MOVE, \ - ELEMENT_DELETE +import Actions import Colors import Utils from Element import Element @@ -86,7 +83,7 @@ class FlowGraph(Element): block.set_coordinate(coor) block.set_rotation(0) block.get_param('id').set_value(id) - ELEMENT_CREATE() + Actions.ELEMENT_CREATE() ########################################################################### # Copy Paste @@ -409,7 +406,7 @@ class FlowGraph(Element): self._old_selected_port is not self._new_selected_port: try: self.connect(self._old_selected_port, self._new_selected_port) - ELEMENT_CREATE() + Actions.ELEMENT_CREATE() except: Messages.send_fail_connection() self._old_selected_port = None self._new_selected_port = None @@ -424,7 +421,7 @@ class FlowGraph(Element): self._selected_elements = list( set.union(old_elements, new_elements) - set.intersection(old_elements, new_elements) ) - ELEMENT_SELECT() + Actions.ELEMENT_SELECT() ########################################################################## ## Event Handlers @@ -446,7 +443,7 @@ class FlowGraph(Element): #double click detected, bring up params dialog if possible if double_click and self.get_selected_block(): self.mouse_pressed = False - BLOCK_PARAM_MODIFY() + Actions.BLOCK_PARAM_MODIFY() def handle_mouse_button_release(self, left_click, coordinate): """ @@ -457,7 +454,7 @@ class FlowGraph(Element): self.time = 0 self.mouse_pressed = False if self.element_moved: - BLOCK_MOVE() + Actions.BLOCK_MOVE() self.element_moved = False self.update_selected_elements() @@ -487,7 +484,7 @@ class FlowGraph(Element): 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(): - ELEMENT_DELETE() + 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))) -- cgit From 61830989ce554e6dfac41bba2ced7006c424e0bc Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sun, 6 Dec 2009 23:18:27 -0500 Subject: removed unused import statements, thanks pyflakes --- grc/gui/FlowGraph.py | 1 - 1 file changed, 1 deletion(-) (limited to 'grc/gui/FlowGraph.py') diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 8feb171f1..5adecccc1 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -22,7 +22,6 @@ import Actions import Colors import Utils from Element import Element -from .. base import FlowGraph as _FlowGraph import pygtk pygtk.require('2.0') import gtk -- cgit From d844c4f06dc10c8499eb2b8f1cb5b55f55a5b48d Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Tue, 9 Mar 2010 12:40:43 -0800 Subject: added a right click context menu for the flow graph elements --- grc/gui/FlowGraph.py | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) (limited to 'grc/gui/FlowGraph.py') diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 5adecccc1..897145a1d 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -1,5 +1,5 @@ """ -Copyright 2007, 2008, 2009 Free Software Foundation, Inc. +Copyright 2007, 2008, 2009, 2010 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -51,6 +51,19 @@ class FlowGraph(Element): #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, + ]: self._context_menu.append(action.create_menu_item()) ########################################################################### # Access Drawing Area @@ -425,14 +438,27 @@ class FlowGraph(Element): ########################################################################## ## Event Handlers ########################################################################## - def handle_mouse_button_press(self, left_click, double_click, coordinate): + def handle_mouse_context_press(self, coordinate, event): """ - A mouse button is pressed, only respond to left clicks. + 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. """ - if not left_click: return self.press_coor = coordinate self.set_coordinate(coordinate) self.time = 0 @@ -444,11 +470,12 @@ class FlowGraph(Element): self.mouse_pressed = False Actions.BLOCK_PARAM_MODIFY() - def handle_mouse_button_release(self, left_click, coordinate): + def handle_mouse_selector_release(self, coordinate): """ - A mouse button is released, record the state. + The selector mouse button was released: + Update the state, handle motion (dragging). + And update the selected flowgraph elements. """ - if not left_click: return self.set_coordinate(coordinate) self.time = 0 self.mouse_pressed = False -- cgit From af1d0a61d01c7c17dedcb5388ed8a077213d4b4f Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Thu, 14 Apr 2011 10:49:23 -0700 Subject: grc: replaced asserts in gui subdirectory --- grc/gui/FlowGraph.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'grc/gui/FlowGraph.py') diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 897145a1d..9f3326ada 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -1,5 +1,5 @@ """ -Copyright 2007, 2008, 2009, 2010 Free Software Foundation, Inc. +Copyright 2007-2011 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -290,10 +290,10 @@ class FlowGraph(Element): for selected in selected_elements: if selected in elements: continue selected_elements.remove(selected) - try: assert self._old_selected_port.get_parent() in elements - except: self._old_selected_port = None - try: assert self._new_selected_port.get_parent() in elements - except: self._new_selected_port = None + 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) -- cgit From 515d1b6f91f5dd28997525b1e88006bbfc0f170a Mon Sep 17 00:00:00 2001 From: Tim O'Shea Date: Wed, 26 Sep 2012 14:18:30 -0400 Subject: added the ability to open custom GRC hier block definitions from graphs using an instance of them --- grc/gui/FlowGraph.py | 1 + 1 file changed, 1 insertion(+) (limited to 'grc/gui/FlowGraph.py') diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 9f3326ada..0f69d4878 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -63,6 +63,7 @@ class FlowGraph(Element): Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, Actions.BLOCK_PARAM_MODIFY, + Actions.OPEN_HIER, ]: self._context_menu.append(action.create_menu_item()) ########################################################################### -- cgit From d7b57e43f186097f147534ad49c6337edc4fdc88 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Wed, 3 Oct 2012 11:10:11 -0400 Subject: grc: ability to automatically create hier_blocks from a flowgraph. Highlight a set of blocks and hit 'c' or right-click and 'Create Hier' to automatically build a new hier_block that opens in GRC with the probes in place. Any parameter/variables become parameters in the new graph. Save, generate, and hit the 'Reload Blocks' to get access to new block. --- grc/gui/FlowGraph.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'grc/gui/FlowGraph.py') diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 0f69d4878..67e5af97b 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -63,6 +63,7 @@ class FlowGraph(Element): Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, Actions.BLOCK_PARAM_MODIFY, + Actions.BLOCK_CREATE_HIER, Actions.OPEN_HIER, ]: self._context_menu.append(action.create_menu_item()) @@ -98,6 +99,8 @@ class FlowGraph(Element): block.get_param('id').set_value(id) Actions.ELEMENT_CREATE() + return id + ########################################################################### # Copy Paste ########################################################################### -- cgit From d3c7e93f2143e31ed5ff80aa21c8d983df92d19f Mon Sep 17 00:00:00 2001 From: Balint Seeber Date: Fri, 18 Jan 2013 16:26:09 -0800 Subject: Removed check for pending events during mouse drag of GRC blocks on FlowGraph canvas. This is always true on Windows with latest PyGTK and so blocks would never move with mouse movement. Disabling the check appears to have no adverse effects. --- grc/gui/FlowGraph.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'grc/gui/FlowGraph.py') diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 67e5af97b..6af4bcb62 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -494,8 +494,9 @@ class FlowGraph(Element): 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 pending events - if gtk.events_pending() or not self.mouse_pressed: return + #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 -- cgit