前言

  上期教程我们介绍了 简单的代码封装。
  这期教程来进行更加有用的实战案例,以求加深对 Qt 运用的理解。

基础运行环境搭建

  下面我们重新建一个类,来实现近似于 Qt Designer 的 Label 双击效果。

代码执行效果

  首先我们先新建一个 Python 脚本,可以名为 My_Label.py ,将脚本放到 我的文档/maya/scripts 目录下。
  使用 Qt 的模块来兼容老版本的 Maya (需要将 Qt.py 放入到 我的文档/maya/scripts 目录下)

  My_Lable 文件下的代码

1
2
3
4
5
6
7
8
9
# -*- coding:UTF-8 -*-
from Qt.QtCore import *
from Qt.QtGui import *
from Qt.QtWidgets import *

class My_Label(QLabel):
def __init__(self):
super(My_Label,self).__init__()
self.setText("My_Label")

   Maya 脚本编辑器下的代码

1
2
3
4
5
# -*- coding:UTF-8 -*-
from My_Label import My_Label
reload(My_Label)
a = My_Label()
a.show()

代码执行效果

双击事件

  下面我们来实现一下双击事件。
  由于 QLabel 不存在双击事件的信号槽(可以通过虚函数添加一个自定义的信号槽)
  因此我们需要借助虚函数来实现双击效果,而这个函数是 mouseDoubleClickEvent
  另外还需要添加一个 QLineEdit 来实现双击修改的效果。
  我们来继续完善 My_Lable 文件下的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# -*- coding:UTF-8 -*-
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *

class My_Label(QLabel):
def __init__(self):
super(My_Label,self).__init__()
# Note 设置默认的 Label 名称
self.setText("My_Label")
# Note 添加一个 QLineEdit 将其依附到 QLabel 上
self.edit = QLineEdit(self)
# Note 默认设置为隐藏
self.edit.setVisible(False)

def mouseDoubleClickEvent(self,event):
# Note 双击触发时候显示
self.edit.setVisible(True)

代码执行效果

  似乎效果还相差甚远呀。我们先来理清楚要实现的需求,然后一步一步去完成。

  • 双击添加鼠标左键判断 (目前鼠标三键的双击都可以触发函数)
  • 根据当前组件大小调整 QLineEdit 大小
  • 点击任意位置修改 My_Label 上的内容
  • 点击任意位置隐藏 QLineEdit
  • 添加 enable 函数来开启双击交互效果

双击添加鼠标左键判断

  事件判断可以通过 虚函数传入的 QMouseEvent 进行处理
  在API文档可以看到 QMouseEventbutton() 方法,可以获取到当前的按键。
  我们可以将获取到的信息打印出来。

1
print event.button()

代码执行效果

  可以看到打印出来的是 MidButton LeftButton RightButton
  这些都对应到当前的鼠标点击按键。
  可以查找 API文档

代码执行效果

  Qt 内置了以上这些按钮的触发。
  通过判断传入的 event 就可以判断是否是鼠标左键点击。

1
2
3
4
5
def mouseDoubleClickEvent(self,event):
# Note 鼠标左键点击过滤
if event.button() == Qt.MouseButton.LeftButton:
# Note 双击触发时候显示
self.edit.setVisible(True)

QLineEdit 大小调整

  QLineEdit 大小固定在 Label 上,是因为没有给它添加 Layout
  这种状态就和 Qt Designer 没有添加 QWidget 的组件一样,属于随意摆放的组件。
  因此我们将它添加到 Layout 中就可以解决大小的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def __init__(self):
super(My_Label,self).__init__()
# Note 设置默认的 Label 名称
self.setText("My_Label")

# Note 添加一个 QLineEdit
self.edit = QLineEdit()
# Note 默认设置为隐藏
self.edit.setVisible(False)

# Note 添加一个新的 layout 将其依附到 QLabel 上
self.layout = QVBoxLayout(self)
# Note 将 edit 添加到 layout 中
self.layout.addWidget(self.edit)

代码执行效果

修改 My_Lable 内容

  隐藏组件需要获取一个事件,当我们点击其它位置的时候可以触发的事件。
  我们可以去查 QLineEdit 的 API文档

