Python理论之三 —— GUI

文章目录

  • 1. Tkinter
    • 1.1 Tkinter GUI 编程介绍
    • 1.2 控件
    • 1.3 偏函数应用示例
    • 1.4 文件系统遍历 GUI 示例
    • 1.5 其他 GUI 示例
  • 2. PyQt5
    • 2.1 环境搭载
      • 2.1.1 安装pyQT5模块包
      • 2.1.2 安装PyQt5-tools模块包
      • 2.1.3 配置QtDesigner和pyUIC
      • 2.1.4 官方QT的下载与安装
    • 2.2 使用说明
      • 2.2.1 新建窗体
      • 2.2.2 类继承
      • 2.2.3 定时器事件
      • 2.2.4 键盘鼠标事件
      • 2.2.5 层级关系调整
      • 2.2.6 窗体状态
      • 2.2.7 窗体大小
      • 2.2.8 【案例】无边框窗体
      • 2.2.9 【案例】窗体综合
      • 2.2.10 控件交互
      • 2.2.11 【案例】简单登录界面
      • 2.2.12 焦点控制
      • 2.2.13 事件响应函数
      • 2.2.14 控件信息提示 [状态栏、help 按钮]
      • 2.2.15 窗体尺寸控制
      • 2.2.16 各种按钮功能测试
      • 2.2.17 绘制事件
      • 2.2.18 右击菜单和按钮下拉菜单
      • 2.2.19 工具按钮
      • 2.2.20 radio 选中按钮
      • 2.2.21 按钮组设定与管理
      • 2.2.22 选中框
      • 2.2.23 输入框
      • 2.2.24 输入框内容编辑
  • 3 pyautogui 库 -- GUI 自动化
    • 3.1 防故障机制
    • 3.2 屏幕操作
    • 3.3 控制鼠标
    • 3.4 图像识别
    • 3.5 键盘输入

1. Tkinter

