对话框的显示分模态对话框和非模态对话框。
单击Qt Creator的菜单项“File”→“New File or Project”,选择Qt类别下的“Qt Designer Form Class”,创建可视化设计的对话框类。在随后出现的向导里,选择窗口模板为Dialog without Buttons,并设置自定义对话框的类名。
会得到3个文件,分别为.h ,.c,ui文件。
设置好自己的对话框界面。
设计QWDialogSize对话框的界面时,在上面放置了两个QPushButton按钮,并分别命名为btnOK和btnCancel,分别是“确定”和“取消”按钮,用于获取对话框运行时用户的选择。
在信号与槽编辑器里,将btnOK的clicked()信号与对话框的accept()槽关联,将btnCancel的clicked()信号与对话框的reject()槽关联即可,如图所示:
单击“确定”按钮会执行accept()槽(或在代码里调用accept()槽函数也是一样的),这会关闭对话框(默认情况下,对话框只是被隐藏,并不被删除),并返回QDialog::Accepted 作为exec()函数的返回值。
单击“取消”按钮会执行reject()槽函数,也会关闭对话框,并返回QDialog::Rejected作为exec()函数的返回值。
注意的是:
我们的主界面是没有办法去访问到我们的对话框上面的组件的信息的,所以我们必须通过对外的接口函数把数据传输出去,就是我们要提供公共的接口把,ui界面的数据给传输给主界面。或者是我们在对话框的构造函数的时候,把一个结构体的指针传入进来,接收数据信息。
void MainWindow::on_actTab_SetSize_triggered()
{ //模态对话框,动态创建,用过后删除
QWDialogSize *dlgTableSize=new QWDialogSize(this);
Qt::WindowFlags flags=dlgTableSize->windowFlags();
dlgTableSize->setWindowFlags(flags | Qt::MSWindowsFixedSizeDialogHint);
dlgTableSize->setRowColumn(theModel->rowCount(),
theModel->columnCount());
int ret=dlgTableSize->exec();// 以模态方式显示对话框,
if (ret==QDialog::Accepted)
{ //OK按钮被按下,获取对话框上的输入,设置行数和列数
int cols=dlgTableSize->columnCount();
theModel->setColumnCount(cols);
int rows=dlgTableSize->rowCount();
theModel->setRowCount(rows);
}
delete dlgTableSize;
}
Qt::Widget //是一个窗口或部件,有父窗口就是部件,没有就是窗口
Qt::Window //是一个窗口,有窗口边框和标题
Qt::Dialog //是一个对话框窗口
Qt::Sheet //是一个窗口或部件Macintosh表单
Qt::Drawer //是一个窗口或部件Macintosh抽屉,去掉窗口左上角的图标
Qt::Popup //是一个弹出式顶层窗口
Qt::Tool //是一个工具窗口
Qt::ToolTip //是一个提示窗口,没有标题栏和窗口边框
Qt::SplashScreen //是一个欢迎窗口,是QSplashScreen构造函数的默认值
Qt::Desktop //是一个桌面窗口或部件
Qt::SubWindow //是一个子窗口
//为窗口添加一些功能,窗口属性:
Qt::CustomizeWindowHint //关闭默认窗口标题提示
Qt::WindowTitleHint //为窗口修饰一个标题栏
Qt::WindowSystemMenuHint //为窗口修饰一个窗口菜单系统
Qt::WindowMinimizeButtonHint //为窗口添加最小化按钮
Qt::WindowMaximizeButtonHint //为窗口添加最大化按钮
Qt::WindowMinMaxButtonsHint //为窗口添加最大化和最小化按钮
Qt::WindowCloseButtonHint //窗口只有一个关闭按钮
Qt::WindowContextHelpButtonHint
Qt::MacWindowToolBarButtonHint
Qt::WindowFullscreenButtonHint
Qt::BypassGraphicsProxyWidget
Qt::WindowShadeButtonHint
Qt::WindowStaysOnTopHint //总在最上面的窗口,置前
Qt::WindowStaysOnBottomHint
Qt::WindowOkButtonHint
Qt::WindowCancelButtonHint
Qt::WindowTransparentForInput
增加
this->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
去除
setWindowFlags(windowFlags()& ~Qt::WindowMaximizeButtonHint);//去掉最大化按钮
从代码中可以看到,每次单击此工具栏按钮时,对话框都被重新创建。创建后用QDialog的setWindowFlags()函数将对话框设置为固定大小,然后调用对话框的自定义函数setRowColumn(),将主窗口数据模型theModel的现有的行数和列数显示到对话框上的两个SpinBox组件里。
调用对话框的exec()函数,以模态显示的方式显示对话框。模态显示方式下,用户只能在对话框上操作,不能操作主窗口,主程序也在此处等待exec()函数的返回结果。
当用户单击“确定”按钮关闭对话框后,exec()返回结果为QDialog::Accepted,主程序获得此返回结果后,通过对话框的自定义函数columnCount()和rowCount()获得对话框上新输入的列数和行数,然后设置为数据模型的列数和行数。
最后使用delete删除创建的对话框对象,释放内存。所以,关闭对话框时,会出现QWDialogSize析构函数里的消息提示对话框。
注意:
在对话框上单击按钮或关闭对话框时,对话框只是隐藏(缺省的),而并没有从内存中删除。如果对话框一关闭就自动删除,则在后面调用对话框的自定义函数获得输入的行数和列数时会出现严重错误。所以,我们需要拿到自己想的数据之后再进行delete。
void MainWindow::on_actTab_SetHeader_triggered()
{//一次创建,多次调用,对话框关闭时只是隐藏
if (dlgSetHeaders==NULL)
dlgSetHeaders = new QWDialogHeaders(this);
if (dlgSetHeaders->headerList().count()!=theModel->columnCount())
{//如果表头列数变化,重新初始化
QStringList strList;
for (int i=0;i<theModel->columnCount();i++)//获取现有的表头标题
strList.append(theModel->headerData(i,Qt::Horizontal,
Qt::DisplayRole).toString());
dlgSetHeaders->setHeaderList(strList);//对话框初始化显示
}
int ret=dlgSetHeaders->exec();// 以模态方式显示对话框
if (ret==QDialog::Accepted) //OK键被按下
{
QStringList strList=dlgSetHeaders->headerList();
theModel->setHorizontalHeaderLabels(strList);// 设置模型的表头标题
}
}
void MainWindow::on_actTab_Locate_triggered()
{//创建 StayOnTop的对话框,对话框关闭时自动删除
ui->actTab_Locate->setEnabled(false);
dlgLocate = new QWDialogLocate(this);
dlgLocate->setAttribute(Qt::WA_DeleteOnClose); //对话框关闭时自动删除
Qt::WindowFlags flags=dlgLocate->windowFlags(); //获取已有flags
dlgLocate->setWindowFlags(flags | Qt::WindowStaysOnTopHint); //StayOnTop
dlgLocate->setSpinRange(theModel->rowCount(),theModel->columnCount());
QModelIndex curIndex=theSelection->currentIndex();
if (curIndex.isValid())
dlgLocate->setSpinValue(curIndex.row(),curIndex.column());
dlgLocate->show(); //非模态显示对话框
}
示例代码:
void QWDialogLocate::on_btnSetText_clicked()
{//定位到单元格,并设置字符串
int row=ui->spinBoxRow->value(); //行号
int col=ui->spinBoxColumn->value();//列号
MainWindow *parWind = (MainWindow*)parentWidget(); //获取主窗口
parWind->setACellText(row,col,ui->edtCaption->text()); //调用主窗口函数
if (ui->chkBoxRow->isChecked()) //行增
ui->spinBoxRow->setValue(1+ui->spinBoxRow->value());
if (ui->chkBoxColumn->isChecked()) //列增
ui->spinBoxColumn->setValue(1+ui->spinBoxColumn->value());
}
想要在对话框中操作主窗口,就需要获取主窗口对象,调用主窗口的函数并传递参数。在上面的代码中,通过下面一行语句获得主窗口对象:
MainWindow *parWind = (MainWindow*)parentWidget();
parentWidget()是QWidget类的一个函数,指向父窗口。在创建此对话框时,将主窗口的指针传递给对话框的构造函数,即:
dlgLocate = new QWDialogLocate(this);
所以,对话框的parentWidget指向主窗口。
然后调用主窗口的一个自定义的public函数setACellText(),传递行号、列号和字符串,由主窗口更新指定单元格的文字。下面是主窗口的setACellText()函数的代码。
void MainWindow::setACellText(int row, int column, QString text)
{//定位到单元格,并设置字符串
QModelIndex index=theModel->index(row,column);//获取模型索引
theSelection->clearSelection();
theSelection->setCurrentIndex(index,QItemSelectionModel::Select);
theModel->setData(index,text,Qt::DisplayRole);//设置单元格字符串
}
这样就实现了在对话框里对主窗口进行的操作,主要是获取主窗口对象,然后调用相应的函数。
因为父窗口中创建了对话框,是可以知道对话框的指针的,可以之间对对话框的指针进行操作就可以了。
示例代码如下:
void MainWindow::on_tableView_clicked(const QModelIndex &index)
{//单击单元格时,将单元格的行号、列号设置到对话框上
if (dlgLocate!=NULL)
dlgLocate->setSpinValue(index.row(),index.column());
}
因为主窗口中定义了对话框的指针,只要它不为NULL,就说明对话框存在,调用对话框的一个自定义函数setSpinValue(),刷新对话框显示界面。QWDialogLocate的setSpinValue()函数实现如下:
void QWDialogLocate::setSpinValue(int rowNo, int colNo)
{//设置SpinBox组件的数值
ui->spinBoxRow->setValue(rowNo);
ui->spinBoxColumn->setValue(colNo);
}
对话框和主窗口之间互相操作的关键是要有对方对象的指针,然后才能传递参数并调用对方的函数。
由于对话框是以非模态方式运行的,程序无法等待对话框结束后作出响应,但是可以利用窗口的CloseEvent事件。
事件(event)是由窗口系统产生的由某些操作触发的特殊函数,例如鼠标操作、键盘操作的一些事件,还有窗口显示、关闭、绘制等相关的事件。从QWidget继承的窗口部件常用的事件函数有如下几种。
closeEvent():窗口关闭时触发的事件,通常在此事件做窗口关闭时的一些处理,例如显示一个对话框询问是否关闭窗口。
showEvent():窗口显示时触发的事件。
paintEvent():窗口绘制事件。
mouseMoveEvent():鼠标移动事件。
mousePressEvent():鼠标键按下事件。
mouseReleaseEvent():鼠标键释放事件。
keyPressEvent():键盘按键按下事件。
keyReleaseEvent():键盘按键释放事件。
要利用对话框的closeEvent()事件,在类定义中声明了此事件的函数,其实现代码如下:
void QWDialogLocate::closeEvent(QCloseEvent *event)
{ //窗口关闭 event
MainWindow *parWind = (MainWindow*)parentWidget(); //获取父窗口指针
parWind->setActLocateEnable(true);//使能 actTab_Locate
parWind->setDlgLocateNull(); //将窗口指针设置为NULL
}
利用closeEvent()事件,可以询问窗口是否退出,例如为主窗口添加closeEvent()事件的处理,代码如下:
void MainWindow::closeEvent(QCloseEvent *event)
{ //窗口关闭时询问是否退出
QMessageBox::StandardButton result=QMessageBox::question(this,
"确认", "确定要退出本程序吗?",
QMessageBox::Yes|QMessageBox::No |QMessageBox::Cancel,
QMessageBox::No);
if (result==QMessageBox::Yes)
event->accept();
else
event->ignore();
}
这样,主窗口关闭时就会出现一个询问对话框,如果不单击“Yes”按钮,程序就不关闭;否则应用程序结束。
前面都是直接利用操作对应的指针,然后调用函数来进行操作主窗口或者对话框,比较的麻烦,而却直接操作指针不大好。可以利用信号与槽来改进。
如我们的主窗口为MainWindow,对话框为QWDialog采用信号与槽机制实现交互操作。
MainWindow类的定义的信号与处理函数:
class MainWindow : public QMainWindow
{
//信号处理函数
public slots:
void setACellText(int row, int column, QString &text);//设置单元格内容
void setActLocateEnable(bool enable);//设置actTab_Locate的enabled属性
//信号
signals:
void cellIndexChanged(int rowNo, int colNo);//当前单元格发生变化
...
};
MainWindow对应函数的代码:
void MainWindow::setACellText(int row, int column, QString &text)
{//定位到单元格,并设置字符串
QModelIndex index=theModel->index(row,column);//获取模型索引
theSelection->clearSelection();
theSelection->setCurrentIndex(index,QItemSelectionModel::Select);
theModel->setData(index,text,Qt::DisplayRole);//设置单元格字符串
}
void MainWindow::setActLocateEnable(bool enable)
{ //设置actTab_Locate的enabled属性
ui->actTab_Locate->setEnabled(enable);
}
void MainWindow::on_tableView_clicked(const QModelIndex &index)
{//单击单元格时发射信号,传递单元格的行号、列号
emit cellIndexChanged(index.row(),index.column());
}
当点击对应的创建对话框按键时代码:
void MainWindow::on_actTab_Locate_triggered()
{//创建 StayOnTop的对话框,对话框关闭时自动删除
QWDialogLocate *dlgLocate = new QWDialogLocate(this);
dlgLocate->setAttribute(Qt::WA_DeleteOnClose);
Qt::WindowFlags flags=dlgLocate->windowFlags();
dlgLocate->setWindowFlags(flags | Qt::WindowStaysOnTopHint);
dlgLocate->setSpinRange(theModel->rowCount(),theModel->columnCount());
QModelIndex curIndex=theSelection->currentIndex();
if (curIndex.isValid())
dlgLocate->setSpinValue(curIndex.row(),curIndex.column());
//对话框发射信号,设置单元格文字
connect(dlgLocate,SIGNAL(changeCellText(int,int,QString&)),
this,SLOT(setACellText(int,int,QString&)));
//对话框发射信号,设置actTab_Locate的属性
connect(dlgLocate,SIGNAL(changeActionEnable(bool)),
this,SLOT(setActLocateEnable(bool)));
//主窗口发射信号,修改对话框上的spinBox的值
connect(this,SIGNAL(cellIndexChanged(int,int)),
dlgLocate,SLOT(setSpinValue(int,int)));
dlgLocate->show(); //非模态显示对话框
}
QWDialog类定义中,与信号和槽相关的定义如下:
class QWDialog: public QDialog
{
private:
void closeEvent(QCloseEvent *event);
void showEvent(QShowEvent *event);
//处理函数
private slots:
void on_btnSetText_clicked();
public slots:
void setSpinValue(int rowNo, int colNo);
//信号
signals:
void changeCellText(int row, int column, QString &text);
void changeActionEnable(bool en);
};
对应的函数:
void QWDialogLocate::closeEvent(QCloseEvent *event)
{ //窗口关闭事件, 发射信号使 actTab_Locate 能用
emit changeActionEnable(true);
}
void QWDialogLocate::showEvent(QShowEvent *event)
{//窗口显示事件, 发射信号使 actTab_Locate 不能用
emit changeActionEnable(false);
}
void QWDialogLocate::setSpinValue(int rowNo, int colNo)
{//响应主窗口信号,更新spinBox的值
ui->spinBoxRow->setValue(rowNo);
ui->spinBoxColumn->setValue(colNo);
}
void QWDialogLocate::on_btnSetText_clicked()
{//发射信号,定位到单元格并设置字符串
int row=ui->spinBoxRow->value(); //行号
int col=ui->spinBoxColumn->value();//列号
QString text=ui->edtCaption->text();//文字
emit changeCellText(row,col,text);//发射信号
if (ui->chkBoxRow->isChecked()) //行增
ui->spinBoxRow->setValue(1+ui->spinBoxRow->value());
if (ui->chkBoxColumn->isChecked()) //列增
ui->spinBoxColumn->setValue(1+ui->spinBoxColumn->value());
}
经过这样修改后的程序,能实现与前面的实例完全相同的主窗口与对话框交互的功能,但是与前面互相引用的方式不同,这里使用Qt的信号与槽的机制,无须获取对方的指针,程序结构上更简单一些。