MqttMonitor/MqttConnectionProfilesDialog.py

branch
connection_profiles
changeset 23
0b23bd856e43
parent 19
889a7c3c0e63
child 26
ad232a5129cc
--- a/MqttMonitor/MqttConnectionProfilesDialog.py	Thu Sep 06 19:34:49 2018 +0200
+++ b/MqttMonitor/MqttConnectionProfilesDialog.py	Thu Sep 06 19:35:43 2018 +0200
@@ -20,6 +20,7 @@
 from .Ui_MqttConnectionProfilesDialog import Ui_MqttConnectionProfilesDialog
 
 import UI.PixmapCache
+from Utilities.crypto import pwConvert
 
 
 class MqttConnectionProfilesDialog(QDialog, Ui_MqttConnectionProfilesDialog):
@@ -48,18 +49,21 @@
         
         self.__profiles = collections.defaultdict(self.__defaultProfile)
         self.__profiles.update(profiles)
+        self.__profilesChanged = False
         
         self.plusButton.setIcon(UI.PixmapCache.getIcon("plus.png"))
         self.minusButton.setIcon(UI.PixmapCache.getIcon("minus.png"))
         
-        self.__populateProfilesList()
+        self.profileTabWidget.setCurrentIndex(0)
         
         if len(self.__profiles) == 0:
             self.minusButton.setEnabled(False)
         
-        self.__updateApplyButton()
+        self.profileFrame.setEnabled(False)
+        self.__populatingProfile = False
+        self.__deletingProfile = False
         
-        self.profileTabWidget.setCurrentIndex(0)
+        self.__populateProfilesList()
     
     @pyqtSlot(str)
     def on_profileEdit_textChanged(self, name):
@@ -83,7 +87,12 @@
             currentProfile = self.__applyProfile()
             self.__populateProfilesList(currentProfile)
         
-        # TODO: not implemented other paths
+        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):
@@ -96,9 +105,25 @@
         @type QListWidgetItem
         """
         self.minusButton.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):
@@ -130,8 +155,13 @@
                         """ 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):
         """
@@ -162,13 +192,14 @@
             "Keepalive": self.keepaliveSpinBox.value(),
             "CleanSession": self.cleanSessionCheckBox.isChecked(),
             "Username": self.usernameEdit.text(),
-            "Password": self.passwordEdit.text(),
+            "Password": pwConvert(self.passwordEdit.text(), encode=True),
             "WillTopic": self.willTopicEdit.text(),
             "WillMessage": self.willMessageEdit.toPlainText(),
             "WillQos": self.willQosSpinBox.value(),
             "WillRetain": self.willRetainCheckBox.isChecked(),
         }
         self.__profiles[profileName] = profile
+        self.__profilesChanged = True
         
         return profileName
     
@@ -205,6 +236,9 @@
                 currentProfile, Qt.MatchExactly)
             if items:
                 self.profilesList.setCurrentItem(items[0])
+        
+        if len(self.__profiles) == 0:
+            self.profileFrame.setEnabled(False)
     
     def __populateProfile(self, profileName):
         """
@@ -218,27 +252,114 @@
         else:
             profile = self.__defaultProfile()
         
-        self.profileEdit.setText(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(profile["Password"])
+        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.__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.__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.__profiles[profileName]
+            return (
+                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"]
+            )
+        
+        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."))
+        
         self.profileButtonBox.button(QDialogButtonBox.Apply).setEnabled(enable)
     
     @pyqtSlot(str)
@@ -258,3 +379,79 @@
         """
         uuid = QUuid.createUuid()
         self.clientIdEdit.setText(uuid.toString(QUuid.WithoutBraces))
+    
+    @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()
+    
+    @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()

eric ide

mercurial