【QT】Qt的随身笔记(持续更新...)

这篇具有很好参考价值的文章主要介绍了【QT】Qt的随身笔记(持续更新...)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Qt 获取当前电脑桌面的路径

#include <QStandardPaths>
QString desktop_path = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);

Qt 获取当前程序运行路径

#include <QCoreApplication>

//方法1:
QString fileName = QCoreApplication::applicationDirPath();

//方法2:
QString fileName = QApplication::applicationDirPath();

QCoreApplication 与 QApplication 的区别:
QApplication 继承于 QGuiApplication,而QGuiApplication 继承于 QCoreApplication
QApplication :带Gui,QCoreApplication :不带Gui

Qt 创建新的文本文件txt,并写入内容

//在当前程序运行路径下创建
QFile file(QCoreApplication::applicationDirPath() + "/test.txt");
if (!file.exists()) {
    qDebug() << "文件不存在!";
}
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
    qDebug() << "文件打开失败!";
}
QString str = "测试文件是否被修改!";
QTextStream out(&file);
out << str;  //将str写入文件
file.close();

如何向QPlainTextEdit 写入内容

void insertPlainText(const QString &text);

void appendPlainText(const QString &text);

QTimer

QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &MainWindow::slotRefresh());
timer->start(1000);  //延迟1秒刷新

QTimer还为单次触发定时器提供了一个静态函数。例如:

QTimer::singleShot(200, this, &MainWindow::slotRefresh());

QMessageBox的使用

构造函数

QMessageBox messageBox(QMessageBox::Question, "Question", "The current project has been modified.\nDo you want to save it?", QMessageBox::Yes | QMessageBox::No);
messageBox.exec();

通过构造函数和属性函数实现

QMessageBox messageBox(this);
messageBox.setIcon(QMessageBox::Warning);
messageBox.setWindowTitle("Warning");
messageBox.setText("Parsed file not found! Please reconfigure!");
messageBox.setStandardButtons(QMessageBox::Yes|QMessageBox::No);
messageBox.setDefaultButton(QMessageBox::Yes);
messageBox.exec();

静态函数

QMessageBox::Information(this, "Information", "Project saved successfully!");

QLatin1String

QLatin1String是const char* 的一层薄薄的封装。

许多QString的成员函数都用const char*代替QString作为参数实现重载。
【QT】Qt的随身笔记(持续更新...),怎样学好QT,qt

例如,假定str是QString对象,

if (str == "auto" || str == "extern"
        || str == "static" || str == "register") {
    ...
}

上面的代码执行会比下面的代码执行快很多

if (str == QString("auto") || str == QString("extern")
        || str == QString("static") || str == QString("register")) {
    ...
}

因为在第二部分的代码中会构造四个临时的QString对象,并复制字符串中的值。

如果利用QLatin1String类来写上述的程序就是

