Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: kambala от Декабрь 21, 2014, 17:46



Название: [РЕШЕНО] отправить перехваченное событие родителю
Отправлено: kambala от Декабрь 21, 2014, 17:46
Здравствуйте. Имеется QListView с кастомным делегатом, в эдиторе (persistent) которого имеется QLabel. Хочу перехватывать даблклики на QLabel и перенаправлять их в QListView (как бы даблкликать сквозь текст чтобы он не выделялся).

У лейбла установлено
Код
C++ (Qt)
label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
чтобы можно было выделять текст мышкой, поэтому он перехватывает даблклики.

Мое решение: фильтруем события лейбла в листвью:
Код
C++ (Qt)
QWidget *FeedItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/*option*/, const QModelIndex &/*index*/) const
{
   ImageWithLabelWidget *w = new ImageWithLabelWidget(parent);
   w->label->installEventFilter(parent->parent()); // listview
   return w;
}
 
class FeedListView : public QListView
{
protected:
   bool eventFilter(QObject *o, QEvent *e)
   {
       if (o->inherits("QLabel") && e->type() == QEvent::MouseButtonDblClick && static_cast<QMouseEvent *>(e)->button() == Qt::LeftButton)
       {
           qApp->sendEvent(this, e);
           return true;
       }
       return QListView::eventFilter(o, e);
   }
};

И тут возникает проблема: ничего не происходит когда первый раз даблкликаешь на текст, на второй и последующие разы уже все нормально. Наблюдения:
  • отсылка сообщения viewport() вместо this не помогает
  • sendEvent в обоих случаях возвращает true
  • лейбл занимает не все место эдитора. Если сперва даблкликнуть вне лейбла (отрабатывает как положено), а потом на лейбл, то срабатывает сразу
  • postEvent вместо sendEvent крашит программу
  • notify вместо sendEvent не помогает

Как чинить? Тестировал в Qt 5.3 и 5.4 на винде.

Решение состоит в отправке нужного сигнала руками (не самый лучший подход на мой взгляд):
Код
C++ (Qt)
class FeedListView : public QListView
{
protected:
   bool eventFilter(QObject *o, QEvent *e)
   {
       if (o->inherits("QLabel") && e->type() == QEvent::MouseButtonDblClick)
       {
           QMouseEvent *me = static_cast<QMouseEvent *>(e);
           if (me->button() == Qt::LeftButton)
           {
               emit doubleClicked(indexAt(me->pos()));
               return true;
           }
       }
       return QListView::eventFilter(o, e);
   }
};


Название: Re: отправить перехваченное событие родителю
Отправлено: Fregloin от Декабрь 22, 2014, 14:51
я так понимаю желательно указать focustPolicy(Qt::StrongPolicy).


Название: Re: отправить перехваченное событие родителю
Отправлено: DenKor29 от Декабрь 22, 2014, 14:58
А что мешает на сигнал linkActivated QLabel повесить слот QListView ?
И весь код выполнить там ?


Название: Re: отправить перехваченное событие родителю
Отправлено: kambala от Декабрь 22, 2014, 15:03
я так понимаю желательно указать focustPolicy(Qt::StrongPolicy).
поставил на листвью — не помогло
А что мешает на сигнал linkActivated QLabel повесить слот QListView ?
И весь код выполнить там ?

мне игнорировать надо даблклики на любой текст. ссылки-то открываются по одиночному клику.


Название: Re: отправить перехваченное событие родителю
Отправлено: DenKor29 от Декабрь 22, 2014, 15:13
мне игнорировать надо даблклики на любой текст. ссылки-то открываются по одиночному клику.

У вас в приложении есть принципиальная разница между двойным и одиночным кликом ?
Странный интерфейс для пользователя.

Можно таймер сделать для определения времени вызова метода ....
Что то типа :

if (time==0) {time=1;return;}
//Обработка
......
time = 0;

return;


Или использовать объект QTimer (start,elapsed)

P.s переопределите mousePressEvent для QLabel в крайнем случае




Название: Re: отправить перехваченное событие родителю
Отправлено: kambala от Декабрь 22, 2014, 15:31
У вас в приложении есть принципиальная разница между двойным и одиночным кликом ?
Странный интерфейс для пользователя.
да: двойной клик открывает новое окно, а одиночный ничего не делает. NoItemSelection выставлено. странного ничего не вижу.

про таймер не понял.
P.s переопределите mousePressEvent для QLabel в крайнем случае
смысл? фильтр событий же и так висит и все ловит. проблема именно в отправке события (точнее его обработке листвью).


Название: Re: отправить перехваченное событие родителю
Отправлено: GreatSnake от Декабрь 22, 2014, 15:39
Попробуй вместо
Код
C++ (Qt)
qApp->sendEvent(this, e);
это
Код
C++ (Qt)
e->ignore();


Название: Re: отправить перехваченное событие родителю
Отправлено: kambala от Декабрь 22, 2014, 19:25
ни ignore ни accept не помогают.

