QT学习记录

学习进度

week2:
Qt Graphics View Framework
QPainterDevice

QPixmap:显示图像,其中QBitmap是它一个黑白图的子类(用isQBitmap()判断)。可以使用QPainter在上面绘图。可以接受图片路径显示,并用drawPixmap()函数把文件绘制到组件上。除此之外还有grabWidget、grabWindow()函数等,将图像绘制到目标上。QPixmap不必使用指针、无法提供像素级操作,显示一致
QImage:像素级的图像访问
QPicture:记录和重现QPainter的命令。使用方法

//记录
QPicture picture; 
QPainter painter; 
painter.begin(&picture);              // paint in picture 
painter.drawEllipse(10,20, 80,70); // draw an ellipse 
painter.end();                           // painting done 
picture.save("drawing.pic");         // save picture

//重现
QPicture picture; 
picture.load("drawing.pic");          // load picture 
QPainter painter; 
painter.begin(&myImage);            // paint in myImage 
painter.drawPicture(0, 0, picture); // draw the picture at (0,0) 
painter.end(); 
旋转

视口与窗口是一致的
世界坐标系通过改变坐标系进行放缩。

QPainter渐变
image.png

重点是:渐变对象要传到QBrush里。如果想画渐变线段,可以把QBrush作为参数传进QPen里。
渐变线段

void PaintedWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    QLinearGradient linearGradient(60,50,200,200);
    linearGradient.setColorAt(0.2,Qt::white);
    linearGradient.setColorAt(0.6,Qt::green);
    linearGradient.setColorAt(1.0,Qt::black);
    painter.setPen(QPen(QBrush(linearGradient),5));
    painter.drawLine(50,50,200,200);
}

渐变图形


void PaintedWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    QLinearGradient linearGradient(60,50,200,200);
    linearGradient.setColorAt(0.2,Qt::white);
    linearGradient.setColorAt(0.6,Qt::green);
    linearGradient.setColorAt(1.0,Qt::black);
    painter.setBrush(QBrush(linearGradient));
    //渐变对象传进QBrush里
    painter.drawEllipse(50,50,200,150);
}
QPainter

QPainter:执行绘制操作
QPaintEngine:绘制操作与抽象二维空间的接口
QPaintDevice:二维空间的抽象
this指针是成员函数的隐含参数,在成员函数内部指向调用对象。当我们调用成员函数时,实际上是替某个对象调用它。成员函数通过一个名为 this 的额外隐式参数来访问调用它的那个对象,当我们调用一个成员函数时,用请求该函数的对象地址初始化 this。例如,如果调用 total.isbn()则编译器负责把 total 的地址传递给 isbn 的隐式形参 this。
pen是线框,brush是填充图形
setRenderHint反走样(打开一次后所有的代码都是反走样的)

main.cpp

#include "mainwindow.h"

#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    PaintedWidget w;
    w.show();
    return a.exec();
}

mainwindow.cpp

#include "mainwindow.h"
#include 
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
}

MainWindow::~MainWindow()
{
}

PaintedWidget::PaintedWidget()
{
    resize(800,600);
    setWindowTitle(tr("Paint Demo"));
}

void PaintedWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.drawLine(80,100,650,400);
    painter.setPen(Qt::red);
    painter.drawRect(10,10,100,400);
    painter.setPen(QPen(Qt::green,5));
    painter.setBrush(Qt::blue);
    painter.drawEllipse(50,150,400,200);
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 


class PaintedWidget:public QWidget
{
    public:
        PaintedWidget();
    protected:
        void paintEvent(QPaintEvent *event);
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
};
#endif // MAINWINDOW_H

自定义事件

事件与信号槽相比,可以是同步的、又可以是异步,而函数调用(槽的回调)总是同步的。同时时间可以使用过滤器。
事件type要大于999(之前的都是保留),介于1000~65535间。

//自定义事件的注册
static int QEvent::registerEventType ( int hint = -1 );

