7 - 可视化控件基类QWidget

7 - QWidget

QWidget VS QObject

QObject不是可视化控件,是所有QT对象的基类。

Qwidget则是所有可视化控件的基类。

(一)描述

  1. 所有的可视控件基类(仅有全部可视控件的共性,比如是个方块,可调整大小,移动位置等等);

  2. 是一个最简单空白控件;

  3. 控件是用户界面的最小元素

    功能:接收各种事件(鼠标、键盘…),绘制在桌面上,展示给用户看。

  4. 每个控件都是矩形的,它们按Z轴顺序排序(Z轴面向人,后面的会被前面的遮挡,后面的是父控件);

    7 - 可视化控件基类QWidget_第1张图片

  5. 控件由其父控件和前面的控件剪切

  6. 没有父控件的控件,称之为窗口

    一般会被包装一个框架(标题栏、图标…),可以通过某些设置更改,窗口不能自动显示,必须调用show()方法。

例1:看一下QWidegt的空白样子。

# 0.导入包和模块
from PyQt5.Qt import *
import sys

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

window = QWidget()
window.show()

# 3.进入消息循环
sys.exit(app.exec_())

7 - 可视化控件基类QWidget_第2张图片

例2:Qwidget的嵌套

# 0.导入包和模块
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)

window = QWidget()
window.resize(500, 500)

red = QWidget(window)
red.resize(100, 100)
red.setStyleSheet("background-color:red;")

window.show()
sys.exit(app.exec_())

7 - 可视化控件基类QWidget_第3张图片

例3:显示时,子控件不会超出父控件的范围。

red.move(450, 0)

7 - 可视化控件基类QWidget_第4张图片

例3:两个子控件沿Z轴分布,后面的会受到前面的裁剪。

green = QWidget(window)
green.resize(100, 100)
green.move(450, 50)
green.setStyleSheet("background-color:green;")

7 - 可视化控件基类QWidget_第5张图片

(二)继承

方法一:【CTRL】+鼠标左键 去找类的定义:

class QWidget(_ _PyQt5_QtCore.QObject, _ _PyQt5_QtGui.QPaintDevice):

继承自QObject(对象)、QPaintDevice(绘制类)。

方法二:属性__bases__

print(QWidget.__bases__)

运行结果:(, )

PS:小括号代表元组,bases只是直接父类。

方法三:方法mro()
print(QWidget.mro())

运行结果:[, , , , , ]

PS:mro代表检索整个链条,不仅包括直接父类,还包括父类的父类。

QWidget->QObject->wrapper->QPaintDevice->simplewrapper->object(python的对象)

(三)功能作用

1、控件的创建
API

_init_(self, parent=None, flags):parent,父控件;flags,标志位。

比如:red = QWidget(window)

应用场景

创建控件的时候,设置父控件以及标志位(顶层窗口相关细讲)。

2、大小位置
理论支撑:控件的坐标系统

左上角为坐标原点,向右为x轴正方向,向下为y轴正方向。

控件位置参照:子控件参照父控件,顶层控件则参照桌面。

7 - 可视化控件基类QWidget_第6张图片

API
(1) 获取

7 - 可视化控件基类QWidget_第7张图片

浅灰色:用户区(用户可以在这儿操作,比如加一个标签栏)。

蓝色:标题栏。

深灰色:外部框架。

7 - 可视化控件基类QWidget_第8张图片

  • x():子控件相对于父控件的x位置,顶层控件窗口框架相对于桌面的x位置。
  • y():窗口框架相对于父控件/桌面的y位置。
  • pos():窗口框架x和y的组合,QPoint(x, y)。
  • width():用户区控件宽度,不包含任何窗口框架。
  • height():用户区控件的高度
  • size():width和height的组合,QSize(width, height),即尺寸组合
  • geometry():用户区域相对于父控件的位置和尺寸组合,QRect(x, y, width, height)。
  • rect():0, 0, width, height的组合,QRect(0, 0, width, height)。即忽略位置信息,只包含宽、高,子控件参照父控件。
  • frameSize():框架大小。
  • frameGeometry():框架尺寸。

注意: 控件显示完毕之后,具体的位置或者尺寸数据才会正确。

# 0.导入包和模块
from PyQt5.Qt import *
import sys

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

# 2.1 创建控件
window = QWidget()

# 2.2 设置控件
window.setWindowTitle("")
window.resize(500, 500)
window.move(100, 100)
print("显示之前获取:")
print(window.pos())
print(window.geometry())


# 2.3 展示控件
window.show()
print("-" * 30)
print("显示之后获取:")
print(window.pos())
print(window.geometry())

# 3.进入消息循环
sys.exit(app.exec_())

7 - 可视化控件基类QWidget_第9张图片

(2) 设置
  • move(x, y):操控的是x, y;也就是pos,针对整个窗口框架
  • resize(width, height):操控的是用户区的宽高。
  • setGeometry(x_noFrame, y_noFrame, width, height):参照为用户区域
  • adjustSize():根据内容自适应大小
  • setFixedSize():设置固定尺寸

注意:resize有最小值限制,因为要放的下标题栏的图标。

window.resize(500, 500)
window.move(100, 100)

7 - 可视化控件基类QWidget_第10张图片

window.setGeometry(100, 100, 500, 500)

7 - 可视化控件基类QWidget_第11张图片

window.show()
window.setGeometry(0, 0, 500, 500)

注意:setGeometry必须在显示之后设置,显示之前不确定要不要包装窗口框架,可能会出错。

7 - 可视化控件基类QWidget_第12张图片

自适应大小:

label = QLabel(window)
label.setText("标签啊")
label.move(100, 100)
label.setStyleSheet("background-color:yellow;")


def cao():
    new_content = label.text() + "标签啊"
    label.setText(new_content)
    label.adjustSize()


btn = QPushButton(window)
btn.setText("按钮啊")
btn.move(200, 200)
btn.clicked.connect(cao)

7 - 可视化控件基类QWidget_第13张图片

固定尺寸:

window.setWindowTitle("")
# window.resize(500, 500)
window.setFixedSize(500, 500)
window.move(100, 100)

最大化按钮失效了!

7 - 可视化控件基类QWidget_第14张图片

(3) 辅助工具
  • xy.exe
应用场景

布局控件位置

计算尺寸

案例
  1. 创建一个窗口, 设置尺寸为500 x 500, 位置为 300, 300。

    window.resize(500, 500)
    window.move(300, 300)
    
  2. 通过给定的的个数, 在一个窗口内创建相应个数的子控件。要求:按照九宫格的布局进行摆放,一行3列。

    # 0.导入包和模块
    from PyQt5.Qt import *
    import sys
    
    # 1.创建应用程序对象
    app = QApplication(sys.argv)
    
    # 2.1 创建控件
    window = QWidget()
    
    # 2.2 设置控件
    window.setWindowTitle("")
    window.resize(500, 500)
    window.move(300, 300)
    
    # 总的控件个数
    widget_count = 20
    column_count = 3
    row_count = (widget_count-1)//column_count + 1    # 整除
    
    # 计算一个控件的宽和高
    widget_width = window.width()/column_count
    widget_height = window.height()/row_count
    
    
    for i in range(0, widget_count):
        w = QWidget(window)
        x = i % column_count
        y = i // column_count
        w.resize(widget_width, widget_height)
        w.move(x * widget_width, y * widget_height)
        w.setStyleSheet("background-color: yellow;border: 1px solid purple")
    
    
    # 2.3 展示控件
    window.show()
    
    # 3.进入消息循环
    sys.exit(app.exec_())
    

    7 - 可视化控件基类QWidget_第15张图片

注意:行号是除数,列号是余数。

