上一章將的QObject是PyQt里所有控件的基類,並不屬於可視化的控件。這一章所講的QWidget,是所有可視化控件的基類。
QWidget包含下面幾點特性
a.控件是用戶界面的最小的元素
b.每個控件都是矩形的,他們按照Z軸順序排序(垂直於桌面),前面的會覆蓋后面的
c.控件由其父控件和前面的控件裁剪
d.沒有父控件的控件就是窗口
功能與作用
1.控件的創建
我們在上一章在創建了控件以后用Obj.setParent()的指定了控件的父子關系,在這里就有更簡單的方法了!

from PyQt5.Qt import * import sys app=QApplication(sys.argv) window = QWidget() window.resize(800,600) red = QWidget(window) red.setStyleSheet('background-color:red;') red.resize(100,100) window.show() sys.exit(app.exec_())
這個方法里就是用繼承的方式在窗口里創建了新的界面。
2.大小和位置
首先我們要理解空間的坐標系統,控件的坐標原點在左上角,向右為x軸正方向,向下為y軸的正方向。頂層窗口的原點為顯示器的左上角,而子控件的原點在父控件的左上角。每個值為一個分辨率。
其次我們看一下大小位置相關的API
a.獲取
window = QWidget() window.x() #控件X軸坐標(包含框架窗口) window.y() #控件Y軸坐標(包含框架窗口) window.pos() #控件X/Y軸坐標集合 window.width() #控件寬度(不包含窗口框架) window.height() #控件高度(不包含窗口框架) window.size() #控件寬度和高度的組合 window.geometry() #用戶區域相對於父控件的位置和尺寸組合 window.rect() #(0,0,width,height)的組合 window.frameSize() #整個界面大小(包含框架窗口) window.frameGeometry()#整個界面(X,Y,Width,height)的集合
用一個圖可以描述一下
b設置
window.move(x,y) #控件移動至x,y;包含窗口框架 window.resize(width,height) #設置寬、高,不包含窗口框架 window.setGeometry(x,y,width,height) #設置用戶區域的坐標和大小 window.adjustSize() #根據內容自適應尺寸 window.setFixedSize(width,height) #設置固定尺寸 設置后窗口最大化按鈕是灰色的,窗口不可拖放
用一個案例來演示一下,定義一個界面,上面按每行平均放3個來布局。

from PyQt5.Qt import * import sys app=QApplication(sys.argv) window = QWidget() window.resize(800,800) widget_num = 20 #放置控件個數 ###########計算寬度########## widget_width = window.width()/3 #每行有3個控件 ###########計算寬度########## ###########計算高度########## row = (widget_num - 1)//3+1 #每行3個,獲取行數 widget_height = window.height()/row ###########計算高度########## for i in range(0,widget_num): a = QLabel(window) a.resize(widget_width,widget_height) a.setStyleSheet('background-color:red;border:1px solid yellow;font-size:22px;') a.setText(str(i)) ###########移動位位置########## a.move(i % 3 * widget_width, i // 3 * widget_height) #i%3是列號,i//3為行號 ###########移動位位置########## window.show() sys.exit(app.exec_())
要注意的是在移動的時候是計算了行號和列號,在乘以控件大小即可實現。
3.最大尺寸和最小尺寸
a.獲取
window = QWidget() window.minimumWidth() #最小寬度 window.minimumHeight() #最小高度 window.minimumSize() #最小尺寸 window.maximumWidth() #最大寬度 window.maximumHeight() #最大高度 window.maximumSize() #最大尺寸
b.設置
window = QWidget() window.setMaximumWidth(width) #最大寬度 window.setMaximumHeight(height) #最大高度 window.setMaximumSize(width,height) #最大尺寸 window.setMinimumWidth(width) #最小寬度 window.setMinimumHeight(height) #最小高度 window.setMinimumSize(width,height) #最小尺寸
設定完最大/小尺寸,用鼠標拖拽尺寸時會被限制,還有一點,
window.setMaximumSize(500,800)
window.resize(1000,1000)
運行后的尺寸還是500*800。並且手動拖拽限制了最大尺寸。
4.內容邊距
先看一下內容邊距的相關API
label.contentsRect() #獲取標簽內容可以顯示的范圍 label.getContentsMargins() #獲取內容左上右下邊距(未定義邊距時為(0,0,0,0) label.setContentsMargins(up,down,left,right) #設定內容區域(左上右下)
距離的定義是這樣的
5.鼠標操作
a.設置鼠標形狀
window.setCursor(鼠標類型)
鼠標類型有定義好的枚舉值,只要根據需求輸入就可以了

Qt.UpArrowCursor #候選 Qt.CrossCursor #精准選擇 Qt.IBeamCursor #文本選擇 Qt.BusyCursor #后台運行 Qt.WaitCursor #忙 Qt.ForbiddenCursor #不可用 Qt.PointingHandCursor#連接選擇 Qt.WhatsThisCurso #幫助選擇 Qt.SizeVerCursor #垂直調整大小 Qt.SizeHorCursor #水平調整大小 Qt.SizeBDiagCursor #延對角線調整大小2 Qt.SizeAllCursor #移動 Qt.SplitVCursor #垂直分割(無圖示) Qt.SplitHCursor #水平分割(無圖示) Qt.OpenHandCursor #伸開的手掌(無圖示) Qt.ClosedHandCursor #縮緊的拳頭(無圖示). Qt.BlankCursor #空白
上面對應的圖是Win10的常規鼠標設置
b.自定義鼠標
出了默認的鼠標圖像,還可以用自定義圖片(位圖)
pic = QPixmap(r'C:\Users\Aaron\Desktop\111.bmp') #加載位圖路徑 pic2 = pic.scaled(10,10) #對圖片進行縮放 cursor = QCursor(pic2) #自定義鼠標圖片 label.setCursor(cursor)
這里有個要注意的地方,常規的鼠標是有個小箭頭的,比如一個按鈕,必須是箭頭的尖尖指到了才可以點擊,而不是只要這個箭頭在按鈕上就能使用。所以,我們在定義是有兩個參數是可以設置的
QCursor(bitmap,hotX,hotY)
bitmap就是位圖的地址,絕對路徑或相對路徑都是可以的。而hotX和hotY就是上面說鼠標尖尖的位置。如果鼠標的圖像是10*10的,把hotX,hotY設置成(10,10)就是鼠標的右下角在按鈕上才可以點擊按鈕。
c.鼠標恢復到初始狀態
label.setCursor(cursor)
d.鼠標獲取
current_cursor = label.cursor() current_cursor.pos() #獲取鼠標位置 current_cursor.setPos(x,y) #設定鼠標位置
這里獲取的鼠標坐標對應的原點是桌面,而不是父控件。
e.鼠標跟蹤

from PyQt5.Qt import * import sys app=QApplication(sys.argv) class MyWidget(QWidget): def mouseMoveEvent(self, a0): print(self.cursor().pos()) window = MyWidget() window.setMouseTracking(True) #開啟鼠標追蹤 print(window.hasMouseTracking()) window.show() sys.exit(app.exec_())
如果不啟動鼠標追蹤,只有改變了鼠標按鍵狀態(左右鍵和滾輪)才啟動了追蹤
演示一個鼠標追蹤的案例,在window里定義一個label,label的位置隨着鼠標的移動而改變

from PyQt5.Qt import * import sys app=QApplication(sys.argv) class MyWindow(QWidget): def mouseMoveEvent(self, a0): label = self.findChild(QLabel) label.move(a0.localPos().x(),a0.localPos().y()) #localPos()是控件內坐標 label.setText(str(a0.localPos().x())+'\n'+str(a0.localPos().y())) window = MyWindow() window.resize(800,800) window.move(200,200) window.setMouseTracking(True) #啟動鼠標追蹤 label = QLabel(window) label.resize(100,100) label.setStyleSheet('background-color:red;font-size:24px')
6.事件機制
事件機制的API

