1 需求
设计两个窗口,每个窗口中都包含一个按钮,最开始的时候只显示窗口1,当按下窗口1中的按钮时,窗口1隐藏(hide),同时窗口2显示(show),当按下窗口2中的按钮时,窗口2隐藏,窗口1显示。
2 分析
这个案例最关键的是,如何让两个窗口建立连接(connect),最开始学QT的时候,我们都是在Widget的构造函数中调用connect函数,但这里不能直接这样做,因为创建的时候,你拿不到另一个Widget对象,如果通过构造函数的参数传进来,则传进来的那个Widget该如何构建?解决这个问题可以使用两种方法:
1 重载构造函数,例如w1通过无参构造函数创建,w2通过有参,w2在创建的时候将w1传进去,然后在有参构造函数中建立联系(connect)
2 新建一个类A,这个类A必须直接或间接继承QObject,这样才能获得connect函数(因为connect是QObject的成员函数),然后将w1和w2都作为A的成员变量,并在A的构造函数中实现两个窗口的连接
需要注意的一点是,信号的发送者是QPushButton指针,而不是窗口对象,并且,为了建立两个窗口的联系,最好将QPushButton指针作为Widget类的成员变量,这样通过窗口就能拿到信号发送者,详见下面的代码。
还有第三种方法,就是使用自定义信号来实现,新建一个窗口类MyWidget,并增加一个信号函数,在构造函数中将按钮与自定义信号建立联系,然后在另一个窗口类的构造函数中,新建一个MyWidget对象,然后建立两个窗口的联系,这个用来练习自定义信号函数。
3 第一种方法
widget.h内容如下:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "QPushButton"
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
Widget(const Widget& w2, QWidget *parent = nullptr);
public:
QPushButton * btn;
};
#endif // WIDGET_H
widget.cpp内容如下,为什么用lambda表达式不行,我想了很久都不知道,最后没有使用lambda表达式,不过后面我找到原因了,就是const,具体问题可以看注释:
#include "widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
btn = new QPushButton("button", this);
btn->move(300, 300);
}
Widget::~Widget()
{
}
Widget::Widget(const Widget& another_w, QWidget *parent) : QWidget(parent)
{
this->btn = new QPushButton("button", this);
this->btn->move(300, 300);
/*
//下面的代码为何会报错?
connect(this->btn, &QPushButton::clicked, [=](){
this->hide();
another_w.show();
});
//another_w在lambda函数中是常对象,不能调用非常成员函数,this是常指针,因此可以调用hide
//1 能不能添加mutable选项(即[=]()mutable),让another_w可以在函数内部调用show?
// 不能,因为another_w是通过构造函数传入,它已经被声明为了const,这里用mutable会冲突
//2 能不能在调用connect之前,对another_w先进行取址操作,然后在lambda函数内部通过指针调用show,像this一样?
// 不能,因为another_w在传入时就已经被声明为了const,无法进行取址操作,除非修改函数声明和定义,去掉const
*/
connect(this->btn, &QPushButton::clicked, this, &QPushButton::hide);
connect(this->btn, &QPushButton::clicked, &another_w, &QPushButton::show);
connect(another_w.btn, &QPushButton::clicked, &another_w, &QPushButton::hide);
connect(another_w.btn, &QPushButton::clicked, this, &QWidget::show);
}
main.cpp内容如下:
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w1;
Widget w2(w1);
w1.setGeometry(100, 100, 600, 600);
w1.setWindowTitle("窗口1");
w2.setGeometry(1000, 100, 600, 600);
w2.setWindowTitle("窗口2");
w1.show();
return a.exec();
}
4 第二种方法
widget.h内容:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "QPushButton"
class widget : public QWidget
{
Q_OBJECT
public:
explicit widget(QWidget *parent = nullptr);
public:
QPushButton* btn;
signals:
};
#endif // WIDGET_H
widget.cpp内容
#include "widget.h"
widget::widget(QWidget *parent) : QWidget(parent)
{
btn = new QPushButton("button", this);
btn->move(300, 300);
}
mainwindow.h 内容如下:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "widget.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
public:
widget w1, w2;
};
#endif // MAINWINDOW_H
mainwindow.cpp内容如下:
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
connect(w1.btn, &QPushButton::clicked, [=](){
//如果写成 [w1, w2]企图捕获类的成员变量w1和w2,会报错
//mainwindow.cpp:6:45: error: 'w2' in capture list does not name a variable
this->w1.hide();
this->w2.show();
});
connect(w2.btn, &QPushButton::clicked, [this](){
//将this指针写到捕获参数列表时,函数内部可以直接使用成员函数和成员变量
w2.hide(); //使用w2和w1,无需通过this指针
w1.show();
});
w1.setGeometry(100, 100, 600, 600);
w1.setWindowTitle("窗口1");
w2.setGeometry(1000, 100, 600, 600);
w2.setWindowTitle("窗口2");
}
MainWindow::~MainWindow()
{
}
main.cpp内容如下:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow mw;
mw.w1.show();
return a.exec();
}
5 第三种方法
Widget.h内容不修改,用自动生成的:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
};
#endif // WIDGET_H
然后新建一个名为MyWidget类,mywidget.h内容如下:
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = nullptr);
signals:
void btnClicked();
};
#endif // MYWIDGET_H
mywidget.cpp内容如下:
#include "mywidget.h"
#include "QPushButton"
MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{
//窗口设置
this->setGeometry(100, 100, 600, 600);
this->setWindowTitle("窗口2");
//新建按钮
QPushButton* btn = new QPushButton("button", this);
btn->move(300, 300);
//将按钮与自定义信号建立关系
connect(this, &MyWidget::btnClicked, [=](){
emit this->btnClicked();
});
}
Widget.cpp内容还没介绍,刚刚只是演示了头文件,其定义如下:
#include "widget.h"
#include "mywidget.h"
#include "QPushButton"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//窗口设置
this->setGeometry(100, 100, 600, 600);
this->setWindowTitle("窗口1");
//新建按钮
QPushButton* btn = new QPushButton("button", this);
btn->move(300, 300);
MyWidget* w2 = new MyWidget();
connect(btn, &QPushButton::clicked, [=](){
this->hide();
w2->show();
});
connect(w2, &MyWidget::btnClicked, [=](){
w2->hide();
this->show();
});
}
Widget::~Widget()
{
}
由于w2是顶层窗口,且开辟在堆中,因此容易出现内存泄漏,可以将其改成通过参数传入到构造函数中,也可以将其作为Widget的成员变量(将指针作为成员变量),然后在析构函数中释放,这里可以回头自己演示。
第三种方法的设计思想比较巧妙,最好学会。