7 Module implementing a dialog to edit the MQTT connection profiles. |
7 Module implementing a dialog to edit the MQTT connection profiles. |
8 """ |
8 """ |
9 |
9 |
10 import collections |
10 import collections |
11 |
11 |
12 from PyQt5.QtCore import pyqtSlot, Qt, QUuid |
12 from PyQt6.QtCore import pyqtSlot, Qt, QUuid |
13 from PyQt5.QtWidgets import ( |
13 from PyQt6.QtWidgets import ( |
14 QDialog, QDialogButtonBox, QAbstractButton, QListWidgetItem, QInputDialog, |
14 QDialog, QDialogButtonBox, QAbstractButton, QListWidgetItem, QInputDialog, |
15 QLineEdit |
15 QLineEdit |
16 ) |
16 ) |
17 |
17 |
18 from E5Gui import E5MessageBox |
18 from EricWidgets import EricMessageBox |
19 from E5Gui.E5PathPicker import E5PathPickerModes |
19 from EricWidgets.EricPathPicker import EricPathPickerModes |
20 |
20 |
21 from .Ui_MqttConnectionProfilesDialog import Ui_MqttConnectionProfilesDialog |
21 from .Ui_MqttConnectionProfilesDialog import Ui_MqttConnectionProfilesDialog |
22 |
22 |
23 import UI.PixmapCache |
23 import UI.PixmapCache |
24 from Utilities.crypto import pwConvert |
24 from Utilities.crypto import pwConvert |
51 |
51 |
52 self.__profiles = collections.defaultdict(self.__defaultProfile) |
52 self.__profiles = collections.defaultdict(self.__defaultProfile) |
53 self.__profiles.update(profiles) |
53 self.__profiles.update(profiles) |
54 self.__profilesChanged = False |
54 self.__profilesChanged = False |
55 |
55 |
56 self.plusButton.setIcon(UI.PixmapCache.getIcon("plus.png")) |
56 self.plusButton.setIcon(UI.PixmapCache.getIcon("plus")) |
57 self.copyButton.setIcon(UI.PixmapCache.getIcon("editCopy.png")) |
57 self.copyButton.setIcon(UI.PixmapCache.getIcon("editCopy")) |
58 self.minusButton.setIcon(UI.PixmapCache.getIcon("minus.png")) |
58 self.minusButton.setIcon(UI.PixmapCache.getIcon("minus")) |
59 |
59 |
60 self.tlsCertsFilePicker.setMode(E5PathPickerModes.OpenFileMode) |
60 self.tlsCertsFilePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) |
61 self.tlsCertsFilePicker.setFilters( |
61 self.tlsCertsFilePicker.setFilters( |
62 self.tr("Certificate Files (*.crt *.pem);;All Files (*)")) |
62 self.tr("Certificate Files (*.crt *.pem);;All Files (*)")) |
63 self.tlsSelfSignedCertsFilePicker.setMode( |
63 self.tlsSelfSignedCertsFilePicker.setMode( |
64 E5PathPickerModes.OpenFileMode) |
64 EricPathPickerModes.OPEN_FILE_MODE) |
65 self.tlsSelfSignedCertsFilePicker.setFilters( |
65 self.tlsSelfSignedCertsFilePicker.setFilters( |
66 self.tr("Certificate Files (*.crt *.pem);;All Files (*)")) |
66 self.tr("Certificate Files (*.crt *.pem);;All Files (*)")) |
67 self.tlsSelfSignedClientCertFilePicker.setMode( |
67 self.tlsSelfSignedClientCertFilePicker.setMode( |
68 E5PathPickerModes.OpenFileMode) |
68 EricPathPickerModes.OPEN_FILE_MODE) |
69 self.tlsSelfSignedClientCertFilePicker.setFilters( |
69 self.tlsSelfSignedClientCertFilePicker.setFilters( |
70 self.tr("Certificate Files (*.crt *.pem);;All Files (*)")) |
70 self.tr("Certificate Files (*.crt *.pem);;All Files (*)")) |
71 self.tlsSelfSignedClientKeyFilePicker.setMode( |
71 self.tlsSelfSignedClientKeyFilePicker.setMode( |
72 E5PathPickerModes.OpenFileMode) |
72 EricPathPickerModes.OPEN_FILE_MODE) |
73 self.tlsSelfSignedClientKeyFilePicker.setFilters( |
73 self.tlsSelfSignedClientKeyFilePicker.setFilters( |
74 self.tr("Key Files (*.key *.pem);;All Files (*)")) |
74 self.tr("Key Files (*.key *.pem);;All Files (*)")) |
75 |
75 |
76 self.profileTabWidget.setCurrentIndex(0) |
76 self.profileTabWidget.setCurrentIndex(0) |
77 |
77 |
101 Private slot handling presses of the profile buttons. |
101 Private slot handling presses of the profile buttons. |
102 |
102 |
103 @param button reference to the pressed button |
103 @param button reference to the pressed button |
104 @type QAbstractButton |
104 @type QAbstractButton |
105 """ |
105 """ |
106 if button == self.profileButtonBox.button(QDialogButtonBox.Apply): |
106 if button == self.profileButtonBox.button( |
|
107 QDialogButtonBox.StandardButton.Apply |
|
108 ): |
107 currentProfile = self.__applyProfile() |
109 currentProfile = self.__applyProfile() |
108 self.__populateProfilesList(currentProfile) |
110 self.__populateProfilesList(currentProfile) |
109 |
111 |
110 elif button == self.profileButtonBox.button(QDialogButtonBox.Reset): |
112 elif button == self.profileButtonBox.button( |
|
113 QDialogButtonBox.StandardButton.Reset |
|
114 ): |
111 self.__resetProfile() |
115 self.__resetProfile() |
112 |
116 |
113 elif button == self.profileButtonBox.button( |
117 elif button == self.profileButtonBox.button( |
114 QDialogButtonBox.RestoreDefaults): |
118 QDialogButtonBox.StandardButton.RestoreDefaults |
|
119 ): |
115 self.__populateProfileDefault() |
120 self.__populateProfileDefault() |
116 |
121 |
117 @pyqtSlot(QListWidgetItem, QListWidgetItem) |
122 @pyqtSlot(QListWidgetItem, QListWidgetItem) |
118 def on_profilesList_currentItemChanged(self, current, previous): |
123 def on_profilesList_currentItemChanged(self, current, previous): |
119 """ |
124 """ |
131 current is not previous and |
136 current is not previous and |
132 not self.__deletingProfile and |
137 not self.__deletingProfile and |
133 self.__isChangedProfile() |
138 self.__isChangedProfile() |
134 ): |
139 ): |
135 # modified profile belongs to previous |
140 # modified profile belongs to previous |
136 yes = E5MessageBox.yesNo( |
141 yes = EricMessageBox.yesNo( |
137 self, |
142 self, |
138 self.tr("Changed Connection Profile"), |
143 self.tr("Changed Connection Profile"), |
139 self.tr("""The current profile has unsaved changes.""" |
144 self.tr("""The current profile has unsaved changes.""" |
140 """ Shall these be saved?"""), |
145 """ Shall these be saved?"""), |
141 icon=E5MessageBox.Warning, |
146 icon=EricMessageBox.Warning, |
142 yesDefault=True) |
147 yesDefault=True) |
143 if yes: |
148 if yes: |
144 self.__applyProfile() |
149 self.__applyProfile() |
145 |
150 |
146 if current: |
151 if current: |
156 """ |
161 """ |
157 profileName, ok = QInputDialog.getText( |
162 profileName, ok = QInputDialog.getText( |
158 self, |
163 self, |
159 self.tr("New Connection Profile"), |
164 self.tr("New Connection Profile"), |
160 self.tr("Enter name for the new Connection Profile:"), |
165 self.tr("Enter name for the new Connection Profile:"), |
161 QLineEdit.Normal) |
166 QLineEdit.EchoMode.Normal) |
162 if ok and bool(profileName): |
167 if ok and bool(profileName): |
163 if profileName in self.__profiles: |
168 if profileName in self.__profiles: |
164 E5MessageBox.warning( |
169 EricMessageBox.warning( |
165 self, |
170 self, |
166 self.tr("New Connection Profile"), |
171 self.tr("New Connection Profile"), |
167 self.tr("""<p>A connection named <b>{0}</b> exists""" |
172 self.tr("""<p>A connection named <b>{0}</b> exists""" |
168 """ already. Aborting...</p>""").format( |
173 """ already. Aborting...</p>""").format( |
169 profileName)) |
174 profileName)) |
170 else: |
175 else: |
171 itm = QListWidgetItem(profileName, self.profilesList) |
176 itm = QListWidgetItem(profileName, self.profilesList) |
172 self.profilesList.setCurrentItem(itm) |
177 self.profilesList.setCurrentItem(itm) |
173 self.brokerAddressEdit.setFocus(Qt.OtherFocusReason) |
178 self.brokerAddressEdit.setFocus( |
|
179 Qt.FocusReason.OtherFocusReason) |
174 |
180 |
175 @pyqtSlot() |
181 @pyqtSlot() |
176 def on_copyButton_clicked(self): |
182 def on_copyButton_clicked(self): |
177 """ |
183 """ |
178 Private slot to copy the selected profile entry. |
184 Private slot to copy the selected profile entry. |
182 profileName = itm.text() |
188 profileName = itm.text() |
183 newProfileName, ok = QInputDialog.getText( |
189 newProfileName, ok = QInputDialog.getText( |
184 self, |
190 self, |
185 self.tr("Copy Connection Profile"), |
191 self.tr("Copy Connection Profile"), |
186 self.tr("Enter name for the copied Connection Profile:"), |
192 self.tr("Enter name for the copied Connection Profile:"), |
187 QLineEdit.Normal) |
193 QLineEdit.EchoMode.Normal) |
188 if ok and bool(newProfileName): |
194 if ok and bool(newProfileName): |
189 if newProfileName in self.__profiles: |
195 if newProfileName in self.__profiles: |
190 E5MessageBox.warning( |
196 EricMessageBox.warning( |
191 self, |
197 self, |
192 self.tr("Copy Connection Profile"), |
198 self.tr("Copy Connection Profile"), |
193 self.tr("""<p>A connection named <b>{0}</b> exists""" |
199 self.tr("""<p>A connection named <b>{0}</b> exists""" |
194 """ already. Aborting...</p>""").format( |
200 """ already. Aborting...</p>""").format( |
195 newProfileName)) |
201 newProfileName)) |
198 profile.update(self.__profiles[profileName]) |
204 profile.update(self.__profiles[profileName]) |
199 self.__profiles[newProfileName] = profile |
205 self.__profiles[newProfileName] = profile |
200 |
206 |
201 itm = QListWidgetItem(newProfileName, self.profilesList) |
207 itm = QListWidgetItem(newProfileName, self.profilesList) |
202 self.profilesList.setCurrentItem(itm) |
208 self.profilesList.setCurrentItem(itm) |
203 self.brokerAddressEdit.setFocus(Qt.OtherFocusReason) |
209 self.brokerAddressEdit.setFocus( |
|
210 Qt.FocusReason.OtherFocusReason) |
204 |
211 |
205 @pyqtSlot() |
212 @pyqtSlot() |
206 def on_minusButton_clicked(self): |
213 def on_minusButton_clicked(self): |
207 """ |
214 """ |
208 Private slot to delete the selected entry. |
215 Private slot to delete the selected entry. |
209 """ |
216 """ |
210 itm = self.profilesList.currentItem() |
217 itm = self.profilesList.currentItem() |
211 if itm: |
218 if itm: |
212 profileName = itm.text() |
219 profileName = itm.text() |
213 yes = E5MessageBox.yesNo( |
220 yes = EricMessageBox.yesNo( |
214 self, |
221 self, |
215 self.tr("Delete Connection Profile"), |
222 self.tr("Delete Connection Profile"), |
216 self.tr("""<p>Shall the Connection Profile <b>{0}</b>""" |
223 self.tr("""<p>Shall the Connection Profile <b>{0}</b>""" |
217 """ really be deleted?</p>""").format(profileName) |
224 """ really be deleted?</p>""").format(profileName) |
218 ) |
225 ) |
221 del self.__profiles[profileName] |
228 del self.__profiles[profileName] |
222 self.__profilesChanged = True |
229 self.__profilesChanged = True |
223 self.__populateProfilesList() |
230 self.__populateProfilesList() |
224 self.__deletingProfile = False |
231 self.__deletingProfile = False |
225 |
232 |
226 self.profilesList.setFocus(Qt.OtherFocusReason) |
233 self.profilesList.setFocus(Qt.FocusReason.OtherFocusReason) |
227 |
234 |
228 def getProfiles(self): |
235 def getProfiles(self): |
229 """ |
236 """ |
230 Public method to return a dictionary of profiles. |
237 Public method to return a dictionary of profiles. |
231 |
238 |
314 self.profilesList.clear() |
321 self.profilesList.clear() |
315 self.profilesList.addItems(sorted(self.__profiles.keys())) |
322 self.profilesList.addItems(sorted(self.__profiles.keys())) |
316 |
323 |
317 if currentProfile: |
324 if currentProfile: |
318 items = self.profilesList.findItems( |
325 items = self.profilesList.findItems( |
319 currentProfile, Qt.MatchExactly) |
326 currentProfile, Qt.MatchFlag.MatchExactly) |
320 if items: |
327 if items: |
321 self.profilesList.setCurrentItem(items[0]) |
328 self.profilesList.setCurrentItem(items[0]) |
322 |
329 |
323 if len(self.__profiles) == 0: |
330 if len(self.__profiles) == 0: |
324 self.profileFrame.setEnabled(False) |
331 self.profileFrame.setEnabled(False) |
479 not self.__populatingProfile and |
486 not self.__populatingProfile and |
480 self.clientIdEdit.text() == "" and |
487 self.clientIdEdit.text() == "" and |
481 not self.cleanSessionCheckBox.isChecked() |
488 not self.cleanSessionCheckBox.isChecked() |
482 ): |
489 ): |
483 enable = False |
490 enable = False |
484 E5MessageBox.critical( |
491 EricMessageBox.critical( |
485 self, |
492 self, |
486 self.tr("Invalid Connection Parameters"), |
493 self.tr("Invalid Connection Parameters"), |
487 self.tr("An empty Client ID requires a clean session.")) |
494 self.tr("An empty Client ID requires a clean session.")) |
488 |
495 |
489 if self.tlsGroupBox.isChecked(): |
496 if self.tlsGroupBox.isChecked(): |
498 bool(self.tlsSelfSignedCertsFilePicker.text()) and |
505 bool(self.tlsSelfSignedCertsFilePicker.text()) and |
499 bool(self.tlsSelfSignedClientCertFilePicker.text()) and |
506 bool(self.tlsSelfSignedClientCertFilePicker.text()) and |
500 bool(self.tlsSelfSignedClientKeyFilePicker.text()) |
507 bool(self.tlsSelfSignedClientKeyFilePicker.text()) |
501 ) |
508 ) |
502 |
509 |
503 self.profileButtonBox.button(QDialogButtonBox.Apply).setEnabled(enable) |
510 self.profileButtonBox.button( |
|
511 QDialogButtonBox.StandardButton.Apply).setEnabled(enable) |
504 |
512 |
505 @pyqtSlot(str) |
513 @pyqtSlot(str) |
506 def on_brokerAddressEdit_textChanged(self, address): |
514 def on_brokerAddressEdit_textChanged(self, address): |
507 """ |
515 """ |
508 Private slot handling a change of the broker address. |
516 Private slot handling a change of the broker address. |
516 def on_generateIdButton_clicked(self): |
524 def on_generateIdButton_clicked(self): |
517 """ |
525 """ |
518 Private slot to generate a client ID. |
526 Private slot to generate a client ID. |
519 """ |
527 """ |
520 uuid = QUuid.createUuid() |
528 uuid = QUuid.createUuid() |
521 self.clientIdEdit.setText(uuid.toString(QUuid.WithoutBraces)) |
529 self.clientIdEdit.setText( |
|
530 uuid.toString(QUuid.StringFormat.WithoutBraces)) |
522 |
531 |
523 @pyqtSlot(str) |
532 @pyqtSlot(str) |
524 def on_clientIdEdit_textChanged(self, clientId): |
533 def on_clientIdEdit_textChanged(self, clientId): |
525 """ |
534 """ |
526 Private slot handling a change of the client ID string. |
535 Private slot handling a change of the client ID string. |
588 @param checked state of the selection |
597 @param checked state of the selection |
589 @type bool |
598 @type bool |
590 """ |
599 """ |
591 if checked and self.brokerPortSpinBox.value() == 1883: |
600 if checked and self.brokerPortSpinBox.value() == 1883: |
592 # port is still standard non-TLS port |
601 # port is still standard non-TLS port |
593 yes = E5MessageBox.yesNo( |
602 yes = EricMessageBox.yesNo( |
594 self, |
603 self, |
595 self.tr("SSL/TLS Enabled"), |
604 self.tr("SSL/TLS Enabled"), |
596 self.tr( |
605 self.tr( |
597 """Encrypted connection using SSL/TLS has been enabled.""" |
606 """Encrypted connection using SSL/TLS has been enabled.""" |
598 """ However, the broker port is still the default""" |
607 """ However, the broker port is still the default""" |
599 """ unencrypted port (port 1883). Shall this be""" |
608 """ unencrypted port (port 1883). Shall this be""" |
600 """ changed?"""), |
609 """ changed?"""), |
601 icon=E5MessageBox.Warning, |
610 icon=EricMessageBox.Warning, |
602 yesDefault=True) |
611 yesDefault=True) |
603 if yes: |
612 if yes: |
604 self.brokerPortSpinBox.setValue(8883) |
613 self.brokerPortSpinBox.setValue(8883) |
605 elif not checked and self.brokerPortSpinBox.value() == 8883: |
614 elif not checked and self.brokerPortSpinBox.value() == 8883: |
606 # port is still standard TLS port |
615 # port is still standard TLS port |
607 yes = E5MessageBox.yesNo( |
616 yes = EricMessageBox.yesNo( |
608 self, |
617 self, |
609 self.tr("SSL/TLS Disabled"), |
618 self.tr("SSL/TLS Disabled"), |
610 self.tr( |
619 self.tr( |
611 """Encrypted connection using SSL/TLS has been disabled.""" |
620 """Encrypted connection using SSL/TLS has been disabled.""" |
612 """ However, the broker port is still the default""" |
621 """ However, the broker port is still the default""" |
613 """ encrypted port (port 8883). Shall this be""" |
622 """ encrypted port (port 8883). Shall this be""" |
614 """ changed?"""), |
623 """ changed?"""), |
615 icon=E5MessageBox.Warning, |
624 icon=EricMessageBox.Warning, |
616 yesDefault=True) |
625 yesDefault=True) |
617 if yes: |
626 if yes: |
618 self.brokerPortSpinBox.setValue(1883) |
627 self.brokerPortSpinBox.setValue(1883) |
619 |
628 |
620 self.__updateApplyButton() |
629 self.__updateApplyButton() |
656 def reject(self): |
665 def reject(self): |
657 """ |
666 """ |
658 Public slot to reject the dialog changes. |
667 Public slot to reject the dialog changes. |
659 """ |
668 """ |
660 if self.__isChangedProfile(): |
669 if self.__isChangedProfile(): |
661 button = E5MessageBox.warning( |
670 button = EricMessageBox.warning( |
662 self, |
671 self, |
663 self.tr("Changed Connection Profile"), |
672 self.tr("Changed Connection Profile"), |
664 self.tr("""The current profile has unsaved changes. Shall""" |
673 self.tr("""The current profile has unsaved changes. Shall""" |
665 """ these be saved?"""), |
674 """ these be saved?"""), |
666 E5MessageBox.StandardButtons( |
675 EricMessageBox.Discard | |
667 E5MessageBox.Discard | |
676 EricMessageBox.Save, |
668 E5MessageBox.Save), |
677 EricMessageBox.Save) |
669 E5MessageBox.Save) |
678 if button == EricMessageBox.Save: |
670 if button == E5MessageBox.Save: |
|
671 self.__applyProfile() |
679 self.__applyProfile() |
672 return |
680 return |
673 |
681 |
674 if self.__profilesChanged: |
682 if self.__profilesChanged: |
675 button = E5MessageBox.warning( |
683 button = EricMessageBox.warning( |
676 self, |
684 self, |
677 self.tr("Changed Connection Profiles"), |
685 self.tr("Changed Connection Profiles"), |
678 self.tr("""The list of connection profiles has unsaved""" |
686 self.tr("""The list of connection profiles has unsaved""" |
679 """ changes."""), |
687 """ changes."""), |
680 E5MessageBox.StandardButtons( |
688 EricMessageBox.Abort | |
681 E5MessageBox.Abort | |
689 EricMessageBox.Discard | |
682 E5MessageBox.Discard | |
690 EricMessageBox.Save, |
683 E5MessageBox.Save), |
691 EricMessageBox.Save) |
684 E5MessageBox.Save) |
692 if button == EricMessageBox.Save: |
685 if button == E5MessageBox.Save: |
|
686 super().accept() |
693 super().accept() |
687 return |
694 return |
688 elif button == E5MessageBox.Abort: |
695 elif button == EricMessageBox.Abort: |
689 return |
696 return |
690 |
697 |
691 super().reject() |
698 super().reject() |
692 |
699 |
693 @pyqtSlot() |
700 @pyqtSlot() |
694 def accept(self): |
701 def accept(self): |
695 """ |
702 """ |
696 Public slot to accept the dialog. |
703 Public slot to accept the dialog. |
697 """ |
704 """ |
698 if self.__isChangedProfile(): |
705 if self.__isChangedProfile(): |
699 yes = E5MessageBox.yesNo( |
706 yes = EricMessageBox.yesNo( |
700 self, |
707 self, |
701 self.tr("Changed Connection Profile"), |
708 self.tr("Changed Connection Profile"), |
702 self.tr("""The current profile has unsaved changes. Shall""" |
709 self.tr("""The current profile has unsaved changes. Shall""" |
703 """ these be saved?"""), |
710 """ these be saved?"""), |
704 icon=E5MessageBox.Warning, |
711 icon=EricMessageBox.Warning, |
705 yesDefault=True) |
712 yesDefault=True) |
706 if yes: |
713 if yes: |
707 self.__applyProfile() |
714 self.__applyProfile() |
708 |
715 |
709 super().accept() |
716 super().accept() |