3、最大和最小尺寸
API
(1) 获取
  • minimumWidth():最小尺寸的宽度;
  • minimumHeight():最小尺寸的高度;
  • minimumSize():最小尺寸;
  • maximumWidth():最大尺寸的宽度;
  • maximumHeight():最大尺寸的高度;
  • maximumSize():最大尺寸(默认显示);
(2) 设置(set)
  • setMaximumWidth():设置最大宽度;
  • setMaximumHeight():设置最大高度;
  • setMaximumSize():设置最大尺寸;
  • setMinimumWidth():设置最小宽度;
  • setMinimumHeight():设置最小高度;
  • setMinimumSize():设置最小尺寸。

注意:控件完全展示前后会有所差异。

应用场景

限定控件大小

案例

创建一个窗口, 设置最小尺寸和最大尺寸。要求:最小为200, 200;最大为400, 400。测试通过resize是否可以改变。(不能)

# 0.导入包和模块
from PyQt5.Qt import *
import sys

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

# 2.1 创建控件
window = QWidget()

# 2.2 设置控件
window.setWindowTitle("最小尺寸最大尺寸")
# window.resize(500, 500)
window.setMinimumSize(200, 200)
window.setMaximumSize(400, 400)


# 2.3 展示控件
window.show()

# 3.进入消息循环
sys.exit(app.exec_())
4、内容边距
API
  • 设置内容边距:setContentsMargins(左, 上, 右, 下),内容区域的边与所在控件边的距离;
  • 获取内容边距:getContentsMargins(),结果是(左, 上, 右, 下)的元组;
  • 获取内容区域:contentsRect(),内容的矩形区域(x, y, width, height)。

注意:必须是控件本身留够对应的大小。

# 0.导入包和模块
from PyQt5.Qt import *
import sys

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

# 2.1 创建控件
window = QWidget()

# 2.2 设置控件
window.setWindowTitle("内容边距的设定")
window.resize(500, 500)

label = QLabel(window)
label.setText("标签在此")
label.resize(300, 300)
label.setStyleSheet("background-color: cyan;")

print(label.contentsRect())
print(label.getContentsMargins())

# 2.3 展示控件
window.show()

# 3.进入消息循环
sys.exit(app.exec_())

结果:什么都没设置的时候,内容区域是整个控件的范围,内容文本展示在内容区域水平靠左、垂直居中的位置。

7 - 可视化控件基类QWidget_第16张图片

image-20220205093812975

修改内容区域后:

label.setContentsMargins(100, 200, 0, 0)
print(label.contentsRect())
print(label.getContentsMargins())

7 - 可视化控件基类QWidget_第17张图片

image-20220205095402476

应用场景

调整控件内容边距, 使得显示更好看。

案例

创建一个窗口, 包含一个标签。要求:标签内容为"Hello Sz";内容区域大小为100, 60;将内容放在标签的右下角。

label.setText("Hello Sz")
label.resize(300, 300)
label.setStyleSheet("background-color: cyan;")

label.setContentsMargins(200, 240, 0, 0)

7 - 可视化控件基类QWidget_第18张图片

5、鼠标相关
API
(1) 设置鼠标形状 setCursor(),取值如下:
  • Qt.ArrowCursor(arrow箭头)

    image-20220205102829076

  • Qt.UpArrowCursor

    image-20220205102834723

  • Qt.CrossCursor

    image-20220205102844320

  • Qt.IBeamCursor(工型光标,beam光)

    image-20220205102902067

  • Qt.WaitCursor

    image-20220205102908403

  • Qt.BusyCursor

    image-20220205103032306

  • Qt.ForbiddenCursor

    image-20220205103038611

  • Qt.PointingHandCursor

    image-20220205103043923

  • Qt.WhatsThisCursor

    image-20220205103050773

  • Qt.SizeVerCursor

    image-20220205103056981

  • Qt.SizeHorCursor

    image-20220205103102602

  • Qt.SizeBDiagCursor

    image-20220205103108928

  • Qt.SizeAllCursor

    image-20220205103117631

  • Qt.SplitVCursor(Split分裂)

    image-20220205103121590

  • Qt.SplitHCursor

    image-20220205103127993

  • Qt.OpenHandCursor

    image-20220205103133242

  • Qt.ClosedHandCursor

    image-20220205103213586

  • Qt.BlankCursor(为了看出来加了个框,其实是一片空白)

    image-20220205103244330

  • 自定义图标:需要QCursor对象

例1:

label = QLabel(window)
label.setText("标签在此")
label.resize(100, 100)
label.setStyleSheet("border:1px solid purple")
label.setCursor(Qt.ForbiddenCursor)

例2:

查看setCursor定义,发现可以传入独一无二的枚举类型参数,也可以传入自定义的QCursor对象。

def setCursor(self, Union, QCursor=None, Qt_CursorShape=None):
        """ setCursor(self, Union[QCursor, Qt.CursorShape]) """
    pass

查看QCursor对象的定义,其中一种构造方法是传入QPixmap图片对象。

class QCursor(__sip.simplewrapper):
    """
    QCursor()
    QCursor(QBitmap, QBitmap, hotX: int = -1, hotY: int = -1)
    QCursor(QPixmap, hotX: int = -1, hotY: int = -1)
    QCursor(Union[QCursor, Qt.CursorShape])
    QCursor(Any)
    """

再去查看QPixmap对象的定义,str即图片的路径,如果在同一目录下直接写就行。

class QPixmap(QPaintDevice):
    """
    QPixmap()
    QPixmap(int, int)
    QPixmap(QSize)
    QPixmap(str, format: str = None, flags: Union[Qt.ImageConversionFlags, Qt.ImageConversionFlag] = Qt.ImageConversionFlag.AutoColor)
    QPixmap(List[str])
    QPixmap(QPixmap)
    QPixmap(Any)
    """

设置自定义鼠标

pixmap = QPixmap("star.png")
# 缩放尺寸,返回值即为修改调整后的图片
newPixmap = pixmap.scaled(100, 100)
# 修改热点为右下角(默认-1,-1)
cursor = QCursor(newPixmap, 100, 100)
window.setCursor(cursor)
(2) 重置形状

unsetCursor()

window.unsetCursor()
(3) 获取鼠标

cursor() -> QCursor

print(window.cursor())

结果:

(4) 鼠标跟踪
  • hasMouseTracking():判定是否设置了鼠标跟踪;

  • setMouseTracking(bool):设置鼠标是否跟踪;

    所谓的鼠标跟踪,其实就是设置检测鼠标移动事件的条件。不跟踪:鼠标移动时,必须处于按下状态,才会触发mouseMoveEvent事件;跟踪:鼠标移动时,不处于按下状态,也会触发mouseMoveEvent事件。

# 0.导入包和模块
from PyQt5.Qt import *
import sys


class MyWindow(QWidget):
    def mouseMoveEvent(self, evt):
        print("鼠标移动了")

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

# 2.1 创建控件
window = MyWindow()

# 2.2 设置控件
window.setWindowTitle("鼠标操作")
window.resize(500, 500)
window.setMouseTracking(True)

# 2.3 展示控件
window.show()

# 3.进入消息循环
sys.exit(app.exec_())

结果:不按下鼠标也会跟踪。

事件对象QMouseEvent里面有全局(针对屏幕左上角)位置、局部(针对控件左上角)位置等方法。

全局:

7 - 可视化控件基类QWidget_第19张图片

局部:

7 - 可视化控件基类QWidget_第20张图片

(5) QCursor对象

pixmap();pos();setPos(x, y)…

应用场景

根据特定场景,设置鼠标样式;使得用户交互时更加明确。

案例

创建一个窗口,内部有一个label控件。要求:鼠标移入窗口时,让label位置跟随鼠标位置;让鼠标设置为指定图标,并封装。

import sys
from PyQt5.Qt import *