if (str == QLatin1String("auto")
        || str == QLatin1String("extern")
        || str == QLatin1String("static")
        || str == QLatin1String("register") {
    ...
}

虽然在代码输入的时候有点长,但是它的执行效率和上面第一段的代码一样。

QLatin1String可以在任何需要QString对象的地方使用

QLabel label;
label.setObjectName(QLatin1String("111"));

QLayout

void QLayout::setContentsMargins(int left, int top, int right, int bottom)

设置要围绕布局使用的左、上、右和下边距。
默认情况下,QLayout使用样式提供的值。在大多数平台上,所有方向的边距都是11像素。

QGridLayout* grid = findChild<QGridLayout*>("gridLayout");  //通过对象名获取布局
grid->setContentsMargins(10, 30, 50, 70);  //上、左、右、下

【QT】Qt的随身笔记(持续更新...),怎样学好QT,qt

int spacing() const
void setSpacing(int)

设置布局中小部件之间的间距
如果没有显式设置值,则布局的间距继承自父布局或父小部件的样式设置。
对于QGridLayout和QFormLayout,可以使用setHorizontalSpacing()和setVerticalSpacing()设置不同的水平和垂直间距。在这种情况下,spacing()返回-1

C++

在c++头文件中写#include类的头文件与直接写class加类名有何区别

class 类名只是声明存在这么一个类,但是通过这个声明无法得到任何关于此类的具体信息。这样你可以在其他使用到的地方声明一个该类型的指针。

#include "xx.h"在编译的时候把xx.h文件直接展开,所以里面的接口都能用,可以申明对象。但是class xx;这种方式就不会,你只能使用它的指针或者引用,你不能创建申明对象。

使用class 类名一般是为了去除编译依赖,减少编译消耗的时间。
举个例子:
有个头文件 “common.h”,你在所有的头文件中都需要它,所以都#include “common.h”,当common.h中的代码有一些改动时,你在运行代码时,需要重新编译所有依赖它的头文件。而如果是Class Common,当common.h中的代码一些改动时,你在运行代码时,当你之前编译过一次之后就不会再次编译所有依赖它的头文件。(当一个文件头编译之后,若这个头文件没有改动,在下次运行代码时就不会重新编译)

只声明类而不include,可以降低各个文件编译时的关联度,不会在改动了一下部分类的时候,引发其他大量文件的重新编译,在做小工程的时候没什么区别,但是做大了,编译一次需要好几个小时的时候,这样做的优势就显现出来了。

mutable关键字

mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数
中。

前向声明

在一个类的头文件当中声明一个类而不去定义它,被称为前向声明。
在头文件中这个前向声明的类A是一个不完全类型(incomplete type),只知道类A的类型,但不知道类A包含哪些成员。
不完全类型不能定义该类型的对象,只能用于定义该类型的指针或者引用,或者用于声明该类型作为参数或返回值的函数。
好处:对于不必要的#include,可以减少编译时间

// 当使用前向引用声明时,只能使用被声明的符号,而不能涉及类的任何细节。
class A;  //前向声明类A
class B{
public:
	A* m_a;  //用于定义指向这个类型的指针或引用
	void func(A a);  //用于声明(不是定义)使用该类型作为形参或者返回类型的函数
}
  1. 前向声明的类不能定义对象(不知道类的大小及内部成员,不能实例化)。

  2. 可以用于定义指向这个类型的指针和引用(即定义该类的指针或引用作为成员变量)。

  3. 可以用于声明(不是定义)使用该类型作为形参或者返回类型的函数

QFont

QFont类用于绘制文本的字体。
注意,在使用QFont之前必须存在一个QGuiApplication实例。可以使用QGuiApplication::setFont()设置应用程序的默认字体。
如果选择的字体不包含需要显示的所有字符,QFont将尝试在最近的等效字体中找到字符。
当QPainter从字体中绘制字符时,QFont将报告它是否具有该字符;如果没有,QPainter将绘制一个未填充的正方形。
常用构造函数

QFont(const QString &family, int pointSize = -1, int weight = -1, bool italic = false);
//参数说明:字体样式、字体大小、字体粗细、是否斜体

常用函数

void setFamily(const QString &family)//设置字体样式

void setPointSize(int pointSize)//设置字体大小 

void setWeight(int weight)//设置字体粗细

void setBold(bool enable)//设置字体是否加粗

void setItalic(bool enable)//设置字体是否斜体

void setPixelSize(int pixelSize)//设置字体像素大小

void setOverline(bool enable)//设置字体上划线

void setUnderline(bool enable)//设置字体下划线

void setStrikeOut(bool enable)//设置字体删除线
void setLetterSpacing(SpacingType type, qreal spacing);
//将字体的字母间距设置为spacing,并将间距的类型设置为type。
//字母间距改变字体中单个字母之间的默认间距。根据所选择的间距类型,字母之间的间距可以按字符宽度的百分比或按像素的百分比设置为更小或更大。

enum QFont::SpacingType

枚举 描述
QFont::PercentageSpacing 0 值为100将保持间距不变;值200将把字符后面的间距扩大到字符本身的宽度。
QFont::AbsoluteSpacing 1 正值使字母间距增加相应的像素;负值会减小间距。
void setCapitalization(Capitalization caps);
//将此字体的文本的大写设置为大写。
//字体的大写使文本以选定的大写模式显示。

enum QFont::Capitalization

枚举 描述
QFont::MixedCase 0 正常的文本呈现选项,其中不应用大小写更改。
QFont::AllUppercase 1 全部大写
QFont::AllLowercase 2 全部小写
QFont::SmallCaps 3 小型大写,意思是全部大写形式,但其尺寸与对应小写字母的相同。
QFont::Capitalize 4 首字母大写

QLabel

设置QLabel字体的大小和颜色

QLabel* label = new QLabel(tr("test"));
font.setFont(QFont("宋体"), 12);

QPalette pa;
pa.setColor(QPalette::WindowText, Qt::red);
font.setPalette(pa);

QLabel设置背景色

	QLabel* colorLabel = new QLabel(this);
    colorLabel->setFixedSize(10, 10);
    QPalette palette = colorLabel->palette();
    palette.setColor(QPalette::Background, Qt::black);
    colorLabel->setAutoFillBackground(true);
    colorLabel->setPalette(palette);

字体超过QLabel长度,如何以省略号的形式显示

QLabel* m_activeText = new QLabel(this);
QString text = "abcdefg";
QFontMetrics fontWidth(m_activeText->font());  //计算字体的宽度
QString elideText = fontWidth.elidedText(text, Qt::ElideMiddle, 80);  //80为最大宽度
m_activeText->setText(elideText);

QLabel显示不完全,怎么自动换行

label->setWordWrap(true);                     // true:自动换行
label->setAlignment(Qt::AlignVCenter);        // 对齐方式

如何为布局添加分割线

QLabel* lineLabel = new QLabel(this);
lineLabel->setFrameStyle(QFrame::HLine | QFrame::Sunken);  //Sunken:凹陷,Raised:凸起

将lineLabel添加到布局即可

Qlabel显示的链接可以点击打开

label->setTextInteractionFlags(Qt::TextBrowserInteraction);  //label中的内容可用鼠标选择文本复制,链接激活
label->setOpenExternalLinks(true);                           //label中的内容若为链接,可直接点击打开
label.setText("<a href = https://blog.csdn.net/WL0616?spm=1000.2115.3001.5343>点我试试");   //https://blog.csdn.net/WL0616?spm=1000.2115.3001.5343为链接网址

获得界面当前点击的按钮

 QPushButton* btn= qobject_cast<QPushButton*>(sender());

qobject_cast

qobject_cast()函数的行为类似于标准c++的dynamic_cast(),其优点是不需要RTTI支持,并且可以跨动态库边界工作。
qobject_cast动态转换QObject类的类型,qobject_cast将括号中()的类型转换成尖括号中<>的类型。
T qobject_cast(QObject *object) 在使用时有两个限制:

  1. 返回的T类型必须继承自QObject。
  2. 在声明时必须有Q_OBJECT宏。
QLabel* label = new QLabel();
QWidget* widget = qobject_cast<QWidget*>(label);

QDialog

QDialog隐藏/删除标题栏

setWindowFlags(Qt::FramelessWindowHint);

生成无边界窗口。用户不能通过窗口系统移动或调整无边界窗口的大小。

QDialog 去掉问号,只保留关闭

Qt::WindowFlags flags = Qt::Dialog;
flags |= Qt::WindowCloseButtonHint;
setWindowFlags(flags);

如何去掉按钮的虚线框

例如:按钮有个focus的效果
【QT】Qt的随身笔记(持续更新...),怎样学好QT,qt
如何取消这种效果?

//方法1
setFocusPolicy(Qt::NoFocus);

//方法2
setStyleSheet("outline: none");  

//方法3
setStyleSheet("padding: -1"); 

QPushButton设置按钮的大小

QPushButton继承于QAbstractButton,QAbstractButton继承于QWidget,下面其实是QWidget的接口

void setMinimumHeight(int minh)
void setMinimumSize(const QSize &)
void setMinimumSize(int minw, int minh)
void setMinimumWidth(int minw)

rgba(0,0,0,0)第四个参数详解

前三个参数表明颜色,第四个参数表示透明度,它的范围为0.0到1.0之间,0.5为半透明。
rgba(0,0,0,1)则表示完全不透明的黑色;
rgba(0,0,0,0.5)则表示半透明的,看起来是灰色;
rgba(0,0,0,0)则表示完全不透明的白色,也即是无色;

QT实现弹窗后背景淡化

QWidget* widget = new QWidget(this);
widget->resize(this->width(), this->height());
widget->move(0, 0);
widget->setStyleSheet(“background-color:rgba(0, 0, 0, 0.3);”);
widget->show();

geometry()与frameGeometry()的区别

geometry():界面大小,不包含窗口装饰器(即标题栏)
frameGeometry():界面大小,包含上方的窗口装饰器(即标题栏)

Qt属性系统Q_PROPERTY

Qt提供了一个绝妙的属性系统,Q_PROPERTY()是一个宏,用来在一个类中声明一个属性property。
一个属性的行为就像一个类的数据成员,但它有通过元对象系统访问的附加功能。

Q_PROPERTY(type name
           (READ getFunction [WRITE setFunction] |
            MEMBER memberName [(READ getFunction | WRITE setFunction)])
           [RESET resetFunction]
           [NOTIFY notifySignal]
           [REVISION int]
           [DESIGNABLE bool]
           [SCRIPTABLE bool]
           [STORED bool]
           [USER bool]
           [CONSTANT]
           [FINAL])

属性例子:

 Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)

