Skip to content

Commit 5bd164b

Browse files
authored
Merge pull request #5200 from nyalldawson/drop_handler
[FEATURE][processing] Allow dropping model files onto QGIS window to execute them
2 parents f282a75 + 06ee6f6 commit 5bd164b

File tree

8 files changed

+225
-46
lines changed

8 files changed

+225
-46
lines changed

python/gui/gui_auto.sip

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Include auto-generated SIP files
22
%Include qgsattributeeditorcontext.sip
33
%Include qgsattributeforminterface.sip
4-
%Include qgscustomdrophandler.sip
54
%Include qgsdetaileditemdata.sip
65
%Include qgsexpressionbuilderdialog.sip
76
%Include qgsgeometryrubberband.sip
@@ -91,6 +90,7 @@
9190
%Include qgsconfigureshortcutsdialog.sip
9291
%Include qgscredentialdialog.sip
9392
%Include qgscurveeditorwidget.sip
93+
%Include qgscustomdrophandler.sip
9494
%Include qgsdetaileditemdelegate.sip
9595
%Include qgsdetaileditemwidget.sip
9696
%Include qgsdial.sip

python/gui/qgscustomdrophandler.sip

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,30 @@
88

99

1010

11-
class QgsCustomDropHandler
11+
class QgsCustomDropHandler : QObject
1212
{
1313
%Docstring
1414
Abstract base class that may be implemented to handle new types of data to be dropped in QGIS.
15-
Implementations will be used when a QgsMimeDataUtils.Uri has layerType equal to "custom",
16-
and the providerKey is equal to key() returned by the implementation.
15+
16+
Implementations have three approaches they can use to handle drops.
17+
18+
1. The simplest approach is to implement handeFileDrop() when they need to handle
19+
dropped files (i.e. with mime type "text/uri-list").
20+
21+
2. Reimplement handleCustomUriDrop() when they want to handle dropped custom
22+
QgsMimeDataUtils.Uri entries, for instance handling dropping custom entries
23+
from the browser tree (with mime type "application/x-vnd.qgis.qgis.uri"). In
24+
this case the implementation's customUriProviderKey() must match the uri
25+
entry's providerKey.
26+
27+
3. Reimplement handleMimeData() to directly handle dropped QMimeData.
28+
Subclasses should take care when overriding this method. When a drop event
29+
occurs, Qt will lock the source application of the drag for the duration
30+
of the drop event handling via handleMimeData() (e.g. dragging files from
31+
explorer to QGIS will lock the explorer window until the drop handling has
32+
been complete). Accordingly handleMimeData() implementations must return
33+
quickly and defer any intensive or slow processing.
34+
1735
.. versionadded:: 3.0
1836
%End
1937

@@ -23,15 +41,55 @@ class QgsCustomDropHandler
2341
public:
2442
virtual ~QgsCustomDropHandler();
2543

26-
virtual QString key() const = 0;
44+
virtual QString customUriProviderKey() const;
2745
%Docstring
28-
Type of custom URI recognized by the handler
46+
Type of custom URI recognized by the handler. This must match
47+
the URI entry's providerKey in order for handleCustomUriDrop()
48+
to be called.
49+
50+
.. seealso:: handleCustomUriDrop()
2951
:rtype: str
3052
%End
3153

32-
virtual void handleDrop( const QgsMimeDataUtils::Uri &uri ) const = 0;
54+
virtual void handleCustomUriDrop( const QgsMimeDataUtils::Uri &uri ) const;
3355
%Docstring
34-
Method called from QGIS after a drop event with custom URI known by the handler
56+
Called from QGIS after a drop event with custom URI known by the handler.
57+
58+
In order for handleCustomUriDrop() to be called, subclasses must
59+
also implement customUriProviderKey() to indicate the providerKey
60+
value which the handler accepts.
61+
62+
.. seealso:: customUriProviderKey()
63+
%End
64+
65+
virtual void handleMimeData( const QMimeData *data );
66+
%Docstring
67+
Called when the specified mime ``data`` has been dropped onto QGIS.
68+
69+
The base class implementation does nothing.
70+
71+
Subclasses should take care when overriding this method. When a drop event
72+
occurs, Qt will lock the source application of the drag for the duration
73+
of the drop event handling (e.g. dragging files from explorer to QGIS will
74+
lock the explorer window until the drop handling has been complete).
75+
76+
Accordingly, only implementations must be lightweight and return ASAP.
77+
(For instance by copying the relevant parts of ``data`` and then handling
78+
the data after a short timeout).
79+
%End
80+
81+
virtual bool handleFileDrop( const QString &file );
82+
%Docstring
83+
Called when the specified ``file`` has been dropped onto QGIS. If true
84+
is returned, then the handler has accepted this file and it should not
85+
be further processed (e.g. by other QgsCustomDropHandlers).
86+
87+
The base class implementation does nothing.
88+
89+
This method is not called directly while drop handling is occurring,
90+
so the limitations described in handleMimeData() about returning
91+
quickly do not apply.
92+
:rtype: bool
3593
%End
3694
};
3795

