Merge pull request #5200 from nyalldawson/drop_handler

[FEATURE][processing] Allow dropping model files onto QGIS window to execute them
This commit is contained in:
Nyall Dawson 2017-09-15 19:42:46 +10:00 committed by GitHub
commit 5bd164b644
8 changed files with 227 additions and 48 deletions

View File

@ -1,7 +1,6 @@
// Include auto-generated SIP files
%Include qgsattributeeditorcontext.sip
%Include qgsattributeforminterface.sip
%Include qgscustomdrophandler.sip
%Include qgsdetaileditemdata.sip
%Include qgsexpressionbuilderdialog.sip
%Include qgsgeometryrubberband.sip
@ -91,6 +90,7 @@
%Include qgsconfigureshortcutsdialog.sip
%Include qgscredentialdialog.sip
%Include qgscurveeditorwidget.sip
%Include qgscustomdrophandler.sip
%Include qgsdetaileditemdelegate.sip
%Include qgsdetaileditemwidget.sip
%Include qgsdial.sip

View File

@ -8,12 +8,30 @@
class QgsCustomDropHandler
class QgsCustomDropHandler : QObject
{
%Docstring
Abstract base class that may be implemented to handle new types of data to be dropped in QGIS.
Implementations will be used when a QgsMimeDataUtils.Uri has layerType equal to "custom",
and the providerKey is equal to key() returned by the implementation.
Implementations have three approaches they can use to handle drops.
1. The simplest approach is to implement handeFileDrop() when they need to handle
dropped files (i.e. with mime type "text/uri-list").
2. Reimplement handleCustomUriDrop() when they want to handle dropped custom
QgsMimeDataUtils.Uri entries, for instance handling dropping custom entries
from the browser tree (with mime type "application/x-vnd.qgis.qgis.uri"). In
this case the implementation's customUriProviderKey() must match the uri
entry's providerKey.
3. Reimplement handleMimeData() to directly handle dropped QMimeData.
Subclasses should take care when overriding this method. When a drop event
occurs, Qt will lock the source application of the drag for the duration
of the drop event handling via handleMimeData() (e.g. dragging files from
explorer to QGIS will lock the explorer window until the drop handling has
been complete). Accordingly handleMimeData() implementations must return
quickly and defer any intensive or slow processing.
.. versionadded:: 3.0
%End
@ -23,15 +41,55 @@ class QgsCustomDropHandler
public:
virtual ~QgsCustomDropHandler();
virtual QString key() const = 0;
virtual QString customUriProviderKey() const;
%Docstring
Type of custom URI recognized by the handler
Type of custom URI recognized by the handler. This must match
the URI entry's providerKey in order for handleCustomUriDrop()
to be called.
.. seealso:: handleCustomUriDrop()
:rtype: str
%End
virtual void handleDrop( const QgsMimeDataUtils::Uri &uri ) const = 0;
virtual void handleCustomUriDrop( const QgsMimeDataUtils::Uri &uri ) const;
%Docstring
Method called from QGIS after a drop event with custom URI known by the handler
Called from QGIS after a drop event with custom URI known by the handler.
In order for handleCustomUriDrop() to be called, subclasses must
also implement customUriProviderKey() to indicate the providerKey
value which the handler accepts.
.. seealso:: customUriProviderKey()
%End
virtual void handleMimeData( const QMimeData *data );
%Docstring
Called when the specified mime ``data`` has been dropped onto QGIS.
The base class implementation does nothing.
Subclasses should take care when overriding this method. When a drop event
occurs, Qt will lock the source application of the drag for the duration
of the drop event handling (e.g. dragging files from explorer to QGIS will
lock the explorer window until the drop handling has been complete).
Accordingly, only implementations must be lightweight and return ASAP.
(For instance by copying the relevant parts of ``data`` and then handling
the data after a short timeout).
%End
virtual bool handleFileDrop( const QString &file );
%Docstring
Called when the specified ``file`` has been dropped onto QGIS. If true
is returned, then the handler has accepted this file and it should not
be further processed (e.g. by other QgsCustomDropHandlers).
The base class implementation does nothing.
This method is not called directly while drop handling is occurring,
so the limitations described in handleMimeData() about returning
quickly do not apply.
:rtype: bool
%End
};

View File