Qt 自定义事件_w3cschool

filter

watched:被监测的对象。函数调用->事件过滤->组件处理。重写时,如果需要过滤某事件需返回true。delete了某个接收组件,需将返回值设为true。
eventFilter()函数建立事件过滤器,installEventFilter()函数安装过滤器。
事件过滤器和被安装的组件需要在同一线程。
QT事件处理分层五个层次:重定义事件处理函数,重定义event()函数,为单个组件安装事件过滤器,为QApplication安装事件过滤器,重定义QCoreApplication的notify()函数。控制权逐层增大

event demo
image.png

accept:事件处理函数接收了该事件,不需要再传递。
ignore:事件处理函数忽略了该事件,需要再传递。
isAccepted:查询事件是不是已经被接收了
不常用,而用响应函数代替。因为子类直接忽略事件,则Qt不会再去寻找其他的接收者,那么父类操作也就不能进行。
窗口关闭时要用。

#include "mainwindow.h"

#include 
#include
#include
#include

class EventLabel :public QLabel
{
    protected:
        void mouseMoveEvent(QMouseEvent *event);
        void mousePressEvent(QMouseEvent *event);
        void mouseReleaseEvent(QMouseEvent *event);
};
void EventLabel::mouseMoveEvent(QMouseEvent *event)
{
    this->setText(QString("

Move:(%1,%2)

").arg(QString::number(event->x()),QString::number(event->y()))); } void EventLabel::mousePressEvent(QMouseEvent *event) { this->setText(QString("

Press:(%1,%2)

").arg(QString::number(event->x()),QString::number(event->y()))); } void EventLabel::mouseReleaseEvent(QMouseEvent *event) { QString msg; msg.sprintf("

Release:(%d,%d)

",event->x(),event->y()); this->setText(msg); } int main(int argc, char *argv[]) { QApplication app(argc, argv); EventLabel *label=new EventLabel; label->setWindowTitle("MouseEvent Demo"); label->resize(300,200); label->show(); MainWindow w; return app.exec(); }
signal与event区别

signal有具体对象发出,然后立即交给connect函数连接的slot进行处理。事件则是追加到事件队列尾部逐个维护(也可以插队、筛选)。
组件关注信号槽,自定义组件关注事件。
protected virtual :protected可以使被修饰的方法或字段只能在当前类及其子类中使用,防止外部的无意更改。virtual是虚方法,可以由设计人员自行决定是否包含方法的实现。
private:不能被外部访问也不能被继承。也会被编译出现在内存中,但编译器限制程序不能访问private成员
public:可以被外部访问也可以被继承。在编译时直接编译
protected:不能被外部访问但可以被继承
子类可以对父类的函数重写,或在其基础上增加功能。父类的指针可以指向子类对象,编译时是从父类到子类。
构造和析构顺序相反(构造先父后子,析构先子后父)
virtual:当一个成员函数需要子类重写,则在父类应该将其声明为virtual。即将被重写的函数加virtual是良好的编写习惯。加了virtual就只能调用重写的函数了。
父类有多个构造函数时可以显式调用其中一个(通过指定参数)。
类的大小与成员变量有关,与成员函数无关(被声明virtual时大小会有些微编译器决定的变化)。
基类的析构函数要加virtual(不加的时候,基类指针指向派生类delete时,派生类部分清楚导致内存泄漏,加上时,调用基类的析构函数编译器会从虚函数表找到要执行的正确的函数地址,即子类的析构函数),构造函数不能加。
尽量不要多对一地多重继承,成员名重复时会报错。
类的继承参考:(21条消息)
【C++】类的继承(protected,virtual)_protected virtual_mjiansun的博客-CSDN博客

event()

将事件对象按类型分发的函数,可以重写该函数让事件分发前做一些操作、或完成对自定义事件的分发。类型判断:event->type(),返回QEvent::Type的类型枚举。
evnet()返回bool类型,其中true为传入事件已识别且处理,QApplication会继续处理事件队列下一事件。若为false则QApplicaion转去寻找下一个该event的处理函数。

QInputDialog

QLineEdit是参数mode,取值范围:QLineEdit::EchoMode,默认是Normal,如果为password则是密码输入。
&isOK:判断用户按下的是OK还是Cancel
第七个参数:flags指定对话框的样式。
返回QString,通过Ok参数判断是不是true。

    bool isOK;
    QString text=QInputDialog::getText(NULL,"TITLE","Label",QLineEdit::Normal,"your comment",&isOK);
    if(isOK)
    {
        QMessageBox::information(NULL,"Information","your comment is:"+text+"",QMessageBox::Yes||QMessageBox::No,QMessageBox::Yes);
    }
QMessageBox

1.QMessageBox一种是static(返回StandardButton,通过返回值判断)一种是构造函数(通过if判断)。
构造函数:

    //msgbox 构造函数方式
    QMessageBox message(QMessageBox::NoIcon,"Show Qt","Do you want to show qt dialog?",QMessageBox::Yes|QMessageBox::No,NULL);
    if(message.exec()==QMessageBox::Yes)
    {
        QMessageBox::aboutQt(NULL,"About Qt");
    }

static:

    //msgbox static方式返回值
    QMessageBox::StandardButton rb=QMessageBox::question(NULL,"Show QT","Do you want to show Qt dialog?",QMessageBox::Yes|QMessageBox::No,QMessageBox::Yes);
    if(rb==QMessageBox::Yes)
    {
        QMessageBox::aboutQt(NULL,"About Qt");
    }

2.Yes和No按钮:QMessageBox::Yes | QMessageBox::No。msgbox static分类:critical(红色禁止),warning(黄色警告),question(问好),about(没有选择按钮只有Ok)。
3.msg对话框文本支持:支持HTML标签
4.定义图片:非静态,用exec()而不是show()
5.png格式,相对路径。

QMessageBox message(QMessageBox::NoIcon, "Title", "Content with icon."); 
message.setIconPixmap(QPixmap("icon.png")); 
message.exec();
parent参数

作用:用于指示是否是顶层容器(有parent的就不是顶层容器)。指明组件的父组件,这样在父组件delete时所有的子也会被回收。

QColorDialog

1%%2是占位符,arg()替换掉占位符,QSttring::number()把值替换成等值字符串。这里是把RGB值列出来并输出。

void MainWindow::open()
{
    QString path=QFileDialog::getOpenFileName(this,tr("Open the file"),".",tr("Image Files(*.jpg*.png)"));
    if(path.length()==0)
    {
        QMessageBox::information(NULL,tr("Path"),tr("You didn;t select any files."));
    }
    else
    {
        QMessageBox::information(NULL,tr("Path"),tr("You selected")+path);
    }
//    QMessageBox::information(NULL,tr("Open"),tr("Open a file"));
    QColor color=QColorDialog::getColor(Qt::white,this);
    QString msg=QString("r:%1,g:%2,b").arg(QString::number(color.red()),QString::number(color.green()),QString::number(color.blue()));
    QMessageBox::information(NULL,"Selected color",msg);
}
QFileDialog与getOpenFileName

getOpenFileName函数签名:parent父组件、caption对话框标题、dir默认打开的目录、filter对话框的后缀名过滤器,如需要多个可使用;;分割、selectedFilter默认选择的过滤器、options对话框参数设定,取值为enumQFileDialog::Option,可用|运算组合。
getOpenFileNames()选择多个文件,返回QStringList
另一种写法。getOpenFileNmae是本地对话框,QFileDialog是Qt自己绘制的

QFileDialog *fileDialog = new QFileDialog(this); 
        fileDialog->setWindowTitle(tr("Open Image")); 
        fileDialog->setDirectory("."); 
        fileDialog->setFilter(tr("Image Files(*.jpg *.png)")); 
        if(fileDialog->exec() == QDialog::Accepted) { 
                QString path = fileDialog->selectedFiles()[0]; 
                QMessageBox::information(NULL, tr("Path"), tr("You selected ") + path); 
        } else { 
                QMessageBox::information(NULL, tr("Path"), tr("You didn't select any files.")); 
        }

mainwindow.cpp,添加了文件打开框的实际操作

#include "mainwindow.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    //定义QAction 创造一个对象
    openAction=new QAction(tr("&Open"),this);
    openAction->setShortcut(QKeySequence::Open);//用于实现跨平台快捷键
    openAction->setStatusTip(tr("Open a file."));//状态栏的提示
    openAction->setIcon(QIcon(":/Open.png"));
    //把QAction添加到菜单和工具条
    connect(openAction,SIGNAL(triggered()),this,SLOT(open()));

    QMenu *file=menuBar()->addMenu(tr("&File"));
    file->addAction(openAction);

    QToolBar *toolBar=addToolBar(tr("&File"));
    toolBar->addAction(openAction);

    msgLabel=new QLabel;
    msgLabel->setMinimumSize(msgLabel->sizeHint());//设置大小为本身建议的大小
    msgLabel->setAlignment(Qt::AlignHCenter);//设置显示规则水平居中
    statusBar()->addWidget(msgLabel);//把label添加到状态栏
    statusBar()->setStyleSheet(QString("QStatusBar::item{border:0px}"));
}

void MainWindow::open()
{
    QString path=QFileDialog::getOpenFileName(this,tr("Open the file"),".",tr("Image Files(*.jpg*.png)"));
    if(path.length()==0)
    {
        QMessageBox::information(NULL,tr("Path"),tr("You didn;t select any files."));
    }
    else
    {
        QMessageBox::information(NULL,tr("Path"),tr("You selected")+path);
    }
//    QMessageBox::information(NULL,tr("Open"),tr("Open a file"));

}
MainWindow::~MainWindow()
{
    }



状态栏

信息类型:临时信息(提示),一般信息(页码),永久信息(不会消失的信息,如按键状态)
demo3
mainwindow.cpp

#include "mainwindow.h"
#include
#include
#include
#include
#include
#include
#include
#include
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    //定义QAction 创造一个对象
    openAction=new QAction(tr("&Open"),this);
    openAction->setShortcut(QKeySequence::Open);//用于实现跨平台快捷键
    openAction->setStatusTip(tr("Open a file."));//状态栏的提示
    openAction->setIcon(QIcon(":/Open.png"));
    //把QAction添加到菜单和工具条
    connect(openAction,SIGNAL(triggered()),this,SLOT(open()));

    QMenu *file=menuBar()->addMenu(tr("&File"));
    file->addAction(openAction);

    QToolBar *toolBar=addToolBar(tr("&File"));
    toolBar->addAction(openAction);

    msgLabel=new QLabel;
    msgLabel->setMinimumSize(msgLabel->sizeHint());//设置大小为本身建议的大小
    msgLabel->setAlignment(Qt::AlignHCenter);//设置显示规则水平居中
    statusBar()->addWidget(msgLabel);//把label添加到状态栏
    statusBar()->setStyleSheet(QString("QStatusBar::item{border:0px}"));
}

void MainWindow::open()
{
    QMessageBox::information(NULL,tr("Open"),tr("Open a file"));

}
MainWindow::~MainWindow()
{
    }

main.cpp

#include "mainwindow.h"
#include 


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 

class QAction;//前向声明
class QLabel;
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private slots:
    void open();
private:
        QAction *openAction;
        QLabel *msgLabel;
};
#endif // MAINWINDOW_H