在Python3.x 中,默认安装Tkinter库,在使用时引入即可(引入方式:import tkinter

Tkinter 最大的好处,就是 python3 自带,但是开发功能偏弱。

GitHub上找到的 Tkinter 可视化编程工具:tkinter-designer

1.1 Tkinter GUI 编程介绍

创建一个 GUI 应用就像画画一样。传统上,艺术家使用单一的画布开展创作。其工作方式如下:首先会从一块干净的画布开始,该“画布”即用来构建其余组件的顶层窗口对象,而后才在“画布”上进行绘画。

窗口和控件

在 GUI 编程中,顶层的根窗口对象包含组成 GUI 应用的所有小窗口对象。它们可能是文字标签、按钮、列表框等。这些独立的 GUI 组件称为控件。所以当我们说创建一个顶层窗口时,只是表示需要一个地方来摆放所有的控件。

控件可以独立存在,也可以作为容器存在。如果一个控件包含其他控件,就可以将其认为是那些控件的父控件。相应地,如果一个控件被其他控件包含,则将其认为是那个控件的子控件,而父控件就是下一个直接包围它的容器控件。通常,控件有一些相关的行为,比如按下按钮、将文本写入文本框等。这些用户行为称为事件,而 GUI 对这类事件的响应称为回调

事件驱动处理
一个 GUI 应用从开始到结束就是通过整套事件体系(如鼠标键盘的点积等)来驱动的。这种方式称为事件驱动处理。

最简单的鼠标移动就是一个带有回调的事件的例子。假设鼠标指针正停在 GUI 应用顶层窗口的某处。如果你将鼠标移动到应用的另一部分,鼠标移动的行为会被复制到屏幕的光标上,于是看起来像是根据你的手移动的。

GUI 应用必须先创建所有的 GUI 组件,然后将它们绘制在屏幕上。这是 布局管理器(geometry manager) 的职责所在。当布局管理器排列好所有控件(包括顶层窗口)后,GUI 应用进入其类似服务器的无限循环。这个循环会一直运行,直到出现 GUI 事件,进行处理,然后再等待更多的事件去处理。

布局管理器

Tk 有 3 种布局管理器来帮助控件集进行定位。

  • 最原始的一种称为 Placer;
  • 第二种布局管理器会是你主要使用的,它叫做 Packer,它会把控件填充到正确的位置(即指定的父控件中),然后对于之后的每个控件,会去寻找剩余的空间进行填充。这个处理很像是旅行时往行李箱中填充行李的过程;
  • 第三种布局管理器是 Grid。你可以基于网格坐标,使用 Grid 来指定GUI 控件的放置。Grid 会在它们的网格位置上渲染 GUI 应用中的每个对象。

1.2 控件

Tk 控件

之前提到过所有主要控件都是构建在顶层窗口对象之上的。该对象在 Tkinter 中使用 Tk类进行创建,然后进行如下实例化:

import Tkinter
top = Tkinter.Tk()  # 顶层窗口对象实例化 

在这个窗口中,可以放置独立的控件,也可以将多个组件拼凑在一起来构成 GUI 程序。

以下是常用的 18 种 Tk 控件

控 件 描 述
Button 与 Label 类似,但提供额外的功能,如鼠标悬浮、按下、释放以及键盘活动/事件
Canvas 提供绘制形状的功能(线段、椭圆、多边形、矩形),可以包含图像或位图
Checkbutton 一组选框,可以勾选其中的任意个(与 HTML 的 checkbox 输入类似)
Entry 单行文本框,用于收集键盘输入(与 HTML 的文本输入类似)
Frame 包含其他控件的纯容器
Label 用于包含文本或图像
LabelFrame 标签和框架的组合,拥有额外的标签属性
Listbox 给用户显示一个选项列表来进行选择
Menu 按下 Menubutton 后弹出的选项列表,用户可以从中选择
Menubutton 用于包含菜单(下拉、级联等)
Message 消息。与 Label 类似,不过可以显示成多行
PanedWindow 一个可以控制其他控件在其中摆放的容器控件
Radiobutton 一组按钮,其中只有一个可以“按下”(与 HTML 的 radio 输入类似)
Scale 线性“滑块”控件,根据已设定的起始值和终止值,给出当前设定的精确值
Scrollbar 为 Text、Canvas、Listbox、Enter 等支持的控件提供滚动功能
Spinbox Entry 和 Button 的组合,允许对值进行调整
Text 多行文本框,用于收集(或显示)用户输入的文本(与 HTML 的 textarea 类似)
Toplevel 与 Frame 类似,不过它提供了一个单独的窗口容器

Label 控件

以下是一个 Label 控件演示

# 添加 Tk 到应用中
import tkinter  # tkinter 模组在 python 3.7 中是自带的
top = tkinter.Tk()  # 创建一个顶层窗口对象(根窗口),用于容纳整个 GUI 应用
# Tkinter.Tk()返回的对象通常称为根窗口,这也是一些应用使用 root 而不是 top 来指代它的原因,顶层窗口是那些在应用中独立显示的部分。GUI 程序中可以有多个顶层窗口,但是其中只能有一个是根窗口
label = tkinter.Label(top, text='hello world')  # 在顶层窗口对象中写入内容
label.pack()  # 通过底层的应用代码将这些 GUI 组件连接起来,这里是让 Packer 来管理和显示控件

tkinter.mainloop()  # 进入主事件循环
# 一般这是程序运行的最后一段代码。当进入主循环后,GUI 就从这里开始接管程序的执行,所有其他行为都会通过回调来处理,甚至包括退出应用。

运行结果是生成一个写有“hello world”的窗口;

Button 控件

import tkinter

top=tkinter.Tk()
quit=tkinter.Button(top,text='Quit',command=top.quit)
quit.pack()

tkinter.mainloop()

运行结果是生成一个写带有“Quit”选项的按钮窗口,按下并释放后,窗口进程关闭;


在 Packer 没有收到其他指示时,所有控件都是垂直排列的(自上而下依次排列)。如果想要水平布局则需要创建一个新的 Frame 对象来添加按钮。该框架将作为单个子对象来代替父对象的位置。

# Label 和 Button 控件演示,也就是两者结合
import tkinter

top=tkinter.Tk()
label=tkinter.Label(top,text='hello world')
label.pack()
quit=tkinter.Button(top,text='quit',command=top.quit)
quit.pack(fill=tkinter.X,expand=1)

tkinter.mainloop()

Scale 控件

Scale 用于与 Label 控件进行交互。Scale 滑块是用来控制 Label 控件中文字字体大小的工具。滑块的位置值越大,字体越大;反之亦然。

from tkinter import *

def resize(ev=None): # 调整 Label 控件中的文本大小
    label.config(font='Helvetica -%d bold'%scale.get())

top=Tk()

top.geometry('250x150')# 定义顶层窗口的大小,其中 x 是字母,不是乘号
label=Label(top,text='hello world',font='Helvetica -12 bold')
# label.config(font='Helvetica -%d bold'%40)
label.pack(fill=X, expand=1)
scale=Scale(top,from_=10,to=40,orient=HORIZONTAL,command=resize) # command=resize 调用 resize 回调函数,该函数依附于 Scale 控件,当 Scale 控件的划款移动时,该函数就会被激活
scale.set(12)
scale.pack(fill=X,expand=1)

quit=Button(top,text='quit',command=top.quit,activeforeground='white',activebackground='red')

quit.pack()
mainloop()

Python理论之三 —— GUI_第1张图片

1.3 偏函数应用示例


偏函数在 Python 2.5 版本中添加进来,是函数式编程一系列重要改进中的一部分。使用偏函数,可以通过有效地“冻结”那些预先确定的参数来缓存函数参数,然后在运行时,当获得需要的剩余参数后,可以将它们解冻,传递到最终的参数中,从而使用最终确定的所有参数去调用函数。

偏函数最好的一点是它不只局限于函数。偏函数可以用于可调用对象(任何包括函数接口的对象),只需要通过使用圆括号即可,包括类、方法或可调用实例。对于有很多可调用对象,并且许多调用都反复使用相同参数的情况,使用偏函数会非常合适。

GUI 编程是一个很好的偏函数用例,因为你很有可能需要 GUI 控件在外观上具有某种一致性,而这种一致性来自于使用相同参数创建相似的对象时。我们现在要实现一个应用,在这个应用中有很多按钮拥有相同的前景色和背景色。对于这种只有细微差别的按钮,每次都使用相同的参数创建相同的实例简直是一种浪费:前景色和背景色都是相同的,只有文本有一点不同。

在下面的示例中你可以看到它是如何一步步展开并最终调用到最底层的;

from functools import partial as pto # 导入偏函数模块
from tkinter import Tk,Button,X
# from tkinter.tix import Control
from tkinter.messagebox import showerror,showinfo,showwarning # 导入弹窗模块

WARN='warn'
CRIT='crit'
REGU='regu'

SIGNS={
    'do not enter':CRIT,
    'railroad crossing':WARN,
    '55  speed limit':REGU,
    'wrong way':CRIT,
    'merging traffic':WARN,
    'obe way':REGU,
}
# 声明弹窗信息
critCB=lambda:showerror('Error','Error Button Pressed')
warnCB=lambda:showwarning('Warning', 'Warning Button Pressed!')
infoCB=lambda:showinfo('Info','Info Button Pressed')

top=Tk()
top.geometry('250x250')
top.title('Rod Signs')


# 偏函数应用
# 一阶偏函数,模板化 Button 类和根窗口 top,即每次调用 MyButton 时,他都会调用 Button 类(创建一个按钮),并将 top 作为它的第一个参数,然后将其冻结为 MyButton
MyButton=pto(Button,top)
# 二阶偏函数应用,对一阶偏函数模板化,创建各种按钮类型
CritButton=pto(MyButton,command=critCB,bg='white',fg='red')
WarnButton=pto(MyButton,command=warnCB,bg='goldenrod1')
ReguButton=pto(MyButton,command=infoCB,bg='white')

# 循环生成按钮
for sign in SIGNS:
    signType=SIGNS[sign]
    cmd='%sButton(text=%r%s).pack(fill=X,expand=True)'%(signType.title(),sign,'.upper()'if signType==CRIT else '.title()')
    eval(cmd)
Button(top,text='Quit',command=top.quit,bg='red',fg='yellow').pack(fill=X,expand=1) # 退出按钮,bg 为按钮背景色, fg 为字体色

top.mainloop()

Python理论之三 —— GUI_第2张图片

1.4 文件系统遍历 GUI 示例


# 这个稍高级的 GUI 程序扩展了控件的使用,新增了列表框、文本框和滚动条。此外,还增加了鼠标单击、键盘按下、滚动操作等回调函数
import os
from time import sleep
from tkinter import *
from tkinter import TclError
class DirList(object):
    def __init__(self,initdir=None):
        self.top=Tk()
        self.label=Label(self.top,text='directory lister v1.1')  # 创建 Label 控件,其中的文本是应用的主标题和版本号
        self.label.pack()

        self.cwd=StringVar(self.top)  # 声明了 Tk 的一个变量 cwd,用于保存当前所在的目录名
        self.dirl=Label(self.top,fg='blue',font=('Helvetica',12,'bold')) # 创建 Label 控件,用于显示当前的目录名
        self.dirl.pack()

        self.dirfm=Frame(self.top)  # 控件 Scrollbar 和 Listvox 都包含在 Frame 控件中
        self.dirsb=Scrollbar(self.dirfm)  # Scrollbar 可以让用户在文件数超过 Listbox 的大小时能够移动列表
        self.dirsb.pack(side=RIGHT,fill=Y)

        self.dirs=Listbox(self.dirfm,height=15,width=50,yscrollcommand=self.dirsb.set)  # 调用 Listbox 控件,该控件包含了要列出的目录的文件列表
        # Shift
        self.dirs.bind('',self.setDirAndGo)  # 通过使用 Listbox 的 bind()方法,Listbox 的列表项可以与回调函数(setDirAndGo)连接起来
# 绑定意味着将一个回调函数与按键、鼠标操作或一些其他事件连接起来,当用户发起这类事件时,回调函数就会执行。当双击 Listbox 中的任意条目时,就会调用 setDirAndGo()函数。而 Scrollbar 通过调用 Scrollbar.config()方法与 Listbox 连接起来。
        # self.dirs.bind('',self.setDir)
        self.dirsb.config(command=self.dirs.yview)
        self.dirs.pack()
        self.dirfm.pack()

        self.dirn=Entry(self.top,width=50,textvariable=self.cwd)  # 创建文本框
        self.dirn.bind('',self.dols)  # 给文本框添加回车键的绑定
        self.dirn.pack()
        # 定义一个按钮的框架(bfm),用来放置 3 个按钮
        self.bfm=Frame(self.top)
        self.clr=Button(self.bfm,text='clear',command=self.clrDir,activeforeground='white',activebackground='blue')
        self.ls=Button(self.bfm,text='list directory',command=self.dols,activeforeground='white',activebackground='green')
        self.quit=Button(self.bfm,text='Quit',command=self.top.quit,activeforeground='white',activebackground='red')
        self.clr.pack()
        self.ls.pack()
        self.quit.pack()
        self.bfm.pack()
        # 初始化 GUI 程序,并以当前工作目录作为起始点
        if initdir:
            self.cwd.set(os.curdir)
            self.dols()
    # 清空 Tk 字符串变量 cwd(包含当前活动目录)
    def clrDir(self,ev=None):
        self.cwd.set('')
    def setDir(self,ev=None):
        check=self.dirs.get(self.dirs.curselection())
        self.cwd.set(check)
        print(check)
        self.dirn.update()

    def setDirAndGo(self,ev=None):
        self.last=self.cwd.get()
        self.dirs.config(selectbackground='red')
        check=self.dirs.get(self.dirs.curselection())
        if not check:
            check=os.curdir
        self.cwd.set(check)
        self.dols()  # 遍历目录

    def dols(self,ev=None):
        error=''
        # 进行所有安全检查(比如,目标是否是一个目录?它是否存在?)
        tdir=self.cwd.get()
        if tdir=='.':
            try:
                tdir=self.dirs.get(self.dirs.curselection())
            except:
                pass
        if not tdir:
            tdir=os.curdir  # 如果发生错误,之前的目录就会重设为当前目录
        if not os.path.exists(tdir):
            error=tdir+':'+'no such file'
        elif not os.path.isdir(tdir):
            error=tdir+':'+'not a directory'
        if error:  # 若发生错误
            self.cwd.set(error)
            self.top.update()
            sleep(1)
            if not (hasattr(self,'last') and self.last):
                self.last=os.curdir
            self.cwd.set(self.last)
            self.dirs.config(selectbackground='LightSkyBlue')
            self.top.update()
            return
        self.cwd.set('FETCHING DIRECTORY CONTENTS...')
        self.top.update()
        dirlist=os.listdir(tdir)  # 如果一切正常,就会调用 os.listdir()获取实际文件列表并在 Listbox 中进行替换
        dirlist.sort()
        os.chdir(tdir)

        self.dirl.config(text=os.getcwd())
        self.dirs.delete(0,END)
        self.dirs.insert(END,os.curdir)
        self.dirs.insert(END,os.pardir)
        for eachFile in dirlist:
            self.dirs.insert(END,eachFile)
        self.cwd.set(os.curdir)
        self.dirs.config(selectbackground='LightSkyBlue')

def main():
    d=DirList(os.curdir)
    mainloop()

if __name__=='__main__':
    main()
    

Python理论之三 —— GUI_第3张图片

1.5 其他 GUI 示例


这将使用 4 个流行的工具包实现同一个简单的 GUI 应用,这 4 个工具包分别是:Tix(Tk 接口扩展)、Pmw(Python MegaWidgets Tkinter扩展)、wxPython(wxWidgets 的 Python 版本)以及 PyGTK(GTK+的 Python 版本)

# Tk 接口扩展(Tix)
# 该文件用.pyw作后缀命名,可阻止 DOS 命令行或终端窗口(cmd)弹出
from tkinter import Label,Button,END
from tkinter.tix import Tk,Control,ComboBox

top=Tk() # 创建顶层窗口对象

label=Label(top,text='Anmials(in pairs;min:pair,max:dozen)')
label.pack()

ct=Control(top,label='Number:',integer=True,max=12,min=2,value=2,step=2) # 控件
ct.label.config(font='Helvetival -14 bold') # 对象 ct 的字体配置
ct.pack()

cb=ComboBox(top,label='Type:',editable=True)
cb.label.config(font='Helvetival -14')
for anmail in ('dog','cat','hamster','python'):  # 输入/下拉选择条
    cb.insert(END,anmail)
cb.pack()

qb=Button(top,text='Quit',command=top.quit,bg='red',fg='white')
qb.pack()

top.mainloop()

Python理论之三 —— GUI_第4张图片

# Tk 接口扩展(Pmw)
# 该文件用.pyw作后缀命名,可阻止 DOS 命令行或终端窗口弹出
from tkinter import Label,Button,END,W
from Pmw import initialise,ComboBox,Counter # Pmw 库需自行安装

top=initialise() # 创建顶层窗口对象

label=Label(top,text='Anmials(in pairs;min:pair,max:dozen)')
label.pack()

ct=Counter(top,labelpos=W,label_text='Number:',datatype='integer',
           entryfield_value=2,increment=2,
           entryfield_validate={'validator':'integer','max':12,'min':2}) # 控件,validator 确保值在设定之内
ct.pack()

cb=ComboBox(top,labelpos=W,label_text='Type')
for anmail in ('dog','cat','hamster','python'):  # 输入/下拉选择条
    cb.insert(END,anmail)
cb.pack()

qb=Button(top,text='Quit',command=top.quit,bg='red',fg='white')
qb.pack()

top.mainloop()

Python理论之三 —— GUI_第5张图片


2. PyQt5

  • 官方教程
  • 关于PyQT QTCreater的使用,请移步Python理论之三 —— GUI之PyQT QTCreater专讲

Qt 是一个跨平台的应用程序 C++开发类库,而PyQT是QT在Python语言下的应用,PyQT5中的"5"代表PyQT已经发展到第五代,"5"就是一个大的版本号。

PyQT的几个优点:

  1. Qt 使用信号与槽(Signals/Slots)机制进行对象之间的通信,它是类型安全且弱耦合的,易于创建可重用的软件模块。
  2. 使用 Qt Creator 中的 UI Designer 或独立的 Qt Designer 可以可视化地设计窗体,然后将窗体转换为 Python 程序,可以大大提高界面设计的效率。
  3. PyQt 将 Qt 和 Python 的优点结合到了一起,程序员可以利用 Qt 丰富的 UI 设计功能,但不需要使用复杂的 C++语言,而是使用 Python 语言编程。

2.1 环境搭载

2.1.1 安装pyQT5模块包

方法一:到Pycharm工程解析器(project interpreter)中搜索PyQt5并添加到工程中,这种方法只能在Pycharm中引入PyQT5,即只能在Pycharm内使用;
方法二:管理员下运行cmd - 输入pip install pyQt5,这种方法在本电脑全局使用;
方法三:到PyQt5下载页面下载对应whl文件,管理员下运行cmd - cd + whl所在目录 - pip install + whl全名

2.1.2 安装PyQt5-tools模块包

方法一:管理员下运行cmd - 输入pip install pyQt5-tools
方法二:到pyqt5-tools下载页面下载对应python版本和电脑版本的whl文件,管理员下运行cmd - cd + whl所在目录 - pip install + whl全名

如:python3.7 和 64位系统选择:pyqt5_tools-5.13.0.1.5-cp37-none-win_amd64.whl

2.1.3 配置QtDesigner和pyUIC

QtDesigner 可实现可视化构造UI界面,一般安装PyQt5后,系统会默认安装QtDesigner工具;

1. 配置QT设计界面“Qt Designer”

Pycharm --> File --> setting --> Tools --> External Tools --> 左上角 + 号Add ,填入对应参数:

  • Name:工具名,自定义,可填 QtDesigner
  • Program:运行程序,选择designer.exe

参考路径:C:\Users\你的用户名\AppData\Local\Programs\Python\Python37\Lib\site-packages\pyqt5_tools\Qt\bin\designer.exe

  • Working directory:工作路径,默认即可

参考路径:C:\Users\你的用户名\AppData\Local\Programs\Python\Python37\Lib\site-packages\pyqt5_tools\Qt\bin

2. 配置.ui转.py工具“pyUIC”

同上操作,新添加一个Tools,填入对应参数:
Name:工具名,自定义,可填 PyUIC

  • Program:运行程序,选择uic.exe

参考路径:C:\Users\你的用户名\AppData\Local\Programs\Python\Python37\Scripts\pyuic5.exe

  • Arguments:参数,填入:$FileName$ -o $FileNameWithoutExtension$.py
  • Working directory:工作路径,$ProjectFileDir$

完成上述两项操作后,记得ApplyOK !!!

在这里插入图片描述

2.1.4 官方QT的下载与安装

到国内清华大学镜像网,选择一个版本打开,
Python理论之三 —— GUI_第6张图片
Windows系统下,下载下面这个并安装:

Python理论之三 —— GUI_第7张图片


  • 安装过程:
  1. 打开安装包,按顺序一直到如下图页面后,根据需要的组件安装
    Python理论之三 —— GUI_第8张图片
  • MSVC(Microsoft Visual C++)编译器的模块,包括 MSVC 2015 64-bit、MSVC 2017 32-bit 和 MSVC 2017 64-bit。若要安装这几个模块,需要在计算机上预先安装相应版本的 Microsoft Visual Studio。一般用于研究 Qt C++编程。
  • MinGW xxx 编译器模块。MinGW( Minimalist GNU for Windows ) 是Windows 平台上使用的 GNU 工具集的导入库的集合。为了使用 Qt 的 IDE Qt Creator,必须安装至少一个编译器,可以选择安装这个模块。
    UWP 平台的编译器模块。UWP(Universal Windows Platform)是 Windows 10 中的编译模块,有不同 CPU 和编译器类型的 UWP 模块。
  • Android 平台的编译模块,包括 Android x86、Android ARM64-v8a 等,这是用于Android 平台开发的编译模块。
  • Sources 是 Qt C++类库的源程序。
  • Qt Charts 是二维图表模块,用于绘制柱状图、饼图、曲线图等常用二维图表。对于 Python,有相应的 PyQtChart 包,选择安装此模块,便于查看相关类的帮助信息。
  • Qt Data Visualization 是三维数据图表模块,用于数据的三维显示,如散点的三维空间分布、三维曲面等。对于 Python,有相应的 PyQtDataVisualization 包,选择安装此模块,便于查看相关类的帮助信息。
  • Qt Purchasing、Qt Virtual Keyboard、Qt WebEngine 等其他模块,可以根据自己的需要选择是否安装。
  • Qt Script(Deprecated)是脚本模块,这是个已经过时的模块,无须安装。
  • “Tools”节点下面是一些工具软件,这些软件如下。
    • Qt Creator xxx 是用于 Qt 程序开发的 IDE,在开发 PyQt5 的程序时需要使用此软件进行窗体可视化设计、查阅类的帮助信息等,必须安装(自动)。
    • Qt Creator xxx CDB Debugger support for Qt Creator,是用于支持在 Qt Creator 中进行程序调试的模块,可以不安装。
    • MinGW 7.3.0 是 MinGW 64-bit 编译工具链,需要安装。
    • Strawberry Perl 是一个 Perl 语言工具,无须安装。

2.2 使用说明

2.2.1 新建窗体

from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)  # 新建应用对象

