OpenCV-PyQT项目实战(3)信号与槽机制

news2024/10/6 14:34:01

欢迎关注『OpenCV-PyQT项目实战 @ Youcans』系列,持续更新中
OpenCV-PyQT项目实战(1)安装与环境配置
OpenCV-PyQT项目实战(2)QtDesigner 和 PyUIC 快速入门
OpenCV-PyQT项目实战(3)信号与槽机制

文章目录

    • 1. 信号与槽(Signals and slots)
      • 1.1 信号与槽的原理
      • 1.2 信号发送者与槽的接收者
    • 2. QtDesigner 建立信号与槽的连接
      • 2.1 信号与槽的连接:不同的发送者与接收者,槽函数为控件的内置函数
      • 2.2 信号与槽的连接:不同的发送者与接收者,槽函数为自定义函数
      • 2.3 信号与槽的连接:相同的发送者与接收者,槽函数为控件的内置函数
      • 2.4 信号与槽的连接:发送者是动作对象
    • 3. OpenCV-PyQt5 图形界面例程-3
      • 3.1 自定义槽函数
      • 3.2 在程序中定义信号与槽的连接
      • 3.3 OpenCV-PyQt 完整例程
      • 3.4 运行结果


在上节的 GUIDemo03.py 中,我们使用 QtDesigner 设计了一个简单的图像界面 ,包括菜单、工具栏、图像和按钮。但是,这些按钮和菜单项还是无效的,点击后不会调用相应的处理函数,这是因为我们还没有为这些控件关联动作程序(连接槽函数)。

本节介绍 PyQt 的信号与槽。信号与槽机制是 PyQt 的核心机制,用于对象之间的通信,也就是实现函数之间的自动调用。


1. 信号与槽(Signals and slots)

1.1 信号与槽的原理

简单地说,将信号与槽函数连接后,当信号被触发时,槽函数将被自动调用。

分析这个过程,涉及到几个基本概念和关系:

  • 信号:信号可以是一个动作,也可以是对象的一种状态,用于触发所连接的槽。
  • 槽:槽就是一个函数,可以由连接的信号触发。
  • 发射信号:信号被发射时,自动调用信号连接的槽函数。通常,在对象的状态改变时发射信号。
  • 信号可以带有参数,但必须与槽函数的参数相对应。
  • 一个信号可以连接多个槽函数,多个信号也可以连接到一个槽函数。
  • 一个信号可以连接到另一个信号上。
  • 很多窗口部件(控件)内置了一下信号和槽,可以直接调用。也可以按需求自定义信号和槽。

1.2 信号发送者与槽的接收者

信号的发送者通常是一个控件对象,在控件对象的状态发生变化时发送信号。常见的发送者是图形窗口中的各种控件对象,但也可以是动作对象。

槽的接收者通常也是控件对象。槽函数是一个自定义的槽函数,或控件内置的槽函数。一般地,槽函数也有一个对象作为主体,即对于接受者这个控件对象执行函数定义的操作。例如槽函数执行的功能是关闭,哪么究竟是关闭那个控件呢?关闭对象就是接受者。

为了方便讲解信号与槽的连接,我们用 QtDesigner 在上节设计的图形窗口 uiDemo3.ui 的基础上,增加几个按钮对象和文本行编辑对象:

  • 打开 PyCharm,从 Tools -> ExternalTools -> QtDesigner 打开 QtDesigner,打开 uiDemo2.ui 文件。
  • 鼠标点击选中 QtDesigner 左侧控件栏 Buttons 中的 PushButton,按住鼠标不放,将其拖动到中间的图形界面后松开鼠标,就在图形界面中创建了一个 PushButton 控件。
  • 重复以上步骤,可以再建立几个 PushButton 控件 控件。
    • 注意在 QtDesigner 右侧 “对象查看器” 中所显示的控件名称和属性,多个 PushButton 被自动赋予不同的命名(objectName),如:pushButton_1、pushButton_2、pushButton_3,这就如同桌子有几个抽屉分别标记为 “抽屉1”、“抽屉2”、“抽屉3” 以便识别。
    • 控件的名称和其它属性都可以在 “属性编辑器” 中编辑修改。
  • 鼠标点击选中 QtDesigner 左侧控件栏 DisplayWidgets 中的 Label,按住鼠标不放,将其拖动到中间的图形界面后松开鼠标,就在图形界面中创建了一个 Label 控件。
    • 在 QtDesigner 右侧上方 “对象查看器” 中选中创建的 label 控件,在右侧中部“属性编辑器”中对 QLabel 属性 pixmap 进行编辑,可以选择初始显示图片。
  • 鼠标选中在图形界面中创建的 PushButton 控件或 LineEdit 控件,可以拖动控件调整位置。
  • 鼠标双击 PushButton 按钮,可以编辑按钮控件的标签即按钮上的显示内容。
    为了便于讲解,本例将各 PushButton 控件的显示内容(text 属性)修改为:“1# 处理”、“2# 帮助”、“3# 退出”。

