[GIS] How to reference to the in QT designer created buttons in python

guipythonqgisqtqt-designer

I have created an own QDialog (using QT Designer) with three push buttons. The names are pButton1, pButton2 and pButton3. After saving the file I created the ui file by using:

pyuic4 ui_xy.ui > ui_xy.py

I can see my created GUI in QGIS, but I have no idea how to get a reference to the buttons. When I use QMessagebox it is very easy to get to know wich button was pushed, but using the same way for this GUI doesn´t work.

Using QMessagebox the following code works:

ret = QMessageBox(..., QMessageBox.Ok)
if (ret = QMessageBox.Ok):
   DO SOMETHING
else:
   DO SOMETHING

When I use the standard GUI with the Ok and Cancel button, the following code works:

dlg = xyDialog()
dlg.show
result = dlg.exec_()
if result = 0:
...

How can I obtain the same result using my buttons? Replacing the 0 with pbutton1 (or 2 or 3) doesn´t seem to work.

Do you know a good solution or tutorial?

Thanks!

Best,

Best Answer

kopi,

QMessageBox, and similar convenience classes, allow for quick creation of standard dialogs, generally using static methods within their class. Creating custom buttons and Qt widgets requires making the Qt signal/slot connections between them, as per the desired GUI interaction for the purpose.

When using QtDesigner and after the compilation of your form to a ui.py file, you will want to import it into a Python module and instantiate it, so that you can directly reference the form's widgets. With PyQGIS plugins, this is usually done in a module specific to the dialog, though with very simple plugins it can be done inside the plugin's base class.

A very good reference for creating PyQGIS plugins, in addition to the PyQGIS Cookbook, is the QGIS Workshop v1.0.0: for help with your question, specifically the section on integrating a tool's output with the GUI:

01  from PyQt4 import QtCore, QtGui
02  from ui_vector_selectbypoint import Ui_vector_selectbypoint
03  
04  class vector_selectbypointDialog(QtGui.QDialog):
05  
06      def __init__(self):
07          QtGui.QDialog.__init__(self)
08          # Set up the user interface from Designer.
09          self.ui = Ui_vector_selectbypoint()
10          self.ui.setupUi(self)

Line by line:

01 Basic PyQt4 imports.
02 Import of custom form's class from its compiled ui file.
04 Your custom dialog's class, which inherits from QDialog.
06 init method called when your dialog is instantiated.
07 Instantiate parent QDialog class, so your class gains its attributes.
09 Instantiate and assign your QtDesigner form class.
10 Call the form class's setupUi() method, so its widgets are available.

Once you have self.ui referencing your custom layout form, you can access its widgets anywhere in your custom dialog class and set up signal/slot connections or named slots.

Example showing new style signal/slot connection (using some object names from comments) and named slot connection:

from PyQt4 import QtCore, QtGui
from ui_ubsearch import Ui_UBSearchDialog  # don't know name of your form class

class UBSearchDialog(QtGui.QDialog):

    def __init__(self):
        QtGui.QDialog.__init__(self)
        # Set up the user interface from Designer.
        self.ui = Ui_UBSearchDialog()
        self.ui.setupUi(self)

        # make new style signal/slot connection
        self.ui.bgrz.clicked.connect(self.exec_grz)

    # slot for connection
    def exec_grz(self):
        # do something
        pass  # <-- replace with actual code

    # named slot example
    # (doesn't require a previously built connection for form widgets)
    @QtCore.pyqtSlot()
    def on_myOtherFormButton_clicked(self):
        # do something
        pass  # <-- replace with actual code

Your UBSearchDialog class will, in turn, need to be instantiated elsewhere (e.g. imported into your plugin's base class and instantiated in its run() method):

ubs_dlg = UBSearchDialog()
ubs_dlg.exec_()

Also, see https://stackoverflow.com/questions/2462401 for more examples concerning named slots and how new style connections keep things cleaner and more 'Pythonic.'


Concerning your second question, there are a couple of things you'll want to clean up before testing more. First, you have multiple imports for the same modules. Your custom dialog module could look like this instead ...

from PyQt4.QtCore import pyqtSlot
from PyQt4.QtGui import QDialog

import datavisualization
from ui_ubsearch import Ui_ubsearch


class ubsearchDialog(QDialog):

    def __init__(self, iface):
        QDialog.__init__(self)

        # Set up the user interface from Designer.
        self.ui = Ui_ubsearch()
        self.ui.setupUi(self)

        # Save reference to the QGIS interface
        self.iface = iface

    @pyqtSlot()
    def on_bgrz_clicked(self):
        # Get an instance of the class
        d = datavisualization.datavisualization(self.iface)

        # Call the method of the intance
        d.data_visualization()

        # Close UI
        self.close()

Of note here is the import of only those names from the PyQt4 modules that are used in the script (QDialog and pyqtSlot). See working with Python modules, for more info. All other unused module imports were removed. You can use pylint to help with finding those unused modules.

Also, when constructing an instance of the class the iface reference is gathered and assigned so it can be passed to your datavisualization module (fixing the parameter error). This means you have to pass iface in from your original plugin module that calls the construction of the custom dialog, e.g. ubsearchDialog(self.iface). So, iface needs a saved reference in your base plugin module as well, in order to be passed to your dialog. This is usually already set up for you if you use tools like Plugin Builder.

You will want to clean up your datavisualization module's imports in a similar fashion.

import os
import datetime
import time
import psycopg2

from PyQt4.QtGui import QMessageBox
from qgis import core

Lastly, you will want to read up on PEP8, Python style guide. Your pasted code contained a mix of spaces and tabs for indention: only-spaces is generally preferred.

Related Question