gtk3: add flowgraph draw code and other gtk3 fixes (WIP)

This commit is contained in:
Sebastian Koslowski 2016-04-18 18:11:52 +02:00 committed by Seth Hitefield
parent f337362360
commit 59cb4ebdd2
14 changed files with 229 additions and 269 deletions

View File

@ -219,10 +219,9 @@ class Platform(Element):
color = n.find('color') or ''
try:
import gi # ugly but handy
from gi.repository import Gdk
Gdk.color_parse(color)
except (ValueError, ImportError):
chars_per_color = 2 if len(color) > 4 else 1
tuple(int(color[o:o + 2], 16) / 255.0 for o in range(1, 3 * chars_per_color, chars_per_color))
except ValueError:
if color: # no color is okay, default set in GUI
print >> sys.stderr, 'Warning: Can\'t parse color code "{}" for domain "{}" '.format(color, key)
color = None

View File

@ -84,9 +84,8 @@ class ActionHandler:
# prevent key event stealing while the search box is active
# .has_focus() only in newer versions 2.17+?
# .is_focus() seems to work, but exactly the same
if self.main_window.btwin.search_entry.flags() & Gtk.HAS_FOCUS:
if self.main_window.btwin.search_entry.has_focus():
return False
if not self.get_focus_flag(): return False
return Actions.handle_key_press(event)
def _quit(self, window, event):
@ -447,9 +446,9 @@ class ActionHandler:
action.save_to_preferences()
elif action == Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR:
if self.init:
md = gtk.MessageDialog(main,
gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_INFO,
gtk.BUTTONS_CLOSE, "Moving the variable editor requires a restart of GRC.")
md = Gtk.MessageDialog(main,
Gtk.DIALOG_DESTROY_WITH_PARENT, Gtk.MESSAGE_INFO,
Gtk.BUTTONS_CLOSE, "Moving the variable editor requires a restart of GRC.")
md.run()
md.destroy()
action.save_to_preferences()

View File

