1

I am trying to create a plugin for QGIS, when I initialize the start of the plugin build and create the GUI without coding any of the buttons associated with the GUI the plugin works, but as soon as I start to edit the code of the plugin I get the following error:

AttributeError: 'object_Finder' object has no attribute 'dlg'

Here is the plugin code for some background:

from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction
# Initialize Qt resources from file resources.py
from .resources import *
# Import the code for the dialog
from .identifier_dialog import object_FinderDialog
import os.path
class object_Finder:
 """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',
 'object_Finder_{}.qm'.format(locale))
 if os.path.exists(locale_path):
 self.translator = QTranslator()
 self.translator.load(locale_path)
 QCoreApplication.installTranslator(self.translator)
 # Declare instance attributes
 self.actions = []
 self.menu = self.tr(u'&object_finder')
 # Check if plugin was started the first time in current QGIS session
 # Must be set in initGui() to survive plugin reloads
 self.first_start = None
 # 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('object_Finder', 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:
 # Adds plugin icon to Plugins toolbar
 self.iface.addToolBarIcon(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/identifier/icon.png'
 self.add_action(
 icon_path,
 text=self.tr(u'object_finder'),
 callback=self.run,
 parent=self.iface.mainWindow())
 # will be set False in run()
 
 def unload(self):
 """Removes the plugin menu item and icon from QGIS GUI."""
 for action in self.actions:
 self.iface.removePluginMenu(
 self.tr(u'&object_finder'),
 action)
 self.iface.removeToolBarIcon(action)
 def run(self):
 """Run method that performs all the real work"""
 # Create the dialog with elements (after translation) and keep reference
 # Only create GUI ONCE in callback, so that it will only load when the plugin is started
 if self.first_start == True:
 self.first_start = False
 self.dlg = object_FinderDialog()
 # show the dialog
 self.dlg.show()
 # Run the dialog event loop
 result = self.dlg.exec_()
 # See if OK was pressed
 #self.first_start = True
 #self.viewRasters()
 #self.dlg.toolButton.clicked.connect(self.openRasters)
 if result:
 # Do something useful here - delete the line containing pass and
 # substitute with your code.
 pass

My functions created are the openRasters and loadRasters.

Kadir Şahbaz
78.6k57 gold badges260 silver badges407 bronze badges
asked Aug 13, 2021 at 20:57
0

1 Answer 1

2

Spoiler: It looks like you have (perhaps inadvertently?) deleted a line from the initGui() method which which sets the self.first_start variable to True. If you look in your initGui() method, you will see that you actually have a commented line: # will be set False in run(). The line: self.first_start = True should normally be created in the boiler-plate code generated by Plugin Builder immediately after that commented line.

First let's look at the reason for the attribute error. Let's track a couple of variables in the code you have posted. The attribute which is causing the problem, self.dlg, is only created in the run() method if self.first_start is True. Ok, let's track the self.first_start variable. It is created in the __init__() method and initialized to None and its value is never changed. So, in the run() method, when you check if self.first_start is True, the condition is not passed, because self.first_start is still None- so the line: self.dlg = object_FinderDialog() is not executed and the dlg attribute of the 'object_Finder' class instance is never created. To fix this, you should add the line: self.first_start = True inside the initGui() method.

Another thing to be aware of is how and where you set up your signal/slot connections. If you prefer to stick with Plugin Builder and the generated boiler-plate code, you should also connect the signals and slots dependent on the self.first_start == True: condition. This will ensure they are only connected once, the first time that your plugin is launched. If you connect them in the run() method outside of the conditional statement, a new additional connection will be created every time you launch your plugin. This means that when you run your plugin and click 'Ok' on your plugin dialog, the slot function will be called once the first time, then twice, three times and so on until you either restart QGIS or re-load your plugin.

Another approach (and this would be my personal preference) is to remove the parts of the code referring to the self.first_start flag, simply create the instance of your dialog class inside the __init__() method of your main plugin class e.g. self.dlg = object_FinderDialog() and set up your signal slot connections inside the initGui() method e.g. self.dlg.toolButton.clicked.connect(self.openRasters). Then in the run() method, just call self.dlg.show().

Example below:

from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction
# Initialize Qt resources from file resources.py
from .resources import *
# Import the code for the dialog
from .identifier_dialog import object_FinderDialog
import os.path
class object_Finder:
 """QGIS Plugin Implementation."""
 def __init__(self, iface):
 # 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',
 'object_Finder_{}.qm'.format(locale))
 if os.path.exists(locale_path):
 self.translator = QTranslator()
 self.translator.load(locale_path)
 QCoreApplication.installTranslator(self.translator)
 # Declare instance attributes
 self.actions = []
 self.menu = self.tr(u'&object_finder')
 # create instance of dialog class
 self.dlg = object_FinderDialog()
 # noinspection PyMethodMayBeStatic
 def tr(self, message):
 # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
 return QCoreApplication.translate('object_Finder', 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):
 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:
 # Adds plugin icon to Plugins toolbar
 self.iface.addToolBarIcon(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/identifier/icon.png'
 self.add_action(
 icon_path,
 text=self.tr(u'object_finder'),
 callback=self.run,
 parent=self.iface.mainWindow())
 # connect dialog signals and slots here
 self.dlg.toolButton.clicked.connect(self.openRasters)
 def unload(self):
 """Removes the plugin menu item and icon from QGIS GUI."""
 for action in self.actions:
 self.iface.removePluginMenu(
 self.tr(u'&object_finder'),
 action)
 self.iface.removeToolBarIcon(action)
 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
 
 def openRasters(self):
 """Slot method called when self.dlg.toolButton is clicked"""
 pass
answered Aug 16, 2021 at 3:52
2
  • hello ben, thank you for the help. I have a follow up question in the init function should I remove the self.first_start = none ? and just for clarification i should remove the self.first_start == true in the run method? Commented Aug 16, 2021 at 15:48
  • @prashant, I added an example. Commented Aug 17, 2021 at 3:55

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.