[Qgis-developer] Listener for Atlas Change Page

Shawn Tse shawntse at gmail.com
Sun Dec 25 00:02:41 PST 2016


Hello there,

So I've managed to solve the problem by removing the QObject reference
in the constructor. I'm working on trying to get a message to play
when a composer is added. (My goal is to get it to add a listener for
when the atlas is turned on, and then maybe to draw a line or
something when the feature is changed.)

However, the message does not play when a composer is added. I'm not
sure what I'm doing wrong here. Thanks so much for all the help.

# -*- coding: utf-8 -*-
"""
/***************************************************************************
 AtlasProfile
                                 A QGIS plugin
 Uses the polygon/polyline atlas features to plot a profile.
                              -------------------
        begin                : 2016-12-17
        git sha              : $Format:%H$
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/
"""
from PyQt4.QtCore import QSettings, QTranslator, qVersion, QCoreApplication
from PyQt4.QtGui import QAction, QIcon
# Initialize Qt resources from file resources.py
import resources
# Import the code for the dialog
from atlas_profile_dialog import AtlasProfileDialog
import os.path
from qgis.gui import QgsMessageBar


class AtlasProfile:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(
            self.plugin_dir,
            'i18n',
            'AtlasProfile_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)


        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&Atlas-based Profile Plotter')
        # TODO: We are going to let the user set this up in a future iteration
        self.toolbar = self.iface.addToolBar(u'AtlasProfile')
        self.toolbar.setObjectName(u'AtlasProfile')

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('AtlasProfile', message)


    def add_action(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        add_to_toolbar=True,
        status_tip=None,
        whats_this=None,
        parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        # Create the dialog (after translation) and keep reference
        self.dlg = AtlasProfileDialog()

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/AtlasProfile/icon.png'
        self.add_action(
            icon_path,
            text=self.tr(u'About'),
            callback=self.run,
            parent=self.iface.mainWindow())

        # QgsMessageLog.logMessage("message here", "name there")
        self.iface.messageBar().pushMessage("Started
AtlasProfile","AtlasProfile plugin successfully reached this point")
        l=AtlasListener()
        l.setCompositionListener(self.iface)

        # this currently doesn't work. what we need it to do is to run
a listener for when we start a composition
        # that will then set a listener in that composition.
        # l.setComposition(iface.activeComposers()[0].composition())

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginMenu(
                self.tr(u'&Atlas-based Profile Plotter'),
                action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar


    def run(self):
        """Run method that performs all the real work"""
        # show the dialog
        self.dlg.show()
        # Run the dialog event loop
        result = self.dlg.exec_()
        # See if OK was pressed
        if result:
            # Do something useful here - delete the line containing pass and
            # substitute with your code.
            pass

class AtlasListener():
    # http://gis.stackexchange.com/questions/31410/how-to-implement-signals-on-add-or-modify-feature
    # @pyqtSlot(composerView)
    def setupAtlasListener(self, composerView):
        print 'a composer was added.'
        iface.messageBar().pushMessage("Error","We added a composer",
level=QgsMessageBar.CRITICAL)

    # @pyqtSlot(feature)
    def featureChanged(self, feature):
        iface.messageBar().pushMessage("Error","The Atlas was
changed", level=QgsMessageBar.CRITICAL)

    def setCompositionListener(self, iface):
        iface.composerAdded.connect(self.setupAtlasListener)
        print 'we set up an atlas listener.'

    def setComposition(self, composition):
        composition.atlasComposition().featureChanged.connect(self.featureChanged)



On Sat, Dec 24, 2016 at 10:46 PM, Shawn Tse <shawntse at gmail.com> wrote:
> Hi, thanks so much for the help. Right now the AtlasListener is throwing up a
>
> NameError: name 'QObject' is not defined
>
> I looked up the API and when instantiating the AtlasListener, I can
> pass a QObject into the constructor. What QObject should I put in
> there?
>
> Thanks
>
> On Thu, Dec 15, 2016 at 7:35 PM, Nyall Dawson <nyall.dawson at gmail.com> wrote:
>> On 14 December 2016 at 13:42, Shawn Tse <shawntse at gmail.com> wrote:
>>> Thanks, that looks like exactly the kind of function that I'm looking
>>> for. I'm using QGIS 2.18.0, I'm wondering, is there any way to use
>>> this function without having to select a menu item in the the plugin?
>>>
>>> I'm not sure how to get started, because most of the existing plugins
>>> seem to be activated by a menu item or a toolbar button in the main
>>> editor instead of via the atlas interface.
>>>
>>> Thank you and I really appreciate the help!
>>
>> Hi Shawn,
>>
>> Have a read up on how signals and slots work in PyQt. Basically you
>> can listen out for the signal and get it to trigger a function in your
>> python class. Untested psuedocode below:
>>
>> class AtlasListener(QObject):
>>    def featureChanged(self, feature):
>>       print('new feature: {}'.format(feature.id())
>>
>>   def setComposition(self, composition):
>>       composition.atlasComposition().featureChanged.connect(self.featureChanged)
>>
>> l=AtlasListener()
>> l.setComposition(iface.activeComposers()[0].composition())
>>
>>
>> Nyall
>>
>>
>>>
>>> On Mon, Dec 12, 2016 at 11:02 PM, Nyall Dawson <nyall.dawson at gmail.com> wrote:
>>>>
>>>>
>>>> On 13 Dec 2016 4:44 PM, "Shawn Tse" <shawntse at gmail.com> wrote:
>>>>
>>>> Hi all,
>>>>
>>>> I'm interested in being able to make graphs that update depending on
>>>> what the active feature in the atlas is. I know some python and a
>>>> little bit of the QGIS API, but I'm curious - is there some sort of
>>>> listener function for the atlas that gets called whenever the Preview
>>>> Atlas/Prev Feature/Next Feature/First Feature/Last Feature buttons are
>>>> pressed?
>>>>
>>>>
>>>> Try QgsAtlasComposition::featureChanged. See
>>>> https://qgis.org/api/classQgsAtlasComposition.html#ab1a15fdb6d20ebb1254ebb6714254fac
>>>>
>>>> Nyall
>>>>
>>>>
>>>> What I eventually want to do is to manipulate the node items that were
>>>> created in a recent updated version of QGIS, and use them to make a
>>>> line graph, gridlines, etc.
>>>>
>>>> I'm hoping to draw the data directly from the shapefiles themselves or
>>>> from a .csv file.
>>>>
>>>> I originally tried to use an expression in a text box to do this, but
>>>> it seems that expressions are unable to use custom functions to
>>>> manipulate the composer. So I'm hoping that there's some sort of
>>>> Listener method.
>>>>
>>>> Thanks.
>>>> _______________________________________________
>>>> Qgis-developer mailing list
>>>> Qgis-developer at lists.osgeo.org
>>>> List info: http://lists.osgeo.org/mailman/listinfo/qgis-developer
>>>> Unsubscribe: http://lists.osgeo.org/mailman/listinfo/qgis-developer
>>>>
>>>>
>>> _______________________________________________
>>> Qgis-developer mailing list
>>> Qgis-developer at lists.osgeo.org
>>> List info: http://lists.osgeo.org/mailman/listinfo/qgis-developer
>>> Unsubscribe: http://lists.osgeo.org/mailman/listinfo/qgis-developer


More information about the Qgis-developer mailing list