代码执行效果

  可以找到 QLineEdit 有 editingFinished 信号槽,
  借助这个信号槽我们可以实现部分效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def __init__(self):
super(My_Label,self).__init__()
# Note 设置默认的 Label 名称
self.setText("My_Label")

# Note 添加一个 QLineEdit 将其依附到 QLabel 上
self.edit = QLineEdit()
# Note 默认设置为隐藏
self.edit.setVisible(False)
self.edit.editingFinished.connect(self.__inputComplete)

# Note 添加一个新的 layout
self.layout = QVBoxLayout(self)
# Note 将 edit 添加到 layout 中
self.layout.addWidget(self.edit)

def __inputComplete(self):
'''函数前添加双下划线让方法作为私有方法,不提供给外部调用'''
# Note 设置 edit 不可见
self.edit.setVisible(False)
# Note 将 edit 获取到的内容添加到 My_Label 上
self.setText(self.edit.text())

代码执行效果

  这样只要在 edit 上输入内容,然后按键盘 enter 键就可以实现隐藏 edit 并且将 edit 的内容添加到 My_Label 上
  但是这样无法实现点击任意位置隐藏 edit 的效果。
  其实也不全然只能通过按 enter 键才可以隐藏, editingFinished 信号槽和 focus 事件相关,只要 focus 转移也可以实现隐藏。
  我们可以将 Maya 中的运行代码做些修改,多添加一些组件进行测试。

  Maya 运行代码

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
# -*- coding:UTF-8 -*-
import My_Label
reload(My_Label)
from My_Label import My_Label
from PySide2.QtCore import *
from PySide2.QtWidgets import *
from PySide2.QtGui import *

# Note 添加窗口组件
win = QWidget()
win.resize(300,150)

# Note 添加布局
layout = QVBoxLayout(win)

# Note 添加组件
my_label = My_Label()
my_label.setText("My_Label")
label = QLabel()
label.setText("QLabel")
edit = QLineEdit()

# Note 将组件添加到布局当中
layout.addWidget(my_label)
layout.addWidget(label)
layout.addWidget(edit)

# Note 显示窗口
win.show()

  My_Label 代码

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
35
36
# -*- coding:UTF-8 -*-
from Qt.QtCore import *
from Qt.QtGui import *
from Qt.QtWidgets import *

class My_Label(QLabel):

editable = False
def __init__(self):
super(My_Label,self).__init__()
# Note 设置默认的 Label 名称
self.setText("My_Label")

# Note 添加一个 QLineEdit 将其依附到 QLabel 上
self.edit = QLineEdit()
# Note 默认设置为隐藏
self.edit.setVisible(False)
self.edit.editingFinished.connect(self.__inputComplete)

# Note 添加一个新的 layout
self.layout = QVBoxLayout(self)
# Note 将 edit 添加到 layout 中
self.layout.addWidget(self.edit)

def __inputComplete(self):
'''函数前添加双下划线让方法作为私有方法,不提供给外部调用'''
# Note 设置 edit 不可见
self.edit.setVisible(False)
# Note 将 edit 获取到的内容添加到 My_Label 上
self.setText(self.edit.text())

def mouseDoubleClickEvent(self,event):
# Note 鼠标左键点击过滤
if event.button() == Qt.MouseButton.LeftButton:
# Note 双击触发时候显示
self.edit.setVisible(True)

代码执行效果

隐藏 QLineEdit - 方案一

  上面虽然实现了 组件focus跳转的时候 隐藏自身。
  却依旧无法实现点击窗口任意位置隐藏 QLineEdit。
  我们要实现的是 如果没有点击到组件自身就要隐藏的效果。
  如果学习过前端 JS 的事件处理就会知道,通过 document 的点击可以获取到全局的点击,还可以通过 preventPropagation 来阻止冒泡从而实现不点击自身的事件反馈。
  然而我并没有在 Qt 中找到简单的实现方法。
  因此下面提到的方法会比较复杂,如果看不懂的话可以暂行跳过这个部分。


  我们先来分析要实现点击任意位置需要怎样的条件。
  首先我们需要获取一个全局事件的方法,因为组件当中的虚函数只有在组件被触发时才奏效,组件自身无法实现全局响应的。
  但是 Qt 的全局事件点击是怎么处理的呢?
  一开始我经过了大量的搜索也没有找到 Qt 的全局事件处理的相关方法,因此只能退而求其次,给所有的组件添加一个处理点击的回调函数来实现这个功能。
  也就是说 通过一个函数遍历窗口中所有的组件,然后给每个组件的 mousePressEvent 添加点击处理函数,这样就可以实现伪全局的效果。

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