если сперва сделать одиночный клик, а потом двойной, то не срабатывает все равно.

есть подозрение, что надо принудительно делать setCurrentIndex перед отправкой сообщения. буду пробовать.


Название: Re: отправить перехваченное событие родителю
Отправлено: lit-uriy от Декабрь 22, 2014, 19:39
kambala, помоему ты зря во вюхе проблему решаешь.
Если описанное поведение - особенность делегата, то в нём и нужно всё разруливать.

А то фильтр событий ты устанавливаешь в делегате, а сам фильтр находится во вюхе.
Да и зачем фьюхе пересылать событие, которое предлагается игнорировать


Название: Re: отправить перехваченное событие родителю
Отправлено: kambala от Декабрь 22, 2014, 19:57
вьюха должна перехватить событие (чтобы отработал QListView::doubleClicked), поэтому в нее и пересылаю.

к тому же, делегатов много создается в памяти, а вьюха лишь одна.

P.S. setCurrentIndex не помогает тоже.


Название: Re: отправить перехваченное событие родителю
Отправлено: __Heaven__ от Декабрь 22, 2014, 23:23
Так а если есть возможность в делегате отлов производить, быть может, пусть делегат эмитит сигнал какой-нибудь, который будет перехватываться вьюхой?


Название: Re: отправить перехваченное событие родителю
Отправлено: kambala от Декабрь 23, 2014, 00:37
ну остается только руками пускать сигнал (это работает) вместо пересылки события. я надеялся на более «чистое» решение :)


Название: Re: [РЕШЕНО] отправить перехваченное событие родителю
Отправлено: Akon от Декабрь 23, 2014, 01:02
А попробуй в твоем исход. вар-те:
Код:
qApp->postEvent(this, e->clone());
Конечно, e->clone() нет, но смысл, думаю, понятен.


Название: Re: [РЕШЕНО] отправить перехваченное событие родителю
Отправлено: Akon от Декабрь 23, 2014, 01:10
Цитировать
И тут возникает проблема: ничего не происходит когда первый раз даблкликаешь на текст, на второй и последующие разы уже все нормально.
Может, как то фокус ввода тут замешан? ... focustPolicy(Qt::StrongPolicy) на лейбл.


Название: Re: [РЕШЕНО] отправить перехваченное событие родителю
Отправлено: kambala от Декабрь 23, 2014, 01:23
А попробуй в твоем исход. вар-те:
Код:
qApp->postEvent(this, e->clone());
Конечно, e->clone() нет, но смысл, думаю, понятен.

с sendEvent/postEvent ничего не происходит. если «постануть» ивент, созданный на стеке, то приложение зависает.
Цитировать
И тут возникает проблема: ничего не происходит когда первый раз даблкликаешь на текст, на второй и последующие разы уже все нормально.
Может, как то фокус ввода тут замешан? ... focustPolicy(Qt::StrongPolicy) на лейбл.

не помогло тоже


Название: Re: [РЕШЕНО] отправить перехваченное событие родителю
Отправлено: Akon от Декабрь 23, 2014, 01:30
Ну а в чем разница между первым даблкликом (когда ничего не происходит) и последующими (когда все ОК)?


Название: Re: [РЕШЕНО] отправить перехваченное событие родителю
Отправлено: kambala от Декабрь 23, 2014, 01:31
если б я знал :)

даблклики вне лейбла срабатывают сразу.


Название: Re: отправить перехваченное событие родителю
Отправлено: DenKor29 от Декабрь 23, 2014, 09:23
P.s переопределите mousePressEvent для QLabel в крайнем случае
смысл? фильтр событий же и так висит и все ловит. проблема именно в отправке события (точнее его обработке листвью).
[/quote]

например в mousePressEvent  не обрабатываете сигнал двойного нажатия мыши и игнорируете данное событие.
Как я понимаю это событие будет послано дальше автоматом QListView (если оно находится за объектом QLabel).
Никаких фильтров при этом использовать не нужно будет.

У Вас слишком сложная логика обработки событий используется ....


Название: Re: [РЕШЕНО] отправить перехваченное событие родителю
Отправлено: Akon от Декабрь 23, 2014, 12:28
А если попробовать табом перейти на ячейку (чтобы последняя получила фокус) и даблкликнуть?


Название: Re: [РЕШЕНО] отправить перехваченное событие родителю
Отправлено: kambala от Декабрь 23, 2014, 16:15
например в mousePressEvent  не обрабатываете сигнал двойного нажатия мыши и игнорируете данное событие.
Как я понимаю это событие будет послано дальше автоматом QListView (если оно находится за объектом QLabel).
Никаких фильтров при этом использовать не нужно будет.

У Вас слишком сложная логика обработки событий используется ....
и чем это отличается от того, что я проигнорирую или приму событие в фильтре? как уже писал выше, это не помогает.

