diff options
Diffstat (limited to 'grc/gui/Block.py')
-rw-r--r-- | grc/gui/Block.py | 291 |
1 files changed, 113 insertions, 178 deletions
diff --git a/grc/gui/Block.py b/grc/gui/Block.py index 55c8805fae..49bba4f3db 100644 --- a/grc/gui/Block.py +++ b/grc/gui/Block.py @@ -17,37 +17,24 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -import pygtk -pygtk.require('2.0') -import gtk -import pango - -from . import Actions, Colors, Utils, Constants - +from __future__ import absolute_import +import math +import gi +gi.require_version('Gtk', '3.0') +gi.require_version('PangoCairo', '1.0') +from gi.repository import Gtk, Pango, PangoCairo + +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 +) from . Element import Element from ..core.Param import num_to_str -from ..core.utils import odict from ..core.utils.complexity import calculate_flowgraph_complexity from ..core.Block import Block as _Block -BLOCK_MARKUP_TMPL="""\ -#set $foreground = $block.is_valid() and 'black' or 'red' -<span foreground="$foreground" font_desc="$font"><b>$encode($block.get_name())</b></span>""" - -# Includes the additional complexity markup if enabled -COMMENT_COMPLEXITY_MARKUP_TMPL="""\ -#set $foreground = $block.get_enabled() and '#444' or '#888' -#if $complexity -<span foreground="#444" size="medium" font_desc="$font"><b>$encode($complexity)</b></span>#slurp -#end if -#if $complexity and $comment -<span></span> -#end if -#if $comment -<span foreground="$foreground" font_desc="$font">$encode($comment)</span>#slurp -#end if -""" - class Block(Element, _Block): """The graphical signal block.""" @@ -58,33 +45,17 @@ class Block(Element, _Block): Add graphics related params to the block. """ _Block.__init__(self, flow_graph, n) + self.W = self.H = 0 + self._add_param(key='_coordinate', name='GUI Coordinate', value='(0, 0)', + hide='all') + self._add_param(key='_rotation', name='GUI Rotation', value='0', + hide='all') - self.W = 0 - self.H = 0 - #add the position param - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({ - 'name': 'GUI Coordinate', - 'key': '_coordinate', - 'type': 'raw', - 'value': '(0, 0)', - 'hide': 'all', - }) - )) - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({ - 'name': 'GUI Rotation', - 'key': '_rotation', - 'type': 'raw', - 'value': '0', - 'hide': 'all', - }) - )) - Element.__init__(self) + Element.__init__(self) # needs the params self._comment_pixmap = None + self._bg_color = Colors.BLOCK_ENABLED_COLOR self.has_busses = [False, False] # source, sink + self.layouts = [] def get_coordinate(self): """ @@ -93,23 +64,13 @@ class Block(Element, _Block): Returns: the coordinate tuple (x, y) or (0, 0) if failure """ - proximity = Constants.BORDER_PROXIMITY_SENSITIVITY - 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 - proximity: - x = fgW - proximity - if y <= 0: - y = 0 - elif y >= fgH - proximity: - y = fgH - proximity - 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): """ @@ -124,41 +85,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): """ @@ -168,11 +95,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(Constants.POSSIBLE_ROTATIONS[0]) - return Constants.POSSIBLE_ROTATIONS[0] + rotation = POSSIBLE_ROTATIONS[0] + self.set_rotation(rotation) + return rotation def set_rotation(self, rot): """ @@ -181,7 +108,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.""" @@ -192,71 +119,53 @@ class Block(Element, _Block): def create_labels(self): """Create the labels for the signal block.""" Element.create_labels(self) - self._bg_color = self.is_dummy_block and Colors.MISSING_BLOCK_BACKGROUND_COLOR or \ - self.get_bypassed() and Colors.BLOCK_BYPASSED_COLOR or \ - self.get_enabled() and Colors.BLOCK_ENABLED_COLOR or Colors.BLOCK_DISABLED_COLOR - - layouts = list() + self._bg_color = Colors.MISSING_BLOCK_BACKGROUND_COLOR if self.is_dummy_block else \ + Colors.BLOCK_BYPASSED_COLOR if self.get_bypassed() else \ + Colors.BLOCK_ENABLED_COLOR if self.get_enabled() else \ + Colors.BLOCK_DISABLED_COLOR + del self.layouts[:] #create the main layout - layout = gtk.DrawingArea().create_pango_layout('') - layouts.append(layout) - layout.set_markup(Utils.parse_template(BLOCK_MARKUP_TMPL, block=self, font=Constants.BLOCK_FONT)) + layout = Gtk.DrawingArea().create_pango_layout('') + layout.set_markup('<span foreground="{foreground}" font_desc="{font}"><b>{name}</b></span>'.format( + foreground='black' if self.is_valid() else 'red', font=BLOCK_FONT, name=Utils.encode(self.get_name()) + )) self.label_width, self.label_height = layout.get_pixel_size() + self.layouts.append(layout) #display the params if self.is_dummy_block: - markups = [ - '<span foreground="black" font_desc="{font}"><b>key: </b>{key}</span>' - ''.format(font=Constants.PARAM_FONT, key=self._key) - ] + markups = ['<span foreground="black" font_desc="{font}"><b>key: </b>{key}</span>'.format( + font=PARAM_FONT, key=self._key + )] else: - markups = [param.get_markup() for param in self.get_params() if param.get_hide() not in ('all', 'part')] + markups = [param.format_block_surface_markup() for param in self.get_params() if param.get_hide() not in ('all', 'part')] if markups: - layout = gtk.DrawingArea().create_pango_layout('') - layout.set_spacing(Constants.LABEL_SEPARATION * pango.SCALE) + layout = Gtk.DrawingArea().create_pango_layout('') + layout.set_spacing(LABEL_SEPARATION*Pango.SCALE) layout.set_markup('\n'.join(markups)) - layouts.append(layout) + self.layouts.append(layout) w, h = layout.get_pixel_size() self.label_width = max(w, self.label_width) - self.label_height += h + Constants.LABEL_SEPARATION - width = self.label_width - height = self.label_height - #setup the pixmap - pixmap = self.get_parent().new_pixmap(width, height) - gc = pixmap.new_gc() - gc.set_foreground(self._bg_color) - pixmap.draw_rectangle(gc, True, 0, 0, width, height) - #draw the layouts - h_off = 0 - for i,layout in enumerate(layouts): - w,h = layout.get_pixel_size() - if i == 0: w_off = (width-w)/2 - else: w_off = 0 - pixmap.draw_layout(gc, w_off, h_off, layout) - h_off = h + h_off + Constants.LABEL_SEPARATION - #create vertical and horizontal pixmaps - self.horizontal_label = pixmap - if self.is_vertical(): - self.vertical_label = self.get_parent().new_pixmap(height, width) - Utils.rotate_pixmap(gc, self.horizontal_label, self.vertical_label) + self.label_height += h + LABEL_SEPARATION + #calculate width and height needed - W = self.label_width + 2 * Constants.BLOCK_LABEL_PADDING + W = self.label_width + 2 * BLOCK_LABEL_PADDING def get_min_height_for_ports(): - visible_ports = filter(lambda p: not p.get_hide(), ports) - min_height = 2*Constants.PORT_BORDER_SEPARATION + len(visible_ports) * Constants.PORT_SEPARATION + visible_ports = [p for p in ports if not p.get_hide()] + min_height = 2*PORT_BORDER_SEPARATION + len(visible_ports) * PORT_SEPARATION if visible_ports: min_height -= ports[0].H return min_height H = max( [ # labels - self.label_height + 2 * Constants.BLOCK_LABEL_PADDING + self.label_height + 2 * BLOCK_LABEL_PADDING ] + [ # ports get_min_height_for_ports() for ports in (self.get_sources_gui(), self.get_sinks_gui()) ] + [ # bus ports only - 2 * Constants.PORT_BORDER_SEPARATION + - sum([port.H + Constants.PORT_SPACING for port in ports if port.get_type() == 'bus']) - Constants.PORT_SPACING + 2 * PORT_BORDER_SEPARATION + + sum([port.H + PORT_SPACING for port in ports if port.get_type() == 'bus']) - PORT_SPACING for ports in (self.get_sources_gui(), self.get_sinks_gui()) ] ) @@ -268,26 +177,31 @@ class Block(Element, _Block): self.create_comment_label() def create_comment_label(self): - comment = self.get_comment() # Returns None if there are no comments - complexity = None + markups = [] - # Show the flowgraph complexity on the top block if enabled + # Show the flow graph complexity on the top block if enabled if Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY.get_active() and self.get_key() == "options": - complexity = calculate_flowgraph_complexity(self.get_parent()) - complexity = "Complexity: {}bal".format(num_to_str(complexity)) + complexity = calculate_flowgraph_complexity(self.parent) + markups.append( + '<span foreground="#444" size="medium" font_desc="{font}">' + '<b>Complexity: {num}bal</b></span>'.format(num=num_to_str(complexity), font=BLOCK_FONT) + ) + comment = self.get_comment() # Returns None if there are no comments + if comment: + if markups: + markups.append('<span></span>') - layout = gtk.DrawingArea().create_pango_layout('') - layout.set_markup(Utils.parse_template(COMMENT_COMPLEXITY_MARKUP_TMPL, - block=self, - comment=comment, - complexity=complexity, - font=Constants.BLOCK_FONT)) + markups.append('<span foreground="{foreground}" font_desc="{font}">{comment}</span>'.format( + foreground='#444' if self.get_enabled() else '#888', font=BLOCK_FONT, comment=Utils.encode(comment) + )) + layout = Gtk.DrawingArea().create_pango_layout('') + layout.set_markup(''.join(markups)) # Setup the pixel map. Make sure that layout not empty width, height = layout.get_pixel_size() if width and height: - padding = Constants.BLOCK_LABEL_PADDING - pixmap = self.get_parent().new_pixmap(width + 2 * padding, + padding = BLOCK_LABEL_PADDING + pixmap = self.parent.new_pixmap(width + 2 * padding, height + 2 * padding) gc = pixmap.new_gc() gc.set_foreground(Colors.COMMENT_BACKGROUND_COLOR) @@ -298,29 +212,50 @@ class Block(Element, _Block): else: self._comment_pixmap = None - def draw(self, gc, window): + def draw(self, widget, cr): """ Draw the signal block with label and inputs/outputs. - - Args: - gc: the graphics context - window: the gtk window to draw on """ # draw ports for port in self.get_ports_gui(): - port.draw(gc, window) + port.draw(widget, cr) # draw main block - x, y = self.get_coordinate() - Element.draw( - self, gc, window, bg_color=self._bg_color, - border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or - self.is_dummy_block and Colors.MISSING_BLOCK_BORDER_COLOR or Colors.BORDER_COLOR, + border_color = ( + Colors.HIGHLIGHT_COLOR if self.is_highlighted() else + Colors.MISSING_BLOCK_BORDER_COLOR if self.is_dummy_block else + Colors.BORDER_COLOR ) - #draw label image + Element.draw(self, widget, cr, border_color, self._bg_color) + x, y = self.get_coordinate() + # create the image surface + width = self.label_width + height = self.label_height + cr.set_source_rgb(*self._bg_color) + cr.save() if self.is_horizontal(): - window.draw_drawable(gc, self.horizontal_label, 0, 0, x+Constants.BLOCK_LABEL_PADDING, y+(self.H-self.label_height)/2, -1, -1) + cr.translate(x + BLOCK_LABEL_PADDING, y + (self.H - self.label_height) / 2) elif self.is_vertical(): - window.draw_drawable(gc, self.vertical_label, 0, 0, x+(self.H-self.label_height)/2, y+Constants.BLOCK_LABEL_PADDING, -1, -1) + cr.translate(x + (self.H - self.label_height) / 2, y + BLOCK_LABEL_PADDING) + cr.rotate(-90 * math.pi / 180.) + cr.translate(-width, 0) + + # cr.rectangle(0, 0, width, height) + # cr.fill() + + # draw the layouts + h_off = 0 + for i, layout in enumerate(self.layouts): + w, h = layout.get_pixel_size() + if i == 0: + w_off = (width - w) / 2 + else: + w_off = 0 + cr.translate(w_off, h_off) + PangoCairo.update_layout(cr, layout) + PangoCairo.show_layout(cr, layout) + cr.translate(-w_off, -h_off) + h_off = h + h_off + LABEL_SEPARATION + cr.restore() def what_is_selected(self, coor, coor_m=None): """ @@ -344,8 +279,8 @@ class Block(Element, _Block): x, y = self.get_coordinate() if self.is_horizontal(): - y += self.H + Constants.BLOCK_LABEL_PADDING + y += self.H + BLOCK_LABEL_PADDING else: - x += self.H + Constants.BLOCK_LABEL_PADDING + x += self.H + BLOCK_LABEL_PADDING window.draw_drawable(gc, self._comment_pixmap, 0, 0, x, y, -1, -1) |