【使用 PyQt6-第02章】 创建信号、槽和事件

Clicked!
Checked? True
Clicked!
Checked? False
Clicked!
Checked? True


您可以根据需要将任意数量的插槽连接到信号,并且可以在插槽上同时响应不同版本的信号。


## 五、存储数据


通常,将小部件的当前状态存储在 Python 变量中很有用。这允许您像任何其他 Python 变量一样使用这些值,而无需访问原始小部件。您可以将这些值存储为单独的变量,也可以根据需要使用字典。在下一个示例中,我们将按钮的检查button\_is\_checked值存储在名为on的变量中self。



class MainWindow(QMainWindow):
def __init__(self):
super().init()

    self.button_is_checked = True

    self.setWindowTitle("My App")

    button = QPushButton("Press Me!")
    button.setCheckable(True)
    button.clicked.connect(self.the_button_was_toggled)
    button.setChecked(self.button_is_checked)

    self.setCentralWidget(button)

def the\_button\_was\_toggled(self, checked):
    self.button_is_checked = checked

    print(self.button_is_checked)

首先,我们设置变量的默认值(为True),然后使用默认值设置小部件的初始状态。当小部件状态发生变化时,我们收到信号并更新变量以匹配。


您可以将相同的模式与任何 PyQt 小部件一起使用。如果小部件不提供发送当前状态的信号,您将需要直接在处理程序中从小部件检索值。例如,这里我们正在检查按下处理程序中的检查状态。



class MainWindow(QMainWindow):
def __init__(self):
super().init()

    self.button_is_checked = True

    self.setWindowTitle("My App")

    self.button = QPushButton("Press Me!")
    self.button.setCheckable(True)
    self.button.released.connect(self.the_button_was_released)
    self.button.setChecked(self.button_is_checked)

    self.setCentralWidget(self.button)

def the\_button\_was\_released(self):
    self.button_is_checked = self.button.isChecked()

    print(self.button_is_checked)

我们需要保留对按钮的引用,self以便我们可以在插槽中访问它。


当按钮被释放时,释放信号会触发,但不会发送检查状态,因此我们使用.isChecked()处理程序中的按钮获取检查状态。


## 六、改变接口


到目前为止,我们已经了解了如何接受信号并将输出打印到控制台。但是当我们点击按钮时让界面发生一些事情怎么样?让我们更新槽方法来修改按钮,更改文本并禁用按钮,使其不再可单击。我们现在还将关闭可检查状态。



class MainWindow(QMainWindow):
def __init__(self):
super().init()

    self.setWindowTitle("My App")

    self.button = QPushButton("Press Me!")
    self.button.clicked.connect(self.the_button_was_clicked)

    self.setCentralWidget(self.button)

def the\_button\_was\_clicked(self):
    self.button.setText("You already clicked me.")
    self.button.setEnabled(False)

    # Also change the window title.
    self.setWindowTitle("My Oneshot App")

同样,因为我们需要能够button在我们的the\_button\_was\_clicked方法中访问 ,所以我们在 上保留对它的引用self。通过传递strto来更改按钮的文本.setText()。要禁用按钮,.setEnabled()请使用False.


运行!如果单击该按钮,文本将发生变化并且该按钮将变得不可单击。


您不仅限于更改触发信号的按钮,您可以在插槽方法中执行任何您想要的操作。例如,尝试将以下行添加到the\_button\_was\_clicked方法中以同时更改窗口标题。



self.setWindowTitle(“A new window title”)


大多数小部件都有自己的信号——QMainWindow我们用于窗口的信号也不例外。在下面更复杂的示例中,我们将.windowTitleChanged上的信号连接QMainWindow到自定义槽方法。


在下面的示例中,我们将.windowTitleChanged上的信号连接QMainWindow到方法 slot the\_window\_title\_changed。该插槽还接收新的窗口标题。



from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton

import sys
from random import choice

