Fixed some code style issues and implemented the broker connect/disconnect group of the MqttMonitorWidget.

Mon, 27 Aug 2018 19:26:27 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Mon, 27 Aug 2018 19:26:27 +0200
changeset 3
82845c0fd550
parent 2
d439c5109829
child 4
991db89bda9c

Fixed some code style issues and implemented the broker connect/disconnect group of the MqttMonitorWidget.

MqttMonitor/MqttClient.py file | annotate | diff | comparison | revisions
MqttMonitor/MqttMonitorWidget.py file | annotate | diff | comparison | revisions
MqttMonitor/MqttMonitorWidget.ui file | annotate | diff | comparison | revisions
PluginMqttMonitor.e4p file | annotate | diff | comparison | revisions
PluginMqttMonitor.py file | annotate | diff | comparison | revisions
__init__.py file | annotate | diff | comparison | revisions
--- a/MqttMonitor/MqttClient.py	Sun Aug 26 19:40:15 2018 +0200
+++ b/MqttMonitor/MqttClient.py	Mon Aug 27 19:26:27 2018 +0200
@@ -174,106 +174,118 @@
         return self.__mqttClient.publish(topic, payload=payload, qos=qos,
                                          retain=retain)
 
+
 def mqttConnackMessage(connackCode):
     """
     Public method to get the string associated with a CONNACK result.
+    
+    @param connackCode result code of the connection request
+    @type int
+    @return textual representation for the result code
+    @rtype str
     """
     if connackCode == mqtt.CONNACK_ACCEPTED:
         return QCoreApplication.translate(
-            "MqttConnackMessage", 
+            "MqttConnackMessage",
             "Connection Accepted.")
     elif connackCode == mqtt.CONNACK_REFUSED_PROTOCOL_VERSION:
         return QCoreApplication.translate(
-            "MqttConnackMessage", 
+            "MqttConnackMessage",
             "Connection Refused: unacceptable protocol version.")
     elif connackCode == mqtt.CONNACK_REFUSED_IDENTIFIER_REJECTED:
         return QCoreApplication.translate(
-            "MqttConnackMessage", 
+            "MqttConnackMessage",
             "Connection Refused: identifier rejected.")
     elif connackCode == mqtt.CONNACK_REFUSED_SERVER_UNAVAILABLE:
         return QCoreApplication.translate(
-            "MqttConnackMessage", 
+            "MqttConnackMessage",
             "Connection Refused: broker unavailable.")
     elif connackCode == mqtt.CONNACK_REFUSED_BAD_USERNAME_PASSWORD:
         return QCoreApplication.translate(
-            "MqttConnackMessage", 
+            "MqttConnackMessage",
             "Connection Refused: bad user name or password.")
     elif connackCode == mqtt.CONNACK_REFUSED_NOT_AUTHORIZED:
         return QCoreApplication.translate(
-            "MqttConnackMessage", 
+            "MqttConnackMessage",
             "Connection Refused: not authorised.")
     else:
         return QCoreApplication.translate(
-            "MqttConnackMessage", 
+            "MqttConnackMessage",
             "Connection Refused: unknown reason.")
 