将这个应用程序图形界面另存为 uiDemo3.ui,其预览效果如下:

在这里插入图片描述



2. QtDesigner 建立信号与槽的连接

2.1 信号与槽的连接:不同的发送者与接收者,槽函数为控件的内置函数

QtDesigner 提供了便捷和直观的信号/槽编辑方法。

本例介绍不同的发送者与接收者,槽函数为控件的内置函数的操作方法。不同类型的控件分别内置了若干方法,例如 QPushButton 控件内置的方法包括:点击、选中、状态变化、显示菜单等,而 QLineEdit 控件内置的方法包括:清空、复制、剪切、粘贴、全选、撤销操作等。使用控件内置的方法作为槽函数,可以直接调用,不需要对函数进行定义。

我们所设计的功能是:当点击按钮控件 “pushButton_1” 时,清空文本编辑控件 “lineEdit_1” 的显示内容。注意我们称按钮控件为 “lineEdit_1” 而不是 “文本编辑行-1”,这是因为 lineEdit_1 才是控件名称,在程序中是不变的,而 “文本编辑行-1” 只是显示内容,可以在程序中修改。

QtDesigner 设置信号/槽的连接的操作步骤如下:

  • (1)选择菜单项 “Edit” -> 编辑信号/槽,或者通过快捷键 F4 或在工具栏选择,进入信号/槽编辑模式。
  • (2)选中控件对象发送者,此处为按钮控件 “pushButton_1”,鼠标左键长按不放,该按钮控件变为浅红色。
    • 鼠标左键继续长按不放,并移动鼠标,当鼠标移出控件对象区域后,出现一条带箭头的红线和一个红色的接地符号。
    • 鼠标左键继续长按不放,并拖动鼠标到控件对象 “lineEdit_1”,松开鼠标左键,就建立了以控件对象 “pushButton_1” 为发送者、控件对象 “lineEdit_1” 为接收者的信号/槽连接。
    • 此时控件对象 “pushButton_1” 和 “lineEdit_1” 都变为浅红色,带箭头的红线从 “pushButton_1” 出发,指向 “lineEdit_1” 结束。
  • (3)同时弹出对话框 “配置连接 - QtDesigner”,对话框的左侧显示发送者控件的信号选项,对话框的右侧显示接收者的控件选项。
    • 根据功能要求,触发信号为按钮 “pushButton_1” 被点击,从对话框左侧选中 “clicked()”;
    • 根据功能要求,收到信号后动作是清空文本编辑控件 “lineEdit_1”,从对话框右侧选中 “clear()”;
    • 点击对话框下方的按钮 “OK”,完成该信号/槽连接的配置。

在这里插入图片描述


2.2 信号与槽的连接:不同的发送者与接收者,槽函数为自定义函数

本例介绍不同的发送者与接收者,槽函数为自定义函数的操作方法。

在 2.1 中介绍了使用控件内置的方法作为槽函数,可以直接调用,不需要对函数进行定义。程序设计中的核心功能通常是程序员根据需求开发的自定义函数。使用自定义函数作为槽函数,一方面当然是要编写自定义函数,另一方面要将自定义函数添加到槽函数配置连接表中。

我们所设计的功能是:当点击按钮控件 “pushButton_2” 时,清空文本编辑控件 “lineEdit_2” 的显示内容,并显示文本信息 “current signal: click pushButton_2”。在主程序中要编写一个自定义函数实现该功能,将该自定义函数命名为 click_pushButton_2()。

