Pyside6 QWidget 控件之父(3)

七、顶层窗口标题、图标及状态设置

前面小节已经学习过了,QWidget及其子类在没有设置父控件时都将作为顶层窗口使用。下列的API即是对顶层窗口的相关设置,即当控件作为子控件时,这些设置不起作用。

API函数

参数说明

返回值

功能作用

setWindowOpacity(self, level)

Level:float

None

设置窗口透明度

windowOpacity(self)

None

float

获取窗口透明度

setWindowIcon(self, icon)

Icon:QIcon

None

设置窗口图标

windowIcon(self)

None

QIcon

获取窗口图标

setWindowTitle(self, a0)

a0:str

None

设置窗口标题

windowTitle(self)

None

Str

获取窗口标题

setWindowState(self,state)

State:windowstate

state是Qt的枚举类

None

设置窗口状态

windowState(self)

None

windowstate

获取窗口状态

showFullScreen(self)

None

None

设置窗口为全屏状态

showMaximized(self)

None

None

设置窗口为最大化状态

showMinimized(self)

None

None

设置窗口为最小化状态

showNormal(self)

None

None

设置窗口为正常状态

isFullSreen(self)

None

bool

返回窗口是否处于全屏状态

isMaximized(self)

None

bool

返回窗口是否处于最大化状态

isminimized(self)

None

bool

返回窗口是否处于最小化状态

这其中几个设置方法在前面键盘事件中我们已经用过,这里再学习一下setWindowState( )方法,实际上它的作用是通过不同参数显示全屏、最大、最小及初始态,showFullScreen()等几个方法是他的方便函数。其参数Qt.Windowstate是一个枚举类,具体如下:

枚举类

枚举常量

枚举值

功能描述

Qt.WindowState

Qt.WindowNoState

0

窗口未设置状态(正常状态)

Qt.WindowMinimized

1

窗口最小化(即图标化)

Qt.WindowMaximized

2

窗口最大化,周围有一个框架

Qt.WindowFullScreen

3

窗口填满整个屏幕,周围没有任何框架。

Qt.WindowActive

4

窗口是活动窗口,即它具有键盘焦点。

窗口图标、标题和透明度设置见下图:(下图窗口透明度设置为0.5,透明度值0-1之间)

其中,setWindowIcon()的参数QIcon是图标类,图标类通常使用QPixmap或者图片的路径字符串作为参数。QIcon将在图片处理类里学习,初期可以直接使用图片路径作字符串。

八、顶层类型及标志设置

顶层窗口的类型是当控件作为顶层窗口时,其被指示为那种窗口,并且按照指示窗口的类型显示相应的组件。如,边框、最大、最小化按钮及标题栏等。它是一个枚举类:

枚举常量

枚举值

功能描述

Qt.Widget

0x00000000

这是QWidget的默认类型。这种类型的控件如果有父控件则为子控件,如果它们没有父控件则为独立窗口。

Qt.Window

0x00000001

指示该控件是一个窗口,通常带有一个窗口系统框架和一个标题栏,而不管该控件是否有父控件。请注意,如果小部件没有父级,则无法取消设置此标志。

Qt.Dialog

0x00000002 | Window

大小在给定矩形之外缩放为尽可能小的矩形,同时保持纵横比。

Qt.Sheet

0x00000004 | Window

指示窗口是 macOS 上的工作表。由于使用工作表意味着窗口模态,因此推荐的方法是使用 QWidget::setWindowModality() 或 QDialog::open()

Qt.Popup

0x00000008 | Window

指示控件是弹出式顶层窗口,即它是模式窗口,但具有适用于弹出菜单的窗口系统框架。

Qt.Tool

Popup | Dialog

指示微件是工具窗口。工具窗口通常是一个小窗口,其标题栏和装饰比平常小,通常用于工具按钮的集合。如果存在父级,则工具窗口将始终位于其顶部。如果没有父级,您也可以考虑使用Qt::WindowStaysOnTopHint。如果窗口系统支持它,则可以用更轻的框架装饰工具窗口。它也可以与Qt::FramelessWindowHint结合使用。

Qt.ToolTip

Popup | Sheet

指示控件为工具提示。

Qt.SplashScreen

ToolTip | Dialog

指示窗口为初始屏幕。这是 QSplashScreen 的默认类型。

