QT学习——记事本项目

目录

1.项目概述

1.1界面预览

1.2功能介绍

2.UI设计

2.1控件

2.2图片的插入

3.程序的实现 

3.1程序实现按键的功能

3.2代码分析

3.2.1打开文件操作

3.2.2保存文件操作 

3.2.3关闭文件操作

​编辑​编辑

3.2.4切换编码格式

3.2.5显示当前行列和设置当前行高亮

 3.2.6快捷键打开和保存文件

3.2.7放大和缩小字体

 4.完整程序代码

4.1main.cpp

4.2widget

4.2.1widget.h

4.2.1widget.cpp

4.3mytextedit

4.3.1mytextedit.h

4.3.2mytextedit.cpp


1.项目概述

1.1界面预览

1.2功能介绍

  • 支持文本创建,打开,保存,关闭的功能

  • UI样式美化

  • 添加打开快捷键,添加保存快捷

  • 底部显示行列号及文本字符编码

  • Ctrl加鼠标滚轮支持字体放大缩小

2.UI设计

2.1控件

  • 按键 QPushButton
  • 水平布局 QHBoxLayout
  • 文本编辑器 TextEdit
  • 显示底色Widget
  • 弹簧

                                                    

                                                        

雏形大概长这个样子,后续插入图片就可以了 

2.2图片的插入

点击添加新文件

 

点击添加前缀 

我们不要前缀 

再次点击添加

 

将下载好的文件添加进去 

添加好的样子

 到UI界面右键点击按键

选择你的图片后点击ok 重复3次就可以把按键添加好了

 

刚插入图片可能是扁的自行调整图片的大小 

3.程序的实现 

3.1程序实现按键的功能

这里我们需要用到信号与槽的知识点,如下是对信号与槽的概述。

  • 信号 (Signals):是由对象在特定事件发生时发出的消息。例如, QPushButton 有一个
  • clicked() 信号,当用户点击按钮时发出。
  • (Slots):是用来响应信号的方法。一个槽可以是任何函数,当其关联的信号被发出时,该槽函数将被调用。
  • 连接信号和槽:使用 QObject::connect() 方法将信号连接到槽。当信号发出时,关联的槽函数 会自动执行。 

按键我们使用如下方式进行信号与槽的关联(信号与槽关联有许多方式这是最简单的一种) 

点击ok即可,分别对3个按键执行上述操作后会在widget.h下生成3个槽函数

在widget.cpp下实现我们的槽函数的功能。

3.2代码分析

3.2.1打开文件操作

我们可能不了解某一个类的使用但是Qt提供了例程和参考我们在这个的基础上修改就可以了

重点:要学会使用翻译软件和Qt的帮助文档这才是学习Qt的精髓

QFileDialog类的使用方法和介绍

QFile类的使用方法和介绍

QFile是Qt中用于文件操作的基本类,提供了读取、写入、拷贝、移动和访问文件属性等功能。

如下是常用的一些方法

open() :打开一个文件。需要指定模式(如只读、只写、读写等)。
close() :关闭文件。
read() write() :用于读取和写入数据。
exists() :检查文件是否存在。
remove() :删除文件。
copy() :复制文件。

 一样的不了解这个类直接照着Qt帮助文档改就好了

void Widget::on_Btopen_clicked()
{
    QString fileName;
    QString strLine;

    // 弹出文件打开对话框,获取用户选择的文件名
    fileName = QFileDialog::getOpenFileName(this, tr("Open file"),
                                            "D:/QT/QtProject2/",
                                            tr("Image Files (*.png *.jpg *.bmp);;Text files (*.txt)"));

    // 清空文本编辑框内容
    ui->textEdit->clear();

    // 设置文件名并尝试打开文件
    file.setFileName(fileName);
    if(!file.open(QIODevice::ReadWrite | QIODevice::Text)){
        qDebug() << "open fail!";
    }

    // 设置窗口标题为文件名
    this->setWindowTitle(fileName + "-记事本");

    // 创建文本流对象,关联文件
    QTextStream in(&file);
    // 设置文本编码格式
    in.setCodec(ui->comboBox->currentText().toStdString().c_str()); // 将QString转成const char *

    // 逐行读取文件内容,并显示在文本编辑框中
    while (!in.atEnd()) {
        strLine = in.readLine(file.size());
        ui->textEdit->append(strLine); // 将每行文本追加到文本编辑框中
    }
}