window_titles = [
‘My App’,
‘My App’,
‘Still My App’,
‘Still My App’,
‘What on earth’,
‘What on earth’,
‘This is surprising’,
‘This is surprising’,
‘Something went wrong’
]

class MainWindow(QMainWindow):
def __init__(self):
super().init()

    self.n_times_clicked = 0

    self.setWindowTitle("My App")

    self.button = QPushButton("Press Me!")
    self.button.clicked.connect(self.the_button_was_clicked)

    self.windowTitleChanged.connect(self.the_window_title_changed)

    # Set the central widget of the Window.
    self.setCentralWidget(self.button)

def the\_button\_was\_clicked(self):
    print("Clicked.")
    new_window_title = choice(window_titles)
    print("Setting title: %s" % new_window_title)
    self.setWindowTitle(new_window_title)

def the\_window\_title\_changed(self, window_title):
    print("Window title changed: %s" % window_title)

    if window_title == 'Something went wrong':
        self.button.setDisabled(True)

app = QApplication(sys.argv)

window = MainWindow()
window.show()

app.exec()


首先,我们设置一个窗口标题列表——我们将使用 Python 的内置random.choice().我们将自定义槽方法挂接the\_window\_title\_changed到窗口的.windowTitleChanged信号上。


当我们单击按钮时,窗口标题将随机更改。如果新窗口标题等于“出现问题”,则该按钮将被禁用。


运行!重复单击该按钮,直到标题更改为“出现问题”并且该按钮将被禁用。


在此示例中,有几件事需要注意。


首先,设置窗口标题时windowTitleChanged并不总是发出信号。仅当新标题较前一标题发生更改时才会触发该信号。如果多次设置相同的标题,则只会在第一次时触发信号。仔细检查信号触发的条件非常重要,以避免在应用程序中使用它们时感到惊讶。


其次,请注意我们如何使用信号将事物链接在一起。发生一件事——按下按钮——可以依次触发其他多件事发生。这些后续影响不需要知道是什么造成的,而只是简单规则的结果。这种效果与其触发器的解耦是构建 GUI 应用程序时需要理解的关键概念之一。


在本节中,我们介绍了信号和槽。我们演示了一些简单的信号以及如何使用它们在应用程序中传递数据和状态。接下来我们将了解 Qt 提供的用于您的应用程序的小部件以及它们提供的信号。


## 七、直接将小部件连接在一起


到目前为止,我们已经看到了将小部件信号连接到 Python 方法的示例。当小部件发出信号时,我们的 Python 方法将被调用并接收来自信号的数据。但您并不总是需要使用 Python 函数来处理信号——您还可以将 Qt 小部件直接相互连接。


在下面的示例中,我们向窗口添加一个QLineEdit小部件和 a 。QLabel在\\_\_init\_\_for 窗口中,我们将行编辑信号.textChanged连接到.现在,每当文本发生变化时,都会将该文本接收到它的方法中。.setTextQLabelQLineEditQLabel.setText



from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel, QLineEdit, QVBoxLayout, QWidget

import sys

class MainWindow(QMainWindow):
def __init__(self):
super().init()

    self.setWindowTitle("My App")

    self.label = QLabel()

    self.input = QLineEdit()
    self.input.textChanged.connect(self.label.setText)

    layout = QVBoxLayout()
    layout.addWidget(self.input)
    layout.addWidget(self.label)

    container = QWidget()
    container.setLayout(layout)

    # Set the central widget of the Window.
    self.setCentralWidget(container)

app = QApplication(sys.argv)

window = MainWindow()
window.show()

app.exec()


请注意,为了将输入连接到标签,必须定义输入和标签。此代码将两个小部件添加到布局中,并将其设置在窗口上。稍后我们将详细介绍布局,您现在可以忽略它。


运行!在上面的框中输入一些文本,您会看到它立即出现在标签上。


输入中键入的任何文本都会立即显示在标签上  
 输入中键入的任何文本都会立即显示在标签上。


