创建项目框架
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