Started to implement the connection options dialog and methods to specify these connection options connecting to the server.

Sat, 01 Sep 2018 20:18:11 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 01 Sep 2018 20:18:11 +0200
changeset 10
7e0e921dc7ea
parent 9
f75a385e9127
child 11
90d3ebed4cc0

Started to implement the connection options dialog and methods to specify these connection options connecting to the server.

MqttMonitor/MqttClient.py file | annotate | diff | comparison | revisions
MqttMonitor/MqttConnectionOptionsDialog.py file | annotate | diff | comparison | revisions
MqttMonitor/MqttConnectionOptionsDialog.ui file | annotate | diff | comparison | revisions
MqttMonitor/MqttMonitorWidget.py file | annotate | diff | comparison | revisions
MqttMonitor/MqttMonitorWidget.ui file | annotate | diff | comparison | revisions
MqttMonitor/icons/connectionOptions.png file | annotate | diff | comparison | revisions
PluginMqttMonitor.e4p file | annotate | diff | comparison | revisions
PluginMqttMonitor.py file | annotate | diff | comparison | revisions
--- a/MqttMonitor/MqttClient.py	Fri Aug 31 19:28:28 2018 +0200
+++ b/MqttMonitor/MqttClient.py	Sat Sep 01 20:18:11 2018 +0200
@@ -79,6 +79,77 @@
         self.__mqttClient.on_unsubscribe = \
             lambda client, userdata, mid: self.onUnsubscribe.emit(mid)
     