Qt.SubWindow

0x00000012

指示此小组件是一个子窗口,例如 QMdiSubWindow 小组件。

Qt.MSWindowsFixedSizeDialogHint

0x00000100

在 Windows 上边框变为细线条,用于固定大小的对话框。多监视器环境中不能使用此标志

Qt.FramelessWindowHint

0x00000800

生成无边框窗口。用户无法通过窗口系统移动无边框窗口或调整其大小。

Qt.CustomizeWindowHint

0x02000000

生成的窗口有边框但无标题栏和按钮,不能移动和拖动

Qt.WindowTitleHint

0x00001000

为窗口添加标题栏和按钮

Qt.WindowSystemMenuHint

0x00002000

添加一个系统目录和关闭按钮

Qt.WindowMinimizeButtonHint

0x00004000

添加最小化按钮。

Qt.WindowMaximizeButtonHint

0x00008000

添加最大化按钮。

Qt.WindowMinMaxButtonsHint

WindowMinimizeButtonHint | WindowMaximizeButtonHint

添加最小化和最大化按钮

Qt.WindowCloseButtonHint

0x08000000

添加关闭按钮。

Qt.WindowContextHelpButtonHint

0x00010000

向对话框添加问号按钮

Qt.MacWindowToolBarButtonHint

0x10000000

在 macOS 上,添加了一个工具栏按钮(即,位于具有工具栏的窗口右上角的长方形按钮)

Qt.WindowFullscreenButtonHint

0x80000000

在 macOS 上,添加一个全屏按钮。

Qt.BypassGraphicsProxyWidget

0x20000000

防止窗口及其子窗口自动嵌入到 QGraphicsProxyWidget 中(如果父窗口小部件已嵌入)。如果您希望微件始终是桌面上的顶级微件,则无论父微件是否嵌入在场景中,都可以设置此标志。

Qt.WindowShadeButtonHint

0x00020000

添加一个阴影按钮来代替最小化按钮(如果基础窗口管理器支持)。

这个属性都是通过setWindowFlags( )方法进行设置的。

API函数

参数说明

返回值

功能作用

setWindowFlags(type)

type:QtCore.Qt.WindowFlags, QtCore.Qt.WindowType

None

设置窗口样式和标志

windowFlags(self)

None

type:QtCore.Qt.WindowFlags, QtCore.Qt.WindowType

获取窗口样式和标志

example:这个例子是从pyqt的官网转过来的,暂时看不明白没关系。将程序运行起来,主要是看Qt.WindowTypes在组合情况的显示样式

from PySide6.QtCore import Qt
from PySide6.QtWidgets import (QApplication, QCheckBox, QGridLayout, QGroupBox,
        QHBoxLayout, QPushButton, QRadioButton, QTextEdit, QVBoxLayout,
        QWidget)
import sys

class PreviewWindow(QWidget):
    def __init__(self, parent=None):
        super(PreviewWindow, self).__init__(parent)

        self.textEdit = QTextEdit()
        self.textEdit.setReadOnly(True)
        self.textEdit.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap)

        closeButton = QPushButton("&Close")
        closeButton.clicked.connect(self.close)

        layout = QVBoxLayout()
        layout.addWidget(self.textEdit)
        layout.addWidget(closeButton)
        self.setLayout(layout)

        self.setWindowTitle("Preview")


    def setWindowFlags(self, flags):
        super(PreviewWindow, self).setWindowFlags(flags)
        flag_type = (flags & Qt.WindowType_Mask)

        if flag_type == Qt.Window:
            text = "Qt.Window"
        elif flag_type == Qt.Dialog:
            text = "Qt.Dialog"
        elif flag_type == Qt.Sheet:
            text = "Qt.Sheet"
        elif flag_type == Qt.Drawer:
            text = "Qt.Drawer"
        elif flag_type == Qt.Popup:
            text = "Qt.Popup"
        elif flag_type == Qt.Tool:
            text = "Qt.Tool"
        elif flag_type == Qt.ToolTip:
            text = "Qt.ToolTip"
        elif flag_type == Qt.SplashScreen:
            text = "Qt.SplashScreen"
        else:
            text = ""

        if flags & Qt.MSWindowsFixedSizeDialogHint:
            text += "\n| Qt.MSWindowsFixedSizeDialogHint"
        if flags & Qt.X11BypassWindowManagerHint:
            text += "\n| Qt.X11BypassWindowManagerHint"
        if flags & Qt.FramelessWindowHint:
            text += "\n| Qt.FramelessWindowHint"
        if flags & Qt.WindowTitleHint:
            text += "\n| Qt.WindowTitleHint"
        if flags & Qt.WindowSystemMenuHint:
            text += "\n| Qt.WindowSystemMenuHint"
        if flags & Qt.WindowMinimizeButtonHint:
            text += "\n| Qt.WindowMinimizeButtonHint"
        if flags & Qt.WindowMaximizeButtonHint:
            text += "\n| Qt.WindowMaximizeButtonHint"
        if flags & Qt.WindowCloseButtonHint:
            text += "\n| Qt.WindowCloseButtonHint"
        if flags & Qt.WindowContextHelpButtonHint:
            text += "\n| Qt.WindowContextHelpButtonHint"
        if flags & Qt.WindowShadeButtonHint:
            text += "\n| Qt.WindowShadeButtonHint"
        if flags & Qt.WindowStaysOnTopHint:
            text += "\n| Qt.WindowStaysOnTopHint"
        if flags & Qt.WindowStaysOnBottomHint:
            text += "\n| Qt.WindowStaysOnBottomHint"
        if flags & Qt.CustomizeWindowHint:
            text += "\n| Qt.CustomizeWindowHint"

        self.textEdit.setPlainText(text)

