创建项目框架
This commit is contained in:
commit
d74d46705d
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.pyc
|
89
poetry.lock
generated
Normal file
89
poetry.lock
generated
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paho-mqtt"
|
||||||
|
version = "2.1.0"
|
||||||
|
description = "MQTT version 5.0/3.1.1 client class"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
groups = ["main"]
|
||||||
|
files = [
|
||||||
|
{file = "paho_mqtt-2.1.0-py3-none-any.whl", hash = "sha256:6db9ba9b34ed5bc6b6e3812718c7e06e2fd7444540df2455d2c51bd58808feee"},
|
||||||
|
{file = "paho_mqtt-2.1.0.tar.gz", hash = "sha256:12d6e7511d4137555a3f6ea167ae846af2c7357b10bc6fa4f7c3968fc1723834"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
proxy = ["pysocks"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyside6"
|
||||||
|
version = "6.5.3"
|
||||||
|
description = "Python bindings for the Qt cross-platform application and UI framework"
|
||||||
|
optional = false
|
||||||
|
python-versions = "<3.12,>=3.7"
|
||||||
|
groups = ["main"]
|
||||||
|
files = [
|
||||||
|
{file = "PySide6-6.5.3-cp37-abi3-macosx_11_0_universal2.whl", hash = "sha256:be53e7c64710fc4307afd33147e241a06cd97b18fae887ee611d8d4b373dbb04"},
|
||||||
|
{file = "PySide6-6.5.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:84f3d3e278e5ea00f1558ac7e1eeb382bba1df7732bdb025ee654e7b4b3cd451"},
|
||||||
|
{file = "PySide6-6.5.3-cp37-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:48f4579ca49225cfff8f512178551bdf6aa9031198527f71799bcc061a0f2327"},
|
||||||
|
{file = "PySide6-6.5.3-cp37-abi3-win_amd64.whl", hash = "sha256:aaaf5acfaaf9575740df03ee1aa706e2f38d8fcca2255acbbd3a5701f6f2f416"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
PySide6-Addons = "6.5.3"
|
||||||
|
PySide6-Essentials = "6.5.3"
|
||||||
|
shiboken6 = "6.5.3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyside6-addons"
|
||||||
|
version = "6.5.3"
|
||||||
|
description = "Python bindings for the Qt cross-platform application and UI framework (Addons)"
|
||||||
|
optional = false
|
||||||
|
python-versions = "<3.12,>=3.7"
|
||||||
|
groups = ["main"]
|
||||||
|
files = [
|
||||||
|
{file = "PySide6_Addons-6.5.3-cp37-abi3-macosx_11_0_universal2.whl", hash = "sha256:047162b158ee929d43c21cdc3ac48e75fec612f2e5492b317190fac98d2de5c6"},
|
||||||
|
{file = "PySide6_Addons-6.5.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:e5bc1fa95351182dc2c003e07320d5509218ccc0840d10197d7d452aa5de5d2e"},
|
||||||
|
{file = "PySide6_Addons-6.5.3-cp37-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:be0dcfb15d44c2973c3c122058f1df8c3c9d93abd4170534e06dbf986aa30e26"},
|
||||||
|
{file = "PySide6_Addons-6.5.3-cp37-abi3-win_amd64.whl", hash = "sha256:dd1d294d48798bd297bde02d3ea02f313a86e38ed3944519228466bdfb537961"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
PySide6-Essentials = "6.5.3"
|
||||||
|
shiboken6 = "6.5.3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyside6-essentials"
|
||||||
|
version = "6.5.3"
|
||||||
|
description = "Python bindings for the Qt cross-platform application and UI framework (Essentials)"
|
||||||
|
optional = false
|
||||||
|
python-versions = "<3.12,>=3.7"
|
||||||
|
groups = ["main"]
|
||||||
|
files = [
|
||||||
|
{file = "PySide6_Essentials-6.5.3-cp37-abi3-macosx_11_0_universal2.whl", hash = "sha256:4d9c95ded938e557052fc67efe68d57108856df141a1b499497fd7999419e3eb"},
|
||||||
|
{file = "PySide6_Essentials-6.5.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:45580138be91f5fdcefb4d28dadb56d3640eb658575af97b49057e10c22a024d"},
|
||||||
|
{file = "PySide6_Essentials-6.5.3-cp37-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:8244bc185b0243ba7c4491033e592b247e44a63d69213e9a45ee38e87e0f1f90"},
|
||||||
|
{file = "PySide6_Essentials-6.5.3-cp37-abi3-win_amd64.whl", hash = "sha256:f928b98ec349c87f9ccc63a482917779f59fa646893722c53c2fe2a1e4f335e0"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
shiboken6 = "6.5.3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shiboken6"
|
||||||
|
version = "6.5.3"
|
||||||
|
description = "Python/C++ bindings helper module"
|
||||||
|
optional = false
|
||||||
|
python-versions = "<3.12,>=3.7"
|
||||||
|
groups = ["main"]
|
||||||
|
files = [
|
||||||
|
{file = "shiboken6-6.5.3-cp37-abi3-macosx_11_0_universal2.whl", hash = "sha256:faaca92dcbbf26c0ae13f189746c38482e40859e0897b0ed4dee5e04f69fda71"},
|
||||||
|
{file = "shiboken6-6.5.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4cdda98df511243c40f1dd4d9eac25a7191c2583ac673147ecdae0ffa3b9223f"},
|
||||||
|
{file = "shiboken6-6.5.3-cp37-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:1bc928ca9f1c1d16ff8fe0585627738a15552bb3329c04fca2c74a443618a6b3"},
|
||||||
|
{file = "shiboken6-6.5.3-cp37-abi3-win_amd64.whl", hash = "sha256:a013367e38a12b3f69ba02e79f133df4fba8d21b55a78c6999cdb31c25609524"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
lock-version = "2.1"
|
||||||
|
python-versions = ">=3.11,<3.12"
|
||||||
|
content-hash = "0bce6e6df23bae4a0db8ab108812470658b0e0b1d792f279a8447835f5a9e1de"
|
28
pyproject.toml
Normal file
28
pyproject.toml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
[project]
|
||||||
|
name = "mqtt-acess"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = ""
|
||||||
|
authors = [
|
||||||
|
{name = "clinton", email = "clinton_luo@qq.com"}
|
||||||
|
]
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.11,<3.12"
|
||||||
|
dependencies = [
|
||||||
|
"PySide6>=6.5,<6.6",
|
||||||
|
"PySide6-Addons>=6.5,<6.6",
|
||||||
|
"PySide6-Essentials>=6.5,<6.6",
|
||||||
|
"shiboken6>=6.5,<6.6",
|
||||||
|
"paho-mqtt (>=2.1.0,<3.0.0)"
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.poetry]
|
||||||
|
packages = [{include = "mqtt_acess", from = "src"}]
|
||||||
|
|
||||||
|
[[tool.poetry.source]]
|
||||||
|
name = "mirrors"
|
||||||
|
url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/"
|
||||||
|
priority = "supplemental"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
0
src/mqtt_acess/__init__.py
Normal file
0
src/mqtt_acess/__init__.py
Normal file
1
src/mqtt_acess/config/__init__.py
Normal file
1
src/mqtt_acess/config/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# config模块
|
8
src/mqtt_acess/config/settings.py
Normal file
8
src/mqtt_acess/config/settings.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
class Settings:
|
||||||
|
MQTT_BROKER = "localhost"
|
||||||
|
MQTT_PORT = 1883
|
||||||
|
MQTT_USERNAME = ""
|
||||||
|
MQTT_PASSWORD = ""
|
||||||
|
MQTT_TLS = False
|
||||||
|
MQTT_TOPICS = [("test/topic", 0)]
|
||||||
|
PROTOCOL_TYPE = "json"
|
18
src/mqtt_acess/main.py
Normal file
18
src/mqtt_acess/main.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
from PySide6.QtWidgets import QApplication
|
||||||
|
from ui.main_window import MainWindow
|
||||||
|
from mqtt.client import MqttClient
|
||||||
|
from parser.factory import ParserFactory
|
||||||
|
from responder.alarm import AlarmResponder
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def main():
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
mqtt_client = MqttClient()
|
||||||
|
parser_factory = ParserFactory()
|
||||||
|
responder = AlarmResponder()
|
||||||
|
window = MainWindow(mqtt_client, parser_factory, responder)
|
||||||
|
window.show()
|
||||||
|
sys.exit(app.exec())
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
1
src/mqtt_acess/mqtt/__init__.py
Normal file
1
src/mqtt_acess/mqtt/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# mqtt模块
|
33
src/mqtt_acess/mqtt/client.py
Normal file
33
src/mqtt_acess/mqtt/client.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
from config.settings import Settings
|
||||||
|
|
||||||
|
class MqttClient:
|
||||||
|
def __init__(self):
|
||||||
|
self.client = mqtt.Client()
|
||||||
|
if Settings.MQTT_USERNAME:
|
||||||
|
self.client.username_pw_set(Settings.MQTT_USERNAME, Settings.MQTT_PASSWORD)
|
||||||
|
if Settings.MQTT_TLS:
|
||||||
|
self.client.tls_set()
|
||||||
|
self.client.on_connect = self.on_connect
|
||||||
|
self.client.on_message = None # 由外部设置
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
self.client.connect(Settings.MQTT_BROKER, Settings.MQTT_PORT)
|
||||||
|
self.client.loop_start()
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
self.client.loop_stop()
|
||||||
|
self.client.disconnect()
|
||||||
|
|
||||||
|
def subscribe(self, topic, qos=0):
|
||||||
|
self.client.subscribe(topic, qos)
|
||||||
|
|
||||||
|
def publish(self, topic, payload, qos=0):
|
||||||
|
self.client.publish(topic, payload, qos)
|
||||||
|
|
||||||
|
def set_on_message(self, callback):
|
||||||
|
self.client.on_message = callback
|
||||||
|
|
||||||
|
def on_connect(self, client, userdata, flags, rc):
|
||||||
|
for topic, qos in Settings.MQTT_TOPICS:
|
||||||
|
client.subscribe(topic, qos)
|
1
src/mqtt_acess/parser/__init__.py
Normal file
1
src/mqtt_acess/parser/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# parser模块
|
3
src/mqtt_acess/parser/base.py
Normal file
3
src/mqtt_acess/parser/base.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
class IParser:
|
||||||
|
def parse(self, data: bytes) -> dict:
|
||||||
|
raise NotImplementedError("必须实现parse方法")
|
8
src/mqtt_acess/parser/factory.py
Normal file
8
src/mqtt_acess/parser/factory.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from .json_parser import JsonParser
|
||||||
|
|
||||||
|
class ParserFactory:
|
||||||
|
def get_parser(self, protocol_type: str):
|
||||||
|
if protocol_type == "json":
|
||||||
|
return JsonParser()
|
||||||
|
# 可扩展更多协议
|
||||||
|
raise ValueError(f"不支持的协议类型: {protocol_type}")
|
6
src/mqtt_acess/parser/json_parser.py
Normal file
6
src/mqtt_acess/parser/json_parser.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import json
|
||||||
|
from .base import IParser
|
||||||
|
|
||||||
|
class JsonParser(IParser):
|
||||||
|
def parse(self, data: bytes) -> dict:
|
||||||
|
return json.loads(data.decode("utf-8"))
|
1
src/mqtt_acess/responder/__init__.py
Normal file
1
src/mqtt_acess/responder/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# responder模块
|
7
src/mqtt_acess/responder/alarm.py
Normal file
7
src/mqtt_acess/responder/alarm.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from .base import IResponder
|
||||||
|
|
||||||
|
class AlarmResponder(IResponder):
|
||||||
|
def respond(self, parsed_data: dict):
|
||||||
|
# 示例:如果数据中有"alarm"字段则打印报警
|
||||||
|
if parsed_data.get("alarm"):
|
||||||
|
print("报警:", parsed_data["alarm"])
|
3
src/mqtt_acess/responder/base.py
Normal file
3
src/mqtt_acess/responder/base.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
class IResponder:
|
||||||
|
def respond(self, parsed_data: dict):
|
||||||
|
raise NotImplementedError("必须实现respond方法")
|
1
src/mqtt_acess/ui/__init__.py
Normal file
1
src/mqtt_acess/ui/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# ui模块
|
50
src/mqtt_acess/ui/mainWindow.ui
Normal file
50
src/mqtt_acess/ui/mainWindow.ui
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Form</class>
|
||||||
|
<widget class="QWidget" name="Form">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>658</width>
|
||||||
|
<height>498</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="horizontalWidget" native="true">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>640</width>
|
||||||
|
<height>480</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>1920</width>
|
||||||
|
<height>1200</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout"/>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
55
src/mqtt_acess/ui/mainWindow_ui.py
Normal file
55
src/mqtt_acess/ui/mainWindow_ui.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Form generated from reading UI file 'mainWindow.ui'
|
||||||
|
##
|
||||||
|
## Created by: Qt User Interface Compiler version 6.5.3
|
||||||
|
##
|
||||||
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||||
|
QMetaObject, QObject, QPoint, QRect,
|
||||||
|
QSize, QTime, QUrl, Qt)
|
||||||
|
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
||||||
|
QFont, QFontDatabase, QGradient, QIcon,
|
||||||
|
QImage, QKeySequence, QLinearGradient, QPainter,
|
||||||
|
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||||
|
from PySide6.QtWidgets import (QApplication, QHBoxLayout, QSizePolicy, QWidget)
|
||||||
|
|
||||||
|
class Ui_Form(object):
|
||||||
|
def setupUi(self, Form):
|
||||||
|
if not Form.objectName():
|
||||||
|
Form.setObjectName(u"Form")
|
||||||
|
Form.resize(658, 498)
|
||||||
|
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth())
|
||||||
|
Form.setSizePolicy(sizePolicy)
|
||||||
|
self.horizontalLayout_2 = QHBoxLayout(Form)
|
||||||
|
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
|
||||||
|
self.horizontalWidget = QWidget(Form)
|
||||||
|
self.horizontalWidget.setObjectName(u"horizontalWidget")
|
||||||
|
sizePolicy1 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||||
|
sizePolicy1.setHorizontalStretch(0)
|
||||||
|
sizePolicy1.setVerticalStretch(0)
|
||||||
|
sizePolicy1.setHeightForWidth(self.horizontalWidget.sizePolicy().hasHeightForWidth())
|
||||||
|
self.horizontalWidget.setSizePolicy(sizePolicy1)
|
||||||
|
self.horizontalWidget.setMinimumSize(QSize(640, 480))
|
||||||
|
self.horizontalWidget.setMaximumSize(QSize(1920, 1200))
|
||||||
|
self.horizontalLayout = QHBoxLayout(self.horizontalWidget)
|
||||||
|
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
||||||
|
|
||||||
|
self.horizontalLayout_2.addWidget(self.horizontalWidget)
|
||||||
|
|
||||||
|
|
||||||
|
self.retranslateUi(Form)
|
||||||
|
|
||||||
|
QMetaObject.connectSlotsByName(Form)
|
||||||
|
# setupUi
|
||||||
|
|
||||||
|
def retranslateUi(self, Form):
|
||||||
|
Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None))
|
||||||
|
# retranslateUi
|
||||||
|
|
43
src/mqtt_acess/ui/main_window.py
Normal file
43
src/mqtt_acess/ui/main_window.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
from PySide6.QtWidgets import QMainWindow, QTextEdit, QPushButton, QVBoxLayout, QWidget
|
||||||
|
from config.settings import Settings
|
||||||
|
|
||||||
|
class MainWindow(QMainWindow):
|
||||||
|
def __init__(self, mqtt_client, parser_factory, responder):
|
||||||
|
super().__init__()
|
||||||
|
self.setWindowTitle("MQTT在线测试平台")
|
||||||
|
self.resize(600, 400)
|
||||||
|
self.mqtt_client = mqtt_client
|
||||||
|
self.parser = parser_factory.get_parser(Settings.PROTOCOL_TYPE)
|
||||||
|
self.responder = responder
|
||||||
|
|
||||||
|
self.text_edit = QTextEdit()
|
||||||
|
self.btn_connect = QPushButton("连接MQTT")
|
||||||
|
self.btn_disconnect = QPushButton("断开MQTT")
|
||||||
|
self.btn_connect.clicked.connect(self.connect_mqtt)
|
||||||
|
self.btn_disconnect.clicked.connect(self.disconnect_mqtt)
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
layout.addWidget(self.text_edit)
|
||||||
|
layout.addWidget(self.btn_connect)
|
||||||
|
layout.addWidget(self.btn_disconnect)
|
||||||
|
|
||||||
|
container = QWidget()
|
||||||
|
container.setLayout(layout)
|
||||||
|
self.setCentralWidget(container)
|
||||||
|
|
||||||
|
def connect_mqtt(self):
|
||||||
|
self.mqtt_client.set_on_message(self.on_message)
|
||||||
|
self.mqtt_client.connect()
|
||||||
|
self.text_edit.append("已连接MQTT")
|
||||||
|
|
||||||
|
def disconnect_mqtt(self):
|
||||||
|
self.mqtt_client.disconnect()
|
||||||
|
self.text_edit.append("已断开MQTT")
|
||||||
|
|
||||||
|
def on_message(self, client, userdata, msg):
|
||||||
|
try:
|
||||||
|
parsed = self.parser.parse(msg.payload)
|
||||||
|
self.text_edit.append(f"收到消息: {parsed}")
|
||||||
|
self.responder.respond(parsed)
|
||||||
|
except Exception as e:
|
||||||
|
self.text_edit.append(f"解析错误: {e}")
|
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
Loading…
Reference in New Issue
Block a user