class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.setWindowTitle("鼠标相关案例")
        self.resize(500, 500)
        self.move(200, 200)
        self.setMouseTracking(True)

        pixmap = QPixmap("star.png").scaled(60, 60)
        cursor = QCursor(pixmap)
        self.setCursor(cursor)

        label = QLabel(self)
        self.label = label
        label.setText("标签在此")
        label.move(100, 100)
        label.setStyleSheet("background-color:cyan;")

    def mouseMoveEvent(self, evt):
        print("鼠标移动到:", evt.localPos())
        # label2 = self.findChild(QLabel) 封装了就不用了,label已经变成类里的一个属性
        self.label.move(evt.localPos().x(), evt.localPos().y())
        # label2.move(evt.localPos()) arguments did not match any overloaded call


app = QApplication(sys.argv)

window = Window()

window.show()

sys.exit(app.exec_())	
6、事件
API
  • 显示和关闭事件:

    showEvent(QShowEvent) :控件显示时调用;

    closeEvent(QCloseEvent) :控件关闭时调用。

  • 移动事件:

    moveEvent(QMoveEvent) :控件移动时调用,窗口显示时已经移动了两次,移动的判定是x、y是否变化。

  • 调整大小:

    resizeEvent(QResizeEvent) :控件调整大小时调用,窗口显示时已经改变了一次;

  • 鼠标事件:

    (1)进入和离开事件:enterEvent(QEvent) 鼠标进入时触发;leaveEvent(QEvent) 鼠标离开时触发,可通过这个显示被选中的阴影效果。

    (2)鼠标按下时触发:mousePressEvent(QMouseEvent);

    (3)鼠标释放时触发:mouseReleaseEvent(QMouseEvent),一次按下加一次释放(控件的空间范围内)就是一次单击。

    (4)鼠标双击时触发:mouseDoubleClickEvent(QMouseEvent);

    (5)鼠标按下后移动时触发:mouseMoveEvent(QMouseEvent),用来跟踪鼠标。

    ​ setMouseTracking(True):追踪设置后,没有按下的移动也能触发。

  • 键盘事件:

    keyPressEvent(QKeyEvent):键盘按下时调用;

    keyReleaseEvent(QKeyEvent):键盘释放时调用。

    如果想监测是哪个按键被按下:查看QKeyEvent事件对象。

  • 焦点事件:

    focusInEvent(QFocusEvent):获取焦点时调用;

    focusOutEvent(QFocusEvent):失去焦点时调用。

  • 拖拽事件:(可上传文件)

    dragEnterEvent(QDragEnterEvent):拖拽进入控件时调用;

    dragLeaveEvent(QDragLeaveEvent):拖拽离开控件时调用;

    dragMoveEvent(QDragMoveEvent):拖拽在控件内移动时调用;

    dropEvent(QDropEvent):拖拽放下时调用。

  • 绘制事件:(美化控件)

    paintEvent(QPaintEvent):显示控件, 更新控件时调用;

  • 改变事件:(中英文切换)

    changeEvent(QEvent):窗体改变, 字体改变时调用。

  • 右键菜单:

    contextMenuEvent(QContextMenuEvent):访问右键菜单时调用。

  • 输入法:

    inputMethodEvent(QInputMethodEvent):输入法调用。

例1:因为是继承特定对象的特定方法,所以采用面向对象的写法。

# 0.导入包和模块
from PyQt5.Qt import *


# 继承类、定义自己的方法
class Window(QWidget):
    # 初始化
    def __init__(self):
        super(Window, self).__init__()
        self.setWindowTitle("事件消息的学习")
        self.resize(500, 500)
        self.setup_ui()

    # 存放所有子控件以及子控件的配置操作
    def setup_ui(self):
        label = QLabel(self)
        label.setText("标签签")

    def showEvent(self, a0: QShowEvent) -> None:
        print("窗口被展示")

    def closeEvent(self, a0: QCloseEvent) -> None:
        print("窗口被关闭")

    def moveEvent(self, a0: QMoveEvent) -> None:
        print("窗口被移动")

    def resizeEvent(self, a0: QResizeEvent) -> None:
        print("窗口改变了尺寸大小")

    def enterEvent(self, a0: QEvent) -> None:
        print("鼠标进来了")
        self.setStyleSheet("background-color: yellow;")

    def leaveEvent(self, a0: QEvent) -> None:
        print("鼠标离开了")
        self.setStyleSheet("background-color: green;")

    def mousePressEvent(self, a0: QMouseEvent) -> None:
        print("鼠标被按下")

    def mouseReleaseEvent(self, a0: QMouseEvent) -> None:
        print("鼠标被释放")

    def mouseDoubleClickEvent(self, a0: QMouseEvent) -> None:
        print("鼠标双击")

    def mouseMoveEvent(self, a0: QMouseEvent) -> None:
        print("鼠标移动")

    def keyPressEvent(self, a0: QKeyEvent) -> None:
        print("键盘某一按键被按下")

    def keyReleaseEvent(self, a0: QKeyEvent) -> None:
        print("键盘某一按键被释放")


if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())
应用场景

当一个控件被触发了一个特定的行为时, 就会调用特定的方法, 来将事件传递给开发人员, 方便处理。重写这些事件方法, 就可以监听相关的信息。

例2:事件机制之事件的传递

# 0.导入包和模块
from PyQt5.Qt import *
import sys


class window(QWidget):
    def mousePressEvent(self, a0: QMouseEvent) -> None:
        print("顶层窗口被按下")


class Midwindow(QWidget):
    def mousePressEvent(self, a0: QMouseEvent) -> None:
        print("中间控件被按下")
    pass


class Label(QLabel):
    def mousePressEvent(self, evt) -> None:
        print("标签控件被按下")
        # evt.accept()
        # 接受事件,不会继续上传给父控件
        # print(evt.isAccepted())
        # 输出True证明本来就会自动被接受
        # 忽略事件,继续上传给父控件
        evt.ignore()
    pass


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

# 2.1 创建控件
window = window()

# 2.2 设置控件
window.setWindowTitle("事件转发")
window.resize(500, 500)

mid_window = Midwindow(window)
mid_window.resize(300, 300)
mid_window.setAttribute(Qt.WA_StyledBackground, True)
mid_window.setStyleSheet("background-color:yellow;")

label = Label(mid_window)
label.setText("标签在此")
label.move(100, 100)
label.setStyleSheet("background-color:cyan;")

btn = QPushButton(mid_window)
btn.setText("按钮在此")
btn.move(50, 50)


# 2.3 展示控件
window.show()

# 3.进入消息循环
sys.exit(app.exec_())

PS:

  1. 如果这个控件没有处理事件,那就提交给父控件。如果注释了标签的事件处理,点击后会显示”中间控件被点击“。
  2. 标签的主要作用是展示,没有mousepressEvent的功能,butten则主要用来点击。
  3. evt.ignore():忽略事件,继续上传给父控件。会输出”标签被按下“,”中间控件被按下“。
  4. evt.accept():接受事件,不会继续上传给父控件。会输出”标签被按下“。
  5. print(evt.isAccepted()):看事件是否被接受,输出True表示默认会被接受不上传。
案例

1、创建一个窗口包含一个标签,要求:鼠标进入标签时, 展示"欢迎光临";鼠标离开标签时, 展示"谢谢惠顾"。

# 0.导入包和模块
from PyQt5.Qt import *
import sys


class newLabel(QLabel):
    def enterEvent(self, a0: QEvent) -> None:
        self.setText("欢迎光临")

    def leaveEvent(self, a0: QEvent) -> None:
        self.setText("谢谢惠顾")


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

# 2.1 创建控件
window = QWidget()

# 2.2 设置控件
window.setWindowTitle("鼠标操作案例1")
window.resize(500, 500)

