HNU小学期综合实验:基于RS485总线的多机评分系统

本文介绍了一个使用Python实现的RS485通信实验,模拟了Modbus协议,包括主从机通信、异常处理和数据包定义。实验中,主节点能检测从机状态、获取评分,并通过串口进行数据交互。代码利用PyQt5构建了GUI界面,支持波特率选择和从机分数查询等功能。
摘要由CSDN通过智能技术生成

本实验代码已上传 github有兴趣自取。

实验验收评分97/100。

应该是第一个实现度这么高的,大概是从来没人有我这么闲…

实验主要目的

通过本案例加深理解RS485通信方式,实现上位机的主控制器与所有的下位机进行通信。

实验中通信协议

  本案例模拟Modbus协议,采用主、从技术,上位机的主控制器可以与所有的下位机通信,也可以单独与一个指定的下位机通信。模拟Modbus协议中,上下位机的数据包都只含5个字节,其基本格式为:数据包头(0x5A)+地址码(广播地址/从机地址)+功能码+携带数据(一个字节)+校验码字节,携带数据部分可以扩充多个字节,可以视情况进行修改。数据包具体定义如下:

1. 主机检测从机是否正常相关数据包:(主机与单个从机设备通信)

  • A、设备正常检测数据包: 方向:上位机----->下位机
    数据包消息:数据包头+从机地址+检测功能码(Fun_CheckSlave)+自定义内容(Check_Content)+校验字节
    功能:查询下位机是否正常。正常,下位机发送回应查询数据包;不正常,则下位机不予回应;数据传输过程发生错误,下位机发送回应错误数据包,上位机可以通过设置多次轮询来重新检测该设备是否正常;
  • B、回应查询数据包: 方向:下位机----->上位机
    数据包消息:数据包头+从机地址+检测功能码(Fun_CheckSlave)+自定义内容(接收自主机Check_Content)+校验字节
  • C、回应错误数据包: 方向:下位机----->上位机
    数据包消息:数据包头+从机地址+检测功能码(Fun_CheckSlave)+错误码(ErrorInfo)+校验字节

2. 主机获取从机评分相关数据包:(主机与单个从机设备通信)

  • D、获取多、单机评分数据包:
    方向:上位机----->下位机
    数据包消息:数据包头+检测正常从机地址(0x00)+读下位机功能码(Fun_ReadInfo)+从机地址+校验字节
    功能:对检测正常的设备,进行一次轮询,获取评分已经准备好的从机的分数。对于单机直接进行通信,没有轮询。
  • E、结果返回数据包:
    方向:下位机----->上位机
    数据包消息:数据包头+从机地址+读下位机功能码(Fun_ReadInfo)+从机返回的分数值+校验字节(分数值>100:表示上面提及的未准备好,回应错误数据包)

3. 此轮评分结束相关数据包

  • F、复位数据包:(主机与所有从机通信)
    方向:上位机----->下位机
    数据包消息:数据包头+广播地址+复位功能码(Fun_Reset)+从机返回的分数值(0x00)+校验字节
    功能:指示所有正常连接的从机进行复位操作,准备下一轮的评分。

4. 相关功能码与定义

  • 功能码
    读下位机功能码 0X03
    检测功能码 0X08
    地址错误功能码 0X10
    复位功能码 0X01
  • 附加数据
    错误码 0X6F
    包头 0X5A
    广播地址 0X00
    自定义内容 0X13

5. PC端串口设置

串口波特率:9600 数据位:8位 校验位:无 停止位:1

实验要求

  • 需要包含串口的设置
  • 主节点发起从机检测过程:发送指定从机编号正常检测数据包,判断回应查询数据包是否符合上一节中的通信协议。
  • 主机获取从机评分过程:发送指定从机评分相关数据包,判断回应查询数据包是否符合上一节中的通信协议。
  • 主机发起结束评分的过程,若复位成功,STC从机上第1位和第8位LED灯会熄灭。
  • 展示串口相关信息,展示检测到的从机编号和从机的评分等。
  • 允许最大从机数
  • 轮询次数
  • 错误数据包的处理
  • 统计多人评分的平均分

实验代码