+    def reinitialise(self, clientId="", cleanSession=True, userdata=None):
+        """
+        Public method to reinitialize the client with given data.
+        
+        @param clientId ID to be used for the client
+        @type str
+        @param cleanSession flag indicating to start a clean session
+        @type bool
+        @param userdata user data
+        @type any
+        """
+        self.__mqttClient.reinitialise(
+            client_id=clientId, clean_session=cleanSession, userdata=userdata)
+    
+    def setMaxInflightMessages(self, inflight=20):
+        """
+        Public method to set the maximum number of messages with QoS > 0 that
+        can be part way through their network flow at once.
+        
+        @param inflight maximum number of messages in flight
+        @type int
+        """
+        self.__mqttClient.max_inflight_messages_set(inflight)
+    
+    def setMaxQueuedMessages(self, queueSize=0):
+        """
+        Public method to set the maximum number of messages with QoS > 0 that
+        can be pending in the outgoing message queue.
+        
+        @param queueSize maximum number of queued messages (0 = unlimited)
+        @type int
+        """
+        self.__mqttClient.max_queued_messages_set(queueSize)
+    
+    def setUserCredentials(self, username, password=None):
+        """
+        Public method to set the user name and optionally the password.
+        
+        @param username user name to be set
+        @type str
+        @param password optional password
+        @type str
+        """
+        self.__mqttClient.username_pw_set(username, password=password)
+    
+    def setUserData(self, userdata):
+        """
+        Public method to set the user data.
+        
+        @param userdata user data
+        @type any
+        """
+        self.__mqttClient.user_data_set(userdata)
+    
+    def setLastWill(self, topic, payload=None, qos=0, retain=False):
+        """
+        Public method to set the last will of the client.
+        
+        @param topic topic the will message should be published on
+        @type str
+        @param payload message to send as a will
+        @type str, bytes, int or float
+        @param qos quality of service level to use for the will
+        @type int, one of 0, 1 or 2
+        @param retain flag indicating to set as the "last known good"/retained
+            message for the will topic
+        @type bool
+        """
+        self.__mqttClient.will_set(topic, payload=payload, qos=qos,
+                                   retain=retain)
+    
     def startLoop(self):
         """
         Public method to start the MQTT client loop.
@@ -115,6 +186,85 @@
         if not self.__loopStarted:
             self.startLoop()
     
+    def connectToServerWithOptions(self, host, port=1883, bindAddress="",
+                                   options=None):
+        """
+        Public method to connect to a remote MQTT broker.
+        
+        @param host host name or IP address of the remote broker
+        @type str
+        @param port network port of the server host to connect to (default:
+            1883, using TLS: 8883)
+        @type int
+        @param bindAddress IP address of a local network interface to bind
+            this client to
+        @type str
+        @param options dictionary containing the connection options. This
+            dictionary should contain the keys "ClientId", "Keepalive",
+            "CleanSession", "Username", "Password", "WillTopic", "WillMessage",
+            "WillQos", "WillRetain"
+        @type dict
+        """
+        if options:
+            parametersDict = self.defaultConnectionOptions()
+            parametersDict.update(options)
+            
+            # step 1: reinitialize to set the client ID and clean session flag
+            self.reinitialise(
+                clientId=parametersDict["ClientId"],
+                cleanSession=parametersDict["CleanSession"]
+            )
+            
+            # step 2: set username and password
+            if parametersDict["Username"]:
+                if parametersDict["Password"]:
+                    self.setUserCredentials(parametersDict["Username"],
+                                            parametersDict["Password"])
+                else:
+                    self.setUserCredentials(parametersDict["Username"])
+            
+            # step 3: set last will data
+            if parametersDict["WillTopic"]:
+                if parametersDict["WillMessage"]:
+                    willMessage = parametersDict["WillMessage"]
+                else:
+                    # empty message to clear the will
+                    willMessage = None
+                self.setLastWill(parametersDict["WillTopic"],
+                                 willMessage,
+                                 parametersDict["WillQos"],
+                                 parametersDict["WillRetain"])
+            
+            # step 4: connect to server
+            self.connectToServer(host, port=port,
+                                 keepalive=parametersDict["Keepalive"])
+        else:
+            keepalive = self.defaultConnectionOptions["Keepalive"]
+            self.connectToServer(host, port=port, keepalive=keepalive,
+                                 bindAddress=bindAddress)
+    
+    def defaultConnectionOptions(self):
+        """
+        Public method to get a connection options dictionary with default
+        values.
+        
+        @return dictionary containing the default connection options. It has
+            the keys "ClientId", "Keepalive", "CleanSession", "Username",
+            "Password", "WillTopic", "WillMessage", "WillQos", "WillRetain"
+        @rtype dict
+        """
+        return {
+            "ClientId": "ERIC6_MQTT_MONITOR_CLIENT",
+            "Keepalive": 60,
+            "CleanSession": True,
+            "Username": "",
+            "Password": "",
+            "WillTopic": "",
+            "WillMessage": "",
+            "WillQos": 0,
+            "WillRetain": False,
+        }
+    
     def reconnectToServer(self):
         """
         Public method to reconnect the client with the same parameters.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MqttMonitor/MqttConnectionOptionsDialog.py	Sat Sep 01 20:18:11 2018 +0200