@ -32,13 +32,16 @@ import os
import sys
from qgis.core import (QgsApplication,
QgsProcessingUtils)
from qgis.gui import QgsOptionsWidgetFactory
QgsProcessingUtils,
QgsProcessingModelAlgorithm)
from qgis.gui import (QgsOptionsWidgetFactory,
QgsCustomDropHandler)
from qgis.PyQt.QtCore import Qt, QCoreApplication, QDir
from qgis.PyQt.QtWidgets import QMenu, QAction
from qgis.PyQt.QtGui import QIcon
from processing.core.Processing import Processing
from processing.gui.AlgorithmDialog import AlgorithmDialog
from processing.gui.ProcessingToolbox import ProcessingToolbox
from processing.gui.HistoryDialog import HistoryDialog
from processing.gui.ConfigDialog import ConfigOptionsPage
@ -66,6 +69,23 @@ class ProcessingOptionsFactory(QgsOptionsWidgetFactory):
return ConfigOptionsPage(parent)
class ProcessingDropHandler(QgsCustomDropHandler):
def handleFileDrop(self, file):
if not file.lower().endswith('.model3'):
return False
alg = QgsProcessingModelAlgorithm()
if not alg.fromFile(file):
return False
alg.setProvider(QgsApplication.processingRegistry().providerById('model'))
dlg = AlgorithmDialog(alg)
dlg.setAttribute(Qt.WA_DeleteOnClose)
dlg.show()
return True
class ProcessingPlugin(object):
def __init__(self, iface):
@ -73,6 +93,8 @@ class ProcessingPlugin(object):
self.options_factory = ProcessingOptionsFactory()
self.options_factory.setTitle(self.tr('Processing'))
iface.registerOptionsWidgetFactory(self.options_factory)
self.drop_handler = ProcessingDropHandler()
iface.registerCustomDropHandler(self.drop_handler)
self.locator_filter = AlgorithmLocatorFilter()
iface.registerLocatorFilter(self.locator_filter)
Processing.initialize()
@ -159,6 +181,7 @@ class ProcessingPlugin(object):
self.iface.unregisterOptionsWidgetFactory(self.options_factory)
self.iface.deregisterLocatorFilter(self.locator_filter)
self.iface.unregisterCustomDropHandler(self.drop_handler)
removeMenus()
Processing.deinitialize()

View File

