• PyQt实现测试工具


    测试工具:

    1. 基本界面实现:

    # coding:utf-8
    import sys
    import os
    import os.path
    import re
    import time
    from PyQt4 import QtCore
    from PyQt4 import QtGui
    from PyQt4 import Qt
    
    msg_file_not_exists = 'Can not find config file'
    msg_file_is_null = 'There is no attribute now'
    project_file = 'D:workspacepy_demoproject.py'
    case_file = 'D:workspacepy_democase.py'
    case_seprator = '|'
    
    
    def get_time():
        return time.strftime("%Y-%m-%d %X", time.localtime(time.time()))
    
    
    def _save_project(project_index, project_info, create_time, edit_time, project_detail):
        all_lines = []
        line_str = str(project_index) + case_seprator + project_info + case_seprator + 
                            create_time + case_seprator + edit_time + case_seprator + project_detail + '
    '
        if os.path.exists(project_file):
            if os.path.getsize(project_file):
                with open(project_file, 'r') as f:
                    all_lines = f.readlines()
                    if all_lines[project_index:project_index + 1]:
                        all_lines[project_index] = line_str
                    else:
                        all_lines.append(line_str)
            else:
                all_lines.append(line_str)
        else:
            all_lines.append(line_str)
        with open(project_file, 'w') as f:
            f.writelines(all_lines)
    
    
    def _save_case(project_index, case_index, case_info, create_time, 
                    edit_time, start_run_time, finish_run_time, run_type, run_script, run_result):
        all_lines = []
        line_str = str(project_index) + case_seprator + str(
                            case_index) + case_seprator + case_info + case_seprator + 
                            create_time + case_seprator + edit_time + case_seprator + 
                            start_run_time + case_seprator + finish_run_time + 
                            case_seprator + run_type + case_seprator + run_script +
                            case_seprator + run_result + '
    '
        if os.path.exists(case_file):
            if os.path.getsize(case_file):
                with open(case_file, 'r') as f:
                    all_lines = f.readlines()
                    if all_lines[case_index:case_index + 1]:
                        all_lines[case_index] = line_str
                    else:
                        all_lines.append(line_str)
            else:
                all_lines.append(line_str)
        else:
            all_lines.append(line_str)
        with open(case_file, 'w') as f:
            f.writelines(all_lines)
    
    
    class MainWindow(QtGui.QMainWindow):
    
        def __init__(self):
            super(MainWindow, self).__init__()
    
            self.setWindowTitle('AutoTest')
            self.resize(600, 300)
    
            self.setTool = self.addToolBar('Set')
            self.setToolAction = Qt.QAction(
                QtGui.QIcon('images/open.png'), u'Set', self)
            self.setToolAction.setShortcut('Ctrl+S')
            self.setToolAction.setStatusTip(u'Config')
            self.setToolAction.triggered.connect(self.set_config_file)
            self.setTool.addAction(self.setToolAction)
    
            self.newProjectTool = self.addToolBar('NewProject')
            self.newProjectToolAction = Qt.QAction(
                QtGui.QIcon('images/selectall.png'), u'CreateProject', self)
            self.newProjectToolAction.triggered.connect(self.newproject)
            self.newProjectTool.addAction(self.newProjectToolAction)
    
            self.newCaseTool = self.addToolBar('NewCase')
            self.newCaseToolAction = Qt.QAction(
                QtGui.QIcon('images/copy.png'), u'CreateCase', self)
            self.newCaseToolAction.triggered.connect(self.newcase)
            self.newCaseTool.addAction(self.newCaseToolAction)
    
            wedgit = QtGui.QWidget()
            layout = QtGui.QVBoxLayout()
    
            self.tree = QtGui.QTreeWidget()
            self.tree.setColumnCount(4)
            self.tree.setHeaderLabels(['Step', 'Start Time','Finish Time','Result'])
            # self.tree.headerItem().setStretchLastSection(True)
            self.load_case()
    
            self.tree.itemChanged.connect(self.save_project)
            self.tree.itemDoubleClicked.connect(self.edit_project)
    
            # self.tree.setExpanded(0,True)
    
            self.log_text = QtGui.QTextBrowser()
            layout.addWidget(self.tree)
            layout.addWidget(self.log_text)
            wedgit.setLayout(layout)
            self.setCentralWidget(wedgit)
    
        def set_config_file(self):
            self.dialog = config_file()
            self.dialog.show()
    
        def load_case(self):
            if os.path.exists(project_file):
                if os.path.getsize(project_file):
                    with open(project_file, 'r') as f:
                        all_lines = f.readlines()
    
                    for i, line in enumerate(all_lines):
                        project_text = line.split(case_seprator)[1]
                        self.project = QtGui.QTreeWidgetItem(self.tree)
                        self.tree.setItemExpanded(self.project, True)  # 设置自动扩展
    
                        self.tree.addTopLevelItem(self.project)
                        self.project.setText(0, project_text)
                        project_no = line.split(case_seprator)[0]
    
                        if os.path.exists(case_file):
                            if os.path.getsize(case_file):
                                with open(case_file, 'r') as f:
                                    case_all_line = f.readlines()
                                for i, line in enumerate(case_all_line):
                                    case_project_no = line.split(
                                        case_seprator)[0]
                                    if case_project_no == project_no:
                                        case_name = line.split(
                                            case_seprator)[1]
                                        case_result = line.split(
                                            case_seprator)[2]
                                        self.case = QtGui.QTreeWidgetItem(
                                            self.project)
                                        self.case.setText(0, case_name)
                                        self.case.setText(0, case_result)
    
        def newproject(self):
            self.tree.blockSignals(True)  # 禁止itemchanged信号
            self.project = QtGui.QTreeWidgetItem(self.tree)
            self.tree.addTopLevelItem(self.project)
            self.project.setFlags(self.project.flags() | QtCore.Qt.ItemIsEditable)
            self.project.setText(0, 'Here is project information')
            self.tree.blockSignals(False)
            self.save_project(self.project, 0)
    
        def newcase(self):
            self.tree.blockSignals(True)
            check_index = self.tree.indexOfTopLevelItem(self.tree.currentItem())
            if check_index > -1:
                self.project = self.tree.currentItem()
            else:
                self.project = self.tree.currentItem().parent()
    
            self.case = QtGui.QTreeWidgetItem(self.project)
            self.case.setFlags(self.project.flags() | QtCore.Qt.ItemIsEditable)
            self.case.setText(0, 'Here is case name')
            self.case.setCheckState(0, QtCore.Qt.Unchecked)
            self.project.addChild(self.case)
            self.tree.blockSignals(False)
            self.save_project(self.case, 0)
    
        def save_case(self, item, column):
            print 'item text', item.text(column)
            print 'index', self.tree.indexFromItem(item).row()
            print 'parent text', item.parent().text(0)
    
        def save_project(self, item, column):
            check_index = self.tree.indexOfTopLevelItem(item)
            if check_index > -1:
                project_index = check_index
                project_info = str(item.text(column))
                create_time = get_time()
                edit_time = get_time()
                _save_project(project_index, project_info,
                              create_time, edit_time, '')
            else:
                project_index = self.tree.indexOfTopLevelItem(item.parent())
                case_index = self.tree.indexFromItem(item).row()
                case_info = str(item.text(column))  # case_index.data()
                create_time=get_time()
                edit_time=get_time()
                _save_case(project_index, case_index, case_info,create_time,edit_time,'','','','','')
    
            print 'index', project_index
            print 'text', item.text(0)
            print 'column', column
    
        def edit_project(self,item,column):
            current_index = self.tree.indexOfTopLevelItem(item)
            print current_index
            if current_index > -1:
                project_dialog = project(None,current_index)
                project_dialog.exec_()
                if project_dialog.reply == QtGui.QMessageBox.Yes:                #窗口之间传值     
                    project_title = project_dialog.get_project_title()
                    item.setText(0,project_title)
            else:
                project_index = self.tree.indexOfTopLevelItem(item.parent())
                case_index = self.tree.indexFromItem(item).row()
                case_dialog = case(None,project_index,case_index)
                case_dialog.exec_()
                if case_dialog.reply == QtGui.QMessageBox.Yes:
                    case_title = case_dialog.get_case_title()
                    item.setText(0,case_title)
    
    
    class case(QtGui.QDialog):
    
        def __init__(self, parent, project_index, case_index):
            super(case, self).__init__(parent)
            self.project_index = project_index
            self.case_index = case_index
            self.setWindowTitle('Case')
            self.resize(500, 300)
            self.setup_ui()
            self.update_flag = False
    
        def closeEvent(self, event):
            if self.update_flag:
                self.reply = QtGui.QMessageBox.question(
                    self, 'Warning', 'Some content was changed,save?', QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
                if self.reply == QtGui.QMessageBox.No:
                    # event.ignore()  # 选择No直接关闭窗口,event.ignore表示保留原窗口
                    pass
                else:
                    case_edit_time = get_time()
                    case_title = self.case_title_edit_line.text()
                    _save_case(self.project_index, int(self.caseNo), self.case_title_edit_line.text(
                    ), self.case_create_time, case_edit_time,'','',self.case_run_type_edit_line.currentText (),'','pending run')
    
        def set_update_flag(self):
            self.update_flag = True
    
        def get_case_title(self):
            return self.case_title_edit_line.text()
    
        def setup_ui(self):
            all_lines=[]
            with open(case_file, 'r') as f:
                for line in f.readlines():
                    if int(line.split(case_seprator)[0]) == int(self.project_index):
                        all_lines.append(line)
    
            case_info = all_lines[self.case_index]
            self.caseNo = case_info.split(case_seprator)[1]
            case_title = case_info.split(case_seprator)[2]
            self.case_create_time = case_info.split(case_seprator)[3]
            case_edit_time = case_info.split(case_seprator)[4]
            case_start_time = case_info.split(case_seprator)[5]
            case_finish_time = case_info.split(case_seprator)[6]
            case_run_type = case_info.split(case_seprator)[7]
            case_run_script = case_info.split(case_seprator)[8]
            case_run_result = case_info.split(case_seprator)[9]
    
            mainlayout = QtGui.QVBoxLayout()
            layout1 = QtGui.QHBoxLayout()
            self.case_number_label = QtGui.QLabel('No:')
            self.case_number_read_line = QtGui.QLabel(self.caseNo)
            self.case_number_label.setBuddy(self.case_number_read_line)
            layout1.addWidget(self.case_number_label)
            layout1.addWidget(self.case_number_read_line)
            layout1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
    
            layout2 = QtGui.QHBoxLayout()
            self.case_title_label = QtGui.QLabel('Title:')
            self.case_title_edit_line = QtGui.QLineEdit(case_title)
            self.case_title_label.setBuddy(self.case_title_edit_line)
            layout2.addWidget(self.case_title_label)
            layout2.addWidget(self.case_title_edit_line)
            layout2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
            self.case_title_edit_line.textChanged.connect(self.set_update_flag)
    
            layout3 = QtGui.QHBoxLayout()
            self.case_create_time_label = QtGui.QLabel('CreateTime:')
            self.case_create_time_read_line = QtGui.QLabel(
                self.case_create_time)
            self.case_create_time_label.setBuddy(
                self.case_create_time_read_line)
            layout3.addWidget(self.case_create_time_label)
            layout3.addWidget(self.case_create_time_read_line)
            layout3.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
    
            layout4 = QtGui.QHBoxLayout()
            self.case_edit_time_label = QtGui.QLabel('EditTime:')
            self.case_edit_time_read_line = QtGui.QLabel(case_edit_time)
            self.case_edit_time_label.setBuddy(self.case_edit_time_read_line)
            layout4.addWidget(self.case_edit_time_label)
            layout4.addWidget(self.case_edit_time_read_line)
            layout4.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
    
            layout5 = QtGui.QHBoxLayout()
            self.case_start_time_label = QtGui.QLabel('Start Run Time:')
            self.case_start_time_read_line = QtGui.QLineEdit(case_start_time)
            self.case_start_time_label.setBuddy(self.case_start_time_read_line)
            layout5.addWidget(self.case_start_time_label)
            layout5.addWidget(self.case_start_time_read_line)
            layout5.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
    
            layout6 = QtGui.QHBoxLayout()
            self.case_finish_time_label = QtGui.QLabel('Finish Run Time:')
            self.case_finish_time_read_line = QtGui.QLineEdit(case_finish_time)
            self.case_finish_time_label.setBuddy(self.case_finish_time_read_line)
            layout6.addWidget(self.case_finish_time_label)
            layout6.addWidget(self.case_finish_time_read_line)
            layout6.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
    
            layout7 = QtGui.QHBoxLayout()
            self.case_run_type_label = QtGui.QLabel('Run Type:')
            self.case_run_type_edit_line = QtGui.QComboBox(self)
            self.case_run_type_edit_line.addItems(['Shell','Python'])
            self.case_run_type_label.setBuddy(self.case_run_type_edit_line)
            layout7.addWidget(self.case_run_type_label)
            layout7.addWidget(self.case_run_type_edit_line)
            layout7.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
    
            layout8 = QtGui.QHBoxLayout()
            self.case_run_script_label = QtGui.QLabel('Run Script:')
            self.case_run_script_edit_line = QtGui.QTextEdit(case_run_script)
            self.case_run_script_label.setBuddy(self.case_run_script_edit_line)
            layout8.addWidget(self.case_run_script_label)
            layout8.addWidget(self.case_run_script_edit_line)
            layout8.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
    
            layout9 = QtGui.QHBoxLayout()
            self.case_run_result_label = QtGui.QLabel('Run Result:')
            self.case_run_result_read_line = QtGui.QLineEdit(case_run_result)
            self.case_run_result_label.setBuddy(self.case_run_result_read_line)
            layout9.addWidget(self.case_run_result_label)
            layout9.addWidget(self.case_run_result_read_line)
            layout9.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
    
            mainlayout.addLayout(layout1)
            mainlayout.addLayout(layout2)
            mainlayout.addLayout(layout3)
            mainlayout.addLayout(layout4)
            mainlayout.addLayout(layout5)
            mainlayout.addLayout(layout6)
            mainlayout.addLayout(layout7)
            mainlayout.addLayout(layout8)
            mainlayout.addLayout(layout9)
            self.setLayout(mainlayout)
    
    
    class project(QtGui.QDialog):
    
        def __init__(self, parent,project_index):
            super(project, self).__init__(parent)
            self.project_index = project_index
            self.setWindowTitle('Project')
            self.resize(500, 300)
            self.setup_ui()
            self.update_flag = False
    
        def closeEvent(self, event):
            if self.update_flag:
                self.reply = QtGui.QMessageBox.question(
                    self, 'Warning', 'Some content was changed,save?', QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
                if self.reply == QtGui.QMessageBox.No:
                    event.accept()  # 选择No直接关闭窗口,event.ignore表示保留原窗口
                else:
                    edit_time = get_time()
                    project_title = self.project_title_edit_line.text()
                    _save_project(int(self.projectNo), self.project_title_edit_line.text(
                    ), self.project_create_time, edit_time, self.project_detail_edit_line.toPlainText())
    
        def set_update_flag(self):
            self.update_flag = True
    
        def get_project_title(self):
            return self.project_title_edit_line.text()
    
        def setup_ui(self):
            with open(project_file, 'r') as f:
                all_lines = f.readlines()
    
            project_info = all_lines[self.project_index]
            self.projectNo = project_info.split(case_seprator)[0]
            project_title = project_info.split(case_seprator)[1]
            self.project_create_time = project_info.split(case_seprator)[2]
            project_edit_time = project_info.split(case_seprator)[3]
            project_detail = project_info.split(case_seprator)[4]
    
            mainlayout = QtGui.QVBoxLayout()
            layout1 = QtGui.QHBoxLayout()
            self.project_number_label = QtGui.QLabel('No:')
            self.project_number_read_line = QtGui.QLabel(self.projectNo)
            self.project_number_label.setBuddy(self.project_number_read_line)
            layout1.addWidget(self.project_number_label)
            layout1.addWidget(self.project_number_read_line)
            layout1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
    
            layout2 = QtGui.QHBoxLayout()
            self.project_title_label = QtGui.QLabel('Title:')
            self.project_title_edit_line = QtGui.QLineEdit(project_title)
            self.project_title_label.setBuddy(self.project_title_edit_line)
            layout2.addWidget(self.project_title_label)
            layout2.addWidget(self.project_title_edit_line)
            layout2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
            self.project_title_edit_line.textChanged.connect(self.set_update_flag)
    
            layout3 = QtGui.QHBoxLayout()
            self.project_create_time_label = QtGui.QLabel('CreateTime:')
            self.project_create_time_read_line = QtGui.QLabel(
                self.project_create_time)
            self.project_create_time_label.setBuddy(
                self.project_create_time_read_line)
            layout3.addWidget(self.project_create_time_label)
            layout3.addWidget(self.project_create_time_read_line)
            layout3.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
    
            layout4 = QtGui.QHBoxLayout()
            self.project_edit_time_label = QtGui.QLabel('EditTime:')
            self.project_edit_time_read_line = QtGui.QLabel(project_edit_time)
            self.project_edit_time_label.setBuddy(self.project_edit_time_read_line)
            layout4.addWidget(self.project_edit_time_label)
            layout4.addWidget(self.project_edit_time_read_line)
            layout4.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
    
            layout5 = QtGui.QHBoxLayout()
            self.project_detail_label = QtGui.QLabel('Detail:')
            self.project_detail_edit_line = QtGui.QTextEdit(project_detail)
            self.project_detail_label.setBuddy(self.project_detail_edit_line)
            layout5.addWidget(self.project_detail_label)
            layout5.addWidget(self.project_detail_edit_line)
            layout5.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
            self.project_detail_edit_line.textChanged.connect(self.set_update_flag)
    
            mainlayout.addLayout(layout1)
            mainlayout.addLayout(layout2)
            mainlayout.addLayout(layout3)
            mainlayout.addLayout(layout4)
            mainlayout.addLayout(layout5)
            self.setLayout(mainlayout)
    
    
    class config_file(QtGui.QDialog):
    
        def __init__(self, parent=None):
            super(config_file, self).__init__(parent)
            self.file_name = 'D:workspacepy_demoNotePadconfig.py'
            self.separator_char = '='
    
            self.config_table = QtGui.QTableWidget(0, 2)
            self.config_table.setSelectionMode(
                QtGui.QAbstractItemView.SingleSelection)       # 设置为只能选中单个目标
            self.config_table.setSelectionBehavior(
                QtGui.QAbstractItemView.SelectRows)            # 设置选中单个单元格
            self.config_table.setEditTriggers(
                QtGui.QAbstractItemView.NoEditTriggers)        # 默认双击可以编辑,这里设置禁止编辑
    
            self.config_table.setAlternatingRowColors(True)    # 隔行改变颜色
            self.config_table.setShowGrid(True)                # 设置显示网格线
            self.config_table.setSortingEnabled(False)         # 设置禁止排序
            self.config_table.setHorizontalHeaderLabels(['Attribute', 'Value'])
    
            self.load_config()
            layout = QtGui.QVBoxLayout()
    
            btnlayout = QtGui.QHBoxLayout()
            self.add_item_btn = QtGui.QPushButton()
            self.add_item_btn.setText('Add')
            self.add_item_btn.clicked.connect(self.add_config)
    
            self.edit_item_btn = QtGui.QPushButton()
            self.edit_item_btn.setText('Edit')
            self.edit_item_btn.clicked.connect(self.edit_config)
    
            self.delete_item_btn = QtGui.QPushButton()
            self.delete_item_btn.setText('Delete')
            self.delete_item_btn.clicked.connect(self.delete_config)
    
            btnlayout.addStretch()
            btnlayout.addWidget(self.add_item_btn)
            btnlayout.addWidget(self.edit_item_btn)
            btnlayout.addWidget(self.delete_item_btn)
    
            layout.addWidget(self.config_table)
            layout.addLayout(btnlayout)
            self.setLayout(layout)
    
        def load_config(self):
            if os.path.exists(self.file_name):
                if os.path.getsize(self.file_name):
                    with open(self.file_name, 'r') as f:
                        for line_count, line in enumerate(f.readlines()):
                            if self.separator_char in line:
                                attribute = line.split(self.separator_char)[
                                    0].strip()
                                value = line.split(self.separator_char)[1].strip()
                                self.config_table.insertRow(line_count)
                                self.newAttr = QtGui.QTableWidgetItem(attribute)
                                self.config_table.setItem(
                                    line_count, 0, self.newAttr)
                                self.newValue = QtGui.QTableWidgetItem(value)
                                self.config_table.setItem(
                                    line_count, 1, self.newValue)
                else:
                    self.config_table.insertRow(0)
                    self.newAttr = QtGui.QTableWidgetItem(
                        'There is no attribute now')
                    self.config_table.setSpan(0, 0, 1, 2)
                    self.newAttr.setTextAlignment(
                        QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
                    self.config_table.setItem(0, 0, self.newAttr)
            else:
                self.config_table.insertRow(0)
                self.newAttr = QtGui.QTableWidgetItem(msg_file_not_exists)
                self.config_table.setSpan(0, 0, 1, 2)
                self.newAttr.setTextAlignment(
                    QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
                self.config_table.setItem(0, 0, self.newAttr)
    
        def add_config(self):
            if str(self.config_table.item(0, 0).text()) in (msg_file_is_null, msg_file_not_exists):
                current_row_index = self.config_table.currentRow()
                self.config_table.removeRow(current_row_index)
    
            total_count_index = self.config_table.rowCount()
            self.config_table.setEditTriggers(
                QtGui.QAbstractItemView.DoubleClicked)
            self.config_table.insertRow(total_count_index)
    
        def edit_config(self):
            self.config_table.setEditTriggers(
                QtGui.QAbstractItemView.DoubleClicked)
    
        def delete_config(self):
            current_row_index = self.config_table.currentRow()
            if current_row_index != -1:
                reply = QtGui.QMessageBox.warning(
                    self, 'Warning', 'Could you confirm to delete the config', QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
                if reply == QtGui.QMessageBox.Yes:
                    self.config_table.removeRow(current_row_index)
    
        def closeEvent(self, event):
            row_count = self.config_table.rowCount()
            all_lines = []
            for row in range(row_count):
                if self.config_table.item(row, 0) and self.config_table.item(row, 1):
                    line = str(self.config_table.item(row, 0).text()) + 
                        self.separator_char + 
                        str(self.config_table.item(row, 1).text())
                    all_lines.append(line + '
    ')
                else:
                    QtGui.QMessageBox.information(
                        self, 'Error', 'Config shoud not be null')
                    event.ignore()
                    return
            with open(self.file_name, 'w') as f:
                f.writelines(all_lines)
    
    app = QtGui.QApplication(sys.argv)
    mainwindow = MainWindow()
    mainwindow.show()
    app.exec_()
  • 相关阅读:
    ubuntu14.04 Cannot find OpenSSL's <evp.h>
    git 常用命令
    Python3常用模块的安装
    Centos7 安装配置优化mysql(mariadb分支)
    Centos7 编译安装python3
    Centos6.5搭建git远程仓库
    年轻
    springboot 报错Field XXX required a bean of type XXX that could not be found.
    springboot 启动报错[classpath:/application.yml] but snakeyaml was not found on the classpath
    idea 使用点击maven clean/install或maven其他命令失败,显示:乱码+archetypeCatalog=internal
  • 原文地址:https://www.cnblogs.com/lypy/p/6411023.html
Copyright © 2020-2023  润新知