label = newLabel(window)
label.resize(200, 200)
label.move(100, 100)
label.setStyleSheet("background-color:cyan;")

# 2.3 展示控件
window.show()

# 3.进入消息循环
sys.exit(app.exec_())

7 - 可视化控件基类QWidget_第21张图片

2、创建一个窗口, 监听用户按键。要求:监听用户输入Tab键;监听用户输入Ctrl+S组合键;监听用户输入Ctrl+Shift+A。快捷键的实现

(按下键盘的事件是def keyPressEvent(self, QKeyEvent),其中按下哪个键的信息存放在QKeyEvent里)

    # 普通键
    def key(self): # real signature unknown; restored from __doc__
        """ key(self) -> int """
        return 0
    
    # 修饰键
    def modifiers(self): # real signature unknown; restored from __doc__
        """ modifiers(self) -> Qt.KeyboardModifiers """
        pass
# 0.导入包和模块
from PyQt5.Qt import *
import sys


class newLabel(QLabel):
    def keyPressEvent(self, evt) -> None:
        # print("xxx")
        if evt.key() == Qt.Key_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键")


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

# 2.1 创建控件
window = QWidget()

# 2.2 设置控件
window.setWindowTitle("鼠标操作案例1")
window.resize(500, 500)

label = newLabel(window)
label.resize(200, 200)
label.move(100, 100)
label.setStyleSheet("background-color:cyan;")
# 标签捕获键盘操作
label.grabKeyboard()


# 2.3 展示控件
window.show()

# 3.进入消息循环
sys.exit(app.exec_())

注意:

  1. label.grabKeyboard()可以捕捉键盘行为到自己身上。

  2. 修饰键:比如Tab就不是修饰键,因为点击后会输入制表符。

    Qt.NoModifier:没有修饰键;

    Qt.ShiftModifier:Shift键被按下;

    Qt.ControlModifier:Ctrl键被按下;

    Qt.AltModifier:Alt键被按下

    多个修饰键的组合之间使用按位或运算(原理:任意两个修饰键的或运算结果是唯一的)

    普通键:Qt.Key_xxx

3、完成窗口, 用户区支持拖拽。要求:鼠标点击了用户区拖拽也可以移动窗口。(以前只有点击标题栏可以移动)

如果嫌弃系统标题栏,想自己画一个标题栏就会用到这个技能。

# 0.导入包和模块
from PyQt5.Qt import *


# 继承类、定义自己的方法
class Window(QWidget):
    # 初始化
    def __init__(self):
        super(Window, self).__init__()
        self.setWindowTitle("窗口移动")
        self.resize(500, 500)
        self.setup_ui()

    # 存放所有子控件以及子控件的配置操作
    def setup_ui(self):
        label = QLabel(self)
        label.setText("标签签")

    def mousePressEvent(self, evt):
        # print("鼠标按下")
        # 确定两个点
        self.mouse_x = evt.globalX()
        self.mouse_y = evt.globalY()
        # print(self.mouse_x, self.mouse_y)
        self.window_x = self.x()
        self.window_y = self.y()

    def mouseMoveEvent(self, evt):
        # print("鼠标移动")
        # 移动向量
        move_x = evt.globalX() - self.mouse_x
        move_y = evt.globalY() - self.mouse_y
        # print(move_x, move_y)
        # 目标位置
        dest_x = self.window_x + move_x
        dest_y = self.window_y + move_y
        self.move(dest_x, dest_y)

    def mouseReleaseEvent(self, evt):
        print("鼠标释放")


if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

首先思考:需要监听三个事件"鼠标按下",“鼠标弹起”,“鼠标移动”。

(1) 确定两个点:鼠标左键的点(确定鼠标的移动向量(x, y)),窗口坐标点(定位窗口移动move)

(2) 让窗口坐标点走一个移动向量。

(3) 参考对象有两个选择——桌面和窗口控件,选择不动的作为参考对象,也就是桌面。

7 - 可视化控件基类QWidget_第22张图片

到这看似已经完成了,但如果设置了鼠标跟踪(window.setMouseTracking(True)也就是不用单击鼠标左键,就会跟踪鼠标)的话,程序会崩掉。报错如下:

Traceback (most recent call last):
  File "C:/Users/16041/PycharmProjects/useQt/7-QWidget-案例3.py", line 31, in mouseMoveEvent
    move_x = evt.globalX() - self.mouse_x
AttributeError: 'Window' object has no attribute 'mouse_x'

就是说此时没有self.mouse_x这一变量,这是因为还没有执行过鼠标单击操作就开始了移动。

此时只需要一个标记就可解决此安全隐患,标记贯穿于初始化、按下、释放、移动整个过程。

# 0.导入包和模块
from PyQt5.Qt import *


# 继承类、定义自己的方法
class Window(QWidget):
    # 初始化
    def __init__(self):
        super(Window, self).__init__()
        self.setWindowTitle("窗口移动")
        self.resize(500, 500)
        self.setup_ui()
        self.moveflag = False

    # 存放所有子控件以及子控件的配置操作
    def setup_ui(self):
        label = QLabel(self)
        label.setText("标签签")

    def mousePressEvent(self, evt):
        self.moveflag = True
        # print("鼠标按下")
        # 确定两个点
        self.mouse_x = evt.globalX()
        self.mouse_y = evt.globalY()
        # print(self.mouse_x, self.mouse_y)
        self.window_x = self.x()
        self.window_y = self.y()

    def mouseMoveEvent(self, evt):
        if self.moveflag:
            # print("鼠标移动")
            # 移动向量
            move_x = evt.globalX() - self.mouse_x
            move_y = evt.globalY() - self.mouse_y
            # print(move_x, move_y)
            # 目标位置
            dest_x = self.window_x + move_x
            dest_y = self.window_y + move_y
            self.move(dest_x, dest_y)

    def mouseReleaseEvent(self, evt):
        self.moveflag = False
        # print("鼠标释放")


if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)
    window = Window()
    window.show()
    window.setMouseTracking(True)
    sys.exit(app.exec_())

现在还有个问题是按下鼠标上任意键都会移动,而不是仅仅左键。

    def mousePressEvent(self, evt):
        if evt.button() == Qt.LeftButton:
            self.moveflag = True
            # print("鼠标按下")
            # 确定两个点
            self.mouse_x = evt.globalX()
            self.mouse_y = evt.globalY()
            # print(self.mouse_x, self.mouse_y)
            self.window_x = self.x()
            self.window_y = self.y()
7、父子关系
API
  • childAt(x, y):获取在指定坐标子控件
  • parentWidget():获取指定控件的父控件
  • childrenRect():所有子控件组成的边界矩形
# 0.导入包和模块
from PyQt5.Qt import *
import sys

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

# 2.1 创建控件
window = QWidget()

# 2.2 设置控件
window.setWindowTitle("父子关系学习")
window.resize(500, 500)

label1 = QLabel(window)
# label1.setParent()
label1.setText("标签1")
label1.move(200, 200)
label1.setStyleSheet("background-color: cyan;")

label2 = QLabel(window)
# label1.setParent()
label2.setText("标签2")
label2.move(50, 50)
label2.setStyleSheet("background-color: cyan;")

label3 = QLabel(window)
# label1.setParent()
label3.setText("标签3")
label3.move(100, 100)
label3.setStyleSheet("background-color: cyan;")

print("window:", window)
print("label1:", label1)
print("label2:", label2)
print("label3:", label3)
print(window.childAt(210, 210))
print(label2.parentWidget())
print(window.childrenRect())

# 2.3 展示控件
window.show()

# 3.进入消息循环
sys.exit(app.exec_())

7 - 可视化控件基类QWidget_第23张图片

案例