Q_PROPERTY:用于声明属性的宏,Qt规定的
bool :属性类型
enabled :属性名称
READ isEnabled(这两个单词可以放一起理解):就是说可以通过 isEnabled去读属性值
WRITE setEnabled(这两个单词可以放一起理解):就是说可以通过 setEnabled去设置属性值

以上只是一个属性一个声明,但是其实没有实现,还需要分别是去实现isEnabled 和setEnabled,这样整个属性才可以真正使用。

例子:

class MaskWidget : public QDialog {
    Q_OBJECT
    Q_PROPERTY(QStringList names READ names WRITE setNames DESIGNABLE true)
public:
    static MaskWidget* instance();
    void setMainWidget(QWidget* pWidget);

    QStringList names() const;
    void setNames(const QStringList& names);
	...
};

属性提供的其他字段含义

READ:用于读取属性值,如果未指定成员变量(通过MEMBER ),则需要读取访问器函数。

WRITE:写访问器函数是可选的。用于设置属性值。它必须返回void,并且必须只接受一个参数,要么是属性的类型,要么是指向该类型的指针或引用。

MEMBER:如果未指定读取访问器函数,则需要成员变量关联。这使得给定的成员变量可读写,而无需创建读写访问器函数。如果需要控制变量访问,除了成员变量关联(但不是两者)之外,还可以使用读或写访问器函数。

RESET:复位功能是可选的。它用于将属性设置回其特定于上下文的默认值。

NOTIFY:通知信号是可选的。如果已定义,它应该指定该类中的一个现有信号,该信号在属性值更改时发出。成员变量的通知信号必须采用零个或一个参数,这些参数必须与属性的类型相同。参数将采用属性的新值。仅当属性确实发生更改时才应发出NOTIFY信号,以避免绑定在QML中被不必要地重新计算。

REVISION:修订号是可选的。如果包含,它将定义属性及其通知程序信号,以便在特定版本的API中使用(通常用于暴露于QML)。如果不包含,则默认为0。

DESIGNABLE:表示属性是否应该在GUI设计工具(例如Qt Designer)的属性编辑器中可见。大多数属性是可设计的(默认为true)。可以指定布尔成员函数,而不是true或false。

SCRIPTABLE:表示脚本引擎是否应该访问此属性(默认为true)。可以指定布尔成员函数,而不是true或false。

STORED:表示属性是应该被认为是独立存在还是依赖于其他值。它还指示在存储对象状态时是否必须保存属性值。

USER:表示是将属性指定为类的面向用户属性还是用户可编辑属性。通常,每个类只有一个用户属性(默认值为false)。

CONSTANT:表示属性值是常量。对于给定的对象实例,常量属性的READ方法每次调用时必须返回相同的值。对于对象的不同实例,此常量值可能不同。常量属性不能有写入方法或通知信号。

FINAL:表示派生类不会重写该属性。在某些情况下,这可以用于性能优化,但不是由moc强制执行的

C++ 11 =default和=delete

C++11中,当我们定义一个类的成员函数时,如果后面使用"=delete"去修饰,那么就表示这个函数被定义为deleted,也就意味着这个成员函数不能再被调用,否则就会出错。
在C++11之前,当我们希望一个类不能被拷贝,就会把构造函数定义为private,但是在C++11里就不需要这样做了,只需要在构造函数后面加上=delete来修饰下就可以了。

对象名获取类名

obj->metaObject()->className() == QLatin1String("Dialog");

如何在Qt中添加文件夹,对左侧树的目录进行分类

【QT】Qt的随身笔记(持续更新...),怎样学好QT,qt

  1. 在当前项目的路径中新建一个文件夹,比如命名为src
  2. 在新建的文件夹src里,新建一个文本文档,将其命名为src.pri(将后缀名txt改为pri)
  3. 切换到Qt,在当前项目工程的.pro中添加 include(src/src.pri),Ctrl+S保存
  4. 保存后(需要等个几秒),就会显示你添加的文件夹
  5. 在你添加的文件夹src里右击添加新文件,就可以创建.h和.cpp文件

setStatusTip、setToolTip、setWhatsThis的区别

setStatusTip用于MainWindow的状态栏提示;
setToolTip用于控件的提示;
setWhatsThis用于Dialog上的帮助信息。

Qt自定义的类需要继承QObject吗?

QObject类是所有Qt对象的基类。
自定义的类最好继承QObject,Q_OBJECT宏也是要写的。
便于使用信号与槽。

Qt 常见编译错误

  1. error: ‘tr’ was not declared in this scope …
    tr() 是QObject类的成员函数。
    原因:自定义的类没有继承QObject

mainWindow工具栏调整icon的大小

toolBar->setIconSize(QSize(18,18));

去除QComboBox中项中含有的省略号

当设置QComboBox的最大宽度时 :

combox->setMaximumWidth(100);

如果项中的内容比较长,此时就会出现在项的中间会出现省略号,看起来不太美观:
【QT】Qt的随身笔记(持续更新...),怎样学好QT,qt
取消省略号的解决办法:

