本文章目的:
1.带你了解 paho-mqtt for python库的使用
2.带你了解wxPython库的使用
3.如何封装mqtt客户端和ui库
运行效果:
使用wxpython UI库开发MQTT客户端,最终效果如下:
操作:
运行后,先点击 连接服务器 按钮,然后填入主题和消息内容,点击发布主题按钮
默认程序订阅主题: python/mqtt,fzb/event
准备备工作:
安装必要的库
pip install -U wxPython
pip install paho-mqtt
代码:
mqttClient.py
from paho.mqtt import client as mqtt_client
import random
import time
import logging
import json
import MyFrame
class mqttApp:
"""MQTT客户端使用类
基于TCP传输 MQTT3.1.1
"""
FIRST_RECONNECT_DELAY = 1
RECONNECT_RATE = 2
MAX_RECONNECT_COUNT = 12
MAX_RECONNECT_DELAY = 60
client_id = f'fzb-{random.randint(0, 1000)}'
def __init__(self, topics: tuple = None):
"""创建MQTT对象,设置回调,初始化参数等
"""
self.client = mqtt_client.Client(client_id = mqttApp.client_id, callback_api_version = mqtt_client.CallbackAPIVersion.VERSION2)
self.client.on_connect = self.on_connect
self.client.on_message = self.on_message
self.client.disconnect = self.on_disconnect
self.topics = topics
self.flag_exit = False
def connect(self, broker = "broker.emqx.io", port=1883, keepalive=60):
"""创建MQTT 连接
"""
# self.client.username_pw_set('emqx', 'public')
self.client.connect(broker, port, keepalive)
def subscribe(self, topic: str, qos: int = 0):
"""订阅主题
"""
print(f"subscribe '{topic}' topic")
self.client.subscribe(topic=topic, qos=qos)
def publish(self, topic, payload=None, qos=0, retain=False):
"""发布主题
"""
self.client.publish(topic=topic, payload=payload, qos=qos, retain=retain)
def on_connect(self, client, userdata, flags, rc, properties):
"""连接服务器成功回调
"""
if rc == 0:
print("connected to MQTT Broker!")
if self.frame is not None:
self.frame.m_staticText_status.SetLabelText("连接成功!")
self.frame.m_button_connect.SetLabelText("断开连接")
#订阅主题
if self.topics is not None:
for topic in self.topics:
print(f"subscribe '{topic}' topic")
self.client.subscribe(topic)
else:
if self.frame is not None:
self.frame.m_staticText_status.SetLabelText("未连接")
self.frame.m_button_connect.SetLabelText("连接服务器")
print(f"failed to connect, return code {rc}")
def on_message(self,client, userdata, msg):
"""订阅消息回调
"""
print(f"received '{msg.payload.decode()}' from '{msg.topic}' topic")
if self.frame is not None:
self.frame.m_textCtrl_message.AppendText(f"received '{msg.payload.decode()}' from '{msg.topic}' topic\n")
def on_disconnect(self, client, userdata, rc):
"""MQTT连接断开回调
"""
print(f"disconnected with result code: {rc}")
reconnect_count, reconnect_delay = 0, mqttApp.FIRST_RECONNECT_DELAY
while reconnect_count < mqttApp.MAX_RECONNECT_COUNT:
print(f"reconnecting in {reconnect_count} seconds...")
time.sleep(reconnect_delay)
try:
client.reconnect()
print("reconnected successfully!")
return
except Exception as err:
print(f"{err}. reconnect failed, retrying...")
reconnect_delay *= mqttApp.RECONNECT_RATE
reconnect_delay = min(reconnect_delay, mqttApp.MAX_RECONNECT_DELAY)
reconnect_count += 1
print(f"reconnect failed after {reconnect_count} attempts.Exiting...")
self.flag_exit = True
def loop_forver(self):
"""开始进入堵塞循环,
适用运行程序只有这一个应用
"""
self.client.loop_forever()
def loop_start(self):
"""开始一个线程循环,不堵塞主线程
用户自己在主线程中维持程序运行
如例程中main_loop()
"""
self.client.loop_start()
def loop_stop(self):
"""和 loop_start()配对使用
"""
self.client.loop_stop()
def set_frame_obj(self, obj:MyFrame.MyFrame = None):
self.frame = obj
MyFrame.py
# -*- coding: utf-8 -*-
###########################################################################
## Python code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
## http://www.wxformbuilder.org/
##
## PLEASE DO *NOT* EDIT THIS FILE!
###########################################################################
import wx
import wx.xrc
import gettext
_ = gettext.gettext
###########################################################################
## Class MyFrame
###########################################################################
class MyFrame ( wx.Frame ):
def __init__( self, parent ):
wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = _(u"MQTT客户端"), pos = wx.DefaultPosition, size = wx.Size( 700,600 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
self.SetSizeHints( wx.Size( 700,600 ), wx.Size( -1,-1 ) )
self.SetBackgroundColour( wx.Colour( 176, 230, 255 ) )
bSizer_main = wx.BoxSizer( wx.VERTICAL )
bSizer7 = wx.BoxSizer( wx.HORIZONTAL )
self.m_staticText2 = wx.StaticText( self, wx.ID_ANY, _(u"连接状态:"), wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText2.Wrap( -1 )
bSizer7.Add( self.m_staticText2, 0, wx.ALL, 5 )
self.m_staticText_status = wx.StaticText( self, wx.ID_ANY, _(u"未连接"), wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText_status.Wrap( -1 )
bSizer7.Add( self.m_staticText_status, 0, wx.ALL, 5 )
bSizer7.Add( ( 0, 0), 1, wx.EXPAND, 5 )
self.m_button_connect = wx.Button( self, wx.ID_ANY, _(u"连接服务器"), wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer7.Add( self.m_button_connect, 0, wx.ALL, 5 )
bSizer_main.Add( bSizer7, 1, wx.EXPAND, 5 )
sbSizer4 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, _(u"消息接收区域") ), wx.VERTICAL )
self.m_textCtrl_message = wx.TextCtrl( sbSizer4.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_MULTILINE|wx.VSCROLL )
sbSizer4.Add( self.m_textCtrl_message, 1, wx.ALL|wx.EXPAND, 5 )
bSizer_main.Add( sbSizer4, 4, wx.EXPAND, 5 )
bSizer_pub = wx.BoxSizer( wx.VERTICAL )
bSizer6 = wx.BoxSizer( wx.HORIZONTAL )
self.m_staticText7 = wx.StaticText( self, wx.ID_ANY, _(u"Qos:"), wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText7.Wrap( -1 )
bSizer6.Add( self.m_staticText7, 0, wx.ALL, 10 )
m_comboBox_qosChoices = [ _(u"0 最多一次"), _(u"1 至少一次"), _(u"2 仅一次"), wx.EmptyString ]
self.m_comboBox_qos = wx.ComboBox( self, wx.ID_ANY, _(u"0 最多一次"), wx.DefaultPosition, wx.DefaultSize, m_comboBox_qosChoices, 0 )
self.m_comboBox_qos.SetSelection( 0 )
bSizer6.Add( self.m_comboBox_qos, 0, wx.ALL, 10 )
self.m_radioBtn_retain = wx.RadioButton( self, wx.ID_ANY, _(u"Retain"), wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer6.Add( self.m_radioBtn_retain, 0, wx.ALL, 10 )
bSizer6.Add( ( 0, 0), 1, wx.EXPAND, 10 )
self.m_button_clear = wx.Button( self, wx.ID_ANY, _(u"清空"), wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer6.Add( self.m_button_clear, 0, wx.ALL, 10 )
bSizer_pub.Add( bSizer6, 1, wx.EXPAND, 5 )
self.m_staticText4 = wx.StaticText( self, wx.ID_ANY, _(u"主题:"), wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText4.Wrap( -1 )
bSizer_pub.Add( self.m_staticText4, 0, wx.ALL, 10 )
self.m_textCtrl_topic = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer_pub.Add( self.m_textCtrl_topic, 0, wx.ALL|wx.EXPAND, 10 )
self.m_staticText5 = wx.StaticText( self, wx.ID_ANY, _(u"内容:"), wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText5.Wrap( -1 )
bSizer_pub.Add( self.m_staticText5, 0, wx.ALL, 10 )
self.m_textCtrl_payload = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer_pub.Add( self.m_textCtrl_payload, 0, wx.ALL|wx.EXPAND, 10 )
self.m_button_pub = wx.Button( self, wx.ID_ANY, _(u"发布主题"), wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer_pub.Add( self.m_button_pub, 0, wx.ALL, 10 )
bSizer_main.Add( bSizer_pub, 1, wx.EXPAND, 5 )
self.SetSizer( bSizer_main )
self.Layout()
self.Centre( wx.BOTH )
# Connect Events
self.m_button_connect.Bind( wx.EVT_BUTTON, self.on_button_clicked_connect )
self.m_button_clear.Bind( wx.EVT_BUTTON, self.on_button_clicked_clear )
self.m_button_pub.Bind( wx.EVT_BUTTON, self.on_button_clicked_pub )
def __del__( self ):
pass
# Virtual event handlers, override them in your derived class
def on_button_clicked_connect( self, event ):
if self.client is not None:
#使用默认配置连接MQTT服务器
self.client.connect()
#开始事件循环线程
self.client.loop_start()
event.Skip()
def on_button_clicked_clear( self, event ):
self.m_textCtrl_message.Clear()
event.Skip()
def on_button_clicked_pub( self, event ):
topic_str = self.m_textCtrl_topic.GetLineText(0)
payload_str = self.m_textCtrl_payload.GetLineText(0)
print(f"pub topic = {topic_str} payload = {payload_str}")
self.client.publish(topic = str(topic_str), payload=str(payload_str))
event.Skip()
def set_mqtt_object(self, obj=None):
self.client = obj
main.py
# -*- coding: utf-8 -*-
import wx
import mqttClient as mqtt
from MyFrame import MyFrame
import time
def main_loop():
while True:
pass
if __name__ == '__main__':
print("start mqtt client application.....")
#创建对象,并传递订阅主题
client = mqtt.mqttApp(("python/mqtt","fzb/event"))
#使用默认配置连接MQTT服务器
# client.connect()
#开始事件循环线程
# client.loop_start()
print("start wxPython application......")
app = wx.App()
frm = MyFrame(None)
frm.set_mqtt_object(client)
client.set_frame_obj(frm)
frm.Show()
app.MainLoop()
#保持程序一直运行
main_loop()
client.loop_stop()