注意我们编写的自定义函数 click_pushButton_2(),虽然功能只是对文本编辑控件 “lineEdit_2” 进行操作,但对于自定义函数也可以完成任意的其它功能,对其它控件按照控件名称进行操作。因此该槽函数的接收者并不是文本编辑控件 “lineEdit_2”,而是主窗口控件 “MainWindow”。

QtDesigner 设置信号/槽的连接的操作步骤如下:

**首先要在 QtDesigner 将自定义函数添加到槽函数配置连接表中——非常重要。**网上的很多文章都没有讲具体实现方法,这个操作的入口也很难找到。

  • (1)在 QtDesigner 右侧上方的 “对象查看器”,选中 MainWindow 或其它顶层对象,单击鼠标右键唤出下拉菜单,选择 “改变信号/槽”;
    • 弹出 “MainWindow 的信号/槽” 对话框,对话框的上方显示槽的选项,下方显示信号选项。
    • 点击对话框上方 “槽” 选项框下部的绿色 “+”,系统在 “槽” 选项表的最后自动增加了一行 “slot1()”。这就是新增的自定义槽函数。
    • 点击选中 “slot1()”,再鼠标双击,就可以修改槽函数的函数名,例如修改为 click_pushButton_2()。
    • 再点击对话框上方 “槽” 选项框下部的绿色 “+”,可以继续逐一添加自定义的槽函数。

在这里插入图片描述


然后设置信号/槽的连接:

  • (2)选择菜单项 “Edit” -> 编辑信号/槽,或者通过快捷键 F4 或在工具栏选择,进入信号/槽编辑模式。
    • 选中控件对象发送者,此处为按钮控件 “pushButton_2”,长按鼠标左键并移动,当鼠标移出控件对象区域后,出现一条带箭头的红线和一个红色的接地符号。
    • 松开鼠标左键,就建立了以控件对象 “pushButton_2” 为发送者、控件对象 “MainWindow” 为接收者的信号/槽连接。
      此时控件对象 “pushButton_2” 变为浅红色,带箭头的红线从 “pushButton_2” 出发,并不指向其它控件,而是以一个接地符号结束。
  • (3)同时弹出对话框 “配置连接 - QtDesigner”,对话框的左侧显示发送者控件 “pushButton_2” 的信号选项,对话框的右侧显示接收者 “MainWindow” 的控件选项。
    • 根据功能要求,触发信号为按钮 “pushButton_2” 被点击,从对话框左侧选中 “clicked()”;
    • 对话框右侧接收者 “MainWindow” 的控件选项列表中,显示了刚才添加的几个自定义函数,选择 “click_pushButton_2()”;
    • 点击对话框下方的按钮 “OK”,完成该信号/槽连接的配置。

在这里插入图片描述


最后,别忘了要在主程序中编写自定义的函数。但这已不属于 QtDesigner 设计的内容了,在此不再详述。

类似地,我们设计:当点击按钮控件 “pushButton_3” 时,在文本编辑控件 "lineEdit_1"显示当前系统日期,在文本编辑控件 "lineEdit_2"显示当前系统时间,在文本编辑控件 “lineEdit_3” 显示提示信息。在主程序中编写一个自定义函数 click_pushButton_3(),并与 “pushButton_3” 建立信号/槽连接。

这表明:在自定义的子函数中,可以同时操作多个控件对象,进而可以实现用户定义的各种功能。


2.3 信号与槽的连接:相同的发送者与接收者,槽函数为控件的内置函数

本例介绍相同的发送者与接收者,槽函数为控件的内置函数的操作方法。

顾名思义,相同的发送者与接收者,就是说信号的发送者与槽函数的接收者是同一个控件对象。这是什么情况?例如,一个开关按钮有 “On/Off” 两种状态,每按一次则按钮状态发生翻转。类似地,选项框也有选中、未选中两种状态。特殊地,点击按钮后,关闭该按钮控件,也属于相同的发送者与接收者。

我们首先将控件对象 “pushButton_4” 从按钮控件 QPushButton 改变为 选项框控件 “QCheckBox”:

  • 点击控件对象 “pushButton_4”,控件对象的周围边界显示几个蓝色小方块;
  • 点击鼠标右键唤出下拉菜单,选择:“变型为” ->“QCheckBox”。

