Python 中 PyQt5 庫語法(一)

PyQt5庫(一)

一、 簡介

1、 什麼是 Qt

使用 C++ 語言編寫的跨平台 GUI 庫,支援Windows 、MacOS和Linux。由於 Qt 使用C++語言編寫,所以使用Qt開發的GUI程式的介面風格與當前作業系統完全相同,而且運行效率很高

2、 什麼是PyQt

PyQt實現了一個Python模組集。它有超過300類,將近6000個函數和方法。它是一個多平台的工具包,可以運行在所有主要作業系統上,包括UNIX,Windows和Mac。 PyQt採用雙許可證,開發人員可以選擇GPL和商業許可。在此之前,GPL的版本只能用在Unix上,從PyQt的版本4開始,GPL許可證可用於所有支援的平台 ,同時Qt能實現的功能PyQt都能實現

3、 環境搭建

安裝 PyQt5

pip install pyqt5

官方文檔:【//www.riverbankcomputing.com/static/Docs/PyQt5/sip-classes.html】

二、 基本結構

1、 第一個程式

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
# @file: Demo.py
# @time: 2022/3/28 9:20
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, qApp
import sys


# 創建一個應用程式
# sys.argv 當別人通過命令行執行這個程式的時候,可以設定一種功能(接收命令行傳遞的參數)
app = QApplication(sys.argv)
print(app.arguments())  # 得到命令行的參數
print(qApp.arguments())  # qApp為全局變數
# 創建一個窗口
w = QWidget()
# 窗口尺寸
w.resize(300, 150)
# 移動窗口,窗口左上角的坐標
w.move(300, 300)  
# 設置窗口的標題
w.setWindowTitle("第一個基於pyqt5的桌面應用")
# 設置標籤
label = QLabel(w)
label.setText("hello world")
label.move(150, 75)
# 顯示窗口
w.show()
# 進入程式的消息循環,並通過exit函數確保主循環安全結束,相當於無限循環
# 檢測整個程式所接收到的用戶交互資訊
sys.exit(app.exec_())

2、 控制項操作

步驟:

  • 創建控制項

    • # 設置標籤
      label = QLabel(contain)  
      

      參數:

      • contain:代表要在哪個控制項(容器)上面展示,可以為空

      當我們創建一個控制項之後,如果說,這個控制項沒有父控制項,則把它當作頂層控制項(窗口)

  • 設置控制項

    • 大小,樣式,位置,樣式等
    • 頂層控制項有許可權去設置窗口內容,結構等
  • 展示控制項

    • 當控制項沒有父控制項時,要使用 show 方法去展示控制項

3、 快速生成程式碼

在pycharm中的活動模板配置如下程式碼,快速生成程式碼

from PyQt5.Qt import *