window = QWidget()  # 新建窗体
window.setWindowTitle("sb")  # 窗体标题
window.resize(500, 500)  # 窗体初始化大小
window.move(0, 0)  # 窗体初始化位置

label = QLabel(window)  # 新建窗体标签对象
label.setText("sb")  # 标签内容
label.move(200, 200)  # 标签在窗体中的位置

window.show()  # 显示窗体

sys.exit(app.exec_())  # 循环运行应用对象

2.2.2 类继承

from PyQt5.Qt import *


class Window(QWidget):  # 新建窗体类,原理同 window = QWidget()
    def __init__(self):  # 类初始化
        super().__init__()  # 继承超类 QWidget,详见下面链接
        self.setWindowTitle("Window-Title")  # 窗体标题
        self.resize(500, 500)  # 窗体初始化尺寸
        self.setup_ui()  # 初始化运行的函数

    def QObject继承结构测试(self):
        # mros = QObject.__subclasses__()  # 返回所有子类
        mros = QObject.mro()  # ???
        for mro in mros:
            print(mro)

    def setup_ui(self):
        self.QObject继承结构测试()
        self.QObject类型判定()
        self.QObject对象的父子关系操作()
        self.QObject信号的操作()
        self.QObject对象名称和属性的操作()
        self.QObject对象释放()

    def QObject类型判定(self):
        label1 = QLabel(self)  # 新建窗体标签对象
        label1.setText("sb")  # 标签内容
        label1.move(100, 100)  # 标签在窗体中的位置

        label2 = QLabel(self)  # 新建窗体标签对象
        label2.setText("sbbbbb")  # 标签内容
        label2.move(0, 150)  # 标签在窗体中的位置
        # del label2  # 直接删除对象
        label2.deleteLater()  # 删除父类对象下的所有子对象,详见下面链接

        btn = QPushButton(self)  # 新建窗体按钮对象
        btn.setText("一个按钮")  # 按钮内容
        btn.move(200, 200)  # 按钮在窗体内的位置

        # for widget in self.findChildren(QLabel):
        for widget in self.children():  # 遍历所有子类(widget:控件),即遍历所有窗体控件
            print(widget)  # 查看所有控件
            if widget.isWidgetType():  # 如果子类是控件
                # if widget.inherits("QLabel"):  # 如果子类继承于类 QLabel,即如果控件是一个标签
                widget.setStyleSheet("background-color: cyan;")  # 设置对象背景色

    def QObject对象的父子关系操作(self):
        obj1 = QObject()

        self.obj1 = obj1
        obj2 = QObject()
        obj2.setParent(obj1)  # 指定父类窗体

        # 监听obj2对象被释放
        obj2.destroyed.connect(lambda: print("obj2对象被释放了"))
        del self.obj1  # 直接删除对象,即释放对象

    def QObject信号的操作(self):
        def destroy_cao(obj):
            print("对象被释放了", obj)

        def obj_name_cao(name):
            print("对象名称发生了改变", name)

        self.obj = QObject()  # 创建一个对象

        self.obj.destroyed.connect(destroy_cao)  # 事件与函数绑定,此处是对象被释放事件
        self.obj.objectNameChanged.connect(obj_name_cao)  # 事件与函数绑定,从此处是对象名被改变事件
        self.obj.objectNameChanged.disconnect(obj_name_cao)  # 事件与函数解绑定

        self.obj.setObjectName("改变对象名字")  # 改变对象名
        print(self.obj.receivers(self.obj.objectNameChanged))  # 查看对象接收器中接收到的对象数量
        # del self.obj  # 直接删除对象,即释放对象

        print(self.obj.signalsBlocked(), "还没被阻断")  # 判断信号是否被临时阻断
        self.obj.blockSignals(True)  # 临时阻断信号与槽函数的连接
        print(self.obj.signalsBlocked(), "信号被阻断")  # 判断信号是否被临时阻断
        self.obj.blockSignals(False)  # 恢复被临时阻断的信号与槽函数的连接
        print(self.obj.signalsBlocked(), "信号恢复了")

        # *************信号与槽案例***************开始
        def cao():
            print("按钮被点击")

        btn = QPushButton(self)  # 新建按钮对象
        btn.setText("点击我")  # 按钮内容

        btn.clicked.connect(cao)  # 事件与函数绑定,此处是按钮被点击事件
        # *************信号与槽案例***************结束

    def QObject继承结构测试(self):
        # mros = QObject.__subclasses__()  # 返回所有子类
        mros = QObject.mro()  # ???
        for mro in mros:
            print(mro)

    def QObject对象名称和属性的操作(self):
        # *************案例演示***************开始
        with open("QObject.qss", "r") as f:  # 打开并读取 qss 文件
            qApp.setStyleSheet(f.read())

        label = QLabel(self)  # 新建窗体标签对象
        label.setObjectName("notice")  # 对象改名
        ## setProperty (String prop, String value)
        ## - prop - 系统属性的名称
        ## - value - 系统属性的值
        label.setProperty("notice_level", "warning")  # 设置指定键对值的系统属性
        label.setText("warning")  # 标签内容
        label.setStyleSheet("font-size: 20px; color: red;")  # 设置标签样式

        label2 = QLabel(self)  # 新建窗体标签对象
        label2.move(100, 100)  # 标签在窗体的位置
        label2.setObjectName("notice")  # 对象改名
        label2.setProperty("notice_level", "error")  # 设置指定键对值的系统属性
        label2.setText("error")  # 标签内容

        print(label.dynamicPropertyNames())  # 查看对象键对值的系统属性
        print(label2.parentWidget())  # 查看 label2 的父类控件
        # *************案例演示***************结束

    def QObject对象释放(self):
        obj1 = QObject()  # 新建对象
        self.obj1 = obj1  # 把对象赋值给父类
        obj2 = QObject()  # 新建对象
        obj3 = QObject()  # 新建对象

        obj3.setParent(obj2)  # 指定父类窗体
        obj2.setParent(obj1)  # 指定父类窗体

        obj1.destroyed.connect(lambda: print("obj1被释放了"))  # 事件与函数绑定,此处是对象释放事件
        obj2.destroyed.connect(lambda: print("obj2被释放了"))  # 事件与函数绑定,此处是对象释放事件
        obj3.destroyed.connect(lambda: print("obj3被释放了"))  # 事件与函数绑定,此处是对象释放事件

        # del obj2  # 直接删除对象,即释放对象
        # 由于上面指定 obj3 的父类为 obj2,所以执行语句 obj2.deleteLater() 释放 obj2,其子类 obj3 也会被释放
        obj2.deleteLater()  # 删除父类对象下的所有子对象,详见下面链接

        print(obj1.children())  # 查看 obj1 的子类,即 obj2,与下句运行结果相同
        print(obj2)  # 查看 obj2


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

    window = Window()  # 新建一个窗体对象
    # window = QWidget()

    window.show()  # 展示窗体

    print(window.childAt(255, 255))  # 查看位于窗体内坐标(255, 255) 处的子类
    print(window.childrenRect())  # 查看窗体子类结构,即(x坐标,y坐标,宽,高)

    sys.exit(app.exec_())  # 循环运行应用对象
    

参考:
QObject
Python super() 函数
Qt deletelater函数分析
Setparent函数用法技巧

2.2.3 定时器事件

from PyQt5.Qt import *
import sys

# class MyObject(QObject):
#     def timerEvent(self, evt):  # 定时器响应函数
#         print(evt, "1")

class MyLabel(QLabel):
    def __init__(self, *args, **kwargs):  # 类初始化函数
        super().__init__(*args, **kwargs)  # 继承超类 QLabel,详见下面链接
        self.setText("10")  # 初始化标签内容
        self.move(100, 100)  # 初始化标签在窗体内的位置
        self.setStyleSheet("font-size: 22px;")  # 初始化标签样式

    def setSec(self, sec):
        self.setText(str(sec))

    def startMyTimer(self, ms):
        self.timer_id = self.startTimer(ms)  # 创建并开始一个定时器

    def timerEvent(self, *args, **kwargs):  # 定时器响应函数
        print("计时一次")

        current_sec = int(self.text())  # 获取当前的标签的内容
        current_sec -= 1
        self.setText(str(current_sec))  # 更新标签内容

        if current_sec == 0:
            print("停止")
            self.killTimer(self.timer_id)  # 销毁一个计时器

class MyWidget(QWidget):
    def timerEvent(self, *args, **kwargs):
        current_w = self.width()
        current_h = self.height()
        self.resize(current_w + 10, current_h + 10)

app = QApplication(sys.argv)  # 新建一个应用程序对象

