跳转至

信号与槽

信号是Qt的核心内容。

信号 [Signal]

 信号就是事件(按钮点击、内容发生改变等),或者状态(窗口选中、切换等);当程序触发了某种事件,那么即可发射出来一个信号

槽 [Slot]

 如果想捕获这个信号,然后执行相应的逻辑代码,那么就需要使用到实际上是一个函数,当信号发射出来后,会执行与之绑定的函数。

信号与槽的连接

 为了实现点击某个按钮实现对应函数时,需要把具体的信号绑定到一起。

大致操作流程与方法如下

对象.信号.connect(槽函数)

信号与槽 Demon

接收信号

import sys

from PyQt5.QtWidgets import QApplication, QWidget, QPushButton


class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        # 更改当前Widge的宽高
        self.resize(500, 300)
        # 创建一个按钮
        btn = QPushButton("点我点我", self)
        # 设置窗口位置、宽高
        btn.setGeometry(200, 200, 100, 30)
        # 将按钮被点击时触发的信号与我们定义的函数(方法)进行绑定
        # 注意:这里没有(),即写函数的名字,而不是名字()
        btn.clicked.connect(self.click_my_btn)

    def click_my_btn(self, arg):
        # 槽函数,点击按钮则调用该函数
        # 这里的参数正好是信号发出,传递的参数
        print("点击按钮啦~", arg)


if __name__ == '__main__':
    app = QApplication(sys.argv)

    w = MyWindow()
    w.show()

    app.exec()

实现效果

自定义信号

 除了接收Qt自带的信号外,也可以自行定义信号,在合适的时机,自行发射信号。

方法:pyqtSingal

声明信号需要在类中的函数之外进行声明

import sys
import time

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *


class MyWindow(QWidget):
    # 声明一个信号 只能放在函数的外面
    my_signal = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.init_ui()
        self.msg_history = list()  # 用来存放消息

    def init_ui(self):
        self.resize(500, 200)

        # 创建一个整体布局器
        container = QVBoxLayout()

        # 用来显示检测到漏洞的信息
        self.msg = QLabel("")
        self.msg.resize(440, 15)
        # print(self.msg.frameSize())
        self.msg.setWordWrap(True)  # 自动换行
        self.msg.setAlignment(Qt.AlignTop)  # 靠上
        # self.msg.setStyleSheet("background-color: yellow; color: black;")

        # 创建一个滚动对象
        scroll = QScrollArea()
        scroll.setWidget(self.msg)

        # 创建垂直布局器,用来添加自动滚动条
        v_layout = QVBoxLayout()
        v_layout.addWidget(scroll)

        # 创建水平布局器
        h_layout = QHBoxLayout()
        btn = QPushButton("开始检测", self)
        # 绑定按钮的点击,点击按钮则开始检测
        btn.clicked.connect(self.check)
        h_layout.addStretch(1)  # 伸缩器
        h_layout.addWidget(btn)
        h_layout.addStretch(1)

        # 操作将要显示的控件以及子布局器添加到container
        container.addLayout(v_layout)
        container.addLayout(h_layout)

        # 设置布局器
        self.setLayout(container)

        # 绑定信号和槽
        self.my_signal.connect(self.my_slot)

    def my_slot(self, msg):
        # 更新内容
        print(msg)
        self.msg_history.append(msg)
        self.msg.setText("<br>".join(self.msg_history))
        self.msg.resize(440, self.msg.frameSize().height() + 15)
        self.msg.repaint()  # 更新内容,如果不更新可能没有显示新内容

    def check(self):
        for i, ip in enumerate(["192.168.1.%d" % x for x in range(1, 255)]):
            msg = "模拟,正在检查 %s 上的漏洞...." % ip
            # print(msg)
            if i % 5 == 3:
                # 表示发射信号 对象.信号.发射(参数)
                self.my_signal.emit(msg + "【发现漏洞】")
            time.sleep(0.01)


if __name__ == '__main__':
    app = QApplication(sys.argv)

    w = MyWindow()
    w.show()

    app.exec()

实现效果

综合应用

import sys
import time

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *


