Python 中 PyQt5 庫語法(一)

Kun發表於2022-03-29

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

官方文件:【https://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_())

常見的滑鼠樣式查詢:https://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)  # 自定義上下文選單請求訊號

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

相關文章