3.2.2保存文件操作 

void Widget::on_Btsave_clicked()
{
    // 检查文件是否已经打开
    if(!file.isOpen()){
        // 弹出文件保存对话框,获取文件名
        QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
                                                        "D:/QT/QtProject2/",
                                                        tr("Text files (*.txt)"));

        // 设置文件名并尝试打开文件
        file.setFileName(fileName);
        if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
            qDebug() << "open fail!";
        }

        // 设置窗口标题为文件名
        this->setWindowTitle(fileName + "-记事本");
    }

    // 创建文本流对象,关联文件
    QTextStream out(&file);
    // 设置文本编码格式
    out.setCodec(ui->comboBox->currentText().toStdString().c_str()); // 将QString转成const char *
    // 获取文本编辑框中的文本
    QString strText = ui->textEdit->toPlainText();
    // 将文本写入文件
    out << strText;
}

3.2.3关闭文件操作

void Widget::on_Btclose_clicked()
{
    // 弹出消息框,询问用户是否保存修改的文档
    int ret = QMessageBox::warning(this, tr("记事本"),
                                   tr("文档已修改,要保存更改吗?"),
                                   QMessageBox::Save | QMessageBox::Discard
                                   | QMessageBox::Cancel,
                                   QMessageBox::Save);

    // 根据用户的选择进行相应的操作
    switch (ret) {
    case QMessageBox::Save:
        // 用户选择保存
        on_Btsave_clicked(); // 调用保存按钮的槽函数,保存文档
        break;
    case QMessageBox::Discard:
        // 用户选择不保存
        ui->textEdit->clear(); // 清空文本编辑框内容
        this->setWindowTitle("记事本"); // 设置窗口标题为默认值
        if(file.isOpen()){
            file.close(); // 关闭文件
        }
        break;
    case QMessageBox::Cancel:
        // 用户选择取消
        break;
    default:
        break;
    }
}

经过这3个按键的开发流程是不是Qt的学习很简单照着帮助文档改就可以了

3.2.4切换编码格式

这里的信号与槽使用的是手动关联的方式

 connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int)));

widget.h文件也要声明一下这里的槽函数名字要一致

void onCurrentIndexChanged(int index);

widget.cpp对槽函数的具体实现 

void Widget::onCurrentIndexChanged(int index)
{
    QString strLine;

    // 清空文本编辑框内容
    ui->textEdit->clear();

    // 将文件指针移动到文件开头
    file.seek(0);

    // 如果文件已经打开
    if(file.isOpen()){
        // 创建文本流对象,关联文件
        QTextStream in(&file);
        // 设置文本编码格式
        in.setCodec(ui->comboBox->currentText().toStdString().c_str()); // 将QString转成const char *

        // 逐行读取文件内容,并显示在文本编辑框中
        while (!in.atEnd()) {
            strLine = in.readLine(file.size());
            ui->textEdit->append(strLine); // 将每行文本追加到文本编辑框中
        }
    }
}

3.2.5显示当前行列和设置当前行高亮

 设置当前行高亮要用到c++模板的知识点

C++ 中,模板( Template )是一种通用的编程工具,允许程序员编写 泛型代码 ,使得 类或函数能够适 用于多种不同的数据类型 而不需要重复编写相似的代码

