Commit 44c9ff3d authored by Maxime Perrotin's avatar Maxime Perrotin
Browse files

Introduce connection start/end zones

parent 61f6d9f6
......@@ -457,6 +457,12 @@ class Channel(Signalroute):
def add_point(self, scene_coord):
self._middle_points.append(scene_coord)
def paint(self, painter, _, ___):
''' Apply anti-aliasing '''
painter.setRenderHint(QPainter.Antialiasing, True)
super(Channel, self).paint(painter, _, ___)
class Controlpoint(QGraphicsPathItem, object):
''' Class handling one edge control point (to change bezier curves) '''
......
......@@ -89,7 +89,8 @@ class Symbol(QObject, QGraphicsPathItem, object):
_insertable_followers = [] # no limit to insert below current symbol
_terminal_followers = [] # cannot be inserted between two symbols
# List of symbols that can be connected, but without parent-child relation
_connectable_siblings = []
_conn_sources = [] # source types that can connect to this symbol
_conn_targets = [] # target types that can connect to this symbol
# By default a symbol is resizeable
resizeable = True
# By default symbol size may expand when inner text exceeds border
......@@ -154,7 +155,7 @@ class Symbol(QObject, QGraphicsPathItem, object):
# and default text alignment within a textbox
self.text_alignment = Qt.AlignLeft
# Activate cache mode to boost rendering by calling paint less often
self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
# self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
# Apply symbol default mouse cursor
self.setCursor(self.default_cursor)
# De-ativate cache mode otherwise paint is not properly updated
......@@ -226,6 +227,28 @@ class Symbol(QObject, QGraphicsPathItem, object):
''' Set the value of the nested scene '''
self._nested_scene = value
# Connection zones (for start and end of connection) are lists of QRect
# that define areas for the mouse to grab the symbol and start a connection
# These zones depend on the shape of the symbol and must be defined in
# sub-classes. By default there are none. Example in SDL symbol "Process".
# Zones are defined as properties because they must be dynamically
# computed, e.g. based on the current size of the symbol.
@property
def conn_start_zones(self):
return []
@property
def conn_end_zones(self):
return []
def in_start_zone(self, point): # type: QPoint
''' Return true if "point" is in one of the connection start zones '''
return any(rect.contains(point) for rect in self.conn_start_zones)
def in_end_zone(self, point): # type: QPoint
''' Return true if "point" is in one of the connection end zones '''
return any(rect.contains(point) for rect in self.conn_end_zones)
def closest_connection_point(self, coord):
'''
Given a position (QPointF), expected in this symbol's
......@@ -901,6 +924,8 @@ class Cornergrabber(QGraphicsPolygonItem, object):
# Parent item may have changed its cursor (e.g. when inserting
# items). In that case, don't override the cursor for that area
cursor = self.parent.cursor()
elif self.parent.in_start_zone(event.pos().toPoint()):
cursor = Qt.CrossCursor
elif not self.parent.resizeable:
cursor = self.parent.default_cursor
self.resize_mode = ''
......
......@@ -410,7 +410,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
self.click_coordinates = None
self.orig_pos = None
# When connecting symbols, store list of intermediate points
self.edge_points = [] # type: List[QPointF] in scene coordinates
self.edge_points = [] # type: List[QPointF] in scene coordinates
self.temp_lines = [] # type: List[QGraphicsLineItem]
self.process_name = 'opengeode'
# Scene name is used to update the tab window name when scene changes
......@@ -1296,10 +1296,10 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
item.bezier_set_visible(False)
except AttributeError:
pass
elif symb.user_can_connect \
and event.modifiers() == Qt.ControlModifier:
# TODO check if symbol can be a connection source, can have
# more than one connection if there is already one, etc.
elif symb.user_can_connect and symb.in_start_zone(event.pos().toPoint()):
# TODO check if symbol can have more than
# one connection if there is already one, if start
# and end can be on the same symbol, etc.
self.mode = 'wait_next_connection_point'
click_point = event.scenePos()
point = self.border_point(symb, click_point)
......@@ -1371,6 +1371,13 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
if map(setup_action, candidates):
menu.exec_(pos)
def cancel(self):
''' Return to idle mode, reset current actions '''
self.select_rect.hide()
for each in self.temp_lines:
each.setVisible(False)
self.mode = 'idle'
# pylint: disable=C0103
def mouseReleaseEvent(self, event):
if self.mode == 'select_items':
......@@ -1389,9 +1396,8 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
# No items to select, so propose a context dependent menu
self.quick_menu(event.screenPos(), rect)
#self.removeItem(self.select_rect)
# XXX stop with removeItem, it provokes segfault
self.select_rect.hide()
self.mode = 'idle'
# stop with removeItem, it provokes segfault
self.cancel()
elif self.mode == 'wait_next_connection_point':
point = event.scenePos()
previous = self.edge_points[-1]
......@@ -1400,26 +1406,8 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
if abs(point.y() - previous.y()) < 15:
point.setY(previous.y())
symb = self.symbol_near(point, dist=1)
if symb:
# Clicked on a symbol: create the actual connector
connector = Channel(parent=self.connection_start, child=symb)
connector.start_point = self.edge_points[0]
connector.middle_points = self.edge_points[1:]
connector.end_point = self.border_point(symb, point)
# connector = Connection(parent=self.connection_start,
# child=symb)
# connector._start_point = \
# connector.mapFromScene(self.edge_points[0])
# connector._middle_points = [connector.mapFromScene(p)
# for p in self.edge_points[1:]]
# connector._end_point = \
# connector.mapFromScene(self.border_point(symb, point))
for each in self.temp_lines:
# Just hide to avoid pyside segfaults
each.setVisible(False)
self.mode = 'idle'
else:
if previous != point:
# Draw a temporary line to the scene
current_line = self.temp_lines[-1]
line = current_line.line()
current_line.setLine(line.x1(), line.y1(),
......@@ -1429,6 +1417,23 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
point.y(),
point.x(),
point.y()))
# Decide if the connection is valid, create it accordingly
valid = (symb and symb.__class__.__name__
in self.connection_start._conn_sources and
self.connection_start.__class__.__name__
in symb._conn_targets and
len(self.edge_points) > 2)
if symb and valid:
nb_segments = len(self.edge_points) - 1
for each in self.temp_lines[-nb_segments:]:
# check lines that collide with the source or dest TODO
pass
# Clicked on a symbol: create the actual connector
connector = Channel(parent=self.connection_start, child=symb)
connector.start_point = self.edge_points[0]
connector.middle_points = self.edge_points[1:-1]
connector.end_point = self.border_point(symb, point)
self.cancel()
super(SDL_Scene, self).mouseReleaseEvent(event)
......@@ -1442,6 +1447,8 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
self.clearSelection()
self.clear_highlight()
self.clear_focus()
elif event.key() == Qt.Key_Escape:
self.cancel()
elif event.matches(QtGui.QKeySequence.Undo):
if not isinstance(self.focusItem(), EditableText):
LOG.debug('UNDO ' + self.undo_stack.undoText())
......
......@@ -1008,7 +1008,8 @@ class Process(HorizontalSymbol):
arrow_tail = 'angle'
# Process can be connected to other processes by the user
user_can_connect = True
_connectable_siblings = ['Process']
_conn_sources = ['Process']
_conn_targets = ['Process']
def __init__(self, ast=None, subscene=None):
ast = ast or ogAST.Process()
......@@ -1028,6 +1029,25 @@ class Process(HorizontalSymbol):
self.output_signals = ast.output_signals
self.insert_symbol(None, self.x(), self.y())
@property
def conn_start_zones(self):
''' Redefined - define the zones in the symbol from which user can
start a connection with another symbol '''
rect = self.boundingRect()
yield QRect(15, 5, rect.width() - 30, 10)
yield QRect(5, 5, 10, rect.height() - 10)
yield QRect(rect.width() - 15, 5, 10, rect.height() - 10)
yield QRect(15, rect.height() - 15, rect.width() - 30, 10)
@property
def conn_end_zones(self):
''' Redefined - define the zones that can receive a connection '''
rect = self.boundingRect()
yield QRect(15, 5, rect.width() - 30, 10)
yield QRect(5, 5, 10, rect.height() - 10)
yield QRect(rect.width() - 15, 5, 10, rect.height() - 10)
yield QRect(15, rect.heigth() - 15, rect.width() - 30, 10)
def insert_symbol(self, parent, x, y):
''' Redefinition - adds connection line to env '''
super(Process, self).insert_symbol(parent, x, y)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment