GUI学习之四——QWidget控件学习总结

Posted yinsedeyinse

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GUI学习之四——QWidget控件学习总结相关的知识,希望对你有一定的参考价值。

上一章将的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(rC:UsersAaronDesktop111.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())+
+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_())
事件API

事件里的鼠标、键盘事件只是触发了事件,至于是哪个键的响应这里还没说,后期再细说

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的学习笔记

以上是关于GUI学习之四——QWidget控件学习总结的主要内容,如果未能解决你的问题,请参考以下文章

GUI学习之二十二——QRubberBand学习总结

GUI学习之二十七——布局管理学习总结

PyQt5 控件学习(一个一个学习之QAbstractButton)

GUI学习之十四——QAbstractSpinBox学习总结

GUI学习之二十三——QDialog学习总结

PyQt5 控件学习(一个一个学习之QKeySequenceEdit)