实现了较高程度的图形可视化,在完成实验要求的基础上,增添了许多功能:

  • 当进行从机在线状况轮询时,若有从机尚未确认编号,可在轮询结束后通过手动查询的方式将其添加入在线序列
  • 为提高系统稳定性,避免杜邦线的连接不稳定,所有指令发送均连续进行10次,直至收到从机应答或超时。
  • 输出时直接刷新缓存区并刷新GUI,避免因缓存区或其他原因导致的输出延迟。
  • 实现了GUI的交互性和更新,在不同的场景下显示不同的组件

特别的,实验要求文档中并未提到以下一些场景,但在实际调试中遇到:

  • 当从机编号未确定时,主机向该从机发送查询数据包,若查询数据包中从机编号与本从机一致,从机将返回一个错误数据包,格式如下:包头+从机当前编号+读下位机功能码+错误码+校验字节,同时从机第四位LED灯被点亮。复位后将被清除。

运行截图如下:
连接界面
连接成功
其他
指定查询
主动断开
端口被拔出

import binascii
import sys
import time
from copy import deepcopy

import serial.tools.list_ports
from PyQt5 import QtCore
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QLineEdit, QPushButton, QTextEdit, \
    QHBoxLayout, QComboBox


# 串口类
class MyDevice:

    def __init__(self):
        self.__port__ = None
        self.devices = set()  # 设备集合
        self.scores = {}  # 所有设备的分数字典,键为设备地址,值为分数列表
        self.temp_scores = {}  # 指定设备的分数字典

        # 初始化功能码
        self.Fun_ReadInfo = 0x03
        self.Fun_CheckSlave = 0x08
        self.Fun_fault = 0x10
        self.Fun_Reset = 0x01

        # 初始化信息码及错误码
        self.ErrorInfo = 0x6F
        self.header = 0x5A
        self.broad_addr = 0x00
        self.Check_Content = 0x13

        # 初始化串口链接信息
        self.baud_rate = 9600
        self.bytesize = serial.EIGHTBITS
        self.parity = serial.PARITY_NONE
        self.stop_bits = serial.STOPBITS_ONE
        self.timeout = 0.05

        # 初始化预计从机编号极值
        self.devices_num = 10

    # 更新从机编号极值
    def update_devices_num(self, num):
        self.devices_num = num

    # 连接串口
    def link(self, __baud_rate):
        # 连接第一个串口
        ports = list(serial.tools.list_ports.comports())
        if len(ports):
            port = ports[0].device
            try:
                self.__port__ = serial.Serial(port,
                                              baudrate=__baud_rate,
                                              bytesize=self.bytesize,
                                              parity=self.parity,
                                              stopbits=self.stop_bits,
                                              timeout=self.timeout)
                return [port, 0]
            except serial.SerialException as e:
                return [port, e]
        else:
            return [0, 0]

    # 串口监测
    def is_port_connected(self):
        ports = list(serial.tools.list_ports.comports())
        if self.__port__ and self.__port__.port in [
            port.device for port in ports
        ]:
            return True
        return False

    # 检测下位机设备
    def check_devices(self, __addr__):
        __data__ = [
            self.header, __addr__, self.Fun_CheckSlave, self.Check_Content
        ]
        __data__.append(sum(__data__))
        for p in range(5):
            self.__port__.write(__data__)
            ret_date = self.read_serial_data(self.__port__)
            if ret_date and [int(i, 16) for i in ret_date] == __data__:
                self.devices.add(__addr__)  # 维护当前在线集合
                self.scores[__addr__] = []  # 初始化设备的分数列表
                break

    # 检测指定下位机,可输入用空格隔开的序列
    def check_more_devices(self, __addresses__):
        for __addr__ in __addresses__:
            self.check_devices(__addr__)

    # 轮询所有下位机设备是否在线
    def check_all_online_devices(self):
        self.devices.clear()
        self.scores.clear()
        for __addr__ in range(self.devices_num + 1):
            self.check_devices(__addr__)
        return self.devices

    # 读串口信息
    def read_serial_data(self, __port__):
        __data__ = []
        reading = self.__port__.read(5)  # 读取串口数据
        if reading:
            hex_data = binascii.hexlify(reading).decode('utf-8')
            __data__ = [
                hex_data[index:index + 2]
                for index in range(0, len(hex_data), 2)
            ]
            return __data__
        return []

    # 获取指定从机的分数
    def get_score(self, __addr__):
        __data__ = [self.header, self.broad_addr, self.Fun_ReadInfo, __addr__]
        __data__.append(sum(__data__))
        flag = False
        for p in range(10):
            self.__port__.write(__data__)
            ret_data = self.read_serial_data(self.__port__)
            if ret_data:
                ret_data = [int(i, 16) for i in ret_data]
                self.devices.add(__addr__)
                if ret_data[1] == __addr__ and ret_data[4] == sum(
                        ret_data[:4]) and ret_data[3] != self.ErrorInfo:
                    if ret_data[3] <= 100:
                        self.scores[__addr__] = ret_data[3]
                        return ret_data[3]
                    else:
                        return -2
                elif ret_data[2] == self.Fun_ReadInfo and ret_data[
                    3] == self.ErrorInfo:
                    return -3  # 从机未确认编号
                elif ret_data[3] == self.ErrorInfo:
                    return -2  # 分数大于100或者未确认分数
                else:
                    return -1  # 数据校验错误
            if p == 9:
                flag = True
        if flag:
            return -3  # 从机无回应

    # 分别获取指定从机序列的分数
    def get_more_score(self, __addresses__):
        self.temp_scores.clear()
        for __addr__ in __addresses__:
            ret = self.get_score(__addr__)
            self.temp_scores[__addr__] = ret
        return self.temp_scores

    # 获取所有在线从机的分数
    def get_all_score(self):  # 获取所有在线从机分数
        for __device__ in self.devices:
            ret = self.get_score(__device__)
            self.scores[__device__] = ret
        return self.scores

    # 获得当前已知分数的最大值
    def get_highest_score(self):
        if self.scores:
            highest = max(scores for scores in self.scores.values())  # 计算最高分
            return highest
        else:
            return -1

    # 获得当前已知分数的最小值
    def get_lowest_score(self):
        if self.scores:
            res = deepcopy(self.scores)
            for _id, score in res.items():
                if score < 0:
                    del self.scores[_id]
            lowest = min(scores for scores in self.scores.values())  # 计算最低分
            return lowest
        else:
            return -1

    # 获得当前已知分数的平均值
    def get_average_score(self):
        if self.scores:
        	res = deepcopy(self.scores)
            for _id, score in res.items():
                if score < 0:
                    del self.scores[_id]
            total_scores = [scores
                            for scores in self.scores.values()]  # 所有分数的列表
            average = sum(total_scores) / len(
                total_scores) if total_scores else 0  # 计算平均分
            return average
        else:
            return -1

    # 获得给出指定分数的从机序列
    def get_device_ids_with_score(self, score):
        device_ids = []
        for device_id, device_score in self.scores.items():
            if device_score == score:
                device_ids.append(device_id)
        return device_ids

    # 复位从机
    def ReSet(self):
        __data__ = [self.header, self.broad_addr, self.Fun_Reset, 0x00]
        __data__.append(sum(__data__))
        for p in range(10):
            self.__port__.write(__data__)

    # 关闭串口
    def Close(self):
        self.__port__.close()