QAction、工具栏和菜单的demo 添加图标等demo2:

https://www.w3cschool.cn/learnroadqt/7yl81j4f.html

图片.png

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 

class QAction;//前向声明

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private slots:
    void open();
private:
        QAction *openAction;
};
#endif // MAINWINDOW_H

main.cpp

#include "mainwindow.h"
#include 


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.cpp

#include "mainwindow.h"
#include
#include
#include
#include
#include
#include
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    //定义QAction 创造一个对象
    openAction=new QAction(tr("&Open"),this);
    openAction->setShortcut(QKeySequence::Open);//用于实现跨平台快捷键
    openAction->setStatusTip(tr("Open a file."));//状态栏的提示
    openAction->setIcon(QIcon(":/Open.png"));
    //把QAction添加到菜单和工具条
    connect(openAction,SIGNAL(triggered()),this,SLOT(open()));

    QMenu *file=menuBar()->addMenu(tr("&File"));
    file->addAction(openAction);

    QToolBar *toolBar=addToolBar(tr("&File"));
    toolBar->addAction(openAction);
}

void MainWindow::open()
{
    QMessageBox::information(NULL,tr("Open"),tr("Open a file"));

}
MainWindow::~MainWindow()
{
    }



QAction、工具栏和菜单的demo

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 

