Merge pull request #5221 from nyalldawson/browser_awesome

Add QLR, processing models to browser
This commit is contained in:
Nyall Dawson 2017-09-21 05:20:07 +10:00 committed by GitHub
commit 15e650d624
11 changed files with 277 additions and 21 deletions

View File

@ -49,7 +49,8 @@ class QgsDataItem : QObject
Layer,
Error,
Favorites,
Project
Project,
Custom,
};
@ -153,6 +154,15 @@ Create new data item.
:rtype: bool
%End
virtual bool handleDoubleClick();
%Docstring
Called when a user double clicks on the item. Subclasses should return true
if they have implemented a double-click handler and do not want the default
double-click behavior for items.
.. versionadded:: 3.0
:rtype: bool
%End
virtual bool hasDragEnabled() const;
%Docstring
Returns true if the item may be dragged.

View File

@ -33,10 +33,14 @@ import sys
from qgis.core import (QgsApplication,
QgsProcessingUtils,
QgsProcessingModelAlgorithm)
QgsProcessingModelAlgorithm,
QgsDataItemProvider,
QgsDataProvider,
QgsDataItem,
QgsMimeDataUtils)
from qgis.gui import (QgsOptionsWidgetFactory,
QgsCustomDropHandler)
from qgis.PyQt.QtCore import Qt, QCoreApplication, QDir
from qgis.PyQt.QtCore import Qt, QCoreApplication, QDir, QFileInfo
from qgis.PyQt.QtWidgets import QMenu, QAction
from qgis.PyQt.QtGui import QIcon
@ -74,7 +78,10 @@ class ProcessingDropHandler(QgsCustomDropHandler):
def handleFileDrop(self, file):
if not file.lower().endswith('.model3'):
return False
self.runAlg(file)
@staticmethod
def runAlg(file):
alg = QgsProcessingModelAlgorithm()
if not alg.fromFile(file):
return False
@ -85,6 +92,73 @@ class ProcessingDropHandler(QgsCustomDropHandler):
dlg.show()
return True
def customUriProviderKey(self):
return 'processing'
def handleCustomUriDrop(self, uri):
path = uri.uri
self.runAlg(path)
class ProcessingModelItem(QgsDataItem):
def __init__(self, parent, name, path):
super(ProcessingModelItem, self).__init__(QgsDataItem.Custom, parent, name, path)
self.setState(QgsDataItem.Populated) # no children
self.setIconName(":/images/themes/default/processingModel.svg")
self.setToolTip(QDir.toNativeSeparators(path))
def hasDragEnabled(self):
return True
def handleDoubleClick(self):
self.runModel()
return True
def mimeUri(self):
u = QgsMimeDataUtils.Uri()
u.layerType = "custom"
u.providerKey = "processing"
u.name = self.name()
u.uri = self.path()
return u
def runModel(self):
ProcessingDropHandler.runAlg(self.path())
def editModel(self):
dlg = ModelerDialog()
dlg.loadModel(self.path())
dlg.show()
def actions(self):
run_model_action = QAction(self.tr('&Run Model…'), self)
run_model_action.triggered.connect(self.runModel)
edit_model_action = QAction(self.tr('&Edit Model…'), self)
edit_model_action.triggered.connect(self.editModel)
return [run_model_action, edit_model_action]
class ProcessingDataItemProvider(QgsDataItemProvider):
def __init__(self):
super(ProcessingDataItemProvider, self).__init__()
def name(self):
return 'processing'
def capabilities(self):
return QgsDataProvider.File
def createDataItem(self, path, parentItem):
file_info = QFileInfo(path)
if file_info.suffix().lower() == 'model3':
alg = QgsProcessingModelAlgorithm()
if alg.fromFile(path):
return ProcessingModelItem(parentItem, alg.name(), path)
return None
class ProcessingPlugin(object):
@ -95,6 +169,8 @@ class ProcessingPlugin(object):
iface.registerOptionsWidgetFactory(self.options_factory)
self.drop_handler = ProcessingDropHandler()
iface.registerCustomDropHandler(self.drop_handler)
self.item_provider = ProcessingDataItemProvider()
QgsApplication.dataItemProviderRegistry().addProvider(self.item_provider)
self.locator_filter = AlgorithmLocatorFilter()
iface.registerLocatorFilter(self.locator_filter)
Processing.initialize()
@ -182,6 +258,7 @@ class ProcessingPlugin(object):
self.iface.unregisterOptionsWidgetFactory(self.options_factory)
self.iface.deregisterLocatorFilter(self.locator_filter)
self.iface.unregisterCustomDropHandler(self.drop_handler)
QgsApplication.dataItemProviderRegistry().removeProvider(self.item_provider)
removeMenus()
Processing.deinitialize()

View File