с удовольствием выслушаю логику попроще. есть листвью с моделью:
- каждый айтем содержит текст, который должен выделяться мышкой
- использую делегат т.к. надо еще и другие вещи отрисовывать в айтеме
- есть обработчик даблклика листвью
- даблклик не должен выделять отдельные слова в тексте, а должен срабатывать обработчик из предыдущего пункта
А если попробовать табом перейти на ячейку (чтобы последняя получила фокус) и даблкликнуть?
жал таб произвольное количество раз и даблкликал — не помогает.

могу написать тестовый проект если кто-то поковыряться хочет.


Название: Re: [РЕШЕНО] отправить перехваченное событие родителю
Отправлено: Igors от Декабрь 23, 2014, 16:31
могу написать тестовый проект если кто-то поковыряться хочет.
Давайте попробуем (ничего не обещаю но разобраться попытаюсь)


Название: Re: [РЕШЕНО] отправить перехваченное событие родителю
Отправлено: kambala от Декабрь 23, 2014, 17:27
проект в аттаче. в Qt 4 беда аналогичная.


Название: Re: [РЕШЕНО] отправить перехваченное событие родителю
Отправлено: Igors от Декабрь 23, 2014, 20:13
"Так и должно быть" - хотя от этого не легче  :) Испущенное Вами в фильтре событие обрабатывается здесь
Код
C++ (Qt)
void QAbstractItemView::mouseDoubleClickEvent(QMouseEvent *event)
{
   Q_D(QAbstractItemView);
 
   QModelIndex index = indexAt(event->pos());
   if (!index.isValid()
       || !d->isIndexEnabled(index)
       || (d->pressedIndex != index)) {
       QMouseEvent me(QEvent::MouseButtonPress,
                      event->localPos(), event->windowPos(), event->screenPos(),
                      event->button(), event->buttons(), event->modifiers());
       mousePressEvent(&me);
       return;
   }
   // signal handlers may change the model
   QPersistentModelIndex persistent = index;
   emit doubleClicked(persistent);
...
 
То есть если даблкликнутая ячейка неактивна, то она активируется - но до emit doubleClicked(persistent) дело не доходит. Только если она уже активна - тогда да.

Может лучше перекрыть mouseDoubleClickEvent - не обдумывал, просто первое что приходит в голову


Название: Re: [РЕШЕНО] отправить перехваченное событие родителю
Отправлено: kambala от Декабрь 23, 2014, 20:25
пробовал перед sendEvent делать setCurrentIndex() — почему-то не помогало...
Может лучше перекрыть mouseDoubleClickEvent - не обдумывал, просто первое что приходит в голову
получилось только так, но это ж ничем не отличается от эмита прямо в фильтре :)
Код
C++ (Qt)
   void mouseDoubleClickEvent(QMouseEvent *e)
   {
       emit doubleClicked(indexAt(e->pos()));
   }
 
   bool eventFilter(QObject *o, QEvent *e)
   {
       if (o->inherits("QLabel") && e->type() == QEvent::MouseButtonDblClick)
       {
           auto me = static_cast<QMouseEvent *>(e);
           if (me->button() == Qt::LeftButton)
           {
               e->ignore();
               return true;
           }
       }
       return QListView::eventFilter(o, e);
   }


Название: Re: [РЕШЕНО] отправить перехваченное событие родителю
Отправлено: Igors от Декабрь 24, 2014, 08:45
получилось только так, но это ж ничем не отличается от эмита прямо в фильтре :)
Первым dbl событие получает QLabel. Если ничего не делать, то событие будет обработано и другие его не получат. Это не устраивает, значит решение навесить фильтр правильное. Остается только где эмиттить - да, разницы тоже не вижу, хотя путь события разный.

Итого: все норм, какого-то лучшего пути не видно (неоткуда взяться)


Название: Re: [РЕШЕНО] отправить перехваченное событие родителю
Отправлено: kambala от Декабрь 24, 2014, 13:56
например, в Cocoa Touch (думаю и в Cocoa тоже) есть возможность переопределить метод hitTest:withEvent: у виджета (UIView), и таким образом контролировать доставку событий. если из метода вернуть false, то событие просто отправится в супервью (parent widget). в Qt ничего подобного нету? или e->ignore() это и делает, но почему-то не отрабатывает как хотелось бы?


Название: Re: [РЕШЕНО] отправить перехваченное событие родителю
Отправлено: Igors от Декабрь 24, 2014, 14:39
.. или e->ignore() это и делает, но почему-то не отрабатывает как хотелось бы?
Делает и отрабатывает. "Позиционное" событие от мыши сначала подается видимому виджету под мышей.  Если он его "сожрет" - на этом все и кончается. Если нет, то его паренту и.т.д. вверх пока кто-то его не примет (accepted). Когда проигнорирвали в фильтре - дело доходит до QAbstractItemView::mouseDoubleClickEvent, просто он не всегда испускает нужный сигнал


Название: Re: [РЕШЕНО] отправить перехваченное событие родителю
Отправлено: kambala от Декабрь 24, 2014, 14:59
понял, спасибо