14.PyQt5应用程序主窗口QmainWindow详解

PyQt5 应用程序主窗口

对于日常见到的应用程序而言,许多都是基于主窗口的,主窗口包含了菜单栏、工具栏、状态栏和中心区域等。

QT/PyQt中提供了以QmainWindow类为核心的主窗口框架,它包含了众多相关的类,它们的继承关系如下图所示:

20221120233606

一. QMainWindow类(主窗口)基础

QMainWindow 类继承自 QWidget 类, 因此主窗口就是一个普通的部件,只不过主窗口拥有自已的布局,在主窗口中特定的位置只能添加特定的子部件而已。 因为主窗口有自已的布局,因此不能在主窗口上设置布局管理器。

下图为主窗口的布局:

20221120233853

  • 中心部件:主窗口必须有且只能有一个中心部件。中心部件可以是任意类型的部件(比如 QTextEdit、 QLineEDit 等)。中心部件使用 setCentralWidget()函数设置
  • 状态栏:状态栏主要用于向向户显示一些程序的状态信息,状态栏位于主窗口底部,一个主窗口只能有一个状态栏。使用 setStatursBar()函数可设置状态栏。
  • 工具栏: 工具栏通常是由一系列的类似按钮的部件组成的。 使用 addToolBar()可以把工具栏添加到主窗口。工具栏是由 QToolBar 类实现的。一个主窗口可以有多个工具栏,工具栏可以位于主窗口的上、下、左、右四个方向上。
  • Dock 部件(悬浮窗口、可停靠窗口):一个主窗口可以有多个可停靠窗口。 使用addDockWidget()可以把可停靠窗口添加到主窗口。
  • 菜单与菜单栏:菜单位于菜单栏之中,一个主窗口只能有一个菜单栏,一个菜单栏中可以包含多个菜单。 QMainWindow 有一个默认的菜单栏,该菜单栏使用 menuBar()函数返回。

二. QMenu类(菜单)、QMenuBar类(菜单栏)、QAction类(动作)基础

QMenu 和 MenuBar 类都继承自 QWidget。

20221120234327

1. 基本概念

20221120234543

  • 菜单:为便于讲解本文会把单独的一个菜单项和含有其他菜单项的菜单统称为菜单,因此菜单这一概念比较易产生混淆
  • 弹出菜单、下拉菜单:展开后包含其他菜单或菜单项的菜单称为下拉菜单或弹出菜单。通常顶级菜单是弹出菜单
  • 主菜单:如上图所示,“文件”、“编辑”、“视图”等都是主菜单
  • 子菜单:位于弹出菜单中的弹出菜单称为子菜单,因此子菜单也是弹出菜单。通常子菜单在右侧有一个向右的箭头。比如上图中的“新建”、“打开”都是子菜单
  • 系统菜单: 在标题栏最左边的小图标上左击或右击鼠标,会激活系统菜单
  • 快捷菜单(上下文菜单):指的是点击鼠标右键弹出的菜单,这种菜单也是弹出菜单
  • 标记菜单: 菜单项可以被选中,即在菜单文本左侧显示一个小的选中标记(一般为一个勾形符号),这种类型的菜单被称为标记菜单。 顶级菜单不能被选中
  • 助记符:是指使用“AlT+某个字符”形式的快捷键,通常这个字符会加上下划线或被小括号括起来,如上图中的“文件(F)”,其中的 F 就是助记符
  • 快捷键、加速键、助记符:把 Ctrl+C(复制)这种类型的快捷键称为加速键,其实助记符和加速键都是快捷键,为避免引起概念混淆,本文尽量注意区分