python/plugins/processing/ProcessingPlugin.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,16 @@
3232
import sys
3333

3434
from qgis.core import (QgsApplication,
35-
QgsProcessingUtils)
36-
from qgis.gui import QgsOptionsWidgetFactory
35+
QgsProcessingUtils,
36+
QgsProcessingModelAlgorithm)
37+
from qgis.gui import (QgsOptionsWidgetFactory,
38+
QgsCustomDropHandler)
3739
from qgis.PyQt.QtCore import Qt, QCoreApplication, QDir
3840
from qgis.PyQt.QtWidgets import QMenu, QAction
3941
from qgis.PyQt.QtGui import QIcon
4042

4143
from processing.core.Processing import Processing
44+
from processing.gui.AlgorithmDialog import AlgorithmDialog
4245
from processing.gui.ProcessingToolbox import ProcessingToolbox
4346
from processing.gui.HistoryDialog import HistoryDialog
4447
from processing.gui.ConfigDialog import ConfigOptionsPage
@@ -66,13 +69,32 @@ def createWidget(self, parent):
6669
return ConfigOptionsPage(parent)
6770

6871

72+
class ProcessingDropHandler(QgsCustomDropHandler):
73+
74+
def handleFileDrop(self, file):
75+
if not file.lower().endswith('.model3'):
76+
return False
77+
78+
alg = QgsProcessingModelAlgorithm()
79+
if not alg.fromFile(file):
80+
return False
81+
82+
alg.setProvider(QgsApplication.processingRegistry().providerById('model'))
83+
dlg = AlgorithmDialog(alg)
84+
dlg.setAttribute(Qt.WA_DeleteOnClose)
85+
dlg.show()
86+
return True
87+
88+
6989
class ProcessingPlugin(object):
7090

7191
def __init__(self, iface):
7292
self.iface = iface
7393
self.options_factory = ProcessingOptionsFactory()
7494
self.options_factory.setTitle(self.tr('Processing'))
7595
iface.registerOptionsWidgetFactory(self.options_factory)
96+
self.drop_handler = ProcessingDropHandler()
97+
iface.registerCustomDropHandler(self.drop_handler)
7698
self.locator_filter = AlgorithmLocatorFilter()
7799
iface.registerLocatorFilter(self.locator_filter)
78100
Processing.initialize()
@@ -159,6 +181,7 @@ def unload(self):
159181

160182
self.iface.unregisterOptionsWidgetFactory(self.options_factory)
161183
self.iface.deregisterLocatorFilter(self.locator_filter)
184+
self.iface.unregisterCustomDropHandler(self.drop_handler)
162185

163186
removeMenus()
164187
Processing.deinitialize()

src/app/qgisapp.cpp

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,6 +1333,14 @@ void QgisApp::dropEvent( QDropEvent *event )
13331333
timer->setSingleShot( true );
13341334
timer->setInterval( 50 );
13351335

1336+
// first, allow custom handlers to directly operate on the mime data
1337+
const QList<QPointer<QgsCustomDropHandler >> handlers = mCustomDropHandlers;
1338+
for ( QgsCustomDropHandler *handler : handlers )
1339+
{
1340+
if ( handler )
1341+
handler->handleMimeData( event->mimeData() );
1342+
}
1343+
13361344
// get the file list
13371345
QList<QUrl>::iterator i;
13381346
QList<QUrl>urls = event->mimeData()->urls();
@@ -1392,40 +1400,49 @@ void QgisApp::dropEvent( QDropEvent *event )
13921400
files << fileName;
13931401
}
13941402
}
1395-
timer->setProperty( "files", files );
13961403

13971404
QgsMimeDataUtils::UriList lst;
13981405
if ( QgsMimeDataUtils::isUriList( event->mimeData() ) )
13991406
{
14001407
lst = QgsMimeDataUtils::decodeUriList( event->mimeData() );
14011408
}
1402-
timer->setProperty( "uris", QVariant::fromValue( lst ) );
14031409