此时,设计界面窗口中的按钮控件,变成了一个选项框。同时,右侧 “对象查看器” 中的控件 “pushButton_4”,也自动变更为 “checkBox_4”。变更的控件 “checkBox_4” 继承了原来控件 “pushButton_4” 的一些属性,如:位置、尺寸、显示内容。

接下来设置信号/槽的连接:

  • (1)选择菜单项 “Edit” -> 编辑信号/槽,或者通过快捷键 F4 或在工具栏选择,进入信号/槽编辑模式。
  • (2)选中控件对象发送者,此处为按钮控件 “checkBox_4”,长按鼠标左键并移动,当鼠标移出控件对象区域后,出现一条带箭头的红线和一个红色的接地符号。
    • 长按鼠标左键,拖动鼠标再回到控件对象 “checkBox_4” 区域后松开鼠标左键,就建立了发送者和接收者都是控件对象 “checkBox_4” 的信号/槽连接。
    • 此时控件对象 “checkBox_4” 变为浅红色,带箭头的红线从 “checkBox_4” 出发,又返回到 “checkBox_4” 结束。
  • (3)同时弹出了对话框 “配置连接 - QtDesigner”,对话框的左侧和右侧分别是控件对象 “checkBox_4” 的信号和槽函数。
    • 从对话框左侧选中 “clicked(bool)”;
    • 从对话框右侧选中 “setChecked(bool)”;
    • 点击对话框下方的按钮 “OK”,完成该信号/槽连接的配置。

在这里插入图片描述


2.4 信号与槽的连接:发送者是动作对象

注意:本节的方法用于讲解,在本节例程中没有使用这种方法,但后续项目例程中会用到。

常见的信号发送者是图形窗口中的各种控件对象,但也可以是动作对象。本例介绍对菜单栏和工具栏中控件对象建立信号与槽的连接。

信号的发送者是动作对象时,信号的接收者通常是顶层对象 “MainWindow”,而槽函数可以是对象 “MainWindow” 的内置函数,也可以是自定义函数。

在上一篇文章中我们曾为菜单栏和工具栏中的动作 “actionQuit” 建立信号/槽连接,就是发送者是动作对象、连接到对象 “MainWindow” 的内置函数的案例。其操作过程如下:

  • 从在 QtDesigner 右侧下方窗口 “信号/槽编辑器”,点击绿色的 “+” 新建一个信号/槽连接;
  • 点击 “<发送者>”,从菜单中选择对象 “actionQuit”;
  • 点击 “<信号>”,从菜单中选择 “triggered()”;
  • 点击 “<接收者>”,从菜单中选择 “MainWindow”;
  • 点击 “<槽>”,从菜单中选择 “closed()”。

以上操作的作用是:发送者 对象 “actionQuit” 触发 “triggered()” 时,接收者 对象"MainWindow" 执行槽函数 “closed()”。

下面我们再为另一个动作 “actionHelp” 建立信号/槽连接,连接的槽函数为自定义函数 trigger_actHelp()。其操作过程如下:

  • 在 QtDesigner 将自定义函数 trigger_actHelp() 添加到槽函数配置连接表中;
  • 从在 QtDesigner 右侧下方窗口 “信号/槽编辑器”,点击绿色的 “+” 新建一个信号/槽连接;
  • 点击 “<发送者>”,从菜单中选择对象 “actionHelp”;
  • 点击 “<信号>”,从菜单中选择 “triggered()”;
  • 点击 “<接收者>”,从菜单中选择 “MainWindow”;
  • 点击 “<槽>”,从菜单中选择 “trigger_actHelp()”。

至此,本章介绍了用 QtDesigner 进行几种常见的信号/槽连接的编辑和设置方法。如下图所示,在 QtDesigner 中所添加的信号/槽连接都会在信号/槽编辑器窗口内显示。

在这里插入图片描述



3. OpenCV-PyQt5 图形界面例程-3

上节在 QtDesigner 中完成了图形界面的设计,将该文件另存为 uiDemo4.ui。打开 PyCharm,选中文件 uiDemo3.ui,使用 PyUIC 可以将其转换为 uiDemo3.py。

接下来我们要编写图形界面的主程序,调用图形界面设计文件 uiDemo4.py。

3.1 自定义槽函数