2. 创建菜单的方法

  • QMenu 类可用于创建菜单,该类可创建单独的一个菜单项,并可使用 QMenu::addMenu()函数把一个菜单项添加到另一个菜单项中,从而创一个子菜单
  • QMenuBar 是菜单栏,使用 QMenu 创建的菜单通常会添加到菜单栏中。 使用QMenuBar::addMenu()函数可把菜单添加到菜单栏之中
  • 一个菜单只应使用 addMenu()函数添加一次,后续的添加将是无效的
  • QMenu 和 MenuBar 类都继承自 QWidget,可见,他们其实就是一个 QWidget 部件,只不过他们有些特殊的限制。因此 QMenuBar 可以以窗口的形式使用 show()被单独显示出来,但 QMenu 应使用 exec()来显示
  • 下面是 addMenu 函数的原型(QMenu::addMenu 和 QMenuBar::addMenu 功能是相同的)
    • QAction* addMenu(QMenu *menu);
      添加菜单 menu 到菜单栏或菜单中,并返回与此菜单关联的 Action(动作)。此函数不会使菜单或菜单栏拥有 menu 的所有权(关于所有权,见后文)。
    • QMenu* addMenu(const QString &title);
      QMenu* addMenu(const QIcon& icon, const QString &title);
      使用 title 和 icon 创建一个新菜单,并把其添加到菜单或菜单栏,并返回该新菜单,菜单栏或菜单拥有该新菜单的所有权

3. 部件所有权

所有权的意义:比如部件 1 若拥有部件 2 的所有权,则当部件 1 被删除(销毁)时,会删除部件 2

4. QAction(动作)基础

  • QAction 用于使来自不同地方的命令都能够以相同的方式执行,比如 QAction 可以使菜单、工具栏和快捷键执行相同的操作
  • QAction 就是一个动作,只不过这个动作会执行某一操作,而且 QAction 还可以包含有图标、文本、快捷方式、状态文字、 What this 文字和工具提示。其中 QAction 的文本通常用作菜单的文本
  • 使用 QAction 需要完成以下关键步骤:
    • 把 QAction 对象使用 QWidget::addAction()函数添加到部件。 必须把 QAtion 添加到部件才能使用
    • 把 QAction 的 triggered()信号连接到需要执行的槽函数
    • 激活 QAction
      • 若已把 QAction 添加到菜单中,则直接点击该菜单项即可
      • 若 QAction 含有快捷键,则直接使用快捷键便可激活 QAction
      • 若 QAction 是添加到某个 QWidget 部件的(比如 QPushButton),则可以把该部件的某个信号连接到 QAction::trigger()槽或 QAction::triggered()信号,来激活QAction
  • 若添加 QAction 的部件被禁用,则该动作将不会被激活

三. QShortcut类(快捷键)

1. 快捷键基础

  • QShortcut 类对快捷键进行了描述,该类继承自 QObject
  • 助记符:是指使用“AlT+某个字符”形式的快捷键,通常这个字符会加上下划线或被小括号括起来
  • 快捷键、加速键、助记符:把 Ctrl+C(复制)这种类型的快捷键称为加速键,其实助记符和加速键都是快捷键
  • 快捷键的实现原理: 首先使用指定的 QKeySequence(键序列)创建一个快捷键 QShortcut,然后把 QShortcut::activated()信号(激活快捷键时会发送此信号)关联到需要执行的槽函数即可。 下面为其示例代码 QKeySequence 类见后文(此小节只需知道可以这样指定键序列即可)
  • QShortcutEvent 事件:当用户按下一个键组合时会产生该事件

2. QShortcut 类中的属性

20221121000202

3. QShortcut 类中的方法

参考官方文档 https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qshortcut.html?highlight=qshortcut

四. QKeySequence 类(键序列)

QKeySequence 是一个单独的父类,未继承其他类。

