diff options
Diffstat (limited to 'grc/gui')
-rw-r--r-- | grc/gui/ActionHandler.py | 17 | ||||
-rw-r--r-- | grc/gui/Block.py | 70 | ||||
-rw-r--r-- | grc/gui/BlockTreeWindow.py | 2 | ||||
-rw-r--r-- | grc/gui/Constants.py | 5 | ||||
-rw-r--r-- | grc/gui/DrawingArea.py | 126 | ||||
-rw-r--r-- | grc/gui/FlowGraph.py | 146 | ||||
-rw-r--r-- | grc/gui/MainWindow.py | 4 | ||||
-rw-r--r-- | grc/gui/NotebookPage.py | 68 | ||||
-rw-r--r-- | grc/gui/PropsDialog.py | 4 | ||||
-rw-r--r-- | grc/gui/Utils.py | 30 |
10 files changed, 218 insertions, 254 deletions
diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py index 7dc37cc91d..de635b61fd 100644 --- a/grc/gui/ActionHandler.py +++ b/grc/gui/ActionHandler.py @@ -676,7 +676,7 @@ class ActionHandler: can_disable = any(block.get_state() != Constants.BLOCK_DISABLED for block in selected_blocks) can_bypass_all = all(block.can_bypass() for block in selected_blocks) \ - and any (not block.get_bypassed() for block in selected_blocks) + and any(not block.get_bypassed() for block in selected_blocks) Actions.BLOCK_ENABLE.set_sensitive(can_enable) Actions.BLOCK_DISABLE.set_sensitive(can_disable) Actions.BLOCK_BYPASS.set_sensitive(can_bypass_all) @@ -687,21 +687,16 @@ class ActionHandler: Actions.BUSSIFY_SINKS.set_sensitive(bool(selected_blocks)) Actions.RELOAD_BLOCKS.set_sensitive(True) Actions.FIND_BLOCKS.set_sensitive(True) - #set the exec and stop buttons + self.update_exec_stop() - #saved status + Actions.FLOW_GRAPH_SAVE.set_sensitive(not page.get_saved()) main.update() - try: #set the size of the flow graph area (if changed) - new_size = (flow_graph.get_option('window_size') or - self.platform.config.default_canvas_size) - if flow_graph.get_size() != tuple(new_size): - flow_graph.set_size(*new_size) - except: pass - #draw the flow graph + flow_graph.update_selected() flow_graph.queue_draw() - return True #action was handled + + return True # action was handled def update_exec_stop(self): """ diff --git a/grc/gui/Block.py b/grc/gui/Block.py index 5e5c5c402c..1b90cf69a0 100644 --- a/grc/gui/Block.py +++ b/grc/gui/Block.py @@ -27,8 +27,7 @@ from . import Actions, Colors, Utils from .Constants import ( BLOCK_LABEL_PADDING, PORT_SPACING, PORT_SEPARATION, LABEL_SEPARATION, - PORT_BORDER_SEPARATION, POSSIBLE_ROTATIONS, BLOCK_FONT, PARAM_FONT, - BORDER_PROXIMITY_SENSITIVITY + PORT_BORDER_SEPARATION, POSSIBLE_ROTATIONS, BLOCK_FONT, PARAM_FONT ) from . Element import Element from ..core.Param import num_to_str @@ -64,22 +63,13 @@ class Block(Element, _Block): Returns: the coordinate tuple (x, y) or (0, 0) if failure """ - try: #should evaluate to tuple - coor = eval(self.get_param('_coordinate').get_value()) - x, y = map(int, coor) - fgW,fgH = self.get_parent().get_size() - if x <= 0: - x = 0 - elif x >= fgW - BORDER_PROXIMITY_SENSITIVITY: - x = fgW - BORDER_PROXIMITY_SENSITIVITY - if y <= 0: - y = 0 - elif y >= fgH - BORDER_PROXIMITY_SENSITIVITY: - y = fgH - BORDER_PROXIMITY_SENSITIVITY - return (x, y) + try: + coor = self.get_param('_coordinate').get_value() # should evaluate to tuple + coor = tuple(int(x) for x in coor[1:-1].split(',')) except: - self.set_coordinate((0, 0)) - return (0, 0) + coor = 0, 0 + self.set_coordinate(coor) + return coor def set_coordinate(self, coor): """ @@ -94,41 +84,7 @@ class Block(Element, _Block): Utils.align_to_grid(coor[0] + offset_x) - offset_x, Utils.align_to_grid(coor[1] + offset_y) - offset_y ) - self.get_param('_coordinate').set_value(str(coor)) - - def bound_move_delta(self, delta_coor): - """ - Limit potential moves from exceeding the bounds of the canvas - - Args: - delta_coor: requested delta coordinate (dX, dY) to move - - Returns: - The delta coordinate possible to move while keeping the block on the canvas - or the input (dX, dY) on failure - """ - dX, dY = delta_coor - - try: - fgW, fgH = self.get_parent().get_size() - x, y = map(int, eval(self.get_param("_coordinate").get_value())) - if self.is_horizontal(): - sW, sH = self.W, self.H - else: - sW, sH = self.H, self.W - - if x + dX < 0: - dX = -x - elif dX + x + sW >= fgW: - dX = fgW - x - sW - if y + dY < 0: - dY = -y - elif dY + y + sH >= fgH: - dY = fgH - y - sH - except: - pass - - return ( dX, dY ) + self.get_param('_coordinate').set_value(repr(coor)) def get_rotation(self): """ @@ -138,11 +94,11 @@ class Block(Element, _Block): the rotation in degrees or 0 if failure """ try: #should evaluate to dict - rotation = eval(self.get_param('_rotation').get_value()) - return int(rotation) + rotation = int(self.get_param('_rotation').get_value()) except: - self.set_rotation(POSSIBLE_ROTATIONS[0]) - return POSSIBLE_ROTATIONS[0] + rotation = POSSIBLE_ROTATIONS[0] + self.set_rotation(rotation) + return rotation def set_rotation(self, rot): """ @@ -151,7 +107,7 @@ class Block(Element, _Block): Args: rot: the rotation in degrees """ - self.get_param('_rotation').set_value(str(rot)) + self.get_param('_rotation').set_value(repr(int(rot))) def create_shapes(self): """Update the block, parameters, and ports when a change occurs.""" diff --git a/grc/gui/BlockTreeWindow.py b/grc/gui/BlockTreeWindow.py index e8de6e9277..9a147bd8be 100644 --- a/grc/gui/BlockTreeWindow.py +++ b/grc/gui/BlockTreeWindow.py @@ -281,5 +281,5 @@ class BlockTreeWindow(Gtk.VBox): Handle the mouse button press. If a left double click is detected, call add selected block. """ - if event.button == 1 and event.type == Gdk._2BUTTON_PRESS: + if event.button == 1 and event.type == Gdk.EventType._2BUTTON_PRESS: self._add_selected_block() diff --git a/grc/gui/Constants.py b/grc/gui/Constants.py index 4ab644e8a1..55739a7c03 100644 --- a/grc/gui/Constants.py +++ b/grc/gui/Constants.py @@ -86,11 +86,8 @@ CONNECTOR_ARROW_HEIGHT = 17 # possible rotations in degrees POSSIBLE_ROTATIONS = (0, 90, 180, 270) -# How close can the mouse get to the window border before mouse events are ignored. -BORDER_PROXIMITY_SENSITIVITY = 50 - # How close the mouse can get to the edge of the visible window before scrolling is invoked. -SCROLL_PROXIMITY_SENSITIVITY = 30 +SCROLL_PROXIMITY_SENSITIVITY = 50 # When the window has to be scrolled, move it this distance in the required direction. SCROLL_DISTANCE = 15 diff --git a/grc/gui/DrawingArea.py b/grc/gui/DrawingArea.py index 2bce21fa6d..d1e0e78634 100644 --- a/grc/gui/DrawingArea.py +++ b/grc/gui/DrawingArea.py @@ -17,15 +17,10 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -import gi -gi.require_version('Gtk', '3.0') -from gi.repository import Gtk -from gi.repository import Gdk -from gi.repository import GObject +from gi.repository import Gtk, Gdk, GObject -from Constants import MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT, DND_TARGETS -import Colors +from . import Constants, Colors class DrawingArea(Gtk.DrawingArea): @@ -42,11 +37,16 @@ class DrawingArea(Gtk.DrawingArea): Args: main_window: the main_window containing all flow graphs """ + Gtk.DrawingArea.__init__(self) + + self._flow_graph = flow_graph + + self.zoom_factor = 1.0 self.ctrl_mask = False self.mod1_mask = False - self._flow_graph = flow_graph - GObject.GObject.__init__(self) - self.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT) + self.button_state = [False] * 10 + + # self.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT) self.connect('realize', self._handle_window_realize) self.connect('draw', self.draw) self.connect('motion-notify-event', self._handle_mouse_motion) @@ -54,24 +54,28 @@ class DrawingArea(Gtk.DrawingArea): self.connect('button-release-event', self._handle_mouse_button_release) self.connect('scroll-event', self._handle_mouse_scroll) self.add_events( - Gdk.EventMask.BUTTON_PRESS_MASK | \ - Gdk.EventMask.POINTER_MOTION_MASK | \ - Gdk.EventMask.BUTTON_RELEASE_MASK | \ - Gdk.EventMask.LEAVE_NOTIFY_MASK | \ - Gdk.EventMask.ENTER_NOTIFY_MASK #| \ + Gdk.EventMask.BUTTON_PRESS_MASK | + Gdk.EventMask.POINTER_MOTION_MASK | + Gdk.EventMask.BUTTON_RELEASE_MASK | + Gdk.EventMask.SCROLL_MASK | + Gdk.EventMask.LEAVE_NOTIFY_MASK | + Gdk.EventMask.ENTER_NOTIFY_MASK #Gdk.EventMask.FOCUS_CHANGE_MASK ) - #setup drag and drop + + # setup drag and drop self.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY) self.connect('drag-data-received', self._handle_drag_data_received) self.drag_dest_set_target_list(None) self.drag_dest_add_text_targets() - #setup the focus flag + + # setup the focus flag self._focus_flag = False self.get_focus_flag = lambda: self._focus_flag def _handle_notify_event(widget, event, focus_flag): self._focus_flag = focus_flag self.connect('leave-notify-event', _handle_notify_event, False) self.connect('enter-notify-event', _handle_notify_event, True) + # todo: fix # self.set_flags(Gtk.CAN_FOCUS) # self.set_can_focus(True) # self.connect('focus-out-event', self._handle_focus_lost_event) @@ -86,10 +90,21 @@ class DrawingArea(Gtk.DrawingArea): def _handle_mouse_scroll(self, widget, event): if event.get_state() & Gdk.ModifierType.SHIFT_MASK: - if event.direction == Gdk.ScrollDirection.UP: - event.direction = Gdk.ScrollDirection.LEFT - else: - event.direction = Gdk.ScrollDirection.RIGHT + if event.direction == Gdk.ScrollDirection.UP: + event.direction = Gdk.ScrollDirection.LEFT + else: + event.direction = Gdk.ScrollDirection.RIGHT + + elif event.get_state() & Gdk.ModifierType.CONTROL_MASK: + change = 1.2 if event.direction == Gdk.ScrollDirection.UP else 1/1.2 + zoom_factor = min(max(self.zoom_factor * change, 0.1), 5.0) + + if zoom_factor != self.zoom_factor: + self.zoom_factor = zoom_factor + self.queue_draw() + return True + + return False def _handle_mouse_button_press(self, widget, event): """ @@ -98,14 +113,18 @@ class DrawingArea(Gtk.DrawingArea): self.grab_focus() self.ctrl_mask = event.get_state() & Gdk.ModifierType.CONTROL_MASK self.mod1_mask = event.get_state() & Gdk.ModifierType.MOD1_MASK - if event.button == 1: self._flow_graph.handle_mouse_selector_press( - double_click=(event.type == Gdk.EventType._2BUTTON_PRESS), - coordinate=(event.x, event.y), - ) - if event.button == 3: self._flow_graph.handle_mouse_context_press( - coordinate=(event.x, event.y), - event=event, - ) + self.button_state[event.button] = True + + if event.button == 1: + self._flow_graph.handle_mouse_selector_press( + double_click=(event.type == Gdk.EventType._2BUTTON_PRESS), + coordinate=self._translate_event_coords(event), + ) + elif event.button == 3: + self._flow_graph.handle_mouse_context_press( + coordinate=self._translate_event_coords(event), + event=event, + ) def _handle_mouse_button_release(self, widget, event): """ @@ -113,9 +132,11 @@ class DrawingArea(Gtk.DrawingArea): """ self.ctrl_mask = event.get_state() & Gdk.ModifierType.CONTROL_MASK self.mod1_mask = event.get_state() & Gdk.ModifierType.MOD1_MASK - if event.button == 1: self._flow_graph.handle_mouse_selector_release( - coordinate=(event.x, event.y), - ) + self.button_state[event.button] = False + if event.button == 1: + self._flow_graph.handle_mouse_selector_release( + coordinate=self._translate_event_coords(event), + ) def _handle_mouse_motion(self, widget, event): """ @@ -123,20 +144,59 @@ class DrawingArea(Gtk.DrawingArea): """ self.ctrl_mask = event.get_state() & Gdk.ModifierType.CONTROL_MASK self.mod1_mask = event.get_state() & Gdk.ModifierType.MOD1_MASK + + if self.button_state[1]: + self._auto_scroll(event) + self._flow_graph.handle_mouse_motion( - coordinate=(event.x, event.y), + coordinate=self._translate_event_coords(event), + button1_pressed=self.button_state[1] ) + def _auto_scroll(self, event): + x, y = event.x, event.y + scrollbox = self.get_parent().get_parent() + + w, h = self._flow_graph.get_max_coords(initial=(x, y)) + self.set_size_request(w + 100, h + 100) + + def scroll(pos, adj): + """scroll if we moved near the border""" + adj_val = adj.get_value() + adj_len = adj.get_page_size() + if pos - adj_val > adj_len - Constants.SCROLL_PROXIMITY_SENSITIVITY: + adj.set_value(adj_val + Constants.SCROLL_DISTANCE) + adj.emit('changed') + elif pos - adj_val < Constants.SCROLL_PROXIMITY_SENSITIVITY: + adj.set_value(adj_val - Constants.SCROLL_DISTANCE) + adj.emit('changed') + + scroll(x, scrollbox.get_hadjustment()) + scroll(y, scrollbox.get_vadjustment()) + def _handle_window_realize(self, widget): """ Called when the window is realized. Update the flowgraph, which calls new pixmap. """ self._flow_graph.update() + w, h = self._flow_graph.get_max_coords() + self.set_size_request(w + 100, h + 100) def draw(self, widget, cr): + width = widget.get_allocated_width() + height = widget.get_allocated_height() + cr.set_source_rgb(*Colors.FLOWGRAPH_BACKGROUND_COLOR) + cr.rectangle(0, 0, width, height) + + cr.scale(self.zoom_factor, self.zoom_factor) + cr.fill() + self._flow_graph.draw(widget, cr) + def _translate_event_coords(self, event): + return event.x / self.zoom_factor, event.y / self.zoom_factor + def _handle_focus_lost_event(self, widget, event): # don't clear selection while context menu is active if not self._flow_graph.get_context_menu().flags() & Gtk.VISIBLE: diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index c3ea6770c1..802c54f7a7 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -76,9 +76,10 @@ class FlowGraph(Element, _Flowgraph): Returns: a unique id """ + block_ids = set(b.get_id() for b in self.blocks) for index in count(): block_id = '{}_{}'.format(base_id, index) - if block_id not in (b.get_id() for b in self.blocks): + if block_id not in block_ids: break return block_id @@ -128,7 +129,7 @@ class FlowGraph(Element, _Flowgraph): 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_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) @@ -146,8 +147,8 @@ class FlowGraph(Element, _Flowgraph): 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()), + int(random.uniform(.25, .75)*h_adj.get_page_size() + h_adj.get_value()), + int(random.uniform(.25, .75)*v_adj.get_page_size() + v_adj.get_value()), ) #get the new block block = self.new_block(key) @@ -169,7 +170,8 @@ class FlowGraph(Element, _Flowgraph): """ #get selected blocks blocks = self.get_selected_blocks() - if not blocks: return None + if not blocks: + return None #calc x and y min x_min, y_min = blocks[0].get_coordinate() for block in blocks: @@ -281,7 +283,8 @@ class FlowGraph(Element, _Flowgraph): """ changed = False for selected_block in self.get_selected_blocks(): - if selected_block.set_enabled(enable): changed = True + if selected_block.set_enabled(enable): + changed = True return changed def bypass_selected(self): @@ -295,7 +298,8 @@ class FlowGraph(Element, _Flowgraph): """ changed = False for selected_block in self.get_selected_blocks(): - if selected_block.set_bypassed(): changed = True + if selected_block.set_bypassed(): + changed = True return changed def move_selected(self, delta_coordinate): @@ -306,9 +310,6 @@ class FlowGraph(Element, _Flowgraph): delta_coordinate: the change in coordinates """ for selected_block in self.get_selected_blocks(): - delta_coordinate = selected_block.bound_move_delta(delta_coordinate) - - for selected_block in self.get_selected_blocks(): selected_block.move(delta_coordinate) self.element_moved = True @@ -401,26 +402,18 @@ class FlowGraph(Element, _Flowgraph): """ Draw the background and grid if enabled. """ - - cr.set_source_rgb(*Colors.FLOWGRAPH_BACKGROUND_COLOR) - cr.rectangle(0, 0, *self.get_size()) - cr.fill() - # draw comments first if Actions.TOGGLE_SHOW_BLOCK_COMMENTS.get_active(): for block in self.blocks: if block.get_enabled(): # block.draw_comment(widget, cr) pass - #draw multi select rectangle + # 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 cr.set_source_rgb(*Colors.HIGHLIGHT_COLOR) cr.rectangle(x, y, w, h) cr.fill() @@ -428,7 +421,7 @@ class FlowGraph(Element, _Flowgraph): cr.rectangle(x, y, w, h) cr.stroke() - #draw blocks on top of connections + # draw blocks on top of connections hide_disabled_blocks = Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active() hide_variables = Actions.TOGGLE_HIDE_VARIABLES.get_active() blocks = sorted(self.blocks, key=methodcaller('get_enabled')) @@ -439,7 +432,8 @@ class FlowGraph(Element, _Flowgraph): if hide_variables and (element.is_variable or element.is_import): continue # skip hidden disabled blocks and connections element.draw(widget, cr) - #draw selected blocks on top of selected connections + + # draw selected blocks on top of selected connections for selected_element in self.get_selected_connections() + self.get_selected_blocks(): selected_element.draw(widget, cr) @@ -666,7 +660,7 @@ class FlowGraph(Element, _Flowgraph): self.mouse_pressed = True self.update_selected_elements() self.mouse_pressed = False - self._context_menu.popup(None, None, None, event.button, event.time) + self._context_menu.popup(None, None, None, None, event.button, event.time) def handle_mouse_selector_press(self, double_click, coordinate): """ @@ -677,11 +671,12 @@ class FlowGraph(Element, _Flowgraph): """ self.press_coor = coordinate self.set_coordinate(coordinate) - self.time = 0 self.mouse_pressed = True - if double_click: self.unselect() + + 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 Actions.BLOCK_PARAM_MODIFY() @@ -693,69 +688,70 @@ class FlowGraph(Element, _Flowgraph): And update the selected flowgraph elements. """ self.set_coordinate(coordinate) - self.time = 0 self.mouse_pressed = False if self.element_moved: Actions.BLOCK_MOVE() self.element_moved = False self.update_selected_elements() - def handle_mouse_motion(self, coordinate): + def handle_mouse_motion(self, coordinate, button1_pressed): """ The mouse has moved, respond to mouse dragging or notify elements 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 + # 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: - # only continue if mouse-over stuff is enabled (just the auto-hide port label stuff for now) - if not Actions.TOGGLE_AUTO_HIDE_PORT_LABELS.get_active(): return - redraw = False - for element in reversed(self.get_elements()): - over_element = element.what_is_selected(coordinate) - if not over_element: continue - if over_element != self.element_under_mouse: # over sth new - if self.element_under_mouse: - redraw |= self.element_under_mouse.mouse_out() or False - self.element_under_mouse = over_element - redraw |= over_element.mouse_over() or False - break - else: + if not button1_pressed: + self._handle_mouse_motion_move(coordinate) + else: + self._handle_mouse_motion_drag(coordinate) + + def _handle_mouse_motion_move(self, coordinate): + # only continue if mouse-over stuff is enabled (just the auto-hide port label stuff for now) + if not Actions.TOGGLE_AUTO_HIDE_PORT_LABELS.get_active(): + return + redraw = False + for element in reversed(self.get_elements()): + over_element = element.what_is_selected(coordinate) + if not over_element: + continue + if over_element != self.element_under_mouse: # over sth new if self.element_under_mouse: redraw |= self.element_under_mouse.mouse_out() or False - self.element_under_mouse = None - if redraw: - #self.create_labels() - self.create_shapes() - self.queue_draw() + self.element_under_mouse = over_element + redraw |= over_element.mouse_over() or False + break else: - #perform auto-scrolling - 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: - Actions.ELEMENT_DELETE() - #move the selected elements and record the new coordinate - if not self.get_ctrl_mask(): - X, Y = self.get_coordinate() - dX, dY = int(x - X), int(y - Y) - active = Actions.TOGGLE_SNAP_TO_GRID.get_active() or self.get_mod1_mask() - if not active or abs(dX) >= Utils.CANVAS_GRID_SIZE or abs(dY) >= Utils.CANVAS_GRID_SIZE: - self.move_selected((dX, dY)) - self.set_coordinate((x, y)) - #queue draw for animation + if self.element_under_mouse: + redraw |= self.element_under_mouse.mouse_out() or False + self.element_under_mouse = None + if redraw: + # self.create_labels() + self.create_shapes() self.queue_draw() + + 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: + Actions.ELEMENT_DELETE() + + # move the selected elements and record the new coordinate + x, y = coordinate + if not self.get_ctrl_mask(): + X, Y = self.get_coordinate() + dX, dY = int(x - X), int(y - Y) + active = Actions.TOGGLE_SNAP_TO_GRID.get_active() or self.get_mod1_mask() + if not active or abs(dX) >= Utils.CANVAS_GRID_SIZE or abs(dY) >= Utils.CANVAS_GRID_SIZE: + self.move_selected((dX, dY)) + self.set_coordinate((x, y)) + # queue draw for animation + self.queue_draw() + + def get_max_coords(self, initial=(0, 0)): + w, h = initial + for block in self.blocks: + x, y = block.get_coordinate() + w = max(w, x + block.W) + h = max(h, y + block.H) + return w, h diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py index 751e0d9b3f..126a9afff9 100644 --- a/grc/gui/MainWindow.py +++ b/grc/gui/MainWindow.py @@ -241,14 +241,14 @@ class MainWindow(Gtk.Window): file_path=file_path, ) if file_path: Messages.send_end_load() - except Exception, e: #return on failure + except Exception as e: #return on failure Messages.send_fail_load(e) if isinstance(e, KeyError) and str(e) == "'options'": # This error is unrecoverable, so crash gracefully exit(-1) return #add this page to the notebook - self.notebook.append_page(page, page.get_tab()) + self.notebook.append_page(page, page.tab) self.notebook.set_tab_reorderable(page, True) #only show if blank or manual if not file_path or show: self._set_page(page) diff --git a/grc/gui/NotebookPage.py b/grc/gui/NotebookPage.py index 9a76897fe6..e15617aec9 100644 --- a/grc/gui/NotebookPage.py +++ b/grc/gui/NotebookPage.py @@ -17,17 +17,15 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -import gi -gi.require_version('Gtk', '3.0') -from gi.repository import Gtk -from gi.repository import GObject - -import Actions -from StateCache import StateCache -from Constants import MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT -from DrawingArea import DrawingArea import os +from gi.repository import Gtk, Gdk, GObject + +from . import Actions +from .StateCache import StateCache +from .Constants import MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT +from .DrawingArea import DrawingArea + class NotebookPage(Gtk.HBox): """A page in the notebook.""" @@ -40,49 +38,46 @@ class NotebookPage(Gtk.HBox): main_window: main window file_path: path to a flow graph file """ + Gtk.HBox.__init__(self) + + self.main_window = main_window self._flow_graph = flow_graph self.process = None - #import the file - self.main_window = main_window + + # import the file self.file_path = file_path initial_state = flow_graph.get_parent().parse_flow_graph(file_path) + self.get_flow_graph().import_data(initial_state) self.state_cache = StateCache(initial_state) self.saved = True - #import the data to the flow graph - self.get_flow_graph().import_data(initial_state) - #initialize page gui - GObject.GObject.__init__(self) - self.show() - #tab box to hold label and close button - self.tab = Gtk.HBox(homogeneous=False, spacing=0) - #setup tab label + + # tab box to hold label and close button self.label = Gtk.Label() - self.tab.pack_start(self.label, False, False, 0) - #setup button image image = Gtk.Image() image.set_from_stock('gtk-close', Gtk.IconSize.MENU) - #setup image box image_box = Gtk.HBox(homogeneous=False, spacing=0) image_box.pack_start(image, True, False, 0) - #setup the button button = Gtk.Button() button.connect("clicked", self._handle_button) button.set_relief(Gtk.ReliefStyle.NONE) button.add(image_box) - #button size - #w, h = Gtk.icon_size_lookup_for_settings(button.get_settings(), Gtk.IconSize.MENU) - #button.set_size_request(w+6, h+6) - self.tab.pack_start(button, False, False, 0) - self.tab.show_all() - #setup scroll window and drawing area + + tab = self.tab = Gtk.HBox(homogeneous=False, spacing=0) + tab.pack_start(self.label, False, False, 0) + tab.pack_start(button, False, False, 0) + tab.show_all() + + # setup scroll window and drawing area + self.drawing_area = DrawingArea(self.get_flow_graph()) + self.scrolled_window = Gtk.ScrolledWindow() self.scrolled_window.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT) - self.scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + self.scrolled_window.set_policy(Gtk.PolicyType.ALWAYS, Gtk.PolicyType.ALWAYS) self.scrolled_window.connect('key-press-event', self._handle_scroll_window_key_press) - self.drawing_area = DrawingArea(self.get_flow_graph()) self.scrolled_window.add_with_viewport(self.drawing_area) self.pack_start(self.scrolled_window, True, True, 0) - #inject drawing area into flow graph + + # inject drawing area into flow graph self.get_flow_graph().drawing_area = self.drawing_area self.show_all() @@ -125,15 +120,6 @@ class NotebookPage(Gtk.HBox): """ self.label.set_markup(markup) - def get_tab(self): - """ - Get the gtk widget for this page's tab. - - Returns: - gtk widget - """ - return self.tab - def get_proc(self): """ Get the subprocess for the flow graph. diff --git a/grc/gui/PropsDialog.py b/grc/gui/PropsDialog.py index ca41abc003..d6b64944cc 100644 --- a/grc/gui/PropsDialog.py +++ b/grc/gui/PropsDialog.py @@ -87,7 +87,9 @@ class PropsDialog(Gtk.Dialog): self._code_text_display = code_view = SimpleTextDisplay() code_view.set_wrap_mode(Gtk.WrapMode.NONE) code_view.get_buffer().create_tag('b', weight=Pango.Weight.BOLD) - code_view.override_font(Pango.FontDescription('monospace %d' % Constants.FONT_SIZE)) + code_view.set_monospace(True) + # todo: set font size in non-deprecated way + # code_view.override_font(Pango.FontDescription('monospace %d' % Constants.FONT_SIZE)) code_box = Gtk.ScrolledWindow() code_box.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) code_box.add_with_viewport(self._code_text_display) diff --git a/grc/gui/Utils.py b/grc/gui/Utils.py index e5d69bcf54..311b37f468 100644 --- a/grc/gui/Utils.py +++ b/grc/gui/Utils.py @@ -19,36 +19,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import gi gi.require_version('Gtk', '3.0') -from gi.repository import Gtk -from gi.repository import Gdk -from gi.repository import GdkPixbuf -from gi.repository import GObject from gi.repository import GLib -from Constants import POSSIBLE_ROTATIONS, CANVAS_GRID_SIZE - - -def rotate_pixmap(gc, src_pixmap, dst_pixmap, angle=GdkPixbuf.PixbufRotation.COUNTERCLOCKWISE): - """ - Load the destination pixmap with a rotated version of the source pixmap. - The source pixmap will be loaded into a pixbuf, rotated, and drawn to the destination pixmap. - The pixbuf is a client-side drawable, where a pixmap is a server-side drawable. - - Args: - gc: the graphics context - src_pixmap: the source pixmap - dst_pixmap: the destination pixmap - angle: the angle to rotate by - """ - width, height = src_pixmap.get_size() - pixbuf = GdkPixbuf.Pixbuf( - colorspace=GdkPixbuf.Colorspace.RGB, - has_alpha=False, bits_per_sample=8, - width=width, height=height, - ) - pixbuf.get_from_drawable(src_pixmap, src_pixmap.get_colormap(), 0, 0, 0, 0, -1, -1) - pixbuf = pixbuf.rotate_simple(angle) - dst_pixmap.draw_pixbuf(gc, pixbuf, 0, 0, 0, 0) +from .Constants import POSSIBLE_ROTATIONS, CANVAS_GRID_SIZE def get_rotated_coordinate(coor, rotation): @@ -97,7 +70,6 @@ def encode(value): Older versions of glib seg fault if the last byte starts a multi-byte character. """ - valid_utf8 = value.decode('utf-8', errors='replace').encode('utf-8') return GLib.markup_escape_text(valid_utf8) |