changeset 276:5de5699ad570 am-2

first display in QCairoWidget; no mouse interaction yet
author am@osimis.io
date Mon, 27 Aug 2018 12:21:52 +0200
parents 58e23e0dd86a
children a38465cc909f
files Applications/Generic/BasicNativeApplication.cpp Applications/Generic/BasicNativeApplication.h Applications/Qt/BasicQtApplication.cpp Applications/Qt/BasicQtApplication.h Applications/Qt/QCairoWidget.cpp Applications/Qt/QCairoWidget.h Applications/Samples/Qt/MainWindow.cpp Applications/Samples/Qt/MainWindow.h Applications/Samples/Qt/MainWindow.ui Applications/Samples/SampleMainNative.cpp Applications/Sdl/BasicSdlApplication.cpp Applications/Sdl/BasicSdlApplication.h Platforms/Generic/CMakeLists.txt Resources/CMake/OrthancStoneConfiguration.cmake Resources/CMake/QtConfiguration.cmake
diffstat 15 files changed, 720 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Generic/BasicNativeApplication.cpp	Fri Aug 24 13:58:06 2018 +0200
+++ b/Applications/Generic/BasicNativeApplication.cpp	Mon Aug 27 12:21:52 2018 +0200
@@ -74,7 +74,26 @@
      ******************************************************************/
 
     boost::program_options::options_description options;
+
+    { // generic options
+      boost::program_options::options_description generic("Generic options");
+      generic.add_options()
+          ("help", "Display this help and exit")
+          ("verbose", "Be verbose in logs")
+          ("orthanc", boost::program_options::value<std::string>()->default_value("http://localhost:8042/"),
+          "URL to the Orthanc server")
+          ("username", "Username for the Orthanc server")
+          ("password", "Password for the Orthanc server")
+          ("https-verify", boost::program_options::value<bool>()->default_value(true), "Check HTTPS certificates")
+          ;
+
+      options.add(generic);
+    }
+
+    // platform specific options
     DeclareCommandLineOptions(options);
+    
+    // application specific options
     application.DeclareStartupOptions(options);
 
     boost::program_options::variables_map parameters;
@@ -123,35 +142,8 @@
       Orthanc::Logging::EnableInfoLevel(true);
     }
 
-    if (!parameters.count("width") ||
-        !parameters.count("height") ||
-        !parameters.count("opengl"))
-    {
-      LOG(ERROR) << "Parameter \"width\", \"height\" or \"opengl\" is missing";
-      return -1;
-    }
+    ParseCommandLineOptions(parameters);
 
-    int w = parameters["width"].as<int>();
-    int h = parameters["height"].as<int>();
-    if (w <= 0 || h <= 0)
-    {
-      LOG(ERROR) << "Parameters \"width\" and \"height\" must be positive";
-      return -1;
-    }
-
-    unsigned int width = static_cast<unsigned int>(w);
-    unsigned int height = static_cast<unsigned int>(h);
-    LOG(WARNING) << "Initial display size: " << width << "x" << height;
-
-    bool opengl = parameters["opengl"].as<bool>();
-    if (opengl)
-    {
-      LOG(WARNING) << "OpenGL is enabled, disable it with option \"--opengl=off\" if the application crashes";
-    }
-    else
-    {
-      LOG(WARNING) << "OpenGL is disabled, enable it with option \"--opengl=on\" for best performance";
-    }
 
     bool success = true;
     try
@@ -220,8 +212,7 @@
        * Run the application
        ****************************************************************/
 
