本章目标

利用PySide2的信号和槽,在点击了GUI上的“启动”按钮后:

1、从界面上获得各项输入的参数数据

2、对参数进行转换和输出

3、进行合理范围判断,对于异常的数据进行弹窗处理

步骤实施

首先引入 Slot 模块,学过vue的话知道这个叫 插槽 。

from PySide2.QtCore import Slot  # 插槽模块

然后创建槽函数,用来接受点击了“启动”按钮后传过来的数据:

@Slot()
def collect_data():
    print('clicked')

其中 “@Slot”是装饰器,表明这是一个槽函数。

接下来是建立连接:

start_btn = QPushButton()  # 修改父类
start_btn.setText('启动')
start_btn.clicked.connect(collect_data)  # 建立连接

测试一下,运行界面,然后点一下启动按钮,看看有没有输出:

python OPCUA库 安全策略选项 python开发安全工具_PySide2

很好,成功了。

这样就开始试着去调用各个控件去输出数据:

@Slot()
def collect_data():
    print(ip_line_edit.text())
    print(type(ip_line_edit.text()))
    print(thread_line_edit.text())
    print(type(thread_line_edit.text()))
    print(port_line_edit1.text())
    print(type(port_line_edit1.text()))
    print(port_line_edit2.text())
    print(type(port_line_edit2.text()))

打印获得的数据,和判断一下类型:

python OPCUA库 安全策略选项 python开发安全工具_PySide2_02

127.0.0.1
<class 'str'>
500
<class 'str'>
0
<class 'str'>
65535
<class 'str'>

成功接收到数据,那接下来开始做数据清洗的动作:

MESSAGE = (
    'ip或者网址不得为空',
    '并发数不得小于1',
    '开始端口号不得小于0',
    '结束端口号需要大于开始端口号'
)


def show_tip(message):  # 遇到问题,则丢到这里,抛到界面上
    tip = QMessageBox(window)
    tip.setWindowTitle('提示')
    tip.setText(message)
    tip.show()


@Slot()
def collect_data():
    ip = ip_line_edit.text()
    thread_line = int(thread_line_edit.text())
    port_start = int(port_line_edit1.text())
    port_end = int(port_line_edit2.text())
    if not ip:
        show_tip(MESSAGE[0])
        return
    if thread_line < 1:
        show_tip(MESSAGE[1])
        return
    if port_start < 0:
        show_tip(MESSAGE[2])
        return
    if port_end <= port_start:
        show_tip(MESSAGE[3])
        return
    # TODO
    # 调用逻辑代码

这里又引入了QMessageBox模块,用于错误信息弹窗。

测试了下各种情况,符合预期,继续下一步,封装之前写好的逻辑代码,方便调用:

PortSearch.py的代码

# coding=utf-8
import socket
import threading


class PortSearch:
    def __init__(self, ip='', thread_line=500, port_start=0, port_end=65535):
        self.global_list = []  # 收集开着的端口信息
        self.global_err_list = []  # 收集关着的端口信息
        self.global_thread_list = []  # 线程收集列表
        self.lock = threading.Lock()  # 创建一个锁
        self.sem = threading.Semaphore(thread_line)  # 并发500
        self.ip = ip
        self.port_start = port_start
        self.port_end = port_end

    def port_search(self, _ip, _port):
        address = (_ip, _port)  # 地址必须是一个元祖,第一个是str,第二个是int
        client = socket.socket()  # 创建套接字
        code = client.connect_ex(address)
        self.lock.acquire()
        try:
            if code == 0:  # 当端口开启时,错误码是0,其他错误码均表示未开该端口
                self.global_list.append([_ip, _port, code, client.recv(1024)])
            else:
                self.global_err_list.append([_ip, _port, code])  # 此处不能调用 recv api,会抛出异常
        except BaseException:
            print('进行列表操作时报错')
        self.lock.release()
        client.close()  # 关闭连接

    def run(self):
        for i in range(self.port_start, self.port_end+1):  # 0~65535
            with self.sem:
                thread = threading.Thread(target=self.port_search, args=(self.ip, i))
                self.global_thread_list.append(thread)
                thread.start()

        for t in self.global_thread_list:  # 遍历,等待所有进程运行完毕
            t.join()
        return self.global_list

继续修改PortSearchGUI.py的代码,引入逻辑代码:

from PortSearch import PortSearch
import json

在槽函数里调用:

search = PortSearch(ip=ip, thread_line=thread_line, port_start=port_start, port_end=port_end)
    result = search.run()
    type(result)
    print(result)
    # string = json.dumps(result)
    # report_box_edit.setPlainText(string)

结语

试运行,能出结果,还有一些问题待优化,比如点击按钮之后工具会卡住,结果还不能输出到大编辑框内。

这些问题都留到下个篇章处理。

今天的目标已经达到,而且超出。下个篇章主要做一些优化方面的事情,使得工具更好用。