chiefenne
chiefenne

Reputation: 605

How to make the bounding rect of a selected qgraphicsitem show automatically?

I found this example by @serge_gubenko in SO. Moving a QGraphicsItem around a central point in PyQt4

I did then some modifications to end up with: Why is my QGraphicsItem not selectable?

If I run the example (Moving a QGraphicsItem around a central point in PyQt4) and click on the graphics item, it automatically shows up with a dashed frame which indicates that it is selected. I prepared images to show the effect, but due to my low reputation I am not yet allowed to upload those ;)

To me it looks that this "is selected indication" by the dashed frame comes automatically somehow. In my modified example (Why is my QGraphicsItem not selectable?), this does not happen and I can't figure out why?

Upvotes: 2

Views: 8106

Answers (3)

kblst
kblst

Reputation: 519

Updated for PyQt5

import sys
from PyQt5 import QtGui, QtCore, QtWidgets

class GraphicsItem(QtWidgets.QGraphicsItem):
    """
     From the QT docs:
     To write your own graphics item, you first create a subclass
     of QGraphicsItem, and then start by implementing its two pure
     virtual public functions: boundingRect(), which returns an estimate
     of the area painted by the item, and paint(),
     which implements the actual painting.
    """
    # call constructor of GraphicsItem
    def __init__(self, rect, pen, brush, tooltip='No tip here', parent=None):
        # call constructor of QGraphicsItem
        super(GraphicsItem, self).__init__()

        self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsFocusable, True)

        self.setAcceptHoverEvents(True)

        self.pen = pen
        pw = self.pen.widthF()
        self.brush = QtGui.QBrush(QtCore.Qt.blue)
        self.brush = brush
        self.setToolTip(tooltip)
        self.parent = parent

        self.rect = QtCore.QRectF(rect[0], rect[1], rect[2], rect[3])
        self.focusrect = QtCore.QRectF(rect[0]-pw/2, rect[1]-pw/2,
                rect[2]+pw, rect[3]+pw)

    def mouseMoveEvent(self, event):
        # move object
        QtWidgets.QGraphicsItem.mouseMoveEvent(self, event)

    def mousePressEvent(self, event):
        # select object
        # set item as topmost in stack
        self.setZValue(self.parent.scene.items()[0].zValue() + 1)
        self.setSelected(True)
        QtWidgets.QGraphicsItem.mousePressEvent(self, event)

    def boundingRect(self):
        return self.rect

    def paint(self, painter, option, widget=None):
        painter.setBrush(self.brush)
        painter.setPen(self.pen)
        painter.drawEllipse(self.rect)
        if self.isSelected():
            self.drawFocusRect(painter)

    def drawFocusRect(self, painter):
        self.focusbrush = QtGui.QBrush()
        self.focuspen = QtGui.QPen(QtCore.Qt.DotLine)
        self.focuspen.setColor(QtCore.Qt.black)
        self.focuspen.setWidthF(1.5)
        painter.setBrush(self.focusbrush)
        painter.setPen(self.focuspen)
        painter.drawRect(self.focusrect)

    def hoverEnterEvent(self, event):
        self.pen.setStyle(QtCore.Qt.DotLine)
        QtWidgets.QGraphicsItem.hoverEnterEvent(self, event)

    def hoverLeaveEvent(self, event):
        self.pen.setStyle(QtCore.Qt.SolidLine)
        QtWidgets.QGraphicsItem.hoverLeaveEvent(self, event)


class MyMainWindow(QtWidgets.QMainWindow):
    # call constructor of MyMainWindow
    def __init__(self, parent=None):
        # call constructor of QMainWindow
        super(MyMainWindow, self).__init__(parent)

        w = 1000
        h = 800
        self.scene = QtWidgets.QGraphicsScene(-w/2, -h/2, w, h)

        self.view = QtWidgets.QGraphicsView()
        # set QGraphicsView attributes
        self.view.setRenderHints(QtGui.QPainter.Antialiasing |
            QtGui.QPainter.HighQualityAntialiasing)
        self.view.setViewportUpdateMode(QtWidgets.QGraphicsView.FullViewportUpdate)
        self.view.setScene(self.scene)

        # set central widget for the application
        self.setCentralWidget(self.view)

        # add items to the scene
        self.addGraphicsItem((0, 0, 250, 250), 8.0, (255, 0, 0), (0, 0, 255), 'My first item')
        self.addGraphicsItem((-250, -250, 300, 200), 4.0, (0, 0, 0), (255, 0, 100), 'My 2nd item')
        self.addGraphicsItem((200, -200, 200, 200), 10.0, (0, 0, 255), (0, 255, 100), 'My 3rd item')

    def addGraphicsItem(self, rect, pw, pc, bc, tooltip):
        pen = QtGui.QPen(QtCore.Qt.SolidLine)
        pen.setColor(QtGui.QColor(pc[0], pc[1], pc[2], 255))
        pen.setWidth(pw)
        brush = QtGui.QBrush(QtGui.QColor(bc[0], bc[1], bc[2], 255))
        item = GraphicsItem(rect, pen, brush, tooltip, self)
        self.scene.addItem(item)

    def mousePressEvent(self, event):
        #print 'from MainWindow'
        pass


def main():
    app = QtWidgets.QApplication(sys.argv)
    form = MyMainWindow()
    form.setGeometry(700, 100, 1050, 850)
    form.show()
    app.exec_()

if __name__ == '__main__':
    main()

Upvotes: 3

chiefenne
chiefenne

Reputation: 605