class MyWindow(QWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("信号与槽")
        self.init_ui()

    # 主初始化方法
    def main_window_init(self):
        # 设置窗口大小
        self.resize(300, 200)
        #  主界面布局 [垂直布局]
        self.main_layout = QVBoxLayout()

    def init_ui(self):
        self.main_window_init()

        # 信息Box初始化
        self.message_widget_init()
        # 设置Box初始化
        self.seeting_widget_init()

        # 将两个Group添加至主布局器中
        self.main_layout.addWidget(self.message_group_box)
        self.main_layout.addWidget(self.seeting_group_box)
        self.setLayout(self.main_layout)

    # 信息部分初始化方法
    def message_widget_init(self):
        # 信息界面组Group
        self.message_group_box = QGroupBox("信息")

        # 两个控件水平布局
        horizon_layout = QHBoxLayout()

        # 控件1:设置转盘控件
        self.control_1 = QDial()
        # 是否循环滚动 [默认是]
        self.control_1.setWrapping(True)
        # 是否显示刻度
        self.control_1.setNotchesVisible(True)
        # 绑定信号至槽函数Dialg_Signal
        self.control_1.valueChanged.connect(self.Dialg_Signal)

        # 控件2:信息框
        # 信息框垂直布局
        message_vertical_layout = QVBoxLayout()
        # 文字
        Message_Lable = QLabel("数值")
        # 信息框
        self.control_2 = QSpinBox()
        # 两个控件添加至垂直布局中
        message_vertical_layout.addWidget(self.control_2)
        message_vertical_layout.addWidget(Message_Lable)

        # 两个控件绑定至垂直布局中
        horizon_layout.addWidget(self.control_1)
        horizon_layout.addLayout(message_vertical_layout)

        # 将垂直布局添加至组中
        self.message_group_box.setLayout(horizon_layout)

    # 设置部分初始化方法
    def seeting_widget_init(self):
        # 设置界面组Group
        self.seeting_group_box = QGroupBox("设置")
        # 控件网格布局
        self.grid_layout = QGridLayout()

        # 设置项:是否显示刻度
        self.seeting_norchesvisible()
        self.seeting_notchesVisile()

        # 显示刻度组添加至网格布局中
        self.grid_layout.addWidget(self.norchesvisible_group_box,1,1)
        # 是否循环组添加至网格布局中
        self.grid_layout.addWidget(self.notchesVisile_group_box,1,2)

        # 将网格布局添加至主Group中
        self.seeting_group_box.setLayout(self.grid_layout)

    # 设置项:是否显示刻度
    def seeting_norchesvisible(self):
        # 显示刻度组Group
        self.norchesvisible_group_box = QGroupBox("显示刻度")

        # 下拉选框控件[是否显示刻度]
        self.Combo_NotchesVisible = QComboBox()
        # 该Group中为垂直布局
        norchesvisible_vertical_layout = QVBoxLayout()
        # 配置下拉选框是否可以编辑
        self.Combo_NotchesVisible.setEditable(False)

        # 添加控件元素
        self.Combo_NotchesVisible.addItem("显示")
        self.Combo_NotchesVisible.addItem("不显示")

        # 连接槽函数至seeting_norchesvisible_Signal
        self.Combo_NotchesVisible.currentIndexChanged.connect(self.seeting_norchesvisible_Signal)

        # 将Combo_NotchesVisible[下拉选择框]添加至垂直布局中
        norchesvisible_vertical_layout.addWidget(self.Combo_NotchesVisible)
        # 将norchesvisible_vertical_layout[Group]添加至主Group中
        self.norchesvisible_group_box.setLayout(norchesvisible_vertical_layout)

    # 设置项:是否允许转盘循环
    def seeting_notchesVisile(self):
        # 是否循环Group
        self.notchesVisile_group_box = QGroupBox("循环滚动")

        # 下拉框选择控件
        self.Combo_notchesVisile = QComboBox()
        # 该Group内为垂直布局
        notchesVisile_vertical_layout = QVBoxLayout()
        # 配置下拉选框是否可以编辑
        self.Combo_NotchesVisible.setEditable(False)

        # 添加控件元素
        self.Combo_notchesVisile.addItem("循环")
        self.Combo_notchesVisile.addItem("不循环")

        # 连接槽函数至
        self.Combo_notchesVisile.currentIndexChanged.connect(self.seeting_notchesVisile_Signal)

        # 将Combo_notchesVisile[下拉选择框]添加至垂直布局中
        notchesVisile_vertical_layout.addWidget(self.Combo_notchesVisile)
        # 将notchesVisile_vertical_layout[Group]添加至主Group中
        self.notchesVisile_group_box.setLayout(notchesVisile_vertical_layout)

    # 转盘信号槽方法 [回调函数]
    def Dialg_Signal(self):
        self.control_2.setValue(self.control_1.value())

    # 设置项:是否显示刻度 [回调函数]
    def seeting_norchesvisible_Signal(self):
        # 判断索引号:0[显示] 1[不显示]
        if self.Combo_NotchesVisible.currentIndex() == 0:
            self.control_1.setNotchesVisible(True)
        elif self.Combo_NotchesVisible.currentIndex() == 1:
            self.control_1.setNotchesVisible(False)

    # 设置项:是否允许转盘循环 [回调函数]
    def seeting_notchesVisile_Signal(self):
        # 判断索引号:0[循环] 1[不循环]
        if self.Combo_notchesVisile.currentIndex() == 0:
            self.control_1.setWrapping(True)
        elif self.Combo_notchesVisile.currentIndex() == 1:
            self.control_1.setWrapping(False)
if __name__ == '__main__':
    app = QApplication(sys.argv)

    w = MyWindow()
    w.show()

    app.exec()

实现效果