class Window(QWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("$test$")  # 設置標題
        self.resize($500$, $500$)  # 設置窗口大小
        self.move($300$, $300$)  # 移動窗口
        self.setup_ui()  # 調用創建控制項的方法

    def setup_ui(self):  # 添加控制項的操作
        pass


if __name__ == '__main__':
    # 可以通過導包來運行窗口
    import sys

    app = QApplication(sys.argv)
    # 創建窗口
    w = Window()
    # 顯示窗口
    w.show()
    sys.exit(app.exec_())

4、 面向對象

提高程式碼的可維護性

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
# @file: Demo.py
# @time: 2022/3/28 9:20
from PyQt5.QtWidgets import QApplication, QWidget, QLabel


class Window(QWidget):

    def __init__(self):
        super(Window, self).__init__()
        self.setWindowTitle("Hello")  # 設置標題
        self.resize(300, 150)  # 設置窗口大小
        self.move(300, 300)  # 移動窗口
        self.setup_label()  # 調用創建控制項的方法

    def setup_label(self):  # 添加控制項的操作
        label = QLabel(self)
        label.setText("Hello World")
        label.move(150, 75)


if __name__ == '__main__':
    # 可以通過導包來運行窗口
    import sys
    app = QApplication(sys.argv)
    # 創建窗口
    w = Window()
    # 顯示窗口
    w.show()
    sys.exit(app.exec_())

三、 基類控制項

什麼是控制項?

  • 一個程式介面上的各個獨立的標準,一塊矩形區域
  • 每類控制項都具備不同的功能
  • 同時,有些控制項有相似的功能,可以通過繼承關係學習

1、 QObject

1.1 設置標識

1.1.1 語法
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
# @file: Demo.py
# @time: 2022/3/28 9:20
from PyQt5.Qt import *

obj = QObject(self)
obj.setObjectName("notice")  # 設置Qt對象名稱
print(obj.objectName())  # 獲得Qt對象名稱
obj.setProperty("notice_l", "error")  # 設置對象的屬性
obj.setProperty("notice_l2", "warning")
print(obj.property("notice_l"))  # 獲得一個對象的屬性值
print(obj.dynamicPropertyNames())  # 獲取一個對象中所有通過setProperty設置的屬性名稱
1.1.2 應用場景
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
# @file: Demo.py
# @time: 2022/3/28 9:20
from PyQt5.Qt import *


class Window(QWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("QObject")  # 設置標題
        self.resize(500, 300)  # 設置窗口大小
        self.move(500, 500)  # 移動窗口
        self.setup_ui()  # 調用創建控制項的方法

    def setup_ui(self):  # 添加控制項的操作
        self.qObject_ui()

    def qObject_ui(self):  # 所有類的基類
        # 案例演示
        label = QLabel(self)  # 創建標籤
        label.setText("Hello")  # 設置標籤內容
        label.setObjectName("notice")  # id 為 notice
        label.setProperty("level", "error")  # 屬性選擇器
        with open("QStyle.qss", "r") as f:
            """
            文件內容為:
            QLabel#notice[level='error'] {
            font-size: 20px;
            color: blue;
            }
            """
            qApp.setStyleSheet(f.read())
        # label.setStyleSheet("font-size: 20px; color: blue;")
        # qss 樣式表,相當於前端的 css 樣式表,可以將其放入一個 `.qss` 的文件中,方便分離和讀取,同時,所有符合條件的內容都會變成相應的樣式


if __name__ == '__main__':
    # 可以通過導包來運行窗口
    import sys

    app = QApplication(sys.argv)
    # 創建窗口
    w = Window()
    # 顯示窗口
    w.show()
    sys.exit(app.exec_())

1.3 父子對象

1.3.1 語法
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *

obj1 = QObject()
obj2 = QObject()
obj3 = QObject()
obj2.setParent(obj1)  # 設置父子關係
obj3.setObjectName("2")  # 設置id
obj3.setParent(obj2)
print(obj1, obj2.parent())  # 獲取父類,返回記憶體地址
print(obj2, obj1.children())  # 獲取所有直系子對象
print(obj1.findChild(QObject))  # 獲取後代,遞歸查找,只返回一個
print(obj1.findChild(QObject, "2"))  # 獲取id為2的後代,只返回一個
print(obj1.findChildren(QObject))  # 獲取符合條件的所有後代
1.3.2 應用場景

記憶體管理機制,父控制項刪除,那麼子控制項也會自動刪除

  • 按鈕和對話框本身是父子控制項關係
    • 當對話框被刪除時,內部的子控制項也會自動刪除——非常合理
  • 我們操作的時候,是操作的對話框控制項本身,而不是內部的子控制項
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *

obj1 = QObject()
obj2 = QObject()
obj1.setParent(obj2)
# 監聽 obj2 對象被釋放
obj2.destroyed.connect(lambda: print("obj2,被釋放"))
print("刪除父對象")
del obj1
print("刪除完成")

如果一個控制項,沒有任何父控制項,那麼就會被當成頂層控制項——多個頂層窗口相互獨立

如果想要一個控制項被包含在另外一個控制項內部,就需要設置父子關係

  • 顯示位置受父控制項約束
  • 生命周期也被符對象接管

案例:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
win1 = QWidget()
win1.setStyleSheet("background-color: blue;")
win1.setWindowTitle("blue")
win1.resize(500, 500)
win1.show()

win2 = QWidget(win1)
win2.setStyleSheet("background-color: red;")
win2.setWindowTitle("red")
win2.resize(100, 100)
# win2.setParent(win1)  # win1 為父對象,win2 = QWidget(win1) 效果一樣
win2.show()

l1 = QLabel()
b1 = QPushButton()
l1.setParent(win1)
b1.setParent(win1)
l1.setText("label")
b1.setText("button")
l1.move(200, 200)
b1.move(300, 200)
l2 = QLabel()
l2.setParent(win1)
l2.setText("Label")
l2.move(100, 200)

# 遍歷設置樣式
for i in win1.findChildren(QLabel):
    i.setStyleSheet("color: green;")

l1.show()
b1.show()
l2.show()


sys.exit(app.exec_())

1.4 訊號操作

1.4.1 訊號與槽機制

訊號和槽是Qt中的核心機制,主要作用在於對象之間進行通訊

  • 當達到某個條件時,執行某個操作

訊號(widget):

  • 當一個控制項的狀態發生改變時,向外發出資訊

槽(connect):

  • 一個執行某些操作的函數/方法

所有繼承自 QWidget 的控制項都支援訊號與槽機制

1.4.2 機制描述

手動操作:

  • 訊號和槽相關聯

自動操作:

  • 當訊號發出時,連接槽函數會自動執行
1.4.3 訊號處理

訊號:

  • objectNameChange(objectName):對象名稱發生改變時,發射此訊號
  • destroyed(obj):對象被銷毀時,發射訊號
  • blockSignals(True):臨時阻斷連接
  • receivers(widget):查看當前訊號連接了多少個槽
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *


def destroy():
    print("對象被摧毀")


obj1 = QObject()
obj2 = QObject()
obj1.setParent(obj2)
# obj.destroyed
obj1.destroyed.connect(lambda: destroy())

# obj.objectNameChanged
obj1.objectNameChanged.connect(lambda name: print("對象名稱被改變: " + name))
obj1.setObjectName("xxx")

# obj1.objectNameChanged.disconnect()  # 斷開槽與訊號的連接
obj1.blockSignals(True)  # 臨時阻斷連接
obj1.setObjectName("1")
print("連接數量:" + str(obj1.receivers(obj1.objectNameChanged)))  # 查看有多少個槽連接這個訊號

obj1.blockSignals(False)  # 再次開啟連接
obj1.objectNameChanged.connect(lambda name: print("第二個連接:" + name))
print("連接數量:" + str(obj1.receivers(obj1.objectNameChanged)))  # 查看有多少個槽連接這個訊號
obj1.setObjectName("2")
del obj2
1.4.4 案例
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *


class Window(QWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("test")  # 設置標題
        self.resize(300, 300)  # 設置窗口大小
        self.move(200, 300)  # 移動窗口
        self.setup_ui()  # 調用創建控制項的方法

        def ret():
            print("標題變化了")
            # self.windowTitleChanged.disconnect()  # 斷開槽與訊號的關係
            self.blockSignals(True)  # 臨時斷開連接也行

        self.windowTitleChanged.connect(ret)

    def setup_ui(self):  # 添加控制項的操作
        self.btn()  # 添加按鈕

    def btn(self):
        b = QPushButton(self)
        b.setText("點擊我")

        def ret():
            self.setWindowTitle("hello")

        b.clicked.connect(ret)


if __name__ == '__main__':
    # 可以通過導包來運行窗口
    import sys

    app = QApplication(sys.argv)
    # 創建窗口
    w = Window()
    # 顯示窗口
    w.show()
    sys.exit(app.exec_())

1.5 類型判定

1.5.1 語法
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
w = QWidget()
obj = QObject(w)
w1 = QWidget(w)
l = QLabel(w)
print(w.isWidgetType())  # 判斷是繼承控制項類型
print(obj.isWidgetType())  # 判斷是否繼承控制項類型
print(l.isWidgetType())  # 判斷是否繼承控制項類型
print(w.inherits("QWidget"))  # 判斷是否繼承控制項類型
print(obj.inherits("QLabel"))  # 判斷是否繼承標籤類型
print(l.inherits("QLabel"))  # 判斷是否繼承標籤類型
sys.exit(app.exec_())
1.5.2 案例
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *


class Window(QWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("test")  # 設置標題
        self.resize(500, 500)  # 設置窗口大小
        self.move(300, 300)  # 移動窗口
        self.setup_ui()  # 調用創建控制項的方法

    def setup_ui(self):  # 添加控制項的操作
        l = QLabel(self)
        l.setText("first")
        l.move(100, 100)
        
        l2 = QLabel(self)
        l2.setText("second")
        l2.move(200, 100)
        
        btn = QPushButton(self)
        btn.setText("點我")
        btn.move(300, 100)
        
        for i in self.findChildren(QWidget):
            if i.inherits("QLabel"):  # 如果為 label 類型,修改樣式
                i.setStyleSheet("color: red; font-size: 20px")
            else:
                i.setStyleSheet("color: blue; font-size: 20px")


if __name__ == '__main__':
    # 可以通過導包來運行窗口
    import sys

    app = QApplication(sys.argv)
    # 創建窗口
    w = Window()
    # 顯示窗口
    w.show()
    sys.exit(app.exec_())

1.6 對象刪除

1.6.1 語法

刪除一個對象時,也會解除它與父對象之間的關係

  • obj.deleteLater():其並沒有將對象立即銷毀,而是向主消息循環發送了一個event,下一次主消息循環收到這個event之後才會銷毀對象
  • 這樣做的好處是可以在這些延遲刪除的時間裡面完成一些操作;壞處是記憶體釋放會不及時
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
w = QWidget()
obj1 = QObject()
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被釋放"))
# obj1.deleteLater()
obj2.deleteLater()  # 等下一個循環開始時才會真正釋放
# del obj2 # 直接刪除,但是這是把變數和對象的關聯替換了,使得系統無法訪問對象,不是真的刪除
print(obj1)
w.show()
sys.exit(app.exec_())

1.7 事件處理

訊號與槽機制是對事件機制的高級封裝

事件機制更偏離底層

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys


class App(QApplication):

    def notify(self, rev, evt):
        if rev.inherits("QPushButton") and evt.type() == QEvent.MouseButtonPress:  # 過濾器,只查看按鈕事件,並且只查看滑鼠點擊事件
            print("底層運行原理:")
            print(rev, evt)
        return super().notify(rev, evt)  # 分發事件


class Btn(QPushButton):
    def event(self, evt):
        if evt.type() == QEvent.MouseButtonPress:  # 事件過濾
            print("按鈕點擊了", evt)  # 裡面很多事件
        return super().event(evt)

    def mousePressEvent(self, evt):
        print("滑鼠被點擊了。。。。。。。。。。")  # 如果只有這行程式碼的話,沒有運行訊號與槽的操作
        return super().mousePressEvent(evt)


app = App(sys.argv)
w = QWidget()
btn = Btn(w)
btn.setText("按鈕")
btn.move(100, 100)
btn.pressed.connect(lambda: print("按鈕被按下"))
btn.clicked.connect(lambda: print("按鈕被點擊"))
btn.released.connect(lambda: print("按鈕被鬆開"))
w.show()
sys.exit(app.exec_())

1.8 定時器操作

1.8.1 語法
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys


class MyObject(QObject):

    def timerEvent(self, evt):
        print(evt, "1")  # 每個1秒列印
        return super().timerEvent(evt)


app = QApplication(sys.argv)
w = QWidget()
w.setWindowTitle("定時器")
w.resize(500, 500)
obj = MyObject()
t_id = obj.startTimer(1000)  # 1秒執行一次,通過繼承的方法來啟動計時器
obj.killTimer(t_id)  # 關閉定時器
w.show()
sys.exit(app.exec_())
1.8.2 應用場景
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys


class MyLabel(QLabel):
    def __init__(self, sec, *args, **kwargs):  # 使用不定長參數傳參
        super().__init__(*args, **kwargs)
        self.move(20, 20)
        self.setText(sec)
        self.setStyleSheet("font-size: 20px; color: red;")
        self.t_id = self.startTimer(1000)  # 1秒執行一次,通過繼承的方法來啟動計時器

    def timerEvent(self, evt):
        # 倒計時原理
        sec = int(self.text())
        sec -= 1
        self.setText(str(sec))
        if (sec == 0):
            self.killTimer(self.t_id)  # 關閉定時器


class MyQWidget(QWidget):
    def __init__(self, *args, **kwargs):
        super(MyQWidget, self).__init__(*args, **kwargs)
        self.setWindowTitle("定時器")
        self.resize(500, 500)
        self.startTimer(1000)  # 動畫

    def timerEvent(self, evt):
        current_w, current_h = self.width(), self.height()
        if current_w > 1000 and current_h > 1000:
            current_w -= 20
            current_h -= 20
        elif current_h < 1000 and current_w < 1000:
            current_w += 20
            current_h += 20
        self.resize(current_w, current_h)


app = QApplication(sys.argv)
w = MyQWidget()
MyLabel("10", w)  # 同時,10秒倒計時
w.show()
sys.exit(app.exec_())

2、 QWidget

2.1 簡介

介紹

  • 其為所有可視控制項的基類

  • 是一個最簡單的空白控制項

  • 控制項是用戶介面的最小元素

    • 接收各種事件(滑鼠、鍵盤等)
    • 繪製在桌面上面,展示給用戶看
  • 每個控制項都是矩形的,它們按Z軸順序排序

  • 控制項由其父控制項和前面的控制項剪切

  • 沒有父控制項的控制項稱之為窗口

2.2 繼承

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *

print(QWidget.__bases__)  # 直系父類
print(QWidget.mro())  # 所有父類,祖宗類

父類幾乎所有的方法,子類都可以使用

2.3 控制項創建

語法:

w = QWidget(parent)  # 如果沒有父類,可以不用填寫

2.4 大小位置

2.4.1 獲取

控制項的坐標系統:

  • 以左上角為坐標原點,縱軸為垂直方向,橫軸為水平方向

獲取位置:

  • (x(), y()) / pos()
    • 相對於父控制項的位置,包含窗口框架;頂層控制項則相對於桌面的位置
    • QPoint(x, y)
  • (width(), height()) / size()
    • 控制項的寬度和高度,不包含窗口框架
    • QSize(width, height)
  • geometry()
    • 用戶區域相對於父控制項的位置和尺寸的組合,不包含窗口框架
    • QRect(x, y, width, height)
  • rect()
    • 控制項尺寸的組合,不包含窗口框架
    • QRect(0, 0, width, height)
  • frameSize()
    • 框架大小
  • frameGeometry()
    • 框架位置和尺寸
  • 注意:
    • 控制項顯示完畢之後,具體位置或者尺寸數據才會正確
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
w = QWidget()
w.move(100, 100)
w.resize(200, 200)
w.show()
# 桌面顯示後獲取的數據才準確
print(w.x())
print(w.width())
print(w.geometry())
print(w.rect())
print(w.frameSize())
print(w.frameGeometry())
sys.exit(app.exec_())
2.4.2 設置
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

""" 
move(x, y)  操作的是 x, y 也就是 pos,包含窗口框架
resize(width, height)  操作的是寬高:不包含窗口框架
setGeometry(x_noFrame, y_noFrame, width, height)  此處參照為用戶區域,即不包含框架
adjustSize()  根據內容自適應大小
setFixedSize()  設置固定尺寸 
"""

app = QApplication(sys.argv)
w = QWidget()
"""
w.move(100, 100)
w.resize(200, 200)
# 下面這行程式碼和上面兩行程式碼的作用類似
w.setGeometry(100, 100, 200, 200)
"""
w.setFixedSize(500, 500)  # 不可以修改它的大小
w.show()
sys.exit(app.exec_())

2.5 尺寸最值

2.5.1 獲取

獲取:

  • (minimumWidth(), minimumHeight()) / minimumSize()
    • 最小尺寸
  • (maximumWidth(), msximumHeight()) / maximumSize()
    • 最大尺寸
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
w = QWidget()
w.resize(500, 500)
w.show()
print(w.minimumSize())  # 得到最小尺寸
print(w.maximumSize())  # 得到最大尺寸
sys.exit(app.exec_())
2.5.2 設置

設置最大最小尺寸

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
w = QWidget()
w.resize(500, 500)
w.setMaximumWidth(1000)  # 限定最大寬度
w.setMaximumHeight(1000)  # 限定最大高度
w.setMaximumSize(1000, 1000)  # 限定最大寬度和高度
w.setMinimumSize(100, 100)  # 限定最小寬度和高度
w.setMinimumWidth(100)  # 限定最小寬度
w.setMinimumHeight(100)  # 限定最小高度
w.show()
sys.exit(app.exec_())

限定最值尺寸後,如果修改後的尺寸超過這個最值,最終大小為最值大小

2.6 內容邊距

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
w = QWidget()
w.resize(500, 500)

l = QLabel(w)
l.setText("Hello World")
l.setStyleSheet("background-color: red; font-size: 20px; font-weight: 500;")
l.resize(300, 300)
l.setContentsMargins(110, 0, 0, 0)  # 設置內容外邊距
print(l.contentsRect().getRect())  # 得到內容區域
print(l.getContentsMargins())  # 得到內容外邊距

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

必須是控制項本身有足夠空間

2.7 滑鼠相關

2.7.1 設置滑鼠形狀
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
w = QWidget()
w.resize(500, 500)
pixmap = QPixmap(r"D:\35005\Pictures\Screenshots\微信圖片_20220302175157.jpg")  # 傳入圖片設置滑鼠圖標
pixmap_new = pixmap.scaled(50, 50)  # 輸入縮放的尺寸
cursor = QCursor(pixmap_new)  # 實例化滑鼠對象
# w.setCursor(Qt.BusyCursor)  # 設置滑鼠形狀,參數裡面輸入滑鼠樣式
w.setCursor(cursor, 0, 0)  # 也可以傳入一個滑鼠對象,後面的數字傳入的意思是以左上角為標準
w.unsetCursor()  # 取消設置滑鼠的形狀
w.show()
sys.exit(app.exec_())

常見的滑鼠樣式查詢://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtcore/qt.html#CursorShape

如果滑鼠進入了控制項的範圍內,形狀發生變化

2.7.2 重置滑鼠形狀
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
w = QWidget()
w.resize(500, 500)
current_cursor = w.cursor()  # 獲取滑鼠對象
print(current_cursor.pos())  # 獲得滑鼠相對於電腦螢幕的位置坐標
current_cursor.setPos(10, 10)  # 設置滑鼠的位置

w.show()
sys.exit(app.exec_())
2.7.3 滑鼠跟蹤

所謂的滑鼠跟蹤,其實就是設置檢測滑鼠移動事件的條件

滑鼠跟蹤:

  • 滑鼠移動時,不處於按下狀態,也會觸發 mouseMoveEvent 事件

滑鼠不跟蹤:

  • 滑鼠移動時,必須處於按下狀態,才會觸發 mouseMoveEvent 事件
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys


class MyWidget(QWidget):
    def mouseMoveEvent(self, me):
        print("滑鼠移動了", me.localPos())  # 當不跟蹤時,滑鼠要按下才會觸發
        # me.pos()和 me.localPos() 得到滑鼠相對於控制項的位置


app = QApplication(sys.argv)
w = MyWidget()
w.setWindowTitle("滑鼠事件")
w.resize(500, 500)
w.setMouseTracking(True)  # 開啟滑鼠跟蹤
print(w.hasMouseTracking())  # 判定是否開啟滑鼠跟蹤
w.show()
sys.exit(app.exec_())
2.7.4 案例
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys


# 滑鼠按下移動標籤
class MyWidget(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.resize(500, 500)
        self.setWindowTitle("滑鼠事件案例")
        self.label = QLabel(self)
        self.setup_ui()

    def mouseMoveEvent(self, me):
        x, y = me.x(), me.y()
        self.label.move(x, y)

    def setup_ui(self):
        self.label.resize(100, 100)
        self.label.setText("按住我!")
        self.label.setStyleSheet("background-color: green; font-weight: bold;")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MyWidget()
    w.show()
    sys.exit(app.exec_())

2.8 事件

2.8.1 API

顯示和關閉事件:

showEvent(QShowEvent)  # 控制項顯示是調用
closeEvent(QCloseEvent)  # 控制項關閉時調用

移動事件:

moveEvent(QMoveEvent)  # 控制項移動時調用

調整大小事件:

resizeEvent(QResizeEvent)  # 控制項大小改變時調用

滑鼠事件:

enterEvent(QEvent)  # 滑鼠進入時觸發
leaveEvent(QEvent)  # 滑鼠離開時觸發
mousePressEvent(QMouseEvent)  # 滑鼠按下時觸發
mouseReleaseEvent(QMouseEvent)  # 滑鼠按下時觸發
mouseDoubleClickEvent(QMouseEvent)  # 滑鼠雙擊時觸發
mouseMoveEvent(QMouseEvent)  # 滑鼠按下後移動時觸發,設置追蹤後,沒按下也能觸發

鍵盤事件:

keyPressEvent(QKeyEvent)  # 鍵盤按下時使用
keyReleaseEvent(QKeyEvent)  # 鍵盤鬆開時使用

焦點事件:

focusInEvent(QFocusEvent)  # 獲得焦點
focuseOutEvent(QFocusEvent)  # 失去焦點

拖拽事件:

dragEnterEvent(QDragEnterEvent)  # 拖拽進入控制項時使用
dragLeaveEvent(QDragLeaveEvent)  # 拖拽離開控制項時使用
dragMoveEvent(QDragMoveEvent)  # 拖拽在控制項內移動時使用
dropEvent(QDropEvent)  # 拖拽放下時使用

繪製事件:

paintEvent(QPaintEvent)  # 顯示控制項,更新控制項時使用

改變事件:

changeEvent(QEvent)  # 窗體改變,字體改變時調用

右鍵菜單:

contextMenuEvent(QContextMenuEvent)  # 訪問右鍵菜單時使用

輸入法:

inputMethodEvent(QInputMethodEvent)  # 輸入法切換調用

當一個控制項被觸發了一個特定的行為時,就會調用特定的方法,來將事件傳遞給開發人員,方便處理

重寫事件方法,就可以監聽相關的資訊


2.8.2 示例
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *


class Window(QWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("test")  # 設置標題
        self.resize(500, 500)  # 設置窗口大小
        self.move(100, 100)  # 移動窗口

    def showEvent(self, ev):
        print("窗口被展示了出來", ev)

    def closeEvent(self, ev):
        print("窗口關閉", ev)

    def moveEvent(self, ev):
        print("窗口移動", ev.pos())

    def resizeEvent(self, ev):
        print("控制項大小改變", ev.size())

    def enterEvent(self, ev):
        print("滑鼠進入了", ev)

    def leaveEvent(self, ev):
        print("滑鼠離開了", ev)

    def mousePressEvent(self, ev):
        print("滑鼠按下了", ev.button())

    def mouseReleaseEvent(self, ev):
        print("滑鼠鬆開", ev)

    def mouseDoubleClickEvent(self, ev):
        print("滑鼠雙擊了", ev.flags())

    def mouseMoveEvent(self, ev):
        print("滑鼠在移動", ev)

    def keyPressEvent(self, ev):
        print("鍵盤按下了:", chr(ev.key()))

    def keyReleaseEvent(self, ev):
        print("鍵盤鬆開了:", chr(ev.key()))

    def focusInEvent(self, ev):
        print("獲得焦點")

    def focusOutEvent(self, ev):
        print("失去焦點")


if __name__ == '__main__':
    # 可以通過導包來運行窗口
    import sys

    app = QApplication(sys.argv)
    # 創建窗口
    w = Window()
    # 顯示窗口
    w.show()
    sys.exit(app.exec_())
2.8.3 事件傳遞

如果一個控制項沒有處理該事件,則會自動傳遞給父控制項進行處理

事件對象具有兩種特殊方法:

  • accept()
    • 自己處理這個事件,並告訴系統不要在向上層傳遞
  • ignore()
    • 自己忽略這個事件,但是會執行;告訴系統,繼續往後傳遞
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys


class Window(QWidget):
    def mousePressEvent(self, ev):
        print("頂層滑鼠按下")


class MidWindow(QWidget):
    def mousePressEvent(self, ev):
        ev.ignore()  # 轉發給父對象,但是也會接收對象
        print(ev.isAccepted())
        print("中間滑鼠按下")


class Label(QLabel):
    def mousePressEvent(self, ev):
        print("標籤控制項滑鼠按下")
        ev.accept()  # 不用轉發給父對象,接收對象
        print(ev.isAccepted())


app = QApplication(sys.argv)
w = Window()
w.setWindowTitle("事件轉發")
w.resize(500, 500)
m_w = MidWindow(w)
m_w.resize(300, 300)
m_w.setAttribute(Qt.WA_StyledBackground, True)
m_w.setStyleSheet("background-color: yellow;")

label = Label(m_w)
label.setText("這是一個標籤")
label.setStyleSheet("background-color: skyblue;")
label.move(110, 110)
# 注意這個底層事件會被頂層事件覆蓋

w.show()
sys.exit(app.exec_())
2.8.4 案例
  1. 創建一個窗口包含一個標籤

    • 滑鼠進入標籤時,展示歡迎光臨
    • 滑鼠離開標籤時,展示謝謝惠顧
    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    # @author: kun
    from PyQt5.Qt import *
    import sys
    
    
    class Label(QLabel):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.resize(200, 200)
            self.move(100, 100)
            self.setStyleSheet("background-color: skyblue;")
    
        def enterEvent(self, *args, **kwargs):
            self.setText("歡迎光臨")
    
        def leaveEvent(self, *args, **kwargs):
            self.setText("謝謝惠顧")
    
    
    app = QApplication(sys.argv)
    w = QWidget()
    w.setWindowTitle("滑鼠操作案例1")
    w.resize(500, 500)
    label = Label(w)
    w.show()
    sys.exit(app.exec_())
    
  2. 創建一個窗口,監聽用戶按鍵

    • 監聽用戶輸入Tab鍵
    • 監聽用戶輸入Ctrl + S 組合鍵
    • 監聽用戶輸入Ctrl + Shift + v 組合鍵
    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    # @author: kun
    from PyQt5.Qt import *
    import sys
    
    
    class Label(QLabel):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.resize(200, 200)
            self.move(100, 100)
            self.setStyleSheet("background-color: skyblue; font-weight: 500;")
            self.grabKeyboard()  # 捕獲鍵盤事件
            # self.releaseKeyborad()  # 停止捕獲鍵盤事件
    
        def keyPressEvent(self, ev):
            # 監聽 Tab 鍵
            if ev.key() == Qt.Key_Tab:  # 監聽普通鍵
                self.setText('用戶點擊的是Tab鍵')
            # 監聽 Ctrl + C  修飾鍵 Ctrl 並且 普通鍵 C  Qt.AltModifier:其為 Alt 組合鍵
            if ev.modifiers() == Qt.ControlModifier and ev.key() == Qt.Key_C:
                self.setText("正在複製文本內容")
            # 監聽 Ctrl + Shift + v 修飾鍵 Ctrl + Shift (使用按位或組合獲取) 並且普通鍵 V
            if ev.modifiers() == Qt.ControlModifier | Qt.ShiftModifier and ev.key() == Qt.Key_V:
                self.setText("正在粘貼內容")
    
    
    app = QApplication(sys.argv)
    w = QWidget()
    w.setWindowTitle("滑鼠操作案例1")
    w.resize(500, 500)
    label = Label(w)
    w.show()
    sys.exit(app.exec_())
    
  3. 完成窗口,用戶區支援拖拽

    • 確定滑鼠移動的距離(向量 x, y)
    • 原始窗口坐標點 + 向量
    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    # @author: kun
    from PyQt5.Qt import *
    
    
    class Window(QWidget):
    
        def __init__(self):
            super().__init__()
            self.move_flag = False  # 判斷是否在移動
            self.setWindowTitle("內容拖動")  # 設置標題
            self.resize(500, 500)  # 設置窗口大小
            self.move(100, 100)  # 移動窗口
            self.mouse = ()  # 移動前的坐標
            self.origin = ()  # 原始的坐標
    
        def mousePressEvent(self, evt):
            """
            print(evt.localPos())  # 得到是相對於窗口左上角的坐標
            print(evt.globalPos())  # 得到的是相對於電腦左上角的坐標
            """
            if evt.button() == Qt.LeftButton:
                self.move_flag = True  # 設定標記
                self.mouse = (evt.globalX(), evt.globalY())  
                self.origin = (self.x(), self.y())  # 左上角坐標
    
        def mouseMoveEvent(self, evt):
            if self.move_flag:
                # 相對移動
                move_x = evt.globalX() - self.mouse[0]
                move_y = evt.globalY() - self.mouse[1]
                self.move(self.origin[0] + move_x, self.origin[1] + move_y)
    
        def mouseReleaseEvent(self, evt):
            # 確定最終位置
            self.move_flag = False
    
    
    if __name__ == '__main__':
        # 可以通過導包來運行窗口
        import sys
    
        app = QApplication(sys.argv)
        # 創建窗口
        w = Window()
        # 顯示窗口
        w.show()
        sys.exit(app.exec_())
    

2.9 父子關係

2.9.1 語法
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
w = QWidget()
w.setWindowTitle("父子關係")
w.resize(500, 500)

l1 = QLabel(w)
l1.setText("標籤1")

l2 = QLabel(w)
l2.setText("標籤2")
l2.move(50, 0)

l3 = QLabel(w)
l3.setText("標籤3")
l3.move(100, 0)

print(w.childAt(50, 0))  # 獲得對應坐標的子控制項
print(l2.parentWidget())  # 獲得父控制項
print(w.childrenRect().getRect())  # 列印子控制項所佔有的矩形區域

w.show()
sys.exit(app.exec_())
2.9.2 案例

創建窗口,若干個Label控制項,實現點擊功能

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys


# class MyLabel(QLabel):  # 藉助子控制項
#     def mousePressEvent(self, evt):
#         for i in self.parentWidget().findChildren(MyLabel):  # 獲取父控制項
#             i.setStyleSheet("")
#         self.setStyleSheet("background-color: red;")

class MyWidget(QWidget):
    def mousePressEvent(self, evt):
        subLabel = self.childAt(evt.x(), evt.y())
        if subLabel:  # 如果點擊了標籤;避免了報錯
            for i in self.findChildren(QLabel):  # 獲取所有子控制項,並清空樣式
                i.setStyleSheet("")
            subLabel.setStyleSheet("background-color: red;")  # 給制定內容修改樣式


app = QApplication(sys.argv)
w = MyWidget()
w.setWindowTitle("父子關係")
w.resize(500, 500)
for i in range(11):
    l = QLabel(w)
    l.setText(f"標籤{i}")
    l.move(50 * i, 50 * i)

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

2.10 層級控制

高層級控制項會被低層級控制項覆蓋

語法:

lower()  # 將控制項層級降低到最底層
raise_()  # 將控制項提升到最上層
a.stzckUnder(b)  # 讓 a 放在 b 下面

注意:以上操作專指同級順序

應用:需要調整控制項Z軸順序

2.11 頂層窗口

2.11.1 基本語法
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
w = QWidget()
icon = QIcon(r"D:\35005\Pictures\Screenshots\微信圖片_20220302175157.jpg")  # 創建圖標對象
w.setWindowIcon(icon)  # 設置窗口圖標
print(w.windowIcon())  # 獲取圖標對象
w.setWindowTitle("Hello")  # 默認為 python
print(w.windowTitle())  # 獲取窗口的標題
w.setWindowOpacity(0.9)  # 設置不透明度,範圍是 0 ~ 1
print(w.windowOpacity())  # 獲取窗口不透明度
w.setWindowState(Qt.WindowActive)
# 設置窗口狀態,有 無狀態(WindowNoSate) 最小化(WindowMinimized)
# 最大化(WindowMaximized) 全螢幕(WindowFullScreen) 活動窗口(WindowActive)
print(w.windowState())  # 獲取窗口狀態
w.show()
sys.exit(app.exec_())
2.11.2 最大化和最小化
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
w = QWidget()
# w.showMaximized()  # 最大化展示
# w.showMinimized()  # 最小化展示
# w.showNormal()  # 正常展示
# w.showFullScreen()  # 全螢幕展示
print(w.isMinimized())  # 查看是否為最小化
print(w.isMaximized())  # 查看是否為最大化
print(w.isFullScreen())  # 查看是否為全螢幕
w.show()
sys.exit(app.exec_())
2.11.3 窗口標誌
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
w = QWidget()
w.setWindowFlags(Qt.WindowStaysOnTopHint)  # 設置頂層窗口的外觀標誌
w.show()
sys.exit(app.exec_())
屬性 描述
Qt.MSWindowsFixedSizeDialogHint 固定窗口,無法調整大小
Qt.FramelessWindowHint 窗口無邊框
Qt.CustomizeWindowHint 有邊框,無標題欄與按鈕,不能移動和拖動
Qt.WindowTitleHint 添加標題欄與關閉按鈕
Qt.WindowSystemMenuHint 添加系統目錄和關閉按鈕
Qt.WindowMaximizeButtonHint 激活最大化按鈕與關閉按鈕,禁止最小化按鈕
Qt.WindowMinimizeButtonHint 激活最小化按鈕與關閉按鈕,禁止最大化按鈕
Qt.WindowMinMaxButtonsHint 激活最大化與最小化按鈕和關閉按鈕
Qt.WindowCloseButtonHint 添加一個關閉按鈕
Qt.WindowContextHelpButtonHint 添加問號與關閉按鈕,像對話框一樣
Qt.WindowStaysOnTopHint 窗口始終處於頂部位置
Qt.windowStaysOnButtonHint 窗口始終處於底部位置
2.11.4 案例

創建一個窗口,要求:

  • 無邊框,無標題欄
  • 窗口半透明
  • 自定義最大化,最小化,關閉按鈕
  • 支援拖拽用戶區移動
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *


class Window(QWidget):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setWindowTitle("案例")  # 設置標題
        self.move_flag = False  # 默認沒有移動
        self.resize(500, 500)  # 設置窗口大小
        self.move(300, 300)  # 移動窗口
        self.setWindowOpacity(0.9)  # 設置窗口透明
        self.setup_ui()  # 調用創建控制項的方法
        self.origin = ()  # 初始坐標,相對於窗口的
        self.mouse = ()  # 滑鼠按下時,滑鼠相對於窗口的坐標

    def setup_ui(self):  # 添加控制項的操作
        def close_w():
            self.deleteLater()  # 刪除主窗口

        btn_close = QPushButton(self)
        btn_close.clicked.connect(close_w)
        btn_close.setText("X")
        btn_close.move(self.width() - 25, 8)

        def max_or_min(btn):
            if self.isMaximized():
                self.showNormal()
                btn.setText("+")
            else:
                self.showMaximized()
                btn.setText("-")
            count = 1
            self.findChild(QLabel).resize(self.width(), 45)
            for i in self.findChildren(QPushButton):
                i.move(self.width() - count * 25, 8)  # 動態化設置按鍵的位置
                count += 1

        btn_maxOrMin = QPushButton(self)
        btn_maxOrMin.move(self.width() - 50, 8)
        btn_maxOrMin.clicked.connect(lambda: max_or_min(btn_maxOrMin))
        btn_maxOrMin.setText("+")  
        qApp.setStyleSheet("""QPushButton{  
            font-size: 20px;
            color: black;
            font-weight: 800;
            border: 2px solid black;
            margin: 5px;
        }""")  # 設置樣式
        # 設置窗口頭部框架
        label = QLabel(self)
        label.resize(self.width(), 45)
        label.setStyleSheet("background-color: skyblue;")
        label.lower()

    # 設置窗口移動
    def mousePressEvent(self, evt):
        if evt.button() == Qt.LeftButton:
            self.move_flag = True
            self.mouse = (evt.globalX(), evt.globalY())
            self.origin = (self.x(), self.y())

    def mouseMoveEvent(self, evt):
        self.move(evt.globalX() - self.mouse[0] + self.origin[0],
                  evt.globalY() - self.mouse[1] + self.origin[1]) if self.move_flag else None

    def mouseReleaseEvent(self, *args, **kwargs):
        self.move_flag = False


if __name__ == '__main__':
    # 可以通過導包來運行窗口
    import sys

    app = QApplication(sys.argv)
    # 創建窗口,同時無邊框,無標題欄
    w = Window(flags=Qt.FramelessWindowHint)
    # 顯示窗口
    w.show()
    sys.exit(app.exec_())

2.12 交互狀態

2.12.1 是否可見
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys
"""
setEnabled(bool)  # 設置控制項是否可用
isEnabled()  # 獲取控制項是否可用
"""
app = QApplication(sys.argv)
w = QWidget()
btn = QPushButton(w)
btn.setText("點擊")
print(btn.isEnabled())  # 判斷按鈕是否可用
btn.pressed.connect(lambda: btn.setEnabled(False))  # 點擊後按鈕禁用
w.show()
sys.exit(app.exec_())
2.12.2 顯示和隱藏
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

"""
# 顯示和隱藏
setVisible(bool)  # 設置控制項是否可見,傳遞的參數值為 True 也不一定可見,如果父控制項沒有展示,那麼子控制項為 True 也不可見
    setHidden(bool)  # 設置隱藏
    show()  # 設置顯示
    hide()  # 設置隱藏
isHidden()  #  獲取相對於父控制項是否隱藏
isVisible()  # 獲取是否可見
isVisibleTo(widget)  # 獲取相對於widget控制項是否可見,即該控制項是否跟著widget控制項一起顯示 
"""

app = QApplication(sys.argv)
w = QWidget()
w.resize(500, 500)
btn = QPushButton(w)
print(btn.isVisible())  # 判斷控制項是否可見
btn.clicked.connect(lambda: btn.hide())  # 隱藏按鈕
print(w.isHidden())  # 判斷窗口是否隱藏
w.setVisible(True)  # 使得窗口可見,先繪製父窗口
# w.setHidden(False)  # 效果一樣
sys.exit(app.exec_())
2.12.3 是否可編輯
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

"""
setWindowModified(bool)  # 設置是否被編輯
isWindowModified()  # 窗口是否被編輯
"""

app = QApplication(sys.argv)
w = QWidget()
w.setWindowTitle("[*]交互狀態")
w.setWindowModified(True)  # 可編輯狀態會顯示 [] 裡面的*號,即有 * 號就處於被編輯狀態
print(w.isWindowModified())  # 判斷窗口是否處於可編輯狀態
w.setVisible(True)  # 和show作用類似
sys.exit(app.exec_())
2.12.4 是否為活躍窗口
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
w1 = QWidget()
w2 = QWidget()
w2.show()
w1.show()
print(w2.isActiveWindow())  # 展示是否處於活躍窗口
print(w1.isActiveWindow())
sys.exit(app.exec_())
2.12.5 控制項關閉
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

"""
setAttribute(Qt.WA_DeleteOnClose, True)  # 如果加了這行程式碼,窗口/控制項就會在關閉的同時刪除,效果和deleteLater類似
close()  # 效果和setVisible類似
"""

app = QApplication(sys.argv)
w1 = QWidget()
w2 = QWidget()
w2.destroyed.connect(lambda: print("w2銷毀"))
w1.destroyed.connect(lambda: print("w1銷毀"))
w2.show()
w1.show()
w1.setAttribute(Qt.WA_DeleteOnClose, True)  # 如果加了這行程式碼,窗口/控制項就會在關閉的同時刪除,效果和deleteLater類似
w1.close()  # 效果和setVisible類似
w2.setVisible(False)  # 使得窗口不可見,但是窗口並沒有刪除
w2.deleteLater()  # 銷毀窗口/控制項
sys.exit(app.exec_())
2.12.6 案例

創建一個窗口,包含一個文本框和一個按鈕和一個標籤:

  • 默認狀態下,標籤隱藏,文本框和按鈕顯示,按鈕為不可使用狀態
  • 當文本框有內容時,讓按鈕可用,否則不可用
  • 當文本框內容為kun時,點擊按鈕顯示標籤,並且展示文本為登錄成功,否則為失敗

涉及知識點:

  • 文本框的創建
    • QLineEdit類
  • 文本框內容監測
    • testChanged 訊號
  • 文本框內容獲取
    • text() 方法
  • 按鈕狀態設置
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import hashlib


class Window(QWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("案例[*]")  # 設置標題
        self.resize(500, 500)  # 設置窗口大小
        self.move(100, 100)  # 移動窗口
        self.setup_ui()  # 調用創建控制項的方法

    def setup_ui(self):  # 添加控制項的操作 5 + 40 + 5 + 40 + 5
        qApp.setStyleSheet("*{font-size: 15px; line-height: 15px;}")  # 設置樣式
        # 創建文本框
        text = QLineEdit(self)
        text.resize(300, 40)
        text.move(100, 5)

        def change_():
            self.setWindowModified(True)
            btn.setEnabled(len(text.text()) > 0)  # 當有文本框內容時,按鈕可用

        text.textChanged.connect(change_)  # 綁定事件
        # 創建按鈕
        btn = QPushButton(self)
        btn.resize(80, 40)
        btn.setText("登錄")
        btn.move(210, 50)
        btn.setEnabled(False)  # 按鈕不可用

        def click_():
            label.setVisible(True)  # 顯示標籤
            # 使用 hash 加密
            salt = hashlib.md5("liu".encode("utf-8"))
            salt.update(text.text().encode("utf-8"))
            content = salt.hexdigest()
            if content == 'a8b2a2561ec21479990c48706a743c9a':  # 條件判斷
                label.setText("登錄成功")
            else:
                label.setText("登錄失敗")

            text.setText("")
            self.setWindowModified(False)

        btn.clicked.connect(click_)  # 綁定事件
        # 創建標籤
        label = QLabel(self)
        label.resize(100, 40)
        label.move(220, 95)
        label.setVisible(False)  # 標籤隱藏


if __name__ == '__main__':
    # 可以通過導包來運行窗口
    import sys

    app = QApplication(sys.argv)
    # 創建窗口
    w = Window()
    # 顯示窗口
    w.show()
    sys.exit(app.exec_())

2.13 資訊提示

2.13.1 狀態提示

滑鼠懸停在空間上一會兒後,展示內容在狀態欄上面

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
w = QMainWindow()  # 使用組合窗口
w.resize(500, 500)
w.statusBar()  # 創建狀態欄,可以顯示狀態提示
l = QLabel(w)
l.setStatusTip("這是資訊提示")   # 設置狀態提示
l.setText("你好")
print(l.statusTip())  # 獲取狀態提示
w.show()
sys.exit(app.exec_())
2.13.2 工具提示

滑鼠懸停在控制項上一會兒後,展示在旁邊

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
w = QWidget()  
w.resize(500, 500)
l = QLabel(w)
l.setToolTip("這是一個工具提示")
l.setText("坤坤")
l.setToolTipDuration(1000)  # 工具提示展示時間為 1 秒
print(l.toolTip())  # 獲取工具提示
w.show()
sys.exit(app.exec_())
2.13.3 意思提示

切換到查看這是啥模式,點擊該控制項時顯示

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)
w = QWidget(flags=Qt.WindowContextHelpButtonHint)  # 這是啥模式
w.resize(500, 500)
l = QLabel(w)
l.setWhatsThis("這是啥,這是一個意思提示")  # 設置一個意思提示
l.setText("坤坤")
print(l.whatsThis())  # 獲取意思提示
w.show()
sys.exit(app.exec_())

2.14 焦點控制

2.14.1 單個控制項
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys
"""
setFocus()  # 指定控制項獲取焦點
setFocusPolicy(mode)  # 設置焦點獲取策略
    - 參數
        Qt.TabFocus    通過 Tab 獲取焦點
        Qt.ClickFocus  通過點擊獲取焦點
        Qt.StrongFocus 通過以上兩種方式獲取焦點
        Qt.NoFocus     不能通過以上兩種方式獲取焦點
clearFocus()  # 取消焦點
"""


app = QApplication(sys.argv)
# 創建三個文本框
w = QWidget()
w.resize(500, 500)
t1 = QLineEdit(w)
t2 = QLineEdit(w)
t2.move(50, 50)
t2.setFocus()  # 設置焦點
t2.setFocusPolicy(Qt.TabFocus)  # 獲取焦點的方式
t2.clearFocus()  # 取消焦點
t3 = QLineEdit(w)
t3.move(100, 100)
w.show()
sys.exit(app.exec_())
2.14.2 父控制項
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: kun
from PyQt5.Qt import *
import sys
"""
focusWidget()  # 獲取子控制項中當前焦距的控制項
focusNextChild()  # 聚焦下一個子控制項
focusPreviousChild()  # 聚焦上一個子控制項
focusNextPrevChild()  # true:下一個 false:上一個
setTabOrder(pre_widget, next_widget)  # 靜態方法,用於設置子控制項獲取焦點的先後順序
"""


app = QApplication(sys.argv)
w = QWidget()
w.resize(500, 500)
t1 = QLineEdit(w)
t1.setFocus()  # 給 t1 設置焦點
t2 = QLineEdit(w)
t2.move(50, 50)
t3 = QLineEdit(w)
t3.move(100, 100)
w.show()
# QWidget.setTabOrder(t3, t2)  # 按 tab 獲取焦點的順序 t3 > t2 > t1
w.focusNextChild()  # 聚焦下一個子控制項
w.focusNextPrevChild(True)  # 結果是t3有焦點
print(w.focusWidget())  # 獲取子控制項中當前焦距的控制項
sys.exit(app.exec_())

2.15 訊號

windowTitleChanged(QString)  # 窗口標題改變訊號
windowIconChanged(QIcon)  # 窗口圖標改變訊號
customContentMenuRequested(Qpoint)  # 自定義上下文菜單請求訊號

基類控制項學習完成,下面我們開始學習具體的子類控制項

Tags: