55from NodeGraphQt import QtGui , QtCore , QtWidgets
66from NodeGraphQt .constants import (IN_PORT , OUT_PORT ,
77 PIPE_LAYOUT_CURVED ,
8- PIPE_STYLE_DASHED ,
9- SCENE_AREA ,
10- Z_VAL_NODE_WIDGET )
8+ SCENE_AREA ,)
119from NodeGraphQt .qgraphics .node_abstract import AbstractNodeItem
1210from NodeGraphQt .qgraphics .node_backdrop import BackdropNodeItem
13- from NodeGraphQt .qgraphics .pipe import Pipe
11+ from NodeGraphQt .qgraphics .pipe import Pipe , LivePipe
1412from NodeGraphQt .qgraphics .port import PortItem
1513from NodeGraphQt .qgraphics .slicer import SlicerPipe
1614from NodeGraphQt .widgets .scene import NodeScene
@@ -55,7 +53,6 @@ def __init__(self, parent=None):
5553 self .resize (1000 , 800 )
5654
5755 self ._pipe_layout = PIPE_LAYOUT_CURVED
58- self ._live_pipe = None
5956 self ._detached_port = None
6057 self ._start_port = None
6158 self ._origin_pos = None
@@ -65,6 +62,11 @@ def __init__(self, parent=None):
6562 self ._rubber_band = QtWidgets .QRubberBand (
6663 QtWidgets .QRubberBand .Rectangle , self
6764 )
65+
66+ self ._live_pipe = LivePipe ()
67+ self ._live_pipe .setVisible (False )
68+ self .scene ().addItem (self ._live_pipe )
69+
6870 self ._pipe_slicer = SlicerPipe ()
6971 self ._pipe_slicer .setVisible (False )
7072 self .scene ().addItem (self ._pipe_slicer )
@@ -87,6 +89,9 @@ def __init__(self, parent=None):
8789 self .LMB_state = False
8890 self .RMB_state = False
8991 self .MMB_state = False
92+ self .ALT_state = False
93+ self .CTRL_state = False
94+ self .SHIFT_state = False
9095
9196 def __repr__ (self ):
9297 return '{}.{}()' .format (
@@ -123,7 +128,10 @@ def _items_near(self, pos, item_type=None, width=20, height=20):
123128 x , y = pos .x () - width , pos .y () - height
124129 rect = QtCore .QRectF (x , y , width , height )
125130 items = []
131+ excl = [self ._live_pipe , self ._pipe_slicer ]
126132 for item in self .scene ().items (rect ):
133+ if item in excl :
134+ continue
127135 if not item_type or isinstance (item , item_type ):
128136 items .append (item )
129137 return items
@@ -135,7 +143,8 @@ def _on_search_submitted(self, node_type):
135143 def _on_pipes_sliced (self , path ):
136144 self .connection_sliced .emit ([
137145 [i .input_port , i .output_port ]
138- for i in self .scene ().items (path ) if isinstance (i , Pipe )
146+ for i in self .scene ().items (path )
147+ if isinstance (i , Pipe ) and i != self ._live_pipe
139148 ])
140149
141150 # --- reimplemented events ---
@@ -151,15 +160,13 @@ def contextMenuEvent(self, event):
151160 return super (NodeViewer , self ).contextMenuEvent (event )
152161
153162 def mousePressEvent (self , event ):
154- alt_modifier = event .modifiers () == QtCore .Qt .AltModifier
155- shift_modifier = event .modifiers () == QtCore .Qt .ShiftModifier
156-
157163 if event .button () == QtCore .Qt .LeftButton :
158164 self .LMB_state = True
159165 elif event .button () == QtCore .Qt .RightButton :
160166 self .RMB_state = True
161167 elif event .button () == QtCore .Qt .MiddleButton :
162168 self .MMB_state = True
169+
163170 self ._origin_pos = event .pos ()
164171 self ._previous_pos = event .pos ()
165172 self ._prev_selection = self .selected_nodes ()
@@ -172,19 +179,21 @@ def mousePressEvent(self, event):
172179 map_pos = self .mapToScene (event .pos ())
173180
174181 # pipe slicer enabled.
175- if event . modifiers () == ( QtCore . Qt . AltModifier | QtCore . Qt . ShiftModifier ) :
182+ if self . ALT_state and self . SHIFT_state :
176183 self ._pipe_slicer .draw_path (map_pos , map_pos )
177184 self ._pipe_slicer .setVisible (True )
178185 return
179186
180- if alt_modifier :
187+ # pan mode.
188+ if self .ALT_state :
181189 return
182190
183191 items = self ._items_near (map_pos , None , 20 , 20 )
184192 nodes = [i for i in items if isinstance (i , AbstractNodeItem )]
193+ pipes = [i for i in items if isinstance (i , Pipe )]
185194
186195 # toggle extend node selection.
187- if shift_modifier :
196+ if self . SHIFT_state :
188197 for node in nodes :
189198 node .selected = not node .selected
190199
@@ -193,7 +202,7 @@ def mousePressEvent(self, event):
193202 {n : n .xy_pos for n in self .selected_nodes ()}
194203 )
195204
196- # show selection selection marquee
205+ # show selection selection marquee.
197206 if self .LMB_state and not items :
198207 rect = QtCore .QRect (self ._previous_pos , QtCore .QSize ())
199208 rect = rect .normalized ()
@@ -202,7 +211,8 @@ def mousePressEvent(self, event):
202211 self ._rubber_band .setGeometry (rect )
203212 self ._rubber_band .show ()
204213
205- if not shift_modifier :
214+ # allow new live pipe with the shift modifier.
215+ if not self .SHIFT_state or self .SHIFT_state and pipes :
206216 super (NodeViewer , self ).mousePressEvent (event )
207217
208218 def mouseReleaseEvent (self , event ):
@@ -241,22 +251,21 @@ def mouseReleaseEvent(self, event):
241251 super (NodeViewer , self ).mouseReleaseEvent (event )
242252
243253 def mouseMoveEvent (self , event ):
244- alt_modifier = event .modifiers () == QtCore .Qt .AltModifier
245- shift_modifier = event .modifiers () == QtCore .Qt .ShiftModifier
246- if event .modifiers () == (QtCore .Qt .AltModifier | QtCore .Qt .ShiftModifier ):
247- if self .LMB_state :
254+ if self .ALT_state and self .SHIFT_state :
255+ if self .LMB_state and self ._pipe_slicer .isVisible ():
248256 p1 = self ._pipe_slicer .path ().pointAtPercent (0 )
249257 p2 = self .mapToScene (self ._previous_pos )
250258 self ._pipe_slicer .draw_path (p1 , p2 )
251- self ._previous_pos = event .pos ()
252- super (NodeViewer , self ).mouseMoveEvent (event )
253- return
259+ self ._pipe_slicer .show ()
260+ self ._previous_pos = event .pos ()
261+ super (NodeViewer , self ).mouseMoveEvent (event )
262+ return
254263
255- if self .MMB_state and alt_modifier :
264+ if self .MMB_state and self . ALT_state :
256265 pos_x = (event .x () - self ._previous_pos .x ())
257266 zoom = 0.1 if pos_x > 0 else - 0.1
258267 self ._set_viewer_zoom (zoom , 0.05 )
259- elif self .MMB_state or (self .LMB_state and alt_modifier ):
268+ elif self .MMB_state or (self .LMB_state and self . ALT_state ):
260269 pos_x = (event .x () - self ._previous_pos .x ())
261270 pos_y = (event .y () - self ._previous_pos .y ())
262271 self ._set_viewer_pan (pos_x , pos_y )
@@ -270,7 +279,7 @@ def mouseMoveEvent(self, event):
270279 self .scene ().setSelectionArea (path , QtCore .Qt .IntersectsItemShape )
271280 self .scene ().update (map_rect )
272281
273- if shift_modifier and self ._prev_selection :
282+ if self . SHIFT_state and self ._prev_selection :
274283 for node in self ._prev_selection :
275284 if node not in self .selected_nodes ():
276285 node .selected = True
@@ -309,6 +318,24 @@ def dragMoveEvent(self, event):
309318 def dragLeaveEvent (self , event ):
310319 event .ignore ()
311320
321+ def keyPressEvent (self , event ):
322+ self .ALT_state = event .modifiers () == QtCore .Qt .AltModifier
323+ self .CTRL_state = event .modifiers () == QtCore .Qt .ControlModifier
324+ self .SHIFT_state = event .modifiers () == QtCore .Qt .ShiftModifier
325+
326+ # Todo: find a better solution to catch modifier keys.
327+ if event .modifiers () == (QtCore .Qt .AltModifier | QtCore .Qt .ShiftModifier ):
328+ self .ALT_state = True
329+ self .SHIFT_state = True
330+
331+ super (NodeViewer , self ).keyPressEvent (event )
332+
333+ def keyReleaseEvent (self , event ):
334+ self .ALT_state = event .modifiers () == QtCore .Qt .AltModifier
335+ self .CTRL_state = event .modifiers () == QtCore .Qt .ControlModifier
336+ self .SHIFT_state = event .modifiers () == QtCore .Qt .ShiftModifier
337+ super (NodeViewer , self ).keyReleaseEvent (event )
338+
312339 # --- scene events ---
313340
314341 def sceneMouseMoveEvent (self , event ):
@@ -320,7 +347,7 @@ def sceneMouseMoveEvent(self, event):
320347 event (QtWidgets.QGraphicsSceneMouseEvent):
321348 The event handler from the QtWidgets.QGraphicsScene
322349 """
323- if not self ._live_pipe :
350+ if not self ._live_pipe . isVisible () :
324351 return
325352 if not self ._start_port :
326353 return
@@ -334,7 +361,7 @@ def sceneMouseMoveEvent(self, event):
334361 pos .setX (pos .x () + x )
335362 pos .setY (pos .y () + y )
336363
337- self ._live_pipe .draw_path (self ._start_port , None , pos )
364+ self ._live_pipe .draw_path (self ._start_port , cursor_pos = pos )
338365
339366 def sceneMousePressEvent (self , event ):
340367 """
@@ -347,10 +374,10 @@ def sceneMousePressEvent(self, event):
347374 The event handler from the QtWidgets.QGraphicsScene
348375 """
349376 # pipe slicer enabled.
350- if event . modifiers () == ( QtCore . Qt . AltModifier | QtCore . Qt . ShiftModifier ) :
377+ if self . ALT_state and self . SHIFT_state :
351378 return
352379 # viewer pan mode.
353- if event . modifiers () == QtCore . Qt . AltModifier :
380+ if self . ALT_state :
354381 return
355382
356383 pos = event .scenePos ()
@@ -384,13 +411,18 @@ def sceneMousePressEvent(self, event):
384411 if not self .LMB_state :
385412 return
386413 pipe = pipe_items [0 ]
387- attr = {IN_PORT : 'output_port' , OUT_PORT : 'input_port' }
388414 from_port = pipe .port_from_pos (pos , True )
389415 from_port ._hovered = True
390416
417+ attr = {IN_PORT : 'output_port' , OUT_PORT : 'input_port' }
391418 self ._detached_port = getattr (pipe , attr [from_port .port_type ])
392419 self .start_live_connection (from_port )
393- self ._live_pipe .draw_path (self ._start_port , None , pos )
420+ self ._live_pipe .draw_path (self ._start_port , cursor_pos = pos )
421+
422+ if self .SHIFT_state :
423+ self ._live_pipe .shift_selected = True
424+ return
425+
394426 pipe .delete ()
395427
396428 def sceneMouseReleaseEvent (self , event ):
@@ -402,7 +434,7 @@ def sceneMouseReleaseEvent(self, event):
402434 event (QtWidgets.QGraphicsSceneMouseEvent):
403435 The event handler from the QtWidgets.QGraphicsScene
404436 """
405- if not self ._live_pipe :
437+ if not self ._live_pipe . isVisible () :
406438 return
407439
408440 self ._start_port ._hovered = False
@@ -419,7 +451,7 @@ def sceneMouseReleaseEvent(self, event):
419451
420452 # if port disconnected from existing pipe.
421453 if end_port is None :
422- if self ._detached_port :
454+ if self ._detached_port and not self . _live_pipe . shift_selected :
423455 disconnected .append ((self ._start_port , self ._detached_port ))
424456 self .connection_changed .emit (disconnected , connected )
425457
@@ -477,29 +509,25 @@ def sceneMouseReleaseEvent(self, event):
477509 def start_live_connection (self , selected_port ):
478510 """
479511 create new pipe for the connection.
480- (draws the live pipe from the port following the cursor position)
512+ (show the live pipe visibility from the port following the cursor position)
481513 """
482514 if not selected_port :
483515 return
484516 self ._start_port = selected_port
485- self ._live_pipe = Pipe ()
486- self ._live_pipe .setZValue (Z_VAL_NODE_WIDGET )
487- self ._live_pipe .activate ()
488- self ._live_pipe .style = PIPE_STYLE_DASHED
489517 if self ._start_port .type == IN_PORT :
490518 self ._live_pipe .input_port = self ._start_port
491519 elif self ._start_port == OUT_PORT :
492520 self ._live_pipe .output_port = self ._start_port
493- self .scene (). addItem ( self . _live_pipe )
521+ self ._live_pipe . setVisible ( True )
494522
495523 def end_live_connection (self ):
496524 """
497525 delete live connection pipe and reset start port.
498- (removes the pipe item used for drawing the live connection)
526+ (hides the pipe item used for drawing the live connection)
499527 """
500- if self ._live_pipe :
501- self ._live_pipe .delete ( )
502- self ._live_pipe = None
528+ self ._live_pipe . reset_path ()
529+ self ._live_pipe .setVisible ( False )
530+ self ._live_pipe . shift_selected = False
503531 self ._start_port = None
504532
505533 def establish_connection (self , start_port , end_port ):
@@ -596,8 +624,9 @@ def save_dialog(self, current_dir=None, ext=None):
596624
597625 def all_pipes (self ):
598626 pipes = []
627+ excl = [self ._live_pipe , self ._pipe_slicer ]
599628 for item in self .scene ().items ():
600- if isinstance (item , Pipe ):
629+ if isinstance (item , Pipe ) and item not in excl :
601630 pipes .append (item )
602631 return pipes
603632
0 commit comments