window = MyWidget()  # 类实例化
window.setWindowTitle("QObject定时器的使用")  # 窗体标题
window.resize(500, 500)  # 初始化窗体尺寸
window.startTimer(1000)  # 启动一个定时器,定时器启动间隔 1000毫秒,即每 1 秒调用一次类

label = MyLabel(window)  # 类实例化
label.setSec(5)  # 调用类内函数 setSec(),即修改标签内容为 "5"
label.startMyTimer(1000)  # 调用类内函数 startMyTimer(),即设定定时器间隔为 1000毫秒

window.show()  # 展示窗体

sys.exit(app.exec_())  # 循环运行应用对象

2.2.4 键盘鼠标事件

from PyQt5.Qt import *
import sys

class MyLabel(QLabel):
    def enterEvent(self, *args, **kwargs):  # 鼠标进入事件响应函数
        print("鼠标进入")
        self.setText("鼠标进入")

    def leaveEvent(self, *args, **kwargs):  # 鼠标离开事件响应函数
        print("鼠标离开")
        self.setText("鼠标离开")

    def keyPressEvent(self, evt):  # 键盘按下事件响应函数
        # QKeyEvent
        print("检测到键盘被按下")

        if evt.key() == Qt.Key_Tab:  # 如果被按下的是 Tab 键
            print("----- 被点击的是 Tab 键")

        if evt.modifiers() == Qt.ControlModifier and evt.key() == Qt.Key_S:  # 获取组合键
            print("----- 组合键被按下,ctrl + S 被点击")

        if evt.modifiers() == Qt.ControlModifier | Qt.ShiftModifier and evt.key() == Qt.Key_A:  # 获取组合键
            print("----- 组合键被按下,ctrl + Shift + A 被点击")

app = QApplication(sys.argv)  # 创建一个应用程序对象

window = QWidget()  # 创建一个窗体控件
window.setWindowTitle("鼠标操作案例")  # 窗体标题
window.resize(500, 500)  # 窗体尺寸

label = MyLabel(window)  # 类实例化
label.resize(200, 200)  # 标签尺寸
label.move(100, 100)  # 标签在窗体内位置
label.setStyleSheet("background-color: cyan;")  # 标签样式
label.grabKeyboard()  # 获取键盘事件

window.show()  # 展示窗体控件

sys.exit(app.exec_())  # 循环运行应用对象

2.2.5 层级关系调整

from PyQt5.Qt import *
import sys

class Label(QLabel):  # 继承于标签 QLabel 的类
    def mousePressEvent(self, evt):  # 鼠标按下事件响应函数
        self.raise_()  # 让标签升层

app = QApplication(sys.argv)  # 创建一个应用程序对象

window = QWidget()  # 新建窗体对象
window.setWindowTitle("层级关系调整")  # 窗体标题
window.resize(500, 500)  # 初始化窗体尺寸

label1 = Label(window)  # 类实例化
label1.setText("标签1")  # 标签内容
label1.resize(200, 200)  # 标签尺寸
label1.setStyleSheet("background-color: red;")  # 标签样式

label2 = Label(window)  # 类实例化
label2.setText("标签2")  # 标签内容
label2.resize(200, 200)  # 标签尺寸
label2.setStyleSheet("background-color: green;")  # 标签样式
label2.move(100, 100)  # 标签位置

# label1.lower()  # 让标签1 降层
# label1.raise_()  # 让标签1 升层
# label2.stackUnder(label1)  # 让标签2 位于标签1 下层

window.show()  # 展示控件

sys.exit(app.exec_())  # 循环运行应用对象

2.2.6 窗体状态

from PyQt5.Qt import *
import sys

class Window(QWidget):  # 继承于控件 QWidget 的类
    def mousePressEvent(self, QMouseEvent):  # 鼠标按下事件响应函数
        if self.isMaximized():  # 如果窗体处于最大化
            self.showNormal()  # 窗体正常化
        else:
            self.showMaximized()  # 窗体最大化

app = QApplication(sys.argv)  # 创建一个应用程序对象

window = Window()  # 类实例化
window.resize(500, 500)  # 窗体尺寸
window.setWindowTitle("w1")  # 窗体标题

icon = QIcon("xxx.png")  # 创建图标对象
window.setWindowIcon(icon)  # 设置窗体图标
print(window.windowIcon())

window.setWindowOpacity(0.9)  # 设置窗体透明度
print(window.windowOpacity())

# window.windowState() : 读取窗体状态
# Qt.WindowNoState : 窗体无状态,即默认状态
print(window.windowState() == Qt.WindowNoState)  # 判断窗口状态
# window.setWindowState(Qt.WindowMinimized)  # 设置窗体状态,此处是最小化
# window.setWindowState(Qt.WindowMaximized)  # 设置窗体状态,此处是最大化(最大化有标题栏)
# window.setWindowState(Qt.WindowFullScreen)  # 设置窗体状态,此处是全屏化(全屏化没有标题栏)
# window.setWindowState(Qt.WindowActive)  # 设置窗体状态,此处是激活状态

window.show() # 展示控件

sys.exit(app.exec_())  # 循环运行应用对象

Qt::WindowStates 类型有以下几个取值:

  • Qt::WindowNoState:无标志,正常状态,默认状态。

  • Qt::WindowMinimized:最小化状态。

  • Qt::WindowMaxmized:最大化状态。

  • Qt::WindowFullScreen:全屏状态。

  • Qt::WindowActive:激活状态。

参考:
Qt 常用类 (9)—— QWidget

2.2.7 窗体大小

from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)  # 创建一个应用程序对象

window = QWidget()  # 创建一个窗体对象
window.move(200, 100)  # 初始化窗体在屏幕中的位置
# window.resize(500, 500)  # 初始化窗体尺寸
window.setFixedSize(500, 500)  # 窗体固定尺寸
# window.setGeometry(0, 0, 500, 150)  # 设置窗体位置与大小,输入参数:(x坐标,y坐标,宽,高),如果上面已设置过,此语句不起作用
print(window.x())
print(window.width())
print(window.geometry())


label = QLabel(window)  # 新建窗体内标签对象
label.setText("sb")  # 标签内容
label.move(100, 100)  # 标签在窗体内的位置
label.setStyleSheet("background-color: cyan;")  # 标签样式

def changeCao():
    new_content = label.text() + "+1 "
    label.setText(new_content)  # 标签内容
    # label.resize(label.width() + 100, label.height())  # 手动改变标签尺寸
    label.adjustSize()  # 自动调整标签尺寸

btn = QPushButton(window)  # 新建窗体内按钮对象
btn.setText("增加内容")  # 按钮内容
btn.move(100, 300)  # 按钮在窗体中的位置
btn.clicked.connect(changeCao)  # 事件与函数绑定,此处是按钮被点击事件

window.show()  # 展示窗体

sys.exit(app.exec_())  # 循环运行应用对象

2.2.8 【案例】无边框窗体

from PyQt5.Qt import *
import sys

class Window(QWidget):  # 继承于控件 QWidget 的类
    def __init__(self, *args, **kwargs):  # 类初始化函数
        super().__init__(*args, **kwargs)  # 继承超类 QWidget
        self.setWindowFlags(Qt.FramelessWindowHint)  # 设置窗口属性,此处是设置窗体为无边框,不可拖动拖拽拉伸
        self.setWindowOpacity(0.9)  # 窗体透明度
        self.setWindowTitle("顶层窗口操作-案例")  # 窗体标题
        self.resize(500, 500)  # 窗体尺寸

        # 公共数据
        self.top_margin = 10
        self.btn_w = 80
        self.btn_h = 40

        self.setup_ui()  # 调用函数

    def setup_ui(self):
        # 添加三个子控件 - 窗口的右上角
        close_btn = QPushButton(self)  # 创建按钮对象
        self.close_btn = close_btn  # 把对象传递给父类
        close_btn.setText("关闭")  # 按钮内容
        close_btn.resize(self.btn_w, self.btn_h)  # 按钮尺寸

        max_btn = QPushButton(self)
        self.max_btn = max_btn
        max_btn.setText("最大化")
        max_btn.resize(self.btn_w, self.btn_h)

        mini_btn = QPushButton(self)
        self.mini_btn = mini_btn
        mini_btn.setText("最小化")
        mini_btn.resize(self.btn_w, self.btn_h)

        def max_normal():
            if self.isMaximized():
                self.showNormal()
                max_btn.setText("最大化")
            else:
                self.showMaximized()
                max_btn.setText("恢复")

        close_btn.pressed.connect(self.close)  # 事件与函数绑定,此处是按钮被按下事件
        max_btn.pressed.connect(max_normal)  # 事件与函数绑定,此处是按钮被按下事件
        mini_btn.pressed.connect(self.showMinimized)  # 事件与函数绑定,此处是按钮被按下事件

    def resizeEvent(self, QResizeEvent):  # 窗体尺寸变化事件响应函数
        print("窗口大小发生了改变")
        window_w = self.width()

        close_btn_x = window_w - self.btn_w
        close_btn_y = self.top_margin
        self.close_btn.move(close_btn_x, close_btn_y)  # 改变关闭按钮在窗体中的位置

        max_btn_x = close_btn_x - self.btn_w
        max_btn_y = self.top_margin
        self.max_btn.move(max_btn_x, max_btn_y)  # 改变最大化按钮在窗体中的位置

        mini_btn_x = max_btn_x - self.btn_w
        mini_btn_y = self.top_margin
        self.mini_btn.move(mini_btn_x, mini_btn_y)  # 改变最小化按钮在窗体中的位置

    def mousePressEvent(self, QMouseEvent):  # 鼠标按下事件响应函数
        pass

    def mouseMoveEvent(self, QMouseEvent):  # 鼠标移动事件响应函数
        pass

    def mouseReleaseEvent(self, QMouseEvent):  # 鼠标释放事件响应函数
        pass

app = QApplication(sys.argv)  # 创建一个应用程序对象

window = Window()  # 新建窗体对象
window.show()  # 展示窗体

sys.exit(app.exec_())  # 循环运行应用对象

参考
setWindowFlags设置窗口属性

2.2.9 【案例】窗体综合

from PyQt5.Qt import *
import sys

# class Label(QLabel):
#     def mousePressEvent(self, QMouseEvent):
#         self.setStyleSheet("background-color: red;")

class Window(QWidget):  # 新建窗体类,原理同 window = QWidget()
    def mousePressEvent(self, evt):  # 鼠标按下事件响应函数
        local_x = evt.x()  # 获取鼠标 x 坐标
        local_y = evt.y()  # 获取鼠标 y 坐标
        sub_widget = self.childAt(local_x, local_y)  # 查看位于窗体内坐标(x 坐标, y 坐标)处的子类
        if sub_widget is not None:  # 如果该坐标处有子类
            sub_widget.setStyleSheet("background-color: red;")  # 设置对象样式
        print(local_x, local_y, "被点击了")


app = QApplication(sys.argv)  # 创建一个应用程序对象

window = Window()  # 新建一个窗体对象
window.setWindowTitle("父子关系案例")  # 窗体标题
window.resize(500, 500)  # 初始化窗体尺寸

for i in range(1, 11):  # 循环生成 10 个标签
    label = QLabel(window)  # 新建窗体内标签对象
    label.setText("标签" + str(i))  # 标签内容
    label.move(40*i, 40*i)  # 标签在窗体内的位置

window.show()  # 展示窗体