创建窗口, 包含若干Label控件。要求:点击哪个标签, 就让哪个标签背景变红。

方法一:使用父控件处理

# 0.导入包和模块
from PyQt5.Qt import *
import sys

class newWidget(QWidget):
    def mousePressEvent(self, evt):
        if evt.button() == Qt.LeftButton:
            local_x = evt.x()
            local_y = evt.y()
            press_widget = self.childAt(local_x, local_y)
            if press_widget:
                press_widget.setStyleSheet("background-color:cyan;")


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

# 2.1 创建控件
window = newWidget()

# 2.2 设置控件
window.setWindowTitle("自学父子关系")
window.resize(500, 500)

for i in range(1, 11):
    label = QLabel(window)
    label.setText("label" + str(i))
    label.move(40 * i, 40 * i)
    label.setStyleSheet("border:1px solid black;")

# 2.3 展示控件
window.show()

# 3.进入消息循环
sys.exit(app.exec_())

还需要注意通过循环创建标签的方法,label的变量名可以是一样的。

方法二:自定义QLabel子类

class newLabel(QLabel):
    def mousePressEvent(self, evt):
        if evt.button() == Qt.LeftButton:
            self.setStyleSheet("background-color:red;")
8、层级控制
API
  • lower():将控件降低到最底层
  • raise_():将控件提升到最上层
  • a.stackUnder(b):让a放在b面。

注意:以上操作专指同级控件,默认后添加的在上面

# 0.导入包和模块
from PyQt5.Qt import *
import sys

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

# 2.1 创建控件
window = QWidget()

# 2.2 设置控件
window.setWindowTitle("层级关系调整")
window.resize(500, 500)

label = QLabel(window)
label.setText("标签1")
label.resize(200, 200)
label.setStyleSheet("background-color:green;")

label2 = QLabel(window)
label2.setText("标签2")
label2.resize(200, 200)
label2.move(50, 50)
label2.setStyleSheet("background-color:cyan;")

# 2.3 展示控件
window.show()

# 3.进入消息循环
sys.exit(app.exec_())

7 - 可视化控件基类QWidget_第24张图片

label2.lower()
或 label.raise_()
或 label2.stackUnder(label)

效果均如下:

7 - 可视化控件基类QWidget_第25张图片

改进:如果点谁谁在上面。

class newLabel(QLabel):
    def mousePressEvent(self, evt):
        self.raise_()

class newQWidget(QWidget):
    def mousePressEvent(self, evt):
        sub_widget = self.childAt(evt.x(), evt.y())
        if sub_widget:
            sub_widget.raise_()
应用场景

需要调整控件Z轴顺序

9、顶层窗口相关
API
(1) 图标
  • setWindowIcon(QIcon(“resource/header_icon.png”))
  • windowIcon()

PS:不知道函数里面放什么变量,就先创建一个提示类型的变量,再逐层追溯。

class QIcon(__sip.wrapper):
    """
    QIcon()
    QIcon(QPixmap)
    QIcon(QIcon)
    QIcon(str)
    QIcon(QIconEngine)
    QIcon(Any)
    """
icon = QIcon("star.png")
window.setWindowIcon(icon)
print(window.windowIcon())
(2) 标题
  • setWindowTitle(“这不就经常用的吗”),默认为“python”。
  • windowTitle()
(3) 不透明度
  • setWindowOpacity(float) 1.0:不透明;…0.0:透明;
  • windowOpacity()
(4) 窗口状态
  • setWindowState(state)

    Qt.WindowNoState,无状态;(默认)

    Qt.WindowMinimized,最小化;

    Qt.WindowMaximized,最大化;

    Qt.WindowFullScreen,全屏;(通过CTRL+alt+delete在任务管理器里关)

    Qt.WindowActive,活动窗口。(多个窗口时谁后show谁在上面,也可用setWindowState设置)

  • windowState()

print(window.windowState() == Qt.WindowNoState)
window.setWindowState(Qt.WindowMinimized)
window.setWindowState(Qt.WindowMaximized)
window.setWindowState(Qt.WindowFullScreen)

window.show()
window2.show()
window.setWindowState(Qt.WindowActive)

最终还是窗口1在上面。

(5) 最大化最小化

控制 自定义标题栏的最大小化、关闭等等

使用时可不使用show()方法。

showFullScreen():全屏显示,不包含窗口框架。

showMaximized():最大化,包括窗口框架。

showMinimized():最小化。

showNormal():正常。

例子:双击窗口最大化

class newWidget(QWidget):
    def mouseDoubleClickEvent(self, evt):
        if evt.button() == Qt.LeftButton:
            self.showMaximized()

判定(比如最大化和正常显示的最大化功能按键长得不一样)

isMinimized():是否是最小化窗口;

isMaximized():是否是最大化窗口;

isFullScreen():是否全屏。

例:双击,若已经是最大化就正常化,否则最大化。

class newWidget(QWidget):
    def mouseDoubleClickEvent(self, evt):
        if evt.button() == Qt.LeftButton:
            if self.isMaximized():
                self.showNormal()
            else:
                self.showMaximized()
(6) 窗口标志 设置整个窗口的外观

window.setWindowFlags(Qt.WindowStaysOnTopHint)

windowFlags()

其中参数Qt.WindowStaysOnTopHint可以是:

窗口样式

  • Qt.Widget:默认参数,是一个窗口或控件。有父控件,就是一般控件;没有父控件,则是窗口。

    窗口包括窗口边框和标题栏(图标、标题、最小化、最大化、关闭)。

    7 - 可视化控件基类QWidget_第26张图片

  • Qt.Window:是一个窗口,有窗口边框和标题。

    标题栏(图标、标题、最小化、最大化、关闭)

    7 - 可视化控件基类QWidget_第27张图片

  • Qt.Dialog:是一个对话框窗口,有窗口边框和标题栏。

    标题栏(图标、标题、问号、关闭)

    7 - 可视化控件基类QWidget_第28张图片

  • Qt.Sheet:是一个窗口或部件Macintosh表单。(跟上面有啥区别???)

    7 - 可视化控件基类QWidget_第29张图片

  • Qt.Drawer:是一个窗口或部件Macintosh抽屉。

    7 - 可视化控件基类QWidget_第30张图片

  • Qt.Popup:是一个弹出式顶层窗口。

    7 - 可视化控件基类QWidget_第31张图片

  • Qt.Tool:是一个工具窗口。

    7 - 可视化控件基类QWidget_第32张图片

  • Qt.ToolTip:是一个提示窗口,没有标题栏和窗口边框。

    image-20220209105621850

  • Qt.SplashScreen:是一个欢迎窗口,是QSplashScreen构造函数的默认值。(和上面的区别?)

    image-20220209105640702

  • Qt.SubWindow:是一个子窗口。

    7 - 可视化控件基类QWidget_第33张图片

顶层窗口外观标志

  • Qt.MSWindowsFixedSizeDialogHint:窗口无法调整大小。

    7 - 可视化控件基类QWidget_第34张图片

  • Qt.FramelessWindowHint:窗口无边框。

    7 - 可视化控件基类QWidget_第35张图片

  • Qt.CustomizeWindowHint:有边框但无标题栏和按钮,不能移动和拖动。

    7 - 可视化控件基类QWidget_第36张图片

  • Qt.WindowTitleHint:添加标题栏和一个关闭按钮。

    7 - 可视化控件基类QWidget_第37张图片

  • Qt.WindowSystemMenuHint:添加系统目录和一个关闭按钮。

    7 - 可视化控件基类QWidget_第38张图片

  • Qt.WindowMaximizeButtonHint:激活最大化和关闭按钮,禁止最小化按钮。

    7 - 可视化控件基类QWidget_第39张图片

  • Qt.WindowMinimizeButtonHint:激活最小化和关闭按钮,禁止最大化按钮。

    7 - 可视化控件基类QWidget_第40张图片

  • Qt.WindowMinMaxButtonsHint:激活最小化,最大化和关闭按钮。

    7 - 可视化控件基类QWidget_第41张图片

  • Qt.WindowCloseButtonHint:添加一个关闭按钮。

    7 - 可视化控件基类QWidget_第42张图片

  • Qt.WindowContextHelpButtonHint:添加问号和关闭按钮,同对话框。

    7 - 可视化控件基类QWidget_第43张图片

  • Qt.WindowStaysOnTopHint:窗口始终处于顶层位置(QQ登录界面)。

  • Qt.WindowStaysOnBottomHint:窗口始终处于底层位置。

