Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: Jokerochek от Декабрь 15, 2008, 14:15



Название: QTreeWidget - как отследить завершение Event'а?
Отправлено: Jokerochek от Декабрь 15, 2008, 14:15
Добрый день.
Qt занимаюсь недавно, поэтому многого ещё не понимаю.
Возник такой вопрос. У себя в программе использую стандартный QTreeWidget. Понадобилось настроить перетаскивание, но переписывать QTreeWidget не хочется.
При помощи eventFilter() я могу перехватить управление непосредственно перед тем, как произойдет QDrop. Но тогда я не могу получить информацию о том, куда собственно данные будут сброшены (ну кроме как плясок с бубном вокруг QDropEvent::pos() и QTreeWidget::itemAt(), опять же dropIndicatorPosition() в QtreeWidget является protected).
Можно ли каким-нибудь образом перехватить управление программой после завершения обработки QDropEvent?
И второй вопрос - можно ли как-нибудь получить доступ к dropIndicatorPosition() не переписывая стандартный класс?
Заранее, спасибо.


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: pastor от Декабрь 15, 2008, 14:18
Цитировать
Понадобилось настроить перетаскивание

А откуда куда будет происходить перетаскивание?


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: Jokerochek от Декабрь 15, 2008, 14:22
Сорри. Перетаскивание происходит в пределах виджета (фактически обычная работа с деревом - просто нужно изменения в дереве переносить на собственную структуру данных).


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: pastor от Декабрь 15, 2008, 14:29
В таком случание ненужно ничего изобретать. Это уже все реализовано. Установите setAcceptDrops(true), setDragEnabled(true) для QTreeWidget


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: Jokerochek от Декабрь 15, 2008, 14:33
Уточню. В самом QTreeWidget перетаскивание работает прекрасно. Но мой объект тоже хранит структуру этого дерева, и чтобы ее оперативно изменять мне нужно знать, что поменялось в QTreeWidget'е. подходящих сигналов, которые бы сообщали о изменении структуры дерева я не нашел. Фактически мне надо после завершения перетаскивания знать какой QTreeWidgetItem поменял своего родителя, и кто теперь его новый папа.


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: pastor от Декабрь 15, 2008, 14:48
Ясненько. Это можно всетяки седлать через эвент фильтр. Это будет выглядеть примерно так:

Код
C++ (Qt)
bool SomeClass::eventFilter(QObject *obj, QEvent *event)
{
   if (event->type() == QEvent::Drop && obj == treeWidget) {
       QDropEvent *dropEvent = static_cast<QDropEvent*>(event);
 
       QTreeWidgetItem *draggedItem = treeWidget->currentItem();
       QTreeWidgetItem *droppedOnItem = treeWidget->itemAt(dropEvent->pos());
 
       Q_ASSERT(draggedItem);
       Q_ASSERT(droppedOnItem);
 
       if (!droppedOnItem || !draggedItem)
               return false;
 
       if (draggedItem == droppedOnItem)
               return false;
 
       int draggedItemIdx = treeWidget->indexOfTopLevelItem(draggedItem);
       int droppedOnItemIdx = treeWidget->indexOfTopLevelItem(droppedOnItem);
 
       Q_ASSERT(draggedItemIdx >= 0);
       Q_ASSERT(droppedOnItemIdx >= 0);
 
       <do_something with indexes or items>
 
 
       return false; //event should be handled in QTreeWidget
   }
 
   return BaseClass::eventFilter(obj, event);
}
 
 


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: SABROG от Декабрь 15, 2008, 14:51
Или можно законнектить сигнал к своему слоту от модели, если он приходит конечно после drop'a

Цитировать
void QAbstractItemModel::dataChanged ( const QModelIndex & topLeft, const QModelIndex & bottomRight )   [signal]
This signal is emitted whenever the data in an existing item changes. The affected items are those between topLeft and bottomRight inclusive (of the same parent).
Note that this signal must be emitted explicitly when reimplementing the setData() function.

В итоге получишь диапозон элементов, которые были изменены.


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: Jokerochek от Декабрь 15, 2008, 15:18
Это будет выглядеть примерно так:
Спасибо. Работает, но... Опять похоже упирается в dropIndicatorPosition().
Если я сбрасываю элемент между двумя элементами, то treeWidget->itemAt(dropEvent->pos()) вернет один из этих двух элементов.


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: Jokerochek от Декабрь 15, 2008, 15:19
Или можно законнектить сигнал к своему слоту от модели, если он приходит конечно после drop'a
так одна из проблем - как отследить, что сигнал отправлен после того, как отработал QDropEvent.


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: pastor от Декабрь 15, 2008, 15:33
Если я сбрасываю элемент между двумя элементами, то treeWidget->itemAt(dropEvent->pos()) вернет один из этих двух элементов.

Первое решение - это всетаки наследоватся от QTreeWidget
Второе решение - написать аналог dropIndicatorPosition самому. Если заглянуть в исходники, то мы увидем следующе:

