Qt笔记录

目录

Qt中添加资源文件(图片等)

QByteArray

访问与赋值

数据转换

信号与槽

信号

 槽

信号和槽关联

 菜单栏和工具栏

菜单栏

工具栏

文件操作(QFileDialog)

QDataStream与QTextStream

QDir和QFile

对话框(QDialog)

基本用法

模态与非模态

字体颜色和大小

布局

时间

定时器(QTimer)

QLineEdit

QTextEdit

QListwidget

QTableWidget

QAbstractTableModel

QCalendarWidget(日历) 

线程(QThread)  

线程使用

线程另一种用法 

线程间资源竞争

变量竞争

互斥锁

读写锁

线程池(QThreadPool) 

 QRunnable

 常用接口

 QThreadPool

常用接口

线程池实现

QtConcurrent

pro文件中添加模块

QProcess

坐标系统

Qt自定义界面提升

QPushButton

 自定义开关

QTabWidget

QSQLITE数据库 

QMap

截图实现

事件

画图

 画渐变图形

QLinearGradient线性渐变 

QRadialGradient线性渐变 

QConicalGradient圆锥渐变

渐变实现进度 

QVariant

Movie

 QPropertyAnimation(动画) 

QString

样式表

基本语法 

样式表使用方法

 设置背景

按键风格(QPushButon)

QTabWidget样式 设置

qss实现渐变 

圆锥渐变(qconicalgradient)

线性渐变(qlineargradient)

QSerialPort(串口)

 实现串口调试助手

正则表达式

Qt下使用正则表达式

QRegExp和QRegExpValidator

 正则表达式常用语法

Lambda表达式 

 Qt常用函数



Qt中添加资源文件(图片等)

1.远中工程右键->添加新文件,弹出下图对话框

2选中Qt->Qt Resoure File,弹出下列对话框

3.取名,下一步,完成,然后会出现如下对话框

 4.点击添加,选择添加前缀,添加好前缀之后就可以添加文件了,选择需要添加的文件,这里需要把需要添加的文件先放到项目目录下的一个文件夹下,添加好之后保存,就能在资源浏览器中看到了,也就能在代码中引用了。<图标下载百度icon>


QByteArray

访问与赋值

访问QByteArray主要有4中方式,分别为[]、at()、data[]和constData[]。其中[]和data[]为可读可写,at()和constData[]仅为可读。如果仅是读,则通过at()和constData[]访问速度最快,因可避免复制处理。示例如下:

QByteArray ba;
ba.resize(5);
ba[0] = 0x41;        //十进制为65,对应ASCII为'A'
ba[1] = 0x30;        //十进制为48,对应ASCII为'0'
ba[2] = 66;          //十进制为66,对应ASCII为'B'
ba.data()[3] = 0x31; //十进制为49,对应ASCII为'1'
ba.data()[4] = 0x43; //十进制为67,对应ASCII为'C'

/*QByteArray输入数字,则输出值为ASCII码表对应字符*/
qDebug()<<"[]"<<ba[0]; //'A'
qDebug()<<"at()"<<ba.at(1); //'0'
qDebug()<<"data()"<<ba.data()[2]; //'B'
qDebug()<<"constData()"<<ba.constData()[3]; //'1'
qDebug()<<"constData()"<<ba.constData()[4]; //'C'


QByteArray byte(2,0);//定义大小为2,其值为空
qDebug()<<"byte = "<<byte<<endl;    //"\x00\x00"

数据转换

1、QByteArray转为Hex

QByteArray ba;
ba.resize(5);
ba[0] = 0x41;        //十进制为65,对应ASCII为'A'
ba[1] = 0x30;        //十进制为48,对应ASCII为'0'
ba[2] = 66;          //十进制为66,对应ASCII为'B'
ba.data()[3] = 0x31; //十进制为49,对应ASCII为'1'
ba.data()[4] = 0x43; //十进制为67,对应ASCII为'C'
qDebug()<<"ba = "<<ba<<endl;    //ba =  "A0B1C"
qDebug()<<"ba.Hex() = "<<ba.toHex()<<endl; //ba.Hex() =  "4130423143"


QByteArray ba("ABCDEF");
Debug()<<"ba = "<<ba<<endl;    //ba =  "ABCDEF"
qDebug()<<"ba.Hex() = "<<ba.toHex()<<endl; //ba.Hex() =  "414243444546"

2、Hex转QByteArray 

QByteArray text = QByteArray::fromHex("517420697320677265617421");
text.data();            // returns "Qt is great!"

qDebug()<<"text = "<<text<<endl;    //Qt is great!
qDebug()<<"text.data() = "<<text.data()<<endl; //Qt is great!

3、数值转换

int num = 63;
QByteArray ba1 = QByteArray::number(num);    //"63"
QByteArray ba2 = QByteArray::number(num,2);  //"111111"
QByteArray ba3 = QByteArray::number(num,16); //"3f"
QByteArray ba4 = QByteArray::number(num,16).toUpper();  //"3F"

QByteArray ba5;
ba5 = ba5.setNum(num);      //"63"
ba5 = ba5.setNum(num,16);   //"3f"

 4、QByteArray <-> char *

char ch[] = "hello world!";
QByteArray byte = QByteArray(ch);

char *ch1;//不要定义成ch1[n];
QByteArray byte1("hello world!");
ch1 = byte1.data();

5、QString <-> QByteArray 

QString str("hello world!");
QByteArray byte = str.toLatin1(); //QString -> QByteArray
    
QByteArray byte("hello world!");
QString string = QString(byte);	//QByteArray -> QString
//string.prepend(byte);         //QByteArray -> QString

中文 QByteArray 转换成QString会乱码,解决如下:

QByteArray byte("我爱我的祖国");
QTextCodec *tc = QTextCodec::codecForName("UTF-8");//QTextCodec::codecForName("GBK")
QString tmpQStr = tc->toUnicode(byte);

 5、QByteArray 转换为数字(串口通信常用)

  toInt()是把里面的字符串(如果是数)转成整数,如不是数学,则值为0 

QByteArray byte("122");
quint16 num = byte.toUInt();//num = 122
qDebug()<<"num = "<<num<<endl;

QByteArray byte2("a123");
quint16 num2 = byte2.toUInt();
qDebug()<<"num2 = "<<num2<<endl; //num = 0
int num = 35;
char test[4];
memcpy(test,&num,4);
int sum = *((int*)(test)); //35
qDebug()<<"sum = "<<sum<<endl;  //35

QByteArray temp;
memcpy(temp.data(),&num,4);
qint32 res;
//res=temp.toInt();                     //值为0,因为toInt()是把里面的字符串(如果是数)转成整数
//memcpy(&res,temp.constData(),4);      //有可能值异常
memcpy(&res,temp.left(4).constData(),4);//建议这样转换
//res = *((int*)(temp.data()));
qDebug()<<"temp = "<<temp<<endl;//"#"
qDebug()<<"res = "<<res<<endl;  //35

信号与槽

信号和槽使用注意事项:

1、需要继承自QObject或其子类;

2、 使用信号和槽必须在类声明的最开始处添加Q_OBJECT宏;

3、槽中参数类型要和信号参数的类型相对应,且不能比信号的参数多;

4、信号只有声明,没有定义,且返回值为void类型;

5、槽有声明,也有定义。

信号

Qt内部自带了各种窗口部件的信号,最常见的就是按钮的clicked()。但是也可以自己声明信号,声明信号需要使用signals关键字,在signals前面不能用public、private和protected等限定符,信号默认是public函数,可以从任何地方进行发射。

1、自定义信号,在头文件中定义,信号没有实体。

signals:
    void singalSelf();

2、信号发射

emit this->singalSelf();

 槽

槽就是普通的C++函数,可以像一般的函数一样使用。声明槽函数要使用slots关键字,可以是private、public或者protected类型的,槽也可以被声明为虚函数。它最大的特点就是可以和信号关联,这是其它函数比不了的。

信号和槽关联

信号和槽关联使用的是QObject类的connect()函数,需要注意的是,connect()函数中信号和槽的参数只能为类型,不能有变量名。 

  • 第一个参数:发射信号的对象
  • 第二个参数:发射的信号
  • 第三个参数:接受信号的对象
  • 第四个参数:要执行的槽
  • 第五个参数:信号和槽的关联方式,默认为自动关联

第五个参数:

  1. Qt::AutoConnection: 默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。
  2. Qt::DirectConnection(槽函数在接收者所依附线程执行):槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程(无论槽函数所属对象在哪个线程,槽函数都在发射信号的线程内执行)。效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成奔溃。
  3. Qt::QueuedConnection:槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。
  4. Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
  5. Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接。

3.1 Qt4原型

static QMetaObject::Connection connect(const QObject *sender, const char *signal,
    const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection);

 Qt4示例:

connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(slotDealDate()));

3.2 Qt5原型

static QMetaObject::Connection connect(const QObject *sender, const QMetaMethod &signal,
        const QObject *receiver, const QMetaMethod &method,Qt::ConnectionType type = Qt::AutoConnection);

 Qt5示例:

   注意:如果信号和槽有参数,Qt5连接时不需要参数。

connect(ui->pushButton,&QPushButton::clicked,this,&MainWindow::slotDealDate);

3.3 多个相同部件用同一个槽函数(以QPushButton举例)

connect(ui->pushButton1,SIGNAL(pressed()),this,SLOT(SLOTButtonClick()));
connect(ui->pushButton2,SIGNAL(pressed()),this,SLOT(SLOTButtonClick()));

void Widget::SLOTButtonClick()
{
    QPushButton *btn=qobject_cast<QPushButton*>(sender()); //得到当前操作的按钮
    if(btn==ui->pushButton1)
    {
        //添加响应信息
    }
    else if(btn == ui->pushButton2)
    {
        //添加响应信息
    }
    else
    {

    }
}

3.4 自定义数据类型需要在main函数中向Qt注册自定义的数据格式,用于信号和槽

 //向Qt注册自定义的数据格式,用于信号和槽
 qRegisterMetaType<FunctionCodeType>("FunctionCodeType");
 qRegisterMetaType<StructData>("StructData");

 菜单栏和工具栏

示例图:

菜单栏

   1.在菜单栏添加菜单

 QMenu *filemenu = menuBar()->addMenu("&文件"); //添加文件菜单

   2.在菜单中添加菜单项

      方法一:

QAction *newfile = new QAction("&new");
newfile->setIcon(QIcon(":/image/new.png")); //为文件添加图标
filemenu->addAction(newfile);   //菜单下拉组(菜单下添加新文件)

      方法二:

QAction *newfil = new QAction(QIcon(":/image/new.png"),"&new",this);
filemenu->addAction(savefile);

connect(newfile,SIGNAL(triggered(bool)),this,SLOT(new_fun())); //新文件信号与槽

  3.菜单项中添加分割线

filemenu->addSeparator(); //设置分割线

工具栏

 QToolBar *newtool = addToolBar("newtool"); //声明一个工具栏
 newtool->addAction(newfile); //添加内容(新建文件)

文件操作(QFileDialog)

(一)打开文件

    QSting filename = QFileDialog::getOpenFileName(this,"open file",".","file (*.*)");//获取文件名,2-4个参数分别为:标题,位置,文件类型
    QFile file(filename);
    if(!file.open(QIODevice::ReadWrite | QIODevice::Text)) //打开文件
    {
        qDebug()<<"open file failing"<<endl;
        return;
    }
    while(!file.atEnd())
    {
        QByteArray line = file.readLine();
        line.chop(1);

        textedit->append(line); //将文本文件显示在textedit文本框中
    }
    file.close();

(二)保存文件

if(filename.isEmpty()) //如果文件名为空,就获得文件名(用户取);如果是另存为则不需判断
{
    filename = QFileDialog::getSaveFileName();
}
QFile file(filename);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
    return;
file.write(textedit->toPlainText().toStdString().c_str());//将文本框的内容,导入文件中
file.close();

QDataStream与QTextStream

     QDataStream类为我们的程序提供了读写二进制数据的能力。一个数据流如果是二进制编码的数据流,那么它肯定是与计算机的操作系统、CPU或者字节序无关的。例如,一个数据流是在一个运行Windows系统的PC机上被写入的,那么它照样可以
在一台运行Solaris的Sun SPARC的机器上被读取出来。同样,我们也可以使用
QDataStream去读写原生的未编码的二进制数据。

     QDataStream类实现了序列化C++的基本数据类型的功能,比如char,short,int,char*,QBrush,QColor,QDateTime,QList,QLinkedList,QVector,QSet,QHash,QMap 和QImage等等。

     注意:写入什么类型就读出什么类型

void Widget::writeData()
{
    QFile file("../test.txt");//没有就创建
    if(!file.open(QIODevice::WriteOnly))
    {
        qDebug()<<"---open fial---"<<endl;
        return;
    }
    QDataStream stream(&file);
    stream<<QString("akdlfjl")<<234;

    QMap<qint16,bool> mymap;
    mymap.insert(12,false);
    mymap.insert(22,false);
    mymap.insert(12,false);
    mymap.insert(55,false);
    mymap.insert(66,false);
    stream<<mymap;
}

void Widget::readData()
{
    QFile file("../test.txt");
    if(!file.open(QIODevice::ReadOnly))
    {
        qDebug()<<"---open fial---"<<endl;
        return;
    }
    QDataStream stream(&file);
    QString str;
    quint32 num;
    stream>>str>>num;
    qDebug()<<"str = "<<str<<" num = "<<num<<endl;

    QMap<qint16,bool> mymap;
    stream>>mymap;
    QMapIterator<qint16,bool> it(mymap);
    while(it.hasNext())
    {
        it.next();
        qDebug()<<"key = "<<it.key()<<"value = "<<it.value()<<endl;
    }
}

  QTextStream可以设置编码类型

