Wed, 21 Sep 2022 09:42:33 +0200
Performed some code refactoring and reformatted the source code with 'Black'
--- a/ChangeLog Thu Dec 30 16:36:40 2021 +0100 +++ b/ChangeLog Wed Sep 21 09:42:33 2022 +0200 @@ -1,5 +1,8 @@ ChangeLog --------- +Version 10.2.2: +- some code refactoring + Version 10.2.1: - updated Russian translations
--- a/MqttMonitor/ConfigurationPage/MqttPage.py Thu Dec 30 16:36:40 2021 +0100 +++ b/MqttMonitor/ConfigurationPage/MqttPage.py Wed Sep 21 09:42:33 2022 +0200 @@ -7,9 +7,7 @@ Module implementing the MQTT Monitor configuration page. """ -from Preferences.ConfigurationPages.ConfigurationPageBase import ( - ConfigurationPageBase -) +from Preferences.ConfigurationPages.ConfigurationPageBase import ConfigurationPageBase from .Ui_MqttPage import Ui_MqttPage @@ -20,32 +18,32 @@ """ Class implementing the MQTT Monitor configuration page. """ + def __init__(self, plugin): """ Constructor - + @param plugin reference to the plugin object @type RefactoringRopePlugin """ ConfigurationPageBase.__init__(self) self.setupUi(self) self.setObjectName("MqttPage") - + self.__plugin = plugin - + # set initial values protocol = self.__plugin.getPreferences("DefaultProtocol") - self.mqttv31Button.setChecked( - protocol == MqttProtocols.MQTTv31) - self.mqttv311Button.setChecked( - protocol == MqttProtocols.MQTTv311) - self.mqttv5Button.setChecked( - protocol == MqttProtocols.MQTTv5) + self.mqttv31Button.setChecked(protocol == MqttProtocols.MQTTv31) + self.mqttv311Button.setChecked(protocol == MqttProtocols.MQTTv311) + self.mqttv5Button.setChecked(protocol == MqttProtocols.MQTTv5) self.recentBrokersSpinBox.setValue( - self.__plugin.getPreferences("RecentBrokersNumber")) + self.__plugin.getPreferences("RecentBrokersNumber") + ) self.recentTopicsSpinBox.setValue( - self.__plugin.getPreferences("RecentTopicsNumber")) - + self.__plugin.getPreferences("RecentTopicsNumber") + ) + def save(self): """ Public slot to save the Rope Autocompletion configuration. @@ -59,10 +57,11 @@ else: # should never happen protocol = MqttProtocols.MQTTv311 - - self.__plugin.setPreferences( - "DefaultProtocol", protocol) + + self.__plugin.setPreferences("DefaultProtocol", protocol) self.__plugin.setPreferences( - "RecentBrokersNumber", self.recentBrokersSpinBox.value()) + "RecentBrokersNumber", self.recentBrokersSpinBox.value() + ) self.__plugin.setPreferences( - "RecentTopicsNumber", self.recentTopicsSpinBox.value()) + "RecentTopicsNumber", self.recentTopicsSpinBox.value() + )
--- a/MqttMonitor/Documentation/source/Plugin_Mqtt_Monitor.MqttMonitor.MqttClient.html Thu Dec 30 16:36:40 2021 +0100 +++ b/MqttMonitor/Documentation/source/Plugin_Mqtt_Monitor.MqttMonitor.MqttClient.html Wed Sep 21 09:42:33 2022 +0200 @@ -153,8 +153,52 @@ <td>Private method to initialize the MQTT callback methods.</td> </tr> <tr> +<td><a href="#MqttClient.__onConnectV3">__onConnectV3</a></td> +<td>Private method to handle the connect to the broker (MQTT v3.1 and v3.1.1).</td> +</tr> +<tr> +<td><a href="#MqttClient.__onConnectV5">__onConnectV5</a></td> +<td>Private method to handle the connect to the broker (MQTT v5.0).</td> +</tr> +<tr> +<td><a href="#MqttClient.__onDisconnectedV3">__onDisconnectedV3</a></td> +<td>Private method to handle the disconnect from the broker (MQTT v3.1 and v3.1.1).</td> +</tr> +<tr> <td><a href="#MqttClient.__onDisconnectedV5">__onDisconnectedV5</a></td> -<td>Private method to handle the disconnect from the broker.</td> +<td>Private method to handle the disconnect from the broker (MQTT v5.0).</td> +</tr> +<tr> +<td><a href="#MqttClient.__onLog">__onLog</a></td> +<td>Private method to handle a log event (MQTT v3.1, v3.1.1 and v5.0).</td> +</tr> +<tr> +<td><a href="#MqttClient.__onMessageV3">__onMessageV3</a></td> +<td>Private method to handle a new message received from the broker (MQTT v3.1 and v3.1.1).</td> +</tr> +<tr> +<td><a href="#MqttClient.__onMessageV5">__onMessageV5</a></td> +<td>Private method to handle a new message received from the broker (MQTT v5.0).</td> +</tr> +<tr> +<td><a href="#MqttClient.__onPublish">__onPublish</a></td> +<td>Private method to handle the publishing of a message (MQTT v3.1, v3.1.1 and v5.0).</td> +</tr> +<tr> +<td><a href="#MqttClient.__onSubscribeV3">__onSubscribeV3</a></td> +<td>Private method to handle a subscribe event (MQTT v3.1 and v3.1.1).</td> +</tr> +<tr> +<td><a href="#MqttClient.__onSubscribeV5">__onSubscribeV5</a></td> +<td>Private method to handle a subscribe event (MQTT v5.0).</td> +</tr> +<tr> +<td><a href="#MqttClient.__onUnsubscribeV3">__onUnsubscribeV3</a></td> +<td>Private method to handle an unsubscribe event (MQTT v3.1 and v3.1.1).</td> +</tr> +<tr> +<td><a href="#MqttClient.__onUnsubscribeV5">__onUnsubscribeV5</a></td> +<td>Private method to handle an unsubscribe event (MQTT v5.0).</td> </tr> <tr> <td><a href="#MqttClient.clearLastWill">clearLastWill</a></td> @@ -261,7 +305,7 @@ </dl> <a NAME="MqttClient.__init__" ID="MqttClient.__init__"></a> <h4>MqttClient (Constructor)</h4> -<b>MqttClient</b>(<i>clientId="", cleanSession=True, userdata=None, protocol=mqtt.MQTTv311, transport="tcp", parent=None</i>) +<b>MqttClient</b>(<i>clientId="", cleanSession=True, userdata=None, protocol=mqtt.MQTTv311, transport="tcp", parent=None, </i>) <p> Constructor @@ -344,12 +388,147 @@ MQTT protocol version </dd> </dl> +<a NAME="MqttClient.__onConnectV3" ID="MqttClient.__onConnectV3"></a> +<h4>MqttClient.__onConnectV3</h4> +<b>__onConnectV3</b>(<i>client, userdata, flags, rc, properties=None</i>) + +<p> + Private method to handle the connect to the broker (MQTT v3.1 and v3.1.1). +</p> +<dl> + +<dt><i>client</i> (paho.mqtt.Client)</dt> +<dd> +reference to the client object +</dd> +<dt><i>userdata</i> (Any)</dt> +<dd> +user data +</dd> +<dt><i>flags</i> (dict)</dt> +<dd> +dictionary containing the response flags sent by the broker +</dd> +<dt><i>rc</i> (int)</dt> +<dd> +result code +</dd> +<dt><i>properties</i> (dict (optional))</dt> +<dd> +optional properties (defaults to None) +</dd> +</dl> +<a NAME="MqttClient.__onConnectV5" ID="MqttClient.__onConnectV5"></a> +<h4>MqttClient.__onConnectV5</h4> +<b>__onConnectV5</b>(<i>client, userdata, flags, rc, properties=None</i>) + +<p> + Private method to handle the connect to the broker (MQTT v5.0). +</p> +<dl> + +<dt><i>client</i> (paho.mqtt.Client)</dt> +<dd> +reference to the client object +</dd> +<dt><i>userdata</i> (Any)</dt> +<dd> +user data +</dd> +<dt><i>flags</i> (dict)</dt> +<dd> +dictionary containing the response flags sent by the broker +</dd> +<dt><i>rc</i> (paho.mqtt.ReasonCodes)</dt> +<dd> +reason code +</dd> +<dt><i>properties</i> (dict (optional))</dt> +<dd> +optional properties (defaults to None) +</dd> +</dl> +<a NAME="MqttClient.__onDisconnectedV3" ID="MqttClient.__onDisconnectedV3"></a> +<h4>MqttClient.__onDisconnectedV3</h4> +<b>__onDisconnectedV3</b>(<i>client, userdata, rc</i>) + +<p> + Private method to handle the disconnect from the broker (MQTT v3.1 and v3.1.1). +</p> +<dl> + +<dt><i>client</i> (paho.mqtt.Client)</dt> +<dd> +reference to the client object +</dd> +<dt><i>userdata</i> (Any)</dt> +<dd> +user data +</dd> +<dt><i>rc</i> (int)</dt> +<dd> +result code +</dd> +</dl> <a NAME="MqttClient.__onDisconnectedV5" ID="MqttClient.__onDisconnectedV5"></a> <h4>MqttClient.__onDisconnectedV5</h4> <b>__onDisconnectedV5</b>(<i>client, userdata, rc, properties=None</i>) <p> - Private method to handle the disconnect from the broker. + Private method to handle the disconnect from the broker (MQTT v5.0). +</p> +<dl> + +<dt><i>client</i> (paho.mqtt.Client)</dt> +<dd> +reference to the client object +</dd> +<dt><i>userdata</i> (Any)</dt> +<dd> +user data +</dd> +<dt><i>rc</i> (int or paho.mqtt.ReasonCodes)</dt> +<dd> +result code or reason code +</dd> +<dt><i>properties</i> (dict (optional))</dt> +<dd> +optional properties (defaults to None) +</dd> +</dl> +<a NAME="MqttClient.__onLog" ID="MqttClient.__onLog"></a> +<h4>MqttClient.__onLog</h4> +<b>__onLog</b>(<i>client, userdata, level, buf</i>) + +<p> + Private method to handle a log event (MQTT v3.1, v3.1.1 and v5.0). +</p> +<dl> + +<dt><i>client</i> (paho.mqtt.Client)</dt> +<dd> +reference to the client object +</dd> +<dt><i>userdata</i> (Any)</dt> +<dd> +user data +</dd> +<dt><i>level</i> (int)</dt> +<dd> +severity of the log message +</dd> +<dt><i>buf</i> (str)</dt> +<dd> +log message +</dd> +</dl> +<a NAME="MqttClient.__onMessageV3" ID="MqttClient.__onMessageV3"></a> +<h4>MqttClient.__onMessageV3</h4> +<b>__onMessageV3</b>(<i>client, userdata, message</i>) + +<p> + Private method to handle a new message received from the broker (MQTT v3.1 + and v3.1.1). </p> <dl> @@ -361,15 +540,164 @@ <dd> user data </dd> -<dt><i>rc</i> (int or ReasonCodes)</dt> +<dt><i>message</i> (paho.mqtt.MQTTMessage)</dt> +<dd> +received message object +</dd> +</dl> +<a NAME="MqttClient.__onMessageV5" ID="MqttClient.__onMessageV5"></a> +<h4>MqttClient.__onMessageV5</h4> +<b>__onMessageV5</b>(<i>client, userdata, message</i>) + +<p> + Private method to handle a new message received from the broker (MQTT v5.0). +</p> +<dl> + +<dt><i>client</i> (paho.mqtt.Client)</dt> +<dd> +reference to the client object +</dd> +<dt><i>userdata</i> (Any)</dt> +<dd> +user data +</dd> +<dt><i>message</i> (paho.mqtt.MQTTMessage)</dt> +<dd> +received message object +</dd> +</dl> +<a NAME="MqttClient.__onPublish" ID="MqttClient.__onPublish"></a> +<h4>MqttClient.__onPublish</h4> +<b>__onPublish</b>(<i>client, userdata, mid</i>) + +<p> + Private method to handle the publishing of a message (MQTT v3.1, v3.1.1 + and v5.0). +</p> +<dl> + +<dt><i>client</i> (paho.mqtt.Client)</dt> +<dd> +reference to the client object +</dd> +<dt><i>userdata</i> (Any)</dt> +<dd> +user data +</dd> +<dt><i>mid</i> (int)</dt> <dd> -result code or reason code +message ID +</dd> +</dl> +<a NAME="MqttClient.__onSubscribeV3" ID="MqttClient.__onSubscribeV3"></a> +<h4>MqttClient.__onSubscribeV3</h4> +<b>__onSubscribeV3</b>(<i>client, userdata, mid, grantedQos</i>) + +<p> + Private method to handle a subscribe event (MQTT v3.1 and v3.1.1). +</p> +<dl> + +<dt><i>client</i> (paho.mqtt.Client)</dt> +<dd> +reference to the client object +</dd> +<dt><i>userdata</i> (Any)</dt> +<dd> +user data +</dd> +<dt><i>mid</i> (int)</dt> +<dd> +message ID +</dd> +<dt><i>grantedQos</i> (list of int)</dt> +<dd> +list of granted QoS for each subscription request +</dd> +</dl> +<a NAME="MqttClient.__onSubscribeV5" ID="MqttClient.__onSubscribeV5"></a> +<h4>MqttClient.__onSubscribeV5</h4> +<b>__onSubscribeV5</b>(<i>client, userdata, mid, reasonCodes, properties=None</i>) + +<p> + Private method to handle a subscribe event (MQTT v5.0). +</p> +<dl> + +<dt><i>client</i> (paho.mqtt.Client)</dt> +<dd> +reference to the client object +</dd> +<dt><i>userdata</i> (Any)</dt> +<dd> +user data +</dd> +<dt><i>mid</i> (int)</dt> +<dd> +message ID +</dd> +<dt><i>reasonCodes</i> (list of paho.mqtt.ReasonCodes)</dt> +<dd> +list of reason code for each subscribed topic </dd> <dt><i>properties</i> (dict (optional))</dt> <dd> optional properties (defaults to None) </dd> </dl> +<a NAME="MqttClient.__onUnsubscribeV3" ID="MqttClient.__onUnsubscribeV3"></a> +<h4>MqttClient.__onUnsubscribeV3</h4> +<b>__onUnsubscribeV3</b>(<i>client, userdata, mid</i>) + +<p> + Private method to handle an unsubscribe event (MQTT v3.1 and v3.1.1). +</p> +<dl> + +<dt><i>client</i> (paho.mqtt.Client)</dt> +<dd> +reference to the client object +</dd> +<dt><i>userdata</i> (Any)</dt> +<dd> +user data +</dd> +<dt><i>mid</i> (int)</dt> +<dd> +message ID +</dd> +</dl> +<a NAME="MqttClient.__onUnsubscribeV5" ID="MqttClient.__onUnsubscribeV5"></a> +<h4>MqttClient.__onUnsubscribeV5</h4> +<b>__onUnsubscribeV5</b>(<i>client, userdata, mid, properties, reasonCodes</i>) + +<p> + Private method to handle an unsubscribe event (MQTT v5.0). +</p> +<dl> + +<dt><i>client</i> (paho.mqtt.Client)</dt> +<dd> +reference to the client object +</dd> +<dt><i>userdata</i> (Any)</dt> +<dd> +user data +</dd> +<dt><i>mid</i> (int)</dt> +<dd> +message ID +</dd> +<dt><i>properties</i> (dict (optional))</dt> +<dd> +optional properties (defaults to None) +</dd> +<dt><i>reasonCodes</i> (list of paho.mqtt.ReasonCodes)</dt> +<dd> +list of reason code for each unsubscribed topic +</dd> +</dl> <a NAME="MqttClient.clearLastWill" ID="MqttClient.clearLastWill"></a> <h4>MqttClient.clearLastWill</h4> <b>clearLastWill</b>(<i></i>) @@ -380,7 +708,7 @@ </p> <a NAME="MqttClient.connectToServer" ID="MqttClient.connectToServer"></a> <h4>MqttClient.connectToServer</h4> -<b>connectToServer</b>(<i>host, port=1883, keepalive=60, bindAddress="", properties=None, clearWill=False</i>) +<b>connectToServer</b>(<i>host, port=1883, keepalive=60, bindAddress="", properties=None, clearWill=False, </i>) <p> Public method to connect to a remote MQTT broker.
--- a/MqttMonitor/MqttClient.py Thu Dec 30 16:36:40 2021 +0100 +++ b/MqttMonitor/MqttClient.py Wed Sep 21 09:42:33 2022 +0200 @@ -7,9 +7,7 @@ Module implementing a PyQt wrapper around the paho MQTT client. """ -from PyQt6.QtCore import ( - pyqtSignal, pyqtSlot, QObject, QCoreApplication, QTimer -) +from PyQt6.QtCore import pyqtSignal, pyqtSlot, QObject, QCoreApplication, QTimer import paho.mqtt.client as mqtt from paho.mqtt.packettypes import PacketTypes @@ -23,7 +21,7 @@ class MqttClient(QObject): """ Class implementing a PyQt wrapper around the paho MQTT client. - + @signal onConnectV3(flags, rc) emitted after the client has connected to the broker (MQTT v3) @signal onConnectV5(flags, rc, packetType, properties emitted after the @@ -49,6 +47,7 @@ @signal connectTimeout() emitted to indicate, that a connection attempt timed out """ + onConnectV3 = pyqtSignal(dict, int) onConnectV5 = pyqtSignal(dict, int, int, dict) onDisconnectedV3 = pyqtSignal(int) @@ -61,30 +60,37 @@ onSubscribeV5 = pyqtSignal(int, list, dict) onUnsubscribeV3 = pyqtSignal(int) onUnsubscribeV5 = pyqtSignal(int, int, int, dict) - + connectTimeout = pyqtSignal() - - DefaultConnectTimeout = 15 # connect timeout in seconds - + + DefaultConnectTimeout = 15 # connect timeout in seconds + LogDebug = 0x01 LogInfo = 0x02 LogNotice = 0x04 LogWarning = 0x08 LogError = 0x10 - LogDisabled = 0xff + LogDisabled = 0xFF LogLevelMap = { mqtt.MQTT_LOG_DEBUG: LogDebug, mqtt.MQTT_LOG_INFO: LogInfo, mqtt.MQTT_LOG_NOTICE: LogNotice, - mqtt.MQTT_LOG_WARNING: LogWarning, # __NO-TASK__ + mqtt.MQTT_LOG_WARNING: LogWarning, # __NO-TASK__ mqtt.MQTT_LOG_ERR: LogError, } - - def __init__(self, clientId="", cleanSession=True, userdata=None, - protocol=mqtt.MQTTv311, transport="tcp", parent=None): + + def __init__( + self, + clientId="", + cleanSession=True, + userdata=None, + protocol=mqtt.MQTTv311, + transport="tcp", + parent=None, + ): """ Constructor - + @param clientId ID to be used for the client @type str @param cleanSession flag indicating to start a clean session @@ -99,109 +105,163 @@ @type QObject """ QObject.__init__(self, parent=parent) - + self.__loopStarted = False - + self.__connectTimeoutTimer = QTimer(self) self.__connectTimeoutTimer.setSingleShot(True) - self.__connectTimeoutTimer.setInterval( - MqttClient.DefaultConnectTimeout * 1000) + self.__connectTimeoutTimer.setInterval(MqttClient.DefaultConnectTimeout * 1000) self.__connectTimeoutTimer.timeout.connect(self.__connectTimeout) - + self.onConnectV3.connect(self.__connectTimeoutTimer.stop) self.onConnectV5.connect(self.__connectTimeoutTimer.stop) - + self.__cleanSession = cleanSession self.__protocol = protocol self.__disconnectUserProperties = [] - + if protocol == MqttProtocols.MQTTv5: cleanSession = None - + self.__mqttClient = mqtt.Client( - client_id=clientId, clean_session=cleanSession, userdata=userdata, - protocol=int(protocol), transport=transport) - + client_id=clientId, + clean_session=cleanSession, + userdata=userdata, + protocol=int(protocol), + transport=transport, + ) + self.__initCallbacks(protocol) def __initCallbacks(self, protocol): """ Private method to initialize the MQTT callback methods. - + @param protocol MQTT protocol version @type MqttProtocols """ if protocol in (MqttProtocols.MQTTv31, MqttProtocols.MQTTv311): - self.__mqttClient.on_connect = ( - lambda client, userdata, flags, rc, properties=None: - self.onConnectV3.emit(flags, rc) - ) - self.__mqttClient.on_disconnect = ( - lambda client, userdata, rc: - self.onDisconnectedV3.emit(rc) - ) - self.__mqttClient.on_subscribe = ( - lambda client, userdata, mid, grantedQos, properties=None: - self.onSubscribeV3.emit(mid, grantedQos) - ) - self.__mqttClient.on_unsubscribe = ( - lambda client, userdata, mid: - self.onUnsubscribeV3.emit(mid) - ) - self.__mqttClient.on_message = ( - lambda client, userdata, message: - self.onMessageV3.emit(message.topic, message.payload, - message.qos, message.retain) - ) + self.__mqttClient.on_connect = self.__onConnectV3 + self.__mqttClient.on_disconnect = self.__onDisconnectedV3 + self.__mqttClient.on_subscribe = self.__onSubscribeV3 + self.__mqttClient.on_unsubscribe = self.__onUnsubscribeV3 + self.__mqttClient.on_message = self.__onMessageV3 else: - self.__mqttClient.on_connect = ( - lambda client, userdata, flags, rc, properties=None: - self.onConnectV5.emit( - flags, rc.value, rc.packetType, - properties.json() if properties is not None else {} - ) - ) + self.__mqttClient.on_connect = self.__onConnectV5 self.__mqttClient.on_disconnect = self.__onDisconnectedV5 - self.__mqttClient.on_subscribe = ( - lambda client, userdata, mid, reasonCodes, properties=None: - self.onSubscribeV5.emit( - mid, reasonCodes, - properties.json() if properties is not None else {} - ) - ) - self.__mqttClient.on_unsubscribe = ( - lambda client, userdata, mid, properties, rc: - self.onUnsubscribeV5.emit( - mid, rc.value, rc.packetType, - properties.json() if properties is not None else {} - ) - ) - self.__mqttClient.on_message = ( - lambda client, userdata, message: - self.onMessageV5.emit( - message.topic, message.payload, message.qos, - message.retain, message.properties.json() - ) - ) - self.__mqttClient.on_log = ( - lambda client, userdata, level, buf: - self.onLog.emit(level, buf) + self.__mqttClient.on_subscribe = self.__onSubscribeV5 + self.__mqttClient.on_unsubscribe = self.__onUnsubscribeV5 + self.__mqttClient.on_message = self.__onMessageV5 + + self.__mqttClient.on_log = self.__onLog + self.__mqttClient.on_publish = self.__onPublish + + def __onConnectV3(self, client, userdata, flags, rc, properties=None): + """ + Private method to handle the connect to the broker (MQTT v3.1 and v3.1.1). + + @param client reference to the client object + @type paho.mqtt.Client + @param userdata user data + @type Any + @param flags dictionary containing the response flags sent by the broker + @type dict + @param rc result code + @type int + @param properties optional properties (defaults to None) + @type dict (optional) + """ + self.onConnectV3.emit(flags, rc) + + def __onDisconnectedV3(self, client, userdata, rc): + """ + Private method to handle the disconnect from the broker (MQTT v3.1 and v3.1.1). + + @param client reference to the client object + @type paho.mqtt.Client + @param userdata user data + @type Any + @param rc result code + @type int + """ + self.onDisconnectedV3.emit(rc) + + def __onSubscribeV3(self, client, userdata, mid, grantedQos): + """ + Private method to handle a subscribe event (MQTT v3.1 and v3.1.1). + + @param client reference to the client object + @type paho.mqtt.Client + @param userdata user data + @type Any + @param mid message ID + @type int + @param grantedQos list of granted QoS for each subscription request + @type list of int + """ + self.onSubscribeV3.emit(mid, grantedQos) + + def __onUnsubscribeV3(self, client, userdata, mid): + """ + Private method to handle an unsubscribe event (MQTT v3.1 and v3.1.1). + + @param client reference to the client object + @type paho.mqtt.Client + @param userdata user data + @type Any + @param mid message ID + @type int + """ + self.onUnsubscribeV3.emit(mid) + + def __onMessageV3(self, client, userdata, message): + """ + Private method to handle a new message received from the broker (MQTT v3.1 + and v3.1.1). + + @param client reference to the client object + @type paho.mqtt.Client + @param userdata user data + @type Any + @param message received message object + @type paho.mqtt.MQTTMessage + """ + self.onMessageV3.emit( + message.topic, message.payload, message.qos, message.retain ) - self.__mqttClient.on_publish = ( - lambda client, userdata, mid: - self.onPublish.emit(mid) + + def __onConnectV5(self, client, userdata, flags, rc, properties=None): + """ + Private method to handle the connect to the broker (MQTT v5.0). + + @param client reference to the client object + @type paho.mqtt.Client + @param userdata user data + @type Any + @param flags dictionary containing the response flags sent by the broker + @type dict + @param rc reason code + @type paho.mqtt.ReasonCodes + @param properties optional properties (defaults to None) + @type dict (optional) + """ + self.onConnectV5.emit( + flags, + rc.value, + rc.packetType, + properties.json() if properties is not None else {}, ) - + def __onDisconnectedV5(self, client, userdata, rc, properties=None): """ - Private method to handle the disconnect from the broker. - + Private method to handle the disconnect from the broker (MQTT v5.0). + @param client reference to the client object @type paho.mqtt.Client @param userdata user data @type Any @param rc result code or reason code - @type int or ReasonCodes + @type int or paho.mqtt.ReasonCodes @param properties optional properties (defaults to None) @type dict (optional) """ @@ -212,7 +272,98 @@ packetType = rc.packetType resultCode = rc.value self.onDisconnectedV5.emit(resultCode, packetType) - + + def __onSubscribeV5(self, client, userdata, mid, reasonCodes, properties=None): + """ + Private method to handle a subscribe event (MQTT v5.0). + + @param client reference to the client object + @type paho.mqtt.Client + @param userdata user data + @type Any + @param mid message ID + @type int + @param reasonCodes list of reason code for each subscribed topic + @type list of paho.mqtt.ReasonCodes + @param properties optional properties (defaults to None) + @type dict (optional) + """ + self.onSubscribeV5.emit( + mid, + reasonCodes, + properties.json() if properties is not None else {}, + ) + + def __onUnsubscribeV5(self, client, userdata, mid, properties, reasonCodes): + """ + Private method to handle an unsubscribe event (MQTT v5.0). + + @param client reference to the client object + @type paho.mqtt.Client + @param userdata user data + @type Any + @param mid message ID + @type int + @param properties optional properties (defaults to None) + @type dict (optional) + @param reasonCodes list of reason code for each unsubscribed topic + @type list of paho.mqtt.ReasonCodes + """ + self.onUnsubscribeV5.emit( + mid, + reasonCodes.value, + reasonCodes.packetType, + properties.json() if properties is not None else {}, + ) + + def __onMessageV5(self, client, userdata, message): + """ + Private method to handle a new message received from the broker (MQTT v5.0). + + @param client reference to the client object + @type paho.mqtt.Client + @param userdata user data + @type Any + @param message received message object + @type paho.mqtt.MQTTMessage + """ + self.onMessageV5.emit( + message.topic, + message.payload, + message.qos, + message.retain, + message.properties.json(), + ) + + def __onLog(self, client, userdata, level, buf): + """ + Private method to handle a log event (MQTT v3.1, v3.1.1 and v5.0). + + @param client reference to the client object + @type paho.mqtt.Client + @param userdata user data + @type Any + @param level severity of the log message + @type int + @param buf log message + @type str + """ + self.onLog.emit(level, buf) + + def __onPublish(self, client, userdata, mid): + """ + Private method to handle the publishing of a message (MQTT v3.1, v3.1.1 + and v5.0). + + @param client reference to the client object + @type paho.mqtt.Client + @param userdata user data + @type Any + @param mid message ID + @type int + """ + self.onPublish.emit(mid) + @pyqtSlot() def __connectTimeout(self): """ @@ -220,61 +371,60 @@ """ self.stopLoop() self.connectTimeout.emit() - + def setConnectionTimeout(self, timeout): """ Public method to set the connection timeout value. - + @param timeout timeout value to be set in seconds @type int """ self.__connectTimeoutTimer.setInterval(timeout * 1000) - + 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, - properties=None): + + def setLastWill(self, topic, payload=None, qos=0, retain=False, properties=None): """ 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 @@ -288,20 +438,21 @@ last will message @type list of tuple of (str, str) """ - self.__mqttClient.will_set(topic, payload=payload, qos=qos, - retain=retain, properties=properties) - + self.__mqttClient.will_set( + topic, payload=payload, qos=qos, retain=retain, properties=properties + ) + def clearLastWill(self): """ Public method to remove a will that was previously configured with setLastWill(). """ self.__mqttClient.will_clear() - + def setTLS(self, caCerts=None, certFile=None, keyFile=None): """ Public method to enable secure connections and set the TLS parameters. - + @param caCerts path to the Certificate Authority certificates file @type str @param certFile PEM encoded client certificate file @@ -313,42 +464,50 @@ @rtype tuple of (bool, str) """ try: - self.__mqttClient.tls_set(ca_certs=caCerts, certfile=certFile, - keyfile=keyFile) + self.__mqttClient.tls_set( + ca_certs=caCerts, certfile=certFile, keyfile=keyFile + ) return True, "" except (ValueError, FileNotFoundError) as err: return False, str(err) - + return False, "unspecific error occurred" - + def getProtocol(self): """ Public method to get the MQTT protocol version. - + @return MQTT protocol version in use @rtype int """ return self.__protocol - + def startLoop(self): """ Public method to start the MQTT client loop. """ self.__mqttClient.loop_start() self.__loopStarted = True - + def stopLoop(self): """ Public method to stop the MQTT client loop. """ self.__mqttClient.loop_stop() self.__loopStarted = False - - def connectToServer(self, host, port=1883, keepalive=60, bindAddress="", - properties=None, clearWill=False): + + def connectToServer( + self, + host, + port=1883, + keepalive=60, + bindAddress="", + properties=None, + clearWill=False, + ): """ 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: @@ -368,26 +527,32 @@ """ if clearWill: self.clearLastWill() - + props = ( self.__createPropertiesObject(PacketTypes.CONNECT, properties) - if properties else - None + if properties + else None ) self.__mqttClient.connect_async( - host, port=port, keepalive=keepalive, bind_address=bindAddress, - clean_start=self.__cleanSession, properties=props) - + host, + port=port, + keepalive=keepalive, + bind_address=bindAddress, + clean_start=self.__cleanSession, + properties=props, + ) + self.__connectTimeoutTimer.start() - + if not self.__loopStarted: self.startLoop() - - def connectToServerWithOptions(self, host, port=1883, bindAddress="", - options=None, clearWill=False): + + def connectToServerWithOptions( + self, host, port=1883, bindAddress="", options=None, clearWill=False + ): """ 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: @@ -409,18 +574,19 @@ if options: parametersDict = self.defaultConnectionOptions() parametersDict.update(options) - + self.setConnectionTimeout(parametersDict["ConnectionTimeout"]) - + # step 1: set username and password if parametersDict["Username"]: if parametersDict["Password"]: self.setUserCredentials( parametersDict["Username"], - pwConvert(parametersDict["Password"], encode=False)) + pwConvert(parametersDict["Password"], encode=False), + ) else: self.setUserCredentials(parametersDict["Username"]) - + # step 2: set last will data if not clearWill and parametersDict["WillTopic"]: if parametersDict["WillMessage"]: @@ -430,35 +596,38 @@ willMessage = None props = ( self.__createPropertiesObject( - PacketTypes.WILLMESSAGE, - parametersDict["WillProperties"]) - if (parametersDict["WillProperties"] and - self.__protocol == MqttProtocols.MQTTv5) else - None + PacketTypes.WILLMESSAGE, parametersDict["WillProperties"] + ) + if ( + parametersDict["WillProperties"] + and self.__protocol == MqttProtocols.MQTTv5 + ) + else None ) - self.setLastWill(parametersDict["WillTopic"], - payload=willMessage, - qos=parametersDict["WillQos"], - retain=parametersDict["WillRetain"], - properties=props) - + self.setLastWill( + parametersDict["WillTopic"], + payload=willMessage, + qos=parametersDict["WillQos"], + retain=parametersDict["WillRetain"], + properties=props, + ) + # step 3: set TLS parameters if parametersDict["TlsEnable"]: - if ( - parametersDict["TlsCaCert"] and - parametersDict["TlsClientCert"] - ): + if parametersDict["TlsCaCert"] and parametersDict["TlsClientCert"]: # use self signed client certificate - self.setTLS(caCerts=parametersDict["TlsCaCert"], - certFile=parametersDict["TlsClientCert"], - keyFile=parametersDict["TlsClientKey"]) + self.setTLS( + caCerts=parametersDict["TlsCaCert"], + certFile=parametersDict["TlsClientCert"], + keyFile=parametersDict["TlsClientKey"], + ) elif parametersDict["TlsCaCert"]: # use CA certificate file self.setTLS(caCerts=parametersDict["TlsCaCert"]) else: # use default TLS configuration self.setTLS() - + # step 4: get the connect user properties if self.__protocol == MqttProtocols.MQTTv5: try: @@ -466,8 +635,8 @@ properties = userProperties["connect"][:] self.__disconnectUserProperties = ( userProperties["connect"][:] - if userProperties["use_connect"] else - userProperties["disconnect"][:] + if userProperties["use_connect"] + else userProperties["disconnect"][:] ) except KeyError: properties = None @@ -475,22 +644,29 @@ properties = None # step 4: connect to server self.__cleanSession = parametersDict["CleanSession"] - self.connectToServer(host, port=port, - keepalive=parametersDict["Keepalive"], - properties=properties, - clearWill=clearWill) + self.connectToServer( + host, + port=port, + keepalive=parametersDict["Keepalive"], + properties=properties, + clearWill=clearWill, + ) else: keepalive = self.defaultConnectionOptions()["Keepalive"] - self.connectToServer(host, port=port, keepalive=keepalive, - bindAddress=bindAddress, - clearWill=clearWill) - + self.connectToServer( + host, + port=port, + keepalive=keepalive, + bindAddress=bindAddress, + clearWill=clearWill, + ) + @classmethod def defaultConnectionOptions(cls): """ Class method to get a connection options dictionary with default values. - + @return dictionary containing the default connection options. It has the keys "ClientId", "Protocol", "ConnectionTimeout", "Keepalive", "CleanSession", "Username", "Password", "WillTopic", "WillMessage", @@ -499,7 +675,7 @@ @rtype dict """ from PluginMqttMonitor import mqttPluginObject - + return { "ClientId": "ERIC7_MQTT_MONITOR_CLIENT", "Protocol": mqttPluginObject.getPreferences("DefaultProtocol"), @@ -523,36 +699,37 @@ "use_connect": True, }, } - + def reconnectToServer(self): """ Public method to reconnect the client with the same parameters. """ self.__connectTimeoutTimer.start() - + self.__mqttClient.reconnect() - + if not self.__loopStarted: self.startLoop() - + def disconnectFromServer(self): """ Public method to disconnect the client from the remote broker. """ self.__connectTimeoutTimer.stop() - + props = ( self.__createPropertiesObject( - PacketTypes.DISCONNECT, self.__disconnectUserProperties) - if self.__disconnectUserProperties else - None + PacketTypes.DISCONNECT, self.__disconnectUserProperties + ) + if self.__disconnectUserProperties + else None ) self.__mqttClient.disconnect(properties=props) - + def subscribe(self, topic, qos=0, properties=None): """ Public method to subscribe to topics with quality of service. - + @param topic single topic to subscribe to or a tuple with a topic and a QoS or a list of tuples with a topic and a QoS each @type str or tuple of (str, int) or list of tuple of (str, int) @@ -566,15 +743,15 @@ """ props = ( self.__createPropertiesObject(PacketTypes.SUBSCRIBE, properties) - if properties else - None + if properties + else None ) return self.__mqttClient.subscribe(topic, qos=qos, properties=props) - + def unsubscribe(self, topic, properties=None): """ Public method to unsubscribe topics. - + @param topic topic or list of topics to unsubscribe @type str or list of str @param properties list of user properties to be sent with the @@ -585,16 +762,15 @@ """ props = ( self.__createPropertiesObject(PacketTypes.UNSUBSCRIBE, properties) - if properties else - None + if properties + else None ) return self.__mqttClient.unsubscribe(topic, properties=props) - - def publish(self, topic, payload=None, qos=0, retain=False, - properties=None): + + def publish(self, topic, payload=None, qos=0, retain=False, properties=None): """ Public method to publish to a topic. - + @param topic topic to publish to @type str @param payload data to be published @@ -612,16 +788,17 @@ """ props = ( self.__createPropertiesObject(PacketTypes.PUBLISH, properties) - if properties else - None + if properties + else None ) - return self.__mqttClient.publish(topic, payload=payload, qos=qos, - retain=retain, properties=props) - + return self.__mqttClient.publish( + topic, payload=payload, qos=qos, retain=retain, properties=props + ) + def __createPropertiesObject(self, packetType, properties): """ Private method to assemble the MQTT v5 properties object. - + @param packetType type of the MQTT packet @type PacketTypes (= int) @param properties list of user properties @@ -637,127 +814,105 @@ def mqttConnackMessage(connackCode): """ Module function 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", - "Connection Accepted.") + return QCoreApplication.translate("MqttConnackMessage", "Connection Accepted.") elif connackCode == mqtt.CONNACK_REFUSED_PROTOCOL_VERSION: return QCoreApplication.translate( - "MqttConnackMessage", - "Connection Refused: unacceptable protocol version.") + "MqttConnackMessage", "Connection Refused: unacceptable protocol version." + ) elif connackCode == mqtt.CONNACK_REFUSED_IDENTIFIER_REJECTED: return QCoreApplication.translate( - "MqttConnackMessage", - "Connection Refused: identifier rejected.") + "MqttConnackMessage", "Connection Refused: identifier rejected." + ) elif connackCode == mqtt.CONNACK_REFUSED_SERVER_UNAVAILABLE: return QCoreApplication.translate( - "MqttConnackMessage", - "Connection Refused: broker unavailable.") + "MqttConnackMessage", "Connection Refused: broker unavailable." + ) elif connackCode == mqtt.CONNACK_REFUSED_BAD_USERNAME_PASSWORD: return QCoreApplication.translate( - "MqttConnackMessage", - "Connection Refused: bad user name or password.") + "MqttConnackMessage", "Connection Refused: bad user name or password." + ) elif connackCode == mqtt.CONNACK_REFUSED_NOT_AUTHORIZED: return QCoreApplication.translate( - "MqttConnackMessage", - "Connection Refused: not authorised.") + "MqttConnackMessage", "Connection Refused: not authorised." + ) else: return QCoreApplication.translate( - "MqttConnackMessage", - "Connection Refused: unknown reason.") + "MqttConnackMessage", "Connection Refused: unknown reason." + ) def mqttErrorMessage(mqttErrno): """ Module function 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", - "No error.") + return QCoreApplication.translate("MqttErrorMessage", "No error.") elif mqttErrno == mqtt.MQTT_ERR_NOMEM: - return QCoreApplication.translate( - "MqttErrorMessage", - "Out of memory.") + return QCoreApplication.translate("MqttErrorMessage", "Out of memory.") elif mqttErrno == mqtt.MQTT_ERR_PROTOCOL: return QCoreApplication.translate( "MqttErrorMessage", - "A network protocol error occurred when communicating with" - " the broker.") + "A network protocol error occurred when communicating with" " the broker.", + ) elif mqttErrno == mqtt.MQTT_ERR_INVAL: return QCoreApplication.translate( - "MqttErrorMessage", - "Invalid function arguments provided.") + "MqttErrorMessage", "Invalid function arguments provided." + ) elif mqttErrno == mqtt.MQTT_ERR_NO_CONN: return QCoreApplication.translate( - "MqttErrorMessage", - "The client is not currently connected.") + "MqttErrorMessage", "The client is not currently connected." + ) elif mqttErrno == mqtt.MQTT_ERR_CONN_REFUSED: return QCoreApplication.translate( - "MqttErrorMessage", - "The connection was refused.") + "MqttErrorMessage", "The connection was refused." + ) elif mqttErrno == mqtt.MQTT_ERR_NOT_FOUND: return QCoreApplication.translate( - "MqttErrorMessage", - "Message not found (internal error).") + "MqttErrorMessage", "Message not found (internal error)." + ) elif mqttErrno == mqtt.MQTT_ERR_CONN_LOST: return QCoreApplication.translate( - "MqttErrorMessage", - "The connection was lost.") + "MqttErrorMessage", "The connection was lost." + ) elif mqttErrno == mqtt.MQTT_ERR_TLS: - return QCoreApplication.translate( - "MqttErrorMessage", - "A TLS error occurred.") + return QCoreApplication.translate("MqttErrorMessage", "A TLS error occurred.") elif mqttErrno == mqtt.MQTT_ERR_PAYLOAD_SIZE: - return QCoreApplication.translate( - "MqttErrorMessage", - "Payload too large.") + return QCoreApplication.translate("MqttErrorMessage", "Payload too large.") elif mqttErrno == mqtt.MQTT_ERR_NOT_SUPPORTED: return QCoreApplication.translate( - "MqttErrorMessage", - "This feature is not supported.") + "MqttErrorMessage", "This feature is not supported." + ) elif mqttErrno == mqtt.MQTT_ERR_AUTH: - return QCoreApplication.translate( - "MqttErrorMessage", - "Authorisation failed.") + return QCoreApplication.translate("MqttErrorMessage", "Authorisation failed.") elif mqttErrno == mqtt.MQTT_ERR_ACL_DENIED: - return QCoreApplication.translate( - "MqttErrorMessage", - "Access denied by ACL.") + return QCoreApplication.translate("MqttErrorMessage", "Access denied by ACL.") elif mqttErrno == mqtt.MQTT_ERR_UNKNOWN: - return QCoreApplication.translate( - "MqttErrorMessage", - "Unknown error.") + return QCoreApplication.translate("MqttErrorMessage", "Unknown error.") elif mqttErrno == mqtt.MQTT_ERR_ERRNO: - return QCoreApplication.translate( - "MqttErrorMessage", - "Error defined by errno.") + return QCoreApplication.translate("MqttErrorMessage", "Error defined by errno.") elif mqttErrno == mqtt.MQTT_ERR_QUEUE_SIZE: - return QCoreApplication.translate( - "MqttErrorMessage", - "Message queue full.") + return QCoreApplication.translate("MqttErrorMessage", "Message queue full.") else: - return QCoreApplication.translate( - "MqttErrorMessage", - "Unknown error.") + return QCoreApplication.translate("MqttErrorMessage", "Unknown error.") def mqttLogLevelString(mqttLogLevel, isMqttLogLevel=True): """ Module function to get the log level string associated with a log level. - + @param mqttLogLevel log level of the paho-mqtt client @type int @param isMqttLogLevel flag indicating a MQTT log level is given (if @@ -773,7 +928,7 @@ return QCoreApplication.translate("MqttLogLevelString", "Unknown") else: logLevel = mqttLogLevel - + if logLevel == MqttClient.LogInfo: return QCoreApplication.translate("MqttLogLevelString", "Info") elif logLevel == MqttClient.LogNotice: @@ -785,7 +940,6 @@ elif logLevel == MqttClient.LogDebug: return QCoreApplication.translate("MqttLogLevelString", "Debug") elif logLevel == MqttClient.LogDisabled: - return QCoreApplication.translate("MqttLogLevelString", - "Logging Disabled") + return QCoreApplication.translate("MqttLogLevelString", "Logging Disabled") else: return QCoreApplication.translate("MqttLogLevelString", "Unknown")
--- a/MqttMonitor/MqttConnectionOptionsDialog.py Thu Dec 30 16:36:40 2021 +0100 +++ b/MqttMonitor/MqttConnectionOptionsDialog.py Wed Sep 21 09:42:33 2022 +0200 @@ -28,10 +28,11 @@ """ Class implementing a dialog to enter MQTT connection options. """ + def __init__(self, options=None, parent=None): """ Constructor - + @param options dictionary containing the connection options to populate the dialog with. It must have the keys "ClientId", "Protocol", "ConnectionTimeout", "Keepalive", "CleanSession", @@ -44,96 +45,93 @@ """ super().__init__(parent) self.setupUi(self) - + self.tlsCertsFilePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) self.tlsCertsFilePicker.setFilters( - self.tr("Certificate Files (*.crt *.pem);;All Files (*)")) - - self.willPropertiesButton.setIcon( - UI.PixmapCache.getIcon("listSelection")) - + self.tr("Certificate Files (*.crt *.pem);;All Files (*)") + ) + + self.willPropertiesButton.setIcon(UI.PixmapCache.getIcon("listSelection")) + self.optionsWidget.setCurrentIndex(0) - + # initialize MQTTv5 related stuff self.on_mqttv5Button_toggled(False) - + self.__populateDefaults(options=options) - + self.connectPropertiesButton.clicked[bool].connect( - self.__propertiesTypeSelected) + self.__propertiesTypeSelected + ) self.disconnectPropertiesButton.clicked[bool].connect( - self.__propertiesTypeSelected) - + self.__propertiesTypeSelected + ) + self.__updateOkButton() - + def __updateOkButton(self): """ Private method to update the enabled state of the OK button. """ - if ( - self.clientIdEdit.text() == "" and - not self.cleanSessionCheckBox.isChecked() - ): + if self.clientIdEdit.text() == "" and not self.cleanSessionCheckBox.isChecked(): enable = False EricMessageBox.critical( self, self.tr("Invalid Connection Parameters"), - self.tr("""An empty Client ID requires a clean session.""")) + self.tr("""An empty Client ID requires a clean session."""), + ) else: enable = True - - self.buttonBox.button( - QDialogButtonBox.StandardButton.Ok).setEnabled(enable) - + + self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(enable) + @pyqtSlot() def on_generateIdButton_clicked(self): """ Private slot to generate a client ID. """ uuid = QUuid.createUuid() - self.clientIdEdit.setText( - uuid.toString(QUuid.StringFormat.WithoutBraces)) - + self.clientIdEdit.setText(uuid.toString(QUuid.StringFormat.WithoutBraces)) + @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.__updateOkButton() - + @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.__updateOkButton() - + @pyqtSlot(bool) def on_mqttv5Button_toggled(self, checked): """ Private slot to handle the selection of the MQTT protocol. - + @param checked state of the button @type bool """ self.optionsWidget.setTabEnabled( - self.optionsWidget.indexOf(self.propertiesTab), - checked + self.optionsWidget.indexOf(self.propertiesTab), checked ) self.willPropertiesButton.setEnabled(checked) self.willPropertiesButton.setVisible(checked) - + @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 """ @@ -141,12 +139,12 @@ QDialogButtonBox.StandardButton.RestoreDefaults ): self.__populateDefaults(options=None) - + @pyqtSlot(bool) def on_samePropertiesCheckBox_toggled(self, checked): """ Private slot to handle a change of the properties usage. - + @param checked flag indicating to use the same user properties for CONNECT and DISCONNECT @type bool @@ -154,47 +152,46 @@ if checked and not self.connectPropertiesButton.isChecked(): self.connectPropertiesButton.click() self.disconnectPropertiesButton.setEnabled(not checked) - + @pyqtSlot(bool) def __propertiesTypeSelected(self, checked): """ Private slot to handle the switching of the user properties type. - + @param checked state of the buttons @type bool """ if checked: # handle the selection only if self.connectPropertiesButton.isChecked(): - self.__userProperties["disconnect"] = ( - self.propertiesWidget.getProperties()) - self.propertiesWidget.setProperties( - self.__userProperties["connect"]) + self.__userProperties[ + "disconnect" + ] = self.propertiesWidget.getProperties() + self.propertiesWidget.setProperties(self.__userProperties["connect"]) else: - self.__userProperties["connect"] = ( - self.propertiesWidget.getProperties()) - self.propertiesWidget.setProperties( - self.__userProperties["disconnect"]) - + self.__userProperties["connect"] = self.propertiesWidget.getProperties() + self.propertiesWidget.setProperties(self.__userProperties["disconnect"]) + @pyqtSlot() def on_willPropertiesButton_clicked(self): """ Private slot to edit the last will user properties. """ from .MqttUserPropertiesEditor import MqttUserPropertiesEditorDialog - + dlg = MqttUserPropertiesEditorDialog( - self.tr("Last Will User Properties"), self.__willProperties, self) + self.tr("Last Will User Properties"), self.__willProperties, self + ) if dlg.exec() == QDialog.DialogCode.Accepted: self.__willProperties = dlg.getProperties() - + 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. - + @param options dictionary containing the connection options to populate the dialog with. It must have the keys "ClientId", "Protocol", "ConnectionTimeout", "Keepalive", "CleanSession", "Username", @@ -204,60 +201,54 @@ """ if options is None: options = MqttClient.defaultConnectionOptions() - + # general self.clientIdEdit.setText(options["ClientId"]) - self.mqttv31Button.setChecked( - options["Protocol"] == MqttProtocols.MQTTv31) - self.mqttv311Button.setChecked( - options["Protocol"] == MqttProtocols.MQTTv311) - self.mqttv5Button.setChecked( - options["Protocol"] == MqttProtocols.MQTTv5) + self.mqttv31Button.setChecked(options["Protocol"] == MqttProtocols.MQTTv31) + self.mqttv311Button.setChecked(options["Protocol"] == MqttProtocols.MQTTv311) + self.mqttv5Button.setChecked(options["Protocol"] == MqttProtocols.MQTTv5) self.connectionTimeoutSpinBox.setValue(options["ConnectionTimeout"]) self.keepaliveSpinBox.setValue(options["Keepalive"]) self.cleanSessionCheckBox.setChecked(options["CleanSession"]) - + # user credentials self.usernameEdit.setText(options["Username"]) self.passwordEdit.setText(pwConvert(options["Password"], encode=False)) - + # last will and testament self.willQosSpinBox.setValue(options["WillQos"]) self.willRetainCheckBox.setChecked(options["WillRetain"]) self.willTopicEdit.setText(options["WillTopic"]) self.willMessageEdit.setPlainText(options["WillMessage"]) - self.__willProperties = copy.deepcopy( - options.get("WillProperties", [])) - + self.__willProperties = copy.deepcopy(options.get("WillProperties", [])) + # TLS parameters self.tlsEnableCheckBox.setChecked(options["TlsEnable"]) self.tlsCertsFilePicker.setText(options["TlsCaCert"]) - + # user properties - self.__userProperties = copy.deepcopy( - options.get("UserProperties", {})) + self.__userProperties = copy.deepcopy(options.get("UserProperties", {})) if not self.__userProperties: self.__userProperties = { "connect": [], "disconnect": [], "use_connect": True, } - + if options["Protocol"] == MqttProtocols.MQTTv5: self.connectPropertiesButton.setChecked(True) - self.propertiesWidget.setProperties( - self.__userProperties["connect"]) - self.samePropertiesCheckBox.setChecked( - self.__userProperties["use_connect"]) + self.propertiesWidget.setProperties(self.__userProperties["connect"]) + self.samePropertiesCheckBox.setChecked(self.__userProperties["use_connect"]) self.disconnectPropertiesButton.setEnabled( - not self.__userProperties["use_connect"]) + not self.__userProperties["use_connect"] + ) else: self.propertiesWidget.clear() - + def getConnectionOptions(self): """ Public method get the entered connection options. - + @return dictionary containing the connection options. It has the keys "ClientId", "Protocol", "ConnectionTimeout", "Keepalive", "CleanSession", "Username", "Password", "WillTopic", "WillMessage", @@ -274,20 +265,21 @@ else: # should never happen protocol = MqttProtocols.MQTTv311 - + if protocol == MqttProtocols.MQTTv5: if self.connectPropertiesButton.isChecked(): - self.__userProperties["connect"] = ( - self.propertiesWidget.getProperties()) + self.__userProperties["connect"] = self.propertiesWidget.getProperties() else: - self.__userProperties["disconnect"] = ( - self.propertiesWidget.getProperties()) - self.__userProperties["use_connect"] = ( - self.samePropertiesCheckBox.isChecked()) + self.__userProperties[ + "disconnect" + ] = self.propertiesWidget.getProperties() + self.__userProperties[ + "use_connect" + ] = self.samePropertiesCheckBox.isChecked() else: self.__userProperties = {} self.__willProperties = [] - + return { "ClientId": self.clientIdEdit.text(), "Protocol": protocol,
--- a/MqttMonitor/MqttConnectionProfilesDialog.py Thu Dec 30 16:36:40 2021 +0100 +++ b/MqttMonitor/MqttConnectionProfilesDialog.py Wed Sep 21 09:42:33 2022 +0200 @@ -12,8 +12,12 @@ from PyQt6.QtCore import pyqtSlot, Qt, QUuid from PyQt6.QtWidgets import ( - QDialog, QDialogButtonBox, QAbstractButton, QListWidgetItem, QInputDialog, - QLineEdit + QDialog, + QDialogButtonBox, + QAbstractButton, + QListWidgetItem, + QInputDialog, + QLineEdit, ) from EricWidgets import EricMessageBox @@ -32,10 +36,11 @@ """ Class implementing a dialog to edit the MQTT connection profiles. """ + def __init__(self, profiles, currentProfile="", parent=None): """ Constructor - + @param profiles dictionary containing dictionaries containing the connection parameters. Each entry must have the keys "BrokerAddress", "BrokerPort", "ClientId", "Protocol", @@ -51,66 +56,72 @@ """ super().__init__(parent) self.setupUi(self) - + self.__profiles = collections.defaultdict(self.__defaultProfile) self.__profiles.update(copy.deepcopy(profiles)) self.__profilesChanged = False - + self.plusButton.setIcon(UI.PixmapCache.getIcon("plus")) self.copyButton.setIcon(UI.PixmapCache.getIcon("editCopy")) self.minusButton.setIcon(UI.PixmapCache.getIcon("minus")) self.showPasswordButton.setIcon(UI.PixmapCache.getIcon("showPassword")) - self.willPropertiesButton.setIcon( - UI.PixmapCache.getIcon("listSelection")) - + self.willPropertiesButton.setIcon(UI.PixmapCache.getIcon("listSelection")) + self.tlsCertsFilePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) self.tlsCertsFilePicker.setFilters( - self.tr("Certificate Files (*.crt *.pem);;All Files (*)")) - self.tlsSelfSignedCertsFilePicker.setMode( - EricPathPickerModes.OPEN_FILE_MODE) + self.tr("Certificate Files (*.crt *.pem);;All Files (*)") + ) + self.tlsSelfSignedCertsFilePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) self.tlsSelfSignedCertsFilePicker.setFilters( - self.tr("Certificate Files (*.crt *.pem);;All Files (*)")) + self.tr("Certificate Files (*.crt *.pem);;All Files (*)") + ) self.tlsSelfSignedClientCertFilePicker.setMode( - EricPathPickerModes.OPEN_FILE_MODE) + EricPathPickerModes.OPEN_FILE_MODE + ) self.tlsSelfSignedClientCertFilePicker.setFilters( - self.tr("Certificate Files (*.crt *.pem);;All Files (*)")) + self.tr("Certificate Files (*.crt *.pem);;All Files (*)") + ) self.tlsSelfSignedClientKeyFilePicker.setMode( - EricPathPickerModes.OPEN_FILE_MODE) + EricPathPickerModes.OPEN_FILE_MODE + ) self.tlsSelfSignedClientKeyFilePicker.setFilters( - self.tr("Key Files (*.key *.pem);;All Files (*)")) - + self.tr("Key Files (*.key *.pem);;All Files (*)") + ) + self.profileTabWidget.setCurrentIndex(0) - + self.connectPropertiesButton.clicked[bool].connect( - self.__propertiesTypeSelected) + self.__propertiesTypeSelected + ) self.disconnectPropertiesButton.clicked[bool].connect( - self.__propertiesTypeSelected) - + self.__propertiesTypeSelected + ) + if len(self.__profiles) == 0: self.minusButton.setEnabled(False) self.copyButton.setEnabled(False) - + self.profileFrame.setEnabled(False) self.__populatingProfile = False self.__deletingProfile = False - + self.__populateProfilesList(currentProfile=currentProfile) - + @pyqtSlot(str) def on_profileEdit_textChanged(self, name): """ Private slot to handle changes of the profile name. - + @param name name of the profile @type str """ self.__updateApplyButton() - + @pyqtSlot(QAbstractButton) def on_profileButtonBox_clicked(self, button): """ Private slot handling presses of the profile buttons. - + @param button reference to the pressed button @type QAbstractButton """ @@ -119,22 +130,22 @@ ): currentProfile = self.__applyProfile() self.__populateProfilesList(currentProfile=currentProfile) - + elif button == self.profileButtonBox.button( QDialogButtonBox.StandardButton.Reset ): self.__resetProfile() - + elif button == self.profileButtonBox.button( - QDialogButtonBox.StandardButton.RestoreDefaults + QDialogButtonBox.StandardButton.RestoreDefaults ): self.__populateProfileDefault() - + @pyqtSlot(QListWidgetItem, QListWidgetItem) def on_profilesList_currentItemChanged(self, current, previous): """ Private slot to handle a change of the current profile. - + @param current new current item @type QListWidgetItem @param previous previous current item @@ -142,29 +153,32 @@ """ self.minusButton.setEnabled(current is not None) self.copyButton.setEnabled(current is not None) - + if ( - current is not previous and - not self.__deletingProfile and - self.__isChangedProfile() + current is not previous + and not self.__deletingProfile + and self.__isChangedProfile() ): # modified profile belongs to previous yes = EricMessageBox.yesNo( self, self.tr("Changed Connection Profile"), - self.tr("""The current profile has unsaved changes.""" - """ Shall these be saved?"""), + self.tr( + """The current profile has unsaved changes.""" + """ Shall these be saved?""" + ), icon=EricMessageBox.Warning, - yesDefault=True) + yesDefault=True, + ) if yes: self.__applyProfile() - + if current: profileName = current.text() self.__populateProfile(profileName) else: self.__clearProfile() - + @pyqtSlot() def on_plusButton_clicked(self): """ @@ -174,21 +188,23 @@ self, self.tr("New Connection Profile"), self.tr("Enter name for the new Connection Profile:"), - QLineEdit.EchoMode.Normal) + QLineEdit.EchoMode.Normal, + ) if ok and bool(profileName): if profileName in self.__profiles: EricMessageBox.warning( self, self.tr("New Connection Profile"), - self.tr("""<p>A connection named <b>{0}</b> exists""" - """ already. Aborting...</p>""").format( - profileName)) + self.tr( + """<p>A connection named <b>{0}</b> exists""" + """ already. Aborting...</p>""" + ).format(profileName), + ) else: itm = QListWidgetItem(profileName, self.profilesList) self.profilesList.setCurrentItem(itm) - self.brokerAddressEdit.setFocus( - Qt.FocusReason.OtherFocusReason) - + self.brokerAddressEdit.setFocus(Qt.FocusReason.OtherFocusReason) + @pyqtSlot() def on_copyButton_clicked(self): """ @@ -201,27 +217,30 @@ self, self.tr("Copy Connection Profile"), self.tr("Enter name for the copied Connection Profile:"), - QLineEdit.EchoMode.Normal) + QLineEdit.EchoMode.Normal, + ) if ok and bool(newProfileName): if newProfileName in self.__profiles: EricMessageBox.warning( self, self.tr("Copy Connection Profile"), - self.tr("""<p>A connection named <b>{0}</b> exists""" - """ already. Aborting...</p>""").format( - newProfileName)) + self.tr( + """<p>A connection named <b>{0}</b> exists""" + """ already. Aborting...</p>""" + ).format(newProfileName), + ) else: connectionProfile = self.__defaultProfile() connectionProfile.update( - copy.deepcopy(self.__profiles[profileName])) + copy.deepcopy(self.__profiles[profileName]) + ) self.__profiles[newProfileName] = connectionProfile self.__profilesChanged = True - + itm = QListWidgetItem(newProfileName, self.profilesList) self.profilesList.setCurrentItem(itm) - self.brokerAddressEdit.setFocus( - Qt.FocusReason.OtherFocusReason) - + self.brokerAddressEdit.setFocus(Qt.FocusReason.OtherFocusReason) + @pyqtSlot() def on_minusButton_clicked(self): """ @@ -233,8 +252,10 @@ yes = EricMessageBox.yesNo( self, self.tr("Delete Connection Profile"), - self.tr("""<p>Shall the Connection Profile <b>{0}</b>""" - """ really be deleted?</p>""").format(profileName) + self.tr( + """<p>Shall the Connection Profile <b>{0}</b>""" + """ really be deleted?</p>""" + ).format(profileName), ) if yes: self.__deletingProfile = True @@ -242,13 +263,13 @@ self.__profilesChanged = True self.__populateProfilesList() self.__deletingProfile = False - + self.profilesList.setFocus(Qt.FocusReason.OtherFocusReason) - + def getProfiles(self): """ Public method to return a dictionary of profiles. - + @return dictionary containing dictionaries containing the defined connection profiles. Each entry has the keys "BrokerAddress", "BrokerPort", "ClientId", "Protocol", "ConnectionTimeout", @@ -261,11 +282,11 @@ profilesDict = {} profilesDict.update(copy.deepcopy(dict(self.__profiles))) return profilesDict - + def __applyProfile(self): """ Private method to apply the entered data to the list of profiles. - + @return name of the applied profile @rtype str """ @@ -278,20 +299,21 @@ else: # should never happen protocol = MqttProtocols.MQTTv311 - + if protocol == MqttProtocols.MQTTv5: if self.connectPropertiesButton.isChecked(): - self.__userProperties["connect"] = ( - self.propertiesWidget.getProperties()) + self.__userProperties["connect"] = self.propertiesWidget.getProperties() else: - self.__userProperties["disconnect"] = ( - self.propertiesWidget.getProperties()) - self.__userProperties["use_connect"] = ( - self.samePropertiesCheckBox.isChecked()) + self.__userProperties[ + "disconnect" + ] = self.propertiesWidget.getProperties() + self.__userProperties[ + "use_connect" + ] = self.samePropertiesCheckBox.isChecked() else: self.__userProperties = {} self.__willProperties = [] - + profileName = self.profileEdit.text() connectionProfile = { "BrokerAddress": self.brokerAddressEdit.text(), @@ -318,22 +340,25 @@ if self.tlsCertsFileButton.isChecked(): connectionProfile["TlsCaCert"] = self.tlsCertsFilePicker.text() elif self.tlsSelfSignedCertsButton.isChecked(): - connectionProfile["TlsCaCert"] = ( - self.tlsSelfSignedCertsFilePicker.text()) - connectionProfile["TlsClientCert"] = ( - self.tlsSelfSignedClientCertFilePicker.text()) - connectionProfile["TlsClientKey"] = ( - self.tlsSelfSignedClientKeyFilePicker.text()) - + connectionProfile[ + "TlsCaCert" + ] = self.tlsSelfSignedCertsFilePicker.text() + connectionProfile[ + "TlsClientCert" + ] = self.tlsSelfSignedClientCertFilePicker.text() + connectionProfile[ + "TlsClientKey" + ] = self.tlsSelfSignedClientKeyFilePicker.text() + self.__profiles[profileName] = connectionProfile self.__profilesChanged = True - + return profileName - + def __defaultProfile(self): """ Private method to populate non-existing profile items. - + @return default dictionary entry @rtype dict """ @@ -343,13 +368,13 @@ defaultProfile["BrokerPort"] = 8883 else: defaultProfile["BrokerPort"] = 1883 - + return defaultProfile - + def __populateProfilesList(self, currentProfile=""): """ Private method to populate the list of defined profiles. - + @param currentProfile name of the current profile @type str """ @@ -357,111 +382,114 @@ currentItem = self.profilesList.currentItem() if currentItem: currentProfile = currentItem.text() - + self.profilesList.clear() self.profilesList.addItems(sorted(self.__profiles.keys())) - + if currentProfile: items = self.profilesList.findItems( - currentProfile, Qt.MatchFlag.MatchExactly) + currentProfile, Qt.MatchFlag.MatchExactly + ) if items: self.profilesList.setCurrentItem(items[0]) - + if len(self.__profiles) == 0: self.profileFrame.setEnabled(False) - + def __populateProfile(self, profileName): """ Private method to populate the profile data entry fields. - + @param profileName name of the profile to get data from @type str """ connectionProfile = self.__defaultProfile() if profileName: connectionProfile.update(self.__profiles[profileName]) - + self.__populatingProfile = True if profileName is not None: self.profileEdit.setText(profileName) self.brokerAddressEdit.setText(connectionProfile["BrokerAddress"]) self.brokerPortSpinBox.setValue(connectionProfile["BrokerPort"]) self.clientIdEdit.setText(connectionProfile["ClientId"]) - + # general tab self.mqttv31Button.setChecked( - connectionProfile["Protocol"] == MqttProtocols.MQTTv31) + connectionProfile["Protocol"] == MqttProtocols.MQTTv31 + ) self.mqttv311Button.setChecked( - connectionProfile["Protocol"] == MqttProtocols.MQTTv311) + connectionProfile["Protocol"] == MqttProtocols.MQTTv311 + ) self.mqttv5Button.setChecked( - connectionProfile["Protocol"] == MqttProtocols.MQTTv5) + connectionProfile["Protocol"] == MqttProtocols.MQTTv5 + ) self.on_mqttv5Button_toggled(self.mqttv5Button.isChecked()) - self.connectionTimeoutSpinBox.setValue( - connectionProfile["ConnectionTimeout"]) + self.connectionTimeoutSpinBox.setValue(connectionProfile["ConnectionTimeout"]) self.keepaliveSpinBox.setValue(connectionProfile["Keepalive"]) self.cleanSessionCheckBox.setChecked(connectionProfile["CleanSession"]) - + # user credentials tab self.usernameEdit.setText(connectionProfile["Username"]) self.passwordEdit.setText( - pwConvert(connectionProfile["Password"], encode=False)) - + pwConvert(connectionProfile["Password"], encode=False) + ) + # will tab self.willTopicEdit.setText(connectionProfile["WillTopic"]) self.willMessageEdit.setPlainText(connectionProfile["WillMessage"]) self.willQosSpinBox.setValue(connectionProfile["WillQos"]) self.willRetainCheckBox.setChecked(connectionProfile["WillRetain"]) self.__willProperties = copy.deepcopy( - connectionProfile.get("WillProperties", [])) - + connectionProfile.get("WillProperties", []) + ) + # SSL/TLS tab self.tlsGroupBox.setChecked(connectionProfile["TlsEnable"]) - if ( - connectionProfile["TlsCaCert"] and - connectionProfile["TlsClientCert"] - ): + if connectionProfile["TlsCaCert"] and connectionProfile["TlsClientCert"]: self.tlsSelfSignedCertsButton.setChecked(True) - self.tlsSelfSignedCertsFilePicker.setText( - connectionProfile["TlsCaCert"]) + self.tlsSelfSignedCertsFilePicker.setText(connectionProfile["TlsCaCert"]) self.tlsSelfSignedClientCertFilePicker.setText( - connectionProfile["TlsClientCert"]) + connectionProfile["TlsClientCert"] + ) self.tlsSelfSignedClientKeyFilePicker.setText( - connectionProfile["TlsClientKey"]) + connectionProfile["TlsClientKey"] + ) elif connectionProfile["TlsCaCert"]: self.tlsCertsFileButton.setChecked(True) self.tlsCertsFilePicker.setText(connectionProfile["TlsCaCert"]) else: self.tlsDefaultCertsButton.setChecked(True) - + # user properties tab self.__userProperties = copy.deepcopy( - connectionProfile.get("UserProperties", {})) + connectionProfile.get("UserProperties", {}) + ) if not self.__userProperties: self.__userProperties = { "connect": [], "disconnect": [], "use_connect": True, } - + if connectionProfile["Protocol"] == MqttProtocols.MQTTv5: self.connectPropertiesButton.setChecked(True) - self.propertiesWidget.setProperties( - self.__userProperties["connect"]) - self.samePropertiesCheckBox.setChecked( - self.__userProperties["use_connect"]) + self.propertiesWidget.setProperties(self.__userProperties["connect"]) + self.samePropertiesCheckBox.setChecked(self.__userProperties["use_connect"]) self.disconnectPropertiesButton.setEnabled( - not self.__userProperties["use_connect"]) + not self.__userProperties["use_connect"] + ) else: self.propertiesWidget.clear() - + self.__populatingProfile = False - + self.showPasswordButton.setChecked(False) self.profileFrame.setEnabled(True) self.__updateApplyButton() - + self.profileTabWidget.setCurrentIndex(0) - + def __clearProfile(self): """ Private method to clear the profile data entry fields. @@ -489,7 +517,7 @@ self.tlsSelfSignedCertsFilePicker.setText("") self.tlsSelfSignedClientCertFilePicker.setText("") self.tlsSelfSignedClientKeyFilePicker.setText("") - + self.__userProperties = { "connect": [], "disconnect": [], @@ -499,13 +527,13 @@ self.samePropertiesCheckBox.setChecked(True) self.connectPropertiesButton.setChecked(True) self.disconnectPropertiesButton.setEnabled(False) - + self.__populatingProfile = False - + self.showPasswordButton.setChecked(False) self.profileFrame.setEnabled(False) self.__updateApplyButton() - + def __resetProfile(self): """ Private method to reset the profile data entry fields to their stored @@ -514,26 +542,26 @@ 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 @rtype bool """ profileName = self.profileEdit.text() if profileName == "": return False - + elif profileName in self.__profiles: if self.mqttv31Button.isChecked(): protocol = MqttProtocols.MQTTv31 @@ -544,98 +572,97 @@ else: # should never happen protocol = MqttProtocols.MQTTv311 - + connectionProfile = self.__defaultProfile() connectionProfile.update(self.__profiles[profileName]) changed = ( - self.brokerAddressEdit.text() != - connectionProfile["BrokerAddress"] or - self.brokerPortSpinBox.value() != - connectionProfile["BrokerPort"] or - self.clientIdEdit.text() != connectionProfile["ClientId"] or - protocol != connectionProfile["Protocol"] or - self.connectionTimeoutSpinBox.value() != - connectionProfile["ConnectionTimeout"] or - self.keepaliveSpinBox.value() != - connectionProfile["Keepalive"] or - self.cleanSessionCheckBox.isChecked() != - connectionProfile["CleanSession"] or - self.usernameEdit.text() != connectionProfile["Username"] or - self.passwordEdit.text() != - pwConvert(connectionProfile["Password"], encode=False) or - self.willTopicEdit.text() != connectionProfile["WillTopic"] or - self.willMessageEdit.toPlainText() != - connectionProfile["WillMessage"] or - self.willQosSpinBox.value() != connectionProfile["WillQos"] or - self.willRetainCheckBox.isChecked() != - connectionProfile["WillRetain"] or - self.tlsGroupBox.isChecked() != connectionProfile["TlsEnable"] + self.brokerAddressEdit.text() != connectionProfile["BrokerAddress"] + or self.brokerPortSpinBox.value() != connectionProfile["BrokerPort"] + or self.clientIdEdit.text() != connectionProfile["ClientId"] + or protocol != connectionProfile["Protocol"] + or self.connectionTimeoutSpinBox.value() + != connectionProfile["ConnectionTimeout"] + or self.keepaliveSpinBox.value() != connectionProfile["Keepalive"] + or self.cleanSessionCheckBox.isChecked() + != connectionProfile["CleanSession"] + or self.usernameEdit.text() != connectionProfile["Username"] + or self.passwordEdit.text() + != pwConvert(connectionProfile["Password"], encode=False) + or self.willTopicEdit.text() != connectionProfile["WillTopic"] + or self.willMessageEdit.toPlainText() + != connectionProfile["WillMessage"] + or self.willQosSpinBox.value() != connectionProfile["WillQos"] + or self.willRetainCheckBox.isChecked() + != connectionProfile["WillRetain"] + or self.tlsGroupBox.isChecked() != connectionProfile["TlsEnable"] ) # check will properties only, ig not yet changed if not changed and protocol == MqttProtocols.MQTTv5: - changed |= ( - sorted(self.__willProperties) != - sorted(connectionProfile["WillProperties"]) + changed |= sorted(self.__willProperties) != sorted( + connectionProfile["WillProperties"] ) # check TLS stuff only, if not yet changed if not changed: if self.tlsCertsFileButton.isChecked(): changed |= ( - self.tlsCertsFilePicker.text() != - connectionProfile["TlsCaCert"] + self.tlsCertsFilePicker.text() != connectionProfile["TlsCaCert"] ) elif self.tlsSelfSignedCertsButton.isChecked(): changed |= ( - self.tlsSelfSignedCertsFilePicker.text() != - connectionProfile["TlsCaCert"] or - self.tlsSelfSignedClientCertFilePicker.text() != - connectionProfile["TlsClientCert"] or - self.tlsSelfSignedClientKeyFilePicker.text() != - connectionProfile["TlsClientKey"] + self.tlsSelfSignedCertsFilePicker.text() + != connectionProfile["TlsCaCert"] + or self.tlsSelfSignedClientCertFilePicker.text() + != connectionProfile["TlsClientCert"] + or self.tlsSelfSignedClientKeyFilePicker.text() + != connectionProfile["TlsClientKey"] ) # check user properties only, if not yet changed if not changed and protocol == MqttProtocols.MQTTv5: - properties = { - "connect": self.propertiesWidget.getProperties(), - "disconnect": self.__userProperties["disconnect"], - } if self.connectPropertiesButton.isChecked() else { - "connect": self.__userProperties["connect"], - "disconnect": self.propertiesWidget.getProperties(), - } + properties = ( + { + "connect": self.propertiesWidget.getProperties(), + "disconnect": self.__userProperties["disconnect"], + } + if self.connectPropertiesButton.isChecked() + else { + "connect": self.__userProperties["connect"], + "disconnect": self.propertiesWidget.getProperties(), + } + ) changed |= ( - self.samePropertiesCheckBox.isChecked() != - connectionProfile["UserProperties"]["use_connect"] or - sorted(properties["connect"]) != - sorted(connectionProfile["UserProperties"]["connect"]) or - sorted(properties["disconnect"]) != - sorted(connectionProfile["UserProperties"]["disconnect"]) + self.samePropertiesCheckBox.isChecked() + != connectionProfile["UserProperties"]["use_connect"] + or sorted(properties["connect"]) + != sorted(connectionProfile["UserProperties"]["connect"]) + or sorted(properties["disconnect"]) + != sorted(connectionProfile["UserProperties"]["disconnect"]) ) - + return changed - + 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())) - + 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 and - self.clientIdEdit.text() == "" and - not self.cleanSessionCheckBox.isChecked() + not self.__populatingProfile + and self.clientIdEdit.text() == "" + and not self.cleanSessionCheckBox.isChecked() ): enable = False EricMessageBox.critical( self, self.tr("Invalid Connection Parameters"), - self.tr("An empty Client ID requires a clean session.")) - + self.tr("An empty Client ID requires a clean session."), + ) + if self.tlsGroupBox.isChecked(): if self.tlsCertsFileButton.isChecked(): # condition 3a: if CA certificates file shall be used, it must @@ -645,139 +672,139 @@ # condition 3b: if client certificates shall be used, all files # must be given enable &= ( - bool(self.tlsSelfSignedCertsFilePicker.text()) and - bool(self.tlsSelfSignedClientCertFilePicker.text()) and - bool(self.tlsSelfSignedClientKeyFilePicker.text()) + bool(self.tlsSelfSignedCertsFilePicker.text()) + and bool(self.tlsSelfSignedClientCertFilePicker.text()) + and bool(self.tlsSelfSignedClientKeyFilePicker.text()) ) - - self.profileButtonBox.button( - QDialogButtonBox.StandardButton.Apply).setEnabled(enable) - + + self.profileButtonBox.button(QDialogButtonBox.StandardButton.Apply).setEnabled( + enable + ) + @pyqtSlot(str) def on_brokerAddressEdit_textChanged(self, address): """ Private slot handling a change of the broker address. - + @param address broker address @type str """ self.__updateApplyButton() - + @pyqtSlot() def on_generateIdButton_clicked(self): """ Private slot to generate a client ID. """ uuid = QUuid.createUuid() - self.clientIdEdit.setText( - uuid.toString(QUuid.StringFormat.WithoutBraces)) - + self.clientIdEdit.setText(uuid.toString(QUuid.StringFormat.WithoutBraces)) + @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() - + @pyqtSlot(bool) def on_mqttv5Button_toggled(self, checked): """ Private slot to handle the selection of the MQTT protocol. - + @param checked state of the button @type bool """ self.profileTabWidget.setTabEnabled( - self.profileTabWidget.indexOf(self.propertiesTab), - checked + self.profileTabWidget.indexOf(self.propertiesTab), checked ) self.willPropertiesButton.setEnabled(checked) self.willPropertiesButton.setVisible(checked) - + @pyqtSlot(bool) def on_showPasswordButton_toggled(self, checked): """ Private slot to show or hide the password. - + @param checked flag indicating the button state @type bool """ ( self.passwordEdit.setEchoMode(QLineEdit.EchoMode.Normal) - if checked else - self.passwordEdit.setEchoMode(QLineEdit.EchoMode.Password) + if checked + else self.passwordEdit.setEchoMode(QLineEdit.EchoMode.Password) ) - + @pyqtSlot() def on_willPropertiesButton_clicked(self): """ Private slot to edit the last will user properties. """ from .MqttUserPropertiesEditor import MqttUserPropertiesEditorDialog - + dlg = MqttUserPropertiesEditorDialog( - self.tr("Last Will User Properties"), self.__willProperties, self) + self.tr("Last Will User Properties"), self.__willProperties, self + ) if dlg.exec() == QDialog.DialogCode.Accepted: self.__willProperties = dlg.getProperties() - + @pyqtSlot(str) def on_tlsCertsFilePicker_textChanged(self, path): """ Private slot handling a change of the TLS CA certificates file. - + @param path file path @type str """ self.__updateApplyButton() - + @pyqtSlot(str) def on_tlsSelfSignedCertsFilePicker_textChanged(self, path): """ Private slot handling a change of the TLS CA certificates file. - + @param path file path @type str """ self.__updateApplyButton() - + @pyqtSlot(str) def on_tlsSelfSignedClientCertFilePicker_textChanged(self, path): """ Private slot handling a change of the TLS client certificate file. - + @param path file path @type str """ self.__updateApplyButton() - + @pyqtSlot(str) def on_tlsSelfSignedClientKeyFilePicker_textChanged(self, path): """ Private slot handling a change of the TLS client key file. - + @param path file path @type str """ self.__updateApplyButton() - + @pyqtSlot(bool) def on_tlsGroupBox_toggled(self, checked): """ Private slot handling the selection of TLS mode. - + @param checked state of the selection @type bool """ @@ -790,9 +817,11 @@ """Encrypted connection using SSL/TLS has been enabled.""" """ However, the broker port is still the default""" """ unencrypted port (port 1883). Shall this be""" - """ changed?"""), + """ changed?""" + ), icon=EricMessageBox.Warning, - yesDefault=True) + yesDefault=True, + ) if yes: self.brokerPortSpinBox.setValue(8883) elif not checked and self.brokerPortSpinBox.value() == 8883: @@ -804,52 +833,54 @@ """Encrypted connection using SSL/TLS has been disabled.""" """ However, the broker port is still the default""" """ encrypted port (port 8883). Shall this be""" - """ changed?"""), + """ changed?""" + ), icon=EricMessageBox.Warning, - yesDefault=True) + yesDefault=True, + ) if yes: self.brokerPortSpinBox.setValue(1883) - + self.__updateApplyButton() - + @pyqtSlot(bool) def on_tlsDefaultCertsButton_toggled(self, checked): """ Private slot handling the selection of using the default certificates file. - + @param checked state of the selection @type bool """ self.__updateApplyButton() - + @pyqtSlot(bool) def on_tlsCertsFileButton_toggled(self, checked): """ Private slot handling the selection of using a non-default certificates file. - + @param checked state of the selection @type bool """ self.__updateApplyButton() - + @pyqtSlot(bool) def on_tlsSelfSignedCertsButton_toggled(self, checked): """ Private slot handling the selection of using self signed client certificate and key files. - + @param checked state of the selection @type bool """ self.__updateApplyButton() - + @pyqtSlot(bool) def on_samePropertiesCheckBox_toggled(self, checked): """ Private slot to handle a change of the properties usage. - + @param checked flag indicating to use the same user properties for CONNECT and DISCONNECT @type bool @@ -857,28 +888,26 @@ if checked and not self.connectPropertiesButton.isChecked(): self.connectPropertiesButton.click() self.disconnectPropertiesButton.setEnabled(not checked) - + @pyqtSlot(bool) def __propertiesTypeSelected(self, checked): """ Private slot to handle the switching of the user properties type. - + @param checked state of the buttons @type bool """ if checked: # handle the selection only if self.connectPropertiesButton.isChecked(): - self.__userProperties["disconnect"] = ( - self.propertiesWidget.getProperties()) - self.propertiesWidget.setProperties( - self.__userProperties["connect"]) + self.__userProperties[ + "disconnect" + ] = self.propertiesWidget.getProperties() + self.propertiesWidget.setProperties(self.__userProperties["connect"]) else: - self.__userProperties["connect"] = ( - self.propertiesWidget.getProperties()) - self.propertiesWidget.setProperties( - self.__userProperties["disconnect"]) - + self.__userProperties["connect"] = self.propertiesWidget.getProperties() + self.propertiesWidget.setProperties(self.__userProperties["disconnect"]) + @pyqtSlot() def reject(self): """ @@ -888,33 +917,35 @@ button = EricMessageBox.warning( self, self.tr("Changed Connection Profile"), - self.tr("""The current profile has unsaved changes. Shall""" - """ these be saved?"""), - EricMessageBox.Discard | + self.tr( + """The current profile has unsaved changes. Shall""" + """ these be saved?""" + ), + EricMessageBox.Discard | EricMessageBox.Save, EricMessageBox.Save, - EricMessageBox.Save) + ) if button == EricMessageBox.Save: self.__applyProfile() return - + if self.__profilesChanged: button = EricMessageBox.warning( self, self.tr("Changed Connection Profiles"), - self.tr("""The list of connection profiles has unsaved""" - """ changes."""), - EricMessageBox.Abort | - EricMessageBox.Discard | + self.tr( + """The list of connection profiles has unsaved""" """ changes.""" + ), + EricMessageBox.Abort | EricMessageBox.Discard | EricMessageBox.Save, EricMessageBox.Save, - EricMessageBox.Save) + ) if button == EricMessageBox.Save: super().accept() return elif button == EricMessageBox.Abort: return - + super().reject() - + @pyqtSlot() def accept(self): """ @@ -924,11 +955,14 @@ yes = EricMessageBox.yesNo( self, self.tr("Changed Connection Profile"), - self.tr("""The current profile has unsaved changes. Shall""" - """ these be saved?"""), + self.tr( + """The current profile has unsaved changes. Shall""" + """ these be saved?""" + ), icon=EricMessageBox.Warning, - yesDefault=True) + yesDefault=True, + ) if yes: self.__applyProfile() - + super().accept()
--- a/MqttMonitor/MqttMonitorWidget.py Thu Dec 30 16:36:40 2021 +0100 +++ b/MqttMonitor/MqttMonitorWidget.py Wed Sep 21 09:42:33 2022 +0200 @@ -23,7 +23,10 @@ from .Ui_MqttMonitorWidget import Ui_MqttMonitorWidget from .MqttClient import ( - MqttClient, mqttConnackMessage, mqttErrorMessage, mqttLogLevelString + MqttClient, + mqttConnackMessage, + mqttErrorMessage, + mqttLogLevelString, ) from .MqttReasonCodes import mqttReasonCode from .MqttProtocols import MqttProtocols @@ -36,14 +39,15 @@ """ Class implementing the MQTT Monitor widget. """ + BrokerStatusTopicPrefix = "$SYS/broker/" BrokerStatusTopic = "$SYS/broker/#" BrokerStatusTopicLoadPrefix = "$SYS/broker/load/" - + def __init__(self, plugin, usesDarkPalette, parent=None): """ Constructor - + @param plugin reference to the plug-in object @type MqttMonitorPlugin @param usesDarkPalette flag indicating the use of a dark application @@ -54,30 +58,34 @@ """ super().__init__(parent) self.setupUi(self) - + self.layout().setContentsMargins(0, 3, 0, 0) - + self.__plugin = plugin self.__iconSuffix = "dark" if usesDarkPalette else "light" - + self.__connectedToBroker = False self.__brokerStatusTopicSubscribed = False - + with contextlib.suppress(AttributeError): # backward compatibility if not ericApp().usesSmallScreen(): - self.pixmapLabel.setPixmap(UI.PixmapCache.getPixmap( - os.path.join("MqttMonitor", "icons", - "mqtt48-{0}".format(self.__iconSuffix)) - )) - - self.publishPayloadFilePicker.setMode( - EricPathPickerModes.OPEN_FILE_MODE) + self.pixmapLabel.setPixmap( + UI.PixmapCache.getPixmap( + os.path.join( + "MqttMonitor", + "icons", + "mqtt48-{0}".format(self.__iconSuffix), + ) + ) + ) + + self.publishPayloadFilePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) self.publishPayloadFilePicker.setFilters(self.tr("All Files (*)")) - + self.brokerComboBox.lineEdit().setClearButtonEnabled(True) self.publishTopicComboBox.lineEdit().setClearButtonEnabled(True) - + self.__messagesFormat = self.messagesEdit.currentCharFormat() self.__messagesTopicFormat = self.messagesEdit.currentCharFormat() self.__messagesTopicFormat.setFontWeight(QFont.Weight.Bold) @@ -85,30 +93,32 @@ self.__messagesQosFormat.setFontItalic(True) self.__messagesSubheaderFormat = self.messagesEdit.currentCharFormat() self.__messagesSubheaderFormat.setFontUnderline(True) - + self.__propertiesFormat = self.propertiesEdit.currentCharFormat() self.__propertiesTopicFormat = self.propertiesEdit.currentCharFormat() self.__propertiesTopicFormat.setFontWeight(QFont.Weight.Bold) self.__propertiesNameFormat = self.propertiesEdit.currentCharFormat() self.__propertiesNameFormat.setFontItalic(True) - + self.messagesSearchWidget.attachTextEdit(self.messagesEdit) self.messagesSearchWidget.setWidthForHeight(False) - + self.__isMessageAlternate = False self.__isPropertiesAlternate = False - - for logLevel in (MqttClient.LogDisabled, - MqttClient.LogDebug, - MqttClient.LogInfo, - MqttClient.LogNotice, - MqttClient.LogWarning, - MqttClient.LogError): - self.logLevelComboBox.addItem(mqttLogLevelString( - logLevel, isMqttLogLevel=False), logLevel) - self.logLevelComboBox.setCurrentIndex( - self.logLevelComboBox.count() - 1) - + + for logLevel in ( + MqttClient.LogDisabled, + MqttClient.LogDebug, + MqttClient.LogInfo, + MqttClient.LogNotice, + MqttClient.LogWarning, + MqttClient.LogError, + ): + self.logLevelComboBox.addItem( + mqttLogLevelString(logLevel, isMqttLogLevel=False), logLevel + ) + self.logLevelComboBox.setCurrentIndex(self.logLevelComboBox.count() - 1) + if usesDarkPalette: self.__logMessagesBackgrounds = { MqttClient.LogDebug: QBrush(QColor("#2f2f2f")), @@ -129,61 +139,65 @@ MqttClient.LogDisabled: QBrush(Qt.GlobalColor.magenta) # reuse LogDisabled for unknown log levels } - + self.logSearchWidget.attachTextEdit(self.logEdit) self.logSearchWidget.setWidthForHeight(False) - + self.brokerWidget.setCurrentIndex(0) - + self.__connectionModeProfile = True self.__setConnectionMode(True) # initial mode is 'profile connection' self.__populateProfileComboBox() - + self.connectButton.setIcon(UI.PixmapCache.getIcon("ircConnect")) - self.brokerConnectionOptionsButton.setIcon(UI.PixmapCache.getIcon( - os.path.join("MqttMonitor", "icons", - "connectionOptions-{0}".format(self.__iconSuffix)) - )) + self.brokerConnectionOptionsButton.setIcon( + UI.PixmapCache.getIcon( + os.path.join( + "MqttMonitor", + "icons", + "connectionOptions-{0}".format(self.__iconSuffix), + ) + ) + ) self.__populateBrokerComboBoxes() self.brokerStatusLabel.hide() - self.clearWillButton.setIcon( - UI.PixmapCache.getIcon("certificateDelete")) - + self.clearWillButton.setIcon(UI.PixmapCache.getIcon("certificateDelete")) + self.subscribeTopicComboBox.lineEdit().setClearButtonEnabled(True) self.subscribeTopicComboBox.lineEdit().returnPressed.connect( - self.on_subscribeButton_clicked) + self.on_subscribeButton_clicked + ) self.__populateSubscribeTopicComboBox() - + self.subscribeButton.setIcon(UI.PixmapCache.getIcon("plus")) self.subscribeButton.setEnabled(False) - self.subscribePropertiesButton.setIcon( - UI.PixmapCache.getIcon("listSelection")) + self.subscribePropertiesButton.setIcon(UI.PixmapCache.getIcon("listSelection")) self.subscribePropertiesButton.setEnabled(False) self.subscribePropertiesButton.setVisible(False) - + self.unsubscribeButton.setIcon(UI.PixmapCache.getIcon("minus")) self.unsubscribeButton.setEnabled(False) self.unsubscribePropertiesButton.setIcon( - UI.PixmapCache.getIcon("listSelection")) + UI.PixmapCache.getIcon("listSelection") + ) self.unsubscribePropertiesButton.setEnabled(False) self.unsubscribePropertiesButton.setVisible(False) - + self.__initPropertiesEditMenu() - + self.__subscribedTopics = [] self.__topicQueue = {} self.__updateUnsubscribeTopicComboBox() - + self.__publishedTopics = [] self.__updatePublishTopicComboBox() self.publishButton.setEnabled(False) - self.publishPropertiesButton.setIcon( - UI.PixmapCache.getIcon("listSelection")) + self.publishPropertiesButton.setIcon(UI.PixmapCache.getIcon("listSelection")) self.publishPropertiesButton.setEnabled(False) self.publishPropertiesButton.setVisible(False) - + self.__connectionOptions = None - + prefix = MqttMonitorWidget.BrokerStatusTopicPrefix self.__statusLabelMapping = { # broker @@ -206,10 +220,8 @@ prefix + "retained messages/count": self.messagesRetainedLabel, # publish messages prefix + "publish/messages/sent": self.publishMessagesSentLabel, - prefix + "publish/messages/received": - self.publishMessagesReceivedLabel, - prefix + "publish/messages/dropped": - self.publishMessagesDroppedLabel, + prefix + "publish/messages/received": self.publishMessagesReceivedLabel, + prefix + "publish/messages/dropped": self.publishMessagesDroppedLabel, # traffic prefix + "bytes/sent": self.bytesSentLabel, prefix + "bytes/received": self.bytesReceivedLabel, @@ -224,15 +236,13 @@ prefix + "load/connections": self.loadConnectionsLabel, prefix + "load/sockets": self.loadSocketsLabel, } - - self.__statusLoadValues = collections.defaultdict( - self.__loadDefaultDictFactory) - - def __createClient(self, clientId="", cleanSession=None, - protocol=None): + + self.__statusLoadValues = collections.defaultdict(self.__loadDefaultDictFactory) + + def __createClient(self, clientId="", cleanSession=None, protocol=None): """ Private method to instantiate a MQTT client for a given protocol. - + @param clientId ID to be used for the client @type str @param cleanSession flag indicating to start a clean session @@ -244,10 +254,11 @@ """ if protocol is None: protocol = self.__plugin.getPreferences("DefaultProtocol") - - client = MqttClient(clientId=clientId, cleanSession=cleanSession, - protocol=protocol) - + + client = MqttClient( + clientId=clientId, cleanSession=cleanSession, protocol=protocol + ) + # connect the MQTT client signals client.onConnectV3.connect(self.__brokerConnected) client.onConnectV5.connect(self.__brokerConnected) @@ -261,11 +272,11 @@ client.onSubscribeV5.connect(self.__topicSubscribedV5) client.onUnsubscribeV3.connect(self.__topicUnsubscribed) client.onUnsubscribeV5.connect(self.__topicUnsubscribedV5) - + client.connectTimeout.connect(self.__connectTimeout) - + return client - + def __initPropertiesEditMenu(self): """ Private method to create the properties output context menu. @@ -273,31 +284,36 @@ self.__propertiesEditMenu = QMenu(self) self.__copyPropertiesAct = self.__propertiesEditMenu.addAction( UI.PixmapCache.getIcon("editCopy"), - self.tr("Copy"), self.propertiesEdit.copy) + self.tr("Copy"), + self.propertiesEdit.copy, + ) self.__propertiesEditMenu.addSeparator() self.__selectAllPropertiesAct = self.__propertiesEditMenu.addAction( UI.PixmapCache.getIcon("editSelectAll"), - self.tr("Select All"), self.propertiesEdit.selectAll) + self.tr("Select All"), + self.propertiesEdit.selectAll, + ) self.__propertiesEditMenu.addSeparator() self.__clearPropertiesAct = self.__propertiesEditMenu.addAction( UI.PixmapCache.getIcon("editDelete"), - self.tr("Clear"), self.propertiesEdit.clear) - - self.propertiesEdit.copyAvailable.connect( - self.__copyPropertiesAct.setEnabled) - + self.tr("Clear"), + self.propertiesEdit.clear, + ) + + self.propertiesEdit.copyAvailable.connect(self.__copyPropertiesAct.setEnabled) + self.__copyPropertiesAct.setEnabled(False) - + ####################################################################### ## Slots handling MQTT related signals ####################################################################### - + @pyqtSlot(dict, int) @pyqtSlot(dict, int, int, dict) def __brokerConnected(self, flags, rc, packetType=None, properties=None): """ Private slot to handle being connected to a broker. - + @param flags flags set for the connection @type dict @param rc CONNACK result code or MQTTv5 reason code @@ -309,53 +325,55 @@ @type dict """ self.brokerStatusLabel.hide() - + if rc == 0: self.__connectedToBroker = True self.__connectionOptions = None - + try: sessionPresent = flags["session present"] == 1 except KeyError: sessionPresent = False - + msg = ( mqttReasonCode(rc, packetType) - if packetType is not None else - mqttConnackMessage(rc) + if packetType is not None + else mqttConnackMessage(rc) ) if sessionPresent: msg = self.tr("{0} - Session still present").format(msg) self.__flashBrokerStatusLabel(msg) - + if properties: self.__showProperties("Connect", properties) - + self.connectButton.setEnabled(True) if rc == 0: self.__connectedToBroker = True self.__connectionOptions = None - - self.connectButton.setIcon( - UI.PixmapCache.getIcon("ircDisconnect")) - + + self.connectButton.setIcon(UI.PixmapCache.getIcon("ircDisconnect")) + self.subscribeGroup.setEnabled(True) self.subscribePropertiesButton.setVisible( - self.__client.getProtocol() == MqttProtocols.MQTTv5) + self.__client.getProtocol() == MqttProtocols.MQTTv5 + ) self.unsubscribeGroup.setEnabled(True) self.unsubscribePropertiesButton.setVisible( - self.__client.getProtocol() == MqttProtocols.MQTTv5) + self.__client.getProtocol() == MqttProtocols.MQTTv5 + ) self.publishGroup.setEnabled(True) self.brokerStatusButton.setEnabled(True) self.publishPropertiesButton.setVisible( - self.__client.getProtocol() == MqttProtocols.MQTTv5) - + self.__client.getProtocol() == MqttProtocols.MQTTv5 + ) + self.__statusLoadValues.clear() self.__clearBrokerStatusLabels() self.__setBrokerStatusSubscribed(False) else: self.__client.stopLoop() - + @pyqtSlot() def __connectTimeout(self): """ @@ -363,36 +381,37 @@ """ self.__flashBrokerStatusLabel(self.tr("Connection timed out")) self.__setConnectButtonState() - + @pyqtSlot(int) @pyqtSlot(int, int) def __brokerDisconnected(self, rc, packetType=None): """ Private slot to handle a disconnection from the broker. - + @param rc MQTT error result code @type int @param packetType packet type as reported by the client @type int """ self.__connectedToBroker = False - + # ensure, the client loop is stopped self.__client.stopLoop() - + msg = ( # MQTT v5 mqttReasonCode(rc, packetType) - if packetType is not None else + if packetType is not None + else # MQTT v3 ( mqttErrorMessage(rc) - if rc > 0 else - self.tr("Connection to Broker shut down cleanly.") + if rc > 0 + else self.tr("Connection to Broker shut down cleanly.") ) ) self.__flashBrokerStatusLabel(msg) - + self.connectButton.setIcon(UI.PixmapCache.getIcon("ircConnect")) self.__setConnectButtonState() @@ -400,7 +419,7 @@ self.__topicQueue = {} self.__updateUnsubscribeTopicComboBox() self.__updatePublishTopicComboBox() - + self.subscribeGroup.setEnabled(False) self.subscribePropertiesButton.setVisible(False) self.unsubscribeGroup.setEnabled(False) @@ -408,14 +427,14 @@ self.publishGroup.setEnabled(False) self.publishPropertiesButton.setVisible(False) self.brokerStatusButton.setEnabled(False) - + self.__statusLoadValues.clear() - + @pyqtSlot(int, str) def __clientLog(self, level, message): """ Private slot to handle the receipt of a log message. - + @param level log level @type int @param message log message @@ -423,42 +442,45 @@ """ with contextlib.suppress(KeyError): if MqttClient.LogLevelMap[level] < self.logLevelComboBox.itemData( - self.logLevelComboBox.currentIndex()): + self.logLevelComboBox.currentIndex() + ): return - + scrollbarValue = self.logEdit.verticalScrollBar().value() - + textCursor = self.logEdit.textCursor() if not self.logEdit.document().isEmpty(): textCursor.movePosition(QTextCursor.MoveOperation.End) self.logEdit.setTextCursor(textCursor) self.logEdit.insertPlainText("\n") - + textBlockFormat = textCursor.blockFormat() try: textBlockFormat.setBackground( - self.__logMessagesBackgrounds[MqttClient.LogLevelMap[level]]) + self.__logMessagesBackgrounds[MqttClient.LogLevelMap[level]] + ) except KeyError: textBlockFormat.setBackground( - self.__logMessagesBackgrounds[MqttClient.LogDisabled]) + self.__logMessagesBackgrounds[MqttClient.LogDisabled] + ) textCursor.setBlockFormat(textBlockFormat) textCursor.movePosition(QTextCursor.MoveOperation.End) self.logEdit.setTextCursor(textCursor) - + txt = self.tr("{0}: {1}").format(mqttLogLevelString(level), message) self.logEdit.insertPlainText(Utilities.filterAnsiSequences(txt)) - + if self.followLogMessagesCheckBox.isChecked(): self.logEdit.ensureCursorVisible() else: self.logEdit.verticalScrollBar().setValue(scrollbarValue) - + @pyqtSlot(str, bytes, int, bool) @pyqtSlot(str, bytes, int, bool, dict) def __messageReceived(self, topic, payload, qos, retain, properties=None): """ 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 @@ -474,26 +496,25 @@ # handle broker status messages self.__handleBrokerStatusMessage(topic, payload) else: - self.__appendMessage(topic, payload, qos, retain, - properties=properties) - + self.__appendMessage(topic, payload, qos, retain, properties=properties) + @pyqtSlot(int) def __messagePublished(self, mid): """ Private slot to handle a message being published. - + @param mid ID of the published message @type int """ # nothing to show for this pass - + @pyqtSlot(int) def __topicSubscribed(self, mid): """ Private slot to handle being subscribed to topics (MQTT v3.1, MQTT v3.1.1). - + @param mid ID of the subscribe request @type int """ @@ -501,15 +522,15 @@ topic = self.__topicQueue.pop(mid) self.__subscribedTopics.append(topic) self.__addTopicToRecent(topic) - + self.__updateUnsubscribeTopicComboBox() self.__updatePublishTopicComboBox() - + @pyqtSlot(int, list, dict) def __topicSubscribedV5(self, mid, reasonCodes, properties): """ Private slot to handle being subscribed to topics (MQTT v5). - + @param mid ID of the subscribe request @type int @param reasonCodes list of reason codes, one for each topic @@ -520,18 +541,18 @@ """ msg = mqttReasonCode(reasonCodes[0].value, reasonCodes[0].packetType) self.__flashBrokerStatusLabel(msg) - + if properties: self.__showProperties("Subscribe", properties) - + self.__topicSubscribed(mid) - + @pyqtSlot(int) def __topicUnsubscribed(self, mid): """ Private slot to handle being unsubcribed from a topic (MQTT v3.1, MQTT v3.1.1). - + @param mid ID of the unsubscribe request @type int """ @@ -541,12 +562,12 @@ self.__subscribedTopics.remove(topic) self.__updateUnsubscribeTopicComboBox() self.__updatePublishTopicComboBox() - + @pyqtSlot(int, int, int, dict) def __topicUnsubscribedV5(self, mid, rc, packetType, properties): """ Private slot to handle being unsubscribed to topics (MQTT v5). - + @param mid ID of the subscribe request @type int @param rc MQTTv5 reason code @@ -559,29 +580,29 @@ """ msg = mqttReasonCode(rc, packetType) self.__flashBrokerStatusLabel(msg) - + if properties: self.__showProperties("Unsubscribe", properties) - + self.__topicUnsubscribed(mid) - + ####################################################################### ## 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() def on_modeButton_clicked(self): """ @@ -589,27 +610,27 @@ connection mode. """ self.__setConnectionMode(not self.__connectionModeProfile) - + @pyqtSlot(str) def on_profileComboBox_currentIndexChanged(self, profileName): """ Private slot handling the change of the selected profile. - + @param profileName name of the selected profile @type str """ self.__setConnectButtonState() - + @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 """ self.__setConnectButtonState() - + @pyqtSlot() def on_brokerConnectionOptionsButton_clicked(self): """ @@ -617,23 +638,22 @@ dialog to edit connection profiles. """ if self.__connectionModeProfile: - from .MqttConnectionProfilesDialog import ( - MqttConnectionProfilesDialog - ) + from .MqttConnectionProfilesDialog import MqttConnectionProfilesDialog + profileName = self.profileComboBox.currentText() dlg = MqttConnectionProfilesDialog( self.__plugin.getPreferences("BrokerProfiles"), - currentProfile=profileName, parent=self) + currentProfile=profileName, + parent=self, + ) if dlg.exec() == QDialog.DialogCode.Accepted: profilesDict = dlg.getProfiles() self.__plugin.setPreferences("BrokerProfiles", profilesDict) self.__populateProfileComboBox() else: - from .MqttConnectionOptionsDialog import ( - MqttConnectionOptionsDialog - ) - dlg = MqttConnectionOptionsDialog( - self.__connectionOptions, parent=self) + from .MqttConnectionOptionsDialog import MqttConnectionOptionsDialog + + dlg = MqttConnectionOptionsDialog(self.__connectionOptions, parent=self) if dlg.exec() == QDialog.DialogCode.Accepted: self.__connectionOptions = dlg.getConnectionOptions() if self.__connectionOptions["TlsEnable"]: @@ -646,7 +666,7 @@ if port == "8883": # it is default TLS port => set to non-encrypted port self.brokerPortComboBox.setEditText("1883") - + @pyqtSlot() def on_connectButton_clicked(self): """ @@ -659,7 +679,7 @@ self.__profileConnectToBroker() else: self.__directConnectToBroker() - + @pyqtSlot() def on_subscribePropertiesButton_clicked(self): """ @@ -669,20 +689,20 @@ self.__editProperties( "subscribe", self.tr("SUBSCRIBE: User Properties for '{0}'").format(topic), - topic + topic, ) - + @pyqtSlot(str) def on_subscribeTopicComboBox_editTextChanged(self, topic): """ Private slot to handle a change of the entered topic. - + @param topic entered topic text @type str """ self.subscribeButton.setEnabled(bool(topic)) self.subscribePropertiesButton.setEnabled(bool(topic)) - + @pyqtSlot() def on_subscribeButton_clicked(self): """ @@ -695,19 +715,22 @@ EricMessageBox.warning( self, self.tr("Subscribe to Topic"), - self.tr("Subscriptions to the Status topic '$SYS' shall" - " be done on the 'Status' tab.")) + self.tr( + "Subscriptions to the Status topic '$SYS' shall" + " be done on the 'Status' tab." + ), + ) else: properties = ( - self.__plugin.getPreferences("SubscribeProperties") - .get(topic, []) - if self.__client.getProtocol() == MqttProtocols.MQTTv5 else - None + self.__plugin.getPreferences("SubscribeProperties").get(topic, []) + if self.__client.getProtocol() == MqttProtocols.MQTTv5 + else None ) result, mid = self.__client.subscribe( - topic, qos=qos, properties=properties) + topic, qos=qos, properties=properties + ) self.__topicQueue[mid] = topic - + @pyqtSlot() def on_unsubscribePropertiesButton_clicked(self): """ @@ -717,20 +740,20 @@ self.__editProperties( "unsubscribe", self.tr("UNSUBSCRIBE: User Properties for '{0}'").format(topic), - topic + 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)) self.unsubscribePropertiesButton.setEnabled(bool(topic)) - + @pyqtSlot() def on_unsubscribeButton_clicked(self): """ @@ -739,15 +762,13 @@ topic = self.unsubscribeTopicComboBox.currentText() if topic: properties = ( - self.__plugin.getPreferences("SubscribeProperties") - .get(topic, []) - if self.__client.getProtocol() == MqttProtocols.MQTTv5 else - None + self.__plugin.getPreferences("SubscribeProperties").get(topic, []) + if self.__client.getProtocol() == MqttProtocols.MQTTv5 + else None ) - result, mid = self.__client.unsubscribe( - topic, properties=properties) + result, mid = self.__client.unsubscribe(topic, properties=properties) self.__topicQueue[mid] = topic - + @pyqtSlot() def on_publishPropertiesButton_clicked(self): """ @@ -757,20 +778,20 @@ self.__editProperties( "publish", self.tr("PUBLISH: User Properties for '{0}'").format(topic), - topic + topic, ) - + @pyqtSlot(str) def on_publishTopicComboBox_editTextChanged(self, topic): """ Private slot to handle changes of the publish topic name. - + @param topic topic text @type str """ self.publishButton.setEnabled(bool(topic)) self.publishPropertiesButton.setEnabled(bool(topic)) - + @pyqtSlot() def on_publishButton_clicked(self): """ @@ -781,9 +802,9 @@ retain = self.publishRetainCheckBox.isChecked() payloadFile = self.publishPayloadFilePicker.text() if ( - bool(payloadFile) and - os.path.exists(payloadFile) and - os.path.getsize(payloadFile) <= 268435455 + bool(payloadFile) + and os.path.exists(payloadFile) + and os.path.getsize(payloadFile) <= 268435455 ): # payload size limit is 268,435,455 bytes try: @@ -793,9 +814,11 @@ EricMessageBox.critical( self, self.tr("Read Payload from File"), - self.tr("""<p>The file <b>{0}</b> could not be read.""" - """ Aborting...</p><p>Reason: {1}</p>""").format( - payloadFile, str(err))) + self.tr( + """<p>The file <b>{0}</b> could not be read.""" + """ Aborting...</p><p>Reason: {1}</p>""" + ).format(payloadFile, str(err)), + ) return else: payloadStr = self.publishPayloadEdit.toPlainText() @@ -805,20 +828,20 @@ payloadStr = None properties = ( self.__plugin.getPreferences("PublishProperties").get(topic, []) - if self.__client.getProtocol() == MqttProtocols.MQTTv5 else - None + if self.__client.getProtocol() == MqttProtocols.MQTTv5 + else None ) - + msgInfo = self.__client.publish( - topic, payload=payloadStr, qos=qos, retain=retain, - properties=properties) + topic, payload=payloadStr, qos=qos, retain=retain, properties=properties + ) if msgInfo.rc == 0: if topic not in self.__publishedTopics: self.__publishedTopics.append(topic) self.__updatePublishTopicComboBox(resetTopic=False) if self.clearPublishCheckBox.isChecked(): self.on_publishClearButton_clicked() - + @pyqtSlot() def on_publishClearRetainedButton_clicked(self): """ @@ -827,17 +850,18 @@ topic = self.publishTopicComboBox.currentText() properties = ( self.__plugin.getPreferences("PublishProperties").get(topic, []) - if self.__client.getProtocol() == MqttProtocols.MQTTv5 else - None + if self.__client.getProtocol() == MqttProtocols.MQTTv5 + else None ) - + msgInfo = self.__client.publish( - topic, payload=None, retain=True, properties=properties) + topic, payload=None, retain=True, properties=properties + ) if msgInfo.rc == 0: if topic not in self.__publishedTopics: self.__publishedTopics.append(topic) self.__updatePublishTopicComboBox(resetTopic=False) - + @pyqtSlot() def on_publishClearButton_clicked(self): """ @@ -848,27 +872,27 @@ self.publishQosSpinBox.setValue(0) self.publishRetainCheckBox.setChecked(False) self.publishPayloadFilePicker.clear() - + @pyqtSlot(str) def on_publishPayloadFilePicker_textChanged(self, path): """ Private slot handling a change of path of the payload file. - + @param path path of the payload file @type str """ self.publishPayloadEdit.setEnabled(not bool(path)) - + @pyqtSlot(QPoint) def on_propertiesEdit_customContextMenuRequested(self, pos): """ Private slot to show the context menu for the properties output. - + @param pos the position of the mouse pointer @type QPoint """ self.__propertiesEditMenu.popup(self.propertiesEdit.mapToGlobal(pos)) - + @pyqtSlot() def on_brokerStatusButton_clicked(self): """ @@ -876,31 +900,29 @@ """ if self.__brokerStatusTopicSubscribed: # unsubscribe status topic - rc, _ = self.__client.unsubscribe( - MqttMonitorWidget.BrokerStatusTopic) + rc, _ = self.__client.unsubscribe(MqttMonitorWidget.BrokerStatusTopic) if rc == 0: # successfully sent self.__setBrokerStatusSubscribed(False) else: # subscribe status topic - rc, _ = self.__client.subscribe( - MqttMonitorWidget.BrokerStatusTopic) + rc, _ = self.__client.subscribe(MqttMonitorWidget.BrokerStatusTopic) if rc == 0: # successfully sent self.__setBrokerStatusSubscribed(True) - + @pyqtSlot(int) def on_messagesEdit_blockCountChanged(self, newBlockCount): """ Private slot handling changes of received messages. - + @param newBlockCount (ignored) @type int """ enable = not self.messagesEdit.document().isEmpty() self.saveMessagesButton.setEnabled(enable) self.clearMessagesButton.setEnabled(enable) - + @pyqtSlot() def on_saveMessagesButton_clicked(self): """ @@ -912,12 +934,13 @@ "", self.tr("Messages Files (*.txt);;All Files (*)"), "", - EricFileDialog.DontConfirmOverwrite) - + EricFileDialog.DontConfirmOverwrite, + ) + if fn: if fn.endswith("."): fn = fn[:-1] - + ext = QFileInfo(fn).suffix() if not ext: ex = selectedFilter.split("(*")[1].split(")")[0] @@ -927,12 +950,14 @@ res = EricMessageBox.yesNo( self, self.tr("Save Messages"), - self.tr("<p>The file <b>{0}</b> already exists." - " Overwrite it?</p>").format(fn), - icon=EricMessageBox.Warning) + self.tr( + "<p>The file <b>{0}</b> already exists." " Overwrite it?</p>" + ).format(fn), + icon=EricMessageBox.Warning, + ) if not res: return - + fn = Utilities.toNativeSeparators(fn) try: with open(fn, "w") as f: @@ -941,22 +966,24 @@ EricMessageBox.critical( self, self.tr("Save Messages"), - self.tr("""<p>The file <b>{0}</b> could not be written.""" - """</p><p>Reason: {1}</p>""").format( - fn, str(err))) - + self.tr( + """<p>The file <b>{0}</b> could not be written.""" + """</p><p>Reason: {1}</p>""" + ).format(fn, str(err)), + ) + @pyqtSlot(int) def on_logEdit_blockCountChanged(self, newBlockCount): """ Private slot handling changes of received messages. - + @param newBlockCount (ignored) @type int """ enable = not self.logEdit.document().isEmpty() self.saveLogMessagesButton.setEnabled(enable) self.clearLogMessagesButton.setEnabled(enable) - + @pyqtSlot() def on_saveLogMessagesButton_clicked(self): """ @@ -968,12 +995,13 @@ "", self.tr("Log Files (*.log);;All Files (*)"), "", - EricFileDialog.DontConfirmOverwrite) - + EricFileDialog.DontConfirmOverwrite, + ) + if fn: if fn.endswith("."): fn = fn[:-1] - + ext = QFileInfo(fn).suffix() if not ext: ex = selectedFilter.split("(*")[1].split(")")[0] @@ -983,12 +1011,14 @@ res = EricMessageBox.yesNo( self, self.tr("Save Log Messages"), - self.tr("<p>The file <b>{0}</b> already exists." - " Overwrite it?</p>").format(fn), - icon=EricMessageBox.Warning) + self.tr( + "<p>The file <b>{0}</b> already exists." " Overwrite it?</p>" + ).format(fn), + icon=EricMessageBox.Warning, + ) if not res: return - + fn = Utilities.toNativeSeparators(fn) try: with open(fn, "w") as f: @@ -997,19 +1027,21 @@ EricMessageBox.critical( self, self.tr("Save Log Messages"), - self.tr("""<p>The file <b>{0}</b> could not be written.""" - """</p><p>Reason: {1}</p>""").format( - fn, str(err))) - + self.tr( + """<p>The file <b>{0}</b> could not be written.""" + """</p><p>Reason: {1}</p>""" + ).format(fn, str(err)), + ) + ####################################################################### ## Utility methods ####################################################################### - + def __setBrokerStatusSubscribed(self, subscribed): """ Private method to set the subscription status for the broker status topics. - + @param subscribed subscription status for the broker status topics @type bool """ @@ -1017,17 +1049,19 @@ if subscribed: self.brokerStatusButton.setText(self.tr("Unsubscribe")) self.brokerStatusButton.setToolTip( - self.tr("Press to deactivate the status display")) + self.tr("Press to deactivate the status display") + ) else: self.brokerStatusButton.setText(self.tr("Subscribe")) self.brokerStatusButton.setToolTip( - self.tr("Press to activate the status display")) - + self.tr("Press to activate the status display") + ) + 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 @@ -1042,60 +1076,59 @@ maxBrokers = self.__plugin.getPreferences("RecentBrokersNumber") brokerList = brokerList[:maxBrokers] self.__plugin.setPreferences("RecentBrokersWithPort", brokerList) - + self.__populateBrokerComboBoxes() - + def __populateBrokerComboBoxes(self): """ Private method to populate the broker name and port combo boxes. """ brokerPortList = self.__plugin.getPreferences("RecentBrokersWithPort") - + # step 1: clear combo boxes self.brokerComboBox.clear() self.brokerPortComboBox.clear() - + # step 2a: populate the broker name list currentBroker = brokerPortList[0][0] if brokerPortList else "" brokerSet = {b[0].strip() for b in brokerPortList} self.brokerComboBox.addItems(sorted(brokerSet)) index = self.brokerComboBox.findText(currentBroker) self.brokerComboBox.setCurrentIndex(index) - + # step 2b: populate the broker ports list currentPort = brokerPortList[0][1] if brokerPortList else 1883 currentPortStr = "{0:5}".format(currentPort) portsSet = {b[1] for b in brokerPortList} portsSet.update({1883, 8883}) - self.brokerPortComboBox.addItems( - sorted("{0:5}".format(p) for p in portsSet)) + self.brokerPortComboBox.addItems(sorted("{0:5}".format(p) for p in portsSet)) index = self.brokerPortComboBox.findText(currentPortStr) self.brokerPortComboBox.setCurrentIndex(index) - + # step 3: update the connect button state self.__setConnectButtonState() - + def __populateProfileComboBox(self): """ Private method to populate the profiles selection box. """ profilesDict = self.__plugin.getPreferences("BrokerProfiles") mostRecentProfile = self.__plugin.getPreferences("MostRecentProfile") - + self.profileComboBox.clear() self.profileComboBox.addItems(sorted(profilesDict.keys())) if mostRecentProfile: index = self.profileComboBox.findText(mostRecentProfile) if index >= 0: self.profileComboBox.setCurrentIndex(index) - + self.__setConnectButtonState() - + def __addTopicToRecent(self, topic): """ Private method to add a topic to the list of recently subscribed topics. - + @param topic subscribed topic @type str """ @@ -1107,51 +1140,50 @@ maxTopics = self.__plugin.getPreferences("RecentTopicsNumber") topicsList = topicsList[:maxTopics] self.__plugin.setPreferences("RecentTopics", topicsList) - + self.__populateSubscribeTopicComboBox() - + def __populateSubscribeTopicComboBox(self): """ Private method to populate the subscribe topic combo box. """ topicsList = self.__plugin.getPreferences("RecentTopics") - + self.subscribeTopicComboBox.clear() self.subscribeTopicComboBox.addItems(sorted(topicsList)) self.subscribeTopicComboBox.clearEditText() - + def __updateUnsubscribeTopicComboBox(self): """ Private method to update the unsubcribe topic combo box. """ self.unsubscribeTopicComboBox.clear() self.unsubscribeTopicComboBox.addItems(sorted(self.__subscribedTopics)) - self.unsubscribeButton.setEnabled( - bool(self.__subscribedTopics)) - self.unsubscribePropertiesButton.setEnabled( - bool(self.__subscribedTopics)) - + self.unsubscribeButton.setEnabled(bool(self.__subscribedTopics)) + self.unsubscribePropertiesButton.setEnabled(bool(self.__subscribedTopics)) + def __updatePublishTopicComboBox(self, resetTopic=True): """ Private method to update the publish topic combo box. - + @param resetTopic flag indicating to reset the topic @type bool """ currentTopic = self.publishTopicComboBox.currentText() self.publishTopicComboBox.clear() self.publishTopicComboBox.addItems( - list(set(self.__publishedTopics + self.__subscribedTopics))) + list(set(self.__publishedTopics + self.__subscribedTopics)) + ) if resetTopic: self.publishTopicComboBox.clearEditText() else: topicIndex = self.publishTopicComboBox.findText(currentTopic) self.publishTopicComboBox.setCurrentIndex(topicIndex) - + def __appendMessage(self, topic, payload, qos, retain, properties=None): """ Private method to append a received message to the output. - + @param topic topic of the received message @type str @param payload payload of the received message @@ -1164,45 +1196,43 @@ @type dict """ scrollbarValue = self.messagesEdit.verticalScrollBar().value() - + textCursor = self.messagesEdit.textCursor() if not self.messagesEdit.document().isEmpty(): textCursor.movePosition(QTextCursor.MoveOperation.End) self.messagesEdit.setTextCursor(textCursor) self.messagesEdit.insertPlainText("\n") - + textBlockFormat = textCursor.blockFormat() if self.__isMessageAlternate: - textBlockFormat.setBackground( - self.messagesEdit.palette().alternateBase()) + textBlockFormat.setBackground(self.messagesEdit.palette().alternateBase()) else: - textBlockFormat.setBackground( - self.messagesEdit.palette().base()) + textBlockFormat.setBackground(self.messagesEdit.palette().base()) textCursor.setBlockFormat(textBlockFormat) textCursor.movePosition(QTextCursor.MoveOperation.End) self.messagesEdit.setTextCursor(textCursor) - + self.messagesEdit.setCurrentCharFormat(self.__messagesTopicFormat) self.messagesEdit.insertPlainText(topic + "\n") - + self.messagesEdit.setCurrentCharFormat(self.__messagesQosFormat) self.messagesEdit.insertPlainText(self.tr("QoS: {0}\n").format(qos)) - + if retain: self.messagesEdit.setCurrentCharFormat(self.__messagesQosFormat) self.messagesEdit.insertPlainText(self.tr("Retained Message\n")) - + if properties: - self.messagesEdit.setCurrentCharFormat( - self.__messagesSubheaderFormat) + self.messagesEdit.setCurrentCharFormat(self.__messagesSubheaderFormat) self.messagesEdit.insertPlainText(self.tr("Properties:\n")) self.messagesEdit.setCurrentCharFormat(self.__messagesFormat) for name, value in sorted(properties.items()): self.messagesEdit.insertPlainText( - self.tr("{0}: {1}\n", "property name, property value") - .format(name, value) + self.tr("{0}: {1}\n", "property name, property value").format( + name, value + ) ) - + self.messagesEdit.setCurrentCharFormat(self.__messagesSubheaderFormat) self.messagesEdit.insertPlainText(self.tr("Message:\n")) payloadStr = str(payload, encoding="utf-8", errors="replace") @@ -1212,18 +1242,18 @@ self.messagesEdit.insertPlainText(payloadStr) else: self.messagesEdit.insertPlainText(self.tr("<empty>")) - + if self.followMessagesCheckBox.isChecked(): self.messagesEdit.ensureCursorVisible() else: self.messagesEdit.verticalScrollBar().setValue(scrollbarValue) - + self.__isMessageAlternate = not self.__isMessageAlternate - + def __handleBrokerStatusMessage(self, topic, payload): """ Private method to handle a status message of the broker. - + @param topic topic of the received message @type str @param payload payload of the received message @@ -1231,18 +1261,18 @@ """ payloadStr = str(payload, encoding="utf-8", errors="replace").strip() topic = topic.strip() - + if topic.startswith(MqttMonitorWidget.BrokerStatusTopicLoadPrefix): self.__handleBrokerLoadStatusMessage(topic, payloadStr) else: with contextlib.suppress(KeyError): label = self.__statusLabelMapping[topic] label.setText(payloadStr) - + def __handleBrokerLoadStatusMessage(self, topic, payloadStr): """ Private method to append a received message to the output. - + @param topic topic of the received message @type str @param payloadStr string representation of the payload of the @@ -1251,15 +1281,17 @@ """ subtopic, topicElement = topic.rsplit("/", 1) self.__statusLoadValues[subtopic][topicElement] = payloadStr - + with contextlib.suppress(KeyError): label = self.__statusLabelMapping[subtopic] - label.setText("{0} / {1} / {2}".format( - self.__statusLoadValues[subtopic]["1min"], - self.__statusLoadValues[subtopic]["5min"], - self.__statusLoadValues[subtopic]["15min"], - )) - + label.setText( + "{0} / {1} / {2}".format( + self.__statusLoadValues[subtopic]["1min"], + self.__statusLoadValues[subtopic]["5min"], + self.__statusLoadValues[subtopic]["15min"], + ) + ) + def __clearBrokerStatusLabels(self): """ Private method to clear the broker status labels. @@ -1268,15 +1300,16 @@ label = ( "- / - / -" if statusLabelKey.startswith( - MqttMonitorWidget.BrokerStatusTopicLoadPrefix) else - "-" + MqttMonitorWidget.BrokerStatusTopicLoadPrefix + ) + else "-" ) self.__statusLabelMapping[statusLabelKey].setText(label) - + def __loadDefaultDictFactory(self): """ Private method to populate non-existing load items. - + @return default dictionary entry @rtype dict """ @@ -1285,41 +1318,47 @@ "5min": "-", "15min": "-", } - + def __setConnectionMode(self, profileMode): """ Private method to set the connection mode. - + @param profileMode flag indicating the profile connection mode @type bool """ self.__connectionModeProfile = profileMode if profileMode: - self.modeButton.setIcon(UI.PixmapCache.getIcon( - os.path.join("MqttMonitor", "icons", - "profiles-{0}".format(self.__iconSuffix)) - )) + self.modeButton.setIcon( + UI.PixmapCache.getIcon( + os.path.join( + "MqttMonitor", "icons", "profiles-{0}".format(self.__iconSuffix) + ) + ) + ) else: - self.modeButton.setIcon(UI.PixmapCache.getIcon( - os.path.join("MqttMonitor", "icons", - "quickopen-{0}".format(self.__iconSuffix)) - )) - + self.modeButton.setIcon( + UI.PixmapCache.getIcon( + os.path.join( + "MqttMonitor", + "icons", + "quickopen-{0}".format(self.__iconSuffix), + ) + ) + ) + self.profileComboBox.setVisible(profileMode) self.brokerConnectionWidget.setVisible(not profileMode) self.__setConnectButtonState() - + def __setConnectButtonState(self): """ Private method to set the enabled state of the connect button. """ if self.__connectionModeProfile: - self.connectButton.setEnabled( - bool(self.profileComboBox.currentText())) + self.connectButton.setEnabled(bool(self.profileComboBox.currentText())) else: - self.connectButton.setEnabled( - bool(self.brokerComboBox.currentText())) - + self.connectButton.setEnabled(bool(self.brokerComboBox.currentText())) + def __directConnectToBroker(self): """ Private method to connect to the broker with entered data. @@ -1333,33 +1372,35 @@ port = 1883 if host: self.brokerStatusLabel.setText( - self.tr("Connecting to {0}:{1} ...").format( - host, port)) + self.tr("Connecting to {0}:{1} ...").format(host, port) + ) self.brokerStatusLabel.show() - + self.__addBrokerToRecent(host, port) self.connectButton.setEnabled(False) - + if self.clearWillButton.isChecked(): clearWill = True self.clearWillButton.setChecked(False) else: clearWill = False - + if self.__connectionOptions is None: self.__client = self.__createClient() - self.__client.connectToServer( - host, port=port, clearWill=clearWill) + self.__client.connectToServer(host, port=port, clearWill=clearWill) else: self.__client = self.__createClient( clientId=self.__connectionOptions["ClientId"], cleanSession=self.__connectionOptions["CleanSession"], - protocol=self.__connectionOptions["Protocol"] + protocol=self.__connectionOptions["Protocol"], ) self.__client.connectToServerWithOptions( - host, port=port, options=self.__connectionOptions, - clearWill=clearWill) - + host, + port=port, + options=self.__connectionOptions, + clearWill=clearWill, + ) + def __profileConnectToBroker(self): """ Private method to connect to the broker with selected profile. @@ -1367,7 +1408,7 @@ profileName = self.profileComboBox.currentText() if profileName: self.__plugin.setPreferences("MostRecentProfile", profileName) - + profilesDict = self.__plugin.getPreferences("BrokerProfiles") connectionProfile = copy.deepcopy(profilesDict[profileName]) host = connectionProfile["BrokerAddress"] @@ -1376,35 +1417,36 @@ protocol = connectionProfile["Protocol"] except KeyError: protocol = MqttProtocols( - self.__plugin.getPreferences("DefaultProtocol")) - + self.__plugin.getPreferences("DefaultProtocol") + ) + self.brokerStatusLabel.setText( - self.tr("Connecting to {0}:{1} ...").format( - host, port)) + self.tr("Connecting to {0}:{1} ...").format(host, port) + ) self.brokerStatusLabel.show() - + self.connectButton.setEnabled(False) - + if self.clearWillButton.isChecked(): clearWill = True self.clearWillButton.setChecked(False) else: clearWill = False - + self.__client = self.__createClient( clientId=connectionProfile["ClientId"], cleanSession=connectionProfile["CleanSession"], - protocol=protocol + protocol=protocol, ) self.__client.connectToServerWithOptions( - host, port=port, options=connectionProfile, - clearWill=clearWill) - + host, port=port, options=connectionProfile, clearWill=clearWill + ) + def __showProperties(self, typeStr, properties): """ Private method to display the received properties in the properties pane. - + @param typeStr message type @type str @param properties dictionary containing the relevant properties @@ -1414,36 +1456,33 @@ if not self.propertiesEdit.document().isEmpty(): textCursor.movePosition(QTextCursor.MoveOperation.End) self.propertiesEdit.setTextCursor(textCursor) - + textBlockFormat = textCursor.blockFormat() if self.__isPropertiesAlternate: - textBlockFormat.setBackground( - self.propertiesEdit.palette().alternateBase()) + textBlockFormat.setBackground(self.propertiesEdit.palette().alternateBase()) else: - textBlockFormat.setBackground( - self.propertiesEdit.palette().base()) + textBlockFormat.setBackground(self.propertiesEdit.palette().base()) textCursor.setBlockFormat(textBlockFormat) textCursor.movePosition(QTextCursor.MoveOperation.End) self.propertiesEdit.setTextCursor(textCursor) - + self.propertiesEdit.setCurrentCharFormat(self.__propertiesTopicFormat) self.propertiesEdit.insertPlainText(typeStr + "\n") - + for name, value in sorted(properties.items()): - self.propertiesEdit.setCurrentCharFormat( - self.__propertiesNameFormat) + self.propertiesEdit.setCurrentCharFormat(self.__propertiesNameFormat) self.propertiesEdit.insertPlainText("{0}: ".format(name)) self.propertiesEdit.setCurrentCharFormat(self.__propertiesFormat) self.propertiesEdit.insertPlainText("{0}\n".format(str(value))) - + self.propertiesEdit.ensureCursorVisible() - + self.__isPropertiesAlternate = not self.__isPropertiesAlternate - + def __editProperties(self, propertiesType, header, key): """ Private method to edit user properties of a given type. - + @param propertiesType properties type (one of 'subscribe', 'unsubscribe', 'publish') @type str @@ -1453,11 +1492,10 @@ @type str """ from .MqttUserPropertiesEditor import MqttUserPropertiesEditorDialog - + preferencesKey = "{0}Properties".format(propertiesType.capitalize()) properties = self.__plugin.getPreferences(preferencesKey) - dlg = MqttUserPropertiesEditorDialog( - header, properties.get(key, []), self) + dlg = MqttUserPropertiesEditorDialog(header, properties.get(key, []), self) if dlg.exec() == QDialog.DialogCode.Accepted: properties[key] = dlg.getProperties() self.__plugin.setPreferences(preferencesKey, properties)
--- a/MqttMonitor/MqttProtocols.py Thu Dec 30 16:36:40 2021 +0100 +++ b/MqttMonitor/MqttProtocols.py Wed Sep 21 09:42:33 2022 +0200 @@ -16,15 +16,18 @@ """ Class defining the supported MQTT protocol versions. """ + MQTTv31 = mqtt.MQTTv31 MQTTv311 = mqtt.MQTTv311 MQTTv5 = mqtt.MQTTv5 + except ImportError: # define the enum with known values class MqttProtocols(enum.IntEnum): """ Class defining the supported MQTT protocol versions. """ + MQTTv31 = 3 MQTTv311 = 4 MQTTv5 = 5
--- a/MqttMonitor/MqttReasonCodes.py Thu Dec 30 16:36:40 2021 +0100 +++ b/MqttMonitor/MqttReasonCodes.py Wed Sep 21 09:42:33 2022 +0200 @@ -13,196 +13,273 @@ MqttReasonCodeNames = { 0: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Success" - ): [PacketTypes.CONNACK, PacketTypes.PUBACK, PacketTypes.PUBREC, - PacketTypes.PUBREL, PacketTypes.PUBCOMP, PacketTypes.UNSUBACK, - PacketTypes.AUTH], - QCoreApplication.translate( - "MqttReasonCodeNames", "Normal disconnection" - ): [PacketTypes.DISCONNECT], - QCoreApplication.translate( - "MqttReasonCodeNames", "Granted QoS 0" - ): [PacketTypes.SUBACK]}, + QCoreApplication.translate("MqttReasonCodeNames", "Success"): [ + PacketTypes.CONNACK, + PacketTypes.PUBACK, + PacketTypes.PUBREC, + PacketTypes.PUBREL, + PacketTypes.PUBCOMP, + PacketTypes.UNSUBACK, + PacketTypes.AUTH, + ], + QCoreApplication.translate("MqttReasonCodeNames", "Normal disconnection"): [ + PacketTypes.DISCONNECT + ], + QCoreApplication.translate("MqttReasonCodeNames", "Granted QoS 0"): [ + PacketTypes.SUBACK + ], + }, 1: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Granted QoS 1" - ): [PacketTypes.SUBACK]}, + QCoreApplication.translate("MqttReasonCodeNames", "Granted QoS 1"): [ + PacketTypes.SUBACK + ] + }, 2: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Granted QoS 2" - ): [PacketTypes.SUBACK]}, + QCoreApplication.translate("MqttReasonCodeNames", "Granted QoS 2"): [ + PacketTypes.SUBACK + ] + }, 4: { QCoreApplication.translate( "MqttReasonCodeNames", "Disconnect with will message" - ): [PacketTypes.DISCONNECT]}, + ): [PacketTypes.DISCONNECT] + }, 16: { - QCoreApplication.translate( - "MqttReasonCodeNames", "No matching subscribers" - ): [PacketTypes.PUBACK, PacketTypes.PUBREC]}, + QCoreApplication.translate("MqttReasonCodeNames", "No matching subscribers"): [ + PacketTypes.PUBACK, + PacketTypes.PUBREC, + ] + }, 17: { - QCoreApplication.translate( - "MqttReasonCodeNames", "No subscription found" - ): [PacketTypes.UNSUBACK]}, + QCoreApplication.translate("MqttReasonCodeNames", "No subscription found"): [ + PacketTypes.UNSUBACK + ] + }, 24: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Continue authentication" - ): [PacketTypes.AUTH]}, + QCoreApplication.translate("MqttReasonCodeNames", "Continue authentication"): [ + PacketTypes.AUTH + ] + }, 25: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Re-authenticate" - ): [PacketTypes.AUTH]}, + QCoreApplication.translate("MqttReasonCodeNames", "Re-authenticate"): [ + PacketTypes.AUTH + ] + }, 128: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Unspecified error" - ): [PacketTypes.CONNACK, PacketTypes.PUBACK, - PacketTypes.PUBREC, PacketTypes.SUBACK, - PacketTypes.UNSUBACK, PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Unspecified error"): [ + PacketTypes.CONNACK, + PacketTypes.PUBACK, + PacketTypes.PUBREC, + PacketTypes.SUBACK, + PacketTypes.UNSUBACK, + PacketTypes.DISCONNECT, + ] + }, 129: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Malformed packet" - ): [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Malformed packet"): [ + PacketTypes.CONNACK, + PacketTypes.DISCONNECT, + ] + }, 130: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Protocol error" - ): [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Protocol error"): [ + PacketTypes.CONNACK, + PacketTypes.DISCONNECT, + ] + }, 131: { QCoreApplication.translate( "MqttReasonCodeNames", "Implementation specific error" - ): [PacketTypes.CONNACK, PacketTypes.PUBACK, - PacketTypes.PUBREC, PacketTypes.SUBACK, - PacketTypes.UNSUBACK, PacketTypes.DISCONNECT]}, + ): [ + PacketTypes.CONNACK, + PacketTypes.PUBACK, + PacketTypes.PUBREC, + PacketTypes.SUBACK, + PacketTypes.UNSUBACK, + PacketTypes.DISCONNECT, + ] + }, 132: { QCoreApplication.translate( "MqttReasonCodeNames", "Unsupported protocol version" - ): [PacketTypes.CONNACK]}, + ): [PacketTypes.CONNACK] + }, 133: { QCoreApplication.translate( "MqttReasonCodeNames", "Client identifier not valid" - ): [PacketTypes.CONNACK]}, + ): [PacketTypes.CONNACK] + }, 134: { QCoreApplication.translate( "MqttReasonCodeNames", "Bad user name or password" - ): [PacketTypes.CONNACK]}, + ): [PacketTypes.CONNACK] + }, 135: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Not authorized" - ): [PacketTypes.CONNACK, PacketTypes.PUBACK, - PacketTypes.PUBREC, PacketTypes.SUBACK, - PacketTypes.UNSUBACK, PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Not authorized"): [ + PacketTypes.CONNACK, + PacketTypes.PUBACK, + PacketTypes.PUBREC, + PacketTypes.SUBACK, + PacketTypes.UNSUBACK, + PacketTypes.DISCONNECT, + ] + }, 136: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Server unavailable" - ): [PacketTypes.CONNACK]}, + QCoreApplication.translate("MqttReasonCodeNames", "Server unavailable"): [ + PacketTypes.CONNACK + ] + }, 137: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Server busy" - ): [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Server busy"): [ + PacketTypes.CONNACK, + PacketTypes.DISCONNECT, + ] + }, 138: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Banned" - ): [PacketTypes.CONNACK]}, + QCoreApplication.translate("MqttReasonCodeNames", "Banned"): [ + PacketTypes.CONNACK + ] + }, 139: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Server shutting down" - ): [PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Server shutting down"): [ + PacketTypes.DISCONNECT + ] + }, 140: { QCoreApplication.translate( "MqttReasonCodeNames", "Bad authentication method" - ): [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + ): [PacketTypes.CONNACK, PacketTypes.DISCONNECT] + }, 141: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Keep alive timeout" - ): [PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Keep alive timeout"): [ + PacketTypes.DISCONNECT + ] + }, 142: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Session taken over" - ): [PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Session taken over"): [ + PacketTypes.DISCONNECT + ] + }, 143: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Topic filter invalid" - ): [PacketTypes.SUBACK, PacketTypes.UNSUBACK, - PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Topic filter invalid"): [ + PacketTypes.SUBACK, + PacketTypes.UNSUBACK, + PacketTypes.DISCONNECT, + ] + }, 144: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Topic name invalid" - ): [PacketTypes.CONNACK, PacketTypes.PUBACK, - PacketTypes.PUBREC, PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Topic name invalid"): [ + PacketTypes.CONNACK, + PacketTypes.PUBACK, + PacketTypes.PUBREC, + PacketTypes.DISCONNECT, + ] + }, 145: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Packet identifier in use" - ): [PacketTypes.PUBACK, PacketTypes.PUBREC, - PacketTypes.SUBACK, PacketTypes.UNSUBACK]}, + QCoreApplication.translate("MqttReasonCodeNames", "Packet identifier in use"): [ + PacketTypes.PUBACK, + PacketTypes.PUBREC, + PacketTypes.SUBACK, + PacketTypes.UNSUBACK, + ] + }, 146: { QCoreApplication.translate( "MqttReasonCodeNames", "Packet identifier not found" - ): [PacketTypes.PUBREL, PacketTypes.PUBCOMP]}, + ): [PacketTypes.PUBREL, PacketTypes.PUBCOMP] + }, 147: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Receive maximum exceeded" - ): [PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Receive maximum exceeded"): [ + PacketTypes.DISCONNECT + ] + }, 148: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Topic alias invalid" - ): [PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Topic alias invalid"): [ + PacketTypes.DISCONNECT + ] + }, 149: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Packet too large" - ): [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Packet too large"): [ + PacketTypes.CONNACK, + PacketTypes.DISCONNECT, + ] + }, 150: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Message rate too high" - ): [PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Message rate too high"): [ + PacketTypes.DISCONNECT + ] + }, 151: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Quota exceeded" - ): [PacketTypes.CONNACK, PacketTypes.PUBACK, - PacketTypes.PUBREC, PacketTypes.SUBACK, - PacketTypes.DISCONNECT], }, + QCoreApplication.translate("MqttReasonCodeNames", "Quota exceeded"): [ + PacketTypes.CONNACK, + PacketTypes.PUBACK, + PacketTypes.PUBREC, + PacketTypes.SUBACK, + PacketTypes.DISCONNECT, + ], + }, 152: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Administrative action" - ): [PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Administrative action"): [ + PacketTypes.DISCONNECT + ] + }, 153: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Payload format invalid" - ): [PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Payload format invalid"): [ + PacketTypes.PUBACK, + PacketTypes.PUBREC, + PacketTypes.DISCONNECT, + ] + }, 154: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Retain not supported" - ): [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Retain not supported"): [ + PacketTypes.CONNACK, + PacketTypes.DISCONNECT, + ] + }, 155: { - QCoreApplication.translate( - "MqttReasonCodeNames", "QoS not supported" - ): [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "QoS not supported"): [ + PacketTypes.CONNACK, + PacketTypes.DISCONNECT, + ] + }, 156: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Use another server" - ): [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Use another server"): [ + PacketTypes.CONNACK, + PacketTypes.DISCONNECT, + ] + }, 157: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Server moved" - ): [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Server moved"): [ + PacketTypes.CONNACK, + PacketTypes.DISCONNECT, + ] + }, 158: { QCoreApplication.translate( "MqttReasonCodeNames", "Shared subscription not supported" - ): [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, + ): [PacketTypes.SUBACK, PacketTypes.DISCONNECT] + }, 159: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Connection rate exceeded" - ): [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Connection rate exceeded"): [ + PacketTypes.CONNACK, + PacketTypes.DISCONNECT, + ] + }, 160: { - QCoreApplication.translate( - "MqttReasonCodeNames", "Maximum connect time" - ): [PacketTypes.DISCONNECT]}, + QCoreApplication.translate("MqttReasonCodeNames", "Maximum connect time"): [ + PacketTypes.DISCONNECT + ] + }, 161: { QCoreApplication.translate( "MqttReasonCodeNames", "Subscription identifiers not supported" - ): [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, + ): [PacketTypes.SUBACK, PacketTypes.DISCONNECT] + }, 162: { QCoreApplication.translate( "MqttReasonCodeNames", "Wildcard subscription not supported" - ): [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, + ): [PacketTypes.SUBACK, PacketTypes.DISCONNECT] + }, } @@ -210,7 +287,7 @@ """ Function to get the readable reason code string given the result code and the packet type. - + @param rc result code @type int @param packetType packet type @@ -220,15 +297,16 @@ """ if rc not in MqttReasonCodeNames: return QCoreApplication.translate( - "MqttReasonCodeNames", "Unknown result code ({0})").format(rc) - + "MqttReasonCodeNames", "Unknown result code ({0})" + ).format(rc) + messages = MqttReasonCodeNames[rc] - messagesList = [message for message in messages.keys() - if packetType in messages[message]] + messagesList = [ + message for message in messages.keys() if packetType in messages[message] + ] if len(messagesList) == 0: return QCoreApplication.translate( - "MqttReasonCodeNames", - "Unknown result code ({0}) for packet type '{1}'" + "MqttReasonCodeNames", "Unknown result code ({0}) for packet type '{1}'" ).format(rc, packetType) - + return messagesList[0]
--- a/MqttMonitor/MqttUserPropertiesEditor.py Thu Dec 30 16:36:40 2021 +0100 +++ b/MqttMonitor/MqttUserPropertiesEditor.py Wed Sep 21 09:42:33 2022 +0200 @@ -9,7 +9,12 @@ from PyQt6.QtCore import pyqtSlot, Qt from PyQt6.QtWidgets import ( - QDialog, QDialogButtonBox, QTableWidgetItem, QVBoxLayout, QLabel, QWidget + QDialog, + QDialogButtonBox, + QTableWidgetItem, + QVBoxLayout, + QLabel, + QWidget, ) from .Ui_MqttUserPropertiesEditor import Ui_MqttUserPropertiesEditor @@ -21,41 +26,40 @@ """ Class implementing an editor for MQTT v5 user properties. """ + def __init__(self, parent=None): """ Constructor - + @param parent reference to the parent widget (defaults to None) @type QWidget (optional) """ super().__init__(parent) self.setupUi(self) - + self.addButton.setIcon(UI.PixmapCache.getIcon("plus")) self.deleteButton.setIcon(UI.PixmapCache.getIcon("minus")) self.clearButton.setIcon(UI.PixmapCache.getIcon("editDelete")) - + self.clearButton.clicked.connect(self.clear) - + self.deleteButton.setEnabled(False) - + @pyqtSlot() def on_propertiesTable_itemSelectionChanged(self): """ Private slot to handle the selection of rows. """ - self.deleteButton.setEnabled( - bool(self.propertiesTable.selectedItems())) - + self.deleteButton.setEnabled(bool(self.propertiesTable.selectedItems())) + @pyqtSlot() def on_addButton_clicked(self): """ Private slot to add a row to the table. """ self.propertiesTable.setRowCount(self.propertiesTable.rowCount() + 1) - self.propertiesTable.setCurrentCell( - self.propertiesTable.rowCount() - 1, 0) - + self.propertiesTable.setCurrentCell(self.propertiesTable.rowCount() - 1, 0) + @pyqtSlot() def on_deleteButton_clicked(self): """ @@ -66,7 +70,7 @@ for bottomRow, topRow in sorted(selectedRows, reverse=True): for row in range(bottomRow, topRow - 1, -1): self.propertiesTable.removeRow(row) - + @pyqtSlot() def clear(self): """ @@ -75,11 +79,11 @@ self.propertiesTable.clearContents() self.propertiesTable.setRowCount(10) self.propertiesTable.setCurrentCell(0, 0) - + def setProperties(self, properties): """ Public method to populate the editor with a list of user properties. - + @param properties list of defined user properties @type list of tuple of (str, str) """ @@ -90,16 +94,16 @@ self.propertiesTable.setItem(row, 1, QTableWidgetItem(value)) else: self.clear() - + def getProperties(self): """ Public method to get the list of defined user properties. - + @return list of defined user properties @rtype list of tuple of (str, str) """ properties = [] - + for row in range(self.propertiesTable.rowCount()): keyItem = self.propertiesTable.item(row, 0) key = keyItem.text() if keyItem else "" @@ -107,7 +111,7 @@ valueItem = self.propertiesTable.item(row, 1) value = valueItem.text() if valueItem else "" properties.append([key, value]) - + return properties @@ -115,10 +119,11 @@ """ Class implementing an editor dialog for MQTT v5 user properties. """ + def __init__(self, header, properties, parent=None): """ Constructor - + @param header text to be shown in the dialog header label @type str @param properties list of defined user properties @@ -127,37 +132,37 @@ @type QWidget (optional) """ super().__init__(parent) - + self.setObjectName("MqttUserPropertiesEditor") self.resize(400, 300) self.setSizeGripEnabled(True) self.setWindowTitle(self.tr("User Properties")) - + self.__layout = QVBoxLayout(self) - + self.__headerLabel = QLabel(header, self) self.__layout.addWidget(self.__headerLabel) - + self.__propertiesEditor = MqttUserPropertiesEditor(self) self.__layout.addWidget(self.__propertiesEditor) - + self.__buttonBox = QDialogButtonBox(self) self.__buttonBox.setOrientation(Qt.Orientation.Horizontal) self.__buttonBox.setStandardButtons( - QDialogButtonBox.StandardButton.Cancel | - QDialogButtonBox.StandardButton.Ok) + QDialogButtonBox.StandardButton.Cancel | QDialogButtonBox.StandardButton.Ok + ) self.__buttonBox.setObjectName("buttonBox") self.__layout.addWidget(self.__buttonBox) - + self.__buttonBox.accepted.connect(self.accept) self.__buttonBox.rejected.connect(self.reject) - + self.__propertiesEditor.setProperties(properties) - + def getProperties(self): """ Public method to get the list of defined user properties. - + @return list of defined user properties @rtype list of tuple of (str, str) """
--- a/PluginMqttMonitor.epj Thu Dec 30 16:36:40 2021 +0100 +++ b/PluginMqttMonitor.epj Wed Sep 21 09:42:33 2022 +0200 @@ -1,19 +1,21 @@ { "header": { "comment": "eric project file for project PluginMqttMonitor", - "copyright": "Copyright (C) 2021 Detlev Offenbach, detlev@die-offenbachs.de" + "copyright": "Copyright (C) 2022 Detlev Offenbach, detlev@die-offenbachs.de" }, "project": { "AUTHOR": "Detlev Offenbach", "CHECKERSPARMS": { "Pep8Checker": { "AnnotationsChecker": { + "AllowStarArgAny": false, "AllowUntypedDefs": false, "AllowUntypedNested": false, "DispatchDecorators": [ "singledispatch", "singledispatchmethod" ], + "ForceFutureAnnotations": false, "MaximumComplexity": 3, "MaximumLength": 7, "MinimumCoverage": 75, @@ -59,20 +61,25 @@ }, "CopyrightAuthor": "", "CopyrightMinFileSize": 0, - "DocstringType": "eric", + "DocstringType": "eric_black", "EnabledCheckerCategories": "C, D, E, M, N, S, Y, W", "ExcludeFiles": "*/Ui_*.py, */*_rc.py", - "ExcludeMessages": "C101,E265,E266,E305,E402,M201,M301,M302,M303,M304,M305,M306,M307,M308,M311,M312,M313,M314,M315,M321,M701,M702,M811,M834,N802,N803,N807,N808,N821,W293,W504,Y119,Y401,Y402", + "ExcludeMessages": "C101,E265,E266,E305,E402,M201,M301,M302,M303,M304,M305,M306,M307,M308,M311,M312,M313,M314,M315,M321,M701,M702,M811,M834,N802,N803,N807,N808,N821,W293,W503,Y119,Y401,Y402", "FixCodes": "", "FixIssues": false, "FutureChecker": "", "HangClosing": false, + "ImportsChecker": { + "ApplicationPackageNames": [], + "BanRelativeImports": "", + "BannedModules": [] + }, "IncludeMessages": "", "LineComplexity": 25, "LineComplexityScore": 10, "MaxCodeComplexity": 10, - "MaxDocLineLength": 79, - "MaxLineLength": 79, + "MaxDocLineLength": 88, + "MaxLineLength": 88, "NoFixCodes": "E501", "RepeatMessages": true, "SecurityChecker": { @@ -129,6 +136,7 @@ } }, "EMAIL": "detlev@die-offenbachs.de", + "EMBEDDED_VENV": false, "EOL": 1, "FILETYPES": { "*.epj": "OTHERS", @@ -152,11 +160,11 @@ "makefile": "OTHERS" }, "FORMS": [ + "MqttMonitor/ConfigurationPage/MqttPage.ui", "MqttMonitor/MqttConnectionOptionsDialog.ui", "MqttMonitor/MqttConnectionProfilesDialog.ui", "MqttMonitor/MqttMonitorWidget.ui", - "MqttMonitor/MqttUserPropertiesEditor.ui", - "MqttMonitor/ConfigurationPage/MqttPage.ui" + "MqttMonitor/MqttUserPropertiesEditor.ui" ], "HASH": "8b864e3e4a3495e242eae3cb3ef4dc8522bf6ce7", "IDLPARAMS": { @@ -166,6 +174,7 @@ }, "INTERFACES": [], "LEXERASSOCS": {}, + "LICENSE": "GNU General Public License v3 or later (GPLv3+)", "MAINSCRIPT": "PluginMqttMonitor.py", "MAKEPARAMS": { "MakeEnabled": false, @@ -192,10 +201,27 @@ "MqttMonitor/icons/quickopen-dark.svg", "MqttMonitor/icons/quickopen-light.svg", "PKGLIST", - "PluginMqttMonitor.zip", - "PluginMqttMonitor.epj" + "PluginMqttMonitor.epj", + "PluginMqttMonitor.zip" ], - "OTHERTOOLSPARMS": {}, + "OTHERTOOLSPARMS": { + "Black": { + "exclude": "/(\\.direnv|\\.eggs|\\.git|\\.hg|\\.mypy_cache|\\.nox|\\.tox|\\.venv|venv|\\.svn|_build|buck-out|build|dist|__pypackages__)/", + "extend-exclude": "", + "force-exclude": "", + "line-length": 88, + "skip-magic-trailing-comma": false, + "skip-string-normalization": false, + "source": "project", + "target-version": [ + "py311", + "py310", + "py39", + "py38", + "py37" + ] + } + }, "PACKAGERSPARMS": {}, "PROGLANGUAGE": "Python3", "PROJECTTYPE": "E7Plugin", @@ -209,22 +235,23 @@ }, "RESOURCES": [], "SOURCES": [ + "MqttMonitor/ConfigurationPage/MqttPage.py", + "MqttMonitor/ConfigurationPage/__init__.py", "MqttMonitor/MqttClient.py", "MqttMonitor/MqttConnectionOptionsDialog.py", "MqttMonitor/MqttConnectionProfilesDialog.py", "MqttMonitor/MqttMonitorWidget.py", + "MqttMonitor/MqttProtocols.py", + "MqttMonitor/MqttReasonCodes.py", + "MqttMonitor/MqttUserPropertiesEditor.py", "MqttMonitor/__init__.py", "PluginMqttMonitor.py", - "__init__.py", - "MqttMonitor/MqttReasonCodes.py", - "MqttMonitor/MqttUserPropertiesEditor.py", - "MqttMonitor/ConfigurationPage/__init__.py", - "MqttMonitor/ConfigurationPage/MqttPage.py", - "MqttMonitor/MqttProtocols.py" + "__init__.py" ], "SPELLEXCLUDES": "", "SPELLLANGUAGE": "en", "SPELLWORDS": "", + "TESTING_FRAMEWORK": "", "TRANSLATIONEXCEPTIONS": [], "TRANSLATIONPATTERN": "MqttMonitor/i18n/mqttmonitor_%language%.ts", "TRANSLATIONS": [
--- a/PluginMqttMonitor.py Thu Dec 30 16:36:40 2021 +0100 +++ b/PluginMqttMonitor.py Wed Sep 21 09:42:33 2022 +0200 @@ -26,7 +26,7 @@ author = "Detlev Offenbach <detlev@die-offenbachs.de>" autoactivate = True deactivateable = True -version = "10.2.1" +version = "10.2.2" className = "MqttMonitorPlugin" packageName = "MqttMonitor" shortDescription = "Plug-in implementing a tool to connect to a MQTT broker" @@ -50,7 +50,7 @@ def createMqttPage(configDlg): """ Module function to create the autocompletion configuration page. - + @param configDlg reference to the configuration dialog @type ConfigurationWidget @return reference to the configuration page @@ -58,7 +58,7 @@ """ global mqttPluginObject from MqttMonitor.ConfigurationPage.MqttPage import MqttPage - + page = MqttPage(mqttPluginObject) return page @@ -66,37 +66,41 @@ def getConfigData(): """ Module function returning data as required by the configuration dialog. - + @return dictionary containing the relevant data @rtype dict """ usesDarkPalette = ericApp().usesDarkPalette() iconSuffix = "dark" if usesDarkPalette else "light" - + return { "mqttPage": [ QCoreApplication.translate("MqttMonitorPlugin", "MQTT Monitor"), - os.path.join( - "MqttMonitor", "icons", "mqtt22-{0}".format(iconSuffix)), - createMqttPage, None, None], + os.path.join("MqttMonitor", "icons", "mqtt22-{0}".format(iconSuffix)), + createMqttPage, + None, + None, + ], } def exeDisplayData(): """ Module function to support the display of some executable info. - + @return dictionary containing the data to query the presence of the executable @rtype dict """ try: import paho.mqtt + version = paho.mqtt.__version__ except ImportError: version = QCoreApplication.translate( - "MqttMonitorPlugin", "(package not available)") - + "MqttMonitorPlugin", "(package not available)" + ) + return { "programEntry": False, "header": QCoreApplication.translate("MqttMonitorPlugin", "MQTT"), @@ -116,111 +120,118 @@ """ Class implementing the MQTT Monitor plug-in. """ + PreferencesKey = "MqttMonitor" - + def __init__(self, ui): """ Constructor - + @param ui reference to the user interface object @type UI.UserInterface """ super().__init__(ui) self.__ui = ui self.__initialize() - + self.__defaults = { - "RecentBrokersWithPort": "[]", # JSON formatted empty list - "BrokerProfiles": "{}", # JSON formatted empty dict + "RecentBrokersWithPort": "[]", # JSON formatted empty list + "BrokerProfiles": "{}", # JSON formatted empty dict # __IGNORE_WARNING_M613__ - "MostRecentProfile": "", # most recently used profile - "PublishProperties": "{}", # JSON formatted empty dict + "MostRecentProfile": "", # most recently used profile + "PublishProperties": "{}", # JSON formatted empty dict # __IGNORE_WARNING_M613__ - "SubscribeProperties": "{}", # JSON formatted empty dict + "SubscribeProperties": "{}", # JSON formatted empty dict # __IGNORE_WARNING_M613__ - "UnsubscribeProperties": "{}", # JSON formatted empty dict + "UnsubscribeProperties": "{}", # JSON formatted empty dict # __IGNORE_WARNING_M613__ "DefaultProtocol": MqttProtocols.MQTTv311, "RecentBrokersNumber": 20, "RecentTopicsNumber": 20, - "RecentTopics": "[]", # JSON formatted empty list + "RecentTopics": "[]", # JSON formatted empty list } - + self.__translator = None self.__loadTranslator() - + def __initialize(self): """ Private slot to (re)initialize the plugin. """ self.__widget = None - + def activate(self): """ Public method to activate this plug-in. - + @return tuple of None and activation status @rtype tuple of (None, bool) """ global error, mqttPluginObject - error = "" # clear previous error + error = "" # clear previous error mqttPluginObject = self - + try: - import paho.mqtt # __IGNORE_WARNING__ + import paho.mqtt # __IGNORE_WARNING__ except ImportError: error = self.tr("The 'paho-mqtt' package is not available.") return None, False - + from MqttMonitor.MqttMonitorWidget import MqttMonitorWidget - + usesDarkPalette = ericApp().usesDarkPalette() iconSuffix = "dark" if usesDarkPalette else "light" - + self.__widget = MqttMonitorWidget(self, usesDarkPalette) iconName = ( "sbMqttMonitor96" - if self.__ui.getLayoutType() == "Sidebars" else - "mqtt22-{0}".format(iconSuffix) + if self.__ui.getLayoutType() == "Sidebars" + else "mqtt22-{0}".format(iconSuffix) ) self.__ui.addSideWidget( - self.__ui.RightSide, self.__widget, - UI.PixmapCache.getIcon( - os.path.join("MqttMonitor", "icons", iconName)), - self.tr("MQTT Monitor")) - + self.__ui.RightSide, + self.__widget, + UI.PixmapCache.getIcon(os.path.join("MqttMonitor", "icons", iconName)), + self.tr("MQTT Monitor"), + ) + self.__activateAct = EricAction( - self.tr('MQTT Monitor'), - self.tr('M&QTT Monitor'), + self.tr("MQTT Monitor"), + self.tr("M&QTT Monitor"), QKeySequence(self.tr("Alt+Shift+Q")), - 0, self, - 'mqtt_monitor_activate') - self.__activateAct.setStatusTip(self.tr( - "Switch the input focus to the MQTT Monitor window.")) - self.__activateAct.setWhatsThis(self.tr( - """<b>Activate MQTT Monitor</b>""" - """<p>This switches the input focus to the MQTT Monitor""" - """ window.</p>""" - )) + 0, + self, + "mqtt_monitor_activate", + ) + self.__activateAct.setStatusTip( + self.tr("Switch the input focus to the MQTT Monitor window.") + ) + self.__activateAct.setWhatsThis( + self.tr( + """<b>Activate MQTT Monitor</b>""" + """<p>This switches the input focus to the MQTT Monitor""" + """ window.</p>""" + ) + ) self.__activateAct.triggered.connect(self.__activateWidget) - - self.__ui.addEricActions([self.__activateAct], 'ui') + + self.__ui.addEricActions([self.__activateAct], "ui") menu = self.__ui.getMenu("subwindow") menu.addAction(self.__activateAct) - + return None, True - + def deactivate(self): """ Public method to deactivate this plug-in. """ menu = self.__ui.getMenu("subwindow") menu.removeAction(self.__activateAct) - self.__ui.removeEricActions([self.__activateAct], 'ui') + self.__ui.removeEricActions([self.__activateAct], "ui") self.__ui.removeSideWidget(self.__widget) - + self.__initialize() - + def __loadTranslator(self): """ Private method to load the translation file. @@ -229,7 +240,8 @@ loc = self.__ui.getLocale() if loc and loc != "C": locale_dir = os.path.join( - os.path.dirname(__file__), "MqttMonitor", "i18n") + os.path.dirname(__file__), "MqttMonitor", "i18n" + ) translation = "mqttmonitor_{0}".format(loc) translator = QTranslator(None) loaded = translator.load(translation, locale_dir) @@ -237,16 +249,18 @@ self.__translator = translator ericApp().installTranslator(self.__translator) else: - print("Warning: translation file '{0}' could not be" - " loaded.".format(translation)) + print( + "Warning: translation file '{0}' could not be" + " loaded.".format(translation) + ) print("Using default.") - + def __activateWidget(self): """ Private slot to handle the activation of the MQTT Monitor. """ uiLayoutType = self.__ui.getLayoutType() - + if uiLayoutType == "Toolboxes": self.__ui.rToolboxDock.show() self.__ui.rToolbox.setCurrentWidget(self.__widget) @@ -256,65 +270,88 @@ else: self.__widget.show() self.__widget.setFocus(Qt.FocusReason.ActiveWindowFocusReason) - + def getPreferences(self, key): """ Public method to retrieve the various settings. - + @param key key of the setting @type str @return value of the requested setting @rtype Any """ - if key in ("RecentBrokersWithPort", "BrokerProfiles", - "SubscribeProperties", "UnsubscribeProperties", - "PublishProperties", "RecentTopics"): - return json.loads(Preferences.Prefs.settings.value( - self.PreferencesKey + "/" + key, self.__defaults[key])) - elif key in ("DefaultProtocol", ): - return MqttProtocols(int(Preferences.Prefs.settings.value( - self.PreferencesKey + "/" + key, self.__defaults[key]))) - elif key in ("DefaultProtocol", "RecentBrokersNumber", - "RecentTopicsNumber"): - return int(Preferences.Prefs.settings.value( - self.PreferencesKey + "/" + key, self.__defaults[key])) + if key in ( + "RecentBrokersWithPort", + "BrokerProfiles", + "SubscribeProperties", + "UnsubscribeProperties", + "PublishProperties", + "RecentTopics", + ): + return json.loads( + Preferences.Prefs.settings.value( + self.PreferencesKey + "/" + key, self.__defaults[key] + ) + ) + elif key in ("DefaultProtocol",): + return MqttProtocols( + int( + Preferences.Prefs.settings.value( + self.PreferencesKey + "/" + key, self.__defaults[key] + ) + ) + ) + elif key in ("DefaultProtocol", "RecentBrokersNumber", "RecentTopicsNumber"): + return int( + Preferences.Prefs.settings.value( + self.PreferencesKey + "/" + key, self.__defaults[key] + ) + ) else: return Preferences.Prefs.settings.value( - self.PreferencesKey + "/" + key, self.__defaults[key]) - + self.PreferencesKey + "/" + key, self.__defaults[key] + ) + def setPreferences(self, key, value): """ Public method to store the various settings. - + @param key key of the setting to be set @type str @param value value to be set @type Any """ - if key in ("RecentBrokersWithPort", "BrokerProfiles", - "SubscribeProperties", "UnsubscribeProperties", - "PublishProperties", "RecentTopics"): + if key in ( + "RecentBrokersWithPort", + "BrokerProfiles", + "SubscribeProperties", + "UnsubscribeProperties", + "PublishProperties", + "RecentTopics", + ): Preferences.Prefs.settings.setValue( - self.PreferencesKey + "/" + key, json.dumps(value)) - elif key in ("DefaultProtocol", ): + self.PreferencesKey + "/" + key, json.dumps(value) + ) + elif key in ("DefaultProtocol",): Preferences.Prefs.settings.setValue( - self.PreferencesKey + "/" + key, int(value)) + self.PreferencesKey + "/" + key, int(value) + ) else: - Preferences.Prefs.settings.setValue( - self.PreferencesKey + "/" + key, value) + Preferences.Prefs.settings.setValue(self.PreferencesKey + "/" + key, value) def installDependencies(pipInstall): """ Function to install dependencies of this plug-in. - + @param pipInstall function to be called with a list of package names. @type function """ try: - import paho.mqtt # __IGNORE_WARNING__ + import paho.mqtt # __IGNORE_WARNING__ except ImportError: pipInstall(["paho-mqtt"]) + # # eflag: noqa = M801