sys.exit(app.exec_())  # 循环运行应用对象

2.2.10 控件交互

from PyQt5.Qt import *
import sys

class Window(QWidget):  # 新建窗体类,原理同 window = QWidget()
    def paintEvent(self, evt):  # 绘制事件响应函数
        print("窗口被绘制了")
        return super().paintEvent(evt)

class Btn(QPushButton):  # 新建按钮类
    def paintEvent(self, evt):  # 绘制事件响应函数
        print("按钮被绘制了")
        return super().paintEvent(evt)

app = QApplication(sys.argv)  # 创建一个应用程序对象

window = Window()  # 类实例化
window.setWindowTitle("交互状态")  # 窗体标题
window.resize(500, 500)  # 初始化窗体尺寸

btn = Btn(window)  # 类实例化
btn.setText("按钮")  # 按钮内容
btn.destroyed.connect(lambda : print("按钮被释放了"))  # 事件与函数绑定,此处是按钮被释放事件

# btn.setVisible(True)  # 让按钮可见
# btn.setHidden(True)  # 让按钮不可见
# btn.hide()  # 隐藏按钮

# btn.deleteLater()  # 删除父类对象下的所有子对象
# btn.setAttribute(Qt.WA_DeleteOnClose, True)  # 设置控件属性,此处是释放按钮,详见下面参考链接
# btn.close()  # 关闭按钮控件

window.show()  # 展示窗体

sys.exit(app.exec_())  # 循环运行应用对象

参考:
Qt 窗口属性简介之Qt::WA_DeleteOnClose

2.2.11 【案例】简单登录界面

from PyQt5.Qt import *

class Window(QWidget):  # 新建窗体类,原理同 window = QWidget()
    def __init__(self):  # 类初始化函数
        super().__init__()  # 继承超类 QWidget
        self.setWindowTitle("简单登录界面")  # 窗体标题
        self.resize(500, 500)  # 初始化窗体尺寸
        self.setup_ui()  # 调用函数

    def setup_ui(self):
        # 添加三个子控件
        label = QLabel(self)  # 新建窗体内标签
        label.setText("请输入登录密码:")
        label.move(100, 50)

        le = QLineEdit(self)  # 新建窗体内输入框
        # le.setText("文本框")
        le.move(100, 100)

        btn = QPushButton(self)  # 新建窗体内按钮
        btn.setText("登录")
        btn.move(100, 150)
        btn.setEnabled(False)  # 设置按钮状态,此处是让按钮不可按

        def text_cao(text):
            print("文本内容发生了改变", text)
            btn.setEnabled(len(text) > 0)  # 让按钮可按
            if len(text) == 0:
                label.setText("请输入登录密码:")  # 复原标签内容
            if text == "2333":  # 密码正确直接登录
                label.setText("登录成功")
            else:
                pass
            label.show()  # 展示标签
            label.adjustSize()  # 自动调整标签尺寸

        def check():
            print("按钮被点击了")
            content = le.text()  # 获取文本框内容
            if content != "2333":
                label.setText("登录失败")
            else:
                pass
            label.show()  # 展示标签
            label.adjustSize()  # 自动调整标签尺寸

        le.textChanged.connect(text_cao)  # 事件与函数绑定,此处是文本被改变事件
        btn.pressed.connect(check)  # 事件与函数绑定,此处是按钮被按下事件
        btn.pressed.connect(check)  # 事件与函数绑定,此处是按钮被按下事件

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)  # 创建一个应用程序对象
    window = Window()  # 类实例化
    window.show()  # 展示窗体
    sys.exit(app.exec_())  # 循环运行应用对象

2.2.12 焦点控制

from PyQt5.Qt import *
import sys

class Window(QWidget):  # 新建窗体类,原理同 window = QWidget()
    def mousePressEvent(self, evt):  # 鼠标点击事件响应函数
        print(self.focusWidget())  # 查看当前焦点所在文本框
        # self.focusNextChild()  # 聚焦下一个子类控件
        # self.focusPreviousChild()  # 聚焦上一个子类控件
        self.focusNextPrevChild(True)  # 聚焦到上(False)/下(True)一个子类控件

app = QApplication(sys.argv)  # 创建一个应用程序对象
window = Window()  # 类实例化
window.setWindowTitle("焦点控制")  # 窗体标题
window.resize(500, 500)  # 窗体尺寸

le1 = QLineEdit(window)  # 新建窗体内文本框
le1.move(50, 50)  # 文本框在窗体内位置

le2 = QLineEdit(window)
le2.move(100, 100)

le3 = QLineEdit(window)
le3.move(150, 150)

QWidget.setTabOrder(le1, le2)  # 设置 Tab 键指令,实现文本框间跳转
QWidget.setTabOrder(le2, le3)

le1.setFocus()  # 对指定的控件设置焦点,此处为聚焦于文本框1
# 下面还不知道是干嘛用的
# le2.setFocusPolicy(Qt.TabFocus)
# le2.setFocusPolicy(Qt.ClickFocus)
# le2.setFocusPolicy(Qt.StrongFocus)  # 设置了 strongFocus 的控件会接收来自Tab键和鼠标点击的焦点
#
# le2.setFocus()
# le2.clearFocus()

window.show()  # 展示窗体

print(le1)  # 查看三个文本框信息
print(le2)
print(le3)
#
# le2.setFocus()
# le1.clearFocus()

sys.exit(app.exec_())   # 循环运行应用对象

在这里插入图片描述

2.2.13 事件响应函数

from PyQt5.Qt import *

class Window(QWidget):  # 新建窗体类,原理同 window = QWidget()
    def __init__(self):  # 类初始化函数
        super().__init__()  # 继承超类 QWidget
        self.setWindowTitle("事件消息的学习")  # 窗体标题
        self.resize(500, 500)  # 窗体尺寸
        self.setup_ui()  # 调用函数

    def setup_ui(self):
        pass

    def showEvent(self, QShowEvent):  # 控件展示事件响应函数
        print("窗口被展示了出来")

    def closeEvent(self, QCloseEvent):  # 控件关闭事件响应函数
        print("窗口被关闭了")

    def moveEvent(self, QMoveEvent):  # 控件移动事件响应函数
        print("窗口被移动了")

    def resizeEvent(self, QResizeEvent):  # 控件尺寸变化事件响应函数
        print("窗口改变了尺寸大小")

    def enterEvent(self, QEvent):  # 鼠标进入控件事件响应函数
        print("鼠标进来了")
        self.setStyleSheet("background-color: yellow;")

    def leaveEvent(self, QEvent):  # 鼠标离开控件事件响应函数
        print("鼠标移开了")
        self.setStyleSheet("background-color: green;")

    def mousePressEvent(self, QMouseEvent):  # 鼠标被按下事件响应函数
        print("鼠标被按下")

    def mouseReleaseEvent(self, QMouseEvent):  # 鼠标被释放事件响应函数
        print("鼠标被释放")

    def mouseDoubleClickEvent(self, QMouseEvent):  # 鼠标双击事件响应函数
        print("鼠标双击")

    def mouseMoveEvent(self, QMouseEvent):  # 鼠标移动事件响应函数
        print("鼠标移动了")

    def keyPressEvent(self, QKeyEvent):  # 键盘按下事件响应函数
        print("键盘上某一个按键被按下了")

    def keyReleaseEvent(self, QKeyEvent):  # 键盘松开事件响应函数
        print("键盘上某一个按键被释放了")

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)  # 创建一个应用程序对象
    window = Window()  # 类实例化
    window.show()  # 展示窗体
    sys.exit(app.exec_())  # 循环运行应用对象

在这里插入代码片

参考:
从Qt到PyQt - event()事件处理机制
QMainWindow
PyQt5 实现状态栏(statusBar)显示和隐藏功能

2.2.14 控件信息提示 [状态栏、help 按钮]

from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)  # 创建一个应用程序对象
# window = QWidget()
window = QMainWindow()  # 新建主应用程序窗口
window.setWindowTitle("信息提示案例")  # 窗体标题
window.resize(500, 500)  # 窗体尺寸
window.setWindowFlags(Qt.WindowContextHelpButtonHint)  # 设置窗口属性,此处是在窗体标题栏增加 help 按钮
window.statusBar()  # 新建状态栏
window.setStatusTip("您已进入本窗口")  # 添加状态栏提示文本,即当鼠标停留在窗口控件身上之后, 在状态栏提示的一段文本
print(window.statusTip())

label = QLabel(window)  # 新建窗体内标签
label.setText("sb")  # 标签内容
label.setStatusTip("这是标签")  # 标签状态栏提示,鼠标停留在此处,状态栏有对应提示
label.setToolTip("这是一个提示标签")  # 标签工具提示,此处是鼠标悬停一段时间会有提示信息出现
# label.setToolTipDuration(2000)  # 控件的工具提示停留时间,单位毫秒,此处是鼠标悬停在标签处后出现提示信息 2 秒,然后消失
print(label.toolTip())
print(label.toolTipDuration())

label.setWhatsThis("这是标签1111")  # 标签 help 按钮提示

window.show()  # 展示窗体
sys.exit(app.exec_())  # 循环运行应用对象

在这里插入图片描述

2.2.15 窗体尺寸控制

from PyQt5.Qt import * 
import sys

app = QApplication(sys.argv)  # 创建一个应用程序对象
window = QWidget()  # 新建窗体控件
window.setWindowTitle("最小尺寸最大尺寸限定")  # 窗体标题
# window.resize(500, 500)  # 窗体尺寸
# window.setFixedSize(500, 500)  # 窗体固定尺寸
window.setMinimumSize(200, 200)  # 窗体最小尺寸
window.setMaximumSize(500, 500)  # 窗体最大尺寸
# window.setMinimumWidth(500)  # 窗体最小宽度
# window.setMaximumWidth(800)  # 窗体最大宽度
# window.setMinimumHeight(800)  # 窗体最大高度
# window.setMaximumHeight(800)  # 窗体最大高度
window.show()  # 展示窗体
sys.exit(app.exec_())  # 循环运行应用对象

2.2.16 各种按钮功能测试

from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)  # 创建一个应用程序对象
window = QWidget()  # 新建窗体对象
window.setWindowTitle("按钮的功能测试-抽象类")  # 窗体标题
window.resize(500, 500)  # 窗体尺寸

btn = QPushButton(window)  # 新建窗体内按钮
btn.move(300, 200)
# *************文本操作***************开始
def plus_one():
    print("加一")
    num = int(btn.text()) + 1
    btn.setText(str(num))

btn.setText("1")  # 按钮内容
btn.pressed.connect(plus_one)  # 事件与函数绑定,此处是按钮按下事件
# *************文本操作***************结束

# *************图标操作***************开始
icon = QIcon("xxx.png")  # 新建图标对象
btn.setIcon(icon)  # 设置按钮图标
size = QSize(20, 20)
btn.setIconSize(size)  # 设置按钮图标大小
print(btn.icon())
print(btn.iconSize())
# *************图标操作***************结束

# *************快捷键的设定***************开始
btn.setShortcut("Alt+c")
# *************快捷键的设定***************结束

# *************自动重复***************开始
btn.setAutoRepeat(True)  # 对象自动重复,此处是长按按钮实现重复按下按钮
btn.setAutoRepeatDelay(200)  # 重复延时
btn.setAutoRepeatInterval(100)  # 重复间隔
print(btn.autoRepeat())
print(btn.autoRepeatInterval())
print(btn.autoRepeatDelay())
# *************自动重复***************结束