void Widget::writeData()
{
    QFile file("../test.txt");
    if(!file.open(QIODevice::WriteOnly))
    {
        qDebug()<<"---open fial---"<<endl;
        return;
    }
    QTextStream stream(&file);
    //指定编码
    stream.setCodec("UTF-8");

    stream<<QString("挖机噶")<<234;//QTextStream会把两个字符串连在一起即“挖机噶234"
}

void Widget::readData()
{
    QFile file("../test.txt");
    if(!file.open(QIODevice::ReadOnly))
    {
        qDebug()<<"---open fial---"<<endl;
        return;
    }
    QTextStream stream(&file);
    QString str;
    quint32 num;
//    stream>>str>>num;//因为两个字符串连在一起了,所以str ="挖机噶234",num = 0
    str = stream.readAll();// 一般以read的方式读出
    qDebug()<<"str = "<<str<<" num = "<<num<<endl;
}

QDir和QFile

QDir writeDir;
writeDir.setPath(RUNTIME_PATH);
if (writeDir.exists() == false) //如果该文件夹路径不存在,新建该路径
{
	writeDir.mkpath(RUNTIME_PATH);
}
writeDir.setCurrent(RUNTIME_PATH);

QFile   file;
file.setFileName(RUNTIME_FILE);
if(file.open(QIODevice::WriteOnly))
{
	file.write();
	file.flush();
	file.close();
}
else
{
	
}

对话框(QDialog)

基本用法

QDialog *findDlog = new QDialog;
findDlog->setWindowTitle(tr("查找"));
findLineEdit = new QLineEdit(findDlog);
QPushButton *FrontButton = new QPushButton(tr("向下查找"),findDlog);
QPushButton *RearButton = new QPushButton(tr("向上查找"),findDlog);
QVBoxLayout *Layout = new QVBoxLayout(findDlog); //布局
Layout->addWidget(findLineEdit);
Layout->addWidget(FrontButton);
Layout->addWidget(RearButton);
findDlog->setLayout(Layout);
findDlog->show();

   示例对话框如下图:

模态与非模态

      show() 与 exec() 并不是模态与非模态的区别。

  • 模态对话框:在其没有被关闭之前,用户不能与同一个应用程序的其他窗口进行交互,直到该对话框关闭。
  • 非模态对话框:当其被打开时,用户既可选择和该对话框进行交互,也可以选择同应用程序的其他窗口交互。

模态与非模态设置

setModal(bool modal);

 更多知识点参考:QDialog 模态对话框与事件循环_1+1=10-CSDN博客

字体颜色和大小

/*颜色*/
QColor color1 = QColorDialog::getColor();
textedit->setTextColor(color1);

/*类型大小*/
bool ok = true;
QFont font1 = QFontDialog::getFont(&ok);
if(ok)
{
    textedit->setFont(font1);
}    

布局

   布局有水平布局(QHBoxLayout)、垂直布局(QVBoxLayout)、网格布局(QGtidLayout)等

/*垂直布局和水平布局一样*/
QPushButton *Button1 = new QPushButton(tr("Yes"),this);
QPushButton *Button2 = new QPushButton(tr("No"),this);
QVBoxLayout *Layout = new QVBoxLayout(this); //布局
Layout->addWidget(Button1 );
Layout->addWidget(Button2 );
this->setLayout(Layout);


/*网格布局*/
QPushButton *Button1 = new QPushButton(tr("Yes"),this);
QPushButton *Button2 = new QPushButton(tr("No"),this);
QGridLayout *Layout = new QGridLayout (this); //布局
Layout->addWidget(Button1,0,1);
Layout->addWidget(Button2,1,0);
this->setLayout(Layout);

时间

 1、获取时间与显示时间 

QDateTime current = QDateTime::currentDateTime();
QString time = current.toString("yyyy-M-dd hh:mm:ss");//显示时间格式
QString Time = QDate::currentDate().toString("yyyy-MM-dd")+"  "+QTime::currentTime().toString("hh:mm:ss");
textedit->append(time); //在文本框中显示

2、判断时间显示格式是否正确,可以用来字符串相关格式 

QTime::fromString("04:00","hh:mm").isValid();//反回真假

3、字符串转时间

QDateTime  Time =  QDateTime::fromString( (Date.toString("yyyy-MM-dd")), "yyyy-MM-dd");

 4、时间撮

QDateTime current = QDateTime::currentDateTime();
int timeNum = current.toTime_t();//时间转时间撮
current = QDateTime::fromTime_t(timeNum.toString("yyyy-MM-dd hh:mm:ss");//时间撮转时间

定时器(QTimer)

QTimer *timer;
timer = new QTimer();
timer->start(1000);//= timer->setInterval(1000),timer->start();
connect(timer,SIGNAL(timeout()),this,SLOT(time_out()));
timet->stop(); //停止定时器

  直接触发定时器

QTimer::singleShot(10,this,SLOT(槽函数));//10ms后触发槽函数(槽函数不能有参数)

QLineEdit

 (一)设置LineEdit输入值范围

double m_rangeMin = 12.0;
double m_rangeMax = 99.0;
//设置整数和浮点数的输入范围
QDoubleValidator *doubleValidator = new QDoubleValidator(m_rangeMin,m_rangeMax,1,this);//1是小数位数
//这里作为一个标准的数字的字符串被写入,另外还有一种科学计数方式写入
doubleValidator->setNotation(QDoubleValidator::StandardNotation);
ui->lineEdit->setValidator(doubleValidator);

qint8 m_rangeMin = 14;
qint8 m_rangeMax = 100;
QIntValidator *intValidator = new QIntValidator(m_rangeMin, m_rangeMax, this);
ui->lineEdit->setValidator(intValidator);

if(ui->lineEdit->hasAcceptableInput())//判断lineEdit的内容是否满足输入格式
{
}

(二)QLineEdit的一些用法

1、ui->lineEdit->setEchoMode(QLineEdit::Password);//将lineEdit输入的内容为隐式

QTextEdit

 1、禁止鼠标选中文本

ui->textEdit->setTextInteractionFlags(Qt::NoTextInteraction);

QListwidget

1.向列表中添加控件,如下图:

QListWidget *listwidget = new QListWidget();
QListWidgetItem *item = new QListWidgetItem;
item->setSizeHint(QSize(0,50));     //设置项的高框
listwidget->addItem(item);          //加载列表项到列表框

QWidget *w = new QWidget;
QHBoxLayout *layout=new QHBoxLayout;
QLabel *label = new QLabel("swich:",w);
QPushButton *pushButton1 = new QPushButton("ON",w);
QPushButton *pushButton2 = new QPushButton("OFF",w);
layout->addWidget(label);
layout->addWidget(pushButton1);
layout->addWidget(pushButton2);
w->setLayout(layout);
listwidget->setItemWidget(item,w);

 2.添加列表项,如下图:

listwidget->addItem("北京");
listwidget->addItem("上海");

QListWidgetItem *item = new QListWidgetItem(QICon("test.png"),"深圳");
listwidget->addItem(item);

listwidget->insertItem(1,"深圳");//插入列表

3.删除列表

listwidget->takeItem(row); //删除列表,并没删除只是隐藏了

4.获取哪一页

ListWidget->setCurrentIndex(num);

QTableWidget

ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);//表格不能编辑
ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);//表格每次选择一行
qint8 row = ui->tableWidget->rowCount();//获取行数
ui->tableWidget->setRowCount(row+1);//增加一行
QString ip = "127.0.0.1";
qint8 port = 123;
ui->tableWidget->setItem(row,colum,new QTableWidgetItem(ip));//想表格里添加文本
ui->tableWidget->setItem(row,colum+1,new QTableWidgetItem(QString::number(port)));

QAbstractTableModel

QAbstractTableModel 是 Qt 中的一个抽象类,在某些应用场景下我们可能会继承该类来实现一些特殊业务逻辑。

   GridTable.h

#ifndef GRIDTABLE_H
#define GRIDTABLE_H

#include <QAbstractTableModel>
#include <QStringList>

class GridTable:public QAbstractTableModel
{
public:
    GridTable(QObject *parent=0);
    virtual int rowCount(const QModelIndex &/*parent=QModelIndex()*/) const;
    virtual int columnCount(const QModelIndex &/*parent=QModelIndex()*/) const;

    //如果表格可编辑添加下列两个函数
    virtual Qt::ItemFlags flags(const QModelIndex &index) const;
    virtual bool setData(const QModelIndex &index, const QVariant &value, int role);

    QVariant data(const QModelIndex &index, int role) const;
    QVariant headerData(int section, Qt::Orientation orientation, int role) const;
    void insertInformationRows(const QList<QStringList> &warningInfoList); //插入信息
    void removeAllRows(void);//清空内容

private:
    QStringList header;        //Table表头
    QStringList nameList;
    QStringList sexList;
    QStringList ageList;
    QStringList heightList;

};
#endif

    GridTable.cpp

#include "gridtable.h"

GridTable::GridTable(QObject *parent) : QAbstractTableModel(parent)
{
    header<<tr("姓名")<<tr("性别")<<tr("年龄")<<tr("身高");
}

int GridTable::columnCount(const QModelIndex &/*parent*/) const
{
    return 4;
}

int GridTable::rowCount(const QModelIndex &/*parent*/) const
{
    return nameList.size();
}

QVariant GridTable::data(const QModelIndex &index, int role) const
{
    if(!index.isValid())
    {
        return QVariant();
    }
    if(role == Qt::TextAlignmentRole)        //设置显示数据居中显示
        return int(Qt::AlignCenter);
    else if(role==Qt::DisplayRole)
    {
        switch(index.column())
        {
        case 0:
            return nameList[index.row()];
            break;

        case 1:
            return sexList[index.row()];
            break;

        case 2:
            return ageList[index.row()];
            break;

        case 3:
            return heightList[index.row()];
            break;

        default:
            return QVariant();
        }
    }
    return QVariant();
}

QVariant GridTable::headerData(int section, Qt::Orientation orientation, int role) const
{
    if((role == Qt::DisplayRole)&&(orientation == Qt::Horizontal))
        {
            return header[section];
        }
        return QAbstractTableModel::headerData(section,orientation,role);
}

void GridTable::insertInformationRows(const QList<QStringList> &InfoList)
{
    beginInsertRows(QModelIndex(), 0, InfoList.size()-1);
    foreach(QStringList Info, InfoList)
    {
        nameList.insert(0,(Info.at(0)));
        sexList.insert(0,(Info.at(1)));
        ageList.insert(0,(Info.at(2)));
        heightList.insert(0,(Info.at(3)));
    }
    endInsertRows();
}

void GridTable::removeAllRows(void)
{
    beginRemoveRows(QModelIndex(), 0, nameList.size()-1);
    nameList.clear();
    sexList.clear();
    ageList.clear();
    heightList.clear();
    endRemoveRows();
}

Qt::ItemFlags GridTable::flags(const QModelIndex &/*index*/) const
{
    return  Qt::ItemIsEnabled | Qt::ItemIsSelectable|Qt::ItemIsEditable;
}
bool GridTable::setData(const QModelIndex &index, const QVariant &/*value*/, int role)
{
    //处理 Qt::EditRole 角色,此时的 value 就是修改后的值
    if(index.isValid()&&role==Qt::EditRole){
        switch (index.column()) {
        case 0:
            //
            break;
        case 1:
            //
            break;
        case 2:
            //
            break;
        case 3:
            //
            break;

        default:
            break;
        }
    }
    return true;
}

  main.c

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    
    GridTable SetTable;
    ui->TableShow->setModel(&SetTable);
    ui->TableShow->horizontalHeader()->resizeSection(0,140);   //设置第一列列宽
    ui->TableShow->horizontalHeader()->resizeSection(1,140);   //设置第二列列宽
    ui->TableShow->horizontalHeader()->resizeSection(2,140);   //设置第三列列宽
    ui->TableShow->horizontalHeader()->resizeSection(3,140);   //设置第四列列宽
    ui->TableShow->setSelectionBehavior(QAbstractItemView::SelectRows); //一次选择一行
    ui->TableShow->setSelectionMode(QAbstractItemView::SingleSelection); //只允许单行选择
    ui->TableShow->verticalHeader()->setDefaultSectionSize(40); //调整默认行间距
    ui->historywarningtable->verticalHeader()->setMinimumSectionSize(40); //设置最小行间距
    ui->TableShow->setStyleSheet("QScrollBar:vertical { width:40px; }");//滑条宽度
    //ui->TableShow->horizontalHeader()->setResizeMode(QHeaderView::Fixed);    //限定列宽大小,无法拉伸
    //ui->TableShow->verticalHeader()->hide();   //隐藏列头
    //ui->TableShow->horizontalHeader()->hideSection(5);       //隐藏第五列

    QList<QStringList> InfoList;
    QStringList Info;
    Info<<"小明"<<"男"<<"16"<<"162";
    InfoList.append(Info);
    SetTable.insertInformationRows(InfoList);
 
    return a.exec();
}


QCalendarWidget(日历) 

m_calendar = new QCalendarWidget(this);
m_calendar->resize(580, 340); //原始大小 360,210
m_calendar->move(100, 220-(m_calendar->size().height()-210));
QTableView *table_calendarview = m_calendar->findChild<QTableView *> ("qt_calendar_calendarview");
if(table_calendarview)
{
    table_calendarview->hideColumn(0); //隐藏掉第0列显示的“一年中的第几周”
}