combox->view()->setTextElideMode(Qt::ElideNone);  //取消项中的省略号,但是会导致看不见后面的字体

【QT】Qt的随身笔记(持续更新...),怎样学好QT,qt

enum TextElideMode {
        ElideLeft,    //省略号在最左边
        ElideRight,   //省略号在最右边
        ElideMiddle,  //省略号在最中间
        ElideNone     //无省略号
    };

有两个工具栏,怎样让一个工具栏靠右(右对齐)

再maintoolbar上设置一个widget,将两个工具栏放到widget里面,并用QHBoxLaout进行布局,一个放到左边Qt::AlignLeft,一个放到右边Qt::AlignRight,两者之间再放一个弹簧即可。
在第一个工具栏里设置:

	QWidget* widget = new QWidget(this);
    QComboBox* com1 = new QComboBox(this);
    QComboBox* com2 = new QComboBox(this);

    QToolBar* bar1 = new QToolBar(widget);
    bar1->addWidget(com1);
    QToolBar* bar2 = new QToolBar(widget);
    bar2->addWidget(com2);

    QHBoxLayout* lay = new QHBoxLayout;
    lay->addWidget(bar1, Qt::AlignLeft);
    lay->addStretch();
    lay->addWidget(bar2, Qt::AlignRight);
    widget->setLayout(lay);
    ui->mainToolBar->addWidget(widget);

Qwidget

widget绘制背景色

//CustomLabel继承与QLabel
void CustomLabel::setBackgroundColor(const QColor& color) {
    QPalette palette;
    palette.setColor(QPalette::Background, color);
    setAutoFillBackground(true);
    setPalette(palette);
}

widget设置边框

/**
 * @brief paintEvent: 绘制边框
 */
void ProjectTree::paintEvent(QPaintEvent* event) {
    Q_UNUSED(event)
    QPainter p(this);
    p.setPen(Qt::black);                          //设置画笔记颜色
    p.drawRect(0, 0, width() - 1, height() - 1);  //绘制边框
}

鼠标点击其他地方,使当前widget隐藏

setWindowFlags(Qt::Popup);

将窗口置于当前窗口之下

void QWidget::stackUnder(QWidget *w)

将小部件放置在父小部件堆栈的w下。要做到这一点,小部件本身和w必须是兄弟关系(小部件和w都继承于一个父类)。
例如:我想在当前弹窗下放置一个窗口,当前窗口继承与QDialog,那么要放置的窗口也必须继承与QDialog。

如何获取父类的窗口

QWidget* mainWindow = this->parentWidget()

比如

自定义数据类型Q_DECLARE_METATYPE()注册

QFontMetrics函数计算给定字体的字符和字符串的大小。

  QFont font("times", 24);
  QFontMetrics fm(font);
  int pixelsWide = fm.width("What's the width of this text?");
  int pixelsHigh = fm.height();
QString elidedText(const QString &text, Qt::TextElideMode mode, int width, int flags = 0) const;

(Elide:省略的意思)
如果字符串文本的宽度大于width,则返回字符串的省略版本(即包含"…"的字符串)。否则,返回原始字符串。

enum TextElideMode {
        ElideLeft,    //省略号在左边
        ElideRight,   //省略号在右边
        ElideMiddle,  //省略号在中间
        ElideNone     //无省略号
    };

Q_ASSERT

Q_ASSERT是一个宏,接受布尔值,当其中的布尔值为真时,便什么也不做,当其中的布尔值为假时,便断下。

Q_ASSERT(index.isValid());

编译常见错误

error: static assertion failed: The slot requires more arguments than the signal provides. [ #define Q_STATIC_ASSERT_X(Condition, Message) static_assert(bool(Condition), Message)]

原因:一般是信号与槽的参数不一致导致的

error: No rule to make target ‘xxx.png’, needed by ‘debug/qrc_resource.cpp’. Stop.

原因:在项目当中删除resourse下的图片导致的
解决:重新执行qmake即可

error: [debug/qrc_resource.cpp] Error 1

【QT】Qt的随身笔记(持续更新...),怎样学好QT,qt

原因:项目中添加此图片,但是文件中并不存在此图片(或已删除),此时找不到,所以报错
怎么找这个不存在的图片?
方法:一个一个去点击图片,当点击到不存在的图片时,会显示以下错误
【QT】Qt的随身笔记(持续更新...),怎样学好QT,qt

解决:删除项目中此图片,重新添加

error: field has incomplete type

原因:只是前向声明,并没有包含include文件
解决:包含include文件

error: jump to case label [-fpermissive] case xxx: ^

原因:case语句之后如果有多行定义代码,特别是变量定义,最好使用{}来给予约束作用域,负责计算机无法识别作用域。
解决:case语句之后的代码加上{}

//错误示范:

switch (a)
{
    case 1:
        int a = 0;
        //stuff
        break;

    case 2:
        //stuff
        break;
}

//正确示范:

switch (a)
{
    case 1:
        {
            int a = 0;
            //stuff
        }
        break;

    case 2:
        //stuff
        break;
}

error: passing ‘const ProjectTreeViewDelegate’ as ‘this’ argument discards qualifiers [-fpermissive] drawTreeViewItem(painter, drawRect, modelData);^

原因:当某一个函数是const函数时,另一个函数会调用这个函数的参数,则另一个函数也应设为const
解决:另一个函数也应为const函数

错误示范:

void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void drawTreeViewItem(QPainter* painter, QRect drawRect, ProjectTreeModelData modelData);  //错误

正确示范:

//drawTreeViewItem参数里面会调用paint的参数,paint为const函数,则drawTreeViewItem也应为const函数
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void drawTreeViewItem(QPainter* painter, QRect drawRect, ProjectTreeModelData modelData) const;

另外一种情况:
在const函数里面不能改变成员变量的值或赋值

error: default argument given for parameter x of

原因:默认参数既可以在函数声明中,也可以在函数定义中声明缺省参数,但不能既在函数声明中又在函数定义中同时声明缺省参数。
解决:将定义或声明中的任一个缺省参数删除即可。

错误示范:

//.h文件
void func(int a, int b = 0);
//.cpp文件
void func(int a, int b = 0);  //错误

正确示范:

//.h文件
void func(int a, int b = 0);
//.cpp文件
void func(int a, int b);

error: static assertion failed: Signal and slot arguments are not compatible.

原因:信号的参数与槽函数参数不对应
解决:信号的参数与槽函数参数改为一致

错误示范:

//信号
sigData(int a);
//槽函数
slotGetData(float a);  //错误,参数不一致

正确示范:

//信号
sigData(int a);
//槽函数
slotGetData(int a);

error: invalid application of ‘sizeof’ to incomplete type ‘XXX’(‘VariableData’): sizeOf = sizeof(T)

原因:前置声明一个类,只是声明,并不知道类的大小
解决:使用include包含头文件

错误示范:

class B;  //只是声明
class A  {
	...
    QList<B> m_ list;  //无法知道类B的大小
};

正确示范:

#include "B.h"

class A  {
	...
    QList<B> m_ list;
};

关于内联inline的 error: invalid use of incomplete type ‘xxx’

原因:inline在头文件声明,在源文件定义时,若其他文件调用inline函数时,会出现此种错误。因为该函数对其他编译单元不可见,也就是其他cpp文件不能链接该函数库。
解决:inline函数的声明和定义都写在头文件中

  • 如果将函数的实现放在头文件中,那么每一个包含该头文件的cpp文件都将得到一份关于该函数的定义,那么链接器会报函数重定义错误。
  • 如果将函数的实现放在头文件,并且标记为 inline 那么每一个包含该头文件的cpp文件都将得到一份关于该函数的定义,并且链接器不会报错。
  • 如果将函数的实现放在cpp文件中,并且没有标记为inline,那么该函数可以被连接到其他编译单元中。
  • 如果将函数的实现放在cpp文件中,并且标记为inline, 那么该函数对其他编译单元不可见(类似static的效果),也就是其他cpp文件不能链接该函数库,这就是标题中出现的 … undefined reference to …

槽函数的lambda错误 error: passing ‘const VarBasicData’ as ‘this’ argument discards qualifiers [-fpermissive][=](const VarBasicData& varinfo) { varData.basic = varinfo; });