from PyQt5.Qt import * import sys app=QApplication(sys.argv) class MyWindow(QWidget): def showEvent(self, a0): print('窗口被展開',a0) def closeEvent(self, a0): print('窗口關閉') def moveEvent(self, a0): print('窗口移動') def resizeEvent(self, a0): print('改變窗口尺寸') def mousePressEvent(self, a0): print('鼠標按下') def mouseReleaseEvent(self, a0): print('鼠標彈起') def mouseDoubleClickEvent(self, a0): print('鼠標雙擊') #雙擊時候會觸發鼠標彈起 def enterEvent(self,a0): print('鼠標進入控件') def leaveEvent(self,a0): print('鼠標離開控件') def keyPressEvent(self, a0): print('鍵盤上有按鍵被按下') def keyReleaseEvent(self, a0):\ print('鍵盤上有按鍵彈起') def focusInEvent(self, a0): print('獲取焦點') def focusOutEvent(self, a0): print('失去焦點') def dragEnterEvent(self, a0): print('拖拽進入控件') def dragLeaveEvent(self, a0): print('拖拽離開控件') def dragMoveEvent(self, a0): print('在控件中拖拽') def dropEvent(self, a0): print('拖拽放下') def paintEvent(self, a0): print('繪制事件') def changeEvent(self, a0): print('改變事件') def contextMenuEvent(self, a0): print('右鍵菜單') def inputMethodEvent(self, a0): print('輸入法調用') window = MyWindow() window.show() sys.exit(app.exec_())
事件里的鼠標、鍵盤事件只是觸發了事件,至於是哪個鍵的響應這里還沒說,后期再細說
7.事件轉發機制
Widget的控件有自己的事件轉發機制:如果一個控件沒有處理該事件,則該事件會自動傳遞給父級控件進行處理
例如上圖,一個頂層窗口,一個中間窗口還有一個標簽是依次繼承的,我們定義一個事件,鼠標點擊控件,打印’控件被點擊‘,點擊中間界面打印’中間界面被點擊‘,點擊頂層窗口打印’頂層窗口被點擊’我們把標簽的事件忽略掉,那么事件是會傳遞給中間界面的。所以,這里要引出一個方法
class MyLabel(QLabel): def mousePressEvent(self, a0): a0.ignore() #忽略事件,把事件傳遞給父級控件 a0.isAccepted() #獲取是否處理事件 a0.accept() #處理事件

import sys from PyQt5.Qt import * class MyWindow(QWidget): def mousePressEvent(self, a0): print('頂層鼠標按下') class MidWindow(QWidget): def mousePressEvent(self, a0): print('中間界面被鼠標按下') class MyLabel(QLabel): def mousePressEvent(self, a0): print('標簽控件鼠標按下') print(a0.isAccepted()) a0.ignore() #忽略事件 app = QApplication(sys.argv) window = MyWindow() window.resize(800,600) midwindow = MidWindow(window) midwindow.resize(500,500) midwindow.setAttribute(Qt.WA_StyledBackground,True) midwindow.setStyleSheet('background-color:cyan') midwindow.move(50,50) label = MyLabel(midwindow) label.resize(200,100) label.move(100,100) label.setStyleSheet('background-color:green;font-size:22px') label.setText('這是個標簽') window.show() sys.exit(app.exec_())
8.父子關系補充
API
window = QWidget() label = QLabel(window) window.childAt(x,y) #獲取window內x、y坐標位置存在的控件,無控件返回None label.parentWidget() #獲取控件的父控件 window.childrenRect() #window內所有控件組成的矩形區域(位置、尺寸)(左上角——右下角)
9層級控制
由於界面上的控件是按層級顯示的,就有可能存在被遮擋的可能。先看下層級控制的API
obj.lower() #控件放在最底層 obj.raise_() #控件放在最頂層 a.stackUnder(b) #a放在b下面
上面所說的控件操作必須是同級的控件。一般情況后定義的控件比先定義的控件層級靠前。
下面的案例就是兩個Label,鼠標點擊哪個哪個顯示在前面