QToolButton *calendar_prevmonth = m_calendar->findChild<QToolButton *>("qt_calendar_prevmonth");//调日历的左右按钮
QToolButton *calendar_nextmonth = m_calendar->findChild<QToolButton *>("qt_calendar_nextmonth");
if(calendar_prevmonth && calendar_nextmonth)
{
    calendar_prevmonth->setIconSize(QSize(40,40));
    calendar_nextmonth->setIconSize(QSize(40,40));
}

m_calendar->setLocale(QLocale::Chinese);//将日历转为中文

线程(QThread)  

线程使用

Qt中线程常用函数:start()、run()、quit()、wait();

  1. start(),子线程开始执行函数,即开始运行run()函数。
  2. quit()函数是用来停止QThread的,但是由于Qt本身是事件循环机制,所以在调用完quit()后,QThread可能还没有完全停止,此时如果执行delete thread,程序就会报错。在执行quit()后,调用wait()来等待QThread子线程的结束(即从run()函数的返回),这样就能保证在清除QThread时,其子线程是停止运行的。

一、创建线程的步骤

  1. 子类化 QThread:新建一个类MyThread,继承QThread(点击工程右键->添加新文件->c++ class->基类设为QObject,建立完成将QObject改为QThread);
  2. 重载 run 函数:重写类MyThread的虚函数void run(),即新建一个函数protected void run(),为线程处理函数和主线程不是同一个线程;
  3. 在需要用到多线程的地方,MyThread *mythread = new MyThread(this),然后调用函数mythread ->start()后,则开启一条线程,将会运行函数run();
  4. 当要停止线程时,调用mythread ->wait()函数,等待线程结束,并且回收线程资源;
  5. 线程处理函数内部,不允许操作图形界面(一般只做后台处理);
/***************Mythread.h**************************/
#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
class Mythread : public QThread
{
    Q_OBJECT
public:
    explicit Mythread(QObject *parent = 0);
protected:
    void run(); //QTread的虚函数;
                //不能直接调用,通过start()间接调用

signals:

public slots:
};

#endif // MYTHREAD_H

/******************Mythread.cpp*********************************/
#include "mythread.h"

Mythread::Mythread(QObject *parent) : QThread(parent)
{

}
void Mythread::run()
{
    /*
    1.数据处理(为了实现子线程的循环运行,可以用两种方法:用while()循环、在run()函数中实现事件循环如定时器)
    2.emit 处理完可以发射一个信号
    3.然后在主线程的槽函数中停止线程:mythread->quit(),
      再等待线程结束:mytread->wait();
    */
}
  • 该方法除了run()函数是在子线程中运行,其他地方都是在主线程中运行;
  • 若为该类定义信号和槽,实质槽函数并不会在子thread 运行,而是在主线程运行;因为Thread的对象在主线程中创建,Thread的对象依附在主线程,所以他的slot函数会在主线程中执行,而不是次线程,除非将Thread 对象依附到次线程中(通过movetoThread),也就是下列方法。

线程另一种用法 

Qt4.4开始, run 默认调用 QThread::exec() 。这样一来不需要子类化 QThread 了,只需要子类化一个 QObject 就够了

(一)线程类
1、设定一个类Mythread,继承于QObject
2、类中设置一个线程函数(只是一个线程函数)

class Mythread:public QObject
{
    void MythreadFun//自定义线程处理函数
    {
        while(1)
        {
            emit MySignal();
        }
    }
} 

(二)主窗口
1、构造函数中创建一个线程对象(不能指定父对象)
    myT = new Mythread;

2、在Mythread构造函数中将Mythread依附于次线  //下面方法同样的效果

    this->moveToThread(this);//
2、创建一个子线程对象
    QThread *thread = new QThread(this);
    把自定义模块依附于子线程
    myT->movetoThread(thread);

3、启动线程。
    thread->start();
4、通过信号与槽(signal - slot),槽函数则在子线程中运行
    connect(this,&startThread,myT,&MythreadFun);
5、接受线程的信号,并处理
    connect(myT,&MySignal,this,&槽函数);

 不管那种方法构造函数始终是在主线程中执行的,如下:

#include <QDebug>
#include "subthread.h"
int main()
{
    qDebug()<<"main threadID : "<<QThread::currentThreadId()<<endl;
    subThread thread;
    thread.start();
    thread.quit();
    thread.wait();
    return 0;
}


#include <QDebug>
subThread::subThread(QThread *parent) : QThread(parent)
{
    qDebug()<<"subThread_threadID : " <<currentThreadId()<<endl;
}

void subThread::run()
{
    qDebug()<<"subThread_runID : " <<currentThreadId()<<endl;
}

/*
打印信息:
main threadID :  0x25a4
subThread_threadID :  0x25a4
subThread_runID :  0x33c0
*/


线程间资源竞争

变量竞争

   当多个线程对某个变量进行读写操作,很容易造成这个变量的值是意想不到的结果 ,如下例子:

#include <QCoreApplication>
#include "threadtestclass.h"

int cnt = 0;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    ThreadTestClass t1;
    ThreadTestClass t2;
    t1.start();
    t2.start();

    t1.wait();  //相当于pthread_join()
    t2.wait();

    qDebug() << "cnt=" << cnt;

    return 0;
}
/***************************************/
#include "threadtestclass.h"

extern int cnt;

ThreadTestClass::ThreadTestClass(QObject *parent) : QThread(parent)
{

}

void ThreadTestClass::run()
{
    for(int i = 0; i < 200000; i++)//
    {
        cnt++;
    }

    this->quit();
}

  编译运行,cnt每个结果都不相同,cnt变量同时被两个线程操作,所以就需要用到锁。

互斥锁

QMutex类提供的是线程之间的访问顺序化。QMutex的目的是保护一个对象、数据结构或者代码段,所以同一时间只有一个线程可以访问它。

QMutex用法1:

QMutex mutex;//全局变量

mutex.lock();
/*需要锁的内容*/
mutex.unlock();

 QMutex用法2:

QMutex mutex;

{
    QMutexLocker locker(&mutex);
    /*需要加锁的代码*/
    /*当QMutexLocker对象被销毁时,互斥锁将始终被解锁*/
}
#include <QCoreApplication>
#include "threadtestclass.h"

int cnt = 0;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    ThreadTestClass t1;
    ThreadTestClass t2;
    t1.start();
    t2.start();

    t1.wait();  //相当于pthread_join()
    t2.wait();

    qDebug() << "cnt = " << cnt;
    return 0;
}
/****************************/
#include "threadtestclass.h"
#include <QDebug>

extern int cnt;
QMutex ThreadTestClass::s_mutex;

ThreadTestClass::ThreadTestClass(QObject *parent) : QThread(parent)
{

}

void ThreadTestClass::run()
{
    for(int i = 0; i < 200000; i++)
    {
        s_mutex.lock();
        cnt++;
        s_mutex.unlock();
    }

    this->quit();
}

  运行结果:cnt = 40000;

读写锁

QReadWriteLock用法1:

QReadWriteLock s_readWriteLock;
s_readWriteLock.lockForRead();//读锁
/*加锁代码*/
s_readWriteLock.unlock();

s_readWriteLock.lockForWrite();//写锁
/*加锁代码*/
s_readWriteLock.unlock();

QReadWriteLock用法2:

QReadWriteLock s_readWriteLock;

QReadLocker locker(&s_readWriteLock);
/*需要读锁代码*/
/*QReadLocker 对象销毁时,自动解锁,一般放在局部*/

QWriteLocker locker(&s_readWriteLock);
/*需要写锁代码*/
/*QWriteLocker对象销毁时,自动解锁,一般放在局部*/

1、QReadWriteLock类为我们提供了读写锁的功能,从名字上可以看得出来是把lock分为了readlock和writelock,读写锁是用来保护可以被读访问和写访问的资源的一种同步工具。如果你想让多个线程同时的对资源进行读访问,但只要有一个线程要对资源进行写访问时,所有其他的线程必须等待,直到写访问完成。对于这种情况,读写锁是非常有用的。 

#include <QCoreApplication>
#include "threadtestclass.h"

int cnt = 0;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    ThreadTestClass t1;
    ThreadTestClass t2;
    t1.start();
    t2.start();

    t1.wait(); 
    t2.wait();

    qDebug() << "cnt = " << cnt;

    return 0;
}

/****************************/
#include "threadtestclass.h"
#include <QDebug>

extern int cnt;
QReadWriteLock ThreadTestClass::s_mutex;

ThreadTestClass::ThreadTestClass(QObject *parent) : QThread(parent)
{

}

void ThreadTestClass::run()
{
    for(int i = 0; i < 200000; i++)
    {
        s_mutex.lockForWrite();//其他线程可以读不能写(若是lockForRead,则相反)
        cnt++;
        s_mutex.unlock();
    }

    this->quit();
}

2、 读写锁允许并行的读,如果遇到写锁时其它锁被锁住。写锁的优先级要高于读锁,如等待的锁中有读锁和写锁时,一旦上一个锁被解锁时会优先执行写锁。QReadWriteLock相对于QMutex的好处是当要保护的对象在大多数的情况是读操作偶尔写操作时,不会造成不必要的堵塞。

#include <QCoreApplication>
#include "ReadThread.h"
QReadWriteLock s_readWriteLock;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    ReadThreadt1;
    ReadThreadt2;
    t1.start();
    t2.start();

    t1.wait();  
    t2.wait();

    return 0;
}
/*******************************************/
#include "ReadThread.h"
#include <QDebug>

extern QReadWriteLock s_readWriteLock;

ReadThread::ReadThread(QObject *parent) : QThread(parent)
{

}

void ReadThread::run()
{
    s_readWriteLock.lockForRead();
    qDebug()<<"1--------read : "<<QThread::currentThreadId()<<endl;
    qDebug()<<"2--------read : "<<QThread::currentThreadId()<<endl;
    qDebug()<<"3--------read : "<<QThread::currentThreadId()<<endl;
    s_readWriteLock.unlock();
    this->quit();
}

运行结果:

可以看出两个线程还是交替运行的,是因为QReadWriteLock是允许并行读的,当调用写锁时其他的lock就要等待。

写锁的优先级要高于读锁,如等待的锁中有读锁和写锁时,一旦上一个锁被解锁时会优先执行写锁。

#include <QCoreApplication>
#include "ReadThread.h"
QReadWriteLock s_readWriteLock;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    ReadThreadt1;
    ReadThreadt2;
    t1.start();
    t2.start();

    t1.wait();  
    t2.wait();

    return 0;
}
/*******************************************/
#include "ReadThread.h"
#include <QDebug>

extern QReadWriteLock s_readWriteLock;

ReadThread::ReadThread(QObject *parent) : QThread(parent)
{

}

void ReadThread::run()
{
    s_readWriteLock.lockForRead();
    qDebug()<<"1--------read : "<<QThread::currentThreadId()<<endl;
    qDebug()<<"2--------read : "<<QThread::currentThreadId()<<endl;
    qDebug()<<"3--------read : "<<QThread::currentThreadId()<<endl;
    s_readWriteLock.unlock();
    this->quit();
}
/***************************************************/
#include "writethread.h"

QReadWriteLock writeThread::m_writeLock;
extern QReadWriteLock s_readWriteLock;

writeThread::writeThread(QObject *parent) : QThread(parent)
{

}
void writeThread::run()
{
    s_readWriteLock.lockForWrite();
    qDebug()<<"1--------write : "<<QThread::currentThreadId()<<endl;
    qDebug()<<"2--------write : "<<QThread::currentThreadId()<<endl;
    qDebug()<<"3--------write : "<<QThread::currentThreadId()<<endl;
    s_readWriteLock.unlock();
}

运行结果:

可以看出写锁不能并行。


线程池(QThreadPool) 

对于频繁创建和销毁线程,频繁切换线程对系统资源是极大的浪费,为了提高cpu利用效率,于是产生了分装好了的线程池用于管理线程。

线程池需要用到两个类:QThreadPool、QRunnable;

QRunnable 类是一个接口,用于表示一个任务或要执行的代码,需要重新实现 run() 函数。

QThreadPool 管理和循环使用单独的 QThread 对象,以帮助程序减少创建线程的成本。每个 Qt 应用程序都有一个全局 QThreadPool 对象,可以不定义QThreadPool 对象,直接通过调用 globalInstance() 访问<QThreadPool::globalInstance()->start(任务)>。

 QRunnable

新建一个任务类,继承 QRunnable,重写 run() 方法,并将其传递给线程池 QThreadPool 进行管理。

 常用接口

bool QRunnable::autoDelete() const;
void QRunnable::setAutoDelete(bool autoDelete);

QRunnable 常用函数不多,setAutoDelete主要设置其传到底给线程池后,是否需要自动析构(如果启用了 autoDelete,当最后一个线程退出 run() 函数,QRunnable 将被删除)。不调用次函数,默认为true;若该值为false,则需要程序员手动析构,要注意内存泄漏;

 QThreadPool

常用接口

1、start()和tryStart

void QThreadPool::start(QRunnable * runnable, int priority = 0);
bool QThreadPool::tryStart(QRunnable * runnable);
  • start() 预定一个线程用于执行QRunnable接口,当预定的线程数量超出线程池的最大线程数后,QRunnable接口将会进入队列,等有空闲线程后,再执行;
  • priority指定优先级
  • tryStart() 和 start() 的不同之处在于,当没有空闲线程后,不进入队列,返回false

 2、cancel()和clear()

