Part 7: Moving and deleting items

Part 7: Moving and deleting items


[<< Contents] [<< Prev] [Next >>]

Moving and deleting items on a Graphics View scene

Here we are going to enhance our application to allow the user to move and delete previously placed radio stations. The user will be able to select a station by pointing at it with the mouse cursor and pressing the left button, while holding the button down move the station by moving the mouse, and releasing the station by releasing the mouse button. To allow the user to delete a station we will provide a context menu when the user presses the right button while pointing at a station.

To implement this functionality we will enhance our Scene class by implementing the inherited QGraphicsScene mouseMoveEvent, mouseReleaseEvent and contextMenuEvent virtual methods.

Enhancing the Scene

scene.h

Add forward declarations for the classes QGraphicsSceneContextMenuEvent and Station.

class QGraphicsSceneContextMenuEvent;
class Station;

After the mousePressEvent declaration add the following lines to declare the mouseMoveEvent, mouseReleaseEvent and contextMenuEvent methods plus some private variables. The private variables will be used to track which station is being moved and where from.

  void  mouseMoveEvent( QGraphicsSceneMouseEvent* );         // receive mouse move events
  void  mouseReleaseEvent( QGraphicsSceneMouseEvent* );      // receive mouse release events
  void  contextMenuEvent( QGraphicsSceneContextMenuEvent* ); // receive context menu events

private:
  Station*     m_movingStation;             // station being moved (otherwise zero )
  qreal        m_movingFromX;               // station being moved original x coord
  qreal        m_movingFromY;               // station being moved original y coord

scene.cpp

Include the QGraphicsSceneContextMenuEvent, QMenu and QAction class header files.

#include <QGraphicsSceneContextMenuEvent>
#include <QMenu>
#include <QAction>

Inside the constructor initialise the m_movingStation private variable to indicate no station currently being moved.

  // initialise variables
  m_movingStation = 0;

In the mousePressEvent method replace the lines after checking which button was pressed up to emitting the message with the new lines below. By using the inherited itemAt method this will check if the user is pointing at a station. If the user is pointing at a station the code records the starting position and returns, otherwise adds a new station to the scene.

  // if existing station clicked, move it, otherwise create new
  qreal  x = event->scenePos().x();
  qreal  y = event->scenePos().y();
  m_movingStation = dynamic_cast<Station*>( itemAt( x, y ) );
  if ( m_movingStation )
  {
    m_movingFromX = m_movingStation->x();
    m_movingFromY = m_movingStation->y();
    return;
  }

  // create new Station at point where user clicked scene
  addItem( new Station( x, y ) );

Add the code for the new method mouseMoveEvent. This code will be call everytime the mouse moves over the scene and if a station is being moved will move that station to the new mouse pointer position.

/********************************** mouseMoveEvent ***********************************/

void  Scene::mouseMoveEvent( QGraphicsSceneMouseEvent* event )
{
  // when moving a station, move it to new mouse position
  qreal  x = event->scenePos().x();
  qreal  y = event->scenePos().y();
  if ( m_movingStation ) m_movingStation->setPos( x, y );
}

Add the code for the new method mouseReleaseEvent. When the user releases the button, we need to set the m_movingStation variable back to zero and emit a message.

/********************************* mouseReleaseEvent *********************************/

void  Scene::mouseReleaseEvent( QGraphicsSceneMouseEvent* event )
{
  // if moving a station, release it when mouse button released
  if ( m_movingStation )
  {
    qreal  x = event->scenePos().x();
    qreal  y = event->scenePos().y();
    m_movingStation = 0;
    emit message( QString("Station moved from %1,%2 to %3,%4")
             .arg(m_movingFromX).arg(m_movingFromY).arg(x).arg(y) );
  }
}

Add the code for the new method contextMenuEvent. When the user presses the right mouse button over the scene this routine is called. We only want to display a context menu if currently not moving a station and user is pointing at a station. We then create a menu with just one option to delete the station. If the user selects this option we remove the station from the scene, delete the station from memory and emit a useful message.

/********************************* contextMenuEvent **********************************/

void  Scene::contextMenuEvent( QGraphicsSceneContextMenuEvent* event )
{
  // we only want to display a menu if not currently moving a station
  if ( m_movingStation ) return;

  // we only want to display a menu if station where user clicked scene
  qreal     x       = event->scenePos().x();
  qreal     y       = event->scenePos().y();
  Station*  station = dynamic_cast<Station*>( itemAt( x, y ) );
  if ( station == 0 ) return;

  // display context menu and action accordingly
  QMenu     menu;
  QAction*  deleteAction = menu.addAction("Delete Station");
  if ( menu.exec( event->screenPos() ) == deleteAction )
  {
    removeItem( station );
    delete station;
    emit message( QString("Station deleted at %1,%2").arg(x).arg(y) );
  }
}

Compile and run

The new code will automatically re-compile when you attempt to run the application. Play with the application adding, moving and deleting stations.


[<< Contents] [<< Prev] [Next >>]


Last updated 05-May-2008