+
 def mqttErrorMessage(self, mqttErrno):
     """
     Public method to get the error string associated with an MQTT error
     number.
+    
+    @param mqttErrno result code of a MQTT request
+    @type int
+    @return textual representation of the result code
+    @rtype str
     """
     if mqttErrno == mqtt.MQTT_ERR_SUCCESS:
         return QCoreApplication.translate(
-            "MqttErrorMessage", 
+            "MqttErrorMessage",
             "No error.")
     elif mqttErrno == mqtt.MQTT_ERR_NOMEM:
         return QCoreApplication.translate(
-            "MqttErrorMessage", 
+            "MqttErrorMessage",
             "Out of memory.")
     elif mqttErrno == mqtt.MQTT_ERR_PROTOCOL:
         return QCoreApplication.translate(
-            "MqttErrorMessage", 
+            "MqttErrorMessage",
             "A network protocol error occurred when communicating with"
             " the broker.")
     elif mqttErrno == mqtt.MQTT_ERR_INVAL:
         return QCoreApplication.translate(
-            "MqttErrorMessage", 
+            "MqttErrorMessage",
             "Invalid function arguments provided.")
     elif mqttErrno == mqtt.MQTT_ERR_NO_CONN:
         return QCoreApplication.translate(
-            "MqttErrorMessage", 
+            "MqttErrorMessage",
             "The client is not currently connected.")
     elif mqttErrno == mqtt.MQTT_ERR_CONN_REFUSED:
         return QCoreApplication.translate(
-            "MqttErrorMessage", 
+            "MqttErrorMessage",
             "The connection was refused.")
     elif mqttErrno == mqtt.MQTT_ERR_NOT_FOUND:
         return QCoreApplication.translate(
-            "MqttErrorMessage", 
+            "MqttErrorMessage",
             "Message not found (internal error).")
     elif mqttErrno == mqtt.MQTT_ERR_CONN_LOST:
         return QCoreApplication.translate(
-            "MqttErrorMessage", 
+            "MqttErrorMessage",
             "The connection was lost.")
     elif mqttErrno == mqtt.MQTT_ERR_TLS:
         return QCoreApplication.translate(
-            "MqttErrorMessage", 
+            "MqttErrorMessage",
             "A TLS error occurred.")
     elif mqttErrno == mqtt.MQTT_ERR_PAYLOAD_SIZE:
         return QCoreApplication.translate(
-            "MqttErrorMessage", 
+            "MqttErrorMessage",
             "Payload too large.")
     elif mqttErrno == mqtt.MQTT_ERR_NOT_SUPPORTED:
         return QCoreApplication.translate(
-            "MqttErrorMessage", 
+            "MqttErrorMessage",
             "This feature is not supported.")
     elif mqttErrno == mqtt.MQTT_ERR_AUTH:
         return QCoreApplication.translate(
-            "MqttErrorMessage", 
+            "MqttErrorMessage",
             "Authorisation failed.")
     elif mqttErrno == mqtt.MQTT_ERR_ACL_DENIED:
         return QCoreApplication.translate(
-            "MqttErrorMessage", 
+            "MqttErrorMessage",
             "Access denied by ACL.")
     elif mqttErrno == mqtt.MQTT_ERR_UNKNOWN:
         return QCoreApplication.translate(
-            "MqttErrorMessage", 
+            "MqttErrorMessage",
             "Unknown error.")
     elif mqttErrno == mqtt.MQTT_ERR_ERRNO:
         return QCoreApplication.translate(
-            "MqttErrorMessage", 
+            "MqttErrorMessage",
             "Error defined by errno.")
     else:
         return QCoreApplication.translate(
-            "MqttErrorMessage", 
+            "MqttErrorMessage",
             "Unknown error.")
--- a/MqttMonitor/MqttMonitorWidget.py	Sun Aug 26 19:40:15 2018 +0200
+++ b/MqttMonitor/MqttMonitorWidget.py	Mon Aug 27 19:26:27 2018 +0200
@@ -1,34 +1,213 @@
 # -*- coding: utf-8 -*-
 
+# Copyright (c) 2018 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
 """
-Module implementing MqttMonitorWidget.
+Module implementing the MQTT Monitor widget.
 """
 
 from __future__ import unicode_literals
 
 import os
 
-from PyQt5.QtCore import pyqtSlot
+from PyQt5.QtCore import pyqtSlot, QTimer
 from PyQt5.QtWidgets import QWidget
 
 from .Ui_MqttMonitorWidget import Ui_MqttMonitorWidget
 
+from .MqttClient import MqttClient, mqttConnackMessage, mqttErrorMessage
+
 import UI.PixmapCache
 
 
 class MqttMonitorWidget(QWidget, Ui_MqttMonitorWidget):
     """
-    Class documentation goes here.
+    Class implementing the MQTT Monitor widget.
     """
-    def __init__(self, parent=None):
+    def __init__(self, plugin, parent=None):
         """
         Constructor
         
+        @param plugin reference to the plug-in object
+        @type MqttMonitorPlugin
         @param parent reference to the parent widget
         @type QWidget
         """
         super(MqttMonitorWidget, self).__init__(parent)
         self.setupUi(self)
         
+        self.__plugin = plugin
+        
+        self.__connectedToBroker = False
+        
         self.pixmapLabel.setPixmap(UI.PixmapCache.getPixmap(
             os.path.join("MqttMonitor", "icons", "mqtt48.png")))
