基本概念
-
项目文件(.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() 获取完整的文件后缀