class ControllerWindow(QWidget):
    def __init__(self):
        super(ControllerWindow, self).__init__()

        self.previewWindow = PreviewWindow(self)

        self.createTypeGroupBox()
        self.createHintsGroupBox()

        quitButton = QPushButton("&Quit")
        quitButton.clicked.connect(self.close)

        bottomLayout = QHBoxLayout()
        bottomLayout.addStretch()
        bottomLayout.addWidget(quitButton)

        mainLayout = QVBoxLayout()
        mainLayout.addWidget(self.typeGroupBox)
        mainLayout.addWidget(self.hintsGroupBox)
        mainLayout.addLayout(bottomLayout)
        self.setLayout(mainLayout)

        self.setWindowTitle("Window Flags")
        self.updatePreview()

    def updatePreview(self):
        flags = self.windowFlags()

        if self.windowRadioButton.isChecked():
            flags = Qt.Window
        elif self.dialogRadioButton.isChecked():
            flags = Qt.Dialog
        elif self.sheetRadioButton.isChecked():
            flags = Qt.Sheet
        elif self.drawerRadioButton.isChecked():
            flags = Qt.Drawer
        elif self.popupRadioButton.isChecked():
            flags = Qt.Popup
        elif self.toolRadioButton.isChecked():
            flags = Qt.Tool
        elif self.toolTipRadioButton.isChecked():
            flags = Qt.ToolTip
        elif self.splashScreenRadioButton.isChecked():
            flags = Qt.SplashScreen

        if self.msWindowsFixedSizeDialogCheckBox.isChecked():
            flags |= Qt.MSWindowsFixedSizeDialogHint
        if self.x11BypassWindowManagerCheckBox.isChecked():
            flags |= Qt.X11BypassWindowManagerHint
        if self.framelessWindowCheckBox.isChecked():
            flags |= Qt.FramelessWindowHint
        if self.windowTitleCheckBox.isChecked():
            flags |= Qt.WindowTitleHint
        if self.windowSystemMenuCheckBox.isChecked():
            flags |= Qt.WindowSystemMenuHint
        if self.windowMinimizeButtonCheckBox.isChecked():
            flags |= Qt.WindowMinimizeButtonHint
        if self.windowMaximizeButtonCheckBox.isChecked():
            flags |= Qt.WindowMaximizeButtonHint
        if self.windowCloseButtonCheckBox.isChecked():
            flags |= Qt.WindowCloseButtonHint
        if self.windowContextHelpButtonCheckBox.isChecked():
            flags |= Qt.WindowContextHelpButtonHint
        if self.windowShadeButtonCheckBox.isChecked():
            flags |= Qt.WindowShadeButtonHint
        if self.windowStaysOnTopCheckBox.isChecked():
            flags |= Qt.WindowStaysOnTopHint
        if self.windowStaysOnBottomCheckBox.isChecked():
            flags |= Qt.WindowStaysOnBottomHint
        if self.customizeWindowHintCheckBox.isChecked():
            flags |= Qt.CustomizeWindowHint

        self.previewWindow.setWindowFlags(flags)

        pos = self.previewWindow.pos()
        if pos.x() < 0:
            pos.setX(0)

        if pos.y() < 0:
            pos.setY(0)
        self.previewWindow.move(pos)
        self.previewWindow.show()

    def createTypeGroupBox(self):
        self.typeGroupBox = QGroupBox("Type")

        self.windowRadioButton = self.createRadioButton("Window")
        self.dialogRadioButton = self.createRadioButton("Dialog")
        self.sheetRadioButton = self.createRadioButton("Sheet")
        self.drawerRadioButton = self.createRadioButton("Drawer")
        self.popupRadioButton = self.createRadioButton("Popup")
        self.toolRadioButton = self.createRadioButton("Tool")
        self.toolTipRadioButton = self.createRadioButton("Tooltip")
        self.splashScreenRadioButton = self.createRadioButton("Splash screen")
        self.windowRadioButton.setChecked(True)

        layout = QGridLayout()
        layout.addWidget(self.windowRadioButton, 0, 0)
        layout.addWidget(self.dialogRadioButton, 1, 0)
        layout.addWidget(self.sheetRadioButton, 2, 0)
        layout.addWidget(self.drawerRadioButton, 3, 0)
        layout.addWidget(self.popupRadioButton, 0, 1)
        layout.addWidget(self.toolRadioButton, 1, 1)
        layout.addWidget(self.toolTipRadioButton, 2, 1)
        layout.addWidget(self.splashScreenRadioButton, 3, 1)
        self.typeGroupBox.setLayout(layout)

    def createHintsGroupBox(self):
        self.hintsGroupBox = QGroupBox("Hints")

        self.msWindowsFixedSizeDialogCheckBox = self.createCheckBox("MS Windows fixed size dialog")
        self.x11BypassWindowManagerCheckBox = self.createCheckBox("X11 bypass window manager")
        self.framelessWindowCheckBox = self.createCheckBox("Frameless window")
        self.windowTitleCheckBox = self.createCheckBox("Window title")
        self.windowSystemMenuCheckBox = self.createCheckBox("Window system menu")
        self.windowMinimizeButtonCheckBox = self.createCheckBox("Window minimize button")
        self.windowMaximizeButtonCheckBox = self.createCheckBox("window maximize button")
        self.windowCloseButtonCheckBox = self.createCheckBox("window close button")
        self.windowContextHelpButtonCheckBox = self.createCheckBox("Window context help button")
        self.windowShadeButtonCheckBox = self.createCheckBox("Window shade button")
        self.windowStaysOnTopCheckBox = self.createCheckBox("Window stays on top")
        self.windowStaysOnBottomCheckBox = self.createCheckBox("Window stays on bottom")
        self.customizeWindowHintCheckBox = self.createCheckBox("Customize window")

        layout = QGridLayout()
        layout.addWidget(self.msWindowsFixedSizeDialogCheckBox, 0, 0)
        layout.addWidget(self.x11BypassWindowManagerCheckBox, 1, 0)
        layout.addWidget(self.framelessWindowCheckBox, 2, 0)
        layout.addWidget(self.windowTitleCheckBox, 3, 0)
        layout.addWidget(self.windowSystemMenuCheckBox, 4, 0)
        layout.addWidget(self.windowMinimizeButtonCheckBox, 0, 1)
        layout.addWidget(self.windowMaximizeButtonCheckBox, 1, 1)
        layout.addWidget(self.windowCloseButtonCheckBox, 2, 1)
        layout.addWidget(self.windowContextHelpButtonCheckBox, 3, 1)
        layout.addWidget(self.windowShadeButtonCheckBox, 4, 1)
        layout.addWidget(self.windowStaysOnTopCheckBox, 5, 1)
        layout.addWidget(self.windowStaysOnBottomCheckBox, 6, 1)
        layout.addWidget(self.customizeWindowHintCheckBox, 5, 0)
        self.hintsGroupBox.setLayout(layout)

    def createCheckBox(self, text):
        checkBox = QCheckBox(text)
        checkBox.clicked.connect(self.updatePreview)
        return checkBox

    def createRadioButton(self, text):
        button = QRadioButton(text)
        button.clicked.connect(self.updatePreview)
        return button

