Qt4_使用对话框

使用对话框

这一节将说明如何在Qt中使用对话框——如何创建、初始化以及运行它们,并且对用户交互中的选择做出响应。本节将会使用在之前创建的Find、GotoCell和Sort对话框,也会创建一个简单的About 对话框。

我们从如图所示的Find对话框开始。由于希望用户能够在Spreadsheet窗口和Find对话框之间进行切换,所以Find对话框必须是非模态(modeless)的。非模态窗口就是运行在应用程序中对于任何其他窗口都独立的窗口。
在这里插入图片描述
创建非模态对话框时,通常会把它的信号连接到能够对用户的交互做出响应的那些槽上。

void MainWindow::find()
{
    if (!findDialog) {
        findDialog = new FindDialog(this);
        connect(findDialog, SIGNAL(findNext(const QString &,
                                            Qt::CaseSensitivity)),
                spreadsheet, SLOT(findNext(const QString &,
                                           Qt::CaseSensitivity)));
        connect(findDialog, SIGNAL(findPrevious(const QString &,
                                                Qt::CaseSensitivity)),
                spreadsheet, SLOT(findPrevious(const QString &,
                                               Qt::CaseSensitivity)));
    }
   
    findDialog->show();
    findDialog->raise();
    findDialog->activateWindow();
}

Find对话框是一个可以让用户在电子制表软件中搜索文本的窗口。当用户单击Edit->Find时,就会调用find( )槽来弹出Find对话框。这时,就可能出现下列几种情形:
●这是用户第一次调用Find对话框。
●以前曾经调用过Find对话框,但用户关闭了它。.
●以前曾经调用过Find对话框,并且现在它还是可见的。

如果Find对话框还不曾存在过,就可以创建它并且把它的findNext()信号和findPrevious()信号与Spreadsheet中相对应的那些槽连接起来。

本应该在MainWndow的构造函数中创建这个对话框,但是推迟对话框的创建过程将可以使程序的启动更加快速。还有,如果从来没有使用到这个对话框,那么它就决不会被创建,这样可以既节省时间又节省内存。

然后,调用show()、raise()和activateWindow()来确保窗口位于其他窗口之上并且是可见的和激活的。只调用show()就足以让一个隐藏窗口变为可见的、位于最上方并且是激活的,但是也有可能是在Find对话框窗口已经是可见的时候又再次调用了它,在这种情况下,show()调用可能什么也不做,那么就必须调用raise()和activateWindow()让窗口成为顶层窗口和激活状态。

现在看一下如图3.13所示的Go to Cell对话框。我们希望用户可以弹出、使用和关闭它,但是却不希望让这个窗口能够与应用程序中的其他窗口相互切换。也就是说,GotoCell对话框窗口必须是模态(modal)的。模态窗口就是一个在得到调用可以弹出并可以阻塞应用程序的窗口,从而会从调用发生开始起妨碍其他的任意处理或者交互操作,直到关闭该窗口为止。前面使用的文件对话框和消息框就是模态的。
在这里插入图片描述
如果对话框是通过show()调用的,那么它就是非模态对话框[除非此后又调用了setModal(),才会让它变为模态对话框]。但是,如果它是通过exec()调用的,那么该对话框就会是模态对话框。

void MainWindow::goToCell()
{ 
    GoToCellDialog dialog(this);
    if (dialog.exec()) {
        QString str = dialog.lineEdit->text().toUpper();
        spreadsheet->setCurrentCell(str.mid(1).toInt() - 1,
                                    str[0].unicode() - 'A');
    }
}

如果对话框被接受,函数QDialog::exec()可返回一个true值(QDialog::Accepted),否则就会返回一个false值(QDialog::Rected)。可以回想一下,当初利用Qt设计师创建Go to Cell对话框时,就曾经把OK连接到accept(),把Cancel连接到reject()。如果用户选择0K,就把当前单元格的值设置成行编辑器中的值。

QTable::setCurentCell()函数需要两个参数:一个行索引和一个列索引。

在Spreadsheet 应用程序中,单元格A1就是单元格(0,0),单元格B27就是单元格(26,1)。
为了从函数QLineEdit::text()返回的QString中获得行索引,可以使用QString::mid()来提取行号(这个函数将返回一个从字符串的开始直到末尾位置的子字符串),然后使用QString::toInt()把它转换成一个整数值,并且把该值再减去1。

对于列号,则可以用这个字符串中第一个字符的大写数值减去字符‘A’的数值而得到。
我们知道,该字符串将具有正确的格式,因为为对话框创建了一个QRegExpValidator检验器,只有满足一个字符后面再跟至多三位数字格式的字符串才能让OK按钮起作用。

goToCell()函数与目前着到的所有代码都有些不同,因为它在堆栈中创建了一个作为变量的窗口部件(一个GoToCellDialog)。虽然多使用了一行代码,但是换来了不需要使用new和delete的简便

void MainWindow::goToCell()
{ 
    GoToCellDialog dialog(this);
    if (dialog.exec()) {
        QString str = dialog.lineEdit->text().toUpper();
        spreadsheet->setCurrentCell(str.mid(1).toInt() - 1,
                                    str[0].unicode() - 'A');
    }
    delete dialog;
}

由于在使用完一个对话框(或者菜单)后,通常就不再需要它了,所以在堆栈中创建对话框(和上下文菜单)是一种常见的编程模式,并且对话框会在作用域结束后自动销毁掉。

现在转到Sort对话框上。Sort对话框是一个模态对话框,它允许用户在当前的选定区域中使用给定的列进行排序。下图给出了一个排序的实例,用列B作为排序的主键,列A作为排序的第二键(两个都采用升序)。
在这里插入图片描述

