Skip to content
Permalink
Browse files

Allow plugins and scripts to register custom logic to prevent

the QGIS application from exiting

This interface allows plugins to implement custom logic to determine whether it is safe
for the application to exit, e.g. by checking whether the plugin or script has any
unsaved changes which should be saved or discarded before allowing QGIS to exit.
  • Loading branch information
nyalldawson committed Oct 6, 2020
1 parent 64bf347 commit df6c8d74cd745ce59ecd2a6bce2dc59952955e16
@@ -1234,6 +1234,30 @@ Unregister a previously registered tool factory from the development/debugging t
.. seealso:: :py:func:`registerDevToolWidgetFactory`

.. versionadded:: 3.14
%End

virtual void registerApplicationExitBlocker( QgsApplicationExitBlockerInterface *blocker ) = 0;
%Docstring
Register a new application exit blocker, which can be used to prevent the QGIS application
from exiting while a plugin or script has unsaved changes.

.. note::

Ownership of ``blocker`` is not transferred, and the blocker must
be unregistered when plugin is unloaded.

.. seealso:: :py:func:`unregisterApplicationExitBlocker`

.. versionadded:: 3.16
%End

virtual void unregisterApplicationExitBlocker( QgsApplicationExitBlockerInterface *blocker ) = 0;
%Docstring
Unregister a previously registered application exit ``blocker``.

.. seealso:: :py:func:`registerApplicationExitBlocker`

.. versionadded:: 3.16
%End

virtual void registerCustomDropHandler( QgsCustomDropHandler *handler ) = 0;
@@ -0,0 +1,77 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgsapplicationexitblockerinterface.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/



class QgsApplicationExitBlockerInterface
{
%Docstring
An interface that may be implemented to allow plugins or scripts to temporarily block
the QGIS application from exiting.

This interface allows plugins to implement custom logic to determine whether it is safe
for the application to exit, e.g. by checking whether the plugin or script has any
unsaved changes which should be saved or discarded before allowing QGIS to exit.

QgsApplicationExitBlockerInterface are registered via the iface object:

Example
-------

.. code-block:: python

class MyPluginExitBlocker(QgsApplicationExitBlockerInterface):

def allowExit(self):
if self.has_unsaved_changes():
# show a warning prompt
# ...
# prevent QGIS application from exiting
return False

# allow QGIS application to exit
return True

my_blocker = MyPluginExitBlocker()
iface.registerApplicationExitBlocker(my_blocker)

.. versionadded:: 3.16
%End

%TypeHeaderCode
#include "qgsapplicationexitblockerinterface.h"
%End
public:

virtual ~QgsApplicationExitBlockerInterface();

virtual bool allowExit() = 0;
%Docstring
Called whenever the QGIS application has been asked to exit by a user.

The subclass can use this method to implement custom logic handling whether it is safe
for the application to exit, e.g. by checking whether the plugin or script has any unsaved
changes which should be saved or discarded before allowing QGIS to exit.

The implementation should return ``True`` if it is safe for QGIS to exit, or ``False`` if it
wishes to prevent the application from exiting.

.. note::

It is safe to use GUI widgets in implementations of this function, including message
boxes or custom dialogs with event loops.
%End
};

/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgsapplicationexitblockerinterface.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
@@ -7,6 +7,7 @@
%Include auto_generated/qgsadvanceddigitizingfloater.sip
%Include auto_generated/qgsaggregatetoolbutton.sip
%Include auto_generated/qgsalignmentcombobox.sip
%Include auto_generated/qgsapplicationexitblockerinterface.sip
%Include auto_generated/qgsattributedialog.sip
%Include auto_generated/qgsattributeeditorcontext.sip
%Include auto_generated/qgsattributeform.sip
@@ -181,6 +181,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
#include "qgsauthsslerrorsdialog.h"
#endif
#include "qgsappscreenshots.h"
#include "qgsapplicationexitblockerinterface.h"
#include "qgsbookmarks.h"
#include "qgsbookmarkeditordialog.h"
#include "qgsbrowserdockwidget.h"
@@ -6388,7 +6389,7 @@ void QgisApp::fileExit()
}

