summaryrefslogtreecommitdiff
path: root/grc/gui/Block.py
diff options
context:
space:
mode:
Diffstat (limited to 'grc/gui/Block.py')
-rw-r--r--grc/gui/Block.py291
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)