def __eventHandler(self,widget,event):
# widget.mousePressEvent(event)
# Note 判断 edit 是否可见 以及 点击区域是否是 edit 当中
if self.edit.isVisible() and widget != self.edit:
self.__inputComplete()

def setPressEvent(self,win):
# Note 判断组件是否有 mousePressEvent 事件
if hasattr(win,"mousePressEvent"):
# Note 复写组件的 mousePressEvent 虚函数
win.mousePressEvent = partial(self.__eventHandler,win,win.mousePressEvent)
for child in win.children():
# Note 递归执行
self.setPressEvent(child)

  由于组件的children是通过嵌套方式存储在各个组件的的 children() 方法当中的,因此不能用简单的 for 循环来实现所有组件的遍历。
  这里需要使用递归的方式来实现。 递归-百度百科
  递归,就是在运行的过程中调用自己。 -> 我们创建了一个 setPressEvent 方法,不断在children for循环中调用自己,就可以实现遍历一个组件下所有的子组件。

  这里点击函数是通过复写的形式实现的。
  国外的大大其实非常不推荐使用这种方式来改写函数的方法,一些代码处理工具没有办法识别这种修改,代码的可读性也会大打折扣。 参考链接
  正确的写法应该通过类来继承。
  但是这里所有的child都已经实例化了。
  再加上并不是所有的组件都有触发点击的信号槽的,而继承QWidget的组件都有 mousePressEvent 虚函数的,因此我只想到了这种不太好的操作。

  在函数内部不好获取当前调用的 __eventHandler 是哪个组件触发的,我们可以将触发的组件作为参数传入到函数中。
  复写函数是无法修改传参的,那这种效果要如何实现呢?
  我们可以利用python自带的包 from functools import partial patial 来实现,同样在使用信号槽的时候也可以用这种黑科技实现槽函数的传参。 partial内核原理分析

代码执行效果

  其实上述方案是有 BUG 的,由于复写函数的原因,如果有多个 My_Label 实例存在的话就会互相覆盖。
  我最初想到的方案就是类似于 super 的方法来继承这个方法的调用。
  当然因为这里已经实例化当然无法调用 super 方法,因此我想到的是直接 调用在 __eventHandler 第一行加上 widget.mousePressEvent(event)
  然而这样执行的话会报错!!!

代码执行效果

  原因是 mousePressEvent 已经被复写了,所以在这里直接调用就会陷入递归循环。
  所以这里的处理必须是执行被复写之前的函数,因此我想到的方案是通过传参的形式加进来。
  下面是完整代码。

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# -*- coding:UTF-8 -*-
from Qt.QtCore import *
from Qt.QtGui import *
from Qt.QtWidgets import *
from functools import partial

class My_Label(QLabel):

editable = False
def __init__(self):
super(My_Label,self).__init__()
# Note 设置默认的 Label 名称
self.setText("My_Label")

# Note 添加一个 QLineEdit 将其依附到 QLabel 上
self.edit = QLineEdit()
# Note 默认设置为隐藏
self.edit.setVisible(False)
self.edit.editingFinished.connect(self.__inputComplete)

# Note 添加一个新的 layout
self.layout = QVBoxLayout(self)
# Note 将 edit 添加到 layout 中
self.layout.addWidget(self.edit)

def __inputComplete(self):
'''函数前添加双下划线让方法作为私有方法,不提供给外部调用'''
# Note 设置 edit 不可见
self.edit.setVisible(False)
# Note 将 edit 获取到的内容添加到 My_Label 上
self.setText(self.edit.text())

