diff options
author | Sebastian Koslowski <koslowski@kit.edu> | 2016-07-08 15:34:48 +0200 |
---|---|---|
committer | Sebastian Koslowski <koslowski@kit.edu> | 2016-07-13 16:35:49 +0200 |
commit | c2efab9967bfc0b3cf47836eb53bc4ece23b7641 (patch) | |
tree | 0717dbb07a880509020f81d1b0daea0ca8e109e0 | |
parent | 020878e8468d848cebe1bfe5b0dc7c9b557214f7 (diff) |
grc: refactor: selected blocks handling
-rw-r--r-- | grc/gui/ActionHandler.py | 23 | ||||
-rw-r--r-- | grc/gui/FlowGraph.py | 335 |
2 files changed, 171 insertions, 187 deletions
diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py index bb2f488b07..16fed00db5 100644 --- a/grc/gui/ActionHandler.py +++ b/grc/gui/ActionHandler.py @@ -202,7 +202,7 @@ class ActionHandler: elif action == Actions.BLOCK_CREATE_HIER: # keeping track of coordinates for pasting later - coords = flow_graph.get_selected_blocks()[0].get_coordinate() + coords = flow_graph.selected_blocks()[0].get_coordinate() x,y = coords x_min = x y_min = y @@ -211,7 +211,7 @@ class ActionHandler: params = []; # Save the state of the leaf blocks - for block in flow_graph.get_selected_blocks(): + for block in flow_graph.selected_blocks(): # Check for string variables within the blocks for param in block.params.values(): @@ -239,10 +239,10 @@ class ActionHandler: sink_id = connection.sink_block.get_id() # If connected block is not in the list of selected blocks create a pad for it - if flow_graph.get_block(source_id) not in flow_graph.get_selected_blocks(): + if flow_graph.get_block(source_id) not in flow_graph.selected_blocks(): pads.append({'key': connection.sink_port.key, 'coord': connection.source_port.get_coordinate(), 'block_id' : block.get_id(), 'direction': 'source'}) - if flow_graph.get_block(sink_id) not in flow_graph.get_selected_blocks(): + if flow_graph.get_block(sink_id) not in flow_graph.selected_blocks(): pads.append({'key': connection.source_port.key, 'coord': connection.sink_port.get_coordinate(), 'block_id' : block.get_id(), 'direction': 'sink'}) @@ -451,10 +451,7 @@ class ActionHandler: # Param Modifications ################################################## elif action == Actions.BLOCK_PARAM_MODIFY: - if action.args: - selected_block = action.args[0] - else: - selected_block = flow_graph.get_selected_block() + selected_block = action.args[0] if action.args else flow_graph.selected_block if selected_block: self.dialog = PropsDialog(self.main_window, selected_block) response = Gtk.ResponseType.APPLY @@ -616,12 +613,12 @@ class ActionHandler: main.btwin.search_entry.show() main.btwin.search_entry.grab_focus() elif action == Actions.OPEN_HIER: - for b in flow_graph.get_selected_blocks(): + for b in flow_graph.selected_blocks(): if b._grc_source: main.new_page(b._grc_source, show=True) elif action == Actions.BUSSIFY_SOURCES: n = {'name':'bus', 'type':'bus'} - for b in flow_graph.get_selected_blocks(): + for b in flow_graph.selected_blocks(): b.bussify(n, 'source') flow_graph._old_selected_port = None flow_graph._new_selected_port = None @@ -629,7 +626,7 @@ class ActionHandler: elif action == Actions.BUSSIFY_SINKS: n = {'name':'bus', 'type':'bus'} - for b in flow_graph.get_selected_blocks(): + for b in flow_graph.selected_blocks(): b.bussify(n, 'sink') flow_graph._old_selected_port = None flow_graph._new_selected_port = None @@ -647,12 +644,12 @@ class ActionHandler: page = main.current_page # page and flow graph might have changed flow_graph = page.flow_graph if page else None - selected_blocks = flow_graph.get_selected_blocks() + selected_blocks = list(flow_graph.selected_blocks()) selected_block = selected_blocks[0] if selected_blocks else None #update general buttons Actions.ERRORS_WINDOW_DISPLAY.set_sensitive(not flow_graph.is_valid()) - Actions.ELEMENT_DELETE.set_sensitive(bool(flow_graph.get_selected_elements())) + Actions.ELEMENT_DELETE.set_sensitive(bool(flow_graph.selected_elements)) Actions.BLOCK_PARAM_MODIFY.set_sensitive(bool(selected_block)) Actions.BLOCK_ROTATE_CCW.set_sensitive(bool(selected_blocks)) Actions.BLOCK_ROTATE_CW.set_sensitive(bool(selected_blocks)) diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index d592242e2e..7a0f2475ff 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -50,22 +50,19 @@ class FlowGraph(Element, _Flowgraph): """ Element.__init__(self) _Flowgraph.__init__(self, **kwargs) - #when is the flow graph selected? (used by keyboard event handler) - self.is_selected = lambda: bool(self.get_selected_elements()) - #important vars dealing with mouse event tracking + # important vars dealing with mouse event tracking self.element_moved = False self.mouse_pressed = False - self._selected_elements = [] self.press_coor = (0, 0) - #selected ports + # selected + self.selected_elements = set() self._old_selected_port = None self._new_selected_port = None # current mouse hover element self.element_under_mouse = None - #context menu + # context menu self._context_menu = Bars.ContextMenu() self.get_context_menu = lambda: self._context_menu - self._elements_to_draw = [] self._elements_to_draw = [] self._external_updaters = {} @@ -125,17 +122,6 @@ class FlowGraph(Element, _Flowgraph): return Actions.EXTERNAL_UPDATE() - - ########################################################################### - # Access Drawing Area - ########################################################################### - def get_drawing_area(self): return self.drawing_area - def queue_draw(self): self.get_drawing_area().queue_draw() - def get_scroll_pane(self): return self.drawing_area.get_parent().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): """ Add a block of the given key to this flow graph. @@ -160,6 +146,55 @@ class FlowGraph(Element, _Flowgraph): Actions.ELEMENT_CREATE() return id + def make_connection(self): + """this selection and the last were ports, try to connect them""" + if self._old_selected_port and self._new_selected_port: + try: + self.connect(self._old_selected_port, self._new_selected_port) + Actions.ELEMENT_CREATE() + except: + Messages.send_fail_connection() + self._old_selected_port = None + self._new_selected_port = None + return True + return False + + def update(self): + """ + Call the top level rewrite and validate. + Call the top level create labels and shapes. + """ + self.rewrite() + self.validate() + self.create_labels() + self.create_shapes() + + def reload(self): + """ + Reload flow-graph (with updated blocks) + + Args: + page: the page to reload (None means current) + Returns: + False if some error occurred during import + """ + success = False + data = self.export_data() + if data: + self.unselect() + success = self.import_data(data) + self.update() + return success + + ########################################################################### + # Access Drawing Area + ########################################################################### + def get_drawing_area(self): return self.drawing_area + def queue_draw(self): self.get_drawing_area().queue_draw() + def get_scroll_pane(self): return self.drawing_area.get_parent().get_parent() + def get_ctrl_mask(self): return self.drawing_area.ctrl_mask + def get_mod1_mask(self): return self.drawing_area.mod1_mask + ########################################################################### # Copy Paste ########################################################################### @@ -171,7 +206,7 @@ class FlowGraph(Element, _Flowgraph): the clipboard """ #get selected blocks - blocks = self.get_selected_blocks() + blocks = list(self.selected_blocks()) if not blocks: return None #calc x and y min @@ -249,8 +284,9 @@ class FlowGraph(Element, _Flowgraph): sink = old_id2block[connection_n.get('sink_block_id')].get_sink(connection_n.get('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) + for block in selected: + selected = selected.union(set(block.get_connections())) + self.selected_elements = set(selected) ########################################################################### # Modify Selected @@ -265,7 +301,7 @@ class FlowGraph(Element, _Flowgraph): Returns: true for change """ - return any(sb.type_controller_modify(direction) for sb in self.get_selected_blocks()) + return any(sb.type_controller_modify(direction) for sb in self.selected_blocks()) def port_controller_modify_selected(self, direction): """ @@ -277,7 +313,7 @@ class FlowGraph(Element, _Flowgraph): Returns: true for changed """ - return any(sb.port_controller_modify(direction) for sb in self.get_selected_blocks()) + return any(sb.port_controller_modify(direction) for sb in self.selected_blocks()) def change_state_selected(self, new_state): """ @@ -302,7 +338,7 @@ class FlowGraph(Element, _Flowgraph): Args: delta_coordinate: the change in coordinates """ - for selected_block in self.get_selected_blocks(): + for selected_block in self.selected_blocks(): selected_block.move(delta_coordinate) self.element_moved = True @@ -316,7 +352,7 @@ class FlowGraph(Element, _Flowgraph): Returns: True if changed, otherwise False """ - blocks = self.get_selected_blocks() + blocks = list(self.selected_blocks()) if calling_action is None or not blocks: return False @@ -357,13 +393,13 @@ class FlowGraph(Element, _Flowgraph): Returns: true if changed, otherwise false. """ - if not self.get_selected_blocks(): + if not any(self.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() + min_x, min_y = self.selected_block.get_coordinate() + max_x, max_y = self.selected_block.get_coordinate() #rotate each selected block, and find min/max coordinate - for selected_block in self.get_selected_blocks(): + for selected_block in self.selected_blocks(): selected_block.rotate(rotation) #update the min/max coordinate x, y = selected_block.get_coordinate() @@ -372,7 +408,7 @@ class FlowGraph(Element, _Flowgraph): #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(): + for selected_block in self.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)) @@ -386,11 +422,35 @@ class FlowGraph(Element, _Flowgraph): true if changed. """ changed = False - for selected_element in self.get_selected_elements(): + for selected_element in self.selected_elements: self.remove_element(selected_element) changed = True return changed + def update_selected(self): + """ + Remove deleted elements from the selected elements list. + Update highlighting so only the selected are highlighted. + """ + selected_elements = self.selected_elements + elements = self.get_elements() + # remove deleted elements + for selected in selected_elements: + if selected in elements: + continue + selected_elements.remove(selected) + if self._old_selected_port and self._old_selected_port.parent not in elements: + self._old_selected_port = None + if self._new_selected_port and self._new_selected_port.parent not in elements: + self._new_selected_port = None + # update highlighting + for element in elements: + element.highlighted = element in selected_elements + + ########################################################################### + # Draw stuff + ########################################################################### + def update_elements_to_draw(self): hide_disabled_blocks = Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active() hide_variables = Actions.TOGGLE_HIDE_VARIABLES.get_active() @@ -443,64 +503,54 @@ class FlowGraph(Element, _Flowgraph): cr.rectangle(x, y, w, h) cr.stroke() - def update_selected(self): + ########################################################################## + # selection handling + ########################################################################## + def update_selected_elements(self): """ - Remove deleted elements from the selected elements list. - Update highlighting so only the selected are highlighted. + 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 = self.get_selected_elements() - elements = self.get_elements() - #remove deleted elements - for selected in selected_elements: - if selected in elements: continue - selected_elements.remove(selected) - if self._old_selected_port and self._old_selected_port.parent not in elements: - self._old_selected_port = None - if self._new_selected_port and self._new_selected_port.parent not in elements: - self._new_selected_port = None - #update highlighting - for element in elements: - element.set_highlighted(element in selected_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 new_selections not in self.selected_elements: + selected_elements = new_selections - def update(self): - """ - Call the top level rewrite and validate. - Call the top level create labels and shapes. - """ - self.rewrite() - self.validate() - self.create_labels() - self.create_shapes() + if self._old_selected_port: + self._old_selected_port.force_label_unhidden(False) + self.create_shapes() + self.queue_draw() + elif self._new_selected_port: + self._new_selected_port.force_label_unhidden() - def reload(self): - """ - Reload flow-graph (with updated blocks) + else: # called from a mouse release + if not self.element_moved and (not self.selected_elements or self.get_ctrl_mask()): + selected_elements = self.what_is_selected(self.get_coordinate(), self.press_coor) - Args: - page: the page to reload (None means current) - Returns: - False if some error occurred during import - """ - success = False - data = self.export_data() - if data: - self.unselect() - success = self.import_data(data) - self.update() - return success + # this selection and the last were ports, try to connect them + if self.make_connection(): + return - ########################################################################## - ## Get Selected - ########################################################################## - def unselect(self): - """ - Set selected elements to an empty set. - """ - self._selected_elements = [] + # update selected elements + if selected_elements is None: + return - def select_all(self): - """Select all blocks in the flow graph""" - self._selected_elements = list(self.get_elements()) + # if ctrl, set the selected elements to the union - intersection of old and new + if self.get_ctrl_mask(): + self.selected_elements ^= selected_elements + else: + self.selected_elements.clear() + self.selected_elements.update(selected_elements) + Actions.ELEMENT_SELECT() def what_is_selected(self, coor, coor_m=None): """ @@ -520,68 +570,55 @@ class FlowGraph(Element, _Flowgraph): """ selected_port = None selected = set() - #check the elements - hide_disabled_blocks = Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active() - hide_variables = Actions.TOGGLE_HIDE_VARIABLES.get_active() - for element in reversed(self.get_elements()): - if hide_disabled_blocks and not element.get_enabled(): - continue # skip hidden disabled blocks and connections - if hide_variables and (element.is_variable or element.is_import): - continue # skip hidden disabled blocks and connections + # check the elements + for element in reversed(self._elements_to_draw): selected_element = element.what_is_selected(coor, coor_m) if not selected_element: continue - #update the selected port information + # update the selected port information if selected_element.is_port: - if not coor_m: selected_port = selected_element + if not coor_m: + selected_port = selected_element selected_element = selected_element.parent_block + 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 + if not coor_m: + break + # update selected ports if selected_port is not self._new_selected_port: self._old_selected_port = self._new_selected_port self._new_selected_port = selected_port - return list(selected) + return selected - def get_selected_connections(self): + def unselect(self): """ - Get a group of selected connections. - - Returns: - sub set of connections in this flow graph + Set selected elements to an empty set. """ - selected = set() - for selected_element in self.get_selected_elements(): - if selected_element.is_connection: - selected.add(selected_element) - return list(selected) + self.selected_elements.clear() + + def select_all(self): + """Select all blocks in the flow graph""" + self.selected_elements.clear() + self.selected_elements.update(self._elements_to_draw) - def get_selected_blocks(self): + def selected_blocks(self): """ Get a group of selected blocks. Returns: sub set of blocks in this flow graph """ - selected = set() - for selected_element in self.get_selected_elements(): - if selected_element.is_block: - selected.add(selected_element) - return list(selected) + return (e for e in self.selected_elements if e.is_block) - def get_selected_block(self): + @property + def selected_block(self): """ Get the selected block when a block or port is selected. Returns: a block or None """ - selected_blocks = self.get_selected_blocks() - return selected_blocks[0] if selected_blocks else None + return next(self.selected_blocks(), None) def get_selected_elements(self): """ @@ -590,7 +627,7 @@ class FlowGraph(Element, _Flowgraph): Returns: sub set of elements in this flow graph """ - return self._selected_elements + return self.selected_elements def get_selected_element(self): """ @@ -599,60 +636,10 @@ class FlowGraph(Element, _Flowgraph): Returns: a block, port, or connection or None """ - selected_elements = self.get_selected_elements() - return selected_elements[0] if selected_elements else 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 - if self._old_selected_port: - self._old_selected_port.force_label_unhidden(False) - self.create_shapes() - self.queue_draw() - elif self._new_selected_port: - self._new_selected_port.force_label_unhidden() - 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: - try: - self.connect(self._old_selected_port, self._new_selected_port) - Actions.ELEMENT_CREATE() - except: Messages.send_fail_connection() - self._old_selected_port = None - self._new_selected_port = None - return - #update selected elements - if selected_elements is None: return - old_elements = set(self.get_selected_elements()) - self._selected_elements = list(set(selected_elements)) - new_elements = set(self.get_selected_elements()) - #if ctrl, set the selected elements to the union - intersection of old and new - if self.get_ctrl_mask(): - self._selected_elements = list( - set.union(old_elements, new_elements) - set.intersection(old_elements, new_elements) - ) - Actions.ELEMENT_SELECT() + return next(iter(self.selected_elements), None) ########################################################################## - ## Event Handlers + # Event Handlers ########################################################################## def handle_mouse_context_press(self, coordinate, event): """ @@ -661,7 +648,7 @@ class FlowGraph(Element, _Flowgraph): 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()): + if not selections.intersection(self.selected_elements): self.set_coordinate(coordinate) self.mouse_pressed = True self.update_selected_elements() @@ -683,7 +670,7 @@ class FlowGraph(Element, _Flowgraph): self.unselect() self.update_selected_elements() - if double_click and self.get_selected_block(): + if double_click and self.selected_block: self.mouse_pressed = False Actions.BLOCK_PARAM_MODIFY() @@ -739,7 +726,7 @@ class FlowGraph(Element, _Flowgraph): def _handle_mouse_motion_drag(self, coordinate): # remove the connection if selected in drag event - if len(self.get_selected_elements()) == 1 and self.get_selected_element().is_connection: + if len(self.selected_elements) == 1 and self.get_selected_element().is_connection: Actions.ELEMENT_DELETE() # move the selected elements and record the new coordinate |