1. 键序列基础

  • QKeySequence 类(键序列):描述了组合成快捷键的键,比如 Ctrl+C、 Ctrl+D 等。该类是一个顶级类,未继承自任何其他类。 注意: QKeySequence 类只能创建一个按键的组合,但该组合具体能执行什么操作,还需要结合其他类使用,比如与 QShortcut 或 QAction 类结合使用
  • 可使用标准键、字符串和硬编码 3 种方式来创建键序列,示例如下:
    • QKeySequence(QKeySequence:Copy); //使用标准键创建键序列
    • QKeySequence(“Ctrl+C”); 或 QKeySequence(“Ctrl+c”); //使用字符串创建键序列
    • QKeySequence(Qt::CTRL+Qt::Key_P); //使用硬编码创建键序列
  • 键盘布局与键序列:在使用字符串创建键序列时,使用字母指定的键是不区分大小写的,比如 Ctrl+C 与 Ctrl+c 是相同的。但对于 Ctrl+ +这种类型的快捷键,对于不同键盘布局就会产生不同的结果,比如对于常用的英式键盘布局,因为需要使用 Shift 才能输入 + 符号,所以键序列 Ctrl + +需要按下 Ctrl+Shift + =才能被激活,这就使得 Ctrl + +与 Ctrl + Shift + =两个键序列是相同的,对于其他键盘布局(比如挪威键盘)可以直接使用 Ctrl + +,因此在编程时需要注意键盘布局对键序列的影响,使用 Qt 预定义的标准键序列可以解决键盘布局的问题
  • 使用逗号进行分隔的多个键组合(最多 4 个),可以创建按指定顺序激活的键序列,比如:
    • QKeySequence(“Ctrl+C, Ctrl+D”); //按下 Ctrl+C,然后按下 Ctrl+D,用户在输入时,可以按住 Ctrl 不放,然后按下 C,再按下 D,注意:按下 C 之后需要紧接着按下D。因此此方法创建的快捷键类似于 Ctrl+C+D。
    • QKeySequence(“Ctrl+C,D”); //按下 Ctrl+C,然后按下 D
    • QKeySequence(Qt::CTRL+D, Qt::Key_D); //按下 Ctrl+C,然后按下 D
    • QKeySequence(“Ctrl+C+D”); //这是无效的键序列
    • QKeySequence(“Ctrl+Alt+D”); //按下 Ctrl+Alt+D
  • 键序列列表:是指同一个操作对应多个快捷键的情形,比如 Ctrl+C 和 Ctrl+Ins 都表示复制的快捷键。 Qt 使用键序列表表来完成以上功能。键序列表表需要使用静态函数listFromString()创建,其步骤如下(C++代码示例):
    QList<QKeySequence> qs;
    qs<<QKeySequence::listFromString("Ctrl+D");
    qs<<QKeySequence::listFromString("Ctrl+E"); //此时按下 Ctrl+D 和 Ctrl+E 会执行相同的操作
    

2. QKeySequence 类中的枚举

  • SequenceFormat枚举
  • SequenceMatch枚举
  • StandardKey枚举

内容比较多,具体可以参考官方链接https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtgui/qkeysequence.html?highlight=sequenceformat##SequenceFormat

3. QKeySequence 类中的函数

具体参考官方链接https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtgui/qkeysequence.html?highlight=sequenceformat##SequenceFormat

五. QAction 类(动作)、 QActionGroup 类(动作组)

1. 基本规则

  • 图标(icon 属性)、文本(text 属性)、图标文本(iconText 属性)、快捷键(shortcut 属性)
    • 若把动作添加到菜单中
      • 则菜单项将显示图标(icon 属性)、文本(text 属性)、快捷键(shortcut 属性)。
      • 若 text 属性未设置,则动作的图标文本(即 iconText 属性)将用作文本,否则显示 text属性的文本
    • 若把动作添加到工具栏中
      • 若动作有图标、文本、快捷键、图标文本,则工具栏默认只显示图标。
      • 若动作未设置图标,则显示图标文本。
      • 若动作图标和图标文本都未设置,则显示文本(text 属性),
      • 工具栏显示的文本会被去掉一些字符,比如"&BBB…“会被显示为"BBB”。
      • 快捷键始终不会被显示。
      • 若工具栏的 QToolBar::toolButtonStyle 属性被设置为允许显示文本,则默认显示图标文本(注意:菜单显示的是文本),若图标文本为未设置,则显示文本。

2. QAction 类中的属性

以下属性除了 checked 的相关信号是 void toggled(bool)信号外,其他属性都是 void changed()
信号。

20221121002023

20221121002056

20221121002111

3. QAction 类中的函数

具体可参考官方文档https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qaction.html?highlight=qaction#QAction

