Skip to content

Commit

Permalink
Add Python utils method to start a Processing specific plugin
Browse files Browse the repository at this point in the history
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 2f82bab
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 9 deletions.
59 changes: 50 additions & 9 deletions python/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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

Expand Down
10 changes: 10 additions & 0 deletions src/python/qgspythonutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
7 changes: 7 additions & 0 deletions src/python/qgspythonutilsimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/python/qgspythonutilsimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
33 changes: 33 additions & 0 deletions tests/src/app/testqgisapppython.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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" ) );
Expand All @@ -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" ) );
Expand All @@ -91,5 +123,6 @@ void TestQgisAppPython::evalString()
QVERIFY( !mQgisApp->mPythonUtils->evalString( "1+", result ) );
}


QGSTEST_MAIN( TestQgisAppPython )
#include "testqgisapppython.moc"
45 changes: 45 additions & 0 deletions tests/testdata/test_plugin_path/ProcessingPluginTest/__init__.py
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -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.