从零开始学Qt(44):进阶!非模态对话框

前面设计的两个对话框是以模态(Model)方式显示的,即用QDialog::exec()函数显示。模态 显示的对话框不允许鼠标再去单击其他窗口,直到对话框退出。

使用QDialog::show(),则能以非模态(Modeless)方式显示对话框。非模态显示的对话框在显示后继续运行主程序,还可以在主窗口上操作,主窗口和非模态对话框之间可以交互控制,典型的例子是文字编辑软件里的“查找/替换”对话。

本文以一个例子展示自定义非模态对话框的基本方法。

窗口的界面和功能设计

上图中的单元格定位与文字设置对话框以非模态方式显示,对话框类是myDialogLocate,它有如下的一些功能。

  • 主窗口每次调用此对话框时,就会创建此对话框对象。并以StayOnTop的方式显示,对话框关闭时自动删除。
  • 在对话框中可以定位主窗口上TableWidget组件的单元格,并设置单元格的文字。
  • 在主窗口的TableWidget中单击鼠标时,如果对话框已经创建,则自动更新对话框上单元 格的行号,列号和文字组件的值。
  • 主窗口上的pushButton用于调用对话框,调用时pushButton设置为禁用。当对话框关闭时自动使能pushButton。这样避免对话框显示时,在主窗口上再次单击“定位单元格”按钮,而在对话框关闭和释球后,按钮又恢复为可用。

对话框的创建与调用

对话框myDialogLocate是从QDialog继承而来的可视化设计的对话框类。为了在主窗口中也能操作对话框,需要保留对话框实例对象名,所以在Widget定义对话框myDialogLocate的一个指针dlgLocate,并初始化为NULL。

private:
	myDialogLocate *dlgLocate=NULL;

主窗口上的pushButton用于调用此对话框,代码如下:

void Widget::on_pushButton_clicked()
{ // 创建StayOnTop的对话框
  ui->pushButton->setEnabled(false);
  dlgLocate=new myDialogLocate(this);
  // 设置对话框关闭时自动删除
  dlgLocate->setAttribute(Qt::WA_DeleteOnClose);
  // 获取已有的flags
  Qt::WindowFlags flags=dlgLocate->windowFlags();
  // StayOnTop
  dlgLocate->setWindowFlags(flags | Qt::WindowStaysOnTopHint);
  // 更新对话框显示信息
  updateDlg(ui->tableWidget->currentItem());
  // 非模态显示对话框
  dlgLocate->show();
}

在这段代码中,使用QWidget::setAttribute()函数将对话框设置为关闭时自动删除。

dlgLocate->setAttribute(Qt::WA_DeleteOnClose);

setAttribute()用于对窗体的一些属性进行设置,当设置为Qt::WA_DeleteOnClose时,窗口关闭时会自动删除,以释放内存。

程序还调用QWidget::setWindowFlags()将对话框设置为StayOnTop显示。

dlgLocate->setWindowFlags(flags | Qt::WindowStaysOnTopHint);

对话框窗口效果设置后,再设置其初始数据,然后调用show()显示对话框。显示每对话框后,主程序继续运行,不会等待对话框的返回结果。鼠标可以操作主窗口上的界面,但是因为 pushButton被禁用了,不能再重复单击“定位单元格”按钮。

对话框中操作主窗口

在对话框上单击“设置文字”按钮,会在主窗口中设定为单元格的文字,按钮的代码如下:

void myDialogLocate::on_btnSetText_clicked()
{
  int row=ui->edtRowIndex->text().toInt();
  int col=ui->edtColumnIndex->text().toInt();
  QString text=ui->edtCellText->text();
  // 获取主窗口
  Widget *parWind=(Widget*)parentWidget();
  // 调用主窗口函数设置文本
  parWind->setCellText(row,col,text);
}

想要在对话框中操作主窗口,就需要获取主窗口对象,调用主窗口的函数并传递参数。在上 面的代码中,通过下面一行语句获得主窗口对象:

