Skip to content
Permalink
Browse files

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.
  • Loading branch information
nyalldawson committed Mar 2, 2019
1 parent 92b7356 commit 2f82bab1d9f62de445de277b819a7940852b9390
@@ -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,33 +326,74 @@ 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:
plugins[packageName].initGui()
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

@@ -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.
*
@@ -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;
@@ -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;
@@ -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"
@@ -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)
@@ -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

0 comments on commit 2f82bab

Please sign in to comment.
You can’t perform that action at this time.