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(r‘C: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_())
事件里的鼠标、键盘事件只是触发了事件,至于是哪个键的响应这里还没说,后期再细说
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控件学习总结的主要内容,如果未能解决你的问题,请参考以下文章
PyQt5 控件学习(一个一个学习之QAbstractButton)