class QAction;//前向声明

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private:
        QAction *openAction;
};
#endif // MAINWINDOW_H

main.cpp

#include "mainwindow.h"
#include 


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.cpp

#include "mainwindow.h"
#include
#include
#include
#include
#include

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    //定义QAction 创造一个对象
    openAction=new QAction(tr("&Open"),this);
    openAction->setShortcut(QKeySequence::Open);//用于实现跨平台快捷键
    openAction->setStatusTip(tr("Open a file."));//状态栏的提示

    //把QAction添加到菜单和工具条
    QMenu 。菜单是上面的,工具栏是下面的
*file=menuBar()->addMenu(tr("&File"));
    file->addAction(openAction);

    QToolBar *toolBar=addToolBar(tr("&File"));
    toolBar->addAction(openAction);
}

MainWindow::~MainWindow()
{
}


Action类

保存action的信息,如文本描述、图标、快捷键、信号槽

Meta-Object 系统

内省(反射):程序在运行时获取类的相关信息,如方法属性信号列表槽列表,这些基本信息就是meta-information。

信号槽进阶

函数签名:定义函数的输入输出。包含参数及其类型,返回值及其类型,可能抛出传回的异常,可用性信息(public/static/prototype)
一个槽可以连接多个信号(任一信号触发,执行slot),一个信号可以连接多个槽(槽会接连调用,但调用顺序不定,类似verilog),一个信号可以连接一个信号,槽可以被取消链接(disconnect)。
信号和槽的参数个数类型顺序必须一致,多的就会被忽略掉。
通过if防止循环连接
一个典型的类及其使用:

