Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Snapping a QGraphicsItemGroup to a grid

Snapping a QGraphicsItemGroup to a grid

Scheduled Pinned Locked Moved Unsolved Qt for Python
1 Posts 1 Posters 472 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • W Offline
    W Offline
    WieJ
    wrote on last edited by
    #1

    I've been trying to create a subclass of QGraphicsItemGroup which will snap to a grid on a QGraphicsScene, but I've been having problems with the movement being overly jumpy. Below is a MWE:

    from os import environ
    
    environ["QT_ENABLE_HIGHDPI_SCALING"] = "0"
    
    from PyQt6.QtWidgets import *
    from PyQt6.QtCore import *
    from PyQt6.QtGui import *
    
    app = QApplication([])
    
    class Location(QGraphicsItem):
        color = QColor(0, 110, 200, 75)
        border_color = QColor(0, 255, 0, 255)
    
        def __init__(self, size, **kwargs):
            super().__init__(**kwargs)
            self.width, self.height = size
            
        def boundingRect(self):
            return QRectF(0, 0, 32*self.width, 32*self.height)
    
        def paint(self, *args):
            painter = args[0]
            painter.fillRect(1, 1, int(32*self.width) - 2, int(32*self.height) - 2, Location.color)
            
            pen = QPen(Location.border_color)
            pen.setWidth(0)
            painter.setPen(pen)
            painter.drawRect(0, 0, int(32*self.width) - 1, int(32*self.height) - 1)
            
        def x(self):
            """Returns the x-coordinate of the location's position."""
            return int(self.pos().x())
        
        def y(self):
            """Returns the y-coordinate of the location's position."""
            return int(self.pos().y())
    
    class GridSnappingQGIG(QGraphicsItemGroup):
    
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable, True)
            self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemSendsGeometryChanges, True)
    
        def itemChange(self, change, value):
            scene = self.scene()
            if change == QGraphicsItem.GraphicsItemChange.ItemPositionChange and scene:
                x, y = scene.snap_to_grid(value.x(), value.y(), scene.grid_size)
                return QPointF(x, y)
            return super().itemChange(change, value)
            
    class Scene(QGraphicsScene):
    
        def __init__(self, *args):
            super().__init__(*args)
            self.grid_size = 32
            self.locations = []
    
        def snap_to_grid(self, x, y, grid_size):
            return [grid_size * round(x / grid_size), grid_size * round(y / grid_size)]
                    
        def place_location(self, x, y, size):
            """Places a location of dimensions size at position (x, y)."""
            loc = Location(size)
            self.locations.append(loc)
            self.addItem(loc)
            loc.setPos(x, y)
                    
        def move_locations(self, locations):
            """Puts the locations in the input list in a GridSnappingQGIG to move them."""
            x, y = min([loc.x() for loc in locations]), min([loc.y() for loc in locations])
            group = GridSnappingQGIG()
            self.addItem(group)
            group.setPos(x, y)
            
            for loc in locations:
                group.addToGroup(loc)
                    
        def mousePressEvent(self, event):
            self.move_locations(self.locations)
            QGraphicsScene.mousePressEvent(self, event)
            
        def mouseReleaseEvent(self, event):
            group = self.locations[0].group()
            for loc in self.locations:
                group.removeFromGroup(loc)
            self.removeItem(group)
            QGraphicsScene.mouseReleaseEvent(self, event)
    
    scene = Scene(0, 0, 480, 480)
    scene.setBackgroundBrush(QColor(0, 0, 0))
    
    scene.place_location(128, 128, [2, 2])
    scene.place_location(64, 64, [2, 2])
    
    frame = QFrame()
    window = QMainWindow()
    view = QGraphicsView(scene)
    
    layout = QVBoxLayout()
    layout.addWidget(view)
    frame.setLayout(layout)
    
    window.setCentralWidget(frame)
    window.showMaximized()    
    app.exec()
    

    The Location class is the type of object I'm interested in moving around (in groups) on the scene. To accomplish this, when the mouse is pressed, I form a GridSnappingQGIG to house the locations, which is then destroyed when the mouse is released. The snapping is achieved by reimplementing the itemChange function of GridSnappingQGIG.

    The problem is that the group often seems to jump wildly past the position of the mouse cursor. The behavior doesn't seem very consistent, so it's hard to pin down what the problem is. The very first movement seems to always work as expected, and sudden mouse movements seem more likely to trigger the problem.

    I asked this question here on the StackExchange, and it was recommended that I make a bug report, but I figured I'd ask here first to see if there's something I've messed up.

    1 Reply Last reply
    0

    • Login

    • Login or register to search.
    • First post
      Last post
    0
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Get Qt Extensions
    • Unsolved