QT_Session1

基本概念

  • 项目文件(.pro)

  • 父窗口和子窗口(也叫控件、部件、构件)

  • 信号和槽

  • 坐标系统

  • 具有内存回收机制 new delete

  • 对话框

  • QWidget(父类,一般创建这个,都是空白)

    • QMainWindow(PC端,带菜单栏)
    • QDialog(对话框)
  • 版本控制系统

    • svn
    • vss
    • git
  • 基本步骤

    • 创建唯一的应用程序对象

    • 创建窗口对象

    • 调用show方法显示窗口对象

    • 调用应用程序的exec()函数进入消息循环

    • 	QApplication a  应用程序对象,有且仅有一个
      	myWidget w;实例化窗口对象
      	w.show()调用show函数 显示窗口
      	return a.exec() 让应用程序对象进入消息循环机制中,代码阻塞到当前行
      
      
  • 在这里插入图片描述

  • .pro文件简单解释

    QT       += core gui  //包含的模块
    greaterThan(QT_MAJOR_VERSION, 4): QT += widgets //大于Qt4版本 才包含widget模块
    TARGET = QtFirst  //应用程序名  生成的.exe程序名称
    TEMPLATE = app    //模板类型    应用程序模板
    SOURCES += main.cpp\   //源文件
            mywidget.cpp
    HEADERS  += mywidget.h   //头文件
    
    
  • 快捷键

    • ctrl+/ 注释
    • ctrl+r 运行
    • ctrl+b 编译
    • F1进入帮助界面,再按一次F1全屏界面,esc退出
    • 整行移动 ctrl+shift+↑/↓
    • 自动对齐 ctrl+i
    • 同名之间的.h和.cpp文件的切换 F4
  • Q_OBJECT宏,允许类中使用信号和槽机制

  • .show()以顶层方式弹窗窗口控件

按钮常用api

  • 学会用帮助文档
    QPushButton * btn=new QPushButton;
    btn->setParent(this);
    btn->setText("第一个按钮");
    resize(600,4000);//重设窗口大小
    btn->move(100,100);
    setWindowTitle("第一个窗口");//设置窗口名称
    setFixedSize(600,400);//设置固定大小
  • 创建 QPushButton * btn = new QPushButton
  • 设置父亲 setParent(this)
  • 设置文本 setText(“文字”)
  • 设置位置 move(宽,高)
  • 重新指定窗口大小 resize
  • 设置窗口标题 setWindowTitle
  • 设置窗口固定大小 setFixedSize