1404-
connect( timer, &QTimer::timeout, this, &QgisApp::dropEventTimeout );
1410+
connect( timer, &QTimer::timeout, this, [this, timer, files, lst]
1411+
{
1412+
freezeCanvases();
14051413

1406-
event->acceptProposedAction();
1407-
timer->start();
1408-
}
1414+
for ( const QString &file : qgsAsConst( files ) )
1415+
{
1416+
bool handled = false;
14091417

1410-
void QgisApp::dropEventTimeout()
1411-
{
1412-
freezeCanvases();
1413-
QStringList files = sender()->property( "files" ).toStringList();
1414-
sender()->deleteLater();
1418+
// give custom drop handlers first priority at handling the file
1419+
const QList<QPointer<QgsCustomDropHandler >>handlers = mCustomDropHandlers;
1420+
for ( QgsCustomDropHandler *handler : handlers )
1421+
{
1422+
if ( handler && handler->handleFileDrop( file ) )
1423+
{
1424+
handled = true;
1425+
break;
1426+
}
1427+
}
14151428

1416-
Q_FOREACH ( const QString &file, files )
1417-
{
1418-
openFile( file );
1419-
}
1429+
if ( !handled )
1430+
openFile( file );
1431+
}
14201432

1421-
QgsMimeDataUtils::UriList lst = sender()->property( "uris" ).value<QgsMimeDataUtils::UriList>();
1422-
if ( !lst.isEmpty() )
1423-
{
1424-
handleDropUriList( lst );
1425-
}
1433+
if ( !lst.isEmpty() )
1434+
{
1435+
handleDropUriList( lst );
1436+
}
14261437

1427-
freezeCanvases( false );
1428-
refreshMapCanvas();
1438+
freezeCanvases( false );
1439+
refreshMapCanvas();
1440+
1441+
timer->deleteLater();
1442+
} );
1443+
1444+
event->acceptProposedAction();
1445+
timer->start();
14291446
}
14301447

14311448
void QgisApp::annotationCreated( QgsAnnotation *annotation )
@@ -1474,9 +1491,9 @@ void QgisApp::handleDropUriList( const QgsMimeDataUtils::UriList &lst )
14741491
{
14751492
Q_FOREACH ( QgsCustomDropHandler *handler, mCustomDropHandlers )
14761493
{
1477-
if ( handler->key() == u.providerKey )
1494+
if ( handler && handler->customUriProviderKey() == u.providerKey )
14781495
{
1479-
handler->handleDrop( u );
1496+
handler->handleCustomUriDrop( u );
14801497
break;
14811498
}
14821499
}

src/app/qgisapp.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1511,9 +1511,6 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
15111511
//! Set the layer for the map style dock. Doesn't show the style dock
15121512
void setMapStyleDockLayer( QgsMapLayer *layer );
15131513

1514-
//! Handles processing of dropped mimedata
1515-
void dropEventTimeout();
1516-
15171514
void annotationCreated( QgsAnnotation *annotation );
15181515

15191516
void updateCrsStatusBar();
@@ -2056,7 +2053,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
20562053
QList<QgsMapLayerConfigWidgetFactory *> mMapLayerPanelFactories;
20572054
QList<QPointer<QgsOptionsWidgetFactory>> mOptionsWidgetFactories;
20582055

2059-
QList<QgsCustomDropHandler *> mCustomDropHandlers;
2056+
QList<QPointer<QgsCustomDropHandler>> mCustomDropHandlers;
20602057

20612058
QDateTime mProjectLastModified;
20622059

src/gui/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ SET(QGIS_GUI_MOC_HDRS
385385
qgsconfigureshortcutsdialog.h
386386
qgscredentialdialog.h
387387
qgscurveeditorwidget.h
388+
qgscustomdrophandler.h
388389
qgsdatumtransformdialog.h
389390
qgsdetaileditemdelegate.h
390391
qgsdetaileditemwidget.h
@@ -684,7 +685,6 @@ SET(QGIS_GUI_HDRS
684685
qgsattributeforminterface.h
685686
qgsattributeformlegacyinterface.h
686687
qgscursors.h
687-
qgscustomdrophandler.h
688688
qgsdetaileditemdata.h
689689
qgsexpressionbuilderdialog.h
690690
qgsgeometryrubberband.h

src/gui/qgscustomdrophandler.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,24 @@
1414
***************************************************************************/
1515

1616
#include "qgscustomdrophandler.h"
17+
18+
QString QgsCustomDropHandler::customUriProviderKey() const
19+
{
20+
return QString();
21+
}
22+
23+
void QgsCustomDropHandler::handleCustomUriDrop( const QgsMimeDataUtils::Uri &uri ) const
24+
{
25+
Q_UNUSED( uri );
26+
}
27+
28+
void QgsCustomDropHandler::handleMimeData( const QMimeData *data )
29+
{
30+
Q_UNUSED( data );
31+
}
32+
33+
bool QgsCustomDropHandler::handleFileDrop( const QString &file )
34+
{
35+
Q_UNUSED( file );
36+
return false;
37+
}

0 commit comments

Comments
 (0)