-      Run(context, title, width, height, opengl);
-
+      Run(context, title, argc, argv);
 
       /****************************************************************
        * Finalize the application
--- a/Applications/Generic/BasicNativeApplication.h	Fri Aug 24 13:58:06 2018 +0200
+++ b/Applications/Generic/BasicNativeApplication.h	Mon Aug 27 12:21:52 2018 +0200
@@ -42,7 +42,9 @@
 
     virtual void Initialize() = 0;
     virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) = 0;
-    virtual void Run(BasicNativeApplicationContext& context, const std::string& title, unsigned int width, unsigned int height, bool enableOpenGl) = 0;
+    virtual void ParseCommandLineOptions(const boost::program_options::variables_map& parameters) = 0;
+
+    virtual void Run(BasicNativeApplicationContext& context, const std::string& title, int argc, char* argv[]) = 0;
     virtual void Finalize() = 0;
   };
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Qt/BasicQtApplication.cpp	Mon Aug 27 12:21:52 2018 +0200
@@ -0,0 +1,68 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2018 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#if ORTHANC_ENABLE_QT != 1
+#error this file shall be included only with the ORTHANC_ENABLE_QT set to 1
+#endif
+
+#include "BasicQtApplication.h"
+#include <boost/program_options.hpp>
+#include <QApplication>
+
+#include "../../Framework/Toolbox/MessagingToolbox.h"
+
+#include <Core/Logging.h>
+#include <Core/HttpClient.h>
+#include <Core/Toolbox.h>
+#include <Plugins/Samples/Common/OrthancHttpConnection.h>
+#include "../../Platforms/Generic/OracleWebService.h"
+#include "../../Applications/Samples/Qt/MainWindow.h"
+
+
+namespace OrthancStone
+{
+  void BasicQtApplication::Initialize()
+  {
+  }
+
+  void BasicQtApplication::DeclareCommandLineOptions(boost::program_options::options_description& options)
+  {
+  }
+
+  void BasicQtApplication::Run(BasicNativeApplicationContext& context, const std::string& title, int argc, char* argv[])
+  {
+    context.Start();
+
+    QApplication app(argc, argv);
+    MainWindow window(context);
+
+    window.show();
+    app.exec();
+
+    context.Stop();
+  }
+
+  void BasicQtApplication::Finalize()
+  {
+  }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Qt/BasicQtApplication.h	Mon Aug 27 12:21:52 2018 +0200
@@ -0,0 +1,42 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2018 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Generic/BasicNativeApplication.h"
+
+#if ORTHANC_ENABLE_QT != 1
+#error this file shall be included only with the ORTHANC_ENABLE_QT set to 1
+#endif
+
+namespace OrthancStone
+{
+  class BasicQtApplication : public BasicNativeApplication
+  {
+  public:
+    virtual void Initialize();
+    virtual void DeclareCommandLineOptions(boost::program_options::options_description& options);
+    virtual void ParseCommandLineOptions(const boost::program_options::variables_map& parameters) {}
+    virtual void Run(BasicNativeApplicationContext& context, const std::string& title, int argc, char* argv[]);
+    virtual void Finalize();
+  };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Qt/QCairoWidget.cpp	Mon Aug 27 12:21:52 2018 +0200
@@ -0,0 +1,257 @@
+#include "QCairoWidget.h"
+
+#include <QPainter>
+#include <QPaintEvent>
+
+#include <stdexcept>
+
+QCairoWidget::QCairoWidget(QWidget *parent) :
+  QWidget(parent),
+  context_(NULL),
+  isMouseOver_(false),
+  mouseOverX_(0),
+  mouseOverY_(0)
+{
+  setMouseTracking(true);
+  updateNeeded_ = true;
+}
+
+
+
+QCairoWidget::~QCairoWidget()
+{
+}
+
+
+void QCairoWidget::paintEvent(QPaintEvent* event)
+{
+  QPainter painter(this);
+
+  if (image_.get() != NULL && context_ != NULL)
+  {
+    OrthancStone::BasicNativeApplicationContext::GlobalMutexLocker locker(*context_);
+    OrthancStone::IViewport& vp = context_->GetCentralViewport();
+    Orthanc::ImageAccessor a = surface_.GetAccessor();
+    vp.Render(a);
+    //image_->fill(0);
+    painter.drawImage(0, 0, *image_);
+  }
+  else
+  {
+    painter.fillRect(rect(), Qt::red);
+  }
+}
+
+
+//static void GetPixelCenter(double& x,
+//                           double& y,
+//                           const QMouseEvent* event)
+//{
+//  x = static_cast<double>(event->x()) + 0.5;
+//  y = static_cast<double>(event->y()) + 0.5;
+//}
+
+
+//void QCairoWidget::mousePressEvent(QMouseEvent* event)
+//{
+//  if (mouseTracker_.get() == NULL)
+//  {
+//    OrthancStone::MouseButton button;
+
+//    switch (event->button())
+//    {
+//      case Qt::LeftButton:
+//        button = OrthancStone::MouseButton_Left;
+//        break;
+
+//      case Qt::RightButton:
+//        button = OrthancStone::MouseButton_Right;
+//        break;
+
+//      case Qt::MiddleButton:
+//        button = OrthancStone::MouseButton_Middle;
+//        break;
+
+//      default:
+//        return;  // Unsupported button
+//    }
+
+//    double x, y;
+//    GetPixelCenter(x, y, event);
+////TODO    mouseTracker_.reset(viewport_.CreateMouseTracker(*scene_, button, x, y));
+//    repaint();
+//  }
+//}
+
+
+//void QCairoWidget::mouseReleaseEvent(QMouseEvent* event)
+//{
+//  if (mouseTracker_.get() != NULL)
+//  {
+//    mouseTracker_->MouseUp();
+//    mouseTracker_.reset(NULL);
+//    repaint();
+//  }
+//}
+
+
+//void QCairoWidget::mouseMoveEvent(QMouseEvent* event)
+//{
+//  if (mouseTracker_.get() == NULL)
+//  {
+//    if (rect().contains(event->pos()))
+//    {
+//      // Mouse over widget
+//      isMouseOver_ = true;
+//      mouseOverX_ = event->x();
+//      mouseOverY_ = event->y();
+//    }
+//    else
+//    {
+//      // Mouse out of widget
+//      isMouseOver_ = false;
+//    }
+//  }
+//  else
+//  {
+//    double x, y;
+//    GetPixelCenter(x, y, event);
+//    mouseTracker_->MouseMove(x, y);
+//    isMouseOver_ = false;
+//  }
+
+////  if (isMouseOver_)
+////  {
+////    UpdateMouseCoordinates(event->x(), event->y());
+////  }
+
+//  repaint();
+//}
+
+
+//void QCairoWidget::wheelEvent(QWheelEvent * event)
+//{
+//  if (mouseTracker_.get() == NULL)
+//  {
+//#if 0
+//    double x = static_cast<double>(event->x()) + 0.5;
+//    double y = static_cast<double>(event->y()) + 0.5;
+//    mouseTracker_.reset(viewport_.CreateMouseTracker(*scene_, MouseButton_Middle, x, y));
+
+//    switch (event->orientation())
+//    {
+//      case Qt::Horizontal:
+//        x += event->delta();
+//        break;
+
+//      case Qt::Vertical:
+//        y += event->delta();
+//        break;
+
+//      default:
+//        break;
+//    }
+
+//    mouseTracker_->MouseMove(x, y);
+//    mouseTracker_->MouseUp();
+//    mouseTracker_.reset(NULL);
+
+//    repaint();
+//#else
+//    if (event->orientation() == Qt::Vertical)
+//    {
+//      unsigned int slice = scene_->GetCurrentSlice();
+
+//      if (event->delta() < 0)
+//      {
+//        if (slice > 0)
+//        {
+//          scene_->SetCurrentSlice(slice - 1);
+//          emit SliceChanged(slice - 1);
+//        }
+//      }
+//      else
+//      {
+//        if (slice + 1 < scene_->GetSlicesCount())
+//        {
+//          scene_->SetCurrentSlice(slice + 1);
+//          emit SliceChanged(slice + 1);
+//        }
+//      }
+
+//      repaint();
+//    }
+//#endif
+//  }
+
+//  UpdateMouseCoordinates(event->x(), event->y());
+//}
+
+
+
+void QCairoWidget::resizeEvent(QResizeEvent* event)
+{
+  grabGesture(Qt::PanGesture);
+  QWidget::resizeEvent(event);
+
+  if (event)
+  {
+    surface_.SetSize(event->size().width(), event->size().height());
+
+    image_.reset(new QImage(reinterpret_cast<uchar*>(surface_.GetBuffer()),
+                            event->size().width(), 
+                            event->size().height(),
+                            surface_.GetPitch(),
+                            QImage::Format_RGB32));
+
+    context_->GetCentralViewport().SetSize(event->size().width(), event->size().height());
+
+  }
+}
+
+//void QCairoWidget::UpdateMouseCoordinates(int x,
+//                                         int y)
+//{
+//  if (scene_ != NULL)
+//  {
+//    double cx = static_cast<double>(x) + 0.5;
+//    double cy = static_cast<double>(y) + 0.5;
+
+//    std::string coordinates;
+//    viewport_.FormatCoordinates(coordinates, *scene_, cx, cy);
+        
+//    emit PositionChanged(QString(coordinates.c_str()));
+//  }
+//}
+
+
+//void QCairoWidget::SetDefaultView()
+//{
+//  viewport_.SetDefaultView();
+//  repaint();
+//}
+
+
+//void QCairoWidget::Refresh()
+//{
+//  if (scene_ != NULL &&
+//      updateNeeded_)
+//  {
+//    updateNeeded_ = false;
+//    emit ContentChanged();
+//    repaint();
+//  }
+//}
+
+
+//void QCairoWidget::SetSlice(int index)
+//{
+//  if (scene_ != NULL &&
+//      index >= 0 &&
+//      index < static_cast<int>(scene_->GetSlicesCount()))
+//  {
+//    scene_->SetCurrentSlice(index);
+//    emit SliceChanged(index);
+//    repaint();
+//  }
+//}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Qt/QCairoWidget.h	Mon Aug 27 12:21:52 2018 +0200
@@ -0,0 +1,92 @@
+#pragma once
+
+#include "../../Framework/Widgets/CairoWidget.h"
+#include "../../Applications/Generic/BasicNativeApplicationContext.h"
+#include "../../Framework/Viewport/CairoSurface.h"
+
+#include <QWidget>
+#include <QGestureEvent>
+#include <memory>
+#include <cassert>
+
+class QCairoWidget : public QWidget
+{
+  Q_OBJECT
+
+private:
+  std::auto_ptr<QImage>         image_;
+  OrthancStone::CairoSurface   surface_;
+  OrthancStone::BasicNativeApplicationContext* context_;
+  bool                          isMouseOver_;
+  int                           mouseOverX_;
+  int                           mouseOverY_;
+  bool                          updateNeeded_;
+
+  std::auto_ptr<OrthancStone::IMouseTracker>  mouseTracker_;
+
+//  void UpdateMouseCoordinates(int x,
+//                              int y);
+
+protected:
+  void paintEvent(QPaintEvent *event);
+
+  void resizeEvent(QResizeEvent *event);
+
+//  void mouseMoveEvent(QMouseEvent *event);
+
+//  void mousePressEvent(QMouseEvent *event);
+
+//  void mouseReleaseEvent(QMouseEvent *event);
+
+//  void wheelEvent(QWheelEvent *event);
+
+public:
+  explicit QCairoWidget(QWidget *parent);
+ 
+  virtual ~QCairoWidget();
+
+  void SetContext(OrthancStone::BasicNativeApplicationContext& context)
+  {
+    context_ = &context;
+  }
+
+//  void SetCentralWidget(OrthancStone::CairoWidget& widget)
+//  {
+//    centralWidget_ = &widget;
+//  }
+
+//  void SetScene(OrthancStone::ICairoScene& scene);
+
+//  bool HasScene() const
+//  {
+//    return scene_ != NULL;
+//  }
+
+//  OrthancStone::ICairoScene& GetScene() const
+//  {
+//    assert(HasScene());
+//    return *scene_;
+//  }
+
+//  virtual void SignalUpdatedScene(OrthancStone::ICairoScene& scene)
+//  {
+//    //printf("UPDATE NEEDED\n"); fflush(stdout);
+//    updateNeeded_ = true;
+//  }
+
+signals:
+//  void SliceChanged(int position);
+  
+//  void RangeChanged(unsigned int countSlices);
+  
+//  void PositionChanged(const QString& position);
+
+//  void ContentChanged();
+                                               
+public slots:
+//  void SetDefaultView();
+
+//  void Refresh();
+
+//  void SetSlice(int index);
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Qt/MainWindow.cpp	Mon Aug 27 12:21:52 2018 +0200
@@ -0,0 +1,29 @@
+#include "MainWindow.h"
+
+/**
+ * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as
+ * this makes CMake unable to detect when the UI file changes.
+ **/
+#include <ui_MainWindow.h>
+
+MainWindow::MainWindow(OrthancStone::BasicNativeApplicationContext& context, QWidget *parent) :
+  QMainWindow(parent),
+  ui_(new Ui::MainWindow),
+  context_(context)
+{
+  refreshTimer_ = new QTimer(this);
+  ui_->setupUi(this);
+  cairoCentralWidget_ = ui_->cairoCentralWidget;
+  cairoCentralWidget_->SetContext(context_);
+
+  //connect(refreshTimer_, SIGNAL(timeout()), ui_->ca, SLOT(Refresh()));
+  refreshTimer_->start(100);
+}
+
+MainWindow::~MainWindow()
+{
+  delete ui_;
+  delete refreshTimer_;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Qt/MainWindow.h	Mon Aug 27 12:21:52 2018 +0200
@@ -0,0 +1,32 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QTimer>
+
+#include "../../Qt/QCairoWidget.h"
+#include "../../Generic/BasicNativeApplicationContext.h"
+namespace Ui 
+{
+  class MainWindow;
+}
+
+
+class MainWindow : public QMainWindow
+{
+  Q_OBJECT
+
+public:
+
+private:
+  Ui::MainWindow        *ui_;
+  QTimer                *refreshTimer_;
+  OrthancStone::BasicNativeApplicationContext& context_;
+  QCairoWidget          *cairoCentralWidget_;
+
+public:
+  explicit MainWindow(OrthancStone::BasicNativeApplicationContext& context, QWidget *parent = 0);
+  ~MainWindow();
+};
+
+#endif // MAINWINDOW_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Qt/MainWindow.ui	Mon Aug 27 12:21:52 2018 +0200
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>800</width>
+    <height>600</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Stone of Orthanc</string>
+  </property>
+  <property name="layoutDirection">
+   <enum>Qt::LeftToRight</enum>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <property name="sizePolicy">
+    <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+     <horstretch>0</horstretch>
+     <verstretch>0</verstretch>
+    </sizepolicy>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::LeftToRight</enum>
+   </property>
+   <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0">
+    <property name="sizeConstraint">
+     <enum>QLayout::SetDefaultConstraint</enum>
+    </property>
+    <item>
+     <widget class="QCairoWidget" name="cairoCentralWidget"/>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>800</width>
+     <height>25</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuTest">
+    <property name="title">
+     <string>Test</string>
+    </property>
+   </widget>
+   <addaction name="menuTest"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QCairoWidget</class>
+   <extends>QGraphicsView</extends>
+   <header location="global">QCairoWidget.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
--- a/Applications/Samples/SampleMainNative.cpp	Fri Aug 24 13:58:06 2018 +0200
+++ b/Applications/Samples/SampleMainNative.cpp	Mon Aug 27 12:21:52 2018 +0200
@@ -20,7 +20,12 @@
 
 
 #include "SampleList.h"
+#if ORTHANC_ENABLE_SDL==1
 #include "../Sdl/BasicSdlApplication.h"
+#endif
+#if ORTHANC_ENABLE_QT==1
+#include "../Qt/BasicQtApplication.h"
+#endif
 #include "../../Framework/Messages/MessageBroker.h"
 
 int main(int argc, char* argv[]) 
@@ -28,6 +33,12 @@
   OrthancStone::MessageBroker broker;
   Application application(broker);
 
+#if ORTHANC_ENABLE_SDL==1
   OrthancStone::BasicSdlApplication sdlApplication;
   return sdlApplication.Execute(broker, application, argc, argv);
+#endif
+#if ORTHANC_ENABLE_QT==1
+  OrthancStone::BasicQtApplication qtApplication;
+  return qtApplication.Execute(broker, application, argc, argv);
+#endif
 }