void QThreadPool::cancel(QRunnable * runnable);
void QThreadPool::clear();
  •  cancel()如果指定的QRunnable还没有执行,则从队列中移除;
  • clear()清空队列中还没有执行的QRunnable;

3、waitForDone()

bool QThreadPool::waitForDone(int msecs = -1);
  • 等待所有线程结束并释放资源(如果需要自动释放的话);
  • msecs指定超时;
  • 若所有线程都被移除,则,返回true,否则返回false;

4、最大线程数

int	maxThreadCount() const
void setMaxThreadCount(int maxThreadCount)
  • 线程池维护的最大线程数量;
  • 设定该值,不会影响已经开始的线程;
  • 默认值是最大线程数为QThread::idealThreadCount()获取;一般硬件平台几核,最大理想线程数就为几;

 5、超时

int	expiryTimeout() const
void setExpiryTimeout(int expiryTimeout)
  •  线程的终结超时;
  • 没有开启,且超过终结时间的线程,会退出,这些线程会根据需要重启开始,即这些线程不会消失,线程池会重新取出这些线程,开启或者放入队列,所谓的终结超时就是重新排列等待队列;
  • 建议在创建线程池后,调用 start() 前设定终结超时;

6、全局线程池

static QThreadPool * QThreadPool::globalInstance();
  • 全局内存池实例;
  • 若创建QThreadPool实例,则在实例生存周期内,内存池有效,

线程池实现

示例1:

(1)首先自定了接口类:

/*********customtasks.h*************/
#ifndef CUSTOMTASKS_H
#define CUSTOMTASKS_H

#include <QObject>
#include <QRunnable>

class CustomTasks : public QRunnable
{
public:
    explicit CustomTasks(const QString &threadName);
    ~CustomTasks();

protected:
    void run();

private:
    QString threadName;
};

#endif // CUSTOMTASKS_H

/********customtasks.cpp***********/
#include "customtasks.h"
#include <QDebug>
#include <QThread>

CustomTasks::CustomTasks(const QString &name):threadName(name)
{
}

CustomTasks::~CustomTasks()
{
}

void CustomTasks::run()
{
    qDebug()<<"Thread Name :"<<threadName<<endl;
    qDebug()<<threadName<<"Thread ID :"<<QThread::currentThreadId()<<endl;
}

 (2)线程池创建

#include <QApplication>
#include <QThreadPool>
#include "customtasks.h"
#include <QDebug>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    qDebug()<<"Main Thread ID :"<<QThread::currentThreadId()<<endl;
    QThreadPool *threadPool = new QThreadPool(this);

    CustomTasks *tasksRun_1 = new CustomTasks("1# thread");
    tasksRun_1->setAutoDelete(true);

    CustomTasks *tasksRun_2 = new CustomTasks("2# thread");
    tasksRun_2->setAutoDelete(true);

    threadPool->start(tasksRun_1);
    threadPool->start(tasksRun_2);

    return a.exec();
}

示例2:

 QRunnable 并不继承自 QObject,也就是说,根本无法使用 QObject 的特性,例如:信号/槽、事件等。为了便于使用,我们可以继承 QObject。

(1)接口类


/*********customtasks.h************/
#ifndef CUSTOMTASKS_H
#define CUSTOMTASKS_H

#include <QObject>
#include <QRunnable>

class CustomTasks : public QObject,public QRunnable
{
    Q_OBJECT
public:
    explicit CustomTasks(const QString &threadName);
    ~CustomTasks();

protected:
    void run();

private:
    QString threadName;

signals:
    void signalFinish();
};
#endif // CUSTOMTASKS_H

/*********customtasks.cpp************/
#include "customtasks.h"
#include <QDebug>
#include <QThread>

CustomTasks::CustomTasks(const QString &name):threadName(name)
{
}

CustomTasks::~CustomTasks()
{
}

void CustomTasks::run()
{
    qDebug()<<"Thread Name :"<<threadName<<endl;
    qDebug()<<threadName<<"Thread ID :"<<QThread::currentThreadId()<<endl;
    emit signalFinish();
}

(2) 线程池创建

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "customtasks.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    qDebug()<<"Main Thread ID :"<<QThread::currentThreadId()<<endl;
    threadPool = new QThreadPool(this);

    CustomTasks *tasksRun_1 = new CustomTasks("1# thread");
    tasksRun_1->setAutoDelete(false);
    CustomTasks *tasksRun_2 = new CustomTasks("2# thread");
    tasksRun_2->setAutoDelete(false);

    threadPool->start(tasksRun_1);
    threadPool->start(tasksRun_2);
    connect(tasksRun_1,&CustomTasks::signalFinish,this,&MainWindow::slotThreadFinish, Qt::UniqueConnection);
}

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

void MainWindow::slotThreadFinish()
{
    qDebug()<<"The Thread is finish"<<endl;
}

QtConcurrent

若有大量工作需要完成,则使用方式1、2、3均可,但是若只有一小段工作,需要在线程中完成,无论是使用QThread,还是moveToThread,更或者,使用QThreadPool,都有大材小用的感觉,这时候,使用 QtConcurrent 就是最佳选择。

pro文件中添加模块

QT += concurrent

接口函数

QFuture<T> QtConcurrent::run(Function function, ...);
QFuture<T> QtConcurrent::run(QThreadPool * pool, Function function, ...);
  •  QtConcurrent::run() 方法的第一个参数是线程池,可以指定线程池,若没有指定,则使用全局线程池;
  • 第二个参数,执行普通函数,并传参,获取返回值。

示例:

#include <QApplication>
#include <QtConcurrent>
#include <QThreadPool>
#include <QDebug>

void func(QString name)
{
    qDebug() << name << "is ID" << QThread::currentThreadId();
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    qDebug()<<"main Thread ID : "<< QThread::currentThreadId();

    QThreadPool thread;
    QFuture<void> fut1 = QtConcurrent::run(func, QString("Thread 1"));
    QFuture<void> fut2 = QtConcurrent::run(&thread,func, QString("Thread 2"));

    fut1.waitForFinished();
    fut2.waitForFinished();

    return a.exec();
}

输出结果:

main Thread ID :  0x28a4
"Thread 1" is ID 0x1154
"Thread 2" is ID 0x2e20

参考网址:1、 (44条消息) Qt中的线程池_SunnyFish-ty的博客-CSDN博客_qt线程池

                  2、Qt线程池 - sherlock_lin - 博客园 (cnblogs.com)


QProcess

 QProcess可用于完成启动外部程序,并与之交互通信。

QProcess *ShotProcess = new QProcess();
ShotProcess->start("路径");

坐标系统

(一)性质:

  1. 窗口,坐标系统相对于父窗口;
  2. 原点相对于窗口空白区域左上角(不包括边框);
  3. X:往右递增,Y:往下递增;

(二)移动坐标

控件->move(100,100);

  (三)获取控件的全局坐标 

QPoint LinePoint = 控件->mapToGlobal(QPoint(0,0));

  (四)坐标函数

frameGeometry().topLeft() //窗口左上角全局坐标
globalPos(); //全局坐标


Qt自定义界面提升

(一)用法

      当需要把一个界面(B),放到另一个界面中(A),则需要用到提升。实质是widget继承B类;

(二)特点

  1. 界面A不能直接操作界面B的控件,必须调用A类的函数来间接操作;
  2. 界面A中需要拖动一个widget控件,然后将其提升(点击右键->提升为...->提升类的名称:B类的名称->提升),实质是widget继承B类;

QPushButton

(一)点击信号

  • pressed  --- pressed按下就触发;
  • clicked --- clicked按下再弹起才触发;
  • toggled ---  这个信号触发的前提是按钮的Checkable属性要设置成true,这样在点击按钮之后就会触发toggled信号;当调用setChecked()函数时,会自动运行槽函数;
  • 三个触发先后顺序:toggled 、pressed  、clicked ;

(二)将按钮设为凹陷形式

     方法一:需要将按钮Checkable属性要设置成true,setChecked(true)---凹陷形式;

    方法二:直接setdown(true),但点击其他地方有恢复,目前我还没找到方法;

 (三)将按钮Checkable属性要设置成true时,有两种状态Checked(ture)<按钮凹陷形式>和Checked(false),点击按钮两种状态相互切换;

  (三)添加背景图片

ui->pushButton->setStyleSheet("background-image: url(:/new/prefix1/image/cut.png);");

 自定义开关

效果如下:

设计思路:

  1. 在widget上添加一个PushButton,上图蓝色为widget,白色为按钮;
  2. 点击按钮通过动画(QPropertyAnimation)移动按钮;
  3. 在需要使用此自定义按钮时,添加一个widget,然后提升为自定义按钮。

程序实现:

/************************.h*************************/
#ifndef SWITCHBUTTONWIDGET_H
#define SWITCHBUTTONWIDGET_H

#include <QWidget>
#include <QPropertyAnimation>
#include <QPointer>
#include <QTimer>

namespace Ui {
class SwitchButtonWidget;
}

class SwitchButtonWidget : public QWidget
{
    Q_OBJECT

public:
    explicit SwitchButtonWidget(QWidget *parent = nullptr);
    ~SwitchButtonWidget();
    void setSwitchWidgetResize(quint8 wide, quint8 high);   //设置窗口大小
    bool getSwitchStatus();                                 //获取开关状态

protected:
    void resizeEvent(QResizeEvent* event);

private slots:
    void on_pushButton_Switch_clicked();
    void slotAnimationStop();                               //清零动画标志位

private:
    Ui::SwitchButtonWidget *ui;
    QPointer<QPropertyAnimation> mAnimation;    //动画
    bool mSwitchFlag = false;                   //按钮开关
    bool mAnimationing = false;                 //动画使能
    qint8 mWidth = 111;                         //窗口宽度(设置值)
    qint8 mHeight = 50;                         //窗口高度(设置值)
    qint8 mPreWidth = 111;                      //窗口宽度
    qint8 mPreHeight = 50;                      //窗口高度
    qreal mWidthRatio;                          //宽度比列,用于设置按钮大小
    qreal mHeightRatio;                         //高度比列,用于设置按钮大小
    qreal mLeftPointRatio;                      //按钮在左边时坐标比列
    qreal mRightPointRatio;                     //按钮在右边时坐标比列
};

#endif // SWITCHBUTTONWIDGET_H

/************************.cpp*************************/
#include "SwitchButtonWidget.h"
#include "ui_SwitchButtonWidget.h"

/***************************************************************************************************
 函数名称:  SwitchButtonWidget()
 功能描述:  构造函数
 输入参数:  无
 返回的值:  无
 ***************************************************************************************************/
SwitchButtonWidget::SwitchButtonWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::SwitchButtonWidget)
{
    ui->setupUi(this);
    mPreWidth = this->width();
    mPreHeight = this->height();
    mWidthRatio = 1.0*ui->pushButton_Switch->width()/ui->widget->width();
    mHeightRatio = 1.0*ui->pushButton_Switch->height()/ui->widget->height();
    mLeftPointRatio = 6.0/mPreWidth;        //6为按钮在左边时的x坐标(见ui)
    mRightPointRatio = 55.0/mPreWidth;      //55为按钮在右边时的x坐标(见ui)
}

/***************************************************************************************************
 函数名称:  ~SwitchButtonWidget()
 功能描述:  析构函数
 输入参数:  无
 返回的值:  无
 ***************************************************************************************************/
SwitchButtonWidget::~SwitchButtonWidget()
{
    if(mAnimation != nullptr)
    {
        delete mAnimation;
    }

    delete ui;
}

/***************************************************************************************************
 函数名称:  setSwitchWidgetResize()
 功能描述:  重置窗口大小
 输入参数:  无
 返回的值:  无
 ***************************************************************************************************/
void SwitchButtonWidget::setSwitchWidgetResize(quint8 wide, quint8 high)
{
    mWidth = wide;
    mHeight = high;
    this->resize(mWidth,mHeight);
}

/***************************************************************************************************
 函数名称:  getSwitchStatus()
 功能描述:  获取开关状态
 输入参数:  无
 返回的值:  无
 ***************************************************************************************************/
bool SwitchButtonWidget::getSwitchStatus()
{
    return mSwitchFlag;
}

/***************************************************************************************************
 函数名称:  resizeEvent()
 功能描述:  大小重置事件
 输入参数:  event---事件
 返回的值:  无
 ***************************************************************************************************/
void SwitchButtonWidget::resizeEvent(QResizeEvent *event)
{
    Q_UNUSED(event);

    ui->widget->resize(mWidth,mHeight);

    QString widgetQss = QString("QWidget{border-radius: %1px;background-color: #2058A8;}").arg(mHeight/2);
    ui->widget->setStyleSheet(widgetQss);

    ui->pushButton_Switch->resize(int(mWidth*mWidthRatio),int(mHeight*mHeightRatio));
    QString pushButtonQss = QString("QPushButton{border-radius:%1px;border-style:outset;background-color:rgb(255,255,255);}").arg(ui->pushButton_Switch->height()/2);
    ui->pushButton_Switch->setStyleSheet(pushButtonQss);
    ui->pushButton_Switch->move(mWidth*mLeftPointRatio,3);
}

/***************************************************************************************************
 函数名称:  on_pushButton_Switch_clicked()
 功能描述:  按钮点击槽函数
 输入参数:  无
 返回的值:  无
 ***************************************************************************************************/
