zoukankan      html  css  js  c++  java
  • QT学习笔记——06-Day16_C++_QT

    在学习QT总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

    06-Day16_C++_QT

    目录:
    一、常用控件
    1、自定义控件封装
    二、Qt消息机制和事件
    1、QLabel中鼠标常用事件
    2、定时器第一种方式
    3、定时器第二种方式
    4、event事件
    5、事件过滤器
    三、绘图和绘图设备
    1、QPainter基本绘图
    2、绘图高级设置
    3、手动调用绘图事件
    4、绘图设备
    四、文件
    1、文件读写操作
    2、文件信息
    3、文本流和数据流
    五、总结

    一、常用控件

    在搭建Qt窗口界面的时候,在一个项目中很多窗口,或者是窗口中的某个模块会被经常性的重复使用。一般遇到这种情况我们都会将这个窗口或者模块拿出来做成一个独立的窗口类,以备以后重复使用。
    在使用Qt的ui文件搭建界面的时候,工具栏栏中只为我们提供了标准的窗口控件,如果我们想使用自定义控件怎么办?


    例如:可以从QWidget派生出一个类SmallWidget,实现了一个自定窗口,

    1、自定义控件封装

    界面设置如下:

    新建工程:选择“QWidget”,勾选“显示主界面”(默认勾选)

    1)在工程处右键“添加新文件”,选择“QT”—“设计师界面类”——>“choose”,选择界面模板“Widget”,选择类名,输入:SmallWidget。然后在smallwidget.ui中添加“Spin Box”和“Horizontal Slider”,并将其更改为合适大小。

    2)在widget.ui中拖拽Widget,右键提升为——>类名写入(SmallWidget)—勾选:“全局包含”—点击:添加—提升

    3)添加两个按钮“PushButton”,并更改名字

    4)对外接口:setValue和getValue

    在smallwidget.cpp中更改

     1 #include "smallwidget.h"
     2 #include "ui_smallwidget.h"
     3 
     4 SmallWidget::SmallWidget(QWidget *parent) :
     5     QWidget(parent),
     6     ui(new Ui::SmallWidget)
     7 {
     8     ui->setupUi(this);
     9 
    10     //QSpinBox移动,Slider跟着移动 发生了重载,函数指向:返回值(作用域)参数类型
    11     void(QSpinBox:: * signal)(int) = &QSpinBox::valueChanged;
    12     connect(ui->spinBox, signal, ui->horizontalSlider,&QSlider::setValue);
    13 
    14     //Slider移动,SpinBox跟着移动
    15     connect(ui->horizontalSlider, &QSlider::valueChanged,ui->spinBox,&QSpinBox::setValue);
    16 
    17 
    18 
    19 }
    20 
    21 void SmallWidget::setValue(int v)
    22 {
    23     ui->spinBox->setValue(v);
    24 }
    25 
    26 int SmallWidget::getValue()
    27 {
    28     return ui->spinBox->value();
    29 }
    30 
    31 
    32 SmallWidget::~SmallWidget()
    33 {
    34     delete ui;
    35 }

    在smallwidget.h中更改

     1 #ifndef SMALLWIDGET_H
     2 #define SMALLWIDGET_H
     3 
     4 #include <QWidget>
     5 
     6 namespace Ui {
     7 class SmallWidget;
     8 }
     9 
    10 class SmallWidget : public QWidget
    11 {
    12     Q_OBJECT
    13 
    14 public:
    15     explicit SmallWidget(QWidget *parent = 0);
    16     ~SmallWidget();
    17 
    18     void setValue(int v);
    19 
    20     int getValue();
    21 
    22 
    23 private:
    24     Ui::SmallWidget *ui;
    25 };
    26 
    27 #endif // SMALLWIDGET_H

    在widget.cpp中更改

     1 #include "widget.h"
     2 #include "ui_widget.h"
     3 #include<QDebug>
     4 
     5 Widget::Widget(QWidget *parent) :
     6     QWidget(parent),
     7     ui(new Ui::Widget)
     8 {
     9     ui->setupUi(this);
    10 
    11     //点击设置,设置到一半的位置
    12     connect(ui->btnSet,&QPushButton::clicked,[=](){
    13         ui->widget->setValue(50);
    14     });
    15 
    16     //点击获取,拿到当前值
    17     connect(ui->btnGet,&QPushButton::clicked,[=](){
    18         qDebug() << "当前值为:" << ui->widget->getValue();
    19     });
    20 
    21 }
    22 
    23 Widget::~Widget()
    24 {
    25     delete ui;
    26 }

    二、Qt消息机制和事件


    事件
    事件(event)是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件在对用户操作做出响应时发出,如键盘事件等;另一些事件则是由系统自动发出,如计时器事件。
    在前面我们也曾经简单提到,Qt 程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始 Qt 的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(event handler),关于这一点,会在后边详细说明。
    在所有组件的父类QWidget中,定义了很多事件处理的回调函数,如
    》 keyPressEvent()
    》 keyReleaseEvent()
    》 mouseDoubleClickEvent()
    》 mouseMoveEvent()
    》 mousePressEvent()
    》 mouseReleaseEvent() 等。
    这些函数都是 protected virtual 的,也就是说,我们可以在子类中重新实现这些函数。

    》EventLabel继承了QLabel,覆盖了mousePressEvent()、mouseMoveEvent()和MouseReleaseEvent()三个函数。我们并没有添加什么功能,只是在鼠标按下(press)、鼠标移动(move)和鼠标释放(release)的时候,把当前鼠标的坐标值显示在这个Label上面。由于QLabel是支持 HTML 代码的,因此我们直接使用了 HTML 代码来格式化文字。
    》QString的arg()函数可以自动替换掉QString中出现的占位符。其占位符以 % 开始,后面是占位符的位置,例如 %1,%2 这种。
    QString("[%1, %2]").arg(x).arg(y);
    语句将会使用x替换 %1,y替换 %2,因此,生成的QString为[x, y]。
    》在mouseReleaseEvent()函数中,我们使用了另外一种QString的构造方法。我们使用类似 C 风格的格式化函数sprintf()来构造QString。
    运行上面的代码,当我们点击了一下鼠标之后,label 上将显示鼠标当前坐标值。

    为什么要点击鼠标之后才能在mouseMoveEvent()函数中显示鼠标坐标值?
    这是因为QWidget中有一个mouseTracking属性,该属性用于设置是否追踪鼠标。只有鼠标被追踪时,mouseMoveEvent()才会发出。如果mouseTracking是 false(默认即是),组件在至少一次鼠标点击之后,才能够被追踪,也就是能够发出mouseMoveEvent()事件。如果mouseTracking为 true,则mouseMoveEvent()直接可以被发出。


    1、QLabel中鼠标常用事件

    新建工程:选择“QWidget”,勾选“显示主界面”(默认勾选)

    1)然后在widget.ui中添加“Label”
    2)在工程处右键“添加新文件”,选择“C++”—“C++ Class”——>“choose”,输入类名:MyLabel,选择继承于“QWidget”,。
    3)更改3处QLabel,mylabel.h中(更改头文件#include <QLabel>和class MyLabel : public QLabel);在mylabel.cpp(更改MyLabel::MyLabel(QWidget *parent) : QLabel(parent))
    4)在widget.ui,在“Label”右键提升为——>基类名称,选择“QLabel”;提升的类名称,写入:MyLabel—勾选:“全局包含”(选不选都行)—点击:添加—提升(右上角QLabel更改为MyLabel)

    在mylabel.cpp中更改

    Qt5.5乱码问题?qDebug() << str更改为qDebug() << str.toUtf8().data(); QString转char*:先转成QByteArray类型,再转char*

     1 #ifndef MYLABEL_H
     2 #define MYLABEL_H
     3 
     4 #include <QLabel>
     5 
     6 class MyLabel : public QLabel
     7 {
     8     Q_OBJECT
     9 public:
    10     explicit MyLabel(QWidget *parent = 0);
    11 
    12    //捕获事件
    13     //鼠标进入
    14     void enterEvent(QEvent *);
    15 
    16     //鼠标离开
    17     void leaveEvent(QEvent *);
    18 
    19 
    20     //鼠标按下
    21      void mousePressEvent(QMouseEvent * ev);
    22      //鼠标释放
    23      void mouseReleaseEvent(QMouseEvent * ev);
    24      //鼠标移动
    25      void mouseMoveEvent(QMouseEvent * ev);
    26 };
    27 
    28 #endif // MYLABEL_H

    在mylabel.h中更改

     1 #include "mylabel.h"
     2 #include<QDebug>
     3 #include<QMouseEvent>
     4 
     5 MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
     6 {
     7     //设置鼠标追踪
     8     this->setMouseTracking(true);
     9 }
    10 
    11 //鼠标进入
    12 void MyLabel::enterEvent(QEvent *)
    13 {
    14     //qDebug() << "鼠标进入了!!!";
    15 }
    16 //鼠标离开
    17 void MyLabel::leaveEvent(QEvent *)
    18 {
    19      //qDebug() << "鼠标离开了!!!";
    20 }
    21 
    22 //鼠标按下
    23  void MyLabel::mousePressEvent(QMouseEvent * ev)
    24  {
    25      //如果鼠标案子啊的是左键,然后提示内容
    26      //找按下的位置
    27      if(ev->button() == Qt::LeftButton)
    28      {
    29         QString str = QString("鼠标按下了!!! x = %1 y = %2").arg(ev->x()).arg(ev->y());
    30 
    31 
    32         qDebug() << str.toUtf8().data();
    33      }
    34  }
    35  //鼠标释放
    36  void MyLabel::mouseReleaseEvent(QMouseEvent * ev)
    37  {
    38      if(ev->button() == Qt::LeftButton)
    39      {  
          //QString拼接字符串
    40 QString str = QString("鼠标释放了!!! x = %1 y = %2").arg(ev->x()).arg(ev->y()); 41 42 qDebug() << str.toUtf8().data(); 43 } 44 } 45 //鼠标移动 46 void MyLabel::mouseMoveEvent(QMouseEvent * ev) 47 { 48 //持续状态,需要用buttons,用&与操作符进行判断 49 // if(ev->buttons() & Qt::LeftButton) 50 // { 51 QString str = QString("鼠标移动了!!! x = %1 y = %2").arg(ev->x()).arg(ev->y()); 52 53 qDebug() << str.toUtf8().data(); 54 // } 55 56 }

    2、定时器第一种方式

    1)timerEvent事件

    2)启动定时器,startTimer(毫秒),返回值就是Id号

    3)区分定时器 timerId

    在widget.ui中添加两个“Label”,

    在widget.h中更改

     1 #ifndef WIDGET_H
     2 #define WIDGET_H
     3 
     4 #include <QWidget>
     5 
     6 namespace Ui {
     7 class Widget;
     8 }
     9 
    10 class Widget : public QWidget
    11 {
    12     Q_OBJECT
    13 
    14 public:
    15     explicit Widget(QWidget *parent = 0);
    16     ~Widget();
    17 
    18 private:
    19     Ui::Widget *ui;
    20 
    21     //定时器事件
    22     void timerEvent(QTimerEvent *);
    23 
    24     //定时器标识号
    25     int id1;
    26     int id2;
    27 
    28 };
    29 
    30 #endif // WIDGET_H

    在widget.cpp中更改

     1 #include "widget.h"
     2 #include "ui_widget.h"
     3 #include<QTimerEvent>
     4 
     5 Widget::Widget(QWidget *parent) :
     6     QWidget(parent),
     7     ui(new Ui::Widget)
     8 {
     9     ui->setupUi(this);
    10 
    11     //启动定时器
    12     id1 = startTimer(1000);//每1000毫秒调用一次timeEvent
    13     id2 = startTimer(2000);
    14 }
    15 
    16 void Widget::timerEvent(QTimerEvent * e)
    17 {
    18     if(e->timerId() == id1)
    19     {
    20         static int num = 0;
    21         ui->label_2->setText(QString::number(num++));
    22     }
    23     if(e->timerId() == id2)
    24     {
    25         static int num2 = 0;
    26         ui->label_3->setText(QString::number(num2++));
    27     }
    28 
    29 }
    30 
    31 Widget::~Widget()
    32 {
    33     delete ui;
    34 }

    3、定时器第二种方式

    1)QTimer头文件

    2)创建 QTimer* timer

    3)启动定时器 timer->start(毫秒)

    4)发送信号:timeout

    5)暂停stop

    在widget.ui中添加1个“Label”,一个“Push Button”

    在widget.cpp中更改

     1 #include "widget.h"
     2 #include "ui_widget.h"
     3 #include<QTimerEvent>
     4 #include<QTimer>
     5 
     6 Widget::Widget(QWidget *parent) :
     7     QWidget(parent),
     8     ui(new Ui::Widget)
     9 {
    10     ui->setupUi(this);
    11 
    12     //启动定时器
    13     id1 = startTimer(1000);//每1000毫秒调用一次timeEvent
    14     id2 = startTimer(2000);
    15 
    16     //定时器第二种方式
    17     QTimer* timer1 = new QTimer(this);
    18     //启动定时器对象
    19     timer1->start(500);//毫秒做单位
    20     //每隔0.5秒发送信号
    21     connect(timer1, &QTimer::timeout,[=](){
    22         static int num = 0;
    23         ui->label_4->setText(QString::number(num++));
    24     });
    25 
    26     //点击按钮,暂停定时器
    27     connect(ui->pushButton,&QPushButton::clicked,[=](){
    28         timer1->stop();
    29     });
    30 
    31 }
    32 
    33 void Widget::timerEvent(QTimerEvent * e)
    34 {
    35     if(e->timerId() == id1)
    36     {
    37         static int num = 0;
    38         ui->label_2->setText(QString::number(num++));
    39     }
    40     if(e->timerId() == id2)
    41     {
    42         static int num2 = 0;
    43         ui->label_3->setText(QString::number(num2++));
    44     }
    45 
    46 }
    47 
    48 Widget::~Widget()
    49 {
    50     delete ui;
    51 }

    4、event事件


    事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。

    如上所述,event()函数主要用于事件的分发所以,如果你希望在事件分发之前做一些操作,就可以重写这个event()函数了。例如,我们希望在一个QWidget组件中监听 tab 键的按下,那么就可以继承QWidget,并重写它的event()函数,来达到这个目的。

    也就是说,event()函数中实际是通过事件处理器来响应一个具体的事件。这相当于event()函数将具体事件的处理“委托”给具体的事件处理器。而这些事件处理器是 protected virtual 的,因此,我们重写了某一个事件处理器,即可让 Qt 调用我们自己实现的版本。

    由此可以见,event()是一个集中处理不同类型的事件的地方。如果你不想重写一大堆事件处理器,就可以重写这个event()函数,通过QEvent::type()判断不同的事件。鉴于重写event()函数需要十分小心注意父类的同名函数的调用,一不留神就可能出现问题,所以一般还是建议只重写事件处理器(当然,也必须记得是不是应该调用父类的同名处理器)。这其实暗示了event()函数的另外一个作用:屏蔽掉某些不需要的事件处理器。


    1)主要功能:事件的分发

    2)bool event(QEvent * e)

    3)返回值如果是true,代表用户自己处理

    4)false系统处理,最好抛给父类去处理

    5)static_cast<转换类型>(原对象)

    6)e-type 具体事件

    在mylabel.h中更改

     1 #ifndef MYLABEL_H
     2 #define MYLABEL_H
     3 
     4 #include <QLabel>
     5 
     6 class MyLabel : public QLabel
     7 {
     8     Q_OBJECT
     9 public:
    10     explicit MyLabel(QWidget *parent = 0);
    11 
    12    //捕获事件
    13     //鼠标进入
    14     void enterEvent(QEvent *);
    15 
    16     //鼠标离开
    17     void leaveEvent(QEvent *);
    18 
    19 
    20     //鼠标按下
    21      void mousePressEvent(QMouseEvent * ev);
    22      //鼠标释放
    23      void mouseReleaseEvent(QMouseEvent * ev);
    24      //鼠标移动
    25      void mouseMoveEvent(QMouseEvent * ev);
    26 
    27      //事件分发,Event事件
    28      bool event(QEvent *e);
    29 
    30 };
    31 
    32 #endif // MYLABEL_H

    在mylabel.cpp中更改

     1 #include "mylabel.h"
     2 #include<QDebug>
     3 #include<QMouseEvent>
     4 
     5 MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
     6 {
     7     //设置鼠标追踪
     8     //this->setMouseTracking(true);
     9 }
    10 
    11 //鼠标进入
    12 void MyLabel::enterEvent(QEvent *)
    13 {
    14     //qDebug() << "鼠标进入了!!!";
    15 }
    16 //鼠标离开
    17 void MyLabel::leaveEvent(QEvent *)
    18 {
    19      //qDebug() << "鼠标离开了!!!";
    20 }
    21 
    22 //鼠标按下
    23  void MyLabel::mousePressEvent(QMouseEvent * ev)
    24  {
    25      //如果鼠标案子啊的是左键,然后提示内容
    26      //找按下的位置
    27 //     if(ev->button() == Qt::LeftButton)
    28 //     {
    29         QString str = QString("鼠标按下了!!! x = %1 y = %2").arg(ev->x()).arg(ev->y());
    30 
    31 
    32         qDebug() << str.toUtf8().data();
    33 //     }
    34  }
    35  //鼠标释放
    36  void MyLabel::mouseReleaseEvent(QMouseEvent * ev)
    37  {
    38 //     if(ev->button() == Qt::LeftButton)
    39 //     {
    40         QString str = QString("鼠标释放了!!! x = %1 y = %2").arg(ev->x()).arg(ev->y());
    41 
    42         qDebug() << str.toUtf8().data();
    43 //     }
    44  }
    45  //鼠标移动
    46  void MyLabel::mouseMoveEvent(QMouseEvent * ev)
    47  {
    48      //持续状态,需要用buttons,用&与操作符进行判断
    49 //     if(ev->buttons() & Qt::LeftButton)
    50 //     {
    51         QString str = QString("鼠标移动了!!! x = %1 y = %2").arg(ev->x()).arg(ev->y());
    52 
    53         qDebug() << str.toUtf8().data();
    54 //     }
    55 
    56  }
    57 
    58  bool MyLabel::event(QEvent *e)
    59  {
    60      //通常不会做拦截,event只要分发事件就可以了
    61      if(e->type() == QEvent::MouseButtonPress)
    62      {
    63         QMouseEvent* ev = static_cast<QMouseEvent*>(e);
    64 
    65          QString str = QString("Event:::鼠标按下了!!! x = %1 y = %2").arg(ev->x()).arg(ev->y());
    66 
    67          qDebug() << str.toUtf8().data();
    68 
    69          //只有鼠标按下,自己处理,返回true
    70          return true;
    71      }
    72 
    73      //其他事件,让父亲做默认处理
    74 
    75      return QLabel::event(e);
    76 
    77  }

    5、事件过滤器


    有时候,对象需要查看、甚至要拦截发送到另外对象的事件。例如,对话框可能想要拦截按键事件,不让别的组件接收到;或者要修改回车键的默认处理。

    通过前面的章节,我们已经知道,Qt 创建了QEvent事件对象之后,会调用QObject的event()函数处理事件的分发。显然,我们可以在event()函数中实现拦截的操作。由于event()函数是 protected 的,因此,需要继承已有类。如果组件很多,就需要重写很多个event()函数。这当然相当麻烦,更不用说重写event()函数还得小心一堆问题。好在 Qt 提供了另外一种机制来达到这一目的:事件过滤器。

    QObject有一个eventFilter()函数,用于建立事件过滤器。函数原型如下:

    virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );

    这个函数正如其名字显示的那样,是一个“事件过滤器”。所谓事件过滤器,可以理解成一种过滤代码。事件过滤器会检查接收到的事件。如果这个事件是我们感兴趣的类型,就进行我们自己的处理;如果不是,就继续转发。这个函数返回一个 bool 类型,如果你想将参数 event 过滤出来,比如,不想让它继续转发,就返回 true,否则返回 false事件过滤器的调用时间是目标对象(也就是参数里面的watched对象)接收到事件对象之前。也就是说,如果你在事件过滤器中停止了某个事件,那么,watched对象以及以后所有的事件过滤器根本不会知道这么一个事件。

    注意,

    事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。


    event事件解析图

    1)哪个控件需要过滤事件,就给哪个控件安装过滤器

    2)步骤1:安装过滤器

    3)ui->label->installEventFilter(this);

    4)步骤2:重写事件

    在“widget.h”中更改

     1 #ifndef WIDGET_H
     2 #define WIDGET_H
     3 
     4 #include <QWidget>
     5 
     6 namespace Ui {
     7 class Widget;
     8 }
     9 
    10 class Widget : public QWidget
    11 {
    12     Q_OBJECT
    13 
    14 public:
    15     explicit Widget(QWidget *parent = 0);
    16     ~Widget();
    17 
    18 private:
    19     Ui::Widget *ui;
    20 
    21     //定时器事件
    22     void timerEvent(QTimerEvent *);
    23 
    24     //定时器标识号
    25     int id1;
    26     int id2;
    27 
    28     //事件过滤器的事件
    29     bool eventFilter(QObject *, QEvent *);
    30 
    31 };
    32 
    33 #endif // WIDGET_H

    在“widget.cpp”中更改

     1 #include "widget.h"
     2 #include "ui_widget.h"
     3 #include<QTimerEvent>
     4 #include<QTimer>
     5 #include<QMouseEvent>
     6 #include<QDebug>
     7 
     8 Widget::Widget(QWidget *parent) :
     9     QWidget(parent),
    10     ui(new Ui::Widget)
    11 {
    12     ui->setupUi(this);
    13 
    14     //启动定时器
    15     id1 = startTimer(1000);//每1000毫秒调用一次timeEvent
    16     id2 = startTimer(2000);
    17 
    18     //定时器第二种方式
    19     QTimer* timer1 = new QTimer(this);
    20     //启动定时器对象
    21     timer1->start(500);//毫秒做单位
    22     //每隔0.5秒发送信号
    23     connect(timer1, &QTimer::timeout,[=](){
    24         static int num = 0;
    25         ui->label_4->setText(QString::number(num++));
    26     });
    27 
    28     //点击按钮,暂停定时器
    29     connect(ui->pushButton,&QPushButton::clicked,[=](){
    30         timer1->stop();
    31     });
    32 
    33 
    34     //给ui->label做事件过滤器,拦截
    35     //步骤1:给控件安装过滤器
    36     //参数this通过父窗口给label安装过滤器
    37     ui->label->installEventFilter(this);
    38     //步骤2:重写eventFilter事件
    39 
    40 }
    41 
    42 bool Widget::eventFilter(QObject * obj, QEvent *e)
    43 {
    44     if(obj == ui->label)
    45     {
    46         if(e->type() == QEvent::MouseButtonPress)
    47         {
    48            QMouseEvent* ev = static_cast<QMouseEvent*>(e);
    49 
    50             QString str = QString("事件过滤器:::鼠标按下了!!! x = %1 y = %2").arg(ev->x()).arg(ev->y());
    51 
    52             qDebug() << str.toUtf8().data();
    53 
    54             //只有鼠标按下,自己处理,返回true
    55             return true;
    56         }
    57 
    58 
    59     }
    60     //其他让父类处理
    61     return QWidget::eventFilter(obj, e);
    62 
    63 }
    64 
    65 void Widget::timerEvent(QTimerEvent * e)
    66 {
    67     if(e->timerId() == id1)
    68     {
    69         static int num = 0;
    70         ui->label_2->setText(QString::number(num++));
    71     }
    72     if(e->timerId() == id2)
    73     {
    74         static int num2 = 0;
    75         ui->label_3->setText(QString::number(num2++));
    76     }
    77 
    78 }
    79 
    80 Widget::~Widget()
    81 {
    82     delete ui;
    83 }

    总结

    Qt 的事件是整个 Qt 框架的核心机制之一,也比较复杂。说它复杂,更多是因为它涉及到的函数众多,而处理方法也很多,有时候让人难以选择。现在我们简单总结一下 Qt 中的事件机制。

    Qt 中有很多种事件:鼠标事件、键盘事件、大小改变的事件、位置移动的事件等等。如何处理这些事件,实际有两种选择:

    》所有事件对应一个事件处理函数,在这个事件处理函数中用一个很大的分支语句进行选择,其代表作就是 win32 API 的WndProc()函数:

    LRESULT CALLBACK WndProc(HWND hWnd,

                             UINT message,

                             WPARAM wParam,

                             LPARAM lParam)

    在这个函数中,我们需要使用switch语句,选择message参数的类型进行处理,典型代码是:

    switch(message)

    {

        case WM_PAINT:

            // ...

            break;

        case WM_DESTROY:

            // ...

            break;

        ...

    }

    》每一种事件对应一个事件处理函数。Qt 就是使用的这么一种机制:

      mouseEvent()

      keyPressEvent()

      …

    Qt 具有这么多种事件处理函数,肯定有一个地方对其进行分发,否则,Qt 怎么知道哪一种事件调用哪一个事件处理函数呢?这个分发的函数,就是event()。显然,当QMouseEvent产生之后,event()函数将其分发给mouseEvent()事件处理器进行处理。

    event()函数会有两个问题:

    》event()函数是一个 protected 的函数,这意味着我们要想重写event(),必须继承一个已有的类。试想,我的程序根本不想要鼠标事件,程序中所有组件都不允许处理鼠标事件,是不是我得继承所有组件,一一重写其event()函数?protected 函数带来的另外一个问题是,如果我基于第三方库进行开发,而对方没有提供源代码,只有一个链接库,其它都是封装好的。我怎么去继承这种库中的组件呢?

    》event()函数的确有一定的控制,不过有时候我的需求更严格一些:我希望那些组件根本看不到这种事件。event()函数虽然可以拦截,但其实也是接收到了QMouseEvent对象。我连让它收都收不到。这样做的好处是,模拟一种系统根本没有那个事件的效果,所以其它组件根本不会收到这个事件,也就无需修改自己的事件处理函数。这种需求怎么办呢?

    这两个问题是event()函数无法处理的。于是,Qt 提供了另外一种解决方案:事件过滤器。事件过滤器给我们一种能力,让我们能够完全移除某种事件。事件过滤器可以安装到任意QObject类型上面,并且可以安装多个。如果要实现全局的事件过滤器,则可以安装到QApplication或者QCoreApplication上面。这里需要注意的是,如果使用installEventFilter()函数给一个对象安装事件过滤器,那么该事件过滤器只对该对象有效,只有这个对象的事件需要先传递给事件过滤器的eventFilter()函数进行过滤,其它对象不受影响。如果给QApplication对象安装事件过滤器,那么该过滤器对程序中的每一个对象都有效,任何对象的事件都是先传给eventFilter()函数。

    事件过滤器可以解决刚刚我们提出的event()函数的两点不足:

    》首先,事件过滤器不是 protected 的,因此我们可以向任何QObject子类安装事件过滤器;

    》其次,事件过滤器在目标对象接收到事件之前进行处理,如果我们将事件过滤掉,目标对象根本不会见到这个事件。

    事实上,还有一种方法,我们没有介绍。Qt 事件的调用最终都会追溯到QCoreApplication::notify()函数,因此,最大的控制权实际上是重写QCoreApplication::notify()。这个函数的声明是:

    virtual bool QCoreApplication::notify ( QObject * receiver,

                                            QEvent * event );

    该函数会将event发送给receiver,也就是调用receiver->event(event),其返回值就是来自receiver的事件处理器。注意,这个函数为任意线程的任意对象的任意事件调用,因此,它不存在事件过滤器的线程的问题。不过我们并不推荐这么做,因为notify()函数只有一个,而事件过滤器要灵活得多。

    现在我们可以总结一下 Qt 的事件处理,实际上是有五个层次:

    》重写paintEvent()、mousePressEvent()等事件处理函数。这是最普通、最简单的形式,同时功能也最简单。

    》重写event()函数。event()函数是所有对象的事件入口,QObject和QWidget中的实现,默认是把事件传递给特定的事件处理函数。

    》在特定对象上面安装事件过滤器。该过滤器仅过滤该对象接收到的事件。

    》在QCoreApplication::instance()上面安装事件过滤器。该过滤器将过滤所有对象的所有事件,因此和notify()函数一样强大,但是它更灵活,因为可以安装多个过滤器。全局的事件过滤器可以看到 disabled 组件上面发出的鼠标事件。全局过滤器有一个问题:只能用在主线程。

    重写QCoreApplication::notify()函数。这是最强大的,和全局事件过滤器一样提供完全控制,并且不受线程的限制。但是全局范围内只能有一个被使用(因为QCoreApplication是单例的)。


    三、绘图和绘图设备


    QPainter

    Qt 的绘图系统允许使用相同的 API 在屏幕和其它打印设备上进行绘制。整个绘图系统基于QPainter,QPainterDevice和QPaintEngine三个类。

    QPainter用来执行绘制的操作;QPaintDevice是一个二维空间的抽象,这个二维空间允许QPainter在其上面进行绘制,也就是QPainter工作的空间;QPaintEngine提供了画笔(QPainter)在不同的设备上进行绘制的统一的接口。QPaintEngine类应用于QPainter和QPaintDevice之间,通常对开发人员是透明的。除非你需要自定义一个设备,否则你是不需要关心QPaintEngine这个类的。我们可以把QPainter理解成画笔;把QPaintDevice理解成使用画笔的地方,比如纸张、屏幕等;而对于纸张、屏幕而言,肯定要使用不同的画笔绘制,为了统一使用一种画笔,我们设计了QPaintEngine类,这个类让不同的纸张、屏幕都能使用一种画笔。

    下图给出了这三个类之间的层次结构:

    上面的示意图告诉我们,Qt 的绘图系统实际上是,使用QPainter在QPainterDevice上进行绘制,它们之间使用QPaintEngine进行通讯(也就是翻译QPainter的指令)。


    1、QPainter基本绘图

    1)绘图事件paintEvent

    2)QPainter painter(绘图设备this)

    3)draw 线 圆 矩形 文字

    4)QPen 设置笔宽度、样式、让画家用 笔

    5)QBrush 设置画刷样式,让画家用画刷

    新建工程:选择“QWidget”,勾选“显示主界面”(默认勾选)

    在“widget.h”中更改

     1 #ifndef WIDGET_H
     2 #define WIDGET_H
     3 
     4 #include <QWidget>
     5 
     6 namespace Ui {
     7 class Widget;
     8 }
     9 
    10 class Widget : public QWidget
    11 {
    12     Q_OBJECT
    13 
    14 public:
    15     explicit Widget(QWidget *parent = 0);
    16     ~Widget();
    17 
    18 private:
    19     Ui::Widget *ui;
    20 
    21 public:
    22     //绘图事件
    23     void paintEvent(QPaintEvent*);
    24 
    25 };
    26 
    27 #endif // WIDGET_H

    在“widget.cpp”中更改

     1 #include "widget.h"
     2 #include "ui_widget.h"
     3 #include<QPainter>
     4 
     5 Widget::Widget(QWidget *parent) :
     6     QWidget(parent),
     7     ui(new Ui::Widget)
     8 {
     9     ui->setupUi(this);
    10 }
    11 
    12 void Widget::paintEvent(QPaintEvent*)
    13 {
    14     //创建画家
    15     QPainter painter(this);
    16 
    17     //设置画笔颜色
    18     QPen pen(QColor(255,0,0));
    19     //设置笔宽度
    20     pen.setWidth(3);
    21     //设置笔的风格
    22     pen.setStyle(Qt::DotLine);
    23     //画家用这只笔
    24     painter.setPen(pen);
    25 
    26 
    27     //画刷填充颜色
    28     QBrush brush(Qt::cyan);
    29     //让画家使用画刷
    30     brush.setStyle(Qt::Dense4Pattern);
    31     painter.setBrush(brush);
    32 
    33     //利用画家画画
    34     //画线
    35     painter.drawLine(QPoint(0,0),QPoint(100,100));
    36 
    37     //画圆(椭圆)
    38     painter.drawEllipse(QPoint(100,100),100,50);
    39 
    40     //画矩形
    41     painter.drawRect(QRect(10,10,50,50));
    42 
    43     //画字体
    44     painter.drawText(QRect(10,200,150,50),"好好学习,天天向上");
    45 
    46 }
    47 
    48 
    49 Widget::~Widget()
    50 {
    51     delete ui;
    52 }

    2、绘图高级设置

    1)抗锯齿 painter.setRenderHint(QPainter::Antialiasing);

    2)画家移动 painter.translate(QPoint(100,0));

    3)画家状态 painter.save();  painter.restore();

    在“widget.cpp”中更改

     1 #include "widget.h"
     2 #include "ui_widget.h"
     3 #include<QPainter>
     4 
     5 Widget::Widget(QWidget *parent) :
     6     QWidget(parent),
     7     ui(new Ui::Widget)
     8 {
     9     ui->setupUi(this);
    10 }
    11 
    12 void Widget::paintEvent(QPaintEvent*)
    13 {
    14     //创建画家
    15     QPainter painter(this);
    16 
    17     //高级设置
    18 //    painter.drawEllipse(QPoint(100,100),50,50);
    19 //    //设置抗锯齿,效率低
    20 //    painter.setRenderHint(QPainter::Antialiasing);
    21 //    painter.drawEllipse(QPoint(200,100),50,50);
    22 
    23 
    24     painter.drawRect(QRect(20,20,50,50));
    25 
    26     //移动画家
    27     painter.translate(QPoint(100,0));
    28 
    29     //保存状态
    30     painter.save();
    31 
    32     painter.drawRect(QRect(20,20,50,50));
    33 
    34     painter.translate(QPoint(100,0));
    35 
    36     //取出状态
    37     painter.restore();
    38 
    39     painter.drawRect(QRect(20,20,50,50));
    40 
    41 
    42 //    //设置画笔颜色
    43 //    QPen pen(QColor(255,0,0));
    44 //    //设置笔宽度
    45 //    pen.setWidth(3);
    46 //    //设置笔的风格
    47 //    pen.setStyle(Qt::DotLine);
    48 //    //画家用这只笔
    49 //    painter.setPen(pen);
    50 
    51 
    52 //    //画刷填充颜色
    53 //    QBrush brush(Qt::cyan);
    54 //    //让画家使用画刷
    55 //    brush.setStyle(Qt::Dense4Pattern);
    56 //    painter.setBrush(brush);
    57 
    58 //    //利用画家画画
    59 //    //画线
    60 //    painter.drawLine(QPoint(0,0),QPoint(100,100));
    61 
    62 //    //画圆(椭圆)
    63 //    painter.drawEllipse(QPoint(100,100),100,50);
    64 
    65 //    //画矩形
    66 //    painter.drawRect(QRect(10,10,50,50));
    67 
    68 //    //画字体
    69 //    painter.drawText(QRect(10,200,150,50),"好好学习,天天向上");
    70 
    71 }
    72 
    73 
    74 Widget::~Widget()
    75 {
    76     delete ui;
    77 }

    3、手动调用绘图事件

    1)update

    2)画家可以画图片

    3)drawPixmap(x,y,文件路径)

    在“widget.h”中更改

     1 #ifndef WIDGET_H
     2 #define WIDGET_H
     3 
     4 #include <QWidget>
     5 
     6 namespace Ui {
     7 class Widget;
     8 }
     9 
    10 class Widget : public QWidget
    11 {
    12     Q_OBJECT
    13 
    14 public:
    15     explicit Widget(QWidget *parent = 0);
    16     ~Widget();
    17 
    18 private:
    19     Ui::Widget *ui;
    20 
    21 public:
    22     //绘图事件
    23     void paintEvent(QPaintEvent*);
    24 
    25     int posX;
    26 
    27 };
    28 
    29 #endif // WIDGET_H

    在“widget.cpp”中更改

     1 #include "widget.h"
     2 #include "ui_widget.h"
     3 #include<QPainter>
     4 
     5 Widget::Widget(QWidget *parent) :
     6     QWidget(parent),
     7     ui(new Ui::Widget)
     8 {
     9     ui->setupUi(this);
    10 
    11     //点击按钮,移动图片
    12     posX = 10;
    13 
    14     connect(ui->pushButton,&QPushButton::clicked,[=](){
    15         posX += 10;
    16         //手动调用绘图事件
    17         update();
    18     });
    19 
    20 }
    21 
    22 void Widget::paintEvent(QPaintEvent*)
    23 {
    24     //创建画家
    25     QPainter painter(this);
    26 
    27     //画图片
    28     painter.drawPixmap(posX,100,QPixmap(":/Image/OnePiece.png"));
    29 
    30     //如果出屏幕,强制变回10
    31     if(posX > this->width())
    32     {
    33         posX = 10;
    34     }
    35 
    36     //高级设置
    37 //    painter.drawEllipse(QPoint(100,100),50,50);
    38 //    //设置抗锯齿,效率低
    39 //    painter.setRenderHint(QPainter::Antialiasing);
    40 //    painter.drawEllipse(QPoint(200,100),50,50);
    41 
    42 
    43 //    painter.drawRect(QRect(20,20,50,50));
    44 
    45 //    //移动画家
    46 //    painter.translate(QPoint(100,0));
    47 
    48 //    //保存状态
    49 //    painter.save();
    50 
    51 //    painter.drawRect(QRect(20,20,50,50));
    52 
    53 //    painter.translate(QPoint(100,0));
    54 
    55 //    //取出状态
    56 //    painter.restore();
    57 
    58 //    painter.drawRect(QRect(20,20,50,50));
    59 
    60 
    61 //    //设置画笔颜色
    62 //    QPen pen(QColor(255,0,0));
    63 //    //设置笔宽度
    64 //    pen.setWidth(3);
    65 //    //设置笔的风格
    66 //    pen.setStyle(Qt::DotLine);
    67 //    //画家用这只笔
    68 //    painter.setPen(pen);
    69 
    70 
    71 //    //画刷填充颜色
    72 //    QBrush brush(Qt::cyan);
    73 //    //让画家使用画刷
    74 //    brush.setStyle(Qt::Dense4Pattern);
    75 //    painter.setBrush(brush);
    76 
    77 //    //利用画家画画
    78 //    //画线
    79 //    painter.drawLine(QPoint(0,0),QPoint(100,100));
    80 
    81 //    //画圆(椭圆)
    82 //    painter.drawEllipse(QPoint(100,100),100,50);
    83 
    84 //    //画矩形
    85 //    painter.drawRect(QRect(10,10,50,50));
    86 
    87 //    //画字体
    88 //    painter.drawText(QRect(10,200,150,50),"好好学习,天天向上");
    89 
    90 }
    91 
    92 
    93 Widget::~Widget()
    94 {
    95     delete ui;
    96 }

    4、绘图设备


    绘图设备

    绘图设备是指继承QPainterDevice的子类。Qt一共提供了四个这样的类,分别是QPixmap、QBitmap、QImage和 QPicture。其中,

    》QPixmap专门为图像在屏幕上的显示做了优化

    》QBitmap是QPixmap的一个子类,它的色深限定为1,可以使用 QPixmap的isQBitmap()函数来确定这个QPixmap是不是一个QBitmap。

    》QImage专门为图像的像素级访问做了优化。

    》QPicture则可以记录和重现QPainter的各条命令。

    QPixmap、QBitmap、QImage

    QPixmap继承了QPaintDevice,因此,你可以使用QPainter直接在上面绘制图形。QPixmap也可以接受一个字符串作为一个文件的路径来显示这个文件,比如你想在程序之中打开png、jpeg之类的文件,就可以使用 QPixmap。使用QPainter的drawPixmap()函数可以把这个文件绘制到一个QLabel、QPushButton或者其他的设备上面。QPixmap是针对屏幕进行特殊优化的,因此,它与实际的底层显示设备息息相关。注意,这里说的显示设备并不是硬件,而是操作系统提供的原生的绘图引擎。所以,在不同的操作系统平台下,QPixmap的显示可能会有所差别。

    QBitmap继承自QPixmap,因此具有QPixmap的所有特性,提供单色图像QBitmap的色深始终为1. 色深这个概念来自计算机图形学,是指用于表现颜色的二进制的位数。我们知道,计算机里面的数据都是使用二进制表示的。为了表示一种颜色,我们也会使用二进制。比如我们要表示8种颜色,需要用3个二进制位,这时我们就说色深是3. 因此,所谓色深为1,也就是使用1个二进制位表示颜色。1个位只有两种状态:0和1,因此它所表示的颜色就有两种,黑和白。所以说,QBitmap实际上是只有黑白两色的图像数据。

    由于QBitmap色深小,因此只占用很少的存储空间,所以适合做光标文件和笔刷。

     

    QPixmap使用底层平台的绘制系统进行绘制,无法提供像素级别的操作,而QImage则是使用独立于硬件的绘制系统,实际上是自己绘制自己,因此提供了像素级别的操作,并且能够在不同系统之上提供一个一致的显示形式。

    QImage与QPixmap的区别

    》QPixmap主要是用于绘图,针对屏幕显示而最佳化设计,QImage主要是为图像I/O、图片访问和像素修改而设计的

    》QPixmap依赖于所在的平台的绘图引擎,故例如反锯齿等一些效果在不同的平台上可能会有不同的显示效果,QImage使用Qt自身的绘图引擎,可在不同平台上具有相同的显示效果

    》由于QImage是独立于硬件的,也是一种QPaintDevice,因此我们可以在另一个线程中对其进行绘制,而不需要在GUI线程中处理,使用这一方式可以很大幅度提高UI响应速度。

    》QImage可通过setPixpel()和pixel()等方法直接存取指定的像素。

    QImage与QPixmap之间的转换:

    》QImage转QPixmap

    使用QPixmap的静态成员函数: fromImage()

    QPixmap    fromImage(const QImage & image,

    Qt::ImageConversionFlags flags = Qt::AutoColor)

    》QPixmap转QImage:

    使用QPixmap类的成员函数: toImage()

    QImage toImage() const

     

    QPicture

    最后一个需要说明的是QPicture。这是一个可以记录和重现QPainter命令的绘图设备。 QPicture将QPainter的命令序列化到一个IO设备,保存为一个平台独立的文件格式。这种格式有时候会是“元文件(meta- files)”。Qt的这种格式是二进制的,不同于某些本地的元文件,Qt的pictures文件没有内容上的限制,只要是能够被QPainter绘制的元素,不论是字体还是pixmap,或者是变换,都可以保存进一个picture中。

    QPicture是平台无关的因此它可以使用在多种设备之上,比如svg、pdf、ps、打印机或者屏幕。回忆下我们这里所说的QPaintDevice,实际上是说可以有QPainter绘制的对象。QPicture使用系统的分辨率,并且可以调整 QPainter来消除不同设备之间的显示差异。

    如果我们要记录下QPainter的命令,首先要使用QPainter::begin()函数,将QPicture实例作为参数传递进去,以便告诉系统开始记录,记录完毕后使用QPainter::end()命令终止。


    1)QPixmap、QBitMap(黑白色)、QImage、QPicture、QWidget

    2)QPixmap:创建对象——QPixmap pix(w,h);fill填充色;保存save

    3)QImage(w,h,format) 设置像素setPixel

    4)QPicture重现记录,绘图指令,后缀名无要求

    新建工程:选择“QWidget”,勾选“显示主界面”(默认勾选)

    在“widget.h”中更改

     1 #ifndef WIDGET_H
     2 #define WIDGET_H
     3 
     4 #include <QWidget>
     5 
     6 namespace Ui {
     7 class Widget;
     8 }
     9 
    10 class Widget : public QWidget
    11 {
    12     Q_OBJECT
    13 
    14 public:
    15     explicit Widget(QWidget *parent = 0);
    16     ~Widget();
    17 
    18 private:
    19     Ui::Widget *ui;
    20 
    21 public:
    22     //绘图
    23     void paintEvent(QPaintEvent *);
    24 
    25 };
    26 
    27 #endif // WIDGET_H

    在“widget.cpp”中更改

     1 #include "widget.h"
     2 #include "ui_widget.h"
     3 #include<QPainter>
     4 #include<QPicture>
     5 
     6 Widget::Widget(QWidget *parent) :
     7     QWidget(parent),
     8     ui(new Ui::Widget)
     9 {
    10     ui->setupUi(this);
    11 
    12     //QPixmap绘图设备,对不同平台显示做了优化
    13 //    QPixmap pix(300, 300);
    14 //    //设置默认填充色
    15 //    pix.fill(Qt::white);
    16 //    QPainter painter(&pix);
    17 
    18 //    painter.setPen(QPen(Qt::green));
    19 //    painter.drawEllipse(QPoint(150, 150), 100, 100);
    20 
    21 //    //保存
    22 //    pix.save("E:/pix.png");
    23 
    24     //QImage做绘图设备,对像素级访问进行了优化
    25 //    QImage img(300,300,QImage::Format_RGB32);
    26 //    img.fill(Qt::white);
    27 //    QPainter painter(&img);
    28 //    painter.setPen(QPen(Qt::blue));
    29 //    painter.drawEllipse(QPoint(150, 150), 100, 100);
    30 //    img.save("E:/img.png");
    31 
    32     
    33     //QPicture绘图设备
    34     QPicture pic;
    35     QPainter painter;
    36     painter.begin(&pic);
    37     
    38     painter.setPen(QPen(Qt::cyan));
    39     painter.drawEllipse(QPoint(150, 150), 100, 100);
    40     
    41     painter.end();
    42     
    43     //保存
    44     pic.save("E:/pic.wp");
    45 
    46 }
    47 
    48 void Widget::paintEvent(QPaintEvent *)
    49 {
    50     //QImage可修改像素
    51 //    QImage img;
    52 //    img.load(":/Image/Luffy.png");
    53 
    54 //    for(int i = 50; i < 150; i++)
    55 //    {
    56 //        for(int j = 50; j < 150; j++)
    57 //        {
    58 //            QRgb value = qRgb(255, 0, 0);
    59 //            img.setPixel(i,j,value);
    60 //        }
    61 //    }
    62 
    63 //    QPainter painter(this);
    64 //    painter.drawImage(QPoint(0,0), img);
    65     
    66     //重现绘图指令QPicture
    67     QPicture pic;
    68     pic.load("E:/pic.wp");
    69     QPainter painter(this);
    70     painter.drawPicture(0,0,pic);
    71     
    72 }
    73 
    74 
    75 Widget::~Widget()
    76 {
    77     delete ui;
    78 }

    四、文件


    文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。Qt 通过QIODevice提供了对 I/O 设备的抽象,这些设备具有读写字节块的能力。下面是 I/O 设备的类图(Qt5):

    》QIODevice:所有 I/O 设备类的父类,提供了字节块读写的通用操作以及基本接口;

    》QFileDevice:Qt5新增加的类,提供了有关文件操作的通用实现。

    》QFlie:访问本地文件或者嵌入资源;

    》QTemporaryFile:创建和访问本地文件系统的临时文件;

    》QBuffer:读写QbyteArray, 内存文件;

    》QProcess:运行外部程序,处理进程间通讯;

    》QAbstractSocket:所有套接字类的父类;

    》QTcpSocket:TCP协议网络数据传输;

    》QUdpSocket:传输 UDP 报文;

    》QSslSocket:使用 SSL/TLS 传输数据;

    文件系统分类:

    》顺序访问设备:

    是指它们的数据只能访问一遍:从头走到尾,从第一个字节开始访问,直到最后一个字节,中途不能返回去读取上一个字节,这其中,QProcess、QTcpSocket、QUdpSoctet和QSslSocket是顺序访问设备。

    》随机访问设备:

    可以访问任意位置任意次数,还可以使用QIODevice::seek()函数来重新定位文件访问位置指针,QFile、QTemporaryFile和QBuffer是随机访问设备,

    基本文件操作

    文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。在所有的 I/O 设备中,文件 I/O 是最重要的部分之一。因为我们大多数的程序依旧需要首先访问本地文件(当然,在云计算大行其道的将来,这一观点可能改变)。QFile提供了从文件中读取和写入数据的能力。

    我们通常会将文件路径作为参数传给QFile的构造函数。不过也可以在创建好对象最后,使用setFileName()来修改QFile需要使用 / 作为文件分隔符,不过,它会自动将其转换成操作系统所需要的形式。例如 C:/windows 这样的路径在 Windows 平台下同样是可以的。

    QFile主要提供了有关文件的各种操作,比如打开文件、关闭文件、刷新文件等。我们可以使用QDataStream或QTextStream类来读写文件,也可以使用QIODevice类提供的read()、readLine()、readAll()以及write()这样的函数。值得注意的是,有关文件本身的信息,比如文件名、文件所在目录的名字等,则是通过QFileInfo获取而不是自己分析文件路径字符串。

    二进制文件读写

    QDataStream提供了基于QIODevice的二进制数据的序列化。数据流是一种二进制流,这种流完全不依赖底层操作系统、CPU 或者字节顺序(大端或小端)。例如,在安装了 Windows 平台的 PC 上面写入的一个数据流,可以不经过任何处理,直接拿到运行了 Solaris 的 SPARC 机器上读取。由于数据流就是二进制流,因此我们也可以直接读写没有编码的二进制数据,例如图像、视频、音频等。

    QDataStream既能够存取 C++ 基本类型,如 int、char、short 等,也可以存取复杂的数据类型,例如自定义的类。实际上,QDataStream对于类的存储,是将复杂的类分割为很多基本单元实现的。

    唯一需要注意的是,你必须按照写入的顺序,将数据读取出来。顺序颠倒的话,程序行为是不确定的,严重时会直接造成程序崩溃。

    文本文件读写

    上一节我们介绍了有关二进制文件的读写。二进制文件比较小巧,却不是人可读的格式。而文本文件是一种人可读的文件。为了操作这种文件,我们需要使用QTextStream类。QTextStream和QDataStream的使用类似,只不过它是操作纯文本文件的。

    QTextStream会自动将 Unicode 编码同操作系统的编码进行转换,这一操作对开发人员是透明的。它也会将换行符进行转换,同样不需要自己处理。QTextStream使用 16 位的QChar作为基础的数据存储单位,同样,它也支持 C++ 标准类型,如 int 等。实际上,这是将这种标准类型与字符串进行了相互转换。


    1、文件读写操作

    1)QFile file(路径)

    2)file.open 打开方式 QIODevice::ReadOnly

    3)file.readAll、readLine(file.atEnd判断是否到文件尾)

    4)写QIODevice::WriteOnly

    5)file.write(“....”);QFileDevice::Append追加

    新建工程:选择“QWidget”,勾选“显示主界面”(默认勾选)

    在“widget.ui”中添加“Widget”,在“Widget”中添加“Line Edit”和“Push Button”(水平布局),然后添加“Text Edit”(整体垂直布局)

    在“widget.cpp”中更改

     1 #include "widget.h"
     2 #include "ui_widget.h"
     3 #include<QFileDialog>
     4 #include<QMessageBox>
     5 #include<QFile>
     6 #include<QTextCodec>
     7 
     8 Widget::Widget(QWidget *parent) :
     9     QWidget(parent),
    10     ui(new Ui::Widget)
    11 {
    12     ui->setupUi(this);
    13 
    14     //点击按钮,选取文件
    15     connect(ui->pushButton,&QPushButton::clicked,[=](){
    16 
    17         QString path = QFileDialog::getOpenFileName(this,"打开文件","D:/WorkSpace/QT/day01/05_QFile");
    18         if(path.isEmpty())
    19         {
    20             QMessageBox::warning(this,"警告", "打开失败");
    21         }
    22         else
    23         {
    24             //将路径放入到lineEdit
    25             ui->lineEdit->setText(path);
    26             //读取文件
    27             //QFile默认支持UTF-8格式
    28 
    29 
    30             //QTextCodec* codec = QTextCodec::codecForName("gbk");
    31 
    32             QFile file(path);//参数路径名称
    33             //指定打开方式(只读)
    34             //file.open(QIODevice::ReadOnly);//下边的也行
    35             file.open(QFileDevice::ReadOnly);
    36 
    37             QByteArray array;
    38             array = file.readAll();
    39 
    40 //            while(!file.atEnd())
    41 //            {
    42 //                array += file.readLine();
    43 //            }
    44 
    45 
    46             //设置到文本编辑框中
    47             ui->textEdit->setText(array);
    48             file.close();
    49 
    50 
    51             //读gbk
    52             //ui->textEdit->setText(codec->toUnicode(array));
    53 
    54 
    55             //写文件
    56             //重新指定打开方式
    57             file.open(QFileDevice::Append);
    58             file.write("哦哦哦哦哦");
    59             file.close();
    60 
    61         }
    62 
    63 
    64     });
    65 
    66 }
    67 
    68 Widget::~Widget()
    69 {
    70     delete ui;
    71 }

    2、文件信息

    1)QFileInfo info

    2)info读取到文件信息

    3)路径、名称、后缀名、大小

    4)创建日期、修改日期 QDateTime

    在“widget.cpp”中更改

     1 #include "widget.h"
     2 #include "ui_widget.h"
     3 #include<QFileDialog>
     4 #include<QMessageBox>
     5 #include<QFile>
     6 #include<QTextCodec>
     7 #include<QFileInfo>
     8 #include<QDebug>
     9 #include<QDateTime>
    10 
    11 Widget::Widget(QWidget *parent) :
    12     QWidget(parent),
    13     ui(new Ui::Widget)
    14 {
    15     ui->setupUi(this);
    16 
    17     //点击按钮,选取文件
    18     connect(ui->pushButton,&QPushButton::clicked,[=](){
    19 
    20         QString path = QFileDialog::getOpenFileName(this,"打开文件","D:/WorkSpace/QT/day01/05_QFile");
    21         if(path.isEmpty())
    22         {
    23             QMessageBox::warning(this,"警告", "打开失败");
    24         }
    25         else
    26         {
    27             //将路径放入到lineEdit
    28             ui->lineEdit->setText(path);
    29             //读取文件
    30             //QFile默认支持UTF-8格式
    31 
    32 
    33             //QTextCodec* codec = QTextCodec::codecForName("gbk");
    34 
    35             QFile file(path);//参数路径名称
    36             //指定打开方式(只读)
    37             //file.open(QIODevice::ReadOnly);
    38             file.open(QFileDevice::ReadOnly);
    39 
    40             QByteArray array;
    41             array = file.readAll();
    42 
    43 //            while(!file.atEnd())
    44 //            {
    45 //                array += file.readLine();
    46 //            }
    47 
    48 
    49             //设置到文本编辑框中
    50             ui->textEdit->setText(array);
    51             file.close();
    52 
    53 
    54             //读gbk
    55             //ui->textEdit->setText(codec->toUnicode(array));
    56 
    57 
    58             //写文件
    59             //重新指定打开方式
    60 //            file.open(QFileDevice::Append);
    61 //            file.write("哦哦哦哦哦");
    62 //            file.close();
    63 
    64             //通过QFileInfo读取文件信息
    65             QFileInfo info(path);
    66             qDebug() << "路径:" << info.filePath().toUtf8().data()
    67                      << "名称:" << info.fileName().toUtf8().data()
    68                      << "文件大小:" << info.size()
    69                      << "后缀名:" << info.suffix();
    70 
    71             qDebug() << "创建日期:" << info.created().toString("yyyy-MM-dd hh:mm:ss");
    72             qDebug() << "修改日期:" << info.lastModified().toString("yyyy/MM/dd hh:mm:ss");
    73 
    74 
    75         }
    76 
    77 
    78     });
    79 
    80 }
    81 
    82 Widget::~Widget()
    83 {
    84     delete ui;
    85 }

    3、文本流和数据流

    (1)文本流

    1)QFile file(path)

    2)QTextStream(&file)

    3)stream << 写

    4)stream.readAll;左移遇到空格结束

    (2) 数据流

    1)QDataStream stream(&file)

    2)stream << 写数据

    3)stream >> 读数据;数据块读取

    在“widget.cpp”中更改

      1 #include "widget.h"
      2 #include "ui_widget.h"
      3 #include<QFileDialog>
      4 #include<QMessageBox>
      5 #include<QFile>
      6 #include<QTextCodec>
      7 #include<QFileInfo>
      8 #include<QDebug>
      9 #include<QDateTime>
     10 #
     11 
     12 Widget::Widget(QWidget *parent) :
     13     QWidget(parent),
     14     ui(new Ui::Widget)
     15 {
     16     ui->setupUi(this);
     17 
     18     //点击按钮,选取文件
     19     connect(ui->pushButton,&QPushButton::clicked,[=](){
     20 
     21         QString path = QFileDialog::getOpenFileName(this,"打开文件","D:/WorkSpace/QT/day01/05_QFile");
     22         if(path.isEmpty())
     23         {
     24             QMessageBox::warning(this,"警告", "打开失败");
     25         }
     26         else
     27         {
     28             //将路径放入到lineEdit
     29             ui->lineEdit->setText(path);
     30             //读取文件
     31             //QFile默认支持UTF-8格式
     32 
     33 
     34             //QTextCodec* codec = QTextCodec::codecForName("gbk");
     35 
     36             QFile file(path);//参数路径名称
     37             //指定打开方式(只读)
     38             //file.open(QIODevice::ReadOnly);
     39             file.open(QFileDevice::ReadOnly);
     40 
     41             QByteArray array;
     42             array = file.readAll();
     43 
     44 //            while(!file.atEnd())
     45 //            {
     46 //                array += file.readLine();
     47 //            }
     48 
     49 
     50             //设置到文本编辑框中
     51             ui->textEdit->setText(array);
     52             file.close();
     53 
     54 
     55             //读gbk
     56             //ui->textEdit->setText(codec->toUnicode(array));
     57 
     58 
     59             //写文件
     60             //重新指定打开方式
     61 //            file.open(QFileDevice::Append);
     62 //            file.write("哦哦哦哦哦");
     63 //            file.close();
     64 
     65             //通过QFileInfo读取文件信息
     66             QFileInfo info(path);
     67             qDebug() << "路径:" << info.filePath().toUtf8().data()
     68                      << "名称:" << info.fileName().toUtf8().data()
     69                      << "文件大小:" << info.size()
     70                      << "后缀名:" << info.suffix();
     71 
     72             qDebug() << "创建日期:" << info.created().toString("yyyy-MM-dd hh:mm:ss");
     73             qDebug() << "修改日期:" << info.lastModified().toString("yyyy/MM/dd hh:mm:ss");
     74 
     75 
     76         }
     77 
     78 
     79     });
     80 
     81     //文件流读写文件
     82 
     83     //分类:文本流(基础数据类型)和数据流(大型QImage)
     84 
     85     //文本流
     86 //    QFile file("aaa.txt");
     87 //    file.open(QFileDevice::WriteOnly);
     88 //    QTextStream stream(&file);
     89 //    stream << QString("hello world") << 123456;
     90 //    file.close();
     91 
     92 //    //读取
     93 //    file.open(QFileDevice::ReadOnly);
     94 //    QString str;
     95 //    //stream >> str;//读取空格 结束
     96 //    str = stream.readAll();//推荐这种
     97 
     98 //    qDebug() << str;
     99 
    100     //数据流 二进制
    101     QFile file("bbb.txt");
    102     file.open(QFileDevice::WriteOnly);
    103     QDataStream stream(&file);
    104     stream << QString("hello world") << 123456;
    105 
    106     file.close();
    107 
    108     //读数据
    109     file.open(QFileDevice::ReadOnly);
    110     QString str;
    111     int num;
    112     stream >> str >> num;//数据块
    113 
    114     qDebug() << str << num;
    115 
    116 }
    117 
    118 Widget::~Widget()
    119 {
    120     delete ui;
    121 }

    五、总结

    1    QLabel
    1.1    显示图片
    1.1.1    setPixmap(QPixmap(“:/…”))
    1.2    显示gif
    1.2.1    setMovie()
    1.2.2    new movie
    1.2.3    movie->start();
    2    Combox
    2.1    setCurrentIndex(索引)
    2.2    setCurrentText(“拖拉机”)
    3    自定义控件
    3.1    smallWidget 设计师界面类
    3.2    提升完了 使用
    3.3    信号和槽
    3.3.1    valueChanged
    3.3.2    setValue
    4    事件
    4.1    鼠标进入  参数要加入
    4.2    鼠标离开
    4.3    鼠标按下
    4.3.1    位置信息  ev->x()  ev->y()
    4.3.2    判断按键  ev->button()  Qt::LeftButton
    4.4    鼠标释放
    4.5    鼠标移动
    4.5.1    判断按键 ev->buttons() & Qt::LeftBtton
    5    定时器
    5.1    timerEvent
    5.2    启动 startTimer
    5.3    timerId
    5.4    第二种 QTimer
    5.5    启动 start
    5.6    timeout 信号
    5.7    stop 暂停
    6    event
    6.1    作用  事件的分发
    6.2    返回值 bool
    6.2.1    作用 true 用户自己处理 ,不向下分发
    7    事件过滤器  上层拦截
    7.1    安装事件过滤器
    7.2    重写eventfilter
    8    QPainter    
    8.1    QPainter painter(绘图设备 this)
    8.2    painter.draw…
    8.3    QPen pen(Qt::red); painter.setPen(pen); 画笔
    8.4    QBrush brush   painter.setBrush()画刷
    8.5    高级绘图
    8.5.1    抗锯齿
    8.5.2    painter.setRenderHint(QPainter::Antialiasing);
    8.5.3    移动画家
    8.5.4    painter.translate(QPoint(100,0))
    8.5.5    保存画家状态
    8.5.6    painter.save
    8.5.7    取出状态
    8.5.8    painter.restore
    8.6    画图片
    8.6.1    drawPixmap( QPixmap( “ …png ”) );
    8.7    手动调用paintEvent
    8.7.1    update()
    9    绘图设备
    9.1    QPixmap pix(300,300)
    9.2    利用画家画图
    9.3    保存 save
    9.4    fill 填充颜色
    9.5    QImage img(300,300, …RGB32)
    9.6    画图
    9.7    保存 save
    9.8    对像素进行访问
    9.8.1    setPixel
    9.9    QBitmap
    9.9.1    色深 1 黑白色
    9.10    Qpicture
    9.10.1    绘图指令
    9.10.2    save 格式没有限定
    9.10.3    画图  进行加载 load
    9.10.4    painter.drawPicture(0,0,pic)
    10    QFile
    10.1    读 readAll 读取所有内容
    10.2    write 写
    10.3    默认编码  utf8
    11    QFileInfo
    11.1    文件信息
    11.2    // 大小   后缀名 文件名 文件路径
    11.3    qDebug() << info.size() <<info.suffix()<<info.fileName() << info.filePath();
    11.4    QDateTime  toString
    12    文件流
    12.1    文本流
    12.1.1    写 file.open(QIODevice::WriteOnly | QIODevice::Text)
    12.1.2     readAll
    12.2    数据流
    12.2.1    dataStrema << QString("hello world")<<123456 数据块写入
    12.2.2    dataStrema >> str >> num 读时候也按照数据类型读取

    在学习QT总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

  • 相关阅读:
    笛卡尔积
    Oracle语句以及各种知识点
    vue和angular的区别:
    vue的生命周期的理解
    使用递归实现一个数组的快速排序
    对css盒模型的理解
    对 超链接 的理解
    HTML语义化的理解
    HTML 、XHTML、H5的区别:
    mysql的使用相关问题
  • 原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_QT_03.html
Copyright © 2011-2022 走看看