Add Python utils method to start a Processing specific plugin

This command adds a plugin to active plugins and calls initProcessing(),
initializing only Processing related components of that plugin.

The new initProcessing() hook should be implemented by plugins
which provide Processing providers or algorithm, and should only
implement code which is required to load the provider and algorithms.
Strictly no GUI related code should be used here, that MUST
be moved out of initializers and deferred to the plugin's
initGui implementation.
This commit is contained in:
Nyall Dawson 2019-03-01 11:02:58 +10:00
parent 92b7356734
commit 2f82bab1d9
7 changed files with 153 additions and 9 deletions

View File

@ -314,8 +314,8 @@ def loadPlugin(packageName):
return False
def startPlugin(packageName):
""" initialize the plugin """
def _startPlugin(packageName):
""" initializes a plugin, but does not load GUI """
global plugins, active_plugins, iface, plugin_times
if packageName in active_plugins:
@ -326,18 +326,30 @@ def startPlugin(packageName):
package = sys.modules[packageName]
errMsg = QCoreApplication.translate("Python", "Couldn't load plugin '{0}'").format(packageName)
start = time.process_time()
# create an instance of the plugin
try:
plugins[packageName] = package.classFactory(iface)
except:
_unloadPluginModules(packageName)
errMsg = QCoreApplication.translate("Python", "Couldn't load plugin '{0}'").format(packageName)
msg = QCoreApplication.translate("Python", "{0} due to an error when calling its classFactory() method").format(errMsg)
showException(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2], msg, messagebar=True)
return False
return True
def _addToActivePlugins(packageName, duration):
""" Adds a plugin to the list of active plugins """
active_plugins.append(packageName)
plugin_times[packageName] = "{0:02f}s".format(duration)
def startPlugin(packageName):
""" initialize the plugin """
global plugins, active_plugins, iface, plugin_times
start = time.process_time()
if not _startPlugin(packageName):
return False
# initGui
try:
@ -345,14 +357,43 @@ def startPlugin(packageName):
except:
del plugins[packageName]
_unloadPluginModules(packageName)
errMsg = QCoreApplication.translate("Python", "Couldn't load plugin '{0}'").format(packageName)
msg = QCoreApplication.translate("Python", "{0} due to an error when calling its initGui() method").format(errMsg)
showException(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2], msg, messagebar=True)
return False
# add to active plugins
active_plugins.append(packageName)
end = time.process_time()
plugin_times[packageName] = "{0:02f}s".format(end - start)
_addToActivePlugins(packageName, end - start)
return True
def startProcessingPlugin(packageName):
""" initialize only the Processing components of a plugin """
global plugins, active_plugins, iface, plugin_times
start = time.process_time()
if not _startPlugin(packageName):
return False
errMsg = QCoreApplication.translate("Python", "Couldn't load plugin '{0}'").format(packageName)
if not hasattr(plugins[packageName], 'initProcessing'):
del plugins[packageName]
_unloadPluginModules(packageName)
msg = QCoreApplication.translate("Python", "{0} - plugin has no initProcessing() method").format(errMsg)
showException(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2], msg, messagebar=True)
return False
# initProcessing
try:
plugins[packageName].initProcessing()
except:
del plugins[packageName]
_unloadPluginModules(packageName)
msg = QCoreApplication.translate("Python", "{0} due to an error when calling its initProcessing() method").format(errMsg)
showException(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2], msg, messagebar=True)
return False
end = time.process_time()
_addToActivePlugins(packageName, end - start)
return True

View File