void SwitchButtonWidget::on_pushButton_Switch_clicked()
{
    if(mAnimationing)
    {
        return;
    }

    if(!mSwitchFlag)
    {
        if(mAnimation == nullptr)
        {
            mAnimation = new QPropertyAnimation(ui->pushButton_Switch, "pos", this);
        }
        mAnimation->setDuration(300);
        mAnimation->setEasingCurve(QEasingCurve::Linear);
        mAnimation->setStartValue(QPointF(mWidth*mLeftPointRatio,3));
        mAnimation->setEndValue(QPointF(mWidth*mRightPointRatio, 3));
        mAnimation->start();

        mSwitchFlag = true;

    }
    else
    {
        if(mAnimation == nullptr)
        {
            mAnimation = new QPropertyAnimation(ui->pushButton_Switch, "pos", this);
        }
        mAnimation->setDuration(300);
        mAnimation->setEasingCurve(QEasingCurve::Linear);
        mAnimation->setStartValue(QPointF(mWidth*mRightPointRatio, 3));
        mAnimation->setEndValue(QPointF(mWidth*mLeftPointRatio, 3));
        mAnimation->start();

        mSwitchFlag = false;
    }

    mAnimationing = true;
    QTimer::singleShot(300,this,[=](){
        slotAnimationStop();
    });
}

/***************************************************************************************************
 函数名称:  slotAnimationStop()
 功能描述:  清零动画标志位
 输入参数:  无
 返回的值:  无
 ***************************************************************************************************/
void SwitchButtonWidget::slotAnimationStop()
{
    if(mAnimationing)
    {
        mAnimationing = false;
    }
}



QTabWidget

(一)删除页(实质是隐藏) 

ui->tabWidget->removeTab(2);//删除第二页
ui->tabWidget->addTab(ui->tab_2,QObject::tr("方法"));//删除后又让其显示

(二)插入页

ui->tabWidget->addTab();

QSQLITE数据库 

   (一)使用方法

  1. 在.pro文件添加:QT += core gui spl
  2. dayinQT支持的数据库驱动:qDebug()<<QSqlDatabase::drivers();
  3. sqlite数据库需要与QSqlQuery类关联使用;

 (二) sqlite数据的函数

QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE"); //创建一个QSqlite数据库 
database.setHostName("localhost");  //数据库主机名   
database.setDatabaseName("scott");  //数据库名   
database.setUserName("stott");      //数据库用户名   
database.setPassword("tiger");      //数据库密码   
database.open();          //打开数据库连接 
database.close();         //释放数据库连接  

(三)QSqlQuery类

        用于处理sqlite的命令

  方法一:

QSqlQuery qsQuery = QSqlQuery(database);
qsQuery.exec("sqlite数据库的命令");

  方法二:

QSqlQuery qsQuery = QSqlQuery(database);
QString strSqlText = QString("sqlite命令");
qsQuery.prepare(strSqlText); 
qsQuery.exec();

  示例: 

//设置数据库驱动名称
QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE"); //创建一个QSqlite数据库

 /*一般这样用
    if(database.contains("数据库名"))
           database = QSqlDatabase::database("数据库名");
     else
           database = QSqlDatabase::addDatabase("QSQLITE" , "数据库名");
 */
//设置数据库名
database.setDatabaseName("../test.db");

if(database.open())
{
    qDebug()<<"dadabase open success"<<endl;
}
else
{
    qDebug()<<"dadabase open fail"<<endl;
}

QSqlQuery qsQuery = QSqlQuery(database);
qsQuery.exec("create table stu(nu integer,name char,score float)");//创建表
qsQuery.exec("insert into stu values(1,'xiaoming',89)");//插入数据

qsQuery.prepare("select *from stu"); //查询数据
while(qsQuery.next())
{
    int num = qsQuery.value(0).toInt();
    QString name = qsQuery.value(1).toString();
    int score = qsQuery.value(2).toInt();
    qDebug()<<"num = "<<num<<"  name = "<<name<<"  score = "<<score<<endl;
}
qsQuery.exec();
database.close();

 (三)查询数据库sqlite中是否含有某个表

QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE"); //创建一个QSqlite数据库
//设置数据库名
database.setDatabaseName("../test.db");

QString cmd = QString("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='%1'").arg("表名");
SQLBase db = SQLBase(cmd);
QSqlQuery query(database);
query.prepare(cmd); 
if(query.first())
{
    bool isHaveTable = query.value(0).toBool();//含有某个表返回ture
    if(isHaveTable == true)
    {
            
    }
}

QMap

(一)性质

   1、键不能重复,重复后一个覆盖前一个;

QMap<qint16,bool> mymap;
mymap.insert(12,false);
mymap.insert(22,false);
mymap.insert(12,false);
mymap.insert(55,false);
mymap.insert(66,false);

QMapIterator<qint16,bool> it(mymap);
while(it.hasNext())
{
    it.next();
    qDebug()<<"key = "<<it.key()<<"value = "<<it.value()<<endl;
}
qDebug()<<mymap[12]<<endl;

  (二)用法

   1、简单用法

QMap<int,QString>Info;

Info.insert(100,"xiaoming");
Info.insert(87,"xiaohong");
Info.insert(99,"shax");
QString name1 = Info.value(100,NULL);
QString name2 = Info.value(87,NULL);
qDebug()<<"name1 = "<<name1<<endl;    //name1 = "xiaoming";
qDebug()<<"name2 = "<<name2<<endl;    //name2 = "xiaohong";

   常用于QMap<QString,类> Info;和上诉一样。 


截图实现

QPixmap::grabWindow(WinId);    //获取图片
QDesktopWidget    //获取当前程序所在窗口的Id
winId()

#include <QPixmap>
#include <QDesktopWidget>
#include <QDesktopServices>
#include <QFileDialog>
#include <QClipboard>
QPixmap pixmap = QPixmap::grabWindow(QApplication::desktop()->window()->winId());//获取桌面图片
ui->ScreenLabel->setPixmap(pixmap.scaled(ui->ScreenLabel->size()));//将图片显示在label中并按比例缩放
QString fileName = QFileDialog::getSaveFileName(this,"Save file",".");//保存图片
pixmap.save(fileName);

QClipboard *clipboard = QGuiApplication::clipboard();//创建剪切板
//QString originalText = clipboard->text();
clipboard->setPixmap(this->pixmap);//将图片放在剪切板上,可以直接粘贴
   

事件

所有的事件都是受保护的成员函数。

一、显示鼠标右键光标位置事件

#include <QContextMenuEvent>
void contextMenuEvent(QContextMenuEvent *event)//在函数中实现内容
{
    对话框->exec(QCursor::pos());//显示在光标位置
}

二、关闭事件-closeEvent()

#include <QCloseEvent>
void MainWindow::closeEvent(QCloseEvent *event)
{
    if()
    {
        event->accept();//接受事件
    }
    else
    {
        event->ignore();//忽略事件
    }
}

三、鼠标事件

      (一)鼠标按下-mousePressEvent()

void Widget::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        QPoint p = event->globalPos() - this->frameGeometry().topLeft();
    }
    else if(event->button() == Qt::RightButton)
    {
        this->close();
    }
    else
    {}
    Widget::mousePressEvent(event);//信号继续往下传(传给Widget)
}

   (二)鼠标移动-mouseMoveEvent()

void Widget::mouseMoveEvent(QMouseEvent *event)
{
    move(event->globalPos());
}

  (三)鼠标释放-mouseReleaseEvent()

void Widget::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
    }
    else if(event->button() == Qt::RightButton)
    {
    }
    else
    {}
}

 示例: 

/*移动对话框*/
void Widget::mousePressEvent(QMouseEvent *event)
{
   if (event->button() == Qt::LeftButton)
   {
       firstPoint = QCursor::pos() - frameGeometry().topLeft();
   }
}

void Widget::mouseMoveEvent(QMouseEvent *event)
{
    if (event->buttons() & Qt::LeftButton)
    {
        QPoint curPoint = event->globalPos() - firstPoint;
        move(curPoint);
    }
}

四 、键盘事件

    (一)键盘按下-keyPressEvent()

void Widget::keyPressEvent(QKeyEvent *event)
{
    if(event->key() == Qt::Key_A)
    {
    }
}

五、其他事件 

 (一)、resizeEvent()

     resizeEvent()事件触发:

  1. resize调用或者其他导致窗口事件大小发生变化产生。
  2. 如果想拖动主窗口的时候,能够让窗口中的组件随着窗口也能缩放的话,需要重写resizeEvent,原因在于,在构造之后,子窗口的大小就是固定的。

画图

画图事件:

  1. 函数:void paintEvent(QPaintEvent *event);
  2. 如果在主窗口绘图。必须放在绘图事件里;
  3. 如果需要重绘图,一定要在 paintEvent()中绘图;
  4. 绘图事件内部自动调用,窗口需要重绘的时候(状态改变)--调用update()就间接调用了画图事件;

   画图事件触发原因:

  • 窗口显隐导致重画
  • 窗口大小(重新调整)改变,或者重新排布(布局)导致重画
  • 调用update 或者 repaint重画
  • 当窗口第一次显示时,系统会自动产生绘图事件
  • 当窗口部件被其他部件遮挡时,然后又再次显示出来,会对隐藏区域进行重绘事件

  画图默认坐标为以窗口左上角为O点,如下图所示:

 设置坐标O点为窗口中心:

QPainter painter(this);
painter.translate(this->width()/2.0, this->height()/2.0);

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter p(this);//创建画家对象
    /*QPainter p;
    p.begin(this);
    //绘图操作
    p.end();*/

    //设置背景图
    // p.drawPixmap(0,0,this->width(),this->height(),QPixmap("../IMG"));//1
    //p.drawPixmap(rect(),QPixmap("../IMG"));//2

    //定义画笔
    QPen pen;
    pen.setWidth(5);//设置线宽
    //pen.setColor(Qt::red);//设置颜色
    pen.setColor(QColor(12,23,56));//rgb设置颜色

    //把画笔交给画家
    p.setPen(pen);

    //画直线
    p.drawLine(50,50,50,150);

    //创建画刷对象
    QBrush brush;
    brush.setColor(Qt::red);//设置颜色
    brush.setStyle(Qt::Dense1Pattern);//设置样式

    //把画刷交给画家
    p.setBrush(brush);

    //画矩形
    p.drawRect(50,150,50,50);

    //画圆形
    p.drawEllipse(QPoint(150,150),60,60);//60为半径

    QRectF rect(50,50,this->width(),this->height());//(50,50)是坐标,width()/heigh()为宽高,非半径
    painter.drawEllipse(rect);

     //图片 方法一
    p.drawPixmap(0,0,QPixmap("../IMG.png");
    p.drawPixmap(0,100,QBitmap("../IMG.png"));//QBitmap只有黑白两种颜色
    //图片 方法二
    QPixmap pixmap;
    pixmap.load("../IMG.png");
    p.drawPixmap(0,200,pixmap);
}

(二)绘图设备 

  1. QPixmap:针对屏幕进行优化,和平台有关(显示的效果不一样),不能对图片进行修改;
  2. QImage:和平台无关,可以对图片进行修改,可以在线程中绘图;
  3. QPicture:保存绘图的状态(二进制文件);
//绘图设备 400*300
QPixmap pixmap(400,300);
QPainter p(&pixmap);
//填充白色背景色
p.fillRect(0,0,400,300,QBrush(Qt::white));
//pixmap.fill(Qt::white);

p.drawLine(20,20,50,60);
//保存图片
pixmap.save("../pixmap.png");
QPicture pic;
QPainter p(&pic);
p.drawRect(40,40,60,60);
//保存的是二进制文件
pic.save("../pic.png");

 (三)画曲线

      曲线实质是很多条直线组合而成。

QPainter p(this);//创建画家对象
QPen pen;//定义画笔
pen.setWidth(5);//设置线宽
pen.setColor(Qt::red);//设置颜色

p.setPen(pen);//把画笔交给画家

//方法一
QPoint a(20,30);
QPoint b(150,150);
QPoint c(80,20);
 
QVector<QPoint> v;
v.append(a);
v.append(b);
v.append(c);
QPolygon polyline(v);
p.drawPolyline(polyline);

//方法二
QPoint a(20,30);
QPoint b(150,150);
QPoint c(80,20);
QPolygon polyline(3);
polyline[0] = a;
polyline[1] = b;
polyline[2] = c;
p.drawPolyline(polyline);

 画渐变图形

 渐变有三种:线性渐变(QLinearGradient)、半径渐变(QConicalGradient) 、圆锥渐变 (QRadialGradient)。

QGradient父类的常用公共函数有:

void QGradient::setSpread (QGradient::Spread method);
设置填充梯度区域外的区域,参数有:

QGradient::PadSpread  :填充区域内最接近的停止颜色。这是默认的。
QGradient::RepeatSpread : 在区域外继续重复填充
QGradient::ReflectSpread : 在区域外反射填充

QGradient::setCoordinateMode (QGradient::CoordinateMode mode);
参数:设置渐变的坐标模式,比如QGradient::LogicalMode设置坐标为逻辑坐标(默认为该值)

void setColorAt (qreal position, const QColor & color );
参数:设置梯度颜色, position处于0~1之间

QLinearGradient线性渐变 

构造函数:QLinearGradient(qreal x1, qreal y1, qreal x2, qreal y2);

参数:1、x1,y1表示渐变起始坐标, x2,y2表示渐变终点坐标;

           2、如果只有x相等,则表示垂直线性渐变,如果只有y相等,则表示平行线性渐变,否则就是斜角线性渐变。