--- a/Applications/Sdl/BasicSdlApplication.cpp	Fri Aug 24 13:58:06 2018 +0200
+++ b/Applications/Sdl/BasicSdlApplication.cpp	Mon Aug 27 12:21:52 2018 +0200
@@ -44,20 +44,6 @@
 
   void BasicSdlApplication::DeclareCommandLineOptions(boost::program_options::options_description& options)
   {
-    // Declare the supported parameters
-    boost::program_options::options_description generic("Generic options");
-    generic.add_options()
-        ("help", "Display this help and exit")
-        ("verbose", "Be verbose in logs")
-        ("orthanc", boost::program_options::value<std::string>()->default_value("http://localhost:8042/"),
-         "URL to the Orthanc server")
-        ("username", "Username for the Orthanc server")
-        ("password", "Password for the Orthanc server")
-        ("https-verify", boost::program_options::value<bool>()->default_value(true), "Check HTTPS certificates")
-        ;
-
-    options.add(generic);
-
     boost::program_options::options_description sdl("SDL options");
     sdl.add_options()
         ("width", boost::program_options::value<int>()->default_value(1024), "Initial width of the SDL window")
@@ -68,7 +54,41 @@
     options.add(sdl);
   }
 
-  void BasicSdlApplication::Run(BasicNativeApplicationContext& context, const std::string& title, unsigned int width, unsigned int height, bool enableOpenGl)
+  void BasicSdlApplication::ParseCommandLineOptions(const boost::program_options::variables_map& parameters)
+  {
+    if (!parameters.count("width") ||
+        !parameters.count("height") ||
+        !parameters.count("opengl"))
+    {
+      LOG(ERROR) << "Parameter \"width\", \"height\" or \"opengl\" is missing";
+      return -1;
+    }
+
+    int w = parameters["width"].as<int>();
+    int h = parameters["height"].as<int>();
+    if (w <= 0 || h <= 0)
+    {
+      LOG(ERROR) << "Parameters \"width\" and \"height\" must be positive";
+      return -1;
+    }
+
+    width_ = static_cast<unsigned int>(w);
+    height_ = static_cast<unsigned int>(h);
+    LOG(WARNING) << "Initial display size: " << width_ << "x" << height_;
+
+    opengl_ = parameters["opengl"].as<bool>();
+    if (opengl_)
+    {
+      LOG(WARNING) << "OpenGL is enabled, disable it with option \"--opengl=off\" if the application crashes";
+    }
+    else
+    {
+      LOG(WARNING) << "OpenGL is disabled, enable it with option \"--opengl=on\" for best performance";
+    }
+
+  }
+
+  void BasicSdlApplication::Run(BasicNativeApplicationContext& context, const std::string& title, int argc, char* argv[])
   {
     /**************************************************************
      * Run the application inside a SDL window
@@ -76,7 +96,7 @@
 
     LOG(WARNING) << "Starting the application";
 
-    SdlWindow window(title.c_str(), width, height, enableOpenGl);
+    SdlWindow window(title.c_str(), width_, height_, enableOpenGl_);
     SdlEngine sdl(window, context);
 
     {
--- a/Applications/Sdl/BasicSdlApplication.h	Fri Aug 24 13:58:06 2018 +0200
+++ b/Applications/Sdl/BasicSdlApplication.h	Mon Aug 27 12:21:52 2018 +0200
@@ -33,10 +33,14 @@
 {
   class BasicSdlApplication : public BasicNativeApplication
   {
+    unsigned int width_;
+    unsigned int height_;
+    bool enableOpenGl_;
   public:
     virtual void Initialize();
     virtual void DeclareCommandLineOptions(boost::program_options::options_description& options);
-    virtual void Run(BasicNativeApplicationContext& context, const std::string& title, unsigned int width, unsigned int height, bool enableOpenGl);
+    virtual void Run(BasicNativeApplicationContext& context, const std::string& title, int argc, char* argv[]);
+    virtual void ParseCommandLineOptions(const boost::program_options::variables_map& parameters);
     virtual void Finalize();
   };
 
--- a/Platforms/Generic/CMakeLists.txt	Fri Aug 24 13:58:06 2018 +0200
+++ b/Platforms/Generic/CMakeLists.txt	Mon Aug 27 12:21:52 2018 +0200
@@ -29,6 +29,8 @@
 
 LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
 
+SET(ENABLE_SDL OFF)
+SET(ENABLE_QT ON)
 SET(ORTHANC_SANDBOXED OFF)
 SET(ENABLE_CRYPTO_OPTIONS ON)
 SET(ENABLE_GOOGLE_TEST ON)
@@ -48,7 +50,6 @@
 macro(BuildSample Target Header Sample)
   add_executable(${Target}
     ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleMainNative.cpp
-#    ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleApplicationContext.cpp
     ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleInteractor.h
     ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleApplicationBase.h
     ${ORTHANC_STONE_ROOT}/Applications/Samples/${Header}
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Fri Aug 24 13:58:06 2018 +0200
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Mon Aug 27 12:21:52 2018 +0200
@@ -74,16 +74,21 @@
 
 
 if (ENABLE_SDL AND ENABLE_QT)
-    message("SDL and QT may not be defined together")
+  message("SDL and QT may not be defined together")
 elseif(ENABLE_SDL)
-  include(${CMAKE_CURRENT_LIST_DIR}/SdlConfiguration.cmake)  
+  message("SDL is enabled")
+  include(${CMAKE_CURRENT_LIST_DIR}/SdlConfiguration.cmake)
   add_definitions(-DORTHANC_ENABLE_NATIVE=1)
+  add_definitions(-DORTHANC_ENABLE_QT=0)
   add_definitions(-DORTHANC_ENABLE_SDL=1)
 elseif(ENABLE_QT)
-    include(${CMAKE_CURRENT_LIST_DIR}/QtConfiguration.cmake)
-    add_definitions(-DORTHANC_ENABLE_NATIVE=1)
-    add_definitions(-DORTHANC_ENABLE_QT=1)
+  message("QT is enabled")
+  include(${CMAKE_CURRENT_LIST_DIR}/QtConfiguration.cmake)
+  add_definitions(-DORTHANC_ENABLE_NATIVE=1)
+  add_definitions(-DORTHANC_ENABLE_QT=1)
+  add_definitions(-DORTHANC_ENABLE_SDL=0)
 else()
+  message("SDL and QT are both disabled")
   unset(USE_SYSTEM_SDL CACHE)
   add_definitions(-DORTHANC_ENABLE_SDL=0)
   add_definitions(-DORTHANC_ENABLE_QT=0)
@@ -278,6 +283,7 @@
 
   # Optional components
   ${SDL_SOURCES}
+  ${QT_SOURCES}
   ${BOOST_EXTENDED_SOURCES}
   )
 
--- a/Resources/CMake/QtConfiguration.cmake	Fri Aug 24 13:58:06 2018 +0200
+++ b/Resources/CMake/QtConfiguration.cmake	Mon Aug 27 12:21:52 2018 +0200
@@ -0,0 +1,46 @@
+# Stone of Orthanc
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+# Copyright (C) 2017-2018 Osimis S.A., Belgium
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Affero General Public License for more details.
+# 
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# Instruct CMake to run moc automatically when needed.
+set(CMAKE_AUTOMOC ON)
+SET(CMAKE_AUTOUIC ON)
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+# Find the QtWidgets library
+find_package(Qt5Widgets)
+find_package(Qt5Core)
+
+#qt5_wrap_ui(UI_HEADERS
+#    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/MainWindow.ui
+#)
+set_property(SOURCE ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/MainWindow.h PROPERTY SKIP_AUTOMOC ON)
+
+list(APPEND QT_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.cpp
+    ${ORTHANC_STONE_ROOT}/Applications/Qt/BasicQtApplication.cpp
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/MainWindow.cpp
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/MainWindow.ui
+)
+
+include_directories(${ORTHANC_STONE_ROOT}/Applications/Qt/)
+
+link_libraries(
+    Qt5::Widgets
+    Qt5::Core
+)