Commit e9e9348b authored by Maxime Perrotin's avatar Maxime Perrotin
Browse files

Add connection handling code

The code is however disabled because the tool does not support more than
one process at a time.
parent 8b6f95e7
......@@ -50,8 +50,10 @@ class Connection(QGraphicsPathItem):
self.childRect = child.sceneBoundingRect()
# Activate cache mode to boost rendering by calling paint less often
self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
# When the child moves, the connection may need to adjust the end point
# When the parent or child move, the connection may need
# to adjust the end point: done upon signal reception
self.child.moved.connect(self.child_moved)
self.parent.moved.connect(self.parent_moved)
# Syntax error indicator
self.syntax_error: Boolean = False
......@@ -60,6 +62,11 @@ class Connection(QGraphicsPathItem):
''' When the connection child moves - redefine in subclasses '''
pass
@Slot(float, float)
def parent_moved(self, delta_x, delta_y):
''' When the connection parent moves - redefine in subclasses '''
self.parent.update_connections()
@property
def start_point(self):
''' Compute connection origin - redefine in subclasses '''
......@@ -427,8 +434,36 @@ class Channel(Signalroute):
@Slot(float, float)
def child_moved(self, delta_x, delta_y):
''' When the connection child moves - redefined function '''
# compute the distance between the start and end points
dist_x = abs(self.end_point.x() - self.start_point.x())
dist_y = abs(self.end_point.y() - self.start_point.y())
new_dist_x = dist_x - delta_x
new_dist_y = dist_y - delta_y
self._end_point.setX(self._end_point.x() - delta_x)
self._end_point.setY(self._end_point.y() - delta_y)
x_shift, y_shift = [], []
middle_points = list(self.middle_points)
self._middle_points = []
for ratio, point in zip(self._ratios, middle_points):
fact_x, fact_y = ratio
sp = self.start_point
new_x = (sp.x() + new_dist_x * fact_x) if 0 <= fact_x <= 1 \
else point.x() - delta_x
new_y = (sp.y() + new_dist_y * fact_y) if 0 <= fact_y <= 1 \
else point.y() - delta_y
self._middle_points.append(
self.parent.mapToScene(QPointF(new_x, new_y)))
self.reshape()
self.update() # force a repaint
@Slot(float, float)
def parent_moved(self, delta_x, delta_y):
''' When the connection parent moves - redefined function '''
self.reshape()
self.update() # force a repaint
@property
def start_point(self):
......@@ -458,6 +493,25 @@ class Channel(Signalroute):
@middle_points.setter
def middle_points(self, points_scene_coord):
''' Redefined function: also store the relative position (percentage)
to the line length, in order to ensure proper dimensionning of
the connection when blocks are moved '''
# compute the distance between the start and end points
dist_x = abs(self.end_point.x() - self.start_point.x())
dist_y = abs(self.end_point.y() - self.start_point.y())
# Compute the distance ratio
self._ratios = []
for point in points_scene_coord:
pCoord = self.parent.mapFromScene(point)
len_x = abs(pCoord.x() - self.start_point.x())
len_y = abs(pCoord.y() - self.start_point.y())
fact_x = 1 if dist_x == 0 else len_x / dist_x
fact_y = 1 if dist_y == 0 else len_y / dist_y
if pCoord.y() < self.start_point.y():
fact_y = -fact_y
if pCoord.x() < self.start_point.x():
fact_x = -fact_x
self._ratios.append((fact_x, fact_y))
self._middle_points = points_scene_coord
def add_point(self, scene_coord):
......
......@@ -634,8 +634,10 @@ class Symbol(QObject, QGraphicsPathItem):
self.cam(self.coord, self.position)
# Emit signal to indicate that the symbol moved
# typically caught by connectors
self.moved.emit(self.coord.x() - self.pos_x,
self.coord.y() - self.pos_y)
# Moved to sdlSymbols.Process class, this event is actually
# sent while moving, so that connection is updated on the fly
#self.moved.emit(self.coord.x() - self.pos_x,
# self.coord.y() - self.pos_y)
self.mode = ''
def updateConnectionPoints(self):
......@@ -661,11 +663,18 @@ class Symbol(QObject, QGraphicsPathItem):
top_level = top_level.parentItem() or top_level.parent
return top_level
def cam_group(self):
''' Set the graphical boundaries of the item to apply the CAM on
This can be redifined in subclasses, for example to exclude
connections '''
return (self.sceneBoundingRect() |
self.mapRectToScene(self.childrenBoundingRect()))
# pylint: disable=R0914
def cam(self, old_pos, new_pos, ignore=None):
''' Collision Avoidance Manoeuvre for top level symbols '''
# Since the cam function is recursive it may be time consuming
# Call the Qt event prcessing to avoid blocking the application
# Call the Qt event processing to avoid blocking the application
# Removed (had bad visual side effects)
# QApplication.processEvents()
#print 'CAM', str(self)[slice(0, 20)]
......@@ -686,8 +695,7 @@ class Symbol(QObject, QGraphicsPathItem):
delta = new_pos - old_pos
# Rectangle of current group of item in scene coordinates
rect = (self.sceneBoundingRect() |
self.mapRectToScene(self.childrenBoundingRect()))
rect = self.cam_group()
# Move the rectangle to the new position, and move the current item
animation = False
......@@ -860,7 +868,7 @@ class Comment(Symbol):
QPoint(w, h), QPoint(x, h)])
def mouse_move(self, event):
''' Handle item move '''
''' Comment symbol: Handle item move '''
super().mouse_move(event)
if self.mode == 'Move':
self.pos_y += event.pos().y() - event.lastPos().y()
......@@ -1098,7 +1106,7 @@ class HorizontalSymbol(Symbol):
return None
def mouse_move(self, event):
''' Will prevent move from being above the parent '''
''' Horizontal symbols: prevent move from being above the parent '''
if self.mode == 'Move':
event_pos = event.pos()
new_y = self.pos_y + (event_pos.y() - event.lastPos().y())
......
......@@ -219,9 +219,6 @@ def log_errors(window, errors, warnings, clearfirst=True):
item = QListWidgetItem('[ERROR] ' + error[0])
if len(error) == 3:
item.setData(Qt.UserRole, error[1])
#found = self.scene().symbol_near(QPoint(*error[1]), 1)
# Pyside bug: setData cannot store 'found' directly
#item.setData(Qt.UserRole + 2, id(found))
item.setData(Qt.UserRole + 1, error[2])
if window:
window.addItem(item)
......@@ -451,7 +448,6 @@ class SDL_Scene(QGraphicsScene):
# only applies to 1st level hierarchy (process) allowing to have
# unmodifiable list of DCL and STATES at the 1st level of hierarchy
ACTIONS[self.context] = []
#self.allowed_symbols = [] if readonly else ACTIONS[self.context]
def is_aggregation(self):
''' Determine if the current scene is a state aggregation, i.e. if
......@@ -663,16 +659,14 @@ class SDL_Scene(QGraphicsScene):
# Render nested scenes, recursively:
for each in (item for item in dest_scene.visible_symb
if item.nested_scene):
LOG.debug(u'Recursive scene: ' + str(each))
LOG.debug('Recursive scene: ' + str(each))
if isinstance(each.nested_scene, ogAST.CompositeState) \
and (not each.nested_scene.statename
or each.nested_scene in already_created):
# Ignore nested state scenes that already exist
LOG.debug('Subscene "{}" ignored'.format(str(each)))
continue
subscene = \
self.create_subscene(each.context_name,
dest_scene)
subscene = self.create_subscene(each.context_name, dest_scene)
already_created.append(each.nested_scene)
subscene.name = str(each)
......@@ -1494,18 +1488,27 @@ class SDL_Scene(QGraphicsScene):
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)
in symb._conn_targets)# and
#len(self.edge_points) > 2)
# (The above was commented because it prevented
# direct lines between two blocks)
# "valid" could also check if it's allowed to connect
# a symbol to itself.
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
# Use a Channel type by default, but this could be something
# else in a different context
connector = Channel(parent=self.connection_start, child=symb)
# Set start and end points first, so that the distance can
# be computed when storing the middle points's relative
# positions
connector.start_point = self.edge_points[0]
connector.middle_points = self.edge_points[1:-1]
connector.end_point = self.border_point(symb, point)
connector.middle_points = self.edge_points[1:-1]
self.cancel()
super().mouseReleaseEvent(event)
......
......@@ -1074,7 +1074,7 @@ class Process(HorizontalSymbol):
blackbold = SDL_BLACKBOLD
redbold = SDL_REDBOLD
completion_list = set()
is_singleton = True
is_singleton = True #(False to allow multiple processes)
arrow_head = 'angle'
arrow_tail = 'angle'
# Process can be connected to other processes by the user
......@@ -1159,6 +1159,26 @@ class Process(HorizontalSymbol):
self.setPath(path)
super().set_shape(width, height)
def cam_group(self):
''' Redefine the graphical boundaries of the item to apply the CAM
If process has child connections (connections to other processes)
the CAM group should only include the process block itself,
not all the lines around it '''
return self.sceneBoundingRect()
def mouse_move(self, event):
''' In addition to default behaviour: update channel connections '''
#super().mouse_move(event)
if self.mode == 'Move':
event_pos = event.pos()
new_y = self.pos_y + (event_pos.y() - event.lastPos().y())
new_x = self.pos_x + (event_pos.x() - event.lastPos().x())
self.position = QPointF(new_x, new_y)
# Signal the move to the connections
self.moved.emit(event.lastPos().x() - event.pos().x(),
event.lastPos().y() - event.pos().y())
def update_completion_list(self, pr_text):
''' When text was entered, update completion list at block level '''
for each in CONTEXT.processes:
......
Supports Markdown
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