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);//获取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相关推荐
最新文章
- SQLSERVER中统计所有表的记录数
- 如何用python画爱心型线_python怎么画爱心
- 将ListT集合用DataGridView展示
- CMake基础 第7节 编译标志
- WebService学习总结——调用第三方提供的webService服务
- Spring框架整合MyBatis框架
- vmware-linux虚拟机上网配置
- UbuntuServer安装Node.js
- 【每日算法Day 101】字节跳动 AI Lab 精选面试编程题
- linux ext4分区无损扩容,linux操作系统无损升级文件系统ext3至ext4--数据盘篇
- c语言使用CodeBlocks软件,使用CodeBlocks学习C语言
- linux---dns/yum安装软件/定时任务
- 静态库与动态库的区别和使用
- 2019年高中数学圆锥曲线解题技巧方法总结及高考试题
- 如何给视频加背景音乐?简单快速上手,制作抖音等小视频必备!
- window7修改屏幕旋转快捷键
- LeetCode刷题(158)~从尾到头打印链表【递归|辅助栈】
- 一对同居男女同一天的日记对比
- 【nginx】4xx,5xx 保持自定义header
- 任何产品需求,挖到最后都是人性
热门文章
- 3D模型欣赏:中世纪骑士
- [C#]键盘↑↓←→控制图片加速移动
- 彻底解决DeLL Power Edge T640显卡不兼容导致的风扇转速狂飙
- [附源码]Node.js计算机毕业设计-Java网名推荐系统Express
- android手机nfc功能安装,Android手机NFC分享功能实测-头条网
- 《终结者》里的液态金属,会是我们造不出芯片的解决方案吗?
- 小米4联通版刷flyme,工程模式无法选择网络
- Linux安装和配置ftp
- 什么是SPI的bitbang / bit bang / bit-bang / bitbanging
- TextBox中只能输入数字的几种常用方法(C#)