@@ -31,18 +31,26 @@ def __init__(self, input_port=None, output_port=None):
3131 self .setZValue (Z_VAL_PIPE )
3232 self .setAcceptHoverEvents (True )
3333 self .setFlag (QtWidgets .QGraphicsItem .ItemIsSelectable )
34+ self .setCacheMode (ITEM_CACHE_MODE )
35+
3436 self ._color = PipeEnum .COLOR .value
3537 self ._style = PipeEnum .DRAW_TYPE_DEFAULT .value
3638 self ._active = False
3739 self ._highlight = False
3840 self ._input_port = input_port
3941 self ._output_port = output_port
42+
4043 size = 6.0
41- self ._arrow = QtGui .QPolygonF ()
42- self ._arrow .append (QtCore .QPointF (- size , size ))
43- self ._arrow .append (QtCore .QPointF (0.0 , - size * 1.5 ))
44- self ._arrow .append (QtCore .QPointF (size , size ))
45- self .setCacheMode (ITEM_CACHE_MODE )
44+ self ._poly = QtGui .QPolygonF ()
45+ self ._poly .append (QtCore .QPointF (- size , size ))
46+ self ._poly .append (QtCore .QPointF (0.0 , - size * 1.5 ))
47+ self ._poly .append (QtCore .QPointF (size , size ))
48+
49+ self ._dir_pointer = QtWidgets .QGraphicsPolygonItem (self )
50+ self ._dir_pointer .setPolygon (self ._poly )
51+ self ._dir_pointer .setFlag (self .ItemIsSelectable , False )
52+
53+ self .reset ()
4654
4755 def __repr__ (self ):
4856 in_name = self ._input_port .name if self ._input_port else ''
@@ -63,6 +71,13 @@ def hoverLeaveEvent(self, event):
6371 if self .isSelected ():
6472 self .highlight ()
6573
74+ def itemChange (self , change , value ):
75+ if change == self .ItemSelectedChange and self .scene ():
76+ self .reset ()
77+ if value :
78+ self .highlight ()
79+ return super (PipeItem , self ).itemChange (change , value )
80+
6681 def paint (self , painter , option , widget ):
6782 """
6883 Draws the connection line between nodes.
@@ -73,92 +88,52 @@ def paint(self, painter, option, widget):
7388 used to describe the parameters needed to draw.
7489 widget (QtWidgets.QWidget): not used.
7590 """
91+ painter .save ()
7692
77- # only draw if a port or node is visible.
78- is_visible = all ([
79- self ._input_port .isVisible (),
80- self ._output_port .isVisible (),
81- self ._input_port .node .isVisible (),
82- self ._output_port .node .isVisible ()
83- ])
84- if not is_visible :
85- painter .save ()
86- painter .setBrush (QtCore .Qt .NoBrush )
87- painter .setPen (QtCore .Qt .NoPen )
88- painter .restore ()
89- return
90-
91- color = QtGui .QColor (* self ._color )
92- pen_style = PIPE_STYLES .get (self .style )
93- pen_width = PipeEnum .WIDTH .value
94- if self ._active :
95- color = QtGui .QColor (* PipeEnum .ACTIVE_COLOR .value )
96- if pen_style == QtCore .Qt .DashDotDotLine :
97- pen_width += 1
98- else :
99- pen_width += 0.35
100- elif self ._highlight :
101- color = QtGui .QColor (* PipeEnum .HIGHLIGHT_COLOR .value )
102- pen_style = PIPE_STYLES .get (PipeEnum .DRAW_TYPE_DEFAULT .value )
103-
93+ pen = self .pen ()
10494 if self .disabled ():
10595 if not self ._active :
106- color = QtGui .QColor (* PipeEnum .DISABLED_COLOR .value )
107- pen_width += 0.2
108- pen_style = PIPE_STYLES .get (PipeEnum .DRAW_TYPE_DOTTED .value )
109-
110- pen = QtGui .QPen (color , pen_width , pen_style )
111- pen .setCapStyle (QtCore .Qt .RoundCap )
112- pen .setJoinStyle (QtCore .Qt .MiterJoin )
96+ pen .setColor (QtGui .QColor (* PipeEnum .DISABLED_COLOR .value ))
97+ pen .setStyle (PIPE_STYLES .get (PipeEnum .DRAW_TYPE_DOTTED .value ))
11398
114- painter .save ()
11599 painter .setPen (pen )
100+ painter .setBrush (self .brush ())
116101 painter .setRenderHint (painter .Antialiasing , True )
117102 painter .drawPath (self .path ())
118103
119- # draw arrow
120- if self .input_port and self .output_port :
121- cen_x = self .path ().pointAtPercent (0.5 ).x ()
122- cen_y = self .path ().pointAtPercent (0.5 ).y ()
123- loc_pt = self .path ().pointAtPercent (0.49 )
124- tgt_pt = self .path ().pointAtPercent (0.51 )
125-
126- dist = math .hypot (tgt_pt .x () - cen_x , tgt_pt .y () - cen_y )
127- if dist < 0.5 :
128- painter .restore ()
129- return
130-
131- color .setAlpha (255 )
132- if self ._highlight :
133- painter .setBrush (QtGui .QBrush (color .lighter (150 )))
134- elif self ._active or self .disabled ():
135- painter .setBrush (QtGui .QBrush (color .darker (200 )))
136- else :
137- painter .setBrush (QtGui .QBrush (color .darker (130 )))
138-
139- pen_width = 0.6
140- if dist < 1.0 :
141- pen_width *= (1.0 + dist )
142-
143- pen = QtGui .QPen (color , pen_width )
144- pen .setCapStyle (QtCore .Qt .RoundCap )
145- pen .setJoinStyle (QtCore .Qt .MiterJoin )
146- painter .setPen (pen )
147-
148- transform = QtGui .QTransform ()
149- transform .translate (cen_x , cen_y )
150- radians = math .atan2 (tgt_pt .y () - loc_pt .y (),
151- tgt_pt .x () - loc_pt .x ())
152- degrees = math .degrees (radians ) - 90
153- transform .rotate (degrees )
154- if dist < 1.0 :
155- transform .scale (dist , dist )
156- painter .drawPolygon (transform .map (self ._arrow ))
157-
158104 # QPaintDevice: Cannot destroy paint device that is being painted.
159105 painter .restore ()
160106
161- def __draw_path_cycled_vertical (self , start_port , pos1 , pos2 , path ):
107+ @staticmethod
108+ def _calc_distance (p1 , p2 ):
109+ x = math .pow ((p2 .x () - p1 .x ()), 2 )
110+ y = math .pow ((p2 .y () - p1 .y ()), 2 )
111+ return math .sqrt (x + y )
112+
113+ def _draw_direction_pointer (self ):
114+ """
115+ updates the pipe direction pointer arrow.
116+ """
117+ if not (self .input_port and self .output_port ):
118+ self ._dir_pointer .setVisible (False )
119+ return
120+
121+ self ._dir_pointer .setVisible (True )
122+ loc_pt = self .path ().pointAtPercent (0.49 )
123+ tgt_pt = self .path ().pointAtPercent (0.51 )
124+ radians = math .atan2 (tgt_pt .y () - loc_pt .y (),
125+ tgt_pt .x () - loc_pt .x ())
126+ degrees = math .degrees (radians ) - 90
127+ self ._dir_pointer .setRotation (degrees )
128+ self ._dir_pointer .setPos (self .path ().pointAtPercent (0.5 ))
129+
130+ cen_x = self .path ().pointAtPercent (0.5 ).x ()
131+ cen_y = self .path ().pointAtPercent (0.5 ).y ()
132+ dist = math .hypot (tgt_pt .x () - cen_x , tgt_pt .y () - cen_y )
133+ if dist < 1.0 :
134+ self ._dir_pointer .setScale (dist )
135+
136+ def _draw_path_cycled_vertical (self , start_port , pos1 , pos2 , path ):
162137 """
163138 Draw pipe vertically around node if connection is cyclic.
164139
@@ -184,7 +159,7 @@ def __draw_path_cycled_vertical(self, start_port, pos1, pos2, path):
184159 path .lineTo (start_pos )
185160 self .setPath (path )
186161
187- def __draw_path_cycled_horizontal (self , start_port , pos1 , pos2 , path ):
162+ def _draw_path_cycled_horizontal (self , start_port , pos1 , pos2 , path ):
188163 """
189164 Draw pipe horizontally around node if connection is cyclic.
190165
@@ -210,7 +185,7 @@ def __draw_path_cycled_horizontal(self, start_port, pos1, pos2, path):
210185 path .lineTo (end_pos )
211186 self .setPath (path )
212187
213- def __draw_path_vertical (self , start_port , pos1 , pos2 , path ):
188+ def _draw_path_vertical (self , start_port , pos1 , pos2 , path ):
214189 """
215190 Draws the vertical path between ports.
216191
@@ -254,7 +229,7 @@ def __draw_path_vertical(self, start_port, pos1, pos2, path):
254229 path .lineTo (pos2 )
255230 self .setPath (path )
256231
257- def __draw_path_horizontal (self , start_port , pos1 , pos2 , path ):
232+ def _draw_path_horizontal (self , start_port , pos1 , pos2 , path ):
258233 """
259234 Draws the horizontal path between ports.
260235
@@ -322,6 +297,20 @@ def draw_path(self, start_port, end_port=None, cursor_pos=None):
322297 else :
323298 return
324299
300+ # visibility check for connected pipe.
301+ if self .input_port and self .output_port :
302+ is_visible = all ([
303+ self ._input_port .isVisible (),
304+ self ._output_port .isVisible (),
305+ self ._input_port .node .isVisible (),
306+ self ._output_port .node .isVisible ()
307+ ])
308+ self .setVisible (is_visible )
309+
310+ # don't draw pipe if a port or node is not visible.
311+ if not is_visible :
312+ return
313+
325314 line = QtCore .QLineF (pos1 , pos2 )
326315 path = QtGui .QPainterPath ()
327316
@@ -330,39 +319,49 @@ def draw_path(self, start_port, end_port=None, cursor_pos=None):
330319 if end_port and not self .viewer ().acyclic :
331320 if end_port .node == start_port .node :
332321 if direction is LayoutDirectionEnum .VERTICAL .value :
333- self .__draw_path_cycled_vertical (
322+ self ._draw_path_cycled_vertical (
334323 start_port , pos1 , pos2 , path
335324 )
325+ self ._draw_direction_pointer ()
336326 return
337327 elif direction is LayoutDirectionEnum .HORIZONTAL .value :
338- self .__draw_path_cycled_horizontal (
328+ self ._draw_path_cycled_horizontal (
339329 start_port , pos1 , pos2 , path
340330 )
331+ self ._draw_direction_pointer ()
341332 return
342333
343334 path .moveTo (line .x1 (), line .y1 ())
344335
345336 if self .viewer_pipe_layout () == PipeLayoutEnum .STRAIGHT .value :
346337 path .lineTo (pos2 )
347338 self .setPath (path )
339+ self ._draw_direction_pointer ()
348340 return
349341
350342 if direction is LayoutDirectionEnum .VERTICAL .value :
351- self .__draw_path_vertical (start_port , pos1 , pos2 , path )
343+ self ._draw_path_vertical (start_port , pos1 , pos2 , path )
352344 elif direction is LayoutDirectionEnum .HORIZONTAL .value :
353- self .__draw_path_horizontal (start_port , pos1 , pos2 , path )
345+ self ._draw_path_horizontal (start_port , pos1 , pos2 , path )
346+
347+ self ._draw_direction_pointer ()
354348
355349 def reset_path (self ):
350+ """
351+ reset the pipe initial path position.
352+ """
356353 path = QtGui .QPainterPath (QtCore .QPointF (0.0 , 0.0 ))
357354 self .setPath (path )
358355
359- @staticmethod
360- def _calc_distance (p1 , p2 ):
361- x = math .pow ((p2 .x () - p1 .x ()), 2 )
362- y = math .pow ((p2 .y () - p1 .y ()), 2 )
363- return math .sqrt (x + y )
364-
365356 def port_from_pos (self , pos , reverse = False ):
357+ """
358+ Args:
359+ pos (QtCore.QPointF): current scene position.
360+ reverse (bool): false to return the nearest port.
361+
362+ Returns:
363+ PortItem: port item.
364+ """
366365 inport_pos = self .input_port .scenePos ()
367366 outport_pos = self .output_port .scenePos ()
368367 input_dist = self ._calc_distance (inport_pos , pos )
@@ -399,34 +398,59 @@ def viewer_layout_direction(self):
399398 if viewer :
400399 return viewer .get_layout_direction ()
401400
401+ def set_pipe_styling (self , color , width = 0.5 , style = 0 ):
402+ """
403+ Args:
404+ color (list or tuple): (r, g, b, a) values 0-255
405+ width (float): pipe width.
406+ style (int): pipe style.
407+ """
408+ pen = self .pen ()
409+ pen .setWidth (width )
410+ pen .setColor (QtGui .QColor (* color ))
411+ pen .setStyle (PIPE_STYLES .get (style ))
412+ pen .setJoinStyle (QtCore .Qt .MiterJoin )
413+ pen .setCapStyle (QtCore .Qt .RoundCap )
414+ self .setPen (pen )
415+ self .setBrush (QtCore .Qt .NoBrush )
416+
417+ pen = self ._dir_pointer .pen ()
418+ pen .setJoinStyle (QtCore .Qt .MiterJoin )
419+ pen .setCapStyle (QtCore .Qt .RoundCap )
420+ pen .setWidth (width )
421+ pen .setColor (QtGui .QColor (* color ))
422+ self ._dir_pointer .setPen (pen )
423+ self ._dir_pointer .setBrush (QtGui .QColor (* color ).darker (200 ))
424+
402425 def activate (self ):
403426 self ._active = True
404- color = QtGui .QColor (* PipeEnum .ACTIVE_COLOR .value )
405- pen = QtGui .QPen (
406- color , 2.5 , PIPE_STYLES .get (PipeEnum .DRAW_TYPE_DEFAULT .value )
427+ self .set_pipe_styling (
428+ color = PipeEnum .ACTIVE_COLOR .value ,
429+ width = 2.5 ,
430+ style = PipeEnum .DRAW_TYPE_DEFAULT .value
407431 )
408- self .setPen (pen )
409432
410433 def active (self ):
411434 return self ._active
412435
413436 def highlight (self ):
414437 self ._highlight = True
415- color = QtGui .QColor (* PipeEnum .HIGHLIGHT_COLOR .value )
416- pen = QtGui .QPen (
417- color , 2 , PIPE_STYLES .get (PipeEnum .DRAW_TYPE_DEFAULT .value )
438+ self .set_pipe_styling (
439+ color = PipeEnum .HIGHLIGHT_COLOR .value ,
440+ width = 2.5 ,
441+ style = PipeEnum .DRAW_TYPE_DEFAULT .value
418442 )
419- self .setPen (pen )
420443
421444 def highlighted (self ):
422445 return self ._highlight
423446
424447 def reset (self ):
448+ """
449+ reset the pipe state and styling.
450+ """
425451 self ._active = False
426452 self ._highlight = False
427- color = QtGui .QColor (* self .color )
428- pen = QtGui .QPen (color , 2 , PIPE_STYLES .get (self .style ))
429- self .setPen (pen )
453+ self .set_pipe_styling (color = self .color , width = 1.2 , style = self .style )
430454
431455 def set_connections (self , port1 , port2 ):
432456 ports = {
@@ -445,13 +469,6 @@ def disabled(self):
445469 return True
446470 return False
447471
448- def itemChange (self , change , value ):
449- if change == self .ItemSelectedChange and self .scene ():
450- self .reset ()
451- if value :
452- self .highlight ()
453- return super (PipeItem , self ).itemChange (change , value )
454-
455472 @property
456473 def input_port (self ):
457474 return self ._input_port
@@ -515,7 +532,7 @@ def __init__(self):
515532 pen .setCapStyle (QtCore .Qt .RoundCap )
516533
517534 self ._idx_pointer = LivePipePolygonItem (self )
518- self ._idx_pointer .setPolygon (self ._arrow )
535+ self ._idx_pointer .setPolygon (self ._poly )
519536 self ._idx_pointer .setBrush (color .darker (300 ))
520537 self ._idx_pointer .setPen (pen )
521538
@@ -638,7 +655,7 @@ def draw_index_pointer(self, start_port, cursor_pos, color_mode=None):
638655 self ._idx_text .setPos (* text_pos )
639656 self ._idx_text .setPlainText ('{}' .format (start_port .name ))
640657
641- self ._idx_pointer .setPolygon (transform .map (self ._arrow ))
658+ self ._idx_pointer .setPolygon (transform .map (self ._poly ))
642659
643660 if color_mode == 'accept' :
644661 color = QtGui .QColor (* PipeEnum .HIGHLIGHT_COLOR .value )
@@ -660,7 +677,7 @@ class LivePipePolygonItem(QtWidgets.QGraphicsPolygonItem):
660677
661678 def __init__ (self , parent ):
662679 super (LivePipePolygonItem , self ).__init__ (parent )
663- self .setFlag (self .ItemIsSelectable , True )
680+ self .setFlag (QtWidgets . QGraphicsItem .ItemIsSelectable , True )
664681
665682 def paint (self , painter , option , widget ):
666683 """
0 commit comments