if __name__ == '__main__':
    app = QApplication(sys.argv)
    controller = ControllerWindow()
    controller.show()
    sys.exit(app.exec())

九、QWidget的交互状态

API函数

参数说明

返回值

功能作用

activateWindow(self)

None

None

将包含此构件的顶级构件设置为活动窗口。

isActiveWindow(self)

None

None

查看此窗口是否为活动窗口。

setEnabled(self, arg__1)

arg_1:bool

None

设置控件是否激活

isEnabled(self)

None

bool

查看控件是否激活

isEnabledTo(self, arg__1)

arg_1:QWidget

bool

如果启用了同窗口的父控件,则此小部件将被启用,则返回true;否则返回false

isWindow(self)

None

bool

查看部件是否为独立窗口/顶层窗口

window(self)

Noen

QWidget

返回控件所在的顶层窗口

setWindowModified(self, arg__1)

arg_1:bool

None

设置窗口是否显示有未保存的更改

isWindowModified(self)

None

bool

查看窗口是否是否显示有未保存的更改

所谓交互状态是指控件是否为活动窗口、是否可以被使用等属性。

活动状态的窗口具有获取焦点的特征,与鼠标点击窗口的效果一致。但需要注意的是,不是放在底层窗口就不活动。

setEnable()和isEnable()这两个方法主要体现在其子类控件上,如按钮的如果isEnable()为Flase将被禁用,无法点击。