示例:

void Widget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    QLinearGradient Linear(100,100,100,200);        //垂直渐变

    Linear.setColorAt(0,Qt::red);
    Linear.setColorAt(1,Qt::blue);

    painter.setBrush(Linear);
    painter.setPen(Qt::transparent);
    painter.drawRect(100,100,100,100); 

    //QRectF rect(50,50,this->width()/2,this->height()/2);
    //painter.drawEllipse(rect);      
}

        

QRadialGradient线性渐变 

构造函数: QRadialGradient(qreal cx, qreal cy, qreal radius, qreal fx, qreal fy);

参数:1、cx cy : 设置圆的中心原点(center);

           2、radius:设置圆半径;

           3、fx fy : 设置焦点focus,也就是颜色的起始位置;

 示例:

void Widget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    painter.translate(width()/2,height()/2); //坐标O为窗口中心
    QRadialGradient Radial(0,0,100,0,0);    //设置圆的原点和焦点在中心,半径100(此半径为渐变半径)

    Radial.setColorAt(0,Qt::red);
    Radial.setColorAt(0.5,Qt::blue);        //设置50%处的半径为蓝色
    Radial.setColorAt(1,Qt::green);

    painter.setPen(Qt::transparent);
    painter.setBrush(Radial);
    painter.drawEllipse(-120,-120,240,240);
}

QConicalGradient圆锥渐变

构造函数:QConicalGradient ( qreal cx, qreal cy, qreal angle );

参数:1、(cx,cy)位置为圆锥尖设置;

           2、angle角度为起始颜色位置(逆时针渐变)、x轴逆时针方向计算角度。 

示例1: 

void Widget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    painter.translate(width()/2,height()/2);

    QConicalGradient Conical(0,0,90);    //设置点在中心,角度为90
    Conical.setColorAt(0,Qt::red);
    Conical.setColorAt(0.5,Qt::blue);
    Conical.setColorAt(1,Qt::green);

    painter.setPen(Qt::transparent);
    painter.setBrush(Conical);
    painter.drawEllipse(-120,-120,240,240);
}

 

 示例2: 

void Widget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    painter.translate(width()/2,height()/2);

    QConicalGradient Conical(0,0,90);    //设置点在中心,角度为90
    Conical.setColorAt(0,Qt::blue);
    Conical.setColorAt(1,Qt::green);

    painter.setPen(Qt::NoPen);
    painter.setBrush(Conical);
    QRectF rect(-100, -100, 200,200);
    QPainterPath path;
    path.arcTo(rect, 90, -60);      //开始角度->当前角度;注意:第二个参数为起始角度应与Conical一致,
                                    //第三个参数为终点角度,正数表示逆时针方向,负数为顺时针方向
    painter.drawPath(path);
}

渐变实现进度 

//.文件
protected:
    void paintEvent(QPaintEvent *);

private:
    void drawBaseCircle(QPainter &painter); //画基础圆
    void drawGraduaCirle(QPainter &painter);//画渐变圆
    void drawCoverCirle(QPainter &painter); //画覆盖圆
    void drawMoveCirlde(QPainter &painter); //画移动圆

private slots:
   void slotOutTimer();

private:
    Ui::Widget *ui;
    quint16 mBaseCircleR;   //基础圆(外圆)半径
    quint16 mCoverCircleR;  //遮盖圆(内圆)半径
    quint16 mMoveCircleR;   //移动圆半径

    qreal mStartAngle = 90; //开始角度
    qreal mCurrAngle = 0;  //当前角度

    QColor  mBorderColor = QColor("#B8B8B8");   //边框颜色
    QColor  mBaseColor = QColor("#EAF3F7");     //基础圆颜色
    QColor  mStartColor = QColor("#0D74BE");    //起始颜色
    QColor  mCurrColor = QColor("#0F99E4");     //当前颜色

    QPointer<QTimer> timer;
    quint16 mProgressValue = 0;//进度

//.cpp文件
void Widget::slotOutTimer()
{
    mProgressValue++;
    if(mProgressValue >= 60)
    {
        mProgressValue = 60;
        timer->stop();
    }

    mCurrAngle = -360*(mProgressValue/60.0);//计算当前角度,负数为顺时针
    ui->label->setText(QString::number(quint8(mProgressValue/60.0*100)) + tr("%"));
    update();
}

void Widget::paintEvent(QPaintEvent *)
{
    mBaseCircleR = qMin(width(), height())/2-10;//基础圆(外圆)半径,这里减少10像素是为了保证边缘能完全显示
    mCoverCircleR = mBaseCircleR - 20;          //遮盖圆(内圆)半径
    mMoveCircleR = mBaseCircleR - mCoverCircleR;//移动圆半径
    QPainter painter(this);
    painter.translate(width()/2.0, height()/2.0);
    painter.setRenderHint(QPainter::Antialiasing, true); //反锯齿
    drawBaseCircle(painter);    //画基础圆
    drawGraduaCirle(painter);   //画渐变圆
    drawCoverCirle(painter);    //画覆盖圆
    drawMoveCirlde(painter);    //画移动圆
}

void Widget::drawBaseCircle(QPainter &painter)
{
    QRectF rect(-mBaseCircleR,-mBaseCircleR,mBaseCircleR*2,mBaseCircleR*2);
    painter.setPen(QPen(mBorderColor));     //边框
    painter.setBrush(mBaseColor);
    painter.drawEllipse(rect);
}

void Widget::drawGraduaCirle(QPainter &painter)
{
    QRectF rect(-mBaseCircleR, -mBaseCircleR, mBaseCircleR*2, mBaseCircleR*2);

    //渐变,当前角度->结束角度
    QConicalGradient conical(0, 0, 90);                 //开始角度90°
    conical.setColorAt(-mCurrAngle/360.0, mCurrColor);  //当前角度颜色
    conical.setColorAt(1.0, mStartColor);               //结束角度颜色

    painter.setPen(Qt::NoPen);
    painter.setBrush(conical);
    QPainterPath path;
    path.arcTo(rect, 90, mCurrAngle);      //开始角度->当前角度,顺时针
    painter.drawPath(path);
}

void Widget::drawCoverCirle(QPainter &painter)
{
    QRectF rect(-mCoverCircleR, -mCoverCircleR, mCoverCircleR*2, mCoverCircleR*2);
    painter.setPen(QPen(QColor("#FFFFFF"), 3));//边框颜色白色,边框大小3
    painter.setBrush(QColor("#E4EAEF"));
    painter.drawEllipse(rect);
}

void Widget::drawMoveCirlde(QPainter &painter)
{
    QPointF point(0, 0);
    qreal tempR = mCoverCircleR + (mBaseCircleR - mCoverCircleR)/2.0;
    point.setX(point.x() + qCos((mCurrAngle+90)*M_PI/180) * tempR);
    point.setY(point.y() - qSin((mCurrAngle+90)*M_PI/180) * tempR);

    painter.setPen(QPen(mBorderColor));
    painter.setBrush(Qt::white);
    painter.drawEllipse(point, mMoveCircleR, mMoveCircleR);

    painter.setPen(Qt::NoPen);
    painter.setBrush(mCurrColor);
    painter.drawEllipse(point, (mBaseCircleR - mCoverCircleR)/2.0, (mBaseCircleR - mCoverCircleR)/2.0);
}


QVariant

 一、简述
        QVariant 可以保存很多Qt的数据类型,包括QBrush、QColor、QCursor、QDateTime、QFont、QKeySequence、 QPalette、QPen、QPixmap、QPoint、QRect、QRegion、QSize和QString,并且还有C++基本类型,如 int、float等。到需要使用的时候使用一系列的to函数取出来即可,注意:对于存入的是什么类型,取出也要为这个类型。
QVariant也可以支持自定义的数据类型。被QVariant存储的数据类型需要有一个默认的构造函数和一个拷贝构造函数。为了实现这个功能,首先必须使用Q_DECLARE_METATYPE()宏。通常会将这个宏放在类的声明所在头文件的下面:

二、简单例子实现
    存储用到了QVariant QVariant::fromValue(const T &value) 或 void QVariant::setValue(const T &value)。获取用到了                  T QVariant::value() const 或 to函数,在这之前一般要bool QVariant::canConvert(int targetTypeId) const先用进行判断,是否可以转换。

QVariant v1(520);
int num1 = v1.toInt();

QVariant v2 = 520;
int num2 = v2.toInt();

QVariant v3;
v3.setValue(5);
int num3 = v3.toInt();         // num3 = 5
QString str3 = v3.toString();   // str3 = "5"

QVariant v4;
v4.fromValue(5);
int num4 = v4.toInt();  //num4 =5
QString str4 = v4.toString(); //str4 = ""

QVariant v5;
v5.setValue(250);
int num5 = v5.value<int>(); //num5 = 250
QString str5 = v5.value<QString>(); //str5 = "250";

三、自定义存储QVariant类型 

       QVariant也可以支持自定义的数据类型。被QVariant存储的数据类型需要有一个默认的构造函数和一个拷贝构造函数。为了实现这个功能,首先必须使用Q_DECLARE_METATYPE()宏。通常会将这个宏放在类的声明所在头文件的下面。

struct Student{
    QString name;
    int age;
    int score;
};
Q_DECLARE_METATYPE(Student)

//存储数据
Student stu;
stu.name = "xiaoming";
stu.age = 18;
stu.score = 98;
QVariant v6 = QVariant::fromValue(stu);

//获取数据
Student stu1 = v6.value<Student>();

Movie

  一、QMovie实现简单的.gif动态图显示

#include <QMovie>

int main(int argc,char *argv[])
{
    QApplication app(argc,argv);

    QLabel *label = new QLabel();
    //方法一
    /*
    QMovie *movie = new QMovie("../Photograph/tt.gif");
    label->setMovie(movie);
    */
    //方法二
    QMovie *movie = new QMovie(this);
    Movie->setFileName("../Photograph/tt.gif");
    label->setMovie(Movie);

    Movie->setScaledSize(label->size());//图片大小为label大小
    Movie->setSpeed(200);//播放速度 
    movie->start();
    label->show();

    return app.exec();
}

 二、手动打开.gif

#define MAINWINDOW_H

#include <QMainWindow>
#include <QMovie>
#include <QPixmap>
#include <QString>
#include <QFileDialog>
#include <QFileInfo>
#include <QDebug>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_pushButton_2_clicked();

    void on_pushButton_3_clicked();

    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
    QMovie *Movie;
};

#endif // MAINWINDOW_H

/*************************************/
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    Movie = new QMovie(this);
    Movie->setScaledSize(ui->label->size());//图片大小为label尺寸
    Movie->setSpeed(200);//设置播放速度
}

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

void MainWindow::on_pushButton_2_clicked()
{
    Movie->start();
}

void MainWindow::on_pushButton_3_clicked()
{
    Movie->stop();
}

void MainWindow::on_pushButton_clicked()
{
    QString filename = QFileDialog::getOpenFileName(this,"open file","../","file (*.gif*)");//获取文件名,2-4个参数分别为:标题,位置,文件类型
    if(!filename.isEmpty())
    {
        if(Movie->isValid())
        {
            Movie->stop();
        }
        Movie->setFileName(filename);
        ui->label->setMovie(Movie);
        Movie->start();
        return;
    }

}

 QPropertyAnimation(动画) 

m_animation = new QPropertyAnimation(ui->label, "pos", this);
m_animation->setDuration(300);                    //动画,持续3s
m_animation->setEasingCurve(QEasingCurve::Linear);//直线移动
m_animation->setStartValue(QPoint(130, 200));     //开始坐标
m_animation->setEndValue(QPoint(130, 80));        //结束坐标
m_animation->start();

QString

1 、获取str中从第row起连续length个字符

QString str1 = str.mid(row,length);

2 、组合字符串

QString cureTime = QString("%1%2%3").arg(timeHours).arg(":").arg(timeMinutes);

3 、数转字符串

QString str1 = QString::number(num,'f',2);//将浮点型转化为字符串,2为精度
QString str2 = QString::number(num,10);    //将整数转化为字符串,10为十进制
QString str3 = QString("%1").arg(num, 8, 16, QLatin1Char('0'));   //8代表宽度,16表示16进制,空位补零

 4、分开字符

QStringList MyList = str.split("~");//将字符串以“~”分开

5 、填补字符

QString s = "apple";
QString t = s.leftJustified(8, '.');    // t == "apple..."

QString s = "apple";
QString t = s.rightJustified(8, '.');    // t == "...apple"

QString str = "Pineapple";
str = str.leftJustified(5, '.', true);    // str == "Pinea"

6 、删除字符串中某个字符

QString s = "Montreal";
s.remove(1, 4);// s == "Meal"
QString t = "Ali Baba";
t.remove(QChar('a'), Qt::CaseInsensitive);// t == "li Bb"

 7 、删除字符串后面几个字符

QString str = "Montreal";
str = str.chop(2);    //删除后两个字符

7、SString类型转换为const char* (toLatin1)

Qstring str = "helloworld";
char *s;
QByteArray ba = str.toLatin1(); 
s = ba.data();

 8、QString转 char*

QString str = "hello word!";
QByteArray temp = str.toLocal8bit();
char *name = temp.data();

 9、char转QString

char buf[128];
QString str = QString::fromLocal8Bit(buf);

样式表

背影样式:

  • background-color:背景颜色
  • background-image:背景图片
  • background-repeat:背景重复
  • alternate-background-color:交替背景  //适用于QTableView、QTableWidget和QListView等。

