I would like to connect two items together by a line and when I move these items the line changes automatically (orientation and dimensions).
I managed to create a menu that can create rectangle items with circles (one above and one below), in order to connect these items I right click on one of the circles of each item. I would like a line to be created in order to make a connection between these items and therefore for each end of the line to be attached to one of the circles of the item and can be modified in accordance with the movement of the item.
here is my code for now
import sys
import os
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QComboBox, QMainWindow, QToolBar, QToolButton, QLabel, QGraphicsEllipseItem, \
QGraphicsScene, QGraphicsView, QGraphicsLineItem, QGraphicsRectItem, QGraphicsTextItem, QInputDialog, QGraphicsObject
from PyQt5.QtCore import Qt, QPoint, QCoreApplication, pyqtSignal, QObject, QRectF, QLineF
from PyQt5.QtGui import QIcon, QPen, QBrush, QCursor, QTransform, QKeyEvent, QPainterPathStroker
from enum import Enum
class App(QMainWindow):
def __init__(self):
super().__init__()
self.setMouseTracking(True)
self.title = 'Glisser-Deposer d Indicateurs'
self.left = 10
self.top = 40
self.width = 640
self.height = 480
self.drawing_line = None
self.start_circle = None
self.initUI()
self.source_circle = None
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.toolbar = QToolBar(self)
self.addToolBar(Qt.LeftToolBarArea, self.toolbar)
constructionLabel = QLabel("Construction")
self.toolbar.addWidget(constructionLabel)
groupeButton = QToolButton(self)
groupeButton.setText("Groupe")
groupeButton.clicked.connect(self.showConstantDialogForGroupe)
self.toolbar.addWidget(groupeButton)
analyseLabel = QLabel("Analyse")
self.toolbar.addWidget(analyseLabel)
self.indicateursComboBox = QComboBox(self)
self.indicateursComboBox.addItems(
["indicateurs", "Mama", "Rase", "Small"])
self.indicateursComboBox.activated.connect(self.showConstantDialogForIndicateurs)
self.toolbar.addWidget(self.indicateursComboBox)
self.scene = QGraphicsScene()
self.view = QGraphicsView(self.scene, self)
self.setCentralWidget(self.view)
self.show()
def showConstantDialogForGroupe(self):
constant, ok = QInputDialog.getInt(self, 'Modifie constantes', ' l constante for Groupe:')
if ok:
cursor_pos = self.mapFromGlobal(QCursor.pos())
x, y = cursor_pos.x(), cursor_pos.y()
item = QGraphicsRectItem(x, y, 100, 40)
item.setBrush(QBrush(Qt.blue))
item.setFlag(QGraphicsRectItem.ItemIsMovable)
top_circle = HoverableEllipse(x + 45, y - 10, 10, 10, item)
bottom_circle = HoverableEllipse(x + 45, y + 40, 10, 10, item)
top_circle.setAcceptHoverEvents(True)
bottom_circle.setAcceptHoverEvents(True)
top_circle.connectionRequested.connect(self.connectCircles)
bottom_circle.connectionRequested.connect(self.connectCircles)
text = QGraphicsTextItem("Groupe", item)
text.setDefaultTextColor(Qt.white)
text.setPos(x + 10, y + 10)
self.scene.addItem(item)
def showConstantDialogForIndicateurs(self, index):
if index == 0:
return
indicator = self.indicateursComboBox.currentText()
constant, ok = QInputDialog.getInt(self, 'Modifie constantes', f' constante for {indicator}:')
if ok:
cursor_pos = self.mapFromGlobal(QCursor.pos())
x, y = cursor_pos.x(), cursor_pos.y()
item = QGraphicsRectItem(x, y, 100, 40)
item.setBrush(QBrush(Qt.blue))
item.setFlag(QGraphicsRectItem.ItemIsMovable)
top_circle = HoverableEllipse(x + 45, y - 10, 10, 10, item)
bottom_circle = HoverableEllipse(x + 45, y + 40, 10, 10, item)
top_circle.setAcceptHoverEvents(True)
bottom_circle.setAcceptHoverEvents(True)
top_circle.connectionRequested.connect(self.connectCircles)
bottom_circle.connectionRequested.connect(self.connectCircles)
text = QGraphicsTextItem(indicator, item)
text.setDefaultTextColor(Qt.white)
text.setPos(x + 10, y + 10)
self.scene.addItem(item)
self.indicateursComboBox.setCurrentIndex(0)
def mousePressEvent(self, event):
print("mousePressEvent active")
item = self.scene.itemAt(self.view.mapToScene(event.pos()), QTransform())
print(type(item))
if isinstance(item, HoverableEllipse):
if not self.drawing_line:
start_point = item.scenePos()
self.drawing_line = QGraphicsLineItem(start_point.x(), start_point.y(), start_point.x(), start_point.y())
self.scene.addItem(self.drawing_line)
self.start_circle = item
else:
end_point = item.scenePos()
self.drawing_line.setLine(self.start_circle.scenePos().x(), self.start_circle.scenePos().y(), end_point.x(), end_point.y())
self.drawing_line = None
self.start_circle = None
def mouseMoveEvent(self, event):
print("mouseMoveEvent active")
super().mouseMoveEvent(event)
if self.drawing_line:
line = self.drawing_line.line()
self.drawing_line.setLine(line.x1(), line.y1(), event.pos().x(), event.pos().y())
def closeEvent(self, event):
super(App, self).closeEvent(event)
QCoreApplication.instance().quit()
def connectCircles(self):
if not self.source_circle:
self.source_circle = self.sender()
return
line = GraphicsLineItem(self.source_circle, self.sender())
self.scene.addItem(line)
self.source_circle = None
class GraphicsLineItem(QGraphicsLineItem):
def __init__(self, source, destination, parent=None):
super().__init__(parent)
self.source = source
self.destination = destination
self.move()
self.source.moved.connect(self.move)
self.destination.moved.connect(self.move)
def contextMenuEvent(self, event):
menu = QMenu()
menu.addAction("Delete", self.remove)
menu.exec_(self.cursor().pos())
def remove(self):
self.scene().removeItem(self)
def shape(self):
p = super(GraphicsLineItem, self).shape()
stroker = QPainterPathStroker()
stroker.setWidth(20)
return stroker.createStroke(p)
def move(self):
self.setLine(QLineF(self.source.scenePos(), self.destination.scenePos()))
class HoverableEllipse(QGraphicsObject):
moved = pyqtSignal()
connectionRequested = pyqtSignal(QGraphicsObject)
def __init__(self, x, y, width, height, parent=None):
super().__init__(parent)
self.ellipseItem = QGraphicsEllipseItem(x, y, width, height, self)
self.setAcceptHoverEvents(True)
def setRect(self, x, y, width, height):
self.ellipseItem = QGraphicsEllipseItem(x, y, width, height, self)
def hoverEnterEvent(self, event):
self.ellipseItem.setBrush(QBrush(Qt.green))
def hoverLeaveEvent(self, event):
self.ellipseItem.setBrush(QBrush(Qt.blue))
def mousePressEvent(self, event):
if event.button() == Qt.RightButton:
self.connectionRequested.emit(self)
self.moved.emit()
def boundingRect(self):
return self.ellipseItem.boundingRect()
def paint(self, painter, option, widget):
self.ellipseItem.paint(painter, option, widget)
if __name__ == '__main__':
app = QApplication([])
ex = App()
ex.show()
sys.exit(app.exec_())