void Widget::onCursorPositionChanged()
{
    QTextCursor cursor;

    // 获取文本编辑框的光标对象
    cursor = ui->textEdit->textCursor();

    // 获取光标所在的行号和列号
    QString block  = QString::number(cursor.blockNumber() + 1);
    QString column = QString::number(cursor.columnNumber() + 1);

    // 构建显示位置的字符串
    const QString labpos = "行:"+block+",列:"+column;

    // 在标签上显示位置信息
    ui->labelpos->setText(labpos);

    // 设置整行高亮
    QList<QTextEdit::ExtraSelection> extra;
    QTextEdit::ExtraSelection ext;

    // 获取当前行的光标
    ext.cursor = cursor;

    // 设置高亮颜色为浅灰色
    QBrush qBrush(Qt::lightGray);
    ext.format.setBackground(qBrush);

    // 设置选中区域为整行
    ext.format.setProperty(QTextFormat::FullWidthSelection, true);

    // 将设置好的高亮添加到额外选择列表中
    extra.append(ext);

    // 将额外选择列表应用到文本编辑框中
    ui->textEdit->setExtraSelections(extra);
}

 3.2.6快捷键打开和保存文件

部分快捷键

参考QT中的例子 

在关联信号与槽时使用了Lambda 表达式(匿名函数

基本语法如下: 

[捕获列表](形参列表) -> 返回值的类型 {
// 函数体
// 可以使用捕获列表中的变量
return expression; // 可选的返回语句
}
    QShortcut *shortcutopen = new QShortcut(QKeySequence(tr("Ctrl+O", "File|Open")),this);
    QShortcut *shortcutsave = new QShortcut(QKeySequence(tr("Ctrl+s", "File|Save")),this);

    connect(shortcutopen, &QShortcut::activated, [=](){
        on_Btopen_clicked();
    });

    connect(shortcutsave, &QShortcut::activated, [=](){
        on_Btsave_clicked();
    });

3.2.7放大和缩小字体

这里用到了事件这个知识点

事件的概述

众所周知 Qt 是一个基于 C++ 的框架,主要用来开发带窗口的应用程序(不带窗口的也行,但不是主流)。 我们使用的基于窗口的应用程序都是基于事件,其目的主要是用来实现回调(因为只有这样程序的效率 才是最高的)。所以在Qt 框架内部为我们提供了一些列的事件处理机制,当窗口事件产生之后,事件会 经过: 事件派发 -> 事件过滤->事件分发->事件处理 几个阶段。 Qt 窗口中对于产生的一系列事件都有默认 的处理动作,如果我们有特殊需求就需要在合适的阶段重写事件的处理动作,比如信号与槽就是一种 事件(event )是由系统或者 Qt 本身在不同的场景下发出的。当用户按下 / 移动鼠标、敲下键盘,或者是 窗口关闭/ 大小发生变化 / 隐藏或显示都会发出一个相应的事件。一些事件在对用户操作做出响应时发出, 如鼠标/ 键盘事件等;另一些事件则是由系统自动发出,如计时器事件。
每一个 Qt 应用程序都对应一个唯一的 QApplication 应用程序对象,然后调用这个对象的 exec() 函 数,这样Qt 框架内部的事件检测就开始了( 程序将进入事件循环来监听应用程序的事件 )。
事件在Qt中产生之后,的分发过程是这样的:
//1. 当事件产生之后,Qt使用用应用程序对象调用 notify() 函数将事件发送到指定的窗口:
[override virtual] bool QApplication::notify(QObject *receiver, QEvent *e);
//2. 事件在发送过程中可以通过事件过滤器进行过滤,默认不对任何产生的事件进行过滤。
// 需要先给窗口安装过滤器, 该事件才会触发
[virtual] bool QObject::eventFilter(QObject *watched, QEvent *event)
//3. 当事件发送到指定窗口之后,窗口的事件分发器会对收到的事件进行分类:
[override virtual protected] bool QWidget::event(QEvent *event);
//4. 事件分发器会将分类之后的事件(鼠标事件、键盘事件、绘图事件。。。)分发给对应的事件处理
//器函数进行处理,每个事件处理器函数都有默认的处理动作(我们也可以重写这些事件处理器函
//数),比如:鼠标事件:
// 鼠标按下
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);
// 鼠标释放
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event);
// 鼠标移动
[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event);

 鼠标滑轮和按键事件的实现