注意:窗口是没有父控件的控件,即顶层控件。控件一般指非窗口控件。

应用场景

调整整个应用程序窗口外观。

案例

创建一个窗口,要求:无边框无标题栏,窗口半透明,自定义最小化、最大化、关闭按钮,支持拖拽用户区移动。

无边框设置

方法一:调用方法

window.setWindowFlags(Qt.FramelessWindowHint)

方法二:创建控件时就进行设置

window = QWidget(flags=Qt.FramelessWindowHint)

自定义最小化、最大化、关闭按钮发挥作用

  1. 监听信号。2. 修改事件方法。

这里选择方法一,因为监听信号是对事件的高级封装,更贴近于开发人员。

最大化的问题:

最大化按钮应该和还原互相转换。——写槽函数判断当前状态。

7 - 可视化控件基类QWidget_第44张图片

# 0.导入包和模块
from PyQt5.Qt import *
import sys

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

# 2.1 创建控件
window = QWidget()
# flags=Qt.FramelessWindowHint
window.setWindowFlags(Qt.FramelessWindowHint)
window.setWindowOpacity(0.5)

# 添加三个子控件 - 窗口右上角


# 2.2 设置控件
window.setWindowTitle("顶层窗口案例")
window.resize(500, 500)

top_margin = 5
# 添加三个子控件 - 窗口右上角
close_btn = QPushButton(window)
close_btn.setText("关闭")
close_btn_w = close_btn.width()
window_w = window.width()
close_btn_x = window_w - close_btn_w
close_btn_y = top_margin
close_btn.move(close_btn_x, close_btn_y)

max_btn = QPushButton(window)
max_btn.setText("最大化")
max_btn_w = max_btn.width()
max_btn_x = close_btn_x - max_btn_w
max_btn_y = top_margin
max_btn.move(max_btn_x, max_btn_y)

min_btn = QPushButton(window)
min_btn.setText("最小化")
min_btn_w = min_btn.width()
min_btn_x = max_btn_x - min_btn_w
min_btn_y = top_margin
min_btn.move(min_btn_x, min_btn_y)


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


close_btn.pressed.connect(window.close)
max_btn.pressed.connect(max_normal)
min_btn.pressed.connect(window.showMinimized)


# 2.3 展示控件
window.show()

# 3.进入消息循环
sys.exit(app.exec_())

代码太乱:封装重构(封装为类)

PS:为什么按钮之间有宽度?因为自动调整的宽度和获取的宽度可能不等。若想没有间隙,需要自设宽度。

# 0.导入包和模块
from PyQt5.Qt import *
import sys