对象树

  • 对于C++类对象的构造和析构函数而言:

    • 1、构造函数的调用顺序

      基类构造函数、对象成员构造函数、派生类本身的构造函数

    • 2、析构函数的调用顺序

      派生类本身的析构函数、对象成员析构函数、基类析构函数(与构造顺序正好相反)

    • 3、特例:

      • 静态对象,在定义所在文件结束时析构
      • 全局对象,在程序结束时析构
  • C++类的对象创建

    • 在C++中类的对象建立分为两种,一种是静态建立,如A a;
    • 另一种是动态建立,如A* p=new A(),A *p=(A*)malloc();
    • 静态建立一个类对象,是由编译器为对象在栈空间中分配内存,通过直接移动栈顶指针挪出适当的空间,然后在这片内存空间上调用构造函数形成一个栈对象。
    • 动态建立类对象,是使用new运算符将对象建立在堆空间中,在栈中只保留了指向该对象的指针。
    • 构建堆上的对象时一般使用new关键字,而对象的指针在栈上。使用new在堆上构建的对象需要主动的delete销毁。
    • 栈是由编译器自动分配释放 ,存放函数的参数值,局部变量的值,对象的引用地址等。其操作方式类似于数据结构中的栈,通常都是被调用时处于存储空间中,调用完毕立即释放。
    • 堆中通常保存程序运行时动态创建的对象,C++堆中存放的对象需要由程序员分配释放,它存在程序运行的整个生命期,直到程序结束由OS释放。
    • C++对象可以在堆或栈中,函数的传参可以是对象(对象的拷贝),或是对象的指针
  • 对于QT

    • 在这里插入图片描述

    • 概念简单: QObject 类有一个私有变量 QList<QObject *>,专门存储这个类的子孙后代们。比如创建一个 QObject 并指定父对象时,就会把自己加入到父对象的 childre() 列表中,也就是 QList<QObject *> 变量中。

    • 使用对象树模式有什么好处?

      • 好处就是:当父对象被析构时子对象也会被析构。
    • 举个例子,有一个窗口 Window,使用new在堆上创建,里面有 Label标签、TextEdit文本输入框、Button按钮这三个元素,并且都设置 Window 为它们的父对象。这时候我做了一个关闭窗口的操作,作为程序员的你是不是自然想到将所有和窗口相关的对象析构啊?古老的办法就是一个个手动 delete 呗。是不是很麻烦?Qt 运用对象树模式,当父对象被析构时,子对象自动就 delete 掉了,不用再写一大堆的代码了。

      • 即使这个对象是在堆上创建了,也遵循这个机制。
    • 所以,对象树在 GUI 编程中是非常非常有用的。

    • 注意构建/析构 QObject 的顺序问题

      • 正常情况下,最后被创建出来的会先被析构掉。就好比我有一个大桌子,上面先摆放一个盘子,再摆放一个碗。当我要把桌子撤掉的时候,会先撤掉碗,再撤掉盘子,最后撤掉桌子。

      • 正常情况

        int main()
        {
             QWidget window;
             QPushButton quit("Quit", &window);
        }
        

        后创建的 quit 对象指定了 window 为其父对象。那么关闭程序时,会先调用它的析构函数,然后调用 window 的析构函数。所以quit对象只调用一次析构函数。

      • 异常情况

        int main()
        {
            QPushButton quit("Quit");
            QWidget window;
         
            quit.setParent(&window);
        }
        

        如果反过来,由于 window 后创建,程序关闭时先调用 window 的析构函数(此时 quit 被第一次析构)。接着调用 quit 的析构函数(此时 quit 被第二次析构),这时由于被两次析构,所以出问题了。

  • 由此我们看到,Qt 的对象树机制虽然帮助我们在一定程度上解决了内存问题,但是也引入了一些值得注意的事情。这些细节在今后的开发过程中很可能时不时跳出来烦扰一下,所以,我们最好从开始就养成良好习惯:在 Qt 中,尽量在构造的时候就指定 parent 对象,并且大胆在堆上创建。

QT的窗口坐标体系

  • 坐标体系:以左上角为原点(0,0),X向右增加,Y向下增加。
  • 和Opencv坐标系一样
  • 对于嵌套窗口,其坐标是相对于父窗口来说的。

信号与槽

  • connect(信号的发送者,发送的具体信号函数,信号的接受者,信号的处理(槽)函数)。

  • 优点:松散耦合,信号发送端和接收端本身没有关联,通过connect连接,将两端耦合。

  • 例子

    • connect(btn,&QPushButton::clicked,this , &Widget::close);
      

自定义信号和槽函数

  • 自定义信号

    • 写在signal下
    • 返回值void,只需要声明,不需要实现
    • 可以有参数,即可以重载
  • 自定义槽函数

    • 可以写在public slots下,也可以写在public下,或者全局下
    • 返回值void
    • 需要声明,也需要实现(.h声明,.cpp实现)
    • 可以有参数,可以重载
  • 先连接connect,再触发信号

  • 触发信号的函数写法

    //使用emit
    void MyWidget::ClassIsOver()
    {
        //发送信号
        emit teacher->hungury();
    }
    
    
  • 注意点

    • l 任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数。
    • l 信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。
    • l 如果信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少)。
  • 当遇到函数重载冲突是,需要用到函数指针,具体到那个函数。不同的是,需要声明类的作用域。

    void (Teacher:: * teacherSingal)(QString) = &Teacher::hungury;
    void (Student:: * studentSlot)(QString) = &Student::treat;
    connect(teacher,teacherSingal,student,studentSlot);
    
  • Qstring转出char *的方法

    • name.toUtf8().data
    • .toUtf8()转成QByArray,再用.data()转成char*。
    • char* 使用qDebug<<打印的时候,不会有引号的问题。