将自定义的槽函数 click_pushButton_1()、click_pushButton_2、trigger_actHelp(self) 添加到主程序中。

    def click_pushButton_1(self):  # 点击 pushButton_1 触发
        img = cv.imread("../images/Lena.tif")  # OpenCV 读取图像
        qtImg = QtGui.QImage(img.data, img.shape[1], img.shape[0],
                             QtGui.QImage.Format_RGB888).rgbSwapped()  # 转为为 PyQt 图像格式
        self.label.setPixmap((QtGui.QPixmap.fromImage(qtImg)))  # 加载 PyQt 图像
        self.label.setScaledContents(True)  # 图片自适应 QLabel 区域大小
        return

    def trigger_actHelp(self):  # 动作 actHelp 触发
        QMessageBox.about(self, "About",
                          """数字图像处理工具箱 v1.0\nCopyright YouCans, XUPT 2023""")
        return

3.2 在程序中定义信号与槽的连接

第 2 节介绍了使用 QtDesigner 建立信号与槽的连接。

对于较为复杂的项目,在程序中直接定义信号与槽的连接更加方便、灵活。例如:

        # 建立信号与槽的连接
        self.pushButton_1.clicked.connect(self.click_pushButton_1)  # 点击 pushButton_1 触发
        self.pushButton_2.clicked.connect(self.trigger_actHelp)  # 点击 pushButton_2 触发
        self.pushButton_3.clicked.connect(self.close)  # 点击 pushButton_3 关闭窗口

其中,click_pushButton_1 和 trigger_actHelp 是上文的自定义槽函数,而 close 关闭窗口是内部函数。


3.3 OpenCV-PyQt 完整例程

主程序 OpenCVPyqt03.py:

# OpenCVPyqt03.py
# Demo03 of GUI by PyQt5
# Copyright 2023 Youcans, XUPT
# Crated:2023-01-26

import sys
import cv2 as cv
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox
from uiDemo3 import Ui_MainWindow  # 导入 uiDemo5.py 中的 Ui_MainWindow 界面类

class MyMainWindow(QMainWindow, Ui_MainWindow):  # 继承 QMainWindow 类和 Ui_MainWindow 界面类
    def __init__(self, parent=None):
        super(MyMainWindow, self).__init__(parent)  # 初始化父类
        self.setupUi(self)  # 继承 Ui_MainWindow 界面类

        # 建立信号与槽的连接
        self.pushButton_1.clicked.connect(self.click_pushButton_1)  # 点击 pushButton_1 触发
        self.pushButton_2.clicked.connect(self.trigger_actHelp)  # 点击 pushButton_2 触发
        self.pushButton_3.clicked.connect(self.close)  # 点击 pushButton_3 关闭窗口
        return

    def click_pushButton_1(self):  # 点击 pushButton_1 触发
        img = cv.imread("../images/Lena.tif")  # OpenCV 读取图像
        qtImg = QtGui.QImage(img.data, img.shape[1], img.shape[0],
                             QtGui.QImage.Format_RGB888).rgbSwapped()  # 转为为 PyQt 图像格式
        self.label.setPixmap((QtGui.QPixmap.fromImage(qtImg)))  # 加载 PyQt 图像
        self.label.setScaledContents(True)  # 图片自适应 QLabel 区域大小
        return

    def trigger_actHelp(self):  # 动作 actHelp 触发
        QMessageBox.about(self, "About",
                          """数字图像处理工具箱 v1.0\nCopyright YouCans, XUPT 2023""")
        return

if __name__ == '__main__':
    app = QApplication(sys.argv)  # 在 QApplication 方法中使用,创建应用程序对象
    myWin = MyMainWindow()  # 实例化 MyMainWindow 类,创建主窗口
    myWin.show()  # 在桌面显示控件 myWin
    sys.exit(app.exec_())  # 结束进程,退出程序

UI 程序 uiDemo3.py(由 uiDemo3.ui 自动生成,不需要自己编程):

