Part 11: Loading from a file

Part 11: Loading from a file


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

Loading from an XML file

Here we enable the user to load simulation data from XML format files saved previously. We add a new File menu action "Open ...", use QFileDialog functionality to let the user select the file to be read, and use QXmlStreamReader functionality to read the XML file contents.

Enhancing the MainWindow

mainwindow.h

We need to add a public slot to receive the signal when user selects the new menu action.

  bool fileOpen();                    // load simulation file returning true if successful

mainwindow.cpp

Include the header file for the QXmlStreamReader functionality.

#include <QXmlStreamReader>

In the constructor just above were we previously created the save action, create and add the new open action to the File menu.

  QAction* openAction    = fileMenu->addAction( "&Open ...",         this, SLOT(fileOpen()) );

Set the keyboard shortcut.

  openAction->setShortcut( QKeySequence::Open );

Now add the code for the new public slot. In this slot we ask the user for the filename and location of the file, check we can read the file, and then process the file using QXmlStreamReader functionality. The simulation data is loaded into a new scene that is swapped with the old scene if no errors are encountered. First we look for the "qsimulate" element and then call a new scene method to read the remainder. The slot returns true if a XML file was successfully read, otherwise it returns false.

/************************************* fileOpen **************************************/

bool  MainWindow::fileOpen()
{
  // get user to select filename and location
  QString filename = QFileDialog::getOpenFileName();
  if ( filename.isEmpty() ) return false;

  // open the file and check we can read from it
  QFile file( filename );
  if ( !file.open( QIODevice::ReadOnly ) )
  {
    showMessage( QString("Failed to open '%1'").arg(filename) );
    return false;
  }

  // open an xml stream reader and load simulation data
  QXmlStreamReader  stream( &file );
  Scene*            newScene = new Scene( m_undoStack );
  while ( !stream.atEnd() )
  {
    stream.readNext();
    if ( stream.isStartElement() )
    {
      if ( stream.name() == "qsimulate" )
        newScene->readStream( &stream );
      else
        stream.raiseError( QString("Unrecognised element '%1'").arg(stream.name().toString()) );
    }
  }

  // check if error occured
  if ( stream.hasError() )
  {
    file.close();
    showMessage( QString("Failed to load '%1' (%2)").arg(filename).arg(stream.errorString()) );
    delete newScene;
    return false;
  }

  // close file, display new scene, delete old scene, and display useful message
  file.close();
  m_undoStack->clear();
  QGraphicsView*   view = dynamic_cast<QGraphicsView*>( centralWidget() );
  view->setScene( newScene );
  delete m_scene;
  m_scene = newScene;
  showMessage( QString("Loaded '%1'").arg(filename) );
  return true;
}

Enhancing the Scene

scene.h

Add a forward declaration for the QXmlStreamReader class.

class QXmlStreamReader;

Add the declaration for the new public scene method that reads the simulation data from a QXmlStreamReader.

  void  readStream( QXmlStreamReader* );                     // read scene data from xml stream

scene.cpp

Include the header file for QXmlStreamReader.

#include <QXmlStreamReader>

Add the code for our new scene method. This method creates a new scene station for each "station" element found in the XML stream and sets its x & y coordinates according to any XML attributes found.

/************************************ readStream *************************************/

void  Scene::readStream( QXmlStreamReader* stream )
{
  // read station data from xml stream
  while ( !stream->atEnd() )
  {
    stream->readNext();
    if ( stream->isStartElement() && stream->name() == "station" )
    {
      qreal x = 0.0, y = 0.0;
      foreach( QXmlStreamAttribute attribute, stream->attributes() )
      {
        if ( attribute.name() == "x" ) x = attribute.value().toString().toDouble();
        if ( attribute.name() == "y" ) y = attribute.value().toString().toDouble();
      }
      addItem( new Station( x, y ) );
    }
  }
}

Compile and run

The new code will automatically re-compile when you attempt to run the application. Play with the application and load some of the files you saved earlier.


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


Last updated 24-Jul-2008