原因:参数是const的,赋值的时候会出错
解决 :添加mutable关键字

错误示范:

connect(SignalsMedium::getInstance(), &SignalsMedium::sigBasicVarInfo,
            [=](const VarBasicData& varinfo) { varData.basic = varinfo; });

正确示范:

connect(SignalsMedium::getInstance(), &SignalsMedium::sigBasicVarInfo,
            [=](const VarBasicData& varinfo) mutable { varData.basic = varinfo; });

update()的作用

用于更新UI界面的,它会触发重绘事件并调用paintEvent函数。
在QWidget或其子类中,可以通过调用update函数来触发重绘事件,从而更新UI界面。

QColor

打印颜色的名称

QString QColor::name() const
qDebug() << color.name();

基本颜色

QString arrayColor[5][8] = {
        {"#000000", "#A52A2A", "#004040", "#005500", "#00005E", "#00008B", "#4B0082", "#696969"},
        {"#8B0000", "#FF6820", "#8B8B00", "#009300", "#388E8E", "#0000FF", "#7B7BC0", "#808080"},
        {"#FF0000", "#FFAD5B", "#32CD32", "#3CB371", "#7FFFD4", "#7D9EC0", "#800080", "#A9A9A9"},
        {"#FFC0CB", "#FFD700", "#FFFF00", "#00FF00", "#40E0D0", "#C0FFFF", "#480048", "#C0C0C0"},
        {"#FFE4E1", "#D2B48C", "#FFFFE0", "#98FB98", "#AFEEEE", "#68838B", "#E6E6FA", "#FFFFFF"}};

    QString arrayToolTip[5][8] = {
        {"Black", "Brown", "Dark Olive Green", "Dark Green", "Dark Teal", "Dark Blue", "Indigo", "Dark Gray"},
        {"Dark Red", "Orange", "Dark Yellow", "Green", "Teal", "Blue", "Blue Gray", "Gray"},
        {"Red", "Light Orange", "Lime", "Sea Green", "Aqua", "Light Blue", "Violet", "Dim Gray"},
        {"Pink", "Gold", "Yellow", "Bright Green", "Turquoise", "SkyBlue", "Dark Magenta", "Light Gray"},
        {"Light Pink", "Tan", "Light Yellow", "Pale Green", "Pale Turquoise", "Pale Blue", "Lavender", "White"}};

event: 设置鼠标进入和离开时的框架样式

bool BasicColorItem::event(QEvent* event) {
    /*鼠标进入时的框架样式*/
    if (QEvent::Enter == event->type()) {
        setLineWidth(0);
        setMidLineWidth(0);
        setFrameShape(QFrame::WinPanel);
        setFrameShadow(QFrame::Raised);
    }

    /*鼠标离开时的框架样式*/
    if (QEvent::Leave == event->type()) {
        initItemFram();
    }

    return QLabel::event(event);
}

.pri中的INCLUDEPATH的作用

若有:INCLUDEPATH += $$PWD/treeview/
则在其他层级的文件中直接:#include “abc.h”

若没有:INCLUDEPATH += $$PWD/treeview/
则在其他层级的文件中需要一级一级查找并include:#include “xxx/yyy/zzz/abc.h”

只设置右上角的关闭按钮

setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint);
setWindowModality(Qt::WindowModal);  //模态窗口

在break之前可以直接return吗(switch中return后是否需要break)

在switch中使用return,不仅会跳出switch,还会直接结束循环。
已知执行到break就会跳出switch,不再执行后面的语句;return也有同样的效果。

在idea中,无论是在return后面写break,还是在break后写return,都会直接报错无法到达这条语句

多层for循环,break只会跳出一层循环

QTreeView

set属性设置

setHeaderHidden(true);                               //隐藏标题
setExpandsOnDoubleClick(false);                      //是否双击展开项
setRootIsDecorated(false);                           //是否显示展开和折叠顶级项的控件(v和>)
setEditTriggers(QAbstractItemView::NoEditTriggers);  //不可编辑
setIndentation(0);                                   //所有项的缩进为0
setAcceptDrops(true);                                //接受拖放事件
setAnimated(true);                                   //子项展开的动画

setExpanded不展开的问题