# -*- coding: utf-8 -*-

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(600, 480)
        MainWindow.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("../images/youcansSmallLogo.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        MainWindow.setWindowIcon(icon)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(20, 10, 560, 330))
        self.label.setText("")
        self.label.setPixmap(QtGui.QPixmap("../images/Fig0301.png"))
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setObjectName("label")
        self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_3.setGeometry(QtCore.QRect(390, 350, 81, 40))
        self.pushButton_3.setObjectName("pushButton_3")
        self.pushButton_1 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_1.setGeometry(QtCore.QRect(130, 350, 81, 40))
        self.pushButton_1.setObjectName("pushButton_1")
        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_2.setGeometry(QtCore.QRect(260, 350, 81, 40))
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Ignored)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.pushButton_2.sizePolicy().hasHeightForWidth())
        self.pushButton_2.setSizePolicy(sizePolicy)
        self.pushButton_2.setObjectName("pushButton_2")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 600, 25))
        self.menubar.setBaseSize(QtCore.QSize(9, 0))
        font = QtGui.QFont()
        font.setPointSize(10)
        self.menubar.setFont(font)
        self.menubar.setObjectName("menubar")
        self.menuFile = QtWidgets.QMenu(self.menubar)
        self.menuFile.setObjectName("menuFile")
        self.menuQuit = QtWidgets.QMenu(self.menubar)
        self.menuQuit.setObjectName("menuQuit")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.toolBar = QtWidgets.QToolBar(MainWindow)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.toolBar.sizePolicy().hasHeightForWidth())
        self.toolBar.setSizePolicy(sizePolicy)
        self.toolBar.setMinimumSize(QtCore.QSize(0, 30))
        font = QtGui.QFont()
        font.setPointSize(10)
        self.toolBar.setFont(font)
        self.toolBar.setObjectName("toolBar")
        MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
        self.actionOpen = QtWidgets.QAction(MainWindow)
        self.actionOpen.setEnabled(True)
        self.actionOpen.setIconVisibleInMenu(False)
        self.actionOpen.setObjectName("actionOpen")
        self.actionSave = QtWidgets.QAction(MainWindow)
        self.actionSave.setIconVisibleInMenu(False)
        self.actionSave.setObjectName("actionSave")
        self.actionClose = QtWidgets.QAction(MainWindow)
        self.actionClose.setIconVisibleInMenu(False)
        self.actionClose.setObjectName("actionClose")
        self.actionQuit = QtWidgets.QAction(MainWindow)
        self.actionQuit.setVisible(True)
        self.actionQuit.setIconVisibleInMenu(False)
        self.actionQuit.setObjectName("actionQuit")
        self.actionSetup = QtWidgets.QAction(MainWindow)
        self.actionSetup.setObjectName("actionSetup")
        self.actionHelp = QtWidgets.QAction(MainWindow)
        self.actionHelp.setObjectName("actionHelp")
        self.menuFile.addAction(self.actionOpen)
        self.menuFile.addAction(self.actionSave)
        self.menuFile.addAction(self.actionClose)
        self.menuQuit.addAction(self.actionQuit)
        self.menubar.addAction(self.menuFile.menuAction())
        self.menubar.addAction(self.menuQuit.menuAction())
        self.toolBar.addAction(self.actionOpen)
        self.toolBar.addAction(self.actionClose)
        self.toolBar.addAction(self.actionSave)
        self.toolBar.addAction(self.actionSetup)
        self.toolBar.addAction(self.actionHelp)
        self.toolBar.addAction(self.actionQuit)

        self.retranslateUi(MainWindow)
        self.actionQuit.triggered.connect(MainWindow.close)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "OpenCV-PyQt"))
        self.pushButton_3.setText(_translate("MainWindow", "3 退出"))
        self.pushButton_1.setText(_translate("MainWindow", "1 处理"))
        self.pushButton_2.setText(_translate("MainWindow", "2 帮助"))
        self.menuFile.setTitle(_translate("MainWindow", "文件"))
        self.menuQuit.setTitle(_translate("MainWindow", "退出"))
        self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
        self.actionOpen.setText(_translate("MainWindow", "打开"))
        self.actionSave.setText(_translate("MainWindow", "保存"))
        self.actionClose.setText(_translate("MainWindow", "关闭"))
        self.actionQuit.setText(_translate("MainWindow", "退出"))
        self.actionSetup.setText(_translate("MainWindow", "设置"))
        self.actionHelp.setText(_translate("MainWindow", "帮助"))


3.4 运行结果

现在我们就得到了一个虽然简单但是很完整的面向对象的图形界面应用程序 OpenCVPyqt03。

在这里插入图片描述