边框样式:

  • border-color:边框颜色
  • border-style:边框风格
    • none: 无样式;
    • hidden: 同样是无样式,主要用于解决和表格的边框冲突;
    • dotted: 点划线;
    • dashed: 虚线;
    • solid: 实线/立体;
    • double: 双线,两条线加上中间的空白等于border-width的取值;
    • groove: 槽状;
    • ridge: 脊状,和groove相反;
    • inset: 凹陷;
    • outset:凸出,和inset相反;
  • border-width:边框宽度
  • border-image:边框图片
  • border-radius:元素的外边框圆角
    • border-top-left-radius
    • border-top-right-radius
    • border-bottom-right-radius
    • border-bottom-left-radius
  • border-top
    • border-top-color
    • border-top-style
    • border-top-width
  • border-right
    • border-right-color
    • border-right-style
    • border-right-width
  • border-bottom
    • border-bottom-color
    • border-bottom-style
    • border-bottom-width
  • border-left
    • border-left-color
    • border-left-style
    • border-left-width

字体样式:

  • color:字体颜色
  • font:字体样式
    • font-family:字体类型
    • font-size:字体大小  //font:13pt 或 font-size:13pt
    • font-style:字体风格
    • font-weight:字体粗细  //font-weight: bold 黑体

轮廓样式:

  • outline:轮廓属性
  • outline-color:设置一个元素轮廓的颜色
  • outline-offset:设置 outline 与元素边缘或边框之间的间隙
  • outline-style:设置元素轮廓的样式
  • outline-radius:设置元素的轮廓圆角
    • outline-bottom-left-radius
    • outline-bottom-right-radius
    • outline-top-left-radius
    • outline-top-right-radius

padding:

PADDING:是包围content的矩形区域,主要是窗口部件内容与边缘线(border)之间的缝隙,通过padding属性可以定义padding的宽度。

  • padding-top
  • padding-bottom
  • padding-left
  • padding-right

内容(content)、填衬(padding)、边框(border)、边距(margin)。使用background-image来指定背景图片,如果希望背景图片随着部件的大小变化,就必须使用border-image。image属性可以用来在border-image之上绘制一个图片、图片对齐参考image-position属性。 

基本语法 

下面列出了一些基本组合,可以任意组合,各个组合间通过,分割即可。

选择器
{
	属性: 值;
}

QPushButton
{
	color: red;
}
选择器 : 状态
{
	属性: 值;
}

QPushButton : hover
{
	color: red;
}
选择器 :: 辅助控制器
{
	属性 : 值;
}

QCheckBox :: indicator
{
	color : red;
}
选择器 : 状态
{
	属性 : 值;
}

QPushButton : hover
{
	color : red;
}
选择器 :: 辅助控制器 : 状态
{
	属性 : 值;
}

QTabBar::tab:selected, QTabBar::tab:hover
{
	background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
									stop: 0 #E0E0E0, stop: 1 #FFFFFF);
}

样式表使用方法

1、程序中实现

ui->pushButton->setStyleSheet("background-color:red; color: blue");
ui->pushButton->setStyleSheet("QPushButton{background-color:red; color: blue}");
ui->pushButton->setStyleSheet("QPushButton{background-color:rgb(23,234,245); color: blue}");
ui->pushButton->setStyleSheet("QPushButton{background-color:#00ffff; color: blue}");

2、UI界面实现

 

 设置背景

1、设置背景图片

控件->setStyleSheet("background-image: url(:/new/prefix1/image/cut.png);");

background-image: url(:/new/prefix1/ICO/check_adjust.png);
background-position:center;//图片居中
background-repeat:no-repeat;//图片没有重复
background-color: #B9F8F8;//背景颜色
background-color:transparent;//继承父窗口的背景色

 border-image与background-image的区别:

  •  border-image
border-image:url(:/refresh.png);
border:1px solid red;

在这里插入图片描述

  •  background-image
background-image:url(:/refresh.png);
border:1px solid red;

在这里插入图片描述

 background-image: url(:/refresh.png);
 border:1px solid red;
 background-repeat:no-repeat;              //不重复
 background-position:center;               //在中心位置显示

在这里插入图片描述

总结
(1)border-image:图片可以自动居中显示,但是设置边框属性无效。
(2)background-iamge:虽然图片不会自动居中显示,但是可以设置其属性,使其居中显示,还可以设置边框的属性。个人觉得background-image比较灵活。 

2 、背景渐变色

if(num > 100)
{
    num = 100;
}
qreal realValue = num/100.0;
if(realValue == 1)
{
    realValue = 0.999;
}
QString qss = QString("QPushButton{border-radius: 0; background: qlineargradient(x1:0,y1:0,x2:1,y2:0,\
        stop:0 #15CED0,stop:%1 #15CED0,stop:%2 #B9F8F8,stop:1 #B9F8F8);}")
                .arg(realValue).arg(realValue+0.0001);
ui->teButton->setStyleSheet(qss);

按键风格(QPushButon)

 1、按键四角圆弧设置

ui->pushButton->setStyleSheet("QPushButton{background-color: rgb(225, 225, 225);border-color:gray;\
                               border-width:2px;border-radius:20px;border-style:outset groove;}");

 

2、将按键设置为圆形

首先将按键的长宽设置为一样,将border-radius设置为宽高值的一半,其他设置不变!比如我这里宽高大小为100,那么border-radius就是50px。【原因:圆角大小,因为矩形的一半为50,所以圆角大小 设置为50以后,将会成为一个圆型,小于50,就是圆角矩形了。

 3、按键按下和释放状态不一样

   代码设置:

ui->pushButton->setStyleSheet("QPushButton{background-color: rgb(225, 225, 225);border-color:gray;\
                               border-width:2px;border-radius:20px;border-style:outset groove;}"
                              "QPushButton:pressed{background-color:rgb(204, 228, 247);border-style: inset;}");

  ui界面设置:

  方法一:

#pushButton //pushButton为按键名
{
	background-color: rgb(225, 225, 225);
	border-color:gray;
    border-width:2px;
	border-radius:10px;
	border-style:outset groove;
	padding:2px 4px;
}
#pushButton:pressed
{
	background-color:rgb(204, 228, 247);
	border-style: inset;
}
#pushButton:checked
{
}
#pushButton:disabled
{
}

 若此样式在QPushButton上层控件样式表里设置(widget,TabWidget),则只针对按键名为“pushButton”有效。

 方法二:

QPushButton
{
	background-color: rgb(225, 225, 225);
	border-color:gray;
    border-width:2px;
	border-radius:10px;
	border-style:outset groove;
	padding:2px 4px;
}
QPushButton:pressed
{
	background-color:rgb(204, 228, 247);
	border-style: inset;
}
QPushButton:checked
{
}
QPushButton:disabled
{
}

若此样式在QPushButton上层控件样式表里设置(widget,TabWidget),则此控件下所有QPushButton都有效。 

QTabWidget样式 设置

1、QTabWidget各位置样式表示

2、示例(效果如上图)

QTabWidget::pane 
{
   border: 1px solid lightgray;
   top:-1px; 
   background: rgb(245, 245, 245); 
} 
QTabBar::tab 
{
   background: rgb(230, 230, 230); 
   border:1px solid lightgray; 
   padding: 15px;
   font:13pt;
   font-weight: bold;
  } 
QTabBar::tab:selected 
{ 
   background: rgb(245, 245, 245); 
   margin-bottom: -1px; 
  }

qss实现渐变 

圆锥渐变(qconicalgradient)

效果如下(图中花屏录屏软件的问题):

void MainWindow::slotTimerOut()    //100ms刷一次
{
    mValue--;// 初值为100
    if(mValue == 0)
    {
        mValue = 0;
        mTimer->stop();
    }

    qreal realValue = mValue/100.0;

    QString qss = QString("border-radius:40px; border-style:solid;background-color:qconicalgradient(cx:0.5, cy:0.5, angle:90, stop:0 rgba(255, 255, 255,100),stop:%1 rgba(255, 255, 255,100),stop:%2 rgba(0, 85, 255,160),stop:1 rgba(0, 85, 255,160));}").arg(realValue).arg(realValue+0.000001);

    ui->pushButton->setStyleSheet(qss);
}

实现扇形渐变的话,我们需要确定扇形的圆心(中心点)、扇形的半径、扇形的起始角度、扇形的跨度,以及扇形所填充颜色的渐变方向。不过这里我们不需要给定半径大小,因为按钮的大小和形状是未知的,所以我们可以认为qconicalgradient()已经将扇形半径设定为无限大了。
 

qconicalgradient()中的参数:

  • cx和cy的值是中心点的逻辑坐标,所谓逻辑坐标就是将它所描述的对象(箱体模型)的左上顶点设为(0.0, 0.0),右下顶点设为(1.0,1.0)。所以这里的(cx:0.5, cy:0.5)就是指定扇形的圆心在按钮的中心点上。
  • angle的值代表扇形的起始角度(单位:度),和数学上定义的一样,默认扇形的起始角度为0,即指向x轴的正方向,角度为正是逆时针方向,角度为负是顺时针方向。
  • stop可以控制扇形的跨度和颜色,确切来说它控制的是扇形从哪里、以什么颜色开始渐变。stop 0 表示渐变开始的位置(以angle指定的的角度为90,逆时针转360度后为1),stop 01表示结束。rgba(),a表示透明度。

线性渐变(qlineargradient)

void MainWindow::slotTimerOut()    //100ms一次
{

    mValue++;
    if(mValue >= 100)
    {
        mValue = 100;
        mTimer->stop();
    }
    
    qreal realValue = mValue/100.0;

    QString qss = QString("border-style:solid;background-color:qlineargradient(x1:0,y1:0,x2:1,y2:0, angle:90, stop:0 rgb(26, 156, 226),stop:%1 rgb(26, 156, 226),stop:%2 rgb(127, 186, 0),stop:1 rgb(127, 186, 0));}").arg(realValue).arg(realValue+0.000001);

ui->pushButton->setStyleSheet(qss);
}

QSerialPort(串口)

在 Qt5 中提供了QtSerialPort模块,方便编程人员快速的开发应用串口的应用程序。

当前的QtSerialPort模块中提供了两个C++类,分别是QSerialPort 和QSerialPortInfo。

QSerialPort 类提供了操作串口的各种接口。

QSerialPortInfo 是一个辅助类,可以提供计算机中可用串口的各种信息。

使用时需要在pro文件中增加:QT += serialport

 实现串口调试助手

效果及界面:

程序实现:

/***********  .h ***************/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSerialPortInfo>
#include <QSerialPort>
#include <QPointer>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();


public slots:
    void SLOTgetSerialPortName();  //获取所有串口设备
    void SLOTopenSerialPort(bool); //打开串口
    void SLOTreadData();           //读取串口数据

private slots:
    void on_SendBtn_clicked();

private:
    Ui::MainWindow *ui;
    QPointer<QSerialPort> m_serialPort;
};
#endif // MAINWINDOW_H

/***********  .cpp ***************/
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QMessageBox>
#include <QTextCodec>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    SLOTgetSerialPortName();
    connect(ui->OpenSerialBtn,SIGNAL(clicked(bool)),this,SLOT(SLOTopenSerialPort(bool)),Qt::UniqueConnection);
}

void MainWindow::SLOTgetSerialPortName()
{
    //列举出电脑上全部的串口设备
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        qDebug() << "Name : " << info.portName();
        qDebug() << "Description : " << info.description();
        qDebug() << "Manufacturer: " << info.manufacturer();
        qDebug() << "Serial Number: " << info.serialNumber();
        qDebug() << "System Location: " << info.systemLocation();

        ui->NameComboBox->addItem(info.portName());
    }
}

void MainWindow::SLOTopenSerialPort(bool checked)
{
    if(checked)
    {
        quint8 index;
        m_serialPort = new QSerialPort(this);
        m_serialPort->setPortName(ui->NameComboBox->currentText());
        if(m_serialPort->open(QIODevice::ReadWrite))
        {
            ui->radioButton->setChecked(true);
            ui->OpenSerialBtn->setText(tr("关闭串口"));
            //设置波特率
            m_serialPort->setBaudRate(ui->BaudComboBox->currentText().toInt());

            //设置数据位
            index = ui->DataComboBox->currentIndex();
            switch(index)
            {
            case 0:
                m_serialPort->setDataBits(QSerialPort::Data8);
                break;
            case 1:
                m_serialPort->setDataBits(QSerialPort::Data7);
                break;
            case 2:
                m_serialPort->setDataBits(QSerialPort::Data6);
                break;
            case 3:
                m_serialPort->setDataBits(QSerialPort::Data5);
                break;
            default:
                m_serialPort->setDataBits(QSerialPort::UnknownDataBits);
                break;
            }

            //设置校验位
            index = ui->CheckComboBox->currentIndex();
            switch(index)
            {
            case 0:
                m_serialPort->setParity(QSerialPort::NoParity);
                break;
            case 1:
                m_serialPort->setParity(QSerialPort::OddParity);//奇校验
                break;
            case 2:
                m_serialPort->setParity(QSerialPort::EvenParity);//偶校验
                break;
            default:
                 m_serialPort->setParity(QSerialPort::UnknownParity);
            }

            //设置停止位
            index = ui->StopComboBox->currentIndex();
            switch(index)
            {
            case 0:
                m_serialPort->setStopBits(QSerialPort::OneStop);
                break;
            case 1:
                 m_serialPort->setStopBits(QSerialPort::OneAndHalfStop);
                break;
            case 2:
                m_serialPort->setStopBits(QSerialPort::TwoStop);
                break;
            default:
                 m_serialPort->setStopBits(QSerialPort::UnknownStopBits);
            }

            connect(this->m_serialPort, SIGNAL(readyRead()), this, SLOT(SLOTreadData()));
        }
        else
        {
            QMessageBox::about(NULL, "提示", "没有找到串口!");
             return;
        }
    }
    else
    {
        ui->radioButton->setChecked(false);
        ui->OpenSerialBtn->setText(tr("打开串口"));
    }
}

