Skip to content

Commit 524ec79

Browse files
committed
live pipe connection overhaul clean up.
1 parent 9ec4d0a commit 524ec79

File tree

2 files changed

+88
-45
lines changed

2 files changed

+88
-45
lines changed

NodeGraphQt/qgraphics/pipe.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
PIPE_DEFAULT_COLOR, PIPE_ACTIVE_COLOR,
77
PIPE_HIGHLIGHT_COLOR, PIPE_DISABLED_COLOR,
88
PIPE_STYLE_DASHED, PIPE_STYLE_DEFAULT, PIPE_STYLE_DOTTED,
9-
PIPE_LAYOUT_STRAIGHT, PIPE_WIDTH, IN_PORT, OUT_PORT, Z_VAL_PIPE
9+
PIPE_LAYOUT_STRAIGHT, PIPE_WIDTH, IN_PORT, OUT_PORT, Z_VAL_PIPE,
10+
Z_VAL_NODE_WIDGET
1011
)
1112
from NodeGraphQt.qgraphics.port import PortItem
1213

@@ -130,7 +131,7 @@ def paint(self, painter, option, widget):
130131

131132
painter.restore() # QPaintDevice: Cannot destroy paint device that is being painted
132133

133-
def draw_path(self, start_port, end_port, cursor_pos=None):
134+
def draw_path(self, start_port, end_port=None, cursor_pos=None):
134135
"""
135136
Draws the path between ports.
136137
@@ -182,6 +183,10 @@ def draw_path(self, start_port, end_port, cursor_pos=None):
182183
path.cubicTo(ctr_point1, ctr_point2, pos2)
183184
self.setPath(path)
184185

186+
def reset_path(self):
187+
path = QtGui.QPainterPath(QtCore.QPointF(0.0, 0.0))
188+
self.setPath(path)
189+
185190
def calc_distance(self, p1, p2):
186191
x = math.pow((p2.x() - p1.x()), 2)
187192
y = math.pow((p2.y() - p1.y()), 2)
@@ -293,3 +298,12 @@ def delete(self):
293298
# TODO: not sure if we need this...?
294299
del self
295300

301+
302+
class LivePipe(Pipe):
303+
304+
def __init__(self, input_port=None, output_port=None):
305+
super(LivePipe, self).__init__(input_port, output_port)
306+
self.setZValue(Z_VAL_NODE_WIDGET + 1)
307+
self.activate()
308+
self.style = PIPE_STYLE_DASHED
309+
self.shift_selected = False

NodeGraphQt/widgets/viewer.py

Lines changed: 72 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@
55
from NodeGraphQt import QtGui, QtCore, QtWidgets
66
from 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,)
119
from NodeGraphQt.qgraphics.node_abstract import AbstractNodeItem
1210
from NodeGraphQt.qgraphics.node_backdrop import BackdropNodeItem
13-
from NodeGraphQt.qgraphics.pipe import Pipe
11+
from NodeGraphQt.qgraphics.pipe import Pipe, LivePipe
1412
from NodeGraphQt.qgraphics.port import PortItem
1513
from NodeGraphQt.qgraphics.slicer import SlicerPipe
1614
from 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

Comments
 (0)