# 图形化窗口
class MyWindow(QWidget):

    def __init__(self, _master):
        super().__init__()
        self.baud_rate = 9600

        # 初始化波特率选择部分
        self.baud_rate_label = QLabel("波特率:")
        self.baud_rate_combo = QComboBox()
        self.baud_rate_combo.addItem("50")
        self.baud_rate_combo.addItem("75")
        self.baud_rate_combo.addItem("110")
        self.baud_rate_combo.addItem("134")
        self.baud_rate_combo.addItem("150")
        self.baud_rate_combo.addItem("200")
        self.baud_rate_combo.addItem("300")
        self.baud_rate_combo.addItem("600")
        self.baud_rate_combo.addItem("1200")
        self.baud_rate_combo.addItem("1800")
        self.baud_rate_combo.addItem("2400")
        self.baud_rate_combo.addItem("4800")
        self.baud_rate_combo.addItem("9600")
        self.baud_rate_combo.addItem("19200")
        self.baud_rate_combo.addItem("38400")
        self.baud_rate_combo.addItem("57600")
        self.baud_rate_combo.addItem("115200")
        self.baud_rate_combo.addItem("230400")
        self.baud_rate_combo.addItem("460800")
        self.baud_rate_combo.addItem("500000")
        self.baud_rate_combo.addItem("576000")
        self.baud_rate_combo.addItem("921600")
        self.baud_rate_combo.addItem("1000000")
        self.baud_rate_combo.addItem("1152000")
        self.baud_rate_combo.addItem("1500000")
        self.baud_rate_combo.addItem("2000000")
        self.baud_rate_combo.addItem("2500000")
        self.baud_rate_combo.addItem("3000000")
        self.baud_rate_combo.addItem("3500000")
        self.baud_rate_combo.addItem("4000000")
        self.baud_rate_combo.setCurrentIndex(12)
        self.baud_rate_combo.currentTextChanged.connect(self.baud_rate_changed)

        # 初始化串口连接监测计时器
        self.check_port_timer = None

        # 初始化从机编号最大值,轮询时从0轮询至最大值
        self.devices_Num_label = QLabel("请输入从机编号最大值:")
        self.devices_Num_edit = QLineEdit()
        self.devices_Num_edit.setPlaceholderText("无输入或未确认则默认10")
        self.devices_Num_edit.setAlignment(QtCore.Qt.AlignCenter)
        self.confirm = QPushButton("确认")

        # 输入指定查询的从机编号
        self.devices_id_label = QLabel("输入从机编号:")
        self.text_edit = QLineEdit()
        self.text_edit.setAlignment(QtCore.Qt.AlignCenter)
        self.query = QPushButton("查询")

        # 功能键
        self.update_all_online_devices = QPushButton("更新当前在线从机")
        self.update_all_online_devices_score = QPushButton("查询当前在线从机分数")
        self.highest_score = QPushButton("查看最高分")
        self.lowest_score = QPushButton("查看最低分")
        self.average_score = QPushButton("查看平均分")
        self.reset = QPushButton("复位")
        self.clear = QPushButton("清屏")
        self.unlink = QPushButton("断开连接")
        self.link = QPushButton("连接主机")
        self.cLose = QPushButton("断开连接并退出")
        self.output = QTextEdit()
        self.output.setReadOnly(True)
        self.master = _master

        # 初始化UI
        self.initUI()

    # 排列各个组件
    def initUI(self):
        self.setWindowTitle("基于RS-485总线的多机交互的分数查询工具")
        self.setGeometry(1300, 300, 500, 500)

        # 波特率选择栏
        baud_layout = QHBoxLayout()
        baud_layout.addWidget(self.baud_rate_label)
        baud_layout.addWidget(self.baud_rate_combo)

        # 输入从机编号最大值栏
        devices_num_layout = QHBoxLayout()
        devices_num_layout.addWidget(self.devices_Num_label)
        devices_num_layout.addWidget(self.devices_Num_edit)
        devices_num_layout.addWidget(self.confirm)

        # 标签、文本框和查询
        h_layout = QHBoxLayout()
        h_layout.addWidget(self.devices_id_label)
        h_layout.addWidget(self.text_edit)
        h_layout.addWidget(self.query)

        # 查询
        update_layout = QHBoxLayout()
        update_layout.addWidget(self.update_all_online_devices)
        update_layout.addWidget(self.update_all_online_devices_score)

        # 分数处理
        score_layout = QHBoxLayout()
        score_layout.addWidget(self.highest_score)
        score_layout.addWidget(self.lowest_score)
        score_layout.addWidget(self.average_score)

        # 复位和清屏
        clean_layout = QHBoxLayout()
        clean_layout.addWidget(self.reset)
        clean_layout.addWidget(self.clear)

        # 连接与断开连接
        link_layout = QHBoxLayout()
        link_layout.addWidget(self.link)
        link_layout.addWidget(self.unlink)

        # 退出
        cLose_layout = QHBoxLayout()
        cLose_layout.addWidget(self.cLose)

        # 水平排列上述块
        v_layout = QVBoxLayout()
        v_layout.addLayout(h_layout)
        v_layout.addLayout(update_layout)
        v_layout.addLayout(score_layout)
        v_layout.addLayout(clean_layout)
        v_layout.addLayout(baud_layout)
        v_layout.addLayout(devices_num_layout)
        v_layout.addLayout(link_layout)
        v_layout.addLayout(cLose_layout)
        v_layout.addWidget(self.output)

        # 设置窗口主布局
        self.setLayout(v_layout)

        # 链接相关函数
        self.confirm.clicked.connect(self._update_devices_num)
        self.query.clicked.connect(self.queryScore)
        self.update_all_online_devices.clicked.connect(self.update_Devices)
        self.update_all_online_devices_score.clicked.connect(self.update_Score)
        self.highest_score.clicked.connect(self.highest_Score)
        self.lowest_score.clicked.connect(self.lowest_Score)
        self.average_score.clicked.connect(self.average_Score)
        self.reset.clicked.connect(self.reSet)
        self.clear.clicked.connect(self.Clear)
        self.unlink.clicked.connect(self.unLink)
        self.link.clicked.connect(self.Link)
        self.cLose.clicked.connect(self.Close)

        # 检查串口连接状态并设置按钮可见性
        self.check_port_connection()

    # 设置按钮可见性,主要是连接成功后的显示
    def set_button_visibility(self, visible):
        self.devices_id_label.setVisible(visible)
        self.text_edit.setVisible(visible)
        self.query.setVisible(visible)
        self.update_all_online_devices.setVisible(visible)
        self.update_all_online_devices_score.setVisible(visible)
        self.highest_score.setVisible(visible)
        self.lowest_score.setVisible(visible)
        self.average_score.setVisible(visible)
        self.reset.setVisible(visible)
        self.clear.setVisible(visible)
        self.unlink.setVisible(visible)
        self.cLose.setVisible(visible)

    # 设置按钮可见,主要是连接成功前的组件
    def set_combo_visibility(self, visible):
        self.baud_rate_label.setVisible(visible)
        self.baud_rate_combo.setVisible(visible)
        self.devices_Num_label.setVisible(visible)
        self.devices_Num_edit.setVisible(visible)
        self.confirm.setVisible(visible)

    # 使组件不可见
    def set_button_unable(self):
        self.set_button_visibility(False)

    # 使组件可见
    def set_button_able(self):
        self.set_button_visibility(True)

    # 使组件不可见
    def set_combo_unable(self):
        self.set_combo_visibility(False)

    # 使组件可见
    def set_combo_able(self):
        self.set_combo_visibility(True)

    # 设置连接相关组建可见性
    def set_link(self, visible):
        self.link.setVisible(visible)
        self.unlink.setVisible(not visible)

    # 使组件可见
    def set_link_able(self):
        self.set_link(True)

    # 使组件不可见
    def set_link_unable(self):
        self.set_link(False)

    # 监测串口状态,修改组件可见性
    def check_port_connection(self):
        if not self.master.is_port_connected():
            self.set_button_unable()
            self.set_combo_able()
            self.set_link_able()
            # self._output("端口连接已断开")

    # 修改连接参数波特率
    def baud_rate_changed(self, text):
        self.baud_rate = int(text)

    # 向输出栏输出,并立即刷新GUI,避免缓存区引起的输出延迟
    def _output(self, *args):
        message = " ".join(str(arg) for arg in args)
        self.output.append(message)
        QApplication.processEvents()

    # 设置从机编号极值
    def _update_devices_num(self):
        _num = self.devices_Num_edit.text()
        _num = _num.split()
        try:
            if len(_num) <= 0:
                self._output("未输入,请重新输入")
            elif len(_num) > 1:
                self._output("参数过多,请重新输入")
            else:
                self.master.update_devices_num(int(_num[0]))
                self._output("设置成功")
                return
        except ValueError:
            self._output("非法字符,请重新输入")
            return

    # 查询指定从机序列分数
    def queryScore(self):
        addr = self.text_edit.text()
        addr = addr.split()
        try:
            addr = [int(address) for address in addr]
            for a in addr:
                if a < 0 or a > 255:
                    self._output("地址超出有效范围,请重新输入")
                    return
        except ValueError:
            self._output("地址输入错误,请重新输入")
            return
        scores = self.master.get_more_score(addr)
        if scores:
            sorted_scores = sorted(scores.items(), key=lambda x: x[0])
            self._output("指定从机分数:")
            for device_id, score in sorted_scores:
                if score == -1:
                    self._output("从机 {" + str(device_id) + "} 数据校验错误")
                elif score == -2:
                    self._output("从机 {" + str(device_id) + "} 分数大于100或未确认分数")
                elif score == -3:
                    self._output("从机 {" + str(device_id) + "} 未确认自身编号")
                else:
                    self._output("从机 {" + str(device_id) + "} 分数:" +
                                 str(score))
        else:
            self._output("没有查询到从机分数")

    # 更新当前在线从机序列
    def update_Devices(self):
        online_devices = self.master.check_all_online_devices()
        if online_devices:
            self._output("在线下位机编号:")
            devices = [device for device in online_devices]
            self._output(devices)
        else:
            self._output("没有检测到在线下位机")
        return

    # 更新当前在线从机序列的分数
    def update_Score(self):
        scores = self.master.get_all_score()
        if scores:
            sorted_scores = sorted(scores.items(), key=lambda x: x[0])
            self._output("所有在线从机分数:")
            for device_id, score in sorted_scores:
                if score == -1:
                    self._output("从机 {" + str(device_id) + "} 数据校验错误")
                elif score == -2:
                    self._output("从机 {" + str(device_id) + "} 分数大于100或未确认分数")
                elif score == -3:
                    self._output("从机 {" + str(device_id) + "} 未确认自身编号")
                else:
                    self._output("从机 {" + str(device_id) + "} 分数:" +
                                 str(score))
        else:
            self._output("没有查询到从机分数")

    # 计算最高分并列出给出最高分的从机序列
    def highest_Score(self):
        highest_score = self.master.get_highest_score()
        if highest_score == -1:
            self._output("请先查询从机分数")
        else:
            device_ids = self.master.get_device_ids_with_score(highest_score)
            output_message = "给出最高分的从机:"
            output_message += "".join(
                str(device_id) for device_id in device_ids)
            self._output("最高分:", str(highest_score))
            self._output(output_message)

    # 计算最低分并列出给出最低分的从机序列
    def lowest_Score(self):
        lowest_score = self.master.get_lowest_score()
        if lowest_score == -1:
            self._output("请先查询从机分数")
        else:
            device_ids = self.master.get_device_ids_with_score(lowest_score)
            output_message = "给出最低分的从机:"
            output_message += "".join(
                str(device_id) for device_id in device_ids)
            self._output("最低分:", str(lowest_score))
            self._output(output_message)

    # 计算平均分并列出给出平均分的从机序列
    def average_Score(self):
        average_score = self.master.get_average_score()
        if average_score == -1:
            self._output("请先查询从机分数")
        else:
            self._output("平均分:" + str(average_score))

    # 复位
    def reSet(self):
        self.master.ReSet()
        self._output("已发送复位指令")

    # 清理输出栏
    def Clear(self):
        self.output.clear()

    # 关闭串口并退出
    def Close(self):
        self.master.Close()
        self.close()

    # 与串口断开连接
    def unLink(self):
        ret = self.master.Close()
        if ret:
            self._output("与端口断开连接失败。")
        else:
            self._output("成功与端口断开连接。")
            self.set_button_unable()
            self.set_combo_able()
            self.set_link_able()

    # 连接串口
    def Link(self):
        # 设置最大尝试次数
        max_attempts = 6
        attempt = 1
        while attempt < max_attempts:
            self._output("第 " + str(attempt) + " 次尝试连接...")
            ret = self.master.link(self.baud_rate)
            if ret[0] == 0:
                self._output("无可用端口")
            elif ret[1] == 0:
                self._output("端口 " + ret[0] + " 连接成功")
                self.set_button_able()
                self.set_combo_unable()
                self.set_link_unable()
                break
            else:
                self._output("端口 " + ret[0] + " 连接失败: " + str(ret[1]))
            attempt += 1
            time.sleep(1)
        if attempt == max_attempts:
            self._output("已达最大连接次数,连接失败。")
            exit(0)

    # 重写show()方法,以控制显示可见行,当串口被拔出后回到连接界面
    def showEvent(self, event):
        super().showEvent(event)
        self.check_port_timer = QtCore.QTimer()
        self.check_port_timer.timeout.connect(self.check_port_connection)
        self.check_port_timer.start(1000)  # 每1秒检查一次串口连接状态

    # 重写hide()方法,同上
    def hideEvent(self, event):
        super().hideEvent(event)
        self.check_port_timer.stop()


# 主程序
if __name__ == "__main__":
    app = QApplication(sys.argv)

    # 创建串口实例
    master = MyDevice()
    # 创建UI实例
    window = MyWindow(master)
    window.show()

    sys.exit(app.exec_())
  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值