[GIS] Plugin Development “ignore close event”

pluginspyqgisqgis

I'm quite new in either QGis-Plugin- and Qt-development.

I started to work on a Plugin, which displays a dialog (I used pluginBuilder and Qt-Designer) in which the user should select some options.
After the user has clicked Ok the action is performed. Everything works well so far.
Now I'd like to check the options, and if important information is missig I display a QMessageBox which tells the user what was wrong. So now to my Question: In this case the plugin-Dialog should not be closed but stay open for the user to fix the issue.
Can anybody give an example how to accomplish this?

Best Answer

To achieve this, you'll have to change the default code, both in the .ui and in the main python file. Basically, you have to use signals to catch the click event on your OK button.

First thing first: in your XX_dialog_base.ui file, replace the QDialogButtonBox object (which contains the "OK" and "Cancel" buttons) with 2 simple QPushButton objects. Let's say that the "OK" Push Button is called runButton (in QT Designer, you can set the name of an object in the right panel).

Then, the main work: in your main .py file, you have to import pyqtSignal and pyqtSlot (from PyQt4.QtCore import pyqtSignal, pyqtSlot). To display a message box, you also have to import QMessageBox (from PyQt4.QtGui import QMessageBox).

Your run method should simply look like this:

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

    # add some code to preset the GUI if needed
    self.dlg.runButton.setEnabled(True)

    # show the dialog
    self.dlg.show() 

    # Run the dialog event loop
    self.dlg.exec_()

At the beginning of the __init__ method, add the following line: super(XXX, self).__init__() (don't ask me why, it just won't work otherwise). Replace XXX with the name of your class (which is usually the name of your plugin).

In the initGui method, add (wherever) the following line: self.dlg.runButton.clicked.connect( self.onStart ). This will connect the "clicked on runButton" event to the onStart method, which will look like this (you can write it wherever you want):

@pyqtSlot()
def onStart(self):
    # here you can do whatever you want before running the main code

    # for example, display a message under some condition:
    if wrongOptions:
        messageBox = QMessageBox()
        messageBox.setWindowTitle( "Title" )
        messageBox.setText( "Content" )
        messageBox.setIcon( QMessageBox.Critical )
        messageBox.exec_()
        return

    else:
        self.dlg.runButton.setEnabled(False) # Disable OK button
        self.dlg.closeButton.setEnabled(False) # Disable cancel button
        self.main() # Run main function

You may need to add some changes to what I wrote, but this should cover most of what you want. Note that with this approach (events and signals), you can event control the options before the user clicks on OK (no need for warning messages).

Bonus links:

Related Question