// 滑轮事件
void myTextEdit::wheelEvent(QWheelEvent *e)
{
    // 获取滚轮滚动的度数变化量
    qDebug() << e->angleDelta().y();

    // 如果 Ctrl 键被按下
    if (CtrlKeyCheck == 1) {
        // 如果滚轮向上滚动
        if (e->angleDelta().y() > 0) {
            zoomIn(); // 调用放大函数
        }
        // 如果滚轮向下滚动
        else if (e->angleDelta().y() < 0) {
            zoomOut(); // 调用缩小函数
        }
        e->accept(); // 接受事件
    } else {
        QTextEdit::wheelEvent(e); // 让事件继续进行 QTextEdit 的操作
    }
}

// 键盘按下事件
void myTextEdit::keyPressEvent(QKeyEvent *e)
{
    // 如果按下的是 Ctrl 键
    if (e->key() == Qt::Key_Control) {
        CtrlKeyCheck = 1; // 设置 CtrlKeyCheck 为 1
    } else {
        QTextEdit::keyPressEvent(e); // 让事件继续进行 QTextEdit 的操作
    }
}

// 键盘松开事件
void myTextEdit::keyReleaseEvent(QKeyEvent *e)
{
    // 如果松开的是 Ctrl 键
    if (e->key() == Qt::Key_Control) {
        CtrlKeyCheck = 0; // 设置 CtrlKeyCheck 为 0
    } else {
        QTextEdit::keyReleaseEvent(e); // 让事件继续进行 QTextEdit 的操作
    }
}

这里搞重名了注意一下

void Widget::zoomIn()
{
    // 获取当前的字体
    QFont qfont = ui->textEdit->font();

    // 获取当前字体的大小
    int sizeFont = qfont.pointSize();

    // 如果字体大小为-1,表示使用的是像素单位,不进行缩放
    if (sizeFont == -1)
        return;

    // 改变字体大小,增加1个点大小
    int newSetFont = sizeFont + 1;
    qfont.setPointSize(newSetFont);

    // 设置新的字体到文本编辑器
    ui->textEdit->setFont(qfont);
}
void Widget::zoomOut()
{
    // 获取当前的字体
    QFont qfont = ui->textEdit->font();

    // 获取当前字体的大小
    int sizeFont = qfont.pointSize();

    // 如果字体大小为-1,表示使用的是像素单位,不进行缩放
    if (sizeFont == -1)
        return;

    // 改变字体大小,减少1个点大小
    int newSetFont = sizeFont - 1;
    qfont.setPointSize(newSetFont);

    // 设置新的字体到文本编辑器
    ui->textEdit->setFont(qfont);
}

 4.完整程序代码

4.1main.cpp

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}

4.2widget

4.2.1widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QFile>
#include <QWidget>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    QFile file;

    explicit Widget(QWidget *parent = 0);
    ~Widget();

    void zoomIn();

    void zoomOut();
private slots:
    void on_Btopen_clicked();

    void on_Btsave_clicked();

    void on_Btclose_clicked();

    void onCurrentIndexChanged(int index);

    void onCursorPositionChanged();

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

4.2.1widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QFileDialog>
#include <QDebug>
#include <QMessageBox>
#include <QShortcut>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    //让窗口变化
    this->setLayout(ui->verticalLayout);

    QShortcut *shortcutopen = new QShortcut(QKeySequence(tr("Ctrl+O", "File|Open")),this);
    QShortcut *shortcutsave = new QShortcut(QKeySequence(tr("Ctrl+s", "File|Save")),this);
    QShortcut *zommIn  = new QShortcut(QKeySequence(tr("Ctrl+Shift+=", "File|ZoomIn")),this);
    QShortcut *zommout = new QShortcut(QKeySequence(tr("Ctrl+Shift+-", "File|Zoomout")),this);

    connect(shortcutopen, &QShortcut::activated, [=](){
        on_Btopen_clicked();
    });

    connect(shortcutsave, &QShortcut::activated, [=](){
        on_Btsave_clicked();
    });

    connect(zommIn, &QShortcut::activated, [=](){
        zoomIn();
    });

    connect(zommout, &QShortcut::activated, [=](){
        zoomOut();
    });

    connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int)));
    connect(ui->textEdit, SIGNAL(cursorPositionChanged()), this, SLOT(onCursorPositionChanged()));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::zoomIn()
{
    //获取当前的字体
    QFont qfont = ui->textEdit->font();
    //获取当前字体的大小
    int sizeFont = qfont.pointSize();
    if(sizeFont == -1) return;
    //改变字体
    int newSetFont = sizeFont + 1;
    qfont.setPointSize(newSetFont);
    ui->textEdit->setFont(qfont);
}

