上一章将的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_())
事件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的学习笔记