一、编译方式
1、Qt内部编码方式:unicode编码
可以表示所有常见的文件,提供世界上几乎所有的文字的唯一编码方式。具有通用性;具有两字节和四字节标准。
但是我们在编写代码时,不能采用unicode编码,因为unicode编码每个字符都采用两个字符表示,但是char类型只有一个字节。多余的用0表示,但是字符串的结束标志就是空,所以不能用unicode编码方式来写代码。
查看QString帮助,可以看到,它也是提供的unicode编码。在QT中所有字符串都要转换成QString来存储,而字符是QChar(即unicode)
查看参数为常字符串的构造函数,可以看到它使用fromUtf8()函数将我们输入的字符串(Utf-8)转换成unicode字符串。即在构造函数中,进行了编码方式转换。
如果在不同的系统中,不经过编码转换将会发生乱码。
例:
将设置虚拟机与宿主机的共享文件夹,将QT下昨天编写的源文件拷贝到Linux的共享文件夹下,用在Windows下用记事本打开,再点击另存为,可以看到默认的编码方式默认为UTF-8,将其修改为ANSI(GBK编码),保存并替换,此时编码方式已经改变为GBK编码。回到Linux将共享文件替换原来的源文件。再编译将出现执行可执行程序,将出现乱码现象。GBK的编码使用fromUtf8()函数无法完成正常转换。
注意:
不同的编码方式,对中文的影响较大,因为中文的字符个数较多。但是英文基本不受编码方式影响。
2、手动编码方式转换
(1)QTextCodec类(最后一个字母是转换的缩写,即文本编码转换)
(2)实例
...表示当前编辑器默认的编码方式,这里为KOI8-R。也可以直接将字符串放在第三行的参数中
第二句表示获取一个KOI8-R的对象
第三句表示将这种编码的字符串转换为unicode编码
如果是想将GBK编码转换为unicode编码,只需将KOI8-R替换为GBK即可。
注意:
在QT5中,QString内部可以通过类型转换构造函数,将utf8编码自动转换成unicode编码。其他编码需要手动转换为unicode。
二、信号和槽
1、基本概念
信号和槽是QT自行定义的一种通信机制,实现对象之间的数据交互。如:实现点击按钮,关闭标签。
其本质就是函数的调用,一个信号发出,连接到该信号的槽函数将被执行。
2、定义方式
class xx{
Q_OBJECT//元对象编译器,使不符合标准C++的语法转换成符合标准C++的语法
signals://QT中定义的关键字
void sig_func(..);//信号函数,信号函数只能被声明,不能被定义,因为他是执行时动态调用槽函数。
public slots://slots为QT中定义的关键字
void sig_func(...);//槽函数
};
3、建立信号和槽的连接
使用如下的函数,它是一个静态成员函数。信号连接发生在运行期间,这一点需要注意。
注意:
信号函数和槽函数是字符串,即函数签名,不是函数的指针。即按字符串形式处理。
QT提供两个宏可以将信号函数或者槽函数转换为const char*指针:
SIGNAL(信号函数);//将信号函数转换为const char*
SLOT(槽函数);//将槽函数转换为const char*
参数:
sender:信号发送的指针
signal:信号函数
receiver:信号接收的对象指针
method:槽函数
#include#include #include #include int main(int arg,char* argv[]){ QApplication app(argc,argv); QLabel label("Hello QT!"); QPushButton button("关闭标签"); label.show(); button.show(); //连接信号和槽函数 QObject::connect(&button,SIGNAL(clicked()),&label,SLOT(close())); return app.exec(); }
练习:
实现点击按钮,关闭应用
思路:在帮助文档中查找QApplication及其基类相关可以退出或者关闭的函数,可以查找到:
void closeAllWindows();
和上一层基类中的
void quit();
#include#include #include #include int main(int arg,char* argv[]){ QApplication app(argc,argv); QLabel label("Hello QT!"); QPushButton button("关闭标签"); label.show(); button.show(); //连接信号和槽函数 QObject::connect(&button,SIGNAL(clicked()),&app,SLOT(closeAllWindows())); // QObject::connect(&button,SIGNAL(clicked()),&app,SLOT(quit())); return app.exec(); }
4、信号和槽函数连接的语法要求
1)一般信号和槽函数参数顺序和类型要相同
QObject::connect(&button,SIGNAL(sig_func(int)),&app,SLOT(slot_func(int)));//ok QObject::connect(&button,SIGNAL(sig_func(int)),&app,SLOT(slot_func(int,string)));//error
2)信号函数和槽函数可带有缺省参数
QObject::connect(&button,SIGNAL(sig_func(int)),&app,SLOT(slot_func(int,string="")));//ok
3)信号函数的参数可以多于槽函数
QObject::connect(&button,SIGNAL(sig_func(int,string)),&app,SLOT(slot_func(int)));//ok多于的参数将被忽略
4)一个信号可以连接到多个槽函数,信号发生时,都会被执行
QObject::connect(&button,SIGNAL(sig_func(int)),&app1,SLOT(slot_func1(int)));//ok QObject::connect(&button,SIGNAL(sig_func(int)),&app2,SLOT(slot_func2(int)));//ok
5)多个信号可以对应同一个槽函数,无论谁发了信号,槽函数都会被执行
QObject::connect(&button1,SIGNAL(sig_func1(int)),&app,SLOT(slot_func(int)));//ok QObject::connect(&button2,SIGNAL(sig_func2(int)),&app,SLOT(slot_func(int)));//ok
三、滑块类(QSlider)、微调框/选值框(QSpinbox)
1、<案例>事件同步
(1)滑块类
构造函数
槽函数
setRange(int min,int max);//设置滑动范围 setRange(int);//设置滑块位置
信号函数
valueChanged(int value);//滑块滑动时发送位置信号
(2)微调框/选值框(QSpinbox)
构造函数:
QSpinbox(QWidget* parent=0);
其他:
setRange(int minimum,int maxmum);//设置选值的范围
槽函数:
setValue(int val);
信号函数:
valueChanged(int i);发送值改变的信号 valueChanged(const QString& text);
#include#include #include int main(int arg,char* argv[]){ QApplication app(argc,argv); //创建滑块组件,并设置其属性 QSlider slider(Qt::Horizontal);//设置为水平滑块 slider.setRange(0,100);//设置范围 slider.show() //创建选值框 QSpinbox spin; spin.setRange(0,100); spin.show(); //连接信号和槽函数,相互连接 QObject::connect(&slider,SIGNAL(valueChanged(int)),&spin,SLOT(setValue(int))); QObject::connect(&spin,SIGNAL(valueChanged(int)),&slider,SLOT(setValue(int))); return app.exec(); }
因为信号发送和槽函数接收是动态的,所以在它们出现拼写错误时,编译不会报错,执行时才会出现问题。如果执行时出现问题可以考虑是否是这个问题造成的。
三、容器窗口(父窗口)与事件同步
1、父窗口
如果一个组件在创建时指定了父窗口,那么他就会停靠在父窗口上面,如果不指定,就会游离在外部形成独立的窗体。
(1)常用的父窗口类
QWidget(绝大多数图形组件的基类,它的成员函数子类都可以使用)、QMainWindow(QWidget的子类,一般是主界面使用此类)、QDialog(也是QWidget的子类,一般对话框就是用这个类)是比较常用的父窗口类
在实际的开发中,一般使用后两者比较多。
(2)QWidget基类中两个常用的成员函数
resize(int x,int y);调整大小,单位为分辨率 move(int x,int y);//调整位置
注意:
对于父窗口对象,起始位置是相对于屏幕左上角,如果是父窗口中的停靠组件,则相对的是父窗口的左上角。
#include#include #include #include #include //默认左上角显示,也可以使用QDialog(默认居中显示,没有最大化,最小化)和QMianWindow(默认左上角显示)类 int main(int arg,char* argv[]){ QApplication app(argc,argv); //创建一个父窗口对象 QWidget parent; //设置父窗口大小,200*200像素 parent.resize(200,200); //调节父窗口位置 parent.move(300,300); //创建lable和button时指针停靠在parent父窗口 QLabel label("Hello QT!",&parent/*指定父窗口*/); label.move(0,100);//垂直方向不变,水平方向向右移动100像素 QPushButton button("关闭标签",&parent); button.move(100,100);//移动按钮 //label.show(); //button.show(); //显示父窗口,它所包含的组件也会一起显示 parent.show();//主窗口必须显示 //连接信号和槽函数 QObject::connect(&button,SIGNAL(clicked()),&app,SLOT(closeAllWindows())); // QObject::connect(&button,SIGNAL(clicked()),&app,SLOT(quit())); return app.exec(); }
补充:使用付窗口的优点
(1)布局看起来更为美观,更容易管理
(2)因为有时候会动态分配内存,但是可能忘记释放。使用父窗口可以不用担心这个问题,因为主窗口的析构函数会调用所有组件的析构函数,进行内存释放。
四、面向对象的QT编程
1、基于对象的QT编程
因为我们是使用QT已经封装好的类,但是只能在外部使用其公有的成员,限制较大,不推荐。例如前面的的所有案例都是基于对象的编程。
2、面向对象的QT编程
<案例>实现加法计算器
思路:
封装一个类:
class CalculatorDialog:public QDialog{ 行为: 构造函数;初始化图形界面(UI) 槽函数:当输入左右操作数时,使能等号按钮 槽函数:点击等号按钮时调用,计算结果 属性: QLineEdit QPushButton QLabel }; int main(int argc,char** argv){ QApplication app(argc,argv); CalculatorDialog cal; cal.show() return app.exec(); }
实现代码:
//CalculatorDialog.h文件
#ifndef __CalculatorDialog__ #define __CalculatorDialog__ #include#include #includeQ #include //水平布局器,用于自动调整组件的大小和位置 #include //验证器,输入组件只能输入数字 #include class CalculatorDialog:public Dialog{ Q_OBJECT //元对象,使用MOC编译器声明QT语法转换为标准C++ public: CalculatorDialog(void); private slots: //使能等号按钮 void enbleCalculation(void); //计算结果 calcClicked(); private: QLineEdit* m_editX;//左操作数 QLineEdit* m_editY;//右操作数 QPushButton* m_btnCalc;//等号按钮 QLineEdit* m_edit;//保存结果 }; #endif
扩展:
使用元对象编译器可以生成moc开头的文件,再将这些文件通过标准C++编译器编译。
//CalculatorDialog.cpp文件 #include "CalculatorDialog.h" //构造函数 CalculatorDialog::CalculatorDialog(void){ //设置标题 setWindowTitle("计算器"); //创建编辑框(左右操作数) m_editX =new QLineEdit(this);//this指向计算机父窗口 //设置对齐方式 m_editX->setAlignment(Qt::AlignRignt);//设置右对齐 //设置验证器,让其只能输入数字 m_editX->setValidator(new QDoubleValidator(this)); m_editY =new QLineEdit(this); //设置对齐方式 m_editY->setAlignment(Qt::AlignRignt);//设置右对齐 //设置验证器,让其只能输入数字 m_editY->setValidator(new QDoubleValidator(this)); m_editZ =new QLineEdit(this); //设置对齐方式 m_editZ->setAlignment(Qt::AlignRignt);//设置右对齐 //输出结果只读 m_editZ->setReadOnly(true); //创建等号 m_btnCalc=new QPushButton("=",this) //设置默认属性为禁用 m_btnCalc->setEnanled(false); //创建水平布局器,将所有的组件从左至右添加到布局器中,它将自动调节每个组件的大小和位置 QHBoxLayout* layout =new QHBoxLayout(this); //为水平布局器添加组件 layout->addWidget(m_editX);//左操作数 layout->addWidget(new QLabel('+'));//加号 layout->addWidget(m_editY);//右操作数 layout->addWidget(m_btnCalc);//等号 layout->addWidget(m_editZ);//结果(只读) //设置启用布局器 setLayout(layout); //QLineEdit组件中输入时,将发送信号:textChanged connect(m_editX,SIGNAL(textChanged(const string&)),this,SLOT(enableCalcButton())); connect(m_editX,SIGNAL(textChanged(const string&)),this,SLOT(enableCalcButton())); //点击=号按钮发送clicked信号 connect(m_btnCalc,SIGNAL(clicked()),this,SLOT(calcClicked())); } //使能等号按钮的槽函数 void CalculatorDialog::enableCalcButton(void){ //检查左右操作数是否为有效数字 bool bXOk; bool bYOk; //text():获取QLineEdit输入的数据,返回值是QString类型 //toDouble():将QString类型转换成double类型,参数是值结果参数,表示转换是否成功 m_editX->text().toDouble(&bXOk); m_editX->text().toDouble(&bXOk); //设置=号按钮 m_btnCalc->setEnabled(bXOk&&bYOk); } //计算结果的槽函数 void CalculatorDialog::calcClicked(void){ //转换为double并计算 double res=m_editX->text().toDouble()+m_editY->text().toDouble(); //将double转换为字符串文本 QString str=QString::number(res,'g',15); //显示文本 m_editZ->serText(str); }
//Calculator.cpp主函数文件 #include#include"CalculatorDialog.h" int main(int argc,char** argv){ QApplication app(argc,argv); CalculatorDialog calc; calc.show() return app.exec(); }