How to snap to grid a QGraphicsTextItem?
To set new width and height of QGraphicsTextItem
you can overwrite boundingRect()
, to setPos()
you can use any appropiate event, MouseReleaseEvent
or MouseMoveEvent
for example, and calculate the next top-left grid-intersection from the last pos()
:
class CI(QGraphicsTextItem): def __init__(self, text, pos): QGraphicsTextItem.__init__(self) self.content = text self.setPlainText(text) self.setPos(pos) self.setTextInteractionFlags(Qt.TextEditorInteraction) self.setDefaultTextColor(Qt.black) self.setFlags( self.flags() | QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable) self.setZValue(-1) def boundingRect(self): # get the original width and height of boundingrect br = QGraphicsTextItem.boundingRect(self) brw = br.width() brh = br.height() # new width and height in "blocks" wblocks = int(brw/Settings.WIDTH) + 1 hblocks = int(brh/Settings.HEIGHT) + 1 w = wblocks*Settings.WIDTH h = hblocks*Settings.HEIGHT return QtCore.QRectF(0, 0, w, h) def mouseReleaseEvent(self, event): # QGraphicsItem.mouseReleaseEvent(self, event) QGraphicsTextItem.mouseReleaseEvent(self, event) self.setGridIntersection(self.pos()) def mouseMoveEvent(self, event): QGraphicsTextItem.mouseMoveEvent(self, event) self.setGridIntersection(self.pos()) def setGridIntersection(self, pos): # get the next grid intersection top left of items top-left corner grid_x = int(pos.x()/Settings.WIDTH) grid_y = int(pos.y()/Settings.HEIGHT) self.setPos(grid_x*Settings.WIDTH, grid_y*Settings.HEIGHT) def paint(self, painter, option, widget): painter.save() painter.setBrush(QBrush(Qt.white)) painter.drawRect(self.boundingRect()) painter.restore() super().paint(painter, option, widget) def keyPressEvent(self, event): super().keyPressEvent(event) self.content = self.toPlainText() def focusInEvent(self, event): self.scene().writing_comment = True # self.setPlainText(self.content) # print("Focusinevent") def focusOutEvent(self, event): self.scene().writing_comment = False # self.setHtml(self.content) # print("FocusOutevent") def contextMenuEvent(self, scme): super().contextMenuEvent(scme)
Edit 01.02.17:the solution above is only suitable for singleselections, for both single- and multiselections use:
class CI(QGraphicsTextItem): def __init__(self, text, pos): QGraphicsTextItem.__init__(self) self.content = text self.setPlainText(text) self.setPos(pos) self.setTextInteractionFlags(Qt.TextEditorInteraction) self.setDefaultTextColor(Qt.black) self.setFlags( self.flags() | QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable) self.setZValue(-1) self.setAcceptDrops(True) def boundingRect(self): # get the original width and height of boundingrect br = QGraphicsTextItem.boundingRect(self) brw = br.width() brh = br.height() # new width and height in "blocks" wblocks = int(brw / Settings.WIDTH) + 1 hblocks = int(brh / Settings.HEIGHT) + 1 w = wblocks * Settings.WIDTH h = hblocks * Settings.HEIGHT return QtCore.QRectF(0, 0, w, h) def setGridIntersection(self, pos): # get the next grid intersection top left of items top-left corner grid_x = int(pos.x() / Settings.WIDTH) grid_y = int(pos.y() / Settings.HEIGHT) self.setPos(grid_x * Settings.WIDTH, grid_y * Settings.HEIGHT) def paint(self, painter, option, widget): painter.save() painter.setBrush(QBrush(Qt.white)) painter.drawRect(self.boundingRect()) painter.restore() super().paint(painter, option, widget) def keyPressEvent(self, event): super().keyPressEvent(event) self.content = self.toPlainText() def focusInEvent(self, event): self.scene().writing_comment = True # self.setPlainText(self.content) # print("Focusinevent") def focusOutEvent(self, event): self.scene().writing_comment = False # self.setHtml(self.content) # print("FocusOutevent") def contextMenuEvent(self, scme): super().contextMenuEvent(scme)
and in class QS add:
def mouseMoveEvent(self, event): QtWidgets.QGraphicsScene.mouseMoveEvent(self, event) for i in self.selectedItems(): if type(i) == CI: i.setGridIntersection(i.pos())