+        
+        self.connectButton.setIcon(UI.PixmapCache.getIcon("ircConnect.png"))
+        self.brokerComboBox.addItems(
+            self.__plugin.getPreferences("RecentBrokers"))
+        self.brokerStatusLabel.hide()
+        
+        self.__client = MqttClient()
+        
+        # connect the MQTT client signals
+        self.__client.onConnect.connect(self.__brokerConnected)
+        self.__client.onDisconnected.connect(self.__brokerDisconnected)
+        self.__client.onMessage.connect(self.__messageReceived)
+        self.__client.onPublish.connect(self.__messagePublished)
+        self.__client.onSubscribe.connect(self.__topicSubscribed)
+        self.__client.onUnsubscribe.connect(self.__topicUnsubscribed)
+    
+    #######################################################################
+    ## Slots handling MQTT related signals
+    #######################################################################
+    
+    @pyqtSlot(dict, int)
+    def __brokerConnected(self, flags, rc):
+        """
+        Private slot to handle being connected to a broker.
+        
+        @param flags flags set for the connection
+        @type dict
+        @param rc CONNACK result code
+        @type int
+        """
+        if rc == 0:
+            self.__connectedToBroker = True
+        
+        msg = mqttConnackMessage(rc)
+        self.__flashBrokerStatusLabel(msg)
+        
+        self.connectButton.setIcon(UI.PixmapCache.getIcon("ircDisconnect.png"))
+    
+    @pyqtSlot(int)
+    def __brokerDisconnected(self, rc):
+        """
+        Private slot to handle a disconnection from the broker.
+        
+        @param rc MQTT error result code
+        @type int
+        """
+        self.__connectedToBroker = False
+        
+        if rc != 0:
+            msg = mqttErrorMessage(rc)
+        else:
+            msg = self.tr("Connection to Broker shut down cleanly.")
+        self.__flashBrokerStatusLabel(msg)
+        
+        self.connectButton.setIcon(UI.PixmapCache.getIcon("ircConnect.png"))
+        
+        self.__client.stopLoop()
+    
+    @pyqtSlot(str, bytes, int, bool)
+    def __messageReceived(self, topic, payload, qos, retain):
+        """
+        Private slot to handle the receipt of a message.
+        
+        @param topic topic of the message
+        @type str
+        @param payload payload (i.e. data) of the message
+        @type bytes
+        @param qos quality of service indicator
+        @type int
+        @param retain flag indicating a retained message
+        @type bool
+        """
+        pass
+    
+    @pyqtSlot(int)
+    def __messagePublished(self, mid):
+        """
+        Private slot to handle a message being published.
+        
+        @param mid ID of the published message
+        @type int
+        """
+        pass
+    
+    @pyqtSlot(int, tuple)
+    def __topicSubscribed(self, mid, grantedQos):
+        """
+        Private slot to handle being subscribed to topics.
+        
+        @param mid ID of the subscribe request
+        @type int
+        @param grantedQos tuple of granted quality of service
+        @type tuple of int
+        """
+        pass
+    
+    @pyqtSlot(int)
+    def __topicUnsubscribed(self, mid):
+        """
+        Private slot to handle being unsubcribed from a topic.
+        
+        @param mid ID of the unsubscribe request
+        @type int
+        """
+        pass
+    
+    #######################################################################
+    ## Slots handling UI interactions
+    #######################################################################
+    
+    @pyqtSlot()
+    def __flashBrokerStatusLabel(self, message):
+        """
+        Private slot to show the broker status label with some text for
+        5 seconds.
+        
+        @param message message to be shown
+        @type str
+        """
+        self.brokerStatusLabel.setText(message)
+        self.brokerStatusLabel.show()
+        QTimer.singleShot(5000, self.brokerStatusLabel.hide)
+    
+    @pyqtSlot(str)
+    def on_brokerComboBox_editTextChanged(self, host):
+        """
+        Private slot to handling entering or selecting a broker host name.
+        
+        @param host host name of the broker
+        @type str
+        """
+        if not self.__connectedToBroker and not host:
+            self.connectButton.setEnabled(False)
+        else:
+            self.connectButton.setEnabled(True)
+    
+    @pyqtSlot()
+    def on_connectButton_clicked(self):
+        """
+        Private slot to handle a connect or disconnect request.
+        """
+        if self.__connectedToBroker:
+            self.__client.disconnectFromServer()
+        else:
+            host = self.brokerComboBox.currentText()
+            self.__addBrokerToRecent(host)
+            self.__client.connectToServer(host)     # use standard port at 1883
+    
+    #######################################################################
+    ## Utility methods
+    #######################################################################
+    
+    def __addBrokerToRecent(self, host):
+        """
+        Private method to add a host name to the list of recently connected
+        brokers.
+        
+        @param host host name of broker
+        @type str
+        """
+        brokerList = self.__plugin.getPreferences("RecentBrokers")
+        if host in brokerList:
+            brokerList.remove(host)
+        brokerList.insert(0, host)
+        self.__plugin.setPreferences("RecentBrokers", brokerList)
+        
+        self.brokerComboBox.clear()
+        self.brokerComboBox.addItems(brokerList)
--- a/MqttMonitor/MqttMonitorWidget.ui	Sun Aug 26 19:40:15 2018 +0200
+++ b/MqttMonitor/MqttMonitorWidget.ui	Mon Aug 27 19:26:27 2018 +0200
@@ -35,6 +35,48 @@
     </layout>
    </item>
    <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Broker</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <widget class="QComboBox" name="brokerComboBox">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Enter the host name of the broker</string>