# *************按钮状态***************开始
push_button = QPushButton(window)  # 新建窗体内按钮
push_button.setText("这是QPushButton")  # 按钮内容
push_button.move(300, 100)  # 按钮在窗体内的位置

radio_button = QRadioButton(window)
radio_button.setText("这是一个radio")
radio_button.move(100, 150)

checkbox = QCheckBox(window)
checkbox.setText("这是checkbox")
checkbox.move(100, 200)

push_button.setStyleSheet("QPushButton:pressed {background-color: red;}")  # 窗体样式

# 把三个按钮, 置为按下状态
push_button.setDown(True)
radio_button.setDown(True)
checkbox.setDown(True)

push_button.setCheckable(True)  # 设定按钮为可以选择
print(push_button.isCheckable())
print(radio_button.isCheckable())
print(checkbox.isCheckable())

# 设定按钮为选定状态
radio_button.setChecked(True)
push_button.setChecked(True)
checkbox.setChecked(True)

print(push_button.isChecked())
print(radio_button.isChecked())
print(checkbox.isChecked())

def cao():
    push_button.toggle()  # 让按钮处于选定/不选定状态
    radio_button.toggle()
    checkbox.toggle()
    push_button.setChecked(not push_button.isChecked())

btn.pressed.connect(cao)  # 事件与函数绑定,此处是按钮按下事件


push_button.setEnabled(True)
radio_button.setEnabled(True)
checkbox.setEnabled(True)
# *************按钮状态***************结束

# 2.3 展示控件
# *************排他性设置***************开始
for i in range(0, 3):
    btn5 = QCheckBox(window)
    btn5.setText("btn" + str(i))
    btn5.move(50 * i, 50 * i)

    btn5.setAutoExclusive(True)  # 设置排他性,即三个按钮只能选其一
    print(btn5.autoExclusive())
    print(btn5.isCheckable())
    btn5.setCheckable(True)
# *************排他性设置***************结束

# *************按钮模拟点击***************开始
btn9 = QPushButton(window)
btn9.setText("这是按钮")
btn9.move(200, 200)
btn9.pressed.connect(lambda :print("点击了这个按钮"))
btn9.click()
btn9.animateClick(2000)
# *************按钮模拟点击***************结束

class Btn(QPushButton):
    def hitButton(self, point):  # 按钮按下事件响应函数
        import math
        print(point)
        if point.x() > self.width() / 2:
            return True
        return False

        # 通过给定的一个点坐标, 计算与圆心的距离
        yuanxin_x = self.width() / 2
        yuanxin_y = self.height() / 2

        hit_x = point.x()
        hit_y = point.y()

        # ((x1 - x2) 平方 + (y1 - y2) 平方) 开平方
        distance = math.sqrt(math.pow(hit_x - yuanxin_x, 2) + math.pow(hit_y - yuanxin_y, 2))
        if distance < self.width() / 2:
            return True
        # 如果距离 < 半径  True
        # 返回 False
        return False

    def paintEvent(self, evt):  # 绘制事件响应函数
        super().paintEvent(evt)
        painter = QPainter(self)
        painter.setPen(QPen(QColor(100, 150, 200), 6))
        painter.drawEllipse(self.rect())

btn0 = Btn(window)  # 类实例化
btn0.move(200, 300)  # 按钮在窗体内的位置
btn0.setText("点击")  #按钮内容
btn0.resize(200, 200)
btn.setCheckable(True)
btn.pressed.connect(lambda : print("按钮被按下了"))
btn.released.connect(lambda : print("按钮鼠标被释放了"))
btn.clicked.connect(lambda value: print("按钮被点击", value))
btn0.toggled.connect(lambda value: print("按钮选中状态发生了改变", value))

window.show()  # 窗体展示
sys.exit(app.exec_())  # 循环运行应用对象

参考:
Qt5 Python GUI 编程指南

2.2.17 绘制事件

from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)  # 创建一个应用程序对象
window = QWidget()  # 新建窗体控件
window.setWindowTitle("QAbstractButton")  # 窗体标题
window.resize(300, 200)  # 窗体尺寸

class Btn(QAbstractButton):
    def paintEvent(self, evt):  # 绘制事件响应函数
        painter = QPainter(self)  # 新建画布对象
        pen = QPen(QColor(111, 200, 20), 2)  # 绘制几何图形边缘,(颜色,线粗)
        painter.setPen(pen)  # 在画布上进行绘制
        painter.drawText(25, 40, self.text())  # 绘制按钮内容
        painter.drawEllipse(0, 0, 50, 100)  # 绘制椭圆,(x坐标,y坐标,x长,y长)

btn = Btn(window)  # 新建窗体按钮
btn.setText("xxx")
btn.resize(200, 200)
btn.pressed.connect(lambda : print("点击了这个按钮"))

window.show()  # 展示窗体
sys.exit(app.exec_())  # 循环运行应用对象

在这里插入图片描述

参考:
QT-QPainter介绍

2.2.18 右击菜单和按钮下拉菜单

from PyQt5.Qt import *
import sys

class Window(QWidget):
    def contextMenuEvent(self, evt):  # 右键菜单事件
        print("默认上下文菜单调用这个方法")
        menu = QMenu(self)

        # 子菜单 最近打开

        open_recent_menu = QMenu(menu)
        open_recent_menu.setTitle("最近打开")
        open_recent_menu.setIcon()

        # 行为动作 新建  打开  分割线 退出
        new_action = QAction()
        new_action.setText("新建")
        new_action.setIcon(QIcon("xxx.png"))
        new_action = QAction(QIcon("xxx.png"), "新建", menu)
        new_action.triggered.connect(lambda: print("新建文件"))

        open_action = QAction(QIcon("xxx.png"), "打开", menu)
        open_action.triggered.connect(lambda: print("打开文件"))

        exit_action = QAction("退出", menu)
        exit_action.triggered.connect(lambda: print("退出程序"))

        file_action = QAction("Python-GUI编程-PyQt5")

        menu.addAction(new_action)
        menu.addAction(open_action)
        open_recent_menu.addAction(file_action)
        menu.addMenu(open_recent_menu)
        menu.addSeparator()
        menu.addAction(exit_action)

        # point
        menu.exec_(evt.globalPos())
        # menu.exec_(evt.pos())

app = QApplication(sys.argv)  # 创建一个应用程序对象
window = Window()  # 类实例化
window.setWindowTitle("按钮的功能")  # 窗体标题
window.resize(500, 500)  # 窗体尺寸

btn = QPushButton(window)  # 新建窗体内按钮对象
btn.setParent(window)  # 指定父类
btn.setText("xxx")  # 标题内容
btn.setIcon(QIcon("xxx.png"))  # 按钮图标

# *************菜单的设置***************开始
menu = QMenu(window)  # 新建窗体内菜单对象

# 子菜单 最近打开
open_recent_menu = QMenu(menu)  # 新建子菜单对象
open_recent_menu.setTitle("最近打开")  # 子菜单内容
open_recent_menu.setIcon(QIcon("xxx.png"))  # 子菜单图标

# 行为动作 新建  打开  分割线 退出
# new_action = QAction()  # 新建菜单动作对象
# new_action.setText("新建")  # 动作内容
# new_action.setIcon(QIcon("xxx.png"))  # 动作图标
new_action = QAction(QIcon("xxx.png"), "新建", menu)  # 新建菜单动作对象、内容、图标
new_action.triggered.connect(lambda :print("新建文件"))  # 动作与函数绑定

open_action = QAction(QIcon("xxx.png"), "打开", menu)
open_action.triggered.connect(lambda :print("打开文件"))

exit_action = QAction("退出", menu)
exit_action.triggered.connect(lambda :print("退出程序"))

file_action = QAction("Python-GUI编程-PyQt5")  # 新建菜单对象

# 把各菜单动作对象添加到菜单或子菜单
menu.addAction(new_action)
menu.addAction(open_action)
open_recent_menu.addAction(file_action)
menu.addMenu(open_recent_menu)
menu.addSeparator()
menu.addAction(exit_action)

btn.setMenu(menu)  # 给按钮添加下拉菜单
print(btn.menu())  # 查看按钮是否有菜单
# btn.setStyleSheet("background-color: red;")  # 按钮样式,按下有下陷且为红色
btn.setStyleSheet("QPushButton border:none;")  # 按钮样式,无边框,按下无下陷
btn.setFlat(True)  # 取消按钮突出效果
print(btn.isFlat())

# *************菜单的设置***************结束
btn2 = QPushButton(window)  # 新建窗体内按钮
btn2.setText("btn2")  # 按钮内容
btn2.move(200, 200)  # 按钮尺寸

btn2.setAutoDefault(True)  # 设置按钮选中(焦点所在)时样式,True:显示蓝色边框,False:无
print(btn.autoDefault())
print(btn2.autoDefault())
# btn2.setDefault(True) # 同 setAutoDefault(),区别详见下面参考链接

def show_menu(point):
    menu = QMenu(window)  # 新建窗体内菜单对象

    open_recent_menu = QMenu(menu)  # 新建子菜单对象
    open_recent_menu.setTitle("最近打开")  # 子菜单内容
    # open_recent_menu.setIcon()  # 子菜单图标

    # 这是菜单动作 新建  打开  分割线 退出
    # new_action = QAction()
    # new_action.setText("新建")
    # new_action.setIcon(QIcon("xxx.png"))
    new_action = QAction(QIcon("xxx.png"), "新建", menu)
    new_action.triggered.connect(lambda: print("新建文件"))

    open_action = QAction(QIcon("xxx.png"), "打开", menu)
    open_action.triggered.connect(lambda: print("打开文件"))

    exit_action = QAction("退出", menu)
    exit_action.triggered.connect(lambda: print("退出程序"))

    file_action = QAction("Python-GUI编程-PyQt5")

    menu.addAction(new_action)
    menu.addAction(open_action)
    open_recent_menu.addAction(file_action)
    menu.addMenu(open_recent_menu)
    menu.addSeparator()
    menu.addAction(exit_action)

    # point
    dest_point = window.mapToGlobal(point)
    menu.exec_(dest_point)

window.setContextMenuPolicy(Qt.CustomContextMenu)  # 给窗体添加又见菜单
window.customContextMenuRequested.connect(show_menu)  # 窗体与函数绑定,此处是绑定显示菜单

btn.show()  # 展示按钮
window.show()  # 展示窗体
btn.showMenu()  # 展示按钮菜单
sys.exit(app.exec_())  # 循环运行应用对象

参考:
QPushButton 之 default、autoDefault 分析

在这里插入图片描述

2.2.19 工具按钮

from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)  # 创建一个应用程序对象
window = QWidget()  # 新建窗体对象
window.setWindowTitle("QToolButton使用")  # 窗体标题
window.resize(500, 500)  # 窗体尺寸

tb = QToolButton(window)  # 新建窗体内工具按钮
tb.setText("工具按钮")
tb.setIcon(QIcon("xxx.png"))
tb.setIconSize(QSize(30, 30))  # 按钮图标尺寸
tb.setToolTip("这是一个新建按钮")  # 标签工具提示
# Qt.ToolButtonIconOnly
# 	仅显示图标
# Qt.ToolButtonTextOnly
# 	仅显示文字
# Qt.ToolButtonTextBesideIcon
# 	文本显示在图标旁边
# Qt.ToolButtonTextUnderIcon
# 	文本显示在图标下方
# Qt.ToolButtonFollowStyle
# 	遵循风格
tb.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)  # 图标按钮风格
tb.setAutoRaise(True)  # 设置图标浮动,True:浮动,无边框