信号与槽拓展点

  • 一个信号可以和多个槽相连

  • 如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。

  • 多个信号可以连接到一个槽

  • 只要任意一个信号发出,这个槽就会被调用。

  • 一个信号可以连接到另外的一个信号

  • 当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。

  • 槽可以被取消链接

  • 这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。

  • 信号槽可以断开

  • 利用disconnect关键字是可以断开信号槽的

Lambda表达式

  • 基本构成

    [capture](parameters) mutable ->return-type
    {
    statement
    }
    [函数对象参数](操作符重载函数参数)mutable ->返回值{函数体}
    //mutable省略的话表示参数只是只读状态,除非传入引用
    
  • ①函数对象参数;

    [],标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式:

    • 空。没有使用任何函数对象参数。
    • =。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
    • &。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)
    • this。函数体内可以使用Lambda所在类中的成员变量。
    • a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。
    • &a。将a按引用进行传递。
    • a, &b。将a按值进行传递,b按引用进行传递。
    • =,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。
    • &, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。
  • ② 操作符重载函数参数;

    标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。

  • ③ 可修改标示符;

    mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。

  • //例子
    QPushButton * myBtn = new QPushButton (this);
    QPushButton * myBtn2 = new QPushButton (this);
    myBtn2->move(100,100);
    int m = 10;
    connect(myBtn,&QPushButton::clicked,this,[m] ()mutable { m = 100 + 10; qDebug() << m; });
    
    connect(myBtn2,&QPushButton::clicked,this,[=] ()  { qDebug() << m; });
    
    qDebug() << m;
    
  • ④ 函数返回值;

    ->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。

  • ⑤ 是函数体;

    {},标识函数的实现,这部分不能省略,但函数体可以为空。

  • 建议一般用=,按值传递,外加mutable修饰符。

QMainWindow

  • 注意QAction类和对应的信号triggered()。
1.1	菜单栏 最多有一个(添加<QMenuBar>)
    1.1.1	 QMenuBar * bar = MenuBar();
    1.1.2	 setMenuBar( bar ) 
    1.1.3	 QMenu * fileMenu = bar -> addMenu(“文件”)   创建菜单
    1.1.4	 QAction * newAction =  fileMenu ->addAction(“新建”); 创建菜单项
    1.1.5	 添加分割线 fileMenu->addSeparator();
1.2	工具栏 可以有多个(添加<QToolBar>)
    1.2.1	QToolBar * toolbar = new QToolBar(this);
    1.2.2	addToolBar( 默认停靠区域, toolbar );  Qt::LeftToolBarArea(枚举值)
    1.2.3	设置  后期停靠区域,设置浮动,设置移动
            Toolbar->setAllowedAreas(…)
            Toolbar->setFloatable (…)
            Toolbar->setMovable (…)
    1.2.4	添加菜单项 或者添加 小控件
            Toolbar->addAction(…)
            Toolbar->addWidget (…)
1.3	状态栏  最多一个(添加<QStatusBar>)
    1.3.1	QStatusBar * stBar = statusBar();
    1.3.2	设置到窗口中 setStatusBar(stBar);
    1.3.3	  stBar->addWidget(label);放左侧信息,标签
    1.3.4	  stBar->addPermanentWidget(label2); 放右侧信息
1.4	铆接部件  浮动窗口  可以多个(添加<QDocWidget>)
    1.4.1	QDockWidget *
    1.4.2	addDockWidget( 默认停靠区域,浮动窗口指针)
    1.4.3	设置后期停靠区域
1.5	设置核心部件  只能一个
	1.5.1	setCentralWidget(edit);

资源文件

1	将图片文件 拷贝到项目位置下
2	右键项目->添加新文件 ->  Qt - > Qt recourse File  - >给资源文件起名(如res)
3	res 生成  res.qrc  
4	res.qrc右键open in editor  编辑资源
5	添加前缀  添加文件
6	使用  “ : + 前缀名 + 文件名 ”
	ui->actionNew->setIcon(QIcon(…));