@ -503,23 +503,26 @@ class ModelerDialog(BASE, WIDGET):
ModelerUtils.modelsFolders()[0],
self.tr('Processing models (*.model3 *.MODEL3)'))
if filename:
alg = QgsProcessingModelAlgorithm()
if alg.fromFile(filename):
self.model = alg
self.model.setProvider(QgsApplication.processingRegistry().providerById('model'))
self.textGroup.setText(alg.group())
self.textName.setText(alg.name())
self.repaintModel()
self.loadModel(filename)
self.view.centerOn(0, 0)
self.hasChanged = False
else:
QgsMessageLog.logMessage(self.tr('Could not load model {0}').format(filename),
self.tr('Processing'),
QgsMessageLog.CRITICAL)
QMessageBox.critical(self, self.tr('Could not open model'),
self.tr('The selected model could not be loaded.\n'
'See the log for more information.'))
def loadModel(self, filename):
alg = QgsProcessingModelAlgorithm()
if alg.fromFile(filename):
self.model = alg
self.model.setProvider(QgsApplication.processingRegistry().providerById('model'))
self.textGroup.setText(alg.group())
self.textName.setText(alg.name())
self.repaintModel()
self.view.centerOn(0, 0)
self.hasChanged = False
else:
QgsMessageLog.logMessage(self.tr('Could not load model {0}').format(filename),
self.tr('Processing'),
QgsMessageLog.CRITICAL)
QMessageBox.critical(self, self.tr('Could not open model'),
self.tr('The selected model could not be loaded.\n'
'See the log for more information.'))
def repaintModel(self, controls=True):
self.scene = ModelerScene(self, dialog=self)

View File