isEnableTo()方法与上面两个方法有一定的区别,它主要是考察的同窗口下的父控件。

最后,setWindowModified()方法的用法是,标识窗口内容是否已被编辑,即是否进行了修改。但要使用该方法,前提是需调用setWidowTitle()方法,且其参数str中文本前要加上“[*]”。

十、QWidget的信息提示

QWidget的信息提示主要是指控件状态提示信息,控件的气泡提示以及“?”这个帮助按钮。控件的状态提示信息通常是在窗口的状态栏显示,控件的状态栏是QStatusBar的实例,将在QMainWindow章节学习。控件的气泡提示是当鼠标停留在控件上时显示,而“?”帮助按钮提示信息,需要窗口带有帮助按钮,且点击“?”后再点击相应控件。

API函数

参数说明

返回值

功能作用

setStatusTip(self, arg__1)

arg_1:str

None

设置提示信息,再状态栏显示

statusTip(self)

None

str

获取将要在状态栏显示的提示信息

setToolTip(self, arg__1)

arg_1:str

None

设置气泡提示

toolTip(self)

None

str

获取气泡提示

setToolTipDuration(self, msec)

msec:int

(毫秒)

None

设置提示气泡的存在时长

toolTipDuration(self)

None

int

获取提示气泡的存在时长

setWhatsThis(self, arg__1)

arg_1:str

None

设置帮助信息

whatsThis(self)

None

str

获取帮助信息

example:

from PySide6.QtWidgets import QApplication,QWidget,QPushButton,QLabel,QStatusBar
from PySide6.QtCore import Qt
import sys

class Mytest(QWidget):
   def __init__(self):
      super(Mytest, self).__init__()
      self.setWindowTitle("QWidget信息提示")
      self.resize(500,500)
      #改变窗口样式为信息提示窗口
      self.setWindowFlags(Qt.WindowType.WindowContextHelpButtonHint | Qt.WindowType.WindowCloseButtonHint)
      
      # 创建一个状态栏
      self.statusBar = QStatusBar(self)
      self.statusBar.setStyleSheet("background:green")
      self.statusBar.resize(self.width(),40)
      self.statusBar.move(0,460)

      # 设置状态信息
      self.setStatusTip("我被鼠标点了")

      # 创建一个按钮
      btn = QPushButton("测试按钮",self)
      # 设置按钮提示,在旁边气泡提示
      btn.setToolTip("你要点我吗?")
      # 获取气泡提示信息
      print(btn.toolTip())
      # 设置气泡提示时长(毫秒)
      btn.setToolTipDuration(2000)
      # 获取气泡提示信息
      print(btn.toolTipDuration())

      label = QLabel(self)
      label.setText("标签")
      label.move(100, 100)
      # 设置标签的这是啥提示,当点击窗口上方“?”时,鼠标
      # 改变为带问号样式,再点击标签就会显示设置的提示信息
      label.setWhatsThis("这是啥?你说这是啥!")
      # 获取这是啥提示的信息内容
      print(label.whatsThis())

   def mousePressEvent(self, event):
       super(Mytest, self).mousePressEvent(event)
       # 鼠标点击时,将状态信息展示在状态栏
       # 状态栏的showMessage()方法第一个参数是需要展示的信息,第二个是消息停留时间ms
       self.statusBar.showMessage(self.statusTip(),2000)

