基本概念

  • 项目文件(.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);//获取IDvoid 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 pic4.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::readOnly3.2  全部读取  file.readAll()   按行读  file.readLine()  atend()判断是否读到文件尾3.3    默认支持编码格式 utf-83.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\nQIODevice::Unbuffered  忽略缓存我们在这里使用了QFile::WriteOnly | QIODevice::Truncate,也就是以只写并且覆盖已有内容的形式操作文件。注意,QIODevice::Truncate会直接将文件内容清空。
  • QFileInfo有很多类型的函数,我们只举出一些例子。比如:isDir()检查该文件是否是目录;isExecutable() 检查该文件是否是可执行文件等。baseName()       可以直接获得文件名;completeBaseName()     获取完整的文件名suffix()    则直接获取文件后缀名。completeSuffix()     获取完整的文件后缀
    

多线程编程

  • 参考例子

QT_Session1相关推荐

最新文章

  1. SQLSERVER中统计所有表的记录数
  2. 如何用python画爱心型线_python怎么画爱心
  3. 将ListT集合用DataGridView展示
  4. CMake基础 第7节 编译标志
  5. WebService学习总结——调用第三方提供的webService服务
  6. Spring框架整合MyBatis框架
  7. vmware-linux虚拟机上网配置
  8. UbuntuServer安装Node.js
  9. 【每日算法Day 101】字节跳动 AI Lab 精选面试编程题
  10. linux ext4分区无损扩容,linux操作系统无损升级文件系统ext3至ext4--数据盘篇
  11. c语言使用CodeBlocks软件,使用CodeBlocks学习C语言
  12. linux---dns/yum安装软件/定时任务
  13. 静态库与动态库的区别和使用
  14. 2019年高中数学圆锥曲线解题技巧方法总结及高考试题
  15. 如何给视频加背景音乐?简单快速上手,制作抖音等小视频必备!
  16. window7修改屏幕旋转快捷键
  17. LeetCode刷题(158)~从尾到头打印链表【递归|辅助栈】
  18. 一对同居男女同一天的日记对比
  19. 【nginx】4xx,5xx 保持自定义header
  20. 任何产品需求,挖到最后都是人性

热门文章

  1. 3D模型欣赏:中世纪骑士
  2. [C#]键盘↑↓←→控制图片加速移动
  3. 彻底解决DeLL Power Edge T640显卡不兼容导致的风扇转速狂飙
  4. [附源码]Node.js计算机毕业设计-Java网名推荐系统Express
  5. android手机nfc功能安装,Android手机NFC分享功能实测-头条网
  6. 《终结者》里的液态金属,会是我们造不出芯片的解决方案吗?
  7. 小米4联通版刷flyme,工程模式无法选择网络
  8. Linux安装和配置ftp
  9. 什么是SPI的bitbang / bit bang / bit-bang / bitbanging
  10. TextBox中只能输入数字的几种常用方法(C#)