1、QWidget
QWidget类是所有界面对象的基类,它是基础窗口部件,如下所示:
QWidget类的构造函数:QWidget(QWidget * parent = 0, Qt::WindowFlags f = 0); 其中parent指定父窗口(默认为0,QWidget为一个窗口,非0的话QWidget是一个子部件),WindowFlags指定窗口类型和标志,例如:QT::Widget默认类型(如果没有父窗口的话为独立窗口类型,有父窗口的话为子部件类型),QT::Window窗口类型,Qt::Dialog对话框类型,Qt::WindowStaysOnTopHint始终处于顶层,Qt::FramelessWindowHint无标题栏和边框样式,Qt::SplashScreen显示效果与FramelessWindowHint相同,但任务栏中无该部件图标,一般与WindowStaysOnTopHint配合用于欢迎界面,Qt::WindowMaximized最大化,Qt::WindowMinMaxButtonsHint具有最小最大化按钮。我们也可以在对象定义后调用QWidget::setWindowFlags来设置窗口类型和标志。
setWindowState()可以用来设置窗口的状态,如最小化,Qt::WindowMinimized,最大化Qt::WindowMaximized,全屏显示Qt::WindowFullScreen。这里需要注意的一点,如果部件是无边框的则最大化会全屏显示,我们可以通过QDesktopWidget::availableGeometry()函数获得不含任务栏的桌面大小,然后来设置部件最大化。
在QT的示例程序中有一个Window Flags程序演示了所有的窗口类型和标志,可以在Qt Creator的欢迎模式中找到该示例。
还有两点需要注意,如果一个父窗口部件我们没有指定大小,那么他的大小会由子部件大小决定。最后在释放对象的时候不必单独释放子部件,在释放父窗口部件的时候会自动释放其子部件(销毁一个QObject时其子对象也会被自动销毁,析构顺序为先执行父对象的析构方法,再执行子对象的析构方法)。
下面是一个示例和效果:
//#include <QApplication> //#include <QLabel> //#include <QWidget> #include <QtGui> //QApplication和GUI类头文件都包含在QtGui int main(int argc, char**argv) { QApplication app(argc, argv); //下面的widget和label都没有父窗口,关闭任何一个部件后另一个不受影响,仍然显示 QWidget* pWidget = new QWidget; pWidget->setWindowTitle(QString::fromUtf8("我是Widget")); //一般窗口都有边框和标题栏,可以通过QWidget构造函数的第二个参数进行设置 pWidget->show(); //没有设置位置的话默认居中显示,没有设置大小的话,如果当前有子部件会根据子部件设置大小,当前没有子部件的话默认设置一个大小 QLabel label; label.setWindowTitle(QString::fromUtf8("我是Label")); label.move(600,500); label.resize(180, 30); label.show(); QDialog dlg; dlg.setWindowTitle(QString::fromUtf8("我是Dialog")); dlg.resize(200, 150); dlg.show(); QLabel* pSubLabel = new QLabel(pWidget, Qt::FramelessWindowHint); pSubLabel->setText(QString::fromUtf8("我是子label")); pSubLabel->move(0, 0); pSubLabel->resize(100, 30); pSubLabel->show(); int ret = app.exec(); delete pWidget; //销毁父窗口的时候自动销毁子部件,所以不用再delete label2 return ret; }
2、布局、位置信息函数
因为窗口可能会带有框架,所以窗口的方法一类是包含框架的(x()、y()、pos()、frameGeometry()、move()),一类是不包含框架的(geometry()、width()、height()、rect()、size()),如下图所示:
QWidget* pt = new QWidget; //设置位置 pt->move(100, 200); //获得位置 QPoint p = pt->pos(); int x = pt->x(); int w = pt->width(); //设置大小 pt->resize(100, 200); pt->setFixedSize(100, 200); //获得大小 QSize s = pt->size(); //设置位置和大小 pt->setGeometry(10, 20, 100, 200); //获得位置和大小 QRect r = pt->geometry(); //r的x、y是相对于父窗口的坐标 r = pt->rect(); //r的x、y始终为0 x = pt->geometry().x(); w = pt->geometry().width();
3、模态对话框
#include "dialog.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); Dialog w; w.setWindowTitle(QString::fromUtf8("main")); /*****创建模态对话框的三种方法*****/ /*****关闭对话框可以使用done(int)或者使用accept()、reject()来隐藏对话框*****/ //调用对话框的exec,程序会一直等待exec方法返回才继续执行,所以w会在dlg关闭后才显示 QDialog dlg(&w); dlg.exec(); //调用对话框的setModal后再调用show,此时程序不会阻塞,w也会显示,但只有关闭dlg后才能操作w QDialog* dialog = new QDialog(&w); dialog->setModal(true); dialog->show(); //调用对话框的setWindowModality后再调用show,setWindowModality与setModal类似,当它可以设置阻塞窗口的类型: //Qt::ApplicationModal为阻塞所有窗口,相当于setModal(true) //Qt::WindowModal为阻塞父窗口、子窗口 //Qt::NonModal不阻塞窗口,相当于一个非模态对话框 QDialog* pDlg = new QDialog(&w); pDlg->setWindowModality(Qt::ApplicationModal); pDlg->show(); w.show(); return a.exec(); }
4、初识信号与槽
信号和槽用于两个对象之间的通信,例如用户单击了关闭按钮则希望执行窗口的close()方法来关闭窗口,只有QObject和其子类能够使用信号槽机制。使用回调方法也可以来实现两个对象之间的通信,但是回调方法只能是全局方法或类的静态成员方法,想要访问对象成员的话还需要向回调传入对象地址,实现比较麻烦。
使用信号和槽必须在类的声明中添加Q_OBJECT宏,对于我们自定义的类需要注意这点,如:
class MyClass
{
Q_OBJECT
public:
......
};
一个信号可以关联多个槽(槽执行顺序是随机的),多个信号也可以关联到一个槽上。
新建一个Qt Gui项目,基类选择QWidget,然后在设计模式下添加一个Push Button,我们想要对按钮的点击事件进行处理的话就需要添加信号和槽的处理,信号即按钮点击时候产生,然后执行槽来处理,即槽其实为一个执行函数。有四种方法来实现信号和槽:
①、
1)、在QWidget派生类的头文件和源文件中添加槽的声明和定义:
public slots:
void showChildDialog();
void MyWidget::showChildDialog()
{
......
}
2)、在QWidget派生类的构造函数中调用connect()将按钮的单击信号和槽进行关联,connect()的四个参数分别为发送信号的对象(指针)、发送的信号名(使用SIGNAL()宏获得)、接收信号的对象(指针,如果是当前对象即this的话该参数可以省略)、要执行的槽名(使用SLOT()宏获得):
connect(ui->showChildButton, SIGNAL(clicked(bool)), this, SLOT(showChildDialog(bool)));
信号与槽机制与回调方法相比另外不同的一点是可以指定槽方法的同步/异步执行,通过connect()方法的最后一个带默认参数的连接类型参数,以下为connect的连接类型参数的取值,默认为Qt::AutoConnection,即如果信号和槽在同一个线程中,发射完信号后会立即调用槽方法,即同步调用,如果信号和槽在不同的线程中,会在接收部件线程的事件循环中来执行槽,发射完信号后的代码也会立即执行,即为在其它线程中异步调用槽。
设置部件对象名可以通过Qt设计器中的属性一栏的ObjectName,还可以调用setObjectName()函数来实现。
信号不仅可以connect关联到槽方法上,还可以可以关联信号到另一个信号上:connect(myButton, SIGNAL(clicked()), this, SIGNAL(buttonClicked()));
②、在设计界面的菜单栏上选择"编辑信号/槽"模式,拖动按钮然后在当前主窗口上释放(想要调用哪个widget的槽方法就在哪个widget上释放),选择按钮的clicked信号,选择对应widget的槽方法,如果槽方法我们还没有定义的话点击下方法的 “编辑” 添加槽方法名称后还需再在widget类中添加这个槽方法的定义。在选择信号和槽的对话框上有一个“显示从QWidget继承的信号和槽”的选项,如下图所示,勾选它后可以看到多了一些信号和槽方法,我们可以直接选择QWidget中一些成员槽函数来直接作为槽方法,这样就不用自己再定义槽方法。
下面是QWidget和Qdialog中一些成员槽函数的说明:
QWidget::close(); //关闭部件
QWidget::show(); //显示部件
QDialog::accept(); //对于执行exec()产生的模态对话框隐藏,即使对话框的exec()函数返回,且返回值为QDialog::Accepted
QDialog::reject(); //对于执行exec()产生的模态对话框隐藏,即使对话框的exec()函数返回,且返回值为QDialog::Rejected
③、上面这种将信号和槽进行关联的方法是手动关联,还有一种简单的 “自动关联” 的方式,只需将槽的函数名设置为on + 发送信号的对象名 + 信号名,而不再需要connect()函数,eg: void on_showChildButton_clicked();
使用这种方式的话必须要在ui->setupUi(this)语句之前来定义部件(因为在setupUi方法中调用了conncetSlotsByName方法来支持信号和槽的自动关联),并且设置部件的objectName,如下所示:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { QPushButton* pushBtn = new QPushButton(this); pushBtn->setObjectName("myPushBtn"); ui->setupUi(this); }
如果是对于自定义的信号,则只能使用手动关联的方法。
④、在设计界面右击Button->转到槽->选择clicked()信号->点击确定。与第二种方法相比,这种方法自动选择了Button的父窗口为执行槽的对象,然后在父窗口类中生成了槽方法而不用我们自己再定义槽方法。这种方法
实际上就是替我们实现了第③种方法中所说的对信号和槽的“自动关联”方式。
自动生成的槽方法是按照"on_对象名_信号名"来生成的,比如void on_pushButton_clicked(),我们可以修改部件的objectName对象名来定制自动生成的槽方法的名称。
⑤、第五种方法是针对自定义信号执行槽方法的实现,一般使用它来实现两个对象之间的通信。如以下实现当Dialog中的Button点击时,向Widget发送信号来执行指定的槽:
首先,使用信号和槽必须在类的声明中添加Q_OBJECT宏,然后在Dialog中使用 signals 来声明自定义的信号(信号只需要进行声明,不用实现),signals类似于protected,即只有当前类和子类能够发射该信号:
//mydialog.h class myDialog : public QDialog { Q_OBJECT ...... signals: void dlgReturn(int);//声明自定义的信号 };
然后,在Dialog上按钮点击信号的槽里使用 emit 来发送自定义的信号:
//mydialog.cpp void myDialog::on_pushButton_clicked() { int iValue = 100; emit dlgReturn(iValue);//发送自定义的信号,信号中的参数可以多于槽中的参数 close(); }
然后,在Widget上添加自定义信号的槽的声明和实现:
//mywidget.h class myWidget : public QWidget { Q_OBJECT ...... private slots: void receiveValue(int value); //自定义信号的槽的声明 }; //mywidget.cpp void myWidget::receiveValue(int value)//自定义信号的槽的定义 { qDebug() << "get value: " << value; //qDebug()可以向应用程序输出栏输出各种格式,如int、string、QObject(输出对象名)、QObjectList等。 }
最后,在Widget的构造函数中利用connect()实现自定义信号和槽的关联:
//mywidget.h #include "mydialog.h" myWidget::myWidget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); myDialog* dlg = new myDialog(this); connect(dlg, SIGNAL(dlgReturn(int)), this, SLOT(receiveValue(int)));//实现自定义信号和槽的关联 dlg->show(); }
在槽方法中可以通过QObject::sender()方法来获得发送信号的部件指针,如下所示:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); QObject::connect(ui->pushButton_1, SIGNAL(clicked()), SLOT(on_pushButton_clicked())); QObject::connect(ui->pushButton_2, SIGNAL(clicked()), SLOT(on_pushButton_clicked())); QObject::connect(ui->pushButton_3, SIGNAL(clicked()), SLOT(on_pushButton_clicked())); } void MainWindow::on_pushButton_clicked() { QObject* obj = sender(); //获得发送信号的部件 if(obj == ui->pushButton_1) { //Todo } else if(obj == ui->pushButton_2) { //Todo } else if(obj == ui->pushButton_3) { //Todo } }
使用QSignalMapper信号映射器可以为信号添加int、QString、QObject*或QWidget*类型的参数:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); QSignalMapper* signalMapper = new QSignalMapper(this); //创建信号映射器 connect(ui->pushButton_1, SIGNAL(clicked()), signalMapper, SLOT(map())); //绑定按钮点击信号到信号映射器上 signalMapper->setMapping(ui->pushButton_1, ui->pushButton_1->text()); //设置信号参数 connect(ui->pushButton_2, SIGNAL(clicked()), signalMapper, SLOT(map())); signalMapper->setMapping(ui->pushButton_2, ui->pushButton_2->text()); connect(ui->pushButton_3, SIGNAL(clicked()), signalMapper, SLOT(map())); signalMapper->setMapping(ui->pushButton_3, ui->pushButton_3->text()); connect(signalMapper, SIGNAL(mapped(QString)), SLOT(on_pushButton_clicked(QString))); //绑定信号映射器的mapped信号到当前部件 } void MainWindow::on_pushButton_clicked(QString strParam) //带QString类型参数 { QObject* obj = sender(); if(obj == ui->pushButton_1) { //Todo } else if(obj == ui->pushButton_2) { //Todo } else if(obj == ui->pushButton_3) { //Todo } }
通过以上例子可以看到,使用QSignalMapper的好处是可以给clicked()信号添加一个参数,有时候这样很方便,比如下面直接将参数设置为textEdit部件的显示文本,而不用再在槽方法中进行设置:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); QSignalMapper* signalMapper = new QSignalMapper(this); //创建信号映射器 connect(ui->pushButton_1, SIGNAL(clicked()), signalMapper, SLOT(map())); //绑定按钮点击信号到信号映射器上 signalMapper->setMapping(ui->pushButton_1, ui->pushButton_1->text()); //设置信号参数 connect(ui->pushButton_2, SIGNAL(clicked()), signalMapper, SLOT(map())); signalMapper->setMapping(ui->pushButton_2, ui->pushButton_2->text()); connect(ui->pushButton_3, SIGNAL(clicked()), signalMapper, SLOT(map())); signalMapper->setMapping(ui->pushButton_3, ui->pushButton_3->text()); //绑定信号映射器的mapped信号到textEdit部件上的设置编辑器文本的槽方法 connect(signalMapper, SIGNAL(mapped(QString)), ui->textEdit, SLOT(setText(QString))); }