Как передавать через буфер обмена произвольные типы данныхРано или поздно, при разработке более-менее крупного проекта, возникает задача передачи через буфер обмена самодельных данных. В литературе и документации по Qt этот вопрос освещен слабо, и данный урок должен восполнить этот пробел.
Давайте посмотрим, что написано по вопросу передачи собственных типов данных в книге М.Шлее "Qt4. Профессиональное программирование на C++".
"...может понадобиться перетаскивать и принимать свои собственные типы данных, например звуковые данные. Как поступать в подобных ситуациях? Для этих случаев, в классе QMimeData определен метод setData(), в который первым параметром нужно передать строку, характеризующую тип данных, а вторым сами данные в объекте класса QByteArray. Но можно поступить и иначе - унаследовать класс QMimeData и перезаписать методы formats() и retrieveData() ..."
Городить огород с методом setData() и с преобразованием наших данных в
QByteArray (и обратно) мы не будем. А сделам более правильно, а именно разберемся, что скрывается за фразой "унаследовать класс
QMimeData и перезаписать методы
formats() и
retrieveData()".
Нам нужно будет создать класс, унаследованный от класса
QMimeData. С этим вопросов возникнуть не должно. Метод
formats() просто возвращает список строк, содержащий текстовые идентификаторы данных, которые данный класс может обработать.
(Мы можем сами себе придумать и использовать такие строки, например "myapplication/sounddata", "myapplication/matrixdata" и.т.д. Это нас оградит хотябы от того, что после копирования данных в буфер и попытки вставки в текстовый редактор, в редактор не будет запихиваться бинарный мусор. Блокировка произойдет потому, что текстовый редактор способен работать с данными "text/plain", а всякие "myapplication/sounddata" ему неизвестны, и поэтому данные с таким идентификатором в буфере обмена будут проигнорированы).
В данном классе можно создать свойство (или несколько свойств), которые будут содержать наши передаваемые данные. Заполнять данные в свойствах можно отдельными методами. То есть, не нужно передавать всю требуемую структуру данных в виде каким-то образом сгенерированного
QByteArray. Можно также написать метод, который примет данные в виде определенной пользователем структуры, и запомнит ее (в свойстве класса, которое имеет тип данной структуры). То есть, внесение данных крупной проблемы не представляет.
Но дело осложняется тем, что метод
retrieveData() должен возвращать все данные из буфера единовременно, и возвращать данные надо в виде типа
QVariant. Вот какой прототип, согласно документации, должен быть у перегружаемого нами метода
retrieveData()C++ (Qt)
QVariant retrieveData ( const QString & mimeType, QVariant::Type type ) const
Если бы мы возвращали данные в виде
QByteArray, то особых проблем бы не возникло -
QByteArray прекрасно передается через
QVariant. Но тогда нужно будет городить низкоуровневую упаковку и разбор данных из такого массива. А это ничем не отличается от работы с буфером обмена через метод
setData().
Поэтому, первая наша задача - разобраться, как передавать через QVariant самодельную структуру данных. Как только мы это поймем, сделать все остальное не составит большого труда.Не буду отсылать к документации, и объясню на пальцах, как передавать произвольные структуры через QVariant. Предположим, у нас есть некая структура
C++ (Qt)
// Структура данных, которая будет передаваться через буфер обмена
struct clipb_struct
{
int number; // Некое число
QString stringline; // Некая строка
QMap<QString, QString> namevaluetable; // Некая таблица вида "имя переменной" "значение"
};
Чтобы передавать через QVariant данные с типом
clipb_struct, надо этот тип просто-напросто зарегистрировать в программе. Делается это так
C++ (Qt)
Q_DECLARE_METATYPE(clipb_struct);
само собой, данный код нужно размещать после описания структуры. После этого действия, структура
clipb_struct будет передаваться через QVariant.
Ну а дальше все просто. Пишется класс, унаследованный от
QMimeData. В классе делаем переменную (свойство) типа
clipb_struct. Пишем методы (или метод), которые будут заполнять поля данной переменной. Переопределяем метод
retrieveData(), он как раз и должен возвращать переменную типа
clipb_struct. Можем так же написать методы-хелперы, которые будут возвращать значения полей переменной типа
clipb_struct.
Работу с буфером обмена можно организовать так.
Внесение информации в буфер обмена
C++ (Qt)
// Создается ссылка на буфер обмена
QClipboard *pastebuf=QApplication::clipboard();
// Создается объект, который будет помещен в буфер обмена и вносятся в него данные
clipb *cb=new clipb();
cb->set_number(100);
cb->set_stringline("Hello Qt");
cb->ins_namevalue("name","Article about scintific");
cb->ins_namevalue("author","Aristotel");
cb->ins_namevalue("text","Scintific is paradox area of mind");
cb->print();
// Объект с данными помещается в буфер обмена
pastebuf->setMimeData( cb );
Извлечение данных из буфера обмена
C++ (Qt)
// Создается ссылка на буфер обмена
QClipboard *getbuf=QApplication::clipboard();
// Создается объект для приема данных
const clipb *cb2;
// Данные из буфера вставляются в принимающий объект
cb2=qobject_cast<const clipb *>(getbuf->mimeData());
после таких действий в объекте cb2 в переменной типа
clipb_struct будут лежать переданные через буфер данные. И их можно извлекать далее с помощью методов-хелперов, или с помощью одного метода, который просто возращает переменную типа
clipb_struct.
UPD: При вызове вставки данных из буфера, в первую очередь нужно проверять, в каком формате находятся данные в буфере. Это необходимо, чтобы не произошло сегфолта при попытке вставить данные одного формата в объект другого формата. Проверить формат данных очень просто.
C++ (Qt)
// Если буфер обмена не содержит данные нужного формата,
// вставку из буфера делать нельзя
if(!(QApplication::clipboard()->mimeData()->hasFormat("mytetra/records"))) return;
Чтобы все стало более понятно, прикрепляю готовый пример. В примере демонстрируется передача соственного типа данных через буфер обмена. Пример предельно простой, нет никаких кнопочек, только главное окно программы. В примере происходит копирование в буфер, и, сразу, извлечение из буфера. Вся последовательность действий пишется в консоль.
Проверено на Qt 4.4.1, Linux.