zoukankan      html  css  js  c++  java
  • 33.python串口助手工具案例详解

    逻辑文件

    import sys
    import serial
    import serial.tools.list_ports
    from Ui_day13_test01 import Ui_mainWindow
    from PyQt5.QtWidgets import QMainWindow,QApplication,QMessageBox
    from PyQt5.QtCore import QTimer
    
    # AttributeError: 'Pyqt5_Serial' object has no attribute 'setCentralWidget'的解决方法
    class PyqtSerial(QMainWindow,Ui_mainWindow):
        def __init__(self):
            super().__init__()  # 用super继承父类中的__init__()方法
            # 因为前面Ui程序中mainWindow = QtWidgets.QMainWindow(),而在逻辑文件中self是
            # 指myshow对象,相当于就是用用myshow来设置界面
            self.setupUi(self)
            self.start()
            self.setWindowTitle('我的串口小助手')  # 更改软件标题名称
            self.ser=serial.Serial()    # 调用串口模块
    
            # 接收数据和发送数据数目置零
            self.data_num_received = 0
            self.recv_byte.setText(str(self.data_num_received))
            self.data_num_sended = 0
            self.send_byte.setText(str(self.data_num_sended))
    
            # 函数执行条件变量
            self.com_check=False    # 判断串口是否检查
    
        # 设置界面启动函数
        def start(self):
            self.state_label.setText('请点击【检测串口】按钮!')
            # 串口检测按钮
            self.ser_test.clicked.connect(self.portCheck)
    
            # 串口信息显示
            self.com_port.currentTextChanged.connect(self.portImf)
    
            # 打开串口按钮
            self.open_ser.clicked.connect(self.portOpen)
    
            # 关闭串口按钮
            self.close_ser.clicked.connect(self.portClose)
    
            # 发送数据按钮
            self.send_botton.clicked.connect(self.dataSend)
    
            # 发送定时器发送数据
            self.timer_send=QTimer()    # 括号内可以加self,也可以不加
            self.timer_send.timeout.connect(self.dataSend)  # 每次计时结束,触发括号内的函数
            self.time_send.stateChanged.connect(self.dataTimerSend)   # QCheckBox选择框改变后连接括号内的函数
    
            # 接收定时器接收数据
            # 程序先运行start函数,然后运行到这个定时器时,就不操作了,只要界面上有操作,
            # 并且该操作对应的函数里有self.timer.start()开始计时的代码,则当计时结束后,
            # 就会触发此定时器后面的函数
            self.timer=QTimer() # 创建一个定时器
            self.timer.timeout.connect(self.dataReceive)    # 每次计时结束,触发括号内的函数
    
            # 清除发送窗口
            self.send_clear.clicked.connect(self.sendDataClear)
    
            # 清除接收窗口
            self.recv_clear.clicked.connect(self.receiveDataClear)
    
        # 清除发送窗口
        def sendDataClear(self):
            self.send_data.setText('')
    
        # 清除接收窗口
        def receiveDataClear(self):
            self.recv_data.setText('')
    
        # 定时发送数据
        def dataTimerSend(self):
            if self.time_send.isChecked():  # 检查time_send的QCheckBox是否勾选上
                self.timer_send.start(int(self.time_num.text()))    # 计时器按time_num值开始计时
                self.time_num.setEnabled(False) # time_num的QLineEdit不选中
            else:
                self.timer_send.stop()  # 发送计时器停止
                self.time_num.setEnabled(True)  # # time_num的QLineEdit选中
    
        # 发送数据
        def dataSend(self):
            if self.ser.isOpen():   # 判断串口是否打开
                input_s=self.send_data.toPlainText()    # 将发送文本框中的内容赋值给变量
                if input_s !='':    # 判断发送文本框中有内容
                    # 非空字符串
                    if self.send_hex.isChecked():   # 判断hex的QCheckBox勾选上了
                        # hex发送
                        input_s=input_s.strip()     # 去掉发送文本框内容前后的空格、回车等
                        send_list=[]    # 创建一个发送列表
                        while input_s !='': # 发送内容变量不为空
                            try:
                # >>> int('12',base=16) # 如果是带参数base的话,12要以字符串的形式进行输入,12 为 16进制
                # 18
                                num=int(input_s[0:2],16)    # 将发送内容的前两位转换成16进制
                            except ValueError:
                                QMessageBox.critical(self,'wrong data','请输入十六进制数据,以空格分开!')
                                return
                            input_s=input_s[2:].strip() # 切割input_s中前两位,并去掉前后空格,最后赋值到input_s
                            send_list.append(num)   # 将已经转换的前两位添加到发送列表中
                        input_s=bytes(send_list)    # 将发送列表转换成bytes
                    else:
                        # ascii发送
                        input_s=(input_s+'
    ').encode('gbk')  # 将发送内容按gbk格式编码
    
                    num=self.ser.write(input_s) # 发送数据
                    self.data_num_sended+=num   # 已发送内容计数
                    self.send_byte.setText(str(self.data_num_sended))   # 改变已发送计数框中的值
            else:
                pass
    
        # 关闭串口
        def portClose(self):
            self.timer.stop()   # 接收定时器关闭
            self.timer_send.stop()  # 发送定时器关闭
            try:
                self.ser.close()    # 关闭串口
            except:
                pass
            self.open_ser.setEnabled(True)  # 设置按钮QPushButton状态为True
            self.close_ser.setEnabled(False)    # 设置按钮QPushButton状态为False
            self.time_num.setEnabled(True)  # QLineEdit恢复默认值
    
            # 接收数据和发送数据数目置零
            self.data_num_received=0
            self.recv_byte.setText(str(self.data_num_received))
            self.data_num_sended=0
            self.send_byte.setText(str(self.data_num_sended))
            self.groupBox_4.setTitle('串口状态(已关闭)')   # 改变串口状态框的名称
    
        # 接收数据
        def dataReceive(self):
            # self.timer.start(2000)  # 此处设置的时间为2000毫秒,表示每间隔2000毫秒运行下面的print代码
            # print('数据接收中')
            try:
                num=self.ser.inWaiting()    # 返回接收缓冲区中的字符数
            except:
                self.portClose()    # 关闭串口
                return None
            if num>0:   # 接收缓冲区中的字符数大于零时
                data=self.ser.read(num) # 读取全部数据
                num=len(data)   # 提取数据的字符长度
                # hex显示
                if self.recv_hex.checkState():  # 判断hex接收的QCheckBox是否选中
                    out_s=''
                    for i in range(0,len(data)):    # 循环遍历接收到的数据
                        # X:输出整数的大写十六进制方式;
                        out_s=out_s+'{:X}'.format(data[i])+' '  # 将字符转换成整数的大写十六进制方式
                    self.recv_data.insertPlainText(out_s)   # 将转换好的数据添加到接收数据文本框中
                else:
                    # 串口接收到的字符串为b'123',要转化成unicode字符串才能输出到窗口中去
                    self.recv_data.insertPlainText(data.decode('gbk'))
    
                # 统计接收字符的数量
                self.data_num_received+=num # 将接收到的数据的长度数赋值给计数变量
                self.recv_byte.setText(str(self.data_num_received)) # 将接收到的数据长度数显示在计数文本框中
    
                # 获取到text光标
                textCursor=self.recv_data.textCursor()
    
                # 滚动到底部
                textCursor.movePosition(textCursor.End)
    
                # 设置光标到text中去
                self.recv_data.setTextCursor(textCursor)
            else:
                pass
    
        # 打开串口
        def portOpen(self):
            # try:
            #     # 设置串口参数(此为网友代码),运行出错,
            #     self.ser.port=self.com_port.currentText()
            #     self.ser.baudrate=int(self.baudrate.currentText())
            #     self.ser.bytesize=int(self.data_bit.currentText())
            #     self.ser.stopbits=int(self.stop_bit.currentText())
            #     self.ser.parity=self.check_bit.currentText()  # 出错原因在这里,这个是不用的
            # except Exception as e:
            #     print('请先打开其它设置!',e)
    
            # 设置串口参数
            self.ser.port = self.com_port.currentText()
            self.ser.baudrate = int(self.baudrate.currentText())
            self.ser.bytesize = int(self.data_bit.currentText())
            self.ser.stopbits = int(self.stop_bit.currentText())
    
            try:
                self.ser.open()
            except:
                QMessageBox.critical(self,'Port Error','此串口不能被打开!')
                return None     # return代表结束该函数执行,此处可以写成return不加None
    
            # 打开串口接收定时器,周期为2ms
            self.timer.start(2) # 此处的意思是当2ms的时间到了之后,触发括号内的dataReceive函数
    
            if self.ser.isOpen():
                self.open_ser.setEnabled(False) # 设置按钮QPushButton状态为False
                self.close_ser.setEnabled(True) # 设置按钮QPushButton状态为True
                self.groupBox_4.setTitle('串口状态(已开启)')
    
        # 串口信息
        def portImf(self):
            if self.com_check:
                # 显示选定的串口的详细信息
                imf_s=self.com_port.currentText()   # 将串口选择的当前内容赋值给变量
                if imf_s !='':
                    self.state_label.setText('串口{}选择完成!'.format(self.com_port.currentText()))
            else:
                self.state_label.setText('请先点击检测串口按钮!')
    
        # 串口检查
        def portCheck(self):
            # 检测所有存在的串口,将信息存储在字典中
            self.com_dict={}    # 创建一个空字典
            port_list=list(serial.tools.list_ports.comports())  # 查找串口数据,并保存在列表中
            self.com_port.clear()   # 清空默认串口菜单数据
            # 遍列串口数列表,并将com名称重新添加到串口菜单中
            for port in port_list:
                self.com_dict['%s'%port[0]]='%s'%port[1]
                self.com_port.addItem(port[0])
            # 判断串口是否找到
            if len(self.com_dict)==0:
                self.state_label.setText('无串口!')
            else:
                self.state_label.setText('共有{}个串口,请选择串口!'.format(len(self.com_dict)))
            self.com_check=True
    
    if __name__ == '__main__':
        app=QApplication(sys.argv)  # 由于前面导入的是PyQt5.QtWidgets,而QApplication是该模块里的一个类
        myshow=PyqtSerial()   # 根据类来创建一个对象
        myshow.show()   # 对象显示
        sys.exit(app.exec_())

    界面文件

    from PyQt5 import QtCore, QtGui, QtWidgets
    
    class Ui_mainWindow(object):
        def setupUi(self, mainWindow):
            mainWindow.setObjectName("mainWindow")
            mainWindow.resize(721, 490)
            mainWindow.setMinimumSize(QtCore.QSize(721, 490))
            mainWindow.setMaximumSize(QtCore.QSize(721, 490))
            self.centralWidget = QtWidgets.QWidget(mainWindow)
            self.centralWidget.setObjectName("centralWidget")
            self.widget = QtWidgets.QWidget(self.centralWidget)
            self.widget.setGeometry(QtCore.QRect(20, 20, 681, 451))
            self.widget.setObjectName("widget")
            self.groupBox = QtWidgets.QGroupBox(self.widget)
            self.groupBox.setGeometry(QtCore.QRect(0, 0, 221, 311))
            self.groupBox.setObjectName("groupBox")
            self.layoutWidget = QtWidgets.QWidget(self.groupBox)
            self.layoutWidget.setGeometry(QtCore.QRect(10, 20, 201, 281))
            self.layoutWidget.setObjectName("layoutWidget")
            self.gridLayout = QtWidgets.QGridLayout(self.layoutWidget)
            self.gridLayout.setContentsMargins(0, 0, 0, 0)
            self.gridLayout.setObjectName("gridLayout")
            self.label_3 = QtWidgets.QLabel(self.layoutWidget)
            self.label_3.setObjectName("label_3")
            self.gridLayout.addWidget(self.label_3, 1, 0, 1, 1)
            self.label_4 = QtWidgets.QLabel(self.layoutWidget)
            self.label_4.setObjectName("label_4")
            self.gridLayout.addWidget(self.label_4, 2, 0, 1, 3)
            self.label_5 = QtWidgets.QLabel(self.layoutWidget)
            self.label_5.setObjectName("label_5")
            self.gridLayout.addWidget(self.label_5, 3, 0, 1, 1)
            self.label_6 = QtWidgets.QLabel(self.layoutWidget)
            self.label_6.setObjectName("label_6")
            self.gridLayout.addWidget(self.label_6, 4, 0, 1, 1)
            self.label_7 = QtWidgets.QLabel(self.layoutWidget)
            self.label_7.setObjectName("label_7")
            self.gridLayout.addWidget(self.label_7, 5, 0, 1, 1)
            self.label_8 = QtWidgets.QLabel(self.layoutWidget)
            self.label_8.setObjectName("label_8")
            self.gridLayout.addWidget(self.label_8, 6, 0, 1, 1)
            self.stop_bit = QtWidgets.QComboBox(self.layoutWidget)
            self.stop_bit.setObjectName("stop_bit")
            self.stop_bit.addItem("")
            self.stop_bit.addItem("")
            self.gridLayout.addWidget(self.stop_bit, 6, 1, 1, 2)
            self.open_ser = QtWidgets.QPushButton(self.layoutWidget)
            self.open_ser.setObjectName("open_ser")
            self.gridLayout.addWidget(self.open_ser, 7, 0, 1, 3)
            self.close_ser = QtWidgets.QPushButton(self.layoutWidget)
            self.close_ser.setObjectName("close_ser")
            self.gridLayout.addWidget(self.close_ser, 8, 0, 1, 3)
            self.com_port = QtWidgets.QComboBox(self.layoutWidget)
            self.com_port.setFocusPolicy(QtCore.Qt.WheelFocus)
            self.com_port.setObjectName("com_port")
            self.com_port.addItem("")
            self.com_port.addItem("")
            self.com_port.addItem("")
            self.com_port.addItem("")
            self.gridLayout.addWidget(self.com_port, 1, 1, 1, 2)
            self.label_2 = QtWidgets.QLabel(self.layoutWidget)
            self.label_2.setObjectName("label_2")
            self.gridLayout.addWidget(self.label_2, 0, 0, 1, 1)
            self.baudrate = QtWidgets.QComboBox(self.layoutWidget)
            self.baudrate.setObjectName("baudrate")
            self.baudrate.addItem("")
            self.baudrate.addItem("")
            self.gridLayout.addWidget(self.baudrate, 3, 1, 1, 2)
            self.data_bit = QtWidgets.QComboBox(self.layoutWidget)
            self.data_bit.setObjectName("data_bit")
            self.data_bit.addItem("")
            self.data_bit.addItem("")
            self.data_bit.addItem("")
            self.data_bit.addItem("")
            self.gridLayout.addWidget(self.data_bit, 4, 1, 1, 2)
            self.check_bit = QtWidgets.QComboBox(self.layoutWidget)
            self.check_bit.setObjectName("check_bit")
            self.check_bit.addItem("")
            self.check_bit.addItem("")
            self.gridLayout.addWidget(self.check_bit, 5, 1, 1, 2)
            self.ser_test = QtWidgets.QPushButton(self.layoutWidget)
            self.ser_test.setObjectName("ser_test")
            self.gridLayout.addWidget(self.ser_test, 0, 1, 1, 2)
            self.groupBox_2 = QtWidgets.QGroupBox(self.widget)
            self.groupBox_2.setGeometry(QtCore.QRect(250, 0, 321, 181))
            self.groupBox_2.setObjectName("groupBox_2")
            self.recv_data = QtWidgets.QTextEdit(self.groupBox_2)
            self.recv_data.setGeometry(QtCore.QRect(10, 20, 301, 151))
            self.recv_data.setObjectName("recv_data")
            self.groupBox_3 = QtWidgets.QGroupBox(self.widget)
            self.groupBox_3.setGeometry(QtCore.QRect(250, 200, 321, 181))
            self.groupBox_3.setObjectName("groupBox_3")
            self.send_data = QtWidgets.QTextEdit(self.groupBox_3)
            self.send_data.setGeometry(QtCore.QRect(10, 20, 301, 151))
            self.send_data.setObjectName("send_data")
            self.groupBox_4 = QtWidgets.QGroupBox(self.widget)
            self.groupBox_4.setGeometry(QtCore.QRect(0, 330, 221, 111))
            self.groupBox_4.setObjectName("groupBox_4")
            self.layoutWidget1 = QtWidgets.QWidget(self.groupBox_4)
            self.layoutWidget1.setGeometry(QtCore.QRect(30, 20, 161, 51))
            self.layoutWidget1.setObjectName("layoutWidget1")
            self.gridLayout_2 = QtWidgets.QGridLayout(self.layoutWidget1)
            self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
            self.gridLayout_2.setObjectName("gridLayout_2")
            self.label_9 = QtWidgets.QLabel(self.layoutWidget1)
            self.label_9.setObjectName("label_9")
            self.gridLayout_2.addWidget(self.label_9, 0, 0, 1, 1)
            self.label_11 = QtWidgets.QLabel(self.layoutWidget1)
            self.label_11.setObjectName("label_11")
            self.gridLayout_2.addWidget(self.label_11, 1, 0, 1, 1)
            self.send_byte = QtWidgets.QLineEdit(self.layoutWidget1)
            self.send_byte.setObjectName("send_byte")
            self.gridLayout_2.addWidget(self.send_byte, 1, 2, 1, 1)
            self.recv_byte = QtWidgets.QLineEdit(self.layoutWidget1)
            self.recv_byte.setObjectName("recv_byte")
            self.gridLayout_2.addWidget(self.recv_byte, 0, 2, 1, 1)
            spacerItem = QtWidgets.QSpacerItem(80, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
            self.gridLayout_2.addItem(spacerItem, 0, 1, 1, 1)
            self.state_label = QtWidgets.QLabel(self.groupBox_4)
            self.state_label.setGeometry(QtCore.QRect(20, 80, 181, 21))
            self.state_label.setText("")
            self.state_label.setObjectName("state_label")
            self.recv_hex = QtWidgets.QCheckBox(self.widget)
            self.recv_hex.setGeometry(QtCore.QRect(590, 20, 91, 19))
            self.recv_hex.setObjectName("recv_hex")
            self.send_hex = QtWidgets.QCheckBox(self.widget)
            self.send_hex.setGeometry(QtCore.QRect(590, 200, 91, 19))
            self.send_hex.setObjectName("send_hex")
            self.recv_clear = QtWidgets.QPushButton(self.widget)
            self.recv_clear.setGeometry(QtCore.QRect(590, 50, 70, 30))
            sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
            sizePolicy.setHorizontalStretch(0)
            sizePolicy.setVerticalStretch(0)
            sizePolicy.setHeightForWidth(self.recv_clear.sizePolicy().hasHeightForWidth())
            self.recv_clear.setSizePolicy(sizePolicy)
            self.recv_clear.setStyleSheet("background-color: rgb(174, 255, 235);")
            self.recv_clear.setObjectName("recv_clear")
            self.send_botton = QtWidgets.QPushButton(self.widget)
            self.send_botton.setGeometry(QtCore.QRect(590, 230, 70, 30))
            self.send_botton.setStyleSheet("background-color: rgb(174, 255, 235);")
            self.send_botton.setObjectName("send_botton")
            self.send_clear = QtWidgets.QPushButton(self.widget)
            self.send_clear.setGeometry(QtCore.QRect(590, 280, 70, 30))
            self.send_clear.setStyleSheet("background-color: rgb(174, 255, 235);")
            self.send_clear.setObjectName("send_clear")
            self.layoutWidget2 = QtWidgets.QWidget(self.widget)
            self.layoutWidget2.setGeometry(QtCore.QRect(260, 400, 211, 23))
            self.layoutWidget2.setObjectName("layoutWidget2")
            self.horizontalLayout = QtWidgets.QHBoxLayout(self.layoutWidget2)
            self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
            self.horizontalLayout.setObjectName("horizontalLayout")
            self.time_send = QtWidgets.QCheckBox(self.layoutWidget2)
            self.time_send.setObjectName("time_send")
            self.horizontalLayout.addWidget(self.time_send)
            spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
            self.horizontalLayout.addItem(spacerItem1)
            self.time_num = QtWidgets.QLineEdit(self.layoutWidget2)
            self.time_num.setMinimumSize(QtCore.QSize(60, 0))
            self.time_num.setMaximumSize(QtCore.QSize(60, 16777215))
            self.time_num.setObjectName("time_num")
            self.horizontalLayout.addWidget(self.time_num)
            self.label = QtWidgets.QLabel(self.layoutWidget2)
            self.label.setObjectName("label")
            self.horizontalLayout.addWidget(self.label)
            mainWindow.setCentralWidget(self.centralWidget)
    
            self.retranslateUi(mainWindow)
            QtCore.QMetaObject.connectSlotsByName(mainWindow)
    
        def retranslateUi(self, mainWindow):
            _translate = QtCore.QCoreApplication.translate
            mainWindow.setWindowTitle(_translate("mainWindow", "串口小助手"))
            self.groupBox.setTitle(_translate("mainWindow", "串口设置"))
            self.label_3.setText(_translate("mainWindow", "串口选择:"))
            self.label_4.setText(_translate("mainWindow", "Serial Port (COM1->COM2)"))
            self.label_5.setText(_translate("mainWindow", "波特率:"))
            self.label_6.setText(_translate("mainWindow", "数据位:"))
            self.label_7.setText(_translate("mainWindow", "校验位:"))
            self.label_8.setText(_translate("mainWindow", "停止位:"))
            self.stop_bit.setItemText(0, _translate("mainWindow", "1"))
            self.stop_bit.setItemText(1, _translate("mainWindow", "2"))
            self.open_ser.setText(_translate("mainWindow", "打开串口"))
            self.close_ser.setText(_translate("mainWindow", "关闭串口"))
            self.com_port.setItemText(0, _translate("mainWindow", "COM1"))
            self.com_port.setItemText(1, _translate("mainWindow", "COM2"))
            self.com_port.setItemText(2, _translate("mainWindow", "COM3"))
            self.com_port.setItemText(3, _translate("mainWindow", "COM4"))
            self.label_2.setText(_translate("mainWindow", "串口检测:"))
            self.baudrate.setItemText(0, _translate("mainWindow", "9600"))
            self.baudrate.setItemText(1, _translate("mainWindow", "115200"))
            self.data_bit.setItemText(0, _translate("mainWindow", "8"))
            self.data_bit.setItemText(1, _translate("mainWindow", "7"))
            self.data_bit.setItemText(2, _translate("mainWindow", "6"))
            self.data_bit.setItemText(3, _translate("mainWindow", "5"))
            self.check_bit.setItemText(0, _translate("mainWindow", "NONE"))
            self.check_bit.setItemText(1, _translate("mainWindow", "ODD"))
            self.ser_test.setText(_translate("mainWindow", "检测串口"))
            self.groupBox_2.setTitle(_translate("mainWindow", "接收区"))
            self.groupBox_3.setTitle(_translate("mainWindow", "发送区"))
            self.groupBox_4.setTitle(_translate("mainWindow", "串口状态"))
            self.label_9.setText(_translate("mainWindow", "已接收:"))
            self.label_11.setText(_translate("mainWindow", "已发送:"))
            self.send_byte.setText(_translate("mainWindow", "0"))
            self.recv_byte.setText(_translate("mainWindow", "0"))
            self.recv_hex.setText(_translate("mainWindow", "Hex接收"))
            self.send_hex.setText(_translate("mainWindow", "Hex发送"))
            self.recv_clear.setText(_translate("mainWindow", "清除"))
            self.send_botton.setText(_translate("mainWindow", "发送"))
            self.send_clear.setText(_translate("mainWindow", "清除"))
            self.time_send.setText(_translate("mainWindow", "定时发送"))
            self.time_num.setText(_translate("mainWindow", "1000"))
            self.label.setText(_translate("mainWindow", "ms/次"))
    
    
    if __name__ == "__main__":
        import sys
        app = QtWidgets.QApplication(sys.argv)
        mainWindow = QtWidgets.QMainWindow()
        ui = Ui_mainWindow()
        ui.setupUi(mainWindow)
        mainWindow.show()
        sys.exit(app.exec_())

    以前就是参照网友设计的一个串口助手小工具。

    如有问题,可关注微信公众号进行咨询!

  • 相关阅读:
    ST3 插件和技巧
    博客园中 代码使用 sublime-text 的主题
    JavaScript中的 prototype 和 constructor
    JavaScript 代码小片段
    JavaScript 静态方法和实例方法
    JavaScript 闭包的例子
    关于linux 交叉编译器的安装
    scrapy 实现mysql 数据保存
    django 实现 内网访问 和 用花生壳进行内网穿透
    django 中从外界借助多个网站时 static 的存放和整理
  • 原文地址:https://www.cnblogs.com/ubuntu1987/p/12191633.html
Copyright © 2011-2022 走看看