Оглавление
Перетаскивание (Drag and Drop)Перетаскивание предоставляет простой визуальный механизм, который пользователи могут использовать для перемещения информации между и внутри приложений. (В литературе на это ссылаются как на "модель непосредственного управления".) Перетаскивание - по назначению похоже на механизм буфера обмена вырезать и вставить. Этот документ описывает основной механизм перетаскивания и намечает в общих чертах подход используемый для его включения в пользовательских виджетах. Операции перетаскивания также поддерживаются представлениями элементов Qt и каркасом графического представления. Дополнительная информация доступна в документах Использование перетаскивания (Drag and Drop) и Каркас графического представления. Классы перетаскиванияЭти классы имеют дело с перетаскиванием и необходимым кодированием и декодированием mime-типов.
КонфигурированиеОбъект QApplication предоставляет некоторые свойства, которые относятся к операциям перетаскивания:
Эти параметры предоставляют здравые значения по умолчанию для использования если вы предоставите поддержку перетаскивания в ваших виджетах. ПеретаскиваниеЧтобы начать перетаскивание создайте объект QDrag и вызовите его функцию exec(). В большинстве приложений хорошей идеей начать операцюи перетаскивания только после нажатия кнопки мыши и курсор был перемещён на определённое растояние. Тем не менее, самый простой способ разрешить перетаскивание из виджета - переопределить функцию mousePressEvent() виджета и начать операцию перетаскивания: void MainWindow::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton && iconLabel->geometry().contains(event->pos())) { QDrag *drag = new QDrag(this); QMimeData *mimeData = new QMimeData; mimeData->setText(commentEdit->toPlainText()); drag->setMimeData(mimeData); drag->setPixmap(iconPixmap); Qt::DropAction dropAction = drag->exec(); ... } } Хотя пользователь может получить некоторое время для завершения операции перетаскивания, поскольку приложение связано с функцией exec() являющейся блокирующей функцией, которое возвращает одно из нескольких значений. Это показывает как завершается операция, и ниже описано более подробно. Обратите внимание на то, что функция exec() не блокирует основной цикл обработки событий. Для виджетов, которым нужно различать щелчки мыши и перетаскивание, полезно переопределить функцию виджета mousePressEvent() чтобы записать начальную позицию перетаскивания: void DragWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) dragStartPosition = event->pos(); } Позднее, в mouseMoveEvent(), мы можем определить началось ли перетаскивание, а также сконструировать объект перетаскивания для обработки операции: void DragWidget::mouseMoveEvent(QMouseEvent *event) { if (!(event->buttons() & Qt::LeftButton)) return; if ((event->pos() - dragStartPosition).manhattanLength() < QApplication::startDragDistance()) return; QDrag *drag = new QDrag(this); QMimeData *mimeData = new QMimeData; mimeData->setData(mimeType, data); drag->setMimeData(mimeData); Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction); ... } Этот индивидуальный подход использует функцию QPoint::manhattanLength() чтобы получить грубую оценку расстояния между местом щелчка мышью и текущей позицией курсора. Эта функция меняет точность на скорость, и обычно подходит для этой цели. ОтпусканиеЧтобы быть способным получить информацию отпущенную на виджет вызовите setAcceptDrops(true) для виджета и переопределите функции-обработчики событий dragEnterEvent() и dropEvent(). Например, следующий код разрешает события отпускания в конструкторе подкласса QWidget, делая возможным полезную реализацию обработчиков события отпускания: Window::Window(QWidget *parent) : QWidget(parent) { ... setAcceptDrops(true); } Функция dragEnterEvent() обычно используется для информирования Qt о типах данных, которые принимает виджет. Вы должны переопределить эту функцию если вы хотите принять QDragMoveEvent или QDropEvent в ваши повторно реализованные dragMoveEvent() и dropEvent(). Следующий код показывает, как можно переопределить dragEnterEvent() чтобы сообщить системе перетаскивания, что мы обрабатываем только простой текст: void Window::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasFormat("text/plain")) event->acceptProposedAction(); } dropEvent() используется для распаковки отпущенных данных и обработки их способом, который соответствует вашему приложению. В нижеследующем коде текст доставленный в событие передан в QTextBrowser и QComboBox заполняется со списка MIME-типов, которые ипользуются для описания данных: void Window::dropEvent(QDropEvent *event) { textBrowser->setPlainText(event->mimeData()->text()); mimeTypeCombo->clear(); mimeTypeCombo->addItems(event->mimeData()->formats()); event->acceptProposedAction(); } В данном случае, мы принимаем предлагаемое действие без его проверки. В реальных приложениях, может быть необходимо возвратить из функции dropEvent() без приёма предлагаемого действия или обработки данных, если действие не является соответствующим. Например, мы можем выбрать игнорирование действий Qt::LinkAction если мы не поддерживаем ссылки на внешние источники в нашем приложении. Перегрузка предлагаемых действийМы также можем игнорировать предлагаемое действие, и выполнить некоторое действие на данных. Что сделать это, мы будем вызывать событие объекта setDropAction() с предпочтительным действием из Qt::DropAction перед вызовом accept(). Это гарантирует, что замена действия отпускания используется вместо предлагаемого действия. Для более сложных приложений переопределение dragMoveEvent() и dragLeaveEvent() позволит вам сделать некоторые части ваших виджетов чувствительными к событиям отпускания, и даст вам больше контроля над перетаскиванием в вашем приложении. Создание подклассов сложных виджетовНекоторые стандартные виджеты Qt предоставляют свою собственную поддержку перетаскивания. При создании подклассов этих виджетов, возможно необходимо будет переопределить dragMoveEvent() в дополнение к dragEnterEvent() и dropEvent() чтобы предохранить базовый класс от предоставления обработки перетаскивания по умолчанию, и для обработки любых особых случаев интересных вам. Действия перестаскивания (Drag and Drop)В простейшем случае цель действия перетаскивания получает копию перетаскиваемых данных, а источник решает удалить ли оригинал. Это описывается действием CopyAction. Цель может также выбрать обработку других действий, особенно действия MoveAction LinkAction. Если источник вызывает QDrag::exec() и она возвращает MoveAction, то источник отвечает за удаление любых оригинальных данных если он выбран это делать. Объекты QMimeData и QDrag созданы виджетом источника не будут удалены - их разрушит Qt. Цель отвечает за получение владения над данными, отправленными в операцию перетаскивания; обычно это делают сохраняя ссылки на данные. Если цель понимает действие LinkAction, она будет хранить собственную ссылку на оригинальную информацию; источнику не нужно выполнять какую-либо дополнительную обработку данных. Наиболее общее использование действий перетаскивания - когда выполняется перемещение внутри одного и того же виджета; для получения дополнительной информации об этой возможности смотрите раздел Действия отпускания. Другой значительное использование действий перетаскивания - когда использование типа ссылки, например, text/uri-list, где перетаскиваемые данные действительно ссылаются на файлы или объекты. Добавление новых типов перетаскиванияПеретаскивание не ограничено текстом и изображениями. Любой тип информации может быть передан в операцию перетаскивания. Чтобы перетащить информацию между приложениями, приложение должно быть способно указать для всех других данные в каких форматах каждого они могут принять и в каком они могут произвести. Это достигается используя MIME-типов. Объект QDrag, созданный источником, содержит список MIME-типов, которые он использует для представления данных (упорядоченный от наиболее соответствующих к наименее соответствующим), а цель отпускания использует один из них для доступа к данным. Для обычных типов данных вспомогательные функции обрабатывают MIME-типы, используемые прозрачно, однако для пользовательских типов данных необходимо заявлять их явно. Чтобы реализовать действия перетаскивания для типа информации, который не охватывается вспомогательными функциями QDrag, первый и наиболее важный шаг - взглянуть на существующие форматы, которые являются соответствующими: Internet Assigned Numbers Authority (IANA) предоставляет иерархический список медийных MIME-типов в Information Sciences Institute (ISI). Используя стандартные MIME-типы максимизируют совместимость вашего приложения с другим программным обеспечением сейчас и в будущем. Для поддержки дополнительного медийного типа просто установите данные в объект QMimeData с помощью функции setData(), предоставляющей полный MIME-тип и QByteArray, содержащий данные в соответствующем формате. Следующий код получает растровое изображение из метки и сохраняет его как файл Portable Network Graphics (PNG) в объекте QMimeData: QByteArray output; QBuffer outputBuffer(&output); outputBuffer.open(QIODevice::WriteOnly); imageLabel->pixmap()->toImage().save(&outputBuffer, "PNG"); mimeData->setData("image/png", output); Конечно, для этого случая мы можем просто использовать setImageData() вместо предоставленных данных изображения в различных форматах: mimeData->setImageData(QVariant(*imageLabel->pixmap())); Подход QByteArray по-прежнему полезен в этом случае, поскольку он предоставляет больший контроль над количеством данных сохраняемых в объекте QMimeData. Обратите внимание на то, что пользовательские типы данных используемые в представлениях элементов должны быть объявлены как меат-объекты и что потоковые операторы для них должны быть повторно реализованы. Действия отпусканияВ модели буфера обмена пользователь может вырезать или скопировать информацию источника, а затем позднее вставить её. Подобным же образом в модели перетаскивания пользователь может перетащить копию информации или они могут перетащить саму информацию в новое место (переместить её). Модель перетаскивания имеет дополнительную трудность для программиста: Программа не знает хочет ли пользователь вырезать или скопировать информацию до тех пор, пока операция не завершена. Часто это убирает отличия при перетаскивании информации между приложениями, но внутри приложения важно проверить какое действие отпускания использовалось. Мы можем переопределить mouseMoveEvent() для виджета, а также начать операцию перетаскивания с комбинацией возможных действий отпускания. Например, мы можем захотеть убедиться, что перетаскивание всегда перемещает объекты в виджете: void DragWidget::mouseMoveEvent(QMouseEvent *event) { if (!(event->buttons() & Qt::LeftButton)) return; if ((event->pos() - dragStartPosition).manhattanLength() < QApplication::startDragDistance()) return; QDrag *drag = new QDrag(this); QMimeData *mimeData = new QMimeData; mimeData->setData(mimeType, data); drag->setMimeData(mimeData); Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction); ... } Действие возвращаемое функцией exec() по умолчанию может быть равно CopyAction если информация отпускается в другое приложение, но если она отпускается на другой виджет в этом же приложении, мы можем получить различное действие отпускания. Принятые действия отпускания можно отфильтровать в функции виджета dragMoveEvent(). Тем не менее, возможно принять все предлагаемые действия в dragEnterEvent() и позволить пользователю решить, какие из них принять позднее: void DragWidget::dragEnterEvent(QDragEnterEvent *event) { event->acceptProposedAction(); } Когда отпускание произойдёт в виджете, вызывается функция-обработчик dropEvent(), и мы можем рассмотреть каждое возможное действие по очереди. Сначала мы распределить операции перетаскивания внутри одного и того же виджета: void DragWidget::dropEvent(QDropEvent *event) { if (event->source() == this && event->possibleActions() & Qt::MoveAction) return; В этом случае мы отказались рассматривать операции перемещения. Каждый тип действия отпускания, который мы приняли, проверяется и рассматривался соответственно: if (event->proposedAction() == Qt::MoveAction) { event->acceptProposedAction(); // Обрабатываем данные из события. } else if (event->proposedAction() == Qt::CopyAction) { event->acceptProposedAction(); // Обрабатываем данные из события. } else { // Игнорируем отпускание. return; } ... } Обратите внимание на то, что мы проверяли отдельные действия отпускания в вышеприведённом коде. Как было сказано выше в разделе Перегрузка предлагаемых действий, иногда необходимо перегрузить предлгаемое действие отпускания и выбрать другое из выборки возможных действия отпускания. Чтобы это сделать вам нужно проверить наличие каждого действия в значении функции события possibleActions(), установить действие отпускания с помощью setDropAction() и вызвать accept(). Прямоугольники отпусканияФункция виджета dragMoveEvent() может быть использована для ограничения отпускания определёнными частями виджета принятием предлагаемых действий отпускания только когда курсор находится внутри этих областей. Например, следующий код принимает любые предлагаемые действия отпускания когда курсор находится над дочерним виджетом(dropFrame): void Window::dragMoveEvent(QDragMoveEvent *event) { if (event->mimeData()->hasFormat("text/plain") && event->answerRect().intersects(dropFrame->geometry())) event->acceptProposedAction(); } dragMoveEvent() также может быть использована если вам нужно дать визуальную обратную связь во время операции перетаскивания, для прокручивания окна или чего-либо соответствующего. Буфер обменаПриложения также могут обмениваться информацией друг с другом помещая данные в буфер обмена. Чтобы получить к нему доступ вам нужно получить объект QClipboard из объекта QApplication: clipboard = QApplication::clipboard(); Класс QMimeData используется для представления данных, которые перемещаются в и из буфера обмена. Чтобы поместить данные в буфер обмена вы можете использовать вспомогательные функции setText(), setImage() и setPixmap() для обычных типов данных. Эти функции аналогичны таким же находящимся в классе QMimeData, за исключением того, что они также получают дополнительный аргумент, который управляет сохранением данных: Если задан Clipboard, то данные помещаются в буфер обмена; если задан Selection , то данные помещаются в выделение мыши (только в X11). По умолчанию, данные помещаются в буфер обмена. Например, мы можем скопировать содержимое QLineEdit в буфер обмена с помощью следующего кода: clipboard->setText(lineEdit->text(), QClipboard::Clipboard); Данные с разными MIME-типами также могут быть помещены в буфер обмена. Создайте объект QMimeData и установите данные с помощью функции setData() способом, описанным в предыдущем разделе; этот объект затем можно поместить в буфер обмена с помощью функции setMimeData(). Класс QClipboard может уведомлять приложение об изменении содержащихся в нём данных посредством своего сигнала dataChanged(). Например, мы можем отслеживать буфер обмена присоединив этот сигнал к слоту в виджете: connect(clipboard, SIGNAL(dataChanged()), this, SLOT(updateClipboard())); Слот, соединённый с этим сигналом, может читать данные из буфера обмена используя один из MIME-типов, который может быть использован для их представления: void ClipWindow::updateClipboard() { QStringList formats = clipboard->mimeData()->formats(); QByteArray data = clipboard->mimeData()->data(format); ... } Сигнал selectionChanged() может быть использован в X11 для отслеживания выделения мыши. ПримерыВзаимодействие с другими приложениямиВ X11 используется открытый протокол XDND, в то время как в Windows Qt использует стандарт OLE, а Qt для Mac OS X использует Carbon Drag Manager. В X11 XDND использует MIME, поэтому никакого преобразования не нужно. Qt API не зависит от платформы. В Windows, MIME-ориентированные приложения могут общаться используя имена буфера обмена, которые являются MIME-типам. Уже несколько приложений Windows используют соглашения именования MIME для своих форматов буфера обмена. Внутри Qt использует QWindowsMime и QMacPasteboardMime для преобразования закрытых форматов буфера обмена в и из MIME-типов. В X11 Qt также поддерживает отпускание посредством Протокола перетаскивания Motif. Реализация объединяет некоторый код, который был первоначально написан Далиелем Дардайллером (Daniel Dardailler), и адаптирован для Qt Мэттом Коссом (Matt Koss) <koss@napri.sk> и Nokia. Вот оригинальное уведомление об авторских правах: Copyright 1996 Daniel Dardailler. Copyright 1999 Matt Koss Permission to use, copy, modify, distribute, and sell this software for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Daniel Dardailler not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Daniel Dardailler makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. Note: The Motif Drag & Drop Protocol only allows receivers to request data in response to a QDropEvent. If you attempt to request data in response to e.g. a QDragMoveEvent, an empty QByteArray is returned. |
Попытка перевода Qt документации. Если есть желание присоединиться, или если есть замечания или пожелания, то заходите на форум: Перевод Qt документации на русский язык... Люди внесшие вклад в перевод: Команда переводчиков |