@ -161,6 +161,16 @@ class PYTHON_EXPORT QgsPythonUtils
*/
virtual bool startPlugin( const QString &packageName ) = 0;
/**
* Start a Processing plugin
*
* This command adds a plugin to active plugins and calls initProcessing(),
* initializing only Processing related components of that plugin.
*
* \since QGIS 3.8
*/
virtual bool startProcessingPlugin( const QString &packageName ) = 0;
/**
* Helper function to return some information about a plugin.
*

View File

@ -623,6 +623,13 @@ bool QgsPythonUtilsImpl::startPlugin( const QString &packageName )
return ( output == QLatin1String( "True" ) );
}
bool QgsPythonUtilsImpl::startProcessingPlugin( const QString &packageName )
{
QString output;
evalString( "qgis.utils.startProcessingPlugin('" + packageName + "')", output );
return ( output == QLatin1String( "True" ) );
}
bool QgsPythonUtilsImpl::canUninstallPlugin( const QString &packageName )
{
QString output;

View File

@ -85,6 +85,7 @@ class QgsPythonUtilsImpl : public QgsPythonUtils
QStringList listActivePlugins() override;
bool loadPlugin( const QString &packageName ) override;
bool startPlugin( const QString &packageName ) override;
bool startProcessingPlugin( const QString &packageName ) override;
QString getPluginMetadata( const QString &pluginName, const QString &function ) override;
bool canUninstallPlugin( const QString &packageName ) override;
bool unloadPlugin( const QString &packageName ) override;

View File

@ -40,6 +40,9 @@ class TestQgisAppPython : public QObject
void init() {} // will be called before each testfunction is executed.
void cleanup() {} // will be called after every testfunction.
void hasPython();
void plugins();
void pythonPlugin();
void runString();
void evalString();
@ -53,6 +56,8 @@ TestQgisAppPython::TestQgisAppPython() = default;
//runs before all tests
void TestQgisAppPython::initTestCase()
{
qputenv( "QGIS_PLUGINPATH", QByteArray( TEST_DATA_DIR ) + "/test_plugin_path" );
// Set up the QgsSettings environment
QCoreApplication::setOrganizationName( QStringLiteral( "QGIS" ) );
QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
@ -73,6 +78,33 @@ void TestQgisAppPython::cleanupTestCase()
QgsApplication::exitQgis();
}
void TestQgisAppPython::hasPython()
{
QVERIFY( mQgisApp->mPythonUtils->isEnabled() );
}
void TestQgisAppPython::plugins()
{
QVERIFY( mQgisApp->mPythonUtils->pluginList().contains( QStringLiteral( "PluginPathTest" ) ) );
QVERIFY( !mQgisApp->mPythonUtils->isPluginLoaded( QStringLiteral( "PluginPathTest" ) ) );
QVERIFY( mQgisApp->mPythonUtils->listActivePlugins().isEmpty() );
// load plugin
QVERIFY( !mQgisApp->mPythonUtils->unloadPlugin( QStringLiteral( "PluginPathTest" ) ) );
QVERIFY( mQgisApp->mPythonUtils->loadPlugin( QStringLiteral( "PluginPathTest" ) ) );
QVERIFY( !mQgisApp->mPythonUtils->isPluginLoaded( QStringLiteral( "PluginPathTest" ) ) );
QVERIFY( mQgisApp->mPythonUtils->startPlugin( QStringLiteral( "PluginPathTest" ) ) );
QVERIFY( mQgisApp->mPythonUtils->isPluginLoaded( QStringLiteral( "PluginPathTest" ) ) );
QCOMPARE( mQgisApp->mPythonUtils->listActivePlugins(), QStringList() << QStringLiteral( "PluginPathTest" ) );
}
void TestQgisAppPython::pythonPlugin()
{
QVERIFY( mQgisApp->mPythonUtils->pluginList().contains( QStringLiteral( "ProcessingPluginTest" ) ) );
QVERIFY( mQgisApp->mPythonUtils->loadPlugin( QStringLiteral( "ProcessingPluginTest" ) ) );
QVERIFY( mQgisApp->mPythonUtils->startProcessingPlugin( QStringLiteral( "ProcessingPluginTest" ) ) );
QVERIFY( !mQgisApp->mPythonUtils->startProcessingPlugin( QStringLiteral( "PluginPathTest" ) ) );
}
void TestQgisAppPython::runString()
{
QVERIFY( mQgisApp->mPythonUtils->runString( "a=1+1" ) );
@ -91,5 +123,6 @@ void TestQgisAppPython::evalString()
QVERIFY( !mQgisApp->mPythonUtils->evalString( "1+", result ) );
}
QGSTEST_MAIN( TestQgisAppPython )
#include "testqgisapppython.moc"

View File

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
"""
***************************************************************************
__init__.py
---------------------
Date : July 2013
Copyright : (C) 2013 by Hugo Mercier
Email : hugo dot mercier at oslandia 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. *
* *
***************************************************************************
"""
__author__ = 'Hugo Mercier'
__date__ = 'July 2013'
__copyright__ = '(C) 2013, Hugo Mercier'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
import os
class Test:
def __init__(self, iface):
pass
def initGui(self):
assert False
def initProcessing(self):
pass
def unload(self):
pass
def classFactory(iface):
# load Test class from file Test
return Test(iface)

View File

@ -0,0 +1,7 @@
[general]
name=plugin path test
qgisMinimumVersion=2.0
description=desc
version=0.1
author=HM/Oslandia
email=hugo.mercier@oslandia.com