Drag&drop |
Здравствуйте, гость ( Вход | Регистрация )
Drag&drop |
borune |
8.1.2011, 12:18
Сообщение
#1
|
Участник Группа: Участник Сообщений: 152 Регистрация: 1.1.2011 Пользователь №: 2314 Спасибо сказали: 0 раз(а) Репутация: 0 |
Ребят, помогите плиз разобраться с реализацией данного механизма средствами qt.
Из прочтения ассистанса я мало что понял. Там написано, что базовым классом для реализаций механизма drag&drop является класс QDrag. Перетаскиваемые данные надо положить в объект класса QMimeData. Также предлагается перезагрузить функции нажатия и отпускания кнопок мыши. Написано все складно, да вот я, например, ничего не понял. Вот мне надо реализовать следующее. Есть окно с рядом виджетов. С этими виджетами можно производить операции переноса в определенное место окна (область, куда можно помещать виждет, зависит от самого виждета) и простого клика левой и правой кнопками. Области, куда можно помещать виджет, я думаю, удобно реализовать с помощью класса QFrame, поскольку эти области представляют собой части экрана, образованные параллельными горизонтальными линиями. А вот как сделать все остальное, а именно активировать механизм перетаскивания, сделать так, чтобы если пользователь отпускает виджет в той области, в которой этот виджет располагаться не может, то данный виджет "ехал" бы в исходное положение, а также разделить обработку левого клика при перетаскивании и простого левого клика (если я правильно понял, это делается при помощи QApplication::startDragTime(int)), я бы хотел спросить у вас. Заранее всем спасибо) вот я написал такой простенький примерчик прям по ассистансу почти. QFoto - мой класс, объекты которого надо мне перетаскивать примерчик #include "mainwindow.h" #include "ui_mainwindow.h" #include <QDrag> #include <QMimeData> #include <QMouseEvent> #include "D:/qt/tree/QFoto.h" QFoto *tt; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { tt = new QFoto(QString(""),this,Qt::Widget,20,20,QPixmap(tr("D:\\gnines.gif")),QColor(0,0,0)); connect(tt,SIGNAL(LeftPressed(QMouseEvent*)),this,SLOT(mousePressEvent(QMouseEve nt*))); } MainWindow::~MainWindow() { delete ui; } void MainWindow::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton && tt->geometry().contains(event->pos())) { QDrag *drag = new QDrag(this); QMimeData *mimeData = new QMimeData; mimeData->setText(tr("lala")); drag->setMimeData(mimeData); drag->setPixmap(QPixmap(tr("D:\\tt.gif"))); Qt::DropAction dropAction = drag->exec(); } } если я пытаюсь перетащить созданный объект, то курсор принимает форму черной перечеркнутой окружности и ничего никуда не перетаскивается. Сообщение отредактировал borune - 8.1.2011, 12:42 |
|
|
Алексей1153 |
8.1.2011, 12:31
Сообщение
#2
|
фрилансер Группа: Участник Сообщений: 2939 Регистрация: 19.6.2010 Из: Обливион Пользователь №: 1822 Спасибо сказали: 215 раз(а) Репутация: 34 |
если всё происодит в пределах родительского виджета, то никаких наворотов не нужно - достаточно отловить нажатие левой кнопки, затем запомнить начальные координаты и при движении мыши двигать перетаскиваемое дитя за курсором. По отпусканию кнопки сохранить новое положение (да оно уже и так сохранено)
Сообщение отредактировал Алексей1153 - 8.1.2011, 12:31 |
|
|
borune |
8.1.2011, 13:25
Сообщение
#3
|
Участник Группа: Участник Сообщений: 152 Регистрация: 1.1.2011 Пользователь №: 2314 Спасибо сказали: 0 раз(а) Репутация: 0 |
если всё происодит в пределах родительского виджета, то никаких наворотов не нужно - достаточно отловить нажатие левой кнопки, затем запомнить начальные координаты и при движении мыши двигать перетаскиваемое дитя за курсором. По отпусканию кнопки сохранить новое положение (да оно уже и так сохранено) я нашел пример кутэшный вот этот. там все описано и, главное, все работает. Правда мало что понятно) |
|
|
Алексей1153 |
8.1.2011, 13:59
Сообщение
#4
|
фрилансер Группа: Участник Сообщений: 2939 Регистрация: 19.6.2010 Из: Обливион Пользователь №: 1822 Спасибо сказали: 215 раз(а) Репутация: 34 |
borune, это случай перетаскивания с виджета на виджет. А в пределах одного виджета всё попроще
PS очепятка Цитата где используется перетаскивание, the пользователь начинает |
|
|
Litkevich Yuriy |
8.1.2011, 14:06
Сообщение
#5
|
разработчик РЭА Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: 94 |
|
|
|
borune |
8.1.2011, 15:03
Сообщение
#6
|
Участник Группа: Участник Сообщений: 152 Регистрация: 1.1.2011 Пользователь №: 2314 Спасибо сказали: 0 раз(а) Репутация: 0 |
borune, это случай перетаскивания с виджета на виджет. А в пределах одного виджета всё попроще PS очепятка Цитата где используется перетаскивание, the пользователь начинает Алексей1153 но ведь мне наверное придется создавать в окне QFrame'ы для того, чтобы обеспечить только разрешенные положения перемещаемых виджетов.. Кстати вот как раз насчет этого..вот с того же сайта цитата цитата Функция виджета dragMoveEvent() может быть использована для ограничения отпускания определёнными частями виджета принятием предлагаемых действий отпускания только когда курсор находится внутри этих областей. Например, следующий код принимает любые предлагаемые действия отпускания когда курсор находится над дочерним виджетом(dropFrame): void Window::dragMoveEvent(QDragMoveEvent *event) { if (event->mimeData()->hasFormat("text/plain") && event->answerRect().intersects(dropFrame->geometry())) event->acceptProposedAction(); } dragMoveEvent() также может быть использована если вам нужно дать визуальную обратную связь во время операции перетаскивания, для прокручивания окна или чего-либо соответствующего. В соответствии с этим я сделал такую функцию: моя функция void MainWindow::dragMoveEvent(QDragMoveEvent *event) { if ((event->mimeData()->hasFormat("application/x-dnditemdata")) && (frame->geometry().contains(event->answerRect()))){ event->acceptProposedAction(); frame->setVisible(true); } else event->ignore(); } у меня это как-то непонятно работает. Вот насколько я понял, метод answerRect возвращает прямоугольник, в который поместится перетаскиваемый виджет в случае, если кнопка мыши будет отпущена в данный момент, то есть в answerRect хранится "текущее" полождение виджета (текущее в кавычках, потому что, опять же, как я понимаю, сам виджет не перетаскивается, он удаляется, а на новом месте создается его копия). То есть event->answerRect().intersects(dropFrame->geometry()) примет значение bool в случае, если хотя бы один пиксел "текущего" положения виджета находится внутри dropFrame. Вот я создал этот фрейм, занимающий верхнюю половину окна, сделал его невидимым. При начале перетаскивании виджета фрейм появляется, чтобы обозначить разрешенные границы переноса. Также вместо условия в примере я использую, на мой взгляд, более строгое - frame->geometry().contains(event->answerRect()). Что я наблюдаю. Зажимаю левую кнопку и начинаю тащить лейбл по направлению к нижней границе фрейма. Как только курсор (а именно, самый верхний пиксел стрелки) выходит за рамки фрейма, курсор меняет форму на перечеркнутую окружность. Из этого можно сделать вывод о том, что конец курсора указывает на нижнюю границу перетаскиваемого виджета. Если я правильно понял, то это устанавливается так: drag->setHotSpot(event->pos() - child->pos()); Я пробовал написать так: drag->setHotSpot(QPoint(pixmap.width()/2,pixmap.height()/2)); но это ничего не изменило. Значит дело не в этой строке... Далее. Если я, например, схвачу виджет за самый верхний край, то смогу его вытащить за пределы фрейма почти полностью. Это тоже понятно, поскольку курсор за пределы фрейма при этом не выходит. А вот далее начинаются необъяснимые для меня вещи. Я постарался показать их на скриншотах. 1. 2. 3. Даже после этапа 3 я могу перетаскивать виджет (!!!). Тут уж никак не может выполняться условие нахождения виджета в пределах фрейма. Правда виджет перетаскивается не так, как в пределах фрейма - я могу его перетаскивать только пока курсор мыши находится в пределах прямоугольника, соответствующего положению виджета до начала этого перетаскивания. Объясните пожалуйста, как такое вообще может быть? Сообщение отредактировал borune - 8.1.2011, 15:30 |
|
|
Алексей1153 |
8.1.2011, 16:53
Сообщение
#7
|
фрилансер Группа: Участник Сообщений: 2939 Регистрация: 19.6.2010 Из: Обливион Пользователь №: 1822 Спасибо сказали: 215 раз(а) Репутация: 34 |
borune, ты суть улови:
1) для перемещения внутри одного родителя достаточно сообщений мыши - нажатие, перемещение, отпускание. 2) для перемещения между двумя всё меняется: событие нажатия кнопки используется для запуска алгоритма, который использует методы void dragEnterEvent(QDragEnterEvent *event); void dragMoveEvent(QDragMoveEvent *event); void dropEvent(QDropEvent *event); QFrame тут как бы ни при чём, используются мемберы QWidget, а в статье сделана попытка инкапсулировать код драг-дропа в отдельный класс. Основной объём кода в той статье лишь создаёт картинку для курсора , которую видно при перемещении разберись с логикой методов, почитай их описание в справке - и поймёшь, как это всё для любых виджетов использовать |
|
|
borune |
8.1.2011, 18:28
Сообщение
#8
|
Участник Группа: Участник Сообщений: 152 Регистрация: 1.1.2011 Пользователь №: 2314 Спасибо сказали: 0 раз(а) Репутация: 0 |
borune, ты суть улови: 1) для перемещения внутри одного родителя достаточно сообщений мыши - нажатие, перемещение, отпускание. То есть если у меня один виджет, то QDrag использовать не обязательно, можно использовать три сигнала от мыши - MousePressEvent, mouseMoveEvent mouseReleaseEvent? Сообщение отредактировал borune - 8.1.2011, 18:33 |
|
|
Litkevich Yuriy |
8.1.2011, 18:36
Сообщение
#9
|
разработчик РЭА Группа: Сомодератор Сообщений: 9669 Регистрация: 9.1.2008 Из: Тюмень Пользователь №: 64 Спасибо сказали: 807 раз(а) Репутация: 94 |
|
|
|
borune |
8.1.2011, 18:44
Сообщение
#10
|
Участник Группа: Участник Сообщений: 152 Регистрация: 1.1.2011 Пользователь №: 2314 Спасибо сказали: 0 раз(а) Репутация: 0 |
не..никакого компоновщика нету..
сделал я просто в mouseMoveEvent чтоб координаты виджета изменялись, располагя виджет там, где находится курсор.. теперь надо в mouseReleaseEvent сделать проверку на допустимую область..ну это тоже легко..а вот как сделать, чтобы при выходе за пределы допустимой области размещения виджета курсор принимал форму перечеркнутого круга ну или чего-то в этом роде? и еще..как в таком случае отличить левый клик от перетаскивания? Сообщение отредактировал borune - 8.1.2011, 21:00 |
|
|
Текстовая версия | Сейчас: 19.4.2024, 14:58 |