diff --git a/python/gui/auto_generated/processing/qgsprocessingrecentalgorithmlog.sip.in b/python/gui/auto_generated/processing/qgsprocessingrecentalgorithmlog.sip.in new file mode 100644 index 00000000000..8cafee4fb2c --- /dev/null +++ b/python/gui/auto_generated/processing/qgsprocessingrecentalgorithmlog.sip.in @@ -0,0 +1,72 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/processing/qgsprocessingrecentalgorithmlog.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + +class QgsProcessingRecentAlgorithmLog : QObject +{ +%Docstring +A log for tracking recently used processing algorithms. + +QgsProcessingRecentAlgorithmLog is not usually directly created, instead +use the instance accessible through :py:func:`QgsGui.processingRecentAlgorithmLog()` + +The log contents are saved and restored via QgsSettings. + +.. note:: + + Not stable API + +.. versionadded:: 3.4 +%End + +%TypeHeaderCode +#include "qgsprocessingrecentalgorithmlog.h" +%End + public: + + QgsProcessingRecentAlgorithmLog( QObject *parent = 0 ); +%Docstring +Constructor for QgsProcessingRecentAlgorithmLog, with the specified +``parent`` object. +%End + + QStringList recentAlgorithmIds() const; +%Docstring +Returns a list of the IDs of recently used processing algorithms, where the +first item in the list is the most recently used algorithm. +%End + + void push( const QString &id ); +%Docstring +Pushes the algorithm with matching ``id`` to the top of the recently used +algorithm list. + +If this changes the list of recent algorithm IDs then the changed() signal +will be emitted. +%End + + signals: + + void changed(); +%Docstring +Emitted when the list of recently used algorithms is changed, e.g. when +a new algorithm ID is pushed to the list (see push()). +%End + +}; + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/processing/qgsprocessingrecentalgorithmlog.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/gui/auto_generated/qgsgui.sip.in b/python/gui/auto_generated/qgsgui.sip.in index 5292f5da0d5..e780127bb86 100644 --- a/python/gui/auto_generated/qgsgui.sip.in +++ b/python/gui/auto_generated/qgsgui.sip.in @@ -67,6 +67,13 @@ Returns the global layout item GUI registry, used for registering the GUI behavi Returns the global processing gui registry, used for registering the GUI behavior of processing algorithms. .. versionadded:: 3.2 +%End + + static QgsProcessingRecentAlgorithmLog *processingRecentAlgorithmLog(); +%Docstring +Returns the global processing recent algorithm log, used for tracking recently used processing algorithms. + +.. versionadded:: 3.4 %End static void enableAutoGeometryRestore( QWidget *widget, const QString &key = QString() ); diff --git a/python/gui/gui_auto.sip b/python/gui/gui_auto.sip index 19ef2a2826b..a28ffbb88cf 100644 --- a/python/gui/gui_auto.sip +++ b/python/gui/gui_auto.sip @@ -315,4 +315,5 @@ %Include auto_generated/locator/qgslocatorwidget.sip %Include auto_generated/processing/qgsprocessingalgorithmconfigurationwidget.sip %Include auto_generated/processing/qgsprocessingalgorithmdialogbase.sip +%Include auto_generated/processing/qgsprocessingrecentalgorithmlog.sip %Include auto_generated/qgsadvanceddigitizingcanvasitem.sip diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index db376c16c3e..982668ca866 100755 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -197,6 +197,7 @@ SET(QGIS_GUI_SRCS processing/qgsprocessingalgorithmdialogbase.cpp processing/qgsprocessingconfigurationwidgets.cpp processing/qgsprocessingguiregistry.cpp + processing/qgsprocessingrecentalgorithmlog.cpp qgisinterface.cpp qgsactionmenu.cpp @@ -717,6 +718,7 @@ SET(QGIS_GUI_MOC_HDRS processing/qgsprocessingalgorithmconfigurationwidget.h processing/qgsprocessingalgorithmdialogbase.h processing/qgsprocessingconfigurationwidgets.h + processing/qgsprocessingrecentalgorithmlog.h ) SET_PROPERTY(GLOBAL PROPERTY QGIS_GUI_MOC_HDRS ${QGIS_GUI_MOC_HDRS}) diff --git a/src/gui/processing/qgsprocessingrecentalgorithmlog.cpp b/src/gui/processing/qgsprocessingrecentalgorithmlog.cpp new file mode 100644 index 00000000000..c5a0de4c6f3 --- /dev/null +++ b/src/gui/processing/qgsprocessingrecentalgorithmlog.cpp @@ -0,0 +1,50 @@ +/*************************************************************************** + qgsprocessingrecentalgorithmlog.cpp + ------------------------------------ + Date : July 2018 + Copyright : (C) 2018 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 "qgsprocessingrecentalgorithmlog.h" +#include "qgssettings.h" + +///@cond PRIVATE + +const int MAX_LOG_LENGTH = 5; + +QgsProcessingRecentAlgorithmLog::QgsProcessingRecentAlgorithmLog( QObject *parent ) + : QObject( parent ) +{ + QgsSettings settings; + mRecentAlgorithmIds = settings.value( QStringLiteral( "processing/recentAlgorithms" ), QVariant(), QgsSettings::Gui ).toStringList(); +} + +QStringList QgsProcessingRecentAlgorithmLog::recentAlgorithmIds() const +{ + return mRecentAlgorithmIds; +} + +void QgsProcessingRecentAlgorithmLog::push( const QString &id ) +{ + const QStringList previous = mRecentAlgorithmIds; + mRecentAlgorithmIds.removeAll( id ); + mRecentAlgorithmIds.insert( 0, id ); + if ( mRecentAlgorithmIds.length() > MAX_LOG_LENGTH ) + mRecentAlgorithmIds = mRecentAlgorithmIds.mid( 0, MAX_LOG_LENGTH ); + + QgsSettings settings; + settings.setValue( QStringLiteral( "processing/recentAlgorithms" ), mRecentAlgorithmIds, QgsSettings::Gui ); + + if ( previous != mRecentAlgorithmIds ) + emit changed(); +} + +///@endcond diff --git a/src/gui/processing/qgsprocessingrecentalgorithmlog.h b/src/gui/processing/qgsprocessingrecentalgorithmlog.h new file mode 100644 index 00000000000..f95ce11a6f8 --- /dev/null +++ b/src/gui/processing/qgsprocessingrecentalgorithmlog.h @@ -0,0 +1,79 @@ +/*************************************************************************** + qgsprocessingrecentalgorithmlog.h + ---------------------------------- + Date : July 2018 + Copyright : (C) 2018 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 QGSPROCESSINGRECENTALGORITHMLOG_H +#define QGSPROCESSINGRECENTALGORITHMLOG_H + +#include "qgis.h" +#include "qgis_gui.h" + +///@cond NOT_STABLE + +/** + * \ingroup gui + * \brief A log for tracking recently used processing algorithms. + * + * QgsProcessingRecentAlgorithmLog is not usually directly created, instead + * use the instance accessible through QgsGui::processingRecentAlgorithmLog(). + * + * The log contents are saved and restored via QgsSettings. + * + * \note Not stable API + * \since QGIS 3.4 + */ +class GUI_EXPORT QgsProcessingRecentAlgorithmLog : public QObject +{ + Q_OBJECT + + public: + + /** + * Constructor for QgsProcessingRecentAlgorithmLog, with the specified + * \a parent object. + */ + QgsProcessingRecentAlgorithmLog( QObject *parent = nullptr ); + + /** + * Returns a list of the IDs of recently used processing algorithms, where the + * first item in the list is the most recently used algorithm. + */ + QStringList recentAlgorithmIds() const; + + /** + * Pushes the algorithm with matching \a id to the top of the recently used + * algorithm list. + * + * If this changes the list of recent algorithm IDs then the changed() signal + * will be emitted. + */ + void push( const QString &id ); + + signals: + + /** + * Emitted when the list of recently used algorithms is changed, e.g. when + * a new algorithm ID is pushed to the list (see push()). + */ + void changed(); + + private: + + QStringList mRecentAlgorithmIds; + +}; + +///@endcond + +#endif // QGSPROCESSINGRECENTALGORITHMLOG_H diff --git a/src/gui/qgsgui.cpp b/src/gui/qgsgui.cpp index 4f4a6ef196a..f44cbd51f99 100644 --- a/src/gui/qgsgui.cpp +++ b/src/gui/qgsgui.cpp @@ -32,6 +32,7 @@ #include "qgsshortcutsmanager.h" #include "qgswidgetstatehelper_p.h" #include "qgslogger.h" +#include "qgsprocessingrecentalgorithmlog.h" QgsGui *QgsGui::instance() { @@ -79,6 +80,11 @@ QgsProcessingGuiRegistry *QgsGui::processingGuiRegistry() return instance()->mProcessingGuiRegistry; } +QgsProcessingRecentAlgorithmLog *QgsGui::processingRecentAlgorithmLog() +{ + return instance()->mProcessingRecentAlgorithmLog; +} + void QgsGui::enableAutoGeometryRestore( QWidget *widget, const QString &key ) { if ( widget->objectName().isEmpty() ) @@ -91,6 +97,7 @@ void QgsGui::enableAutoGeometryRestore( QWidget *widget, const QString &key ) QgsGui::~QgsGui() { delete mProcessingGuiRegistry; + delete mProcessingRecentAlgorithmLog; delete mLayoutItemGuiRegistry; delete mLayerTreeEmbeddedWidgetRegistry; delete mEditorWidgetRegistry; @@ -116,5 +123,6 @@ QgsGui::QgsGui() mSourceSelectProviderRegistry = new QgsSourceSelectProviderRegistry(); mLayoutItemGuiRegistry = new QgsLayoutItemGuiRegistry(); mWidgetStateHelper = new QgsWidgetStateHelper(); + mProcessingRecentAlgorithmLog = new QgsProcessingRecentAlgorithmLog(); mProcessingGuiRegistry = new QgsProcessingGuiRegistry(); } diff --git a/src/gui/qgsgui.h b/src/gui/qgsgui.h index f14751a64b7..3948fa164a0 100644 --- a/src/gui/qgsgui.h +++ b/src/gui/qgsgui.h @@ -31,6 +31,7 @@ class QgsNative; class QgsLayoutItemGuiRegistry; class QgsWidgetStateHelper; class QgsProcessingGuiRegistry; +class QgsProcessingRecentAlgorithmLog; /** * \ingroup gui @@ -96,6 +97,12 @@ class GUI_EXPORT QgsGui */ static QgsProcessingGuiRegistry *processingGuiRegistry(); + /** + * Returns the global processing recent algorithm log, used for tracking recently used processing algorithms. + * \since QGIS 3.4 + */ + static QgsProcessingRecentAlgorithmLog *processingRecentAlgorithmLog(); + /** * Register the widget to allow its position to be automatically saved and restored when open and closed. * Use this to avoid needing to call saveGeometry() and restoreGeometry() on your widget. @@ -117,6 +124,7 @@ class GUI_EXPORT QgsGui QgsMapLayerActionRegistry *mMapLayerActionRegistry = nullptr; QgsLayoutItemGuiRegistry *mLayoutItemGuiRegistry = nullptr; QgsProcessingGuiRegistry *mProcessingGuiRegistry = nullptr; + QgsProcessingRecentAlgorithmLog *mProcessingRecentAlgorithmLog = nullptr; #ifdef SIP_RUN QgsGui( const QgsGui &other ); diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index ecf421cc91e..8076081af34 100644 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -140,6 +140,7 @@ ADD_PYTHON_TEST(PyQgsPoint test_qgspoint.py) ADD_PYTHON_TEST(PyQgsPointClusterRenderer test_qgspointclusterrenderer.py) ADD_PYTHON_TEST(PyQgsPointDisplacementRenderer test_qgspointdisplacementrenderer.py) ADD_PYTHON_TEST(PyQgsPostgresDomain test_qgspostgresdomain.py) +ADD_PYTHON_TEST(PyQgsProcessingRecentAlgorithmLog test_qgsprocessingrecentalgorithmslog.py) ADD_PYTHON_TEST(PyQgsProjectionSelectionWidgets test_qgsprojectionselectionwidgets.py) ADD_PYTHON_TEST(PyQgsProjectMetadata test_qgsprojectmetadata.py) ADD_PYTHON_TEST(PyQgsRange test_qgsrange.py) diff --git a/tests/src/python/test_qgsprocessingrecentalgorithmslog.py b/tests/src/python/test_qgsprocessingrecentalgorithmslog.py new file mode 100644 index 00000000000..1dc76d1b866 --- /dev/null +++ b/tests/src/python/test_qgsprocessingrecentalgorithmslog.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsProcessingRecentAlgorithmLog. + +.. note:: 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__ = 'Nyall Dawson' +__date__ = '2018-07' +__copyright__ = 'Copyright 2018, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +from qgis.PyQt.QtCore import QCoreApplication +from qgis.core import QgsSettings +from qgis.gui import QgsProcessingRecentAlgorithmLog, QgsGui +from qgis.testing import start_app, unittest +from qgis.PyQt.QtTest import QSignalSpy + +start_app() + + +class TestQgsProcessingRecentAlgorithmLog(unittest.TestCase): + + @classmethod + def setUpClass(cls): + """Run before all tests""" + QCoreApplication.setOrganizationName("QGIS_Test") + QCoreApplication.setOrganizationDomain("QGIS_TestPyQgsNewGeoPackageLayerDialog.com") + QCoreApplication.setApplicationName("QGIS_TestPyQgsNewGeoPackageLayerDialog") + QgsSettings().clear() + + def test_log(self): + log = QgsProcessingRecentAlgorithmLog() + self.assertFalse(log.recentAlgorithmIds()) + spy = QSignalSpy(log.changed) + + log.push('test') + self.assertEqual(log.recentAlgorithmIds(), ['test']) + self.assertEqual(len(spy), 1) + log.push('test') + self.assertEqual(log.recentAlgorithmIds(), ['test']) + self.assertEqual(len(spy), 1) + + log.push('test2') + self.assertEqual(log.recentAlgorithmIds(), ['test2', 'test']) + self.assertEqual(len(spy), 2) + + log.push('test') + self.assertEqual(log.recentAlgorithmIds(), ['test', 'test2']) + self.assertEqual(len(spy), 3) + + log.push('test3') + self.assertEqual(log.recentAlgorithmIds(), ['test3', 'test', 'test2']) + self.assertEqual(len(spy), 4) + + log.push('test4') + self.assertEqual(log.recentAlgorithmIds(), ['test4', 'test3', 'test', 'test2']) + self.assertEqual(len(spy), 5) + + log.push('test5') + self.assertEqual(log.recentAlgorithmIds(), ['test5', 'test4', 'test3', 'test', 'test2']) + self.assertEqual(len(spy), 6) + + log.push('test6') + self.assertEqual(log.recentAlgorithmIds(), ['test6', 'test5', 'test4', 'test3', 'test']) + self.assertEqual(len(spy), 7) + + log.push('test3') + self.assertEqual(log.recentAlgorithmIds(), ['test3', 'test6', 'test5', 'test4', 'test']) + self.assertEqual(len(spy), 8) + + log.push('test3') + self.assertEqual(log.recentAlgorithmIds(), ['test3', 'test6', 'test5', 'test4', 'test']) + self.assertEqual(len(spy), 8) + + # test that log has been saved to QgsSettings + log2 = QgsProcessingRecentAlgorithmLog() + self.assertEqual(log2.recentAlgorithmIds(), ['test3', 'test6', 'test5', 'test4', 'test']) + + def test_gui_instance(self): + self.assertIsNotNone(QgsGui.instance().processingRecentAlgorithmLog()) + + +if __name__ == '__main__': + unittest.main()