Как отключить доставку событий мыши в виджет, но не его дочерние элементы в Qt?

В течение последних двух дней я искал способ передать события мыши в виджеты за виджетом, используемым в качестве контейнера/родителя для его потомков. Я знаю, что есть способ сделать виджет прозрачным для событий мыши, например так:

QWidget w;
w.setAttribute( Qt::WA_TransparentForMouseEvents );

Но это также отключает доставку событий мыши своим детям! Я хочу, чтобы дети переднего виджета и виджеты за передним виджетом получали события мыши.

Qt :: WA_TransparentForMouseEvents: Если этот параметр включен, этот атрибут отключает доставку событий мыши в виджет и его дочерние элементы. События мыши доставляются в другие виджеты, как если бы виджет и его дочерние элементы отсутствовали в иерархии виджетов; щелчки мыши и другие события эффективно "проходят" через них. Этот атрибут по умолчанию отключен.

Если у вас есть идеи о том, как сделать виджет прозрачным для событий мыши, но не для детей, пожалуйста, поделитесь!

Ответ 1

Наконец я нашел решение :)

QWidget :: setMask (const QRegion & region)

https://doc.qt.io/qt-5/qwidget.html#setMask-1

http://qt-project.org/doc/qt-4.8/qwidget.html#setMask

Я нашел решение здесь: http://www.qtcentre.org/archive/index.php/t-3033.html

QRegion reg(frameGeometry());
reg -= QRegion(geometry());
reg += childrenRegion();
setMask(reg);

Теперь дети переднего виджета и виджеты за передним виджетом реагируют на события мыши по мере необходимости!

Помните, что вам нужно будет вызывать эти строки снова всякий раз, когда размер переднего виджета изменяется, чтобы пересчитать геометрию маски!

void someWidget::resizeEvent(QResizeEvent *e){
  QWidget::resizeEvent(e);
  QRegion reg(frameGeometry());
  reg-=QRegion(geometry()); 
  reg+=childrenRegion();
  setMask(reg);
}

Ответ 2

Решение OP является удивительным и очень элегантным. Для полноты еще один вариант - игнорировать события мыши, когда они достигнут виджета контейнера. Это можно сделать либо путем подклассификации, либо через eventFilter.

Это решение может быть полезно, если виджет динамически скрывает/показывает много дочерних виджетов и вычисляет маску.

Примечание. Если вы хотите отслеживать события перемещения мыши в фоновых виджетах, вы должны setMouseTracking(true) получать (а затем игнорировать) QEvent::MouseMove, когда кнопка не нажата.

Пример по подклассу

ContainerWidget::ContainerWidget(...) {
  setMouseTracking(true);
}

void ContainerWidget::mouseMoveEvent(QMouseEvent* e) {
  e->ignore();
}
void ContainerWidget::mousePressEvent(QMouseEvent* e) {
  e->ignore();
}

Пример использования фильтра событий

// Assume the container widget is configured in the constructor
MainWindow::MainWindow(...) {
  // ...
  containerWidget->installEventFilter(this);
  containerWidget->setMouseTracking(true);
  // ...
}

bool MainWindow::eventFilter(QObject* o, QEvent* e) {
  if (o == containerWidget &&
     (e->type() == QEvent::MouseMove || e->type() == QEvent::MouseButtonPress)) {
    e->ignore();
    return false;
  }
  return QMainWindow::eventFilter(o, e);
}