@ -6,6 +6,7 @@ SET(QGIS_APP_SRCS
qgisappstylesheet.cpp
qgsabout.cpp
qgsalignrasterdialog.cpp
qgsappbrowserproviders.cpp
qgsapplayertreeviewmenuprovider.cpp
qgsaddattrdialog.cpp
qgsaddtaborgroup.cpp
@ -194,6 +195,7 @@ SET (QGIS_APP_MOC_HDRS
qgsabout.h
qgsaddattrdialog.h
qgsalignrasterdialog.h
qgsappbrowserproviders.h
qgsjoindialog.h
qgsaddtaborgroup.h
qgsannotationwidget.h

View File

@ -124,6 +124,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
#include "qgisplugin.h"
#include "qgsabout.h"
#include "qgsalignrasterdialog.h"
#include "qgsappbrowserproviders.h"
#include "qgsapplayertreeviewmenuprovider.h"
#include "qgsapplication.h"
#include "qgsactionmanager.h"
@ -153,6 +154,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
#include "qgscustomization.h"
#include "qgscustomlayerorderwidget.h"
#include "qgscustomprojectiondialog.h"
#include "qgsdataitemproviderregistry.h"
#include "qgsdatasourceuri.h"
#include "qgsdatumtransformdialog.h"
#include "qgsdoublespinbox.h"
@ -975,6 +977,9 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
qApp->processEvents();
QgsApplication::initQgis();
QgsApplication::dataItemProviderRegistry()->addProvider( new QgsQlrDataItemProvider() );
registerCustomDropHandler( new QgsQlrDropHandler() );
mSplash->showMessage( tr( "Starting Python" ), Qt::AlignHCenter | Qt::AlignBottom );
qApp->processEvents();
loadPythonSupport();

View File

@ -0,0 +1,80 @@
/***************************************************************************
qgsappbrowserproviders.cpp
---------------------------
begin : September 2017
copyright : (C) 2017 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. *
* *
***************************************************************************/
#include "qgsappbrowserproviders.h"
#include "qgisapp.h"
//
// QgsQlrDataItem
//
QgsQlrDataItem::QgsQlrDataItem( QgsDataItem *parent, const QString &name, const QString &path )
: QgsLayerItem( parent, name, path, path, QgsLayerItem::NoType, QStringLiteral( "qlr" ) )
{
setState( QgsDataItem::Populated ); // no children
setIconName( QStringLiteral( ":/images/icons/qgis-icon-16x16.png" ) );
setToolTip( QDir::toNativeSeparators( path ) );
}
bool QgsQlrDataItem::hasDragEnabled() const
{
return true;
}
QgsMimeDataUtils::Uri QgsQlrDataItem::mimeUri() const
{
QgsMimeDataUtils::Uri u;
u.layerType = QStringLiteral( "custom" );
u.providerKey = QStringLiteral( "qlr" );
u.name = name();
u.uri = path();
return u;
}
//
// QgsQlrDataItemProvider
//
QString QgsQlrDataItemProvider::name()
{
return QStringLiteral( "QLR" );
}
int QgsQlrDataItemProvider::capabilities()
{
return QgsDataProvider::File;
}
QgsDataItem *QgsQlrDataItemProvider::createDataItem( const QString &path, QgsDataItem *parentItem )
{
QFileInfo fileInfo( path );
if ( fileInfo.suffix().compare( QStringLiteral( "qlr" ), Qt::CaseInsensitive ) == 0 )
{
return new QgsQlrDataItem( parentItem, fileInfo.fileName(), path );
}
return nullptr;
}
QString QgsQlrDropHandler::customUriProviderKey() const
{
return QStringLiteral( "qlr" );
}
void QgsQlrDropHandler::handleCustomUriDrop( const QgsMimeDataUtils::Uri &uri ) const
{
QString path = uri.uri;
QgisApp::instance()->openLayerDefinition( path );
}

View File

@ -0,0 +1,50 @@
/***************************************************************************
qgsappbrowserproviders.h
-------------------------
begin : September 2017
copyright : (C) 2017 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 QGSAPPBROWSERPROVIDERS_H
#define QGSAPPBROWSERPROVIDERS_H
#include "qgsdataitemprovider.h"
#include "qgsdataprovider.h"
#include "qgscustomdrophandler.h"
class QgsQlrDataItem : public QgsLayerItem
{
Q_OBJECT
public:
QgsQlrDataItem( QgsDataItem *parent, const QString &name, const QString &path );
bool hasDragEnabled() const override;
QgsMimeDataUtils::Uri mimeUri() const override;
};
class QgsQlrDataItemProvider : public QgsDataItemProvider
{
public:
QString name() override;
int capabilities() override;
QgsDataItem *createDataItem( const QString &path, QgsDataItem *parentItem ) override;
};
class QgsQlrDropHandler : public QgsCustomDropHandler
{
public:
QString customUriProviderKey() const override;
void handleCustomUriDrop( const QgsMimeDataUtils::Uri &uri ) const override;
};
#endif // QGSAPPBROWSERPROVIDERS_H

View File

@ -511,6 +511,11 @@ bool QgsDataItem::equal( const QgsDataItem *other )
mPath == other->path() );
}
bool QgsDataItem::handleDoubleClick()
{
return false;
}
QgsDataItem::State QgsDataItem::state() const
{
return mState;

View File

@ -79,7 +79,8 @@ class CORE_EXPORT QgsDataItem : public QObject
Layer,
Error,
Favorites, //!< Represents a favorite item
Project //!< Represents a QGIS project
Project, //!< Represents a QGIS project
Custom, //!< Custom item type
};
Q_ENUM( Type );
@ -155,6 +156,14 @@ class CORE_EXPORT QgsDataItem : public QObject
*/
virtual bool handleDrop( const QMimeData * /*data*/, Qt::DropAction /*action*/ ) { return false; }
/**
* Called when a user double clicks on the item. Subclasses should return true
* if they have implemented a double-click handler and do not want the default
* double-click behavior for items.
* \since QGIS 3.0
*/
virtual bool handleDoubleClick();
/** Returns true if the item may be dragged.
* Default implementation returns false.
* A draggable item has to implement mimeUri() that will be used to pass data.

View File

@ -97,7 +97,7 @@ QgsBrowserDockWidget::QgsBrowserDockWidget( const QString &name, QgsBrowserModel
connect( mLeFilter, &QgsFilterLineEdit::textChanged, this, &QgsBrowserDockWidget::setFilter );
connect( group, &QActionGroup::triggered, this, &QgsBrowserDockWidget::setFilterSyntax );
connect( mBrowserView, &QgsDockBrowserTreeView::customContextMenuRequested, this, &QgsBrowserDockWidget::showContextMenu );
connect( mBrowserView, &QgsDockBrowserTreeView::doubleClicked, this, &QgsBrowserDockWidget::addLayerAtIndex );
connect( mBrowserView, &QgsDockBrowserTreeView::doubleClicked, this, &QgsBrowserDockWidget::itemDoubleClicked );
connect( mSplitter, &QSplitter::splitterMoved, this, &QgsBrowserDockWidget::splitterMoved );
}
@ -155,6 +155,18 @@ void QgsBrowserDockWidget::showEvent( QShowEvent *e )
QgsDockWidget::showEvent( e );
}
void QgsBrowserDockWidget::itemDoubleClicked( const QModelIndex &index )
{
QgsDataItem *item = mModel->dataItem( mProxyModel->mapToSource( index ) );
if ( !item )
return;
if ( item->handleDoubleClick() )
return;
else
addLayerAtIndex( index ); // default double-click handler
}
void QgsBrowserDockWidget::showContextMenu( QPoint pt )
{
QModelIndex index = mProxyModel->mapToSource( mBrowserView->indexAt( pt ) );

View File

@ -110,6 +110,9 @@ class GUI_EXPORT QgsBrowserDockWidget : public QgsDockWidget, private Ui::QgsBro
//! Show event override
void showEvent( QShowEvent *event ) override;
private slots:
void itemDoubleClicked( const QModelIndex &index );
private:
//! Refresh the model
void refreshModel( const QModelIndex &index );