3

I started to develop my own QGIS plugin today and I was pretty confident when browsing through all the different literature and tutorials available on the web. I started this beginners tutorial and until step 19 everything just worked like a charm! But starting to edit the code was not going to work for me without getting an error. I was trying to include the "Select file output" functionality in the code but I just get all the time the AttributeError:

Couldn't load plugin SaveAttributes due to an error when calling its classFactory() method 

AttributeError: SaveAttributes instance has no attribute 'select_output_file' 
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/qgis/utils.py", line 333, in startPlugin
    plugins[packageName] = package.classFactory(iface)
  File "/home/alexgis/.qgis2/python/plugins/SaveAttributes/__init__.py", line 35, in classFactory
    return SaveAttributes(iface)
  File "/home/alexgis/.qgis2/python/plugins/SaveAttributes/save_attributes.py", line 73, in __init__
    self.dlg.pushButton.clicked.connect(self.select_output_file)
AttributeError: SaveAttributes instance has no attribute 'select_output_file'


Python version: 2.7.12 (default, Nov 19 2016, 06:48:10) [GCC 5.4.0 20160609] 
QGIS version: 2.18.11 Las Palmas, 29548e7 

My code does exactly match the tutorials code. I also make shure that all components are up-to-date the double checked the folder directory and of course the line indentation. I also followed the advice given in this QGIS bug report, but nothing seems to help.

This is my first time developing a QGIS plugin and I have to admit that I found it quite challenging without knowing the appropriate QT or Core functions by heart, but I really want to learn this, so I do not want to give up in this early stage. I browsed through all the other questions regarding the Attribute Error and tried a lot of workarounds but I still get the same error.

I am working in a OSGeoLive 11.0 Environment.

I am really looking forward to your help and hope that I ask this question in the right way to fit into this board.

Code as an attachment:

from PyQt4.QtCore import QSettings, QTranslator, qVersion, QCoreApplication
from PyQt4.QtGui import QAction, QIcon, QFileDialog
# Initialize Qt resources from file resources.py
import resources
# Import the code for the dialog
from save_attributes_dialog import SaveAttributesDialog
import os.path


class SaveAttributes:
    """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',
            'SaveAttributes_{}.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)


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

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

        self.dlg.lineEdit.clear()
        self.dlg.pushButton.clicked.connect(self.select_output_file)

    # 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('SaveAttributes', 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
        """

        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.addPluginToVectorMenu(
                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/SaveAttributes/icon.png'
        self.add_action(
            icon_path,
            text=self.tr(u'Save Attributes'),
            callback=self.run,
            parent=self.iface.mainWindow())


    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginVectorMenu(
                self.tr(u'&Save Attributes'),
                action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar


    def select_output_file(self):
        filename = QFileDialog.getSaveFileName(self.dlg, "Select output file ","", '*.txt')
        self.dlg.lineEdit.setText(filename)

    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
1

You have to put this code on the bottom of the init method:

self.dlg = SaveAttributesDialog()
self.dlg.lineEdit.clear()
self.dlg.pushButton.clicked.connect(self.select_output_file)

Then clear the following code where is written:

# Create the dialog (after translation) and keep reference
self.dlg = SaveAttributesDialog()  <-- delete this line

Then it should work. But don't know if this has more consequences in the future.

0

I'm totally new with python and QGIS but I guessed that the dialog not yet had been instanciated in the init method so i moved the code to beginning of run method instead. It seems to work just fine. But as I said I'm new to python so I don't know if it breaks any 'rules'.

def run(self):
    """Run method that performs all the real work"""

    self.dlg.lineEdit.clear()
    self.dlg.pushButton.clicked.connect(self.select_output_file)

    # show the dialog
    self.dlg.show()
    ...

Argh, did not now that several clicked events could be added so this method only work if one first check that no event has been added before adding it.

Suggest post gets deleted.

-2

try to intialize self.dlg for the program to identify it.

  • 1
    You should add more detail about how to do what you say in your answer, and also how doing this will solve the problem. Looking at your response, it's difficult to work out if this intended to be an answer, or just a comment – nmtoken Feb 27 '18 at 8:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.