前言

  上期教程我们深刻了解了 QSS 样式,通过 QSS 可以很方便自定义自己组件的主题。装自己的逼,让别人无逼可装。
  下面我们来讲讲 Qt 的信号槽。并且如何自定义信号槽事件。

什么是信号槽

  其实第三期教程的时候就有讲到信号槽为何物。跳转链接
  信号槽是 Qt 的事件响应机制。
  通过信号槽可以在满足特定情况下触发相关的函数。
  通过第四期教程的实战,我们知道 Qt 也自带了很多 虚函数 可以实现在特定情况下执行特定代码。
  但是虚函数需要复写,不便于后续的调用。
  因此虚函数只适合在类里面用,不适合在实例化的对象上使用。
  那么如果我们自定义一个信号槽到虚函数当中,就可以在实例中使用信号槽触发相关功能了。

#

  在第四期教程的实战中,我使用了 keyPressEvent 来实现 Esc 键退出编辑的效果的。
  当然这个写法在类里面写是没有问题的,如果我希望组件外面也有一个专门的信号槽来处理 Esc 键触发的事件,我们就可以利用自定义信号槽来解决问题。
  下面我模仿 QPushButton 的 clicked 信号槽,将这个信号槽添加到 QLabel 上的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# -*- coding:UTF-8 -*-
from Qt.QtCore import *
from Qt.QtGui import *
from Qt.QtWidgets import *

class Clickable_Label(QLabel):
def __init__(self):
super(Clickable_Label,self).__init__()

def mousePressEvent(self,event):
print ("click")

if __name__ == "__main__":
# Note 兼容maya和外部python写法
# QApplication.instance() 可以判断 QApplication 是否存在实例
if QApplication.instance() == None:
QApplication([])

window = QWidget()
window.setLayout(QVBoxLayout())

label = Clickable_Label()
label.setText('Clickable Label')
window.layout().addWidget(label)

window.show()

QApplication.exec_()

代码执行效果

  上面加入兼容 Maya 外部执行的效果。
  另外上面代码实现了,点击 label 就打印出 click 信息
  下面我们来自定义信号槽
  PySide 和 PyQt 开自定义信号槽的名称是不一样的,Qt.py 脚本是以PySide为基准的
  PySide 使用 Signal() || PyQt 使用 pyqtSignal()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from __future__ import print_function

class Clickable_Label(QLabel):

clicked = Signal()

def __init__(self):
super(Clickable_Label,self).__init__()

def mousePressEvent(self,event):
self.clicked.emit()

# 省略...

# Note 调用label信号槽
label.clicked.connect(lambda: print('click!!!!'))

代码执行效果

  特别注意 clicked = Signal() 这个语句不可以写在 init 函数中。
  python是支持变量写在类方法外部的,可以通过self来获取到方法外的变量。


  另外这里用了 lambda 函数来实现打印效果。
  在 Python3 中 lambda 可以使用 print ,因为 Python3 的 print 是个函数。
  而在 Python2 中 print 是关键字,无法在 lambda 函数中执行。

代码执行效果

  在网上查找了一番,也是有解决方案的。from __future__ import print_function 可以让 Python2 的print 升级成 Python3 的 print。 Stackoverflow解答


  我们也可以给emit函数添加参数,这样调用的函数就可以获取到相关的传参。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# -*- coding:UTF-8 -*-
from __future__ import print_function
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *

class Clickable_Label(QLabel):

clicked = Signal(object)

def __init__(self):
super(Clickable_Label,self).__init__()

def mousePressEvent(self,event):
self.clicked.emit(event)

if __name__ == "__main__":
# Note 兼容maya和外部python写法
if QApplication.instance() == None:
QApplication([])

window = QWidget()
window.setLayout(QVBoxLayout())

label = Clickable_Label()
label.setText('Clickable Label')

label.clicked.connect(lambda e: print(e.pos()))

window.layout().addWidget(label)

window.show()

QApplication.exec_()

代码执行效果
代码执行效果

  Qt 的自定义信号槽其实并不难,基本都是依靠虚函数提供的执行调用,然后让信号槽在特定条件下触发。
  我们也可以在虚函数当中加入相关的判断,比如只有点击左上角的特定区域才可以 emit 信号,如此就实现条件更加丰富的自定义效果。


  制定自定义的信号槽我只会这么多,更加复杂的用法可以参照官方的文档相关的函数。 QSignalTransition QSignalMapper
  更为复杂的用法,其实我自己也没有怎么研究,也就不加赘述。

扩展说明

  • 这里填个坑,如何让 PySide 的窗口如同 MEL 的窗口一样不会被遮挡。 参考这里

总结

  自定义信号槽也结束了,至此我第一期教程宣布的所有内容已经完结。
  下一期教程,我们再来一个实战,通过 PySide2 制作一个默写古诗的软件 <( ̄︶ ̄)>
  敬请期待!~