Код
C++ (Qt)
QAbstractItemView::DropIndicatorPosition
QAbstractItemViewPrivate::position(const QPoint &pos, const QRect &rect, const QModelIndex &index) const
{
   QAbstractItemView::DropIndicatorPosition r = QAbstractItemView::OnViewport;
   if (!overwrite) {
       const int margin = 2;
       if (pos.y() - rect.top() < margin) {
           r = QAbstractItemView::AboveItem;
       } else if (rect.bottom() - pos.y() < margin) {
           r = QAbstractItemView::BelowItem;
       } else if (rect.contains(pos, true)) {
           r = QAbstractItemView::OnItem;
       }
   } else {
       QRect touchingRect = rect;
       touchingRect.adjust(-1, -1, 1, 1);
       if (touchingRect.contains(pos, false)) {
           r = QAbstractItemView::OnItem;
       }
   }
 
   if (r == QAbstractItemView::OnItem && (!(model->flags(index) & Qt::ItemIsDropEnabled)))
       r = pos.y() < rect.center().y() ? QAbstractItemView::AboveItem : QAbstractItemView::BelowItem;
 
   return r;
}

А вызов таков:

Код
C++ (Qt)
dropIndicatorPosition = position(event->pos(), q->visualRect(index), index);

Все данные для создания аналога есть.


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: SABROG от Декабрь 15, 2008, 15:39
Или можно законнектить сигнал к своему слоту от модели, если он приходит конечно после drop'a
так одна из проблем - как отследить, что сигнал отправлен после того, как отработал QDropEvent.

Ну MessageBox вставь в свой законнекченный слот и посмотри появится ли после того как отпустил мышку.


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: Jokerochek от Декабрь 15, 2008, 15:42
pastor, спасибо. Буду разбираться.


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: pastor от Декабрь 15, 2008, 15:48
Незачто! Собственно говоря, вам нужен вот этот кусок:

Код
C++ (Qt)
const int margin = 2;
if (pos.y() - rect.top() < margin) {
   r = QAbstractItemView::AboveItem;
} else if (rect.bottom() - pos.y() < margin) {
   r = QAbstractItemView::BelowItem;
} else if (rect.contains(pos, true)) {
   r = QAbstractItemView::OnItem;
}

pos у вас есть, rect можно получить при помощи QTreeWidget::visualItemRect


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: Guk от Март 22, 2009, 22:44
Здравствуйте

У меня вопрос по Вашему коду:

Ясненько. Это можно всетяки седлать через эвент фильтр. Это будет выглядеть примерно так:

Имеется форма, на форме QTreeWidget.
Добавил в класс формы protected метод eventFilter, как у Вас в коде.
В конструкторе формы дописал
Код
C++ (Qt)
ui.treeWidget->installEventFilter(this);
Фильтрация работает на ура, но ни одного события из нужных:
QEvent::DragEnter
QEvent::DragLeave
QEvent::DragMove
QEvent::Drop
Все остальные события "сыпятся" нормально, а вот по перетаскиванию - ни одного.
Нужные свойства TreeWidget задействованы:
Код
C++ (Qt)
setDragEnabled(true);
setAcceptDrops(true);
setDropIndicatorShown(true);
Визуально элементы перетаскиваются нормально. Возникает такое впечатление, что ивенты Drag&Drop фильтруются кем-то другим до моего фильтра.

Отсюда и вопрос: что-то я делаю не так, не могу понять что...
Заранее, спасибо.


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: Jokerochek от Март 23, 2009, 09:23
Здравствуйте

У меня вопрос по Вашему коду:

Добавил в класс формы protected метод eventFilter, как у Вас в коде.
В конструкторе формы дописал
Код
C++ (Qt)
ui.treeWidget->installEventFilter(this);
Попробуйте сделать
Код
C++ (Qt)
ui.treeWidget->viewport()->installEventFilter(this);
 


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: Guk от Март 23, 2009, 11:37
Спасибо большое.
Заработало


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: Danila_Bagrofff от Апрель 06, 2009, 15:25
Возникла похожая проблема.

Есть класс наследованный от QTableWidget. На который переносится класс, наследованный от QWidget.
Переопределяю событие drop Event. Но оно не сработывает.
Где-то раньше перехватывается событие. Причем на QLabel'е перехватывается, а на таблице - нет.

viewport()->instalEventFilter(this) не помог.

В чем еще может быть проблема. setAcceptDrops(true) сделал.


Название: Re: QTreeWidget - как отследить завершение Event'а?
Отправлено: Danila_Bagrofff от Апрель 07, 2009, 09:01
Нашел решение. Почитав форум. И немного поплясав с бубном.

надо обязательно переопределить событие QEvent::DragMove

В конструктор класса, в который перебрасывется что-либо у меня это наследованный класс от QTableWidget, нужно установить:
Код:
MyClassTableWidget::MyClassTableWidget(QWidget *parent) : QTableWidget(parent)
{
     setAcceptDrops(true);
     viewport()->installEventFilter(this);
}

А также нужно переопределить eventFilter:
Код:
bool MyClassTableWidget::eventFilter(QObject *obj, QEvent *event)
{
    if(event->type() == QEvent::DragMove) return true;
    else return QObject::eventFilter(obj, event);
}

И, cобственно переопределим методы drag-drop:
Код:
void MyClassTableWidget::dragEnterEvent(QDragEnterEvent *event)
{
    if(event->mimeData()->hasFormat("qwert/asdf"))
{
    //your code
    event->accept();
}
}

void MyClassTableWidget::dropEvent(QDropEvent *event)
{
     //your code
}


То есть фактически, мы разрешили нами обработку событий drag/drop.

Надеюсь, кому-то поможет.