大多数 Qt 小部件都有可用的插槽,您可以将发出与其接受的类型相同的任何信号连接到该插槽。小部件文档在“公共插槽”下列出了每个小部件的插槽。例如,请参阅 https://doc.qt.io/qt-5/qlabel.html#public-slots[QLabel]。


## 八、活动


用户与 Qt 应用程序的每次交互都是一个事件。事件有多种类型,每种类型代表不同类型的交互。 Qt 使用事件对象来表示这些事件,事件对象打包有关所发生事件的信息。这些事件被传递到发生交互的小部件上的特定事件处理程序。


通过定义自定义或扩展事件处理程序,您可以改变小部件响应这些事件的方式。事件处理程序的定义与任何其他方法一样,但名称特定于它们处理的事件类型。


小部件接收的主要事件之一是QMouseEvent. QMouseEvent 事件是为小部件上的每次鼠标移动和按钮单击创建的。以下事件处理程序可用于处理鼠标事件——


事件处理程序 事件类型已移动  
 mouseMoveEvent 鼠标移动了  
 mousePressEvent 鼠标按钮被按下  
 mouseReleaseEvent 释放鼠标按钮  
 mouseDoubleClickEvent 检测到双击  
 例如,单击某个小部件将导致QMouseEvent将 a 发送到.mousePressEvent该小部件上的事件处理程序。该处理程序可以使用事件对象来查找有关所发生事件的信息,例如触发事件的原因以及事件发生的具体位置。


您可以通过子类化并覆盖类上的处理程序方法来拦截事件。您可以选择过滤、修改或忽略事件,通过使用 调用父类函数将它们传递给事件的普通处理程序super()。这些可以添加到您的主窗口类中,如下所示。在每种情况下e都会收到传入的事件。



import sys

from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QApplication, QLabel, QMainWindow, QTextEdit

class MainWindow(QMainWindow):
def __init__(self):
super().init()
self.label = QLabel(“Click in this window”)
self.setCentralWidget(self.label)

def mouseMoveEvent(self, e):
    self.label.setText("mouseMoveEvent")

def mousePressEvent(self, e):
    self.label.setText("mousePressEvent")

def mouseReleaseEvent(self, e):
    self.label.setText("mouseReleaseEvent")

def mouseDoubleClickEvent(self, e):
    self.label.setText("mouseDoubleClickEvent")

app = QApplication(sys.argv)

window = MainWindow()
window.show()

app.exec()


运行!尝试在窗口中移动和单击(以及双击)并观察事件的出现。


您会注意到,只有按下按钮时才会注册鼠标移动事件。您可以通过调用self.setMouseTracking(True)窗口来更改此设置。您可能还注意到,按下按钮时,按下(单击)和双击事件都会触发。仅当释放按钮时才会触发释放事件。通常,要注册用户的单击,您应该同时观察鼠标的按下和释放。


在事件处理程序中,您可以访问事件对象。该对象包含有关事件的信息,可用于根据具体发生的情况做出不同的响应。接下来我们将看看鼠标事件对象。


## 九、鼠标事件


Qt 中的所有鼠标事件都通过该对象进行跟踪QMouseEvent,并且可以从以下事件方法读取有关该事件的信息。


方法 退货  
 .button() 触发此事件的特定按钮  
 .buttons() 所有鼠标按钮的状态(OR’ed 标志)  
 .position() 小部件相对位置(QPoint 整数)  
 您可以在事件处理程序中使用这些方法来以不同的方式响应不同的事件,或者完全忽略它们。位置方法以对象的形式提供全局和本地(小部件相对)位置信息QPoint,而按钮则使用命名空间中的鼠标按钮类型来报告Qt。


例如,以下内容允许我们对窗口上的左键、右键或中键单击做出不同的响应。



def mousePressEvent(self, e):
if e.button() == Qt.MouseButton.LeftButton:
# handle the left-button press in here
self.label.setText(“mousePressEvent LEFT”)

