laura
laura

Reputation: 2135

Qt C++ QException issue : debug error

I want to read objects from an XML file and i need to handle 2 exceptions : when the file cannot be opened and when the file content cannot be loaded. (incorrectly formated) I have written the following function to read the content from the file and 2 clases for the exceptions. The problem is that when i run my application and i am trying to load a file with different format then XML, instead of a frindly message which informs that there is an exception, i have a debug error : abort() is called. What i am not doing well? reading function

QList<Vehicle> VehicleHelper::readVehicles(QString fileName){
QList<Vehicle> vehicles;
Vehicle newVehicle;
QFile file(fileName);
QDomDocument document;
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
    throw FileCannotBeOpenException();
}
else{
    if(!document.setContent(&file)){
        throw InvalidXMLContentException();
    }
    file.close();
}

QDomElement root = document.firstChildElement();
QDomNodeList carElements = root.elementsByTagName("Car");
for(int i = 0; i < carElements.size(); i++){

    QDomNode carNode = carElements.at(i);

    QDomElement carElement = carNode.toElement();
    QString carID = carElement.attribute("ID");

    //if the idNumber is null, generate one
    if(carID.isEmpty()){
        QUuid newId = Vehicle::generateID();
        newVehicle.setVehicleId(newId);
    }
    else {
        QUuid id;
        try{
            id = QUuid::QUuid(carID);
        } catch(QException &ex){
            continue;
        }


        newVehicle.setVehicleId(id);
    }
  }

the call of the function

void MainWindow::on_actionOpen_triggered()
{
if(isModified){
    QMessageBox msgBox;
    QString message = "There are unsaved changes! Do you proceed? ";
    msgBox.setWindowTitle("Save Changes");
    msgBox.setText(message);
    msgBox.addButton("Don't save",QMessageBox::NoRole);
    msgBox.addButton(QMessageBox::Save);
    msgBox.addButton(QMessageBox::Cancel);
    msgBox.setIcon(QMessageBox::Question);
    int result = msgBox.exec();
    if(result == QMessageBox::Save){
        VehicleHelper::writeVehicles(cars,filename);
        msgBox.close();
    } else {
        QString fileName = QFileDialog::getOpenFileName(this,"Open file");
        if(!fileName.isEmpty()){
            setFileName(fileName);
            QFile file(fileName);
            try {
                cars = VehicleHelper::readVehicles(fileName);
            } catch(FileCannotBeOpenException &ex) {
                QMessageBox msgBox;
                msgBox.setIcon(QMessageBox::Critical);
                msgBox.setWindowTitle("Message!");
                msgBox.setText("Failed to open file");
                msgBox.setDefaultButton(QMessageBox::Ok);
                msgBox.exec();
                return;

            } catch(InvalidXMLContentException &ex){
                QMessageBox msgBox;
                msgBox.setIcon(QMessageBox::Critical);
                msgBox.setWindowTitle("Message!");
                msgBox.setText("Failed to load data!");
                msgBox.setDefaultButton(QMessageBox::Ok);
                msgBox.exec();
                return;
            }

            setTakenNumbersList(cars);
            //set data to the table view
            populate(cars);
       }
    }
 } else {
    QString fileName = QFileDialog::getOpenFileName(this,"Open file");
    if(!fileName.isEmpty()){
        setFileName(fileName);
        QFile file(fileName);
        if(file.open(QFile::ReadOnly | QFile::Text)){
            cars = VehicleHelper::readVehicles(fileName);
            setTakenNumbersList(cars);
            file.close();

            //set data to the table view
            populate(cars);

            ui->actionAdd->setEnabled(true);
            ui->actionBy_name->setEnabled(true);
            ui->actionBy_registration_date->setEnabled(true);
            ui->actionBy_registration_number->setEnabled(true);
            ui->actionBy_revision_date->setEnabled(true);
            ui->actionBy_type->setEnabled(true);
            ui->actionClear_Search->setEnabled(true);
            ui->actionDelete->setEnabled(true);
            ui->actionEdit->setEnabled(true);
            ui->actionSave->setEnabled(true);
            ui->actionSave_As->setEnabled(true);
        }
     }

    }
 }

main

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
try{
    w.show();
} catch(QException &ex){

}
return a.exec();
}

Upvotes: 1

Views: 2253

Answers (1)

The exceptions are thrown from code that runs in the event loop, specifically they will reach the QCoreApplication::notify(). This one is indirectly called from a.exec(), via an intervening operating system event loop call.

Thus you can't merely wrap a.exec() in a try/catch. You must reimplement QCoreApplication::notify() as follows:

class MyApplication : public QApplication
{
public:
   MyApplication(int & c, char ** a) : QApplication(c,a) {}
   virtual bool notify(QObject * obj, QEvent * ev) {
      bool rc = true;
      try {
         rc = QApplication::notify(obj, ev);
      }
      catch (QException &ex) {
         ...
      }
      return rc;
   }
};

int main(...) {
   MyApplication app(...);
   ...
}

Another problem with your code is that it screams to use the State Machine Framework. You should have states that represent file being open and closed, and intermediate states when e.g. an "unsaved changes" dialog box is shown. Then you won't have the ui->actionXYZ->setEnabled(true) littered around the code. Assuming you have a fileOpen state, you'd have

fileOpen->assignProperty(ui->actionBy_name, "setEnabled", true);
...

Then when the file is open, you emit a signal that is attached to a signal transition in the state machine. The state machine will do the rest - it will enable/disable actions for you, etc.

Upvotes: 2

Related Questions