检查一下应用程序 GUIdemo4 的各项功能:

  • 点击按钮 “1 处理”,以 OpenCV 方式读取并显示指定的图像;
  • 点击按钮 “2 帮助”,弹出上图中的信息提示框,点击 “OK” 可以关闭信息框;
  • 点击按钮 “3 退出” 或菜单栏的 “退出”,关闭图形窗口应用程序。

如果以上测试都成功了,那么恭喜你已经掌握了用 QtDesigner 设计 PyQt5 图形界面的基本功能。


【本节完】


版权声明:
原创作品,转载必须标注原文链接:https://blog.csdn.net/youcans/article/details/128775270
Copyright 2023 youcans, XUPT
Crated:2023-01-28

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/194665.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

健身大神都戴什么耳机、健身大佬的耳机清单分享

平时&#xff0c;我们总能看到许多运动健身的人群&#xff0c;在锻炼时都佩戴着耳机。但运动耳机的选择&#xff0c;同样是大有学问的。如果佩戴传统的真无线蓝牙耳机&#xff0c;有可能出现佩戴不稳、耳道肿胀等问题&#xff0c;影响运动体验。所以今天我们特意给大家带来几款…

【大数据实时数据同步】OGG异构多路映射同步原表审计表只存删除数据表实现方案(二)

文章目录前言十一、将SCOTT下所有已同步的HIS表逻辑同步配置改为DEL表操作1、首先来看一下抽取进程和应用进程我们要修改成什么样的配置2、开始前先停止源端的抽取进程3、清除原来HIS表非DELETE操作数据4、启动抽取进程和应用进程验证总结前言 这里是后续&#xff01;&#xf…

尚医通 (一)项目介绍

目录一、功能简介二、技术点三、业务流程四、系统架构一、功能简介 尚医通即为网上预约挂号系统&#xff0c;网上预约挂号是近年来开展的一项便民就医服务&#xff0c;旨在缓解看病难、挂号难的就医难题&#xff0c;许多患者为看一次病要跑很多次医院&#xff0c;最终还不一定…

微信小程序 python垃圾分类知识科普系统 uniapp

目 录 摘 要 III Abstract 4 1 系统概述 5 1.1 概述 5 1.2课题意义 5 1.3 主要内容 5 2 系统开发环境 6 2.1微信开发者工具 6 2.2小程序框架以及目录结构介绍 6 3 需求分析 1 3.1 系统设计目标 1 3.2需求分析概述 1 3.3 系统可行性分析…

分享160个ASP源码,总有一款适合您

分享160个ASP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 160个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/1xMcHd2x-EW0PP4TdVCU5vA?pwd3hby 提取码&#xff1a;3hby…

嵌入式开发:如何在嵌入式市场中平衡敏捷

敏捷方法的好处是有据可查的&#xff0c;但是在遵从驱动的市场中&#xff0c;向敏捷过渡对于嵌入式开发团队来说是一个挑战。需要可追溯性和文档来证明遵从性&#xff0c;但是这可能与敏捷的精神相矛盾&#xff0c;敏捷的精神仍然受到伴随着方法论成长起来的不准确神话的困扰。…

创建者模式—原型模式(深克隆和浅克隆的区别)

目录 1.原型模式 1.1概念 1.2结构 1.3实现 1.4案例 1.5深克隆&#xff08;扩展&#xff09; 2.浅克隆和深克隆 2.1克隆的特点 2.2浅克隆和深克隆的区别 1.原型模式 1.1概念 用一个已经创建的实例作为原型&#xff0c;通过复制该原型对象来创建一个和原型对象相同的新对…

【分布式任务调度】XXL-JOB快速搭建教程

XXL-JOB1. XXL-JOB简介2. XXL-JOB搭建2.1 准备工作2.1.1 下载源码2.1.2 数据库准备 数据库脚本在doc路径下&#xff0c;将其执行之后可以创建一个数据库&#xff0c;如图3所示&#xff1a;2.2 搭建调度中心2.3 搭建“执行器”3. 测试3.1 新增执行器3.2 新增任务3.3 启动任务测试…

【工作笔记0036】C#中Decimal小数取整容易犯错的坑

这两天排查一个bug&#xff0c;数据库中同样的计算公式 和 C#代码中一毛一样的计算公式&#xff0c;最后结果为 Decimal 类型的小数&#xff0c;需求保留两位小数。但是结果是数据库中 和 代码中的结果总是对不上。整理并简化了一下项目中代码计算公式&#xff1a;decimal a 3…