if __name__ == '__main__':
   app = QApplication(sys.argv)
   main = Mytest()
   main.show()
   sys.exit(app.exec())

运行结果:

20240914-151431

十一、QWidget的尺寸及尺寸策略

因本小节的内容对初学者相对不友好,故将其放置再布局管理器章节。

十二、QWidget的焦点获取及焦点策略

1.焦点获取及焦点策略的相关API

API函数

参数说明

返回值

功能作用

setFocus(self)

None

None

如果控件或其父窗口是活动窗口,将焦点设置给此控件。

setFocus(self, reason)

reason: Qt.FocusReason)

参数为Qt枚举类

None

如果控件或其父窗口是活动窗口,将焦点设置给此控件。

reason参数用于解释导致焦点获取的原因。

hasFocus(self)

None

bool

查看控件是否拥有焦点

focusWidget(self)

None

QWidget

获取拥有焦点的控件

setFocusPolicy(self, policy)

policy:

Qt.FocusPolicy

参数为Qt枚举类

None

设置控件的焦点策略

focusPolicy(self)

None

Qt.FocusPolicy

获取控件的焦点策略

setFocusProxy(self, arg__1)

arg_1:QWidget

None

将小部件的焦点代理设置为小部件w。如果w为None,则函数会将此小部件重置为没有焦点代理。

focusProxy(self)

None

QWidget

获取小部件焦点代理

clearFocus(self)

None

None

清除控件焦点

setTabOrder(self, arg__1, arg__2)

arg_1:QWidget

arg_2:QWidget

None

设置在使用tab键时控件获取焦点顺序。将W2放在W1之后。

focusNextChild(self)

None

bool

向后查找一个新的小部件,以使键盘聚焦于Tab,如果它能找到新小部件,则返回true,如果不能,则返回false。

focusPreviousChild(self)

None

bool

向前查找一个新的小部件,以使键盘聚焦于Tab,如果它能找到新小部件,则返回true,如果不能,则返回false。

focusNextPrevChild(self, next)

next:bool

bool

查找一个新的小部件以使键盘聚焦,适用于Tab和Shift+Tab,如果可以找到新小部件,则返回true,如果不能找到,则返回false。如果next为true,则此函数向前搜索,如果next是false,则向后搜索。

nextInFocusChain(self)

None

QWidget

返回此控件焦点链中的下一个控件

2.焦点概念及获取焦点

所谓获取焦点就是激活控件,能够被键盘输入的控件,如单行文本框(QLineEdit)、多行文本框(QTextEdit)等激活后会光标闪烁,而对于按钮等鼠标输入的控件,获取焦点后通常会显示焦点边框或者高亮。

20240914-162649

上面的例子我们创建了两个单行文本框,两个按钮,通过Tab键切换焦点,可以直观的理解焦点的含义。如果我们需要在编程过程中让某一个控件获取焦点,则需要调用下列方法:

3.活动窗口、顶层窗口、焦点窗口的概念

所谓的顶层窗口就是在多个窗口堆叠时,最上面的窗口;而活动窗口则是可与用户交互的窗口;焦点窗口是获得焦点可以进行鼠标、键盘操作的窗口。

前面的层级关系中,我们也看到了活动窗口未必是顶层窗口,但通常来说活动窗口是自动获得键盘焦点的,也是焦点窗口。

焦点窗口的必须条件是窗口父控件或其子控件可获得焦点。比如一个QLabel做为顶层窗口,即使他是活动的,但也无法获取焦点。但,只要是QWidget对象的继承类控件都可以通过修改其焦点策略从而获取焦点。

还有就是,控件获取焦点后未必有特殊的外观表现,比如QLabel,它获取焦点后不会如QLineEdit一样有光标闪烁。而且,我们还要区分,焦点和光标并不是一个东西,QLineEdit的光标闪烁只是其获取焦点后激活了光标。