问题:当前item下没有子节点时,则setExpanded无效。必须先appendRow(),再setExpanded()
setExpanded必须从顶节点开始向下设置,若从最下面的节点向上设置,则无效。

setExpanded从内向外设置无效(先设置从子孙节点,再设置父节点),例子:

QStandardItem* currentItem  = new QStandardItem;
m_model->appendRow(currentItem);
for(int i = 0; i < 3; ++i){
	QStandardItem* childItem  = new QStandardItem;
	for(int i = 0; i < 5; ++i){
		QStandardItem* grandsonItem  = new QStandardItem;
		childItem->appendRow(grandsonItem);
	}
	//此时想的是给childItem添加完子节点后,再去展开它,但是当前节点的父节点都不知道展开了没有,在这里展开子节点是豪无意义的
	//所以这里的childItem展开是无效的。在这之前必须先设置父节点的setExpanded
	setExpanded(childItem ->index(), true);  
	
	currentItem->appendRow(childItem );  
}
setExpanded(currentItem->index(), true);  //currentItem展开

以上代码会导致:childItem不会展开

改正:setExpanded必须从外向内设置(从父到子,再到孙,进行设置setExpanded

//每次appendRow之后就setExpanded
QStandardItem* currentItem  = new QStandardItem;
m_model->appendRow(currentItem);
setExpanded(currentItem->index(), true);  //currentItem展开
for(int i = 0; i < 3; ++i){
	QStandardItem* childItem  = new QStandardItem;
	currentItem->appendRow(childItem );  
	setExpanded(childItem ->index(), true);  
	for(int i = 0; i < 5; ++i){
		QStandardItem* grandsonItem  = new QStandardItem;
		childItem->appendRow(grandsonItem);
	}			
}

迭代器怎么赋值:使用*blockIter

for (QList<BlockData>::iterator blockIter = osciModelData.oscilloscope.blockDList.begin();
     blockIter != osciModelData.oscilloscope.blockDList.end(); ++blockIter) {
    if (blockModelData.block.uid == blockIter->uid) {  //找到是哪个Block
        *blockIter = blockModelData.block;
    }
}

枚举的定义

不同枚举类型的枚举元素不能同名

findChild的使用

返回parentWidget中一个名为“button1”的QPushButton孩子,即使按钮不是父亲的直接孩子:

QPushButton *button = parentWidget->findChild<QPushButton *>("button1");

返回parentWidget中的一个QListWidget孩子:

QListWidget *list = parentWidget->findChild<QListWidget *>();

返回parentWidget(它的直接父亲)中一个名为“button1”的QPushButton孩子:

QPushButton *button = parentWidget->findChild<QPushButton *>("button1", Qt::FindDirectChildrenOnly);

返回parentWidget(它的直接父亲)中的一个QListWidget孩子:

QListWidget *list = parentWidget->findChild<QListWidget *>(QString(), Qt::FindDirectChildrenOnly);

connect

connect写法

connect的lambda表达式

connect(pushButton, &QPushButton::clicked, [=](){...});
// [=]代表把外部所有局部变量、类中所有成员以值的传递方式;如果当前connect处于某个函数中,则表示当前函数下的所有变量、类中所有成员以值的传递方式
// [this]代表把类中所有成员以值的传递方式  
// [&]代表把外部所有局部变量、类中所有成员以引用的传递方式;如果当前connect处于某个函数中,则表示当前函数下的所有变量、类中所有成员以引用的传递方式
// ()传递过来的参数

connect(m_var, &QPushButton::sigSetVar, [=](const VarData& data) mutable {...});
//如果参数为const类型,并且后面使用到该参数,比如赋值之类的,需要写mutable,去掉const属性

lambda中的值捕获[=]与引用捕获[&]

以值捕获 [=]

	ProjectTreeModelData varModelData = varStandardItem->data(Qt::UserRole + 1).value<ProjectTreeModelData>();
	connect(colorWidget, &ColorSelectedDialog::sigBasicColor, [=](const QColor& color) mutable {
		//此时获得是varModelData的副本
		varModelData.variable.variableData.lineColor = color.name();   //若color是蓝色
    	colorWidget->close();
	});
	//因为上面的赋值是赋给varModelData的副本,这里的setData中varModelData中lineColor还是原来的颜色
	varStandardItem->setData(QVariant::fromValue(varModelData), Qt::UserRole + 1);
	//此时获得的varModelData中lineColor还是原来的颜色,并没有改变为蓝色
	varModelData = varStandardItem->data(Qt::UserRole + 1).value<ProjectTreeModelData>();  

以引用捕获 [&]

	ProjectTreeModelData varModelData = varStandardItem->data(Qt::UserRole + 1).value<ProjectTreeModelData>();
	connect(colorWidget, &ColorSelectedDialog::sigBasicColor, [&](const QColor& color) mutable {
		//此时获得是varModelData本身,而非副本
		varModelData.variable.variableData.lineColor = color.name();   //若color是蓝色
    	colorWidget->close();
	});
	//因为上面的赋值是赋给varModelData结构本身,这里的setData中varModelData中lineColor已成为蓝色
	varStandardItem->setData(QVariant::fromValue(varModelData), Qt::UserRole + 1);
	//此时获得的varModelData中lineColor变为蓝色
	varModelData = varStandardItem->data(Qt::UserRole + 1).value<ProjectTreeModelData>();  

发信号之前一定还要先建立连接connect

比如,再给另一个界面发送数据时,一定要先在那个界面当中connect建立连接,否则信号执行那边接收不到数据。

再比如,一定要在一个界面exec()之前写connect,因为exec()会阻塞下面的代码

一般情况写在构造函数里面,在初始化的时候开始监听。具体情况具体处理。

connect中嵌套connect,注意重复连接

connect(addVarAct, &QAction::triggered, [=]() {
        connect(SignalsMedium::getInstance(), &SignalsMedium::sigBasicVarInfo,
                [=](const VarBasicData& varinfo) mutable { addVariable(blockStandardItem, varinfo); });

问题:connect中套connect,造成里面的connect重复连接。
解决:使用disconnect断开连接

connect(addVarAct, &QAction::triggered, [=]() {
        connect(SignalsMedium::getInstance(), &SignalsMedium::sigBasicVarInfo,
                [=](const VarBasicData& varinfo) mutable { addVariable(blockStandardItem, varinfo); });
		...
        /** 必须断开连接,否则会每点一次就会多连接一次,造成重复连接 **/
        disconnect(SignalsMedium::getInstance(), &SignalsMedium::sigBasicVarInfo, 0, 0);

connect的参数

一个信号可以连接到多个槽和信号。如果一个信号连接到多个槽,当信号发出时,这些槽会按照连接的顺序被激活。
enum Qt::ConnectionType

Constant Value Description
Qt::AutoConnection 0 (默认值)如果接收器位于发出信号的线程中,则使用Qt::DirectConnection。否则,使用Qt::QueuedConnection。在发出信号时确定连接类型。
Qt::DirectConnection 1 当发出信号时,立即调用该槽。槽在同一线程中执行。
Qt::QueuedConnection 2 当控制返回到接收者线程的事件循环时调用该槽。槽在接收者的线程中执行。
Qt::BlockingQueuedConnection 3 与Qt::QueuedConnection相同,除了信号线程阻塞直到插槽返回。如果发送方和接收方处于同一线程中,则不能使用此连接,否则应用程序将死锁。
Qt::UniqueConnection 0x80 这是一个标志,可以使用位或与上述任何一种连接类型组合使用。如果当前信号与槽连接过了,就不再连接了,防止重复连接。

怎样避免connect重复连接

特别是connect里面嵌套connect这种情况,特别容易造成内部的connect重复连接
解决:
(1)disconnect;(某些情况下,如果内部每一次的connect处理不同的事,最好使用disconnect)
(2)在构造里面写connect;(不是所有的connect都可以写在构造里面,如果有一个界面在构造中没有被new出来,那与该界面绑定的connect就无效)
(3)blockSignals(阻塞某一个控件的所有信号发送);
(4)Qt::UniqueConnection;(某些情况下,如果内部每一次的connect处理相同的事,则使用Qt::UniqueConnection)

信号槽连接 函数重载、参数不一致问题

QT5写法导致编译错误

connect(m_thread, &QThread::started, m_timer, &QTimer::start);  //线程开始,定时器开始

原因:start()为重载函数,由于QT5这种方式没有标明参数,编译器就无法确定连接的是哪一个函数,从而报错
【QT】Qt的随身笔记(持续更新...),怎样学好QT,qt
只能使用QT4的写法

connect(m_thread, SIGNAL(started()), m_timer, SLOT(start()));  //线程开始,定时器开始

Dialog鼠标在窗口外区域点击时,关闭该窗口

适用于模态的dialog

  • 设置窗口属性为 Qt::Popup,可自动实现窗口外点击关闭窗口
setWindowFlags(Qt::Popup);

以下两种方法不适用于模态的dialog,只适用于非模态的dialog和widget

  • 重写鼠标点击事件 (mousePressEvent()),若鼠标位置不在该窗口区域内,关闭窗口
  • 重写焦点失去事件 (focusOutEvent()),若窗口失去焦点,关闭窗口

QModelIndex

返回当前Index的第row行第column列的兄弟Index

QModelIndex::sibling(int row, int column)

【QT】Qt的随身笔记(持续更新...),怎样学好QT,qt
兄弟之间的index都可以互相得到,只需要知道兄弟的行和列。

QModelIndex index0 = index1.sibling(index.row(), 0);
QModelIndex index3 = index1.sibling(index.row(), 3;

【QT】Qt的随身笔记(持续更新...),怎样学好QT,qt```cpp

QModelIndex index0 = index1.sibling(0, index.column());
QModelIndex index3 = index1.sibling(3, index.column());

删除树的子节点

错误示范:

// 每移除一个子节点,其m_projectNameItem->rowCount()都会少1
//而移除的是m_projectNameItem->child(i),导致某些子节点没有删除
if (m_projectNameItem->hasChildren()) {
	for (int i = 0; i < m_projectNameItem->rowCount(); ++i) {
		removeStandardItem(m_projectNameItem->child(i), m_projectNameItem);
	}
}
m_model->removeRow(m_projectNameItem->row());

正确示例:

//先确定有多少个子节点
//每次都删第一个,才能保证删完
if (m_projectNameItem->hasChildren()) {
	int count = m_projectNameItem->rowCount();
	for (int i = 0; i < count; ++i) {
		removeStandardItem(m_projectNameItem->child(0), m_projectNameItem);
	}
}
m_model->removeRow(m_projectNameItem->row());

递归删除树的子节点

removeStandardItem(QStandardItem* standardItem, QStandardItem* parentStandardItem) {
    int count = standardItem->rowCount();
    if (0 == count) {
        parentStandardItem->removeRow(standardItem->row());
        return;
    }

    for (int i = 0; i < count; ++i) {
        QStandardItem* childStandardItem = standardItem->child(0);
        removeStandardItem(childStandardItem, standardItem);
    }

    parentStandardItem->removeRow(standardItem->row());
}

QLineEdit

setText会触发QLineEdit的textChanged信号

设置不可编辑状态:

setReadOnly(true); //只读

当前坐标与全局坐标的转换

QRect toolRect = geometry();
QPoint point(geometry().x + 100, geometry().y());
QPoint globalPoint = mapToGlobal(point);

判断当前焦点是否在某控件上

hasFocus();

Qt获得和失去焦点事件

重写控件的focusInEvent()focusOutEvent()函数

void focusInEvent(QFocusEvent *event) override;
void focusOutEvent(QFocusEvent *event) override;

关于List的比较

//错误示范
if (RTTVarList.at(i).basic == varModelData.variable.variableData.basic) {...}
//正确示范
//list的at()是const类型的,放在==的右边
if (varModelData.variable.variableData.basic == RTTVarList.at(i).basic) {...}

drag拖动如何显示拖动禁用的标志

在dragMoveEvent(QDragMoveEvent* event)里实现,在移动到某些位置就会出现禁用的标志

//当鼠标移动到rect里面时,接受该事件,否则忽略该事件,就会出现禁用标志
void dragMoveEvent(QDragMoveEvent* event){
	QRect rect(0,0,100,30);
	if(ret.contains(event.pos())){
		event->accept();
	}else{
		event->ignore();	
	}
}

【QT】Qt的随身笔记(持续更新...),怎样学好QT,qt

QFileDialog

enum QFileDialog::AcceptMode

文件对话框打开的样式

  • AcceptOpen:打开文件
    【QT】Qt的随身笔记(持续更新...),怎样学好QT,qt
  • AcceptSave:保存文件
    【QT】Qt的随身笔记(持续更新...),怎样学好QT,qt

enum QFileDialog::DialogLabel

对话框中的标签,可使用 setLabelText() 设置标签上的文本。
LookIn
FileName
FileType
Accept
Reject
【QT】Qt的随身笔记(持续更新...),怎样学好QT,qt

setFocus不生效的问题

不能在你的部件还不可见的时候去setFocus,这样不行。
解决这个问题的办法就是把setFocus()的操作放到我们这个部件的**showEvent()**里面去做。
而我们一般可能喜欢放到构造函数最后去setFocus,这样往往是没有效果的,问题就在这。文章来源地址https://www.toymoban.com/news/detail-730561.html

void DisplayWidget::showEvent(QShowEvent* event) {
    Q_UNUSED(event);
    m_osciNameLine->setFocus();
}

到了这里,关于【QT】Qt的随身笔记(持续更新...)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • QT非UI线程更新UI(跨线程更新UI)

    笔者最近在弄一个QT的一个小工具,需要把日志格式化输出,但是方法的执行过程是耗时的,如果不自己进行处理就会阻塞主界面,瞬间降低软件的使用体验。 所以通过查资料发现QT的信号传递更新UI非常好用,记录一下,也给碰到类似问题的小伙伴直接提供一个参考。 首先

    2024年02月12日
    浏览(46)
  • QT笔记——QT类反射机制简单学习

    学习 QT的 类反射机制 使用Qt 反射机制的条件 1.需要继承自QObject 类 或者 它的 派生类 ,并需要在类中加入Q_OBJECT 宏 2.注册成员函数:若希望普通成员函数能够被反射,需要在函数声明之前加入Q_INVOKABLE 宏。 3.注册成员变量:若希望成员变量能被反射,需要使用Q_PROPERTY 宏。

    2024年02月09日
    浏览(121)
  • 使用国内镜像源在线安装QT(2023.3.25更新)

    STEP1 :下载qt online installer Index of /official_releases/online_installers (qt.io) STEP2 :使用国内镜像源在线安装Qt      qDPass( 12MB/s) 在《STEP1》下载的“qt-unified-windows-x64-4.5.2-online.exe”目录进入CMD,然后运行下面的命令:  ./qt-unified-windows-x64-4.5.2-online.exe --mirror https://mirror.nju.edu.cn/qt   

    2024年02月12日
    浏览(46)
  • 【QT开发专题-天气预报】16.更新 UI 界面

    本专栏将会在未来 4 个月内,完成以下几个 Qt 项目: 《天气预报》 《文本编辑器》 《俄罗斯方块》 《绘图板》 《网络聊天室》 《串口助手》 完成时间预计在 2022-12-31 ,文章数目在 50 篇左右,更新完毕之后,价格恢复到 ¥299 专栏优势: 每个项目都是从零新建工程开始

    2023年04月08日
    浏览(64)
  • 【Qt笔记】1.VSCode搭建Qt运行环境

    Qt6之后已经抛弃qmake,使用cmake。 cmake相对qmake来说只有优点没有缺点。 使用VSCode搭建完Qt的运行环境就能开开心心debug了. vscode需要使用的插件: 1.Qt Configure 2.Qt tools 3.CMake 4.CMake Tools c++插件可以不关注,缺啥都会提醒你.正常安装即可。 安装好以上的插件后,ctrl + shift + p调用起命

    2024年02月06日
    浏览(38)
  • QT学习笔记-QT安装oracle oci驱动

    在使用QT开发应用的过程中,往往会把应用使用过程中产生的数据放入数据库进行统一存储,因此通过QT实现数据库的访问可以说是必须的一个知识点,其实QT访问数据库的语法及相关类和方法的使用并不复杂,但是对于使用QT的新人来说往往会开在数据库驱动加载的地方,比

    2024年02月12日
    浏览(52)
  • Qt学习笔记5---如何在Qt中添加资源文件

              前言: 在我们给Qt加上图片的时候,可以用本地资源添加的方式,但是此方法代码用的只能是本地资源,不好共享。那么我们可以把资源文件添加在Qt中,这样就不会出现这种问题啦! 第一步:在本地复制你想用的资源文件,点开项目的“在Explorer中显示”,粘贴

    2024年02月16日
    浏览(42)
  • Qt5学习笔记:Qt两个窗口(Widget)间传递数据

    0x01 原理介绍 Qt两个窗口之间传递数据实际上还是利用了信号与槽的概念来实现。 本质上讲,我们只需要实现A窗口发射信号,B窗口槽函数响应信号即可。 首先我们在A窗口的头文件中定义发射的信号: 然后在B窗口的头文件中定义槽函数: 最后我们需要将信号与槽连接起来,

    2024年02月13日
    浏览(44)
  • QT学习笔记-开发环境编译Qt MySql数据库驱动与交叉编译Qt MySql数据库驱动

    如果需要在QT程序中实现与MySQL数据库的交互,那么必不可少的一环就是对Qt MySql数据库驱动的编译。 操作系统:Windows10 专业版 64位 Qt版本:Qt 5.15.2 开发环境Qt安装路径:D:Qt 交叉编译服务器:Ubuntu 18.4 交叉编译服务器Qt安装路径:/opt/Qt 目标芯片:rk3568 目标平台:arm64 Qt安装

    2024年02月11日
    浏览(75)
  • Qt开发笔记(Qt5.9.9下载安装环境搭建win10)

    #1 Qt下载网站(国内、国外镜像) #2 Qt5.9.9安装选项 #3 配置系统环境变量 #4 创建测试项目 #1 Qt下载网站(国内、国外镜像) 官方下载地址(慢):http://download.qt.io/ 国内镜像网站 这里给大家推荐几个国内著名的 Qt 镜像网站,主要是各个高校的: 中国科学技术大学:http://mi

    2024年02月15日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包