def mouseDoubleClickEvent(self,event):
# Note 鼠标左键点击过滤
if event.button() == Qt.MouseButton.LeftButton:
# Note 双击触发时候显示
self.edit.setVisible(True)

def __eventHandler(self,widget,pressEvent,event):
# Note 执行复写之前的函数(实现多重实例化不覆盖)__
pressEvent(event)
# Note 判断 edit 是否可见 以及 点击区域是否是 edit 当中
if self.edit.isVisible() and widget != self.edit:
self.__inputComplete()

def setPressEvent(self,win):
# Note 判断组件是否有 mousePressEvent 事件
if hasattr(win,"mousePressEvent"):
# Note 复写组件的 mousePressEvent 虚函数
win.mousePressEvent = partial(self.__eventHandler,win,win.mousePressEvent)
for child in win.children():
# Note 递归执行
self.setPressEvent(child)

  maya 的运行上可以多添加一些 My_Label 来测试多个实例的运行情况。

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
# -*- coding:UTF-8 -*-
import My_Label
reload(My_Label)
from My_Label import My_Label
from PySide2.QtCore import *
from PySide2.QtWidgets import *
from PySide2.QtGui import *

# Note 添加窗口组件
win = QWidget()
win.resize(300,150)

# Note 添加布局
layout = QVBoxLayout(win)

# Note 添加组件
my_label = My_Label()
my_label2 = My_Label()

label = QLabel()
label.setText("QLabel")
edit = QLineEdit()

# Note 将组件添加到布局当中
layout.addWidget(my_label)
layout.addWidget(edit)
layout.addWidget(label)
layout.addWidget(my_label2)
# Note 显示窗口
win.show()

my_label.setPressEvent(win)
my_label2.setPressEvent(win)

代码执行效果

  可以看到这个方案初步实现了 Qt Designer 的双击修改,点击任意位置确认修改的效果。
  但是使用起来会有很多的限制,是非常不方便的。
  setPressEvent(win) 函数必须要添加了所有的组件之后才可以触发,否则递归处理就会缺少了部分组件而导致BUG。
  交互以及UI细节上依旧存在差别(在下一个方案中提供解决方案)
  另外这里也还没有完成处理判断,一旦调用了 setPressEvent 就无法取消组件的效果。
  当然这个无法取消的问题是可以添加额外的判断来修复的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def setEditable(self,editable,parent=None):
# Note 判断 parent 是否存在
if parent == None:
if self.parent() != None:
parent = self.parent()
else:
raise Exception("parent is None")

if editable:
win = parent.window()
# Note 递归设置点击事件
self.__setPressEvent(win)
self.editable = True
else:
self.editable = False

  这部分的代码就是我仿造 PyQt 内核的设置函数的编辑方法,可以实现传入布尔参数来开启和禁用事件调用。
  也可以通过 QWidget 下的 window 方法来获取最顶层的窗口。
  当然执行这个方法的时候,如果组件没有依附到任何窗口上,那么就无法递归,也就无法实现全局点击事件的效果,因此我抛出了错误。

  以上方案是我最初写组件的实现方案。
  在写教程的过程中,我觉得这种实现方式非常不优雅,自己很不甘心就这么留着坑不填,于是我重新研究了实现方案。

扩展说明

隐藏 QLineEdit - 方案二

  回到最初想要实现的效果,最让我疑惑的是 Qt 内部的全局事件处理。
  尽管虚函数是满足相关条件之后才会被触发的,但是根据 Qt 的事件处理机制。
  应该有一个专门的地方来处理所有的被调用的事件才对的,于是我以这个为突破口,在网上有查了不少资料。
  功夫不负有心人,我还是找到了一些有用的线索。 参考

  参考提问者自己提出的解决方案,可以知道 QApplication 有 notify 方法,这个方法就是一个全局事件处理的相关函数。
  于是我尝试继承一个 自己的 QApplication 类来实现效果。

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
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *

# from PyQt4.QtCore import *
# from PyQt4.QtGui import *

