MqttMonitor/MqttMonitorWidget.py

Tue, 28 Aug 2018 18:54:48 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Tue, 28 Aug 2018 18:54:48 +0200
changeset 4
991db89bda9c
parent 3
82845c0fd550
child 5
7162c838cfc9
permissions
-rw-r--r--

MqttMonitorWidget: added functionality to subscribe to and unsubscribe from topics.

# -*- coding: utf-8 -*-

# Copyright (c) 2018 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the MQTT Monitor widget.
"""

from __future__ import unicode_literals

import os

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 implementing the MQTT Monitor widget.
    """
    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.subscribeButton.setIcon(UI.PixmapCache.getIcon("plus.png"))
        self.subscribeButton.setEnabled(False)
        self.unsubscribeButton.setIcon(UI.PixmapCache.getIcon("minus.png"))
        
        self.__subscribedTopics = []
        self.__topicQueue = {}
        self.__updateUnsubscribeTopicComboBox()
        
        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"))
        
        self.subscribeGroup.setEnabled(True)
        self.unsubscribeGroup.setEnabled(True)
    
    @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
        
        # ensure, the client loop is stopped
        self.__client.stopLoop()
        
        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.__subscribedTopics = []
        self.__topicQueue = {}
        self.__updateUnsubscribeTopicComboBox()
        
        self.subscribeGroup.setEnabled(False)
        self.unsubscribeGroup.setEnabled(False)
    
    @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
        """
        if mid in self.__topicQueue:
            topic = self.__topicQueue.pop(mid)
            self.__subscribedTopics.append(topic)
            self.subscribeTopicEdit.clear()
            
            self.__updateUnsubscribeTopicComboBox()
    
    @pyqtSlot(int)
    def __topicUnsubscribed(self, mid):
        """
        Private slot to handle being unsubcribed from a topic.
        
        @param mid ID of the unsubscribe request
        @type int
        """
        if mid in self.__topicQueue:
            topic = self.__topicQueue.pop(mid)
            try:
                self.__subscribedTopics.remove(topic)
                self.__updateUnsubscribeTopicComboBox()
            except ValueError:
                # ignore it
                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()
            if host:
                self.__addBrokerToRecent(host)
                self.__client.connectToServer(host)
                # use standard port at 1883
    
    @pyqtSlot(str)
    def on_subscribeTopicEdit_textChanged(self, topic):
        """
        Private slot to handle a change of the entered topic.
        
        @param topic entered topic text
        @type str
        """
        self.subscribeButton.setEnabled(bool(topic))
    
    @pyqtSlot()
    def on_subscribeButton_clicked(self):
        """
        Private slot to subscribe to the entered topic.
        """
        topic = self.subscribeTopicEdit.text()
        qos = self.subscribeQosSpinBox.value()
        if topic:
            self.__topicQueue[
                self.__client.subscribe(topic, qos)[1]] = topic
    
    @pyqtSlot(str)
    def on_unsubscribeTopicComboBox_currentIndexChanged(self, topic):
        """
        Private slot to handle the selection of a topic to unsubscribe from.
        
        @param topic topic text
        @type str
        """
        self.unsubscribeButton.setEnabled(bool(topic))
    
    @pyqtSlot()
    def on_unsubscribeButton_clicked(self):
        """
        Private slot to unsubscribe from the selected topic.
        """
        topic = self.unsubscribeTopicComboBox.currentText()
        if topic:
            self.__topicQueue[
                self.__client.unsubscribe(topic)[1]] = topic
    
    #######################################################################
    ## 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)
    
    def __updateUnsubscribeTopicComboBox(self):
        """
        Private method to update the unsubcribe topic combo box.
        """
        self.unsubscribeTopicComboBox.clear()
        self.unsubscribeTopicComboBox.addItems(sorted(self.__subscribedTopics))
        self.unsubscribeButton.setEnabled(len(self.__subscribedTopics) > 0)

eric ide

mercurial