Search code examples
c++qtqt-creatorfedoracgal

running draw_polygon example in CGAl package in qt widget application


I try to following CGAL example in Qt widget application : example

main.ccp:

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
     MainWindow w;
 w.show();

        return a.exec();
}

mainwindow.ccp :

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/draw_polyhedron.h>
#include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel  Kernel;
typedef CGAL::Polyhedron_3<Kernel>                       Polyhedron;

void MainWindow::on_pushButton_clicked()
{
   QString fileName = QFileDialog::getOpenFileName(this,tr("Open .off model"), "/home", tr("*.off"));

draw_poly(fileName);
 }

void MainWindow::draw_poly(QString fileName)
{
    QByteArray inBytes;
    const char *c;
     inBytes = fileName.toUtf8();
     c = inBytes.constData();
          std::ifstream input(c);

          if (!input || !(input >> mesh) || mesh.is_empty()) {
            std::cerr << "Not a valid off file." << std::endl;
         //   return 1;
          }

          input >> mesh;

          CGAL::draw(mesh);
}

when I ran it , it open dialog file to select .off file ,then it shows the following error:

QCoreApplication::exec: The event loop is already running

any help ,please ?


Solution

  • I'm using Qt5 in daily business, and once considered CGAL as possible application base (without going further into this direction – not yet). Hence, this question made me curious.

    I digged through the source code of CGAL on github and found out why the error message

    QCoreApplication::exec: The event loop is already running
    

    occurs.

    For this, I copied the relevant lines from CGAL on github: Polyhedron/include/CGAL/draw_polyhedron.h:

    template<class Polyhedron, class ColorFunctor>
    void draw(const Polyhedron& apoly,
              const char* title,
              bool nofill,
              const ColorFunctor& fcolor)
    {  
    #if defined(CGAL_TEST_SUITE)
      bool cgal_test_suite=true;
    #else
      bool cgal_test_suite=false;
    #endif
    
      if (!cgal_test_suite)
      {
        int argc=1;
        const char* argv[2]={"polyhedron_viewer","\0"};
        QApplication app(argc,const_cast<char**>(argv));
        SimplePolyhedronViewerQt<Polyhedron, ColorFunctor>
          mainwindow(app.activeWindow(), apoly, title, nofill, fcolor);
        mainwindow.show();
        app.exec();
      }
    }
    

    Looking at this source code, it becomes obvious that CGAL::draw() is a small ful-featured Qt application in itself which establishs its own QApplication instance. The OP in turn tried to embed the CGAL::draw() in her/his own Qt application. It is not allowed to instance any derivates of QCoreApplication more than once (according to Qt doc. of QApplication):

    For any GUI application using Qt, there is precisely one QApplication object, no matter whether the application has 0, 1, 2 or more windows at any given time.

    (Emphasizing not mine.)

    The CGAL doc. provides an (even shorter) example in Polyhedron/draw_polyhedron.cpp to do this right:

    #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
    #include <CGAL/Polyhedron_3.h>
    #include <CGAL/IO/Polyhedron_iostream.h>
    #include <CGAL/draw_polyhedron.h>
    #include <fstream>
    typedef CGAL::Exact_predicates_inexact_constructions_kernel  Kernel;
    typedef CGAL::Polyhedron_3<Kernel>                       Polyhedron;
    int main(int argc, char* argv[])
    {
      Polyhedron P;
      std::ifstream in1((argc>1)?argv[1]:"data/cross.off");
      in1 >> P;
      CGAL::draw(P);
      return EXIT_SUCCESS;
    }
    

    but there is no place to insert the QFileDialog at the right point.

    Hence, CGAL::draw() is the wrong tool for what OP (probably) intends to do – embed CGAL polyhedron rendering into a Qt application. For this, it is necessary to use the things directly which are called somewhere inside of CGAL::draw().

    So, this is what seems appropriate to me:
    making SimplePolyhedronViewerQt<Polyhedron, ColorFunctor> a (main or child) widget in OPs Qt application.

    I then walked a bit through the github repo to find out from which Qt widget CGAL::SimplePolyhedronViewerQt<Polyhedron, ColorFunctor> is actually derived from and found the following inheritance:

    CGAL::SimplePolyhedronViewerQt<Polyhedron, ColorFunctor>
                               |
                               V
                     CGAL::Basic_viewer_qt
                               |
                               V
                       CGAL::QGLViewer
                               |
                +--------------+--------------+
                |                             |
                V                             V
          QOpenGLWidget               QOpenGLFunctions
    

    So, CGAL::SimplePolyhedronViewerQt<Polyhedron, ColorFunctor> can be used like any QWidget (which involves making it the main window). It can become as well the center widget of a QMainWindow which gets a menu bar/tool bar with the QAction to open the QFileDialog, request a file path, open a file stream with this file path, and load a mesh from this file stream.

    There is another minor detail where I stumbled over: The CGAL::Polyhedron has to be given to the CGAL::SimplePolyhedronViewerQt in the constructor and by const reference. To consider this, it's IMHO necessary (after successful loading of mesh) to construct the CGAL::SimplePolyhedronViewerQt instance by new and set/add it to parent widget afterwards. If this is not acceptable it's probably necessary to go even deeper and replace the CGAL::SimplePolyhedronViewerQt by an own implementation, using the source code of the former as “cheat-sheet”.

    This is how such an application could look like:

    #include <fstream>
    
    #include <QtWidgets>
    
    #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
    #include <CGAL/Polyhedron_3.h>
    #include <CGAL/IO/Polyhedron_iostream.h>
    #include <CGAL/draw_polyhedron.h>
    
    typedef CGAL::Exact_predicates_inexact_constructions_kernel  Kernel;
    typedef CGAL::Polyhedron_3<Kernel>                       Polyhedron;
    
    int main(int argc, char **argv)
    {
      qDebug() << "Qt Version:" << QT_VERSION_STR;
      QApplication app(argc, argv);
      CGAL::DefaultColorFunctorPolyhedron fColor;
      Polyhedron mesh;
      // setup UI
      QMainWindow qWin;
      QToolBar qToolbar;
      QAction qCmdLoad(QString::fromUtf8("Load File..."));
      qToolbar.addAction(&qCmdLoad);
      qWin.addToolBar(&qToolbar);
      qWin.show();
      // install signal handlers
      QObject::connect(&qCmdLoad, &QAction::triggered,
        [&qWin, &mesh, &fColor]() {
          const QString filePath = QFileDialog::getOpenFileName(
            &qWin,
            QString::fromUtf8("Open .off model"),
            QString::fromUtf8("/home"),
            QString::fromUtf8("*.off"));
          if (filePath.isEmpty()) return;
          std::ifstream fIn(filePath.toUtf8().data());
          if (!(fIn >> mesh) || mesh.is_empty()) {
            qDebug() << "Loading of" << filePath << "failed!";
            return;
          }
          qWin.setCentralWidget(
            new CGAL::SimplePolyhedronViewerQt<Polyhedron, CGAL::DefaultColorFunctorPolyhedron>(
              &qWin, mesh, "Basic Polyhedron Viewer", false, fColor));
          qWin.centralWidget()->show();
        });
      // runtime loop
      return app.exec();
    }
    

    Please, take this with a “grain of salt” – I've no CGAL at hand and couldn't compile/test the above code.