【题目应该叫,如何在面试的前一天临时抱佛脚】
【抱佛脚失败】
1,文件目录
项目名称
——pro文件,里面有基础的版本信息,一般不改
——头文件,和c++一样,自动生成
——资源文件
main.cpp :程序入口
mainwindow : 窗体文件,代码在里面
——组成
里面放着ui
2,写代码!
①写个摁钮吧
两种写法,一种是
QPushButton *b = new QPushButton();
然后在后面设定
b -> setParent(this); //继承的窗体
b -> setText("first"); //摁钮内容
或者直接就
QPushButton *b = new QPushButton("first",this);
②一些基础的函数
setFixedSize(400,500); // 设置窗体大小
resize(100,100); //设置摁钮初始位置
b -> move(200,100); //改变b摁钮的位置
setWindowTitle("firstwindow"); //修改窗体名称
b -> resize(100,100);
3,对象树
【别人的对象都有树啦!】
qt里的存储方式是对象树——被继承的窗体为父节点,新创建的构件是子节点,这样,如果删除一个父节点,下面的构件就可以自行删除。
【所以,在代码里不用删除new的对象】
注意,和c++一样,对象树的构建从上到下,析构从下到上
4,信号与槽【据说这是qt的核心问题】
connect(信号的发送者,发送的具体信号,信号的接受者,信号的处理(槽函数))
信号槽的优点:松散耦合,发送端和接收端本来是没有优点的
具体的写一个
connect(c,&QPushButton :: clicked,this,&MainWindow :: close);
//记得要加&
5,自定义信号与槽
①信号
信号写在发送端的头函数里,signals下,返回值是void,可有参数,不需要实现。
比如
signals:
void hungry();
②槽函数
槽函数的声明写在接收端的头文件slot中,返回值是void,可有参数,需要被实现。
比如
person.h
public slots:
void treat();
person.c
void person::treat()
{
qDebug << "给它恰!";
}
③在窗体函数里生成两个对象
【能不能给我生成一个对象】
【要声明哦】
mainwindow.h
private:
person *p;
dog *d;
mainwindow.c
this -> p = new Person(this);
this -> d = new Dog(this);
④做连接
这个连接,不是一时的冲动,是长长久久的连接
格式同上部分,直接给代码啦
connect(d,&Dog::hungry(),p,&Person::treat());
⑤做行动,触发连接
像普通的函数一样
在窗体的头文件里声明,在.c里面实现并调用
具体代码如下
MainWindow.h
private void morning();
MainWindow.c
void MainWindow::morning()
{
emit d -> hungry();
}
MainWindow::MainWindow()
{
……
connect(d,&Dog::hungry(),p,&Person::treat());
morning();
}
这样就可以成功触发啦!
关于信号与槽的重载
信号和槽都是可以带参数的,比如我们在狗狗类里设置这样一个信号,跟之前的函数放在一起
dog.h
void hungry();//之前的
void hungry(QString food);
相同的,在主人类中
person.h
void treat();//之前的
void treat(QString food);
槽是要实现的
void Person::treat()//之前的
{
qDebug()<< "喂食";
}
void Person::treat(QString food)
{
qDebug()<< "喂食" << food;
}
嗯……之后调用触发
MainWindow.c
void MainWindow::morning()
{
emit d -> hungry("meat");
}
然而现在并不能实现,因为每个函数我都设置了带参和不带参的两个同名函数,所以,这个时候需要函数指针出场。
void(Dog :: *dogsignal)(QString) = &Dog :: hungry();
void(Person :: *personslot)(QString) = &Person :: treat();
connnect(d,dogsignal,p,personslot);
这样就解决了信号槽的重载问题
嗯……还有一个小问题,在person中带参信号传递那里
如果我们这么写,出来的结果是
混入了一个""
原因大概是QString屏幕打印的问题,要把它转化成char*
方法如下
void Person::treat(QString food)
{
qDebug()<< "喂食" << food.toUtf8().data();
}
信号触发信号
想做一个名为早上的摁钮,点击触发主人喂食
首先写一个摁钮,方法和第一部分一样
MainWindow.c
QPushButton *b = new QPushButton("morning",this);
b -> resize(100,80);
比较复杂的办法是再写一个连接,摁钮点击连接morning函数,然后morning函数跟之前一样,触发狗狗饿了,这里不提。
我们搞一个快捷一点的,用信号触发信号
MainWindow.c
void(Dog::*dogsignal2)(void) = &Dog::hungry;
void (Person::*personslot2)(void) = &Person::treat;
connect(d,dogsignal2,p,personslot2);
connect(b,&QPushButton::clicked,d,dogsignal2);
顺便一提,断开信号是用disconnect,后面的参数和connect相同。
信号槽相关扩展
1,一个信号可以连接多个槽函数
2,信号和槽可以是多对多的关系
3,信号和槽函数的参数,必须类型一一对应
4,信号和槽的参数数量可以不同,但只能是信号的参数数量大于槽的参数数量。
所以刚刚要用无参数的槽匹配点击摁钮信号,因为那个信号函数参数是bool,数量是1,如果用有参数的那个,不适配。
5,qt4.0中的连接简便写法
非常之简单
connect(d,SIGNAL(hungry()),p,SLOT(treat()));
但是有一定的缺陷,不建议使用。
lambda表达式
我曾经在之前写上升下降字符串里面提过匿名函数的写法,这里再细致的写一点。
所谓匿名函数,就是在函数内部再写一个函数,特点是易于传参和节省空间。
【缺点是,一般情况下,我都记不得要用它……】
格式如下
auto func = [] (int i)
{
//函数内容
};
在这里,我们把它塞进信号里,所以就不用搞返回值,直接用基础格式就完事了。
基础格式:[] () {};
非常之简洁和好记——第一个中括号表示匿名函数的身份,第二个传参(可不传),第三个里面写函数主体。
这个东西,非常好用!因为可以在函数里面写多个槽,还可以触发传参不匹配的槽!
例子如下,前文和之前一样,主人和狗狗
注意:当使用值传递时,如果要改变参数的值,要加入mutable参数,不然会报错。
mutable在()后。
connect(b,&QPushbutton::clicked,this,[=]()
{
this -> close();
emit d -> hungry();
});
【一般都用=,不用&】
第一章作业
写一个窗体,里面有两个摁纽,一个写open,一个写close
点击open出现另一个窗体,点击close关闭
代码如下:
【我先用添加文件创建了一个窗体,叫dialog】
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QPushButton *op = new QPushButton("open",this);
op -> move(50,100);
QPushButton *cl = new QPushButton("close",this);
cl -> move(200,100);
Dialog *d = new Dialog();
d -> move(300,200);
connect(op,QPushButton::clicked,d,[=](){d -> show();});
connect(cl,QPushButton::clicked,d,[=](){d -> close();});
}