QGIS/src/app/main.cpp
2017-12-12 20:39:15 +10:00

1424 lines
50 KiB
C++

/***************************************************************************
main.cpp - description
-------------------
begin : Fri Jun 21 10:48:28 AKDT 2002
copyright : (C) 2002 by Gary E.Sherman
email : sherman at mrcc.com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
//qt includes
#include <QBitmap>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QFont>
#include <QFontDatabase>
#include <QPixmap>
#include <QLocale>
#include <QSplashScreen>
#include <QString>
#include <QStringList>
#include <QStyle>
#include <QStyleFactory>
#include <QDesktopWidget>
#include <QTranslator>
#include <QImageReader>
#include <QMessageBox>
#include <cstdio>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#ifdef WIN32
// Open files in binary mode
#include <fcntl.h> /* _O_BINARY */
#include <windows.h>
#include <dbghelp.h>
#include <time.h>
#ifdef MSVC
#undef _fmode
int _fmode = _O_BINARY;
#else
// Only do this if we are not building on windows with msvc.
// Recommended method for doing this with msvc is with a call to _set_fmode
// which is the first thing we do in main().
// Similarly, with MinGW set _fmode in main().
#endif //_MSC_VER
#else
#include <getopt.h>
#endif
#ifdef Q_OS_MACX
#include <ApplicationServices/ApplicationServices.h>
#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050
typedef SInt32 SRefCon;
#endif
// For setting the maximum open files limit higher
#include <sys/resource.h>
#include <limits.h>
#endif
#if ((defined(linux) || defined(__linux__)) && !defined(ANDROID)) || defined(__FreeBSD__)
#include <unistd.h>
#include <execinfo.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
#endif
#include "qgscustomization.h"
#include "qgssettings.h"
#include "qgsfontutils.h"
#include "qgspluginregistry.h"
#include "qgsmessagelog.h"
#include "qgspythonrunner.h"
#include "qgslocalec.h"
#include "qgisapp.h"
#include "qgsmapcanvas.h"
#include "qgsapplication.h"
#include "qgsconfig.h"
#include "qgsversion.h"
#include "qgsexception.h"
#include "qgsproject.h"
#include "qgsrectangle.h"
#include "qgslogger.h"
#include "qgsdxfexport.h"
#include "qgsmapthemes.h"
#include "qgsvectorlayer.h"
#include "qgis_app.h"
#include "qgscrashhandler.h"
#include "qgsziputils.h"
#include "qgsversionmigration.h"
#include "qgsfirstrundialog.h"
#include "qgsuserprofilemanager.h"
#include "qgsuserprofile.h"
/**
* Print usage text
*/
void usage( const QString &appName )
{
QStringList msg;
msg
<< QStringLiteral( "QGIS - " ) << VERSION << QStringLiteral( " '" ) << RELEASE_NAME << QStringLiteral( "' (" )
<< QGSVERSION << QStringLiteral( ")\n" )
<< QStringLiteral( "QGIS is a user friendly Open Source Geographic Information System.\n" )
<< QStringLiteral( "Usage: " ) << appName << QStringLiteral( " [OPTION] [FILE]\n" )
<< QStringLiteral( " OPTION:\n" )
<< QStringLiteral( "\t[--snapshot filename]\temit snapshot of loaded datasets to given file\n" )
<< QStringLiteral( "\t[--width width]\twidth of snapshot to emit\n" )
<< QStringLiteral( "\t[--height height]\theight of snapshot to emit\n" )
<< QStringLiteral( "\t[--lang language]\tuse language for interface text\n" )
<< QStringLiteral( "\t[--project projectfile]\tload the given QGIS project\n" )
<< QStringLiteral( "\t[--extent xmin,ymin,xmax,ymax]\tset initial map extent\n" )
<< QStringLiteral( "\t[--nologo]\thide splash screen\n" )
<< QStringLiteral( "\t[--noversioncheck]\tdon't check for new version of QGIS at startup\n" )
<< QStringLiteral( "\t[--noplugins]\tdon't restore plugins on startup\n" )
<< QStringLiteral( "\t[--nocustomization]\tdon't apply GUI customization\n" )
<< QStringLiteral( "\t[--customizationfile path]\tuse the given ini file as GUI customization\n" )
<< QStringLiteral( "\t[--globalsettingsfile path]\tuse the given ini file as Global Settings (defaults)\n" )
<< QStringLiteral( "\t[--authdbdirectory path] use the given directory for authentication database\n" )
<< QStringLiteral( "\t[--code path]\trun the given python file on load\n" )
<< QStringLiteral( "\t[--defaultui]\tstart by resetting user ui settings to default\n" )
<< QStringLiteral( "\t[--dxf-export filename.dxf]\temit dxf output of loaded datasets to given file\n" )
<< QStringLiteral( "\t[--dxf-extent xmin,ymin,xmax,ymax]\tset extent to export to dxf\n" )
<< QStringLiteral( "\t[--dxf-symbology-mode none|symbollayer|feature]\tsymbology mode for dxf output\n" )
<< QStringLiteral( "\t[--dxf-scale-denom scale]\tscale for dxf output\n" )
<< QStringLiteral( "\t[--dxf-encoding encoding]\tencoding to use for dxf output\n" )
<< QStringLiteral( "\t[--dxf-preset maptheme]\tmap theme to use for dxf output\n" )
<< QStringLiteral( "\t[--profile name]\tload a named profile from the users profiles folder.\n" )
<< QStringLiteral( "\t[--profiles-path path]\tpath to store user profile folders. Will create profiles inside a {path}\\profiles folder \n" )
<< QStringLiteral( "\t[--version-migration]\tforce the settings migration from older version if found\n" )
<< QStringLiteral( "\t[--help]\t\tthis text\n" )
<< QStringLiteral( "\t[--]\t\ttreat all following arguments as FILEs\n\n" )
<< QStringLiteral( " FILE:\n" )
<< QStringLiteral( " Files specified on the command line can include rasters,\n" )
<< QStringLiteral( " vectors, and QGIS project files (.qgs): \n" )
<< QStringLiteral( " 1. Rasters - supported formats include GeoTiff, DEM \n" )
<< QStringLiteral( " and others supported by GDAL\n" )
<< QStringLiteral( " 2. Vectors - supported formats include ESRI Shapefiles\n" )
<< QStringLiteral( " and others supported by OGR and PostgreSQL layers using\n" )
<< QStringLiteral( " the PostGIS extension\n" ) ; // OK
#ifdef Q_OS_WIN
MessageBox( nullptr,
msg.join( QString() ).toLocal8Bit().constData(),
"QGIS command line options",
MB_OK );
#else
std::cerr << msg.join( QString() ).toLocal8Bit().constData();
#endif
} // usage()
/////////////////////////////////////////////////////////////////
// Command line options 'behavior' flag setup
////////////////////////////////////////////////////////////////
// These two are global so that they can be set by the OpenDocuments
// AppleEvent handler as well as by the main routine argv processing
// This behavior will cause QGIS to autoload a project
static QString sProjectFileName;
// This is the 'leftover' arguments collection
static QStringList sFileList;
/* Test to determine if this program was started on Mac OS X by double-clicking
* the application bundle rather then from a command line. If clicked, argv[1]
* contains a process serial number in the form -psn_0_1234567. Don't process
* the command line arguments in this case because argv[1] confuses the processing.
*/
bool bundleclicked( int argc, char *argv[] )
{
return ( argc > 1 && memcmp( argv[1], "-psn_", 5 ) == 0 );
}
void myPrint( const char *fmt, ... )
{
va_list ap;
va_start( ap, fmt );
#if defined(Q_OS_WIN)
char buffer[1024];
vsnprintf( buffer, sizeof buffer, fmt, ap );
OutputDebugString( buffer );
#else
vfprintf( stderr, fmt, ap );
#endif
va_end( ap );
}
static void dumpBacktrace( unsigned int depth )
{
if ( depth == 0 )
depth = 20;
#if ((defined(linux) || defined(__linux__)) && !defined(ANDROID)) || defined(__FreeBSD__)
// Below there is a bunch of operations that are not safe in multi-threaded
// environment (dup()+close() combo, wait(), juggling with file descriptors).
// Maybe some problems could be resolved with dup2() and waitpid(), but it seems
// that if the operations on descriptors are not serialized, things will get nasty.
// That's why there's this lovely mutex here...
static QMutex sMutex;
QMutexLocker locker( &sMutex );
int stderr_fd = -1;
if ( access( "/usr/bin/c++filt", X_OK ) < 0 )
{
myPrint( "Stacktrace (c++filt NOT FOUND):\n" );
}
else
{
int fd[2];
if ( pipe( fd ) == 0 && fork() == 0 )
{
close( STDIN_FILENO ); // close stdin
// stdin from pipe
if ( dup( fd[0] ) != STDIN_FILENO )
{
QgsDebugMsg( "dup to stdin failed" );
}
close( fd[1] ); // close writing end
execl( "/usr/bin/c++filt", "c++filt", static_cast< char * >( nullptr ) );
perror( "could not start c++filt" );
exit( 1 );
}
myPrint( "Stacktrace (piped through c++filt):\n" );
stderr_fd = dup( STDERR_FILENO );
close( fd[0] ); // close reading end
close( STDERR_FILENO ); // close stderr
// stderr to pipe
int stderr_new = dup( fd[1] );
if ( stderr_new != STDERR_FILENO )
{
if ( stderr_new >= 0 )
close( stderr_new );
QgsDebugMsg( "dup to stderr failed" );
}
close( fd[1] ); // close duped pipe
}
void **buffer = new void *[ depth ];
int nptrs = backtrace( buffer, depth );
backtrace_symbols_fd( buffer, nptrs, STDERR_FILENO );
delete [] buffer;
if ( stderr_fd >= 0 )
{
int status;
close( STDERR_FILENO );
int dup_stderr = dup( stderr_fd );
if ( dup_stderr != STDERR_FILENO )
{
close( dup_stderr );
QgsDebugMsg( "dup to stderr failed" );
}
close( stderr_fd );
wait( &status );
}
#elif defined(Q_OS_WIN)
// TODO Replace with incoming QgsStackTrace
#else
Q_UNUSED( depth );
#endif
}
#if (defined(linux) && !defined(ANDROID)) || defined(__FreeBSD__)
void qgisCrash( int signal )
{
fprintf( stderr, "QGIS died on signal %d", signal );
if ( access( "/usr/bin/gdb", X_OK ) == 0 )
{
// take full stacktrace using gdb
// http://stackoverflow.com/questions/3151779/how-its-better-to-invoke-gdb-from-program-to-print-its-stacktrace
// unfortunately, this is not so simple. the proper method is way more OS-specific
// than this code would suggest, see http://stackoverflow.com/a/1024937
char exename[512];
#if defined(__FreeBSD__)
int len = readlink( "/proc/curproc/file", exename, sizeof( exename ) - 1 );
#else
int len = readlink( "/proc/self/exe", exename, sizeof( exename ) - 1 );
#endif
if ( len < 0 )
{
myPrint( "Could not read link (%d: %s)\n", errno, strerror( errno ) );
}
else
{
exename[ len ] = 0;
char pidstr[32];
snprintf( pidstr, sizeof pidstr, "--pid=%d", getpid() );
int gdbpid = fork();
if ( gdbpid == 0 )
{
// attach, backtrace and continue
execl( "/usr/bin/gdb", "gdb", "-q", "-batch", "-n", pidstr, "-ex", "thread", "-ex", "bt full", exename, NULL );
perror( "cannot exec gdb" );
exit( 1 );
}
else if ( gdbpid >= 0 )
{
int status;
waitpid( gdbpid, &status, 0 );
myPrint( "gdb returned %d\n", status );
}
else
{
myPrint( "Cannot fork (%d: %s)\n", errno, strerror( errno ) );
dumpBacktrace( 256 );
}
}
}
abort();
}
#endif
/*
* Hook into the qWarning/qFatal mechanism so that we can channel messages
* from libpng to the user.
*
* Some JPL WMS images tend to overload the libpng 1.2.2 implementation
* somehow (especially when zoomed in)
* and it would be useful for the user to know why their picture turned up blank
*
* Based on qInstallMsgHandler example code in the Qt documentation.
*
*/
void myMessageOutput( QtMsgType type, const char *msg )
{
switch ( type )
{
case QtDebugMsg:
myPrint( "%s\n", msg );
if ( strncmp( msg, "Backtrace", 9 ) == 0 )
dumpBacktrace( atoi( msg + 9 ) );
break;
case QtCriticalMsg:
myPrint( "Critical: %s\n", msg );
break;
case QtWarningMsg:
myPrint( "Warning: %s\n", msg );
#ifdef QGISDEBUG
// Print all warnings except setNamedColor.
// Only seems to happen on windows
if ( 0 != strncmp( msg, "QColor::setNamedColor: Unknown color name 'param", 48 ) )
{
// TODO: Verify this code in action.
dumpBacktrace( 20 );
QgsMessageLog::logMessage( msg, QStringLiteral( "Qt" ) );
}
#endif
// TODO: Verify this code in action.
if ( 0 == strncmp( msg, "libpng error:", 13 ) )
{
// Let the user know
QgsMessageLog::logMessage( msg, QStringLiteral( "libpng" ) );
}
break;
case QtFatalMsg:
{
myPrint( "Fatal: %s\n", msg );
#if (defined(linux) && !defined(ANDROID)) || defined(__FreeBSD__)
qgisCrash( -1 );
#else
dumpBacktrace( 256 );
abort(); // deliberately dump core
#endif
break; // silence warnings
}
#if QT_VERSION >= 0x050500
case QtInfoMsg:
myPrint( "Info: %s\n", msg );
break;
#endif
}
}
#ifdef _MSC_VER
#undef APP_EXPORT
#define APP_EXPORT __declspec(dllexport)
#endif
#if defined(ANDROID) || defined(Q_OS_WIN)
// On Android, there there is a libqgis.so instead of a qgis executable.
// The main method symbol of this library needs to be exported so it can be called by java
// On Windows this main is included in qgis_app and called from mainwin.cpp
APP_EXPORT
#endif
int main( int argc, char *argv[] )
{
#ifdef Q_OS_MACX
// Increase file resource limits (i.e., number of allowed open files)
// (from code provided by Larry Biehl, Purdue University, USA, from 'MultiSpec' project)
// This is generally 256 for the soft limit on Mac
// NOTE: setrlimit() must come *before* initialization of stdio strings,
// e.g. before any debug messages, or setrlimit() gets ignored
// see: http://stackoverflow.com/a/17726104/2865523
struct rlimit rescLimit;
if ( getrlimit( RLIMIT_NOFILE, &rescLimit ) == 0 )
{
rlim_t oldSoft( rescLimit.rlim_cur );
rlim_t oldHard( rescLimit.rlim_max );
#ifdef OPEN_MAX
rlim_t newSoft( OPEN_MAX );
rlim_t newHard( std::min( oldHard, newSoft ) );
#else
rlim_t newSoft( 4096 );
rlim_t newHard( std::min( ( rlim_t )8192, oldHard ) );
#endif
if ( rescLimit.rlim_cur < newSoft )
{
rescLimit.rlim_cur = newSoft;
rescLimit.rlim_max = newHard;
if ( setrlimit( RLIMIT_NOFILE, &rescLimit ) == 0 )
{
QgsDebugMsg( QString( "Mac RLIMIT_NOFILE Soft/Hard NEW: %1 / %2" )
.arg( rescLimit.rlim_cur ).arg( rescLimit.rlim_max ) );
}
}
Q_UNUSED( oldSoft ); //avoid warnings
QgsDebugMsg( QString( "Mac RLIMIT_NOFILE Soft/Hard ORIG: %1 / %2" )
.arg( oldSoft ).arg( oldHard ) );
}
#endif
QgsDebugMsg( QString( "Starting qgis main" ) );
#ifdef WIN32 // Windows
#ifdef _MSC_VER
_set_fmode( _O_BINARY );
#else //MinGW
_fmode = _O_BINARY;
#endif // _MSC_VER
#endif // WIN32
// Set up the custom qWarning/qDebug custom handler
#ifndef ANDROID
qInstallMsgHandler( myMessageOutput );
#endif
#if (defined(linux) && !defined(ANDROID)) || defined(__FreeBSD__)
signal( SIGQUIT, qgisCrash );
signal( SIGILL, qgisCrash );
signal( SIGFPE, qgisCrash );
signal( SIGSEGV, qgisCrash );
signal( SIGBUS, qgisCrash );
signal( SIGSYS, qgisCrash );
signal( SIGTRAP, qgisCrash );
signal( SIGXCPU, qgisCrash );
signal( SIGXFSZ, qgisCrash );
#endif
#ifdef _MSC_VER
SetUnhandledExceptionFilter( QgsCrashHandler::handle );
#endif
// initialize random number seed
qsrand( time( nullptr ) );
/////////////////////////////////////////////////////////////////
// Command line options 'behavior' flag setup
////////////////////////////////////////////////////////////////
//
// Parse the command line arguments, looking to see if the user has asked for any
// special behaviors. Any remaining non command arguments will be kept aside to
// be passed as a list of layers and / or a project that should be loaded.
//
// This behavior is used to load the app, snapshot the map,
// save the image to disk and then exit
QString mySnapshotFileName;
QString configLocalStorageLocation;
QString profileName;
int mySnapshotWidth = 800;
int mySnapshotHeight = 600;
bool myHideSplash = false;
bool mySettingsMigrationForce = false;
bool mySkipVersionCheck = false;
#if defined(ANDROID)
QgsDebugMsg( QString( "Android: Splash hidden" ) );
myHideSplash = true;
#endif
bool myRestoreDefaultWindowState = false;
bool myRestorePlugins = true;
bool myCustomization = true;
QString dxfOutputFile;
QgsDxfExport::SymbologyExport dxfSymbologyMode = QgsDxfExport::SymbolLayerSymbology;
double dxfScale = 50000.0;
QString dxfEncoding = QStringLiteral( "CP1252" );
QString dxfPreset;
QgsRectangle dxfExtent;
// This behavior will set initial extent of map canvas, but only if
// there are no command line arguments. This gives a usable map
// extent when qgis starts with no layers loaded. When layers are
// loaded, we let the layers define the initial extent.
QString myInitialExtent;
if ( argc == 1 )
myInitialExtent = QStringLiteral( "-1,-1,1,1" );
// This behavior will allow you to force the use of a translation file
// which is useful for testing
QString myTranslationCode;
// The user can specify a path which will override the default path of custom
// user settings (~/.qgis) and it will be used for QgsSettings INI file
QString configpath;
QString authdbdirectory;
QString pythonfile;
QString customizationfile;
QString globalsettingsfile;
// TODO Fix android
#if defined(ANDROID)
QgsDebugMsg( QString( "Android: All params stripped" ) );// Param %1" ).arg( argv[0] ) );
//put all QGIS settings in the same place
configpath = QgsApplication::qgisSettingsDirPath();
QgsDebugMsg( QString( "Android: configpath set to %1" ).arg( configpath ) );
#endif
QStringList args;
if ( !bundleclicked( argc, argv ) )
{
// Build a local QCoreApplication from arguments. This way, arguments are correctly parsed from their native locale
// It will use QString::fromLocal8Bit( argv ) under Unix and GetCommandLine() under Windows.
QCoreApplication coreApp( argc, argv );
args = QCoreApplication::arguments();
for ( int i = 1; i < args.size(); ++i )
{
const QString &arg = args[i];
if ( arg == QLatin1String( "--help" ) || arg == QLatin1String( "-?" ) )
{
usage( args[0] );
return 2;
}
else if ( arg == QLatin1String( "--nologo" ) || arg == QLatin1String( "-n" ) )
{
myHideSplash = true;
}
else if ( arg == QLatin1String( "--version-migration" ) )
{
mySettingsMigrationForce = true;
}
else if ( arg == QLatin1String( "--noversioncheck" ) || arg == QLatin1String( "-V" ) )
{
mySkipVersionCheck = true;
}
else if ( arg == QLatin1String( "--noplugins" ) || arg == QLatin1String( "-P" ) )
{
myRestorePlugins = false;
}
else if ( arg == QLatin1String( "--nocustomization" ) || arg == QLatin1String( "-C" ) )
{
myCustomization = false;
}
else if ( i + 1 < argc && ( arg == QLatin1String( "--profile" ) ) )
{
profileName = args[++i];
}
else if ( i + 1 < argc && ( arg == QLatin1String( "--profiles-path" ) || arg == QLatin1String( "-s" ) ) )
{
configLocalStorageLocation = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
}
else if ( i + 1 < argc && ( arg == QLatin1String( "--snapshot" ) || arg == QLatin1String( "-s" ) ) )
{
mySnapshotFileName = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
}
else if ( i + 1 < argc && ( arg == QLatin1String( "--width" ) || arg == QLatin1String( "-w" ) ) )
{
mySnapshotWidth = QString( args[++i] ).toInt();
}
else if ( i + 1 < argc && ( arg == QLatin1String( "--height" ) || arg == QLatin1String( "-h" ) ) )
{
mySnapshotHeight = QString( args[++i] ).toInt();
}
else if ( i + 1 < argc && ( arg == QLatin1String( "--lang" ) || arg == QLatin1String( "-l" ) ) )
{
myTranslationCode = args[++i];
}
else if ( i + 1 < argc && ( arg == QLatin1String( "--project" ) || arg == QLatin1String( "-p" ) ) )
{
sProjectFileName = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
}
else if ( i + 1 < argc && ( arg == QLatin1String( "--extent" ) || arg == QLatin1String( "-e" ) ) )
{
myInitialExtent = args[++i];
}
else if ( i + 1 < argc && ( arg == QLatin1String( "--authdbdirectory" ) || arg == QLatin1String( "-a" ) ) )
{
authdbdirectory = QDir::toNativeSeparators( QDir( args[++i] ).absolutePath() );
}
else if ( i + 1 < argc && ( arg == QLatin1String( "--code" ) || arg == QLatin1String( "-f" ) ) )
{
pythonfile = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
}
else if ( i + 1 < argc && ( arg == QLatin1String( "--customizationfile" ) || arg == QLatin1String( "-z" ) ) )
{
customizationfile = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
}
else if ( i + 1 < argc && ( arg == QLatin1String( "--globalsettingsfile" ) || arg == QLatin1String( "-g" ) ) )
{
globalsettingsfile = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
}
else if ( arg == QLatin1String( "--defaultui" ) || arg == QLatin1String( "-d" ) )
{
myRestoreDefaultWindowState = true;
}
else if ( arg == QLatin1String( "--dxf-export" ) )
{
dxfOutputFile = args[++i];
}
else if ( arg == QLatin1String( "--dxf-extent" ) )
{
QgsLocaleNumC l;
QString ext( args[++i] );
QStringList coords( ext.split( ',' ) );
if ( coords.size() != 4 )
{
std::cerr << "invalid dxf extent " << ext.toStdString() << std::endl;
return 2;
}
for ( int i = 0; i < 4; i++ )
{
bool ok;
double d;
d = coords[i].toDouble( &ok );
if ( !ok )
{
std::cerr << "invalid dxf coordinate " << coords[i].toStdString() << " in extent " << ext.toStdString() << std::endl;
return 2;
}
switch ( i )
{
case 0:
dxfExtent.setXMinimum( d );
break;
case 1:
dxfExtent.setYMinimum( d );
break;
case 2:
dxfExtent.setXMaximum( d );
break;
case 3:
dxfExtent.setYMaximum( d );
break;
}
}
}
else if ( arg == QLatin1String( "--dxf-symbology-mode" ) )
{
QString mode( args[++i] );
if ( mode == QLatin1String( "none" ) )
{
dxfSymbologyMode = QgsDxfExport::NoSymbology;
}
else if ( mode == QLatin1String( "symbollayer" ) )
{
dxfSymbologyMode = QgsDxfExport::SymbolLayerSymbology;
}
else if ( mode == QLatin1String( "feature" ) )
{
dxfSymbologyMode = QgsDxfExport::FeatureSymbology;
}
else
{
std::cerr << "invalid dxf symbology mode " << mode.toStdString() << std::endl;
return 2;
}
}
else if ( arg == QLatin1String( "--dxf-scale-denom" ) )
{
bool ok;
QString scale( args[++i] );
dxfScale = scale.toDouble( &ok );
if ( !ok )
{
std::cerr << "invalid dxf scale " << scale.toStdString() << std::endl;
return 2;
}
}
else if ( arg == QLatin1String( "--dxf-encoding" ) )
{
dxfEncoding = args[++i];
}
else if ( arg == QLatin1String( "--dxf-preset" ) )
{
dxfPreset = args[++i];
}
else if ( arg == QLatin1String( "--" ) )
{
for ( i++; i < args.size(); ++i )
sFileList.append( QDir::toNativeSeparators( QFileInfo( args[i] ).absoluteFilePath() ) );
}
else
{
sFileList.append( QDir::toNativeSeparators( QFileInfo( args[i] ).absoluteFilePath() ) );
}
}
}
/////////////////////////////////////////////////////////////////////
// If no --project was specified, parse the args to look for a //
// .qgs file and set myProjectFileName to it. This allows loading //
// of a project file by clicking on it in various desktop managers //
// where an appropriate mime-type has been set up. //
/////////////////////////////////////////////////////////////////////
if ( sProjectFileName.isEmpty() )
{
// check for a .qgs
for ( int i = 0; i < args.size(); i++ )
{
QString arg = QDir::toNativeSeparators( QFileInfo( args[i] ).absoluteFilePath() );
if ( arg.endsWith( QLatin1String( ".qgs" ), Qt::CaseInsensitive ) )
{
sProjectFileName = arg;
break;
}
}
}
/////////////////////////////////////////////////////////////////////
// Now we have the handlers for the different behaviors...
////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
// Initialize the application and the translation stuff
/////////////////////////////////////////////////////////////////////
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(ANDROID)
bool myUseGuiFlag = nullptr != getenv( "DISPLAY" );
#else
bool myUseGuiFlag = true;
#endif
if ( !myUseGuiFlag )
{
std::cerr << QObject::tr(
"QGIS starting in non-interactive mode not supported.\n"
"You are seeing this message most likely because you "
"have no DISPLAY environment variable set.\n"
).toUtf8().constData();
exit( 1 ); //exit for now until a version of qgis is capabable of running non interactive
}
// GUI customization is enabled according to settings (loaded when instance is created)
// we force disabled here if --nocustomization argument is used
if ( !myCustomization )
{
QgsCustomization::instance()->setEnabled( false );
}
QCoreApplication::setOrganizationName( QgsApplication::QGIS_ORGANIZATION_NAME );
QCoreApplication::setOrganizationDomain( QgsApplication::QGIS_ORGANIZATION_DOMAIN );
QCoreApplication::setApplicationName( QgsApplication::QGIS_APPLICATION_NAME );
QCoreApplication::setAttribute( Qt::AA_DontShowIconsInMenus, false );
#if QT_VERSION >= 0x051000
QCoreApplication::setAttribute( Qt::AA_DisableWindowContextHelpButton, true );
#endif
QgsSettings settings;
if ( configLocalStorageLocation.isEmpty() )
{
if ( getenv( "QGIS_CUSTOM_CONFIG_PATH" ) )
{
configLocalStorageLocation = getenv( "QGIS_CUSTOM_CONFIG_PATH" );
}
else if ( settings.contains( QStringLiteral( "profilesPath" ), QgsSettings::Core ) )
{
configLocalStorageLocation = settings.value( QStringLiteral( "profilesPath" ), "", QgsSettings::Core ).toString();
QgsDebugMsg( QString( "Loading profiles path from global config at %1" ).arg( configLocalStorageLocation ) );
}
// If it is still empty at this point we get it from the standard location.
if ( configLocalStorageLocation.isEmpty() )
{
configLocalStorageLocation = QStandardPaths::standardLocations( QStandardPaths::AppDataLocation ).value( 0 );
}
}
QString rootProfileFolder = QgsUserProfileManager::resolveProfilesFolder( configLocalStorageLocation );
QgsUserProfileManager manager( rootProfileFolder );
QgsUserProfile *profile = manager.getProfile( profileName, true );
QString profileFolder = profile->folder();
profileName = profile->name();
delete profile;
QgsDebugMsg( "User profile details:" );
QgsDebugMsg( QString( "\t - %1" ).arg( profileName ) );
QgsDebugMsg( QString( "\t - %1" ).arg( profileFolder ) );
QgsDebugMsg( QString( "\t - %1" ).arg( rootProfileFolder ) );
QgsApplication myApp( argc, argv, myUseGuiFlag, profileFolder );
// SetUp the QgsSettings Global Settings:
// - use the path specified with --globalsettingsfile path,
// - use the environment if not found
// - use a default location as a fallback
if ( globalsettingsfile.isEmpty() )
{
globalsettingsfile = getenv( "QGIS_GLOBAL_SETTINGS_FILE" );
}
if ( globalsettingsfile.isEmpty() )
{
QString default_globalsettingsfile = QgsApplication::pkgDataPath() + "/qgis_global_settings.ini";
if ( QFile::exists( default_globalsettingsfile ) )
{
globalsettingsfile = default_globalsettingsfile;
}
}
if ( !globalsettingsfile.isEmpty() )
{
if ( ! QgsSettings::setGlobalSettingsPath( globalsettingsfile ) )
{
QgsMessageLog::logMessage( QStringLiteral( "Invalid globalsettingsfile path: %1" ).arg( globalsettingsfile ), QStringLiteral( "QGIS" ) );
}
else
{
QgsMessageLog::logMessage( QStringLiteral( "Successfully loaded globalsettingsfile path: %1" ).arg( globalsettingsfile ), QStringLiteral( "QGIS" ) );
}
}
// Settings migration is only supported on the default profile for now.
if ( profileName == "default" )
{
// Note: this flag is ka version number so that we can reset it once we change the version.
// Note2: Is this a good idea can we do it better.
int firstRunVersion = settings.value( QStringLiteral( "migration/firstRunVersionFlag" ), 0 ).toInt();
bool showWelcome = ( firstRunVersion == 0 || Qgis::QGIS_VERSION_INT > firstRunVersion );
std::unique_ptr< QgsVersionMigration > migration( QgsVersionMigration::canMigrate( 20000, Qgis::QGIS_VERSION_INT ) );
if ( migration && ( mySettingsMigrationForce || migration->requiresMigration() ) )
{
bool runMigration = true;
if ( !mySettingsMigrationForce && showWelcome )
{
QgsFirstRunDialog dlg;
dlg.exec();
runMigration = dlg.migrateSettings();
settings.setValue( QStringLiteral( "migration/firstRunVersionFlag" ), Qgis::QGIS_VERSION_INT );
}
if ( runMigration )
{
QgsDebugMsg( "RUNNING MIGRATION" );
migration->runMigration();
}
}
}
// Redefine QgsApplication::libraryPaths as necessary.
// IMPORTANT: Do *after* QgsApplication myApp(...), but *before* Qt uses any plugins,
// e.g. loading splash screen, setting window icon, etc.
// Always honor QT_PLUGIN_PATH env var or qt.conf, which will
// be part of libraryPaths just after QgsApplication creation.
#ifdef Q_OS_WIN
// For non static builds on win (static builds are not supported)
// we need to be sure we can find the qt image plugins.
QCoreApplication::addLibraryPath( QApplication::applicationDirPath()
+ QDir::separator() + "qtplugins" );
#endif
#ifdef Q_OS_MAC
// Resulting libraryPaths has critical QGIS plugin paths first, then any Qt plugin paths, then
// any dev-defined paths (in app's qt.conf) and/or user-defined paths (QT_PLUGIN_PATH env var).
//
// NOTE: Minimizes, though does not fully protect against, crashes due to dev/user-defined libs
// built against a different Qt/QGIS, while still allowing custom C++ plugins to load.
QStringList libPaths( QCoreApplication::libraryPaths() );
QgsDebugMsgLevel( QStringLiteral( "Initial macOS QCoreApplication::libraryPaths: %1" )
.arg( libPaths.join( " " ) ), 4 );
// Strip all critical paths that should always be prepended
if ( libPaths.removeAll( QDir::cleanPath( QgsApplication::pluginPath() ) ) )
{
QgsDebugMsgLevel( QStringLiteral( "QgsApplication::pluginPath removed from initial libraryPaths" ), 4 );
}
if ( libPaths.removeAll( QCoreApplication::applicationDirPath() ) )
{
QgsDebugMsgLevel( QStringLiteral( "QCoreApplication::applicationDirPath removed from initial libraryPaths" ), 4 );
}
// Prepend path, so a standard Qt bundle directory is parsed
QgsDebugMsgLevel( QStringLiteral( "Prepending QCoreApplication::applicationDirPath to libraryPaths" ), 4 );
libPaths.prepend( QCoreApplication::applicationDirPath() );
// Check if we are running in a 'release' app bundle, i.e. contains copied-in
// standard Qt-specific plugin subdirectories (ones never created by QGIS, e.g. 'sqldrivers' is).
// Note: bundleclicked(...) is inadequate to determine which *type* of bundle was opened, e.g. release or build dir.
// An app bundled with QGIS_MACAPP_BUNDLE > 0 is considered a release bundle.
QString relLibPath( QDir::cleanPath( QCoreApplication::applicationDirPath().append( "/../PlugIns" ) ) );
// Note: relLibPath becomes the defacto QT_PLUGINS_DIR of a release app bundle
if ( QFile::exists( relLibPath + QStringLiteral( "/imageformats" ) )
&& QFile::exists( relLibPath + QStringLiteral( "/codecs" ) ) )
{
// We are in a release app bundle.
// Strip QT_PLUGINS_DIR because it will crash a launched release app bundle, since
// the appropriate Qt frameworks and plugins have been copied into the bundle.
if ( libPaths.removeAll( QT_PLUGINS_DIR ) )
{
QgsDebugMsgLevel( QStringLiteral( "QT_PLUGINS_DIR removed from initial libraryPaths" ), 4 );
}
// Prepend the Plugins path, so copied-in Qt plugin bundle directories are parsed.
QgsDebugMsgLevel( QStringLiteral( "Prepending <bundle>/Plugins to libraryPaths" ), 4 );
libPaths.prepend( relLibPath );
// TODO: see if this or another method can be used to avoid QCA's install prefix plugins
// from being parsed and loaded (causes multi-Qt-loaded errors when bundled Qt should
// be the only one loaded). QCA core (> v2.1.3) needs an update first.
//setenv( "QCA_PLUGIN_PATH", relLibPath.toUtf8().constData(), 1 );
}
else
{
// We are either running from build dir bundle, or launching Mach-O binary directly.
// Add system Qt plugins, since they are not bundled, and not always referenced by default.
// An app bundled with QGIS_MACAPP_BUNDLE = 0 will still have Plugins/qgis in it.
// Note: Don't always prepend.
// User may have already defined it in QT_PLUGIN_PATH in a specific order.
if ( !libPaths.contains( QT_PLUGINS_DIR ) )
{
QgsDebugMsgLevel( QStringLiteral( "Prepending QT_PLUGINS_DIR to libraryPaths" ), 4 );
libPaths.prepend( QT_PLUGINS_DIR );
}
}
QgsDebugMsgLevel( QStringLiteral( "Prepending QgsApplication::pluginPath to libraryPaths" ), 4 );
libPaths.prepend( QDir::cleanPath( QgsApplication::pluginPath() ) );
// Redefine library search paths.
QCoreApplication::setLibraryPaths( libPaths );
QgsDebugMsgLevel( QStringLiteral( "Rewritten macOS QCoreApplication::libraryPaths: %1" )
.arg( QCoreApplication::libraryPaths().join( " " ) ), 4 );
#endif
#ifdef Q_OS_MAC
// Set hidpi icons; use SVG icons, as PNGs will be relatively too small
QCoreApplication::setAttribute( Qt::AA_UseHighDpiPixmaps );
// Set 1024x1024 icon for dock, app switcher, etc., rendering
myApp.setWindowIcon( QIcon( QgsApplication::iconsPath() + QStringLiteral( "qgis-icon-macos.png" ) ) );
#else
myApp.setWindowIcon( QIcon( QgsApplication::appIconPath() ) );
#endif
// TODO: use QgsSettings
QSettings *customizationsettings = nullptr;
// Using the customizationfile option always overrides the option and config path options.
if ( !customizationfile.isEmpty() )
{
customizationsettings = new QSettings( customizationfile, QSettings::IniFormat );
QgsCustomization::instance()->setEnabled( true );
}
else
{
customizationsettings = new QSettings( QStringLiteral( "QGIS" ), QStringLiteral( "QGISCUSTOMIZATION2" ) );
}
// Load and set possible default customization, must be done after QgsApplication init and QgsSettings ( QCoreApplication ) init
QgsCustomization::instance()->setSettings( customizationsettings );
QgsCustomization::instance()->loadDefault();
#ifdef Q_OS_MACX
// If the GDAL plugins are bundled with the application and GDAL_DRIVER_PATH
// is not already defined, use the GDAL plugins in the application bundle.
QString gdalPlugins( QCoreApplication::applicationDirPath().append( "/lib/gdalplugins" ) );
if ( QFile::exists( gdalPlugins ) && !getenv( "GDAL_DRIVER_PATH" ) )
{
setenv( "GDAL_DRIVER_PATH", gdalPlugins.toUtf8(), 1 );
}
// Point GDAL_DATA at any GDAL share directory embedded in the app bundle
if ( !getenv( "GDAL_DATA" ) )
{
QStringList gdalShares;
QString appResources( QDir::cleanPath( QgsApplication::pkgDataPath() ) );
gdalShares << QCoreApplication::applicationDirPath().append( "/share/gdal" )
<< appResources.append( "/share/gdal" )
<< appResources.append( "/gdal" );
Q_FOREACH ( const QString &gdalShare, gdalShares )
{
if ( QFile::exists( gdalShare ) )
{
setenv( "GDAL_DATA", gdalShare.toUtf8().constData(), 1 );
break;
}
}
}
#endif
QgsSettings mySettings;
// update any saved setting for older themes to new default 'gis' theme (2013-04-15)
if ( mySettings.contains( QStringLiteral( "/Themes" ) ) )
{
QString theme = mySettings.value( QStringLiteral( "Themes" ), "default" ).toString();
if ( theme == QLatin1String( "gis" )
|| theme == QLatin1String( "classic" )
|| theme == QLatin1String( "nkids" ) )
{
mySettings.setValue( QStringLiteral( "Themes" ), QStringLiteral( "default" ) );
}
}
// custom environment variables
QMap<QString, QString> systemEnvVars = QgsApplication::systemEnvVars();
bool useCustomVars = mySettings.value( QStringLiteral( "qgis/customEnvVarsUse" ), QVariant( false ) ).toBool();
if ( useCustomVars )
{
QStringList customVarsList = mySettings.value( QStringLiteral( "qgis/customEnvVars" ), "" ).toStringList();
if ( !customVarsList.isEmpty() )
{
Q_FOREACH ( const QString &varStr, customVarsList )
{
int pos = varStr.indexOf( QLatin1Char( '|' ) );
if ( pos == -1 )
continue;
QString envVarApply = varStr.left( pos );
QString varStrNameValue = varStr.mid( pos + 1 );
pos = varStrNameValue.indexOf( QLatin1Char( '=' ) );
if ( pos == -1 )
continue;
QString envVarName = varStrNameValue.left( pos );
QString envVarValue = varStrNameValue.mid( pos + 1 );
if ( systemEnvVars.contains( envVarName ) )
{
if ( envVarApply == QLatin1String( "prepend" ) )
{
envVarValue += systemEnvVars.value( envVarName );
}
else if ( envVarApply == QLatin1String( "append" ) )
{
envVarValue = systemEnvVars.value( envVarName ) + envVarValue;
}
}
if ( systemEnvVars.contains( envVarName ) && envVarApply == QLatin1String( "unset" ) )
{
#ifdef Q_OS_WIN
putenv( envVarName.toUtf8().constData() );
#else
unsetenv( envVarName.toUtf8().constData() );
#endif
}
else
{
#ifdef Q_OS_WIN
if ( envVarApply != "undefined" || !getenv( envVarName.toUtf8().constData() ) )
putenv( QString( "%1=%2" ).arg( envVarName ).arg( envVarValue ).toUtf8().constData() );
#else
setenv( envVarName.toUtf8().constData(), envVarValue.toUtf8().constData(), envVarApply == QLatin1String( "undefined" ) ? 0 : 1 );
#endif
}
}
}
}
#ifdef QGISDEBUG
QgsFontUtils::loadStandardTestFonts( QStringList() << QStringLiteral( "Roman" ) << QStringLiteral( "Bold" ) );
#endif
// Set the application style. If it's not set QT will use the platform style except on Windows
// as it looks really ugly so we use QPlastiqueStyle.
QString presetStyle = mySettings.value( QStringLiteral( "qgis/style" ) ).toString();
QString activeStyleName = presetStyle;
if ( activeStyleName.isEmpty() ) // not set, using default style
{
//not set, check default
activeStyleName = QApplication::style()->metaObject()->className();
}
if ( activeStyleName.contains( QStringLiteral( "adwaita" ), Qt::CaseInsensitive ) )
{
//never allow Adwaita themes - the Qt variants of these are VERY broken
//for apps like QGIS. E.g. oversized controls like spinbox widgets prevent actually showing
//any content in these widgets, leaving a very bad impression of QGIS
//note... we only do this if there's a known good style available (fusion), as SOME
//style choices can cause Qt apps to crash...
if ( QStyleFactory::keys().contains( QStringLiteral( "fusion" ), Qt::CaseInsensitive ) )
{
presetStyle = QStringLiteral( "fusion" );
}
}
if ( !presetStyle.isEmpty() )
{
QApplication::setStyle( presetStyle );
mySettings.setValue( QStringLiteral( "qgis/style" ), QApplication::style()->objectName() );
}
/* Translation file for QGIS.
*/
QString i18nPath = QgsApplication::i18nPath();
QString myUserLocale = mySettings.value( QStringLiteral( "locale/userLocale" ), "" ).toString();
bool myLocaleOverrideFlag = mySettings.value( QStringLiteral( "locale/overrideFlag" ), false ).toBool();
//
// Priority of translation is:
// - command line
// - user specified in options dialog (with group checked on)
// - system locale
//
// When specifying from the command line it will change the user
// specified user locale
//
if ( !myTranslationCode.isNull() && !myTranslationCode.isEmpty() )
{
mySettings.setValue( QStringLiteral( "locale/userLocale" ), myTranslationCode );
}
else
{
if ( !myLocaleOverrideFlag || myUserLocale.isEmpty() )
{
myTranslationCode = QLocale::system().name();
//setting the locale/userLocale when the --lang= option is not set will allow third party
//plugins to always use the same locale as the QGIS, otherwise they can be out of sync
mySettings.setValue( QStringLiteral( "locale/userLocale" ), myTranslationCode );
}
else
{
myTranslationCode = myUserLocale;
}
}
QTranslator qgistor( nullptr );
QTranslator qttor( nullptr );
if ( myTranslationCode != QLatin1String( "C" ) )
{
if ( qgistor.load( QStringLiteral( "qgis_" ) + myTranslationCode, i18nPath ) )
{
myApp.installTranslator( &qgistor );
}
else
{
QgsDebugMsg( QStringLiteral( "loading of qgis translation failed %1/qgis_%2" ).arg( i18nPath, myTranslationCode ) );
}
/* Translation file for Qt.
* The strings from the QMenuBar context section are used by Qt/Mac to shift
* the About, Preferences and Quit items to the Mac Application menu.
* These items must be translated identically in both qt_ and qgis_ files.
*/
if ( qttor.load( QStringLiteral( "qt_" ) + myTranslationCode, QLibraryInfo::location( QLibraryInfo::TranslationsPath ) ) )
{
myApp.installTranslator( &qttor );
}
else
{
QgsDebugMsg( QStringLiteral( "loading of qt translation failed %1/qt_%2" ).arg( QLibraryInfo::location( QLibraryInfo::TranslationsPath ), myTranslationCode ) );
}
}
// set authentication database directory
if ( !authdbdirectory.isEmpty() )
{
QgsApplication::setAuthDatabaseDirPath( authdbdirectory );
}
//set up splash screen
QString mySplashPath( QgsCustomization::instance()->splashPath() );
QPixmap myPixmap( mySplashPath + QStringLiteral( "splash.png" ) );
int w = 600 * qApp->desktop()->logicalDpiX() / 96;
int h = 300 * qApp->desktop()->logicalDpiY() / 96;
QSplashScreen *mypSplash = new QSplashScreen( myPixmap.scaled( w, h, Qt::KeepAspectRatio, Qt::SmoothTransformation ) );
if ( !myHideSplash && !mySettings.value( QStringLiteral( "qgis/hideSplash" ) ).toBool() )
{
//for win and linux we can just automask and png transparency areas will be used
mypSplash->setMask( myPixmap.mask() );
mypSplash->show();
}
// optionally restore default window state
// use restoreDefaultWindowState setting only if NOT using command line (then it is set already)
if ( myRestoreDefaultWindowState || mySettings.value( QStringLiteral( "qgis/restoreDefaultWindowState" ), false ).toBool() )
{
QgsDebugMsg( "Resetting /UI/state settings!" );
mySettings.remove( QStringLiteral( "/UI/state" ) );
mySettings.remove( QStringLiteral( "/qgis/restoreDefaultWindowState" ) );
}
// set max. thread count
// this should be done in QgsApplication::init() but it doesn't know the settings dir.
QgsApplication::setMaxThreads( mySettings.value( QStringLiteral( "qgis/max_threads" ), -1 ).toInt() );
QgisApp *qgis = new QgisApp( mypSplash, myRestorePlugins, mySkipVersionCheck, rootProfileFolder, profileName ); // "QgisApp" used to find canonical instance
qgis->setObjectName( QStringLiteral( "QgisApp" ) );
myApp.connect(
&myApp, SIGNAL( preNotify( QObject *, QEvent *, bool * ) ),
//qgis, SLOT( preNotify( QObject *, QEvent *))
QgsCustomization::instance(), SLOT( preNotify( QObject *, QEvent *, bool * ) )
);
/////////////////////////////////////////////////////////////////////
// Load a project file if one was specified
/////////////////////////////////////////////////////////////////////
if ( ! sProjectFileName.isEmpty() )
{
qgis->openProject( sProjectFileName );
}
/////////////////////////////////////////////////////////////////////
// autoload any file names that were passed in on the command line
/////////////////////////////////////////////////////////////////////
for ( const QString &layerName : qgis::as_const( sFileList ) )
{
QgsDebugMsg( QString( "Trying to load file : %1" ).arg( layerName ) );
// don't load anything with a .qgs extension - these are project files
if ( !layerName.endsWith( QLatin1String( ".qgs" ), Qt::CaseInsensitive )
&& !QgsZipUtils::isZipFile( layerName ) )
{
qgis->openLayer( layerName );
}
}
/////////////////////////////////////////////////////////////////////
// Set initial extent if requested
/////////////////////////////////////////////////////////////////////
if ( ! myInitialExtent.isEmpty() )
{
QgsLocaleNumC l;
double coords[4];
int pos, posOld = 0;
bool ok = true;
// parse values from string
// extent is defined by string "xmin,ymin,xmax,ymax"
for ( int i = 0; i < 3; i++ )
{
// find comma and get coordinate
pos = myInitialExtent.indexOf( ',', posOld );
if ( pos == -1 )
{
ok = false;
break;
}
coords[i] = myInitialExtent.midRef( posOld, pos - posOld ).toDouble( &ok );
if ( !ok )
break;
posOld = pos + 1;
}
// parse last coordinate
if ( ok )
coords[3] = myInitialExtent.midRef( posOld ).toDouble( &ok );
if ( !ok )
{
QgsDebugMsg( "Error while parsing initial extent!" );
}
else
{
// set extent from parsed values
QgsRectangle rect( coords[0], coords[1], coords[2], coords[3] );
qgis->setExtent( rect );
}
}
if ( !pythonfile.isEmpty() )
{
#ifdef Q_OS_WIN
//replace backslashes with forward slashes
pythonfile.replace( '\\', '/' );
#endif
QgsPythonRunner::run( QStringLiteral( "exec(open('%1').read())" ).arg( pythonfile ) );
}
/////////////////////////////////`////////////////////////////////////
// Take a snapshot of the map view then exit if snapshot mode requested
/////////////////////////////////////////////////////////////////////
if ( !mySnapshotFileName.isEmpty() )
{
/*You must have at least one paintEvent() delivered for the window to be
rendered properly.
It looks like you don't run the event loop in non-interactive mode, so the
event is never occurring.
To achieve this without running the event loop: show the window, then call
qApp->processEvents(), grab the pixmap, save it, hide the window and exit.
*/
//qgis->show();
myApp.processEvents();
QPixmap *myQPixmap = new QPixmap( mySnapshotWidth, mySnapshotHeight );
myQPixmap->fill();
qgis->saveMapAsImage( mySnapshotFileName, myQPixmap );
myApp.processEvents();
qgis->hide();
return 1;
}
if ( !dxfOutputFile.isEmpty() )
{
qgis->hide();
QgsDxfExport dxfExport;
dxfExport.setSymbologyScale( dxfScale );
dxfExport.setSymbologyExport( dxfSymbologyMode );
dxfExport.setExtent( dxfExtent );
QStringList layerIds;
QList< QPair<QgsVectorLayer *, int > > layers;
if ( !dxfPreset.isEmpty() )
{
Q_FOREACH ( QgsMapLayer *layer, QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( dxfPreset ) )
{
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
if ( !vl )
continue;
layers << qMakePair<QgsVectorLayer *, int>( vl, -1 );
layerIds << vl->id();
}
}
else
{
Q_FOREACH ( QgsMapLayer *ml, QgsProject::instance()->mapLayers() )
{
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
if ( !vl )
continue;
layers << qMakePair<QgsVectorLayer *, int>( vl, -1 );
layerIds << vl->id();
}
}
if ( !layers.isEmpty() )
{
dxfExport.addLayers( layers );
}
QFile dxfFile;
if ( dxfOutputFile == QLatin1String( "-" ) )
{
if ( !dxfFile.open( stdout, QIODevice::WriteOnly | QIODevice::Truncate ) )
{
std::cerr << "could not open stdout" << std::endl;
return 2;
}
}
else
{
if ( !dxfOutputFile.endsWith( QLatin1String( ".dxf" ), Qt::CaseInsensitive ) )
dxfOutputFile += QLatin1String( ".dxf" );
dxfFile.setFileName( dxfOutputFile );
}
int res = dxfExport.writeToFile( &dxfFile, dxfEncoding );
if ( res )
std::cerr << "dxf output failed with error code " << res << std::endl;
delete qgis;
return res;
}
/////////////////////////////////////////////////////////////////////
// Continue on to interactive gui...
/////////////////////////////////////////////////////////////////////
qgis->show();
myApp.connect( &myApp, SIGNAL( lastWindowClosed() ), &myApp, SLOT( quit() ) );
mypSplash->finish( qgis );
delete mypSplash;
qgis->completeInitialization();
#if defined(ANDROID)
// fix for Qt Ministro hiding app's menubar in favor of native Android menus
qgis->menuBar()->setNativeMenuBar( false );
qgis->menuBar()->setVisible( true );
#endif
int retval = myApp.exec();
delete qgis;
return retval;
}