menu = QMenu(tb)  # 新建按钮内菜单对象
menu.setTitle("菜单")  # 菜单标题

sub_menu = QMenu(menu)  # 新建子菜单
sub_menu.setTitle("子菜单")  # 菜单标题
sub_menu.setIcon(QIcon("xxx.png"))  # 设置图标

action1 = QAction(QIcon("xxx.png"), "行为1", menu)  # 新建菜单动作
action1.setData([1, 2, 3])  # 设置动作数据
action2 = QAction("行为2", menu)  # 新建菜单动作
action2.setData({"name": "sz"})  # 设置动作数据
action1.triggered.connect(lambda :print("点击了行为菜单选项"))  # 动作一与函数绑定

# 菜单配置
menu.addMenu(sub_menu)
menu.addSeparator()
menu.addAction(action1)
menu.addAction(action2)

tb.clicked.connect(lambda :print("工具按钮被点击了"))

tb.setMenu(menu)  # 按钮添加菜单

tb.setPopupMode(QToolButton.MenuButtonPopup)  # 设置弹出模式,详见下面链接

def do_action(action):
    print("点击了行为", action.data())

tb.triggered.connect(do_action)  # 事件与函数绑定,此处是按钮被点击事件

btn = QPushButton(window)  # 新建一个普通的按下按钮
btn.setText("一般按钮")  # 按钮内容
btn.move(100, 100)  # 按钮尺寸
btn.setFlat(True)  # 按钮突出效果
btn.setMenu(menu)  # 按钮添加菜单

# Qt.NoArrow
# 	无箭头
# Qt.UpArrow
# 	向上箭头
# Qt.DownArrow
# 	向下箭头
# Qt.LeftArrow
# 	向左箭头
# Qt.RightArrow
# 	向右箭头
tb.setArrowType(Qt.RightArrow)  # 选择箭头风格

window.show()  # 展示窗体
sys.exit(app.exec_())  # 循环运行应用对象

参考:
QToolButton

在这里插入图片描述

2.2.20 radio 选中按钮

from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)  # 创建一个应用程序对象

window = QWidget()  # 创建窗体对象
window.setWindowTitle("QRadioButton-功能测试")  # 窗体标题
window.resize(500, 500)  # 窗体尺寸

red = QWidget(window)  # 新建子窗体
red.resize(200, 200)  # 窗体尺寸
red.setStyleSheet("background-color: red;")  # 窗体风格
red.move(50, 50)  # 窗体位置

green = QWidget(window)
green.resize(200, 200)
green.setStyleSheet("background-color: green;")
green.move(red.x() + red.width(), red.y() + red.height())

rb_nan = QRadioButton("男-&Male", red)  # 创建 Radio 选中按钮,其中的 & 可实现按下 Alt 时显示首字母,并实现 Alt+M 的快捷键功能
# rb_nan.setShortcut("Alt+M")  # 关联快捷键
rb_nan.move(10, 10)  # 按钮位置
rb_nan.setChecked(True)  # 设定按钮为选定状态

rb_nv = QRadioButton("女-&Female", red)
rb_nv.move(10, 50)
rb_nv.setIcon(QIcon("xxx.png"))
rb_nv.setIconSize(QSize(20, 20))
rb_nv.toggled.connect(lambda isChecked: print(isChecked))  # 查看按钮是否为选定状态

rb_nv.setAutoExclusive(True)  # 设置排他性,同一窗体内,radio 选中按钮只能选其一

rb_yes = QRadioButton("yes", green)
rb_yes.move(10, 10)
rb_no = QRadioButton("no", green)
rb_no.move(10, 50)

window.show()  # 展示窗体
sys.exit(app.exec_())  # 循环运行应用对象

在这里插入图片描述

2.2.21 按钮组设定与管理

from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)  # 创建一个应用程序对象
window = QWidget()  # 新建窗体对象
window.setWindowTitle("按钮组的使用")
window.resize(500, 500)

# ------------ 创建四个单选按钮 开始 ------------
# 12 男女
r_male = QRadioButton("男", window)
r_female = QRadioButton("女", window)
r_male.move(100, 100)
r_female.move(100, 150)
r_male.setChecked(True)  # 设定按钮为选定状态

sex_group = QButtonGroup(window)  # 新建窗体按钮组
sex_group.addButton(r_male, 1)  # 往组添加元素
sex_group.addButton(r_female, 2)

# 34 是否
r_yes = QRadioButton("是", window)
r_no = QRadioButton("否", window)
r_yes.move(300, 100)
r_no.move(300, 150)

answer_group = QButtonGroup(window)  # 新建窗体按钮组
answer_group.addButton(r_yes)  # 往组添加元素
answer_group.addButton(r_no)

answer_group.setId(r_yes, 1)  # 设置组内元素 ID
answer_group.setId(r_no, 2)
# ------------ 创建四个单选按钮 结束------------

print(answer_group.id(r_yes))  # 查看组内元素 ID
print(answer_group.id(r_no))
r_no.setChecked(True)
print(answer_group.checkedId())  # 查看 ID 数量

sex_group.setExclusive(True)  # 设置排他性
# sex_group.removeButton(r_female)  # 从组内移除元素
print(sex_group.buttons())  # 查看组内按钮元素
print(sex_group.button(2))  # 查看组内第二个按钮元素
print(sex_group.checkedButton())  # 查看组内元素

def test(val):
    # print(val)
    print(sex_group.id(val))  # 查看组内元素 ID

sex_group.buttonClicked.connect(test)  # 事件与函数绑定,此处是按钮组被点击事件
# sex_group.buttonClicked[int].connect(test)  # 事件与函数绑定,此处是按钮组中某个按钮被点击事件

window.show()  # 展示控件
sys.exit(app.exec_())  # 循环运行应用对象

2.2.22 选中框

from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)  # 创建一个应用程序对象
window = QWidget()  # 新建窗体对象
window.setWindowTitle("QCheckBox 功能测试")  # 窗体标题
window.resize(500, 500)  # 窗体尺寸

# print(QCheckBox.__bases__)

cb = QCheckBox("&Python", window)  # 新建窗体内选中框
cb.setIcon(QIcon("xxx.png"))  # 选中框图标
cb.setIconSize(QSize(60, 60))  # 图标尺寸
cb.setTristate(True)  # 打开选中框三态支持,详见下面参考链接
cb.setChecked(True)  # 设定按钮为选定状态
cb.setCheckState(Qt.PartiallyChecked)  # 查看选中框状态,此处是查看是否为 PartiallyChecked 状态
cb.setCheckState(Qt.Checked)

# cb.stateChanged.connect(lambda state: print(state))  # 事件于函数绑定,此处是状态改变事件
cb.toggled.connect(lambda isChecked: print(isChecked))  # 事件于函数绑定,此处是是否为选中状态事件

window.show()  # 展示窗体
sys.exit(app.exec_())  # 循环运行应用对象

在这里插入图片描述

参考;
Qt之QCheckBox

2.2.23 输入框

from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)  # 创建一个应用程序对象
window = QWidget()  # 新建窗体对象
window.setWindowTitle("QLineEdit功能测试")  # 窗体标题
window.resize(500, 500)  # 窗体尺寸

le_a = QLineEdit(window)  # 新建窗口内输入框
le_a.move(100, 200)

le_b = QLineEdit(window)
le_b.move(100, 300)

le_b.setEchoMode(QLineEdit.PasswordEchoOnEdit)  # 设置文本框的回显模式,详见下面参考链接
print(le_b.echoMode())  # 查看文本框的回显模式

copy_btn = QPushButton(window)  # 新建窗体内按钮
copy_btn.setText("复制")
copy_btn.move(100, 400)

def copy_cao():
    content = le_a.text()  # 获取文本框a, 内容
    # 2. 把上面获取到的内容, 设置到文本框B里面
    le_b.setText(content)
    le_b.setText("")
    le_b.insert(content)  # 文本框插入内容
    print(le_b.text())
    print(le_b.displayText())
    print(le_b.isModified())
    le_b.setModified(False)  # 设置修改状态

copy_btn.clicked.connect(copy_cao)  # 事件与函数绑定,此处是按钮被点击事件

le_a.setMaxLength(3)  # 最大长度限制
print(le_a.maxLength())
le_a.setReadOnly(False)  # 设置为只读
le_a.setText("2333")  # 输入框内容

# le_b 设置掩码
# 总共输入5 位  左边 2(必须是大写字母) - 右边 2(必须是一个数字)
# le_b.setInputMask(">AA-99;#")
# le_b.setInputMask("9999-9999999;0")

window.show()  # 展示窗体
sys.exit(app.exec_())  # 循环运行应用对象

在这里插入图片描述

参考:
setEchoMode()设置模式

2.2.24 输入框内容编辑

from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)  # 创建一个应用程序对象
window = QWidget()  # 新建窗体
window.setWindowTitle("QLineEdit-功能测试")  # 窗体标题
window.resize(500, 500)  # 窗体尺寸

le = QLineEdit(window)  # 新建窗体内输入框
le.move(100, 100)  # 输入框位置
le.resize(300, 300)  # 输入框尺寸
le.setContentsMargins(100, 0, 0, 0)  # 设置左侧、顶部、右侧和底部边距,以便在布局周围使用
le.setStyleSheet("background-color: cyan;")  # 输入框样式
le.setTextMargins(0, 0, 50, 50)  # 设置左侧、顶部、右侧和底部边距,以便在布局周围使用
le.setAlignment(Qt.AlignRight | Qt.AlignBottom)  # 消除布局中的空隙,详见下面参考链接
le.setDragEnabled(True)  # 使能拖放,详见下面参考链接

le2 = QLineEdit(window)  # 新建窗体内输入框
le2.resize(50, 50)  # 窗体尺寸
le2.move(200, 0)  # 窗体位置

btn = QPushButton(window)  # 新建按钮
btn.setText("按钮")  # 按钮内容
btn.move(50, 50)  # 按钮位置
def cursor_move():
    # le.cursorBackward(True, 2)  # 光标后退
    # le.cursorForward(True, 3)  # 光标前进
    # le.cursorWordBackward(True)  # 光标后退一词
    # le.cursorWordForward(True)  # 光标前进一词
    # le.home(True)  # 光标跳至最前端
    # le.end(False)  # 光标跳至最末端
    le.setCursorPosition(len(le.text()) / 2)  # 设置光标位置
    print(le.cursorPosition())
    print(le.cursorPositionAt(QPoint(55, 105)))
    le.setText("sb "*10)
    le.setFocus()  # 对指定的控件设置焦点
    le.backspace()  # 光标处按下退格
    le.del_()  # 光标处按下 delete
    le.clear()  # 清除光标处内容
    le.copy()  # 光标处复制内容
    le.cut()  # 光标处剪切内容
    le.paste()  # 光标处粘贴内容

    le.setSelection(2, 21)  # 选中指定区域内的文本
    le.selectAll()  # 选定所有文本
    le.setSelection(0, len(le.text()))
    le.deselect()  # 取消选中任何已选中的文本
    print(le.hasSelectedText())
    print(le.selectedText())
    print(le.selectionStart())
    print(le.selectionEnd())
    print(le.selectionLength())
    pass