@ -27,6 +27,7 @@ import Preferences
NO_MODS_MASK = 0
########################################################################
# Actions API
########################################################################
@ -48,7 +49,7 @@ def handle_key_press(event):
"""
_used_mods_mask = reduce(lambda x, y: x | y, [mod_mask for keyval, mod_mask in _actions_keypress_dict], NO_MODS_MASK)
# extract the key value and the consumed modifiers
keyval, egroup, level, consumed = _keymap.translate_keyboard_state(
_unused, keyval, egroup, level, consumed = _keymap.translate_keyboard_state(
event.hardware_keycode, event.get_state(), event.group)
# get the modifier mask and ignore irrelevant modifiers
mod_mask = event.get_state() & ~consumed & _used_mods_mask
@ -261,32 +262,32 @@ BLOCK_ROTATE_CW = Action(
BLOCK_VALIGN_TOP = Action(
label='Vertical Align Top',
tooltip='Align tops of selected blocks',
keypresses=(gtk.keysyms.t, gtk.gdk.SHIFT_MASK),
keypresses=(Gdk.KEY_t, Gdk.ModifierType.SHIFT_MASK),
)
BLOCK_VALIGN_MIDDLE = Action(
label='Vertical Align Middle',
tooltip='Align centers of selected blocks vertically',
keypresses=(gtk.keysyms.m, gtk.gdk.SHIFT_MASK),
keypresses=(Gdk.KEY_m, Gdk.ModifierType.SHIFT_MASK),
)
BLOCK_VALIGN_BOTTOM = Action(
label='Vertical Align Bottom',
tooltip='Align bottoms of selected blocks',
keypresses=(gtk.keysyms.b, gtk.gdk.SHIFT_MASK),
keypresses=(Gdk.KEY_b, Gdk.ModifierType.SHIFT_MASK),
)
BLOCK_HALIGN_LEFT = Action(
label='Horizontal Align Left',
tooltip='Align left edges of blocks selected blocks',
keypresses=(gtk.keysyms.l, gtk.gdk.SHIFT_MASK),
keypresses=(Gdk.KEY_l, Gdk.ModifierType.SHIFT_MASK),
)
BLOCK_HALIGN_CENTER = Action(
label='Horizontal Align Center',
tooltip='Align centers of selected blocks horizontally',
keypresses=(gtk.keysyms.c, gtk.gdk.SHIFT_MASK),
keypresses=(Gdk.KEY_c, Gdk.ModifierType.SHIFT_MASK),
)
BLOCK_HALIGN_RIGHT = Action(
label='Horizontal Align Right',
tooltip='Align right edges of selected blocks',
keypresses=(gtk.keysyms.r, gtk.gdk.SHIFT_MASK),
keypresses=(Gdk.KEY_r, Gdk.ModifierType.SHIFT_MASK),
)
BLOCK_ALIGNMENTS = [
BLOCK_VALIGN_TOP,
@ -341,9 +342,9 @@ TOGGLE_HIDE_VARIABLES = ToggleAction(
TOGGLE_FLOW_GRAPH_VAR_EDITOR = ToggleAction(
label='Show _Variable Editor',
tooltip='Show the variable editor. Modify variables and imports in this flow graph',
stock_id=gtk.STOCK_EDIT,
stock_id=Gtk.STOCK_EDIT,
default=True,
keypresses=(gtk.keysyms.e, gtk.gdk.CONTROL_MASK),
keypresses=(Gdk.KEY_e, Gdk.ModifierType.CONTROL_MASK),
preference_name='variable_editor_visable',
)
TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR = ToggleAction(
@ -407,7 +408,7 @@ ERRORS_WINDOW_DISPLAY = Action(
TOGGLE_CONSOLE_WINDOW = ToggleAction(
label='Show _Console Panel',
tooltip='Toggle visibility of the console',
keypresses=(Gdk.KEY_c, Gdk.ModifierType.CONTROL_MASK),
keypresses=(Gdk.KEY_r, Gdk.ModifierType.CONTROL_MASK),
preference_name='console_window_visible'
)
TOGGLE_BLOCKS_WINDOW = ToggleAction(

View File

@ -61,6 +61,7 @@ TOOLBAR_LIST = (
Actions.OPEN_HIER,
)
# The list of actions and categories for the menu bar.
MENU_BAR_LIST = (
(Gtk.Action(name='File', label='_File'), [
@ -88,7 +89,7 @@ MENU_BAR_LIST = (
None,
Actions.BLOCK_ROTATE_CCW,
Actions.BLOCK_ROTATE_CW,
(gtk.Action('Align', '_Align', None, None), Actions.BLOCK_ALIGNMENTS),
(Gtk.Action('Align', '_Align', None, None), Actions.BLOCK_ALIGNMENTS),
None,
Actions.BLOCK_ENABLE,
Actions.BLOCK_DISABLE,
@ -140,6 +141,7 @@ MENU_BAR_LIST = (
]),
)
# The list of actions for the context menu.
CONTEXT_MENU_LIST = [
Actions.BLOCK_CUT,

View File

@ -17,10 +17,11 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
import math
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Pango
gi.require_version('PangoCairo', '1.0')
from gi.repository import Gtk, Pango, PangoCairo
from . import Actions, Colors, Utils
@ -89,7 +90,9 @@ class Block(Element, _Block):
))
Element.__init__(self)
self._comment_pixmap = None
self._bg_color = Colors.BLOCK_ENABLED_COLOR
self.has_busses = [False, False] # source, sink
self.layouts = []
def get_coordinate(self):
"""
@ -196,14 +199,14 @@ 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)
self.layouts.append(layout)
layout.set_markup(Utils.parse_template(BLOCK_MARKUP_TMPL, block=self, font=BLOCK_FONT))
self.label_width, self.label_height = layout.get_pixel_size()
#display the params
@ -217,30 +220,11 @@ class Block(Element, _Block):
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 + 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 + 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)
#calculate width and height needed
W = self.label_width + 2 * BLOCK_LABEL_PADDING
@ -301,29 +285,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+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+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):
"""

View File

@ -16,26 +16,30 @@ 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
"""
try:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
# Not gtk3?
#COLORMAP = Gdk.colormap_get_system() #create all of the colors
#def get_color(color_code): return _COLORMAP.alloc_color(color_code, True, True)
def get_color(color_code):
color = Gdk.RGBA()
color.parse(color_code)
return color
chars_per_color = 2 if len(color_code) > 4 else 1
offsets = range(1, 3 * chars_per_color + 1, chars_per_color)
return tuple(int(color_code[o:o + 2], 16) / 255.0 for o in offsets)
HIGHLIGHT_COLOR = get_color('#00FFFF')
BORDER_COLOR = get_color('#444444')
# Missing blocks stuff
MISSING_BLOCK_BACKGROUND_COLOR = get_color('#FFF2F2')
MISSING_BLOCK_BORDER_COLOR = get_color('red')
MISSING_BLOCK_BORDER_COLOR = get_color('#FF0000')
# Param entry boxes
PARAM_ENTRY_TEXT_COLOR = get_color('black')
PARAM_ENTRY_TEXT_COLOR = get_color('#000000')
ENTRYENUM_CUSTOM_COLOR = get_color('#EEEEEE')
# Flow graph color constants
@ -49,12 +53,9 @@ try:
BLOCK_BYPASSED_COLOR = get_color('#F4FF81')
# Connection color constants
CONNECTION_ENABLED_COLOR = get_color('black')
CONNECTION_ENABLED_COLOR = get_color('#000000')
CONNECTION_DISABLED_COLOR = get_color('#BBBBBB')
CONNECTION_ERROR_COLOR = get_color('red')
CONNECTION_ERROR_COLOR = get_color('#FF0000')
except Exception as e:
print 'Unable to import Colors'
DEFAULT_DOMAIN_COLOR_CODE = '#777777'
print 'Unable to import Colors', e