+        </property>
+        <property name="editable">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QToolButton" name="connectButton">
+        <property name="toolTip">
+         <string>Press to connect to/disconnect from the broker</string>
+        </property>
+        <property name="text">
+         <string/>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0" colspan="2">
+       <widget class="QLabel" name="brokerStatusLabel">
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
     <spacer name="verticalSpacer">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
--- a/PluginMqttMonitor.e4p	Sun Aug 26 19:40:15 2018 +0200
+++ b/PluginMqttMonitor.e4p	Mon Aug 27 19:26:27 2018 +0200
@@ -157,4 +157,165 @@
     <FiletypeAssociation pattern="Ui_*.py" type="__IGNORE__"/>
     <FiletypeAssociation pattern="makefile" type="OTHERS"/>
   </FiletypeAssociations>
+  <Checkers>
+    <CheckersParams>
+      <dict>
+        <key>
+          <string>Pep8Checker</string>
+        </key>
+        <value>
+          <dict>
+            <key>
+              <string>BlankLines</string>
+            </key>
+            <value>
+              <tuple>
+                <int>2</int>
+                <int>1</int>
+              </tuple>
+            </value>
+            <key>
+              <string>BuiltinsChecker</string>
+            </key>
+            <value>
+              <dict>
+                <key>
+                  <string>bytes</string>
+                </key>
+                <value>
+                  <list>
+                    <string>unicode</string>
+                  </list>
+                </value>
+                <key>
+                  <string>chr</string>
+                </key>
+                <value>
+                  <list>
+                    <string>unichr</string>
+                  </list>
+                </value>
+                <key>
+                  <string>str</string>
+                </key>
+                <value>
+                  <list>
+                    <string>unicode</string>
+                  </list>
+                </value>
+              </dict>
+            </value>
+            <key>
+              <string>CopyrightAuthor</string>
+            </key>
+            <value>
+              <string></string>
+            </value>
+            <key>
+              <string>CopyrightMinFileSize</string>
+            </key>
+            <value>
+              <int>0</int>
+            </value>
+            <key>
+              <string>DocstringType</string>
+            </key>
+            <value>
+              <string>eric</string>
+            </value>
+            <key>
+              <string>ExcludeFiles</string>
+            </key>
+            <value>
+              <string>*/Ui_*.py, */*_rc.py</string>
+            </value>
+            <key>
+              <string>ExcludeMessages</string>
+            </key>
+            <value>
+              <string>C101, E265, E266, E305, E402, M201, M811, N802, N803, N807, N808, N821, W293, W504</string>
+            </value>
+            <key>
+              <string>FixCodes</string>
+            </key>
+            <value>
+              <string></string>
+            </value>
+            <key>
+              <string>FixIssues</string>
+            </key>
+            <value>
+              <bool>False</bool>
+            </value>
+            <key>
+              <string>FutureChecker</string>
+            </key>
+            <value>
+              <string>unicode_literals</string>
+            </value>
+            <key>
+              <string>HangClosing</string>
+            </key>
+            <value>
+              <bool>False</bool>
+            </value>
+            <key>
+              <string>IncludeMessages</string>
+            </key>
+            <value>
+              <string></string>
+            </value>
+            <key>
+              <string>LineComplexity</string>
+            </key>
+            <value>
+              <int>20</int>
+            </value>
+            <key>
+              <string>LineComplexityScore</string>
+            </key>
+            <value>
+              <int>10</int>
+            </value>
+            <key>
+              <string>MaxCodeComplexity</string>
+            </key>
+            <value>
+              <int>10</int>
+            </value>
+            <key>
+              <string>MaxLineLength</string>
+            </key>
+            <value>
+              <int>79</int>
+            </value>
+            <key>
+              <string>NoFixCodes</string>
+            </key>
+            <value>
+              <string>E501</string>
+            </value>
+            <key>
+              <string>RepeatMessages</string>
+            </key>
+            <value>
+              <bool>True</bool>
+            </value>
+            <key>
+              <string>ShowIgnored</string>
+            </key>
+            <value>
+              <bool>False</bool>
+            </value>
+            <key>
+              <string>ValidEncodings</string>
+            </key>
+            <value>
+              <string>latin-1, utf-8</string>
+            </value>
+          </dict>
+        </value>
+      </dict>
+    </CheckersParams>
+  </Checkers>
 </Project>
--- a/PluginMqttMonitor.py	Sun Aug 26 19:40:15 2018 +0200
+++ b/PluginMqttMonitor.py	Mon Aug 27 19:26:27 2018 +0200
@@ -18,13 +18,14 @@
 from E5Gui.E5Action import E5Action
 
 import UI.PixmapCache
+import Preferences
 
 # Start-Of-Header
 name = "MQTT Monitor Plugin"
 author = "Detlev Offenbach <detlev@die-offenbachs.de>"
 autoactivate = True
 deactivateable = True
-version = "1.x"
+version = "1.0.0"
 className = "MqttMonitorPlugin"
 packageName = "MqttMonitor"
 shortDescription = "Plug-in implementing a tool to connect to a MQTT broker"
@@ -66,10 +67,19 @@
     return data
 
 
+def prepareUninstall():
+    """
+    Module function to prepare for an uninstallation.
+    """
+    Preferences.Prefs.settings.remove(MqttMonitorPlugin.PreferencesKey)
+
+
 class MqttMonitorPlugin(QObject):
     """
     Class implementing the MQTT Monitor plug-in.
     """
+    PreferencesKey = "MqttMonitor"
+    
     def __init__(self, ui):
         """
         Constructor
@@ -81,6 +91,10 @@
         self.__ui = ui
         self.__initialize()
         
+        self.__defaults = {
+            "RecentBrokers": [],
+        }
+        
         self.__translator = None
         self.__loadTranslator()
     
@@ -106,12 +120,9 @@
             error = self.tr("The 'paho-mqtt' package is not available.")
             return None, False
         
-##        self.__object = MqttMonitorWidget(self, self.__ui)
-##        self.__object.activate()
-##        e5App().registerPluginObject("MqttMonitor", self.__object)
         from MqttMonitor.MqttMonitorWidget import MqttMonitorWidget
         
-        self.__widget = MqttMonitorWidget()##self.__ui)
+        self.__widget = MqttMonitorWidget(self)
         self.__ui.addSideWidget(
             self.__ui.RightSide, self.__widget,
             UI.PixmapCache.getIcon(
@@ -143,8 +154,6 @@
         """
         Public method to deactivate this plug-in.
         """
-##        e5App().unregisterPluginObject("TimeTracker")
-##        self.__object.deactivate()
         menu = self.__ui.getMenu("subwindow")
         menu.removeAction(self.__activateAct)
         self.__ui.removeE5Actions([self.__activateAct], 'ui')
@@ -191,3 +200,30 @@
         else:
             self.__widget.show()
         self.__widget.setFocus(Qt.ActiveWindowFocusReason)
+    
+    def getPreferences(self, key):
+        """
+        Public method to retrieve the various settings.
+        
+        @param key the key of the value to get
+        @return the requested setting
+        """
+        if key in ["RecentBrokers"]:
+            return Preferences.toList(Preferences.Prefs.settings.value(
+                self.PreferencesKey + "/" + key, self.__defaults[key]))
+        else:
+            return Preferences.Prefs.settings.value(
+                self.PreferencesKey + "/" + key, self.__defaults[key])
+    
+    def setPreferences(self, key, value):
+        """
+        Public method to store the various settings.
+        
+        @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)
+
+#
+# eflag: noqa = M801
--- a/__init__.py	Sun Aug 26 19:40:15 2018 +0200
+++ b/__init__.py	Mon Aug 27 19:26:27 2018 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2018 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the MQTT Monitor plug-in.
+"""

eric ide

mercurial