class Employee:public QObject
{
    Q_OBJECT
public:
    Employee() {mySalary=0;}
    int salary() const{return mySalary;}
//const在Public函数中常用,表明该函数不能修改类中的成员变量
public slots:
    void setSalary(int newSalary);
signals:
    void salaryChanged(int newSalary);
private:
    int mySalary;
};

void Employee::setSalary(int newSalary) 
{ 
        if (newSalary != mySalary) { 
                mySalary = newSalary; 
                emit salaryChanged(mySalary); 
        } 
}
week1:

配了环境,qtcreator下cmake的配置仍未解决,无法使用VS,但是原生编辑器能跑。学了layout/信号槽/几个常用组件,用fiddler修改下载源。
ifndef:https://blog.csdn.net/SummerXRT/article/details/119741741
头文件:C 头文件 | 菜鸟教程 (runoob.com)
构造和析构:C++构造函数和析构函数详解 - 知乎 (zhihu.com)

image.png

示例:
main.cpp

#include
#include"finddialog.h"

int main(int argc,char *argv[])
{
    QApplication app(argc,argv);
    FindDialog *dialog = new FindDialog;
    dialog->show();
    return app.exec();
}

FindDialog.cpp
::和:https://blog.csdn.net/qingkongyeyue/article/details/52948266