4. QAction 类中的槽和信号

  • void setDisabled(bool b); //槽,若 b 为 true 则禁用动作,若为 false 则启用动作
  • void hover(); //槽,该槽函数相当于是调用 activate(QAction::Hover)。
  • void toggle(); //槽, 该函数会使动作在选中和未选中之间切换。
  • void trigger(); //槽,该槽函数相当于是调用 activate(QAction::Trigger)。
  • void toggled(bool checked); //信号
    当可选中动作的选中状态发生改变时,发送此信号。若动作被选中,则 checked 为 true,未选中,则为 false。
  • void triggered(bool checked = false); //信号
    当用户激活动作时,发送此信号。比如当用户单击菜单项、工具栏按钮,或按下快捷键,或调用 trigger()函数时。注意:调用 setChecked()或 toggle()函数不会发送此信号。若该动作是可选中的,则当动作被选中时, checked 为 true,未选中时,为 false。
  • void hovered(); //信号
    当用户高亮显示某个动作时,发送此信号。比如当用户把光标停留在菜单项、工具栏上,或按下快捷键时。
  • void changed() ; //信号
    当动作发生变化时发送此信号。由 QAction 类的属性可知,除了 checked 属性外,只要QAction 类的其他属性发生改变就会发送此信号。若只对某个部件的动作发生变化感兴趣,可以重新实现 QWidget::actionEvent()函数,并对 QEvent::ActionChanged 事件类型进行处理。

5. QWidget 类中与 QAction 有关的函数

  • QList<QAction *> actions() const;
    返回该部件的动作列表(可能为空)
  • void addAction(QAction* action);
    void addActions(QList<QAction*> actions);
    把 actions 添加到此部件的动作列表中。所有 QWidget 部件都有一个 QAction 列表(这意味着按钮、复选框等部件都可以添加动作),该列表的默认用法是创建一个上下文菜单,一个 QWidget 应该只有动作列表中的一个动作,并且已经拥有的动作不会导致相同的动作在部件中出现两次。调用该函数的部件不会获得动作的所有权。
  • void insertAction(QAction* before, QAction* action);
    void insertActions(QAction* before, QLsit<QAction*> actions);
    把 action 插入到部件动作列表的动作 before 之前,若 before 是 0 或不是一个有效的动作,则追加动作。
  • void removeAction(QAction* action);
    从部件的动作列表中移除动作 action。
  • virtual void actionEvent(QActionEvent* event);
    虚拟的,受保护的只要部件的动作发生变化,就会调用此事件处理程序。

6. QActionGroup 类(动作组)

  • QActionGroup 类继承自 QObject,该类用于把动作组合在一起,从而形成一个动作组
  • 在同一个动作组中的动作是互斥的,任何时候只有一个动作是活动的。文本的对齐方式(左对齐、右对齐、居中对齐等)就是动作组的一个典型应用
  • 动作组中的动作默认是具有排他性的(独占性),可使用 QActionGroup::setExclusive(bool)函数来改变这一状态
  • 使用 QActionGroup::addAction()可把动作添加到动作组中。使用 QWidget::addActions()函数可把动作组添加到 QWidget 部件中

六. QMenu 类(菜单)

1. 基本规则

  • 可分离菜单:通常在顶部显示为虚线, 点击该虚线可使菜单分离, 菜单分离后存在于一个单独的窗口中,并会创建一个与原始菜单有相同菜单项目的副本(见下图)
    20221121003148
  • 菜单应使用 QMenu::exec()或 QMenu::popup()显示,而不应使用 show()显示
  • 不能使用单击来触发子菜单的 triggered()信号,但可以使用快捷键来触发
  • 菜单都会被视为一个 QAction,包括 QMenu
  • 向菜单中添加各种菜单项的方法
    • 子菜单使用 addMenu()或 insertMenu()添加,子菜单在右侧有一个右箭头,子菜单不一定会有自已的菜单项。
    • 不是子菜单的菜单项使用 addAction()或 insertAction()函数添加。
    • 分隔符使用 addSeparator()、 insertSeparator()、 addSection()、 insertSection()函数添加

2. QMenu 类中的属性

