目录
1 对话框的不同调用方式
2 对话框QWDialogSize的创建和使用
3 对话框QWDialogHeaders的创建和使用
4 对话框QWDialogLocate的创建与使用
5 利用信号与槽实现交互操作
1 对话框的不同调用方式
-
设置表格行列数对话框QWDialogSize
-
设置表头标题对话框QWDialogHeaders
-
单元格定位与文字设置对话框QWDialogLocate
2 对话框QWDialogSize的创建和使用
1 创建对话框QWDialogSize
2 对话框的调用和返回值
class QWDialogSize : public QDialog
{
Q_OBJECT
public:
explicit QWDialogSize(QWidget *parent = 0);
~QWDialogSize();
int rowCount();//获取对话框输入的行数
int columnCount();//获取对话框输入的列数
void setRowColumn(int row, int column); //初始对话框上两个SpinBox的值
private slots:
private:
Ui::QWDialogSize *ui;
};
在QWDialogSize的类定义中定义3个public函数,用于与对话框调用者的数据交互。因为窗体上的组件都是私有成员,外界不能直接访问界面组件,只能通过接口函数访问。
下面是类的接口函数实现代码。在析构函数中弹出一个消息提示对话框,以便观察对话框是何时被删除的。
QWDialogSize::~QWDialogSize()
{
QMessageBox::information(this,"提示","设置表格行列数对话框被删除");
delete ui;
}
int QWDialogSize::rowCount()
{ //用于主窗口调用获得行数的输入值
return ui->spinBoxRow->value();
}
int QWDialogSize::columnCount()
{//用于主窗口调用获得列数的输入值
return ui->spinBoxColumn->value();
}
void QWDialogSize::setRowColumn(int row, int column)
{ //初始化数据显示
ui->spinBoxRow->setValue(row);
ui->spinBoxColumn->setValue(column);
}
下面是主窗口中的“设置行数列数”工具栏按钮的响应代码,用于创建、显示对话框,并读框上设置的行数、列数。
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();// 以模态方式显示对话框,用户关闭对话框时返回 DialogCode值
if (ret==QDialog::Accepted) //OK键被按下,对话框关闭,若设置了setAttribute(Qt::WA_DeleteOnClose),对话框被释放,无法获得返回值
{ //OK键被按下,获取对话框上的输入,设置行数和列数
int cols=dlgTableSize->columnCount();
theModel->setColumnCount(cols);
int rows=dlgTableSize->rowCount();
theModel->setRowCount(rows);
}
delete dlgTableSize; //删除对话框
}
从代码中可以看到,每次单击此工具栏按钮时,对话框都被重新创建。创建后用QDialog的setWindowFlags()函数将对话框设置为固定大小,然后调用对话框的自定义函数setRowColumn(),将主窗口数据模型theModel的现有的行数和列数显示到对话框上的两个SpinBox组件里。
调用对话框的exec()函数,以模态显示的方式显示对话框。模态显示方式下,用户只能在对话框上操作,不能操作主窗口,主程序也在此处等待exec()函数的返回结果。
当用户单击“确定”按钮关闭对话框后,exec()返回结果为QDialog::Accepted,主程序获得此返回结果后,通过对话框的自定义函数columnCount()和rowCount()获得对话框上新输入的列数和行数,然后设置为数据模型的列数和行数。
最后使用delete删除创建的对话框对象,释放内存。所以,关闭对话框时,会出现QWDialogSize析构函数里的消息提示对话框。
注意:在对话框上单击按钮或关闭对话框时,对话框只是隐藏(缺省的),而并没有从内存中删除。如果对话框一关闭就自动删除,则在后面调用对话框的自定义函数获得输入的行数和列数时会出现严重错误。
3 对话框QWDialogHeaders的创建和使用
1.对话框的生存期
对话框的生存期是指它从创建到删除的存续区间。前面介绍的设置表格行数和列数的对话框的生存期只在调用它的按钮的槽函数里,因为对话框是动态创建的,调用结束后就会被删除。
而对于图6-6所示的设置表头标题对话框,我们希望在主窗口里首次调用时创建它,对话框关闭时并不删除,只是隐藏,下次调用时再次显示此对话框。只有在主窗口释放时该对话框才释放,所以这个对话框的生存期在主窗口存续期间。
2.QWDialogHeaders的定义和实现
设置表头标题的对话框类是QWDialogHeaders,它也是从QDialog继承的可视对话框类。其界面显示使用QListView组件,用QStringListModel变量管理字符串列表数据,构成Model/View
结构。对话框上同样有“确定”和“取消”两个按钮,设置与对话框的accept()和reject()槽关联。
QWDialogHeaders类的定义如下:
class QWDialogHeaders : public QDialog
{
Q_OBJECT
private:
QStringListModel *model;
public:
explicit QWDialogHeaders(QWidget *parent = 0);
~QWDialogHeaders();
void setHeaderList(QStringList& headers);
QStringList headerList();
private:
Ui::QWDialogHeaders *ui;
};
QWDialogSize类接口函数实现的代码如下:
QWDialogHeaders::QWDialogHeaders(QWidget *parent) :
QDialog(parent),
ui(new Ui::QWDialogHeaders)
{
ui->setupUi(this);
model= new QStringListModel;
ui->listView->setModel(model);
}
QWDialogHeaders::~QWDialogHeaders()
{
QMessageBox::information(this,"提示","设置表头标题对话框被删除");
delete ui;
}
void QWDialogHeaders::setHeaderList(QStringList &headers)
{
model->setStringList(headers);
}
QStringList QWDialogHeaders::headerList()
{
return model->stringList();
}
3.QWDialogHeaders对话框的使用
因为要在主窗口中重复调用此对话框,所以在MainWindow的private部分定义一个QWDialogHeaders类型的指针变量,并且将此指针初始化设置为NULL,用于判断对话框是否己经被创建。
在MainWindow中的定义如下:
private:
QWDiaIogHeaders *dlgSetHeaders=NULL;
下面是主窗口工具栏上的“设置表头标题”按钮的响应代码。
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();//获取对话框上修改后的StringList
theModel->setHorizontalHeaderLabels(strList);// 设置模型的表头标题
}
}
4 对话框QWDialogLocate的创建与使用
-
主窗口每次调用此对话框时,就会创建此对话框对象,并以StayOnTop的方式显示,对话 框关闭时自动删除。
-
在对话框中可以定位主窗口上TableView组件的单元格,并设置单元格的文字。
-
在主窗口的组件中单击鼠标时,如果对话框己创建,则自动更新对话框上单元 格的行号和列号SpinBox组件的值。
-
主窗口上的actTab_Locate用于调用对话框,调用时actTab_Locate设置为禁用,当对话框 关闭时自动使能actTab_Locate。这样避免对话框显示时,在主窗口上再次单击“定位单元 格”按钮,而在对话框关闭和释放后,按钮又恢复为可用。
class QWDialogLocate : public QDialog
{
Q_OBJECT
private:
void closeEvent(QCloseEvent *event);
void showEvent(QShowEvent *event);
public:
explicit QWDialogLocate(QWidget *parent = 0);
~QWDialogLocate();
void setSpinRange(int rowCount, int colCount); //设置最大值
void setSpinValue(int rowNo, int colNo);//设置初始值
private slots:
void on_btnSetText_clicked();
private:
Ui::QWDialogLocate *ui;
};
private:QWDialogLocate *dlgLocate=NULL;
void MainWindow::on_actTab_Locate_triggered()
{//创建 StayOnTop的对话框,对话框关闭时自动删除
//通过控制actTab_Locate的enable属性避免重复点击
ui->actTab_Locate->setEnabled(false);
dlgLocate = new QWDialogLocate(this); //创建对话框,传递指针
dlgLocate->setAttribute(Qt::WA_DeleteOnClose); //对话框关闭时自动删除对话框对象,用于不需要读取返回值的对话框
Qt::WindowFlags flags=dlgLocate->windowFlags(); //获取已有flags
//对话框设置为固定大小和StayOnTop
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(); //非模态显示对话框
}
dlgLocate->setAttribute(Qt::WA_DeleteOnCIose);
dlgLocate->setWindowFIags(flags | Qt::WindowStaysOnTopHint);
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 QModeIIndex &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():窗口关闭时触发的事件,通常在此事件做窗口关闭时的一些处理,例如显示 一个对话框询问是否关闭窗口。
-
showEvent():窗口显示时触发的事件。
-
paintEvent():窗口绘制事件,第8章介绍绘图时会用到。
-
mouseMoveEvent():鼠标移动事件。
-
mousePressEvent():鼠标键按下事件。
-
mouseReleaseEvent():鼠标键释放事件。
-
keyPressEvent():键盘按键按下事件。
-
keyReleaseEvent():键盘按键释放事件。
void QWDialogLocate::closeEvent(QCloseEvent *event)
{ //窗口关闭事件,关闭时释放本窗口
MainWindow *parWind = (MainWindow*)parentWidget(); //获取父窗口指针
parWind->setActLocateEnable(true);//使能 actTab_Locate
parWind->setDlgLocateNull(); //将窗口指针设置为NULL
}
在closeEvent()事件里,调用主窗口的两个函数,将actTab_Locate重新使能,将主窗口内指向对话框的指针设置为NULL。主窗口中这两个函数的实现代码如下:
void MainWindow::setActLocateEnable(bool enable)
{
ui->actTab_Locate->setEnabled(enable);
}
void MainWindow::setDlgLocateNull()
{
dlgLocate=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”按钮,程序就不关闭;否则应用程序结束。
5 利用信号与槽实现交互操作
前面设计的QWDialogLocate对话框与主窗口之间的交互采用互相引用的方式,实现起来比较复杂。另外一种实现方式就是利用Qt的信号与槽机制,设计相应的信号和槽,将信号与槽关联起来,在进行某个操作时发射信号,槽函数自动响应。
对MainWindow和QWDialogLocate稍作修改,采用信号与槽机制实现交互操作。
下面是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);//当前单元格发生变化
};
两个槽函数是对话框操作主窗口时,主窗口作出的响应。信号是主窗口上tableView的当前元格发生变化时发射的一个信号,以便对话框作出响应。
下面是两个槽函数的实现,以及tableView的clicked()信号的槽函数里发射自定义信号的代代码中都无须引用对话框对象。
void MainWindow::setActLocateEnable(bool enable)
{ //设置actTab_Locate的enable属性
ui->actTab_Locate->setEnabled(enable);
}
void MainWindow::selectACell(int row, int column)
{
QModelIndex index=theModel->index(row,column);
theSelection->clearSelection();
theSelection->setCurrentIndex(index,QItemSelectionModel::Select);
}
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_actTab_Locate_triggered()
{//创建 StayOnTop的对话框,对话框关闭时自动删除
//通过控制actTab_Locate的enable属性避免重复点击
QWDialogLocate *dlgLocate;//定位单元格对话框,show()调用,关闭时自己删除
dlgLocate = new QWDialogLocate(this); //创建对话框,传递指针
dlgLocate->setAttribute(Qt::WA_DeleteOnClose); //对话框关闭时自动删除对话框对象,用于不需要读取返回值的对话框
Qt::WindowFlags flags=dlgLocate->windowFlags(); //获取已有flags
//对话框设置为固定大小和StayOnTop
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());
//对话框释放信号,设置单元格文字
connect(dlgLocate,SIGNAL(changeCellText(int,int,QString&)),
this,SLOT(setACellText(int,int,QString&)));
//对话框是否信号,设置action的属性
connect(dlgLocate,SIGNAL(changeActionEnable(bool)),
this,SLOT(setActLocateEnable(bool)));
//主窗口是否信号,修改对话框上的spinBox的值
connect(this,SIGNAL(cellIndexChanged(int,int)),
dlgLocate,SLOT(setSpinValue(int,int)));
dlgLocate->show(); //非模态显示对话框
}
在这里,对话框变量声明为了局部变量,不再需要在主窗口类里保存对话框的指针。这段代码的关键是设置了3对信号与槽的关联。
在QWDialogLocate类定义中,与信号和槽相关的定义如下。
class QWDialogLocate : public QDialog
{
Q_OBJECT
private:
void closeEvent(QCloseEvent *event);
void showEvent(QShowEvent *event);
private slots:
void on_btnSetText_clicked();
public slots:
void setSpinValue(int rowNo, int colNo);//响应主窗口信号,设置spinBox的值
signals:
void changeCellText(int row, int column, QString &text); //释放信号,定位单元格,并设置文字
void changeActionEnable(bool en); //是否信号,改变action的enable
};
QWDialogLocate自定义了一个槽函数和两个信号,还增加了showEvent()事件的处理,用于对话框显示时发射信号使主窗口的actTab_Locate失效。这些槽函数,以及发射信号的实现代码如
下,代码中没有出现对主窗口的引用。
void QWDialogLocate::closeEvent(QCloseEvent *event)
{ //窗口关闭 event,释放信号使 actTab_Locate 能用
Q_UNUSED(event)
emit changeActionEnable(true);
}
void QWDialogLocate::showEvent(QShowEvent *event)
{//窗口显示 event,释放信号使 actTab_Locate 不能用
Q_UNUSED(event)
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的信号与槽的机制,无须获取对方的指针,程序结构上更简单一些。