3 from PyQt4.QtCore import QCoreApplication, QRect, QSize, QPoint, Qt
12 """ Widget which can be connection by PortConnections to other selectable widgets.
14 Supports showing source and sink ports.
15 The widget is owner of PortWidgets.
18 BACKGROUND_SHAPE =
'ROUNDRECT'
19 SHOW_PORT_NAMES =
False
20 SHOW_PORT_LINES =
False
23 PORT_NAMES_NEXT_TO_PORTS = 0
24 PORT_NAMES_ABOVE_PORTS = 1
27 PORT_NAMES_POSITION = PORT_NAMES_NEXT_TO_PORTS
29 NO_VALID_PORT_NAMES_POSITION_MESSAGE =
"No valid position for port names was set."
31 PORT_LINES_TARGET_X = -1
32 PORT_LINES_TARGET_Y = -1
42 VispaWidget.__init__(self, parent)
51 """ If True the port name's will be drawn.
53 The port names wont be on the port itself.
54 Instead they will appear next to the port icons on the ConnectableWidget.
59 """ Sets position where port names will be shown.
61 Possible values are self.PORT_NAMES_NEXT_TO_PORTS and self.PORT_NAMES_ABOVE_PORTS.
66 """ If True lines from all ports to a specific target point are drawn.
68 The target point is defined by PORT_LINES_TARGET_X and PORT_LINES_TARGET_Y.
69 If both of these values are -1 the target point is set to the widget's centre.
74 """ Returns height of all ports of given type.
76 portType can either be 'sink" or 'source'.
78 if portType ==
"sink":
80 elif portType ==
"source":
93 """ Returns size needed to draw widget's content.
106 titleWidth = self.
getDistance(
'titleFieldWidth', 1)
112 sinkPortsWidth = self.
getDistance(
'leftMargin', 1) + PortWidget.WIDTH
114 sourcePortsWidth = self.
getDistance(
'rightMargin', 1) + PortWidget.WIDTH
120 bodyWidth += maxSinkTitleWidth + self.
getDistance(
'rightMargin', 1) + maxSourceTitleWidth
122 if maxSinkTitleWidth > PortWidget.WIDTH:
124 if maxSourceTitleWidth > PortWidget.WIDTH:
127 bodyWidth += maxSinkTitleWidth + maxSourceTitleWidth
130 bodyWidth += sinkPortsWidth + sourcePortsWidth
136 neededWidth +=
max(titleWidth, bodyWidth)
140 neededHeight += self.
getDistance(
'titleFieldHeight', 1)
146 textFieldHeight += self.
textField().getHeight()
147 neededHeight +=
max(sinkPortsHeight, sourcePortsHeight, textFieldHeight, imageSizeF.height())
149 neededHeight += self.
getDistance(
'bottomMargin', 1)
151 neededHeight += self.
getDistance(
'bottomMargin', 1)
153 return QSize(neededWidth, neededHeight)
156 """ Extends distances of VispaWidget by the additionally needed distances for displaying ports.
162 if not VispaWidget.defineDistances(self, keepDefaultRatio):
165 self.
distances()[
'textFieldX'] += PortWidget.WIDTH * scale + self.
distances()[
'leftMargin']
171 firstPortY = self.
distances()[
'height'] - self.
distances()[
'bottomMargin'] - PortWidget.HEIGHT * scale
173 self.
distances()[
'firstSinkY'] = firstPortY
179 self.
distances()[
'firstSourceY'] = firstPortY
190 """ Arranges ports when zoom has changed.
192 VispaWidget.setZoom(self, zoom)
196 """ Makes sure event is forwarded to both base classes.
198 If position of event is within the dropArea of a port a QMouseEvent is sent to the port. See dropArea().
201 if dropAreaPort
and dropAreaPort.isDragable():
202 dropAreaPort.grabMouse()
203 newEvent = QMouseEvent(event.type(), dropAreaPort.mapFromParent(event.pos()), event.button(), event.buttons(), event.modifiers())
204 QCoreApplication.instance().sendEvent(dropAreaPort, newEvent)
206 VispaWidgetOwner.mousePressEvent(self, event)
207 VispaWidget.mousePressEvent(self, event)
210 """ Calls realeseMouse() to make sure the widget does not grab the mouse.
212 Necessary because ConnectableWidgetOwner.propagateEventUnderConnectionWidget() may call grabMouse() on this widget.
216 VispaWidget.mouseReleaseEvent(self, event)
219 """ Returns list containing all source and sink port widgets.
224 """ Adds sink port with name and optional description text.
231 """ Adds source port with name and optional description text.
238 self._ports.append(port)
241 self.
_ports[len(self.
_ports) - 1].setDescription(description)
248 port.deleteAttachedConnections()
249 VispaWidget.deleteLater(self)
252 """ Removes given port if it is port of this widget.
255 port.deleteAttachedConnections()
256 self._ports.remove(port)
263 if port.name() == name
and port.description() == description:
268 """ Remove registered ports.
270 If filter is "sink" only sinks are removed, if it is "source" only sources are removed, otherwise all ports are removed.
272 if filter
and (filter !=
"sink" and filter !=
"source"):
275 parentIsWidgetOwner =
False
278 if not filter
or port.portType() == filter:
279 port.deleteAttachedConnections()
280 self._ports.remove(port)
287 """ Returns list of all sink ports set.
289 return [port
for port
in self.
_ports if port.portType() ==
"sink"]
291 return port.portType() ==
'sink'
295 """ Returns list of all source ports set.
297 return [port
for port
in self.
_ports if port.portType() ==
"source"]
299 return port.portType() ==
'source'
303 """ Returns sink port with given name or None if no such port is found.
305 return self.
port(name,
'sink')
308 """ Returns source port with given name or None if no such port is found.
310 return self.
port(name,
'source')
313 """ Returns port with given name and of given type.
318 if port.portType() == type
and port.name() == name:
325 elif type ==
'source':
332 return max([port.titleField().getWidth()
for port
in ports])
341 """ Returns the bigger value of the source height and the height of the port name text field.
343 portHeight = port.height()
347 titleHeight = port.titleField().getHeight() * self.
scale()
350 return max(portHeight, titleHeight)
352 return portHeight + titleHeight
357 """ Arranges ports after content is rearranged by VispaWidget.
359 VispaWidget.rearangeContent(self)
363 """ Centers port vertically within body part (widget without title) of ModuleWidget.
365 ports can either be the list of source or sink ports of ModuleWidget.
366 portX specifies the designated x coordinate to be adjustable for sinks and sources.
368 if len(ports) != 1
or not isinstance(ports[0], PortWidget):
369 logging.warning(self.__class__.__name__ +
": centerSinglePortVertically() - This method was designed for plugins with one port. Falling back to default arrangement.")
375 """ Sets positions of set ports depending on zoom factor.
377 If filter is set it may be 'sink' or 'source'.
379 if filter
and (filter !=
"sink" and filter !=
"source"):
390 if port.portType() ==
'sink' and (
not filter
or filter ==
"sink"):
392 port.move(sinkX, sinkY)
395 elif port.portType() ==
'source' and (
not filter
or filter ==
"source"):
397 port.move(sourceX, sourceY)
401 """ Takes care of painting widget content on given painter.
409 """ Paints port names next to PortWidget.
411 See setShowPortNames().
417 titleHeightFactor = 0.4
421 if port.titleField():
422 port.titleField().
paint(painter, port.x() + self.
getDistance(
'rightMargin') + port.width(), port.y() - titleHeightFactor * port.getDistance(
'titleFieldHeight'), self.
scale())
425 if port.titleField():
427 port.titleField().
paint(painter, port.x() - port.getDistance(
'titleFieldWidth') - self.
getDistance(
'rightMargin'), port.y() - titleHeightFactor * port.getDistance(
'titleFieldHeight'), self.
scale())
429 painter.pen().setWidth(2)
431 if port.titleField():
432 port.titleField().
paint(painter, self.
getDistance(
'firstSinkX'), port.y() - titleHeightFactor * port.getDistance(
'titleFieldHeight') - port.height(), self.
scale())
435 if port.titleField():
437 port.titleField().
paint(painter, self.
width() - port.getDistance(
'titleFieldWidth')- port.width()*0.5, port.y() - titleHeightFactor * port.getDistance(
'titleFieldHeight') - port.height(), self.
scale())
442 """ Draws lines from every port to a common point.
444 See setShowPortLines().
454 painter.setPen(QPen(QColor(
'black')))
455 painter.pen().setWidth(1)
457 for port
in self.
ports():
458 painter.drawLine(port.connectionPoint(
"widget"), targetPoint)
461 """ A drop area is a QRect in which the ConnectableWidget accepts dropping of PortWidgets to create connections.
463 The area is greater than the port itself to make dropping easier.
466 return port.frameGeometry().united(port.titleField().getDrawRect())
468 topMarginHalf = 0.5 * topMargin
469 frameGeometry = port.frameGeometry()
470 return QRect(frameGeometry.x() - topMarginHalf,
471 frameGeometry.y() - topMarginHalf,
472 frameGeometry.width() + topMargin,
473 frameGeometry.height() + topMargin)
476 """ If a port's drop area is associated with position the port is returned.
478 If there is no drop area associated with the position None is returned.
487 if bool(event.buttons() & Qt.LeftButton):
488 VispaWidget.mouseMoveEvent(self, event)
496 self._menuWidget.show()
497 self._menuWidget.raise_()
501 parentCursorPos = self.parent().mapFromGlobal(self.cursor().
pos())
502 bottomRight = self.geometry().bottomRight()
505 or (self.
_menuWidget and ( parentCursorPos.x() > bottomRight.x()
or parentCursorPos.y() > bottomRight.y())):
506 self._menuWidget.hide()
514 self.setMouseTracking(
True)
515 return self._menuWidget.addEntry(name, slot)
520 self._menuWidget.removeEntry(entry)
521 if self._menuWidget.len() == 0:
525 self._menuWidget.hide()
526 self._menuWidget.setParent(
None)
527 self._menuWidget.deleteLater()
533 if isinstance(self.parent(), VispaWidget):
534 headerOffset = self.parent().
getDistance(
"titleFieldBottom")
535 self._menuWidget.move(
max(0, self.x() - 0.5* (self._menuWidget.width() - self.
width())),
536 max(0, headerOffset, self.y() - self._menuWidget.height() +1))
539 VispaWidget.dragWidget(self, pPos)
542 def select(self, sel=True, multiSelect=False):
543 VispaWidget.select(self, sel, multiSelect)
545 self._menuWidget.hide()
548 VispaWidget.move(self, *target)
550 self._menuWidget.hide()
555 port.updateAttachedConnections()
560 connections += port.attachedConnections()
bool contains(EventRange const &lh, EventID const &rh)
const T & max(const T &a, const T &b)