void Widget::zoomOut()
{
    //获取当前的字体
    QFont qfont = ui->textEdit->font();
    //获取当前字体的大小
    int sizeFont = qfont.pointSize();
    if(sizeFont == -1) return;
    //改变字体
    int newSetFont = sizeFont - 1;
    qfont.setPointSize(newSetFont);
    ui->textEdit->setFont(qfont);
}

void Widget::on_Btopen_clicked()
{
    QString fileName;
    QString strLine;

    fileName = QFileDialog::getOpenFileName(this,
                                            tr("Open file"),
                                            "D:/QT/QtProject2/",
                                            tr("Image Files (*.png *.jpg *.bmp);;Text files (*.txt)"));
    ui->textEdit->clear();
    file.setFileName(fileName);
    if(!file.open(QIODevice::ReadWrite | QIODevice::Text)){
        qDebug() << "open fail!";
    }

    this->setWindowTitle(fileName + "-记事本");
    QTextStream in(&file);
    //in.setCodec("UTF-8");
    in.setCodec(ui->comboBox->currentText().toStdString().c_str());//将QString转成const chat *
    while (!in.atEnd()) {
        strLine = in.readLine(file.size());
        //qDebug() << strLine;
        //ui->textEdit->setText(strLine);
        ui->textEdit->append(strLine);
    }
}

void Widget::on_Btsave_clicked()
{
    if(!file.isOpen()){
        QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
                                                        "D:/QT/QtProject2/",
                                                        tr("Text files (*.txt)"));

        file.setFileName(fileName);
        if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
            qDebug() << "open fail!";
        }

        this->setWindowTitle(fileName + "-记事本");
    }
    QTextStream out(&file);
    //out.setCodec("UTF-8");
    out.setCodec(ui->comboBox->currentText().toStdString().c_str());//将QString转成const chat *
    QString strText = ui->textEdit->toPlainText();
    out << strText;

}

void Widget::on_Btclose_clicked()
{

    int ret = QMessageBox::warning(this, tr("记事本"),
                                   tr("文档已修改,要保存更改吗?"),
                                   QMessageBox::Save | QMessageBox::Discard
                                   | QMessageBox::Cancel,
                                   QMessageBox::Save);
    switch (ret) {
    case QMessageBox::Save:
        // Save was clicked
        on_Btsave_clicked();
        break;
    case QMessageBox::Discard:
        // Don't Save was clicked
        ui->textEdit->clear();
        this->setWindowTitle("记事本");
        if(file.isOpen()){
            file.close();
        }
        break;
    case QMessageBox::Cancel:
        // Cancel was clicked
        break;
    default:
        // should never be reached
        break;
    }
}

void Widget::onCurrentIndexChanged(int index)
{
    QString strLine;

    ui->textEdit->clear();
    file.seek(0);
    if(file.isOpen()){
        QTextStream in(&file);
        in.setCodec(ui->comboBox->currentText().toStdString().c_str());//将QString转成const chat *
        while (!in.atEnd()) {
            strLine = in.readLine(file.size());
            ui->textEdit->append(strLine);
        }
    }
}