class Window(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setWindowOpacity(1)
        # 2.2 设置控件
        self.setWindowTitle("顶层窗口案例")
        self.resize(500, 500)
        self.setup_ui()

    # 添加子控件
    def setup_ui(self):
        # 公共数据
        top_margin = 5
        btn_w = 80
        btn_h = 40
        # 添加三个子控件 - 窗口右上角
        close_btn = QPushButton(self)
        close_btn.setText("关闭")
        close_btn.resize(btn_w, btn_h)
        close_btn_w = btn_w
        window_w = self.width()
        close_btn_x = window_w - close_btn_w
        close_btn_y = top_margin
        close_btn.move(close_btn_x, close_btn_y)

        max_btn = QPushButton(self)
        max_btn.setText("最大化")
        max_btn.resize(btn_w, btn_h)
        max_btn_w = btn_w
        max_btn_x = close_btn_x - max_btn_w
        max_btn_y = top_margin
        max_btn.move(max_btn_x, max_btn_y)

        min_btn = QPushButton(self)
        min_btn.setText("最小化")
        min_btn.resize(btn_w, btn_h)
        min_btn_w = btn_w
        min_btn_x = max_btn_x - min_btn_w
        min_btn_y = top_margin
        min_btn.move(min_btn_x, min_btn_y)

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

        max_btn.pressed.connect(max_normal)
        min_btn.pressed.connect(self.showMinimized)
        close_btn.pressed.connect(self.close)


# 1.创建应用程序对象
app = QApplication(sys.argv)
# 2.1 创建控件
window = Window()

# 2.3 展示控件
window.show()

# 3.进入消息循环
sys.exit(app.exec_())

close_btn.pressed.connect(self.close)一句有警告,不知道为什么??放外面是没错的啊

报错:Expected type ‘Union[(…) -> None, pyqtBoundSignal]’, got ‘() -> bool’ instead

最大化以后标题位置不对。——监听窗口尺寸的变化。

# 0.导入包和模块
from PyQt5.Qt import *
import sys


class Window(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setWindowOpacity(1)

        # 公共数据保存为属性
        self.top_margin = 5
        self.btn_w = 80
        self.btn_h = 40

        # 2.2 设置控件
        self.setWindowTitle("顶层窗口案例")
        self.resize(500, 500)
        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)

        min_btn = QPushButton(self)
        self.min_btn = min_btn
        min_btn.setText("最小化")
        min_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("恢复")

        max_btn.pressed.connect(max_normal)
        min_btn.pressed.connect(self.showMinimized)
        close_btn.pressed.connect(self.close)

    def resizeEvent(self, QResizeEvent):
        close_btn_w = self.btn_w
        window_w = self.width()
        close_btn_x = window_w - close_btn_w
        close_btn_y = self.top_margin
        self.close_btn.move(close_btn_x, close_btn_y)

        max_btn_w = self.btn_w
        max_btn_x = close_btn_x - max_btn_w
        max_btn_y = self.top_margin
        self.max_btn.move(max_btn_x, max_btn_y)

        min_btn_w = self.btn_w
        min_btn_x = max_btn_x - min_btn_w
        min_btn_y = self.top_margin
        self.min_btn.move(min_btn_x, min_btn_y)


# 1.创建应用程序对象
app = QApplication(sys.argv)
# 2.1 创建控件
window = Window()

# 2.3 展示控件
window.show()

# 3.进入消息循环
sys.exit(app.exec_())

跨方法引用:将变量定义为self属性

拖拽用户区移动

    def mousePressEvent(self, evt):
        # 记录初始两个坐标点
        if evt.button() == Qt.LeftButton:
            self.move_flag = True
            self.window_x = self.x()
            self.window_y = self.y()
            self.mouse_x = evt.globalX()
            self.mouse_y = evt.globalY()

    def mouseMoveEvent(self, evt):
        # 计算移动向量
        if self.move_flag:
            current_x = evt.globalX()
            current_y = evt.globalY()
            window_cx = current_x - self.mouse_x + self.window_x
            window_cy = current_y - self.mouse_y + self.window_y
            self.move(window_cx, window_cy)

    def mouseReleaseEvent(self, evt):
        self.move_flag = False

注意:要判定是否需要移动,先按下再移动,释放时标记重置。

if evt.button() == Qt.LeftButton:

这句加不加括号的错了好几次。

10、交互状态
API
(1) 是否可用(是否为灰色)

比如:在没输入账号或密码之前登录按钮不能被点击。

  • setEnabled(bool):设置控件是否禁用;
  • isEnabled():获取控件是否可用。
btn = QPushButton(window)
btn.setText("按钮")
btn.pressed.connect(lambda: print("按钮被点击"))
print(btn.isEnabled())
btn.setEnabled(False)

结果:按钮是灰色了。

7 - 可视化控件基类QWidget_第45张图片

(2) 是否显示/隐藏(是否被绘制)
  • setVisible(bool):设置控件是否可见,传递的参数值为True也不一定可见。

    马甲(本质还是在调用setVisible):

    • setHidden(bool):显示反而用False。
    • show():展示控件;
    • hide():隐藏控件。

为了监听绘制事件是否执行,先重写函数:

class Window(QWidget):
    def paintEvent(self, evt):
        super().paintEvent(evt)
        print("窗口被绘制了")

注释掉show以后就不会打印窗口被绘制了,同理也不显示窗口,若调用setVisibleha函数,上述两个行为又会被恢复。

# window.show()
window.setVisible(True)

绘制时是先绘制父控件,再绘制子控件的。如果父控件隐藏,子控件不可能画出来。

class Window(QWidget):
    def paintEvent(self, evt):
        super().paintEvent(evt)
        print("窗口被绘制了")


class Btn(QPushButton):
    def paintEvent(self, evt):
        super().paintEvent(evt)
        print("按钮被绘制了")

image-20220213220717658

如果要绘制窗口但隐藏按钮:

btn.hide()
window.setHidden(False)

注意:hide()和show()都不用传bool参数。

隐藏不是擦除(delete),而是重新绘制了一遍,比如通过点击来隐藏按钮:

btn.pressed.connect(lambda: btn.setVisible(False))

7 - 可视化控件基类QWidget_第46张图片

首先不停地重新绘制窗口和按钮,点击了按钮后,只绘制了一次窗口然后停止。

  • isHidden():判定控件是否隐藏,一般是基于父控件可见。
  • isVisible():获取控件最终状态是否可见。
  • isVisibleTo(widget):如果能随着widget控件的显示和隐藏而同步变化,则返回True,跟实际显示不没关系。

注意:visible代表控件最终的状态, 相对于用户的肉眼(被其他控件遮挡也属于可见)。

​ hide可理解为相对于父控件是否可见,参数是True还是False。隐藏的一定是不可见的,反之不然。

例如:

print(btn.isHidden())
print(btn.isVisible())

1、若先显示window顺便显示btn。False,True。

2、若不显示window,从而btn也无法显示。False,False。

print(btn.isVisibleTo(window))

True.

也就是如果父控件显示时,子控件能否跟着被显示。

btn.setVisible(False)
print(btn.isVisibleTo(window))

False.

(3) 是否编辑,即设置窗口标题xxx[*]
  • setWindowModified(bool):被编辑状态,自动显示*;没有被编辑,不显示*。
  • isWindowModified():窗口是否是被编辑状态。
window.setWindowTitle("交互状态[*]")
window.resize(500, 500)
window.setWindowModified(True)
print(window.isWindowModified())

image-20220214173848303

PS:设置了窗口标题后,*会在修改时自动出现,[]不会显示。[*]可以放后面也可以放前面,但符号只能是*不能是其他。

应用:记事本编辑后左上角就会出现这个,并且在关闭时提示是否要保存。

(4) 是否为活跃窗口

isActiveWindow():与用户正在交互的那个窗口返回值为True,与画面上下层无关。

(5) 关闭

close():默认也是起隐藏控件的作用,并不会释放。

btn = Btn(window)
btn.setText("按钮")
btn.close()
btn.destroyed.connect(lambda: print("按钮被释放了"))

输出:空白。

setAttribute(Qt.WA_DeleteOnClose, True):但如果修改属性,关闭时删除,就会被释放,但隐藏时仍然不会释放。

btn.setAttribute(Qt.WA_DeleteOnClose, True)
btn.close()
btn.destroyed.connect(lambda: print("按钮被释放了"))

输出:“按钮被释放了”

应用场景

合适的时候,设置不用的状态来控制交互逻辑。

案例

创建一个窗口,包含一个文本框、一个按钮、一个标签。要求:默认状态下,标签隐藏,文本框和按钮显示,按钮设置为不可用状态;当文本框有内容时,让按钮可用,否则不可用==(比如没有输入账号时不允许点击登录)==;当文本框内容为Sz时,点击按钮则显示标签,并展示内容为登录成功,否则显示为失败。

涉及知识点:

文本框的创建,QLineEdit类;文本框监测内容变更,textChanged信号;文本框内容的获取,text()方法;按钮状态的设置。

textchanged的定义参数有个p_str,用来传递修改后的字符串。

def textChanged(self, p_str): # real signature unknown; restored from __doc__
    """ textChanged(self, str) [signal] """
	pass

所以,文本框有无内容可以通过字符串长度来判断。

# 0.导入包和模块
from PyQt5.Qt import *


# 继承类、定义自己的方法
class Window(QWidget):
    # 初始化
    def __init__(self):
        super(Window, self).__init__()
        self.setWindowTitle("交互状态案例")
        self.resize(500, 500)
        self.setup_ui()

    # 存放所有子控件以及子控件的配置操作
    def setup_ui(self):
        label = QLabel(self)
        label.setText("标签")
        label.move(100, 50)
        label.hide()

        le = QLineEdit(self)
        # le.setText("文本框默认输入")
        le.move(100, 100)

        def text_cao(text):
            # if len(text) > 0:
            #     btn.setEnabled(True)
            # else:
            #     btn.setEnabled(False)
            btn.setEnabled(len(text))

        le.textChanged.connect(text_cao)

        btn = QPushButton(self)
        btn.setText("登录")
        btn.move(100, 150)
        btn.setEnabled(False)

        def check():
            if le.text() == "Sz":
                label.setText("登陆成功")
            else:
                label.setText("登陆失败")
            label.adjustSize()
            label.show()
                
        btn.pressed.connect(check)


if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

注意:btn.setEnabled(len(text)),setEnabled的参数可以传给text。还有 label.adjustSize() 如果不设置,只会保持原始label的宽度导致新的内容无法全面显示。

11、信息提示
API
(1) 状态提示
  • statusTip()
  • setStatusTip(str)

效果:鼠标停在控件身上时,展示在状态栏的一段文本(状态栏:主窗口的最下方的横条,显示用户的操作状态)。

window = QMainWindow()
window.setWindowTitle("信息提示")
window.resize(500, 500)

window.statusBar()
window.setStatusTip("这是窗口")
print(window.statusTip())
# 打印状态提示的内容

7 - 可视化控件基类QWidget_第47张图片

PS:QMainWindow()组合窗口,其中一些控件是懒加载(用到的时候才创建)。

也可以对子控件设置,谁调用了这个方法,谁就显示在状态栏。

label.setStatusTip("这是标签")

7 - 可视化控件基类QWidget_第48张图片

(2) 工具提示
  • toolTip()

  • setToolTip(str)

  • 显示时长控制:

    toolTipDuration()

    setToolTipDuration(msec),单位是毫秒。

  • 效果:鼠标悬停在控件上一会后, 展示在旁边

label = QLabel(window)
label.setText("标签")
label.setStatusTip("这是标签")
label.setToolTip("这就是一个提示标签")
label.setToolTipDuration(2000)
print("提示内容是:", label.toolTip())
print("提示时长是:", label.toolTipDuration())

7 - 可视化控件基类QWidget_第49张图片

image-20220214223155608

(3) 这是啥提示 “?”
  • whatsThis()
  • setWhatsThis(str)

效果:切换到"查看这是啥"模式,点击该控件时显示。

# 设置whatsthis模式
window.setWindowFlags(Qt.WindowContextHelpButtonHint)
label.setWhatsThis("这是啥?不就是个标签签吗")
print(label.whatsThis())

先点问号触发”这是啥“模式,再去点击label标签可以显示提示。

12、焦点控制
API
(1) 单个控件角度
  • setFocus():指定控件获取焦点。

  • setFocusPolicy(Policy):设置焦点获取策略。

    Policy:

    • Qt.TabFocus:通过Tab键获得焦点;
    • Qt.ClickFocus:通过被单击获得焦点;
    • Qt.StrongFocus:可通过上面两种方式获得焦点**(默认的)**;
    • Qt.NoFocus:只能用setFocus使其获得焦点,用户操作均不行。
  • clearFocus():取消焦点。

le1 = QLineEdit(window)
le1.move(50, 50)

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

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

默认是第一个控件获得焦点:

7 - 可视化控件基类QWidget_第50张图片

# le2默认获取焦点
le2.setFocus()
# 获取焦点的方式只能是点击(le1,le3仍正常)
le2.setFocusPolicy(Qt.ClickFocus)
# 获取焦点的方式只能是Tab
le2.setFocusPolicy(Qt.TabFocus)
# 获取焦点两种方式(默认)
le2.setFocusPolicy(Qt.StrongFocus)
# 不用上述两种方式获取焦点
le2.setFocusPolicy(Qt.NoFocus)
# 默认不用le2来设置焦点,对le1没用
le2.clearFocus()
(2) 父控件角度
  • focusWidget():获取子控件中当前聚焦的控件;
  • focusNextChild():聚焦下一个子控件;
  • focusPreviousChild():聚焦上一个子控件;
  • focusNextPrevChild(bool):True,下一个(第一个单词Next);False, 上一个;(上两个方法的原型)
  • setTabOrder(pre_widget, next_widget):静态方法(通过类名调用),设置子控件获取焦点的先后顺序。

如果直接输入:

# 获取当前窗口内部,所有子控件中获取焦点的那个控件
print(window.focusWidget())

会输出None。

这是因为焦点的获取是在执行过程中实现的,不是展示窗口时实现的,所以应该在点击窗口时再查找哪个控件获取焦点。

class Window(QWidget):
    def mousePressEvent(self, evt):
        print(self.focusWidget())

7 - 可视化控件基类QWidget_第51张图片

class Window(QWidget):
    def mousePressEvent(self, evt):
        print("当前焦点:", self.focusWidget())
        self.focusNextChild()
        self.focusPreviousChild()
        self.focusNextPrevChild(True)
        self.focusNextPrevChild(False)
# 1-3-2
QWidget.setTabOrder(le1, le3)
QWidget.setTabOrder(le3, le2)

le2完了会自动切回le1。

应用场景

结合程序的业务逻辑,来调整焦点的操作。

比如:输入用户名和密码时焦点在不同的文本框,可用鼠标点击或Tab来切换,选中后周围会有蓝色光环,只有获取焦点的控件才能与用户交互。

13、不太重要
(1) 属性

setAttribute(Qt :: WidgetAttribute 属性,bool on = true)

(2) 遮罩

QWidget :: mask()const

返回当前在小部件上设置的遮罩。如果没有设置掩码,返回值将是一个空白区域。

另请参阅setMask(),clearMask(),QRegion :: isEmpty()和形状时钟示例。

(3) 内容拖拽

setAcceptDrops()

acceptDrops()

(4) 交互控制

scroll(int dx,int dy)

repaint()

(5) 动作行为

addAction(QAction * action)

removeAction(),insertAction(),actions()

(6) 状态控件

keyboardGrabber()

mouseGrabber()

grabMouse()

nextInFocusChain()

previousInFocusChain()

releaseKeyboard()

(7) 快捷方式

setShortcutAutoRepeat(int id,bool enable = true)

setShortcutEnabled(int id,bool enable = true)

grabShortcut()和releaseShortcut()

(8) 坐标转换

mapFrom(const QWidget * parent,const QPoint& pos)

mapFromGlobal(const QPoint& pos)const

将全局屏幕坐标pos转换为小部件坐标。

又见mapToGlobal(),mapFrom(),和mapFromParent()。
mapTo(const QWidget * parent,const QPoint& pos)const

将小部件坐标pos转换为父项的坐标系。在父母不能为0,且必须调用控件的父。

又见mapFrom(),mapToParent(),mapToGlobal(),和underMouse()。

QPoint QWidget :: mapToGlobal(const QPoint& pos)const

14、很重要, 后续讲
(1) 布局
API
  • setLayout(QLayout)
  • layout()
  • setLayoutDirection(Qt.LayoutDirection)
  • layoutDirection()
  • unsetLayoutDirection()
应用场景

布局控件,摆放的更加好看方便。

案例

创建一个窗口, 包含两个标签。要求:两个标签垂直摆放。

(2) 控件存在布局时的大小相关设置
获取
  • sizeHint():推荐大小
  • minimumSizeHint():最小推荐大小
  • sizePolicy():尺寸策略
设置
  • setFixedSize(width, height):相当于setMinimumSize(width, height) + setMaximumSize(width, height):固定大小(不因外界因素而改变)
  • setFixedWidth(width):固定宽度(不因外界因素而改变)
  • setFixedHeight(height):固定高度(不因外界因素而改变)
(3) 样式
API
调色板
  • palette():获取控件的调色板对象
  • setPalette(QPalette):设置控件的调色板对象

作用:

在实际的应用中,经常需要对某个控件的颜色外观,如背景、前景色等,进行设置。Qt中提供的调色板QPalette类就是专门用于管理控件的外观显示。

QPalette类相当于对话框或控件的调色板,管理着控件和窗体的所有颜色。

每个窗体和控件都包含一个QPalette对象,在显示时,对其做相应的设置即可。

设置颜色的方法:

  • setBrush ( ColorRole, QBrush )
  • setBrush (ColorGroup, ColorRole, QBrush )
  • setColor ( ColorRole, QColor )
  • setColor ( ColorGroup, ColorRole, QColor )

注:
ColorGroup:

  • Active:激活状态(获得焦点状态)
  • Disabled:禁用状态(未获得焦点状态)
  • Inactive:未激活状态(不可用状态)

ColorRole:

  • QPalette.Window:窗口颜色
  • QPalette.WindowText:窗口文字颜色
  • QPalette.Base:文本输入控件的背景色
  • QPalette.Text:文本输入控件的字体色
  • QPalette.Button:按钮控件的背景色
  • QPalette.ButtonText:按钮控件的字体色
  • QPalette.Hightlight:高亮背景色
  • QPalette.HightlightText:高亮字体色
  • QPalette.Link:链接颜色
  • QPalette.LinkVisited:访问过的链接色
样式表
  • setStyleSheet(stylesheet):设置样式表字符串;
  • styleSheet():获取样式表字符串。
应用场景

设置控件样式,使得控件更加好看。

案例
  1. 创建一个窗口, 包含一个标签。要求:标签的文字颜色为红色, 背景颜色为灰色,使用调色板实现。
  2. 创建一个窗口, 包含两个标签, 两个按钮。要求:设置所有的标签, 背景色为cyan; 边框为1px solid red。

(四)信号

windowTitleChanged(QString):窗口标题改变信号;

windowIconChanged(QIcon):窗口图标改变信号;

customContextMenuRequested(QPoint):自定义上下文菜单请求信号。


参考博客:

  1. Process finished with exit code -1073740791 (0xC0000409)解决方法:把报错调出来可解决百分之九十九的问题…https://www.cnblogs.com/ranzhong/p/13875853.html
  2. pyqt任务栏图标问题:https://blog.csdn.net/jieCooner/article/details/42238903

你可能感兴趣的:(GUI从零开始,qt,ui,开发语言)