QgsCanvasRefreshBlocker refreshBlocker;
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() )
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && checkExitBlockers() && saveDirty() )
{
closeProject();
userProfileManager()->setDefaultFromActive();
@@ -12686,6 +12687,16 @@ void QgisApp::unregisterDevToolFactory( QgsDevToolWidgetFactory *factory )
mDevToolFactories.removeAll( factory );
}

void QgisApp::registerApplicationExitBlocker( QgsApplicationExitBlockerInterface *blocker )
{
mApplicationExitBlockers << blocker;
}

void QgisApp::unregisterApplicationExitBlocker( QgsApplicationExitBlockerInterface *blocker )
{
mApplicationExitBlockers.removeAll( blocker );
}

QgsMapLayer *QgisApp::activeLayer()
{
return mLayerTreeView ? mLayerTreeView->currentLayer() : nullptr;
@@ -13237,6 +13248,16 @@ bool QgisApp::checkMemoryLayers()
return close;
}

bool QgisApp::checkExitBlockers()
{
for ( QgsApplicationExitBlockerInterface *blocker : qgis::as_const( mApplicationExitBlockers ) )
{
if ( !blocker->allowExit() )
return false;
}
return true;
}

bool QgisApp::checkTasksDependOnProject()
{
QSet< QString > activeTaskDescriptions;
@@ -111,6 +111,7 @@ class Qgs3DMapCanvasDockWidget;
class QgsHandleBadLayersHandler;
class QgsNetworkAccessManager;
class QgsGpsConnection;
class QgsApplicationExitBlockerInterface;

class QDomDocument;
class QNetworkReply;
@@ -746,6 +747,23 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
//! Unregister a previously registered dev tool factory
void unregisterDevToolFactory( QgsDevToolWidgetFactory *factory );

/**
* Register a new application exit blocker, which can be used to prevent the QGIS application
* from exiting while a plugin or script has unsaved changes.
*
* \note Ownership of \a blocker is not transferred, and the blocker must
* be unregistered when plugin is unloaded.
*
* \see unregisterApplicationExitBlocker()
*/
void registerApplicationExitBlocker( QgsApplicationExitBlockerInterface *blocker );

/**
* Unregister a previously registered application exit \a blocker.
* \see registerApplicationExitBlocker()
*/
void unregisterApplicationExitBlocker( QgsApplicationExitBlockerInterface *blocker );

//! Register a new custom drop handler.
void registerCustomDropHandler( QgsCustomDropHandler *handler );

@@ -2094,6 +2112,12 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
*/
bool checkMemoryLayers();

/**
* Checks whether any registered application exit blockers should prevent
* the application from exiting.
*/
bool checkExitBlockers();

//! Checks for running tasks dependent on the open project
bool checkTasksDependOnProject();

@@ -2569,6 +2593,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow

QList<QgsDevToolWidgetFactory * > mDevToolFactories;

QList<QgsApplicationExitBlockerInterface * > mApplicationExitBlockers;

QVector<QPointer<QgsCustomDropHandler>> mCustomDropHandlers;
QVector<QPointer<QgsCustomProjectOpenHandler>> mCustomProjectOpenHandlers;
QVector<QPointer<QgsLayoutCustomDropHandler>> mCustomLayoutDropHandlers;
@@ -571,6 +571,16 @@ void QgisAppInterface::unregisterDevToolWidgetFactory( QgsDevToolWidgetFactory *
qgis->unregisterDevToolFactory( factory );
}

void QgisAppInterface::registerApplicationExitBlocker( QgsApplicationExitBlockerInterface *blocker )
{
qgis->registerApplicationExitBlocker( blocker );
}

void QgisAppInterface::unregisterApplicationExitBlocker( QgsApplicationExitBlockerInterface *blocker )
{
qgis->unregisterApplicationExitBlocker( blocker );
}

void QgisAppInterface::registerCustomDropHandler( QgsCustomDropHandler *handler )
{
qgis->registerCustomDropHandler( handler );
@@ -150,6 +150,8 @@ class APP_EXPORT QgisAppInterface : public QgisInterface
void unregisterProjectPropertiesWidgetFactory( QgsOptionsWidgetFactory *factory ) override;
void registerDevToolWidgetFactory( QgsDevToolWidgetFactory *factory ) override;
void unregisterDevToolWidgetFactory( QgsDevToolWidgetFactory *factory ) override;
void registerApplicationExitBlocker( QgsApplicationExitBlockerInterface *blocker ) override;
void unregisterApplicationExitBlocker( QgsApplicationExitBlockerInterface *blocker ) override;
void registerCustomDropHandler( QgsCustomDropHandler *handler ) override;
void unregisterCustomDropHandler( QgsCustomDropHandler *handler ) override;
void registerCustomProjectOpenHandler( QgsCustomProjectOpenHandler *handler ) override;
@@ -592,6 +592,7 @@ SET(QGIS_GUI_HDRS
qgsadvanceddigitizingfloater.h
qgsaggregatetoolbutton.h
qgsalignmentcombobox.h
qgsapplicationexitblockerinterface.h
qgsattributedialog.h
qgsattributeeditorcontext.h
qgsattributeform.h
@@ -65,6 +65,7 @@ class QgsMeshLayer;
class QgsBrowserGuiModel;
class QgsDevToolWidgetFactory;
class QgsGpsConnection;
class QgsApplicationExitBlockerInterface;


/**
@@ -1025,6 +1026,25 @@ class GUI_EXPORT QgisInterface : public QObject
*/
virtual void unregisterDevToolWidgetFactory( QgsDevToolWidgetFactory *factory ) = 0;

/**
* Register a new application exit blocker, which can be used to prevent the QGIS application
* from exiting while a plugin or script has unsaved changes.
*
* \note Ownership of \a blocker is not transferred, and the blocker must
* be unregistered when plugin is unloaded.
*
* \see unregisterApplicationExitBlocker()
* \since QGIS 3.16
*/
virtual void registerApplicationExitBlocker( QgsApplicationExitBlockerInterface *blocker ) = 0;

/**
* Unregister a previously registered application exit \a blocker.
* \see registerApplicationExitBlocker()
* \since QGIS 3.16
*/
virtual void unregisterApplicationExitBlocker( QgsApplicationExitBlockerInterface *blocker ) = 0;

/**
* Register a new custom drop \a handler.
* \note Ownership of \a handler is not transferred, and the handler must
@@ -0,0 +1,78 @@
/***************************************************************************
qgsapplicationexitblockerinterface.h
---------------------
begin : October 2020
copyright : (C) 2020 by Nyall Dawson
email : nyall dot dawson at gmail dot 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. *
* *
***************************************************************************/

#ifndef QGSAPPLICATIONEXITBLOCKERINTERFACE_H
#define QGSAPPLICATIONEXITBLOCKERINTERFACE_H

#include "qgis_gui.h"
#include <QStringList>
#include <QObject>

/**
* \ingroup gui
* An interface that may be implemented to allow plugins or scripts to temporarily block
* the QGIS application from exiting.
*
* This interface allows plugins to implement custom logic to determine whether it is safe
* for the application to exit, e.g. by checking whether the plugin or script has any
* unsaved changes which should be saved or discarded before allowing QGIS to exit.
*
* QgsApplicationExitBlockerInterface are registered via the iface object:
*
* ### Example
*
* \code{.py}
* class MyPluginExitBlocker(QgsApplicationExitBlockerInterface):
*
* def allowExit(self):
* if self.has_unsaved_changes():
* # show a warning prompt
* # ...
* # prevent QGIS application from exiting
* return False
*
* # allow QGIS application to exit
* return True
*
* my_blocker = MyPluginExitBlocker()
* iface.registerApplicationExitBlocker(my_blocker)
* \endcode
*
* \since QGIS 3.16
*/
class GUI_EXPORT QgsApplicationExitBlockerInterface
{

public:

virtual ~QgsApplicationExitBlockerInterface() = default;

/**
* Called whenever the QGIS application has been asked to exit by a user.
*
* The subclass can use this method to implement custom logic handling whether it is safe
* for the application to exit, e.g. by checking whether the plugin or script has any unsaved
* changes which should be saved or discarded before allowing QGIS to exit.
*
* The implementation should return TRUE if it is safe for QGIS to exit, or FALSE if it
* wishes to prevent the application from exiting.
*
* \note It is safe to use GUI widgets in implementations of this function, including message
* boxes or custom dialogs with event loops.
*/
virtual bool allowExit() = 0;
};

#endif // QgsCustomProjectOpenHandler_H

0 comments on commit df6c8d7

Please sign in to comment.
You can’t perform that action at this time.