import sys from PyQt5.Qt import * app = QApplication(sys.argv) class MyLabel(QLabel): def mousePressEvent(self, ev:QMouseEvent): self.raise_() window = QWidget() window.resize(800,600) label1 = MyLabel(window) label1.resize(300,300) label1.setStyleSheet('background-color:red') label2 = MyLabel(window) label2.resize(300,300) label2.move(50,50) label2.setStyleSheet('background-color:green') window.show() sys.exit(app.exec_())
10.頂層窗口相關操作
a.設定程序圖標
可以改變這個程序圖標
icon = QIcon(r'圖片路徑') window.setWindowIcon(icon)
b.標題
window.setWindowTitle('')
如果是空字符串了,標題展示的字符串就是默認的Python(像上面的圖一樣)如果像不現實,設定空格就行了( ‘ ’)
c.不透明度
window.setWindowOpacity(0.9) #0-1對應透明——不透明 window.windowOpacity() #返回值是個浮點數
獲取的不透明度是個浮點數,和設定的值有些許差異,比如設定值為設定值為0.9,獲取的值為0.8980392156862745
c.窗口狀態
window.setWindowState(Qt.WindowNoState) #無狀態 window.setWindowState(Qt.WindowMaximized) #窗口最大化 window.setWindowState(Qt.WindowMinimized) #窗口最小化 window.setWindowState(Qt.WindowFullScreen) #窗口全屏 window.setWindowState(Qt.WindowActive) #活動窗口 window.windowState() #獲取控件狀態
活動窗口指的是比如有兩個程序,顯示在前面的那個就是活動窗口
d.最大化和最小化
和上面效果的差不多,設置API
window.showFullScreen() #全屏 window.showMaximized() #最大化 window.showMinimized() #最小化 window.showNormal() #正常顯示
判定
window.isMinimized()
window.isMaximized()
window.isFullScreen()
有一點,用這個設置的方法,可以不用show(),直接能顯示窗口。
e.窗口外觀標志
window.setWindowFlags()
用這個窗口的標志位設定能修改出很多的效果,下面就列舉了標志 的枚舉值。

Qt.MSWindowsFixedSizeDialogHint #窗口大小無法調整 Qt.FramelessWindowHint #窗口無邊框,不可拖動大小,移動位置 Qt.CustomizeWindowHint #無邊框,可以拖動大小,不可移動 Qt.WindowTitleHint #標題欄只有關閉按鈕(且不可用?) Qt.WindowSystemMenuHint #效果同上? Qt.WindowMaximizeButtonHint #標題欄內只激活最大化按鈕 Qt.WindowMinimizeButtonHint #標題欄內只激活最小化按鈕 Qt.WindowCloseButtonHint #標題欄只有關閉按鈕(可用) Qt.WindowContextHelpButtonHint #標題欄只有關閉按鈕(不可用)問號按鈕(可用) Qt.WindowStaysOnTopHint #窗口始終顯示在最前 Qt.WindowStaysOnBottomHint #窗口始終顯示在最后
11.交互狀態
控件顯示/禁用
btn.setEnabled() #設定是否可用 btn.isEnabled() #獲取是否可用 btn.setVisible() #設定是否可見 btn.setHidden() #設置隱藏 btn.isHidden() #基於父控件是否被隱藏(父控件不顯示,子控件是可能不被隱藏的) btn.isVisible() #最終狀態是否可見 btn.isVisibleTo() #一個控件是否隨着另一個控件的顯示而顯示
這里要引入幾個知識點:
a我們先運行一下這個程序
import sys from PyQt5.Qt import * app = QApplication(sys.argv) class MyWindow(QWidget): def paintEvent(self, evt): print('窗口被繪制') return super().paintEvent(evt) #不截取繪制的方法,由父類進行繪制 window = MyWindow() window.resize(800,600) class Btn(QPushButton): def paintEvent(self,evt): print('按鈕被繪制') return super().paintEvent(evt) btn = Btn(window) btn.setText('按鈕') btn.clicked.connect(lambda :btn.setVisible(False)) window.show() sys.exit(app.exec_())
在一個窗口里繪制一個按鈕,點擊按鈕后按鈕消失
運行一下看看會發生什么?
在運行程序時打印“窗口被繪制”“按鈕被繪制”,鼠標指向按鈕時按鈕顏色發生變化,再次打印“窗口被繪制”“按鈕被繪制”,點擊按鈕后打印“窗口被繪制”
所以,在每次界面發生改變時,所有的控件都是被依次繪制的。
b控件的顯示時基於父控件的(先畫父控件)如果父控件沒有被展示,即便將子控件設置visable也不會被展示的。
c用ishidden()獲取狀態時,如果父控件沒有顯示,但是有沒有隱藏子控件,返回值是True。
窗口相關
被編輯狀態
window.setWindowTitle('調試[*]') window.setWindowModified(True) print( window.isWindowModified())
這個有什么用呢?
看看是不是顯示的沒有那個中括號了!如果程序被修改可以顯示個星星。(貌似用處不大)
是否為活躍窗口
window.isActiveWindow()
注意的是,並不是哪個在前面哪個一定就是活躍窗口
import sys from PyQt5.Qt import * app = QApplication(sys.argv) w1 = QWidget() w2 = QWidget() w1.show() w2.show() w1.raise_() print(w1.isActiveWindow()) print(w2.isActiveWindow()) sys.exit(app.exec_())
運行以后可以發現通過w1.raise_()把界面提至最前,但返回值依舊為false,所以只有獲取了焦點才能是活躍窗口。
關閉控件
btn =QPushButton(window) btn.setAttribute(Qt.WA_DeleteOnClose,True) #釋放內存 btn.close() #只是不顯示,不釋放內存
釋放內存的設置要放在關閉的前面。
12.信息提示
a.工具提示:鼠標懸停在控件上一段時間后展示在旁邊
btn.setToolTip('這是個按鈕') #定義提示信息 btn.setToolTipDuration(1000) #提示顯示時長(ms) btn.toolTip() #獲取控件提示信息