参考官方文档https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qmenu.html?highlight=qmenu#QMenu

3. QMenu 类中的方法

参考官方文档https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qmenu.html?highlight=qmenu#QMenu

七. QMenuBar 类(菜单栏)

1. 基本规则

  • 菜单栏通常不需要手动布局,默认会自动将菜单栏设置到父部件的顶部,并在父部件大小调整时调整其大小。

  • 使用 QMenuBar::addMenu()函数可把菜单添加到菜单栏上。

2. QMenuBar 类中的属性

参考官方文档https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qmenubar.html?highlight=qmenubar

3. QMenuBar 类中的方法

参考官方文档https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qmenubar.html?highlight=qmenubar

八. QToolBar 类(工具栏)

1. 基本规则

  • QToolBar 类提供了一个可移动的带有一组控件的面板。 由于 QToolBar 就是一个 QWidget部件,默认情况下,该部件就是一个 QWidget,在其中没有任何内容,因此要使 QToolBar类有意义,需要向其中添加项目

  • 因为 QToolBar 类的大部分属性需要在 QWindowMain 类中才有意义,因此本小节只讲解工具栏位于 QWindowMain 之中的情形

  • 工具栏可以被固定在某个位置(比如窗口顶部),也可在工具栏区域内移动。相关函数有setMovable()、 isMovable()、 allowedAreas()、 isAreaAllowed()

  • 若工具栏无法显示所包含的所有项目时,会在工具栏上显示一个扩展按钮(如图)
    20221121004040

  • 向工具栏中添加项目的方法

    • 可把动作(使用 addAction()或 insertAction()添加)作为工具栏上的工具栏按钮。
    • 使用 addSeparator()或 insertSeparator()函数可向工具栏添加分隔符。
    • 还可使用 addWidget()或 insertWidget()向工具栏上插入 QWidget 部件(常见的有QSpixBox、 QComboBox 等)。
  • 按下工具栏按钮时会发送 actionTriggered()信号

  • 工具栏的三种属性:可移动、可放置区域、可作为独立窗口(浮动状态),见下图
    20221121004206

2. QToolBar 类中的属性

参考官方文档https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qtoolbar.html?highlight=qtoolbar

3. QToolBar 类中的方法

参考官方文档https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qtoolbar.html?highlight=qtoolbar

4. QToolBar 类中的信号

  • void actionTriggered(QAction* action); //信号
    当工具栏中的动作被激活时,发送此信号。比如按下包含该动作的工具栏按钮。
  • void allowedAreasChanged(Qt::ToolBarAreas allowedAreas); //信号
    当工具栏允许被放置的区域发生变化时,发送此信号,参数 allowedAreas 为发生变化后的新区域。
  • void iconSizeChanged(const QSize& iconSize); //信号
    当图标的大小发生变化时,发送此信号, iconSize 为新图标的大小。
  • void movableChanged(bool movable); //信号
    当工具栏变得可移动或不可移动时,发送此信号,若工具栏可移动,则 movable 为 true,否则为 false。
  • void orientationChanged(Qt::Orientation orientation); //信号
    当工具栏的方向改变时,发送此信号, orientation 为工具栏的新方向。
  • void toolButtonStyleChanged(Qt::ToolButtonStyle toolButtonStyle); //信号
    当工具栏按钮的样式改变时,发送此信号, toolButtonStyle 为工具样的新样式。
  • void topLevelChanged(bool topLevel); //信号
    当 floating 属性改变时,发送此信号,若工具栏处于浮动状态(即独立窗口状态)时, topLevel为 true,否则为 false。
  • void visibilityChanged(bool visible); //信号
    当工具栏变得可见或不可见时,发送此信号

九. QStatusBar 类(状态栏)