void Widget::onCursorPositionChanged()
{
    QTextCursor cursor;

    cursor = ui->textEdit->textCursor();

    QString block  = QString::number(cursor.blockNumber() + 1);
    QString column = QString::number(cursor.columnNumber() + 1);

    //qDebug() << block << "," << column ;
    const QString labpos = "行:"+block+",列:"+column;
    ui->labelpos->setText(labpos);

    //设置整行高亮
    QList<QTextEdit::ExtraSelection> extra;
    QTextEdit::ExtraSelection ext;
    //获取当前行
    ext.cursor = cursor;
    QBrush qBrush(Qt::lightGray);
    //选择颜色
    ext.format.setBackground(qBrush);
    ext.format.setProperty(QTextFormat::FullWidthSelection, true);
    //设置
    extra.append(ext);
    ui->textEdit->setExtraSelections(extra);
}

4.3mytextedit

4.3.1mytextedit.h

#ifndef MYTEXTEDIT_H
#define MYTEXTEDIT_H

#include <QTextEdit>

class myTextEdit : public QTextEdit
{
public:
    myTextEdit(QWidget *parent);

protected:
    void wheelEvent(QWheelEvent *e) override;//滑轮事件
    void keyPressEvent(QKeyEvent *e) override;//键盘的按下事件
    void keyReleaseEvent(QKeyEvent *e) override;//键盘的松开事件
private:
    bool CtrlKeyCheck = 0;
};

#endif // MYTEXTEDIT_H

4.3.2mytextedit.cpp

#include "mytextedit.h"

#include <QWheelEvent>
#include <QDebug>

myTextEdit::myTextEdit(QWidget *parent) : QTextEdit(parent)
{

}
//滑轮事件
void myTextEdit::wheelEvent(QWheelEvent *e)
{
    //获取滚轮度数的变化量e->angleDelta().y();
    qDebug() << e->angleDelta().y();
    if(CtrlKeyCheck == 1){
        if(e->angleDelta().y() > 0){
            zoomIn();
        }else if(e->angleDelta().y() < 0){
            zoomOut();
        }
        e->accept();
    }else{
        QTextEdit::wheelEvent(e);//让事件进行QTextEdit的操作
    }

}
//键盘的按下事件
void myTextEdit::keyPressEvent(QKeyEvent *e)
{
    if(e->key() == Qt::Key_Control){
        CtrlKeyCheck = 1;
    }else{
        QTextEdit::keyPressEvent(e);
    }

}
//键盘的松开事件
void myTextEdit::keyReleaseEvent(QKeyEvent *e)
{
    if(e->key() == Qt::Key_Control){
        CtrlKeyCheck = 0;
    }else{
        QTextEdit::keyReleaseEvent(e);
    }
}

  • 21
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个简单的Python Qt记事本的实现,可以保存和打开文本文件。 ```python import sys from PyQt5.QtWidgets import QApplication, QWidget, QTextEdit, QPushButton, QVBoxLayout, QFileDialog class Notepad(QWidget): def __init__(self): super().__init__() self.text_edit = QTextEdit(self) self.save_button = QPushButton('Save', self) self.open_button = QPushButton('Open', self) self.save_button.clicked.connect(self.save_file) self.open_button.clicked.connect(self.open_file) layout = QVBoxLayout() layout.addWidget(self.text_edit) layout.addWidget(self.save_button) layout.addWidget(self.open_button) self.setLayout(layout) def save_file(self): file_path, _ = QFileDialog.getSaveFileName(self, 'Save File') if file_path: with open(file_path, 'w') as f: f.write(self.text_edit.toPlainText()) def open_file(self): file_path, _ = QFileDialog.getOpenFileName(self, 'Open File') if file_path: with open(file_path, 'r') as f: self.text_edit.setText(f.read()) if __name__ == '__main__': app = QApplication(sys.argv) notepad = Notepad() notepad.show() sys.exit(app.exec_()) ``` 在这个实现中,我们使用了`QTextEdit`窗口部件作为文本编辑器,`QPushButton`窗口部件作为保存和打开按钮。当用户单击“Save”按钮时,我们使用`QFileDialog`窗口部件来获取用户要保存的文件路径,并使用`with open()`语句将文本写入文件。当用户单击“Open”按钮时,我们使用相同的窗口部件来获取用户要打开的文件路径,并使用`with open()`语句读取文件并将其插入到文本编辑器中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值