4.焦点策略与焦点代理

焦点策略理解起来比较简单,从其枚举常量的表述我们就可以看出它是控件获取焦点方式。比如,TabFocus就是通过按Tab键切换获取焦点,ClickFocus是通过鼠标点击而获取焦点。

而焦点代理的意思就是,当控件A获取焦点时,我们设置另一个控件B做为焦点代理,代替控件A处理焦点事件。比如setFocus、focusInEvent、focusOutEvent等均有代理控件B处理。

枚举类

枚举常量

枚举值

功能描述

Qt.FocusReason

MouseFocusReason

0

发生了鼠标操作

TabFocusReason

1

按下了Tab键

BacktabFocusReason

2

发生了后退选择。如shift+Tab

ActiveWindowFocusReason

3

窗口系统使此窗口处于活动或非活动状态。

PopupFocusReason

4

应用程序打开/关闭了一个弹出窗口,该弹出窗口抓取/释放了焦点

ShortcutFocusReason

5

用户键入了标签的快捷方式

MenuBarFocusReason

6

菜单栏成为焦点

OtherFocusReason

7

其他原因。

Qt.FocusPolicy

TabFocus

0

控件通过 Tab 接受焦点。

ClickFocus

1

控件通过单击接受焦点。

StrongFocus

2

控件通过 Tab 键和单击接受焦点。在 macOS 上,这也将指示小部件在“文本/列表焦点模式”时接受选项卡焦点。

WheelFocus

3

控件通过 Tab 键和单击以及使用鼠标滚轮接受焦点

NoFocus

4

小组件不接受焦点

example:

from PySide6.QtWidgets import QApplication,QWidget,QLineEdit,QLabel,QVBoxLayout
from PySide6.QtCore import Qt
import sys

class Window(QWidget):

    def __init__(self):
        super().__init__()
        self.resize(500,500)
        self.setWindowTitle("焦点策略与焦点代理示例")

        self.line1 = QLineEdit(self)
        self.line1.move(200, 150)
        self.line2 = QLineEdit(self)
        self.line2.move(200, 250)

        self.lab = QLabel(self)
        self.lab.setStyleSheet("background:red")
        self.lab.resize(self.width(),100)
        self.lab.move(0,350)

        # 查看lab的焦点策略
        print(self.lab.focusPolicy())
        # 将lab的焦点策略设置为鼠标点击获取
        self.lab.setFocusPolicy(Qt.FocusPolicy.ClickFocus)
        # 设置lab的焦点代理为line2
        self.lab.setFocusProxy(self.line2)

if __name__ == '__main__':
    App = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(App.exec())

运行结果:

20240914-172319

控制台输出结果:

FocusPolicy.NoFocus

上面的里中,对第一个于QLineEdit来说,他是自动获得焦点的。所以,我们通过将不接受焦点的lab的焦点策略改为鼠标点击,而第二个QlineEdit作为其焦点代理,形成结果为点击lab,self.line2获取焦点。

5.焦点链

Pyside除了把控件对象组织成树状结构外,还把这些控件组织成了一个双向链表结构,这个链表叫做焦点链。焦点链上每个节点代表一个控件,默认情况下,控件在焦点链上的先后位置,与用户把控件添加到窗口的先后顺序有关,越早添加到窗口上的控件,其在焦点链中的位置越靠前。

通过按Tab或者Shift+Tab,可以实现焦点在各个控件之间循环移动。焦点移动顺序与焦点链相关,它的移动规律如下:

1.点击Tab键,焦点链指针向后移动,直至碰到第一个FocusPolicy为TabFocus的窗口,并设置该控件为焦点控件;

2.点击Shift+Tab,焦点链指针向前移动,直至碰到第一个FocusPolicy为TabFocus的控件,并设置该窗口为焦点控件。

from PySide6.QtWidgets import QApplication,QWidget,QLineEdit,\
    QGridLayout,QVBoxLayout,QPushButton
