Refer to Basic Viewer Example.
BaseViewerApplication.hxx
#ifndef BaseViewerApplication_HeaderFile
#define BaseViewerApplication_HeaderFile
#include <cadex/ModelData_Model.hxx>
#include <cadex/ModelPrs_Scene.hxx>
#include <cadex/ModelPrs_SceneNode.hxx>
#include <cadex/ModelPrs_SceneNodeFactoryParameters.hxx>
#include <cadex/STEP_ReaderParameters.hxx>
#include <QtCore/QObject>
class QQuickWindow;
class QQmlEngine;
class QVariant;
class ModelPrsQtQuick_ViewPort;
}
class BaseViewerApplication : public QObject
{
Q_OBJECT
public:
BaseViewerApplication();
bool Initialize (const QUrl& theUrl, const QString& theViewPortName);
bool event (QEvent* theEvent) override;
virtual void CreateSceneNodes();
signals:
void initialized();
protected slots:
void onImportFile (const QVariant& theUrl);
protected:
void Import (const QString& theFilename);
virtual void Clear();
void ShowMessageBox (const QString& theMessage);
private:
QQuickWindow* CreateMainWindow (const QUrl& theUrl);
void onImportCompleted (QEvent* theEvent);
protected:
QQuickWindow* myMainWindow = nullptr;
};
#endif // BaseViewerApplication_HeaderFile
BaseViewerApplication.cxx
#include "BaseViewerApplication.hxx"
#include "ImportRunnable.hxx"
#include "CustomEvents.hxx"
#include <cadex/ModelPrs_Scene.hxx>
#include <cadex/ModelPrs_SceneNodeFactory.hxx>
#include <cadex/ModelPrsQtQuick_ViewPort.hxx>
#include <QtCore/QThreadPool>
#include <QtQml/QQmlApplicationEngine>
#include <QtQuick/QQuickWindow>
#if __ANDROID__
#include <QtAndroidExtras/QtAndroid>
#endif
#if __ANDROID__ && __ANDROID_DEBUG
#include <android/log.h>
namespace {
static void QMessageToAndroidLogRedirector (QtMsgType theType, const QMessageLogContext& , const QString& theMsg)
{
QString aText;
switch (theType) {
case QtDebugMsg: aText += "qDebug(): "; break;
case QtWarningMsg: aText += "qWarning(): "; break;
case QtCriticalMsg: aText += "qCritical(): "; break;
case QtFatalMsg: aText += "qFatal(): "; break;
case QtInfoMsg: aText += "qInfo(): "; break;
default: break;
}
aText += theMsg;
for (const QString& line : aText.split ('\n')) {
__android_log_print (ANDROID_LOG_DEBUG, "baseviewer", "%s", qPrintable (line));
}
}
}
#endif
BaseViewerApplication::BaseViewerApplication()
: myRoot (ModelPrs_SceneNode::initialized())
{
myScene.AddRoot (myRoot);
}
bool BaseViewerApplication::Initialize (const QUrl& theUrl, const QString& theViewPortName)
{
#if __ANDROID__ && __ANDROID_DEBUG
qInstallMessageHandler (QMessageToAndroidLogRedirector);
#endif
myMainWindow = CreateMainWindow (theUrl);
Q_ASSERT (myMainWindow);
#if __ANDROID__
if (!QtAndroid::shouldShowRequestPermissionRationale("android.permission.READ_EXTERNAL_STORAGE")) {
QtAndroid::requestPermissions({{ "android.permission.READ_EXTERNAL_STORAGE" }},
[] (const QtAndroid::PermissionResultMap & theResult) {
if (theResult["android.permission.READ_EXTERNAL_STORAGE"] == QtAndroid::PermissionResult::Denied) {
qWarning ("User refused request to allow access to sdcard for reading.");
}
});
}
#endif
connect (myMainWindow, SIGNAL (importFile (const QVariant&)),
this, SLOT (onImportFile (const QVariant&)));
Q_ASSERT (myViewPort);
auto aRes = myViewPort->AttachToScene (myScene);
myMainWindow->showNormal();
emit initialized();
return aRes;
}
QQuickWindow* BaseViewerApplication::CreateMainWindow (const QUrl& theUrl)
{
auto anEngine = new QQmlApplicationEngine (this);
QQmlComponent aComponent (anEngine);
aComponent.loadUrl (theUrl);
QObject* aQmlApp = nullptr;
if (!aComponent.isReady()) {
qDebug() << aComponent.errorString();
QCoreApplication::postEvent (QCoreApplication::instance(), new QEvent (QEvent::Quit));
return nullptr;
}
aQmlApp = aComponent.create();
aQmlApp->setParent (this);
return qobject_cast<QQuickWindow*> (aQmlApp);
}
void BaseViewerApplication::onImportFile (const QVariant& theUrl)
{
if (!(theUrl.isValid() || !theUrl.canConvert<QUrl>())) {
qWarning() << "BaseViewerApplication::OnImport: Invalid argument [" << theUrl << "]";
return;
}
auto aFilename = theUrl.toUrl().toLocalFile();
Import (aFilename);
}
void BaseViewerApplication::Import (const QString& theFilename)
{
Clear();
auto aRunnable = new ImportRunnable (theFilename, myScene, myModel, myReaderParameters, this);
QThreadPool::globalInstance()->start (aRunnable);
myMainWindow->setProperty ("loading", true);
myMainWindow->setTitle (QString ("CAD Exchanger [%1]").arg (theFilename));
}
void BaseViewerApplication::Clear()
{
myRoot.RemoveChildrenNodes();
myScene.Update();
}
void BaseViewerApplication::CreateSceneNodes()
{
auto aRoot = aFactory.
Create (myModel);
}
void BaseViewerApplication::onImportCompleted (QEvent* theEvent)
{
auto anEventType = static_cast<ImportResult> (theEvent->type());
switch (anEventType) {
case ImportResult::Success: {
myMainWindow->setProperty ("loading", false);
myViewPort->animatedFitAll();
break;
}
case ImportResult::Error: {
Q_ASSERT (dynamic_cast<ErrorEvent*> (theEvent));
auto anEvent = static_cast<ErrorEvent*> (theEvent);
ShowMessageBox (anEvent->Message());
break;
}
}
}
bool BaseViewerApplication::event (QEvent* theEvent)
{
auto anEventType = static_cast<ImportResult> (theEvent->type());
if (ImportResult::Error != anEventType && anEventType != ImportResult::Success) {
return QObject::event (theEvent);
}
onImportCompleted (theEvent);
return true;
}
void BaseViewerApplication::ShowMessageBox (const QString& theMessage)
{
if (auto aDialog = myMainWindow->findChild<QObject*> ("dialog")) {
aDialog->setProperty ("message", theMessage);
QMetaObject::invokeMethod (aDialog, "open");
}
}
CustomEvents.hxx
#ifndef CustomEvents_HeaderFile
#define CustomEvents_HeaderFile
#include <QtCore/QEvent>
#include <QtCore/QString>
enum ImportResult {
Success = QEvent::User + 1,
Error
};
class ErrorEvent : public QEvent
{
public:
ErrorEvent (const QString& myMessage);
const QString& Message() const;
private:
QString myMessage;
};
#endif // CustomEvents_HeaderFile
CustomEvents.cxx
#include "CustomEvents.hxx"
ErrorEvent::ErrorEvent (const QString& theMessage)
: QEvent (static_cast<QEvent::Type> (ImportResult::Error)),
myMessage (theMessage)
{
}
const QString& ErrorEvent::Message() const
{
return myMessage;
}
ImportRunnable.hxx
#ifndef ImportRunnable_HeaderFile
#define ImportRunnable_HeaderFile
#include "BaseViewerApplication.hxx"
#include <cadex/STEP_ReaderParameters.hxx>
#include <QtCore/QRunnable>
class ModelData_Model;
class ModelPrs_Scene;
}
class ImportRunnable : public QRunnable
{
public:
ImportRunnable (const QString& theFilename,
BaseViewerApplication* theParent);
void run() override;
private:
void Read();
void DisplayModel();
private:
QString myFilename;
BaseViewerApplication* myParent;
};
#endif // ImportRunnable_HeaderFile
ImportRunnable.cxx
#include "ImportRunnable.hxx"
#include "CustomEvents.hxx"
#include <cadex/ModelData_Model.hxx>
#include <cadex/ModelPrs_Scene.hxx>
#include <cadex/ModelPrs_SceneNode.hxx>
#include <cadex/ModelPrs_SceneNodeFactory.hxx>
#include <cadex/ModelPrs_SceneNodeFactoryParameters.hxx>
#include <cadex/STEP_Reader.hxx>
#include <QtCore/QCoreApplication>
#include <QtCore/QFileInfo>
ImportRunnable::ImportRunnable (const QString& theFilename,
BaseViewerApplication* theParent) :
myFilename (theFilename),
myModel (theModel),
myScene (theScene),
myParameters (theParameters),
myParent (theParent)
{
}
void ImportRunnable::Read()
{
if (!aReader.
ReadFile (myFilename.toStdString().data()) || !aReader.
Transfer (myModel)) {
QCoreApplication::postEvent (myParent, new ErrorEvent ("Failed to read the file " + myFilename));
return;
}
}
void ImportRunnable::DisplayModel()
{
myParent->CreateSceneNodes();
myScene.Update();
myScene.Wait();
}
void ImportRunnable::run()
{
Read();
DisplayModel();
QCoreApplication::postEvent (myParent, new QEvent (static_cast<QEvent::Type> (ImportResult::Success)));
}
BaseWindow.qml
import QtQuick 2.7
import QtQuick.Controls 2.3
import Qt.labs.platform 1.0 as Platform
import CadEx 1.2
ApplicationWindow {
title: qsTr ("CAD Exchanger")
minimumWidth: 1000
minimumHeight: 600
width: 1000
height: 600
property alias loading: busyIndicator.running
property alias menuBarItem: menuBarItem
property alias fileMenuItem: fileMenuItem
signal menuCompleted
signal importFile (var url);
menuBar: MenuBar {
id: menuBarItem
enabled: !loading
Menu {
id: fileMenuItem
title: qsTr("&File")
MenuItem {
text: qsTr("&Import...")
onTriggered: {
fileDialog.open()
}
}
MenuSeparator { }
MenuItem {
text: qsTr("&Quit")
onTriggered: {
Qt.quit();
}
}
}
Component.onCompleted: {
menuCompleted();
}
}
Platform.FileDialog {
id: fileDialog
title: qsTr ("Please choose a STEP file")
fileMode: Platform.FileDialog.OpenFile
options: Platform.FileDialog.DontResolveSymlinks | Platform.FileDialog.DontUseCustomDirectoryIcons
folder: Platform.StandardPaths.writableLocation (Platform.StandardPaths.HomeLocation)
nameFilters: [ "STEP files (*.stp *.step)" ]
onAccepted: {
importFile (file);
}
}
Dialog {
id: dialog
objectName: "dialog"
title: qsTr ("Error")
parent: ApplicationWindow.overlay
x: (parent.width - width) / 2
y: (parent.height - height) / 2
width: 300
standardButtons: Dialog.Ok
visible: false
closePolicy: Popup.CloseOnEscape
dim: true
property string message
Label {
anchors.left: parent.left
anchors.right: parent.right
text: dialog.message
}
}
BusyIndicator {
id: busyIndicator
running: false
visible: running
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottomMargin: 20
width: 80
height: 80
parent: ApplicationWindow.overlay
}
}
BaseViewerWindow.qml
import CadEx 1.2
BaseWindow {
property var viewPort: viewPortItem
ModelPrsQtQuick_ViewPort {
id: viewPortItem
objectName: "viewPort"
anchors.fill: parent
highlightEnabled: true
}
}
main.cxx
#include "BaseViewerApplication.hxx"
#include <cadex/LicenseManager_Activate.h>
#include <QtCore/QUrl>
#include <QtWidgets/QApplication>
#include "../../../cadex_license.cxx"
int main (int argc, char *argv[])
{
auto aKey = LicenseKey::Value();
if (!CADExLicense_Activate (aKey)) {
qCritical ("Failed to activate CAD Exchanger license.");
return 1;
}
QCoreApplication::setAttribute (Qt::AA_EnableHighDpiScaling);
QApplication app (argc, argv);
BaseViewerApplication anApp;
if (anApp.Initialize (QUrl ("qrc:/qml/BaseViewerWindow.qml"), "viewPort")) {
return app.exec();
}
return 0;
}