对话框

  • Qt 支持模态对话框和非模态对话框。

    模态与非模态的实现:

    • 使用QDialog::exec()实现应用程序级别的模态对话框
    • 使用QDialog::open()实现窗口级别的模态对话框
    • 使用QDialog::show()实现非模态对话框。
  • Qt 有两种级别的模态对话框:

    • 应用程序级别的模态

      当该种模态的对话框出现时,用户必须首先对对话框进行交互,直到关闭对话框,然后才能访问程序中其他的窗口。

    • 窗口级别的模态

      该模态仅仅阻塞与对话框关联的窗口,但是依然允许用户与程序中其它窗口交互。窗口级别的模态尤其适用于多窗口模式。

    • 一般默认是应用程序级别的模态。

  • 添加头文件

  • 分类: 
    1	模态对话框   不可以对其他窗口进行操作 阻塞
            1.1	QDialog  dlg(this)
            1.2	dlg.exec();
    2	非模态对话框  可以对其他窗口进行操作
            2.1	防止一闪而过 创建到堆区
            2.2	QDialog * dlg = new QDialog(this)
            2.3	dlg->show();
            2.4	dlg2->setAttribute(Qt::WA_DeleteOnClose); //55号属性,关闭时候对象删除,防止堆溢出,内存爆炸
    
  • 标准对话框 – 消息对话框

    • QMessageBox 静态成员函数 创建对话框
    • 错误critiacl、信息information、提问question、警告warning
    • 参数1 父亲 ;参数2 标题; 参数3 显示内容; 参数4 按键类型; 参数5 默认关联回车按键。
    • 返回值 也是StandardButton类型,利用返回值判断用户的输入
  • 其他标准对话框

    • 颜色对话框 QColorDialog::getColor
    • 文件对话框 QFileDialog::getOpenFileName(父亲,标题,默认路径,过滤文件)
      • 返回QStirng,选择文件的路径
    • 字体对话框 QFontDialog::getFont

UI界面布局

1 利用布局方式 给窗口进行美化

2 选取 widget 进行布局 ,水平布局、垂直布局、栅格布局

3 可以打破布局

4 默认窗口和控件之间 有9间隙,可以调整 layoutLeftMargin

5 利用弹簧进行布局,但要整体布局之后才有用

6 QWidget的sizePolicy里面有垂直水平策略,可更改为固定值

按钮组

1 QPushButton 常用按钮

2 QToolButton 工具按钮 用于显示图片,如图想显示文字,修改风格:toolButtonStyle , 凸起风格autoRaise

3 radioButton 单选按钮,回到代码设置默认 ui->rBtnMan->setChecked(true); 组控件Group Box确定分组。

4 checkbox多选按钮,同样组控件Group Box确定分组,监听状态,2 选中 1 半选 0 未选中。

QListWidget 列表容器

1 QListWidgetItem * item 一行内容

2 ui->listWidget ->addItem ( item )

3 设置居中方式item->setTextAlignment(Qt::AlignHCenter);

4 可以利用addItems一次性添加整个诗内容

  • QStringList list;
    list<<"锄禾日当午"<<"汗滴禾下土"<<"谁知盘中餐"<<"粒粒皆辛苦";
    ui->ListWidget->addItems(list);
    

QTreeWidget 树控件

1	设置头  
	1.1	ui->treeWidget->setHeaderLabels(QStringList()<< "英雄"<< "英雄介绍");
2	创建根节点
	2.1	QTreeWidgetItem * liItem = new QTreeWidgetItem(QStringList()<< "力量");
3	添加根节点 到 树控件上
	3.1	ui->treeWidget->addTopLevelItem(liItem);
4	添加子节点
	4.1	liItem->addChild(l1);

QTableWidget 表格控件

1  设置列数 
	1.1  ui->tableWidget->setColumnCount(3);
2  设置水平表头
	2.1  ui->tableWidget->setHorizontalHeaderLabels(QStringList()<<"姓名"<< "性别"<< "年龄");
3  设置行数 
	3.1  ui->tableWidget->setRowCount(5);
4  设置正文
	4.1  ui->tableWidget->setItem(0,0, new QTableWidgetItem("亚瑟"));

//int 转为QString
QString::number();