import sys

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(500,500)
        self.windowTitle("焦点链示例")

        self.line1 = QLineEdit()
        self.line2 = QLineEdit()
        self.line3 = QLineEdit()
        self.line4 = QLineEdit()
        
        self.btn1 = QPushButton("停止计时器")
        self.btn2 = QPushButton("查看焦点链节点控件")
        self.btn3 = QPushButton("改变焦点链节点顺序")
        self.btn4 = QPushButton("自定义焦点链")

        self.line1.setObjectName("line1")
        self.line2.setObjectName("line2")
        self.line3.setObjectName("line3")
        self.line4.setObjectName("line4")
        self.btn1.setObjectName("btn1")
        self.btn2.setObjectName("btn2")
        self.btn3.setObjectName("btn3")
        self.btn4.setObjectName("btn4")
        self.setObjectName("window")
        
        # 创建子布局,并将按钮添加到子布局中
        child_lay = QGridLayout()
        child_lay.addWidget(self.btn1, 0, 0)
        child_lay.addWidget(self.btn2, 0, 1)
        child_lay.addWidget(self.btn3, 1, 0)
        child_lay.addWidget(self.btn4, 1, 1)
        
        # 创建主布局,将单行文本框和子布局添加到主布局中
        # 布局这部分还没学,不用去理解,只要知道他们将控件排列整齐就行了
        lay = QVBoxLayout(self)
        lay.addWidget(self.line1)
        lay.addWidget(self.line2)
        lay.addWidget(self.line3)
        lay.addWidget(self.line4)
        lay.addLayout(child_lay)
        
        # 创建一个临时定时器
        self.id = self.startTimer(2000)

        self.btn1.clicked.connect(self.stop_timer)
        self.btn2.clicked.connect(self.get_all_focusWidget)
        self.btn3.clicked.connect(self.changeFocus)
        self.btn4.clicked.connect(self.custom_focus)

    def stop_timer(self):
        # 停止定时器
        print("------------停止计时器-----------", end="\n\n")
        self.killTimer(self.id)
        self.line1.setFocus()

    def get_all_focusWidget(self):
        # 查看程序运行后焦点链
        self.line1.setFocus()
        first = self.focusWidget()
        print("当前窗口的焦点链为:")
        self.get_current_focusOrder()

    def changeFocus(self):
        # 部分改变焦点链的情况
        self.line3.setFocus()
        self.setTabOrder(self.line3, self.line1)
        self.setTabOrder(self.line1, self.line4)
        self.setTabOrder(self.line4, self.line3)
        print("更改后的焦点链为:")
        self.get_current_focusOrder()

    def custom_focus(self):
        # 完整的改变焦点链的顺序
        self.line3.setFocus()
        self.setTabOrder(self.line3, self.line1)
        self.setTabOrder(self.line1, self.line4)
        self.setTabOrder(self.line4, self.line2)
        self.setTabOrder(self.line2, self.btn1)
        self.setTabOrder(self.btn1, self.btn3)
        self.setTabOrder(self.btn3, self.btn2)
        self.setTabOrder(self.btn2, self.btn4)
        self.setTabOrder(self.btn4, self)
        self.setTabOrder(self, self.line1)
        print("自定义后的焦点链为:")
        self.get_current_focusOrder()

    def get_current_focusOrder(self):
        # 获取焦点链方法,供上述方法调用
        first = self.focusWidget()
        print(first.objectName(), end="-->")
        for i in range(len(self.children()) + 2):
            print(first.nextInFocusChain().objectName(), end="-->")
            first = first.nextInFocusChain()
        print("")

    def timerEvent(self, event):
        super(Window, self).timerEvent(event)
        # 重写计时器时间,获取计时器调用时焦点获取的情况
        print("-----------计时器启动-----------")
        print("此时获取焦点的控件是{}".format(self.focusWidget().objectName()))
        print("此时line1是否获取焦点,{}".format(self.line1.hasFocus()))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    # 窗口显示后查看line1是否获取焦点
    print("程序开始运行,获取焦点的控件是{}".format(window.focusWidget()))
    print("程序开始运行,line1是否获取焦点,{}".format(window.line1.hasFocus()))
    sys.exit(app.exec())

运行结果:

20240914-203243

从控制台的输出结果可以看出,虽然line1会自动获取焦点。但是在初始化状态时,并没有控件获取焦点。待窗口显示完成后,才将焦点设置给line1。

焦点链中各节点间顺序的更改通过setTabOrder()、focusNextChild(self)、focusPreviousChild(self)、focusNextPreviousChild(self)等方法进行更改。这几个方法不再举例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值