Widget *parWind=(Widget*)parentWidget();

parentWidget()是QWidget类的一个函数,指向父窗口。在创建此对话框时,将主窗口的指针传递给对话框的构造函数,即:

dlgLocate = new myDialogLocate(this);

所以,对话框的parentWidget指向主窗口。

然后调用主窗口的一个自定义的public函数setCellText(),传递行号、列号和字符串,由主 窗口更新指定单元格的文字。下面是主窗口的setCellText()函数的代码。

void Widget::setCellText(int row, int col, QString text)
{
  QTableWidgetItem *item=ui->tableWidget->item(row,col);
  item->setText(text);
}

这样就实现了在对话框里对主窗口进行的操作,主要是获取主窗口对象,然后调用相应的函数。

主窗口中操作对话框

在主窗口上用鼠标单击TableWidget组件的某个单元格时,如果单元格定位对话框dlgLocate己经存在,就将单元格的行号、列号和文字更新到对话框上,实现代码如下:

void Widget::on_tableWidget_itemClicked(QTableWidgetItem *item)
{
	updateDlg(item);
}

void Widget::updateDlg(QTableWidgetItem *item)
{
  if(dlgLocate==NULL || item==NULL)
  	return;
  dlgLocate->setCell(item->row(),item->column(),item->text());
}

因为主窗口中定义了对话框的指针,只要它不为NULL,就说明对话框存在,调用对话框的 一个自定义函数setCell(),刷新对话框显示界面。myDialogLocate的setCell()函数实现如下:

void myDialogLocate::setCell(int row, int col,QString text)
{
  ui->edtRowIndex->setText(QString::number(row));
  ui->edtColumnIndex->setText(QString::number(col));
  ui->edtCellText->setText(text);
}

窗口的CloseEvent事件

对话框和主窗口之间互相操作的关键是要有对方对象的指针,然后才能传递参数并调用对方 的函数。在对话框关闭时,还需要做一些处理:将主窗口的pushButton重新设置为使能,将主窗口的指向对话框的指针dlgLocate重新设置为NULL。

由于对话框dlgLocate是以非模态方式运行的,程序无法等待对话框结束后做出相应,但是可以利用窗口的CloseEvent事件。

事件(event)是由窗口系统产生的由某些操作触发的特殊函数,例如键盘操作,键盘操作的一些事件,还有窗口显示,关闭、绘制等相关事件。从QWidget继承的窗口部件常用的事件函数有如下几种。

  • CloseEvent():窗口关闭时触发的事件,通常在此事件做窗口关闭时的一些处理,例如显示 一个对话框询问是否关闭对话框。
  • showEvent():窗口显示时触发的事件。
  • paintEvent():窗口绘制事件。
  • mouseMoveEvent():鼠标移动事件。
  • mousePressEvent():鼠标键按下事件。
  • mouseReleaseEvent():鼠标键释放事件。
  • keyPressEvent():键盘按键按下事件。
  • keyReleaseEvent():键盘按键释放事件。

要用某个事件进行一些处理,需要在窗口类里重定义事件函数并编写响应代码。在本例中,要利用对话框的closeEvent()事件,在类定义中声明了此事件的函数,

private:
	void closeEvent(QCloseEvent *);

其实现代码如下:

void myDialogLocate::closeEvent(QCloseEvent *)
{
  // 获取主窗口
  Widget * parWind=(Widget*)parentWidget();
  // 调用主窗口函数设置属性
  parWind->setBtnDlgEnable(true);
  parWind->setDlgPtrNull();
}

在closeEvent()事件里,调用主窗口的两个函数,将pushButton重新使能,将主窗口内指向对话框的指针设置为NULL。主窗口中这两个函数的实现代码如下:

void Widget::setBtnDlgEnable(bool flag)
{
	ui->pushButton->setEnabled(flag);
}

void Widget::setDlgPtrNull()
{
	dlgLocate=NULL;
}
原文链接:,转发请注明来源!