btn.clicked.connect(cursor_move)  # 事件与函数绑定,此处是按钮被点击事件

le.textEdited.connect(lambda val: print("文本框编辑的时候", val))  # 事件与函数绑定,此处是文本被编辑事件
le.textChanged.connect(lambda val: print("文本框内容发生改变", val))  # 事件与函数绑定,此处是文本改变事件

le.returnPressed.connect(lambda :print("回车键被按下"))  # 事件与函数绑定,此处是回车被按下事件
le.returnPressed.connect(lambda :le2.setFocus())  # 事件与函数绑定,此处是回车被按下事件
le.editingFinished.connect(lambda :print("结束编辑"))  # 事件与函数绑定,此处是编辑结束事件

le.cursorPositionChanged.connect(lambda old_Pos, new_Pos: print(old_Pos, new_Pos))  # 事件与函数绑定,此处是文本框光标移动事件
le.selectionChanged.connect(lambda : print("选中文本发生改变", le.selectedText()))  # 事件与函数绑定,此处是当前选中区域发生变化事件

le.setText("xxx")

window.show()  # 展示窗体
sys.exit(app.exec_())  # 循环运行应用对象

参考:
Qt5.9中函数setAlignment()用法
Qt之拖放
Qt之QLineEdit详解(附源码)
视图中选择选中项的操作
Qt——QLineEdit使用总结
控制QLineEdit的输入范围


参考:
QT+SIP+Pyqt5
收藏 - PyQt5图形界面编程
C语言中文网 - qt
pyqt中文网
python GUI编程

3 pyautogui 库 – GUI 自动化


3.1 防故障机制

为了防止 GUI 程序不受控制,在代码中添加 pyautogui.PAUSE 来让 GUI 控制暂停运行,而在 GUI 控制过程中,可启动自动防故障功能,启动该功能后,GUI 控制运行过程中将鼠标移到屏幕的左上角,程序将抛出异常而崩溃,而禁止其抛出异常的方法是加入 pyautogui.FAILSAFE = Faile

应保证在 GUI 自动控制中都启动自动放故障功能,防止 GUI 发生失控!

import pyautogui
pyautogui.PAUSE = 1 # GUI 控制暂停1s
pyautogui.FAILSAFE = True # 启动自动防故障功能

3.2 屏幕操作

计算机屏幕都以左上角为坐标原点,所有坐标都是正整数,没有负坐标;屏幕右下角为坐标最大值,其坐标值 (x,y) 根据屏幕的分辨率而定,如果屏幕分辨率为 1920*1080 ,则屏幕右下角坐标就是 (1919,1079);

获取屏幕分辨率

  • pyautogui.size() 返回一个列表,包含当前屏幕的像素
# 获取屏幕分辨率
import pyautogui
width,height = pyautogui.size() # 获取当前屏幕的像素
print(pyautogui.size())  # 返回一个包含屏幕宽高的元组
print(width)
print(height)
运行结果:
Size(width=1920, height=1080)
1920
1080

获取屏幕快照

  • pyautogui.screenshot() 返回 Image 对象,可向可选参数 region 传入一个元组进行区域截图,元组内容应包括截图区域的 (x,y,宽度,高度),其中 x,y 为截图的起始坐标;
  • pyautogui.screenshot() .getpixel() 返回 Image 对象中该坐标的像素颜色元组;
import pyautogui
im = pyautogui.screenshot() # 创建 Image 对象
print(im.getpixel((0,0))) # 返回 Image 对象该坐标下的 RGB 像素颜色参数元组
运行结果:
(255, 255, 255)

分析屏幕快照

  • pyautogui.pixelMatchesColor() 返回该坐标下的颜色是否符合输入的像素颜色参数
import pyautogui
print(pyautogui.pixelMatchesColor(0,0,(255,255,255)))
运行结果:
True

3.3 控制鼠标

移动鼠标

  • pyautogui.moveTo() 方法可实现鼠标的定点移动,可选的 duration 参数制定了鼠标移动到目标位置所需的秒数,默认值为 0;
  • pyautogui.moveRel() 方法相对于当前鼠标的位置而移动鼠标;
import pyautogui
pyautogui.FAILSAFE = True # 启动自动防故障功能
pyautogui.moveTo(100, 200, duration=0.25)
pyautogui.moveRel(100,0) 

获取鼠标位置

  • pyautogui.position() 可获得当前鼠标的位置坐标;
# 实时获取鼠标坐标脚本
import pyautogui, time
while 1:
	print(pyautogui.position())
	time.sleep(0.5)

鼠标点击

  • pyautogui.click() 方法可实现鼠标的定点点击,可选关键字参数 botton= 可指定鼠标的按键,包括 left,middle,right 三个参数;
  • pyautogui.doubleClick() 实现双击;
  • pyautogui.rightClick() 实现右击;(完全可以用 click 代替)
  • pyautogui.middleClick() 实现鼠标中键点击;(完全可以用 click 代替)
import pyautogui
pyautogui.click(14,1043,button='left')
pyautogui.click(14,1043,button='right')
pyautogui.doubleClick(412,391)
pyautogui.rightClick(412,391)
pyautogui.middleClick(412,391)

拖动鼠标

  • pyautogui.drag() 拖动鼠标到定点坐标;
  • pyautogui.dragRel() 相对于当前鼠标的位置而拖动鼠标;
import pyautogui
pyautogui.click()
distance = 200
while distance > 0:
	pyautogui.dragRel(distance,0,duration=0.2) # move right
	distance -= 5
	pyautogui.dragRel(0,distance,duration=0.2) # move down
    pyautogui.dragRel(-distance,0,duration=0.2) # move left
	distance -= 5
	pyautogui.dragRel(0,-distance,duration=0.2) # move up

滚动鼠标

  • pyautogui.scroll() 实现鼠标向上滚动;
import pyautogui
pyautogui.scroll(200) # 鼠标向上滚动200

3.4 图像识别

识别图片在屏幕上的位置

  • pyautogui.locateOnScreen() 传入一个图像,返回该图像在当前屏幕上的坐标元组;
  • pyautogui.locateCenterOnScreen() 返回区域的中心坐标
  • pyautogui.center() 相当于以上两者的结合
  • pyautogui.locateAllOnScreen() 定位该图片的所有坐标
import pyautogui, os
os.chdir('C:\\Users\\xxxx\\Desktop')
screenShot = pyautogui.locateOnScreen('20190829084103.png')
print(screenShot)
print(pyautogui.center(screenShot)) # 返回该区域的中心坐标
print(pyautogui.locateCenterOnScreen('20190829084103.png'))
for pos in pyautogui.locateAllOnScreen('20190829084103.png'): # 定位该图片的所有位置
	print(pos)
print(list(pyautogui.locateAllOnScreen('20190829084103.png'))) 
运行结果:
Box(left=100, top=640, width=25, height=30)
Point(x=112, y=655)
Point(x=112, y=655)
Box(left=100, top=640, width=25, height=30)
Box(left=1316, top=910, width=25, height=30)
[Box(left=100, top=640, width=25, height=30), Box(left=1316, top=910, width=25, height=30)]

3.5 键盘输入

  • pyautogui.typewrite() 通过键盘发送一个字符串;
  • pyautogui.keyDown() 模拟键盘按下不松
  • pyautogui.press() 模拟敲击键盘
  • pyautogui.keyUp() 模拟松开键盘
  • pyautogui.hotkey() 相当于keyDownkeyUp的组合,可接受多个键字符串参数,按顺序模拟按下,再按反顺序释放;
import pyautogui
pyautogui.click()
pyautogui.typewrite('Hellow world!', interval=0.5) # 每个字符的输入间隔为0.5s
pyautogui.keyDown('shift') # 键盘按下不松
pyautogui.press('4') # 敲击键盘
pyautogui.keyUp('shift') # 松开键盘,结合上面两行代码,将模拟键盘输入 $
pyautogui.hotkey('ctrl','c') # 热键组合

KEYBOARD_KEYS大全(可传入到上述函数的键值)

print(pyautogui.KEYBOARD_KEYS) # 查看模块可接受的所有可能的键字符串

运行结果: [’\t’, ‘\n’, ‘\r’, ’ ‘, ‘!’, ‘"’, ‘#’, ‘$’, ‘%’, ‘&’, "’", ‘(’, ‘)’, ‘*’, ‘+’, ‘,’, ‘-’, ‘.’, ‘/’, ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’,
‘7’, ‘8’, ‘9’, ‘:’, ‘;’, ‘<’, ‘=’, ‘>’, ‘?’, ‘@’, ‘[’, ‘\’, ‘]’, ‘^’, ‘_’, ‘`’, ‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’, ‘j’, ‘k’, ‘l’,
‘m’, ‘n’, ‘o’, ‘p’, ‘q’, ‘r’, ‘s’, ‘t’, ‘u’, ‘v’, ‘w’, ‘x’, ‘y’, ‘z’,
‘{’, ‘|’, ‘}’, ‘~’, ‘accept’, ‘add’, ‘alt’, ‘altleft’, ‘altright’, ‘apps’, ‘backspace’, ‘browserback’, ‘browserfavorites’,
‘browserforward’, ‘browserhome’, ‘browserrefresh’, ‘browsersearch’,
‘browserstop’, ‘capslock’, ‘clear’, ‘convert’, ‘ctrl’, ‘ctrlleft’, ‘ctrlright’, ‘decimal’, ‘del’, ‘delete’, ‘divide’, ‘down’, ‘end’,
‘enter’, ‘esc’, ‘escape’, ‘execute’, ‘f1’, ‘f10’, ‘f11’, ‘f12’, ‘f13’, ‘f14’, ‘f15’, ‘f16’, ‘f17’, ‘f18’, ‘f19’, ‘f2’, ‘f20’, ‘f21’, ‘f22’,
‘f23’, ‘f24’, ‘f3’, ‘f4’, ‘f5’, ‘f6’, ‘f7’, ‘f8’, ‘f9’, ‘final’, ‘fn’,
‘hanguel’, ‘hangul’, ‘hanja’, ‘help’, ‘home’, ‘insert’, ‘junja’, ‘kana’, ‘kanji’, ‘launchapp1’, ‘launchapp2’, ‘launchmail’,
‘launchmediaselect’, ‘left’, ‘modechange’, ‘multiply’, ‘nexttrack’, ‘nonconvert’, ‘num0’, ‘num1’, ‘num2’, ‘num3’, ‘num4’, ‘num5’, ‘num6’,
‘num7’, ‘num8’, ‘num9’, ‘numlock’, ‘pagedown’, ‘pageup’, ‘pause’, ‘pgdn’, ‘pgup’, ‘playpause’, ‘prevtrack’, ‘print’, ‘printscreen’,
‘prntscrn’, ‘prtsc’, ‘prtscr’, ‘return’, ‘right’, ‘scrolllock’, ‘select’, ‘separator’, ‘shift’, ‘shiftleft’, ‘shiftright’, ‘sleep’,
‘space’, ‘stop’, ‘subtract’, ‘tab’, ‘up’, ‘volumedown’, ‘volumemute’, ‘volumeup’, ‘win’, ‘winleft’, ‘winright’, ‘yen’, ‘command’, ‘option’, ‘optionleft’, ‘optionright’]

你可能感兴趣的:(Python,python,pyqt5,gui,可视化)