class MyApp(QApplication):
def notify(self, receiver, event):
# Note QEvent.Type.MouseButtonPress 等于数字 2
if event.type() == 2:
print (receiver)
return super(MyApp, self).notify(receiver, event)

if __name__ == "__main__":
import sys
app = MyApp(sys.argv)

win = QWidget()
layout = QVBoxLayout(win)

label = QLabel()
label.setText('test')
edit = QLineEdit()
combo = QComboBox()

layout.addWidget(label)
layout.addWidget(combo)
layout.addWidget(edit)
win.show()

app.exec_()

代码执行效果

  经过测试可以看到 notify 确实是一个全局事件处理器,而且 PyQt4 也可以正确调用(event.type() 需要改为数字进行识别)
  然而兴奋瞬间就被冷水当头了,因为 QApplication 只能存在一个。
  所以在 Maya 环境下 QApplication 已经是实例了,这也就是为什么 在 Maya 中运行不能声明 QApplication() 参考这里
  所以这里也只能采取上面的方法,用覆盖的方法。
  但是 QApplication 的实例要怎么获取出来呢?
  这个时候真是柳暗花明又一村呀!! 没想到 QApplication 就有 instance() 方法可以直接获取到当前运行的实例。

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
from functools import partial
class My_Label(QLabel):
editable = False
def __init__(self):
super(My_Label,self).__init__()

# Note 添加一个 QLineEdit 将其依附到 QLabel 上
self.edit = QLineEdit()
# Note 默认设置为隐藏
self.edit.setVisible(False)
# Note 隐藏 lineedit 的边框
self.edit.setFrame(False)
self.edit.editingFinished.connect(self.__inputComplete)

# Note 设置默认的 Label 名称
self.setText("My_Label")
self.edit.setText("My_Label")

# Note 添加一个新的 layout
self.layout = QVBoxLayout(self)
self.layout.setContentsMargins(0,0,0,0)
# Note 将 edit 添加到 layout 中
self.layout.addWidget(self.edit)

# Note 初始化 Application 的事件
app = QApplication.instance()
app.notify = partial(self.__notifyClickEvent,app.notify)

def __notifyClickEvent(self, notify, receiver, event):
if self.editable:
if event.type() == QEvent.Type.MouseButtonPress:
if type(receiver) != QWindow:
if receiver != self.edit:
self.__inputComplete()
return notify(receiver, event)

def mouseDoubleClickEvent(self,event):
# Note 鼠标左键点击过滤 Qt.MouseButton.LeftButton 为 1 | 兼容 PyQt 写法
if event.button() == 1 and self.editable:
# Note 双击触发时候显示
self.edit.setVisible(True)
# Note 每次显示 label 中的内容
self.edit.setText(self.text())
# Note 选择全部的内容
self.edit.selectAll()
# Note 进入 focus 输入状态
self.edit.setFocus()


def __inputComplete(self):
# Note 设置 edit 不可见
self.edit.setVisible(False)
# Note 将 edit 获取到的内容添加到 My_Label 上
self.setText(self.edit.text())

def setEditable(self,editable):
self.editable = True if editable else False

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

win = QWidget()
layout = QVBoxLayout(win)

bar = QLabel()
bar.setText('test')
label = My_Label()
label.setEditable(True)
edit = QLineEdit()
combo = QComboBox()

layout.addWidget(bar)
layout.addWidget(label)
layout.addWidget(combo)
layout.addWidget(edit)
win.show()

app.exec_()

代码执行效果

  这里改写了双击事件的处理,让双击效果更接近 Qt Designer 的全选选择效果。
  取消了 QLineedit 的边框更贴近 Qt Designer 的效果
  之前在不拉动窗口双击会看不到 edit 组件,那是因为要放大窗口才可以看到,可以将 layout 的外边框设置为 0 即可。

  在函数实现上,其实上面使用的方法和复写 mousePressEvent 一样的
  只是复写了 QApplication 的 notify 方法而已。
  由于复写的方法来自于 组件的类方法 ,因此之后也可以利用组件的属性作为判断条件。
  这样我们只需要设置 setEditable 函数就可以达到开启和关闭双击效果。

  上述的代码在 PySide2 上可以执行,但是在 PyQt4 下无法实现效果。
  原因是 notify 函数并没有像 PySide2 那样调用起来。
  更令人沮丧的是 Maya2018 的 PySide2 也无法将 notify 函数顺利跑起来。