View File

@ -91,10 +91,10 @@ class Connection(Element, _Connection):
]
source_domain = self.get_source().get_domain()
sink_domain = self.get_sink().get_domain()
self.line_attributes[0] = 2 if source_domain != sink_domain else 0
self.line_attributes[1] = Gdk.LINE_DOUBLE_DASH \
if not source_domain == sink_domain == GR_MESSAGE_DOMAIN \
else Gdk.LINE_ON_OFF_DASH
# self.line_attributes[0] = 2 if source_domain != sink_domain else 0
# self.line_attributes[1] = Gdk.LINE_DOUBLE_DASH \
# if not source_domain == sink_domain == GR_MESSAGE_DOMAIN \
# else Gdk.LINE_ON_OFF_DASH
get_domain_color = lambda d: Colors.get_color((
self.get_parent().get_parent().domains.get(d, {})
).get('color') or Colors.DEFAULT_DOMAIN_COLOR_CODE)
@ -147,13 +147,9 @@ class Connection(Element, _Connection):
self.add_line((x1, y1), points[0])
self.add_line((x2, y2), points[0])
def draw(self, gc, window):
def draw(self, widget, cr):
"""
Draw the connection.
Args:
gc: the graphics context
window: the gtk window to draw on
"""
sink = self.get_sink()
source = self.get_source()
@ -175,11 +171,12 @@ class Connection(Element, _Connection):
Colors.CONNECTION_DISABLED_COLOR if not self.get_enabled() else
color
)
Element.draw(self, gc, window, mod_color(self._color), mod_color(self._bg_color))
Element.draw(self, widget, cr, mod_color(self._color), mod_color(self._bg_color))
# draw arrow on sink port
try:
gc.set_foreground(mod_color(self._arrow_color))
gc.set_line_attributes(0, Gdk.LINE_SOLID, Gdk.CAP_BUTT, Gdk.JOIN_MITER)
window.draw_polygon(gc, True, self._arrow)
except:
pass
cr.set_source_rgb(*self._arrow_color)
# TODO: gc.set_line_attributes(0, Gdk.LINE_SOLID, Gdk.CAP_BUTT, Gdk.JOIN_MITER)
cr.move_to(*self._arrow[0])
cr.line_to(*self._arrow[1])
cr.line_to(*self._arrow[2])
cr.close_path()
cr.fill()

View File

