@@ -158,6 +158,58 @@ def paint(self, painter, option, widget):
158158 # QPaintDevice: Cannot destroy paint device that is being painted.
159159 painter .restore ()
160160
161+ def __draw_path_cycled_vertical (self , start_port , pos1 , pos2 , path ):
162+ """
163+ Draw pipe vertically around node if connection is cyclic.
164+
165+ Args:
166+ start_port (PortItem): port used to draw the starting point.
167+ pos1 (QPointF): start port position.
168+ pos2 (QPointF): end port position.
169+ path (QPainterPath): path to draw.
170+ """
171+ n_rect = start_port .node .boundingRect ()
172+ ptype = start_port .port_type
173+ start_pos = pos1 if ptype == PortTypeEnum .IN .value else pos2
174+ end_pos = pos2 if ptype == PortTypeEnum .IN .value else pos1
175+
176+ padding = 40
177+ top = start_pos .y () - padding
178+ bottom = end_pos .y () + padding
179+ path .moveTo (end_pos )
180+ path .lineTo (end_pos .x (), bottom )
181+ path .lineTo (end_pos .x () + n_rect .right (), bottom )
182+ path .lineTo (end_pos .x () + n_rect .right (), top )
183+ path .lineTo (start_pos .x (), top )
184+ path .lineTo (start_pos )
185+ self .setPath (path )
186+
187+ def __draw_path_cycled_horizontal (self , start_port , pos1 , pos2 , path ):
188+ """
189+ Draw pipe horizontally around node if connection is cyclic.
190+
191+ Args:
192+ start_port (PortItem): port used to draw the starting point.
193+ pos1 (QPointF): start port position.
194+ pos2 (QPointF): end port position.
195+ path (QPainterPath): path to draw.
196+ """
197+ n_rect = start_port .node .boundingRect ()
198+ ptype = start_port .port_type
199+ start_pos = pos1 if ptype == PortTypeEnum .IN .value else pos2
200+ end_pos = pos2 if ptype == PortTypeEnum .IN .value else pos1
201+
202+ padding = 40
203+ left = end_pos .x () + padding
204+ right = start_pos .x () - padding
205+ path .moveTo (start_pos )
206+ path .lineTo (right , start_pos .y ())
207+ path .lineTo (right , end_pos .y () + n_rect .bottom ())
208+ path .lineTo (left , end_pos .y () + n_rect .bottom ())
209+ path .lineTo (left , end_pos .y ())
210+ path .lineTo (end_pos )
211+ self .setPath (path )
212+
161213 def __draw_path_vertical (self , start_port , pos1 , pos2 , path ):
162214 """
163215 Draws the vertical path between ports.
@@ -272,47 +324,79 @@ def draw_path(self, start_port, end_port=None, cursor_pos=None):
272324
273325 line = QtCore .QLineF (pos1 , pos2 )
274326 path = QtGui .QPainterPath ()
327+
328+ direction = self .viewer_layout_direction ()
329+
330+ if end_port and not self .viewer ().acyclic :
331+ if end_port .node == start_port .node :
332+ if direction is LayoutDirectionEnum .VERTICAL .value :
333+ self .__draw_path_cycled_vertical (
334+ start_port , pos1 , pos2 , path
335+ )
336+ return
337+ elif direction is LayoutDirectionEnum .HORIZONTAL .value :
338+ self .__draw_path_cycled_horizontal (
339+ start_port , pos1 , pos2 , path
340+ )
341+ return
342+
275343 path .moveTo (line .x1 (), line .y1 ())
276344
277345 if self .viewer_pipe_layout () == PipeLayoutEnum .STRAIGHT .value :
278346 path .lineTo (pos2 )
279347 self .setPath (path )
280348 return
281349
282- if self . viewer_layout_direction () is LayoutDirectionEnum .VERTICAL .value :
350+ if direction is LayoutDirectionEnum .VERTICAL .value :
283351 self .__draw_path_vertical (start_port , pos1 , pos2 , path )
284- elif self . viewer_layout_direction () is LayoutDirectionEnum .HORIZONTAL .value :
352+ elif direction is LayoutDirectionEnum .HORIZONTAL .value :
285353 self .__draw_path_horizontal (start_port , pos1 , pos2 , path )
286354
287355 def reset_path (self ):
288356 path = QtGui .QPainterPath (QtCore .QPointF (0.0 , 0.0 ))
289357 self .setPath (path )
290358
291359 @staticmethod
292- def calc_distance (p1 , p2 ):
360+ def _calc_distance (p1 , p2 ):
293361 x = math .pow ((p2 .x () - p1 .x ()), 2 )
294362 y = math .pow ((p2 .y () - p1 .y ()), 2 )
295363 return math .sqrt (x + y )
296364
297365 def port_from_pos (self , pos , reverse = False ):
298366 inport_pos = self .input_port .scenePos ()
299367 outport_pos = self .output_port .scenePos ()
300- input_dist = self .calc_distance (inport_pos , pos )
301- output_dist = self .calc_distance (outport_pos , pos )
368+ input_dist = self ._calc_distance (inport_pos , pos )
369+ output_dist = self ._calc_distance (outport_pos , pos )
302370 if input_dist < output_dist :
303371 port = self .output_port if reverse else self .input_port
304372 else :
305373 port = self .input_port if reverse else self .output_port
306374 return port
307375
308- def viewer_pipe_layout (self ):
376+ def viewer (self ):
377+ """
378+ Returns:
379+ NodeViewer: node graph viewer.
380+ """
309381 if self .scene ():
310- viewer = self .scene ().viewer ()
382+ return self .scene ().viewer ()
383+
384+ def viewer_pipe_layout (self ):
385+ """
386+ Returns:
387+ int: pipe layout mode.
388+ """
389+ viewer = self .viewer ()
390+ if viewer :
311391 return viewer .get_pipe_layout ()
312392
313393 def viewer_layout_direction (self ):
314- if self .scene ():
315- viewer = self .scene ().viewer ()
394+ """
395+ Returns:
396+ int: graph layout mode.
397+ """
398+ viewer = self .viewer ()
399+ if viewer :
316400 return viewer .get_layout_direction ()
317401
318402 def activate (self ):
0 commit comments