对话框的出现用于完成一个简单的或者是短期的任务。对话框与主窗口之间的数据交互相当重要。本节将讲解如何在对话框和主窗口之间进行数据交互。按照前文的讲解,对话框分为模态和非模态两种。我们也将以这两种为例,分别进行阐述。
模态对话框使用了exec()
函数将其显示出来。exec()
函数的真正含义是开启一个新的事件循环(我们会在后面的章节中详细介绍有关事件的概念)。所谓事件循环,可以理解成一个无限循环。Qt 在开启了事件循环之后,系统发出的各种事件才能够被程序监听到。这个事件循环相当于一种轮询的作用。既然是无限循环,当然在开启了事件循环的地方,代码就会被阻塞,后面的语句也就不会被执行到。因此,对于使用了exec()
显示的模态对话框,我们可以在exec()
函数之后直接从对话框的对象获取到数据值。
看一下下面的代码:
void MainWindow::open() { QDialog dialog(this); dialog.setWindowTitle(tr("Hello, dialog!")); dialog.exec(); qDebug() << dialog.result(); }
上面的代码中,我们使用exec()
显示一个模态对话框。最后一行代码,qDebug()
类似于std::cout
或者 Java 的System.out.println();
语句,将后面的信息输出到标准输出,一般就是控制台。使用qDebug()
需要引入头文件。在exec()
函数之后,我们直接可以获取到 dialog 的数据值。注意,exec()
开始了一个事件循环,代码被阻塞到这里。由于exec()
函数没有返回,因此下面的result()
函数也就不会被执行。直到对话框关闭,exec()
函数返回,此时,我们就可以取得对话框的数据。
需要注意的一点是,如果我们设置 dialog 的属性为WA_DeleteOnClose
,那么当对话框关闭时,对象被销毁,我们就不能使用这种办法获取数据了。在这种情况下,我们可以考虑使用 parent 指针的方式构建对话框,避免设置WA_DeleteOnClose
属性;或者是利用另外的方式。
实际上,QDialog::exec()
是有返回值的,其返回值是QDialog::Accepted
或者QDialog::Rejected
。一般我们会使用类似下面的代码:
QDialog dialog(this); if (dialog.exec() == QDialog::Accepted) { // do something } else { // do something else }
来判断对话框的返回值,也就是用户是点击了“确定”还是“取消”。更多细节请参考QDialog
文档。
模态对话框相对简单,如果是非模态对话框,QDialog::show()
函数会立即返回,如果我们也这么写,就不可能取得用户输入的数据。因为show()
函数不会阻塞主线程,show()
立即返回,用户还没有来得及输入,就要执行后面的代码,当然是不会有正确结果的。那么我们就应该换一种思路获取数据,那就是使用信号槽机制。
由于非模态对话框在关闭时可以调用QDialog::accept()
或者QDialog::reject()
或者更通用的QDialog::done()
函数,所以我们可以在这里发出信号。另外,如果找不到合适的信号发出点,我们可以重写QDialog::closeEvent()
函数,在这里发出信号。在需要接收数据的窗口(这里是主窗口)连接到这个信号即可。类似的代码片段如下所示:
//!!! Qt 5 // in dialog: void UserAgeDialog::accept() { emit userAgeChanged(newAge); // newAge is an int QDialog::accept(); } // in main window: void MainWindow::showUserAgeDialog() { UserAgeDialog *dialog = new UserAgeDialog(this); connect(dialog, &UserAgeDialog::userAgeChanged, this, &MainWindow::setUserAge); dialog->show(); } // ... void MainWindow::setUserAge(int age) { userAge = age; }
上面的代码很简单,这里不再赘述。另外,上述代码的 Qt 4 版本也应该可以很容易地实现。
不要担心如果对话框关闭,是不是还能获取到数据。因为 Qt 信号槽的机制保证,在槽函数在调用的时候,我们始终可以使用sender()
函数获取到 signal 的发出者。关于sender()
函数,可以在文档中找到更多的介绍。顺便说一句,sender()
函数的存在使我们可以利用这个函数,来实现一个只能打开一个的非模态对话框(方法就是在对话框打开时在一个对话框映射表中记录下标记,在对话框关闭时利用sender()
函数判断是不是该对话框,然后从映射表中将其删除)。
61 评论
Qt有无根据.h文件生成cpp文件的指令?
Qt 本身没有类似的工具,IDE 可能会提供。例如在 Qt Creator 中,你可以在头文件的函数中点右键,会有“在 CPP 文件中生成实现”类似的功能。
”sender() 函数的存在使我们可以利用这个函数,来实现一个只能打开一个的非模态对话框。“
实在不理解这句话什么意思。。。”一个只能打开一个的非模态对话框“???
由于非模态对话框不会阻塞用户的后续操作,因此你可以打开很多个非模态对话框。有时我们需要只能打开一个的非模态对话框(比如查找对话框),此时,我们可以利用 sender() 函数进行判断。
也就是说可以利用sender()函数来控制主窗口无法打开多个相同的非模态对话框。。。但仍然可以在主窗口上打开多个不同的非模态对话框。。。不知道这样理解是否正确?
是的,这也只是一种解决思路
最后一段代码是要新建一个继承自Dialog的UserAgeDialog类,然后在类里添加那些代码吗?
是的,这只是一些代码片段,描述了一下大致的思路
可以把所有代码写出来么?对像我这样初学者还是很难哦。
请教一下老师,为什么这句UserAgeDialog *dialog = new UserAgeDialog(this);会出问题?
编译后它如下提示:
C:\Qt\Qt5.2.0\Tools\QtCreator\bin\untitled6\mainwindow.cpp:42: error: C2664: “UserAgeDialog::UserAgeDialog(const UserAgeDialog &)”: 不能将参数 1 从“MainWindow *const ”转换为“const UserAgeDialog &”
原因如下: 无法从“MainWindow *const ”转换为“const UserAgeDialog”
无构造函数可以接受源类型,或构造函数重载决策不明确
UserAgeDialog 应该有一个接受 QWidget * 的构造函数,应该是你少了这个构造函数。
怎么才能不用QT自带的API将其他程序里的数据输出到界面上呢,类似于摸你的文本编辑器
这个不是很清楚什么意思?
豆子,难道这里UserAgeDialog *dialog = new UserAgeDialog(this);
connect(dialog, &UserAgeDialog::userAgeChanged, this, &MainWindow::setUserAge);
dialog->show();
不应该用setAttribute()函数销毁对话框吗?
可以的,不过因为有 this 指针,当主窗口关闭时,UserAgeDialog 也会被销毁,因此也没有内存泄露。
如果mainwindow这个主窗口不关闭的情况下,我打开一个对话框,然后又关闭,然后又重新打开,关闭,如此反复,那么,这样做不是会造成程序奔溃?因为主窗口没有关闭,对话框一直占用内存。豆子哥,这样理解对吗?如果按照我这个理解的话,我觉得setAttribute()函数必须要有。
是的,理论上存在这个问题。不过感觉在现在的机器上面仅靠这种操作想把内存耗尽,应该还是蛮困难的。不过,尽管如此,设置 attribute 也是一个好的习惯,所以还是应该设置才对。
豆子老师,请问在哪里可以看到Qt的官方文档,英文的也可以,比如你上面提到的QDialog的文档。
当你安装好 Qt 之后,文档也一并安装。你可以在 Qt Creator 左侧的帮助里面看到。或者在 bin 目录下直接打开 assistant 程序。
您好!我有想问题,就是如何将对话框的控件和MainWindow的控件通过信号槽连接起来。我想把slider放到一个对话框里,把spinbox放到MainWindow里,然后打开对话框后拖动slider,MainWindow里的spinbox数据发生相应改变,该如何做?
你可以把 slider 的信号通过对话框 emit 出来,或者将 slider 暴露出来。不过按照面向对象的封装要求,不应该把内部组件暴露出来,所以最好的方法是把 slider 的信号通过对话框发出。这样就可以把主窗口与对话框相连。
emit的方式如何实现
直接发出信号就好了:connect(*oneWidget, SIGNAL(s1), this, SIGNAL(s2));
因为slider是在dialog里的,spinbox是在mainwindow里的,不知道应该把connect放在哪里,放在mainwindow里sender不知道怎么指定,放在带slider的dialog里receiver又不知道怎么指定 :-p
豆子老师,为什么我每次使用dialog( this)的时候,点开open的窗口会有错误:QWindowsWindow :: setGeometry: Unable to set geometry 100x30+530+225 on
这个还不大清楚,不知道哪里的问题。要看具体的代码才好。
这个貌似不是错误, 而是 warning , 除非你的警告级别设高了。 这是因为 Widget 的“建议最大宽高”小于你设置的宽高。 可以 修改你的宽高参数, 也可以增大建议最大宽高。
http://home.cnblogs.com/u/aslistener/
豆子老师,有什么办法可以在connect()调用之后把获得的值传到外部变量。
如果一直持有指针,即可在有效范围内获取实际值了。
豆子大大,新手表示那个useragedialog类不知道怎么搞 QAQ
UserAgeDialog 就是一个简单的对话框,上面只有一个 QTextLine 组件,用于接收用户输入的年纪。你可以尝试编写下这个对话框。
豆子大神,单一的Qtextline组件不能显示在对话框上吧,得用QTextlayout才行啊,而且必须draw在某个QPainter上,不知道我说的对不对。
可以使用 QLineEdit
请问具体的UserAgeChanged这个函数是怎么写的呢?
这是一个信号,不需要自己实现,moc 工具会帮助生成必须的代码。
accept()什么时候调用呢?
点击dialog的确定按钮时候会调用accept()
哪里有确定按钮,dialog只有一个标题,一个?,和一个X
老师,你好,我想问一下,我要实现点击“读卡”按钮之后,它就开始读卡,这个怎么实现呢,或者,哪里有这样的学习资源?
读卡作为一个独立的函数,在 SLOT 里面调用即可。这个函数的实现需要根据不同读卡系统有所不同
实现的主要区别在哪里?比如是用QT的某一个类还是怎么回事?
大神,我现在在主页面有很多按钮,每一个按钮点击后弹出一个新的页面,例://快捷键
void MainDialog::shortcuts()
{ scDlg = new ShortcutsDialog(this);
scDlg->show();
scDlg->move(200,184);
}
然后有设置了快捷键调用,页面调用一次弹出一次,可以有什么方法限制吗,一次只能弹一个页面,
上面这个解决了,用bool判断一下,但是有一个当作锁屏用的页面,因为他是在什么时候都能用,没办法用上面的办法,页面出来后,鼠标点击不能用,但是用快捷键调用他会重复出现,几个页面叠在一起
这个你可以这么做:不要在 shortcuts() 中每次创建一个对话框,而是在构造函数中创建完毕之后,在 shortcut() 函数中只调用 show() 函数即可(可以使用 isVisible() 判断当前对话框是不是已经被显示出来)。
好的,谢谢
大神,因为新弹出的是当锁屏用的,怎么设置之前的页面不可用,现在是页面能正确的弹出来,用bool判断其它页面都不出现,if(dialogState)
{
dialogState=false;
lockDlg = new LockDialog(this);
lockDlg->show();
lockDlg->move(0,0);
if(lockDlg->exec()==QDialog::Rejected)
{
dialogState=true;
txmEdit->setFocus();
}
}
没太明白你的意思。是不是这段代码需要放在所有页面中?
当锁屏的时候设置主界面不可用,被锁屏dialog覆盖的maindialog不可用,解锁后才可用
是不是说你的锁屏对话框弹出来之后,虽然下面的界面不能用鼠标点击,但是快捷键还是可以用?如果是这样的话,试试设置 shortcutContext,将 shortcutContext 设置为 Qt::WidgetWithChildrenShortcut 之类,即当组件有焦点(默认是整个窗口有焦点)的时候才能使用快捷键。
大神,快捷键是用event 和 keyevent设置的,该怎么设置他们可用或者不可用
if(keyEvent->key()==Qt::Key_F2)
{
txmEdit->setText("");
txmEdit->setFocus();
return true;
}这是设置的语句,在另外一个界面弹出的时候,按F2依然会执行,那个界面也会失去焦点,该怎么设置
之前形容的不够精确,是这样的在maindialog有个快捷键事件F2是让lineedit清空并聚焦,Ctrl+L弹出锁屏界面lockdialog,弹出语句:lockDlg->show(); lockDlg->move(0,0);锁屏界面弹出后焦点在lockdialog,这个时候如果按F2 maindialog的lineedit依然会聚焦,此时使用键盘事件已经无法操作lockdialog了,但是显示的还是lockdialog,lockdialog失去焦点了
不好意思,麻烦大神了,有两条评论正在审核,这个问题我已经解决了,把他想复杂了
大神,考虑使用parent指针的方式构建对话框,避免设置WA_DeleteOnClose属性这句话是什么意思?具体怎么做呢?
使用带有
parent
指针的方式创建对话框,例如dialog = new QDialog(this);
这样,而不是dialog = new QDialog;
,这样的话就不需要设置WA_DeleteOnClose
了,因为有parent
指针,Qt 会在销毁parent
时自动销毁对话框自己遇到的一点坑,在这里给大家分享一下。
我按照豆子老师的思路新建了一个派生自QDialog的对话框类,然后在构建时一直卡在链接期,提示LINK2001错误,排查了很多遍都不知道哪里出了问题。最后是随手点了一下“执行qmake”解决了问题。如果也有人遇到了和我一样的问题,可以试一试执行qmake。我觉得应该是新建了类的文件后makefile需要更新的原因。如有谬误还请诸位大神指点。
这个问题有可能是因为在创建类之后又添加了 Q_OBJECT 宏,如果是这样就必须先执行 qmake。因为普通的预处理不能正确处理 Q_OBJECT 宏。
最后那个例子有点云里雾里的,想实现什么功能都没看出来,,,不如放个完整的伪代码上去
最后的例子是什么意思啊,大佬,刚开始学,看不懂
最后的例子是重写 QDialog 的 accept() 函数,在其中发出信号,然后外面可以通过槽连接该信号,就可以在对话框外部拿到发出的值
你重写为啥要在UserAgeDialog::accept()函数中最后加上这个呢QDialog::accept();?
void UserAgeDialog::accept() 应该写在哪里,useragedialog.cpp吗,newage怎么传递?这个例子跑不通,求讲解qwq