QWidget содержит также функции dragMoveEvent() и dragLeaveEvent(), но для большинства приложений не потребуется их переопределять.
Второй пример показывает, как следует инициировать перетаскивание объекта и принимать его после отпускания. Мы создадим подкласс QListWidget, который будет поддерживать механизм «drag-and-drop» и входить в приложение Project Chooser (составитель проектов), показанное на рис. 9.1.
Рис. 9.1. Приложение Project Chooser.
Приложение Project Chooser предоставляет пользователю два виджета со списками имен людей. Каждый список представляет проект. Пользователь может с помощью механизма «drag-and-drop» перевести человека из одного проекта в другой.
Программный код по обеспечению механизма «drag-and-drop» находится в подклассе QListWidget. Ниже приводится определение класса:
01 class ProjectListWidget : public QListWidget
02 {
03 Q_OBJECT
04 public:
05 ProjectListWidget(QWidget *parent= 0);
06 protected:
07 void mousePressEvent(QMouseEvent *event);
08 void mouseMoveEvent(QMouseEvent *event);
09 void dragEnterEvent(QDragEnterEvent *event);
10 void dragMoveEvent(QDragMoveEvent *event);
11 void dropEvent(QDropEvent *event);
12 private:
13 void startDrag();
14 QPoint startPos;
15 };
ProjectListWidget переопределяет пять обработчиков событий, которые объявлены в QWidget.
01 ProjectListWidget::ProjectListWidget(QWidget *parent)
02 : QListWidget(parent)
03 {
04 setAcceptDrops(true);
05 }
В конструкторе мы обеспечиваем возможность приема переносимого объекта в виджете со списком.
01 void ProjectListWidget::mousePressEvent(QMouseEvent *event)
02 {
03 if (event->button() == Qt::LeftButton)
04 startPos = event->pos();
05 QListWidget::mousePressEvent(event);
06 }
Когда пользователь нажимает левую кнопку мышки, мы сохраняем позицию мышки в закрытой переменной startPos. Мы вызываем определенную в классе QListWidget функцию mousePressEvent() для обеспечения обработки в QListWidget обычным образом события нажатия кнопкй мышки.
01 void ProjectListWidget::mouseMoveEvent(QMouseEvent *event)
02 {
03 if (event->buttons() & Qt::LeftButton) {
04 int distance = (event->pos() - startPos).manhattanLength();
05 if (distance >= QApplication::startDragDistance())
06 startDrag();
07 }
08 QListWidget::mouseMoveEvent(event);
09 }
Действие, при котором пользователь перемещает курсор мышки и одновременно держит нажатой левую кнопку, мы рассматриваем как начало перетаскивания объекта. Мы вычисляем расстояние между текущей позицией мышки и позицией нажатия левой кнопки мышки. Если это расстояние превышает рекомендованное в QApplication расстояние для регистрации начала перетаскивания (обычно 4 пикселя), мы вызываем закрытую функцию startDrag() для запуска процесса перетаскивания объекта. Это предотвращает инициирование процесса перетаскивания из-за дрожания руки пользователя.
01 void ProjectListWidget::startDrag()
02 {
03 QListWidgetItem *item = currentItem();
04 if (item) {
05 QMimeData *mimeData = new QMimeData;
06 mimeData->setText(item->text());
07 QDrag *drag = new QDrag(this);
08 drag->setMimeData(mimeData);
09 drag->setPixmap(QPixmap(":/images/реrson.png"));
10 if (drag->start(Qt::MoveAction) == Qt::MoveAction)
11 delete item;
12 }
13 }
В функции startDrag() мы создаем объект типа QDrag с указанием this в качестве родительского элемента. Объект QDrag хранит данные в объекте QMimeData. В нашем примере мы обеспечиваем данные типа text/plain, используя функцию QMimeData::setText(). Класс QMimeData содержит несколько функций, предназначенных для обработки наиболее распространенных типрв объектов переноса (изображений, адресов URL, цветов и т.д.); он может обрабатывать произвольные типы MIME, представленные массивами QByteArray. Вызов QDrag::setPixmap() задает пиктограмму, которая следует за курсором в процессе перетаскивания объекта.
Вызов функции QDrag::start() запускает операцию перетаскивания объекта и ждет, пока пользователь не отпустит перетаскиваемый объект или не отменит перетаскивание. В аргументе этой функции задается перечень поддерживаемых «операций перетаскивания» (Qt::CopyAction, Qt::MoveAction и Qt::LinkAction); она возвращает ту операцию перетаскивания, которая была выполнена (или Qt::IgnoreAction, если не было выполнено никакой операции). Тип выполняемой операции зависит от того, какие операции допускаются исходным виджетом, какие операции поддерживает целевой виджет и какие клавиши—модификаторы нажаты в момент отпуска переносимого объекта. После вызова этой функции Qt становится владельцем переносимого объекта и удалит его, когда он станет ненужным.