@ -48,8 +48,7 @@ class DrawingArea(Gtk.DrawingArea):
GObject.GObject.__init__(self)
self.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
self.connect('realize', self._handle_window_realize)
self.connect('configure-event', self._handle_window_configure)
self.connect('expose-event', self._handle_window_expose)
self.connect('draw', self.draw)
self.connect('motion-notify-event', self._handle_mouse_motion)
self.connect('button-press-event', self._handle_mouse_button_press)
self.connect('button-release-event', self._handle_mouse_button_release)
@ -59,35 +58,22 @@ class DrawingArea(Gtk.DrawingArea):
Gdk.EventMask.POINTER_MOTION_MASK | \
Gdk.EventMask.BUTTON_RELEASE_MASK | \
Gdk.EventMask.LEAVE_NOTIFY_MASK | \
Gdk.EventMask.ENTER_NOTIFY_MASK | \
Gdk.EventMask.FOCUS_CHANGE_MASK
Gdk.EventMask.ENTER_NOTIFY_MASK #| \
#Gdk.EventMask.FOCUS_CHANGE_MASK
)
#setup drag and drop
self.drag_dest_set(Gtk.DestDefaults.ALL, DND_TARGETS, Gdk.DragAction.COPY)
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
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)
self.set_flags(Gtk.CAN_FOCUS) # self.set_can_focus(True)
self.connect('focus-out-event', self._handle_focus_lost_event)
def new_pixmap(self, width, height):
return Gdk.Pixmap(self.window, width, height, -1)
def get_screenshot(self, transparent_bg=False):
pixmap = self._pixmap
W, H = pixmap.get_size()
pixbuf = GdkPixbuf.Pixbuf(GdkPixbuf.Colorspace.RGB, 0, 8, W, H)
pixbuf.fill(0xFF + Colors.FLOWGRAPH_BACKGROUND_COLOR.pixel << 8)
pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0, W-1, H-1)
if transparent_bg:
bgc = Colors.FLOWGRAPH_BACKGROUND_COLOR
pixbuf = pixbuf.add_alpha(True, bgc.red, bgc.green, bgc.blue)
return pixbuf
# self.set_flags(Gtk.CAN_FOCUS) # self.set_can_focus(True)
# self.connect('focus-out-event', self._handle_focus_lost_event)
##########################################################################
## Handlers
@ -96,7 +82,7 @@ class DrawingArea(Gtk.DrawingArea):
"""
Handle a drag and drop by adding a block at the given coordinate.
"""
self._flow_graph.add_new_block(selection_data.data, (x, y))
self._flow_graph.add_new_block(selection_data.get_text(), (x, y))
def _handle_mouse_scroll(self, widget, event):
if event.get_state() & Gdk.ModifierType.SHIFT_MASK:
@ -113,7 +99,7 @@ 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_press(
double_click=(event.type == Gdk._2BUTTON_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(
@ -148,27 +134,8 @@ class DrawingArea(Gtk.DrawingArea):
"""
self._flow_graph.update()
def _handle_window_configure(self, widget, event):
"""
Called when the window is resized.
Create a new pixmap for background buffer.
"""
self._pixmap = self.new_pixmap(*self.get_size_request())
def _handle_window_expose(self, widget, event):
"""
Called when window is exposed, or queue_draw is called.
Double buffering: draw to pixmap, then draw pixmap to window.
"""
gc = self.window.new_gc()
self._flow_graph.draw(gc, self._pixmap)
self.window.draw_drawable(gc, self._pixmap, 0, 0, 0, 0, -1, -1)
# draw a light grey line on the bottom and right end of the canvas.
# this is useful when the theme uses the same panel bg color as the canvas
W, H = self._pixmap.get_size()
gc.set_foreground(Colors.FLOWGRAPH_EDGE_COLOR)
self.window.draw_line(gc, 0, H-1, W, H-1)
self.window.draw_line(gc, W-1, 0, W-1, H)
def draw(self, widget, cr):
self._flow_graph.draw(widget, cr)
def _handle_focus_lost_event(self, widget, event):
# don't clear selection while context menu is active

View File

@ -90,29 +90,33 @@ class Element(object):
self.clear()
for child in self.get_children(): child.create_shapes()
def draw(self, gc, window, border_color, bg_color):
def draw(self, widget, cr, border_color, bg_color):
"""
Draw in the given window.
Args:
gc: the graphics context
window: the gtk window to draw on
widget:
cr:
border_color: the color for lines and rectangle borders
bg_color: the color for the inside of the rectangle
"""
X, Y = self.get_coordinate()
gc.set_line_attributes(*self.line_attributes)
# TODO: gc.set_line_attributes(*self.line_attributes)
for (rX, rY), (W, H) in self._areas_list:
aX = X + rX
aY = Y + rY
gc.set_foreground(bg_color)
window.draw_rectangle(gc, True, aX, aY, W, H)
gc.set_foreground(border_color)
window.draw_rectangle(gc, False, aX, aY, W, H)
cr.set_source_rgb(*bg_color)
cr.rectangle(aX, aY, W, H)
cr.fill()
cr.set_source_rgb(*border_color)
cr.rectangle(aX, aY, W, H)
cr.stroke()
for (x1, y1), (x2, y2) in self._lines_list:
gc.set_foreground(border_color)
gc.set_background(bg_color)
window.draw_line(gc, X+x1, Y+y1, X+x2, Y+y2)
cr.set_source_rgb(*border_color)
cr.move_to(X + x1, Y + y1)
cr.line_to(X + x2, Y + y2)
cr.stroke()
def rotate(self, rotation):
"""

View File

@ -397,23 +397,21 @@ class FlowGraph(Element, _Flowgraph):
changed = True
return changed
def draw(self, gc, window):
def draw(self, widget, cr):
"""
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)
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(gc, window)
# block.draw_comment(widget, cr)
pass
#draw multi select rectangle
if self.mouse_pressed and (not self.get_selected_elements() or self.get_ctrl_mask()):
#coordinates
@ -423,10 +421,13 @@ class FlowGraph(Element, _Flowgraph):
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)
cr.set_source_rgb(*Colors.HIGHLIGHT_COLOR)
cr.rectangle(x, y, w, h)
cr.fill()
cr.set_source_rgb(*Colors.BORDER_COLOR)
cr.rectangle(x, y, w, h)
cr.stroke()
#draw blocks on top of connections
hide_disabled_blocks = Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active()
hide_variables = Actions.TOGGLE_HIDE_VARIABLES.get_active()
@ -437,10 +438,10 @@ class FlowGraph(Element, _Flowgraph):
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
element.draw(gc, window)
element.draw(widget, cr)
#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)
selected_element.draw(widget, cr)
def update_selected(self):
"""

View File

@ -36,6 +36,7 @@ from .NotebookPage import NotebookPage
from ..core import Messages
MAIN_WINDOW_TITLE_TMPL = """\
#if not $saved
*#slurp
@ -104,8 +105,8 @@ class MainWindow(Gtk.Window):
vbox.pack_start(self.tool_bar, False, False, 0)
# Main parent container for the different panels
self.container = Gtk.HPaned()
vbox.pack_start(self.container)
self.main = Gtk.HPaned() #(orientation=Gtk.Orientation.HORIZONTAL)
vbox.pack_start(self.main, True, True, 0)
# Create the notebook
self.notebook = Gtk.Notebook()
@ -127,9 +128,9 @@ class MainWindow(Gtk.Window):
self.vars = VariableEditor(platform, self.get_flow_graph)
# Figure out which place to put the variable editor
self.left = Gtk.VPaned()
self.right = Gtk.VPaned()
self.left_subpanel = Gtk.HPaned()
self.left = Gtk.VPaned() #orientation=Gtk.Orientation.VERTICAL)
self.right = Gtk.VPaned() #orientation=Gtk.Orientation.VERTICAL)
self.left_subpanel = Gtk.HPaned() #orientation=Gtk.Orientation.HORIZONTAL)
self.variable_panel_sidebar = Preferences.variable_editor_sidebar()
if self.variable_panel_sidebar:
@ -147,12 +148,12 @@ class MainWindow(Gtk.Window):
# Create the right panel
self.right.pack1(self.btwin)
self.container.pack1(self.left)
self.container.pack2(self.right, False)
self.main.pack1(self.left)
self.main.pack2(self.right, False)
# Load preferences and show the main window
self.resize(*Preferences.main_window_size())
self.container.set_position(Preferences.blocks_window_position())
self.main.set_position(Preferences.blocks_window_position())
self.left.set_position(Preferences.console_window_position())
if self.variable_panel_sidebar:
self.right.set_position(Preferences.variable_editor_position(sidebar=True))
@ -276,9 +277,7 @@ class MainWindow(Gtk.Window):
return
#add this page to the notebook
self.notebook.append_page(page, page.get_tab())
try: self.notebook.set_tab_reorderable(page, True)
except: pass #gtk too old
self.notebook.set_tab_label_packing(page, False, False, Gtk.PACK_START)
self.notebook.set_tab_reorderable(page, True)
#only show if blank or manual
if not file_path or show: self._set_page(page)
@ -303,7 +302,7 @@ class MainWindow(Gtk.Window):
Preferences.file_open(open_file)
Preferences.main_window_size(self.get_size())
Preferences.console_window_position(self.left.get_position())
Preferences.blocks_window_position(self.container.get_position())
Preferences.blocks_window_position(self.main.get_position())
if self.variable_panel_sidebar:
Preferences.variable_editor_position(self.right.get_position(), sidebar=True)
else:
@ -405,14 +404,11 @@ class MainWindow(Gtk.Window):
Returns:
the selected flow graph
"""
return None
# TODO: Issues with flowgraphs
#return self.get_page().get_flow_graph()
return self.get_page().get_flow_graph()
def get_focus_flag(self):
"""
Get the focus flag from the current page.
Returns:
the focus flag
"""

View File

@ -54,7 +54,7 @@ class NotebookPage(Gtk.HBox):
GObject.GObject.__init__(self)
self.show()
#tab box to hold label and close button
self.tab = Gtk.HBox(False, 0)
self.tab = Gtk.HBox(homogeneous=False, spacing=0)
#setup tab label
self.label = Gtk.Label()
self.tab.pack_start(self.label, False, False, 0)
@ -62,7 +62,7 @@ class NotebookPage(Gtk.HBox):
image = Gtk.Image()
image.set_from_stock('gtk-close', Gtk.IconSize.MENU)
#setup image box
image_box = Gtk.HBox(False, 0)
image_box = Gtk.HBox(homogeneous=False, spacing=0)
image_box.pack_start(image, True, False, 0)
#setup the button
button = Gtk.Button()
@ -79,20 +79,18 @@ class NotebookPage(Gtk.HBox):
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.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.get_drawing_area())
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
#self.get_flow_graph().drawing_area = self.get_drawing_area()
self.get_flow_graph().drawing_area = self.drawing_area
self.show_all()
def get_drawing_area(self): return self.drawing_area
def _handle_scroll_window_key_press(self, widget, event):
"""forward Ctrl-PgUp/Down to NotebookPage (switch fg instead of horiz. scroll"""
is_ctrl_pg = (
event.state & gtk.gdk.CONTROL_MASK and
event.keyval in (gtk.keysyms.Page_Up, gtk.keysyms.Page_Down)
event.state & Gdk.ModifierType.CONTROL_MASK and
event.keyval in (Gdk.KEY_Page_Up, Gdk.KEY_Page_Down)
)
if is_ctrl_pg:
return self.get_parent().event(event)

View File

@ -17,9 +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 math
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gtk, PangoCairo
from . import Actions, Colors, Utils
from .Constants import (
@ -45,15 +46,19 @@ class Port(_Port, Element):
"""
_Port.__init__(self, block, n, dir)
Element.__init__(self)
self.W = self.H = self.w = self.h = 0
self.W = self.w = self.h = 0
self.H = 20 # todo: fix
self._connector_coordinate = (0, 0)
self._connector_length = 0
self._hovering = True
self._force_label_unhidden = False
self.layout = Gtk.DrawingArea().create_pango_layout('')
self._bg_color = Colors.get_color(self.get_color())
def create_shapes(self):
"""Create new areas and labels for the port."""
Element.create_shapes(self)
self._bg_color = Colors.get_color(self.get_color())
if self.get_hide():
return # this port is hidden, no need to create shapes
if self.get_domain() == GR_MESSAGE_DOMAIN:
@ -112,50 +117,34 @@ class Port(_Port, Element):
def create_labels(self):
"""Create the labels for the socket."""
Element.create_labels(self)
self._bg_color = Colors.get_color(self.get_color())
# create the layout
layout = Gtk.DrawingArea().create_pango_layout('')
layout.set_markup(Utils.parse_template(PORT_MARKUP_TMPL, port=self, font=PORT_FONT))
self.w, self.h = layout.get_pixel_size()
self.W = 2 * PORT_LABEL_PADDING + self.w
self.H = 2 * PORT_LABEL_PADDING + self.h * (
3 if self.get_type() == 'bus' else 1)
self.H += self.H % 2
# create the pixmap
pixmap = self.get_parent().get_parent().new_pixmap(self.w, self.h)
gc = pixmap.new_gc()
gc.set_foreground(self._bg_color)
pixmap.draw_rectangle(gc, True, 0, 0, self.w, self.h)
pixmap.draw_layout(gc, 0, 0, layout)
# create vertical and horizontal pixmaps
self.horizontal_label = pixmap
if self.is_vertical():
self.vertical_label = self.get_parent().get_parent().new_pixmap(self.h, self.w)
Utils.rotate_pixmap(gc, self.horizontal_label, self.vertical_label)
self.layout.set_markup(Utils.parse_template(PORT_MARKUP_TMPL, port=self, font=PORT_FONT))
def draw(self, gc, window):
def draw(self, widget, cr):
"""
Draw the socket with a label.
Args:
gc: the graphics context
window: the gtk window to draw on
"""
Element.draw(
self, gc, window, bg_color=self._bg_color,
border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or
self.get_parent().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.get_parent().is_dummy_block else
Colors.BORDER_COLOR
)
Element.draw(self, widget, cr, border_color, self._bg_color)
if not self._areas_list or self._label_hidden():
return # this port is either hidden (no areas) or folded (no label)
X, Y = self.get_coordinate()
(x, y), (w, h) = self._areas_list[0] # use the first area's sizes to place the labels
(x, y), _ = self._areas_list[0]
cr.set_source_rgb(*self._bg_color)
cr.save()
if self.is_horizontal():
window.draw_drawable(gc, self.horizontal_label, 0, 0, x+X+(self.W-self.w)/2, y+Y+(self.H-self.h)/2, -1, -1)
cr.translate(x + X + (self.W - self.w) / 2, y + Y + (self.H - self.h) / 2)
elif self.is_vertical():
window.draw_drawable(gc, self.vertical_label, 0, 0, x+X+(self.H-self.h)/2, y+Y+(self.W-self.w)/2, -1, -1)
cr.translate(x + X + (self.H - self.h) / 2, y + Y + (self.W - self.w) / 2)
cr.rotate(-90 * math.pi / 180.)
cr.translate(-self.w, 0)
PangoCairo.update_layout(cr, self.layout)
PangoCairo.show_layout(cr, self.layout)
cr.restore()
def get_connector_coordinate(self):
"""

View File

@ -1,5 +1,5 @@
"""
Copyright 2015 Free Software Foundation, Inc.
Copyright 2015, 2016 Free Software Foundation, Inc.
This file is part of GNU Radio
GNU Radio Companion is free software; you can redistribute it and/or
@ -19,10 +19,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
from operator import attrgetter
import pygtk
pygtk.require('2.0')
import gtk
import gobject
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GObject
from . import Actions
from . import Preferences
@ -32,34 +33,34 @@ BLOCK_INDEX = 0
ID_INDEX = 1
class VariableEditorContextMenu(gtk.Menu):
class VariableEditorContextMenu(Gtk.Menu):
""" A simple context menu for our variable editor """
def __init__(self, var_edit):
gtk.Menu.__init__(self)
Gtk.Menu.__init__(self)
self.imports = gtk.MenuItem("Add _Import")
self.imports = Gtk.MenuItem("Add _Import")
self.imports.connect('activate', var_edit.handle_action, var_edit.ADD_IMPORT)
self.add(self.imports)
self.variables = gtk.MenuItem("Add _Variable")
self.variables = Gtk.MenuItem("Add _Variable")
self.variables.connect('activate', var_edit.handle_action, var_edit.ADD_VARIABLE)
self.add(self.variables)
self.add(gtk.SeparatorMenuItem())
self.add(Gtk.SeparatorMenuItem())
self.enable = gtk.MenuItem("_Enable")
self.enable = Gtk.MenuItem("_Enable")
self.enable.connect('activate', var_edit.handle_action, var_edit.ENABLE_BLOCK)
self.disable = gtk.MenuItem("_Disable")
self.disable = Gtk.MenuItem("_Disable")
self.disable.connect('activate', var_edit.handle_action, var_edit.DISABLE_BLOCK)
self.add(self.enable)
self.add(self.disable)
self.add(gtk.SeparatorMenuItem())
self.add(Gtk.SeparatorMenuItem())
self.delete = gtk.MenuItem("_Delete")
self.delete = Gtk.MenuItem("_Delete")
self.delete.connect('activate', var_edit.handle_action, var_edit.DELETE_BLOCK)
self.add(self.delete)
self.add(gtk.SeparatorMenuItem())
self.add(Gtk.SeparatorMenuItem())
self.properties = gtk.MenuItem("_Properties...")
self.properties = Gtk.MenuItem("_Properties...")
self.properties.connect('activate', var_edit.handle_action, var_edit.OPEN_PROPERTIES)
self.add(self.properties)
self.show_all()
@ -71,7 +72,7 @@ class VariableEditorContextMenu(gtk.Menu):
self.disable.set_sensitive(selected and enabled)
class VariableEditor(gtk.VBox):
class VariableEditor(Gtk.VBox):
# Actions that are handled by the editor
ADD_IMPORT = 0
@ -83,7 +84,7 @@ class VariableEditor(gtk.VBox):
DISABLE_BLOCK = 6
def __init__(self, platform, get_flow_graph):
gtk.VBox.__init__(self)
Gtk.VBox.__init__(self)
self.platform = platform
self.get_flow_graph = get_flow_graph
self._block = None
@ -91,14 +92,14 @@ class VariableEditor(gtk.VBox):
# Only use the model to store the block reference and name.
# Generate everything else dynamically
self.treestore = gtk.TreeStore(gobject.TYPE_PYOBJECT, # Block reference
gobject.TYPE_STRING) # Category and block name
self.treeview = gtk.TreeView(self.treestore)
self.treestore = Gtk.TreeStore(GObject.TYPE_PYOBJECT, # Block reference
GObject.TYPE_STRING) # Category and block name
self.treeview = Gtk.TreeView(self.treestore)
self.treeview.set_enable_search(False)
self.treeview.set_search_column(-1)
#self.treeview.set_enable_search(True)
#self.treeview.set_search_column(ID_INDEX)
self.treeview.get_selection().set_mode('single')
self.treeview.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
self.treeview.set_headers_visible(True)
self.treeview.connect('button-press-event', self._handle_mouse_button_press)
self.treeview.connect('button-release-event', self._handle_mouse_button_release)
@ -106,67 +107,67 @@ class VariableEditor(gtk.VBox):
self.treeview.connect('key-press-event', self._handle_key_button_press)
# Block Name or Category
self.id_cell = gtk.CellRendererText()
self.id_cell = Gtk.CellRendererText()
self.id_cell.connect('edited', self._handle_name_edited_cb)
id_column = gtk.TreeViewColumn("Id", self.id_cell, text=ID_INDEX)
id_column = Gtk.TreeViewColumn("Id", self.id_cell, text=ID_INDEX)
id_column.set_name("id")
id_column.set_resizable(True)
id_column.set_max_width(300)
id_column.set_min_width(80)
id_column.set_fixed_width(100)
id_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
id_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
id_column.set_cell_data_func(self.id_cell, self.set_properties)
self.id_column = id_column
self.treeview.append_column(id_column)
self.treestore.set_sort_column_id(ID_INDEX, gtk.SORT_ASCENDING)
self.treestore.set_sort_column_id(ID_INDEX, Gtk.SortType.ASCENDING)
# For forcing resize
self._col_width = 0
# Block Value
self.value_cell = gtk.CellRendererText()
self.value_cell = Gtk.CellRendererText()
self.value_cell.connect('edited', self._handle_value_edited_cb)
value_column = gtk.TreeViewColumn("Value", self.value_cell)
value_column = Gtk.TreeViewColumn("Value", self.value_cell)
value_column.set_name("value")
value_column.set_resizable(False)
value_column.set_expand(True)
value_column.set_min_width(100)
value_column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
value_column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
value_column.set_cell_data_func(self.value_cell, self.set_value)
self.value_column = value_column
self.treeview.append_column(value_column)
# Block Actions (Add, Remove)
self.action_cell = gtk.CellRendererPixbuf()
self.action_cell = Gtk.CellRendererPixbuf()
value_column.pack_start(self.action_cell, False)
value_column.set_cell_data_func(self.action_cell, self.set_icon)
# Make the scrolled window to hold the tree view
scrolled_window = gtk.ScrolledWindow()
scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scrolled_window = Gtk.ScrolledWindow()
scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
scrolled_window.add_with_viewport(self.treeview)
scrolled_window.set_size_request(DEFAULT_BLOCKS_WINDOW_WIDTH, -1)
self.pack_start(scrolled_window)
self.pack_start(scrolled_window, True, True, 0)
# Context menus
self._context_menu = VariableEditorContextMenu(self)
self._confirm_delete = Preferences.variable_editor_confirm_delete()
# Sets cell contents
def set_icon(self, col, cell, model, iter):
def set_icon(self, col, cell, model, iter, data):
block = model.get_value(iter, BLOCK_INDEX)
if block:
pb = self.treeview.render_icon(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU, None)
pb = self.treeview.render_icon(Gtk.STOCK_CLOSE, 16, None)
else:
pb = self.treeview.render_icon(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU, None)
pb = self.treeview.render_icon(Gtk.STOCK_ADD, 16, None)
cell.set_property('pixbuf', pb)
def set_value(self, col, cell, model, iter):
def set_value(self, col, cell, model, iter, data):
sp = cell.set_property
block = model.get_value(iter, BLOCK_INDEX)
# Set the default properties for this column first.
# Some set in set_properties() may be overridden (editable for advanced variable blocks)
self.set_properties(col, cell, model, iter)
self.set_properties(col, cell, model, iter, data)
# Set defaults
value = None
@ -198,7 +199,7 @@ class VariableEditor(gtk.VBox):
# Always set the text value.
sp('text', value)
def set_properties(self, col, cell, model, iter):
def set_properties(self, col, cell, model, iter, data):
sp = cell.set_property
block = model.get_value(iter, BLOCK_INDEX)
# Set defaults
@ -268,13 +269,13 @@ class VariableEditor(gtk.VBox):
elif key == self.DELETE_CONFIRM:
if self._confirm_delete:
# Create a context menu to confirm the delete operation
confirmation_menu = gtk.Menu()
confirmation_menu = Gtk.Menu()
block_id = self._block.get_param('id').get_value().replace("_", "__")
confirm = gtk.MenuItem("Delete {}".format(block_id))
confirm = Gtk.MenuItem("Delete {}".format(block_id))
confirm.connect('activate', self.handle_action, self.DELETE_BLOCK)
confirmation_menu.add(confirm)
confirmation_menu.show_all()
confirmation_menu.popup(None, None, None, event.button, event.time)
confirmation_menu.popup(None, None, None, None, event.button, event.time)
else:
self.handle_action(None, self.DELETE_BLOCK, None)
elif key == self.ENABLE_BLOCK:
@ -302,12 +303,12 @@ class VariableEditor(gtk.VBox):
if event.button == 1 and col.get_name() == "value":
# Make sure this has a block (not the import/variable rows)
if self._block and event.type == gtk.gdk._2BUTTON_PRESS:
if self._block and event.type == Gdk.EventType._2BUTTON_PRESS:
# Open the advanced dialog if it is a gui variable
if self._block.get_key() not in ("variable", "import"):
self.handle_action(None, self.OPEN_PROPERTIES, event=event)
return True
if event.type == gtk.gdk.BUTTON_PRESS:
if event.type == Gdk.EventType.BUTTON_PRESS:
# User is adding/removing blocks
# Make sure this is the action cell (Add/Remove Icons)
if path[2] > col.cell_get_position(self.action_cell)[0]:
@ -320,15 +321,15 @@ class VariableEditor(gtk.VBox):
else:
self.handle_action(None, self.DELETE_CONFIRM, event=event)
return True
elif event.button == 3 and event.type == gtk.gdk.BUTTON_PRESS:
elif event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
if self._block:
self._context_menu.update_sensitive(True, enabled=self._block.get_enabled())
else:
self._context_menu.update_sensitive(False)
self._context_menu.popup(None, None, None, event.button, event.time)
self._context_menu.popup(None, None, None, None, event.button, event.time)
# Null handler. Stops the treeview from handling double click events.
if event.type == gtk.gdk._2BUTTON_PRESS:
if event.type == Gdk.EventType._2BUTTON_PRESS:
return True
return False