zoukankan      html  css  js  c++  java
  • 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_())
    事件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的学习笔记

  • 相关阅读:
    第十六周学习进度报告
    个人课程总结
    第一阶段意见评论
    用户评价
    第二阶段10
    第二阶段9
    第二阶段8
    第十五周学习进度报告
    第二阶段7
    第二阶段6
  • 原文地址:https://www.cnblogs.com/yinsedeyinse/p/10665007.html
Copyright © 2011-2022 走看看