elif e.button() == Qt.MouseButton.MiddleButton:
    # handle the middle-button press in here.
    self.label.setText("mousePressEvent MIDDLE")

elif e.button() == Qt.MouseButton.RightButton:
    # handle the right-button press in here.
    self.label.setText("mousePressEvent RIGHT")

def mouseReleaseEvent(self, e):
if e.button() == Qt.MouseButton.LeftButton:
self.label.setText(“mouseReleaseEvent LEFT”)

elif e.button() == Qt.MouseButton.MiddleButton:
    self.label.setText("mouseReleaseEvent MIDDLE")

elif e.button() == Qt.MouseButton.RightButton:
    self.label.setText("mouseReleaseEvent RIGHT")

def mouseDoubleClickEvent(self, e):
if e.button() == Qt.MouseButton.LeftButton:
self.label.setText(“mouseDoubleClickEvent LEFT”)

elif e.button() == Qt.MouseButton.MiddleButton:
    self.label.setText("mouseDoubleClickEvent MIDDLE")

elif e.button() == Qt.MouseButton.RightButton:
    self.label.setText("mouseDoubleClickEvent RIGHT")

按钮标识符在 Qt 命名空间中定义,如下 –


标识符 值(二进制) 代表  
 Qt.MouseButton.NoButton 0 ( 000) 没有按下按钮,或者事件与按钮按下无关。  
 Qt.MouseButton.LeftButton 1 ( 001) 左键被按下  
 Qt.MouseButton.RightButton 2 ( 010) 右侧按钮被按下。  
 Qt.MouseButton.MiddleButton 4 ( 100) 中间的按钮被按下。  
 在惯用左手的鼠标上,左右按钮的位置是相反的,即按下最右边的按钮将返回Qt.LeftButton。这意味着您不需要在代码中考虑鼠标方向。


## 十、上下文菜单


上下文菜单是小的上下文相关菜单,通常在右键单击窗口时出现。 Qt 支持生成这些菜单,并且小部件具有用于触发它们的特定事件。在下面的示例中,我们将拦截.contextMenuEventa QMainWindow。每当要显示上下文菜单时都会触发此事件,并传递一个event类型的值QContextMenuEvent。


要拦截该事件,我们只需使用同名的新方法覆盖对象方法即可。因此,在这种情况下,我们可以在子MainWindow类上创建一个名为 的方法contextMenuEvent,它将接收该类型的所有事件。



import sys

from PyQt6.QtCore import Qt
from PyQt6.QtGui import QAction
from PyQt6.QtWidgets import QApplication, QLabel, QMainWindow, QMenu

class MainWindow(QMainWindow):
def __init__(self):
super().init()

def contextMenuEvent(self, e):
    context = QMenu(self)
    context.addAction(QAction("test 1", self))
    context.addAction(QAction("test 2", self))
    context.addAction(QAction("test 3", self))
    context.exec(e.globalPos())

app = QApplication(sys.argv)

window = MainWindow()
window.show()

app.exec()


如果运行上面的代码并在窗口中右键单击,您将看到出现一个上下文菜单。您可以.triggered照常在菜单操作上设置插槽(并重复使用为菜单和工具栏定义的操作)。


将初始位置传递给exec函数时,这必须相对于定义时传入的父级。在本例中,我们self作为父级传递,因此我们可以使用全局位置。


为了完整起见,实际上有一种基于信号的方法来创建上下文菜单。



class MainWindow(QMainWindow):
def __init__(self):
super().init()
self.show()

    self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
    self.customContextMenuRequested.connect(self.on_context_menu)

def on\_context\_menu(self, pos):

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注Python)

eysKaHXT-1712968895442)]

[外链图片转存中…(img-FbLZpR5j-1712968895442)]

[外链图片转存中…(img-wMxxTjIy-1712968895442)]

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注Python)

img
  • 23
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值