I have created a custom QGraphicsWidget with the ability to resize the widget in the scene. I can also add predefined widgets such as buttons, labels, etc. to my custom widget. I now have two problems.
The first being that the widget doesn't change the size (to re-adjust) upon inserting a new label
or LineEdit
widget as a result newly inserted widget stays out of the custom widget border.
The second problem is encountered when I try to change the setContentMargins
of the QGraphicsLayout
to something other than 0. For example QGraphicsLayout.setContentMargins(1, 1, 1, 20)
will delay the cursor in the LineEdit
widget.
Here is the image.
(Drag the grey triangle to change size)
import sys
from PyQt5 import QtWidgets, QtCore, QtGui, Qt
from PyQt5.QtCore import Qt, QRectF, QPointF
from PyQt5.QtGui import QBrush, QPainterPath, QPainter, QColor, QPen, QPixmap
from PyQt5.QtWidgets import QGraphicsRectItem, QApplication, QGraphicsView, QGraphicsScene, QGraphicsItem
class Container(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.layout = QtWidgets.QVBoxLayout(self)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setSpacing(0)
self.setStyleSheet('Container{background:transparent;}')
class GraphicsFrame(QtWidgets.QGraphicsWidget):
def __init__(self, *args, **kwargs):
super(GraphicsFrame, self).__init__()
x, y, h, w = args
rect = QRectF(x, y, h, w)
self.setGeometry(rect)
self.setMinimumSize(150, 150)
self.setMaximumSize(400, 800)
self.setAcceptHoverEvents(True)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
self.setFlag(QGraphicsItem.ItemIsFocusable, True)
self.mousePressPos = None
self.mousePressRect = None
self.handleSelected = None
self.polygon = QtGui.QPolygon([
QtCore.QPoint(int(self.rect().width()-10), int(self.rect().height()-20)),
QtCore.QPoint(int(self.rect().width()-10), int(self.rect().height()-10)),
QtCore.QPoint(int(self.rect().width()-20), int(self.rect().height()-10))
])
graphic_layout = QtWidgets.QGraphicsLinearLayout(Qt.Vertical, self)
graphic_layout.setContentsMargins(0, 0, 0, 20) # changing this will cause the second problem
self.container = Container()
proxyWidget = QtWidgets.QGraphicsProxyWidget(self)
proxyWidget.setWidget(self.container)
graphic_layout.addItem(proxyWidget)
self.contentLayout = QtWidgets.QFormLayout()
self.contentLayout.setContentsMargins(10, 10, 20, 20)
self.contentLayout.setSpacing(5)
self.container.layout.addLayout(self.contentLayout)
self.options = []
def addOption(self, color=Qt.white, lbl=None, widget=None):
self.insertOption(-1, lbl, widget, color)
def insertOption(self, index, lbl, widget, color=Qt.white):
if index < 0:
index = self.contentLayout.count()
self.contentLayout.addRow(lbl, widget)
self.options.insert(index, (widget, color))
def update_polygon(self):
self.polygon = QtGui.QPolygon([
QtCore.QPoint(int(self.rect().width() - 10), int(self.rect().height() - 20)),
QtCore.QPoint(int(self.rect().width() - 10), int(self.rect().height() - 10)),
QtCore.QPoint(int(self.rect().width() - 20), int(self.rect().height() - 10))
])
def hoverMoveEvent(self, event):
if self.polygon.containsPoint(event.pos().toPoint(), Qt.OddEvenFill):
self.setCursor(Qt.SizeFDiagCursor)
else:
self.unsetCursor()
super(GraphicsFrame, self).hoverMoveEvent(event)
def mousePressEvent(self, event):
self.handleSelected = self.polygon.containsPoint(event.pos().toPoint(), Qt.OddEvenFill)
if self.handleSelected:
self.mousePressPos = event.pos()
self.mousePressRect = self.boundingRect()
super(GraphicsFrame, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self.handleSelected:
self.Resize(event.pos())
else:
super(GraphicsFrame, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
super(GraphicsFrame, self).mouseReleaseEvent(event)
self.handleSelected = False
self.mousePressPos = None
self.mousePressRect = None
self.update()
def paint(self, painter, option, widget):
painter.save()
painter.setBrush(QBrush(QColor(37, 181, 247)))
pen = QPen(Qt.white)
pen.setWidth(2)
if self.isSelected():
pen.setColor(Qt.yellow)
painter.setPen(pen)
painter.drawRoundedRect(self.rect(), 4, 4)
painter.setPen(QtCore.Qt.white)
painter.setBrush(QtCore.Qt.gray)
painter.drawPolygon(self.polygon)
super().paint(painter, option, widget)
painter.restore()
def Resize(self, mousePos):
"""
Perform shape interactive resize.
"""
if self.handleSelected:
self.prepareGeometryChange()
width, height = self.geometry().width()+(mousePos.x()-self.mousePressPos.x()),\
self.geometry().height()+(mousePos.y()-self.mousePressPos.y())
self.setGeometry(QRectF(self.geometry().x(), self.geometry().y(), width, height))
self.contentLayout.setGeometry(QtCore.QRect(0, 30, width-10, height-20))
self.mousePressPos = mousePos
self.update_polygon()
self.updateGeometry()
def main():
app = QApplication(sys.argv)
grview = QGraphicsView()
scene = QGraphicsScene()
grview.setViewportUpdateMode(grview.FullViewportUpdate)
scene.addPixmap(QPixmap('01.png'))
grview.setScene(scene)
item = GraphicsFrame(0, 0, 300, 150)
scene.addItem(item)
item.addOption(Qt.green, lbl=QtWidgets.QLabel('I am a label'), widget=QtWidgets.QLineEdit())
item.addOption(lbl=QtWidgets.QLabel('why'), widget=QtWidgets.QLineEdit())
item.addOption(lbl=QtWidgets.QLabel('How'), widget=QtWidgets.QLineEdit())
item.addOption(lbl=QtWidgets.QLabel('Nooo.'), widget=QtWidgets.QLineEdit())
item.addOption(lbl=QtWidgets.QLabel('Nooo.'), widget=QtWidgets.QLineEdit())
item.addOption(lbl=QtWidgets.QLabel('Nooo.'), widget=QtWidgets.QLineEdit())
item2 = GraphicsFrame(50, 50, 300, 150)
scene.addItem(item2)
grview.fitInView(scene.sceneRect(), Qt.KeepAspectRatio)
grview.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()