[背景]
如果不要QMainWindow自带的标题栏和状态栏,需要自定义的无边框状态时,我们在移除QMainWindow自带的标题栏和状态栏后,窗体就不能移动和拉伸缩放了。需要代码实现窗体无边框状态的移动和拉伸缩放功能。
w.setWindowFlags(Qt::FramelessWindowHint);//移除标题栏
w.setMouseTracking(true);//此属性保存是否为小部件启用鼠标跟踪,如果禁用了鼠标跟踪(默认值),则当移动鼠标时至少按下一个鼠标按钮时,小部件仅接收鼠标移动事件。如果启用了鼠标跟踪,即使没有按下按钮,小部件也会接收鼠标移动事件。
然后重写三个事件
void mousePressEvent(QMouseEvent *event) override;//获取鼠标的移动
void mouseMoveEvent(QMouseEvent *event) override;//获取鼠标的点击
void mouseReleaseEvent(QMouseEvent *event) override;//获取鼠标的释放
[问题]
以上设置在Qwidgets中完全ok,但如果是QMainWindow,mouseMoveEvent只能在鼠标按下时才反应。
[原因]
使用UI(QT设计师)添加控件,那么在QMainWindow窗体上会自动添加一个centralWidget层,而我们使用的是QMAinWindow的鼠标移动监测事件,
centralWidget将QMainWindow遮挡住了,所以鼠标移动也无法进入mouseMoveEvent。
[方法1:同步设置相关联的部件setMouseTracking(true)]
●原理:CentrolWidget 是 QMainWindow 的子类,你如果在子类上响应鼠标事件,只会触发子类的 mouseMoveEvent,根据C++继承和重载的原理,所以子类也要 setMouseTracking(true);所以如果你想响应鼠标事件的控件被某个父控件包含,则该控件及其父控件或容器也需要 setMouseTracking(true);
●例子:
w.setWindowFlags(Qt::FramelessWindowHint);//移除标题栏
ui->centralwidget->setMouseTracking(true);//首先设置centralwidget,注意和QWidgets(QWidgets是直接setMouseTracking(true)就行)的区别。
setMouseTracking(true);//下面就是从父级开始向下相关联的部件都得设置。
ui->widget->setMouseTracking(true);
ui->widget_2->setMouseTracking(true);
ui->widget_3->setMouseTracking(true);
ui->widget_4->setMouseTracking(true);
●评价:最有效的办法,因为效果很好、难点在于要找出所有相关的控件进行设置,要排查梳理所有控件之间的父子级关系等,如果控件很多,这样做操作有点麻烦。
[方法2:grabMouse()和releaseMouse()组合使用]
●原理:
在鼠标移动时捕获鼠标事件,然后在鼠标离开窗口时结束捕获鼠标事件,可以在mouseMoveEvent()函数中调用grabMouse()函数,然后在leaveEvent()函数中调用releaseMouse()函数。这样做可以实现一些跟随鼠标的效果。
如果想要在其他情况下捕获或释放鼠标事件,可以根据自己的需求选择合适的时机和方式。但是注意,不要忘记释放鼠标,否则会影响其他控件的正常使用。
●例子:
2.1 main中设置w.setWindowFlags(Qt::FramelessWindowHint);
w.setMouseTracking(true);
2.2 Maindown的构造函数中设置ui->centralwidget->setMouseTracking(true);
2.3 重写void MainWindow::enterEvent(QEvent *event)函数,即.h中加
protected:
void mousePressEvent(QMouseEvent *event) override;
.cpp中加
void MainWindow::enterEvent(QEvent *event)//进入窗体的事件
{
grabMouse();
}
2.4 记住mouseReleaseEvent函数中要加releaseMouse();
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
releaseMouse();
//m_bLeftPress = false;
//QApplication::restoreOverrideCursor();
}
●评价:
使用grabMouse()函数有一些缺点,例如导致窗体其它控件不能点击。这是因为当一个widget调用grabMouse()函数时,其他widget将无法接收到鼠标事件,导致程序异常或无法响应鼠标事件。因此,如果使用grabMouse()函数捕获了鼠标事件,需确保在适当的时候释放鼠标,以允许其他widget接收到鼠标事件。可以使用releaseMouse()函数来释放鼠标捕获。这种方式实际运用不容易掌控,尤其是控件能否点击和鼠标捕捉释放之间的平衡。
[相关小菜]
●QCursor::pos()和event->globalPos()的区别:
QCursor::pos()返回的是鼠标指针在屏幕坐标系中的当前位置,而event->globalPos()返回的是鼠标指针在屏幕坐标系中的事件发生时的位置。1
QCursor::pos()是直接访问鼠标指针的位置,而event->globalPos()是通过事件系统传递的位置。1
QCursor::pos()和event->globalPos()在正常情况下应该是相同或接近的,但如果鼠标移动太快或事件处理延迟,可能会有一些差异。
QCursor::pos()和event->globalPos()都是全局坐标,如果要转换为窗口或控件的相对坐标,需要使用mapFromGlobal()函数。
●event->pos().x()和event->globalPos()的区别:
前者是事件(event)的位置(pos())的横坐标(x()),表示事件发生时鼠标相对于窗口(window)的左上角(0,0)的水平距离;后者是事件(event)的全局位置(globalPos()),表示事件发生时鼠标相对于桌面(desktop)的左上角(0,0)的水平和垂直距离。这两者的值可能不同,因为窗口和桌面的左上角可能不重合,而且鼠标的位置可能在事件发生后发生变化。