1. 基本规则

  • 状态栏主要用于向向户显示一些程序的状态信息,状态栏通常位于窗口底部

  • 由于状态栏就是一个 QWidget 部件,默认情况下,状态栏什么也不会显示,因此需要自行编写代码处理状态栏上显示的信息,比如需要处理当鼠标进入某个部件时在状态栏显示该部件的一些信息,当离开时清除该信息的显示等。

  • 由于 QMainWindow(主窗口)已经由 Qt 内部实现了状态栏的功能(比如状态位始终位于窗口底部,当鼠标进入部件时显进信息,离开时清除信息等),因此在主窗口中使用状态栏更方便,当然程序员也可以实现自已的状态栏(见后文)。

  • 主窗口(QMainWindow)只能有一个状态栏。使用 QMainWindow::setStatursBar()函数可设置状态栏。

  • 状态栏可以显示 3 种类型的信息,

    • 临时信息:通常用于显示部件的状态信息(即使用 setStatusTip()函数设置的信息),临时信息的显示方式通常是,当鼠标进入该部件时 显示该信息,离开该部件时清除该信息。临时信息使用 QStatusBar::showMessage()函数进行设置。
    • 正常信息:正常信息会一直显示在状态栏,但是当需要显示临时信息时,临时信息可能会隐藏正常信息。正常信息通过一个 QWidget 部件来进行显示(比如 QLabel、QProgressBar 等),可使用 QStatusBar::addWiget()函数向状态栏添加这些部件。
    • 永久信息:永久信息显示在状态栏的最右侧,永久信息不会被临时信息隐藏。永久信息也通过一个 QWidget 部件来进行显示(比如 QLabel、 QProgressBar 等),可使用QStatusBar::addPermanentWidget()函数向状态栏添加这些部件

2. QStatusBar 类中的属性

参考官方文档https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qstatusbar.html?highlight=qstatusbar

3. QStatusBar 类中的方法

参考官方文档https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qstatusbar.html?highlight=qstatusbar

十. QDockWidget 类(可停靠窗口、悬浮窗口)

1. 基本规则

  • QDockWidget 实现了可停靠窗口,可停靠窗口可以停靠在主窗口中,也可作为一个浮动窗口使用,见下图
    20221121005043

  • 在主窗口中的可停靠窗口还可重叠和嵌套,见下图
    20221121005119

  • 由于 QDockWidget 就是一个 QWidget 部件,默认情况下,该部件只提供了一个有标题栏的框架窗口样式,在其中没有任何内容,因此要使 QDockWidget 类有意义,需要向其中添加部件,使用 QDockWidget::setWidget()函数可向其中添加部件

  • QDockWidget 也是由多个对象组合而成的部件,没有任何 QWidget 部件的可停靠窗口

2. QDockWidget 类中的属性

参考官方文档https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qdockwidget.html?highlight=qdockwidget

3. QDockWidget 类中的方法

参考官方文档https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qdockwidget.html?highlight=qdockwidget

4. QDockWidget 类中的信号

  • void topLevelChanged(bool topLevel); //信号
    当 floating 属性改变时,发送此信号,若可停靠窗口当前是浮动的,则 topLevel 为 true,否则为 false。

  • void visibilityChanged(bool visible); //信号
    当可停靠窗口变得可见或不可见时,发送此信号。 当在重叠的可停靠窗口中切换其选项卡使可停靠窗口可见或不可见时,也会发送此信号。

  • void allowedAreasChanged(Qt::DockWidgetAreas allowedAreas); //信号
    当 allowedAreas 属性(即允许放置的区域)更改时,发送此信号, allowedAreas 为更改后的新区域。

  • void dockLocationChanged(Qt::DockWidgetArea area); //信号
    当可停靠窗口移动到另一个可停靠区域或移动到当前可停靠区域的另一位置时,发送此信号,当以编程的方式改变位置时,也会发送此信号。

  • void featuresChanged(QDockWidget::DockWidgetFeatures features); //信号
    当 features 属性改变时,发送此信号,参数 features 为改变后的新值。

十一. QMainWindow 类(主窗口)

主窗口把前面各小节讲解的部件组织在一个窗口中。

1. QMainWindow 类中的属性

参考官方文档https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qmainwindow.html?highlight=qmainwindow

2. QMainWindow 类中的方法

参考官方文档https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qmainwindow.html?highlight=qmainwindow

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

smart_cat

你的鼓励将是我写作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值