def is_intersection(ed1, ed2, norm): vis1 = is_visiable(ed1[0], ed2[0], ed2[1], norm) vis2 = is_visiable(ed1[1], ed2[0], ed2[1], norm) if (vis1 and not vis2) or (not vis1 and vis2): # ищем пересечение p1 = ed1[0] p2 = ed1[1] q1 = ed2[0] q2 = ed2[1] delta = (p2.x() - p1.x()) * (q1.y() - q2.y()) - (q1.x() - q2.x()) * (p2.y() - p1.y()) delta_t = (q1.x() - p1.x()) * (q1.y() - q2.y()) - (q1.x() - q2.x()) * (q1.y() - p1.y()) if abs(delta) <= 1e-6: return p2 t = delta_t / delta I = QPointF() I.setX(ed1[0].x() + (ed1[1].x() - ed1[0].x()) * t) I.setY(ed1[0].y() + (ed1[1].y() - ed1[0].y()) * t) return I else: return False
def Intersection(line1, line2, n): print("check in Inter") vis1 = isVisiable(line1[0], line2, n) vis2 = isVisiable(line1[1], line2, n) if (vis1 and not vis2) or (not vis1 and vis2): # ищем пересечение p1 = line1[0] p2 = line1[1] q1 = line2[0] q2 = line2[1] delta = (p2.x() - p1.x()) * (q1.y() - q2.y()) - (q1.x() - q2.x()) * ( p2.y() - p1.y()) delta_t = (q1.x() - p1.x()) * (q1.y() - q2.y()) - (q1.x() - q2.x()) * ( q1.y() - p1.y()) if abs(delta) <= 1e-6: return p2 t = delta_t / delta I = QPointF() I.setX(line1[0].x() + (line1[1].x() - line1[0].x()) * t) I.setY(line1[0].y() + (line1[1].y() - line1[0].y()) * t) return I else: return False
def itemChange(self, change, value): # if position of label changes if change == QGraphicsItem.ItemPositionChange and self.scene(): newPos = QPointF(value) # new position newPos += QPointF(self.boundingRect().width() / 2, self.boundingRect().height() / 2) # pos of center points = self.parentItem().points firstPoint = points[self.index] endPoint = points[self.index + 1] if firstPoint.x() == endPoint.x(): if min(firstPoint.y(), endPoint.y()) > newPos.y(): newPos.setY(min(firstPoint.y(), endPoint.y())) elif newPos.y() > max(firstPoint.y(), endPoint.y()): newPos.setY(max(firstPoint.y(), endPoint.y())) elif firstPoint.y() == endPoint.y(): if min(firstPoint.x(), endPoint.x()) > newPos.x(): newPos.setX(min(firstPoint.x(), endPoint.x())) elif newPos.x() > max(firstPoint.x(), endPoint.x()): newPos.setX(max(firstPoint.x(), endPoint.x())) newPos -= QPointF(self.boundingRect().width() / 2, self.boundingRect().height() / 2) # change center pos to top-left return newPos if change == QGraphicsItem.ItemPositionHasChanged and self.scene(): self.updateGap() self.updateLine() return return super(LineLabel, self).itemChange(change, value)
def paint_pipe(self, c_e, is_pipe, l_i_nodes, cond_pipe, union_pipe, items, l_nodes): if is_pipe: if not cond_pipe: l_p = l_nodes + self.l_reservoir l_p_2 = l_i_nodes + self.l_i_reservoir node = l_p[l_p_2.index(items[0])] c_e.setX(node.x) c_e.setY(node.y) e_f = QPointF(c_e.x() + 1, c_e.y() + 1) item = self.scene_v.addLine(c_e.x(), c_e.y(), e_f.x(), e_f.y(), QPen(QColor('black'))) union_pipe[0] = node return True, c_e, item, union_pipe else: self.scene_v.removeItem(self.current_item) l_p = l_nodes + self.l_reservoir l_p_2 = l_i_nodes + self.l_i_reservoir node = l_p[l_p_2.index(items[0])] e_f = QPointF() e_f.setX(node.x) e_f.setY(node.y) n_i = union_pipe[0] union_pipe[1] = node item = self.paint_pipe(self.start_e, e_f, n_i, node) return False, None, item, union_pipe else: return False, None, self.current_item, [None, None]
def add_point_by_btn(win): x = win.x.value() y = win.y.value() p = QPointF() p.setX(x) p.setY(y) add_point(p)
def applyLayerValue(self, id, val): layer = self.mObject layerIndex = self.mMapDocument.map().layers().indexOf(layer) command = None x = id if x == PropertyId.NameProperty: command = RenameLayer(self.mMapDocument, layerIndex, val) elif x == PropertyId.VisibleProperty: command = SetLayerVisible(self.mMapDocument, layerIndex, val) elif x == PropertyId.OpacityProperty: command = SetLayerOpacity(self.mMapDocument, layerIndex, val) elif x == PropertyId.OffsetXProperty or x == PropertyId.OffsetYProperty: offset = QPointF(layer.offset()) if id == PropertyId.OffsetXProperty: offset.setX(val) else: offset.setY(val) command = SetLayerOffset(self.mMapDocument, layerIndex, offset) else: x = layer.layerType() if x == Layer.TileLayerType: self.applyTileLayerValue(id, val) elif x == Layer.ObjectGroupType: self.applyObjectGroupValue(id, val) elif x == Layer.ImageLayerType: self.applyImageLayerValue(id, val) if (command): self.mMapDocument.undoStack().push(command)
def applyLayerValue(self, id, val): layer = self.mObject layerIndex = self.mMapDocument.map().layers().indexOf(layer) command = None x = id if x==PropertyId.NameProperty: command = RenameLayer(self.mMapDocument, layerIndex, val) elif x==PropertyId.VisibleProperty: command = SetLayerVisible(self.mMapDocument, layerIndex, val) elif x==PropertyId.OpacityProperty: command = SetLayerOpacity(self.mMapDocument, layerIndex, val) elif x==PropertyId.OffsetXProperty or x==PropertyId.OffsetYProperty: offset = QPointF(layer.offset()) if id == PropertyId.OffsetXProperty: offset.setX(val) else: offset.setY(val) command = SetLayerOffset(self.mMapDocument, layerIndex, offset) else: x = layer.layerType() if x==Layer.TileLayerType: self.applyTileLayerValue(id, val) elif x==Layer.ObjectGroupType: self.applyObjectGroupValue(id, val) elif x==Layer.ImageLayerType: self.applyImageLayerValue(id, val) if (command): self.mMapDocument.undoStack().push(command)
def rotatePointClockWise(self, p: QPointF, angle): ''' Standard 2x2 rotation matrix, counter-clockwise | cos(phi) sin(phi) | | -sin(phi) cos(phi) | ''' p.setX(cos(angle) * p.x() + sin(angle)* p.y()) p.setY((-1.0 * sin(angle) * p.x()) + cos(angle) * p.y())
def resizeEvent(self, ev=None): # Set correct position of label br = self.label.boundingRect() p = QPointF(0, 0) p.setX(int(self.size().width() / 2 - br.width() / 2)) p.setY(10) self.label.setPos(p) self.picture = None
def resizeEvent(self, ev=None): # Set correct position of label br = self.label.boundingRect() p = QPointF(0, 0) p.setX(self.textWidth + 5) p.setY(int(self.size().height() / 2 + br.width() / 2)) self.label.setPos(p) self.picture = None
def drawPolygon(self, refPolygon, painter): # Scale coordinates draw = QPolygonF(refPolygon.size()) for i in range(refPolygon.size()): curr = QPointF() curr.setX(self.refToScreenX(refPolygon.at(i).x())) curr.setY(self.refToScreenY(refPolygon.at(i).y())) draw.replace(i, curr) painter.drawPolygon(draw)
def enforce_bounds(self, pos): pos = QPointF(pos) if pos.x() < self.piano_width: pos.setX(self.piano_width) elif pos.x() >= self.grid_width + self.piano_width: pos.setX(self.grid_width + self.piano_width - 1) if pos.y() < self.header_height + self.padding: pos.setY(self.header_height + self.padding) return pos
def addpoint_btn(win): x = win.xInput.value() y = win.yInput.value() p = QPointF() p.setX(x) p.setY(y) add_point(p)
def paint(self, painter, option, widget=None): path = QPainterPath() path.addRoundedRect(self.m_rect, 5, 5) anchor = self.mapFromParent(self.m_chart.mapToPosition(self.m_anchor)) if not self.m_rect.contains(anchor): point1 = QPointF() point2 = QPointF() # establish the position of the anchor point in relation to m_rect above = anchor.y() <= self.m_rect.top() aboveCenter = (anchor.y() > self.m_rect.top() and anchor.y() <= self.m_rect.center().y()) belowCenter = (anchor.y() > self.m_rect.center().y() and anchor.y() <= self.m_rect.bottom()) below = anchor.y() > self.m_rect.bottom() onLeft = anchor.x() <= self.m_rect.left() leftOfCenter = (anchor.x() > self.m_rect.left() and anchor.x() <= self.m_rect.center().x()) rightOfCenter = (anchor.x() > self.m_rect.center().x() and anchor.x() <= self.m_rect.right()) onRight = anchor.x() > self.m_rect.right() # get the nearest m_rect corner. x = (onRight + rightOfCenter) * self.m_rect.width() y = (below + belowCenter) * self.m_rect.height() cornerCase = ((above and onLeft) or (above and onRight) or (below and onLeft) or (below and onRight)) vertical = abs(anchor.x() - x) > abs(anchor.y() - y) x1 = (x + leftOfCenter * 10 - rightOfCenter * 20 + cornerCase * int(not vertical) * (onLeft * 10 - onRight * 20)) y1 = (y + aboveCenter * 10 - belowCenter * 20 + cornerCase * int(vertical) * (above * 10 - below * 20)) point1.setX(x1) point1.setY(y1) x2 = (x + leftOfCenter * 20 - rightOfCenter * 10 + cornerCase * int(not vertical) * (onLeft * 20 - onRight * 10)) y2 = (y + aboveCenter * 20 - belowCenter * 10 + cornerCase * int(vertical) * (above * 20 - below * 10)) point2.setX(x2) point2.setY(y2) path.moveTo(point1) path.lineTo(anchor) path.lineTo(point2) path = path.simplified() painter.setBrush(QColor(255, 255, 255)) painter.drawPath(path) painter.drawText(self.m_textRect, self.m_text)
def P_t(line, t): print(line, t) p = QPointF() P1 = QPointF(line[0][0], line[0][1]) P2 = QPointF(line[1][0], line[1][1]) print(P1.x(), P1.y()) print(P2.x(), P2.y()) print("chekc 9") p.setX(P1.x() + (P2.x() - P1.x()) * t) p.setY(P1.y() + (P2.y() - P1.y()) * t) print(p.x(), p.y()) return p
def get_line_start(self, index: int) -> QPointF: pt = QPointF() pt1 = self.clickedPoints[index] pt2 = self.clickedPoints[(index + 1) % len(self.clickedPoints)] if self.get_distance(pt1, pt2) == 0: f_rat = 0.5 else: f_rat = float(self.__i_radius) / self.get_distance(pt1, pt2) if f_rat > 0.5: f_rat = 0.5 pt.setX((1.0 - f_rat) * pt1.x() + f_rat * pt2.x()) pt.setY((1.0 - f_rat) * pt1.y() + f_rat * pt2.y()) return pt
def initUi(self): pos = QPointF(0, 0) for i in range(0, self.length): #每一秒有1個刻度,總共 length 秒 t1 = QGraphicsTextItem('|', self) t1.setDefaultTextColor(QColor(Qt.blue)) t1.setPos(pos) t2 = QGraphicsTextItem('{}'.format(i + int(self.start)), self) t2.setDefaultTextColor(QColor(Qt.blue)) t2.setPos(t1.boundingRect().bottomLeft() + pos) pos.setX(pos.x() + int(self.w / self.length)) self.setRect(0, 0, self.w, self.h)
def normal(a: QPointF, b: QPointF, pos: QPointF): fvec = QPointF(b.x() - a.x(), b.y() - a.y()) posvec = QPointF(pos.x() - b.x(), pos.y() - b.y()) if fvec.y(): fpoint = -fvec.x() / fvec.y() normvec = QPointF(1, fpoint) else: normvec = QPointF(0, 1) if scalar_mult(posvec, normvec) < 0: normvec.setX(-normvec.x()) normvec.setY(-normvec.y()) return normvec
def mouseMovedWhileCreatingObject(self, pos, modifiers): renderer = self.mapDocument().renderer() pixelCoords = renderer.screenToPixelCoords_(pos) # Update the size of the new map object objectPos = self.mNewMapObjectItem.mapObject().position() newSize = QPointF(max(0.0, pixelCoords.x() - objectPos.x()), max(0.0, pixelCoords.y() - objectPos.y())) # Holding shift creates circle or square if (modifiers & Qt.ShiftModifier): m = max(newSize.x(), newSize.y()) newSize.setX(m) newSize.setY(m) SnapHelper(renderer, modifiers).snap(newSize) self.mNewMapObjectItem.resizeObject(QSizeF(newSize.x(), newSize.y()))
def paint(self, painter, option, widget): path = QPainterPath() x, y, w, d = self.m_rect.getRect() self.m_rect = QRectF(x, y, w, d) path.addRoundedRect(self.m_rect, 5, 5) anchor = self.mapFromParent(self.chart.mapToPosition(self.m_anchor)) if not self.m_rect.contains(QPoint(int(anchor.x()), int(anchor.y()))): point1 = QPointF() point2 = QPointF() above = anchor.y() <= self.m_rect.top() aboveCenter = anchor.y() > self.m_rect.top() and anchor.y( ) <= self.m_rect.center().y() belowCenter = anchor.y() > self.m_rect.center().y() and anchor.y( ) <= self.m_rect.bottom() below = anchor.y() > self.m_rect.bottom() onLeft = anchor.x() <= self.m_rect.left() leftOfCenter = anchor.x() > self.m_rect.left() and anchor.x( ) <= self.m_rect.center().x() rightOfCenter = anchor.x() > self.m_rect.center().x() and anchor.x( ) <= self.m_rect.right() onRight = anchor.x() > self.m_rect.right() #get the nearest m_rect corner x = (onRight + rightOfCenter) * self.m_rect.width() y = (below + belowCenter) * self.m_rect.height() cornerCase = (above and onLeft) or (above and onRight) or ( below and onLeft) or (below and onRight) vertical = abs(anchor.x() - x) > abs(anchor.y() - y) x1 = x + leftOfCenter * 10 - rightOfCenter * 20 + cornerCase * ( not vertical) * (onLeft * 10 - onRight * 20) y1 = y + aboveCenter * 10 - belowCenter * 20 + cornerCase * vertical * ( above * 10 - below * 20) point1.setX(x1) point1.setY(y1) x2 = x + leftOfCenter * 20 - rightOfCenter * 10 + cornerCase * ( not vertical) * (onLeft * 20 - onRight * 10) y2 = y + aboveCenter * 20 - belowCenter * 10 + cornerCase * vertical * ( above * 20 - below * 10) point2.setX(x2) point2.setY(y2) path.moveTo(point1) path.lineTo(anchor) path.lineTo(point2) path = path.simplified() painter.setBrush(QColor(255, 255, 255)) painter.drawPath(path) painter.drawText(self.m_textRect, Qt.AlignLeft, self.m_text)
def keyPressed(self, event): if (self.mAction != Action.NoAction): event.ignore() return moveBy = QPointF() x = event.key() if x == Qt.Key_Up: moveBy = QPointF(0, -1) elif x == Qt.Key_Down: moveBy = QPointF(0, 1) elif x == Qt.Key_Left: moveBy = QPointF(-1, 0) elif x == Qt.Key_Right: moveBy = QPointF(1, 0) else: super().keyPressed(event) return items = self.mapScene().selectedObjectItems() modifiers = event.modifiers() if (moveBy.isNull() or items.isEmpty() or (modifiers & Qt.ControlModifier)): event.ignore() return moveFast = modifiers & Qt.ShiftModifier snapToFineGrid = preferences.Preferences.instance().snapToFineGrid() if (moveFast): # TODO: This only makes sense for orthogonal maps moveBy.setX(moveBy.x() * self.mapDocument().map().tileWidth()) moveBy.setX(moveBy.y() * self.mapDocument().map().tileHeight()) if (snapToFineGrid): moveBy /= preferences.Preferences.instance().gridFine() undoStack = self.mapDocument().undoStack() undoStack.beginMacro(self.tr("Move %n Object(s)", "", items.size())) i = 0 for objectItem in items: object = objectItem.mapObject() oldPos = object.position() newPos = oldPos + moveBy undoStack.push( MoveMapObject(self.mapDocument(), object, newPos, oldPos)) i += 1 undoStack.endMacro()
def keyPressed(self, event): if (self.mAction != Action.NoAction): event.ignore() return moveBy = QPointF() x = event.key() if x==Qt.Key_Up: moveBy = QPointF(0, -1) elif x==Qt.Key_Down: moveBy = QPointF(0, 1) elif x==Qt.Key_Left: moveBy = QPointF(-1, 0) elif x==Qt.Key_Right: moveBy = QPointF(1, 0) else: super().keyPressed(event) return items = self.mapScene().selectedObjectItems() modifiers = event.modifiers() if (moveBy.isNull() or items.isEmpty() or (modifiers & Qt.ControlModifier)): event.ignore() return moveFast = modifiers & Qt.ShiftModifier snapToFineGrid = preferences.Preferences.instance().snapToFineGrid() if (moveFast): # TODO: This only makes sense for orthogonal maps moveBy.setX(moveBy.x() * self.mapDocument().map().tileWidth()) moveBy.setX(moveBy.y() * self.mapDocument().map().tileHeight()) if (snapToFineGrid): moveBy /= preferences.Preferences.instance().gridFine() undoStack = self.mapDocument().undoStack() undoStack.beginMacro(self.tr("Move %n Object(s)", "", items.size())) i = 0 for objectItem in items: object = objectItem.mapObject() oldPos = object.position() newPos = oldPos + moveBy undoStack.push(MoveMapObject(self.mapDocument(), object, newPos, oldPos)) i += 1 undoStack.endMacro()
def selectWindow(self, p, w, h): if w > 0 and h > 0: pEnd = QPointF() self.imageWindow.setX(p.x() - w / 2) if self.imageWindow.x() < 0: self.imageWindow.setX(0) self.imageWindow.setY(p.y() - h / 2) if self.imageWindow.y() < 0: self.imageWindow.setY(0) pEnd.setX(p.x() + w / 2) if pEnd.x() >= 320: pEnd.setX(319) pEnd.setY(p.y() + h / 2) if pEnd.y() >= 240: pEnd.setY(239) self.imageWindow.setWidth(pEnd.x() - self.imageWindow.x()) self.imageWindow.setHeight(pEnd.y() - self.imageWindow.y()) self.winSelected = True
def AddDrawPolygonOnMap(poly_coordinates): ''' Add Polygon Layer ''' polyLyr = qgsu.selectLayerByName(Polygon_lyr) if polyLyr is None: return polyLyr.startEditing() feature = QgsFeature() point = QPointF() # create float polygon --> construcet out of 'point' list_polygon = QPolygonF() for x in range(0, len(poly_coordinates)): if x % 2 == 0: point.setX(poly_coordinates[x]) point.setY(poly_coordinates[x + 1]) list_polygon.append(point) point.setX(poly_coordinates[0]) point.setY(poly_coordinates[1]) list_polygon.append(point) geomP = QgsGeometry.fromQPolygonF(list_polygon) feature.setGeometry(geomP) # Calculate Area WSG84 (Meters) area_wsg84 = QgsDistanceArea() area_wsg84.setSourceCrs( QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:4326'), QgsProject.instance().transformContext()) if (area_wsg84.sourceCrs().isGeographic()): area_wsg84.setEllipsoid(area_wsg84.sourceCrs().ellipsoidAcronym()) # Calculate Centroid centroid = feature.geometry().centroid().asPoint() feature.setAttributes([ centroid.x(), centroid.y(), 0.0, area_wsg84.measurePolygon(geomP.asPolygon()[0]) ]) polyLyr.addFeatures([feature]) CommonLayer(polyLyr) return
def interactiveResize(self, mousePos): """Выполните форму интерактивного изменения размера.""" offset = self.handleSize + self.handleSpace boundingRect = self.boundingRect() rect = self.rect() diff = QPointF(0, 0) self.prepareGeometryChange() if self.handleSelected == self.handleTopMiddle_c: fromY = self.mousePressRect.top() toY = fromY + mousePos.y() - self.mousePressPos.y() diff.setY(toY - fromY) boundingRect.setTop(toY) rect.setTop(boundingRect.top() + offset) self.setRect(rect) elif self.handleSelected == self.handleMiddleLeft_c: fromX = self.mousePressRect.left() toX = fromX + mousePos.x() - self.mousePressPos.x() diff.setX(toX - fromX) boundingRect.setLeft(toX) rect.setLeft(boundingRect.left() + offset) self.setRect(rect) elif self.handleSelected == self.handleMiddleRight_c: print("MR") fromX = self.mousePressRect.right() toX = fromX + mousePos.x() - self.mousePressPos.x() diff.setX(toX - fromX) boundingRect.setRight(toX) rect.setRight(boundingRect.right() - offset) self.setRect(rect) elif self.handleSelected == self.handleBottomMiddle_c: fromY = self.mousePressRect.bottom() toY = fromY + mousePos.y() - self.mousePressPos.y() diff.setY(toY - fromY) boundingRect.setBottom(toY) rect.setBottom(boundingRect.bottom() - offset) self.setRect(rect) self.updateHandlesPos()
def interactiveResize(self, mousePos, sync=True): """ Perform shape interactive resize. """ diff = QPointF(0, 0) center = self.center() self.prepareGeometryChange() if self.handleSelected: fromX = self.points[self.handleSelected].x() fromY = self.points[self.handleSelected].y() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() else: toX = mousePos.x() toY = mousePos.y() fromX = self.mousePressPos.x() fromY = self.mousePressPos.y() diff.setX(-fromX + toX) diff.setY(-fromY + toY) if not self.right: # left click if self.handleSelected == self.handleTopLeft: self.moveTopLeft(diff) elif self.handleSelected == self.handleTopRight: self.moveTopRight(diff) elif self.handleSelected == self.handleBottomLeft: self.moveBottomLeft(diff) elif self.handleSelected == self.handleBottomRight: self.moveBottomRight(diff) elif self.handleSelected is None and sync: # drag self.move(diff) self.setPolygon(list(self.points.values())) #update points self.mousePressPos = mousePos else: # right click self.rotate(-self._angle) # rotate mpx, mpy = self.mousePressPos.x() - center.x( ), self.mousePressPos.y() - center.y() mx, my = mousePos.x() - center.x(), mousePos.y() - center.y() self._angle = angle_vec(np.array([mpx, mpy]), np.array([mx, my])) self.rotate(self._angle) # rotate self.updateHandlesPos() if sync and self.canvas and self.component_name: # only update others when we now the component name self.canvas.sync_measures(self)
def itemChange(self, change, value): """ Moves position of grip item on resize """ if change == QGraphicsItem.ItemPositionChange and self.isEnabled(): p = QPointF(self.pos()) if self.direction == Qt.Horizontal: p.setX(value.x()) elif self.direction == Qt.Vertical: p.setY(value.y()) # Find change in positions movement = p - self.pos() # Set transform to oppose change in transformation due to parent transform = QTransform() transform.translate(-movement.x() / 2, -movement.y() / 2) self.setTransform(transform, True) self.parentItem().resize(self.m_index, movement) return p return super(SizeGripItem, self).itemChange(change, value)
def mouseMoveEvent(self, event): mouse_pos = event.pos() if not self.is_mouse_pressed: self.cursor_position = CursorPosition(self.cropping_rect, mouse_pos) self.update_cursor_icon(mouse_pos) elif self.cursor_position != CursorPosition.cursor_position_undefined: mouse_delta = QPointF() mouse_delta.setX(mouse_pos.x() - self.start_mouse_pos.x()) mouse_delta.setY(mouse_pos.y() - self.start_mouse_pos.y()) if self.cursor_position != CursorPosition.cursor_position_middle: new_geometry = self._calculate_geometry( self.last_static_cropping_rect, self.cursor_position, mouse_delta ) if not new_geometry.isNull(): self.cropping_rect = new_geometry else: self.cropping_rect.moveTo(self.last_static_cropping_rect.topLeft() + mouse_delta)
def render_snapshot(self, snapshot: Snapshot): self.nodz.clearGraph() nodes = snapshot.data center_position = self.nodz.mapToScene(self.nodz.viewport().rect().center()) highlights = snapshot.highlights for index, node in enumerate(nodes): #TODO calculate pos node_pos = QPointF(center_position) #node_pos.setX(((center_position.x() * 2) / len(nodes)) * (index + 1) - 200) node_pos.setX(center_position.x() + (index + 1) * 185 - 1000) node_pos.setY(center_position.y() + (50+ (index + 1) * 10) * (-1)**(index+1)) if highlights[index] == Snapshot.color_idle: attr_preset = 'attr_color_idle' node_preset = 'node_color_idle' elif highlights[index] == Snapshot.color_selected: attr_preset = 'attr_color_selected' node_preset = 'node_color_selected' elif highlights[index] == Snapshot.color_current: attr_preset = 'attr_color_current' node_preset = 'node_color_current' elif highlights[index] == Snapshot.color_current_final: attr_preset = 'attr_color_current_final' node_preset = 'node_color_current_final' nodz_node = self.create_node(node.name, preset=node_preset, position=node_pos) for attr in node.attributes: nodz_attr = self.create_attribute(nodz_node, attr.name, attr_preset) self.nodz_nodes.append(nodz_node) for node in nodes: for attr in node.attributes: if (not attr.connection is None) and (attr.node_name != attr.connection.node_name): self.nodz.createConnection( attr.node_name, attr.name, attr.connection.node_name, attr.connection.name ) self.nodz.evaluateGraph() pass
def itemChange(self, change, value): """ move position of grabber after resize""" if change == QGraphicsItem.ItemPositionChange and self.isEnabled(): p = QPointF(self.pos()) if self.direction == Qt.Horizontal: p.setX(value.x()) if self.parentItem().refLine and self.m_index == len(self.parentItem().points) - 2: points = self.parentItem().refLine.points point1 = points[self.parentItem().refIndex] point2 = points[self.parentItem().refIndex + 1] point1 = self.parentItem().mapFromItem(self.parentItem().refLine, point1) point2 = self.parentItem().mapFromItem(self.parentItem().refLine, point2) if p.x() < min(point1.x(), point2.x()): p.setX(min(point1.x(), point2.x())) elif p.x() > max(point1.x(), point2.x()): p.setX(max(point1.x(), point2.x())) elif self.direction == Qt.Vertical: p.setY(value.y()) if self.parentItem().refLine and self.m_index == len(self.parentItem().points) - 2: points = self.parentItem().refLine.points point1 = points[self.parentItem().refIndex] point2 = points[self.parentItem().refIndex + 1] point1 = self.parentItem().mapFromItem(self.parentItem().refLine, point1) point2 = self.parentItem().mapFromItem(self.parentItem().refLine, point2) if p.y() < min(point1.y(), point2.y()): p.setY(min(point1.y(), point2.y())) elif p.y() > max(point1.y(), point2.y()): p.setY(max(point1.y(), point2.y())) movement = p - self.pos() self.m_annotation_item.movePoints(self.m_index, movement) return p return super(Grabber, self).itemChange(change, value)
def poly_handler(self, event): if event.type() == QEvent.GraphicsSceneMousePress: if type(self.active_com.gfx) is not PangoPolyGraphic: self.active_com = CreateShape(PangoPolyGraphic, event.scenePos(), self.active_label) self.stack.push(self.active_com) gfx = self.active_com.gfx pos = event.scenePos() if gfx.poly.count() <= 1 or not gfx.poly.isClosed(): if QLineF(event.scenePos(), gfx.poly.first()).length() < gfx.dw() * 2: pos = QPointF() pos.setX(gfx.poly.first().x()) pos.setY(gfx.poly.first().y()) self.active_com = ExtendShape(pos, self.active_com.gfx) self.stack.push(self.active_com) if gfx.poly.count() > 1 and gfx.poly.isClosed(): self.reset_com()
def intersection(self, point1, point2, edge_point1, edge_point2): p1 = point1 p2 = point2 q1 = edge_point1 q2 = edge_point2 delta = (p2.x() - p1.x()) * (q1.y() - q2.y()) - (q1.x() - q2.x()) * ( p2.y() - p1.y()) delta_t = (q1.x() - p1.x()) * (q1.y() - q2.y()) - (q1.x() - q2.x()) * ( q1.y() - p1.y()) if abs(delta) <= 1e-6: return p2 t = delta_t / delta I = QPointF() I.setX(point1.x() + (point2.x() - point1.x()) * t) I.setY(point1.y() + (point2.y() - point1.y()) * t) return I
def interactiveMove(self, mousePos): diff = QPointF(0, 0) rect = self.rect() boundingRect = self.boundingRect() from_x = self.mousePressPos.x() from_y = self.mousePressPos.y() to_x = mousePos.x() to_y = mousePos.y() diff.setX(to_x - from_x) diff.setY(to_y - from_y) self.moveBy(diff.x(), diff.y()) # Emit bounding rect here! if self.scene() is not None: self.scene().rect_changed.emit(self.rect(), self.pos()) self.update() self.updateHandlesPos()
def offsetObjects(self, offset, bounds, wrapX, wrapY): for object in self.mObjects: objectCenter = object.bounds().center() if (not bounds.contains(objectCenter)): continue newCenter = QPointF(objectCenter + offset) if (wrapX and bounds.width() > 0): nx = math.fmod(newCenter.x() - bounds.left(), bounds.width()) if nx < 0: x = bounds.width() + nx else: x = nx newCenter.setX(bounds.left() + x) if (wrapY and bounds.height() > 0): ny = math.fmod(newCenter.y() - bounds.top(), bounds.height()) if ny < 0: x = bounds.height() + ny else: x = ny newCenter.setY(bounds.top() + x) object.setPosition(object.position() + (newCenter - objectCenter))
def activateNeighbor(self, active_prexoveritem: 'PreXoverItem', shortcut: str = None): """Draws a quad line starting from the item5p to the item3p. To be called with whatever the active_prexoveritem is for the parts `active_base`. Args: active_prexoveritem: Description shortcut: Default is None """ if self._getActiveTool().methodPrefix() != "selectTool": return if self.is3p and not active_prexoveritem.is3p: item5p = active_prexoveritem item3p = self elif not self.is3p and active_prexoveritem.is3p: item5p = self item3p = active_prexoveritem else: return same_parity = self.is_fwd == active_prexoveritem.is_fwd p1 = item5p._tick_marks.scenePos() + item5p.exit_pos p2 = item3p._tick_marks.scenePos() + item3p.exit_pos c1 = QPointF() # case 1: same parity if same_parity: dy = abs(p2.y() - p1.y()) c1.setX(p1.x() + _X_SCALE * dy) c1.setY(0.5 * (p1.y() + p2.y())) # case 2: different parity else: if item3p.is_fwd: c1.setX(p1.x() - _X_SCALE * abs(p2.y() - p1.y())) else: c1.setX(p1.x() + _X_SCALE * abs(p2.y() - p1.y())) c1.setY(0.5 * (p1.y() + p2.y())) pp = QPainterPath() pp.moveTo(self._tick_marks.mapFromScene(p1)) pp.quadTo(self._tick_marks.mapFromScene(c1), self._tick_marks.mapFromScene(p2)) # pp.cubicTo(c1, c2, self._tick_marks.mapFromScene(p2)) self._bond_item.setPath(pp) self._bond_item.show()
class MapObject(Object): ## # Enumerates the different object shapes. Rectangle is the default shape. # When a polygon is set, the shape determines whether it should be # interpreted as a filled polygon or a line. ## Rectangle, Polygon, Polyline, Ellipse = range(4) def __init__(self, *args): super().__init__(Object.MapObjectType) self.mPolygon = QPolygonF() self.mName = QString() self.mPos = QPointF() self.mCell = Cell() self.mType = QString() self.mId = 0 self.mShape = MapObject.Rectangle self.mObjectGroup = None self.mRotation = 0.0 self.mVisible = True l = len(args) if l==0: self.mSize = QSizeF(0, 0) elif l==4: name, _type, pos, size = args self.mName = name self.mType = _type self.mPos = pos self.mSize = QSizeF(size) ## # Returns the id of this object. Each object gets an id assigned that is # unique for the map the object is on. ## def id(self): return self.mId ## # Sets the id of this object. ## def setId(self, id): self.mId = id ## # Returns the name of this object. The name is usually just used for # identification of the object in the editor. ## def name(self): return self.mName ## # Sets the name of this object. ## def setName(self, name): self.mName = name ## # Returns the type of this object. The type usually says something about # how the object is meant to be interpreted by the engine. ## def type(self): return self.mType ## # Sets the type of this object. ## def setType(self, type): self.mType = type ## # Returns the position of this object. ## def position(self): return QPointF(self.mPos) ## # Sets the position of this object. ## def setPosition(self, pos): self.mPos = pos ## # Returns the x position of this object. ## def x(self): return self.mPos.x() ## # Sets the x position of this object. ## def setX(self, x): self.mPos.setX(x) ## # Returns the y position of this object. ## def y(self): return self.mPos.y() ## # Sets the x position of this object. ## def setY(self, y): self.mPos.setY(y) ## # Returns the size of this object. ## def size(self): return self.mSize ## # Sets the size of this object. ## def setSize(self, *args): l = len(args) if l==1: size = args[0] self.mSize = QSizeF(size) elif l==2: width, height = args self.setSize(QSizeF(width, height)) ## # Returns the width of this object. ## def width(self): return self.mSize.width() ## # Sets the width of this object. ## def setWidth(self, width): self.mSize.setWidth(width) ## # Returns the height of this object. ## def height(self): return self.mSize.height() ## # Sets the height of this object. ## def setHeight(self, height): self.mSize.setHeight(height) ## # Sets the polygon associated with this object. The polygon is only used # when the object shape is set to either Polygon or Polyline. # # \sa setShape() ## def setPolygon(self, polygon): self.mPolygon = polygon ## # Returns the polygon associated with this object. Returns an empty # polygon when no polygon is associated with this object. ## def polygon(self): return QPolygonF(self.mPolygon) ## # Sets the shape of the object. ## def setShape(self, shape): self.mShape = shape ## # Returns the shape of the object. ## def shape(self): return self.mShape ## # Shortcut to getting a QRectF from position() and size(). ## def bounds(self): return QRectF(self.mPos, self.mSize) ## # Shortcut to getting a QRectF from position() and size() that uses cell tile if present. ## def boundsUseTile(self): if (self.mCell.isEmpty()): # No tile so just use regular bounds return self.bounds() # Using the tile for determing boundary # Note the position given is the bottom-left corner so correct for that return QRectF(QPointF(self.mPos.x(), self.mPos.y() - self.mCell.tile.height()), self.mCell.tile.size()) ## # Sets the tile that is associated with this object. The object will # display as the tile image. # # \warning The object shape is ignored for tile objects! ## def setCell(self, cell): self.mCell = cell ## # Returns the tile associated with this object. ## def cell(self): return self.mCell ## # Returns the object group this object belongs to. ## def objectGroup(self): return self.mObjectGroup ## # Sets the object group this object belongs to. Should only be called # from the ObjectGroup class. ## def setObjectGroup(self, objectGroup): self.mObjectGroup = objectGroup ## # Returns the rotation of the object in degrees. ## def rotation(self): return self.mRotation ## # Sets the rotation of the object in degrees. ## def setRotation(self, rotation): self.mRotation = rotation ## # This is somewhat of a workaround for dealing with the ways different objects # align. # # Traditional rectangle objects have top-left alignment. # Tile objects have bottom-left alignment on orthogonal maps, but # bottom-center alignment on isometric maps. # # Eventually, the object alignment should probably be configurable. For # backwards compatibility, it will need to be configurable on a per-object # level. ## def alignment(self): if (self.mCell.isEmpty()): return Alignment.TopLeft elif (self.mObjectGroup): map = self.mObjectGroup.map() if map: if (map.orientation() == Map.Orientation.Isometric): return Alignment.Bottom return Alignment.BottomLeft def isVisible(self): return self.mVisible def setVisible(self, visible): self.mVisible = visible ## # Flip this object in the given \a direction. This doesn't change the size # of the object. ## def flip(self, direction): if (not self.mCell.isEmpty()): if (direction == FlipDirection.FlipHorizontally): self.mCell.flippedHorizontally = not self.mCell.flippedHorizontally elif (direction == FlipDirection.FlipVertically): self.mCell.flippedVertically = not self.mCell.flippedVertically if (not self.mPolygon.isEmpty()): center2 = self.mPolygon.boundingRect().center() * 2 if (direction == FlipDirection.FlipHorizontally): for i in range(self.mPolygon.size()): # oh, QPointF mPolygon returned is a copy of internal object self.mPolygon[i] = QPointF(center2.x() - self.mPolygon[i].x(), self.mPolygon[i].y()) elif (direction == FlipDirection.FlipVertically): for i in range(self.mPolygon.size()): self.mPolygon[i] = QPointF(self.mPolygon[i].x(), center2.y() - self.mPolygon[i].y()) ## # Returns a duplicate of this object. The caller is responsible for the # ownership of this newly created object. ## def clone(self): o = MapObject(self.mName, self.mType, self.mPos, self.mSize) o.setProperties(self.properties()) o.setPolygon(self.mPolygon) o.setShape(self.mShape) o.setCell(self.mCell) o.setRotation(self.mRotation) return o
class TeleopWidget(QWidget): stopSIG=pyqtSignal() def __init__(self,winParent): super(TeleopWidget, self).__init__() self.winParent=winParent self.line = QPointF(0, 0); self.qimage=QtGui.QImage() self.qimage.load(':images/ball.png') self.stopSIG.connect(self.stop) self.initUI() def initUI(self): layout=QGridLayout() self.setLayout(layout) self.setAutoFillBackground(True) p = self.palette() p.setColor(self.backgroundRole(), Qt.black) self.setPalette(p) self.resize(300,300) self.setMinimumSize(300,300) def stop(self): self.line = QPointF(0, 0); self.repaint(); def mouseMoveEvent(self,e): if e.buttons() == Qt.LeftButton: x = e.x()-self.width()/2 y = e.y()-self.height()/2 self.line = QPointF(x, y) self.repaint() def returnToOrigin(self): x = 0 y = 0 self.line = QPointF(x, y) self.repaint() def paintEvent(self, e): _width = self.width() _height = self.height() width = 2 painter=QtGui.QPainter(self) pen = QtGui.QPen(Qt.blue, width) painter.setPen(pen) #Centro del widget painter.translate(QPoint(_width/2, _height/2)) #eje painter.drawLine(QPointF(-_width, 0), QPointF( _width, 0)) painter.drawLine(QPointF(0, -_height), QPointF(0, _height)) #con el raton pen = QtGui.QPen(Qt.red, width) painter.setPen(pen) #Comprobamos que el raton este dentro de los limites if abs(self.line.x()*2) >= self.size().width(): if self.line.x()>=0: self.line.setX(self.size().width()/2) elif self.line.x()<0: self.line.setX((-self.size().width()/2)+1) if abs(self.line.y()*2) >= self.size().height(): if self.line.y()>=0: self.line.setY(self.size().height()/2) elif self.line.y()<0: self.line.setY((-self.size().height()/2)+1) painter.drawLine(QPointF(self.line.x(), -_height), QPointF(self.line.x(), _height)) painter.drawLine(QPointF(-_width, self.line.y()), QPointF( _width, self.line.y())) #print "x: %f y: %f" % (self.line.x(), self.line.y()) v_normalized = (1.0/(self.size().height()/2)) * self.line.y() v_normalized = float("{0:.2f}".format(v_normalized)) w_normalized = (1.0/(self.size().width()/2)) * self.line.x() w_normalized = float("{0:.2f}".format(w_normalized)) #print "v: %f w: %f" % (v_normalized,w_normalized) self.winParent.setXYValues(w_normalized,v_normalized) painter.drawImage(self.line.x()-self.qimage.width()/2, self.line.y()-self.qimage.height()/2, self.qimage);
class EditPolygonTool(AbstractObjectTool): NoMode, Selecting, Moving = range(3) def __init__(self, parent = None): super().__init__(self.tr("Edit Polygons"), QIcon(":images/24x24/tool-edit-polygons.png"), QKeySequence(self.tr("E")), parent) self.mSelectedHandles = QSet() self.mModifiers = Qt.KeyboardModifiers() self.mScreenStart = QPoint() self.mOldHandlePositions = QVector() self.mAlignPosition = QPointF() ## The list of handles associated with each selected map object self.mHandles = QMapList() self.mOldPolygons = QMap() self.mStart = QPointF() self.mSelectionRectangle = SelectionRectangle() self.mMousePressed = False self.mClickedHandle = None self.mClickedObjectItem = None self.mMode = EditPolygonTool.NoMode def __del__(self): del self.mSelectionRectangle def tr(self, sourceText, disambiguation = '', n = -1): return QCoreApplication.translate('EditPolygonTool', sourceText, disambiguation, n) def activate(self, scene): super().activate(scene) self.updateHandles() # TODO: Could be more optimal by separating the updating of handles from # the creation and removal of handles depending on changes in the # selection, and by only updating the handles of the objects that changed. self.mapDocument().objectsChanged.connect(self.updateHandles) scene.selectedObjectItemsChanged.connect(self.updateHandles) self.mapDocument().objectsRemoved.connect(self.objectsRemoved) def deactivate(self, scene): try: self.mapDocument().objectsChanged.disconnect(self.updateHandles) scene.selectedObjectItemsChanged.disconnect(self.updateHandles) except: pass # Delete all handles self.mHandles.clear() self.mSelectedHandles.clear() self.mClickedHandle = None super().deactivate(scene) def mouseEntered(self): pass def mouseMoved(self, pos, modifiers): super().mouseMoved(pos, modifiers) if (self.mMode == EditPolygonTool.NoMode and self.mMousePressed): screenPos = QCursor.pos() dragDistance = (self.mScreenStart - screenPos).manhattanLength() if (dragDistance >= QApplication.startDragDistance()): if (self.mClickedHandle): self.startMoving() else: self.startSelecting() x = self.mMode if x==EditPolygonTool.Selecting: self.mSelectionRectangle.setRectangle(QRectF(self.mStart, pos).normalized()) elif x==EditPolygonTool.Moving: self.updateMovingItems(pos, modifiers) elif x==EditPolygonTool.NoMode: pass def mousePressed(self, event): if (self.mMode != EditPolygonTool.NoMode): # Ignore additional presses during select/move return x = event.button() if x==Qt.LeftButton: self.mMousePressed = True self.mStart = event.scenePos() self.mScreenStart = event.screenPos() items = self.mapScene().items(self.mStart, Qt.IntersectsItemShape, Qt.DescendingOrder, viewTransform(event)) self.mClickedObjectItem = first(items, MapObjectItem) self.mClickedHandle = first(items, PointHandle) elif x==Qt.RightButton: items = self.mapScene().items(event.scenePos(), Qt.IntersectsItemShape, Qt.DescendingOrder, viewTransform(event)) clickedHandle = first(items) if (clickedHandle or not self.mSelectedHandles.isEmpty()): self.showHandleContextMenu(clickedHandle, event.screenPos()) else: super().mousePressed(event) else: super().mousePressed(event) def mouseReleased(self, event): if (event.button() != Qt.LeftButton): return x = self.mMode if x==EditPolygonTool.NoMode: if (self.mClickedHandle): selection = self.mSelectedHandles modifiers = event.modifiers() if (modifiers & (Qt.ShiftModifier | Qt.ControlModifier)): if (selection.contains(self.mClickedHandle)): selection.remove(self.mClickedHandle) else: selection.insert(self.mClickedHandle) else: selection.clear() selection.insert(self.mClickedHandle) self.setSelectedHandles(selection) elif (self.mClickedObjectItem): selection = self.mapScene().selectedObjectItems() modifiers = event.modifiers() if (modifiers & (Qt.ShiftModifier | Qt.ControlModifier)): if (selection.contains(self.mClickedObjectItem)): selection.remove(self.mClickedObjectItem) else: selection.insert(self.mClickedObjectItem) else: selection.clear() selection.insert(self.mClickedObjectItem) self.mapScene().setSelectedObjectItems(selection) self.updateHandles() elif (not self.mSelectedHandles.isEmpty()): # First clear the handle selection self.setSelectedHandles(QSet()) else: # If there is no handle selection, clear the object selection self.mapScene().setSelectedObjectItems(QSet()) self.updateHandles() elif x==EditPolygonTool.Selecting: self.updateSelection(event) self.mapScene().removeItem(self.mSelectionRectangle) self.mMode = EditPolygonTool.NoMode elif x==EditPolygonTool.Moving: self.finishMoving(event.scenePos()) self.mMousePressed = False self.mClickedHandle = None def modifiersChanged(self, modifiers): self.mModifiers = modifiers def languageChanged(self): self.setName(self.tr("Edit Polygons")) self.setShortcut(QKeySequence(self.tr("E"))) def updateHandles(self): selection = self.mapScene().selectedObjectItems() # First destroy the handles for objects that are no longer selected for l in range(len(self.mHandles)): i = self.mHandles.itemByIndex(l) if (not selection.contains(i[0])): for handle in i[1]: if (handle.isSelected()): self.mSelectedHandles.remove(handle) del handle del self.mHandles[l] renderer = self.mapDocument().renderer() for item in selection: object = item.mapObject() if (not object.cell().isEmpty()): continue polygon = object.polygon() polygon.translate(object.position()) pointHandles = self.mHandles.get(item) # Create missing handles while (pointHandles.size() < polygon.size()): handle = PointHandle(item, pointHandles.size()) pointHandles.append(handle) self.mapScene().addItem(handle) # Remove superfluous handles while (pointHandles.size() > polygon.size()): handle = pointHandles.takeLast() if (handle.isSelected()): self.mSelectedHandles.remove(handle) del handle # Update the position of all handles for i in range(pointHandles.size()): point = polygon.at(i) handlePos = renderer.pixelToScreenCoords_(point) internalHandlePos = handlePos - item.pos() pointHandles.at(i).setPos(item.mapToScene(internalHandlePos)) self.mHandles.insert(item, pointHandles) def objectsRemoved(self, objects): if (self.mMode == EditPolygonTool.Moving): # Make sure we're not going to try to still change these objects when # finishing the move operation. # TODO: In addition to avoiding crashes, it would also be good to # disallow other actions while moving. for object in objects: self.mOldPolygons.remove(object) def deleteNodes(self): if (self.mSelectedHandles.isEmpty()): return p = groupIndexesByObject(self.mSelectedHandles) undoStack = self.mapDocument().undoStack() delText = self.tr("Delete %n Node(s)", "", self.mSelectedHandles.size()) undoStack.beginMacro(delText) for i in p: object = i[0] indexRanges = i[1] oldPolygon = object.polygon() newPolygon = oldPolygon # Remove points, back to front to keep the indexes valid it = indexRanges.end() begin = indexRanges.begin() # assert: end != begin, since there is at least one entry while(it != begin): it -= 1 newPolygon.remove(it.first(), it.length()) if (newPolygon.size() < 2): # We've removed the entire object undoStack.push(RemoveMapObject(self.mapDocument(), object)) else: undoStack.push(ChangePolygon(self.mapDocument(), object, newPolygon, oldPolygon)) undoStack.endMacro() def joinNodes(self): if (self.mSelectedHandles.size() < 2): return p = groupIndexesByObject(self.mSelectedHandles) undoStack = self.mapDocument().undoStack() macroStarted = False for i in p: object = i[0] indexRanges = i[1] closed = object.shape() == MapObject.Polygon oldPolygon = object.polygon() newPolygon = joinPolygonNodes(oldPolygon, indexRanges, closed) if (newPolygon.size() < oldPolygon.size()): if (not macroStarted): undoStack.beginMacro(self.tr("Join Nodes")) macroStarted = True undoStack.push(ChangePolygon(self.mapDocument(), object, newPolygon, oldPolygon)) if (macroStarted): undoStack.endMacro() def splitSegments(self): if (self.mSelectedHandles.size() < 2): return p = groupIndexesByObject(self.mSelectedHandles) undoStack = self.mapDocument().undoStack() macroStarted = False for i in p: object = i[0] indexRanges = i[1] closed = object.shape() == MapObject.Polygon oldPolygon = object.polygon() newPolygon = splitPolygonSegments(oldPolygon, indexRanges, closed) if (newPolygon.size() > oldPolygon.size()): if (not macroStarted): undoStack.beginMacro(self.tr("Split Segments")) macroStarted = True undoStack.push(ChangePolygon(self.mapDocument(), object, newPolygon, oldPolygon)) if (macroStarted): undoStack.endMacro() def setSelectedHandles(self, handles): for handle in self.mSelectedHandles: if (not handles.contains(handle)): handle.setSelected(False) for handle in handles: if (not self.mSelectedHandles.contains(handle)): handle.setSelected(True) self.mSelectedHandles = handles def setSelectedHandle(self, handle): self.setSelectedHandles(QSet([handle])) def updateSelection(self, event): rect = QRectF(self.mStart, event.scenePos()).normalized() # Make sure the rect has some contents, otherwise intersects returns False rect.setWidth(max(1.0, rect.width())) rect.setHeight(max(1.0, rect.height())) oldSelection = self.mapScene().selectedObjectItems() if (oldSelection.isEmpty()): # Allow selecting some map objects only when there aren't any selected selectedItems = QSet() for item in self.mapScene().items(rect, Qt.IntersectsItemShape, Qt.DescendingOrder, viewTransform(event)): if type(item) == MapObjectItem: selectedItems.insert(item) newSelection = QSet() if (event.modifiers() & (Qt.ControlModifier | Qt.ShiftModifier)): newSelection = oldSelection | selectedItems else: newSelection = selectedItems self.mapScene().setSelectedObjectItems(newSelection) self.updateHandles() else: # Update the selected handles selectedHandles = QSet() for item in self.mapScene().items(rect, Qt.IntersectsItemShape, Qt.DescendingOrder, viewTransform(event)): if type(item) == PointHandle: selectedHandles.insert(item) if (event.modifiers() & (Qt.ControlModifier | Qt.ShiftModifier)): self.setSelectedHandles(self.mSelectedHandles | selectedHandles) else: self.setSelectedHandles(selectedHandles) def startSelecting(self): self.mMode = EditPolygonTool.Selecting self.mapScene().addItem(self.mSelectionRectangle) def startMoving(self): # Move only the clicked handle, if it was not part of the selection if (not self.mSelectedHandles.contains(self.mClickedHandle)): self.setSelectedHandle(self.mClickedHandle) self.mMode = EditPolygonTool.Moving renderer = self.mapDocument().renderer() # Remember the current object positions self.mOldHandlePositions.clear() self.mOldPolygons.clear() self.mAlignPosition = renderer.screenToPixelCoords_((self.mSelectedHandles.begin()).pos()) for handle in self.mSelectedHandles: pos = renderer.screenToPixelCoords_(handle.pos()) self.mOldHandlePositions.append(handle.pos()) if (pos.x() < self.mAlignPosition.x()): self.mAlignPosition.setX(pos.x()) if (pos.y() < self.mAlignPosition.y()): self.mAlignPosition.setY(pos.y()) mapObject = handle.mapObject() if (not self.mOldPolygons.contains(mapObject)): self.mOldPolygons.insert(mapObject, mapObject.polygon()) def updateMovingItems(self, pos, modifiers): renderer = self.mapDocument().renderer() diff = pos - self.mStart snapHelper = SnapHelper(renderer, modifiers) if (snapHelper.snaps()): alignScreenPos = renderer.pixelToScreenCoords_(self.mAlignPosition) newAlignScreenPos = alignScreenPos + diff newAlignPixelPos = renderer.screenToPixelCoords_(newAlignScreenPos) snapHelper.snap(newAlignPixelPos) diff = renderer.pixelToScreenCoords_(newAlignPixelPos) - alignScreenPos i = 0 for handle in self.mSelectedHandles: # update handle position newScreenPos = self.mOldHandlePositions.at(i) + diff handle.setPos(newScreenPos) # calculate new pixel position of polygon node item = handle.mapObjectItem() newInternalPos = item.mapFromScene(newScreenPos) newScenePos = item.pos() + newInternalPos newPixelPos = renderer.screenToPixelCoords_(newScenePos) # update the polygon mapObject = item.mapObject() polygon = mapObject.polygon() polygon[handle.pointIndex()] = newPixelPos - mapObject.position() self.mapDocument().mapObjectModel().setObjectPolygon(mapObject, polygon) i += 1 def finishMoving(self, pos): self.mMode = EditPolygonTool.NoMode if (self.mStart == pos or self.mOldPolygons.isEmpty()): # Move is a no-op return undoStack = self.mapDocument().undoStack() undoStack.beginMacro(self.tr("Move %n Point(s)", "", self.mSelectedHandles.size())) # TODO: This isn't really optimal. Would be better to have a single undo # command that supports changing multiple map objects. for i in self.mOldPolygons: undoStack.push(ChangePolygon(self.mapDocument(), i[0], i[1])) undoStack.endMacro() self.mOldHandlePositions.clear() self.mOldPolygons.clear() def showHandleContextMenu(self, clickedHandle, screenPos): if (clickedHandle and not self.mSelectedHandles.contains(clickedHandle)): self.setSelectedHandle(clickedHandle) n = self.mSelectedHandles.size() delIcon = QIcon(":images/16x16/edit-delete.png") delText = self.tr("Delete %n Node(s)", "", n) menu = QMenu() deleteNodesAction = menu.addAction(delIcon, delText) joinNodesAction = menu.addAction(self.tr("Join Nodes")) splitSegmentsAction = menu.addAction(self.tr("Split Segments")) Utils.setThemeIcon(deleteNodesAction, "edit-delete") joinNodesAction.setEnabled(n > 1) splitSegmentsAction.setEnabled(n > 1) deleteNodesAction.triggered.connect(self.deleteNodes) joinNodesAction.triggered.connect(self.joinNodes) splitSegmentsAction.triggered.connect(self.splitSegments) menu.exec(screenPos)
def mouseMoveEvent(self, mouseEvent): """ Executed when then mouse is moved on the view. :type mouseEvent: QGraphicsSceneMouseEvent """ scene = self.scene() mousePos = mouseEvent.pos() mouseButtons = mouseEvent.buttons() viewport = self.viewport() if mouseButtons & Qt.RightButton: if (mouseEvent.pos() - self.mousePressPos).manhattanLength() >= QApplication.startDragDistance(): ######################################################################################################## # # # SCENE DRAG # # # ######################################################################################################## if scene.mode is not DiagramMode.SceneDrag: scene.setMode(DiagramMode.SceneDrag) viewport.setCursor(Qt.ClosedHandCursor) mousePos /= self.zoom mousePressPos = self.mousePressPos / self.zoom self.centerOn(self.mousePressCenterPos - mousePos + mousePressPos) else: super().mouseMoveEvent(mouseEvent) if mouseButtons & Qt.LeftButton: self.stopMove() if scene.mode is DiagramMode.RubberBandDrag: #################################################################################################### # # # RUBBERBAND DRAG # # # #################################################################################################### area = QRectF(self.mapFromScene(self.rubberBandOrigin), mousePos).normalized() path = QPainterPath() path.addRect(area) scene.setSelectionArea(self.mapToScene(path)) self.rubberBand.setGeometry(area.toRect()) if scene.mode in { DiagramMode.BreakPointMove, DiagramMode.InsertEdge, DiagramMode.MoveNode, DiagramMode.ResizeNode, DiagramMode.RubberBandDrag }: #################################################################################################### # # # VIEW SCROLLING # # # #################################################################################################### R = viewport.rect() if not R.contains(mousePos): move = QPointF(0, 0) if mousePos.x() < R.left(): move.setX(mousePos.x() - R.left()) elif mousePos.x() > R.right(): move.setX(mousePos.x() - R.right()) if mousePos.y() < R.top(): move.setY(mousePos.y() - R.top()) elif mousePos.y() > R.bottom(): move.setY(mousePos.y() - R.bottom()) if move: move.setX(clamp(move.x(), -MainView.MoveBound, +MainView.MoveBound)) move.setY(clamp(move.y(), -MainView.MoveBound, +MainView.MoveBound)) self.startMove(move, MainView.MoveRate)
class ObjectSelectionTool(AbstractObjectTool): def __init__(self, parent = None): super().__init__(self.tr("Select Objects"), QIcon(":images/22x22/tool-select-objects.png"), QKeySequence(self.tr("S")), parent) self.mSelectionRectangle = SelectionRectangle() self.mOriginIndicator = OriginIndicator() self.mMousePressed = False self.mHoveredObjectItem = None self.mClickedObjectItem = None self.mClickedRotateHandle = None self.mClickedResizeHandle = None self.mResizingLimitHorizontal = False self.mResizingLimitVertical = False self.mMode = Mode.Resize self.mAction = Action.NoAction self.mRotateHandles = [0, 0, 0, 0] self.mResizeHandles = [0, 0, 0, 0, 0, 0, 0, 0] self.mAlignPosition = QPointF() self.mMovingObjects = QVector() self.mScreenStart = QPoint() self.mStart = QPointF() self.mModifiers = 0 self.mOrigin = QPointF() for i in range(AnchorPosition.CornerAnchorCount): self.mRotateHandles[i] = RotateHandle(i) for i in range(AnchorPosition.AnchorCount): self.mResizeHandles[i] = ResizeHandle(i) def __del__(self): if self.mSelectionRectangle.scene(): self.mSelectionRectangle.scene().removeItem(self.mSelectionRectangle) if self.mOriginIndicator.scene(): self.mOriginIndicator.scene().removeItem(self.mOriginIndicator) for i in range(AnchorPosition.CornerAnchorCount): handle = self.mRotateHandles[i] scene = handle.scene() if scene: scene.removeItem(handle) self.mRotateHandles.clear() for i in range(AnchorPosition.AnchorCount): handle = self.mResizeHandles[i] scene = handle.scene() if scene: scene.removeItem(handle) self.mResizeHandles.clear() def tr(self, sourceText, disambiguation = '', n = -1): return QCoreApplication.translate('ObjectSelectionTool', sourceText, disambiguation, n) def activate(self, scene): super().activate(scene) self.updateHandles() self.mapDocument().objectsChanged.connect(self.updateHandles) self.mapDocument().mapChanged.connect(self.updateHandles) scene.selectedObjectItemsChanged.connect(self.updateHandles) self.mapDocument().objectsRemoved.connect(self.objectsRemoved) if self.mOriginIndicator.scene() != scene: scene.addItem(self.mOriginIndicator) for i in range(AnchorPosition.CornerAnchorCount): handle = self.mRotateHandles[i] if handle.scene() != scene: scene.addItem(handle) for i in range(AnchorPosition.AnchorCount): handle = self.mResizeHandles[i] if handle.scene() != scene: scene.addItem(handle) def deactivate(self, scene): if self.mOriginIndicator.scene() == scene: scene.removeItem(self.mOriginIndicator) for i in range(AnchorPosition.CornerAnchorCount): handle = self.mRotateHandles[i] if handle.scene() == scene: scene.removeItem(handle) for i in range(AnchorPosition.AnchorCount): handle = self.mResizeHandles[i] if handle.scene() == scene: scene.removeItem(handle) self.mapDocument().objectsChanged.disconnect(self.updateHandles) self.mapDocument().mapChanged.disconnect(self.updateHandles) scene.selectedObjectItemsChanged.disconnect(self.updateHandles) super().deactivate(scene) def keyPressed(self, event): if (self.mAction != Action.NoAction): event.ignore() return moveBy = QPointF() x = event.key() if x==Qt.Key_Up: moveBy = QPointF(0, -1) elif x==Qt.Key_Down: moveBy = QPointF(0, 1) elif x==Qt.Key_Left: moveBy = QPointF(-1, 0) elif x==Qt.Key_Right: moveBy = QPointF(1, 0) else: super().keyPressed(event) return items = self.mapScene().selectedObjectItems() modifiers = event.modifiers() if (moveBy.isNull() or items.isEmpty() or (modifiers & Qt.ControlModifier)): event.ignore() return moveFast = modifiers & Qt.ShiftModifier snapToFineGrid = preferences.Preferences.instance().snapToFineGrid() if (moveFast): # TODO: This only makes sense for orthogonal maps moveBy.setX(moveBy.x() * self.mapDocument().map().tileWidth()) moveBy.setX(moveBy.y() * self.mapDocument().map().tileHeight()) if (snapToFineGrid): moveBy /= preferences.Preferences.instance().gridFine() undoStack = self.mapDocument().undoStack() undoStack.beginMacro(self.tr("Move %n Object(s)", "", items.size())) i = 0 for objectItem in items: object = objectItem.mapObject() oldPos = object.position() newPos = oldPos + moveBy undoStack.push(MoveMapObject(self.mapDocument(), object, newPos, oldPos)) i += 1 undoStack.endMacro() def mouseEntered(self): pass def mouseMoved(self, pos, modifiers): super().mouseMoved(pos, modifiers) # Update the hovered item (for mouse cursor) hoveredRotateHandle = None hoveredResizeHandle = None hoveredObjectItem = None view = self.mapScene().views()[0] if view: hoveredItem = self.mapScene().itemAt(pos,view.transform()) hoveredRotateHandle = None hoveredResizeHandle = None tp = type(hoveredItem) if tp==RotateHandle: hoveredRotateHandle = hoveredItem elif tp==ResizeHandle: hoveredResizeHandle = hoveredItem if (not hoveredRotateHandle and not hoveredResizeHandle): hoveredObjectItem = self.topMostObjectItemAt(pos) self.mHoveredObjectItem = hoveredObjectItem if (self.mAction == Action.NoAction and self.mMousePressed): screenPos = QCursor.pos() dragDistance = (self.mScreenStart - screenPos).manhattanLength() if (dragDistance >= QApplication.startDragDistance()): hasSelection = not self.mapScene().selectedObjectItems().isEmpty() # Holding Alt forces moving current selection # Holding Shift forces selection rectangle if ((self.mClickedObjectItem or (modifiers & Qt.AltModifier) and hasSelection) and not (modifiers & Qt.ShiftModifier)): self.startMoving(modifiers) elif (self.mClickedRotateHandle): self.startRotating() elif (self.mClickedResizeHandle): self.startResizing() else: self.startSelecting() x = self.mAction if x==Action.Selecting: self.mSelectionRectangle.setRectangle(QRectF(self.mStart, pos).normalized()) elif x==Action.Moving: self.updateMovingItems(pos, modifiers) elif x==Action.Rotating: self.updateRotatingItems(pos, modifiers) elif x==Action.Resizing: self.updateResizingItems(pos, modifiers) elif x==Action.NoAction: pass self.refreshCursor() def mousePressed(self, event): if (self.mAction != Action.NoAction): # Ignore additional presses during select/move return x = event.button() if x==Qt.LeftButton: self.mMousePressed = True self.mStart = event.scenePos() self.mScreenStart = event.screenPos() clickedRotateHandle = 0 clickedResizeHandle = 0 view = findView(event) if view: clickedItem = self.mapScene().itemAt(event.scenePos(), view.transform()) clickedRotateHandle = None clickedResizeHandle = None tp = type(clickedItem) if tp==RotateHandle: clickedRotateHandle = clickedItem elif tp==ResizeHandle: clickedResizeHandle = clickedItem self.mClickedRotateHandle = clickedRotateHandle self.mClickedResizeHandle = clickedResizeHandle if (not clickedRotateHandle and not clickedResizeHandle): self.mClickedObjectItem = self.topMostObjectItemAt(self.mStart) else: super().mousePressed(event) def mouseReleased(self, event): if (event.button() != Qt.LeftButton): return x = self.mAction if x==Action.NoAction: if (not self.mClickedRotateHandle and not self.mClickedResizeHandle): # Don't change selection as a result of clicking on a handle modifiers = event.modifiers() if (self.mClickedObjectItem): selection = self.mapScene().selectedObjectItems() if (modifiers & (Qt.ShiftModifier | Qt.ControlModifier)): if (selection.contains(self.mClickedObjectItem)): selection.remove(self.mClickedObjectItem) else: selection.insert(self.mClickedObjectItem) elif (selection.contains(self.mClickedObjectItem)): # Clicking one of the selected items changes the edit mode if self.mMode == Mode.Resize: _x = Mode.Rotate else: _x = Mode.Resize self.setMode(_x) else: selection.clear() selection.insert(self.mClickedObjectItem) self.setMode(Mode.Resize) self.mapScene().setSelectedObjectItems(selection) elif (not (modifiers & Qt.ShiftModifier)): self.mapScene().setSelectedObjectItems(QSet()) elif x==Action.Selecting: self.updateSelection(event.scenePos(), event.modifiers()) self.mapScene().removeItem(self.mSelectionRectangle) self.mAction = Action.NoAction elif x==Action.Moving: self.finishMoving(event.scenePos()) elif x==Action.Rotating: self.finishRotating(event.scenePos()) elif x==Action.Resizing: self.finishResizing(event.scenePos()) self.mMousePressed = False self.mClickedObjectItem = None self.mClickedRotateHandle = None self.mClickedResizeHandle = None self.refreshCursor() def modifiersChanged(self, modifiers): self.mModifiers = modifiers self.refreshCursor() def languageChanged(self): self.setName(self.tr("Select Objects")) self.setShortcut(QKeySequence(self.tr("S"))) def updateHandles(self): if (self.mAction == Action.Moving or self.mAction == Action.Rotating or self.mAction == Action.Resizing): return objects = self.mapDocument().selectedObjects() showHandles = objects.size() > 0 if (showHandles): renderer = self.mapDocument().renderer() boundingRect = objectBounds(objects.first(), renderer, objectTransform(objects.first(), renderer)) for i in range(1, objects.size()): object = objects.at(i) boundingRect |= objectBounds(object, renderer, objectTransform(object, renderer)) topLeft = boundingRect.topLeft() topRight = boundingRect.topRight() bottomLeft = boundingRect.bottomLeft() bottomRight = boundingRect.bottomRight() center = boundingRect.center() handleRotation = 0 # If there is only one object selected, align to its orientation. if (objects.size() == 1): object = objects.first() handleRotation = object.rotation() if (resizeInPixelSpace(object)): bounds = pixelBounds(object) transform = QTransform(objectTransform(object, renderer)) topLeft = transform.map(renderer.pixelToScreenCoords_(bounds.topLeft())) topRight = transform.map(renderer.pixelToScreenCoords_(bounds.topRight())) bottomLeft = transform.map(renderer.pixelToScreenCoords_(bounds.bottomLeft())) bottomRight = transform.map(renderer.pixelToScreenCoords_(bounds.bottomRight())) center = transform.map(renderer.pixelToScreenCoords_(bounds.center())) # Ugly hack to make handles appear nicer in this case if (self.mapDocument().map().orientation() == Map.Orientation.Isometric): handleRotation += 45 else: bounds = objectBounds(object, renderer, QTransform()) transform = QTransform(objectTransform(object, renderer)) topLeft = transform.map(bounds.topLeft()) topRight = transform.map(bounds.topRight()) bottomLeft = transform.map(bounds.bottomLeft()) bottomRight = transform.map(bounds.bottomRight()) center = transform.map(bounds.center()) self.mOriginIndicator.setPos(center) self.mRotateHandles[AnchorPosition.TopLeftAnchor].setPos(topLeft) self.mRotateHandles[AnchorPosition.TopRightAnchor].setPos(topRight) self.mRotateHandles[AnchorPosition.BottomLeftAnchor].setPos(bottomLeft) self.mRotateHandles[AnchorPosition.BottomRightAnchor].setPos(bottomRight) top = (topLeft + topRight) / 2 left = (topLeft + bottomLeft) / 2 right = (topRight + bottomRight) / 2 bottom = (bottomLeft + bottomRight) / 2 self.mResizeHandles[AnchorPosition.TopAnchor].setPos(top) self.mResizeHandles[AnchorPosition.TopAnchor].setResizingOrigin(bottom) self.mResizeHandles[AnchorPosition.LeftAnchor].setPos(left) self.mResizeHandles[AnchorPosition.LeftAnchor].setResizingOrigin(right) self.mResizeHandles[AnchorPosition.RightAnchor].setPos(right) self.mResizeHandles[AnchorPosition.RightAnchor].setResizingOrigin(left) self.mResizeHandles[AnchorPosition.BottomAnchor].setPos(bottom) self.mResizeHandles[AnchorPosition.BottomAnchor].setResizingOrigin(top) self.mResizeHandles[AnchorPosition.TopLeftAnchor].setPos(topLeft) self.mResizeHandles[AnchorPosition.TopLeftAnchor].setResizingOrigin(bottomRight) self.mResizeHandles[AnchorPosition.TopRightAnchor].setPos(topRight) self.mResizeHandles[AnchorPosition.TopRightAnchor].setResizingOrigin(bottomLeft) self.mResizeHandles[AnchorPosition.BottomLeftAnchor].setPos(bottomLeft) self.mResizeHandles[AnchorPosition.BottomLeftAnchor].setResizingOrigin(topRight) self.mResizeHandles[AnchorPosition.BottomRightAnchor].setPos(bottomRight) self.mResizeHandles[AnchorPosition.BottomRightAnchor].setResizingOrigin(topLeft) for i in range(AnchorPosition.CornerAnchorCount): self.mRotateHandles[i].setRotation(handleRotation) for i in range(AnchorPosition.AnchorCount): self.mResizeHandles[i].setRotation(handleRotation) self.updateHandleVisibility() def updateHandleVisibility(self): hasSelection = not self.mapDocument().selectedObjects().isEmpty() showHandles = hasSelection and (self.mAction == Action.NoAction or self.mAction == Action.Selecting) showOrigin = hasSelection and self.mAction != Action.Moving and (self.mMode == Mode.Rotate or self.mAction == Action.Resizing) for i in range(AnchorPosition.CornerAnchorCount): self.mRotateHandles[i].setVisible(showHandles and self.mMode == Mode.Rotate) for i in range(AnchorPosition.AnchorCount): self.mResizeHandles[i].setVisible(showHandles and self.mMode == Mode.Resize) self.mOriginIndicator.setVisible(showOrigin) def objectsRemoved(self, objects): if (self.mAction != Action.Moving and self.mAction != Action.Rotating and self.mAction != Action.Resizing): return # Abort move/rotate/resize to avoid crashing... # TODO: This should really not be allowed to happen in the first place. # since it breaks the undo history, for example. for i in range(self.mMovingObjects.size() - 1, -1, -1): object = self.mMovingObjects[i] mapObject = object.item.mapObject() if objects.contains(mapObject): # Avoid referencing the removed object self.mMovingObjects.remove(i) else: mapObject.setPosition(object.oldPosition) mapObject.setSize(object.oldSize) mapObject.setPolygon(object.oldPolygon) mapObject.setRotation(object.oldRotation) self.mapDocument().mapObjectModel().emitObjectsChanged(self.changingObjects) self.mMovingObjects.clear() def updateSelection(self, pos, modifiers): rect = QRectF(self.mStart, pos).normalized() # Make sure the rect has some contents, otherwise intersects returns False rect.setWidth(max(1.0, rect.width())) rect.setHeight(max(1.0, rect.height())) selectedItems = QSet() for item in self.mapScene().items(rect): if type(item) == MapObjectItem: selectedItems.insert(item) if (modifiers & (Qt.ControlModifier | Qt.ShiftModifier)): selectedItems |= self.mapScene().selectedObjectItems() else: self.setMode(Mode.Resize) self.mapScene().setSelectedObjectItems(selectedItems) def startSelecting(self): self.mAction = Action.Selecting self.mapScene().addItem(self.mSelectionRectangle) def startMoving(self, modifiers): # Move only the clicked item, if it was not part of the selection if (self.mClickedObjectItem and not (modifiers & Qt.AltModifier)): if (not self.mapScene().selectedObjectItems().contains(self.mClickedObjectItem)): self.mapScene().setSelectedObjectItems(QSet([self.mClickedObjectItem])) self.saveSelectionState() self.mAction = Action.Moving self.mAlignPosition = self.mMovingObjects[0].oldPosition for object in self.mMovingObjects: pos = object.oldPosition if (pos.x() < self.mAlignPosition.x()): self.mAlignPosition.setX(pos.x()) if (pos.y() < self.mAlignPosition.y()): self.mAlignPosition.setY(pos.y()) self.updateHandleVisibility() def updateMovingItems(self, pos, modifiers): renderer = self.mapDocument().renderer() diff = self.snapToGrid(pos-self.mStart, modifiers) for object in self.mMovingObjects: newPixelPos = object.oldItemPosition + diff newPos = renderer.screenToPixelCoords_(newPixelPos) mapObject = object.item.mapObject() mapObject.setPosition(newPos) self.mapDocument().mapObjectModel().emitObjectsChanged(self.changingObjects()) def finishMoving(self, pos): self.mAction = Action.NoAction self.updateHandles() if (self.mStart == pos): # Move is a no-op return undoStack = self.mapDocument().undoStack() undoStack.beginMacro(self.tr("Move %n Object(s)", "", self.mMovingObjects.size())) for object in self.mMovingObjects: undoStack.push(MoveMapObject(self.mapDocument(), object.item.mapObject(), object.oldPosition)) undoStack.endMacro() self.mMovingObjects.clear() def startRotating(self): self.mAction = Action.Rotating self.mOrigin = self.mOriginIndicator.pos() self.saveSelectionState() self.updateHandleVisibility() def updateRotatingItems(self, pos, modifiers): renderer = self.mapDocument().renderer() startDiff = self.mOrigin - self.mStart currentDiff = self.mOrigin - pos startAngle = math.atan2(startDiff.y(), startDiff.x()) currentAngle = math.atan2(currentDiff.y(), currentDiff.x()) angleDiff = currentAngle - startAngle snap = 15 * M_PI / 180 # 15 degrees in radians if (modifiers & Qt.ControlModifier): angleDiff = math.floor((angleDiff + snap / 2) / snap) * snap for object in self.mMovingObjects: mapObject = object.item.mapObject() offset = mapObject.objectGroup().offset() oldRelPos = object.oldItemPosition + offset - self.mOrigin sn = math.sin(angleDiff) cs = math.cos(angleDiff) newRelPos = QPointF(oldRelPos.x() * cs - oldRelPos.y() * sn, oldRelPos.x() * sn + oldRelPos.y() * cs) newPixelPos = self.mOrigin + newRelPos - offset newPos = renderer.screenToPixelCoords_(newPixelPos) newRotation = object.oldRotation + angleDiff * 180 / M_PI mapObject.setPosition(newPos) mapObject.setRotation(newRotation) self.mapDocument().mapObjectModel().emitObjectsChanged(self.changingObjects()) def finishRotating(self, pos): self.mAction = Action.NoAction self.updateHandles() if (self.mStart == pos): # No rotation at all return undoStack = self.mapDocument().undoStack() undoStack.beginMacro(self.tr("Rotate %n Object(s)", "", self.mMovingObjects.size())) for object in self.mMovingObjects: mapObject = object.item.mapObject() undoStack.push(MoveMapObject(self.mapDocument(), mapObject, object.oldPosition)) undoStack.push(RotateMapObject(self.mapDocument(), mapObject, object.oldRotation)) undoStack.endMacro() self.mMovingObjects.clear() def startResizing(self): self.mAction = Action.Resizing self.mOrigin = self.mOriginIndicator.pos() self.mResizingLimitHorizontal = self.mClickedResizeHandle.resizingLimitHorizontal() self.mResizingLimitVertical = self.mClickedResizeHandle.resizingLimitVertical() self.mStart = self.mClickedResizeHandle.pos() self.saveSelectionState() self.updateHandleVisibility() def updateResizingItems(self, pos, modifiers): renderer = self.mapDocument().renderer() resizingOrigin = self.mClickedResizeHandle.resizingOrigin() if (modifiers & Qt.ShiftModifier): resizingOrigin = self.mOrigin self.mOriginIndicator.setPos(resizingOrigin) ## Alternative toggle snap modifier, since Control is taken by the preserve # aspect ratio option. ## snapHelper = SnapHelper(renderer) if (modifiers & Qt.AltModifier): snapHelper.toggleSnap() pixelPos = renderer.screenToPixelCoords_(pos) snapHelper.snap(pixelPos) snappedScreenPos = renderer.pixelToScreenCoords_(pixelPos) diff = snappedScreenPos - resizingOrigin startDiff = self.mStart - resizingOrigin if (self.mMovingObjects.size() == 1): ## For single items the resizing is performed in object space in order # to handle different scaling on X and Y axis as well as to improve # handling of 0-sized objects. ## self.updateResizingSingleItem(resizingOrigin, snappedScreenPos, modifiers) return ## Calculate the scaling factor. Minimum is 1% to protect against making # everything 0-sized and non-recoverable (it's still possibly to run into # problems by repeatedly scaling down to 1%, but that's asking for it) ## scale = 0.0 if (self.mResizingLimitHorizontal): scale = max(0.01, diff.y() / startDiff.y()) elif (self.mResizingLimitVertical): scale = max(0.01, diff.x() / startDiff.x()) else: scale = min(max(0.01, diff.x() / startDiff.x()), max(0.01, diff.y() / startDiff.y())) if not math.isfinite(scale): scale = 1 for object in self.mMovingObjects: mapObject = object.item.mapObject() offset = mapObject.objectGroup().offset() oldRelPos = object.oldItemPosition + offset - resizingOrigin scaledRelPos = QPointF(oldRelPos.x() * scale, oldRelPos.y() * scale) newScreenPos = resizingOrigin + scaledRelPos - offset newPos = renderer.screenToPixelCoords_(newScreenPos) origSize = object.oldSize newSize = QSizeF(origSize.width() * scale, origSize.height() * scale) if (mapObject.polygon().isEmpty() == False): # For polygons, we have to scale in object space. rotation = object.item.rotation() * M_PI / -180 sn = math.sin(rotation) cs = math.cos(rotation) oldPolygon = object.oldPolygon newPolygon = QPolygonF(oldPolygon.size()) for n in range(oldPolygon.size()): oldPoint = QPointF(oldPolygon[n]) rotPoint = QPointF(oldPoint.x() * cs + oldPoint.y() * sn, oldPoint.y() * cs - oldPoint.x() * sn) scaledPoint = QPointF(rotPoint.x() * scale, rotPoint.y() * scale) newPoint = QPointF(scaledPoint.x() * cs - scaledPoint.y() * sn, scaledPoint.y() * cs + scaledPoint.x() * sn) newPolygon[n] = newPoint mapObject.setPolygon(newPolygon) mapObject.setSize(newSize) mapObject.setPosition(newPos) self.mapDocument().mapObjectModel().emitObjectsChanged(self.changingObjects()) def updateResizingSingleItem(self, resizingOrigin, screenPos, modifiers): renderer = self.mapDocument().renderer() object = self.mMovingObjects.first() mapObject = object.item.mapObject() ## The resizingOrigin, screenPos and mStart are affected by the ObjectGroup # offset. We will un-apply it to these variables since the resize for # single items happens in local coordinate space. ## offset = mapObject.objectGroup().offset() ## These transformations undo and redo the object rotation, which is always # applied in screen space. ## unrotate = rotateAt(object.oldItemPosition, -object.oldRotation) rotate = rotateAt(object.oldItemPosition, object.oldRotation) origin = (resizingOrigin - offset) * unrotate pos = (screenPos - offset) * unrotate start = (self.mStart - offset) * unrotate oldPos = object.oldItemPosition ## In order for the resizing to work somewhat sanely in isometric mode, # the resizing is performed in pixel space except for tile objects, which # are not affected by isometric projection apart from their position. ## pixelSpace = resizeInPixelSpace(mapObject) preserveAspect = modifiers & Qt.ControlModifier if (pixelSpace): origin = renderer.screenToPixelCoords_(origin) pos = renderer.screenToPixelCoords_(pos) start = renderer.screenToPixelCoords_(start) oldPos = object.oldPosition newPos = oldPos newSize = object.oldSize ## In case one of the anchors was used as-is, the desired size can be # derived directly from the distance from the origin for rectangle # and ellipse objects. This allows scaling up a 0-sized object without # dealing with infinite scaling factor issues. # # For obvious reasons this can't work on polygons or polylines, nor when # preserving the aspect ratio. ## if (self.mClickedResizeHandle.resizingOrigin() == resizingOrigin and (mapObject.shape() == MapObject.Rectangle or mapObject.shape() == MapObject.Ellipse) and not preserveAspect): newBounds = QRectF(newPos, newSize) newBounds = align(newBounds, mapObject.alignment()) x = self.mClickedResizeHandle.anchorPosition() if x==AnchorPosition.LeftAnchor or x==AnchorPosition.TopLeftAnchor or x==AnchorPosition.BottomLeftAnchor: newBounds.setLeft(min(pos.x(), origin.x())) elif x==AnchorPosition.RightAnchor or x==AnchorPosition.TopRightAnchor or x==AnchorPosition.BottomRightAnchor: newBounds.setRight(max(pos.x(), origin.x())) else: # nothing to do on this axis pass x = self.mClickedResizeHandle.anchorPosition() if x==AnchorPosition.TopAnchor or x==AnchorPosition.TopLeftAnchor or x==AnchorPosition.TopRightAnchor: newBounds.setTop(min(pos.y(), origin.y())) elif x==AnchorPosition.BottomAnchor or x==AnchorPosition.BottomLeftAnchor or x==AnchorPosition.BottomRightAnchor: newBounds.setBottom(max(pos.y(), origin.y())) else: # nothing to do on this axis pass newBounds = unalign(newBounds, mapObject.alignment()) newSize = newBounds.size() newPos = newBounds.topLeft() else: relPos = pos - origin startDiff = start - origin try: newx = relPos.x() / startDiff.x() except: newx = 0 try: newy = relPos.y() / startDiff.y() except: newy = 0 scalingFactor = QSizeF(max(0.01, newx), max(0.01, newy)) if not math.isfinite(scalingFactor.width()): scalingFactor.setWidth(1) if not math.isfinite(scalingFactor.height()): scalingFactor.setHeight(1) if (self.mResizingLimitHorizontal): if preserveAspect: scalingFactor.setWidth(scalingFactor.height()) else: scalingFactor.setWidth(1) elif (self.mResizingLimitVertical): if preserveAspect: scalingFactor.setHeight(scalingFactor.width()) else: scalingFactor.setHeight(1) elif (preserveAspect): scale = min(scalingFactor.width(), scalingFactor.height()) scalingFactor.setWidth(scale) scalingFactor.setHeight(scale) oldRelPos = oldPos - origin newPos = origin + QPointF(oldRelPos.x() * scalingFactor.width(), oldRelPos.y() * scalingFactor.height()) newSize.setWidth(newSize.width() * scalingFactor.width()) newSize.setHeight(newSize.height() * scalingFactor.height()) if (not object.oldPolygon.isEmpty()): newPolygon = QPolygonF(object.oldPolygon.size()) for n in range(object.oldPolygon.size()): point = object.oldPolygon[n] newPolygon[n] = QPointF(point.x() * scalingFactor.width(), point.y() * scalingFactor.height()) mapObject.setPolygon(newPolygon) if (pixelSpace): newPos = renderer.pixelToScreenCoords_(newPos) newPos = renderer.screenToPixelCoords_(newPos * rotate) mapObject.setSize(newSize) mapObject.setPosition(newPos) self.mapDocument().mapObjectModel().emitObjectsChanged(self.changingObjects()) def finishResizing(self, pos): self.mAction = Action.NoAction self.updateHandles() if (self.mStart == pos): # No scaling at all return undoStack = self.mapDocument().undoStack() undoStack.beginMacro(self.tr("Resize %n Object(s)", "", self.mMovingObjects.size())) for object in self.mMovingObjects: mapObject = object.item.mapObject() undoStack.push(MoveMapObject(self.mapDocument(), mapObject, object.oldPosition)) undoStack.push(ResizeMapObject(self.mapDocument(), mapObject, object.oldSize)) if (not object.oldPolygon.isEmpty()): undoStack.push(ChangePolygon(self.mapDocument(), mapObject, object.oldPolygon)) undoStack.endMacro() self.mMovingObjects.clear() def setMode(self, mode): if (self.mMode != mode): self.mMode = mode self.updateHandles() def saveSelectionState(self): self.mMovingObjects.clear() # Remember the initial state before moving, resizing or rotating for item in self.mapScene().selectedObjectItems(): mapObject = item.mapObject() object = MovingObject() object.item = item object.oldItemPosition = item.pos() object.oldPosition = mapObject.position() object.oldSize = mapObject.size() object.oldPolygon = mapObject.polygon() object.oldRotation = mapObject.rotation() self.mMovingObjects.append(object) def refreshCursor(self): cursorShape = Qt.ArrowCursor if self.mAction == Action.NoAction: hasSelection = not self.mapScene().selectedObjectItems().isEmpty() if ((self.mHoveredObjectItem or ((self.mModifiers & Qt.AltModifier) and hasSelection)) and not (self.mModifiers & Qt.ShiftModifier)): cursorShape = Qt.SizeAllCursor elif self.mAction == Action.Moving: cursorShape = Qt.SizeAllCursor if self.cursor.shape != cursorShape: self.setCursor(cursorShape) def snapToGrid(self, diff, modifiers): renderer = self.mapDocument().renderer() snapHelper = SnapHelper(renderer, modifiers) if (snapHelper.snaps()): alignScreenPos = renderer.pixelToScreenCoords_(self.mAlignPosition) newAlignScreenPos = alignScreenPos + diff newAlignPixelPos = renderer.screenToPixelCoords_(newAlignScreenPos) snapHelper.snap(newAlignPixelPos) return renderer.pixelToScreenCoords_(newAlignPixelPos) - alignScreenPos return diff def changingObjects(self): changingObjects = QList() for movingObject in self.mMovingObjects: changingObjects.append(movingObject.item.mapObject()) return changingObjects
def updateFloatPath(self, point=None): """ Draws a quad curve from the edge of the fromBase to the top or bottom of the toBase (q5), and finally to the center of the toBase (toBaseEndpoint). If floatPos!=None, this is a floatingXover and floatPos is the destination point (where the mouse is) while toHelix, toIndex are potentially None and represent the base at floatPos. Args: point (None, optional): Description """ node3 = self._node3 node5 = self._node5 bw = _BASE_WIDTH vhi5 = self._virtual_helix_item nucleicacid_part_item = vhi5.partItem() pt5 = vhi5.mapToItem(nucleicacid_part_item, *node5.floatPoint()) n5_is_forward = node5.is_forward # Enter/exit are relative to the direction that the path travels # overall. five_enter_pt = pt5 + QPointF(0 if n5_is_forward else 1, .5)*bw five_center_pt = pt5 + QPointF(.5, .5)*bw five_exit_pt = pt5 + QPointF(.5, 0 if n5_is_forward else 1)*bw vhi3 = node3.virtualHelixItem() if point: pt3 = point n3_is_forward = True same_strand = False same_parity = False three_enter_pt = three_center_pt = three_exit_pt = pt3 else: pt3 = vhi3.mapToItem(nucleicacid_part_item, *node3.point()) n3_is_forward = node3.is_forward same_strand = (n5_is_forward == n3_is_forward) and vhi3 == vhi5 same_parity = n5_is_forward == n3_is_forward three_enter_pt = pt3 + QPointF(.5, 0 if n3_is_forward else 1)*bw three_center_pt = pt3 + QPointF(.5, .5)*bw three_exit_pt = pt3 + QPointF(1 if n3_is_forward else 0, .5)*bw c1 = QPointF() # case 1: same strand if same_strand: dx = abs(three_enter_pt.x() - five_exit_pt.x()) c1.setX(0.5 * (five_exit_pt.x() + three_enter_pt.x())) if n5_is_forward: c1.setY(five_exit_pt.y() - _yScale * dx) else: c1.setY(five_exit_pt.y() + _yScale * dx) # case 2: same parity elif same_parity: dy = abs(three_enter_pt.y() - five_exit_pt.y()) c1.setX(five_exit_pt.x() + _xScale * dy) c1.setY(0.5 * (five_exit_pt.y() + three_enter_pt.y())) # case 3: different parity else: if n5_is_forward: c1.setX(five_exit_pt.x() - _xScale * abs(three_enter_pt.y() - five_exit_pt.y())) else: c1.setX(five_exit_pt.x() + _xScale * abs(three_enter_pt.y() - five_exit_pt.y())) c1.setY(0.5 * (five_exit_pt.y() + three_enter_pt.y())) # Construct painter path painterpath = QPainterPath() painterpath.moveTo(five_enter_pt) painterpath.lineTo(five_center_pt) painterpath.lineTo(five_exit_pt) painterpath.quadTo(c1, three_enter_pt) painterpath.lineTo(three_center_pt) painterpath.lineTo(three_exit_pt) self.setPath(painterpath) self._updateFloatPen()
def _updatePath(self, strand5p): """ Draws a quad curve from the edge of the fromBase to the top or bottom of the toBase (q5), and finally to the center of the toBase (toBaseEndpoint). If floatPos!=None, this is a floatingXover and floatPos is the destination point (where the mouse is) while toHelix, toIndex are potentially None and represent the base at floatPos. """ group = self.group() self.tempReparent() node3 = self._node3 node5 = self._node5 bw = _BASE_WIDTH parent = self.partItem() vhi5 = self._virtual_helix_item pt5 = vhi5.mapToItem(parent, *node5.point()) five_is_top = node5.isOnTop() five_is_5to3 = node5.isDrawn5to3() vhi3 = node3.virtualHelixItem() pt3 = vhi3.mapToItem(parent, *node3.point()) three_is_top = node3.isOnTop() three_is_5to3 = node3.isDrawn5to3() same_strand = (node5.strandType() == node3.strandType()) and vhi3 == vhi5 same_parity = five_is_5to3 == three_is_5to3 # Enter/exit are relative to the direction that the path travels # overall. five_enter_pt = pt5 + QPointF(0 if five_is_5to3 else 1, 0.5) * bw five_center_pt = pt5 + QPointF(0.5, 0.5) * bw five_exit_pt = pt5 + QPointF(0.5, 0 if five_is_top else 1) * bw three_enter_pt = pt3 + QPointF(0.5, 0 if three_is_top else 1) * bw three_center_pt = pt3 + QPointF(0.5, 0.5) * bw three_exit_pt = pt3 + QPointF(1 if three_is_5to3 else 0, 0.5) * bw c1 = QPointF() # case 1: same strand if same_strand: dx = abs(three_enter_pt.x() - five_exit_pt.x()) c1.setX(0.5 * (five_exit_pt.x() + three_enter_pt.x())) if five_is_top: c1.setY(five_exit_pt.y() - _Y_SCALE * dx) else: c1.setY(five_exit_pt.y() + _Y_SCALE * dx) # case 2: same parity elif same_parity: dy = abs(three_enter_pt.y() - five_exit_pt.y()) c1.setX(five_exit_pt.x() + _X_SCALE * dy) c1.setY(0.5 * (five_exit_pt.y() + three_enter_pt.y())) # case 3: different parity else: if five_is_top and five_is_5to3: c1.setX(five_exit_pt.x() - _X_SCALE * abs(three_enter_pt.y() - five_exit_pt.y())) else: c1.setX(five_exit_pt.x() + _X_SCALE * abs(three_enter_pt.y() - five_exit_pt.y())) c1.setY(0.5 * (five_exit_pt.y() + three_enter_pt.y())) # Construct painter path painterpath = QPainterPath() painterpath.moveTo(five_enter_pt) painterpath.lineTo(five_center_pt) painterpath.lineTo(five_exit_pt) # The xover5's non-crossing-over end (3') has a connection painterpath.quadTo(c1, three_enter_pt) painterpath.lineTo(three_center_pt) painterpath.lineTo(three_exit_pt) tempR = painterpath.boundingRect() tempR.adjust(-bw / 2, 0, bw, 0) self._click_area.setRect(tempR) self.setPath(painterpath) node3.updatePositionAndAppearance() node5.updatePositionAndAppearance() if group: group.addToGroup(self) self._updateColor(strand5p)
def interactiveResize(self, mousePos): """ Handle the interactive resize of the shape. :type mousePos: QPointF """ scene = self.scene() snap = scene.mainwindow.snapToGrid size = scene.GridSize offset = self.handleSize + self.handleMove moved = self.label.moved R = QRectF(self.boundingRect()) D = QPointF(0, 0) minBoundW = self.minwidth + offset * 2 minBoundH = self.minheight + offset * 2 self.prepareGeometryChange() if self.mousePressHandle == self.handleTL: fromX = self.mousePressBound.left() fromY = self.mousePressBound.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, -offset, snap) toY = snapF(toY, size, -offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.background.setLeft(R.left()) self.background.setTop(R.top()) self.selection.setLeft(R.left()) self.selection.setTop(R.top()) self.polygon.setLeft(R.left() + offset) self.polygon.setTop(R.top() + offset) elif self.mousePressHandle == self.handleTM: fromY = self.mousePressBound.top() toY = fromY + mousePos.y() - self.mousePressPos.y() toY = snapF(toY, size, -offset, snap) D.setY(toY - fromY) R.setTop(toY) ## CLAMP SIZE if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.background.setTop(R.top()) self.selection.setTop(R.top()) self.polygon.setTop(R.top() + offset) elif self.mousePressHandle == self.handleTR: fromX = self.mousePressBound.right() fromY = self.mousePressBound.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, +offset, snap) toY = snapF(toY, size, -offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.background.setRight(R.right()) self.background.setTop(R.top()) self.selection.setRight(R.right()) self.selection.setTop(R.top()) self.polygon.setRight(R.right() - offset) self.polygon.setTop(R.top() + offset) elif self.mousePressHandle == self.handleML: fromX = self.mousePressBound.left() toX = fromX + mousePos.x() - self.mousePressPos.x() toX = snapF(toX, size, -offset, snap) D.setX(toX - fromX) R.setLeft(toX) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) self.background.setLeft(R.left()) self.selection.setLeft(R.left()) self.polygon.setLeft(R.left() + offset) elif self.mousePressHandle == self.handleMR: fromX = self.mousePressBound.right() toX = fromX + mousePos.x() - self.mousePressPos.x() toX = snapF(toX, size, +offset, snap) D.setX(toX - fromX) R.setRight(toX) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) self.background.setRight(R.right()) self.selection.setRight(R.right()) self.polygon.setRight(R.right() - offset) elif self.mousePressHandle == self.handleBL: fromX = self.mousePressBound.left() fromY = self.mousePressBound.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, -offset, snap) toY = snapF(toY, size, +offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.background.setLeft(R.left()) self.background.setBottom(R.bottom()) self.selection.setLeft(R.left()) self.selection.setBottom(R.bottom()) self.polygon.setLeft(R.left() + offset) self.polygon.setBottom(R.bottom() - offset) elif self.mousePressHandle == self.handleBM: fromY = self.mousePressBound.bottom() toY = fromY + mousePos.y() - self.mousePressPos.y() toY = snapF(toY, size, +offset, snap) D.setY(toY - fromY) R.setBottom(toY) ## CLAMP SIZE if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.background.setBottom(R.bottom()) self.selection.setBottom(R.bottom()) self.polygon.setBottom(R.bottom() - offset) elif self.mousePressHandle == self.handleBR: fromX = self.mousePressBound.right() fromY = self.mousePressBound.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, +offset, snap) toY = snapF(toY, size, +offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.background.setRight(R.right()) self.background.setBottom(R.bottom()) self.selection.setRight(R.right()) self.selection.setBottom(R.bottom()) self.polygon.setRight(R.right() - offset) self.polygon.setBottom(R.bottom() - offset) self.updateHandles() self.updateTextPos(moved=moved) self.updateAnchors(self.mousePressData, D)
class PixmapDial(QDial): # enum CustomPaintMode CUSTOM_PAINT_MODE_NULL = 0 # default (NOTE: only this mode has label gradient) CUSTOM_PAINT_MODE_CARLA_WET = 1 # color blue-green gradient (reserved #3) CUSTOM_PAINT_MODE_CARLA_VOL = 2 # color blue (reserved #3) CUSTOM_PAINT_MODE_CARLA_L = 3 # color yellow (reserved #4) CUSTOM_PAINT_MODE_CARLA_R = 4 # color yellow (reserved #4) CUSTOM_PAINT_MODE_CARLA_PAN = 5 # color yellow (reserved #3) CUSTOM_PAINT_MODE_COLOR = 6 # color, selectable (reserved #3) CUSTOM_PAINT_MODE_ZITA = 7 # custom zita knob (reserved #6) CUSTOM_PAINT_MODE_NO_GRADIENT = 8 # skip label gradient # enum Orientation HORIZONTAL = 0 VERTICAL = 1 HOVER_MIN = 0 HOVER_MAX = 9 MODE_DEFAULT = 0 MODE_LINEAR = 1 # signals realValueChanged = pyqtSignal(float) def __init__(self, parent, index=0): QDial.__init__(self, parent) self.fDialMode = self.MODE_LINEAR self.fMinimum = 0.0 self.fMaximum = 1.0 self.fRealValue = 0.0 self.fPrecision = 10000 self.fIsInteger = False self.fIsHovered = False self.fIsPressed = False self.fHoverStep = self.HOVER_MIN self.fLastDragPos = None self.fLastDragValue = 0.0 self.fIndex = index self.fPixmap = QPixmap(":/bitmaps/dial_01d.png") self.fPixmapNum = "01" if self.fPixmap.width() > self.fPixmap.height(): self.fPixmapOrientation = self.HORIZONTAL else: self.fPixmapOrientation = self.VERTICAL self.fLabel = "" self.fLabelPos = QPointF(0.0, 0.0) self.fLabelFont = QFont(self.font()) self.fLabelFont.setPixelSize(8) self.fLabelWidth = 0 self.fLabelHeight = 0 if self.palette().window().color().lightness() > 100: # Light background c = self.palette().dark().color() self.fLabelGradientColor1 = c self.fLabelGradientColor2 = QColor(c.red(), c.green(), c.blue(), 0) self.fLabelGradientColorT = [self.palette().buttonText().color(), self.palette().mid().color()] else: # Dark background self.fLabelGradientColor1 = QColor(0, 0, 0, 255) self.fLabelGradientColor2 = QColor(0, 0, 0, 0) self.fLabelGradientColorT = [Qt.white, Qt.darkGray] self.fLabelGradient = QLinearGradient(0, 0, 0, 1) self.fLabelGradient.setColorAt(0.0, self.fLabelGradientColor1) self.fLabelGradient.setColorAt(0.6, self.fLabelGradientColor1) self.fLabelGradient.setColorAt(1.0, self.fLabelGradientColor2) self.fLabelGradientRect = QRectF(0.0, 0.0, 0.0, 0.0) self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_NULL self.fCustomPaintColor = QColor(0xff, 0xff, 0xff) self.updateSizes() # Fake internal value, custom precision QDial.setMinimum(self, 0) QDial.setMaximum(self, self.fPrecision) QDial.setValue(self, 0) self.valueChanged.connect(self.slot_valueChanged) def getIndex(self): return self.fIndex def getBaseSize(self): return self.fPixmapBaseSize def forceWhiteLabelGradientText(self): self.fLabelGradientColor1 = QColor(0, 0, 0, 255) self.fLabelGradientColor2 = QColor(0, 0, 0, 0) self.fLabelGradientColorT = [Qt.white, Qt.darkGray] def setLabelColor(self, enabled, disabled): self.fLabelGradientColor1 = QColor(0, 0, 0, 255) self.fLabelGradientColor2 = QColor(0, 0, 0, 0) self.fLabelGradientColorT = [enabled, disabled] def updateSizes(self): self.fPixmapWidth = self.fPixmap.width() self.fPixmapHeight = self.fPixmap.height() if self.fPixmapWidth < 1: self.fPixmapWidth = 1 if self.fPixmapHeight < 1: self.fPixmapHeight = 1 if self.fPixmapOrientation == self.HORIZONTAL: self.fPixmapBaseSize = self.fPixmapHeight self.fPixmapLayersCount = self.fPixmapWidth / self.fPixmapHeight else: self.fPixmapBaseSize = self.fPixmapWidth self.fPixmapLayersCount = self.fPixmapHeight / self.fPixmapWidth self.setMinimumSize(self.fPixmapBaseSize, self.fPixmapBaseSize + self.fLabelHeight + 5) self.setMaximumSize(self.fPixmapBaseSize, self.fPixmapBaseSize + self.fLabelHeight + 5) if not self.fLabel: self.fLabelHeight = 0 self.fLabelWidth = 0 return self.fLabelWidth = QFontMetrics(self.fLabelFont).width(self.fLabel) self.fLabelHeight = QFontMetrics(self.fLabelFont).height() self.fLabelPos.setX(float(self.fPixmapBaseSize)/2.0 - float(self.fLabelWidth)/2.0) if self.fPixmapNum in ("01", "02", "07", "08", "09", "10"): self.fLabelPos.setY(self.fPixmapBaseSize + self.fLabelHeight) elif self.fPixmapNum in ("11",): self.fLabelPos.setY(self.fPixmapBaseSize + self.fLabelHeight*2/3) else: self.fLabelPos.setY(self.fPixmapBaseSize + self.fLabelHeight/2) self.fLabelGradient.setStart(0, float(self.fPixmapBaseSize)/2.0) self.fLabelGradient.setFinalStop(0, self.fPixmapBaseSize + self.fLabelHeight + 5) self.fLabelGradientRect = QRectF(float(self.fPixmapBaseSize)/8.0, float(self.fPixmapBaseSize)/2.0, float(self.fPixmapBaseSize*3)/4.0, self.fPixmapBaseSize+self.fLabelHeight+5) def setCustomPaintMode(self, paintMode): if self.fCustomPaintMode == paintMode: return self.fCustomPaintMode = paintMode self.update() def setCustomPaintColor(self, color): if self.fCustomPaintColor == color: return self.fCustomPaintColor = color self.update() def setLabel(self, label): if self.fLabel == label: return self.fLabel = label self.updateSizes() self.update() def setIndex(self, index): self.fIndex = index def setPixmap(self, pixmapId): self.fPixmapNum = "%02i" % pixmapId self.fPixmap.load(":/bitmaps/dial_%s%s.png" % (self.fPixmapNum, "" if self.isEnabled() else "d")) if self.fPixmap.width() > self.fPixmap.height(): self.fPixmapOrientation = self.HORIZONTAL else: self.fPixmapOrientation = self.VERTICAL # special pixmaps if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_NULL: # reserved for carla-wet, carla-vol, carla-pan and color if self.fPixmapNum == "03": self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_COLOR # reserved for carla-L and carla-R elif self.fPixmapNum == "04": self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_CARLA_L # reserved for zita elif self.fPixmapNum == "06": self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_ZITA self.updateSizes() self.update() def setPrecision(self, value, isInteger): self.fPrecision = value self.fIsInteger = isInteger QDial.setMaximum(self, value) def setMinimum(self, value): self.fMinimum = value def setMaximum(self, value): self.fMaximum = value def setValue(self, value, emitSignal=False): if self.fRealValue == value: return if value <= self.fMinimum: qtValue = 0 self.fRealValue = self.fMinimum elif value >= self.fMaximum: qtValue = self.fPrecision self.fRealValue = self.fMaximum else: qtValue = round(float(value - self.fMinimum) / float(self.fMaximum - self.fMinimum) * self.fPrecision) self.fRealValue = value # Block change signal, we'll handle it ourselves self.blockSignals(True) QDial.setValue(self, qtValue) self.blockSignals(False) if emitSignal: self.realValueChanged.emit(self.fRealValue) @pyqtSlot(int) def slot_valueChanged(self, value): self.fRealValue = float(value)/self.fPrecision * (self.fMaximum - self.fMinimum) + self.fMinimum self.realValueChanged.emit(self.fRealValue) @pyqtSlot() def slot_updatePixmap(self): self.setPixmap(int(self.fPixmapNum)) def minimumSizeHint(self): return QSize(self.fPixmapBaseSize, self.fPixmapBaseSize) def sizeHint(self): return QSize(self.fPixmapBaseSize, self.fPixmapBaseSize) def changeEvent(self, event): QDial.changeEvent(self, event) # Force pixmap update if enabled state changes if event.type() == QEvent.EnabledChange: self.setPixmap(int(self.fPixmapNum)) def enterEvent(self, event): self.fIsHovered = True if self.fHoverStep == self.HOVER_MIN: self.fHoverStep = self.HOVER_MIN + 1 QDial.enterEvent(self, event) def leaveEvent(self, event): self.fIsHovered = False if self.fHoverStep == self.HOVER_MAX: self.fHoverStep = self.HOVER_MAX - 1 QDial.leaveEvent(self, event) def mousePressEvent(self, event): if self.fDialMode == self.MODE_DEFAULT: return QDial.mousePressEvent(self, event) if event.button() == Qt.LeftButton: self.fIsPressed = True self.fLastDragPos = event.pos() self.fLastDragValue = self.fRealValue def mouseMoveEvent(self, event): if self.fDialMode == self.MODE_DEFAULT: return QDial.mouseMoveEvent(self, event) if not self.fIsPressed: return range = (self.fMaximum - self.fMinimum) / 4.0 pos = event.pos() dx = range * float(pos.x() - self.fLastDragPos.x()) / self.width() dy = range * float(pos.y() - self.fLastDragPos.y()) / self.height() value = self.fLastDragValue + dx - dy if value < self.fMinimum: value = self.fMinimum elif value > self.fMaximum: value = self.fMaximum elif self.fIsInteger: value = float(round(value)) self.setValue(value, True) def mouseReleaseEvent(self, event): if self.fDialMode == self.MODE_DEFAULT: return QDial.mouseReleaseEvent(self, event) if self.fIsPressed: self.fIsPressed = False def paintEvent(self, event): painter = QPainter(self) event.accept() painter.save() painter.setRenderHint(QPainter.Antialiasing, True) if self.fLabel: if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_NULL: painter.setPen(self.fLabelGradientColor2) painter.setBrush(self.fLabelGradient) painter.drawRect(self.fLabelGradientRect) painter.setFont(self.fLabelFont) painter.setPen(self.fLabelGradientColorT[0 if self.isEnabled() else 1]) painter.drawText(self.fLabelPos, self.fLabel) if self.isEnabled(): normValue = float(self.fRealValue - self.fMinimum) / float(self.fMaximum - self.fMinimum) target = QRectF(0.0, 0.0, self.fPixmapBaseSize, self.fPixmapBaseSize) curLayer = int((self.fPixmapLayersCount - 1) * normValue) if self.fPixmapOrientation == self.HORIZONTAL: xpos = self.fPixmapBaseSize * curLayer ypos = 0.0 else: xpos = 0.0 ypos = self.fPixmapBaseSize * curLayer source = QRectF(xpos, ypos, self.fPixmapBaseSize, self.fPixmapBaseSize) painter.drawPixmap(target, self.fPixmap, source) # Custom knobs (Dry/Wet and Volume) if self.fCustomPaintMode in (self.CUSTOM_PAINT_MODE_CARLA_WET, self.CUSTOM_PAINT_MODE_CARLA_VOL): # knob color colorGreen = QColor(0x5D, 0xE7, 0x3D).lighter(100 + self.fHoverStep*6) colorBlue = QColor(0x3E, 0xB8, 0xBE).lighter(100 + self.fHoverStep*6) # draw small circle ballRect = QRectF(8.0, 8.0, 15.0, 15.0) ballPath = QPainterPath() ballPath.addEllipse(ballRect) #painter.drawRect(ballRect) tmpValue = (0.375 + 0.75*normValue) ballValue = tmpValue - floor(tmpValue) ballPoint = ballPath.pointAtPercent(ballValue) # draw arc startAngle = 216*16 spanAngle = -252*16*normValue if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_CARLA_WET: painter.setBrush(colorBlue) painter.setPen(QPen(colorBlue, 0)) painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2)) gradient = QConicalGradient(15.5, 15.5, -45) gradient.setColorAt(0.0, colorBlue) gradient.setColorAt(0.125, colorBlue) gradient.setColorAt(0.625, colorGreen) gradient.setColorAt(0.75, colorGreen) gradient.setColorAt(0.76, colorGreen) gradient.setColorAt(1.0, colorGreen) painter.setBrush(gradient) painter.setPen(QPen(gradient, 3)) else: painter.setBrush(colorBlue) painter.setPen(QPen(colorBlue, 0)) painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2)) painter.setBrush(colorBlue) painter.setPen(QPen(colorBlue, 3)) painter.drawArc(4.0, 4.0, 26.0, 26.0, startAngle, spanAngle) # Custom knobs (L and R) elif self.fCustomPaintMode in (self.CUSTOM_PAINT_MODE_CARLA_L, self.CUSTOM_PAINT_MODE_CARLA_R): # knob color color = QColor(0xAD, 0xD5, 0x48).lighter(100 + self.fHoverStep*6) # draw small circle ballRect = QRectF(7.0, 8.0, 11.0, 12.0) ballPath = QPainterPath() ballPath.addEllipse(ballRect) #painter.drawRect(ballRect) tmpValue = (0.375 + 0.75*normValue) ballValue = tmpValue - floor(tmpValue) ballPoint = ballPath.pointAtPercent(ballValue) painter.setBrush(color) painter.setPen(QPen(color, 0)) painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.0, 2.0)) # draw arc if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_CARLA_L: startAngle = 216*16 spanAngle = -252.0*16*normValue else: startAngle = 324.0*16 spanAngle = 252.0*16*(1.0-normValue) painter.setPen(QPen(color, 2)) painter.drawArc(3.5, 4.5, 22.0, 22.0, startAngle, spanAngle) # Custom knobs (Color) elif self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_COLOR: # knob color color = self.fCustomPaintColor.lighter(100 + self.fHoverStep*6) # draw small circle ballRect = QRectF(8.0, 8.0, 15.0, 15.0) ballPath = QPainterPath() ballPath.addEllipse(ballRect) tmpValue = (0.375 + 0.75*normValue) ballValue = tmpValue - floor(tmpValue) ballPoint = ballPath.pointAtPercent(ballValue) # draw arc startAngle = 216*16 spanAngle = -252*16*normValue painter.setBrush(color) painter.setPen(QPen(color, 0)) painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2)) painter.setBrush(color) painter.setPen(QPen(color, 3)) painter.drawArc(4.0, 4.0, 26.0, 26.0, startAngle, spanAngle) # Custom knobs (Zita) elif self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_ZITA: a = normValue * pi * 1.5 - 2.35 r = 10.0 x = 10.5 y = 10.5 x += r * sin(a) y -= r * cos(a) painter.setBrush(Qt.black) painter.setPen(QPen(Qt.black, 2)) painter.drawLine(QPointF(11.0, 11.0), QPointF(x, y)) # Custom knobs else: painter.restore() return if self.HOVER_MIN < self.fHoverStep < self.HOVER_MAX: self.fHoverStep += 1 if self.fIsHovered else -1 QTimer.singleShot(20, self.update) else: # isEnabled() target = QRectF(0.0, 0.0, self.fPixmapBaseSize, self.fPixmapBaseSize) painter.drawPixmap(target, self.fPixmap, target) painter.restore() def resizeEvent(self, event): QDial.resizeEvent(self, event) self.updateSizes()
class Node(QGraphicsItem): Type = QGraphicsItem.UserType + 1 def __init__(self, graphWidget): super(Node, self).__init__() self.graph = graphWidget self.edgeList = [] self.newPos = QPointF() self.setFlag(QGraphicsItem.ItemIsMovable) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) self.setCacheMode(QGraphicsItem.DeviceCoordinateCache) self.setZValue(1) def type(self): return Node.Type def addEdge(self, edge): self.edgeList.append(edge) edge.adjust() def edges(self): return self.edgeList def calculateForces(self): if not self.scene() or self.scene().mouseGrabberItem() is self: self.newPos = self.pos() return # Sum up all forces pushing this item away. xvel = 0.0 yvel = 0.0 for item in self.scene().items(): if not isinstance(item, Node): continue line = QLineF(self.mapFromItem(item, 0, 0), QPointF(0, 0)) dx = line.dx() dy = line.dy() l = 2.0 * (dx * dx + dy * dy) if l > 0: xvel += (dx * 150.0) / l yvel += (dy * 150.0) / l # Now subtract all forces pulling items together. weight = (len(self.edgeList) + 1) * 10.0 for edge in self.edgeList: if edge.sourceNode() is self: pos = self.mapFromItem(edge.destNode(), 0, 0) else: pos = self.mapFromItem(edge.sourceNode(), 0, 0) xvel += pos.x() / weight yvel += pos.y() / weight if qAbs(xvel) < 0.1 and qAbs(yvel) < 0.1: xvel = yvel = 0.0 sceneRect = self.scene().sceneRect() self.newPos = self.pos() + QPointF(xvel, yvel) self.newPos.setX(min(max(self.newPos.x(), sceneRect.left() + 10), sceneRect.right() - 10)) self.newPos.setY(min(max(self.newPos.y(), sceneRect.top() + 10), sceneRect.bottom() - 10)) def advance(self): if self.newPos == self.pos(): return False self.setPos(self.newPos) return True def boundingRect(self): adjust = 2.0 return QRectF(-10 - adjust, -10 - adjust, 23 + adjust, 23 + adjust) def shape(self): path = QPainterPath() path.addEllipse(-10, -10, 20, 20) return path def paint(self, painter, option, widget): painter.setPen(Qt.NoPen) painter.setBrush(Qt.darkGray) painter.drawEllipse(-7, -7, 20, 20) gradient = QRadialGradient(-3, -3, 10) if option.state & QStyle.State_Sunken: gradient.setCenter(3, 3) gradient.setFocalPoint(3, 3) gradient.setColorAt(1, QColor(Qt.yellow).lighter(120)) gradient.setColorAt(0, QColor(Qt.darkYellow).lighter(120)) else: gradient.setColorAt(0, Qt.yellow) gradient.setColorAt(1, Qt.darkYellow) painter.setBrush(QBrush(gradient)) painter.setPen(QPen(Qt.black, 0)) painter.drawEllipse(-10, -10, 20, 20) def itemChange(self, change, value): if change == QGraphicsItem.ItemPositionHasChanged: for edge in self.edgeList: edge.adjust() self.graph.itemMoved() return super(Node, self).itemChange(change, value) def mousePressEvent(self, event): self.update() super(Node, self).mousePressEvent(event) def mouseReleaseEvent(self, event): self.update() super(Node, self).mouseReleaseEvent(event)
def __findIntersection(self, p1, p2, p3, p4): """ Private method to calculate the intersection point of two lines. The first line is determined by the points p1 and p2, the second line by p3 and p4. If the intersection point is not contained in the segment p1p2, then it returns (-1.0, -1.0). For the function's internal calculations remember:<br /> QT coordinates start with the point (0,0) as the topleft corner and x-values increase from left to right and y-values increase from top to bottom; it means the visible area is quadrant I in the regular XY coordinate system <pre> Quadrant II | Quadrant I -----------------|----------------- Quadrant III | Quadrant IV </pre> In order for the linear function calculations to work in this method we must switch x and y values (x values become y values and viceversa) @param p1 first point of first line (QPointF) @param p2 second point of first line (QPointF) @param p3 first point of second line (QPointF) @param p4 second point of second line (QPointF) @return the intersection point (QPointF) """ x1 = p1.y() y1 = p1.x() x2 = p2.y() y2 = p2.x() x3 = p3.y() y3 = p3.x() x4 = p4.y() y4 = p4.x() # line 1 is the line between (x1, y1) and (x2, y2) # line 2 is the line between (x3, y3) and (x4, y4) no_line1 = True # it is false, if line 1 is a linear function no_line2 = True # it is false, if line 2 is a linear function slope1 = 0.0 slope2 = 0.0 b1 = 0.0 b2 = 0.0 if x2 != x1: slope1 = (y2 - y1) / (x2 - x1) b1 = y1 - slope1 * x1 no_line1 = False if x4 != x3: slope2 = (y4 - y3) / (x4 - x3) b2 = y3 - slope2 * x3 no_line2 = False pt = QPointF() # if either line is not a function if no_line1 and no_line2: # if the lines are not the same one if x1 != x3: return QPointF(-1.0, -1.0) # if the lines are the same ones if y3 <= y4: if y3 <= y1 and y1 <= y4: return QPointF(y1, x1) else: return QPointF(y2, x2) else: if y4 <= y1 and y1 <= y3: return QPointF(y1, x1) else: return QPointF(y2, x2) elif no_line1: pt.setX(slope2 * x1 + b2) pt.setY(x1) if y1 >= y2: if not (y2 <= pt.x() and pt.x() <= y1): pt.setX(-1.0) pt.setY(-1.0) else: if not (y1 <= pt.x() and pt.x() <= y2): pt.setX(-1.0) pt.setY(-1.0) return pt elif no_line2: pt.setX(slope1 * x3 + b1) pt.setY(x3) if y3 >= y4: if not (y4 <= pt.x() and pt.x() <= y3): pt.setX(-1.0) pt.setY(-1.0) else: if not (y3 <= pt.x() and pt.x() <= y4): pt.setX(-1.0) pt.setY(-1.0) return pt if slope1 == slope2: pt.setX(-1.0) pt.setY(-1.0) return pt pt.setY((b2 - b1) / (slope1 - slope2)) pt.setX(slope1 * pt.y() + b1) # the intersection point must be inside the segment (x1, y1) (x2, y2) if x2 >= x1 and y2 >= y1: if not ((x1 <= pt.y() and pt.y() <= x2) and (y1 <= pt.x() and pt.x() <= y2)): pt.setX(-1.0) pt.setY(-1.0) elif x2 < x1 and y2 >= y1: if not ((x2 <= pt.y() and pt.y() <= x1) and (y1 <= pt.x() and pt.x() <= y2)): pt.setX(-1.0) pt.setY(-1.0) elif x2 >= x1 and y2 < y1: if not ((x1 <= pt.y() and pt.y() <= x2) and (y2 <= pt.x() and pt.x() <= y1)): pt.setX(-1.0) pt.setY(-1.0) else: if not ((x2 <= pt.y() and pt.y() <= x1) and (y2 <= pt.x() and pt.x() <= y1)): pt.setX(-1.0) pt.setY(-1.0) return pt
def interactiveResize(self, mousePos): """ Perform shape interactive resize. """ offset = self.handleSize + self.handleSpace boundingRect = self.boundingRect() rect = self.rect() diff = QPointF(0, 0) self.prepareGeometryChange() if self.handleSelected == self.handleTopLeft: fromX = self.mousePressRect.left() fromY = self.mousePressRect.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() diff.setX(toX - fromX) diff.setY(toY - fromY) boundingRect.setLeft(toX) boundingRect.setTop(toY) rect.setLeft(boundingRect.left() + offset) rect.setTop(boundingRect.top() + offset) self.setRect(rect) elif self.handleSelected == self.handleTopMiddle: fromY = self.mousePressRect.top() toY = fromY + mousePos.y() - self.mousePressPos.y() diff.setY(toY - fromY) boundingRect.setTop(toY) rect.setTop(boundingRect.top() + offset) self.setRect(rect) elif self.handleSelected == self.handleTopRight: fromX = self.mousePressRect.right() fromY = self.mousePressRect.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() diff.setX(toX - fromX) diff.setY(toY - fromY) boundingRect.setRight(toX) boundingRect.setTop(toY) rect.setRight(boundingRect.right() - offset) rect.setTop(boundingRect.top() + offset) self.setRect(rect) elif self.handleSelected == self.handleMiddleLeft: fromX = self.mousePressRect.left() toX = fromX + mousePos.x() - self.mousePressPos.x() diff.setX(toX - fromX) boundingRect.setLeft(toX) rect.setLeft(boundingRect.left() + offset) self.setRect(rect) elif self.handleSelected == self.handleMiddleRight: fromX = self.mousePressRect.right() toX = fromX + mousePos.x() - self.mousePressPos.x() diff.setX(toX - fromX) boundingRect.setRight(toX) rect.setRight(boundingRect.right() - offset) self.setRect(rect) elif self.handleSelected == self.handleBottomLeft: fromX = self.mousePressRect.left() fromY = self.mousePressRect.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() diff.setX(toX - fromX) diff.setY(toY - fromY) boundingRect.setLeft(toX) boundingRect.setBottom(toY) rect.setLeft(boundingRect.left() + offset) rect.setBottom(boundingRect.bottom() - offset) self.setRect(rect) elif self.handleSelected == self.handleBottomMiddle: fromY = self.mousePressRect.bottom() toY = fromY + mousePos.y() - self.mousePressPos.y() diff.setY(toY - fromY) boundingRect.setBottom(toY) rect.setBottom(boundingRect.bottom() - offset) self.setRect(rect) elif self.handleSelected == self.handleBottomRight: fromX = self.mousePressRect.right() fromY = self.mousePressRect.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() diff.setX(toX - fromX) diff.setY(toY - fromY) boundingRect.setRight(toX) boundingRect.setBottom(toY) rect.setRight(boundingRect.right() - offset) rect.setBottom(boundingRect.bottom() - offset) self.setRect(rect) self.updateHandlesPos()