扩展说明

  • 如何在 maya 运行 Stackoverflow 里面的 PyQt 代码 参考这里

隐藏 QLineEdit - 方案三

  上面的方案几乎是最完美的解决方案了,然而在临门一脚的时候却失败了。
  难道就没有方法实现全局调用了吗?
  我又开始在网上寻找答案,最后偶然间在这个Maya官方的技术博客中有了灵感。

代码执行效果

  我最初在方案一的阶段的时候,确实有尝试过 installEventFilter 的方法,然而这个方法和 所有的虚函数一样,如果安装在组件上的话,只能对组件进行过滤。
  但是通过 方案二 的尝试之后,我猛然发现,如果 installEventFilter 放在 QApplication 上,岂不美哉。

  不过说到底 installEventFilter 要怎么使用呢?
  可以参考API文档

代码执行效果

  installEventFilter 需要传入 QObject 参数,不过这个东西 QWidget 是继承过来的。
  因此可以直接将类实例传进去。

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# -*- coding:UTF-8 -*-
from Qt.QtCore import *
from Qt.QtGui import *
from Qt.QtWidgets import *
from functools import partial

class My_Label(QLabel):

editable = False
def __init__(self):
super(My_Label,self).__init__()

# Note 添加一个 QLineEdit
self.edit = QLineEdit()
# Note 默认设置为隐藏
self.edit.setVisible(False)
self.edit.editingFinished.connect(self.__inputComplete)
self.edit.setStyleSheet("border:None")

# Note 添加一个新的 layout 将其依附到 My_Label 上
self.layout = QVBoxLayout(self)
self.layout.setContentsMargins(0,0,0,0)

# Note 将 edit 添加到 layout 中
self.layout.addWidget(self.edit)

# Note 初始化 Application 的事件
app = QApplication.instance()
app.installEventFilter(self)

def eventFilter(self,receiver,event):
# Note QEvent.Type.MouseButtonPress 为 2
if event.type() == 2 and self.editable and self.edit.isVisible():
# Note 过滤接受的组件是否是自己 避免其他组件触发 如 PySide2 QWindow 也会传入进来
if receiver == self:
self.__inputComplete()
return False

def __inputComplete(self):
# Note 设置 edit 不可见
self.edit.setVisible(False)
# Note 将 edit 获取到的内容添加到 My_Label 上
self.setText(self.edit.text())

def keyPressEvent(self,e):
# Note 敲击Esc键触发完成
if e.key() == Qt.Key_Escape:
self.__inputComplete()

def mouseDoubleClickEvent(self,event):
# Note 鼠标左键点击过滤 Qt.MouseButton.LeftButton 为 1
if event.button() == 1 and self.editable:
# Note 双击触发时候显示
self.edit.setVisible(True)
# Note 每次显示 label 中的内容
self.edit.setText(self.text())
# Note 选择全部的内容
self.edit.selectAll()
# Note 进入 focus 输入状态
self.edit.setFocus()

def setEditable(self,editable):
self.editable = True if editable else False

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

  可以看到这次实现了在maya当中双击修改QLabel的效果,交互感受和 Qt Designer 是一样的。
  这里通过 edit 的 keyPressEvent 可以添加 Esc 键触发完成的效果。
  另外这里的代码没有使用 setFrame(False) 的方法来取消边框。
  而是使用了样式表的 setStyleSheet("border:None") 取消边框,可以实现相同的效果。

总结

  这期教程我们介绍了 Qt Designer 的双击效果的实现效果
  并且经过了我自己的大量探索,才最终实现了一个全局事件处理的效果。
  本期教程前面不算很复杂,后面的全局事件的处理还是有点难度,如果是初学者需要好好消化。
  这一次组件封装就实现了 MEL 语言绝对实现不了的效果,终于体现出了 Qt 的强大之处了。
  在末尾我也引出了下一期教程要讲的主题 qss 样式。
  敬请期待第五期教程吧。