void MainWindow::SLOTreadData()
{
    QByteArray readData = this->m_serialPort->readAll();
    ui->ShowTextEdit->setAlignment(Qt::AlignLeft);

    if(!readData.isEmpty())
    {
        quint8 index = ui->ShowWayComboBox->currentIndex();

        if(index == 0) //16进制
        {
            QByteArray showData = readData.toHex();
            ui->ShowTextEdit->append(QString(showData));
        }
        else    //10进制
        {
            QTextCodec *tc = QTextCodec::codecForName("GBK");//QTextCodec::codecForName("GBK")
            QString readStr = tc->toUnicode(readData);
            ui->ShowTextEdit->append(readStr);
        }

        readData.clear();
    }  
}

void MainWindow::on_SendBtn_clicked()
{
    QByteArray sendData = ui->SendLineEdit->text().toLatin1();

    if(!sendData.isEmpty())
    {
        this->m_serialPort->write(sendData);

        ui->ShowTextEdit->setAlignment(Qt::AlignRight); //靠右显示
        quint8 index = ui->ShowWayComboBox->currentIndex();

        if(index == 0) //16进制
        {
            QByteArray showData = sendData.toHex();
            ui->ShowTextEdit->append(QString(showData));
        }
        else  //10进制
        {
            QTextCodec *tc = QTextCodec::codecForName("GBK");
            QString sendStr = tc->toUnicode(sendData);
            ui->ShowTextEdit->append(sendStr);
        }

        ui->SendLineEdit->clear();
    }
}

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






正则表达式

Qt下使用正则表达式

首先先看两个例子:

1、限制输入框只能输入数字

QRegExp regx("[1-9][0-9]+$");
QValidator *validator = new QRegExpValidator(regx, ui->lineEdit);
ui->lineEdit->setValidator(validator);

2、提取有效字段

QRegExp rx("[A-Z]*$");//等价 QRegExp rx; rx.setPattern("[A-Z]*$);
QString str = "ADabc123ASFAFF";
int pos =  rx.indexIn(str);
if(pos > -1)
{
    QString s = rx.cap(0);  //ASFAFF
    qDebug() << s;
}

3、循环检测匹配字符

QRegExp rx("(\\d+)");
QString str = "Offsets: 12 14 99 231 7";
QStringList list;
int pos = 0;
while ((pos = rx.indexIn(str, pos)) != -1) //偏移量pos
{
	list << rx.cap(0);              // 第一个捕获到的文本
	pos += rx.matchedLength();      // 上一个匹配的字符串的长度
}
qDebug() << list;                   // 结果:12,14,99,231,7

QRegExp和QRegExpValidator

QRegExpValidator为正则校验

验证的结果有三种状态:

enum State 
{  
    Invalid,       //验证通不过  
    Intermediate,  //输入未完成,不确定是否能通过验证  
    Acceptable     //验证通过 
}
//正则
QRegExp	mRegExp;
mRegExp.setPattern("[1-9][0-9]+$");

QRegExpValidator mRegExpV;
mRegExpV.setRegExp(mRegExp);

//判断是否可以使用正负号
qint32 pos = 0;
QString strSign = "-";
QValidator::State state = mRegExpV.validate(strSign, pos);
if(state == QValidator::Acceptable)
{

}

 正则表达式常用语法

参考原文:(51条消息) Qt正则表达式_岁小草的草窝-CSDN博客_qt正则表达式

  • 简单转义字符
表达式匹配
/r,/n代表回车和换行符
/t制表符
//代表“/”本身
/^匹配^本身
/$匹配$本身
/.匹配.本身
  • 能够与 '多种字符' 匹配的表达式

正则表达式中的一些表示方法,可以匹配 '多种字符' 其中的任意一个字符。比如,表达式 "/d" 可以匹配任意一个数字。虽然可以匹配其中任意字符,但是只能是一个,不是多个。

表达式可匹配
/d任意一个数字,0~9 中的任意一个
/w任意一个字母或数字或下划线,也就是 A~Z,a~z,0~9,_ 中任意一个
/s包括空格、制表符、换页符等空白字符的其中任意一个
.小数点可以匹配除了换行符(/n)以外的任意一个字符
  • 自定义能够匹配 '多种字符' 的表达式 

 使用方括号 [ ] 包含一系列字符,能够匹配其中任意一个字符。用 [^ ] 包含一系列字符,则能够匹配其中字符之外的任意一个字符。同样的道理,虽然可以匹配其中任意一个,但是只能是一个,不是多个。

表达式说明例子
[ ]包含一系列字符

1、[abc12]: 匹配 "a" 或 "b" 或 "c" 或 "1"或"2"

2、[a-z]:匹配a~z

3、[^A-F0-3]:匹配 "A"~"F","0"~"3" 之外的任意一个字符

[^ ]包含之外一系列字符[^abc]:包含abc之外的任意字符
  •  修饰匹配次数的特殊符号 

前面讲到的表达式,无论是只能匹配一种字符的表达式,还是可以匹配多种字符其中任意一个的表达式,都只能匹配一次。如果使用表达式再加上修饰匹配次数的特殊符号,那么不用重复书写表达式就可以重复匹配。

   使用方法:"次数修饰"放在"被修饰的表达式"后边。比如:"[bcd][bcd]" 可以写成 "[bcd]{2}"。

表达式

作用

{n}

表达式重复n次,比如:"/w{2}" 相当于 "/w/w""a{5}" 相当于 "aaaaa"

{m,n}

表达式至少重复m次,最多重复n次,比如:"ba{1,3}"可以匹配 "ba"或"baa"或"baaa"

{m,}

表达式至少重复m次,比如:"/w/d{2,}"可以匹配 "a12","_456","M12344"...

?

匹配表达式0次或者1次,相当于 {0,1},比如:"a[cd]?"可以匹配 "a","ac","ad"

+

表达式至少出现1次,相当于 {1,},比如:"a+b"可以匹配 "ab","aab","aaab"...

*

表达式不出现或出现任意次,相当于 {0,},比如:"/^*b"可以匹配 "b","^^^b"...

 举例1:表达式 "/d+/.?/d*" 在匹配 "It costs $12.5" 时,匹配的结果是:成功;匹配到的内容是:"12.5";匹配到的位置是:开始于10,结束于14。

     举例2:表达式 "go{2,8}gle" 在匹配 "Ads by goooooogle" 时,匹配的结果是:成功;匹配到的内容是:"goooooogle";匹配到的位置是:开始于7,结束于17。 

  • 其他一些代表抽象意义的特殊符号 

  一些符号在表达式中代表抽象的特殊意义:

表达式

作用

^

与字符串开始的地方匹配,不匹配任何字符

$

与字符串结束的地方匹配,不匹配任何字符

/b

匹配一个单词边界,也就是单词和空格之间的位置,不匹配任何字符

进一步的文字说明仍然比较抽象,因此,举例帮助大家理解。

     举例1:表达式 "^aaa" 在匹配 "xxx aaa xxx" 时,匹配结果是:失败。因为 "^" 要求与字符串开始的地方匹配,因此,只有当 "aaa" 位于字符串的开头的时候,"^aaa" 才能匹配,比如:"aaa xxx xxx"

     举例2:表达式 "aaa$" 在匹配 "xxx aaa xxx" 时,匹配结果是:失败。因为 "$" 要求与字符串结束的地方匹配,因此,只有当 "aaa" 位于字符串的结尾的时候,"aaa$" 才能匹配,比如:"xxx xxx aaa"

     举例3:表达式 "./b." 在匹配 "@@@abc" 时,匹配结果是:成功;匹配到的内容是:"@a";匹配到的位置是:开始于2,结束于4。
     进一步说明:"/b" 与 "^" 和 "$" 类似,本身不匹配任何字符,但是它要求它在匹配结果中所处位置的左右两边,其中一边是 "/w" 范围,另一边是 非"/w" 的范围。

     举例4:表达式 "/bend/b" 在匹配 "weekend,endfor,end" 时,匹配结果是:成功;匹配到的内容是:"end";匹配到的位置是:开始于15,结束于18。

    一些符号可以影响表达式内部的子表达式之间的关系:

表达式

作用

|

左右两边表达式之间 "或" 关系,匹配左边或者右边

( )

(1). 在被修饰匹配次数的时候,括号中的表达式可以作为整体被修饰
 (2). 取匹配结果的时候,括号中的表达式匹配到的内容可以被单独得到

    举例5:表达式 "Tom|Jack" 在匹配字符串 "I'm Tom, he is Jack" 时,匹配结果是:成功;匹配到的内容是:"Tom";匹配到的位置是:开始于4,结束于7。匹配下一个时,匹配结果是:成功;匹配到的内容是:"Jack";匹配到的位置时:开始于15,结束于19。

     举例6:表达式 "(go/s*)+" 在匹配 "Let's go go go!" 时,匹配结果是:成功;匹配到内容是:"go go go";匹配到的位置是:开始于6,结束于14。

     举例7:表达式 "¥(/d+/.?/d*)" 在匹配 "$10.9,¥20.5" 时,匹配的结果是:成功;匹配到的内容是:"¥20.5";匹配到的位置是:开始于6,结束于10。单独获取括号范围匹配到的内容是:"20.5"。

  • 匹配次数中的贪婪与非贪婪

贪婪与非贪婪模式影响的是被量词修饰的子表达式的匹配行为。

贪婪模式在整个表达式匹配成功的前提下,尽可能多的匹配。

非贪婪模式在整个表达式匹配成功的前提下,尽可能少的匹配。

内容aa<div>test1</div>bb<div>test2</div>cc
贪婪表达式<div>.*</div>
贪婪匹配<div>test1</div>bb<div>test2</div>
非贪婪表达式<div>.*?</div>
非贪婪匹配<div>test1</div>

Lambda表达式 

 一个lambda表达式表示一个可调用的代码单元。我们可以将其理解为一个未命名的内联函数。与任何函数类似,一个lambda具有一个返回类型、一个参数列表和一个函数,但与函数不同,lambda可能定义在函数内部。一个lambda表达式具有如下形式:

[capture list](parameter list)->return type {function body}

capture list:捕获列表,是一个lambda所在函数中定义的局部变量的列表(通常为空);通过捕获列表访问一些上下文中的数据。具体地,捕捉列表描述了上下文中哪些数据可以被Lambda使用,以及使用方式(以值传递的方式或引用传递的方式)。语法上,在“[]”包括起来的是捕捉列表,捕捉列表由多个捕捉项组成,并以逗号分隔。捕捉列表有以下几种形式:

  1. [var]表示值传递方式捕捉变量var;
  2. [=]表示值传递方式捕捉所有父作用域的变量(包括this);父作用域,也就是包含Lambda函数的语句块,说通俗点就是包含Lambda的“{}”代码块。
  3. [&var]表示引用传递捕捉变量var;
  4. [&]表示引用传递方式捕捉所有父作用域的变量(包括this);
  5. [this]表示值传递方式捕捉当前的this指针。

上面的捕捉列表还可以进行组合,例如:

  1. [=,&a,&b]表示以引用传递的方式捕捉变量a和b,以值传递方式捕捉其它所有变量;
  2. [&,a,this]表示以值传递的方式捕捉变量a和this,引用传递方式捕捉其它所有变量。

不过值得注意的是,捕捉列表不允许变量重复传递。下面一些例子就是典型的重复,会导致编译时期的错误。例如:

  1. [=,a]这里已经以值传递方式捕捉了所有变量,但是重复捕捉a了,会报错的;
  2. [&,&this]这里&已经以引用传递方式捕捉了所有变量,再捕捉this也是一种重复。

parameter list、return type和function body与任何普通函数一样,分别表示参数列表、返回类型和函数体

QString str1("first use lambda expression");
QString str2("second use lambda expression");
connect(ui->pushButton, &QPushButton::clicked, [=](bool checked)
{
    qDebug() << "str1=" << str1 << endl;
    qDebug() << "checked=" << checked <<endl;
});
 
connect(ui->pushButton_2, &QPushButton::clicked, [=]()
{
    qDebug() << "str2=" << str2 << endl;
});
QTimer::singleShot(0, this, [&](){
        startAnimation();       
    });

 Qt常用函数

1、设置控件大小

void resize(int w, int h); //给控件设置大小
void resize(const QSize &);

2、控件

方法一:setVisible(false);
方法二:hide();//实质调用setVisible(false)

3、foreach

       foreach虽然是for循环的简化版本,但是并不是说foreach就比for更好用,foreach适用于循环次数未知,或者计算循环次数比较麻烦情况下使用效率更高,但是更为复杂的一些循环还是需要用到for循环效率更高。

QStringList slt = {"abc", "qwe", "upo"};
foreach(QString s,slt)
{
    cout<<s<<endl;
}
// 输出结果为:abc qwe upo

4、Q_UNUSED

     Q_UNUSED()消除未用变量警告

int ret;
Q_UNUSED(ret) //消除未用变量警告(如果ret在函数中未使用将会报警)

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值