#include 
#include "finddialog.h"

FindDialog::FindDialog(QWidget *parent)
    :QDialog(parent)
{
    label = new QLabel(tr("Find &what:"));
    lineEdit = new QLineEdit;
    label->setBuddy(lineEdit);

    caseCheckBox = new QCheckBox(tr("Match &case"));
    backwardCheckBox = new QCheckBox(tr("Search &backford"));

    findButton = new QPushButton(tr("&Find"));
    findButton->setDefault(true);
    findButton->setEnabled(false);

    closeButton = new QPushButton(tr("Close"));

    connect(lineEdit,SIGNAL(textChanged(const QString&)),this,SLOT(enableFindButton(const QString&)));
    connect(findButton,SIGNAL(clicked()),this,SLOT(findClicked()));
    connect(closeButton,SIGNAL(clicked()),this,SLOT(close()));

    QHBoxLayout *topLeftLayout = new QHBoxLayout;
    topLeftLayout->addWidget(label);
    topLeftLayout->addWidget(lineEdit);

    QVBoxLayout *leftLayout=new QVBoxLayout;
    leftLayout->addLayout(topLeftLayout);
    leftLayout->addWidget(caseCheckBox);
    leftLayout->addWidget(backwardCheckBox);

    QVBoxLayout *rightLayout=new QVBoxLayout;
    rightLayout->addWidget(findButton);
    rightLayout->addWidget(closeButton);
    rightLayout->addStretch();

    QHBoxLayout *mainLayout=new QHBoxLayout;
    mainLayout->addLayout(leftLayout);
    mainLayout->addLayout(rightLayout);
    setLayout(mainLayout);

    setWindowTitle(tr("Find"));
    setFixedHeight(sizeHint().height());
}
FindDialog::~FindDialog()
{

}
void FindDialog::findClicked()
{
    QString text = lineEdit->text();
    Qt::CaseSensitivity cs=caseCheckBox->isChecked()?Qt::CaseInsensitive : Qt::CaseSensitive;
    if(backwardCheckBox->isChecked())
    {
        emit findPrevious(text,cs);
    }else
    {
        emit findNext(text,cs);
    }
}
void FindDialog::enableFindButton(const QString &text)
{
    findButton->setEnabled(!text.isEmpty());
}

.h文件:

#ifndef FINDDIALOG_H
#define FINDDIALOG_H

#include
#include
#include
#include
#include
#include
#include
#include

class QCheckBox;
class QLabel;
class QLineEdit;
class QPushButton;


class FindDialog : public QDialog
{
    Q_OBJECT

public:
    FindDialog(QWidget *parent = 0);
    ~FindDialog();
signals:
    void findNext(const QString &str,Qt::CaseSensitivity cs);
    void findPrevious(const QString &str,Qt::CaseSensitivity cs);
private slots:
    void findClicked();
    void enableFindButton(const QString &text);
private:
    QLabel *label;
    QLineEdit *lineEdit;
    QCheckBox *caseCheckBox;
    QCheckBox *backwardCheckBox;
    QPushButton *findButton;
    QPushButton *closeButton;
};
#endif // FINDDIALOG_H

参考资料

qt镜像站:https://mirrors.tuna.tsinghua.edu.cn/qt/archive/qt/
组件更新:
https://blog.csdn.net/SHIE_Ww/article/details/124074573
教程:https://www.w3cschool.cn/learnroadqt/c84q1j3t.html
qt串口通信:https://shenmingyi.blog.csdn.net/article/details/81669540?spm=1001.2101.3001.6650.5&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-5-81669540-blog-123432413.235%5Ev28%5Epc_relevant_t0_download&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-5-81669540-blog-123432413.235%5Ev28%5Epc_relevant_t0_download

版权声明:
作者:主机优惠
链接:https://www.techfm.club/p/42000.html
来源:TechFM
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>