一、编码问题
在计算机编程中,流 (Stream )是一种抽象的概念,用于表示数据的输入或输出。根据处理数据的不同方式,流 可以分为字节流 (Byte Stream )和字符流 (Character Stream)两大类。
1. 字节流(Byte Stream)
字节流是处理数据的基本单位是字节(8位二进制数据)的流。它不考虑数据的具体内容,只是简单地将数据视为字节序列进行读取或写入。字节流主要用于处理二进制数据,例如图片、视频、音频文件等,或者当你不确定数据的内容时(比如,当你正在读取一个可能是文本也可能是其他格式的文件时)。
Qt 通过QByteArray为我们提供了一个字节数组容器(它是可变长的)。主要用来存储原始的字节流。
QByteArray 仍可以表示字符串,类似与unsigned char buf[ ] ,但是QT 中多用QString来表示字符串。
QByteArray 一般结合其它类使用,比如QIODevice 类的QByteArray QIODevice::readAll()
cpp
QByteArray(const char *data, int size = -1) //构造
char at(int i) const //返回第i个元素
void clear() //清空
bool contains(const char * str) const //是否包含字符串 ==strstr
bool contains(char ch) const //是否包含字符
char *data() //从QByteArray类型转化为char *
//查找从from开始第一次匹配str的位置
int indexOf(const char *str, int from = 0)
QByteArray & append(char ch) //尾部增加
QByteArray & prepend(char ch) //头部增加
int length() const //返回长度
QByteArray &remove(int pos, int len) //删除
2.字符流(Character Stream)
1) 概念
字符流则是处理数据的基本单位是字符。字符流主要用于处理文本数据。QT通过QString类为我们提供了字符串类。
2) QString相关方法
cpp
//数字转字符串, QString静态方法
QString number(long n, int base = 10) [static]
QString number(double n, char format = 'g', int precision = 6) [static]
例:
cpp
long a = 63;
QString s = QString::number(a, 10); // s == "63"
QString t = QString::number(a, 16).toUpper(); // t == "3F"
cpp
//字符串转为数字
short toShort(bool * ok = 0, int base = 10) const
float toFloat(bool * ok = 0) const
long toLong(bool * ok = 0, int base = 10) const
例:
cpp
QString str1 = "1234.56";
str1.toFloat(); // returns 1234.56
cpp
//从 QString 转化到 QByteArray
QByteArray toLocal8Bit() const //转成本地编码的字节流
QByteArray toUtf8() const//转成utf8编码的字节流
例: 把一个QString 转为char *(QString没有直接转换为char *的方法,需要中间经过一层QByteArray的过渡)
cpp
QString str1 = "hello";
char *s = str1.toLocal8Bit().data();
总结:字符流一定是字节流, 而字节流不一定是字符流。
**问题:**有时候数据写入文件,打开文件会出现乱码的情况,这是为什么,如何解决呢?
**答:**这个是涉及到编码的问题, 如果字节流的编码是UTF8, 而文件中按照GBK编码解析,可能就是乱码。
二、字符集
字符编码的集合
三、字符编码
1. 概念
我们已经讲过了,字符串也是一种数据类型,但是,字符串比较特殊的是还有一个编码问题。
因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。最早的计算机在设计时采用8 个比特(bit )作为一个字节(byte ),所以,一个字节能表示的最大的整数就是255 (二进制11111111 = 十进制255),如果要表示更大的整数,就必须用更多的字节。比如两个字节可以表示的最大整数是65535 ,4个字节可以表示的最大整数是4294967295 。
由于计算机是美国人发明的,因此,最早只有127个字符被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII 编码,比如大写字母A 的编码是65 ,小写字母z 的编码是122 。
但是要处理中文显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了GB2312 编码,用来把中文编进去。
你可以想得到的是,全世界有上百种语言,日本把日文编到Shift_JIS 里,韩国把韩文编到Euc-kr 里,各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。
因此,Unicode 应运而生。Unicode 把所有语言都统一到一套编码里,这样就不会再有乱码问题了。本着节约的精神,又出现了把Unicode编码转化为"可变长编码"的UTF-8 编码。UTF-8 编码把一个Unicode 字符根据不同的数字大小编码成1-6 个字节,常用的英文字母被编码成1 个字节,汉字通常是3 个字节,只有很生僻的字符才会被编码成4-6 个字节。如果你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间。
注意:QT界面的编码是UTF8的
2. QString转为字节流相关的方法
cpp
QByteArray toLocal8Bit() //把QString按照本地编码转换成QByteArray
QByteArray toUtf8() //把QString按照utf-8编码转换成QByteArray
QString fromLocal8Bit(const QByteArray & str) //将QByteArray按照本地编码转换为QString
QString fromUtf8(const QByteArray & str) //将QByteArray按照utf-8编码转换为QString
四、其他容器
QList
QVector
QMap
... ...
五、定时器
QTime: 时间类
QTimer: 定时器类
QDateTime: 日期时间类
1. 常用方法
cpp
//parent可以传界面的指针,但是没有父子窗体关系,作用仅剩内存管理
QTimer(QObject * parent = 0)
void start(int msec) //参数为ms 定时一次,永久生效
void stop() //停止定时器
Signal:
Void timeout();
//一次定时
void QTimer::singleShot(int msec, const QObject * receiver, const char * member) [static]
receiver:接收者
member:执行函数
1s=1000ms 1ms=1000us 1us=1000ns
每次时间到达后会发出 timeout() 信号
2. 编程步骤
cpp
QTimer *timer = new QTimer(this); //实例化
connect(timer, SIGNAL(timeout()), this, SLOT(update())); //绑定超时处理函数
timer->start(1000);
...
timer->stop(); //停止定时器
**实例需求:**做一个时间显示Label,增加"开始"和"结束"按钮,点击开始,lab每1s记录一次时间。(或者通过数码管显示)
QTime:可以获取到系统当前时间
QDateTime:获取系统时间和日期
提示:时间处理用QDateTime
cpp
QDateTime time = QDateTime::currentDateTime();//获取系统现在的时间
QString str = time.toString("yyyy-MM-dd hh:mm:ss dddd");//设置系统时间显示格式
代码:
mytime.h
cpp
#ifndef MYTIME_H
#define MYTIME_H
#include <QObject>
#include <QDebug>
#include <QThread>
#include <QDateTime>
class MyTime : public QObject
{
Q_OBJECT
public:
explicit MyTime(QObject *parent = nullptr);
void thread_time();
void set_flag(int flag);
signals:
void my_signal(QString datatime);
public slots:
private:
int flag = 1;
};
#endif // MYTIME_H
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QDebug>
#include <QThread>
#include "mytime.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
private:
Ui::Widget *ui;
QThread *thread;
MyTime *time;
};
#endif // WIDGET_H
mytime.cpp
cpp
#include "mytime.h"
MyTime::MyTime(QObject *parent) : QObject(parent)
{
}
void MyTime::thread_time()
{
while(flag){
QDateTime currentTime = QDateTime::currentDateTime();
QString formattedTime = currentTime.toString("hh:mm:ss");
qDebug() << formattedTime;
emit my_signal(formattedTime);
QThread::sleep(1);
}
}
void MyTime::set_flag(int flag)
{
this->flag = flag;
}
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
thread = new QThread(this);
time = new MyTime; // 不能指定父对象
ui->lcdNumber->setDigitCount(8);
time->moveToThread(thread); // 移动到thread线程中
connect(thread,&QThread::started,time,&MyTime::thread_time);
connect(this,&Widget::destroyed,this,&Widget::on_pushButton_2_clicked); // 直接点击 X 也能销毁线程
connect(time,&MyTime::my_signal,this,[=](QString datatime){ // 只能在主线程对界面进行操作
this->ui->lcdNumber->display(datatime);
});
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
thread->start();
}
void Widget::on_pushButton_2_clicked()
{
thread->quit();
time->set_flag(0);
thread->wait();
}
输出:
六、事件
由窗口系统或QT自身以及外部外设产生的动作,称为事件。
比如当按下鼠标或释放鼠标时,会产生鼠标事件,按下键盘时,出现按键事件。
当某个事件发生时,某个控件要做出响应实现具体业务逻辑,这个操作可以在对应的事件处理函数中实现。
捕捉并处理一个事件的步骤
总目标:所有的事件处理都是通过重写某个事件方法来实现的,可按照如下步骤实现
**1.**从逻辑角度判断事件类型,比如你要处理鼠标动作,那肯定是鼠标事件
2. 查找QEvent类的帮助文档,找到enum QEvent::Type中对应的事件,比如QEvent::KeyPress
3. 根据帮助文档,找到相应的事件属于的类,比如QKeyEvent
4. 通过帮助文档的详细介绍部分,找到应该重写的方法,比如QWidget::keyPressEvent()
5. 分析是否可以直接重写事件方法(看能不能修改类的源代码),如果不能,自定义一个类,继承于某个控件类,然后在自定义类中重写其事件方法。
6. void QWidget::keyPressEvent(QKeyEvent * event),根据具体的需求,通过参数event所属的类,具体分析需要获取的数据,然后进行业务逻辑处理
**7.**不需要的事件,交由父类做默认处理(如调用父类的keyPressEvent()方法
案例需求:
**1、**完善原登录模块。要求用户输入完后按回车键触发登录事件,ESC键退出登录界面
**2、**界面中添加一个单行输入框,当输入字符为某些特殊字符时候,提示用户并禁止输入。
cpp
提示小技巧:QToolTip::showText(this->mapToGlobal(this->pos()), "不能使用特殊字符", this);
代码:
myline.h
cpp
#ifndef MYLINE_H
#define MYLINE_H
#include <QWidget>
#include <QtWidgets>
class myline : public QLineEdit
{
Q_OBJECT
public:
explicit myline(QWidget *parent = nullptr);
void keyPressEvent(QKeyEvent *event);
signals:
public slots:
};
#endif // MYLINE_H
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QtWidgets>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_exit_button_clicked();
private:
Ui::Widget *ui;
void keyPressEvent(QKeyEvent *event);
};
#endif // WIDGET_H
myline.cpp
cpp
#include "myline.h"
myline::myline(QWidget *parent) : QLineEdit (parent)
{
}
void myline::keyPressEvent(QKeyEvent *event)
{
qDebug("mylineedit: %x",event->key());
if(event->key() == Qt::Key_Slash)
QToolTip::showText(this->mapToGlobal(this->pos()), "不能使用特殊字符", this);
else{
QLineEdit::keyPressEvent(event);
}
}
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include "myline.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::keyPressEvent(QKeyEvent *event)
{
qDebug("%x",event->key());
if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter){ // <CR>
QString name = ui->name_edit->text();
QString passwd = ui->passwd_edit->text();
if(name == "wanghan" && passwd == "123456"){
qDebug()<<"ok"<<endl;
}
}
if(event->key() == Qt::Key_Escape){ // esc
this->close();
}
}
void Widget::on_exit_button_clicked()
{
this->close();
}
输出:
起始页面
输入特殊字符时
七、绘图事件
cpp
void QWidget::paintEvent(QPaintEvent *event);
**注意:**绘图事件不能直接调用, 当窗口发生变化时,QT内部会自动调用绘图事件,或者调用update方法,也会间接调用。
绘图相关的类
cpp
//构造方法, 设置绘制设备
QPainter(QPaintDevice *device)
//设置画笔
void setPen(const QPen &pen)
//设置画刷
void setBrush(const QBrush &brush)
//绘制直线
void drawLine(int x1, int y1, int x2, int y2)
//绘制矩形
void drawRect(const QRectF &rectangle)
void drawRect(int x, int y, int width, int height)
//绘制图片 x,y相对绘制设备的原点, 一般直接传0, 0
void drawPixmap(int x, int y, const QPixmap &pixmap)
void drawPixmap(const QRect &rectangle, const QPixmap &pixmap)
void drawPixmap(int x, int y, int width, int height, const QPixmap &pixmap)
//绘制圆形 或者椭圆
void drawEllipse(int x, int y, int width, int height)
//绘制文本
void drawText(int x, int y, const QString &text)
//画笔构造方法
QPen(const QColor &color)
QPen(Qt::PenStyle style)
void setWidth(int width)//设置画刷的宽度
//画刷构造方法
QBrush(const QColor &color, Qt::BrushStyle style = Qt::SolidPattern)
QBrush(Qt::BrushStyle style)