其他控件

  • 基于Containers

    • Croup Box:分组的

    • Scoroll Area:滑动容器

    • Tool Box:类似QQ的新建分组

    • Tab Widget:分页栏,即类似浏览器顶部

    • tackedWidget 栈控件

      • - ui->stackedWidget->setCurrentIndex(1);
        - 利用按钮就行切换,尽量利用lambda表达式
        - connect(ui->btn_ccrollArea,&QPushButton::clicked,[=](){
            ui->stackedWidget->setCurrentIndex(1);
        });
        
  • 基于Input Widget

    • 下拉框

      ui->comboBox->addItem(“奔驰”);

    • Line Edit:单行输入框

    • Text Edit:多行输入框

  • 基于Display Widget

    • Qlabel

      • 1 QLabel 显示图片

        • 1.1 ui->lbl_Image->setPixmap(QPixmap(":/Image/butterfly.png"))
      • 2 QLabel显示动图 gif图片

        •   QMovie *movie =new QMovie(":/Image/mario.gif");
            ui->lbl_movie->setMovie(movie);
            movie->start();
          
    • Progress Bar

      • 进度条
      • setValue()
      • setRange()

事件event

  • 鼠标进入事件  enterEvent
    鼠标离开事件  leaveEvent
    /*
        1.新建类,在.h和.cpp添加enterEvent、leaveEvent的声明和实现
        2.保证和ui界面的控件继承的基类一致
        3.右键控件提升为,点击添加,点击提升
        4.测试
    */
    
  • 以上是利用自定义控件设置事件,主要作用是把控件和我们自己写cpp关联。也可以直接使用重写虚函数的办法。

	事件(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 的,也就是说,我们可以在子类中重新实现这些函数。

  • 1	鼠标按下   mousePressEvent ( QMouseEvent  ev)
    2	鼠标释放   mouseReleaseEvent
    3	鼠标移动   mouseMoveEvent
    4	ev->x() x坐标  ev->y() y坐标
    5	ev->button() 可以判断所有按键 Qt::LeftButton  Qt::RightButton
    6	ev->buttons()判断组合按键  判断move时候的左右键  结合 & 操作符
    7	格式化字符串  QString( “ %1  %2 ” ).arg( 111 ).arg(222)
    8	设置鼠标追踪    setMouseTracking(true);
    

定时器

方式一

  • 1.1     利用事件 void timerEvent ( QTimerEvent * ev)
    1.2     启动定时器 startTimer( 1000) 毫秒单位
    1.3     timerEvent 的返回值是定时器的唯一标示 可以和ev->timerId 做比较
    
  • //ID1的生命周期应该是长周期
    int ID1=startTimer( 1000);//获取ID
    
    void Widget::timerEvent ( QTimerEvent * ev){
        switch(ev->timerId()){
                case:ID1{
                    ....
                }
        }
    }
    

方式二

  • 1.1     利用定时器类 QTimer
    1.2     创建定时器对象 QTimer * timer = new QTimer(this)
    1.3     启动定时器 timer->start(毫秒)
    1.4     每隔一定毫秒,发送信号 timeout ,进行监听
    1.5     暂停 timer->stop
    
  • QTimer * timer1=new QTimer;
    timer->start(1000);
    connect(timer,&QTimer::timeout,[=](){
        static int num=1;
        ui->label1=>setText(QString::number(num++));
    })
        
    //按钮停止定时器
    connect(ui->btn,&QPushButton::clicked,[=](){
        timer1->stop();
    })    
    

event事件分发器

	事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。
	如上所述,event()函数主要用于事件的分发。所以,如果你希望在事件分发之前做一些操作,就可以重写这个event()函数了。
    bool event(QEvent *ev)
    负责事件分发,返回值为bool类型,如果返回为true,代表用户要处理这个事件,不向下发事件了。
  • bool myLabel::event(QEvent *e){
        if(e->type()==QEvent::MouseButtonPress){
            QMouseEvent *ev=static_cast<QMouseEvent *>(e);//强制转换
            QString str=QString("x=%1,y=%2").arg(ev->x()).arg(ev->y());
            qDebug()<<str;
            return true;//代表自己处理,不下发
        }
        //其他事件,交给父类,默认处理
        return QLabel::event(e);
    }
    
  • event事件
    1	用途:用于事件的分发
    2	也可以做拦截操作,不建议
    3	bool event( QEvent * e); 
    4	返回值 如果是true 代表用户处理这个事件,不向下分发了
    5	e->type() == 鼠标按下、按tab键等 …
    

事件过滤器

  • 1	在程序将时间分发到事件分发器前,可以利用过滤器做拦截
    2	步骤
        2.1给控件安装事件过滤器
        2.2重写 eventFilter函数 (obj , ev)
    
        //obj为控件,ev为事件类型
    
  • 例子

class MainWindow : public QMainWindow
 {
 public:
     MainWindow();
 protected:
     bool eventFilter(QObject *obj, QEvent *event);
 private:
     QTextEdit *textEdit;
 };
 
 MainWindow::MainWindow()
 {
     textEdit = new QTextEdit;
     setCentralWidget(textEdit);
 
     textEdit->installEventFilter(this);
 }
 
 bool MainWindow::eventFilter(QObject *obj, QEvent *event)
 {
     if (obj == textEdit) {
         if (event->type() == QEvent::KeyPress) {
             QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
             qDebug() << "Ate key press" << keyEvent->key();
             return true;
         } else {
             return false;
         }
     } 
    // 传回父类
     return QMainWindow::eventFilter(obj, event);
 }

  • 在这里插入图片描述

QPainter绘图事件

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

  • QPainter用来执行绘制的操作;

  • QPaintDevice是一个二维空间的抽象,这个二维空间允许QPainter在其上面进行绘制,也就是QPainter工作的空间;

  • QPaintEngine提供了画笔(QPainter)在不同的设备上进行绘制的统一的接口。QPaintEngine类应用于QPainter和QPaintDevice之间,通常对开发人员是透明的。除非你需要自定义一个设备,否则你是不需要关心QPaintEngine这个类的。

  • 我们可以把QPainter理解成画笔;把QPaintDevice理解成使用画笔的地方,比如纸张、屏幕等;而对于纸张、屏幕而言,肯定要使用不同的画笔绘制,为了统一使用一种画笔,我们设计了QPaintEngine类,这个类让不同的纸张、屏幕都能使用一种画笔。下图给出了这三个类之间的层次结构:

在这里插入图片描述

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

  • void PaintedWidget::paintEvent(QPaintEvent *)
    {
        QPainter painter(this);
        painter.drawLine(80, 100, 650, 500);
        painter.setPen(Qt::red);
        painter.drawRect(10, 10, 100, 400);
        painter.setPen(QPen(Qt::green, 5));
        painter.setBrush(Qt::blue);
        painter.drawEllipse(50, 150, 400, 200);
    }
    
    /*QPainter接收一个QPaintDevice指针作为参数。QPaintDevice有很多子类,比如QImage,以及QWidget。注意回忆一下,QPaintDevice可以理解成要在哪里去绘制,而现在我们希望画在这个组件,因此传入的是 this 指针。
    
    QPainter有很多以 draw 开头的函数,用于各种图形的绘制,比如这里的drawLine(),drawRect()以及drawEllipse()等。当绘制轮廓线时,使用QPainter的pen()属性。比如,我们调用了painter.setPen(Qt::red)将 pen 设置为红色,则下面绘制的矩形具有红色的轮廓线。接下来,我们将 pen 修改为绿色,5 像素宽(painter.setPen(QPen(Qt::green, 5))),又设置了画刷为蓝色。这时候再调用 draw 函数,则是具有绿色 5 像素宽轮廓线、蓝色填充的椭圆。
    */
    
  • 高级设置

    • 1	抗锯齿 效率低
      	1.1	painter.setRenderHint(QPainter::Antialiasing);
      2	对画家进行移动
      	2.1	painter.translate(100,0);
      2.2	保存状态 paiter.save()
      	2.3	还原状态 painter.restore()
      3	如果想手动调用绘图事件 利用 update();
      4	利用画家画图片 painter.drawPixmap( x,y,QPixmap(  路飞) )
      
      

绘图设备

  • **绘图设备是指继承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则是使用独立于硬件的绘制系统,实际上是自己绘制自己,因此提供了像素级别的操作,并且能够在不同系统之上提供一个一致的显示形式。

  • 1	QPixmap QImage  QBitmap(黑白色) QPicture  QWidget
    2	QPixmap 对不同平台做了显示的优化
        2.1	QPixmap pix( 300,300)
        2.2	pix.fill( 填充颜色 )
        2.3	利用画家 往pix上画画  QPainter painter( & pix)
        2.4	保存  pix.save( “路径”)
    3	Qimage 可以对像素进行访问
        3.1	使用和QPixmap差不多 QImage img(300,300,QImage::Format_RGB32);
        3.2	其他流程和QPixmap一样
        3.3	可以对像素进行修改 img.setPixel(i,j,value);
    4	QPicture  记录和重现 绘图指令
        4.1	QPicture pic
        4.2	painter.begin(&pic);
        4.3	保存 pic.save( 任意后缀名 )
        4.4	重现 
            pic.load();
            利用画家可以重现painter.drawPicture(0,0,pic);
    
    

QFile文件读写

读文件

  • 1	QFile进行读写操作
    2	QFile file( path 文件路径)
    3	读
        3.1	file.open(打开方式) QIODevice::readOnly
        3.2	全部读取  file.readAll()   按行读  file.readLine()  atend()判断是否读到文件尾
        3.3	默认支持编码格式 utf-8
        3.4	利用编码格式类 指定格式 QTextCodeC 
        3.5	QTextCodec * codec = QTextCodec::codecForName("gbk");
        3.6	ui->textEdit->setText( codec->toUnicode(array)  );
        3.7	文件对象关闭 close
    
  • QFile file(path);
    file.open(QIODevice::ReadOnly);
    QByteArray array=file.readAll();
    ui->textEdit->setText(array);
        
    //
    file.open(QIODevice::ReadOnly);
    QByteArray array;
    while(!file.atEnd()){
        array+=file.readLine();
    }
    ui->textEdit->setText(array);
    
    //由gbk转为utf-8
    QTextCodec * codec = QTextCodec::codecForName("gbk");
    ui->textEdit->setText( codec->toUnicode(array)  );
    
    file.close;
    

写文件

  • 1	file.open( QIODevice::writeOnly  / Append)
    2	file.write(内容)
    3	file.close 关闭
    
    
  • file.open(QIODevice::Append);
    file.write("aaaaa");
    file.close();
    

注意点

  • 我们通常会将文件路径作为参数传给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对于类的存储,是将复杂的类分割为很多基本单元实现的。

    结合QIODevice,QDataStream可以很方便地对文件、网络套接字等进行读写操作。

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

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

  • 	open打开方式
        枚举值	               描述
    	QIODevice::NotOpen		未打开
    	QIODevice::ReadOnly		以只读方式打开
    	QIODevice::WriteOnly		以只写方式打开
    	QIODevice::ReadWrite		以读写方式打开
    	QIODevice::Append			以追加的方式打开,新增加的内容将被追加到文件末尾
    	QIODevice::Truncate		以重写的方式打开,在写入新的数据时会将原有数据全部清除,游标设置在文件开头。
    	QIODevice::Text			在读取时,将行结束符转换成 \n;在写入时,将行结束符转换成本地格式,例如 Win32 平台上是 \r\n
    	QIODevice::Unbuffered	忽略缓存
        
        我们在这里使用了QFile::WriteOnly | QIODevice::Truncate,也就是以只写并且覆盖已有内容的形式操作文件。注意,QIODevice::Truncate会直接将文件内容清空。
    
    
  • QFileInfo有很多类型的函数,我们只举出一些例子。比如:
    	isDir()检查该文件是否是目录;
    	isExecutable()	检查该文件是否是可执行文件等。
    	baseName()		可以直接获得文件名;
    	completeBaseName() 	获取完整的文件名
    	suffix()	则直接获取文件后缀名。
    	completeSuffix() 	获取完整的文件后缀
    

多线程编程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值