import sys from PyQt5.Qt import * app = QApplication(sys.argv) window = QWidget() window.resize(800,600) btn = QPushButton(window) btn.setText('按鈕') btn.setToolTip('這是個按鈕') #定義提示信息 btn.setToolTipDuration(1000) #提示顯示時長(ms) btn.toolTip() #獲取控件提示信息 window.show() sys.exit(app.exec_())
b.狀態提示:鼠標停在控件上時,展示在狀態欄上,頂層窗口需要帶狀態欄並且激活
btn.setStatusTip('這是個按鈕') #設定提示信息 btn.statusTip() #獲取控件提示信息

import sys from PyQt5.Qt import * app = QApplication(sys.argv) window = QMainWindow() #組合窗口 window.resize(800,600) window.statusBar() #激活狀態欄 btn = QPushButton(window) btn.setStatusTip('這是個按鈕') #設定提示信息 print(btn.statusTip()) #獲取控件提示信息 window.show() sys.exit(app.exec_()) window.setWindowFlags(Qt.WindowContextHelpButtonHint) btn.setWhatsThis('這是個按鈕') btn.whatsThis() btn.setStatusTip('提示信息')
c."這是啥?"提示:利用幫助按鈕顯示提示
window.setWindowFlags(Qt.WindowContextHelpButtonHint) #先要啟用幫助按鈕 btn.setWhatsThis('這是個按鈕') #設定提示信息 btn.whatsThis() #獲取提示信息

import sys from PyQt5.Qt import * app = QApplication(sys.argv) window = QWidget() window.resize(800,600) btn = QPushButton(window) window.setWindowFlags(Qt.WindowContextHelpButtonHint) #先要啟用幫助按鈕 btn.setWhatsThis('這是個按鈕') #設定提示信息 btn.whatsThis() #獲取提示信息 window.show() sys.exit(app.exec_())
13.焦點控制
首先要明白焦點的定義,比如一個窗口有兩個文本輸入的控件,鍵盤輸入字符,一個控件顯示出輸入的字符,那么這個控件就是獲取焦點的控件。或者通過tab鍵切換按鈕,也就是切換焦點
從單個控件角度來看,先看看API
obj.setFocus() #獲取焦點 obj.setFocusPolicy() #設定焦點策略(枚舉值) obj.clearFocus() #取消焦點

Qt.TabFocus #只能通過Tab鍵獲取焦點 Qt.ClickFocus #只能通過單擊獲取焦點 Qt.StrongFocus#通過上述兩種方式獲取焦點 Qt.NoFocus #禁止通過上述兩種方式獲取焦點
還可以從父控件角度來看焦點的控制
window.focusWidget() #獲取子控件中當前獲得焦點的控件 window.focusNextChild() #聚焦到下個子控件 window.focusPreviousChild() #聚焦到上一個子控件 window.focusNextPrevChild() #Ture:下一個 False :上一個 window.setTabOrder(第一個,第二個) #設置Tab鍵獲取焦點順序

from PyQt5.Qt import * import sys app=QApplication(sys.argv) class Window(QWidget): def mousePressEvent(self, a0:QMouseEvent): if a0.button() == 1:self.focusNextChild() else:self.focusPreviousChild() window = Window() le1 = QLineEdit(window) le2 = QLineEdit(window) le3 = QLineEdit(window) le1.move(100,200) le2.move(150,250) le3.move(200,300) window.show() sys.exit(app.exec_())
案例二就是在界面里有三個文本框,按鼠標左鍵下一個獲得焦點,點擊右鍵上一個獲得焦點。
以上就是QWidget的學習筆記