@ -1333,6 +1333,14 @@ void QgisApp::dropEvent( QDropEvent *event )
timer->setSingleShot( true );
timer->setInterval( 50 );
// first, allow custom handlers to directly operate on the mime data
const QList<QPointer<QgsCustomDropHandler >> handlers = mCustomDropHandlers;
for ( QgsCustomDropHandler *handler : handlers )
{
if ( handler )
handler->handleMimeData( event->mimeData() );
}
// get the file list
QList<QUrl>::iterator i;
QList<QUrl>urls = event->mimeData()->urls();
@ -1392,42 +1400,51 @@ void QgisApp::dropEvent( QDropEvent *event )
files << fileName;
}
}
timer->setProperty( "files", files );
QgsMimeDataUtils::UriList lst;
if ( QgsMimeDataUtils::isUriList( event->mimeData() ) )
{
lst = QgsMimeDataUtils::decodeUriList( event->mimeData() );
}
timer->setProperty( "uris", QVariant::fromValue( lst ) );
connect( timer, &QTimer::timeout, this, &QgisApp::dropEventTimeout );
connect( timer, &QTimer::timeout, this, [this, timer, files, lst]
{
freezeCanvases();
for ( const QString &file : qgsAsConst( files ) )
{
bool handled = false;
// give custom drop handlers first priority at handling the file
const QList<QPointer<QgsCustomDropHandler >>handlers = mCustomDropHandlers;
for ( QgsCustomDropHandler *handler : handlers )
{
if ( handler && handler->handleFileDrop( file ) )
{
handled = true;
break;
}
}
if ( !handled )
openFile( file );
}
if ( !lst.isEmpty() )
{
handleDropUriList( lst );
}
freezeCanvases( false );
refreshMapCanvas();
timer->deleteLater();
} );
event->acceptProposedAction();
timer->start();
}
void QgisApp::dropEventTimeout()
{
freezeCanvases();
QStringList files = sender()->property( "files" ).toStringList();
sender()->deleteLater();
Q_FOREACH ( const QString &file, files )
{
openFile( file );
}
QgsMimeDataUtils::UriList lst = sender()->property( "uris" ).value<QgsMimeDataUtils::UriList>();
if ( !lst.isEmpty() )
{
handleDropUriList( lst );
}
freezeCanvases( false );
refreshMapCanvas();
}
void QgisApp::annotationCreated( QgsAnnotation *annotation )
{
// create canvas annotation item for annotation
@ -1474,9 +1491,9 @@ void QgisApp::handleDropUriList( const QgsMimeDataUtils::UriList &lst )
{
Q_FOREACH ( QgsCustomDropHandler *handler, mCustomDropHandlers )
{
if ( handler->key() == u.providerKey )
if ( handler && handler->customUriProviderKey() == u.providerKey )
{
handler->handleDrop( u );
handler->handleCustomUriDrop( u );
break;
}
}

View File

@ -1511,9 +1511,6 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
//! Set the layer for the map style dock. Doesn't show the style dock
void setMapStyleDockLayer( QgsMapLayer *layer );
//! Handles processing of dropped mimedata
void dropEventTimeout();
void annotationCreated( QgsAnnotation *annotation );
void updateCrsStatusBar();
@ -2056,7 +2053,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
QList<QgsMapLayerConfigWidgetFactory *> mMapLayerPanelFactories;
QList<QPointer<QgsOptionsWidgetFactory>> mOptionsWidgetFactories;
QList<QgsCustomDropHandler *> mCustomDropHandlers;
QList<QPointer<QgsCustomDropHandler>> mCustomDropHandlers;
QDateTime mProjectLastModified;

View File

@ -385,6 +385,7 @@ SET(QGIS_GUI_MOC_HDRS
qgsconfigureshortcutsdialog.h
qgscredentialdialog.h
qgscurveeditorwidget.h
qgscustomdrophandler.h
qgsdatumtransformdialog.h
qgsdetaileditemdelegate.h
qgsdetaileditemwidget.h
@ -684,7 +685,6 @@ SET(QGIS_GUI_HDRS
qgsattributeforminterface.h
qgsattributeformlegacyinterface.h
qgscursors.h
qgscustomdrophandler.h
qgsdetaileditemdata.h
qgsexpressionbuilderdialog.h
qgsgeometryrubberband.h

View File

@ -14,3 +14,24 @@
***************************************************************************/
#include "qgscustomdrophandler.h"
QString QgsCustomDropHandler::customUriProviderKey() const
{
return QString();
}
void QgsCustomDropHandler::handleCustomUriDrop( const QgsMimeDataUtils::Uri &uri ) const
{
Q_UNUSED( uri );
}
void QgsCustomDropHandler::handleMimeData( const QMimeData *data )
{
Q_UNUSED( data );
}
bool QgsCustomDropHandler::handleFileDrop( const QString &file )
{
Q_UNUSED( file );
return false;
}

View File

@ -21,20 +21,83 @@
/** \ingroup gui
* Abstract base class that may be implemented to handle new types of data to be dropped in QGIS.
* Implementations will be used when a QgsMimeDataUtils::Uri has layerType equal to "custom",
* and the providerKey is equal to key() returned by the implementation.
*
* Implementations have three approaches they can use to handle drops.
*
* 1. The simplest approach is to implement handeFileDrop() when they need to handle
* dropped files (i.e. with mime type "text/uri-list").
*
* 2. Reimplement handleCustomUriDrop() when they want to handle dropped custom
* QgsMimeDataUtils::Uri entries, for instance handling dropping custom entries
* from the browser tree (with mime type "application/x-vnd.qgis.qgis.uri"). In
* this case the implementation's customUriProviderKey() must match the uri
* entry's providerKey.
*
* 3. Reimplement handleMimeData() to directly handle dropped QMimeData.
* Subclasses should take care when overriding this method. When a drop event
* occurs, Qt will lock the source application of the drag for the duration
* of the drop event handling via handleMimeData() (e.g. dragging files from
* explorer to QGIS will lock the explorer window until the drop handling has
* been complete). Accordingly handleMimeData() implementations must return
* quickly and defer any intensive or slow processing.
*
* \since QGIS 3.0
*/
class GUI_EXPORT QgsCustomDropHandler
class GUI_EXPORT QgsCustomDropHandler : public QObject
{
Q_OBJECT
public:
virtual ~QgsCustomDropHandler() = default;
//! Type of custom URI recognized by the handler
virtual QString key() const = 0;
/**
* Type of custom URI recognized by the handler. This must match
* the URI entry's providerKey in order for handleCustomUriDrop()
* to be called.
*
* \see handleCustomUriDrop()
*/
virtual QString customUriProviderKey() const;
//! Method called from QGIS after a drop event with custom URI known by the handler
virtual void handleDrop( const QgsMimeDataUtils::Uri &uri ) const = 0;
/**
* Called from QGIS after a drop event with custom URI known by the handler.
*
* In order for handleCustomUriDrop() to be called, subclasses must
* also implement customUriProviderKey() to indicate the providerKey
* value which the handler accepts.
*
* \see customUriProviderKey()
*/
virtual void handleCustomUriDrop( const QgsMimeDataUtils::Uri &uri ) const;
/**
* Called when the specified mime \a data has been dropped onto QGIS.
*
* The base class implementation does nothing.
*
* Subclasses should take care when overriding this method. When a drop event
* occurs, Qt will lock the source application of the drag for the duration
* of the drop event handling (e.g. dragging files from explorer to QGIS will
* lock the explorer window until the drop handling has been complete).
*
* Accordingly, only implementations must be lightweight and return ASAP.
* (For instance by copying the relevant parts of \a data and then handling
* the data after a short timeout).
*/
virtual void handleMimeData( const QMimeData *data );
/**
* Called when the specified \a file has been dropped onto QGIS. If true
* is returned, then the handler has accepted this file and it should not
* be further processed (e.g. by other QgsCustomDropHandlers).
*
* The base class implementation does nothing.
*
* This method is not called directly while drop handling is occurring,
* so the limitations described in handleMimeData() about returning
* quickly do not apply.
*/
virtual bool handleFileDrop( const QString &file );
};
#endif // QGSCUSTOMDROPHANDLER_H