@@ -0,0 +1,94 @@
+# -*- coding: utf-8 -*-
+
+"""
+Module implementing a dialog to enter MQTT connection options.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QAbstractButton
+
+from .Ui_MqttConnectionOptionsDialog import Ui_MqttConnectionOptionsDialog
+
+
+class MqttConnectionOptionsDialog(QDialog, Ui_MqttConnectionOptionsDialog):
+    """
+    Class implementing a dialog to enter MQTT connection options.
+    """
+    def __init__(self, client, options=None, parent=None):
+        """
+        Constructor
+        
+        @param client reference to the MQTT client object
+        @type MqttClient
+        @param options dictionary containing the connection options to
+            populate the dialog with
+        @@type dict
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(MqttConnectionOptionsDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__client = client
+        
+        self.__populateDefaults(options=options)
+        
+    
+    @pyqtSlot(QAbstractButton)
+    def on_buttonBox_clicked(self, button):
+        """
+        Private slot to handle the press of a button box button.
+        
+        @param button button that has been pressed
+        @type QAbstractButton
+        """
+        if button == self.buttonBox.button(QDialogButtonBox.RestoreDefaults):
+            self.__populateDefaults(options=None)
+    
+    def __populateDefaults(self, options=None):
+        """
+        Private method to populate the dialog.
+        
+        If no options dictionary is given, the dialog will be populated with
+        default values.
+        """
+        if options is None:
+            options = self.__client.defaultConnectionOptions()
+        
+        # general
+        self.clientIdEdit.setText(options["ClientId"])
+        self.keepaliveSpinBox.setValue(options["Keepalive"])
+        self.cleanSessionCheckBox.setChecked(options["CleanSession"])
+        
+        # user credentials
+        self.usernameEdit.setText(options["Username"])
+        self.passwordEdit.setText(options["Password"])
+        
+        # last will and testament
+        self.willQosSpinBox.setValue(options["WillQos"])
+        self.willRetainCheckBox.setChecked(options["WillRetain"])
+        self.willTopicEdit.setText(options["WillTopic"])
+        self.willMessageEdit.setPlainText(options["WillMessage"])
+    
+    def getConnectionOptions(self):
+        """
+        Public method get the entered connection options.
+        
+        @return dictionary containing the connection options. It has the keys
+            "ClientId", "Keepalive", "CleanSession", "Username", "Password",
+            "WillTopic", "WillMessage", "WillQos", "WillRetain"
+        @rtype tuple of (int, dict)
+        """
+        return {
+            "ClientId": self.clientIdEdit.text(),
+            "Keepalive": self.keepaliveSpinBox.value(),
+            "CleanSession": self.cleanSessionCheckBox.isChecked(),
+            "Username": self.usernameEdit.text(),
+            "Password": self.passwordEdit.text(),
+            "WillTopic": self.willTopicEdit.text(),
+            "WillMessage": self.willMessageEdit.toPlainText(),
+            "WillQos": self.willQosSpinBox.value(),
+            "WillRetain": self.willRetainCheckBox.isChecked(),
+        }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MqttMonitor/MqttConnectionOptionsDialog.ui	Sat Sep 01 20:18:11 2018 +0200
@@ -0,0 +1,274 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MqttConnectionOptionsDialog</class>
+ <widget class="QDialog" name="MqttConnectionOptionsDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>600</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MQTT Connection Options</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>General</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Client ID:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="E5ClearableLineEdit" name="clientIdEdit">
+        <property name="toolTip">
+         <string>Enter the ID string for this client</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>Keep Alive Interval:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QSpinBox" name="keepaliveSpinBox">
+          <property name="toolTip">
+           <string>Enter the keep alive interval in seconds</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="suffix">
+           <string> s</string>
+          </property>
+          <property name="maximum">
+           <number>300</number>
+          </property>
+          <property name="singleStep">
+           <number>5</number>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>148</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+      <item row="2" column="0" colspan="2">
+       <widget class="QCheckBox" name="cleanSessionCheckBox">
+        <property name="text">
+         <string>Clean Session</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="title">
+      <string>User Credentials</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label_3">
+        <property name="text">
+         <string>User Name:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="E5ClearableLineEdit" name="usernameEdit">
+        <property name="toolTip">
+         <string>Enter the user name</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_4">
+        <property name="text">
+         <string>Password:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="E5ClearableLineEdit" name="passwordEdit">
+        <property name="toolTip">
+         <string>Enter the password</string>
+        </property>
+        <property name="echoMode">
+         <enum>QLineEdit::Password</enum>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_3">
+     <property name="title">
+      <string>Last Will and Testament</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_2">
+        <item>
+         <spacer name="horizontalSpacer_2">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <widget class="QLabel" name="label_5">
+          <property name="text">
+           <string>QoS:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QSpinBox" name="willQosSpinBox">
+          <property name="toolTip">
+           <string>Enter the desired QoS value</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="maximum">
+           <number>2</number>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QCheckBox" name="willRetainCheckBox">
+          <property name="toolTip">
+           <string>Select to retain the last will message</string>
+          </property>
+          <property name="text">
+           <string>Retain</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="QLineEdit" name="willTopicEdit">
+        <property name="toolTip">
+         <string>Enter the topic of the last will</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPlainTextEdit" name="willMessageEdit">
+        <property name="maximumSize">
+         <size>
+          <width>16777215</width>
+          <height>300</height>
+         </size>
+        </property>
+        <property name="toolTip">
+         <string>Enter the last will message to be sent</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5ClearableLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>clientIdEdit</tabstop>
+  <tabstop>keepaliveSpinBox</tabstop>
+  <tabstop>cleanSessionCheckBox</tabstop>
+  <tabstop>usernameEdit</tabstop>
+  <tabstop>passwordEdit</tabstop>
+  <tabstop>willQosSpinBox</tabstop>
+  <tabstop>willRetainCheckBox</tabstop>
+  <tabstop>willTopicEdit</tabstop>
+  <tabstop>willMessageEdit</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>MqttConnectionOptionsDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>MqttConnectionOptionsDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- a/MqttMonitor/MqttMonitorWidget.py	Fri Aug 31 19:28:28 2018 +0200
+++ b/MqttMonitor/MqttMonitorWidget.py	Sat Sep 01 20:18:11 2018 +0200
@@ -19,7 +19,7 @@
 
 from PyQt5.QtCore import pyqtSlot, QTimer
 from PyQt5.QtGui import QTextCursor
-from PyQt5.QtWidgets import QWidget
+from PyQt5.QtWidgets import QWidget, QDialog
 
 from E5Gui import E5MessageBox
 
@@ -62,8 +62,9 @@
         self.brokerWidget.setCurrentIndex(0)
         
         self.connectButton.setIcon(UI.PixmapCache.getIcon("ircConnect.png"))
-        self.brokerComboBox.addItems(
-            self.__plugin.getPreferences("RecentBrokers"))
+        self.brokerConnectionOptionsButton.setIcon(UI.PixmapCache.getIcon(
+            os.path.join("MqttMonitor", "icons", "connectionOptions.png")))
+        self.__populateBrokerComboBoxes()
         self.brokerStatusLabel.hide()
         
         self.subscribeButton.setIcon(UI.PixmapCache.getIcon("plus.png"))
@@ -78,6 +79,8 @@
         self.__updatePublishTopicComboBox()
         self.publishButton.setEnabled(False)
         
+        self.__connectionOptions = None
+        
         prefix = MqttMonitorWidget.BrokerStatusTopicPrefix
         self.__statusLabelMapping = {
             # broker
@@ -148,6 +151,7 @@
         """
         if rc == 0:
             self.__connectedToBroker = True
+            self.__connectionOptions = None
         
         msg = mqttConnackMessage(rc)
         self.__flashBrokerStatusLabel(msg)
@@ -291,6 +295,17 @@
             self.connectButton.setEnabled(True)
     
     @pyqtSlot()
+    def on_brokerConnectionOptionsButton_clicked(self):
+        """
+        Private slot to show a dialog to modify connection options.
+        """
+        from .MqttConnectionOptionsDialog import MqttConnectionOptionsDialog
+        dlg = MqttConnectionOptionsDialog(
+            self.__client, self.__connectionOptions, parent=self)
+        if dlg.exec_() == QDialog.Accepted:
+            self.__connectionOptions = dlg.getConnectionOptions()
+    
+    @pyqtSlot()
     def on_connectButton_clicked(self):
         """
         Private slot to handle a connect or disconnect request.
@@ -299,10 +314,15 @@
             self.__client.disconnectFromServer()
         else:
             host = self.brokerComboBox.currentText()
+            port = self.brokerPortComboBox.currentText().strip()
+            try:
+                port = int(port)
+            except ValueError:
+                # use standard port at 1883
+                port = 1883
             if host:
-                self.__addBrokerToRecent(host)
-                self.__client.connectToServer(host)
-                # use standard port at 1883
+                self.__addBrokerToRecent(host, port)
+                self.__client.connectToServer(host, port=port)
     
     @pyqtSlot(str)
     def on_subscribeTopicEdit_textChanged(self, topic):
@@ -371,6 +391,10 @@
         qos = self.publishQosSpinBox.value()
         retain = self.publishRetainCheckBox.isChecked()
         payloadStr = self.publishPayloadEdit.toPlainText()
+        if not payloadStr:
+            # use empty string together with the retain flag to clean
+            # a retained message by sending None instead
+            payloadStr = None
         
         msgInfo = self.__client.publish(topic, payloadStr, qos, retain)
         if msgInfo.rc == 0:
@@ -408,22 +432,50 @@
     ## Utility methods
     #######################################################################
     
-    def __addBrokerToRecent(self, host):
+    def __addBrokerToRecent(self, host, port):
         """
         Private method to add a host name to the list of recently connected
         brokers.
         
         @param host host name of broker
         @type str
+        @param port port number of the connection
+        @type int
         """
-        brokerList = self.__plugin.getPreferences("RecentBrokers")
-        if host in brokerList:
-            brokerList.remove(host)
-        brokerList.insert(0, host)
-        self.__plugin.setPreferences("RecentBrokers", brokerList)
+        brokerList = self.__plugin.getPreferences("RecentBrokersWithPort")
+        hostAndPort = [host, port]
+        if hostAndPort in brokerList:
+            brokerList.remove(hostAndPort)
+        brokerList.insert(0, hostAndPort)
+        self.__plugin.setPreferences("RecentBrokersWithPort", brokerList)
+        
+        self.__populateBrokerComboBoxes()
+    
+    def __populateBrokerComboBoxes(self):
+        """
+        Private method to populate the broker name and port combo boxes.
+        """
+        brokerList = self.__plugin.getPreferences("RecentBrokersWithPort")
         
+        # step 1: clear combo boxes
         self.brokerComboBox.clear()
-        self.brokerComboBox.addItems(brokerList)
+        self.brokerPortComboBox.clear()
+        
+        # step 2a: populate the broker name list
+        self.brokerComboBox.addItems([b[0].strip() for b in brokerList])
+        
+        # step 2b: populate the broker ports list
+        if brokerList:
+            currentPort = brokerList[0][1]
+        else:
+            currentPort = 1883
+        currentPortStr = "{0:5}".format(currentPort)
+        portsSet = set([b[1] for b in brokerList])
+        portsSet.update([1883, 8883])
+        self.brokerPortComboBox.addItems(
+            sorted(["{0:5}".format(p) for p in portsSet]))
+        index = self.brokerPortComboBox.findText(currentPortStr)
+        self.brokerPortComboBox.setCurrentIndex(index)
     
     def __updateUnsubscribeTopicComboBox(self):
         """
--- a/MqttMonitor/MqttMonitorWidget.ui	Fri Aug 31 19:28:28 2018 +0200
+++ b/MqttMonitor/MqttMonitorWidget.ui	Sat Sep 01 20:18:11 2018 +0200
@@ -41,7 +41,7 @@
      </property>
      <layout class="QGridLayout" name="gridLayout">
       <item row="0" column="0">
-       <widget class="QComboBox" name="brokerComboBox">
+       <widget class="E5ClearableComboBox" name="brokerComboBox">
         <property name="sizePolicy">
          <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
           <horstretch>0</horstretch>
@@ -56,7 +56,7 @@
         </property>
        </widget>
       </item>
-      <item row="0" column="1">
+      <item row="0" column="3">
        <widget class="QToolButton" name="connectButton">
         <property name="toolTip">
          <string>Press to connect to/disconnect from the broker</string>
@@ -66,13 +66,33 @@
         </property>
        </widget>
       </item>
-      <item row="1" column="0" colspan="2">
+      <item row="1" column="0" colspan="4">
        <widget class="QLabel" name="brokerStatusLabel">
         <property name="wordWrap">
          <bool>true</bool>
         </property>
        </widget>
       </item>
+      <item row="0" column="1">
+       <widget class="QComboBox" name="brokerPortComboBox">
+        <property name="toolTip">
+         <string>Enter the broker port to connect to</string>
+        </property>
+        <property name="editable">
+         <bool>true</bool>
+        </property>
+        <property name="sizeAdjustPolicy">
+         <enum>QComboBox::AdjustToContents</enum>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="2">
+       <widget class="QToolButton" name="brokerConnectionOptionsButton">
+        <property name="toolTip">
+         <string>Press to open a dialog to enter connection options</string>
+        </property>
+       </widget>
+      </item>
      </layout>
     </widget>
    </item>
@@ -1127,6 +1147,8 @@
  </customwidgets>
  <tabstops>
   <tabstop>brokerComboBox</tabstop>
+  <tabstop>brokerPortComboBox</tabstop>
+  <tabstop>brokerConnectionOptionsButton</tabstop>
   <tabstop>connectButton</tabstop>
   <tabstop>brokerWidget</tabstop>
   <tabstop>subscribeTopicEdit</tabstop>
@@ -1134,6 +1156,15 @@
   <tabstop>subscribeButton</tabstop>
   <tabstop>unsubscribeTopicComboBox</tabstop>
   <tabstop>unsubscribeButton</tabstop>
+  <tabstop>publishTopicComboBox</tabstop>
+  <tabstop>publishQosSpinBox</tabstop>
+  <tabstop>publishRetainCheckBox</tabstop>
+  <tabstop>publishPayloadEdit</tabstop>
+  <tabstop>publishButton</tabstop>
+  <tabstop>messagesEdit</tabstop>
+  <tabstop>pushButton</tabstop>
+  <tabstop>brokerStatusButton</tabstop>
+  <tabstop>scrollArea</tabstop>
  </tabstops>
  <resources/>
  <connections>
Binary file MqttMonitor/icons/connectionOptions.png has changed
--- a/PluginMqttMonitor.e4p	Fri Aug 31 19:28:28 2018 +0200
+++ b/PluginMqttMonitor.e4p	Sat Sep 01 20:18:11 2018 +0200
@@ -17,12 +17,14 @@
   <Eol index="1"/>
   <Sources>
     <Source>MqttMonitor/MqttClient.py</Source>
+    <Source>MqttMonitor/MqttConnectionOptionsDialog.py</Source>
     <Source>MqttMonitor/MqttMonitorWidget.py</Source>
     <Source>MqttMonitor/__init__.py</Source>
     <Source>PluginMqttMonitor.py</Source>
     <Source>__init__.py</Source>
   </Sources>
   <Forms>
+    <Form>MqttMonitor/MqttConnectionOptionsDialog.ui</Form>
     <Form>MqttMonitor/MqttMonitorWidget.ui</Form>
   </Forms>
   <Others>
--- a/PluginMqttMonitor.py	Fri Aug 31 19:28:28 2018 +0200
+++ b/PluginMqttMonitor.py	Sat Sep 01 20:18:11 2018 +0200
@@ -10,6 +10,7 @@
 from __future__ import unicode_literals
 
 import os
+import json
 
 from PyQt5.QtCore import Qt, QObject, QTranslator, QCoreApplication
 from PyQt5.QtGui import QKeySequence
@@ -92,7 +93,7 @@
         self.__initialize()
         
         self.__defaults = {
-            "RecentBrokers": [],
+            "RecentBrokersWithPort": "[]",      # JSON formatted empty list
         }
         
         self.__translator = None
@@ -208,8 +209,8 @@
         @param key the key of the value to get
         @return the requested setting
         """
-        if key in ["RecentBrokers"]:
-            return Preferences.toList(Preferences.Prefs.settings.value(
+        if key in ["RecentBrokersWithPort"]:
+            return json.loads(Preferences.Prefs.settings.value(
                 self.PreferencesKey + "/" + key, self.__defaults[key]))
         else:
             return Preferences.Prefs.settings.value(
@@ -222,8 +223,12 @@
         @param key the key of the setting to be set (string)
         @param value the value to be set
         """
-        Preferences.Prefs.settings.setValue(
-            self.PreferencesKey + "/" + key, value)
+        if key in ["RecentBrokersWithPort"]:
+            Preferences.Prefs.settings.setValue(
+                self.PreferencesKey + "/" + key, json.dumps(value))
+        else:
+            Preferences.Prefs.settings.setValue(
+                self.PreferencesKey + "/" + key, value)
 
 #
 # eflag: noqa = M801

eric ide

mercurial