void MainWindow::sort()
{
    SortDialog dialog(this);
    QTableWidgetSelectionRange range = spreadsheet->selectedRange();
    dialog.setColumnRange('A' + range.leftColumn(),
                          'A' + range.rightColumn());

    if (dialog.exec()) {
        SpreadsheetCompare compare;
        compare.keys[0] =
              dialog.primaryColumnCombo->currentIndex();
        compare.keys[1] =
              dialog.secondaryColumnCombo->currentIndex() - 1;
        compare.keys[2] =
              dialog.tertiaryColumnCombo->currentIndex() - 1;
        compare.ascending[0] =
              (dialog.primaryOrderCombo->currentIndex() == 0);
        compare.ascending[1] =
              (dialog.secondaryOrderCombo->currentIndex() == 0);
        compare.ascending[2] =
              (dialog.tertiaryOrderCombo->currentIndex() == 0);
        spreadsheet->sort(compare);
    }
}

sort()函数中的代码使用了一种和goToCell()函数中用到的类似模式:
● 在堆栈中创建对话框并且对其进行初始化。
● 使用exec()弹出对话框。
● 如果用户单击OK,就从对话框的各个窗口部件中提取并且使用这些用户输入的值。

setColumnRange()调用将那些可用于排序的列变量设置为选定的列。例如,使用上图中的选择,range.leftColumn()将返回值0,即’A’+0=‘A’,并且range.rightColumn()将返回值2,即’A’ +2=‘C’。

compare对象储存了主键、第二键和第三键以及它们的排序顺序。这个对象会由Spreadsheet::sort()使用,用于两行的比较。keys数组存储了这些键的列号。例如,如果选择区域是从C2扩展到E5,那么列C的位置就是0。ascending数组中按bool格式存储了和每一个键相关的顺序。QComboBox::currentIndex()返回当前选定项的索引值,该值是一个从0开始的数。对于第二键和第三键,考虑到“None”项,我们从当前项减去1。

sort( )函数会完成这项工作,但是它显得稍有不足。它认为Sort 对话框是按照一种特定的方式来实现的,也就是像上面那样来处理组合框和"None"项。这就意味着,如果重新设计了Sort对话框,也许就需要重新编写这些代码。如果对话框只会从一个地方调用,那么这样的方式应该是足够了,但是如果对话框可能会在几个地方调用到,那么这种处理方式就等于打开了维护工作的梦魇之门。

一种更为稳健的方法是让SortDialog类具有自适应性,这可以通过让它自己创建一个SpreadsheetCompare对象,然后使这个对象只能被它的调用者使用来做到这一点。这样就可以有效地简化MainWindow::sort( )函数:

void MainWindow::sort()
{
    SortDialog dialog(this);
    QTableWidgetSelectionRange range = spreadsheet->selectedRange();
    dialog.setColumnRange('A' + range. leftColumn(),
                          'A' + range. rightColunn());
    if (dialog.exec())
        spreadsheet->performSort(dialog.comparisonObject());
}

这种方法可产生松散的耦合组件,并且当从多个地方调用该对话框时,它几乎总可以做出正确的选择。

一种更为极端的方式是在初始化SortDialog对象的时候就为其传递一个指向Spreadsheet对象的指针,并且允许对话框直接对Spreadsheet进行操作。这样做会使SortDialog少一些通用性,因为它仅能适用于一种类型的窗口部件,但是通过去除SortDialog::setColumnRange()函数,它的确是进一步简化了程序代码。于是,现在的MainWindow::sort()函数将变成如下所示的样子:

void MainWindow::sort()
{
    SortDialog dialog(this);
    dialog.setSpreadsheet(spreadsheet);
    dialog. exec();
}

相比而言,第一种方法中调用者需要知道与这个对话框相关的暗示信息,而第二种方法中的对话框需要知道由调用者所提供的与数据结构相关的暗示信息。在对话框需要作用于现场变化的地方,这种方法显得更为有用些。但是就像第一种方法中调用者的代码功能不足一样,如果数据结构发生了变化,则第三种方法也会失效。

一些开发者只会选用一种对话框处理方法并对其持之以恒。这有一个好处,就是能够精通和简练处理方法,因为所有的对话框都使用的是同一种处理模式,但是这也会失去对调用对话框时没有用到的那些其他有益处理方法。理想情况下,应根据每一个对话框的自身来选择应当使用的对话框处理方法。

我们将用About对话框来圆满结束这一节。可以创建-一个像Find或Go to Cell:对话框那样的自定义对话框来显示应用程序的有关信息,但是因为绝大多数About对话框都具有较为固定的格式,所以Qt提供了一种更为简单的解决方案。

void MainWindow::about()
{
    QMessageBox::about(this, tr("About Spreadsheet"),
            tr("<h2>Spreadsheet 1.1</h2>"
               "<p>Copyright &copy; 2008 Software Inc."
               "<p>Spreadsheet is a small application that "
               "demonstrates QAction, QMainWindow, QMenuBar, "
               "QStatusBar, QTableWidget, QToolBar, and many other "
               "Qt classes."));
}

通过调用一个方便的静态函数QMessageBox::about(),就可以获得About对话框。这个函数和QMessageBox::warming()的形式非常相似,只是它使用了父窗口的图标,而不是标准的"警告"图标。
在这里插入图片描述
到现在为止,已经使用了由QMessageBox和QFileDialog 提供的多个方便的静态函数。这些函数可以创建一个对话框,初始化它,并且可以对它调用exec()。当然,也可以像创建其他任意窗口部件一样创建QMessageBox或者QFileDialog窗口部件,然后再明确地对它调用exec()或者甚至是show( ),尽管这样的处理方式会显得有些不大方便。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阳光开朗男孩

你的鼓励是我最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值