Skip to content

Commit

Permalink
Merge pull request #5200 from nyalldawson/drop_handler
Browse files Browse the repository at this point in the history
[FEATURE][processing] Allow dropping model files onto QGIS window to execute them
  • Loading branch information
nyalldawson committed Sep 15, 2017
2 parents f282a75 + 06ee6f6 commit 5bd164b
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 46 deletions.
2 changes: 1 addition & 1 deletion python/gui/gui_auto.sip
@@ -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
Expand Down Expand Up @@ -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
Expand Down
72 changes: 65 additions & 7 deletions python/gui/qgscustomdrophandler.sip
Expand Up @@ -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

Expand All @@ -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
};

Expand Down
27 changes: 25 additions & 2 deletions python/plugins/processing/ProcessingPlugin.py
Expand Up @@ -32,13 +32,16 @@
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
Expand Down Expand Up @@ -66,13 +69,32 @@ def createWidget(self, parent):
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):
self.iface = iface
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()
Expand Down Expand Up @@ -159,6 +181,7 @@ def unload(self):

self.iface.unregisterOptionsWidgetFactory(self.options_factory)
self.iface.deregisterLocatorFilter(self.locator_filter)
self.iface.unregisterCustomDropHandler(self.drop_handler)

removeMenus()
Processing.deinitialize()
Expand Down
65 changes: 41 additions & 24 deletions src/app/qgisapp.cpp
Expand Up @@ -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();
Expand Down Expand Up @@ -1392,40 +1400,49 @@ 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();

event->acceptProposedAction();
timer->start();
}
for ( const QString &file : qgsAsConst( files ) )
{
bool handled = false;

void QgisApp::dropEventTimeout()
{
freezeCanvases();
QStringList files = sender()->property( "files" ).toStringList();
sender()->deleteLater();
// 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;
}
}

Q_FOREACH ( const QString &file, files )
{
openFile( file );
}
if ( !handled )
openFile( file );
}

QgsMimeDataUtils::UriList lst = sender()->property( "uris" ).value<QgsMimeDataUtils::UriList>();
if ( !lst.isEmpty() )
{
handleDropUriList( lst );
}
if ( !lst.isEmpty() )
{
handleDropUriList( lst );
}

freezeCanvases( false );
refreshMapCanvas();
freezeCanvases( false );
refreshMapCanvas();

timer->deleteLater();
} );

event->acceptProposedAction();
timer->start();
}

void QgisApp::annotationCreated( QgsAnnotation *annotation )
Expand Down Expand Up @@ -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;
}
}
Expand Down
5 changes: 1 addition & 4 deletions src/app/qgisapp.h
Expand Up @@ -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();
Expand Down Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion src/gui/CMakeLists.txt
Expand Up @@ -385,6 +385,7 @@ SET(QGIS_GUI_MOC_HDRS
qgsconfigureshortcutsdialog.h
qgscredentialdialog.h
qgscurveeditorwidget.h
qgscustomdrophandler.h
qgsdatumtransformdialog.h
qgsdetaileditemdelegate.h
qgsdetaileditemwidget.h
Expand Down Expand Up @@ -684,7 +685,6 @@ SET(QGIS_GUI_HDRS
qgsattributeforminterface.h
qgsattributeformlegacyinterface.h
qgscursors.h
qgscustomdrophandler.h
qgsdetaileditemdata.h
qgsexpressionbuilderdialog.h
qgsgeometryrubberband.h
Expand Down
21 changes: 21 additions & 0 deletions src/gui/qgscustomdrophandler.cpp
Expand Up @@ -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;
}

0 comments on commit 5bd164b

Please sign in to comment.