vitest第一章(初始vitest)

从三个角度分析vitest 他能做什么&#xff1f; 为什么要使用他&#xff1f; 使用它的优势是什么&#xff1f; vitest官网cn.vitest.dev/ 1.vitest能做什么 从官网介绍说这是一个单元测试框架&#xff0c;大家听到有测试两个字觉得这个是测试同学写的东西&#xff0c;其实这个…

低版本浏览器(webview)空数据音频文件异常报错

问题背景 监控出现大量静态资源加载异常报警&#xff0c;ios和安卓各系统版本都有&#xff08;排查了一段时间&#xff0c;发现是QA同学在全量测试&#xff0c;无语凝噎&#xff09;QA测试反馈&#xff0c;报告页在收音失败的情况稳定复现播放音频失败&#xff08;确实有问题&…

Qos实验配置-CBQ方式

目录 对报文进行重标记 对数据报文进行流量监管-接口入方向 将报文加入相应队列 为语音业务配置丢弃策略 配置流量整形-接口出方向 QoS理论讲解_静下心来敲木鱼的博客-CSDN博客_qos 优先级映射https://blog.csdn.net/m0_49864110/article/details/127414766?ops_request_…

MATLAB 绘制数据图

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…

python中的socket套接字基础 (客户端服务器信息交互)

文章目录1 socket概述2 socket使用2.1 socket常用函数2.2 简单实现 客户端与服务器 信息交互2.3 多线程实现服务器和客户端信息交互1 socket概述 Python中&#xff0c;我们利用Socket套接字来实现网络通信&#xff0c;可以说套接字是实现网络编程进行数据传输的一种技术手段。S…

UNIX环境高级编程_文件IO_共享文件操作

这篇笔记记录下&#xff0c;多进程(包括父子进程)操作同一个文件&#xff0c;或者同一个文件被同一个进程多次打开时的情况&#xff0c;总是傻傻分不清&#xff0c;必须记录一下了。 1 文件IO_共享文件 分下面四种情况记录。 情况1&#xff1a;同一进程多次open同一个文件 …

SAP MESSAGE :000 消息提示「BUG」

SAP MESSAGE :000 消息提示 错误消息 问题分析 前言&#xff1a;这个问题 DEBUG 模式没有抓到&#xff0c;所以才有了下文&#xff1a; 这是迁移程序时会出现的问题&#xff0c;参见下面的图片&#xff1a; 在迁移前&#xff0c;MATNR 转码 FUNC 就没有规范书写 sy-subrc <…

第十三章 常用类(包装类和String 相关类)

一、包装类 1. 包装类的分类 &#xff08;1&#xff09;针对八种基本数据类型相应的引用类型—包装类 &#xff08;2&#xff09;有了类的特点&#xff0c;就可以调用类中的方法。 2. 包装类和基本数据的转换 &#xff08;1&#xff09;jdk5 前的手动装箱和拆箱方式 public cl…

好友靠JVM成功进入阿里,阿里P8力荐的JVM笔记到底有什么魔力?

大家都是有经验的Java开发人员&#xff0c;想想为何要学习JVM? [面试?调优?装逼? ] 不管出于何种原因&#xff0c;总之你得先学好。那怎么学好呢? 每个人对于JVM的了解可能不一样&#xff0c;这就要考虑到怎么切入 既然大家都学习过Java&#xff0c;那不妨就从Java开始…

数据分析思维(四)|分类/矩阵思维

分类与矩阵思维 1、概念 在进行数据分析工作时&#xff0c;我们往往会涉及到多个核心指标&#xff0c;而对于不同数值核心指标的结合又会产生多种不同的结果&#xff0c;我们将相似结果的内容放到一起进行统一决策就会大大节省数据分析的时间&#xff0c;这种思想我们称之为分…

[python入门㊲] - python的继承

目录 ❤ 什么是继承 ❤ 派生和继承 ❤ 单继承 ❤ 多继承 ❤ MRO[方法搜索顺序](多继承顺序) ❤ 新式类和旧式&#xff08;经典&#xff09;类 ❤ 什么是继承 通过继承基类来得到基类的功能 所以我们把被继承的类称作父类或基类&#xff0c;继承者被称作子类 可…