Here is a working example which has its own drawFocusRect method.

Focus is indicated in two ways:
1) By clicking on the Qgraphicsitem, then the bounding rect is drawn.
2) Hovering over the item. When fireing the hoverEnterEvent the pen style is changed to DotLine converting back to SolidLine when the hoverLeaveEvent is fired.

#!d:/python27/python -u

import sys
from PyQt4 import QtGui, QtCore

class GraphicsItem(QtGui.QGraphicsItem):
    """
     From the QT docs:
     To write your own graphics item, you first create a subclass
     of QGraphicsItem, and then start by implementing its two pure 
     virtual public functions: boundingRect(), which returns an estimate
     of the area painted by the item, and paint(), 
     which implements the actual painting.
    """
    # call constructor of GraphicsItem
    def __init__(self, rect, pen, brush, tooltip='No tip here', parent=None):
        # call constructor of QGraphicsItem
        super(GraphicsItem, self).__init__()

        self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QtGui.QGraphicsItem.ItemIsFocusable, True)

        self.setAcceptsHoverEvents(True)

        self.pen = pen
        pw = self.pen.widthF()
        self.brush = QtGui.QBrush(QtCore.Qt.blue)
        self.brush = brush
        self.setToolTip(tooltip)
        self.parent = parent

        self.rect = QtCore.QRectF(rect[0], rect[1], rect[2], rect[3])
        self.focusrect = QtCore.QRectF(rect[0]-pw/2, rect[1]-pw/2,
                rect[2]+pw, rect[3]+pw)

    def mouseMoveEvent(self, event):
        # move object
        QtGui.QGraphicsItem.mouseMoveEvent(self, event)

    def mousePressEvent(self, event):
        # select object
        # set item as topmost in stack
        self.setZValue(self.parent.scene.items()[0].zValue() + 1)
        self.setSelected(True)
        QtGui.QGraphicsItem.mousePressEvent(self, event)

    def boundingRect(self):
        return self.rect

    def paint(self, painter, option, widget):
        painter.setBrush(self.brush)
        painter.setPen(self.pen)
        painter.drawEllipse(self.rect)
        if self.isSelected():
            self.drawFocusRect(painter)

    def drawFocusRect(self, painter):
        self.focusbrush = QtGui.QBrush()
        self.focuspen = QtGui.QPen(QtCore.Qt.DotLine)
        self.focuspen.setColor(QtCore.Qt.black)
        self.focuspen.setWidthF(1.5)
        painter.setBrush(self.focusbrush)
        painter.setPen(self.focuspen)
        painter.drawRect(self.focusrect)

    def hoverEnterEvent(self, event):
        self.pen.setStyle(QtCore.Qt.DotLine)
        QtGui.QGraphicsItem.hoverEnterEvent(self, event)

    def hoverLeaveEvent(self, event):
        self.pen.setStyle(QtCore.Qt.SolidLine)
        QtGui.QGraphicsItem.hoverLeaveEvent(self, event)


class MyMainWindow(QtGui.QMainWindow):
    # call constructor of MyMainWindow
    def __init__(self, parent=None):
        # call constructor of QMainWindow
        super(MyMainWindow, self).__init__(parent)

        w = 1000
        h = 800
        self.scene = QtGui.QGraphicsScene(-w/2, -h/2, w, h)

        self.view = QtGui.QGraphicsView()
        # set QGraphicsView attributes
        self.view.setRenderHints(QtGui.QPainter.Antialiasing |
            QtGui.QPainter.HighQualityAntialiasing)
        self.view.setViewportUpdateMode(QtGui.QGraphicsView.FullViewportUpdate)
        self.view.setScene(self.scene)

        # set central widget for the application
        self.setCentralWidget(self.view)

        # add items to the scene
        self.addGraphicsItem((0, 0, 250, 250), 8.0, (255, 0, 0), (0, 0, 255), 'My first item')
        self.addGraphicsItem((-250, -250, 300, 200), 4.0, (0, 0, 0), (255, 0, 100), 'My 2nd item')
        self.addGraphicsItem((200, -200, 200, 200), 10.0, (0, 0, 255), (0, 255, 100), 'My 3rd item')

    def addGraphicsItem(self, rect, pw, pc, bc, tooltip):
        pen = QtGui.QPen(QtCore.Qt.SolidLine)
        pen.setColor(QtGui.QColor(pc[0], pc[1], pc[2], 255))
        pen.setWidth(pw)
        brush = QtGui.QBrush(QtGui.QColor(bc[0], bc[1], bc[2], 255))
        item = GraphicsItem(rect, pen, brush, tooltip, self)
        self.scene.addItem(item)

    def mousePressEvent(self, event):
        #print 'from MainWindow'
        pass

    def keyPressEvent(self, event):
        key = event.key()

        if key == QtCore.Qt.Key_Escape:
            sys.exit(QtGui.qApp.quit())
        else:
            super(GraphicsView, self).keyPressEvent(event)

def main():
    app = QtGui.QApplication(sys.argv)
    form = MyMainWindow()
    form.setGeometry(700, 100, 1050, 850)
    form.show()
    app.exec_()

if __name__ == '__main__':
    main()

Upvotes: 4

renz0fr
renz0fr

Reputation: 103

You use QtGui.QGraphicsItem, so you define the boundingRect and paint methods, where you used the painter drawEllipse method. In the first example you found, the class uses directly QtGui.QGraphicsEllipseItem and it does all the difference, because those methods are already defined. By the way I didn't find why the boundingRect is not drawn in your case.

Upvotes: 5

Related Questions