MqttMonitor/MqttConnectionProfilesDialog.py

changeset 28
0f02baed8308
parent 26
ad232a5129cc
child 30
17ef10819773
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MqttMonitor/MqttConnectionProfilesDialog.py	Sat Sep 08 16:55:42 2018 +0200
@@ -0,0 +1,699 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2018 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to edit the MQTT connection profiles.
+"""
+
+from __future__ import unicode_literals
+
+import collections
+
+from PyQt5.QtCore import pyqtSlot, Qt, QUuid
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QAbstractButton, \
+    QListWidgetItem, QInputDialog, QLineEdit
+
+from E5Gui import E5MessageBox
+from E5Gui.E5PathPicker import E5PathPickerModes
+
+from .Ui_MqttConnectionProfilesDialog import Ui_MqttConnectionProfilesDialog
+
+import UI.PixmapCache
+from Utilities.crypto import pwConvert
+
+
+class MqttConnectionProfilesDialog(QDialog, Ui_MqttConnectionProfilesDialog):
+    """
+    Class implementing a dialog to edit the MQTT connection profiles.
+    """
+    def __init__(self, client, profiles, parent=None):
+        """
+        Constructor
+        
+        @param client reference to the MQTT client object
+        @type MqttClient
+        @param profiles dictionary containing dictionaries containing the
+            connection parameters. Each entry must have the keys
+            "BrokerAddress", "BrokerPort", "ClientId",
+            "Keepalive", "CleanSession", "Username", "Password", "WillTopic",
+            "WillMessage", "WillQos", "WillRetain", "TlsEnable", "TlsCaCert",
+            "TlsClientCert", "TlsClientKey".
+        @type dict
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(MqttConnectionProfilesDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__client = client
+        
+        self.__profiles = collections.defaultdict(self.__defaultProfile)
+        self.__profiles.update(profiles)
+        self.__profilesChanged = False
+        
+        self.plusButton.setIcon(UI.PixmapCache.getIcon("plus.png"))
+        self.copyButton.setIcon(UI.PixmapCache.getIcon("editCopy.png"))
+        self.minusButton.setIcon(UI.PixmapCache.getIcon("minus.png"))
+        
+        self.tlsCertsFilePicker.setMode(E5PathPickerModes.OpenFileMode)
+        self.tlsCertsFilePicker.setFilters(
+            self.tr("Certificate Files (*.crt *.pem);;All Files (*)"))
+        self.tlsSelfSignedCertsFilePicker.setMode(
+            E5PathPickerModes.OpenFileMode)
+        self.tlsSelfSignedCertsFilePicker.setFilters(
+            self.tr("Certificate Files (*.crt *.pem);;All Files (*)"))
+        self.tlsSelfSignedClientCertFilePicker.setMode(
+            E5PathPickerModes.OpenFileMode)
+        self.tlsSelfSignedClientCertFilePicker.setFilters(
+            self.tr("Certificate Files (*.crt *.pem);;All Files (*)"))
+        self.tlsSelfSignedClientKeyFilePicker.setMode(
+            E5PathPickerModes.OpenFileMode)
+        self.tlsSelfSignedClientKeyFilePicker.setFilters(
+            self.tr("Key Files (*.key *.pem);;All Files (*)"))
+        
+        self.profileTabWidget.setCurrentIndex(0)
+        
+        if len(self.__profiles) == 0:
+            self.minusButton.setEnabled(False)
+            self.copyButton.setEnabled(False)
+        
+        self.profileFrame.setEnabled(False)
+        self.__populatingProfile = False
+        self.__deletingProfile = False
+        
+        self.__populateProfilesList()
+    
+    @pyqtSlot(str)
+    def on_profileEdit_textChanged(self, name):
+        """
+        Private slot to handle changes of the profile name.
+        
+        @param name name of the profile
+        @type str
+        """
+        self.__updateApplyButton()
+    
+    @pyqtSlot(QAbstractButton)
+    def on_profileButtonBox_clicked(self, button):
+        """
+        Private slot handling presses of the profile buttons.
+        
+        @param button reference to the pressed button
+        @type QAbstractButton
+        """
+        if button == self.profileButtonBox.button(QDialogButtonBox.Apply):
+            currentProfile = self.__applyProfile()
+            self.__populateProfilesList(currentProfile)
+        
+        elif button == self.profileButtonBox.button(QDialogButtonBox.Reset):
+            self.__resetProfile()
+        
+        elif button == self.profileButtonBox.button(
+                QDialogButtonBox.RestoreDefaults):
+            self.__populateProfileDefault()
+    
+    @pyqtSlot(QListWidgetItem, QListWidgetItem)
+    def on_profilesList_currentItemChanged(self, current, previous):
+        """
+        Private slot to handle a change of the current profile.
+        
+        @param current new current item
+        @type QListWidgetItem
+        @param previous previous current item
+        @type QListWidgetItem
+        """
+        self.minusButton.setEnabled(current is not None)
+        self.copyButton.setEnabled(current is not None)
+        
+        if current is not previous:
+            if not self.__deletingProfile and self.__isChangedProfile():
+                # modified profile belongs to previous
+                yes = E5MessageBox.yesNo(
+                    self,
+                    self.tr("Changed Connection Profile"),
+                    self.tr("""The current profile has unsaved changes."""
+                            """ Shall these be saved?"""),
+                    icon=E5MessageBox.Warning,
+                    yesDefault=True)
+                if yes:
+                    self.__applyProfile()
+        
+        if current:
+            profileName = current.text()
+            self.__populateProfile(profileName)
+        else:
+            self.__clearProfile()
+    
+    @pyqtSlot()
+    def on_plusButton_clicked(self):
+        """
+        Private slot to add a new empty profile entry.
+        """
+        profileName, ok = QInputDialog.getText(
+            self,
+            self.tr("New Connection Profile"),
+            self.tr("Enter name for the new Connection Profile:"),
+            QLineEdit.Normal)
+        if ok and bool(profileName):
+            if profileName in self.__profiles:
+                E5MessageBox.warning(
+                    self,
+                    self.tr("New Connection Profile"),
+                    self.tr("""<p>A connection named <b>{0}</b> exists"""
+                            """ already. Aborting...</p>""").format(
+                        profileName))
+            else:
+                itm = QListWidgetItem(profileName, self.profilesList)
+                self.profilesList.setCurrentItem(itm)
+                self.brokerAddressEdit.setFocus(Qt.OtherFocusReason)
+    
+    @pyqtSlot()
+    def on_copyButton_clicked(self):
+        """
+        Private slot to copy the selected profile entry.
+        """
+        itm = self.profilesList.currentItem()
+        if itm:
+            profileName = itm.text()
+            newProfileName, ok = QInputDialog.getText(
+                self,
+                self.tr("Copy Connection Profile"),
+                self.tr("Enter name for the copied Connection Profile:"),
+                QLineEdit.Normal)
+            if ok and bool(newProfileName):
+                if newProfileName in self.__profiles:
+                    E5MessageBox.warning(
+                        self,
+                        self.tr("Copy Connection Profile"),
+                        self.tr("""<p>A connection named <b>{0}</b> exists"""
+                                """ already. Aborting...</p>""").format(
+                            newProfileName))
+                else:
+                    profile = self.__defaultProfile()
+                    profile.update(self.__profiles[profileName])
+                    self.__profiles[newProfileName] = profile
+                    
+                    itm = QListWidgetItem(newProfileName, self.profilesList)
+                    self.profilesList.setCurrentItem(itm)
+                    self.brokerAddressEdit.setFocus(Qt.OtherFocusReason)
+    
+    @pyqtSlot()
+    def on_minusButton_clicked(self):
+        """
+        Private slot to delete the selected entry.
+        """
+        itm = self.profilesList.currentItem()
+        if itm:
+            profileName = itm.text()
+            yes = E5MessageBox.yesNo(
+                self,
+                self.tr("Delete Connection Profile"),
+                self.tr("""<p>Shall the Connection Profile <b>{0}</b>"""
+                        """ really be deleted?</p>""").format(profileName)
+            )
+            if yes:
+                self.__deletingProfile = True
+                del self.__profiles[profileName]
+                self.__profilesChanged = True
+                self.__populateProfilesList()
+                self.__deletingProfile = False
+        
+        self.profilesList.setFocus(Qt.OtherFocusReason)
+    
+    def getProfiles(self):
+        """
+        Public method to return a dictionary of profiles.
+        
+        @return dictionary containing dictionaries containing the defined
+            connection profiles. Each entry have the keys "BrokerAddress",
+            "BrokerPort", "ClientId", "Keepalive", "CleanSession", "Username",
+            "Password", "WillTopic", "WillMessage", "WillQos", "WillRetain",
+            "TlsEnable", "TlsCaCert", "TlsClientCert", "TlsClientKey".
+        @rtype dict
+        """
+        profilesDict = {}
+        profilesDict.update(self.__profiles)
+        return profilesDict
+    
+    def __applyProfile(self):
+        """
+        Private method to apply the entered data to the list of profiles.
+        
+        @return name of the applied profile
+        @rtype str
+        """
+        profileName = self.profileEdit.text()
+        profile = {
+            "BrokerAddress": self.brokerAddressEdit.text(),
+            "BrokerPort": self.brokerPortSpinBox.value(),
+            "ClientId": self.clientIdEdit.text(),
+            "Keepalive": self.keepaliveSpinBox.value(),
+            "CleanSession": self.cleanSessionCheckBox.isChecked(),
+            "Username": self.usernameEdit.text(),
+            "Password": pwConvert(self.passwordEdit.text(), encode=True),
+            "WillTopic": self.willTopicEdit.text(),
+            "WillMessage": self.willMessageEdit.toPlainText(),
+            "WillQos": self.willQosSpinBox.value(),
+            "WillRetain": self.willRetainCheckBox.isChecked(),
+            "TlsEnable": self.tlsGroupBox.isChecked(),
+            "TlsCaCert": "",
+            "TlsClientCert": "",
+            "TlsClientKey": "",
+        }
+        if profile["TlsEnable"]:
+            if self.tlsCertsFileButton.isChecked():
+                profile["TlsCaCert"] = self.tlsCertsFilePicker.text()
+            elif self.tlsSelfSignedCertsButton.isChecked():
+                profile["TlsCaCert"] = \
+                    self.tlsSelfSignedCertsFilePicker.text()
+                profile["TlsClientCert"] = \
+                    self.tlsSelfSignedClientCertFilePicker.text()
+                profile["TlsClientKey"] = \
+                    self.tlsSelfSignedClientKeyFilePicker.text()
+        
+        self.__profiles[profileName] = profile
+        self.__profilesChanged = True
+        
+        return profileName
+    
+    def __defaultProfile(self):
+        """
+        Private method to populate non-existing profile items.
+        
+        @return default dictionary entry
+        @rtype dict
+        """
+        defaultProfile = self.__client.defaultConnectionOptions()
+        defaultProfile["BrokerAddress"] = ""
+        if defaultProfile["TlsEnable"]:
+            defaultProfile["BrokerPort"] = 8883
+        else:
+            defaultProfile["BrokerPort"] = 1883
+        
+        return defaultProfile
+    
+    def __populateProfilesList(self, currentProfile=""):
+        """
+        Private method to populate the list of defined profiles.
+        
+        @param currentProfile name of the current profile
+        @type str
+        """
+        if not currentProfile:
+            currentItem = self.profilesList.currentItem()
+            if currentItem:
+                currentProfile = currentItem.text()
+        
+        self.profilesList.clear()
+        self.profilesList.addItems(sorted(self.__profiles.keys()))
+        
+        if currentProfile:
+            items = self.profilesList.findItems(
+                currentProfile, Qt.MatchExactly)
+            if items:
+                self.profilesList.setCurrentItem(items[0])
+        
+        if len(self.__profiles) == 0:
+            self.profileFrame.setEnabled(False)
+    
+    def __populateProfile(self, profileName):
+        """
+        Private method to populate the profile data entry fields.
+        
+        @param profileName name of the profile to get data from
+        @type str
+        """
+        profile = self.__defaultProfile()
+        if profileName:
+            profile.update(self.__profiles[profileName])
+        
+        self.__populatingProfile = True
+        if profileName is not None:
+            self.profileEdit.setText(profileName)
+        self.brokerAddressEdit.setText(profile["BrokerAddress"])
+        self.brokerPortSpinBox.setValue(profile["BrokerPort"])
+        self.clientIdEdit.setText(profile["ClientId"])
+        self.keepaliveSpinBox.setValue(profile["Keepalive"])
+        self.cleanSessionCheckBox.setChecked(profile["CleanSession"])
+        self.usernameEdit.setText(profile["Username"])
+        self.passwordEdit.setText(pwConvert(profile["Password"], encode=False))
+        self.willTopicEdit.setText(profile["WillTopic"])
+        self.willMessageEdit.setPlainText(profile["WillMessage"])
+        self.willQosSpinBox.setValue(profile["WillQos"])
+        self.willRetainCheckBox.setChecked(profile["WillRetain"])
+        self.tlsGroupBox.setChecked(profile["TlsEnable"])
+        if profile["TlsCaCert"] and profile["TlsClientCert"]:
+            self.tlsSelfSignedCertsButton.setChecked(True)
+            self.tlsSelfSignedCertsFilePicker.setText(profile["TlsCaCert"])
+            self.tlsSelfSignedClientCertFilePicker.setText(
+                profile["TlsClientCert"])
+            self.tlsSelfSignedClientKeyFilePicker.setText(
+                profile["TlsClientKey"])
+        elif profile["TlsCaCert"]:
+            self.tlsCertsFileButton.setChecked(True)
+            self.tlsCertsFilePicker.setText(profile["TlsCaCert"])
+        else:
+            self.tlsDefaultCertsButton.setChecked(True)
+        self.__populatingProfile = False
+        
+        self.profileFrame.setEnabled(True)
+        self.__updateApplyButton()
+    
+    def __clearProfile(self):
+        """
+        Private method to clear the profile data entry fields.
+        """
+        self.__populatingProfile = True
+        self.profileEdit.setText("")
+        self.brokerAddressEdit.setText("")
+        self.brokerPortSpinBox.setValue(1883)
+        self.clientIdEdit.setText("")
+        self.keepaliveSpinBox.setValue(60)
+        self.cleanSessionCheckBox.setChecked(True)
+        self.usernameEdit.setText("")
+        self.passwordEdit.setText("")
+        self.willTopicEdit.setText("")
+        self.willMessageEdit.setPlainText("")
+        self.willQosSpinBox.setValue(0)
+        self.willRetainCheckBox.setChecked(False)
+        self.tlsGroupBox.setChecked(False)
+        self.tlsDefaultCertsButton.setChecked(True)
+        self.tlsCertsFileButton.setChecked(True)
+        self.tlsCertsFilePicker.setText("")
+        self.tlsSelfSignedCertsButton.setChecked(False)
+        self.tlsSelfSignedCertsFilePicker.setText("")
+        self.tlsSelfSignedClientCertFilePicker.setText("")
+        self.tlsSelfSignedClientKeyFilePicker.setText("")
+        self.__populatingProfile = False
+        
+        self.profileFrame.setEnabled(False)
+        self.__updateApplyButton()
+    
+    def __resetProfile(self):
+        """
+        Private method to reset the profile data entry fields to their stored
+        values.
+        """
+        profileName = self.profileEdit.text()
+        if profileName in self.__profiles:
+            self.__populateProfile(profileName)
+    
+    def __populateProfileDefault(self):
+        """
+        Private method to populate the profile data entry fields with default
+        profile values.
+        """
+        self.__populateProfile(None)
+    
+    def __isChangedProfile(self):
+        """
+        Private method to check, if the currently shown profile contains some
+        changed data.
+        
+        @return flag indicating changed data
+        @type bool
+        """
+        profileName = self.profileEdit.text()
+        if profileName == "":
+            return False
+        
+        elif profileName in self.__profiles:
+            profile = self.__defaultProfile()
+            profile.update(self.__profiles[profileName])
+            changed = (
+                self.brokerAddressEdit.text() != profile["BrokerAddress"] or
+                self.brokerPortSpinBox.value() != profile["BrokerPort"] or
+                self.clientIdEdit.text() != profile["ClientId"] or
+                self.keepaliveSpinBox.value() != profile["Keepalive"] or
+                self.cleanSessionCheckBox.isChecked() !=
+                profile["CleanSession"] or
+                self.usernameEdit.text() != profile["Username"] or
+                self.passwordEdit.text() !=
+                pwConvert(profile["Password"], encode=False) or
+                self.willTopicEdit.text() != profile["WillTopic"] or
+                self.willMessageEdit.toPlainText() != profile["WillMessage"] or
+                self.willQosSpinBox.value() != profile["WillQos"] or
+                self.willRetainCheckBox.isChecked() != profile["WillRetain"] or
+                self.tlsGroupBox.isChecked() != profile["TlsEnable"]
+            )
+            # check TLS stuff only, if not yet changed
+            if not changed:
+                if self.tlsCertsFileButton.isChecked():
+                    changed |= (
+                        self.tlsCertsFilePicker.text() != profile["TlsCaCert"]
+                    )
+                elif self.tlsSelfSignedCertsButton.isChecked():
+                    changed |= (
+                        self.tlsSelfSignedCertsFilePicker.text() !=
+                        profile["TlsCaCert"] or
+                        self.tlsSelfSignedClientCertFilePicker.text() !=
+                        profile["TlsClientCert"] or
+                        self.tlsSelfSignedClientKeyFilePicker.text() !=
+                        profile["TlsClientKey"]
+                    )
+            return changed
+        
+        else:
+            return True
+    
+    def __updateApplyButton(self):
+        """
+        Private method to set the state of the Apply button.
+        """
+        # condition 1: profile name and broker address need to be given
+        enable = (bool(self.profileEdit.text()) and
+                  bool(self.brokerAddressEdit.text()))
+        
+        # condition 2: if client ID is empty, clean session must be selected
+        if not self.__populatingProfile:
+            if self.clientIdEdit.text() == "" and \
+                    not self.cleanSessionCheckBox.isChecked():
+                enable = False
+                E5MessageBox.critical(
+                    self,
+                    self.tr("Invalid Connection Parameters"),
+                    self.tr("An empty Client ID requires a clean session."))
+        
+        if self.tlsGroupBox.isChecked():
+            if self.tlsCertsFileButton.isChecked():
+                # condition 3a: if CA certificates file shall be used, it must
+                # be given
+                enable &= bool(self.tlsCertsFilePicker.text())
+            elif self.tlsSelfSignedCertsButton.isChecked():
+                # condition 3b: if client certificates shall be used, all files
+                # must be given
+                enable &= (
+                    bool(self.tlsSelfSignedCertsFilePicker.text()) and
+                    bool(self.tlsSelfSignedClientCertFilePicker.text()) and
+                    bool(self.tlsSelfSignedClientKeyFilePicker.text())
+                )
+        
+        self.profileButtonBox.button(QDialogButtonBox.Apply).setEnabled(enable)
+    
+    @pyqtSlot(str)
+    def on_brokerAddressEdit_textChanged(self, address):
+        """
+        Private slot handling a change of the broker address.
+        
+        @param address broker address
+        @type str
+        """
+        self.__updateApplyButton()
+    
+    @pyqtSlot()
+    def on_generateIdButton_clicked(self):
+        """
+        Private slot to generate a client ID.
+        """
+        uuid = QUuid.createUuid()
+        self.clientIdEdit.setText(uuid.toString(QUuid.WithoutBraces))
+    
+    @pyqtSlot(str)
+    def on_clientIdEdit_textChanged(self, clientId):
+        """
+        Private slot handling a change of the client ID string.
+        
+        @param clientId client ID
+        @type str
+        """
+        self.__updateApplyButton()
+    
+    @pyqtSlot(bool)
+    def on_cleanSessionCheckBox_clicked(self, checked):
+        """
+        Private slot to handle a change of the clean session selection.
+        
+        @param checked current state of the clean session selection
+        @type bool
+        """
+        self.__updateApplyButton()
+    
+    @pyqtSlot(str)
+    def on_tlsCertsFilePicker_textChanged(self, path):
+        """
+        Private slot handling a change of the TLS CA certificates file.
+        
+        @param path file path
+        @type str
+        """
+        self.__updateApplyButton()
+    
+    @pyqtSlot(str)
+    def on_tlsSelfSignedCertsFilePicker_textChanged(self, path):
+        """
+        Private slot handling a change of the TLS CA certificates file.
+        
+        @param path file path
+        @type str
+        """
+        self.__updateApplyButton()
+    
+    @pyqtSlot(str)
+    def on_tlsSelfSignedClientCertFilePicker_textChanged(self, path):
+        """
+        Private slot handling a change of the TLS client certificate file.
+        
+        @param path file path
+        @type str
+        """
+        self.__updateApplyButton()
+    
+    @pyqtSlot(str)
+    def on_tlsSelfSignedClientKeyFilePicker_textChanged(self, path):
+        """
+        Private slot handling a change of the TLS client key file.
+        
+        @param path file path
+        @type str
+        """
+        self.__updateApplyButton()
+    
+    @pyqtSlot(bool)
+    def on_tlsGroupBox_toggled(self, checked):
+        """
+        Private slot handling the selection of TLS mode.
+        
+        @param checked state of the selection
+        @type bool
+        """
+        if checked and self.brokerPortSpinBox.value() == 1883:
+            # port is still standard non-TLS port
+            yes = E5MessageBox.yesNo(
+                self,
+                self.tr("SSL/TLS Enabled"),
+                self.tr(
+                    """Encrypted connection using SSL/TLS has been enabled."""
+                    """ However, the broker port is still the default"""
+                    """ unencrypted port (port 1883). Shall this be"""
+                    """ changed?"""),
+                icon=E5MessageBox.Warning,
+                yesDefault=True)
+            if yes:
+                self.brokerPortSpinBox.setValue(8883)
+        elif not checked and self.brokerPortSpinBox.value() == 8883:
+            # port is still standard TLS port
+            yes = E5MessageBox.yesNo(
+                self,
+                self.tr("SSL/TLS Disabled"),
+                self.tr(
+                    """Encrypted connection using SSL/TLS has been disabled."""
+                    """ However, the broker port is still the default"""
+                    """ encrypted port (port 8883). Shall this be"""
+                    """ changed?"""),
+                icon=E5MessageBox.Warning,
+                yesDefault=True)
+            if yes:
+                self.brokerPortSpinBox.setValue(1883)
+        
+        self.__updateApplyButton()
+    
+    @pyqtSlot(bool)
+    def on_tlsDefaultCertsButton_toggled(self, checked):
+        """
+        Private slot handling the selection of using the default
+        certificates file.
+        
+        @param checked state of the selection
+        @type bool
+        """
+        self.__updateApplyButton()
+    
+    @pyqtSlot(bool)
+    def on_tlsCertsFileButton_toggled(self, checked):
+        """
+        Private slot handling the selection of using a non-default
+        certificates file.
+        
+        @param checked state of the selection
+        @type bool
+        """
+        self.__updateApplyButton()
+    
+    @pyqtSlot(bool)
+    def on_tlsSelfSignedCertsButton_toggled(self, checked):
+        """
+        Private slot handling the selection of using self signed
+        client certificate and key files.
+        
+        @param checked state of the selection
+        @type bool
+        """
+        self.__updateApplyButton()
+    
+    @pyqtSlot()
+    def reject(self):
+        """
+        Public slot to reject the dialog changes.
+        """
+        if self.__isChangedProfile():
+            button = E5MessageBox.warning(
+                self,
+                self.tr("Changed Connection Profile"),
+                self.tr("""The current profile has unsaved changes. Shall"""
+                        """ these be saved?"""),
+                E5MessageBox.StandardButtons(
+                    E5MessageBox.Discard |
+                    E5MessageBox.Save),
+                E5MessageBox.Save)
+            if button == E5MessageBox.Save:
+                self.__applyProfile()
+                return
+        
+        if self.__profilesChanged:
+            button = E5MessageBox.warning(
+                self,
+                self.tr("Changed Connection Profiles"),
+                self.tr("""The list of connection profiles has unsaved"""
+                        """ changes."""),
+                E5MessageBox.StandardButtons(
+                    E5MessageBox.Abort |
+                    E5MessageBox.Discard |
+                    E5MessageBox.Save),
+                E5MessageBox.Save)
+            if button == E5MessageBox.Save:
+                super(MqttConnectionProfilesDialog, self).accept()
+                return
+            elif button == E5MessageBox.Abort:
+                return
+        
+        super(MqttConnectionProfilesDialog, self).reject()
+    
+    @pyqtSlot()
+    def accept(self):
+        """
+        Public slot to accept the dialog.
+        """
+        if self.__isChangedProfile():
+            yes = E5MessageBox.yesNo(
+                self,
+                self.tr("Changed Connection Profile"),
+                self.tr("""The current profile has unsaved changes. Shall"""
+                        """ these be saved?"""),
+                icon=E5MessageBox.Warning,
+                yesDefault=True)
+            if yes:
+                self.__applyProfile()
+        
+        super(MqttConnectionProfilesDialog, self).accept()

eric ide

mercurial