天天看点

QT-编码方式,信号和槽,容器窗口与事件同步,面向对象的QT编程(day2)

一、编译方式

1、Qt内部编码方式:unicode编码

  可以表示所有常见的文件,提供世界上几乎所有的文字的唯一编码方式。具有通用性;具有两字节和四字节标准。

  但是我们在编写代码时,不能采用unicode编码,因为unicode编码每个字符都采用两个字符表示,但是char类型只有一个字节。多余的用0表示,但是字符串的结束标志就是空,所以不能用unicode编码方式来写代码。

  查看QString帮助,可以看到,它也是提供的unicode编码。在QT中所有字符串都要转换成QString来存储,而字符是QChar(即unicode)

QT-编码方式,信号和槽,容器窗口与事件同步,面向对象的QT编程(day2)
  查看参数为常字符串的构造函数,可以看到它使用fromUtf8()函数将我们输入的字符串(Utf-8)转换成unicode字符串。即在构造函数中,进行了编码方式转换。
QT-编码方式,信号和槽,容器窗口与事件同步,面向对象的QT编程(day2)

  如果在不同的系统中,不经过编码转换将会发生乱码。

例:

  将设置虚拟机与宿主机的共享文件夹,将QT下昨天编写的源文件拷贝到Linux的共享文件夹下,用在Windows下用记事本打开,再点击另存为,可以看到默认的编码方式默认为UTF-8,将其修改为ANSI(GBK编码),保存并替换,此时编码方式已经改变为GBK编码。回到Linux将共享文件替换原来的源文件。再编译将出现执行可执行程序,将出现乱码现象。GBK的编码使用fromUtf8()函数无法完成正常转换。

QT-编码方式,信号和槽,容器窗口与事件同步,面向对象的QT编程(day2)

  

注意:

  不同的编码方式,对中文的影响较大,因为中文的字符个数较多。但是英文基本不受编码方式影响。

2、手动编码方式转换

(1)QTextCodec类(最后一个字母是转换的缩写,即文本编码转换)

   

QT-编码方式,信号和槽,容器窗口与事件同步,面向对象的QT编程(day2)

(2)实例

  ...表示当前编辑器默认的编码方式,这里为KOI8-R。也可以直接将字符串放在第三行的参数中

  第二句表示获取一个KOI8-R的对象

  第三句表示将这种编码的字符串转换为unicode编码

QT-编码方式,信号和槽,容器窗口与事件同步,面向对象的QT编程(day2)

  如果是想将GBK编码转换为unicode编码,只需将KOI8-R替换为GBK即可。

QT-编码方式,信号和槽,容器窗口与事件同步,面向对象的QT编程(day2)
  在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-编码方式,信号和槽,容器窗口与事件同步,面向对象的QT编程(day2)

 注意:

  信号函数和槽函数是字符串,即函数签名,不是函数的指针。即按字符串形式处理。

  QT提供两个宏可以将信号函数或者槽函数转换为const char*指针:

SIGNAL(信号函数);//将信号函数转换为const char*

SLOT(槽函数);//将槽函数转换为const char*

参数:

  sender:信号发送的指针

  signal:信号函数

  receiver:信号接收的对象指针

  method:槽函数

#include<QApplication>

#include<QLabel>

#include<QPushButton>

#include<QObject> 



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<QApplication>

#include<QLabel>

#include<QPushButton>

#include<QObject> 

 

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)滑块类

QT-编码方式,信号和槽,容器窗口与事件同步,面向对象的QT编程(day2)

  构造函数

QT-编码方式,信号和槽,容器窗口与事件同步,面向对象的QT编程(day2)

槽函数

setRange(int min,int max);//设置滑动范围

setRange(int);//设置滑块位置      

信号函数

valueChanged(int value);//滑块滑动时发送位置信号

(2)微调框/选值框(QSpinbox)

QT-编码方式,信号和槽,容器窗口与事件同步,面向对象的QT编程(day2)

构造函数: 

QSpinbox(QWidget* parent=0);      

其他:

setRange(int minimum,int maxmum);//设置选值的范围      

槽函数:

setValue(int val);      

信号函数:

valueChanged(int i);发送值改变的信号

valueChanged(const QString& text);      
#include<QApplication>

#include<QSlider>

#include<QSpinbox>
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<QApplication>

#include<QLabel>

#include<QPushButton>

#include<QObject> 

#include<QWidget>//默认左上角显示,也可以使用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编程
<案例>实现加法计算器

思路:
        
QT-编码方式,信号和槽,容器窗口与事件同步,面向对象的QT编程(day2)

封装一个类:

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<QDialog>

#include<QLineEdit>

#includeQ<PushButton>

#include<QLabel>



//水平布局器,用于自动调整组件的大小和位置

#include<QHBoxLayout>



//验证器,输入组件只能输入数字

#include<QDoubleValidator>




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      
QT-编码方式,信号和槽,容器窗口与事件同步,面向对象的QT编程(day2)

扩展:

  使用元对象编译器可以生成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<QApplication>
#include"CalculatorDialog.h"


int main(int argc,char** argv){
  QApplication app(argc,argv);
  
  CalculatorDialog calc;
  calc.show()
  
  return app.exec();
}      
QT-编码方式,信号和槽,容器窗口与事件同步,面向对象的QT编程(day2)

继续阅读