目录
0、引言:
1、问题描述:
2、解决方案:
3、解决后效果:
0、引言:
在Qt自定义控件 —— 颜色选择组合控件https://blog.csdn.net/YMGogre/article/details/128955257一文中我们创建了自定义的组合控件,在该控件中包含了三个子控件 —— QGraphicsView、QLineEdit 和 QPushButton。当我们通过 setItemWidget() 方法将自定义控件设置到 QTreeWidget、QTableWidget 或 QListWidget 的 item(QTreeWidegtItem、QTableWidgetItem 或 QListWidgetItem) 中时,可能会发现下面这种奇怪的现象(以 QTreeWidget 的 QTreeWidegtItem 为例):
1、问题描述:
可以看到,当 Qt 自带的控件(比如“网格”项第二列的 QCheckBox、“平面单元格数”项第二列的 QSpinBox 等)通过 setItemWidget() 方法设置到 QTreeWidegtItem 中时,我们去点击这些 Qt 自带的控件那么其所在的 QTreeWidegtItem 也会被选中;而对于我们自定义的控件,就有那么些许不同了:
- 对于自定义控件中的三个子控件 —— QGraphicsView、QLineEdit 和 QPushButton,我们去点击它们时,自定义控件所在的 QTreeWidegtItem 不会被选中;
- 当我们点击自定义控件区域内、三个子控件区域外的自定义控件区域时,和 Qt 自带的控件一样,自定义控件所在的 QTreeWidegtItem 会被选中;
由上面两点我们基本就可以判断出问题所在了:
- setItemWidget() 方法在把控件设置到 QTreeWidegtItem 中的同时也会建立一种绑定关系,这使得我们在点击控件对象(比如上面 GIF 图中这个自定义控件的空白区域)时会自动去选中其所在的 QTreeWidegtItem。这是Qt框架自动完成的事儿,我们无需手动去做。
- 当自定义控件中存在子控件时,在任意子控件上点击,此时点击响应会响应到子控件上;而自定义控件本身不会产生任何响应。
- 所以,QTreeWidegtItem、自定义控件、自定义控件的子控件三者的关系应该如下图所示:
2、解决方案:
我们已经理解了 setItemWidget() 方法已经将自定义控件和 QTreeWidget 项建立了关联了,既然子控件和自定义控件之间没有鼠标事件关联,那我们可以重写自定义控件的 mousePressEvent() 方法或者 eventFilter() 方法来达到我们的目的,这里笔者选择重写事件过滤器(eventFilter)方法:
- 在自定义控件的 .h 文件内包含相关头文件并声明重写事件过滤器方法:
#include <QEvent> #include <QMouseEvent> #include <QPushButton> ... protected: bool eventFilter(QObject *watchedm, QEvent *event) override; //重写事件过滤器方法
- 在 .cpp 文件内自定义控件的构造函数中为需要的子控件安装事件过滤器:
... /*QGraphicsView的鼠标事件会传递给它所显示的QGraphicsScene,然后再传递给场景中的QGraphicsItem。 由于我们并没有设置任何的QGraphicsScene场景,所以我们设置该控件属性:传递给父对象做鼠标事件处理*/ ui->ColorDisplay->setAttribute(Qt::WA_TransparentForMouseEvents); /***************************************** * 为自定义控件的子控件安装该自定义控件的事件过滤器 ****************************************/ ui->ColorLineEdit->installEventFilter(this); ui->btn_SelectColor->installEventFilter(this);
- 在事件过滤器方法中检测鼠标点击事件,通过代码选中自定义控件,随后传递事件给子控件让其正常响应点击:
/** * @brief 重写事件过滤器方法 * @attention 这里简要说下为什么要重写该方法,这是因为当我们的自定义控件中有其他子控件时, * 点击子控件的点击响应是相应到子控件上的,而通常我们希望所有子控件的父对象(也就是自定义控 * 件本身)也会在点击这些子控件时有响应 * @param QMouseEvent *event ———— 事件对象 */ bool MyPalette::eventFilter(QObject *watched, QEvent *event) { //拦截子控件的鼠标按下或释放事件 if(event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) { //QEvent类型转换为QMouseEvent类型 QMouseEvent *mouseevent = static_cast<QMouseEvent *>(event); this->QWidget::mousePressEvent(mouseevent); //调用基类的mousePressEvent方法 //如果事件的被监视对象是QPushButton if(qobject_cast<QPushButton *>(watched)) this->setFocus(); //设置自定义控件本身获得焦点 return false; //传递事件给子控件,让其正常响应点击 //return true; //事件处理完毕(不传递事件给子控件,只响应自定义控件本身被选中) } //其他类型的事件交由基类处理 return QWidget::eventFilter(watched, event); }
由上面三个步骤可以看到,我们设置了 QGraphicsView 由其父对象(自定义控件对象)做鼠标事件处理;而 QLineEdit 和 QPushButton 则是安装了事件过滤器并拦截这两个子控件的鼠标按下或释放事件。
这是因为 QGraphicsView 只是作为显示颜色的控件,它无需与用户交互,所以让它的父对象来做事件处理就行了;而 QLineEdit 和 QPushButton 自己也需要响应鼠标事件(比如点击 QLineEdit 文本框进入编辑模式以及点击 QPushButton 按钮打开窗口),所以需要安装事件过滤器对自定义控件做处理并在处理完成后将事件传递给子控件让其正常响应。
此外,在事件过滤器方法中可能会产生疑惑的代码:
//如果事件的被监视对象是QPushButton
if(qobject_cast<QPushButton *>(watched))
this->setFocus(); //设置自定义控件本身获得焦点
- 为什么只有被监视对象是 QPushButton 时才设置自定义控件获得焦点?QLineEdit 和 QPushButton 不都安装了事件过滤器吗?
答:因为 QLineEdit 的操作是点击文本框,进入编辑模式。而那个时候文本框是获得焦点的对象,如果让自定义控件获得了焦点点击文本框就不会进入编辑模式了。
- 为什么需要这两句代码呢?
答:其实我也不太懂,即便使用 QWidget::mousePressEvent(mouseevent); 调用了基类的mousePressEvent方法仍然无法在点击按钮时选中自定义控件本身,所以还是用了这两句代码让自定义控件获得焦点。(欢迎大佬评论区指点迷津🙁)文章来源:https://www.toymoban.com/news/detail-492476.html
3、解决后效果:
文章来源地址https://www.toymoban.com/news/detail-492476.html
到了这里,关于Qt自定义控件 —— 子控件与父控件的鼠标事件问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!